diff -ur pam-0.76/debian/changelog pam-0.76-new/debian/changelog --- pam-0.76/debian/changelog 2005-08-31 16:00:35.267474000 -0500 +++ pam-0.76-new/debian/changelog 2005-08-31 15:55:45.857647000 -0500 @@ -1,3 +1,10 @@ +pam (0.76-22umn1) unstable; urgency=low + + * Ported patches from newer version of Linux-PAM to support NIS + password changing in the pam_unix module. + + -- Scott M. Dier Wed, 31 Aug 2005 15:30:08 -0500 + pam (0.76-22) unstable; urgency=medium * Add uploaders diff -ur pam-0.76/Linux-PAM/modules/pam_unix/pam_unix_passwd.c pam-0.76-new/Linux-PAM/modules/pam_unix/pam_unix_passwd.c --- pam-0.76/Linux-PAM/modules/pam_unix/pam_unix_passwd.c 2005-08-31 16:00:36.677469000 -0500 +++ pam-0.76-new/Linux-PAM/modules/pam_unix/pam_unix_passwd.c 2005-08-31 15:49:06.547875000 -0500 @@ -73,6 +73,8 @@ #include #endif /* LINUX_PAM */ +#include + #include "yppasswd.h" #include "md5.h" #include "support.h" @@ -210,19 +212,161 @@ return master; } -static struct passwd *_unix_getpwnam(const char *name) +static void _unix_cleanup(pam_handle_t *pamh, void *data, int error_status) { - struct passwd *ent = NULL; - FILE *pwfile; + free(data); +} - pwfile = fopen(PW_FILE, "r"); - if (pwfile != NULL) { - ent = fgetpwent(pwfile); - while (ent && (strcmp(ent->pw_name, name) != 0)) - ent = fgetpwent(pwfile); - fclose(pwfile); - } - return ent; +static int _unix_getpwnam(pam_handle_t *pamh, const char *name, + int files, int nis, struct passwd **ret) +{ + FILE *passwd; + char buf[16384]; + int matched = 0, buflen; + char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p; + + memset(buf, 0, sizeof(buf)); + + if (!matched && files) { + int userlen = strlen(name); + passwd = fopen("/etc/passwd", "r"); + if (passwd != NULL) { + while (fgets(buf, sizeof(buf), passwd) != NULL) { + if ((buf[userlen] == ':') && + (strncmp(name, buf, userlen) == 0)) { + p = buf + strlen(buf) - 1; + while (isspace(*p) && (p >= buf)) { + *p-- = '\0'; + } + matched = 1; + break; + } + } + fclose(passwd); + } + } + + if (!matched && nis) { + char *userinfo = NULL, *domain = NULL; + int len = 0, i; + len = yp_get_default_domain(&domain); + if (len == YPERR_SUCCESS) { + len = yp_bind(domain); + } + if (len == YPERR_SUCCESS) { + i = yp_match(domain, "passwd.byname", name, + strlen(name), &userinfo, &len); + yp_unbind(domain); + if ((i == YPERR_SUCCESS) && (len < sizeof(buf))) { + strncpy(buf, userinfo, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + matched = 1; + } + } + } + + if (matched && (ret != NULL)) { + *ret = NULL; + + slogin = buf; + + spasswd = strchr(slogin, ':'); + if (spasswd == NULL) { + return matched; + } + *spasswd++ = '\0'; + + + suid = strchr(spasswd, ':'); + if (suid == NULL) { + return matched; + } + *suid++ = '\0'; + + sgid = strchr(suid, ':'); + if (sgid == NULL) { + return matched; + } + *sgid++ = '\0'; + + sgecos = strchr(sgid, ':'); + if (sgecos == NULL) { + return matched; + } + *sgecos++ = '\0'; + + shome = strchr(sgecos, ':'); + if (shome == NULL) { + return matched; + } + *shome++ = '\0'; + + sshell = strchr(shome, ':'); + if (sshell == NULL) { + return matched; + } + *sshell++ = '\0'; + + buflen = sizeof(struct passwd) + + strlen(slogin) + 1 + + strlen(spasswd) + 1 + + strlen(suid) + 1 + + strlen(sgid) + 1 + + strlen(sgecos) + 1 + + strlen(shome) + 1 + + strlen(sshell) + 1; + *ret = malloc(buflen); + if (*ret == NULL) { + return matched; + } + memset(*ret, '\0', buflen); + + (*ret)->pw_uid = strtol(suid, &p, 10); + if ((strlen(sgid) == 0) || (*p != '\0')) { + free(*ret); + *ret = NULL; + return matched; + } + + (*ret)->pw_gid = strtol(sgid, &p, 10); + if ((strlen(sgid) == 0) || (*p != '\0')) { + free(*ret); + *ret = NULL; + return matched; + } + + p = ((char*)(*ret)) + sizeof(struct passwd); + (*ret)->pw_name = strcpy(p, slogin); + p += strlen(p) + 1; + (*ret)->pw_passwd = strcpy(p, spasswd); + p += strlen(p) + 1; + (*ret)->pw_gecos = strcpy(p, sgecos); + p += strlen(p) + 1; + (*ret)->pw_dir = strcpy(p, shome); + p += strlen(p) + 1; + (*ret)->pw_shell = strcpy(p, sshell); + + snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name); + + if (pam_set_data(pamh, buf, + *ret, _unix_cleanup) != PAM_SUCCESS) { + free(*ret); + *ret = NULL; + } + } + + return matched; +} + +/* + * _unix_comsefromsource() is a quick check to see if information about a given + * user comes from a particular source (just files and nis for now) + * + */ +static int _unix_comesfromsource(pam_handle_t *pamh, + const char *name, int files, int nis) +{ + return _unix_getpwnam(pamh, name, files, nis, NULL); } static int check_old_password(const char *forwho, const char *newpass) @@ -261,7 +405,8 @@ return retval; } -static int save_old_password(const char *forwho, const char *oldpass, +static int save_old_password(pam_handle_t *pamh, + const char *forwho, const char *oldpass, int howmany) { static char buf[16384]; @@ -333,7 +478,7 @@ fclose(opwfile); if (!found) { - pwd = _unix_getpwnam(forwho); + pwd = _pammodutil_getpwnam(pamh, forwho); if (pwd == NULL) { err = 1; } else { @@ -423,7 +568,7 @@ } } -static int _update_shadow(const char *forwho, char *towhat) +static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat) { struct spwd *spwdent = NULL, *stmpent = NULL; FILE *pwfile, *opwfile; @@ -479,6 +624,7 @@ if (!err) { rename(SH_TMPFILE, "/etc/shadow"); + _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho); return PAM_SUCCESS; } else { unlink(SH_TMPFILE); @@ -498,7 +644,7 @@ if (pwd == NULL) return PAM_AUTHTOK_ERR; - if (on(UNIX_NIS, ctrl)) { + if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) { struct timeval timeout; struct yppasswd yppwd; CLIENT *clnt; @@ -518,7 +664,7 @@ yppwd.newpw.pw_gecos = pwd->pw_gecos; yppwd.newpw.pw_dir = pwd->pw_dir; yppwd.newpw.pw_shell = pwd->pw_shell; - yppwd.oldpass = fromwhat ? fromwhat : ""; + yppwd.oldpass = fromwhat ? strdup (fromwhat) : strdup (""); yppwd.newpw.pw_passwd = towhat; D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho)); @@ -563,7 +709,7 @@ return retval; } /* first, save old password */ - if (save_old_password(forwho, fromwhat, remember)) { + if (save_old_password(pamh, forwho, fromwhat, remember)) { return PAM_AUTHTOK_ERR; } @@ -583,15 +729,20 @@ return PAM_AUTHTOK_LOCK_BUSY; } #endif /* def USE_LCKPWDF */ - - if (on(UNIX_SHADOW, ctrl) || (strcmp(pwd->pw_passwd, "x") == 0)) { - retval = _update_shadow(forwho, towhat); - if (retval == PAM_SUCCESS) - retval = _update_passwd(pamh, forwho, "x"); - } else { - retval = _update_passwd(pamh, forwho, towhat); - } + if (_unix_comesfromsource (pamh, forwho, 1, 0)) + { + if (on(UNIX_SHADOW, ctrl) || _unix_shadowed (pwd)) + { + retval = _update_shadow (pamh, forwho, towhat); + if (retval == PAM_SUCCESS) + if (!_unix_shadowed (pwd)) + retval = _update_passwd (pamh, forwho, "x"); + } + else + retval = _update_passwd (pamh, forwho, towhat); + } + if (retval == PAM_SUCCESS) _log_err(LOG_NOTICE, pamh, "Password for %s was changed", forwho); @@ -610,11 +761,11 @@ int retval = PAM_SUCCESS; /* UNIX passwords area */ - pwd = _unix_getpwnam(user); /* Get password *file* entry... */ + pwd = getpwnam(user); /* Get password *file* entry... */ if (pwd == NULL) return PAM_AUTHINFO_UNAVAIL; /* We don't need to do the rest... */ - if (strcmp(pwd->pw_passwd, "x") == 0) { + if (_unix_shadowed(pwd)) { /* ...and shadow password file entry for this user, if shadowing is enabled */ setspent(); @@ -767,6 +918,30 @@ D(("Got username of %s", user)); /* + * Before we do anything else, check to make sure that the user's + * info is in one of the databases we can modify from this module, + * which currently is 'files' and 'nis'. We have to do this because + * getpwnam() doesn't tell you *where* the information it gives you + * came from, nor should it. That's our job. + */ + if (_unix_comesfromsource(pamh, user, 1, 1) == 0) { + _log_err(LOG_DEBUG, pamh, + "user \"%s\" does not exist in /etc/passwd or NIS", + user); + return PAM_USER_UNKNOWN; + } else { + struct passwd *pwd; + _unix_getpwnam(pamh, user, 1, 1, &pwd); + if (!_unix_shadowed(pwd) && + (strchr(pwd->pw_passwd, '*') != NULL)) { + _log_err(LOG_DEBUG, pamh, + "user \"%s\" does not have modifiable password", + user); + return PAM_USER_UNKNOWN; + } + } + + /* * This is not an AUTH module! */ if (on(UNIX__NONULL, ctrl)) @@ -806,7 +981,7 @@ : "(current) UNIX password: ") ,NULL ,_UNIX_OLD_AUTHTOK - ,(const char **) &pass_old); + ,&pass_old); free(Announce); if (retval != PAM_SUCCESS) { diff -ur pam-0.76/Linux-PAM/modules/pam_unix/support.c pam-0.76-new/Linux-PAM/modules/pam_unix/support.c --- pam-0.76/Linux-PAM/modules/pam_unix/support.c 2005-08-31 16:00:36.797475000 -0500 +++ pam-0.76-new/Linux-PAM/modules/pam_unix/support.c 2005-08-31 15:48:54.957884000 -0500 @@ -791,7 +791,7 @@ */ if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) { - retval = pam_get_item(pamh, authtok_flag, (const void **) pass); + retval = pam_get_item(pamh, authtok_flag, pass); if (retval != PAM_SUCCESS) { /* very strange. */ _log_err(LOG_ALERT, pamh @@ -926,6 +926,21 @@ return PAM_SUCCESS; } +int _unix_shadowed(const struct passwd *pwd) +{ + if (pwd != NULL) { + if (strcmp(pwd->pw_passwd, "x") == 0) { + return 1; + } + if ((pwd->pw_passwd[0] == '#') && + (pwd->pw_passwd[1] == '#') && + (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) { + return 1; + } + } + return 0; +} + /* ****************************************************************** * * Copyright (c) Jan Rêkorajski 1999. * Copyright (c) Andrew G. Morgan 1996-8. diff -ur pam-0.76/Linux-PAM/modules/pam_unix/support.h pam-0.76-new/Linux-PAM/modules/pam_unix/support.h --- pam-0.76/Linux-PAM/modules/pam_unix/support.h 2005-08-31 16:00:36.837467000 -0500 +++ pam-0.76-new/Linux-PAM/modules/pam_unix/support.h 2005-08-31 15:48:54.967880000 -0500 @@ -149,6 +149,7 @@ ,const char *prompt2 ,const char *data_name ,const char **pass); +extern int _unix_shadowed(const struct passwd *pwd); extern unsigned int pass_min_len; extern unsigned int pass_max_len;