I've got a patch here that 'happens to work' for us, but I'm no PAM guru so I don't quite trust it yet. Most of this is cut-and-paste from newer pam sources.

It works with NIS and local /etc/passwd setups. Can someone please try it with LDAP and let me know if it works for them?

I've got an LDAP environment that I'm going to try it out on tonight.

--
Scott Dier <[EMAIL PROTECTED]>
CS/IT Systems Staff
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 <[EMAIL PROTECTED]>  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 <security/pam_appl.h>
 #endif                         /* LINUX_PAM */
 
+#include <security/_pam_modutil.h>
+
 #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;

Reply via email to