This patch 'works for me' and re-uses the (i feel) applicable shadow
doesn't have to be valid/existing option.
Thanks,
--
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;