Index: nslcd/cfg.h =================================================================== --- nslcd/cfg.h (revision 1135) +++ nslcd/cfg.h (working copy) @@ -95,6 +95,8 @@ char *ldc_bindpw; /* bind DN for password modification by administrator */ char *ldc_rootpwmoddn; + /* bind password for password modification by root */ + char *ldc_rootpwmodpw; /* sasl authentication id */ char *ldc_sasl_authcid; /* sasl authorization id */ Index: nslcd/cfg.c =================================================================== --- nslcd/cfg.c (revision 1135) +++ nslcd/cfg.c (working copy) @@ -97,6 +97,7 @@ cfg->ldc_binddn=NULL; cfg->ldc_bindpw=NULL; cfg->ldc_rootpwmoddn=NULL; + cfg->ldc_rootpwmodpw=NULL; cfg->ldc_sasl_authcid=NULL; cfg->ldc_sasl_authzid=NULL; cfg->ldc_sasl_secprops=NULL; @@ -818,6 +819,10 @@ { get_restdup(filename,lnr,keyword,&line,&cfg->ldc_rootpwmoddn); } + else if (strcasecmp(keyword,"rootpwmodpw")==0) + { + get_restdup(filename,lnr,keyword,&line,&cfg->ldc_rootpwmodpw); + } /* SASL authentication options */ else if (strcasecmp(keyword,"sasl_authcid")==0) { Index: nslcd/pam.c =================================================================== --- nslcd/pam.c (revision 1135) +++ nslcd/pam.c (working copy) @@ -129,7 +129,7 @@ } /* check authentication credentials of the user */ -int nslcd_pam_authc(TFILE *fp,MYLDAP_SESSION *session) +int nslcd_pam_authc(TFILE *fp,MYLDAP_SESSION *session,uid_t calleruid) { int32_t tmpint32; int rc; @@ -148,8 +148,8 @@ /* write the response header */ WRITE_INT32(fp,NSLCD_VERSION); WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC); - /* if the username is blank and rootpwmoddn is configure, try to authenticate - as administrator, otherwise validate request as usual */ + /* if the username is blank and rootpwmoddn is configured, try to + authenticate as administrator */ if ((*username=='\0')&&(nslcd_cfg->ldc_rootpwmoddn!=NULL)) { if (strlen(nslcd_cfg->ldc_rootpwmoddn)>=sizeof(userdn)) @@ -158,6 +158,16 @@ return -1; } strcpy(userdn,nslcd_cfg->ldc_rootpwmoddn); + /* if the caller is root we will allow the use of the rootpwmodpw option */ + if ((*password=='\0')&&(calleruid==0)&&(nslcd_cfg->ldc_rootpwmodpw!=NULL)) + { + if (strlen(nslcd_cfg->ldc_rootpwmodpw)>=sizeof(password)) + { + log_log(LOG_ERR,"nslcd_pam_authc(): rootpwmodpw will not fit in password"); + return -1; + } + strcpy(password,nslcd_cfg->ldc_rootpwmodpw); + } } else if (validate_user(session,userdn,sizeof(userdn),username,sizeof(username))) { @@ -423,7 +433,7 @@ return rc; } -int nslcd_pam_pwmod(TFILE *fp,MYLDAP_SESSION *session) +int nslcd_pam_pwmod(TFILE *fp,MYLDAP_SESSION *session,uid_t calleruid) { int32_t tmpint32; char username[256]; @@ -451,6 +461,16 @@ { binddn=nslcd_cfg->ldc_rootpwmoddn; userdn[0]='\0'; /* cause validate_user() to get the user DN */ + /* check if rootpwmodpw should be used */ + if ((*oldpassword=='\0')&&(calleruid==0)&&(nslcd_cfg->ldc_rootpwmodpw!=NULL)) + { + if (strlen(nslcd_cfg->ldc_rootpwmodpw)>=sizeof(oldpassword)) + { + log_log(LOG_ERR,"nslcd_pam_pwmod(): rootpwmodpw will not fit in oldpassword"); + return -1; + } + strcpy(oldpassword,nslcd_cfg->ldc_rootpwmodpw); + } } /* validate request and fill in the blanks */ if (validate_user(session,userdn,sizeof(userdn),username,sizeof(username))) Index: nslcd/common.h =================================================================== --- nslcd/common.h (revision 1135) +++ nslcd/common.h (working copy) @@ -140,11 +140,11 @@ int nslcd_service_all(TFILE *fp,MYLDAP_SESSION *session); int nslcd_shadow_byname(TFILE *fp,MYLDAP_SESSION *session); int nslcd_shadow_all(TFILE *fp,MYLDAP_SESSION *session); -int nslcd_pam_authc(TFILE *fp,MYLDAP_SESSION *session); +int nslcd_pam_authc(TFILE *fp,MYLDAP_SESSION *session,uid_t calleruid); int nslcd_pam_authz(TFILE *fp,MYLDAP_SESSION *session); int nslcd_pam_sess_o(TFILE *fp,MYLDAP_SESSION *session); int nslcd_pam_sess_c(TFILE *fp,MYLDAP_SESSION *session); -int nslcd_pam_pwmod(TFILE *fp,MYLDAP_SESSION *session); +int nslcd_pam_pwmod(TFILE *fp,MYLDAP_SESSION *session,uid_t calleruid); /* macros for generating service handling code */ #define NSLCD_HANDLE(db,fn,readfn,logcall,action,mkfilter,writefn) \ Index: nslcd/nslcd.c =================================================================== --- nslcd/nslcd.c (revision 1135) +++ nslcd/nslcd.c (working copy) @@ -420,11 +420,11 @@ case NSLCD_ACTION_SERVICE_ALL: (void)nslcd_service_all(fp,session); break; case NSLCD_ACTION_SHADOW_BYNAME: if (uid==0) (void)nslcd_shadow_byname(fp,session); break; case NSLCD_ACTION_SHADOW_ALL: if (uid==0) (void)nslcd_shadow_all(fp,session); break; - case NSLCD_ACTION_PAM_AUTHC: (void)nslcd_pam_authc(fp,session); break; + case NSLCD_ACTION_PAM_AUTHC: (void)nslcd_pam_authc(fp,session,uid); break; case NSLCD_ACTION_PAM_AUTHZ: (void)nslcd_pam_authz(fp,session); break; 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; + case NSLCD_ACTION_PAM_PWMOD: (void)nslcd_pam_pwmod(fp,session,uid); break; /* TODO: maybe only do pwmod for (suid) root users */ default: log_log(LOG_WARNING,"invalid request id: %d",(int)action); Index: pam/pam.c =================================================================== --- pam/pam.c (revision 1135) +++ pam/pam.c (working copy) @@ -513,6 +513,7 @@ const char *username,*service; const char *oldpassword=NULL,*newpassword=NULL; struct passwd *pwent; + uid_t myuid; /* set up configuration */ rc=init(pamh,flags,argc,argv,&cfg,&ctx,&username,&service); if (rc!=PAM_SUCCESS) @@ -523,8 +524,17 @@ { /* see if the user is trying to modify another user's password */ pwent=getpwnam(username); - if ((pwent!=NULL)&&(pwent->pw_uid!=getuid())) + myuid=getuid(); + if ((pwent!=NULL)&&(pwent->pw_uid!=myuid)) { + /* we are root so we can test if nslcd will allow us to change the + user's password without the admin password */ + if (myuid==0) + { + rc=nslcd_request_authc(pamh,ctx,&cfg,"",service,""); + if ((rc==PAM_SUCCESS)&&(ctx->authok==PAM_SUCCESS)) + return pam_set_item(pamh,PAM_OLDAUTHTOK,""); + } /* try to authenticate with the LDAP administrator password by passing an empty username to the authc request */ rc=pam_get_authtok(pamh,PAM_OLDAUTHTOK,&oldpassword,"LDAP administrator password: "); @@ -558,6 +568,9 @@ pam_syslog(pamh,LOG_NOTICE,"%s; user=%s",pam_strerror(pamh,ctx->authok),username); else if (cfg.debug) pam_syslog(pamh,LOG_DEBUG,"authentication succeeded"); + /* store password (needed if oldpassword was retreived from context) */ + if (rc==PAM_SUCCESS) + return pam_set_item(pamh,PAM_OLDAUTHTOK,oldpassword); /* remap error code */ return remap_pam_rc(ctx->authok,&cfg); } Index: man/nslcd.conf.5.xml =================================================================== --- man/nslcd.conf.5.xml (revision 1135) +++ man/nslcd.conf.5.xml (working copy) @@ -192,12 +192,28 @@ Specifies the distinguished name to use when the root user tries to - modify a user's password using the PAM module. The PAM module prompts - the user for the admin password instead of the user's password. + modify a user's password using the PAM module. + + PASSWORD + + + Specifies the clear text credentials with which to bind if the root + user tries to change a user's password. + This option is only applicable when used with + above. + If this option is not specified the PAM module prompts the user for + this password. + If you set this option you should consider changing the permissions + of the nslcd.conf file to only grant access to + the root user. + + + +