summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2009-10-07 19:12:50 +0000
committerArthur de Jong <arthur@arthurdejong.org>2009-10-07 19:12:50 +0000
commita78a6872b80622a4f77c82f99119abb04bc5fe9b (patch)
tree0b62ab1a944e2da9032238d992b6217b06243226
parent801b9dc12300f1860def5a9cd3c7ca987a2c8104 (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-update4
-rw-r--r--man/pam_ldap.8.xml11
-rw-r--r--nslcd/myldap.c37
-rw-r--r--nslcd/myldap.h5
-rw-r--r--nslcd/nslcd.c1
-rw-r--r--nslcd/pam.c90
-rw-r--r--pam/pam.c169
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;
}
diff --git a/pam/pam.c b/pam/pam.c
index 4b5adfc..b0ddac8 100644
--- a/pam/pam.c
+++ b/pam/pam.c
@@ -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;
}