diff --git a/auth.h b/auth.h index fb3a75446..db0f72e1a 100644 --- a/auth.h +++ b/auth.h @@ -40,6 +40,8 @@ void send_msg_userauth_banner(buffer *msg); void svr_auth_password(void); void svr_auth_pubkey(void); void svr_auth_pam(void); +void svr_auth_pam_cleanup(void); +void svr_auth_pam_env(void); #if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT int svr_pubkey_allows_agentfwd(void); @@ -122,6 +124,11 @@ struct AuthState { #if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT struct PubKeyOptions* pubkey_options; #endif +#if DROPBEAR_SVR_PAM_AUTH + pam_handle_t* pam_handle; + unsigned pam_sesopen : 1; + unsigned pam_credset : 1; +#endif }; #if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT diff --git a/configure.ac b/configure.ac index 893b9041b..5a64c8e02 100644 --- a/configure.ac +++ b/configure.ac @@ -157,7 +157,7 @@ AC_ARG_ENABLE(pam, if test "x$enableval" = "xyes"; then AC_CHECK_LIB(pam, pam_authenticate, , AC_MSG_ERROR([*** PAM missing - install first or check config.log ***])) AC_MSG_NOTICE(Enabling PAM) - AC_CHECK_FUNCS(pam_fail_delay) + AC_CHECK_FUNC(pam_fail_delay, [AC_DEFINE(WITH_PAM_FAIL_DELAY,1,[Define 1 if pam_fail_delay available])]) else AC_DEFINE(DISABLE_PAM,, Use PAM) AC_MSG_NOTICE(Disabling PAM) diff --git a/includes.h b/includes.h index f91a2c2f0..f40f6110f 100644 --- a/includes.h +++ b/includes.h @@ -177,4 +177,12 @@ typedef u_int32_t uint32_t; # define UNUSED(x) x #endif +#if DROPBEAR_SVR_PAM_AUTH +#if defined(HAVE_SECURITY_PAM_APPL_H) +#include +#elif defined (HAVE_PAM_PAM_APPL_H) +#include +#endif +#endif + #endif /* DROPBEAR_INCLUDES_H_ */ diff --git a/svr-auth.c b/svr-auth.c index 4dc280c4f..8f56504c8 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -191,10 +191,8 @@ void recv_msg_userauth_request() { if (methodlen == AUTH_METHOD_PASSWORD_LEN && strncmp(methodname, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN) == 0) { - if (valid_user) { - svr_auth_pam(); - goto out; - } + svr_auth_pam(); + goto out; } } #endif diff --git a/svr-authpam.c b/svr-authpam.c index ac8e5ece6..51545c399 100644 --- a/svr-authpam.c +++ b/svr-authpam.c @@ -33,12 +33,6 @@ #if DROPBEAR_SVR_PAM_AUTH -#if defined(HAVE_SECURITY_PAM_APPL_H) -#include -#elif defined (HAVE_PAM_PAM_APPL_H) -#include -#endif - struct UserDataS { char* user; char* passwd; @@ -186,7 +180,7 @@ void svr_auth_pam() { &userData /* submitted to pamvConvFunc as appdata_ptr */ }; - pam_handle_t* pamHandlep = NULL; + pam_handle_t* pamHandlep = ses.authstate.pam_handle; char * password = NULL; unsigned int passwordlen; @@ -207,16 +201,23 @@ void svr_auth_pam() { /* used to pass data to the PAM conversation function - don't bother with * strdup() etc since these are touched only by our own conversation * function (above) which takes care of it */ - userData.user = ses.authstate.pw_name; + userData.user = ses.authstate.username; userData.passwd = password; /* Init pam */ - if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) { + if ((rc = pam_start("sshd", ses.authstate.username, &pamConv, &pamHandlep)) != PAM_SUCCESS) { dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s", rc, pam_strerror(pamHandlep, rc)); goto cleanup; } + /* many pam modules expect RHOST */ + if ((rc = pam_set_item(pamHandlep, PAM_RHOST, svr_ses.remotehost)) != PAM_SUCCESS) { + dropbear_log(LOG_WARNING, "pam_set_item() failed, rc=%d, %s", + rc, pam_strerror(pamHandlep, rc)); + goto cleanup; + } + /* just to set it to something */ if ((rc = pam_set_item(pamHandlep, PAM_TTY, "ssh")) != PAM_SUCCESS) { dropbear_log(LOG_WARNING, "pam_set_item() failed, rc=%d, %s", @@ -224,7 +225,7 @@ void svr_auth_pam() { goto cleanup; } -#ifdef HAVE_PAM_FAIL_DELAY +#ifdef WITH_PAM_FAIL_DELAY /* We have our own random delay code already, disable PAM's */ (void) pam_fail_delay(pamHandlep, 0 /* musec_delay */); #endif @@ -236,37 +237,89 @@ void svr_auth_pam() { rc, pam_strerror(pamHandlep, rc)); dropbear_log(LOG_WARNING, "Bad PAM password attempt for '%s' from %s", - ses.authstate.pw_name, + ses.authstate.username, svr_ses.addrstring); send_msg_userauth_failure(0, 1); goto cleanup; } - if ((rc = pam_acct_mgmt(pamHandlep, 0)) != PAM_SUCCESS) { + if ((rc = pam_acct_mgmt(pamHandlep, 0)) == PAM_NEW_AUTHTOK_REQD) + rc = pam_chauthtok(pamHandlep, PAM_CHANGE_EXPIRED_AUTHTOK); + if (rc != PAM_SUCCESS) { dropbear_log(LOG_WARNING, "pam_acct_mgmt() failed, rc=%d, %s", rc, pam_strerror(pamHandlep, rc)); dropbear_log(LOG_WARNING, "Bad PAM password attempt for '%s' from %s", - ses.authstate.pw_name, + ses.authstate.username, svr_ses.addrstring); send_msg_userauth_failure(0, 1); goto cleanup; } + /* establish requested credentials */ + if ((rc = pam_setcred(pamHandlep, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { + dropbear_log(LOG_WARNING, "pam_setcred() failed, rc=%d, %s", + rc, pam_strerror(pamHandlep, rc)); + send_msg_userauth_failure(0, 1); + goto cleanup; + } + + /* open session */ + if ((rc = pam_open_session(pamHandlep, 0)) != PAM_SUCCESS) { + dropbear_log(LOG_WARNING, "pam_open_session() failed, rc=%d, %s", + rc, pam_strerror(pamHandlep, rc)); + send_msg_userauth_failure(0, 1); + goto cleanup; + } + /* successful authentication */ dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s", - ses.authstate.pw_name, + ses.authstate.username, svr_ses.addrstring); + + ses.authstate.pam_sesopen = 1; send_msg_userauth_success(); + goto cleanupok; cleanup: + svr_auth_pam_cleanup(); + +cleanupok: if (password != NULL) { m_burn(password, passwordlen); m_free(password); } - if (pamHandlep != NULL) { - TRACE(("pam_end")) - (void) pam_end(pamHandlep, 0 /* pam_status */); +} + +void svr_auth_pam_cleanup() { + int rc = PAM_SUCCESS; + + if (ses.authstate.pam_handle != NULL) { + if (ses.authstate.pam_sesopen) { + if ((rc = pam_close_session(ses.authstate.pam_handle, PAM_SILENT)) != PAM_SUCCESS) { + dropbear_log(LOG_WARNING, "pam_close_session() failed, rc=%d, %s", + rc, pam_strerror(ses.authstate.pam_handle, rc)); + } + ses.authstate.pam_sesopen = 0; + } + if (ses.authstate.pam_credset) { + if ((rc = pam_setcred(ses.authstate.pam_handle, PAM_DELETE_CRED)) != PAM_SUCCESS) { + dropbear_log(LOG_WARNING, "pam_setcred() failed, rc=%d, %s", + rc, pam_strerror(ses.authstate.pam_handle, rc)); + } + ses.authstate.pam_credset = 0; + } + } + TRACE(("pam_end")) + (void) pam_end(ses.authstate.pam_handle, 0); +} + +void svr_auth_pam_env() { + char **pam_envlist, **pam_env; + if ((pam_envlist = pam_getenvlist(ses.authstate.pam_handle)) != NULL) { + for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) { + putenv(*pam_env); + } } } diff --git a/svr-chansession.c b/svr-chansession.c index 6dbc8ad72..5bfb1bd35 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -323,6 +323,10 @@ static void closechansess(struct Channel *channel) { svr_agentcleanup(chansess); #endif +#if DROPBEAR_SVR_PAM_AUTH + svr_auth_pam_cleanup(); +#endif + /* clear child pid entries */ for (i = 0; i < svr_ses.childpidsize; i++) { if (svr_ses.childpids[i].chansess == chansess) { @@ -927,6 +931,10 @@ static void execchild(void *user_data) { #endif /* HAVE_CLEARENV */ #endif /* DEBUG_VALGRIND */ +#if DROPBEAR_SVR_PAM_AUTH + svr_auth_pam_env(chansess); +#endif + /* We can only change uid/gid as root ... */ if (getuid() == 0) {