auth.c revision 204917
1221828Sgrehan/* $OpenBSD: auth.c,v 1.86 2010/03/05 02:58:11 djm Exp $ */ 2221828Sgrehan/* 3221828Sgrehan * Copyright (c) 2000 Markus Friedl. All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15221828Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16221828Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17221828Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18221828Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19221828Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20221828Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21221828Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22221828Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23221828Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24221828Sgrehan */ 25221828Sgrehan 26221828Sgrehan#include "includes.h" 27221828Sgrehan__RCSID("$FreeBSD: head/crypto/openssh/auth.c 204917 2010-03-09 19:16:43Z des $"); 28221828Sgrehan 29221828Sgrehan#include <sys/types.h> 30221828Sgrehan#include <sys/stat.h> 31221828Sgrehan#include <sys/param.h> 32221828Sgrehan 33234695Sgrehan#include <netinet/in.h> 34221828Sgrehan 35221828Sgrehan#include <errno.h> 36221828Sgrehan#include <fcntl.h> 37221828Sgrehan#ifdef HAVE_PATHS_H 38221828Sgrehan# include <paths.h> 39221828Sgrehan#endif 40221828Sgrehan#include <pwd.h> 41221828Sgrehan#ifdef HAVE_LOGIN_H 42221828Sgrehan#include <login.h> 43221828Sgrehan#endif 44221828Sgrehan#ifdef USE_SHADOW 45221828Sgrehan#include <shadow.h> 46221828Sgrehan#endif 47221828Sgrehan#ifdef HAVE_LIBGEN_H 48221828Sgrehan#include <libgen.h> 49221828Sgrehan#endif 50241489Sneel#include <stdarg.h> 51221914Sjhb#include <stdio.h> 52221828Sgrehan#include <string.h> 53221828Sgrehan#include <unistd.h> 54221828Sgrehan 55221828Sgrehan#include "xmalloc.h" 56221828Sgrehan#include "match.h" 57221828Sgrehan#include "groupaccess.h" 58221828Sgrehan#include "log.h" 59221828Sgrehan#include "buffer.h" 60221828Sgrehan#include "servconf.h" 61242065Sneel#include "key.h" 62221828Sgrehan#include "hostfile.h" 63221828Sgrehan#include "auth.h" 64221828Sgrehan#include "auth-options.h" 65221828Sgrehan#include "canohost.h" 66221828Sgrehan#include "uidswap.h" 67221828Sgrehan#include "misc.h" 68221828Sgrehan#include "packet.h" 69221828Sgrehan#include "loginrec.h" 70241489Sneel#ifdef GSSAPI 71241489Sneel#include "ssh-gss.h" 72221828Sgrehan#endif 73221828Sgrehan#include "authfile.h" 74221828Sgrehan#include "monitor_wrap.h" 75221828Sgrehan 76221828Sgrehan/* import */ 77234695Sgrehanextern ServerOptions options; 78221828Sgrehanextern int use_privsep; 79240894Sneelextern Buffer loginmsg; 80240922Sneelextern struct passwd *privsep_pw; 81241982Sneel 82221828Sgrehan/* Debugging messages */ 83221828SgrehanBuffer auth_debug; 84221828Sgrehanint auth_debug_init; 85221828Sgrehan 86221828Sgrehan/* 87221828Sgrehan * Check if the user is allowed to log in via ssh. If user is listed 88221828Sgrehan * in DenyUsers or one of user's groups is listed in DenyGroups, false 89221828Sgrehan * will be returned. If AllowUsers isn't empty and user isn't listed 90221828Sgrehan * there, or if AllowGroups isn't empty and one of user's groups isn't 91221828Sgrehan * listed there, false will be returned. 92221828Sgrehan * If the user's shell is not executable, false will be returned. 93221828Sgrehan * Otherwise true is returned. 94221828Sgrehan */ 95221828Sgrehanint 96242065Sneelallowed_user(struct passwd * pw) 97242065Sneel{ 98242065Sneel struct stat st; 99241489Sneel const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL; 100221828Sgrehan u_int i; 101221828Sgrehan#ifdef USE_SHADOW 102221828Sgrehan struct spwd *spw = NULL; 103221828Sgrehan#endif 104221828Sgrehan 105221828Sgrehan /* Shouldn't be called if pw is NULL, but better safe than sorry... */ 106221828Sgrehan if (!pw || !pw->pw_name) 107221828Sgrehan return 0; 108221828Sgrehan 109221828Sgrehan#ifdef USE_SHADOW 110221828Sgrehan if (!options.use_pam) 111223621Sgrehan spw = getspnam(pw->pw_name); 112221828Sgrehan#ifdef HAS_SHADOW_EXPIRE 113221828Sgrehan if (!options.use_pam && spw != NULL && auth_shadow_acctexpired(spw)) 114221828Sgrehan return 0; 115223621Sgrehan#endif /* HAS_SHADOW_EXPIRE */ 116221828Sgrehan#endif /* USE_SHADOW */ 117221828Sgrehan 118221828Sgrehan /* grab passwd field for locked account check */ 119221828Sgrehan passwd = pw->pw_passwd; 120221828Sgrehan#ifdef USE_SHADOW 121221828Sgrehan if (spw != NULL) 122221828Sgrehan#ifdef USE_LIBIAF 123240894Sneel passwd = get_iaf_password(pw); 124240894Sneel#else 125221828Sgrehan passwd = spw->sp_pwdp; 126241147Sneel#endif /* USE_LIBIAF */ 127241147Sneel#endif 128241147Sneel 129241147Sneel /* check for locked account */ 130241147Sneel if (!options.use_pam && passwd && *passwd) { 131241147Sneel int locked = 0; 132221828Sgrehan 133221828Sgrehan#ifdef LOCKED_PASSWD_STRING 134221828Sgrehan if (strcmp(passwd, LOCKED_PASSWD_STRING) == 0) 135221828Sgrehan locked = 1; 136221828Sgrehan#endif 137221828Sgrehan#ifdef LOCKED_PASSWD_PREFIX 138221828Sgrehan if (strncmp(passwd, LOCKED_PASSWD_PREFIX, 139221828Sgrehan strlen(LOCKED_PASSWD_PREFIX)) == 0) 140221828Sgrehan locked = 1; 141221828Sgrehan#endif 142221828Sgrehan#ifdef LOCKED_PASSWD_SUBSTR 143221828Sgrehan if (strstr(passwd, LOCKED_PASSWD_SUBSTR)) 144221828Sgrehan locked = 1; 145221828Sgrehan#endif 146221828Sgrehan#ifdef USE_LIBIAF 147234695Sgrehan free(passwd); 148234695Sgrehan#endif /* USE_LIBIAF */ 149221828Sgrehan if (locked) { 150221828Sgrehan logit("User %.100s not allowed because account is locked", 151221828Sgrehan pw->pw_name); 152221828Sgrehan return 0; 153221828Sgrehan } 154221828Sgrehan } 155221828Sgrehan 156221828Sgrehan /* 157221828Sgrehan * Deny if shell does not exist or is not executable unless we 158221828Sgrehan * are chrooting. 159221828Sgrehan */ 160234695Sgrehan if (options.chroot_directory == NULL || 161234695Sgrehan strcasecmp(options.chroot_directory, "none") == 0) { 162221828Sgrehan char *shell = xstrdup((pw->pw_shell[0] == '\0') ? 163221828Sgrehan _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */ 164221828Sgrehan 165221828Sgrehan if (stat(shell, &st) != 0) { 166221828Sgrehan logit("User %.100s not allowed because shell %.100s " 167221828Sgrehan "does not exist", pw->pw_name, shell); 168221828Sgrehan xfree(shell); 169221828Sgrehan return 0; 170221828Sgrehan } 171241489Sneel if (S_ISREG(st.st_mode) == 0 || 172241489Sneel (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { 173221828Sgrehan logit("User %.100s not allowed because shell %.100s " 174221828Sgrehan "is not executable", pw->pw_name, shell); 175240943Sneel xfree(shell); 176234695Sgrehan return 0; 177234695Sgrehan } 178221828Sgrehan xfree(shell); 179221828Sgrehan } 180221828Sgrehan 181240894Sneel if (options.num_deny_users > 0 || options.num_allow_users > 0 || 182240894Sneel options.num_deny_groups > 0 || options.num_allow_groups > 0) { 183240894Sneel hostname = get_canonical_hostname(options.use_dns); 184240894Sneel ipaddr = get_remote_ipaddr(); 185240894Sneel } 186240894Sneel 187240894Sneel /* Return false if user is listed in DenyUsers */ 188240894Sneel if (options.num_deny_users > 0) { 189240894Sneel for (i = 0; i < options.num_deny_users; i++) 190240894Sneel if (match_user(pw->pw_name, hostname, ipaddr, 191240894Sneel options.deny_users[i])) { 192240894Sneel logit("User %.100s from %.100s not allowed " 193240894Sneel "because listed in DenyUsers", 194221828Sgrehan pw->pw_name, hostname); 195221828Sgrehan return 0; 196221828Sgrehan } 197221828Sgrehan } 198221828Sgrehan /* Return false if AllowUsers isn't empty and user isn't listed there */ 199221828Sgrehan if (options.num_allow_users > 0) { 200221828Sgrehan for (i = 0; i < options.num_allow_users; i++) 201221828Sgrehan if (match_user(pw->pw_name, hostname, ipaddr, 202221828Sgrehan options.allow_users[i])) 203221828Sgrehan break; 204221828Sgrehan /* i < options.num_allow_users iff we break for loop */ 205221828Sgrehan if (i >= options.num_allow_users) { 206221828Sgrehan logit("User %.100s from %.100s not allowed because " 207221828Sgrehan "not listed in AllowUsers", pw->pw_name, hostname); 208221828Sgrehan return 0; 209221828Sgrehan } 210221828Sgrehan } 211221828Sgrehan if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { 212221828Sgrehan /* Get the user's group access list (primary and supplementary) */ 213221828Sgrehan if (ga_init(pw->pw_name, pw->pw_gid) == 0) { 214221828Sgrehan logit("User %.100s from %.100s not allowed because " 215221828Sgrehan "not in any group", pw->pw_name, hostname); 216221828Sgrehan return 0; 217221828Sgrehan } 218221828Sgrehan 219221828Sgrehan /* Return false if one of user's groups is listed in DenyGroups */ 220221828Sgrehan if (options.num_deny_groups > 0) 221221828Sgrehan if (ga_match(options.deny_groups, 222221828Sgrehan options.num_deny_groups)) { 223221828Sgrehan ga_free(); 224221828Sgrehan logit("User %.100s from %.100s not allowed " 225221828Sgrehan "because a group is listed in DenyGroups", 226221828Sgrehan pw->pw_name, hostname); 227221828Sgrehan return 0; 228221828Sgrehan } 229241454Sneel /* 230241454Sneel * Return false if AllowGroups isn't empty and one of user's groups 231241454Sneel * isn't listed there 232241454Sneel */ 233241454Sneel if (options.num_allow_groups > 0) 234241454Sneel if (!ga_match(options.allow_groups, 235221828Sgrehan options.num_allow_groups)) { 236221828Sgrehan ga_free(); 237221828Sgrehan logit("User %.100s from %.100s not allowed " 238221828Sgrehan "because none of user's groups are listed " 239221828Sgrehan "in AllowGroups", pw->pw_name, hostname); 240221828Sgrehan return 0; 241221828Sgrehan } 242221828Sgrehan ga_free(); 243221828Sgrehan } 244221828Sgrehan 245221828Sgrehan#ifdef CUSTOM_SYS_AUTH_ALLOWED_USER 246221828Sgrehan if (!sys_auth_allowed_user(pw, &loginmsg)) 247221828Sgrehan return 0; 248221828Sgrehan#endif 249221828Sgrehan 250221828Sgrehan /* We found no reason not to let this user try to log on... */ 251221828Sgrehan return 1; 252221828Sgrehan} 253221828Sgrehan 254221828Sgrehanvoid 255221828Sgrehanauth_log(Authctxt *authctxt, int authenticated, char *method, char *info) 256221828Sgrehan{ 257221828Sgrehan void (*authlog) (const char *fmt,...) = verbose; 258221828Sgrehan char *authmsg; 259221828Sgrehan 260221828Sgrehan if (use_privsep && !mm_is_monitor() && !authctxt->postponed) 261221828Sgrehan return; 262221828Sgrehan 263221828Sgrehan /* Raise logging level */ 264221828Sgrehan if (authenticated == 1 || 265221828Sgrehan !authctxt->valid || 266221828Sgrehan authctxt->failures >= options.max_authtries / 2 || 267221828Sgrehan strcmp(method, "password") == 0) 268221828Sgrehan authlog = logit; 269221828Sgrehan 270221828Sgrehan if (authctxt->postponed) 271221828Sgrehan authmsg = "Postponed"; 272221828Sgrehan else 273221828Sgrehan authmsg = authenticated ? "Accepted" : "Failed"; 274221828Sgrehan 275221828Sgrehan authlog("%s %s for %s%.100s from %.200s port %d%s", 276221828Sgrehan authmsg, 277221828Sgrehan method, 278221828Sgrehan authctxt->valid ? "" : "invalid user ", 279221828Sgrehan authctxt->user, 280221828Sgrehan get_remote_ipaddr(), 281221828Sgrehan get_remote_port(), 282221828Sgrehan info); 283221828Sgrehan 284221828Sgrehan#ifdef CUSTOM_FAILED_LOGIN 285221828Sgrehan if (authenticated == 0 && !authctxt->postponed && 286221828Sgrehan (strcmp(method, "password") == 0 || 287241178Sneel strncmp(method, "keyboard-interactive", 20) == 0 || 288241178Sneel strcmp(method, "challenge-response") == 0)) 289241178Sneel record_failed_login(authctxt->user, 290241178Sneel get_canonical_hostname(options.use_dns), "ssh"); 291241178Sneel# ifdef WITH_AIXAUTHENTICATE 292241362Sneel if (authenticated) 293241178Sneel sys_auth_record_login(authctxt->user, 294241362Sneel get_canonical_hostname(options.use_dns), "ssh", &loginmsg); 295241362Sneel# endif 296241178Sneel#endif 297241178Sneel#ifdef SSH_AUDIT_EVENTS 298241178Sneel if (authenticated == 0 && !authctxt->postponed) 299241178Sneel audit_event(audit_classify_auth(method)); 300241178Sneel#endif 301241178Sneel} 302241178Sneel 303241178Sneel/* 304241362Sneel * Check whether root logins are disallowed. 305241362Sneel */ 306241362Sneelint 307241362Sneelauth_root_allowed(char *method) 308241362Sneel{ 309241362Sneel switch (options.permit_root_login) { 310241362Sneel case PERMIT_YES: 311241178Sneel return 1; 312241178Sneel case PERMIT_NO_PASSWD: 313241178Sneel if (strcmp(method, "password") != 0) 314241178Sneel return 1; 315241178Sneel break; 316241362Sneel case PERMIT_FORCED_ONLY: 317241362Sneel if (forced_command) { 318241362Sneel logit("Root login accepted for forced command."); 319241362Sneel return 1; 320241362Sneel } 321241362Sneel break; 322241178Sneel } 323241178Sneel logit("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr()); 324241178Sneel return 0; 325221828Sgrehan} 326221828Sgrehan 327221828Sgrehan 328221828Sgrehan/* 329221828Sgrehan * Given a template and a passwd structure, build a filename 330221828Sgrehan * by substituting % tokenised options. Currently, %% becomes '%', 331221828Sgrehan * %h becomes the home directory and %u the username. 332221828Sgrehan * 333241178Sneel * This returns a buffer allocated by xmalloc. 334221828Sgrehan */ 335241178Sneelstatic char * 336241178Sneelexpand_authorized_keys(const char *filename, struct passwd *pw) 337221828Sgrehan{ 338221828Sgrehan char *file, ret[MAXPATHLEN]; 339221828Sgrehan int i; 340221828Sgrehan 341221828Sgrehan file = percent_expand(filename, "h", pw->pw_dir, 342221828Sgrehan "u", pw->pw_name, (char *)NULL); 343221828Sgrehan 344221828Sgrehan /* 345221828Sgrehan * Ensure that filename starts anchored. If not, be backward 346221828Sgrehan * compatible and prepend the '%h/' 347221828Sgrehan */ 348221828Sgrehan if (*file == '/') 349221828Sgrehan return (file); 350221828Sgrehan 351221828Sgrehan i = snprintf(ret, sizeof(ret), "%s/%s", pw->pw_dir, file); 352221828Sgrehan if (i < 0 || (size_t)i >= sizeof(ret)) 353221828Sgrehan fatal("expand_authorized_keys: path too long"); 354221828Sgrehan xfree(file); 355221828Sgrehan return (xstrdup(ret)); 356221828Sgrehan} 357221828Sgrehan 358241147Sneelchar * 359241147Sneelauthorized_keys_file(struct passwd *pw) 360221828Sgrehan{ 361221828Sgrehan return expand_authorized_keys(options.authorized_keys_file, pw); 362221828Sgrehan} 363221828Sgrehan 364221828Sgrehanchar * 365221828Sgrehanauthorized_keys_file2(struct passwd *pw) 366221828Sgrehan{ 367241147Sneel return expand_authorized_keys(options.authorized_keys_file2, pw); 368241147Sneel} 369221828Sgrehan 370221828Sgrehan/* return ok if key exists in sysfile or userfile */ 371241041SneelHostStatus 372241041Sneelcheck_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, 373241041Sneel const char *sysfile, const char *userfile) 374241041Sneel{ 375241041Sneel Key *found; 376241041Sneel char *user_hostfile; 377241041Sneel struct stat st; 378241041Sneel HostStatus host_status; 379241041Sneel 380241041Sneel /* Check if we know the host and its host key. */ 381241041Sneel found = key_new(key->type); 382241041Sneel host_status = check_host_in_hostfile(sysfile, host, key, found, NULL); 383241041Sneel 384241041Sneel if (host_status != HOST_OK && userfile != NULL) { 385241041Sneel user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 386241041Sneel if (options.strict_modes && 387241041Sneel (stat(user_hostfile, &st) == 0) && 388241041Sneel ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || 389241041Sneel (st.st_mode & 022) != 0)) { 390241041Sneel logit("Authentication refused for %.100s: " 391241041Sneel "bad owner or modes for %.200s", 392241041Sneel pw->pw_name, user_hostfile); 393221828Sgrehan } else { 394241041Sneel temporarily_use_uid(pw); 395221828Sgrehan host_status = check_host_in_hostfile(user_hostfile, 396241041Sneel host, key, found, NULL); 397241178Sneel restore_uid(); 398241041Sneel } 399241362Sneel xfree(user_hostfile); 400221828Sgrehan } 401221828Sgrehan key_free(found); 402241041Sneel 403241041Sneel debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ? 404241041Sneel "ok" : "not found", host); 405221828Sgrehan return host_status; 406241041Sneel} 407241041Sneel 408241041Sneel 409241041Sneel/* 410241041Sneel * Check a given file for security. This is defined as all components 411241041Sneel * of the path to the file must be owned by either the owner of 412241041Sneel * of the file or root and no directories must be group or world writable. 413241041Sneel * 414241041Sneel * XXX Should any specific check be done for sym links ? 415241041Sneel * 416241041Sneel * Takes an open file descriptor, the file name, a uid and and 417221828Sgrehan * error buffer plus max size as arguments. 418241041Sneel * 419241041Sneel * Returns 0 on success and -1 on failure 420221828Sgrehan */ 421241041Sneelstatic int 422241041Sneelsecure_filename(FILE *f, const char *file, struct passwd *pw, 423221828Sgrehan char *err, size_t errlen) 424241041Sneel{ 425241041Sneel uid_t uid = pw->pw_uid; 426241041Sneel char buf[MAXPATHLEN], homedir[MAXPATHLEN]; 427241041Sneel char *cp; 428241041Sneel int comparehome = 0; 429241041Sneel struct stat st; 430241041Sneel 431221828Sgrehan if (realpath(file, buf) == NULL) { 432221828Sgrehan snprintf(err, errlen, "realpath %s failed: %s", file, 433221828Sgrehan strerror(errno)); 434241362Sneel return -1; 435241362Sneel } 436241178Sneel if (realpath(pw->pw_dir, homedir) != NULL) 437221828Sgrehan comparehome = 1; 438241362Sneel 439241178Sneel /* check the open file to avoid races */ 440241178Sneel if (fstat(fileno(f), &st) < 0 || 441241178Sneel (st.st_uid != 0 && st.st_uid != uid) || 442241178Sneel (st.st_mode & 022) != 0) { 443241178Sneel snprintf(err, errlen, "bad ownership or modes for file %s", 444241178Sneel buf); 445241178Sneel return -1; 446241178Sneel } 447241178Sneel 448241178Sneel /* for each component of the canonical path, walking upwards */ 449241178Sneel for (;;) { 450241178Sneel if ((cp = dirname(buf)) == NULL) { 451241178Sneel snprintf(err, errlen, "dirname() failed"); 452241178Sneel return -1; 453241362Sneel } 454241362Sneel strlcpy(buf, cp, sizeof(buf)); 455241362Sneel 456241362Sneel debug3("secure_filename: checking '%s'", buf); 457241362Sneel if (stat(buf, &st) < 0 || 458241178Sneel (st.st_uid != 0 && st.st_uid != uid) || 459241178Sneel (st.st_mode & 022) != 0) { 460241178Sneel snprintf(err, errlen, 461241178Sneel "bad ownership or modes for directory %s", buf); 462241178Sneel return -1; 463241362Sneel } 464241178Sneel 465221828Sgrehan /* If are past the homedir then we can stop */ 466221828Sgrehan if (comparehome && strcmp(homedir, buf) == 0) { 467221828Sgrehan debug3("secure_filename: terminating check at '%s'", 468241362Sneel buf); 469241362Sneel break; 470241362Sneel } 471241362Sneel /* 472241362Sneel * dirname should always complete with a "/" path, 473241362Sneel * but we can be paranoid and check for "." too 474221828Sgrehan */ 475241041Sneel if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) 476221828Sgrehan break; 477221828Sgrehan } 478221828Sgrehan return 0; 479221828Sgrehan} 480221828Sgrehan 481221828SgrehanFILE * 482241148Sneelauth_openkeyfile(const char *file, struct passwd *pw, int strict_modes) 483221828Sgrehan{ 484241148Sneel char line[1024]; 485241148Sneel struct stat st; 486241148Sneel int fd; 487241148Sneel FILE *f; 488241147Sneel 489221828Sgrehan /* 490221828Sgrehan * Open the file containing the authorized keys 491221828Sgrehan * Fail quietly if file does not exist 492221828Sgrehan */ 493221828Sgrehan if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) { 494221828Sgrehan if (errno != ENOENT) 495221828Sgrehan debug("Could not open keyfile '%s': %s", file, 496221828Sgrehan strerror(errno)); 497221828Sgrehan return NULL; 498221828Sgrehan } 499221828Sgrehan 500221828Sgrehan if (fstat(fd, &st) < 0) { 501221828Sgrehan close(fd); 502221828Sgrehan return NULL; 503221828Sgrehan } 504221828Sgrehan if (!S_ISREG(st.st_mode)) { 505221828Sgrehan logit("User %s authorized keys %s is not a regular file", 506221828Sgrehan pw->pw_name, file); 507221828Sgrehan close(fd); 508221828Sgrehan return NULL; 509221828Sgrehan } 510221828Sgrehan unset_nonblock(fd); 511221828Sgrehan if ((f = fdopen(fd, "r")) == NULL) { 512221828Sgrehan close(fd); 513221828Sgrehan return NULL; 514221828Sgrehan } 515221828Sgrehan if (options.strict_modes && 516221828Sgrehan secure_filename(f, file, pw, line, sizeof(line)) != 0) { 517221828Sgrehan fclose(f); 518221828Sgrehan logit("Authentication refused: %s", line); 519221828Sgrehan return NULL; 520221828Sgrehan } 521221828Sgrehan 522221828Sgrehan return f; 523221828Sgrehan} 524221828Sgrehan 525221828Sgrehanstruct passwd * 526221828Sgrehangetpwnamallow(const char *user) 527221828Sgrehan{ 528221828Sgrehan#ifdef HAVE_LOGIN_CAP 529221828Sgrehan extern login_cap_t *lc; 530221828Sgrehan#ifdef BSD_AUTH 531221828Sgrehan auth_session_t *as; 532221828Sgrehan#endif 533221828Sgrehan#endif 534221828Sgrehan struct passwd *pw; 535221828Sgrehan 536221828Sgrehan parse_server_match_config(&options, user, 537221828Sgrehan get_canonical_hostname(options.use_dns), get_remote_ipaddr()); 538221828Sgrehan 539221828Sgrehan#if defined(_AIX) && defined(HAVE_SETAUTHDB) 540221828Sgrehan aix_setauthdb(user); 541221828Sgrehan#endif 542221828Sgrehan 543221828Sgrehan pw = getpwnam(user); 544221828Sgrehan 545221828Sgrehan#if defined(_AIX) && defined(HAVE_SETAUTHDB) 546221828Sgrehan aix_restoreauthdb(); 547221828Sgrehan#endif 548221828Sgrehan#ifdef HAVE_CYGWIN 549221828Sgrehan /* 550221828Sgrehan * Windows usernames are case-insensitive. To avoid later problems 551221828Sgrehan * when trying to match the username, the user is only allowed to 552221828Sgrehan * login if the username is given in the same case as stored in the 553221828Sgrehan * user database. 554221828Sgrehan */ 555221828Sgrehan if (pw != NULL && strcmp(user, pw->pw_name) != 0) { 556221828Sgrehan logit("Login name %.100s does not match stored username %.100s", 557221828Sgrehan user, pw->pw_name); 558221828Sgrehan pw = NULL; 559221828Sgrehan } 560221828Sgrehan#endif 561221828Sgrehan if (pw == NULL) { 562221828Sgrehan logit("Invalid user %.100s from %.100s", 563221828Sgrehan user, get_remote_ipaddr()); 564221828Sgrehan#ifdef CUSTOM_FAILED_LOGIN 565221828Sgrehan record_failed_login(user, 566221828Sgrehan get_canonical_hostname(options.use_dns), "ssh"); 567221828Sgrehan#endif 568221828Sgrehan#ifdef SSH_AUDIT_EVENTS 569221828Sgrehan audit_event(SSH_INVALID_USER); 570221828Sgrehan#endif /* SSH_AUDIT_EVENTS */ 571221828Sgrehan return (NULL); 572221828Sgrehan } 573221828Sgrehan if (!allowed_user(pw)) 574221828Sgrehan return (NULL); 575221828Sgrehan#ifdef HAVE_LOGIN_CAP 576221828Sgrehan if ((lc = login_getpwclass(pw)) == NULL) { 577221828Sgrehan debug("unable to get login class: %s", user); 578221828Sgrehan return (NULL); 579221828Sgrehan } 580221828Sgrehan#ifdef BSD_AUTH 581221828Sgrehan if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || 582221828Sgrehan auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { 583221828Sgrehan debug("Approval failure for %s", user); 584221828Sgrehan pw = NULL; 585221828Sgrehan } 586221828Sgrehan if (as != NULL) 587221828Sgrehan auth_close(as); 588221828Sgrehan#endif 589221828Sgrehan#endif 590221828Sgrehan if (pw != NULL) 591221828Sgrehan return (pwcopy(pw)); 592221828Sgrehan return (NULL); 593221828Sgrehan} 594221828Sgrehan 595221828Sgrehan/* Returns 1 if key is revoked by revoked_keys_file, 0 otherwise */ 596221828Sgrehanint 597221828Sgrehanauth_key_is_revoked(Key *key) 598221828Sgrehan{ 599221828Sgrehan char *key_fp; 600221828Sgrehan 601221828Sgrehan if (options.revoked_keys_file == NULL) 602221828Sgrehan return 0; 603221828Sgrehan 604221828Sgrehan switch (key_in_file(key, options.revoked_keys_file, 0)) { 605221828Sgrehan case 0: 606221828Sgrehan /* key not revoked */ 607221828Sgrehan return 0; 608221828Sgrehan case -1: 609221828Sgrehan /* Error opening revoked_keys_file: refuse all keys */ 610221828Sgrehan error("Revoked keys file is unreadable: refusing public key " 611221828Sgrehan "authentication"); 612221828Sgrehan return 1; 613221828Sgrehan case 1: 614221828Sgrehan /* Key revoked */ 615221828Sgrehan key_fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); 616221828Sgrehan error("WARNING: authentication attempt with a revoked " 617221828Sgrehan "%s key %s ", key_type(key), key_fp); 618221828Sgrehan xfree(key_fp); 619221828Sgrehan return 1; 620221828Sgrehan } 621221828Sgrehan fatal("key_in_file returned junk"); 622221828Sgrehan} 623221828Sgrehan 624221828Sgrehanvoid 625221828Sgrehanauth_debug_add(const char *fmt,...) 626221828Sgrehan{ 627221828Sgrehan char buf[1024]; 628221828Sgrehan va_list args; 629221828Sgrehan 630221828Sgrehan if (!auth_debug_init) 631221828Sgrehan return; 632221828Sgrehan 633221828Sgrehan va_start(args, fmt); 634221828Sgrehan vsnprintf(buf, sizeof(buf), fmt, args); 635221828Sgrehan va_end(args); 636221828Sgrehan buffer_put_cstring(&auth_debug, buf); 637221828Sgrehan} 638221828Sgrehan 639221828Sgrehanvoid 640221828Sgrehanauth_debug_send(void) 641234695Sgrehan{ 642234695Sgrehan char *msg; 643221828Sgrehan 644234695Sgrehan if (!auth_debug_init) 645221828Sgrehan return; 646221828Sgrehan while (buffer_len(&auth_debug)) { 647221828Sgrehan msg = buffer_get_string(&auth_debug, NULL); 648221828Sgrehan packet_send_debug("%s", msg); 649221828Sgrehan xfree(msg); 650221828Sgrehan } 651234695Sgrehan} 652221828Sgrehan 653221828Sgrehanvoid 654221828Sgrehanauth_debug_reset(void) 655242065Sneel{ 656242065Sneel if (auth_debug_init) 657221828Sgrehan buffer_clear(&auth_debug); 658221828Sgrehan else { 659221828Sgrehan buffer_init(&auth_debug); 660242065Sneel auth_debug_init = 1; 661221828Sgrehan } 662221828Sgrehan} 663242065Sneel 664242065Sneelstruct passwd * 665221828Sgrehanfakepw(void) 666221828Sgrehan{ 667221828Sgrehan static struct passwd fake; 668221828Sgrehan 669221828Sgrehan memset(&fake, 0, sizeof(fake)); 670221828Sgrehan fake.pw_name = "NOUSER"; 671221828Sgrehan fake.pw_passwd = 672242065Sneel "$2a$06$r3.juUaHZDlIbQaO2dS9FuYxL1W9M81R1Tc92PoSNmzvpEqLkLGrK"; 673242065Sneel fake.pw_gecos = "NOUSER"; 674242065Sneel fake.pw_uid = privsep_pw == NULL ? (uid_t)-1 : privsep_pw->pw_uid; 675221828Sgrehan fake.pw_gid = privsep_pw == NULL ? (gid_t)-1 : privsep_pw->pw_gid; 676221828Sgrehan#ifdef HAVE_PW_CLASS_IN_PASSWD 677221828Sgrehan fake.pw_class = ""; 678221828Sgrehan#endif 679221828Sgrehan fake.pw_dir = "/nonexist"; 680221914Sjhb fake.pw_shell = "/nonexist"; 681221828Sgrehan 682234695Sgrehan return (&fake); 683221828Sgrehan} 684241489Sneel