dd84935
--- /dev/null	2007-05-28 11:10:34.936447748 +0200
dd84935
+++ Linux-PAM-0.99.7.1/modules/pam_unix/passupdate.c	2007-06-01 15:13:57.000000000 +0200
dd84935
@@ -0,0 +1,560 @@
dd84935
+/*
dd84935
+ * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
dd84935
+ * Copyright (C) 1996.
dd84935
+ * Copyright (c) Jan RÍkorajski, 1999.
dd84935
+ * Copyright (c) Red Hat, Inc., 2007
dd84935
+ *
dd84935
+ * Redistribution and use in source and binary forms, with or without
dd84935
+ * modification, are permitted provided that the following conditions
dd84935
+ * are met:
dd84935
+ * 1. Redistributions of source code must retain the above copyright
dd84935
+ *    notice, and the entire permission notice in its entirety,
dd84935
+ *    including the disclaimer of warranties.
dd84935
+ * 2. Redistributions in binary form must reproduce the above copyright
dd84935
+ *    notice, this list of conditions and the following disclaimer in the
dd84935
+ *    documentation and/or other materials provided with the distribution.
dd84935
+ * 3. The name of the author may not be used to endorse or promote
dd84935
+ *    products derived from this software without specific prior
dd84935
+ *    written permission.
dd84935
+ *
dd84935
+ * ALTERNATIVELY, this product may be distributed under the terms of
dd84935
+ * the GNU Public License, in which case the provisions of the GPL are
dd84935
+ * required INSTEAD OF the above restrictions.  (This clause is
dd84935
+ * necessary due to a potential bad interaction between the GPL and
dd84935
+ * the restrictions contained in a BSD-style copyright.)
dd84935
+ *
dd84935
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
dd84935
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
dd84935
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
dd84935
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
dd84935
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
dd84935
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
dd84935
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
dd84935
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
dd84935
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
dd84935
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
dd84935
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
dd84935
+ */
dd84935
+
dd84935
+/* this will be included from module and update helper */
dd84935
+
dd84935
+#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
dd84935
+# include "./lckpwdf.-c"
dd84935
+#endif
dd84935
+
dd84935
+/* passwd/salt conversion macros */
dd84935
+
dd84935
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
dd84935
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
dd84935
+
dd84935
+#define PW_TMPFILE		"/etc/npasswd"
dd84935
+#define SH_TMPFILE		"/etc/nshadow"
dd84935
+#define OPW_TMPFILE		"/etc/security/nopasswd"
dd84935
+#define OLD_PASSWORDS_FILE	"/etc/security/opasswd"
dd84935
+
dd84935
+/*
dd84935
+ * i64c - convert an integer to a radix 64 character
dd84935
+ */
dd84935
+static int i64c(int i)
dd84935
+{
dd84935
+	if (i < 0)
dd84935
+		return ('.');
dd84935
+	else if (i > 63)
dd84935
+		return ('z');
dd84935
+	if (i == 0)
dd84935
+		return ('.');
dd84935
+	if (i == 1)
dd84935
+		return ('/');
dd84935
+	if (i >= 2 && i <= 11)
dd84935
+		return ('0' - 2 + i);
dd84935
+	if (i >= 12 && i <= 37)
dd84935
+		return ('A' - 12 + i);
dd84935
+	if (i >= 38 && i <= 63)
dd84935
+		return ('a' - 38 + i);
dd84935
+	return ('\0');
dd84935
+}
dd84935
+
dd84935
+static char *crypt_md5_wrapper(const char *pass_new)
dd84935
+{
dd84935
+	/*
dd84935
+	 * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
dd84935
+	 * removed use of static variables (AGM)
dd84935
+	 */
dd84935
+
dd84935
+	struct timeval tv;
dd84935
+	MD5_CTX ctx;
dd84935
+	unsigned char result[16];
dd84935
+	char *cp = (char *) result;
dd84935
+	unsigned char tmp[16];
dd84935
+	int i;
dd84935
+	char *x = NULL;
dd84935
+
dd84935
+	GoodMD5Init(&ctx;;
dd84935
+	gettimeofday(&tv, (struct timezone *) 0);
dd84935
+	GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
dd84935
+	i = getpid();
dd84935
+	GoodMD5Update(&ctx, (void *) &i, sizeof i);
dd84935
+	i = clock();
dd84935
+	GoodMD5Update(&ctx, (void *) &i, sizeof i);
dd84935
+	GoodMD5Update(&ctx, result, sizeof result);
dd84935
+	GoodMD5Final(tmp, &ctx;;
dd84935
+	strcpy(cp, "$1$");	/* magic for the MD5 */
dd84935
+	cp += strlen(cp);
dd84935
+	for (i = 0; i < 8; i++)
dd84935
+		*cp++ = i64c(tmp[i] & 077);
dd84935
+	*cp = '\0';
dd84935
+
dd84935
+	/* no longer need cleartext */
dd84935
+	x = Goodcrypt_md5(pass_new, (const char *) result);
dd84935
+
dd84935
+	return x;
dd84935
+}
dd84935
+
dd84935
+#ifdef USE_LCKPWDF
dd84935
+static int lock_pwdf(void)
dd84935
+{
dd84935
+	int i;
dd84935
+	int retval;
dd84935
+
dd84935
+#ifndef HELPER_COMPILE
dd84935
+	if (selinux_confined()) {
dd84935
+		return PAM_SUCCESS;
dd84935
+	}
dd84935
+#endif
dd84935
+	/* These values for the number of attempts and the sleep time
dd84935
+	   are, of course, completely arbitrary.
dd84935
+	   My reading of the PAM docs is that, once pam_chauthtok() has been
dd84935
+	   called with PAM_UPDATE_AUTHTOK, we are obliged to take any
dd84935
+	   reasonable steps to make sure the token is updated; so retrying
dd84935
+	   for 1/10 sec. isn't overdoing it. */
dd84935
+	i=0;
dd84935
+	while((retval = lckpwdf()) != 0 && i < 100) {
dd84935
+		usleep(1000);
dd84935
+		i++;
dd84935
+	}
dd84935
+	if(retval != 0) {
dd84935
+		return PAM_AUTHTOK_LOCK_BUSY;
dd84935
+	}
dd84935
+	return PAM_SUCCESS;
dd84935
+}
dd84935
+
dd84935
+static void unlock_pwdf(void)
dd84935
+{
dd84935
+#ifndef HELPER_COMPILE
dd84935
+	if (selinux_confined()) {
dd84935
+		return;
dd84935
+	}
dd84935
+#endif
dd84935
+	ulckpwdf();
dd84935
+}
dd84935
+#endif
dd84935
+
dd84935
+static int
dd84935
+save_old_password(const char *forwho, const char *oldpass,
dd84935
+		  int howmany)
dd84935
+{
dd84935
+    static char buf[16384];
dd84935
+    static char nbuf[16384];
dd84935
+    char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
dd84935
+    int npas;
dd84935
+    FILE *pwfile, *opwfile;
dd84935
+    int err = 0;
dd84935
+    int oldmask;
dd84935
+    int found = 0;
dd84935
+    struct passwd *pwd = NULL;
dd84935
+    struct stat st;
dd84935
+
dd84935
+    if (howmany < 0) {
dd84935
+	return PAM_SUCCESS;
dd84935
+    }
dd84935
+
dd84935
+    if (oldpass == NULL) {
dd84935
+	return PAM_SUCCESS;
dd84935
+    }
dd84935
+
dd84935
+    oldmask = umask(077);
dd84935
+
dd84935
+#ifdef WITH_SELINUX
dd84935
+    if (SELINUX_ENABLED) {
dd84935
+      security_context_t passwd_context=NULL;
dd84935
+      if (getfilecon("/etc/passwd",&passwd_context)<0) {
dd84935
+        return PAM_AUTHTOK_ERR;
dd84935
+      };
dd84935
+      if (getfscreatecon(&prev_context)<0) {
dd84935
+        freecon(passwd_context);
dd84935
+        return PAM_AUTHTOK_ERR;
dd84935
+      }
dd84935
+      if (setfscreatecon(passwd_context)) {
dd84935
+        freecon(passwd_context);
dd84935
+        freecon(prev_context);
dd84935
+        return PAM_AUTHTOK_ERR;
dd84935
+      }
dd84935
+      freecon(passwd_context);
dd84935
+    }
dd84935
+#endif
dd84935
+    pwfile = fopen(OPW_TMPFILE, "w");
dd84935
+    umask(oldmask);
dd84935
+    if (pwfile == NULL) {
dd84935
+      err = 1;
dd84935
+      goto done;
dd84935
+    }
dd84935
+
dd84935
+    opwfile = fopen(OLD_PASSWORDS_FILE, "r");
dd84935
+    if (opwfile == NULL) {
dd84935
+	fclose(pwfile);
dd84935
+      err = 1;
dd84935
+      goto done;
dd84935
+    }
dd84935
+
dd84935
+    if (fstat(fileno(opwfile), &st) == -1) {
dd84935
+	fclose(opwfile);
dd84935
+	fclose(pwfile);
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+
dd84935
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
dd84935
+	fclose(opwfile);
dd84935
+	fclose(pwfile);
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
dd84935
+	fclose(opwfile);
dd84935
+	fclose(pwfile);
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+
dd84935
+    while (fgets(buf, 16380, opwfile)) {
dd84935
+	if (!strncmp(buf, forwho, strlen(forwho))) {
dd84935
+	    char *sptr = NULL;
dd84935
+	    found = 1;
dd84935
+	    if (howmany == 0)
dd84935
+	    	continue;
dd84935
+	    buf[strlen(buf) - 1] = '\0';
dd84935
+	    s_luser = strtok_r(buf, ":", &sptr);
dd84935
+	    s_uid = strtok_r(NULL, ":", &sptr);
dd84935
+	    s_npas = strtok_r(NULL, ":", &sptr);
dd84935
+	    s_pas = strtok_r(NULL, ":", &sptr);
dd84935
+	    npas = strtol(s_npas, NULL, 10) + 1;
dd84935
+	    while (npas > howmany) {
dd84935
+		s_pas = strpbrk(s_pas, ",");
dd84935
+		if (s_pas != NULL)
dd84935
+		    s_pas++;
dd84935
+		npas--;
dd84935
+	    }
dd84935
+	    pass = crypt_md5_wrapper(oldpass);
dd84935
+	    if (s_pas == NULL)
dd84935
+		snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
dd84935
+			 s_luser, s_uid, npas, pass);
dd84935
+	    else
dd84935
+		snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
dd84935
+			 s_luser, s_uid, npas, s_pas, pass);
dd84935
+	    _pam_delete(pass);
dd84935
+	    if (fputs(nbuf, pwfile) < 0) {
dd84935
+		err = 1;
dd84935
+		break;
dd84935
+	    }
dd84935
+	} else if (fputs(buf, pwfile) < 0) {
dd84935
+	    err = 1;
dd84935
+	    break;
dd84935
+	}
dd84935
+    }
dd84935
+    fclose(opwfile);
dd84935
+
dd84935
+    if (!found) {
dd84935
+	pwd = getpwnam(forwho);
dd84935
+	if (pwd == NULL) {
dd84935
+	    err = 1;
dd84935
+	} else {
dd84935
+	    pass = crypt_md5_wrapper(oldpass);
dd84935
+	    snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
dd84935
+		     forwho, (unsigned long)pwd->pw_uid, pass);
dd84935
+	    _pam_delete(pass);
dd84935
+	    if (fputs(nbuf, pwfile) < 0) {
dd84935
+		err = 1;
dd84935
+	    }
dd84935
+	}
dd84935
+    }
dd84935
+
dd84935
+    if (fclose(pwfile)) {
dd84935
+	D(("error writing entries to old passwords file: %m"));
dd84935
+	err = 1;
dd84935
+    }
dd84935
+
dd84935
+done:
dd84935
+    if (!err) {
dd84935
+	if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
dd84935
+	    err = 1;
dd84935
+    }
dd84935
+#ifdef WITH_SELINUX
dd84935
+    if (SELINUX_ENABLED) {
dd84935
+      if (setfscreatecon(prev_context)) {
dd84935
+        err = 1;
dd84935
+      }
dd84935
+      if (prev_context)
dd84935
+        freecon(prev_context);
dd84935
+      prev_context=NULL;
dd84935
+    }
dd84935
+#endif
dd84935
+    if (!err) {
dd84935
+	return PAM_SUCCESS;
dd84935
+    } else {
dd84935
+	unlink(OPW_TMPFILE);
dd84935
+	return PAM_AUTHTOK_ERR;
dd84935
+    }
dd84935
+}
dd84935
+
dd84935
+#ifdef HELPER_COMPILE
dd84935
+static int
dd84935
+_update_passwd(const char *forwho, const char *towhat)
dd84935
+#else
dd84935
+static int
dd84935
+_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat)
dd84935
+#endif
dd84935
+{
dd84935
+    struct passwd *tmpent = NULL;
dd84935
+    struct stat st;
dd84935
+    FILE *pwfile, *opwfile;
dd84935
+    int err = 1;
dd84935
+    int oldmask;
dd84935
+
dd84935
+    oldmask = umask(077);
dd84935
+#ifdef WITH_SELINUX
dd84935
+    if (SELINUX_ENABLED) {
dd84935
+      security_context_t passwd_context=NULL;
dd84935
+      if (getfilecon("/etc/passwd",&passwd_context)<0) {
dd84935
+	return PAM_AUTHTOK_ERR;
dd84935
+      };
dd84935
+      if (getfscreatecon(&prev_context)<0) {
dd84935
+	freecon(passwd_context);
dd84935
+	return PAM_AUTHTOK_ERR;
dd84935
+      }
dd84935
+      if (setfscreatecon(passwd_context)) {
dd84935
+	freecon(passwd_context);
dd84935
+	freecon(prev_context);
dd84935
+	return PAM_AUTHTOK_ERR;
dd84935
+      }
dd84935
+      freecon(passwd_context);
dd84935
+    }
dd84935
+#endif
dd84935
+    pwfile = fopen(PW_TMPFILE, "w");
dd84935
+    umask(oldmask);
dd84935
+    if (pwfile == NULL) {
dd84935
+      err = 1;
dd84935
+      goto done;
dd84935
+    }
dd84935
+
dd84935
+    opwfile = fopen("/etc/passwd", "r");
dd84935
+    if (opwfile == NULL) {
dd84935
+	fclose(pwfile);
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+
dd84935
+    if (fstat(fileno(opwfile), &st) == -1) {
dd84935
+	fclose(opwfile);
dd84935
+	fclose(pwfile);
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+
dd84935
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
dd84935
+	fclose(opwfile);
dd84935
+	fclose(pwfile);
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
dd84935
+	fclose(opwfile);
dd84935
+	fclose(pwfile);
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+
dd84935
+    tmpent = fgetpwent(opwfile);
dd84935
+    while (tmpent) {
dd84935
+	if (!strcmp(tmpent->pw_name, forwho)) {
dd84935
+	    /* To shut gcc up */
dd84935
+	    union {
dd84935
+		const char *const_charp;
dd84935
+		char *charp;
dd84935
+	    } assigned_passwd;
dd84935
+	    assigned_passwd.const_charp = towhat;
dd84935
+
dd84935
+	    tmpent->pw_passwd = assigned_passwd.charp;
dd84935
+	    err = 0;
dd84935
+	}
dd84935
+	if (putpwent(tmpent, pwfile)) {
dd84935
+	    D(("error writing entry to password file: %m"));
dd84935
+	    err = 1;
dd84935
+	    break;
dd84935
+	}
dd84935
+	tmpent = fgetpwent(opwfile);
dd84935
+    }
dd84935
+    fclose(opwfile);
dd84935
+
dd84935
+    if (fclose(pwfile)) {
dd84935
+	D(("error writing entries to password file: %m"));
dd84935
+	err = 1;
dd84935
+    }
dd84935
+
dd84935
+done:
dd84935
+    if (!err) {
dd84935
+	if (!rename(PW_TMPFILE, "/etc/passwd"))
dd84935
+#ifdef HELPER_COMPILE
dd84935
+	    _log_err(
dd84935
+#else
dd84935
+	    pam_syslog(pamh, 
dd84935
+#endif
dd84935
+		LOG_NOTICE, "password changed for %s", forwho);
dd84935
+	else
dd84935
+	    err = 1;
dd84935
+    }
dd84935
+#ifdef WITH_SELINUX
dd84935
+    if (SELINUX_ENABLED) {
dd84935
+      if (setfscreatecon(prev_context)) {
dd84935
+	err = 1;
dd84935
+      }
dd84935
+      if (prev_context)
dd84935
+	freecon(prev_context);
dd84935
+      prev_context=NULL;
dd84935
+    }
dd84935
+#endif
dd84935
+    if (!err) {
dd84935
+	return PAM_SUCCESS;
dd84935
+    } else {
dd84935
+	unlink(PW_TMPFILE);
dd84935
+	return PAM_AUTHTOK_ERR;
dd84935
+    }
dd84935
+}
dd84935
+
dd84935
+#ifdef HELPER_COMPILE
dd84935
+static int
dd84935
+_update_shadow(const char *forwho, char *towhat)
dd84935
+#else
dd84935
+static int
dd84935
+_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
dd84935
+#endif
dd84935
+{
dd84935
+    struct spwd *spwdent = NULL, *stmpent = NULL;
dd84935
+    struct stat st;
dd84935
+    FILE *pwfile, *opwfile;
dd84935
+    int err = 1;
dd84935
+    int oldmask;
dd84935
+
dd84935
+    spwdent = getspnam(forwho);
dd84935
+    if (spwdent == NULL) {
dd84935
+	return PAM_USER_UNKNOWN;
dd84935
+    }
dd84935
+    oldmask = umask(077);
dd84935
+
dd84935
+#ifdef WITH_SELINUX
dd84935
+    if (SELINUX_ENABLED) {
dd84935
+      security_context_t shadow_context=NULL;
dd84935
+      if (getfilecon("/etc/shadow",&shadow_context)<0) {
dd84935
+	return PAM_AUTHTOK_ERR;
dd84935
+      };
dd84935
+      if (getfscreatecon(&prev_context)<0) {
dd84935
+	freecon(shadow_context);
dd84935
+	return PAM_AUTHTOK_ERR;
dd84935
+      }
dd84935
+      if (setfscreatecon(shadow_context)) {
dd84935
+	freecon(shadow_context);
dd84935
+	freecon(prev_context);
dd84935
+	return PAM_AUTHTOK_ERR;
dd84935
+      }
dd84935
+      freecon(shadow_context);
dd84935
+    }
dd84935
+#endif
dd84935
+    pwfile = fopen(SH_TMPFILE, "w");
dd84935
+    umask(oldmask);
dd84935
+    if (pwfile == NULL) {
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+
dd84935
+    opwfile = fopen("/etc/shadow", "r");
dd84935
+    if (opwfile == NULL) {
dd84935
+	fclose(pwfile);
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+
dd84935
+    if (fstat(fileno(opwfile), &st) == -1) {
dd84935
+	fclose(opwfile);
dd84935
+	fclose(pwfile);
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+
dd84935
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
dd84935
+	fclose(opwfile);
dd84935
+	fclose(pwfile);
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
dd84935
+	fclose(opwfile);
dd84935
+	fclose(pwfile);
dd84935
+	err = 1;
dd84935
+	goto done;
dd84935
+    }
dd84935
+
dd84935
+    stmpent = fgetspent(opwfile);
dd84935
+    while (stmpent) {
dd84935
+
dd84935
+	if (!strcmp(stmpent->sp_namp, forwho)) {
dd84935
+	    stmpent->sp_pwdp = towhat;
dd84935
+	    stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
dd84935
+	    err = 0;
dd84935
+	    D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
dd84935
+	}
dd84935
+
dd84935
+	if (putspent(stmpent, pwfile)) {
dd84935
+	    D(("error writing entry to shadow file: %m"));
dd84935
+	    err = 1;
dd84935
+	    break;
dd84935
+	}
dd84935
+
dd84935
+	stmpent = fgetspent(opwfile);
dd84935
+    }
dd84935
+    fclose(opwfile);
dd84935
+
dd84935
+    if (fclose(pwfile)) {
dd84935
+	D(("error writing entries to shadow file: %m"));
dd84935
+	err = 1;
dd84935
+    }
dd84935
+
dd84935
+ done:
dd84935
+    if (!err) {
dd84935
+	if (!rename(SH_TMPFILE, "/etc/shadow"))
dd84935
+#ifdef HELPER_COMPILE
dd84935
+	    _log_err(
dd84935
+#else
dd84935
+	    pam_syslog(pamh, 
dd84935
+#endif
dd84935
+		LOG_NOTICE, "password changed for %s", forwho);
dd84935
+	else
dd84935
+	    err = 1;
dd84935
+    }
dd84935
+
dd84935
+#ifdef WITH_SELINUX
dd84935
+    if (SELINUX_ENABLED) {
dd84935
+      if (setfscreatecon(prev_context)) {
dd84935
+	err = 1;
dd84935
+      }
dd84935
+      if (prev_context)
dd84935
+	freecon(prev_context);
dd84935
+      prev_context=NULL;
dd84935
+    }
dd84935
+#endif
dd84935
+
dd84935
+    if (!err) {
dd84935
+	return PAM_SUCCESS;
dd84935
+    } else {
dd84935
+	unlink(SH_TMPFILE);
dd84935
+	return PAM_AUTHTOK_ERR;
dd84935
+    }
dd84935
+}
dd84935
--- Linux-PAM-0.99.7.1/modules/pam_unix/pam_unix_acct.c.update-helper	2006-06-27 10:38:14.000000000 +0200
dd84935
+++ Linux-PAM-0.99.7.1/modules/pam_unix/pam_unix_acct.c	2007-06-01 15:13:57.000000000 +0200
dd84935
@@ -124,11 +124,11 @@
dd84935
     }
dd84935
 
dd84935
     /* exec binary helper */
dd84935
-    args[0] = x_strdup(CHKPWD_HELPER);
dd84935
+    args[0] = x_strdup(UPDATE_HELPER);
dd84935
     args[1] = x_strdup(user);
dd84935
     args[2] = x_strdup("verify");
dd84935
 
dd84935
-    execve(CHKPWD_HELPER, args, envp);
dd84935
+    execve(UPDATE_HELPER, args, envp);
dd84935
 
dd84935
     pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %m");
dd84935
     /* should not get here: exit with error */
dd84935
@@ -142,11 +142,11 @@
dd84935
       int rc=0;
dd84935
       rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
dd84935
       if (rc<0) {
dd84935
-	pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
dd84935
+	pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
dd84935
 	retval = PAM_AUTH_ERR;
dd84935
       } else {
dd84935
 	retval = WEXITSTATUS(retval);
dd84935
-	if (retval != PAM_AUTHINFO_UNAVAIL) {
dd84935
+	if (retval == PAM_SUCCESS) {
dd84935
           rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
dd84935
 	  if(rc > 0) {
dd84935
 	      buf[rc] = '\0';
dd84935
@@ -157,15 +157,15 @@
dd84935
 		     &spwd.sp_warn, /* days warning for expiration */
dd84935
 		     &spwd.sp_inact, /* days before account inactive */
dd84935
 		     &spwd.sp_expire) /* date when account expires */ != 6 ) retval = PAM_AUTH_ERR;
dd84935
-	    }
dd84935
-	  else {
dd84935
-	    pam_syslog(pamh, LOG_ERR, " ERROR %d: %m", rc); retval = PAM_AUTH_ERR;
dd84935
+    } else {
dd84935
+	    pam_syslog(pamh, LOG_ERR, "read failed: %m"); retval = PAM_AUTH_ERR;
dd84935
 	  }
dd84935
+	} else {
dd84935
+	    pam_syslog(pamh, LOG_ERR, "unix_update returned error %d", retval);
dd84935
 	}
dd84935
       }
dd84935
     } else {
dd84935
-      pam_syslog(pamh, LOG_ERR, "Fork failed: %m");
dd84935
-      D(("fork failed"));
dd84935
+      pam_syslog(pamh, LOG_ERR, "fork failed: %m");
dd84935
       retval = PAM_AUTH_ERR;
dd84935
     }
dd84935
     close(fds[0]);
dd84935
@@ -247,6 +247,8 @@
dd84935
 			setreuid( -1, save_euid );
dd84935
 		}
dd84935
 
dd84935
+	} else if (geteuid() != 0) { /* cannot read shadow when non root */
dd84935
+		return PAM_IGNORE;
dd84935
 	} else if (_unix_shadowed (pwent))
dd84935
 		spent = pam_modutil_getspnam (pamh, uname);
dd84935
 	else
dd84935
--- Linux-PAM-0.99.7.1/modules/pam_unix/pam_unix_passwd.c.update-helper	2007-06-01 15:13:57.000000000 +0200
dd84935
+++ Linux-PAM-0.99.7.1/modules/pam_unix/pam_unix_passwd.c	2007-06-01 15:13:57.000000000 +0200
dd84935
@@ -2,6 +2,7 @@
dd84935
  * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
dd84935
  * Copyright (C) 1996.
dd84935
  * Copyright (c) Jan RÍkorajski, 1999.
dd84935
+ * Copyright (c) Red Hat, Inc., 2007.
dd84935
  *
dd84935
  * Redistribution and use in source and binary forms, with or without
dd84935
  * modification, are permitted provided that the following conditions
dd84935
@@ -92,15 +93,6 @@
dd84935
 #endif				/* GNU libc 2.1 */
dd84935
 
dd84935
 /*
dd84935
- * PAM framework looks for these entry-points to pass control to the
dd84935
- * password changing module.
dd84935
- */
dd84935
-
dd84935
-#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
dd84935
-# include "./lckpwdf.-c"
dd84935
-#endif
dd84935
-
dd84935
-/*
dd84935
    How it works:
dd84935
    Gets in username (has to be done) from the calling program
dd84935
    Does authentication of user (only if we are not running as root)
dd84935
@@ -108,82 +100,15 @@
dd84935
    Sets it.
dd84935
  */
dd84935
 
dd84935
-/* passwd/salt conversion macros */
dd84935
-
dd84935
-#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
dd84935
-#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
dd84935
-
dd84935
 /* data tokens */
dd84935
 
dd84935
 #define _UNIX_OLD_AUTHTOK	"-UN*X-OLD-PASS"
dd84935
 #define _UNIX_NEW_AUTHTOK	"-UN*X-NEW-PASS"
dd84935
 
dd84935
 #define MAX_PASSWD_TRIES	3
dd84935
-#define PW_TMPFILE		"/etc/npasswd"
dd84935
-#define SH_TMPFILE		"/etc/nshadow"
dd84935
 #ifndef CRACKLIB_DICTS
dd84935
 #define CRACKLIB_DICTS		NULL
dd84935
 #endif
dd84935
-#define OPW_TMPFILE		"/etc/security/nopasswd"
dd84935
-#define OLD_PASSWORDS_FILE	"/etc/security/opasswd"
dd84935
-
dd84935
-/*
dd84935
- * i64c - convert an integer to a radix 64 character
dd84935
- */
dd84935
-static int i64c(int i)
dd84935
-{
dd84935
-	if (i < 0)
dd84935
-		return ('.');
dd84935
-	else if (i > 63)
dd84935
-		return ('z');
dd84935
-	if (i == 0)
dd84935
-		return ('.');
dd84935
-	if (i == 1)
dd84935
-		return ('/');
dd84935
-	if (i >= 2 && i <= 11)
dd84935
-		return ('0' - 2 + i);
dd84935
-	if (i >= 12 && i <= 37)
dd84935
-		return ('A' - 12 + i);
dd84935
-	if (i >= 38 && i <= 63)
dd84935
-		return ('a' - 38 + i);
dd84935
-	return ('\0');
dd84935
-}
dd84935
-
dd84935
-static char *crypt_md5_wrapper(const char *pass_new)
dd84935
-{
dd84935
-	/*
dd84935
-	 * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
dd84935
-	 * removed use of static variables (AGM)
dd84935
-	 */
dd84935
-
dd84935
-	struct timeval tv;
dd84935
-	MD5_CTX ctx;
dd84935
-	unsigned char result[16];
dd84935
-	char *cp = (char *) result;
dd84935
-	unsigned char tmp[16];
dd84935
-	int i;
dd84935
-	char *x = NULL;
dd84935
-
dd84935
-	GoodMD5Init(&ctx;;
dd84935
-	gettimeofday(&tv, (struct timezone *) 0);
dd84935
-	GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
dd84935
-	i = getpid();
dd84935
-	GoodMD5Update(&ctx, (void *) &i, sizeof i);
dd84935
-	i = clock();
dd84935
-	GoodMD5Update(&ctx, (void *) &i, sizeof i);
dd84935
-	GoodMD5Update(&ctx, result, sizeof result);
dd84935
-	GoodMD5Final(tmp, &ctx;;
dd84935
-	strcpy(cp, "$1$");	/* magic for the MD5 */
dd84935
-	cp += strlen(cp);
dd84935
-	for (i = 0; i < 8; i++)
dd84935
-		*cp++ = i64c(tmp[i] & 077);
dd84935
-	*cp = '\0';
dd84935
-
dd84935
-	/* no longer need cleartext */
dd84935
-	x = Goodcrypt_md5(pass_new, (const char *) result);
dd84935
-
dd84935
-	return x;
dd84935
-}
dd84935
 
dd84935
 static char *getNISserver(pam_handle_t *pamh)
dd84935
 {
dd84935
@@ -217,7 +142,8 @@
dd84935
 
dd84935
 #ifdef WITH_SELINUX
dd84935
 
dd84935
-static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user, const char *fromwhat, const char *towhat)
dd84935
+static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user,
dd84935
+    const char *fromwhat, const char *towhat, int remember)
dd84935
 {
dd84935
     int retval, child, fds[2];
dd84935
     void (*sighandler)(int) = NULL;
dd84935
@@ -247,7 +173,8 @@
dd84935
         size_t i=0;
dd84935
         struct rlimit rlim;
dd84935
 	static char *envp[] = { NULL };
dd84935
-	char *args[] = { NULL, NULL, NULL, NULL };
dd84935
+	char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
dd84935
+        char buffer[16];
dd84935
 
dd84935
 	/* XXX - should really tidy up PAM here too */
dd84935
 
dd84935
@@ -270,11 +197,18 @@
dd84935
         }
dd84935
 
dd84935
 	/* exec binary helper */
dd84935
-	args[0] = x_strdup(CHKPWD_HELPER);
dd84935
+	args[0] = x_strdup(UPDATE_HELPER);
dd84935
 	args[1] = x_strdup(user);
dd84935
-	args[2] = x_strdup("shadow");
dd84935
+	args[2] = x_strdup("update");
dd84935
+	if (on(UNIX_SHADOW, ctrl))
dd84935
+		args[3] = x_strdup("1");
dd84935
+	else
dd84935
+		args[3] = x_strdup("0");
dd84935
 
dd84935
-	execve(CHKPWD_HELPER, args, envp);
dd84935
+        snprintf(buffer, sizeof(buffer), "%d", remember);
dd84935
+        args[4] = x_strdup(buffer);
dd84935
+	
dd84935
+	execve(UPDATE_HELPER, args, envp);
dd84935
 
dd84935
 	/* should not get here: exit with error */
dd84935
 	D(("helper binary is not available"));
dd84935
@@ -297,7 +231,7 @@
dd84935
 	close(fds[1]);
dd84935
 	rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
dd84935
 	if (rc<0) {
dd84935
-	  pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
dd84935
+	  pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
dd84935
 	  retval = PAM_AUTH_ERR;
dd84935
 	} else {
dd84935
 	  retval = WEXITSTATUS(retval);
dd84935
@@ -315,8 +249,56 @@
dd84935
 
dd84935
     return retval;
dd84935
 }
dd84935
+
dd84935
+static int selinux_confined(void)
dd84935
+{
dd84935
+    static int confined = -1;
dd84935
+    int fd;
dd84935
+    char tempfile[]="/etc/.pwdXXXXXX";
dd84935
+
dd84935
+    if (confined != -1)
dd84935
+    	return confined;
dd84935
+
dd84935
+    /* cannot be confined without SELinux enabled */
dd84935
+    if (!SELINUX_ENABLED){
dd84935
+       	confined = 0;
dd84935
+       	return confined;
dd84935
+    }
dd84935
+    
dd84935
+    /* let's try opening shadow read only */
dd84935
+    if ((fd=open("/etc/shadow", O_RDONLY)) != -1) {
dd84935
+        close(fd);
dd84935
+        confined = 0;
dd84935
+        return confined;
dd84935
+    }
dd84935
+
dd84935
+    if (errno == EACCES) {
dd84935
+	confined = 1;
dd84935
+	return confined;
dd84935
+    }
dd84935
+    
dd84935
+    /* shadow opening failed because of other reasons let's try 
dd84935
+       creating a file in /etc */
dd84935
+    if ((fd=mkstemp(tempfile)) != -1) {
dd84935
+        unlink(tempfile);
dd84935
+        close(fd);
dd84935
+        confined = 0;
dd84935
+        return confined;
dd84935
+    }
dd84935
+    
dd84935
+    confined = 1;
dd84935
+    return confined;
dd84935
+}
dd84935
+
dd84935
+#else
dd84935
+static int selinux_confined(void)
dd84935
+{
dd84935
+    return 0;
dd84935
+}
dd84935
 #endif
dd84935
 
dd84935
+#include "passupdate.c"
dd84935
+
dd84935
 static int check_old_password(const char *forwho, const char *newpass)
dd84935
 {
dd84935
 	static char buf[16384];
dd84935
@@ -353,392 +335,6 @@
dd84935
 	return retval;
dd84935
 }
dd84935
 
dd84935
-static int save_old_password(pam_handle_t *pamh,
dd84935
-			     const char *forwho, const char *oldpass,
dd84935
-			     int howmany)
dd84935
-{
dd84935
-    static char buf[16384];
dd84935
-    static char nbuf[16384];
dd84935
-    char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
dd84935
-    int npas;
dd84935
-    FILE *pwfile, *opwfile;
dd84935
-    int err = 0;
dd84935
-    int oldmask;
dd84935
-    int found = 0;
dd84935
-    struct passwd *pwd = NULL;
dd84935
-    struct stat st;
dd84935
-
dd84935
-    if (howmany < 0) {
dd84935
-	return PAM_SUCCESS;
dd84935
-    }
dd84935
-
dd84935
-    if (oldpass == NULL) {
dd84935
-	return PAM_SUCCESS;
dd84935
-    }
dd84935
-
dd84935
-    oldmask = umask(077);
dd84935
-
dd84935
-#ifdef WITH_SELINUX
dd84935
-    if (SELINUX_ENABLED) {
dd84935
-      security_context_t passwd_context=NULL;
dd84935
-      if (getfilecon("/etc/passwd",&passwd_context)<0) {
dd84935
-        return PAM_AUTHTOK_ERR;
dd84935
-      };
dd84935
-      if (getfscreatecon(&prev_context)<0) {
dd84935
-        freecon(passwd_context);
dd84935
-        return PAM_AUTHTOK_ERR;
dd84935
-      }
dd84935
-      if (setfscreatecon(passwd_context)) {
dd84935
-        freecon(passwd_context);
dd84935
-        freecon(prev_context);
dd84935
-        return PAM_AUTHTOK_ERR;
dd84935
-      }
dd84935
-      freecon(passwd_context);
dd84935
-    }
dd84935
-#endif
dd84935
-    pwfile = fopen(OPW_TMPFILE, "w");
dd84935
-    umask(oldmask);
dd84935
-    if (pwfile == NULL) {
dd84935
-      err = 1;
dd84935
-      goto done;
dd84935
-    }
dd84935
-
dd84935
-    opwfile = fopen(OLD_PASSWORDS_FILE, "r");
dd84935
-    if (opwfile == NULL) {
dd84935
-	fclose(pwfile);
dd84935
-      err = 1;
dd84935
-      goto done;
dd84935
-    }
dd84935
-
dd84935
-    if (fstat(fileno(opwfile), &st) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    while (fgets(buf, 16380, opwfile)) {
dd84935
-	if (!strncmp(buf, forwho, strlen(forwho))) {
dd84935
-	    buf[strlen(buf) - 1] = '\0';
dd84935
-	    s_luser = strtok(buf, ":");
dd84935
-	    s_uid = strtok(NULL, ":");
dd84935
-	    s_npas = strtok(NULL, ":");
dd84935
-	    s_pas = strtok(NULL, ":");
dd84935
-	    npas = strtol(s_npas, NULL, 10) + 1;
dd84935
-	    while (npas > howmany) {
dd84935
-		s_pas = strpbrk(s_pas, ",");
dd84935
-		if (s_pas != NULL)
dd84935
-		    s_pas++;
dd84935
-		npas--;
dd84935
-	    }
dd84935
-	    pass = crypt_md5_wrapper(oldpass);
dd84935
-	    if (s_pas == NULL)
dd84935
-		snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
dd84935
-			 s_luser, s_uid, npas, pass);
dd84935
-	    else
dd84935
-		snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
dd84935
-			 s_luser, s_uid, npas, s_pas, pass);
dd84935
-	    _pam_delete(pass);
dd84935
-	    if (fputs(nbuf, pwfile) < 0) {
dd84935
-		err = 1;
dd84935
-		break;
dd84935
-	    }
dd84935
-	    found = 1;
dd84935
-	} else if (fputs(buf, pwfile) < 0) {
dd84935
-	    err = 1;
dd84935
-	    break;
dd84935
-	}
dd84935
-    }
dd84935
-    fclose(opwfile);
dd84935
-
dd84935
-    if (!found) {
dd84935
-	pwd = pam_modutil_getpwnam(pamh, forwho);
dd84935
-	if (pwd == NULL) {
dd84935
-	    err = 1;
dd84935
-	} else {
dd84935
-	    pass = crypt_md5_wrapper(oldpass);
dd84935
-	    snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
dd84935
-		     forwho, (unsigned long)pwd->pw_uid, pass);
dd84935
-	    _pam_delete(pass);
dd84935
-	    if (fputs(nbuf, pwfile) < 0) {
dd84935
-		err = 1;
dd84935
-	    }
dd84935
-	}
dd84935
-    }
dd84935
-
dd84935
-    if (fclose(pwfile)) {
dd84935
-	D(("error writing entries to old passwords file: %m"));
dd84935
-	err = 1;
dd84935
-    }
dd84935
-
dd84935
-done:
dd84935
-    if (!err) {
dd84935
-	if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
dd84935
-	    err = 1;
dd84935
-    }
dd84935
-#ifdef WITH_SELINUX
dd84935
-    if (SELINUX_ENABLED) {
dd84935
-      if (setfscreatecon(prev_context)) {
dd84935
-        err = 1;
dd84935
-      }
dd84935
-      if (prev_context)
dd84935
-        freecon(prev_context);
dd84935
-      prev_context=NULL;
dd84935
-    }
dd84935
-#endif
dd84935
-    if (!err) {
dd84935
-	return PAM_SUCCESS;
dd84935
-    } else {
dd84935
-	unlink(OPW_TMPFILE);
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-    }
dd84935
-}
dd84935
-
dd84935
-static int _update_passwd(pam_handle_t *pamh,
dd84935
-			  const char *forwho, const char *towhat)
dd84935
-{
dd84935
-    struct passwd *tmpent = NULL;
dd84935
-    struct stat st;
dd84935
-    FILE *pwfile, *opwfile;
dd84935
-    int err = 1;
dd84935
-    int oldmask;
dd84935
-
dd84935
-    oldmask = umask(077);
dd84935
-#ifdef WITH_SELINUX
dd84935
-    if (SELINUX_ENABLED) {
dd84935
-      security_context_t passwd_context=NULL;
dd84935
-      if (getfilecon("/etc/passwd",&passwd_context)<0) {
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-      };
dd84935
-      if (getfscreatecon(&prev_context)<0) {
dd84935
-	freecon(passwd_context);
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-      }
dd84935
-      if (setfscreatecon(passwd_context)) {
dd84935
-	freecon(passwd_context);
dd84935
-	freecon(prev_context);
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-      }
dd84935
-      freecon(passwd_context);
dd84935
-    }
dd84935
-#endif
dd84935
-    pwfile = fopen(PW_TMPFILE, "w");
dd84935
-    umask(oldmask);
dd84935
-    if (pwfile == NULL) {
dd84935
-      err = 1;
dd84935
-      goto done;
dd84935
-    }
dd84935
-
dd84935
-    opwfile = fopen("/etc/passwd", "r");
dd84935
-    if (opwfile == NULL) {
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    if (fstat(fileno(opwfile), &st) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    tmpent = fgetpwent(opwfile);
dd84935
-    while (tmpent) {
dd84935
-	if (!strcmp(tmpent->pw_name, forwho)) {
dd84935
-	    /* To shut gcc up */
dd84935
-	    union {
dd84935
-		const char *const_charp;
dd84935
-		char *charp;
dd84935
-	    } assigned_passwd;
dd84935
-	    assigned_passwd.const_charp = towhat;
dd84935
-
dd84935
-	    tmpent->pw_passwd = assigned_passwd.charp;
dd84935
-	    err = 0;
dd84935
-	}
dd84935
-	if (putpwent(tmpent, pwfile)) {
dd84935
-	    D(("error writing entry to password file: %m"));
dd84935
-	    err = 1;
dd84935
-	    break;
dd84935
-	}
dd84935
-	tmpent = fgetpwent(opwfile);
dd84935
-    }
dd84935
-    fclose(opwfile);
dd84935
-
dd84935
-    if (fclose(pwfile)) {
dd84935
-	D(("error writing entries to password file: %m"));
dd84935
-	err = 1;
dd84935
-    }
dd84935
-
dd84935
-done:
dd84935
-    if (!err) {
dd84935
-	if (!rename(PW_TMPFILE, "/etc/passwd"))
dd84935
-	    pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
dd84935
-	else
dd84935
-	    err = 1;
dd84935
-    }
dd84935
-#ifdef WITH_SELINUX
dd84935
-    if (SELINUX_ENABLED) {
dd84935
-      if (setfscreatecon(prev_context)) {
dd84935
-	err = 1;
dd84935
-      }
dd84935
-      if (prev_context)
dd84935
-	freecon(prev_context);
dd84935
-      prev_context=NULL;
dd84935
-    }
dd84935
-#endif
dd84935
-    if (!err) {
dd84935
-	return PAM_SUCCESS;
dd84935
-    } else {
dd84935
-	unlink(PW_TMPFILE);
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-    }
dd84935
-}
dd84935
-
dd84935
-static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
dd84935
-{
dd84935
-    struct spwd *spwdent = NULL, *stmpent = NULL;
dd84935
-    struct stat st;
dd84935
-    FILE *pwfile, *opwfile;
dd84935
-    int err = 1;
dd84935
-    int oldmask;
dd84935
-
dd84935
-    spwdent = getspnam(forwho);
dd84935
-    if (spwdent == NULL) {
dd84935
-	return PAM_USER_UNKNOWN;
dd84935
-    }
dd84935
-    oldmask = umask(077);
dd84935
-
dd84935
-#ifdef WITH_SELINUX
dd84935
-    if (SELINUX_ENABLED) {
dd84935
-      security_context_t shadow_context=NULL;
dd84935
-      if (getfilecon("/etc/shadow",&shadow_context)<0) {
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-      };
dd84935
-      if (getfscreatecon(&prev_context)<0) {
dd84935
-	freecon(shadow_context);
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-      }
dd84935
-      if (setfscreatecon(shadow_context)) {
dd84935
-	freecon(shadow_context);
dd84935
-	freecon(prev_context);
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-      }
dd84935
-      freecon(shadow_context);
dd84935
-    }
dd84935
-#endif
dd84935
-    pwfile = fopen(SH_TMPFILE, "w");
dd84935
-    umask(oldmask);
dd84935
-    if (pwfile == NULL) {
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    opwfile = fopen("/etc/shadow", "r");
dd84935
-    if (opwfile == NULL) {
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    if (fstat(fileno(opwfile), &st) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    stmpent = fgetspent(opwfile);
dd84935
-    while (stmpent) {
dd84935
-
dd84935
-	if (!strcmp(stmpent->sp_namp, forwho)) {
dd84935
-	    stmpent->sp_pwdp = towhat;
dd84935
-	    stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
dd84935
-	    err = 0;
dd84935
-	    D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
dd84935
-	}
dd84935
-
dd84935
-	if (putspent(stmpent, pwfile)) {
dd84935
-	    D(("error writing entry to shadow file: %m"));
dd84935
-	    err = 1;
dd84935
-	    break;
dd84935
-	}
dd84935
-
dd84935
-	stmpent = fgetspent(opwfile);
dd84935
-    }
dd84935
-    fclose(opwfile);
dd84935
-
dd84935
-    if (fclose(pwfile)) {
dd84935
-	D(("error writing entries to shadow file: %m"));
dd84935
-	err = 1;
dd84935
-    }
dd84935
-
dd84935
- done:
dd84935
-    if (!err) {
dd84935
-	if (!rename(SH_TMPFILE, "/etc/shadow"))
dd84935
-	    pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
dd84935
-	else
dd84935
-	    err = 1;
dd84935
-    }
dd84935
-
dd84935
-#ifdef WITH_SELINUX
dd84935
-    if (SELINUX_ENABLED) {
dd84935
-      if (setfscreatecon(prev_context)) {
dd84935
-	err = 1;
dd84935
-      }
dd84935
-      if (prev_context)
dd84935
-	freecon(prev_context);
dd84935
-      prev_context=NULL;
dd84935
-    }
dd84935
-#endif
dd84935
-
dd84935
-    if (!err) {
dd84935
-	return PAM_SUCCESS;
dd84935
-    } else {
dd84935
-	unlink(SH_TMPFILE);
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-    }
dd84935
-}
dd84935
-
dd84935
 static int _do_setpass(pam_handle_t* pamh, const char *forwho,
dd84935
 		       const char *fromwhat,
dd84935
 		       char *towhat, unsigned int ctrl, int remember)
dd84935
@@ -767,7 +363,7 @@
dd84935
 
dd84935
 		/* Unlock passwd file to avoid deadlock */
dd84935
 #ifdef USE_LCKPWDF
dd84935
-		ulckpwdf();
dd84935
+		unlock_pwdf();
dd84935
 #endif
dd84935
 		unlocked = 1;
dd84935
 
dd84935
@@ -830,33 +426,22 @@
dd84935
 	if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
dd84935
 #ifdef USE_LCKPWDF
dd84935
 		if(unlocked) {
dd84935
-			int i = 0;
dd84935
-			/* These values for the number of attempts and the sleep time
dd84935
-			   are, of course, completely arbitrary.
dd84935
-			   My reading of the PAM docs is that, once pam_chauthtok() has been
dd84935
-			   called with PAM_UPDATE_AUTHTOK, we are obliged to take any
dd84935
-			   reasonable steps to make sure the token is updated; so retrying
dd84935
-			   for 1/10 sec. isn't overdoing it. */
dd84935
-			while((retval = lckpwdf()) != 0 && i < 100) {
dd84935
-				usleep(1000);
dd84935
-				i++;
dd84935
-			}
dd84935
-			if(retval != 0) {
dd84935
+			if (lock_pwdf() != PAM_SUCCESS) {
dd84935
 				return PAM_AUTHTOK_LOCK_BUSY;
dd84935
 			}
dd84935
 		}
dd84935
 #endif
dd84935
+#ifdef WITH_SELINUX
dd84935
+	        if (selinux_confined())
dd84935
+			  return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember);
dd84935
+#endif
dd84935
 		/* first, save old password */
dd84935
-		if (save_old_password(pamh, forwho, fromwhat, remember)) {
dd84935
+		if (save_old_password(forwho, fromwhat, remember)) {
dd84935
 			retval = PAM_AUTHTOK_ERR;
dd84935
 			goto done;
dd84935
 		}
dd84935
 		if (on(UNIX_SHADOW, ctrl) || _unix_shadowed(pwd)) {
dd84935
 			retval = _update_shadow(pamh, forwho, towhat);
dd84935
-#ifdef WITH_SELINUX
dd84935
- 		        if (retval != PAM_SUCCESS && SELINUX_ENABLED)
dd84935
-			  retval = _unix_run_shadow_binary(pamh, ctrl, forwho, fromwhat, towhat);
dd84935
-#endif
dd84935
 			if (retval == PAM_SUCCESS)
dd84935
 				if (!_unix_shadowed(pwd))
dd84935
 					retval = _update_passwd(pamh, forwho, "x");
dd84935
@@ -868,7 +453,7 @@
dd84935
 
dd84935
 done:
dd84935
 #ifdef USE_LCKPWDF
dd84935
-	ulckpwdf();
dd84935
+	unlock_pwdf();
dd84935
 #endif
dd84935
 
dd84935
 	return retval;
dd84935
@@ -889,13 +474,17 @@
dd84935
 	if (_unix_shadowed(pwd)) {
dd84935
 		/* ...and shadow password file entry for this user, if shadowing
dd84935
 		   is enabled */
dd84935
-		setspent();
dd84935
-		spwdent = getspnam(user);
dd84935
-		endspent();
dd84935
-
dd84935
 #ifdef WITH_SELINUX
dd84935
-		if (spwdent == NULL && SELINUX_ENABLED )
dd84935
-		    spwdent = _unix_run_verify_binary(pamh, ctrl, user);
dd84935
+		if (selinux_confined())
dd84935
+			spwdent = _unix_run_verify_binary(pamh, ctrl, user);
dd84935
+		else
dd84935
+		{
dd84935
+#endif
dd84935
+			setspent();
dd84935
+			spwdent = getspnam(user);
dd84935
+			endspent();
dd84935
+#ifdef WITH_SELINUX
dd84935
+		}
dd84935
 #endif
dd84935
 		if (spwdent == NULL)
dd84935
 			return PAM_AUTHINFO_UNAVAIL;
dd84935
@@ -1018,7 +607,7 @@
dd84935
 				int argc, const char **argv)
dd84935
 {
dd84935
 	unsigned int ctrl, lctrl;
dd84935
-	int retval, i;
dd84935
+	int retval;
dd84935
 	int remember = -1;
dd84935
 
dd84935
 	/* <DO NOT free() THESE> */
dd84935
@@ -1238,49 +827,40 @@
dd84935
 			return retval;
dd84935
 		}
dd84935
 #ifdef USE_LCKPWDF
dd84935
-		/* These values for the number of attempts and the sleep time
dd84935
-		   are, of course, completely arbitrary.
dd84935
-		   My reading of the PAM docs is that, once pam_chauthtok() has been
dd84935
-		   called with PAM_UPDATE_AUTHTOK, we are obliged to take any
dd84935
-		   reasonable steps to make sure the token is updated; so retrying
dd84935
-		   for 1/10 sec. isn't overdoing it. */
dd84935
-		i=0;
dd84935
-		while((retval = lckpwdf()) != 0 && i < 100) {
dd84935
-			usleep(1000);
dd84935
-			i++;
dd84935
-		}
dd84935
-		if(retval != 0) {
dd84935
+		if (lock_pwdf() != PAM_SUCCESS) {
dd84935
 			return PAM_AUTHTOK_LOCK_BUSY;
dd84935
 		}
dd84935
 #endif
dd84935
 
dd84935
-		if (pass_old) {
dd84935
+		if (!selinux_confined() && pass_old) {
dd84935
 			retval = _unix_verify_password(pamh, user, pass_old, ctrl);
dd84935
 			if (retval != PAM_SUCCESS) {
dd84935
 				pam_syslog(pamh, LOG_NOTICE, "user password changed by another process");
dd84935
 #ifdef USE_LCKPWDF
dd84935
-				ulckpwdf();
dd84935
+				unlock_pwdf();
dd84935
 #endif
dd84935
 				return retval;
dd84935
 			}
dd84935
 		}
dd84935
 
dd84935
-		retval = _unix_verify_shadow(pamh, user, ctrl);
dd84935
-		if (retval != PAM_SUCCESS) {
dd84935
+		
dd84935
+		if (!selinux_confined() && 
dd84935
+		    (retval=_unix_verify_shadow(pamh, user, ctrl)) != PAM_SUCCESS) {
dd84935
 			pam_syslog(pamh, LOG_NOTICE, "user not authenticated 2");
dd84935
 #ifdef USE_LCKPWDF
dd84935
-			ulckpwdf();
dd84935
+			unlock_pwdf();
dd84935
 #endif
dd84935
 			return retval;
dd84935
 		}
dd84935
 
dd84935
-		retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
dd84935
-		if (retval != PAM_SUCCESS) {
dd84935
+
dd84935
+		if (!selinux_confined() &&
dd84935
+		    (retval=_pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new)) != PAM_SUCCESS) {
dd84935
 			pam_syslog(pamh, LOG_NOTICE,
dd84935
 			         "new password not acceptable 2");
dd84935
 			pass_new = pass_old = NULL;	/* tidy up */
dd84935
 #ifdef USE_LCKPWDF
dd84935
-			ulckpwdf();
dd84935
+			unlock_pwdf();
dd84935
 #endif
dd84935
 			return retval;
dd84935
 		}
dd84935
@@ -1324,7 +904,7 @@
dd84935
 					         "out of memory for password");
dd84935
 					pass_new = pass_old = NULL;	/* tidy up */
dd84935
 #ifdef USE_LCKPWDF
dd84935
-					ulckpwdf();
dd84935
+					unlock_pwdf();
dd84935
 #endif
dd84935
 					return PAM_BUF_ERR;
dd84935
 				}
dd84935
@@ -1347,7 +927,7 @@
dd84935
 
dd84935
 		retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
dd84935
 		                     remember);
dd84935
-	        /* _do_setpass has called ulckpwdf for us */
dd84935
+	        /* _do_setpass has called unlock_pwdf for us */
dd84935
 
dd84935
 		_pam_delete(tpass);
dd84935
 		pass_old = pass_new = NULL;
dd84935
--- /dev/null	2007-05-28 11:10:34.936447748 +0200
dd84935
+++ Linux-PAM-0.99.7.1/modules/pam_unix/passverify.h	2007-06-01 15:13:57.000000000 +0200
dd84935
@@ -0,0 +1,60 @@
dd84935
+/*
dd84935
+ * This program is designed to run setuid(root) or with sufficient
dd84935
+ * privilege to read all of the unix password databases. It is designed
dd84935
+ * to provide a mechanism for the current user (defined by this
dd84935
+ * process' uid) to verify their own password.
dd84935
+ *
dd84935
+ * The password is read from the standard input. The exit status of
dd84935
+ * this program indicates whether the user is authenticated or not.
dd84935
+ *
dd84935
+ * Copyright information is located at the end of the file.
dd84935
+ *
dd84935
+ */
dd84935
+
dd84935
+#define MAXPASS		200	/* the maximum length of a password */
dd84935
+
dd84935
+void _log_err(int err, const char *format,...);
dd84935
+
dd84935
+void setup_signals(void);
dd84935
+
dd84935
+int read_passwords(int fd, int npass, char **passwords);
dd84935
+
dd84935
+int _unix_verify_password(const char *name, const char *p, int nullok);
dd84935
+
dd84935
+char *getuidname(uid_t uid);
dd84935
+
dd84935
+/*
dd84935
+ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
dd84935
+ * Copyright (c) Red Hat, Inc. 2007. All rights reserved
dd84935
+ *
dd84935
+ * Redistribution and use in source and binary forms, with or without
dd84935
+ * modification, are permitted provided that the following conditions
dd84935
+ * are met:
dd84935
+ * 1. Redistributions of source code must retain the above copyright
dd84935
+ *    notice, and the entire permission notice in its entirety,
dd84935
+ *    including the disclaimer of warranties.
dd84935
+ * 2. Redistributions in binary form must reproduce the above copyright
dd84935
+ *    notice, this list of conditions and the following disclaimer in the
dd84935
+ *    documentation and/or other materials provided with the distribution.
dd84935
+ * 3. The name of the author may not be used to endorse or promote
dd84935
+ *    products derived from this software without specific prior
dd84935
+ *    written permission.
dd84935
+ *
dd84935
+ * ALTERNATIVELY, this product may be distributed under the terms of
dd84935
+ * the GNU Public License, in which case the provisions of the GPL are
dd84935
+ * required INSTEAD OF the above restrictions.  (This clause is
dd84935
+ * necessary due to a potential bad interaction between the GPL and
dd84935
+ * the restrictions contained in a BSD-style copyright.)
dd84935
+ *
dd84935
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
dd84935
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
dd84935
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
dd84935
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
dd84935
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
dd84935
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
dd84935
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
dd84935
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
dd84935
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
dd84935
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
dd84935
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
dd84935
+ */
dd84935
--- Linux-PAM-0.99.7.1/modules/pam_unix/unix_chkpwd.c.update-helper	2007-06-01 15:13:57.000000000 +0200
dd84935
+++ Linux-PAM-0.99.7.1/modules/pam_unix/unix_chkpwd.c	2007-06-01 15:16:00.000000000 +0200
dd84935
@@ -41,386 +41,7 @@
dd84935
 
dd84935
 #include "md5.h"
dd84935
 #include "bigcrypt.h"
dd84935
-
dd84935
-/* syslogging function for errors and other information */
dd84935
-
dd84935
-static void _log_err(int err, const char *format,...)
dd84935
-{
dd84935
-	va_list args;
dd84935
-
dd84935
-	va_start(args, format);
dd84935
-	openlog("unix_chkpwd", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
dd84935
-	vsyslog(err, format, args);
dd84935
-	va_end(args);
dd84935
-	closelog();
dd84935
-}
dd84935
-
dd84935
-static int _unix_shadowed(const struct passwd *pwd)
dd84935
-{
dd84935
-	char hashpass[1024];
dd84935
-	if (pwd != NULL) {
dd84935
-		if (strcmp(pwd->pw_passwd, "x") == 0) {
dd84935
-			return 1;
dd84935
-		}
dd84935
-		if (strlen(pwd->pw_name) < sizeof(hashpass) - 2) {
dd84935
-			strcpy(hashpass, "##");
dd84935
-			strcpy(hashpass + 2, pwd->pw_name);
dd84935
-			if (strcmp(pwd->pw_passwd, hashpass) == 0) {
dd84935
-				return 1;
dd84935
-			}
dd84935
-		}
dd84935
-	}
dd84935
-	return 0;
dd84935
-}
dd84935
-
dd84935
-static void su_sighandler(int sig)
dd84935
-{
dd84935
-#ifndef SA_RESETHAND
dd84935
-	/* emulate the behaviour of the SA_RESETHAND flag */
dd84935
-	if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV )
dd84935
-		signal(sig, SIG_DFL);
dd84935
-#endif
dd84935
-	if (sig > 0) {
dd84935
-		_log_err(LOG_NOTICE, "caught signal %d.", sig);
dd84935
-		exit(sig);
dd84935
-	}
dd84935
-}
dd84935
-
dd84935
-static void setup_signals(void)
dd84935
-{
dd84935
-	struct sigaction action;	/* posix signal structure */
dd84935
-
dd84935
-	/*
dd84935
-	 * Setup signal handlers
dd84935
-	 */
dd84935
-	(void) memset((void *) &action, 0, sizeof(action));
dd84935
-	action.sa_handler = su_sighandler;
dd84935
-#ifdef SA_RESETHAND
dd84935
-	action.sa_flags = SA_RESETHAND;
dd84935
-#endif
dd84935
-	(void) sigaction(SIGILL, &action, NULL);
dd84935
-	(void) sigaction(SIGTRAP, &action, NULL);
dd84935
-	(void) sigaction(SIGBUS, &action, NULL);
dd84935
-	(void) sigaction(SIGSEGV, &action, NULL);
dd84935
-	action.sa_handler = SIG_IGN;
dd84935
-	action.sa_flags = 0;
dd84935
-	(void) sigaction(SIGTERM, &action, NULL);
dd84935
-	(void) sigaction(SIGHUP, &action, NULL);
dd84935
-	(void) sigaction(SIGINT, &action, NULL);
dd84935
-	(void) sigaction(SIGQUIT, &action, NULL);
dd84935
-}
dd84935
-
dd84935
-static int _verify_account(const char * const uname)
dd84935
-{
dd84935
-	struct spwd *spent;
dd84935
-	struct passwd *pwent;
dd84935
-
dd84935
-	pwent = getpwnam(uname);
dd84935
-	if (!pwent) {
dd84935
-		_log_err(LOG_ALERT, "could not identify user (from getpwnam(%s))", uname);
dd84935
-		return PAM_USER_UNKNOWN;
dd84935
-	}
dd84935
-
dd84935
-	spent = getspnam( uname );
dd84935
-	if (!spent) {
dd84935
-		_log_err(LOG_ALERT, "could not get username from shadow (%s))", uname);
dd84935
-		return PAM_AUTHINFO_UNAVAIL;	/* Couldn't get username from shadow */
dd84935
-	}
dd84935
-	printf("%ld:%ld:%ld:%ld:%ld:%ld",
dd84935
-		 spent->sp_lstchg, /* last password change */
dd84935
-                 spent->sp_min, /* days until change allowed. */
dd84935
-                 spent->sp_max, /* days before change required */
dd84935
-                 spent->sp_warn, /* days warning for expiration */
dd84935
-                 spent->sp_inact, /* days before account inactive */
dd84935
-                 spent->sp_expire); /* date when account expires */
dd84935
-
dd84935
-	return PAM_SUCCESS;
dd84935
-}
dd84935
-
dd84935
-static int _unix_verify_password(const char *name, const char *p, int nullok)
dd84935
-{
dd84935
-	struct passwd *pwd = NULL;
dd84935
-	struct spwd *spwdent = NULL;
dd84935
-	char *salt = NULL;
dd84935
-	char *pp = NULL;
dd84935
-	int retval = PAM_AUTH_ERR;
dd84935
-	size_t salt_len;
dd84935
-
dd84935
-	/* UNIX passwords area */
dd84935
-	setpwent();
dd84935
-	pwd = getpwnam(name);	/* Get password file entry... */
dd84935
-	endpwent();
dd84935
-	if (pwd != NULL) {
dd84935
-		if (_unix_shadowed(pwd)) {
dd84935
-			/*
dd84935
-			 * ...and shadow password file entry for this user,
dd84935
-			 * if shadowing is enabled
dd84935
-			 */
dd84935
-			setspent();
dd84935
-			spwdent = getspnam(name);
dd84935
-			endspent();
dd84935
-			if (spwdent != NULL)
dd84935
-				salt = x_strdup(spwdent->sp_pwdp);
dd84935
-			else
dd84935
-				pwd = NULL;
dd84935
-		} else {
dd84935
-			if (strcmp(pwd->pw_passwd, "*NP*") == 0) {	/* NIS+ */
dd84935
-				uid_t save_uid;
dd84935
-
dd84935
-				save_uid = geteuid();
dd84935
-				seteuid(pwd->pw_uid);
dd84935
-				spwdent = getspnam(name);
dd84935
-				seteuid(save_uid);
dd84935
-
dd84935
-				salt = x_strdup(spwdent->sp_pwdp);
dd84935
-			} else {
dd84935
-				salt = x_strdup(pwd->pw_passwd);
dd84935
-			}
dd84935
-		}
dd84935
-	}
dd84935
-	if (pwd == NULL || salt == NULL) {
dd84935
-		_log_err(LOG_ALERT, "check pass; user unknown");
dd84935
-		p = NULL;
dd84935
-		return PAM_USER_UNKNOWN;
dd84935
-	}
dd84935
-
dd84935
-	salt_len = strlen(salt);
dd84935
-	if (salt_len == 0) {
dd84935
-		return (nullok == 0) ? PAM_AUTH_ERR : PAM_SUCCESS;
dd84935
-	}
dd84935
-	if (p == NULL || strlen(p) == 0) {
dd84935
-		_pam_overwrite(salt);
dd84935
-		_pam_drop(salt);
dd84935
-		return PAM_AUTHTOK_ERR;
dd84935
-	}
dd84935
-
dd84935
-	/* the moment of truth -- do we agree with the password? */
dd84935
-	retval = PAM_AUTH_ERR;
dd84935
-	if (!strncmp(salt, "$1$", 3)) {
dd84935
-		pp = Goodcrypt_md5(p, salt);
dd84935
-		if (pp && strcmp(pp, salt) == 0) {
dd84935
-			retval = PAM_SUCCESS;
dd84935
-		} else {
dd84935
-			_pam_overwrite(pp);
dd84935
-			_pam_drop(pp);
dd84935
-			pp = Brokencrypt_md5(p, salt);
dd84935
-			if (pp && strcmp(pp, salt) == 0)
dd84935
-				retval = PAM_SUCCESS;
dd84935
-		}
dd84935
-	} else if (*salt == '$') {
dd84935
-	        /*
dd84935
-		 * Ok, we don't know the crypt algorithm, but maybe
dd84935
-		 * libcrypt nows about it? We should try it.
dd84935
-		 */
dd84935
-	        pp = x_strdup (crypt(p, salt));
dd84935
-		if (pp && strcmp(pp, salt) == 0) {
dd84935
-			retval = PAM_SUCCESS;
dd84935
-		}
dd84935
-	} else if (*salt == '*' || *salt == '!' || salt_len < 13) {
dd84935
-	    retval = PAM_AUTH_ERR;
dd84935
-	} else {
dd84935
-		pp = bigcrypt(p, salt);
dd84935
-		/*
dd84935
-		 * Note, we are comparing the bigcrypt of the password with
dd84935
-		 * the contents of the password field. If the latter was
dd84935
-		 * encrypted with regular crypt (and not bigcrypt) it will
dd84935
-		 * have been truncated for storage relative to the output
dd84935
-		 * of bigcrypt here. As such we need to compare only the
dd84935
-		 * stored string with the subset of bigcrypt's result.
dd84935
-		 * Bug 521314.
dd84935
-		 */
dd84935
-		if (pp && salt_len == 13 && strlen(pp) > salt_len) {
dd84935
-		    _pam_overwrite(pp+salt_len);
dd84935
-		}
dd84935
-		
dd84935
-		if (pp && strcmp(pp, salt) == 0) {
dd84935
-			retval = PAM_SUCCESS;
dd84935
-		}
dd84935
-	}
dd84935
-	p = NULL;		/* no longer needed here */
dd84935
-
dd84935
-	/* clean up */
dd84935
-	_pam_overwrite(pp);
dd84935
-	_pam_drop(pp);
dd84935
-
dd84935
-	return retval;
dd84935
-}
dd84935
-
dd84935
-static char *getuidname(uid_t uid)
dd84935
-{
dd84935
-	struct passwd *pw;
dd84935
-	static char username[32];
dd84935
-
dd84935
-	pw = getpwuid(uid);
dd84935
-	if (pw == NULL)
dd84935
-		return NULL;
dd84935
-
dd84935
-	strncpy(username, pw->pw_name, sizeof(username));
dd84935
-	username[sizeof(username) - 1] = '\0';
dd84935
-
dd84935
-	return username;
dd84935
-}
dd84935
-
dd84935
-#define SH_TMPFILE		"/etc/nshadow"
dd84935
-static int _update_shadow(const char *forwho)
dd84935
-{
dd84935
-    struct spwd *spwdent = NULL, *stmpent = NULL;
dd84935
-    FILE *pwfile, *opwfile;
dd84935
-    int err = 1;
dd84935
-    int oldmask;
dd84935
-    struct stat st;
dd84935
-    char pass[MAXPASS + 1];
dd84935
-    char towhat[MAXPASS + 1];
dd84935
-    int npass=0;
dd84935
-
dd84935
-    /* read the password from stdin (a pipe from the pam_unix module) */
dd84935
-
dd84935
-    npass = read(STDIN_FILENO, pass, MAXPASS);
dd84935
-
dd84935
-    if (npass < 0) {	/* is it a valid password? */
dd84935
-
dd84935
-      _log_err(LOG_DEBUG, "no password supplied");
dd84935
-      return PAM_AUTHTOK_ERR;
dd84935
-
dd84935
-    } else if (npass >= MAXPASS) {
dd84935
-
dd84935
-      _log_err(LOG_DEBUG, "password too long");
dd84935
-      return PAM_AUTHTOK_ERR;
dd84935
-
dd84935
-    } else {
dd84935
-      /* does pass agree with the official one? */
dd84935
-      int retval=0;
dd84935
-      pass[npass] = '\0';	/* NUL terminate */
dd84935
-      retval = _unix_verify_password(forwho, pass, 0);
dd84935
-      if (retval != PAM_SUCCESS) {
dd84935
-	return retval;
dd84935
-      }
dd84935
-    }
dd84935
-
dd84935
-    /* read the password from stdin (a pipe from the pam_unix module) */
dd84935
-
dd84935
-    npass = read(STDIN_FILENO, towhat, MAXPASS);
dd84935
-
dd84935
-    if (npass < 0) {	/* is it a valid password? */
dd84935
-
dd84935
-      _log_err(LOG_DEBUG, "no new password supplied");
dd84935
-      return PAM_AUTHTOK_ERR;
dd84935
-
dd84935
-    } else if (npass >= MAXPASS) {
dd84935
-
dd84935
-      _log_err(LOG_DEBUG, "new password too long");
dd84935
-      return PAM_AUTHTOK_ERR;
dd84935
-
dd84935
-    }
dd84935
-
dd84935
-    towhat[npass] = '\0';	/* NUL terminate */
dd84935
-    spwdent = getspnam(forwho);
dd84935
-    if (spwdent == NULL) {
dd84935
-	return PAM_USER_UNKNOWN;
dd84935
-    }
dd84935
-    oldmask = umask(077);
dd84935
-
dd84935
-#ifdef WITH_SELINUX
dd84935
-    if (SELINUX_ENABLED) {
dd84935
-      security_context_t shadow_context=NULL;
dd84935
-      if (getfilecon("/etc/shadow",&shadow_context)<0) {
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-      };
dd84935
-      if (getfscreatecon(&prev_context)<0) {
dd84935
-	freecon(shadow_context);
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-      }
dd84935
-      if (setfscreatecon(shadow_context)) {
dd84935
-	freecon(shadow_context);
dd84935
-	freecon(prev_context);
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-      }
dd84935
-      freecon(shadow_context);
dd84935
-    }
dd84935
-#endif
dd84935
-    pwfile = fopen(SH_TMPFILE, "w");
dd84935
-    umask(oldmask);
dd84935
-    if (pwfile == NULL) {
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    opwfile = fopen("/etc/shadow", "r");
dd84935
-    if (opwfile == NULL) {
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    if (fstat(fileno(opwfile), &st) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
dd84935
-	fclose(opwfile);
dd84935
-	fclose(pwfile);
dd84935
-	err = 1;
dd84935
-	goto done;
dd84935
-    }
dd84935
-
dd84935
-    stmpent = fgetspent(opwfile);
dd84935
-    while (stmpent) {
dd84935
-
dd84935
-	if (!strcmp(stmpent->sp_namp, forwho)) {
dd84935
-	    stmpent->sp_pwdp = towhat;
dd84935
-	    stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
dd84935
-	    err = 0;
dd84935
-	    D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
dd84935
-	}
dd84935
-
dd84935
-	if (putspent(stmpent, pwfile)) {
dd84935
-	    D(("error writing entry to shadow file: %m"));
dd84935
-	    err = 1;
dd84935
-	    break;
dd84935
-	}
dd84935
-
dd84935
-	stmpent = fgetspent(opwfile);
dd84935
-    }
dd84935
-    fclose(opwfile);
dd84935
-
dd84935
-    if (fclose(pwfile)) {
dd84935
-	D(("error writing entries to shadow file: %m"));
dd84935
-	err = 1;
dd84935
-    }
dd84935
-
dd84935
- done:
dd84935
-    if (!err) {
dd84935
-	if (rename(SH_TMPFILE, "/etc/shadow"))
dd84935
-	    err = 1;
dd84935
-    }
dd84935
-
dd84935
-#ifdef WITH_SELINUX
dd84935
-    if (SELINUX_ENABLED) {
dd84935
-      if (setfscreatecon(prev_context)) {
dd84935
-	err = 1;
dd84935
-      }
dd84935
-      if (prev_context)
dd84935
-	freecon(prev_context);
dd84935
-      prev_context=NULL;
dd84935
-    }
dd84935
-#endif
dd84935
-
dd84935
-    if (!err) {
dd84935
-	return PAM_SUCCESS;
dd84935
-    } else {
dd84935
-	unlink(SH_TMPFILE);
dd84935
-	return PAM_AUTHTOK_ERR;
dd84935
-    }
dd84935
-}
dd84935
+#include "passverify.h"
dd84935
 
dd84935
 int main(int argc, char *argv[])
dd84935
 {
dd84935
@@ -430,6 +51,7 @@
dd84935
 	int force_failure = 0;
dd84935
 	int retval = PAM_AUTH_ERR;
dd84935
 	char *user;
dd84935
+	char *passwords[] = { pass };
dd84935
 
dd84935
 	/*
dd84935
 	 * Catch or ignore as many signal as possible.
dd84935
@@ -476,49 +98,24 @@
dd84935
 
dd84935
 	option=argv[2];
dd84935
 
dd84935
-	if (strncmp(argv[2], "verify", 8) == 0) {
dd84935
-	  /* Get the account information from the shadow file */
dd84935
-	  return _verify_account(argv[1]);
dd84935
-	}
dd84935
-
dd84935
-	if (strncmp(option, "shadow", 8) == 0) {
dd84935
-	  /* Attempting to change the password */
dd84935
-	  return _update_shadow(argv[1]);
dd84935
-	}
dd84935
-
dd84935
 	/* read the nullok/nonull option */
dd84935
 	if (strncmp(option, "nullok", 8) == 0)
dd84935
 	  nullok = 1;
dd84935
-	else
dd84935
+	else if (strncmp(option, "nonull", 8) == 0)
dd84935
 	  nullok = 0;
dd84935
+	else
dd84935
+	  return PAM_SYSTEM_ERR;
dd84935
 
dd84935
 	/* read the password from stdin (a pipe from the pam_unix module) */
dd84935
 
dd84935
-	npass = read(STDIN_FILENO, pass, MAXPASS);
dd84935
-
dd84935
-	if (npass < 0) {	/* is it a valid password? */
dd84935
-
dd84935
-		_log_err(LOG_DEBUG, "no password supplied");
dd84935
+	npass = read_passwords(STDIN_FILENO, 1, passwords);
dd84935
 
dd84935
-	} else if (npass >= MAXPASS) {
dd84935
-
dd84935
-		_log_err(LOG_DEBUG, "password too long");
dd84935
-
dd84935
-	} else {
dd84935
-		if (npass == 0) {
dd84935
-			/* the password is NULL */
dd84935
-
dd84935
-			retval = _unix_verify_password(user, NULL, nullok);
dd84935
-
dd84935
-		} else {
dd84935
-			/* does pass agree with the official one? */
dd84935
-
dd84935
-			pass[npass] = '\0';	/* NUL terminate */
dd84935
-			retval = _unix_verify_password(user, pass, nullok);
dd84935
-
dd84935
-		}
dd84935
+	if (npass != 1) {	/* is it a valid password? */
dd84935
+		_log_err(LOG_DEBUG, "no valid password supplied");
dd84935
 	}
dd84935
 
dd84935
+	retval = _unix_verify_password(user, pass, nullok);
dd84935
+
dd84935
 	memset(pass, '\0', MAXPASS);	/* clear memory of the password */
dd84935
 
dd84935
 	/* return pass or fail */
dd84935
@@ -533,6 +130,7 @@
dd84935
 
dd84935
 /*
dd84935
  * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
dd84935
+ * Copyright (c) Red Hat, Inc., 2007. All rights reserved
dd84935
  *
dd84935
  * Redistribution and use in source and binary forms, with or without
dd84935
  * modification, are permitted provided that the following conditions
dd84935
--- /dev/null	2007-05-28 11:10:34.936447748 +0200
dd84935
+++ Linux-PAM-0.99.7.1/modules/pam_unix/unix_update.c	2007-06-01 15:13:57.000000000 +0200
dd84935
@@ -0,0 +1,262 @@
dd84935
+/*
dd84935
+ * This program is designed to run setuid(root) or with sufficient
dd84935
+ * privilege to read all of the unix password databases. It is designed
dd84935
+ * to provide a mechanism for the current user (defined by this
dd84935
+ * process' uid) to verify their own password.
dd84935
+ *
dd84935
+ * The password is read from the standard input. The exit status of
dd84935
+ * this program indicates whether the user is authenticated or not.
dd84935
+ *
dd84935
+ * Copyright information is located at the end of the file.
dd84935
+ *
dd84935
+ */
dd84935
+
dd84935
+#include "config.h"
dd84935
+
dd84935
+#include <stdarg.h>
dd84935
+#include <stdio.h>
dd84935
+#include <stdlib.h>
dd84935
+#include <string.h>
dd84935
+#include <syslog.h>
dd84935
+#include <unistd.h>
dd84935
+#include <sys/types.h>
dd84935
+#include <sys/stat.h>
dd84935
+#include <pwd.h>
dd84935
+#include <shadow.h>
dd84935
+#include <signal.h>
dd84935
+#include <time.h>
dd84935
+#include <sys/time.h>
dd84935
+#ifdef WITH_SELINUX
dd84935
+#include <selinux/selinux.h>
dd84935
+#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
dd84935
+static security_context_t prev_context=NULL;
dd84935
+static int selinux_enabled=-1;
dd84935
+#else
dd84935
+#define SELINUX_ENABLED 0
dd84935
+#endif
dd84935
+
dd84935
+#define MAXPASS		200	/* the maximum length of a password */
dd84935
+
dd84935
+#include <security/_pam_types.h>
dd84935
+#include <security/_pam_macros.h>
dd84935
+
dd84935
+#include "md5.h"
dd84935
+#include "bigcrypt.h"
dd84935
+#include "passverify.h"
dd84935
+
dd84935
+#define _pam_delete(xx)         \
dd84935
+{                               \
dd84935
+	_pam_overwrite(xx);     \
dd84935
+        _pam_drop(xx);          \
dd84935
+}
dd84935
+
dd84935
+static int
dd84935
+_unix_shadowed(const struct passwd *pwd)
dd84935
+{
dd84935
+        if (pwd != NULL) {
dd84935
+                if (strcmp(pwd->pw_passwd, "x") == 0) {
dd84935
+                        return 1;
dd84935
+                }
dd84935
+                if ((pwd->pw_passwd[0] == '#') &&
dd84935
+                    (pwd->pw_passwd[1] == '#') &&
dd84935
+                    (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
dd84935
+                        return 1;
dd84935
+                }
dd84935
+        }
dd84935
+        return 0;
dd84935
+}
dd84935
+
dd84935
+#define HELPER_COMPILE
dd84935
+#include "passupdate.c"
dd84935
+
dd84935
+static int
dd84935
+verify_account(const char * const uname)
dd84935
+{
dd84935
+	struct spwd *spent;
dd84935
+	struct passwd *pwent;
dd84935
+
dd84935
+	pwent = getpwnam(uname);
dd84935
+	if (!pwent) {
dd84935
+		_log_err(LOG_ALERT, "could not identify user (from getpwnam(%s))", uname);
dd84935
+		return PAM_USER_UNKNOWN;
dd84935
+	}
dd84935
+
dd84935
+	spent = getspnam( uname );
dd84935
+	if (!spent) {
dd84935
+		_log_err(LOG_ALERT, "could not get username from shadow (%s))", uname);
dd84935
+		return PAM_AUTHINFO_UNAVAIL;	/* Couldn't get username from shadow */
dd84935
+	}
dd84935
+	printf("%ld:%ld:%ld:%ld:%ld:%ld",
dd84935
+		 spent->sp_lstchg, /* last password change */
dd84935
+                 spent->sp_min, /* days until change allowed. */
dd84935
+                 spent->sp_max, /* days before change required */
dd84935
+                 spent->sp_warn, /* days warning for expiration */
dd84935
+                 spent->sp_inact, /* days before account inactive */
dd84935
+                 spent->sp_expire); /* date when account expires */
dd84935
+
dd84935
+	return PAM_SUCCESS;
dd84935
+}
dd84935
+
dd84935
+static int
dd84935
+set_password(const char *forwho, const char *shadow, const char *remember)
dd84935
+{
dd84935
+    struct passwd *pwd = NULL;
dd84935
+    int retval;
dd84935
+    char pass[MAXPASS + 1];
dd84935
+    char towhat[MAXPASS + 1];
dd84935
+    int npass = 0;
dd84935
+    /* we don't care about number format errors because the helper
dd84935
+       should be called internally only */
dd84935
+    int doshadow = atoi(shadow);
dd84935
+    int nremember = atoi(remember);
dd84935
+    char *passwords[] = { pass, towhat };
dd84935
+
dd84935
+    /* read the password from stdin (a pipe from the pam_unix module) */
dd84935
+
dd84935
+    npass = read_passwords(STDIN_FILENO, 2, passwords);
dd84935
+
dd84935
+    if (npass != 2) {	/* is it a valid password? */
dd84935
+      if (npass == 1) {
dd84935
+        _log_err(LOG_DEBUG, "no new password supplied");
dd84935
+	memset(pass, '\0', MAXPASS);
dd84935
+      } else {
dd84935
+        _log_err(LOG_DEBUG, "no valid passwords supplied");
dd84935
+      }
dd84935
+      return PAM_AUTHTOK_ERR;
dd84935
+    }
dd84935
+
dd84935
+#ifdef USE_LCKPWDF
dd84935
+    if (lock_pwdf() != PAM_SUCCESS)
dd84935
+    	return PAM_AUTHTOK_LOCK_BUSY;
dd84935
+#endif
dd84935
+
dd84935
+    pwd = getpwnam(forwho);
dd84935
+    
dd84935
+    if (pwd == NULL) {
dd84935
+        retval = PAM_USER_UNKNOWN;
dd84935
+        goto done;
dd84935
+    }
dd84935
+
dd84935
+    /* does pass agree with the official one? 
dd84935
+       we always allow change from null pass */
dd84935
+    retval = _unix_verify_password(forwho, pass, 1);
dd84935
+    if (retval != PAM_SUCCESS) {
dd84935
+	goto done;
dd84935
+    }
dd84935
+
dd84935
+    /* first, save old password */
dd84935
+    if (save_old_password(forwho, pass, nremember)) {
dd84935
+	retval = PAM_AUTHTOK_ERR;
dd84935
+	goto done;
dd84935
+    }
dd84935
+
dd84935
+    if (doshadow || _unix_shadowed(pwd)) {
dd84935
+	retval = _update_shadow(forwho, towhat);
dd84935
+	if (retval == PAM_SUCCESS)
dd84935
+	    if (!_unix_shadowed(pwd))
dd84935
+		retval = _update_passwd(forwho, "x");
dd84935
+    } else {
dd84935
+	retval = _update_passwd(forwho, towhat);
dd84935
+    }
dd84935
+
dd84935
+done:
dd84935
+    memset(pass, '\0', MAXPASS);
dd84935
+    memset(towhat, '\0', MAXPASS);
dd84935
+
dd84935
+#ifdef USE_LCKPWDF
dd84935
+    unlock_pwdf();
dd84935
+#endif
dd84935
+
dd84935
+    if (retval == PAM_SUCCESS) {
dd84935
+	return PAM_SUCCESS;
dd84935
+    } else {
dd84935
+	return PAM_AUTHTOK_ERR;
dd84935
+    }
dd84935
+}
dd84935
+
dd84935
+int main(int argc, char *argv[])
dd84935
+{
dd84935
+	char *option;
dd84935
+
dd84935
+	/*
dd84935
+	 * Catch or ignore as many signal as possible.
dd84935
+	 */
dd84935
+	setup_signals();
dd84935
+
dd84935
+	/*
dd84935
+	 * we establish that this program is running with non-tty stdin.
dd84935
+	 * this is to discourage casual use. It does *NOT* prevent an
dd84935
+	 * intruder from repeatadly running this program to determine the
dd84935
+	 * password of the current user (brute force attack, but one for
dd84935
+	 * which the attacker must already have gained access to the user's
dd84935
+	 * account).
dd84935
+	 */
dd84935
+
dd84935
+	if (isatty(STDIN_FILENO) || argc < 3 ) {
dd84935
+		_log_err(LOG_NOTICE
dd84935
+		      ,"inappropriate use of Unix helper binary [UID=%d]"
dd84935
+			 ,getuid());
dd84935
+		fprintf(stderr
dd84935
+		 ,"This binary is not designed for running in this way\n"
dd84935
+		      "-- the system administrator has been informed\n");
dd84935
+		sleep(10);	/* this should discourage/annoy the user */
dd84935
+		return PAM_SYSTEM_ERR;
dd84935
+	}
dd84935
+
dd84935
+	/* We must be root to read/update shadow.
dd84935
+	 */
dd84935
+	if (geteuid() != 0) {
dd84935
+	    return PAM_AUTH_ERR;
dd84935
+	}
dd84935
+	
dd84935
+	option = argv[2];
dd84935
+
dd84935
+	if (strncmp(option, "verify", 8) == 0) {
dd84935
+	  /* Get the account information from the shadow file */
dd84935
+	  return verify_account(argv[1]);
dd84935
+	}
dd84935
+
dd84935
+	if (strncmp(option, "update", 8) == 0) {
dd84935
+          if (argc == 5) 
dd84935
+	      /* Attempting to change the password */
dd84935
+	      return set_password(argv[1], argv[3], argv[4]);
dd84935
+	}
dd84935
+
dd84935
+	return PAM_SYSTEM_ERR;
dd84935
+}
dd84935
+
dd84935
+/*
dd84935
+ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
dd84935
+ * Copyright (c) Red Hat, Inc., 2007. All rights reserved
dd84935
+ *
dd84935
+ * Redistribution and use in source and binary forms, with or without
dd84935
+ * modification, are permitted provided that the following conditions
dd84935
+ * are met:
dd84935
+ * 1. Redistributions of source code must retain the above copyright
dd84935
+ *    notice, and the entire permission notice in its entirety,
dd84935
+ *    including the disclaimer of warranties.
dd84935
+ * 2. Redistributions in binary form must reproduce the above copyright
dd84935
+ *    notice, this list of conditions and the following disclaimer in the
dd84935
+ *    documentation and/or other materials provided with the distribution.
dd84935
+ * 3. The name of the author may not be used to endorse or promote
dd84935
+ *    products derived from this software without specific prior
dd84935
+ *    written permission.
dd84935
+ *
dd84935
+ * ALTERNATIVELY, this product may be distributed under the terms of
dd84935
+ * the GNU Public License, in which case the provisions of the GPL are
dd84935
+ * required INSTEAD OF the above restrictions.  (This clause is
dd84935
+ * necessary due to a potential bad interaction between the GPL and
dd84935
+ * the restrictions contained in a BSD-style copyright.)
dd84935
+ *
dd84935
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
dd84935
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
dd84935
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
dd84935
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
dd84935
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
dd84935
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
dd84935
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
dd84935
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
dd84935
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
dd84935
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
dd84935
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
dd84935
+ */
dd84935
--- /dev/null	2007-05-28 11:10:34.936447748 +0200
dd84935
+++ Linux-PAM-0.99.7.1/modules/pam_unix/passverify.c	2007-06-01 15:13:57.000000000 +0200
dd84935
@@ -0,0 +1,308 @@
dd84935
+/*
dd84935
+ * This program is designed to run setuid(root) or with sufficient
dd84935
+ * privilege to read all of the unix password databases. It is designed
dd84935
+ * to provide a mechanism for the current user (defined by this
dd84935
+ * process' uid) to verify their own password.
dd84935
+ *
dd84935
+ * The password is read from the standard input. The exit status of
dd84935
+ * this program indicates whether the user is authenticated or not.
dd84935
+ *
dd84935
+ * Copyright information is located at the end of the file.
dd84935
+ *
dd84935
+ */
dd84935
+
dd84935
+#include "config.h"
dd84935
+
dd84935
+#include <stdarg.h>
dd84935
+#include <stdio.h>
dd84935
+#include <stdlib.h>
dd84935
+#include <string.h>
dd84935
+#include <syslog.h>
dd84935
+#include <unistd.h>
dd84935
+#include <sys/types.h>
dd84935
+#include <sys/stat.h>
dd84935
+#include <pwd.h>
dd84935
+#include <shadow.h>
dd84935
+#include <signal.h>
dd84935
+#include <time.h>
dd84935
+#include <errno.h>
dd84935
+
dd84935
+#include <security/_pam_types.h>
dd84935
+#include <security/_pam_macros.h>
dd84935
+
dd84935
+#include "md5.h"
dd84935
+#include "bigcrypt.h"
dd84935
+
dd84935
+#include "passverify.h"
dd84935
+
dd84935
+/* syslogging function for errors and other information */
dd84935
+
dd84935
+void
dd84935
+_log_err(int err, const char *format,...)
dd84935
+{
dd84935
+	va_list args;
dd84935
+
dd84935
+	va_start(args, format);
dd84935
+	openlog("unix_chkpwd", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
dd84935
+	vsyslog(err, format, args);
dd84935
+	va_end(args);
dd84935
+	closelog();
dd84935
+}
dd84935
+
dd84935
+static int
dd84935
+_unix_shadowed(const struct passwd *pwd)
dd84935
+{
dd84935
+	char hashpass[1024];
dd84935
+	if (pwd != NULL) {
dd84935
+		if (strcmp(pwd->pw_passwd, "x") == 0) {
dd84935
+			return 1;
dd84935
+		}
dd84935
+		if (strlen(pwd->pw_name) < sizeof(hashpass) - 2) {
dd84935
+			strcpy(hashpass, "##");
dd84935
+			strcpy(hashpass + 2, pwd->pw_name);
dd84935
+			if (strcmp(pwd->pw_passwd, hashpass) == 0) {
dd84935
+				return 1;
dd84935
+			}
dd84935
+		}
dd84935
+	}
dd84935
+	return 0;
dd84935
+}
dd84935
+
dd84935
+static void
dd84935
+su_sighandler(int sig)
dd84935
+{
dd84935
+#ifndef SA_RESETHAND
dd84935
+	/* emulate the behaviour of the SA_RESETHAND flag */
dd84935
+	if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV )
dd84935
+		signal(sig, SIG_DFL);
dd84935
+#endif
dd84935
+	if (sig > 0) {
dd84935
+		_exit(sig);
dd84935
+	}
dd84935
+}
dd84935
+
dd84935
+void
dd84935
+setup_signals(void)
dd84935
+{
dd84935
+	struct sigaction action;	/* posix signal structure */
dd84935
+
dd84935
+	/*
dd84935
+	 * Setup signal handlers
dd84935
+	 */
dd84935
+	(void) memset((void *) &action, 0, sizeof(action));
dd84935
+	action.sa_handler = su_sighandler;
dd84935
+#ifdef SA_RESETHAND
dd84935
+	action.sa_flags = SA_RESETHAND;
dd84935
+#endif
dd84935
+	(void) sigaction(SIGILL, &action, NULL);
dd84935
+	(void) sigaction(SIGTRAP, &action, NULL);
dd84935
+	(void) sigaction(SIGBUS, &action, NULL);
dd84935
+	(void) sigaction(SIGSEGV, &action, NULL);
dd84935
+	action.sa_handler = SIG_IGN;
dd84935
+	action.sa_flags = 0;
dd84935
+	(void) sigaction(SIGTERM, &action, NULL);
dd84935
+	(void) sigaction(SIGHUP, &action, NULL);
dd84935
+	(void) sigaction(SIGINT, &action, NULL);
dd84935
+	(void) sigaction(SIGQUIT, &action, NULL);
dd84935
+}
dd84935
+
dd84935
+int
dd84935
+read_passwords(int fd, int npass, char **passwords)
dd84935
+{
dd84935
+	int rbytes = 0;
dd84935
+	int offset = 0;
dd84935
+	int i = 0;
dd84935
+	char *pptr;
dd84935
+	while (npass > 0) {
dd84935
+        	rbytes = read(fd, passwords[i]+offset, MAXPASS-offset);
dd84935
+
dd84935
+        	if (rbytes < 0) {
dd84935
+                	if (errno == EINTR) continue;
dd84935
+                	break;
dd84935
+		}
dd84935
+		if (rbytes == 0)
dd84935
+			break;
dd84935
+
dd84935
+		while (npass > 0 && (pptr=memchr(passwords[i]+offset, '\0', rbytes))
dd84935
+			!= NULL) {
dd84935
+			rbytes -= pptr - (passwords[i]+offset) + 1;
dd84935
+			i++;
dd84935
+			offset = 0;
dd84935
+			npass--;
dd84935
+			if (rbytes > 0) {
dd84935
+				if (npass > 0)
dd84935
+					memcpy(passwords[i], pptr+1, rbytes);
dd84935
+				memset(pptr+1, '\0', rbytes);
dd84935
+			}
dd84935
+		}
dd84935
+		offset += rbytes;
dd84935
+	}               
dd84935
+
dd84935
+	/* clear up */
dd84935
+	if (offset > 0 && npass > 0) {
dd84935
+		memset(passwords[i], '\0', offset);
dd84935
+	}
dd84935
+
dd84935
+	return i;
dd84935
+}
dd84935
+
dd84935
+int
dd84935
+_unix_verify_password(const char *name, const char *p, int nullok)
dd84935
+{
dd84935
+	struct passwd *pwd = NULL;
dd84935
+	struct spwd *spwdent = NULL;
dd84935
+	char *salt = NULL;
dd84935
+	char *pp = NULL;
dd84935
+	int retval = PAM_AUTH_ERR;
dd84935
+	size_t salt_len;
dd84935
+
dd84935
+	/* UNIX passwords area */
dd84935
+	setpwent();
dd84935
+	pwd = getpwnam(name);	/* Get password file entry... */
dd84935
+	endpwent();
dd84935
+	if (pwd != NULL) {
dd84935
+		if (_unix_shadowed(pwd)) {
dd84935
+			/*
dd84935
+			 * ...and shadow password file entry for this user,
dd84935
+			 * if shadowing is enabled
dd84935
+			 */
dd84935
+			setspent();
dd84935
+			spwdent = getspnam(name);
dd84935
+			endspent();
dd84935
+			if (spwdent != NULL)
dd84935
+				salt = x_strdup(spwdent->sp_pwdp);
dd84935
+			else
dd84935
+				pwd = NULL;
dd84935
+		} else {
dd84935
+			if (strcmp(pwd->pw_passwd, "*NP*") == 0) {	/* NIS+ */
dd84935
+				uid_t save_uid;
dd84935
+
dd84935
+				save_uid = geteuid();
dd84935
+				seteuid(pwd->pw_uid);
dd84935
+				spwdent = getspnam(name);
dd84935
+				seteuid(save_uid);
dd84935
+
dd84935
+				salt = x_strdup(spwdent->sp_pwdp);
dd84935
+			} else {
dd84935
+				salt = x_strdup(pwd->pw_passwd);
dd84935
+			}
dd84935
+		}
dd84935
+	}
dd84935
+	if (pwd == NULL || salt == NULL) {
dd84935
+		_log_err(LOG_ALERT, "check pass; user unknown");
dd84935
+		p = NULL;
dd84935
+		return PAM_USER_UNKNOWN;
dd84935
+	}
dd84935
+
dd84935
+	salt_len = strlen(salt);
dd84935
+	if (salt_len == 0) {
dd84935
+		return (nullok == 0) ? PAM_AUTH_ERR : PAM_SUCCESS;
dd84935
+	}
dd84935
+	if (p == NULL || strlen(p) == 0) {
dd84935
+		_pam_overwrite(salt);
dd84935
+		_pam_drop(salt);
dd84935
+		return PAM_AUTHTOK_ERR;
dd84935
+	}
dd84935
+
dd84935
+	/* the moment of truth -- do we agree with the password? */
dd84935
+	retval = PAM_AUTH_ERR;
dd84935
+	if (!strncmp(salt, "$1$", 3)) {
dd84935
+		pp = Goodcrypt_md5(p, salt);
dd84935
+		if (pp && strcmp(pp, salt) == 0) {
dd84935
+			retval = PAM_SUCCESS;
dd84935
+		} else {
dd84935
+			_pam_overwrite(pp);
dd84935
+			_pam_drop(pp);
dd84935
+			pp = Brokencrypt_md5(p, salt);
dd84935
+			if (pp && strcmp(pp, salt) == 0)
dd84935
+				retval = PAM_SUCCESS;
dd84935
+		}
dd84935
+	} else if (*salt == '$') {
dd84935
+	        /*
dd84935
+		 * Ok, we don't know the crypt algorithm, but maybe
dd84935
+		 * libcrypt nows about it? We should try it.
dd84935
+		 */
dd84935
+	        pp = x_strdup (crypt(p, salt));
dd84935
+		if (pp && strcmp(pp, salt) == 0) {
dd84935
+			retval = PAM_SUCCESS;
dd84935
+		}
dd84935
+	} else if (*salt == '*' || *salt == '!' || salt_len < 13) {
dd84935
+	    retval = PAM_AUTH_ERR;
dd84935
+	} else {
dd84935
+		pp = bigcrypt(p, salt);
dd84935
+		/*
dd84935
+		 * Note, we are comparing the bigcrypt of the password with
dd84935
+		 * the contents of the password field. If the latter was
dd84935
+		 * encrypted with regular crypt (and not bigcrypt) it will
dd84935
+		 * have been truncated for storage relative to the output
dd84935
+		 * of bigcrypt here. As such we need to compare only the
dd84935
+		 * stored string with the subset of bigcrypt's result.
dd84935
+		 * Bug 521314.
dd84935
+		 */
dd84935
+		if (pp && salt_len == 13 && strlen(pp) > salt_len) {
dd84935
+		    _pam_overwrite(pp+salt_len);
dd84935
+		}
dd84935
+		
dd84935
+		if (pp && strcmp(pp, salt) == 0) {
dd84935
+			retval = PAM_SUCCESS;
dd84935
+		}
dd84935
+	}
dd84935
+	p = NULL;		/* no longer needed here */
dd84935
+
dd84935
+	/* clean up */
dd84935
+	_pam_overwrite(pp);
dd84935
+	_pam_drop(pp);
dd84935
+
dd84935
+	return retval;
dd84935
+}
dd84935
+
dd84935
+char *
dd84935
+getuidname(uid_t uid)
dd84935
+{
dd84935
+	struct passwd *pw;
dd84935
+	static char username[256];
dd84935
+
dd84935
+	pw = getpwuid(uid);
dd84935
+	if (pw == NULL)
dd84935
+		return NULL;
dd84935
+
dd84935
+	strncpy(username, pw->pw_name, sizeof(username));
dd84935
+	username[sizeof(username) - 1] = '\0';
dd84935
+
dd84935
+	return username;
dd84935
+}
dd84935
+/*
dd84935
+ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
dd84935
+ * Copyright (c) Red Hat, Inc. 2007. All rights reserved
dd84935
+ *
dd84935
+ * Redistribution and use in source and binary forms, with or without
dd84935
+ * modification, are permitted provided that the following conditions
dd84935
+ * are met:
dd84935
+ * 1. Redistributions of source code must retain the above copyright
dd84935
+ *    notice, and the entire permission notice in its entirety,
dd84935
+ *    including the disclaimer of warranties.
dd84935
+ * 2. Redistributions in binary form must reproduce the above copyright
dd84935
+ *    notice, this list of conditions and the following disclaimer in the
dd84935
+ *    documentation and/or other materials provided with the distribution.
dd84935
+ * 3. The name of the author may not be used to endorse or promote
dd84935
+ *    products derived from this software without specific prior
dd84935
+ *    written permission.
dd84935
+ *
dd84935
+ * ALTERNATIVELY, this product may be distributed under the terms of
dd84935
+ * the GNU Public License, in which case the provisions of the GPL are
dd84935
+ * required INSTEAD OF the above restrictions.  (This clause is
dd84935
+ * necessary due to a potential bad interaction between the GPL and
dd84935
+ * the restrictions contained in a BSD-style copyright.)
dd84935
+ *
dd84935
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
dd84935
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
dd84935
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
dd84935
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
dd84935
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
dd84935
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
dd84935
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
dd84935
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
dd84935
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
dd84935
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
dd84935
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
dd84935
+ */
dd84935
--- Linux-PAM-0.99.7.1/modules/pam_unix/Makefile.am.update-helper	2006-12-18 19:50:50.000000000 +0100
dd84935
+++ Linux-PAM-0.99.7.1/modules/pam_unix/Makefile.am	2007-06-01 15:15:04.000000000 +0200
dd84935
@@ -16,7 +16,8 @@
dd84935
 secureconfdir = $(SCONFIGDIR)
dd84935
 
dd84935
 AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
dd84935
-	-DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\"
dd84935
+	-DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\" \
dd84935
+	-DUPDATE_HELPER=\"$(sbindir)/unix_update\"
dd84935
 
dd84935
 if HAVE_LIBSELINUX
dd84935
   AM_CFLAGS += -D"WITH_SELINUX"
dd84935
@@ -34,9 +35,9 @@
dd84935
 
dd84935
 securelib_LTLIBRARIES = pam_unix.la
dd84935
 
dd84935
-noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h
dd84935
+noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h passverify.h
dd84935
 
dd84935
-sbin_PROGRAMS = unix_chkpwd
dd84935
+sbin_PROGRAMS = unix_chkpwd unix_update
dd84935
 
dd84935
 noinst_PROGRAMS = bigcrypt
dd84935
 
dd84935
@@ -48,11 +49,16 @@
dd84935
 bigcrypt_CFLAGS = $(AM_CFLAGS)
dd84935
 bigcrypt_LDFLAGS = @LIBCRYPT@
dd84935
 
dd84935
-unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c
dd84935
+unix_chkpwd_SOURCES = unix_chkpwd.c passverify.c md5_good.c md5_broken.c bigcrypt.c
dd84935
 unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
dd84935
 unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@ -L$(top_builddir)/libpam -lpam \
dd84935
 	@LIBCRYPT@ @LIBSELINUX@
dd84935
 
dd84935
+unix_update_SOURCES = unix_update.c passverify.c md5_good.c md5_broken.c bigcrypt.c
dd84935
+unix_update_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
dd84935
+unix_update_LDFLAGS = @PIE_LDFLAGS@ -L$(top_builddir)/libpam -lpam \
dd84935
+	@LIBCRYPT@ @LIBSELINUX@
dd84935
+
dd84935
 if ENABLE_REGENERATE_MAN
dd84935
 noinst_DATA = README
dd84935
 README: pam_unix.8.xml