void userauth_user_svc_change(Authctxt *authctxt, char *user, char *service) { /* * NOTE: * * SSHv2 services should be abstracted and service changes during * userauth should be supported as per the userauth draft. In the PAM * case, support for multiple SSHv2 services means that we have to * format the PAM service name according to the SSHv2 service *and* the * SSHv2 userauth being attempted ("passwd", "kbdint" and "other"). * * We'll cross that bridge when we come to it. For now disallow service * changes during userauth if using PAM, but allow username changes. */ /* authctxt->service must == ssh-connection here */ if (service != NULL && strcmp(service, authctxt->service) != 0) { packet_disconnect("Change of service not " "allowed: %s and %s", authctxt->service, service); } if (user != NULL && authctxt->user != NULL && strcmp(user, authctxt->user) == 0) return; /* All good; update authctxt */ xfree(authctxt->user); authctxt->user = xstrdup(user); pwfree(&authctxt->pw); authctxt->pw = getpwnamallow(user); authctxt->valid = (authctxt->pw != NULL); /* Forget method state; abandon postponed userauths */ userauth_reset_methods(); }
/* * To be called from userauth methods, directly (as in keyboard-interactive) or * indirectly (from auth_pam_password() or from do_pam_non_initial_userauth(). * * The caller is responsible for calling new_start_pam() first. * * PAM state is not cleaned up here on error. This is left to subsequent calls * to new_start_pam() or to the cleanup function upon authentication error. */ int finish_userauth_do_pam(Authctxt *authctxt) { int retval; char *user, *method; /* Various checks; fail gracefully */ if (authctxt == NULL || authctxt->pam == NULL) return PAM_SYSTEM_ERR; /* shouldn't happen */ if (compat20) { if (authctxt->method == NULL || authctxt->method->name == NULL) return PAM_SYSTEM_ERR; /* shouldn't happen */ method = authctxt->method->name; } else if ((method = authctxt->v1_auth_name) == NULL) return PAM_SYSTEM_ERR; /* shouldn't happen */ if (AUTHPAM_DONE(authctxt)) return PAM_SYSTEM_ERR; /* shouldn't happen */ if (!(authctxt->pam->state & PAM_S_DONE_ACCT_MGMT)) { retval = pam_acct_mgmt(authctxt->pam->h, 0); authctxt->pam->last_pam_retval = retval; if (retval == PAM_NEW_AUTHTOK_REQD) { userauth_force_kbdint(); return retval; } if (retval != PAM_SUCCESS) return retval; authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT; } /* * Handle PAM_USER change, if any. * * We do this before pam_open_session() because we need the PAM_USER's * UID for: * * a) PermitRootLogin checking * b) to get at the lastlog entry before pam_open_session() updates it. */ retval = pam_get_item(authctxt->pam->h, PAM_USER, (void **) &user); if (retval != PAM_SUCCESS) { fatal("PAM failure: pam_get_item(PAM_USER) " "returned %d: %.200s", retval, PAM_STRERROR(authctxt->pam->h, retval)); } if (user == NULL || *user == '\0') { debug("PAM set NULL PAM_USER"); return PAM_PERM_DENIED; } if (strcmp(user, authctxt->user) != 0) { log("PAM changed the SSH username"); pwfree(&authctxt->pw); authctxt->pw = PRIVSEP(getpwnamallow(user)); authctxt->valid = (authctxt->pw != NULL); xfree(authctxt->user); authctxt->user = xstrdup(user); } if (!authctxt->valid) { debug2("PAM set PAM_USER to unknown user"); /* * Return success, userauth_finish() will catch * this and send back a failure message. */ return PAM_SUCCESS; } /* Check PermitRootLogin semantics */ if (authctxt->pw->pw_uid == 0 && !auth_root_allowed(method)) return PAM_PERM_DENIED; if (!(authctxt->pam->state & PAM_S_DONE_SETCRED)) { retval = pam_setcred(authctxt->pam->h, PAM_ESTABLISH_CRED); authctxt->pam->last_pam_retval = retval; if (retval != PAM_SUCCESS) return retval; authctxt->pam->state |= PAM_S_DONE_SETCRED; #ifdef GSSAPI /* * Store GSS-API delegated creds after pam_setcred(), which may * have set the current credential store. */ ssh_gssapi_storecreds(NULL, authctxt); #endif /* GSSAPI */ } /* * On Solaris pam_unix_session.so updates the lastlog, but does * not converse a PAM_TEXT_INFO message about it. So we need to * fetch the lastlog entry here and save it for use later. */ authctxt->last_login_time = get_last_login_time(authctxt->pw->pw_uid, authctxt->pw->pw_name, authctxt->last_login_host, sizeof(authctxt->last_login_host)); if (!(authctxt->pam->state & PAM_S_DONE_OPEN_SESSION)) { retval = pam_open_session(authctxt->pam->h, 0); authctxt->pam->last_pam_retval = retval; if (retval != PAM_SUCCESS) return retval; authctxt->pam->state |= PAM_S_DONE_OPEN_SESSION; } /* * All PAM work done successfully. * * PAM handle stays around so we can call pam_close_session() on * it later. */ return PAM_SUCCESS; }