diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2009-10-07 19:12:50 +0000 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2009-10-07 19:12:50 +0000 |
commit | a78a6872b80622a4f77c82f99119abb04bc5fe9b (patch) | |
tree | 0b62ab1a944e2da9032238d992b6217b06243226 | |
parent | 801b9dc12300f1860def5a9cd3c7ca987a2c8104 (diff) |
implement password changing in the PAM module by performing an LDAP password modify EXOP request
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/nss-pam-ldapd@1000 ef36b2f9-881f-0410-afb5-c4e39611909c
-rw-r--r-- | debian/libpam-ldapd.pam-auth-update | 4 | ||||
-rw-r--r-- | man/pam_ldap.8.xml | 11 | ||||
-rw-r--r-- | nslcd/myldap.c | 37 | ||||
-rw-r--r-- | nslcd/myldap.h | 5 | ||||
-rw-r--r-- | nslcd/nslcd.c | 1 | ||||
-rw-r--r-- | nslcd/pam.c | 90 | ||||
-rw-r--r-- | pam/pam.c | 169 |
7 files changed, 199 insertions, 118 deletions
diff --git a/debian/libpam-ldapd.pam-auth-update b/debian/libpam-ldapd.pam-auth-update index 6fdfeb7..8e79226 100644 --- a/debian/libpam-ldapd.pam-auth-update +++ b/debian/libpam-ldapd.pam-auth-update @@ -11,9 +11,9 @@ Account: [success=end default=ignore] pam_ldap.so Password-Type: Primary Password-Initial: - [success=end user_unknown=ignore default=die] pam_ldap.so + [success=end default=ignore] pam_ldap.so Password: - [success=end user_unknown=ignore default=die] pam_ldap.so use_authtok try_first_pass + [success=end default=ignore] pam_ldap.so try_first_pass Session-Type: Additional Session: optional pam_ldap.so diff --git a/man/pam_ldap.8.xml b/man/pam_ldap.8.xml index 2195b9d..7d828ad 100644 --- a/man/pam_ldap.8.xml +++ b/man/pam_ldap.8.xml @@ -23,7 +23,7 @@ 02110-1301 USA --> -<refentry id="pamldap5"> +<refentry id="pamldap8"> <refentryinfo> <author> @@ -131,8 +131,9 @@ <listitem> <para> This causes the <acronym>PAM</acronym> module to use the earlier - provided password, analogous to <option>use_first_pass</option>, when - the password is changed. + provided password when chanhing the password. The module will not + prompt the user for a new password (it is analogous to + <option>use_first_pass</option>). </para> </listitem> </varlistentry> @@ -152,8 +153,8 @@ <refsect1 id="moduleservices"> <title>Module Services Provided</title> <para> - All service are provided by this module but currently only authentication - (auth) is supported and password change (password) is under development. + All services are provided by this module but currently only authentication + (auth) and password change (password) are implemented in the nslcd daemon. </para> </refsect1> diff --git a/nslcd/myldap.c b/nslcd/myldap.c index c5cd1e0..7da3446 100644 --- a/nslcd/myldap.c +++ b/nslcd/myldap.c @@ -1647,3 +1647,40 @@ int myldap_set_debuglevel(int level) } return LDAP_SUCCESS; } + +int myldap_passwd( + MYLDAP_SESSION *session, + const char *userdn,const char *oldpassword,const char *newpasswd) +{ + int rc; + struct berval ber_userdn, ber_oldpassword, ber_newpassword, ber_retpassword; + /* check parameters */ + if (!is_valid_session(session)||(userdn==NULL)||(oldpassword==NULL)||(newpasswd==NULL)) + { + log_log(LOG_ERR,"myldap_exop_passwd(): invalid parameter passed"); + errno=EINVAL; + return NULL; + } + /* log the call */ + log_log(LOG_DEBUG,"myldap_exop_passwd(userdn=\"%s\")",userdn); + /* translate to ber stuff */ + ber_userdn.bv_val=userdn; + ber_userdn.bv_len=strlen(userdn); + ber_oldpassword.bv_val=oldpassword; + ber_oldpassword.bv_len=strlen(oldpassword); + ber_newpassword.bv_val=newpasswd; + ber_newpassword.bv_len=strlen(newpasswd); + ber_retpassword.bv_val=NULL; + ber_retpassword.bv_len=0; + /* perform request */ + rc=ldap_passwd_s(session->ld,&ber_userdn,&ber_oldpassword,&ber_newpassword, + &ber_retpassword,NULL,NULL); + if (rc!=LDAP_SUCCESS) + log_log(LOG_ERR,"ldap_passwd_s() failed: %s",ldap_err2string(rc)); + + + /* FIXME: free ber_retpassword data if bv_val!=NULL */ + + return rc; + +} diff --git a/nslcd/myldap.h b/nslcd/myldap.h index 55cf1da..b9d1058 100644 --- a/nslcd/myldap.h +++ b/nslcd/myldap.h @@ -133,4 +133,9 @@ MUST_USE int myldap_escape(const char *src,char *buffer,size_t buflen); /* Set the debug level globally. Returns an LDAP status code. */ int myldap_set_debuglevel(int i); +/* Perform an EXOP password modification call. */ +int myldap_passwd( + MYLDAP_SESSION *session, + const char *userdn,const char *oldpassword,const char *newpasswd); + #endif /* not _MYLDAP_H */ diff --git a/nslcd/nslcd.c b/nslcd/nslcd.c index 5ed9608..d5fe923 100644 --- a/nslcd/nslcd.c +++ b/nslcd/nslcd.c @@ -417,6 +417,7 @@ static void handleconnection(int sock,MYLDAP_SESSION *session) case NSLCD_ACTION_PAM_SESS_O: (void)nslcd_pam_sess_o(fp,session); break; case NSLCD_ACTION_PAM_SESS_C: (void)nslcd_pam_sess_c(fp,session); break; case NSLCD_ACTION_PAM_PWMOD: (void)nslcd_pam_pwmod(fp,session); break; + /* TODO: maybe only do pwmod for (suid) root users */ default: log_log(LOG_WARNING,"invalid request id: %d",(int)action); break; diff --git a/nslcd/pam.c b/nslcd/pam.c index 82a2a0c..d30e703 100644 --- a/nslcd/pam.c +++ b/nslcd/pam.c @@ -140,7 +140,8 @@ int nslcd_pam_authc(TFILE *fp,MYLDAP_SESSION *session) READ_STRING(fp,servicename); READ_STRING(fp,password); /* log call */ - log_log(LOG_DEBUG,"nslcd_pam_authc(\"%s\",\"%s\",\"%s\")",username,userdn,servicename); + log_log(LOG_DEBUG,"nslcd_pam_authc(\"%s\",\"%s\",\"%s\",\"%s\")", + username,userdn,servicename,*password?"***":""); /* write the response header */ WRITE_INT32(fp,NSLCD_VERSION); WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC); @@ -261,37 +262,72 @@ int nslcd_pam_sess_c(TFILE *fp,MYLDAP_SESSION *session) return 0; } +static int try_pwmod(const char *userdn,const char *oldpassword, + const char *newpassword) +{ + MYLDAP_SESSION *session; + int rc; + /* set up a new connection */ + session=myldap_create_session(); + if (session==NULL) + return NSLCD_PAM_AUTH_ERR; + /* set up credentials for the session */ + rc=myldap_set_credentials(session,userdn,oldpassword); + if (rc==LDAP_SUCCESS) + { + /* perform password modification */ + rc=myldap_passwd(session,userdn,oldpassword,newpassword); + } + /* close the session */ + myldap_session_close(session); + /* return */ + return rc; +} + int nslcd_pam_pwmod(TFILE *fp,MYLDAP_SESSION *session) { -/* - struct berval dn, uid, opw, npw; int32_t tmpint32; - char dnc[1024]; - char uidc[256]; - char opwc[256]; - char npwc[256]; - - READ_STRING(fp,dnc); - dn.bv_val = dnc; - dn.bv_len = tmpint32; - READ_STRING(fp,uidc); - uid.bv_val = uidc; - uid.bv_len = tmpint32; - READ_STRING(fp,opwc); - opw.bv_val = opwc; - opw.bv_len = tmpint32; - READ_STRING(fp,npwc); - npw.bv_val = npwc; - npw.bv_len = tmpint32; - - Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(%s), %s\n",dn.bv_val,uid.bv_val,0); - - BER_BVZERO(&npw); + char username[256]; + char userdn[256]; + char servicename[64]; + char oldpassword[64]; + char newpassword[64]; + int rc; + /* read request parameters */ + READ_STRING(fp,username); + READ_STRING(fp,userdn); + READ_STRING(fp,servicename); + READ_STRING(fp,oldpassword); + READ_STRING(fp,newpassword); + /* log call */ + log_log(LOG_DEBUG,"nslcd_pam_pwmod(\"%s\",\"%s\",\"%s\",\"%s\",\"%s\")", + username,userdn,servicename,*oldpassword?"***":"", + *newpassword?"***":""); + /* write the response header */ WRITE_INT32(fp,NSLCD_VERSION); WRITE_INT32(fp,NSLCD_ACTION_PAM_PWMOD); + /* validate request and fill in the blanks */ + if (validate_user(session,userdn,sizeof(userdn),username,sizeof(username))) + { + WRITE_INT32(fp,NSLCD_RESULT_END); + return -1; + } + /* perform password modification */ + rc=try_pwmod(userdn,oldpassword,newpassword); + /* write response */ WRITE_INT32(fp,NSLCD_RESULT_BEGIN); - WRITE_INT32(fp,PAM_SUCCESS); - WRITE_BERVAL(fp,&npw); -*/ + WRITE_STRING(fp,username); + WRITE_STRING(fp,userdn); + if (rc==LDAP_SUCCESS) + { + WRITE_INT32(fp,NSLCD_PAM_SUCCESS); + WRITE_STRING(fp,""); + } + else + { + WRITE_INT32(fp,NSLCD_PAM_PERM_DENIED); + WRITE_STRING(fp,ldap_err2string(rc)); + } + WRITE_INT32(fp,NSLCD_RESULT_END); return 0; } @@ -43,6 +43,7 @@ #endif /* HAVE_SECURITY_PAM_APPL_H */ #ifndef HAVE_PAM_PAM_MODULES_H #include <security/pam_modules.h> +#include <security/pam_ext.h> #else /* not HAVE_PAM_PAM_MODULES_H */ #include <pam/pam_modules.h> #endif @@ -68,7 +69,7 @@ typedef struct pld_ctx { char *dn; char *tmpluser; char *authzmsg; - char *oldpw; + char *oldpassword; int authok; int authz; int sessid; @@ -105,11 +106,11 @@ static void ctx_clear(pld_ctx *ctx) free(ctx->user); ctx->user=NULL; } - if (ctx->oldpw) + if (ctx->oldpassword) { - memset(ctx->oldpw,0,strlen(ctx->oldpw)); - free(ctx->oldpw); - ctx->oldpw=NULL; + memset(ctx->oldpassword,0,strlen(ctx->oldpassword)); + free(ctx->oldpassword); + ctx->oldpassword=NULL; } ctx->dn=NULL; ctx->tmpluser=NULL; @@ -157,7 +158,7 @@ static int ctx_get(pam_handle_t *pamh,const char *username,pld_ctx **pctx) } /* ask the user for an authentication token (password) */ -static int pam_get_authtok(pam_handle_t *pamh,int flags,char *prompt1,char *prompt2,const char **pwd) +static int my_pam_get_authtok(pam_handle_t *pamh,int flags,char *prompt1,char *prompt2,const char **pwd) { int rc; char *p; @@ -284,7 +285,7 @@ int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,const char **argv) { if (!first_pass) { - rc=pam_get_authtok(pamh,flags,i==0?"Password: ":"LDAP Password: ",NULL,(const char **)&passwd); + rc=my_pam_get_authtok(pamh,flags,i==0?"Password: ":"LDAP Password: ",NULL,(const char **)&passwd); if (rc!=PAM_SUCCESS) return rc; /* exit loop after trying this password */ @@ -315,7 +316,7 @@ int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,const char **argv) ctx->user=strdup(username); /* if password change is required, save old password in context */ if (ctx->authz==PAM_NEW_AUTHTOK_REQD) - ctx->oldpw=strdup(passwd); + ctx->oldpassword=strdup(passwd); } /* update caller's idea of the user name */ if ( (rc==PAM_SUCCESS) && ctx->tmpluser && ctx->tmpluser[0] && @@ -333,7 +334,7 @@ int pam_sm_setcred(pam_handle_t UNUSED(*pamh),int UNUSED(flags), return PAM_SUCCESS; } -static int pam_warn( +static int my_pam_warn( struct pam_conv *aconv, const char *message, int style, int no_warn) { struct pam_message msg, *pmsg; @@ -444,18 +445,18 @@ int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc,const char **argv) if (rc!=PAM_SUCCESS) { if (rc!=PAM_IGNORE) - pam_warn(appconv,"LDAP authorization failed",PAM_ERROR_MSG,no_warn); + my_pam_warn(appconv,"LDAP authorization failed",PAM_ERROR_MSG,no_warn); } else { rc=ctx2.authz; if (ctx2.authzmsg && ctx2.authzmsg[0]) - pam_warn(appconv,ctx2.authzmsg,PAM_TEXT_INFO,no_warn); + my_pam_warn(appconv,ctx2.authzmsg,PAM_TEXT_INFO,no_warn); if (ctx2.authz==PAM_SUCCESS) { rc=ctx->authz; if (ctx->authzmsg && ctx->authzmsg[0]) - pam_warn(appconv,ctx->authzmsg,PAM_TEXT_INFO,no_warn); + my_pam_warn(appconv,ctx->authzmsg,PAM_TEXT_INFO,no_warn); } } @@ -553,7 +554,7 @@ int pam_sm_open_session( rc=pam_sm_session(pamh,flags,argc,argv,NSLCD_ACTION_PAM_SESS_O,&no_warn); if ((rc!=PAM_SUCCESS)&&(rc!=PAM_IGNORE)) - pam_warn(appconv,"LDAP open_session failed",PAM_ERROR_MSG,no_warn); + my_pam_warn(appconv,"LDAP open_session failed",PAM_ERROR_MSG,no_warn); return rc; } @@ -569,7 +570,7 @@ int pam_sm_close_session( rc=pam_sm_session(pamh,flags,argc,argv,NSLCD_ACTION_PAM_SESS_C,&no_warn); if ((rc!=PAM_SUCCESS)&&(rc!=PAM_IGNORE)) - pam_warn(appconv,"LDAP close_session failed",PAM_ERROR_MSG,no_warn); + my_pam_warn(appconv,"LDAP close_session failed",PAM_ERROR_MSG,no_warn); return rc; } @@ -578,7 +579,7 @@ static int nslcd_request_pwmod(pld_ctx *ctx,const char *username, const char *service,const char *oldpasswd, const char *newpasswd) { - PAM_REQUEST(NSLCD_ACTION_PAM_AUTHZ, + PAM_REQUEST(NSLCD_ACTION_PAM_PWMOD, /* write the request parameters */ WRITE_STRING(fp,username); WRITE_STRING(fp,ctx->dn); @@ -592,11 +593,41 @@ static int nslcd_request_pwmod(pld_ctx *ctx,const char *username, READ_BUF_STRING(fp,ctx->authzmsg);) } -int pam_sm_chauthtok( - pam_handle_t *pamh, int flags, int argc, const char **argv) +/* ensure that the context includes and oldpassword field */ +static const char *get_old_password(pam_handle_t *pamh, int flags,pld_ctx *ctx) +{ + int rc; + const char *oldpassword; + /* if we already have an old password we are done */ + if ((ctx->oldpassword!=NULL)&&(*ctx->oldpassword!='\0')) + return ctx->oldpassword; + /* try to get the old password from the PAM stack */ + rc=pam_get_item(pamh,PAM_OLDAUTHTOK,(const void **)&oldpassword); + if ((rc==PAM_SUCCESS)&&(oldpassword!=NULL)&&(*oldpassword!='\0')) + return oldpassword; + /* otherwise prompt for it */ + rc=my_pam_get_authtok(pamh,flags,"(current) LDAP Password: ",NULL, + (const char **)&oldpassword); + if ((rc==PAM_SUCCESS)&&(oldpassword!=NULL)&&(*oldpassword!='\0')) + { + /* save the password */ + pam_set_item(pamh,PAM_OLDAUTHTOK,oldpassword); + return oldpassword; + } + return NULL; +} + +/* Change the password of the user. This function is first called with + PAM_PRELIM_CHECK set in the flags and then without the flag. In the first + pass it is determined whether we can contact the LDAP server and the + provided old password is valid. In the second pass we get the new + password and actually modify the password. */ +int pam_sm_chauthtok(pam_handle_t *pamh,int flags,int argc,const char **argv) { int rc; - const char *username, *p=NULL, *q=NULL, *svc; + const char *username,*service; + const char *oldpassword=NULL; + const char *newpassword=NULL; int first_pass=0, no_warn=0, ignore_flags=0; int i; struct pam_conv *appconv; @@ -640,81 +671,51 @@ int pam_sm_chauthtok( if (rc!=PAM_SUCCESS) return rc; - rc=pam_get_item(pamh,PAM_SERVICE,(const void **)&svc); + rc=pam_get_item(pamh,PAM_SERVICE,(const void **)&service); if (rc!=PAM_SUCCESS) return rc; - - if (flags & PAM_PRELIM_CHECK) { - if (getuid()) { - if (!first_pass) { - rc=pam_get_authtok(pamh,flags,"(current) LDAP Password: ",NULL,&p); - if (rc==PAM_SUCCESS) { - pam_set_item(pamh,PAM_OLDAUTHTOK,p); - memset((void *)p,0,strlen(p)); - free((void *)p); - } - } - rc=pam_get_item(pamh,PAM_OLDAUTHTOK,(const void **)&p); - if (rc) - return rc; - } - else - rc=PAM_SUCCESS; - if (!ctx->dn) - { - rc=nslcd_request_pwmod(ctx,username,svc,p,NULL); - if ((rc==PAM_AUTHINFO_UNAVAIL)&&(ignore_flags&IGNORE_UNAVAIL)) - rc=PAM_IGNORE; - else if ((rc==PAM_USER_UNKNOWN)&&(ignore_flags&IGNORE_UNKNOWN)) - rc=PAM_IGNORE; - } - return rc; - } - - rc=pam_get_item(pamh,PAM_OLDAUTHTOK,(const void **)&p); - if (rc) - return rc; - - if (!p) - p=ctx->oldpw; - - if (first_pass) - { - rc=pam_get_item(pamh,PAM_AUTHTOK,(const void **)&q); - if ((rc!=PAM_SUCCESS || !q) && (first_pass & (USE_FIRST|USE_TOKEN))) { - if (rc==PAM_SUCCESS) - rc=PAM_AUTHTOK_RECOVERY_ERR; - return rc; - } - } - if (!q) + /* TODO: if we are root we may want to authenticate with the LDAP + administrator password (this shouldn't be a problem because + root is unlikely to be in LDAP anyway but perhaps we can check + the requested username and only use the administrator if that + isn't root) */ + /* prelimenary check, just see if we can connect to the LDAP server */ + if (flags&PAM_PRELIM_CHECK) { - rc=pam_get_authtok(pamh, flags, "Enter new LDAP Password: ", - "Retype new LDAP Password: ", &q); + /* get old (current) password */ + oldpassword=get_old_password(pamh,flags,ctx); + /* check the old password */ + rc=nslcd_request_authc(ctx,username,service,oldpassword); if (rc==PAM_SUCCESS) - { - pam_set_item(pamh,PAM_AUTHTOK,q); - memset((void *)q,0,strlen(q)); - free((void *)q); - rc=pam_get_item(pamh,PAM_AUTHTOK,(const void **)&q); - } - if (rc!=PAM_SUCCESS) - return rc; + rc=ctx->authok; + if ((rc==PAM_AUTHINFO_UNAVAIL)&&(ignore_flags&IGNORE_UNAVAIL)) + rc=PAM_IGNORE; + else if ((rc==PAM_USER_UNKNOWN)&&(ignore_flags&IGNORE_UNKNOWN)) + rc=PAM_IGNORE; + /* TODO: figure out when to return PAM_TRY_AGAIN */ + /* TODO: if password is incorrect (NSLCD_PAM_AUTH_ERR) log that */ + return rc; } - rc=nslcd_request_pwmod(ctx,username,svc,p,q); + /* get the old password (from the previous call) */ + rc=pam_get_item(pamh,PAM_OLDAUTHTOK,(const void **)&oldpassword); + if (rc!=PAM_SUCCESS) + return rc; + /* get the new password */ + rc=pam_get_authtok(pamh,PAM_AUTHTOK,&newpassword,NULL); + if (rc!=PAM_SUCCESS) + return rc; + /* perform the password modification */ + rc=nslcd_request_pwmod(ctx,username,service,oldpassword,newpassword); + if (rc==PAM_SUCCESS) + rc=ctx->authz; + else + ctx->authzmsg=(char *)pam_strerror(pamh,rc); if ((rc==PAM_AUTHINFO_UNAVAIL)&&(ignore_flags&IGNORE_UNAVAIL)) rc=PAM_IGNORE; else if ((rc==PAM_USER_UNKNOWN)&&(ignore_flags&IGNORE_UNKNOWN)) rc=PAM_IGNORE; - p=NULL; q=NULL; - if (rc==PAM_SUCCESS) - { - rc=ctx->authz; - if (rc!=PAM_SUCCESS) - pam_warn(appconv, ctx->authzmsg, PAM_ERROR_MSG, no_warn); - } - else if (rc!=PAM_IGNORE) - pam_warn(appconv, "LDAP pwmod failed", PAM_ERROR_MSG, no_warn); + else + my_pam_warn(appconv,ctx->authzmsg,PAM_ERROR_MSG,no_warn); return rc; } |