Handle interacting with NIS servers which append aging information to the
pw_passwd field, separated with a comma.
Don't try to change passwords for users who aren't authenticated using local
files or NIS, because we don't know how, and another module will probably want
to take care of it.
diff -uNr Linux-PAM-0.75/modules/pam_unix/pam_unix_passwd.c Linux-PAM-0.75/modules/pam_unix/pam_unix_passwd.c
--- Linux-PAM-0.75/modules/pam_unix/pam_unix_passwd.c	Tue Oct  2 13:57:50 2001
+++ Linux-PAM-0.75/modules/pam_unix/pam_unix_passwd.c	Tue Oct  2 14:31:56 2001
@@ -337,6 +337,7 @@
 	int retval = 0;
 	int err = 0;
 	int oldmask;
+	char *what;
 
 	oldmask = umask(077);
 	pwfile = fopen(PW_TMPFILE, "w");
@@ -344,7 +345,7 @@
 	opwfile = fopen("/etc/passwd", "r");
 	if (pwfile == NULL || opwfile == NULL)
 		return PAM_AUTHTOK_ERR;
-	if(fstat(fileno(pwfile), &st) == -1) {
+	if(fstat(fileno(opwfile), &st) == -1) {
 		chown(PW_TMPFILE, 0, 0);
 		chmod(PW_TMPFILE, 0644);
 	} else {
@@ -352,9 +353,10 @@
 		chmod(PW_TMPFILE, st.st_mode);
 	}
 	tmpent = fgetpwent(opwfile);
+	what = strdup(towhat);
 	while (tmpent) {
 		if (!strcmp(tmpent->pw_name, forwho)) {
-			tmpent->pw_passwd = towhat;
+			tmpent->pw_passwd = what;
 		}
 		if (putpwent(tmpent, pwfile)) {
 			fprintf(stderr, "error writing entry to password file: %s\n",
@@ -378,6 +380,8 @@
 	else
 		unlink(PW_TMPFILE);
 
+	free(what);
+
 	return retval;
 }
 
@@ -454,7 +458,7 @@
 	if (pwd == NULL)
 		return PAM_AUTHTOK_ERR;
 
-	if (on(UNIX_NIS, ctrl)) {
+	if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(forwho, 0, 1)) {
 		struct timeval timeout;
 		struct yppasswd yppwd;
 		CLIENT *clnt;
@@ -518,12 +522,14 @@
 	if (save_old_password(forwho, fromwhat, remember)) {
 		return PAM_AUTHTOK_ERR;
 	}
-	if (on(UNIX_SHADOW, ctrl) || (strcmp(pwd->pw_passwd, "x") == 0)) {
-		retval = _update_shadow(forwho, towhat);
-		if (retval == PAM_SUCCESS)
-			retval = _update_passwd(forwho, "x");
-	} else {
-		retval = _update_passwd(forwho, towhat);
+	if (_unix_comesfromsource(forwho, 1, 0)) {
+		if (on(UNIX_SHADOW, ctrl)||(strcmp(pwd->pw_passwd, "x") == 0)) {
+			retval = _update_shadow(forwho, towhat);
+			if (retval == PAM_SUCCESS)
+				retval = _update_passwd(forwho, "x");
+		} else {
+			retval = _update_passwd(forwho, towhat);
+		}
 	}
 
 	return retval;
@@ -635,7 +641,7 @@
 #else
 		if (strlen(pass_new) < 6)
 			remark = "You must choose a longer password";
-		D(("lenth check [%s]", remark));
+		D(("length check [%s]", remark));
 #endif
 		if (on(UNIX_REMEMBER_PASSWD, ctrl))
 			if ((retval = check_old_password(user, pass_new)) != PAM_SUCCESS)
@@ -721,6 +727,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(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(user, 1, 1, &pwd);
+		if((strcmp(pwd.pw_passwd, "x") != 0) &&
+		   (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))
diff -uNr Linux-PAM-0.75/modules/pam_unix/support.c Linux-PAM-0.75/modules/pam_unix/support.c
--- Linux-PAM-0.75/modules/pam_unix/support.c	Tue Oct  2 13:57:49 2001
+++ Linux-PAM-0.75/modules/pam_unix/support.c	Tue Oct  2 14:37:47 2001
@@ -308,6 +308,110 @@
 }
 
 /*
+ * _unix_getpwnam() is a quick check that the user's info is actually
+ * coming from one of the sources this module is able to change, so that
+ * a password-changing stack will execute properly
+ */
+
+int _unix_getpwnam(const char *name, int files, int nis, struct passwd *ret)
+{
+	FILE *passwd;
+	char buf[LINE_MAX];
+	int matched = 0;
+	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)) {
+		slogin = buf;
+
+		spasswd = strchr(slogin, ':');
+		if(spasswd != NULL) *spasswd++ = '\0';
+
+		suid = strchr(spasswd, ':');
+		if(suid != NULL) *suid++ = '\0';
+
+		sgid = strchr(suid, ':');
+		if(sgid != NULL) *sgid++ = '\0';
+
+		sgecos = strchr(sgid, ':');
+		if(sgecos != NULL) *sgecos++ = '\0';
+
+		shome = strchr(sgecos, ':');
+		if(shome != NULL) *shome++ = '\0';
+
+		sshell = strchr(shome, ':');
+		if(sshell != NULL) *sshell++ = '\0';
+
+		ret->pw_uid = strtol(suid, &p, 0);
+		if((strlen(sgid) == 0) || (*p != '\0')) {
+			return 0;
+		}
+
+		ret->pw_gid = strtol(sgid, &p, 0);
+		if((strlen(sgid) == 0) || (*p != '\0')) {
+			return 0;
+		}
+
+		ret->pw_name = strdup(slogin);
+		ret->pw_passwd = strdup(spasswd);
+		ret->pw_gecos = strdup(sgecos);
+		ret->pw_dir = strdup(shome);
+		ret->pw_shell = strdup(sshell);
+	}
+
+	return matched;
+}
+
+/*
+ * _unix_getsource() is a quick check to see if information about a given
+ * user comes from a particular source (just files and nis for now)
+ *
+ */
+int _unix_comesfromsource(const char *name, int files, int nis)
+{
+	return _unix_getpwnam(name, files, nis, NULL);
+}
+
+/*
  * _unix_blankpasswd() is a quick check for a blank password
  *
  * returns TRUE if user does not have a password
@@ -360,7 +464,7 @@
 				setreuid( save_uid, save_euid );
 			else {
 				if (setreuid( -1, 0 ) == -1)
-				setreuid( save_uid, -1 );
+					setreuid( save_uid, -1 );
 				setreuid( -1, save_euid );
 			}
 		} else if (strcmp(pwd->pw_passwd, "x") == 0) {
@@ -582,12 +686,21 @@
 		} else if (!p) {
 				retval = PAM_AUTH_ERR;
 		} else {
-			if (!strncmp(salt, "$1$", 3)) {
+			if (strncmp(salt, "$1$", 3) == 0) {
 				pp = Goodcrypt_md5(p, salt);
 				if (strcmp(pp, salt) != 0) {
 					pp = Brokencrypt_md5(p, salt);
 				}
 			} else {
+				if (strlen(salt) > 13)
+				for (pp = salt + 13; *pp != '\0'; pp++) {
+					if (!strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+						    "abcdefghijklmnopqrstuvwxyz"
+						    "0123456789./", *pp)) {
+						*pp = '\0';
+						break;
+					}
+				}
 				pp = bigcrypt(p, salt);
 			}
 			p = NULL;		/* no longer needed here */
diff -uNr Linux-PAM-0.75/modules/pam_unix/support.h Linux-PAM-0.75/modules/pam_unix/support.h
--- Linux-PAM-0.75/modules/pam_unix/support.h	Tue Oct  2 13:57:50 2001
+++ Linux-PAM-0.75/modules/pam_unix/support.h	Tue Oct  2 13:58:03 2001
@@ -131,6 +131,9 @@
 		       ,int type, const char *text);
 extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int argc,
 		     const char **argv);
+extern int _unix_getpwnam(const char *name, int files, int nis,
+			  struct passwd *ret);
+extern int _unix_comesfromsource(const char *name, int files, int nis);
 extern int _unix_blankpasswd(unsigned int ctrl, const char *name);
 extern int _unix_verify_password(pam_handle_t * pamh, const char *name
 			  ,const char *p, unsigned int ctrl);
diff -uNr Linux-PAM-0.75/modules/pam_unix/unix_chkpwd.c Linux-PAM-0.75/modules/pam_unix/unix_chkpwd.c
--- Linux-PAM-0.75/modules/pam_unix/unix_chkpwd.c	Tue Oct  2 13:57:49 2001
+++ Linux-PAM-0.75/modules/pam_unix/unix_chkpwd.c	Tue Oct  2 14:39:48 2001
@@ -142,6 +144,15 @@
 				retval = UNIX_PASSED;
 		}
 	} else {
+		if (strlen(salt) > 13)
+		for (pp = salt + 13; *pp != '\0'; pp++) {
+			if (!strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+				    "abcdefghijklmnopqrstuvwxyz"
+				    "0123456789./", *pp)) {
+				*pp = '\0';
+				break;
+			}
+		}
 		pp = bigcrypt(p, salt);
 		if (strcmp(pp, salt) == 0) {
 			retval = UNIX_PASSED;
