static void mm_session_close(Session *s) { debug3("%s: session %d pid %d", __func__, s->self, s->pid); if (s->ttyfd != -1) { debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd); fatal_remove_cleanup(session_pty_cleanup2, (void *)s); session_pty_cleanup2(s); } s->used = 0; }
static void pam_free_ctx(void *ctxtp) { struct pam_ctxt *ctxt = ctxtp; int status; fatal_remove_cleanup(pam_cleanup, ctxt); close(ctxt->pam_sock); kill(ctxt->pam_pid, SIGHUP); waitpid(ctxt->pam_pid, &status, 0); xfree(ctxt->pam_user); xfree(ctxt); }
static int krb4_init(void *context) { static int cleanup_registered = 0; Authctxt *authctxt = (Authctxt *)context; const char *tkt_root = TKT_ROOT; struct stat st; int fd; if (!authctxt->krb4_ticket_file) { /* Set unique ticket string manually since we're still root. */ authctxt->krb4_ticket_file = xmalloc(MAXPATHLEN); #ifdef AFS if (lstat("/ticket", &st) != -1) tkt_root = "/ticket/"; #endif /* AFS */ snprintf(authctxt->krb4_ticket_file, MAXPATHLEN, "%s%u_%ld", tkt_root, authctxt->pw->pw_uid, (long)getpid()); krb_set_tkt_string(authctxt->krb4_ticket_file); } /* Register ticket cleanup in case of fatal error. */ if (!cleanup_registered) { fatal_add_cleanup(krb4_cleanup_proc, authctxt); cleanup_registered = 1; } /* Try to create our ticket file. */ if ((fd = mkstemp(authctxt->krb4_ticket_file)) != -1) { close(fd); return (1); } /* Ticket file exists - make sure user owns it (just passed ticket). */ if (lstat(authctxt->krb4_ticket_file, &st) != -1) { if (st.st_mode == (S_IFREG | S_IRUSR | S_IWUSR) && st.st_uid == authctxt->pw->pw_uid) return (1); } /* Failure - cancel cleanup function, leaving ticket for inspection. */ log("WARNING: bad ticket file %s", authctxt->krb4_ticket_file); fatal_remove_cleanup(krb4_cleanup_proc, authctxt); cleanup_registered = 0; xfree(authctxt->krb4_ticket_file); authctxt->krb4_ticket_file = NULL; return (0); }
/* * read packets, try to authenticate the user and * return only if authentication is successful */ static void do_authloop(Authctxt *authctxt) { int authenticated = 0; u_int bits; Key *client_host_key; BIGNUM *n; char *client_user, *password; char info[1024]; u_int dlen; u_int ulen; int type = 0; struct passwd *pw = authctxt->pw; debug("Attempting authentication for %s%.100s.", authctxt->valid ? "" : "illegal user ", authctxt->user); /* If the user has no password, accept authentication immediately. */ if (options.password_authentication && #if defined(KRB4) || defined(KRB5) (!options.kerberos_authentication || options.kerberos_or_local_passwd) && #endif PRIVSEP(auth_password(authctxt, ""))) { auth_log(authctxt, 1, "without authentication", ""); return; } /* Indicate that authentication is needed. */ packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); client_user = NULL; for ( ;; ) { /* default to fail */ authenticated = 0; info[0] = '\0'; /* Get a packet from the client. */ authctxt->v1_auth_type = type = packet_read(); authctxt->v1_auth_name = get_authname(type); authctxt->attempt++; /* Process the packet. */ switch (type) { #if defined(KRB4) || defined(KRB5) case SSH_CMSG_AUTH_KERBEROS: if (!options.kerberos_authentication) { verbose("Kerberos authentication disabled."); } else { char *kdata = packet_get_string(&dlen); packet_check_eom(); if (kdata[0] == 4) { /* KRB_PROT_VERSION */ #ifdef KRB4 KTEXT_ST tkt, reply; tkt.length = dlen; if (tkt.length < MAX_KTXT_LEN) memcpy(tkt.dat, kdata, tkt.length); if (PRIVSEP(auth_krb4(authctxt, &tkt, &client_user, &reply))) { authenticated = 1; snprintf(info, sizeof(info), " tktuser %.100s", client_user); packet_start( SSH_SMSG_AUTH_KERBEROS_RESPONSE); packet_put_string((char *) reply.dat, reply.length); packet_send(); packet_write_wait(); } #endif /* KRB4 */ } else { #ifdef KRB5 krb5_data tkt, reply; tkt.length = dlen; tkt.data = kdata; if (PRIVSEP(auth_krb5(authctxt, &tkt, &client_user, &reply))) { authenticated = 1; snprintf(info, sizeof(info), " tktuser %.100s", client_user); /* Send response to client */ packet_start( SSH_SMSG_AUTH_KERBEROS_RESPONSE); packet_put_string((char *) reply.data, reply.length); packet_send(); packet_write_wait(); if (reply.length) xfree(reply.data); } #endif /* KRB5 */ } xfree(kdata); } break; #endif /* KRB4 || KRB5 */ #if defined(AFS) || defined(KRB5) /* XXX - punt on backward compatibility here. */ case SSH_CMSG_HAVE_KERBEROS_TGT: packet_send_debug("Kerberos TGT passing disabled before authentication."); break; #ifdef AFS case SSH_CMSG_HAVE_AFS_TOKEN: packet_send_debug("AFS token passing disabled before authentication."); break; #endif /* AFS */ #endif /* AFS || KRB5 */ case SSH_CMSG_AUTH_RHOSTS: if (!options.rhosts_authentication) { verbose("Rhosts authentication disabled."); break; } /* * Get client user name. Note that we just have to * trust the client; this is one reason why rhosts * authentication is insecure. (Another is * IP-spoofing on a local network.) */ client_user = packet_get_string(&ulen); packet_check_eom(); /* Try to authenticate using /etc/hosts.equiv and .rhosts. */ authenticated = auth_rhosts(pw, client_user); snprintf(info, sizeof info, " ruser %.100s", client_user); break; case SSH_CMSG_AUTH_RHOSTS_RSA: if (!options.rhosts_rsa_authentication) { verbose("Rhosts with RSA authentication disabled."); break; } /* * Get client user name. Note that we just have to * trust the client; root on the client machine can * claim to be any user. */ client_user = packet_get_string(&ulen); /* Get the client host key. */ client_host_key = key_new(KEY_RSA1); bits = packet_get_int(); packet_get_bignum(client_host_key->rsa->e); packet_get_bignum(client_host_key->rsa->n); if (bits != BN_num_bits(client_host_key->rsa->n)) verbose("Warning: keysize mismatch for client_host_key: " "actual %d, announced %d", BN_num_bits(client_host_key->rsa->n), bits); packet_check_eom(); authenticated = auth_rhosts_rsa(pw, client_user, client_host_key); key_free(client_host_key); snprintf(info, sizeof info, " ruser %.100s", client_user); break; case SSH_CMSG_AUTH_RSA: if (!options.rsa_authentication) { verbose("RSA authentication disabled."); break; } /* RSA authentication requested. */ if ((n = BN_new()) == NULL) fatal("do_authloop: BN_new failed"); packet_get_bignum(n); packet_check_eom(); authenticated = auth_rsa(pw, n); BN_clear_free(n); break; case SSH_CMSG_AUTH_PASSWORD: authctxt->init_attempt++; if (!options.password_authentication) { verbose("Password authentication disabled."); break; } /* * Read user password. It is in plain text, but was * transmitted over the encrypted channel so it is * not visible to an outside observer. */ password = packet_get_string(&dlen); packet_check_eom(); /* Try authentication with the password. */ if (authctxt->init_failures < options.max_init_auth_tries) authenticated = PRIVSEP(auth_password(authctxt, password)); memset(password, 0, strlen(password)); xfree(password); break; case SSH_CMSG_AUTH_TIS: debug("rcvd SSH_CMSG_AUTH_TIS"); if (options.challenge_response_authentication == 1) { char *challenge = get_challenge(authctxt); if (challenge != NULL) { debug("sending challenge '%s'", challenge); packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); packet_put_cstring(challenge); xfree(challenge); packet_send(); packet_write_wait(); continue; } } break; case SSH_CMSG_AUTH_TIS_RESPONSE: debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); if (options.challenge_response_authentication == 1) { char *response = packet_get_string(&dlen); debug("got response '%s'", response); packet_check_eom(); authenticated = verify_response(authctxt, response); memset(response, 'r', dlen); xfree(response); } break; default: /* * Any unknown messages will be ignored (and failure * returned) during authentication. */ log("Unknown message during authentication: type %d", type); break; } #ifdef BSD_AUTH if (authctxt->as) { auth_close(authctxt->as); authctxt->as = NULL; } #endif if (!authctxt->valid && authenticated) { authenticated = 0; log("Ignoring authenticated invalid user %s", authctxt->user); } #ifdef _UNICOS if (type == SSH_CMSG_AUTH_PASSWORD && !authenticated) cray_login_failure(authctxt->user, IA_UDBERR); if (authenticated && cray_access_denied(authctxt->user)) { authenticated = 0; fatal("Access denied for user %s.",authctxt->user); } #endif /* _UNICOS */ #ifdef HAVE_CYGWIN if (authenticated && !check_nt_auth(type == SSH_CMSG_AUTH_PASSWORD, pw)) { packet_disconnect("Authentication rejected for uid %d.", pw == NULL ? -1 : pw->pw_uid); authenticated = 0; } #else /* Special handling for root */ if (!use_privsep && authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed(get_authname(type))) authenticated = 0; #endif #ifdef USE_PAM /* XXX PAM and PRIVSEP don't mix */ if (use_privsep && authenticated) fatal("Privsep is not supported"); if (authenticated && type != SSH_CMSG_AUTH_PASSWORD) authenticated = do_pam_non_initial_userauth(authctxt); else if (authenticated && !AUTHPAM_DONE(authctxt)) authenticated = 0; if (!authenticated) authctxt->pam_retval = AUTHPAM_ERROR(authctxt, PAM_PERM_DENIED); #endif /* USE_PAM */ /* Log before sending the reply */ auth_log(authctxt, authenticated, get_authname(type), info); if (client_user != NULL) { xfree(client_user); client_user = NULL; } if (authenticated) return; if (type == SSH_CMSG_AUTH_PASSWORD) authctxt->init_failures++; if (authctxt->failures++ > options.max_auth_tries) { #ifdef HAVE_BSM fatal_remove_cleanup(audit_failed_login_cleanup, authctxt); audit_sshd_login_failure(&ah, PAM_MAXTRIES); #endif /* HAVE_BSM */ packet_disconnect(AUTH_FAIL_MSG, authctxt->user); } packet_start(SSH_SMSG_FAILURE); packet_send(); packet_write_wait(); } }
void userauth_finish(Authctxt *authctxt, char *method) { int authenticated, partial; if (authctxt == NULL) fatal("%s: missing context", __func__); /* unknown method handling -- must elicit userauth failure msg */ if (authctxt->method == NULL) { authenticated = 0; partial = 0; goto done_checking; } #ifndef USE_PAM /* Special handling for root (done elsewhere for PAM) */ if (authctxt->method->authenticated && authctxt->pw != NULL && authctxt->pw->pw_uid == 0 && !auth_root_allowed(method)) authctxt->method->authenticated = 0; #endif /* USE_PAM */ #ifdef _UNICOS if (authctxt->method->authenticated && cray_access_denied(authctxt->user)) { authctxt->method->authenticated = 0; fatal("Access denied for user %s.",authctxt->user); } #endif /* _UNICOS */ partial = userauth_check_partial_failure(authctxt); authenticated = authctxt->method->authenticated; #ifdef USE_PAM /* * If the userauth method failed to complete PAM work then force * partial failure. */ if (authenticated && !AUTHPAM_DONE(authctxt)) partial = 1; #endif /* USE_PAM */ /* * To properly support invalid userauth method names we set * authenticated=0, partial=0 above and know that * authctxt->method == NULL. * * No unguarded reference to authctxt->method allowed from here. * Checking authenticated != 0 is a valid guard; authctxt->method * MUST NOT be NULL if authenticated. */ done_checking: if (!authctxt->valid && authenticated) { /* * We get here if the PreUserauthHook fails but the * user is otherwise valid. * An error in the PAM handling could also get us here * but we need not panic, just treat as a failure. */ authctxt->method->authenticated = 0; authenticated = 0; log("Ignoring authenticated invalid user %s", authctxt->user); auth_log(authctxt, 0, method, " ssh2"); } /* Log before sending the reply */ auth_log(authctxt, authenticated, method, " ssh2"); if (authenticated && !partial) { /* turn off userauth */ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); packet_start(SSH2_MSG_USERAUTH_SUCCESS); packet_send(); packet_write_wait(); /* now we can break out */ authctxt->success = 1; } else { char *methods; if (authctxt->method && authctxt->method->is_initial) authctxt->init_failures++; authctxt->method = NULL; #ifdef USE_PAM /* * Keep track of last PAM error (or PERM_DENIED) for BSM * login failure auditing, which may run after the PAM * state has been cleaned up. */ authctxt->pam_retval = AUTHPAM_ERROR(authctxt, PAM_PERM_DENIED); #endif /* USE_PAM */ if (authctxt->failures++ > options.max_auth_tries) { #ifdef HAVE_BSM fatal_remove_cleanup(audit_failed_login_cleanup, authctxt); audit_sshd_login_failure(&ah, PAM_MAXTRIES, authctxt->user); #endif /* HAVE_BSM */ packet_disconnect(AUTH_FAIL_MSG, authctxt->user); } #ifdef _UNICOS if (strcmp(method, "password") == 0) cray_login_failure(authctxt->user, IA_UDBERR); #endif /* _UNICOS */ packet_start(SSH2_MSG_USERAUTH_FAILURE); /* * If (partial) then authmethods_get() will return only * required methods, likely only "keyboard-interactive;" * (methods == NULL) implies failure, even if (partial == 1) */ methods = authmethods_get(); packet_put_cstring(methods); packet_put_char((authenticated && partial && methods) ? 1 : 0); if (methods) xfree(methods); packet_send(); packet_write_wait(); } }
/* Cleanly shutdown PAM */ void finish_pam(Authctxt *authctxt) { fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam); do_pam_cleanup_proc(authctxt->pam); }
void new_start_pam(Authctxt *authctxt, struct pam_conv *conv) { int retval; pam_handle_t *pamh; const char *rhost, *svc; char *user = NULL; pam_stuff *pam; if (authctxt == NULL) fatal("Internal error during userauth"); if (compat20 && authctxt->method == NULL) fatal("Userauth method unknown while starting PAM"); /* PAM service selected here */ svc = derive_pam_svc_name(authctxt->method); debug2("Starting PAM service %s for method %s", svc, get_method_name(authctxt)); if (authctxt->user != NULL) user = authctxt->user; /* Cleanup previous PAM state */ if (authctxt->pam != NULL) { fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam); do_pam_cleanup_proc(authctxt->pam); } pam = xmalloc(sizeof(pam_stuff)); (void) memset(pam, 0, sizeof(pam_stuff)); /* * pam->last_pam_retval has to be and is considered * along with pam->state. * * pam->state = 0; -> no PAM auth, account, etc, work * done yet. (Set by memset() above.) * * pam->last_pam_retval = PAM_SUCCESS; -> meaningless at * this point. * * See finish_userauth_do_pam() below. */ pam->authctxt = authctxt; pam->last_pam_retval = PAM_SUCCESS; authctxt->pam = pam; /* Free any previously stored text/error PAM prompts */ if (__pam_msg) { xfree(__pam_msg); __pam_msg = NULL; } if ((retval = pam_start(svc, user, conv, &pamh)) != PAM_SUCCESS) { fatal("PAM initialization failed during %s userauth", get_method_name(authctxt)); } fatal_add_cleanup((void (*)(void *)) &do_pam_cleanup_proc, (void *) authctxt->pam); rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping); if ((retval = pam_set_item(pamh, PAM_RHOST, rhost)) != PAM_SUCCESS) { (void) pam_end(pamh, retval); fatal("Could not set PAM_RHOST item during %s userauth", get_method_name(authctxt)); } if ((retval = pam_set_item(pamh, PAM_TTY, "sshd")) != PAM_SUCCESS) { (void) pam_end(pamh, retval); fatal("Could not set PAM_TTY item during %s userauth", get_method_name(authctxt)); } authctxt->pam->h = pamh; }