/* * Processes input from the client and the program. Input data is stored * in buffers and processed later. */ static void process_input(struct ssh *ssh, fd_set *readset) { int len, r; char buf[16384]; /* Read and buffer any input data from the client. */ if (FD_ISSET(connection_in, readset)) { len = read(connection_in, buf, sizeof(buf)); if (len == 0) { verbose("Connection closed by %.100s", ssh_remote_ipaddr(ssh)); connection_closed = 1; if (compat20) return; cleanup_exit(255); } else if (len < 0) { if (errno != EINTR && errno != EAGAIN) { verbose("Read error from remote host " "%.100s: %.100s", ssh_remote_ipaddr(ssh), strerror(errno)); cleanup_exit(255); } } else { /* Buffer any received data. */ ssh_packet_process_incoming(ssh, buf, len); } } if (compat20) return; /* Read and buffer any available stdout data from the program. */ if (!fdout_eof && FD_ISSET(fdout, readset)) { len = read(fdout, buf, sizeof(buf)); if (len < 0 && (errno == EINTR || errno == EAGAIN)) { /* do nothing */ } else if (len <= 0) { fdout_eof = 1; } else { if ((r = sshbuf_put(stdout_buffer, buf, len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); fdout_bytes += len; } } /* Read and buffer any available stderr data from the program. */ if (!fderr_eof && FD_ISSET(fderr, readset)) { len = read(fderr, buf, sizeof(buf)); if (len < 0 && (errno == EINTR || errno == EAGAIN)) { /* do nothing */ } else if (len <= 0) { fderr_eof = 1; } else { if ((r = sshbuf_put(stderr_buffer, buf, len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); } } }
void audit_event(struct ssh *ssh, ssh_audit_event_t event) { switch(event) { case SSH_AUTH_SUCCESS: case SSH_CONNECTION_CLOSE: case SSH_NOLOGIN: case SSH_LOGIN_EXCEED_MAXTRIES: case SSH_LOGIN_ROOT_DENIED: break; case SSH_AUTH_FAIL_NONE: case SSH_AUTH_FAIL_PASSWD: case SSH_AUTH_FAIL_KBDINT: case SSH_AUTH_FAIL_PUBKEY: case SSH_AUTH_FAIL_HOSTBASED: case SSH_AUTH_FAIL_GSSAPI: case SSH_INVALID_USER: linux_audit_record_event(-1, audit_username(), NULL, ssh_remote_ipaddr(ssh), "sshd", 0); break; default: debug("%s: unhandled event %d", __func__, event); break; } }
/* * Check whether root logins are disallowed. */ int auth_root_allowed(const char *method) { struct ssh *ssh = active_state; /* XXX */ switch (options.permit_root_login) { case PERMIT_YES: return 1; case PERMIT_NO_PASSWD: if (strcmp(method, "publickey") == 0 || strcmp(method, "hostbased") == 0 || strcmp(method, "gssapi-with-mic") == 0) return 1; break; case PERMIT_FORCED_ONLY: if (forced_command) { logit("Root login accepted for forced command."); return 1; } break; } logit("ROOT LOGIN REFUSED FROM %.200s port %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); return 0; }
void auth_log(Authctxt *authctxt, int authenticated, int partial, const char *method, const char *submethod) { struct ssh *ssh = active_state; /* XXX */ void (*authlog) (const char *fmt,...) = verbose; char *authmsg; if (use_privsep && !mm_is_monitor() && !authctxt->postponed) return; /* Raise logging level */ if (authenticated == 1 || !authctxt->valid || authctxt->failures >= options.max_authtries / 2 || strcmp(method, "password") == 0) authlog = logit; if (authctxt->postponed) authmsg = "Postponed"; else if (partial) authmsg = "Partial"; else authmsg = authenticated ? "Accepted" : "Failed"; authlog("%s %s%s%s for %s%.100s from %.200s port %d ssh2%s%s", authmsg, method, submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod, authctxt->valid ? "" : "invalid user ", authctxt->user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), authctxt->info != NULL ? ": " : "", authctxt->info != NULL ? authctxt->info : ""); free(authctxt->info); authctxt->info = NULL; #ifdef CUSTOM_FAILED_LOGIN if (authenticated == 0 && !authctxt->postponed && (strcmp(method, "password") == 0 || strncmp(method, "keyboard-interactive", 20) == 0 || strcmp(method, "challenge-response") == 0)) record_failed_login(authctxt->user, auth_get_canonical_hostname(ssh, options.use_dns), "ssh"); # ifdef WITH_AIXAUTHENTICATE if (authenticated) sys_auth_record_login(authctxt->user, auth_get_canonical_hostname(ssh, options.use_dns), "ssh", &loginmsg); # endif #endif #ifdef SSH_AUDIT_EVENTS if (authenticated == 0 && !authctxt->postponed) audit_event(audit_classify_auth(method)); #endif }
int auth_rhosts(struct passwd *pw, const char *client_user) { struct ssh *ssh = active_state; /* XXX */ const char *hostname, *ipaddr; hostname = auth_get_canonical_hostname(ssh, options.use_dns); ipaddr = ssh_remote_ipaddr(ssh); return auth_rhosts2(pw, client_user, hostname, ipaddr); }
void auth_maxtries_exceeded(Authctxt *authctxt) { struct ssh *ssh = active_state; /* XXX */ error("maximum authentication attempts exceeded for " "%s%.100s from %.200s port %d ssh2", authctxt->valid ? "" : "invalid user ", authctxt->user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); packet_disconnect("Too many authentication failures"); /* NOTREACHED */ }
const char * auth_get_canonical_hostname(struct ssh *ssh, int use_dns) { static char *dnsname; if (!use_dns) return ssh_remote_ipaddr(ssh); else if (dnsname != NULL) return dnsname; else { dnsname = remote_hostname(ssh); return dnsname; } }
struct passwd * getpwnamallow(const char *user) { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; #ifdef BSD_AUTH auth_session_t *as; #endif #endif struct ssh *ssh = active_state; /* XXX */ struct passwd *pw; struct connection_info *ci = get_connection_info(1, options.use_dns); ci->user = user; parse_server_match_config(&options, ci); log_change_level(options.log_level); process_permitopen(ssh, &options); pw = getpwnam(user); if (pw == NULL) { pfilter_notify(1); logit("Invalid user %.100s from %.100s port %d", user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); return (NULL); } if (!allowed_user(pw)) return (NULL); #ifdef HAVE_LOGIN_CAP if ((lc = login_getclass(pw->pw_class)) == NULL) { debug("unable to get login class: %s", user); return (NULL); } #ifdef BSD_AUTH if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { debug("Approval failure for %s", user); pw = NULL; } if (as != NULL) auth_close(as); #endif #endif if (pw != NULL) return (pwcopy(pw)); return (NULL); }
void auth_log(Authctxt *authctxt, int authenticated, int partial, const char *method, const char *submethod) { struct ssh *ssh = active_state; /* XXX */ void (*authlog) (const char *fmt,...) = verbose; const char *authmsg; char *extra = NULL; if (use_privsep && !mm_is_monitor() && !authctxt->postponed) return; /* Raise logging level */ if (authenticated == 1 || !authctxt->valid || authctxt->failures >= options.max_authtries / 2 || strcmp(method, "password") == 0) authlog = logit; if (authctxt->postponed) authmsg = "Postponed"; else if (partial) authmsg = "Partial"; else authmsg = authenticated ? "Accepted" : "Failed"; if ((extra = format_method_key(authctxt)) == NULL) { if (authctxt->auth_method_info != NULL) extra = xstrdup(authctxt->auth_method_info); } authlog("%s %s%s%s for %s%.100s from %.200s port %d ssh2%s%s", authmsg, method, submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod, authctxt->valid ? "" : "invalid user ", authctxt->user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), extra != NULL ? ": " : "", extra != NULL ? extra : ""); free(extra); if (!authctxt->postponed) pfilter_notify(!authenticated); }
/* * Processes input from the client and the program. Input data is stored * in buffers and processed later. */ static void process_input(fd_set *readset) { struct ssh *ssh = active_state; /* XXX */ int len; char buf[16384]; /* Read and buffer any input data from the client. */ if (FD_ISSET(connection_in, readset)) { len = read(connection_in, buf, sizeof(buf)); if (len == 0) { verbose("Connection closed by %.100s port %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); connection_closed = 1; if (compat20) return; cleanup_exit(255); } else if (len < 0) { if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { verbose("Read error from remote host " "%.100s port %d: %.100s", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), strerror(errno)); cleanup_exit(255); } } else { /* Buffer any received data. */ packet_process_incoming(buf, len); } } if (compat20) return; /* Read and buffer any available stdout data from the program. */ if (!fdout_eof && FD_ISSET(fdout, readset)) { errno = 0; len = read(fdout, buf, sizeof(buf)); if (len < 0 && (errno == EINTR || ((errno == EAGAIN || errno == EWOULDBLOCK) && !child_terminated))) { /* do nothing */ #ifndef PTY_ZEROREAD } else if (len <= 0) { #else } else if ((!isatty(fdout) && len <= 0) || (isatty(fdout) && (len < 0 || (len == 0 && errno != 0)))) { #endif fdout_eof = 1; } else { buffer_append(&stdout_buffer, buf, len); fdout_bytes += len; } } /* Read and buffer any available stderr data from the program. */ if (!fderr_eof && FD_ISSET(fderr, readset)) { errno = 0; len = read(fderr, buf, sizeof(buf)); if (len < 0 && (errno == EINTR || ((errno == EAGAIN || errno == EWOULDBLOCK) && !child_terminated))) { /* do nothing */ #ifndef PTY_ZEROREAD } else if (len <= 0) { #else } else if ((!isatty(fderr) && len <= 0) || (isatty(fderr) && (len < 0 || (len == 0 && errno != 0)))) { #endif fderr_eof = 1; } else { buffer_append(&stderr_buffer, buf, len); } } }
/* * Check if the user is allowed to log in via ssh. If user is listed * in DenyUsers or one of user's groups is listed in DenyGroups, false * will be returned. If AllowUsers isn't empty and user isn't listed * there, or if AllowGroups isn't empty and one of user's groups isn't * listed there, false will be returned. * If the user's shell is not executable, false will be returned. * Otherwise true is returned. */ int allowed_user(struct passwd * pw) { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; int match_name, match_ip; char *cap_hlist, *hp; #endif struct ssh *ssh = active_state; /* XXX */ struct stat st; const char *hostname = NULL, *ipaddr = NULL; int r; u_int i; /* Shouldn't be called if pw is NULL, but better safe than sorry... */ if (!pw || !pw->pw_name) return 0; #ifdef HAVE_LOGIN_CAP hostname = auth_get_canonical_hostname(ssh, options.use_dns); ipaddr = ssh_remote_ipaddr(ssh); lc = login_getclass(pw->pw_class); /* * Check the deny list. */ cap_hlist = login_getcapstr(lc, "host.deny", NULL, NULL); if (cap_hlist != NULL) { hp = strtok(cap_hlist, ","); while (hp != NULL) { match_name = match_hostname(hostname, hp); match_ip = match_hostname(ipaddr, hp); /* * Only a positive match here causes a "deny". */ if (match_name > 0 || match_ip > 0) { free(cap_hlist); login_close(lc); return 0; } hp = strtok(NULL, ","); } free(cap_hlist); } /* * Check the allow list. If the allow list exists, and the * remote host is not in it, the user is implicitly denied. */ cap_hlist = login_getcapstr(lc, "host.allow", NULL, NULL); if (cap_hlist != NULL) { hp = strtok(cap_hlist, ","); if (hp == NULL) { /* Just in case there's an empty string... */ free(cap_hlist); login_close(lc); return 0; } while (hp != NULL) { match_name = match_hostname(hostname, hp); match_ip = match_hostname(ipaddr, hp); /* * Negative match causes an immediate "deny". * Positive match causes us to break out * of the loop (allowing a fallthrough). */ if (match_name < 0 || match_ip < 0) { free(cap_hlist); login_close(lc); return 0; } if (match_name > 0 || match_ip > 0) break; hp = strtok(NULL, ","); } free(cap_hlist); if (hp == NULL) { login_close(lc); return 0; } } login_close(lc); #endif #ifdef USE_PAM if (!options.use_pam) { #endif /* * password/account expiration. */ if (pw->pw_change || pw->pw_expire) { struct timeval tv; (void)gettimeofday(&tv, (struct timezone *)NULL); if (pw->pw_expire) { if (tv.tv_sec >= pw->pw_expire) { logit("User %.100s not allowed because account has expired", pw->pw_name); return 0; /* expired */ } } #ifdef _PASSWORD_CHGNOW if (pw->pw_change == _PASSWORD_CHGNOW) { logit("User %.100s not allowed because password needs to be changed", pw->pw_name); return 0; /* can't force password change (yet) */ } #endif if (pw->pw_change) { if (tv.tv_sec >= pw->pw_change) { logit("User %.100s not allowed because password has expired", pw->pw_name); return 0; /* expired */ } } } #ifdef USE_PAM } #endif /* * Deny if shell does not exist or is not executable unless we * are chrooting. */ /* * XXX Should check to see if it is executable by the * XXX requesting user. --thorpej */ if (options.chroot_directory == NULL || strcasecmp(options.chroot_directory, "none") == 0) { char *shell = xstrdup((pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */ if (stat(shell, &st) != 0) { logit("User %.100s not allowed because shell %.100s " "does not exist", pw->pw_name, shell); free(shell); return 0; } if (S_ISREG(st.st_mode) == 0 || (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { logit("User %.100s not allowed because shell %.100s " "is not executable", pw->pw_name, shell); free(shell); return 0; } free(shell); } /* * XXX Consider nuking {Allow,Deny}{Users,Groups}. We have the * XXX login_cap(3) mechanism which covers all other types of * XXX logins, too. */ if (options.num_deny_users > 0 || options.num_allow_users > 0 || options.num_deny_groups > 0 || options.num_allow_groups > 0) { hostname = auth_get_canonical_hostname(ssh, options.use_dns); ipaddr = ssh_remote_ipaddr(ssh); } /* Return false if user is listed in DenyUsers */ if (options.num_deny_users > 0) { for (i = 0; i < options.num_deny_users; i++) { r = match_user(pw->pw_name, hostname, ipaddr, options.deny_users[i]); if (r < 0) { fatal("Invalid DenyUsers pattern \"%.100s\"", options.deny_users[i]); } else if (r != 0) { logit("User %.100s from %.100s not allowed " "because listed in DenyUsers", pw->pw_name, hostname); return 0; } } } /* Return false if AllowUsers isn't empty and user isn't listed there */ if (options.num_allow_users > 0) { for (i = 0; i < options.num_allow_users; i++) { r = match_user(pw->pw_name, hostname, ipaddr, options.allow_users[i]); if (r < 0) { fatal("Invalid AllowUsers pattern \"%.100s\"", options.allow_users[i]); } else if (r == 1) break; } /* i < options.num_allow_users iff we break for loop */ if (i >= options.num_allow_users) { logit("User %.100s from %.100s not allowed because " "not listed in AllowUsers", pw->pw_name, hostname); return 0; } } if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { /* Get the user's group access list (primary and supplementary) */ if (ga_init(pw->pw_name, pw->pw_gid) == 0) { logit("User %.100s from %.100s not allowed because " "not in any group", pw->pw_name, hostname); return 0; } /* Return false if one of user's groups is listed in DenyGroups */ if (options.num_deny_groups > 0) if (ga_match(options.deny_groups, options.num_deny_groups)) { ga_free(); logit("User %.100s from %.100s not allowed " "because a group is listed in DenyGroups", pw->pw_name, hostname); return 0; } /* * Return false if AllowGroups isn't empty and one of user's groups * isn't listed there */ if (options.num_allow_groups > 0) if (!ga_match(options.allow_groups, options.num_allow_groups)) { ga_free(); logit("User %.100s from %.100s not allowed " "because none of user's groups are listed " "in AllowGroups", pw->pw_name, hostname); return 0; } ga_free(); } /* We found no reason not to let this user try to log on... */ return 1; }
static char * remote_hostname(struct ssh *ssh) { struct sockaddr_storage from; socklen_t fromlen; struct addrinfo hints, *ai, *aitop; char name[NI_MAXHOST], ntop2[NI_MAXHOST]; const char *ntop = ssh_remote_ipaddr(ssh); /* Get IP address of client. */ fromlen = sizeof(from); memset(&from, 0, sizeof(from)); if (getpeername(ssh_packet_get_connection_in(ssh), (struct sockaddr *)&from, &fromlen) < 0) { debug("getpeername failed: %.100s", strerror(errno)); return strdup(ntop); } debug3("Trying to reverse map address %.100s.", ntop); /* Map the IP address to a host name. */ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), NULL, 0, NI_NAMEREQD) != 0) { /* Host name not found. Use ip address. */ return strdup(ntop); } /* * if reverse lookup result looks like a numeric hostname, * someone is trying to trick us by PTR record like following: * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 */ memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; /*dummy*/ hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(name, NULL, &hints, &ai) == 0) { logit("Nasty PTR record \"%s\" is set up for %s, ignoring", name, ntop); freeaddrinfo(ai); return strdup(ntop); } /* Names are stored in lowercase. */ lowercase(name); /* * Map it back to an IP address and check that the given * address actually is an address of this host. This is * necessary because anyone with access to a name server can * define arbitrary names for an IP address. Mapping from * name to IP address can be trusted better (but can still be * fooled if the intruder has access to the name server of * the domain). */ memset(&hints, 0, sizeof(hints)); hints.ai_family = from.ss_family; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { logit("reverse mapping checking getaddrinfo for %.700s " "[%s] failed.", name, ntop); return strdup(ntop); } /* Look for the address from the list of addresses. */ for (ai = aitop; ai; ai = ai->ai_next) { if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && (strcmp(ntop, ntop2) == 0)) break; } freeaddrinfo(aitop); /* If we reached the end of the list, the address was not there. */ if (ai == NULL) { /* Address not found for the host name. */ logit("Address %.100s maps to %.600s, but this does not " "map back to the address.", ntop, name); return strdup(ntop); } return strdup(name); }
/*ARGSUSED*/ static int input_userauth_request(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; Authmethod *m = NULL; char *user, *service, *method, *style = NULL; int authenticated = 0; struct ssh *ssh = active_state; /* XXX */ if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); user = packet_get_cstring(NULL); service = packet_get_cstring(NULL); method = packet_get_cstring(NULL); debug("userauth-request for user %s service %s method %s", user, service, method); if (!log_flag) { logit("SSH: Server;Ltype: Authname;Remote: %s-%d;Name: %s", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), user); log_flag = 1; } debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); if ((style = strchr(user, ':')) != NULL) *style++ = 0; if (authctxt->attempt++ == 0) { /* setup auth context */ authctxt->pw = PRIVSEP(getpwnamallow(user)); authctxt->user = xstrdup(user); if (authctxt->pw && strcmp(service, "ssh-connection")==0) { authctxt->valid = 1; debug2("input_userauth_request: setting up authctxt for %s", user); } else { logit("input_userauth_request: invalid user %s", user); authctxt->pw = fakepw(); pfilter_notify(1); } #ifdef USE_PAM if (options.use_pam) PRIVSEP(start_pam(authctxt)); #endif setproctitle("%s%s", authctxt->valid ? user : "******", use_privsep ? " [net]" : ""); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; if (use_privsep) mm_inform_authserv(service, style); userauth_banner(); if (auth2_setup_methods_lists(authctxt) != 0) packet_disconnect("no authentication methods enabled"); } else if (strcmp(user, authctxt->user) != 0 || strcmp(service, authctxt->service) != 0) { packet_disconnect("Change of username or service not allowed: " "(%s,%s) -> (%s,%s)", authctxt->user, authctxt->service, user, service); } /* reset state */ auth2_challenge_stop(authctxt); #ifdef GSSAPI /* XXX move to auth2_gssapi_stop() */ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); #endif authctxt->postponed = 0; authctxt->server_caused_failure = 0; /* try to authenticate user */ m = authmethod_lookup(authctxt, method); if (m != NULL && authctxt->failures < options.max_authtries) { debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(authctxt); } userauth_finish(authctxt, authenticated, method, NULL); free(service); free(user); free(method); return 0; }
/* * return 1 if access is granted, 0 if not. * side effect: sets key option flags */ int auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) { struct ssh *ssh = active_state; /* XXX */ const char *cp; int i, r; /* reset options */ auth_clear_options(); if (!opts) return 1; while (*opts && *opts != ' ' && *opts != '\t') { if ((r = match_flag("cert-authority", 0, &opts, NULL)) != -1) { key_is_cert_authority = r; goto next_option; } if ((r = match_flag("restrict", 0, &opts, NULL)) != -1) { auth_debug_add("Key is restricted."); no_port_forwarding_flag = 1; no_agent_forwarding_flag = 1; no_x11_forwarding_flag = 1; no_pty_flag = 1; no_user_rc = 1; goto next_option; } if ((r = match_flag("port-forwarding", 1, &opts, "Port forwarding")) != -1) { no_port_forwarding_flag = r != 1; goto next_option; } if ((r = match_flag("agent-forwarding", 1, &opts, "Agent forwarding")) != -1) { no_agent_forwarding_flag = r != 1; goto next_option; } if ((r = match_flag("x11-forwarding", 1, &opts, "X11 forwarding")) != -1) { no_x11_forwarding_flag = r != 1; goto next_option; } if ((r = match_flag("pty", 1, &opts, "PTY allocation")) != -1) { no_pty_flag = r != 1; goto next_option; } if ((r = match_flag("user-rc", 1, &opts, "User rc execution")) != -1) { no_user_rc = r != 1; goto next_option; } cp = "command=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { opts += strlen(cp); free(forced_command); forced_command = xmalloc(strlen(opts) + 1); i = 0; while (*opts) { if (*opts == '"') break; if (*opts == '\\' && opts[1] == '"') { opts += 2; forced_command[i++] = '"'; continue; } forced_command[i++] = *opts++; } if (!*opts) { debug("%.100s, line %lu: missing end quote", file, linenum); auth_debug_add("%.100s, line %lu: missing end quote", file, linenum); free(forced_command); forced_command = NULL; goto bad_option; } forced_command[i] = '\0'; auth_debug_add("Forced command."); opts++; goto next_option; } cp = "principals=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { opts += strlen(cp); free(authorized_principals); authorized_principals = xmalloc(strlen(opts) + 1); i = 0; while (*opts) { if (*opts == '"') break; if (*opts == '\\' && opts[1] == '"') { opts += 2; authorized_principals[i++] = '"'; continue; } authorized_principals[i++] = *opts++; } if (!*opts) { debug("%.100s, line %lu: missing end quote", file, linenum); auth_debug_add("%.100s, line %lu: missing end quote", file, linenum); free(authorized_principals); authorized_principals = NULL; goto bad_option; } authorized_principals[i] = '\0'; auth_debug_add("principals: %.900s", authorized_principals); opts++; goto next_option; } cp = "environment=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { char *s; struct envstring *new_envstring; opts += strlen(cp); s = xmalloc(strlen(opts) + 1); i = 0; while (*opts) { if (*opts == '"') break; if (*opts == '\\' && opts[1] == '"') { opts += 2; s[i++] = '"'; continue; } s[i++] = *opts++; } if (!*opts) { debug("%.100s, line %lu: missing end quote", file, linenum); auth_debug_add("%.100s, line %lu: missing end quote", file, linenum); free(s); goto bad_option; } s[i] = '\0'; opts++; if (options.permit_user_env) { auth_debug_add("Adding to environment: " "%.900s", s); debug("Adding to environment: %.900s", s); new_envstring = xcalloc(1, sizeof(*new_envstring)); new_envstring->s = s; new_envstring->next = custom_environment; custom_environment = new_envstring; s = NULL; } free(s); goto next_option; } cp = "from=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { const char *remote_ip = ssh_remote_ipaddr(ssh); const char *remote_host = auth_get_canonical_hostname( ssh, options.use_dns); char *patterns = xmalloc(strlen(opts) + 1); opts += strlen(cp); i = 0; while (*opts) { if (*opts == '"') break; if (*opts == '\\' && opts[1] == '"') { opts += 2; patterns[i++] = '"'; continue; } patterns[i++] = *opts++; } if (!*opts) { debug("%.100s, line %lu: missing end quote", file, linenum); auth_debug_add("%.100s, line %lu: missing end quote", file, linenum); free(patterns); goto bad_option; } patterns[i] = '\0'; opts++; switch (match_host_and_ip(remote_host, remote_ip, patterns)) { case 1: free(patterns); /* Host name matches. */ goto next_option; case -1: debug("%.100s, line %lu: invalid criteria", file, linenum); auth_debug_add("%.100s, line %lu: " "invalid criteria", file, linenum); /* FALLTHROUGH */ case 0: free(patterns); logit("Authentication tried for %.100s with " "correct key but not from a permitted " "host (host=%.200s, ip=%.200s).", pw->pw_name, remote_host, remote_ip); auth_debug_add("Your host '%.200s' is not " "permitted to use this key for login.", remote_host); break; } /* deny access */ return 0; } cp = "permitopen=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { char *host, *p; int port; char *patterns = xmalloc(strlen(opts) + 1); opts += strlen(cp); i = 0; while (*opts) { if (*opts == '"') break; if (*opts == '\\' && opts[1] == '"') { opts += 2; patterns[i++] = '"'; continue; } patterns[i++] = *opts++; } if (!*opts) { debug("%.100s, line %lu: missing end quote", file, linenum); auth_debug_add("%.100s, line %lu: missing " "end quote", file, linenum); free(patterns); goto bad_option; } patterns[i] = '\0'; opts++; p = patterns; /* XXX - add streamlocal support */ host = hpdelim(&p); if (host == NULL || strlen(host) >= NI_MAXHOST) { debug("%.100s, line %lu: Bad permitopen " "specification <%.100s>", file, linenum, patterns); auth_debug_add("%.100s, line %lu: " "Bad permitopen specification", file, linenum); free(patterns); goto bad_option; } host = cleanhostname(host); if (p == NULL || (port = permitopen_port(p)) < 0) { debug("%.100s, line %lu: Bad permitopen port " "<%.100s>", file, linenum, p ? p : ""); auth_debug_add("%.100s, line %lu: " "Bad permitopen port", file, linenum); free(patterns); goto bad_option; } if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) channel_add_permitted_opens(host, port); free(patterns); goto next_option; } cp = "tunnel=\""; if (strncasecmp(opts, cp, strlen(cp)) == 0) { char *tun = NULL; opts += strlen(cp); tun = xmalloc(strlen(opts) + 1); i = 0; while (*opts) { if (*opts == '"') break; tun[i++] = *opts++; } if (!*opts) { debug("%.100s, line %lu: missing end quote", file, linenum); auth_debug_add("%.100s, line %lu: missing end quote", file, linenum); free(tun); forced_tun_device = -1; goto bad_option; } tun[i] = '\0'; forced_tun_device = a2tun(tun, NULL); free(tun); if (forced_tun_device == SSH_TUNID_ERR) { debug("%.100s, line %lu: invalid tun device", file, linenum); auth_debug_add("%.100s, line %lu: invalid tun device", file, linenum); forced_tun_device = -1; goto bad_option; } auth_debug_add("Forced tun device: %d", forced_tun_device); opts++; goto next_option; } next_option: /* * Skip the comma, and move to the next option * (or break out if there are no more). */ if (!*opts) fatal("Bugs in auth-options.c option processing."); if (*opts == ' ' || *opts == '\t') break; /* End of options. */ if (*opts != ',') goto bad_option; opts++; /* Process the next option. */ } /* grant access */ return 1; bad_option: logit("Bad options in %.100s file, line %lu: %.50s", file, linenum, opts); auth_debug_add("Bad options in %.100s file, line %lu: %.50s", file, linenum, opts); /* deny access */ return 0; }
/* * Check if the user is allowed to log in via ssh. If user is listed * in DenyUsers or one of user's groups is listed in DenyGroups, false * will be returned. If AllowUsers isn't empty and user isn't listed * there, or if AllowGroups isn't empty and one of user's groups isn't * listed there, false will be returned. * If the user's shell is not executable, false will be returned. * Otherwise true is returned. */ int allowed_user(struct passwd * pw) { struct ssh *ssh = active_state; /* XXX */ struct stat st; const char *hostname = NULL, *ipaddr = NULL, *passwd = NULL; u_int i; int r; #ifdef USE_SHADOW struct spwd *spw = NULL; #endif /* Shouldn't be called if pw is NULL, but better safe than sorry... */ if (!pw || !pw->pw_name) return 0; #ifdef USE_SHADOW if (!options.use_pam) spw = getspnam(pw->pw_name); #ifdef HAS_SHADOW_EXPIRE if (!options.use_pam && spw != NULL && auth_shadow_acctexpired(spw)) return 0; #endif /* HAS_SHADOW_EXPIRE */ #endif /* USE_SHADOW */ /* grab passwd field for locked account check */ passwd = pw->pw_passwd; #ifdef USE_SHADOW if (spw != NULL) #ifdef USE_LIBIAF passwd = get_iaf_password(pw); #else passwd = spw->sp_pwdp; #endif /* USE_LIBIAF */ #endif /* check for locked account */ if (!options.use_pam && passwd && *passwd) { int locked = 0; #ifdef LOCKED_PASSWD_STRING if (strcmp(passwd, LOCKED_PASSWD_STRING) == 0) locked = 1; #endif #ifdef LOCKED_PASSWD_PREFIX if (strncmp(passwd, LOCKED_PASSWD_PREFIX, strlen(LOCKED_PASSWD_PREFIX)) == 0) locked = 1; #endif #ifdef LOCKED_PASSWD_SUBSTR if (strstr(passwd, LOCKED_PASSWD_SUBSTR)) locked = 1; #endif #ifdef USE_LIBIAF free((void *) passwd); #endif /* USE_LIBIAF */ if (locked) { logit("User %.100s not allowed because account is locked", pw->pw_name); return 0; } } /* * Deny if shell does not exist or is not executable unless we * are chrooting. */ if (options.chroot_directory == NULL || strcasecmp(options.chroot_directory, "none") == 0) { char *shell = xstrdup((pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell); /* empty = /bin/sh */ if (stat(shell, &st) != 0) { logit("User %.100s not allowed because shell %.100s " "does not exist", pw->pw_name, shell); free(shell); return 0; } if (S_ISREG(st.st_mode) == 0 || (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { logit("User %.100s not allowed because shell %.100s " "is not executable", pw->pw_name, shell); free(shell); return 0; } free(shell); } if (options.num_deny_users > 0 || options.num_allow_users > 0 || options.num_deny_groups > 0 || options.num_allow_groups > 0) { hostname = auth_get_canonical_hostname(ssh, options.use_dns); ipaddr = ssh_remote_ipaddr(ssh); } /* Return false if user is listed in DenyUsers */ if (options.num_deny_users > 0) { for (i = 0; i < options.num_deny_users; i++) { r = match_user(pw->pw_name, hostname, ipaddr, options.deny_users[i]); if (r < 0) { fatal("Invalid DenyUsers pattern \"%.100s\"", options.deny_users[i]); } else if (r != 0) { logit("User %.100s from %.100s not allowed " "because listed in DenyUsers", pw->pw_name, hostname); return 0; } } } /* Return false if AllowUsers isn't empty and user isn't listed there */ if (options.num_allow_users > 0) { for (i = 0; i < options.num_allow_users; i++) { r = match_user(pw->pw_name, hostname, ipaddr, options.allow_users[i]); if (r < 0) { fatal("Invalid AllowUsers pattern \"%.100s\"", options.allow_users[i]); } else if (r == 1) break; } /* i < options.num_allow_users iff we break for loop */ if (i >= options.num_allow_users) { logit("User %.100s from %.100s not allowed because " "not listed in AllowUsers", pw->pw_name, hostname); return 0; } } if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { /* Get the user's group access list (primary and supplementary) */ if (ga_init(pw->pw_name, pw->pw_gid) == 0) { logit("User %.100s from %.100s not allowed because " "not in any group", pw->pw_name, hostname); return 0; } /* Return false if one of user's groups is listed in DenyGroups */ if (options.num_deny_groups > 0) if (ga_match(options.deny_groups, options.num_deny_groups)) { ga_free(); logit("User %.100s from %.100s not allowed " "because a group is listed in DenyGroups", pw->pw_name, hostname); return 0; } /* * Return false if AllowGroups isn't empty and one of user's groups * isn't listed there */ if (options.num_allow_groups > 0) if (!ga_match(options.allow_groups, options.num_allow_groups)) { ga_free(); logit("User %.100s from %.100s not allowed " "because none of user's groups are listed " "in AllowGroups", pw->pw_name, hostname); return 0; } ga_free(); } #ifdef CUSTOM_SYS_AUTH_ALLOWED_USER if (!sys_auth_allowed_user(pw, &loginmsg)) return 0; #endif /* We found no reason not to let this user try to log on... */ return 1; }
struct passwd * getpwnamallow(const char *user) { struct ssh *ssh = active_state; /* XXX */ #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; #ifdef BSD_AUTH auth_session_t *as; #endif #endif struct passwd *pw; struct connection_info *ci = get_connection_info(1, options.use_dns); ci->user = user; parse_server_match_config(&options, ci); log_change_level(options.log_level); process_permitopen(ssh, &options); #if defined(_AIX) && defined(HAVE_SETAUTHDB) aix_setauthdb(user); #endif pw = getpwnam(user); #if defined(_AIX) && defined(HAVE_SETAUTHDB) aix_restoreauthdb(); #endif #ifdef HAVE_CYGWIN /* * Windows usernames are case-insensitive. To avoid later problems * when trying to match the username, the user is only allowed to * login if the username is given in the same case as stored in the * user database. */ if (pw != NULL && strcmp(user, pw->pw_name) != 0) { logit("Login name %.100s does not match stored username %.100s", user, pw->pw_name); pw = NULL; } #endif if (pw == NULL) { logit("Invalid user %.100s from %.100s port %d", user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); #ifdef CUSTOM_FAILED_LOGIN record_failed_login(user, auth_get_canonical_hostname(ssh, options.use_dns), "ssh"); #endif #ifdef SSH_AUDIT_EVENTS audit_event(SSH_INVALID_USER); #endif /* SSH_AUDIT_EVENTS */ return (NULL); } if (!allowed_user(pw)) return (NULL); #ifdef HAVE_LOGIN_CAP if ((lc = login_getclass(pw->pw_class)) == NULL) { debug("unable to get login class: %s", user); return (NULL); } #ifdef BSD_AUTH if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { debug("Approval failure for %s", user); pw = NULL; } if (as != NULL) auth_close(as); #endif #endif if (pw != NULL) return (pwcopy(pw)); return (NULL); }
void auth_log(Authctxt *authctxt, int authenticated, int partial, const char *method, const char *submethod) { struct ssh *ssh = active_state; /* XXX */ int level = SYSLOG_LEVEL_VERBOSE; const char *authmsg; char *extra = NULL; if (use_privsep && !mm_is_monitor() && !authctxt->postponed) return; /* Raise logging level */ if (authenticated == 1 || !authctxt->valid || authctxt->failures >= options.max_authtries / 2 || strcmp(method, "password") == 0) level = SYSLOG_LEVEL_INFO; if (authctxt->postponed) authmsg = "Postponed"; else if (partial) authmsg = "Partial"; else authmsg = authenticated ? "Accepted" : "Failed"; if ((extra = format_method_key(authctxt)) == NULL) { if (authctxt->auth_method_info != NULL) extra = xstrdup(authctxt->auth_method_info); } do_log2(level, "%s %s%s%s for %s%.100s from %.200s port %d ssh2%s%s", authmsg, method, submethod != NULL ? "/" : "", submethod == NULL ? "" : submethod, authctxt->valid ? "" : "invalid user ", authctxt->user, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), extra != NULL ? ": " : "", extra != NULL ? extra : ""); free(extra); #ifdef CUSTOM_FAILED_LOGIN if (authenticated == 0 && !authctxt->postponed && (strcmp(method, "password") == 0 || strncmp(method, "keyboard-interactive", 20) == 0 || strcmp(method, "challenge-response") == 0)) record_failed_login(authctxt->user, auth_get_canonical_hostname(ssh, options.use_dns), "ssh"); # ifdef WITH_AIXAUTHENTICATE if (authenticated) sys_auth_record_login(authctxt->user, auth_get_canonical_hostname(ssh, options.use_dns), "ssh", &loginmsg); # endif #endif #ifdef SSH_AUDIT_EVENTS if (authenticated == 0 && !authctxt->postponed) audit_event(audit_classify_auth(method)); #endif }
/* return 1 if given hostkey is allowed */ int hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, struct sshkey *key) { struct ssh *ssh = active_state; /* XXX */ const char *resolvedname, *ipaddr, *lookup, *reason; HostStatus host_status; int len; char *fp; if (auth_key_is_revoked(key)) return 0; resolvedname = auth_get_canonical_hostname(ssh, options.use_dns); ipaddr = ssh_remote_ipaddr(ssh); debug2("%s: chost %s resolvedname %s ipaddr %s", __func__, chost, resolvedname, ipaddr); if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') { debug2("stripping trailing dot from chost %s", chost); chost[len - 1] = '\0'; } if (options.hostbased_uses_name_from_packet_only) { if (auth_rhosts2(pw, cuser, chost, chost) == 0) { debug2("%s: auth_rhosts2 refused " "user \"%.100s\" host \"%.100s\" (from packet)", __func__, cuser, chost); return 0; } lookup = chost; } else { if (strcasecmp(resolvedname, chost) != 0) logit("userauth_hostbased mismatch: " "client sends %s, but we resolve %s to %s", chost, ipaddr, resolvedname); if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) { debug2("%s: auth_rhosts2 refused " "user \"%.100s\" host \"%.100s\" addr \"%.100s\"", __func__, cuser, resolvedname, ipaddr); return 0; } lookup = resolvedname; } debug2("%s: access allowed by auth_rhosts2", __func__); if (sshkey_is_cert(key) && sshkey_cert_check_authority(key, 1, 0, lookup, &reason)) { error("%s", reason); auth_debug_add("%s", reason); return 0; } host_status = check_key_in_hostfiles(pw, key, lookup, _PATH_SSH_SYSTEM_HOSTFILE, options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE); /* backward compat if no key has been found. */ if (host_status == HOST_NEW) { host_status = check_key_in_hostfiles(pw, key, lookup, _PATH_SSH_SYSTEM_HOSTFILE2, options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE2); } if (host_status == HOST_OK) { if (sshkey_is_cert(key)) { if ((fp = sshkey_fingerprint(key->cert->signature_key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint fail", __func__); verbose("Accepted certificate ID \"%s\" signed by " "%s CA %s from %s@%s", key->cert->key_id, sshkey_type(key->cert->signature_key), fp, cuser, lookup); } else { if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint fail", __func__); verbose("Accepted %s public key %s from %s@%s", sshkey_type(key), fp, cuser, lookup); } free(fp); } return (host_status == HOST_OK); }
static int parse_option_list(struct sshbuf *oblob, struct passwd *pw, u_int which, int crit, int *cert_no_port_forwarding_flag, int *cert_no_agent_forwarding_flag, int *cert_no_x11_forwarding_flag, int *cert_no_pty_flag, int *cert_no_user_rc, char **cert_forced_command, int *cert_source_address_done) { struct ssh *ssh = active_state; /* XXX */ char *command, *allowed; const char *remote_ip; char *name = NULL; struct sshbuf *c = NULL, *data = NULL; int r, ret = -1, result, found; if ((c = sshbuf_fromb(oblob)) == NULL) { error("%s: sshbuf_fromb failed", __func__); goto out; } while (sshbuf_len(c) > 0) { sshbuf_free(data); data = NULL; if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 || (r = sshbuf_froms(c, &data)) != 0) { error("Unable to parse certificate options: %s", ssh_err(r)); goto out; } debug3("found certificate option \"%.100s\" len %zu", name, sshbuf_len(data)); found = 0; if ((which & OPTIONS_EXTENSIONS) != 0) { if (strcmp(name, "permit-X11-forwarding") == 0) { *cert_no_x11_forwarding_flag = 0; found = 1; } else if (strcmp(name, "permit-agent-forwarding") == 0) { *cert_no_agent_forwarding_flag = 0; found = 1; } else if (strcmp(name, "permit-port-forwarding") == 0) { *cert_no_port_forwarding_flag = 0; found = 1; } else if (strcmp(name, "permit-pty") == 0) { *cert_no_pty_flag = 0; found = 1; } else if (strcmp(name, "permit-user-rc") == 0) { *cert_no_user_rc = 0; found = 1; } } if (!found && (which & OPTIONS_CRITICAL) != 0) { if (strcmp(name, "force-command") == 0) { if ((r = sshbuf_get_cstring(data, &command, NULL)) != 0) { error("Unable to parse \"%s\" " "section: %s", name, ssh_err(r)); goto out; } if (*cert_forced_command != NULL) { error("Certificate has multiple " "force-command options"); free(command); goto out; } *cert_forced_command = command; found = 1; } if (strcmp(name, "source-address") == 0) { if ((r = sshbuf_get_cstring(data, &allowed, NULL)) != 0) { error("Unable to parse \"%s\" " "section: %s", name, ssh_err(r)); goto out; } if ((*cert_source_address_done)++) { error("Certificate has multiple " "source-address options"); free(allowed); goto out; } remote_ip = ssh_remote_ipaddr(ssh); result = addr_match_cidr_list(remote_ip, allowed); free(allowed); switch (result) { case 1: /* accepted */ break; case 0: /* no match */ logit("Authentication tried for %.100s " "with valid certificate but not " "from a permitted host " "(ip=%.200s).", pw->pw_name, remote_ip); auth_debug_add("Your address '%.200s' " "is not permitted to use this " "certificate for login.", remote_ip); goto out; case -1: default: error("Certificate source-address " "contents invalid"); goto out; } found = 1; } } if (!found) { if (crit) { error("Certificate critical option \"%s\" " "is not supported", name); goto out; } else { logit("Certificate extension \"%s\" " "is not supported", name); } } else if (sshbuf_len(data) != 0) { error("Certificate option \"%s\" corrupt " "(extra data)", name); goto out; } free(name); name = NULL; } /* successfully parsed all options */ ret = 0; out: if (ret != 0 && cert_forced_command != NULL && *cert_forced_command != NULL) { free(*cert_forced_command); *cert_forced_command = NULL; } free(name); sshbuf_free(data); sshbuf_free(c); return ret; }
int auth_authorise_keyopts(struct ssh *ssh, struct passwd *pw, struct sshauthopt *opts, int allow_cert_authority, const char *loc) { const char *remote_ip = ssh_remote_ipaddr(ssh); const char *remote_host = auth_get_canonical_hostname(ssh, options.use_dns); time_t now = time(NULL); char buf[64]; /* * Check keys/principals file expiry time. * NB. validity interval in certificate is handled elsewhere. */ if (opts->valid_before && now > 0 && opts->valid_before < (uint64_t)now) { format_absolute_time(opts->valid_before, buf, sizeof(buf)); debug("%s: entry expired at %s", loc, buf); auth_debug_add("%s: entry expired at %s", loc, buf); return -1; } /* Consistency checks */ if (opts->cert_principals != NULL && !opts->cert_authority) { debug("%s: principals on non-CA key", loc); auth_debug_add("%s: principals on non-CA key", loc); /* deny access */ return -1; } /* cert-authority flag isn't valid in authorized_principals files */ if (!allow_cert_authority && opts->cert_authority) { debug("%s: cert-authority flag invalid here", loc); auth_debug_add("%s: cert-authority flag invalid here", loc); /* deny access */ return -1; } /* Perform from= checks */ if (opts->required_from_host_keys != NULL) { switch (match_host_and_ip(remote_host, remote_ip, opts->required_from_host_keys )) { case 1: /* Host name matches. */ break; case -1: default: debug("%s: invalid from criteria", loc); auth_debug_add("%s: invalid from criteria", loc); /* FALLTHROUGH */ case 0: logit("%s: Authentication tried for %.100s with " "correct key but not from a permitted " "host (host=%.200s, ip=%.200s, required=%.200s).", loc, pw->pw_name, remote_host, remote_ip, opts->required_from_host_keys); auth_debug_add("%s: Your host '%.200s' is not " "permitted to use this key for login.", loc, remote_host); /* deny access */ return -1; } } /* Check source-address restriction from certificate */ if (opts->required_from_host_cert != NULL) { switch (addr_match_cidr_list(remote_ip, opts->required_from_host_cert)) { case 1: /* accepted */ break; case -1: default: /* invalid */ error("%s: Certificate source-address invalid", loc); /* FALLTHROUGH */ case 0: logit("%s: Authentication tried for %.100s with valid " "certificate but not from a permitted source " "address (%.200s).", loc, pw->pw_name, remote_ip); auth_debug_add("%s: Your address '%.200s' is not " "permitted to use this certificate for login.", loc, remote_ip); return -1; } } /* * * XXX this is spammy. We should report remotely only for keys * that are successful in actual auth attempts, and not PK_OK * tests. */ auth_log_authopts(loc, opts, 1); return 0; }