Package: libpam0g
Version: 0.79-3.2
Severity: normal
Tags: patch

Hi,

        I have an updated version of debian/patches-applied/57_selinux
 that has been updated to match current versions of SELinux. I have
 tested this patch, indeed, I am running this version of pam now.

        manoj


diff -uBbwr Linux-PAM-+patch56/configure.in Linux-PAM/configure.in
--- Linux-PAM-+patch56/configure.in	2006-10-19 16:44:22.000000000 -0500
+++ Linux-PAM/configure.in	2006-10-19 18:46:02.000000000 -0500
@@ -231,6 +231,13 @@
 if test $HAVE_LIBNSL = yes ; then
 	pwdblibs="$pwdblibs -lnsl"
 fi
+AC_CHECK_LIB(selinux, getfilecon, HAVE_LIBSELINUX=yes ; AC_DEFINE(HAVE_LIBSELINUX),
+	HAVE_LIBSELINUX=no)
+AC_SUBST(HAVE_LIBSELINUX)
+
+if test $HAVE_LIBSELINUX = yes ; then
+	pwdblibs="$pwdblibs -lselinux" 
+fi
 AC_CHECK_LIB(pwdb, pwdb_db_name, HAVE_LIBPWDB=yes ; AC_DEFINE(HAVE_LIBPWDB),
 	HAVE_LIBPWDB=no,$pwdblibs)
 AC_SUBST(HAVE_LIBPWDB)
diff -uBbwr Linux-PAM-+patch56/modules/pam_rootok/Makefile Linux-PAM/modules/pam_rootok/Makefile
--- Linux-PAM-+patch56/modules/pam_rootok/Makefile	2000-11-19 17:54:05.000000000 -0600
+++ Linux-PAM/modules/pam_rootok/Makefile	2006-10-19 18:46:26.000000000 -0500
@@ -13,3 +13,6 @@
 TITLE=pam_rootok
 
 include ../Simple.Rules
+
+CFLAGS += -DWITH_SELINUX
+LINK_PAMMODUTILS += -lselinux 
diff -uBbwr Linux-PAM-+patch56/modules/pam_rootok/pam_rootok.c Linux-PAM/modules/pam_rootok/pam_rootok.c
--- Linux-PAM-+patch56/modules/pam_rootok/pam_rootok.c	2002-05-26 18:00:28.000000000 -0500
+++ Linux-PAM/modules/pam_rootok/pam_rootok.c	2006-10-19 18:46:50.000000000 -0500
@@ -39,6 +39,11 @@
 }
 
 
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/av_permissions.h>
+#endif
+
 /* argument parsing */
 
 #define PAM_DEBUG_ARG       01
@@ -73,6 +78,9 @@
 
     ctrl = _pam_parse(argc, argv);
     if (getuid() == 0)
+#ifdef WITH_SELINUX
+      if (is_selinux_enabled()<1 || selinux_check_passwd_access(PASSWD__ROOTOK)==0)
+#endif
 	retval = PAM_SUCCESS;
 
     if (ctrl & PAM_DEBUG_ARG) {
Only in Linux-PAM/modules: pam_selinux
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/lckpwdf.-c Linux-PAM/modules/pam_unix/lckpwdf.-c
--- Linux-PAM-+patch56/modules/pam_unix/lckpwdf.-c	2000-06-20 17:12:02.000000000 -0500
+++ Linux-PAM/modules/pam_unix/lckpwdf.-c	2006-10-19 17:56:44.000000000 -0500
@@ -26,6 +26,9 @@
 
 #include <fcntl.h>
 #include <signal.h>
+#ifdef WITH_SELINUX
+#  include <selinux/selinux.h>
+#endif
 
 #define LOCKFILE "/etc/.pwd.lock"
 #define TIMEOUT 15
@@ -64,6 +67,28 @@
 	if (lockfd != -1)
 		return -1;
 
+#ifdef WITH_SELINUX
+       if (is_selinux_enabled() > 0)
+       {
+               lockfd = open(LOCKFILE, O_WRONLY);
+               if(lockfd == -1 && errno == ENOENT)
+               {
+                       security_context_t create_context;
+                       int rc;
+
+                       if(getfilecon("/etc/passwd", &create_context))
+                               return -1;
+                       rc = setfscreatecon(create_context);
+                       freecon(create_context);
+                       if(rc)
+                               return -1;
+                       lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
+                       if(setfscreatecon(NULL))
+                               return -1;
+               }
+       }
+       else
+#endif
 	lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
 	if (lockfd == -1)
 		return -1;
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/Makefile Linux-PAM/modules/pam_unix/Makefile
--- Linux-PAM-+patch56/modules/pam_unix/Makefile	2006-10-19 16:39:57.000000000 -0500
+++ Linux-PAM/modules/pam_unix/Makefile	2006-10-19 17:57:56.000000000 -0500
@@ -60,9 +60,9 @@
 ########################################################################
 
 CFLAGS += $(USE_CRACKLIB) $(USE_LCKPWDF) $(NEED_LCKPWDF) $(EXTRAS) \
-	 $(INCLUDE_PAMMODUTILS)
+	 $(INCLUDE_PAMMODUTILS) -DWITH_SELINUX
 
-LDLIBS = $(EXTRALS) $(LINK_PAMMODUTILS)
+LDLIBS = $(EXTRALS) $(LINK_PAMMODUTILS) -lselinux
 
 ifdef USE_CRACKLIB
 CRACKLIB = -lcrack
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/pam_unix_acct.c Linux-PAM/modules/pam_unix/pam_unix_acct.c
--- Linux-PAM-+patch56/modules/pam_unix/pam_unix_acct.c	2006-10-19 16:39:57.000000000 -0500
+++ Linux-PAM/modules/pam_unix/pam_unix_acct.c	2006-10-19 18:01:11.000000000 -0500
@@ -45,6 +45,15 @@
 #include <pwd.h>
 #include <shadow.h>
 #include <time.h>		/* for time() */
+#include <linux/limits.h>
+#include <errno.h>
+#include <sys/wait.h>
+#ifdef WITH_SELINUX
+#  include <selinux/selinux.h>
+#  define SELINUX_ENABLED is_selinux_enabled() > 0
+#else
+#  define SELINUX_ENABLED 0
+#endif
 
 #include <security/_pam_macros.h>
 
@@ -60,6 +69,112 @@
 #endif				/* LINUX_PAM */
 
 #include "support.h"
+struct spwd spwd;
+ 
+struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user)
+{
+  int retval=0, child, fds[2];
+  void (*sighandler)(int) = NULL;
+  D(("running verify_binary"));
+
+  /* create a pipe for the messages */
+  if (pipe(fds) != 0) {
+    D(("could not make pipe"));
+    _log_err(LOG_ERR, pamh, "Could not make pipe %s",strerror(errno));
+    return NULL;
+  }
+  D(("called."));
+  
+  if (off(UNIX_NOREAP, ctrl)) {
+    /*
+     * This code arranges that the demise of the child does not cause
+     * the application to receive a signal it is not expecting - which
+     * may kill the application or worse.
+     *
+     * The "noreap" module argument is provided so that the admin can
+     * override this behavior.
+     */
+    sighandler = signal(SIGCHLD, SIG_DFL);
+  }
+
+  /* fork */
+  child = fork();
+  if (child == 0) {
+    int i=0;
+    struct rlimit rlim;
+    static char *envp[] = { NULL };
+    char *args[] = { NULL, NULL, NULL, NULL };
+
+    close(0); close(1);
+    /* reopen stdin as pipe */
+    close(fds[0]);
+    dup2(fds[1], STDOUT_FILENO);
+    
+    /* XXX - should really tidy up PAM here too */
+    
+    if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
+      for (i=2; i < rlim.rlim_max; i++) {
+       if (fds[1] != i) {
+         close(i);
+       }
+      }        
+    }
+    /* exec binary helper */
+    args[0] = x_strdup(CHKPWD_HELPER);
+    args[1] = x_strdup(user);
+    args[2] = x_strdup("verify");
+    
+    execve(CHKPWD_HELPER, args, envp);
+    
+    _log_err(LOG_ERR, pamh, "helper binary execve failed: %s",strerror(errno));
+    /* should not get here: exit with error */
+    close (fds[1]);
+    D(("helper binary is not available"));
+    exit(PAM_AUTHINFO_UNAVAIL);
+  } else { 
+    close(fds[1]);
+    if (child > 0) {
+      char buf[1024];
+      int rc=0;
+      rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
+      if (rc<0) {
+       _log_err(LOG_ERR, pamh, "unix_chkpwd waitpid returned %d: %s", rc, strerror(errno));
+       retval = PAM_AUTH_ERR;
+      } else {
+       retval = WEXITSTATUS(retval);
+       if (retval != PAM_AUTHINFO_UNAVAIL) {
+          rc = _pammodutil_read(fds[0], buf, sizeof(buf) - 1);
+         if(rc > 0) {
+             buf[rc] = '\0';
+             if (sscanf(buf,"%ld:%ld:%ld:%ld:%ld:%ld",
+                    &spwd.sp_lstchg, /* last password change */
+                    &spwd.sp_min, /* days until change allowed. */
+                    &spwd.sp_max, /* days before change required */
+                    &spwd.sp_warn, /* days warning for expiration */
+                    &spwd.sp_inact, /* days before account inactive */
+                    &spwd.sp_expire) /* date when account expires */ != 6 ) retval = PAM_AUTH_ERR;
+           }           
+         else {
+           _log_err(LOG_ERR, pamh, " ERROR %d:%s \n",rc, strerror(errno)); retval = PAM_AUTH_ERR;
+         }
+       }
+      }
+    } else {
+      _log_err(LOG_ERR, pamh, "Fork failed %s \n",strerror(errno));
+      D(("fork failed"));
+      retval = PAM_AUTH_ERR;
+    }
+    close(fds[0]);
+  }
+  if (sighandler != NULL) {
+    (void) signal(SIGCHLD, sighandler);   /* restore old signal handler */
+  }
+  D(("Returning %d",retval));
+  if (retval != PAM_SUCCESS) {
+    return NULL;
+  } 
+  return &spwd;
+}      
  
 /*
  * PAM framework looks for this entry-point to pass control to the
@@ -128,6 +243,9 @@
 	else
 		return PAM_SUCCESS;
 
+       if (!spent && SELINUX_ENABLED ) 
+           spent = _unix_run_verify_binary(pamh, ctrl, uname);
+
 	if (!spent)
 		if (on(UNIX_BROKEN_SHADOW,ctrl))
 			return PAM_SUCCESS;
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/pam_unix_passwd.c Linux-PAM/modules/pam_unix/pam_unix_passwd.c
--- Linux-PAM-+patch56/modules/pam_unix/pam_unix_passwd.c	2006-10-19 16:44:14.000000000 -0500
+++ Linux-PAM/modules/pam_unix/pam_unix_passwd.c	2006-10-19 18:20:33.000000000 -0500
@@ -57,6 +57,19 @@
 #include <rpcsvc/yp_prot.h>
 #include <rpcsvc/ypclnt.h>
 
+#include <signal.h>
+#include <linux/limits.h>
+#include <errno.h>
+#include <sys/wait.h>
+#ifdef WITH_SELINUX
+static int selinux_enabled = -1;
+#include <selinux/selinux.h>
+static security_context_t prev_context = NULL;
+#define SELINUX_ENABLED (selinux_enabled != -1 ? selinux_enabled : (selinux_enabled = is_selinux_enabled() > 0))
+#else
+#define SELINUX_ENABLED 0
+#endif
+
 #ifdef USE_CRACKLIB
 #include <crack.h>
 #endif
@@ -273,36 +286,57 @@
     }
 
     oldmask = umask(077);
+
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      security_context_t passwd_context=NULL;
+      if (getfilecon("/etc/passwd",&passwd_context)<0) {
+        return PAM_AUTHTOK_ERR;
+      };
+      if (getfscreatecon(&prev_context)<0) {
+        freecon(passwd_context);
+        return PAM_AUTHTOK_ERR;
+      }
+      if (setfscreatecon(passwd_context)) {
+        freecon(passwd_context);
+        freecon(prev_context);
+        return PAM_AUTHTOK_ERR;
+      }
+      freecon(passwd_context);
+    }
+#endif
     pwfile = fopen(OPW_TMPFILE, "w");
     umask(oldmask);
     if (pwfile == NULL) {
-	return PAM_AUTHTOK_ERR;
+      err = 1;
+      goto done;
     }
 
     opwfile = fopen(OLD_PASSWORDS_FILE, "r");
     if (opwfile == NULL) {
 	fclose(pwfile);
-	return PAM_AUTHTOK_ERR;
+      err = 1;
+      goto done;
     }
 
-    if (fstat (fileno (opwfile), &st) == -1)
-      {
+    if (fstat(fileno(opwfile), &st) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
 
-    if (fchown (fileno (pwfile), st.st_uid, st.st_gid) == -1)
-      {
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
-    if (fchmod (fileno (pwfile), st.st_mode) == -1)
-      {
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
 
     while (fgets(buf, 16380, opwfile)) {
@@ -360,15 +394,28 @@
 	err = 1;
     }
 
+done:
     if (!err) {
-	if (!rename(OPW_TMPFILE, OLD_PASSWORDS_FILE)) {
-	    return PAM_SUCCESS;
+	if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
+	    err = 1;
+    }
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      if (setfscreatecon(prev_context)) {
+        err = 1;
 	}
+      if (prev_context)
+        freecon(prev_context);
+      prev_context=NULL;
     }
-
+#endif
+    if (!err) {
+	return PAM_SUCCESS;
+    } else {
     unlink(OPW_TMPFILE);
     return PAM_AUTHTOK_ERR;
 }
+}
 
 static int _update_passwd(pam_handle_t *pamh,
 			  const char *forwho, const char *towhat)
@@ -380,35 +427,56 @@
     int oldmask;
 
     oldmask = umask(077);
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      security_context_t passwd_context=NULL;
+      if (getfilecon("/etc/passwd",&passwd_context)<0) {
+	return PAM_AUTHTOK_ERR;
+      };
+      if (getfscreatecon(&prev_context)<0) {
+	freecon(passwd_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      if (setfscreatecon(passwd_context)) {
+	freecon(passwd_context);
+	freecon(prev_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      freecon(passwd_context);
+    }
+#endif
     pwfile = fopen(PW_TMPFILE, "w");
     umask(oldmask);
     if (pwfile == NULL) {
-	return PAM_AUTHTOK_ERR;
+      err = 1;
+      goto done;
     }
 
     opwfile = fopen("/etc/passwd", "r");
     if (opwfile == NULL) {
 	fclose(pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
     }
 
-    if (fstat (fileno (opwfile), &st) == -1)
-      {
+    if (fstat(fileno(opwfile), &st) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
 
-    if (fchown (fileno (pwfile), st.st_uid, st.st_gid) == -1)
-      {
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
-    if (fchmod (fileno (pwfile), st.st_mode) == -1)
-      {
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
+	err = 1;
+	goto done;
       }
 
     tmpent = fgetpwent (opwfile);
@@ -439,16 +507,30 @@
 	err = 1;
     }
 
+done:
     if (!err) {
-	if (!rename(PW_TMPFILE, "/etc/passwd")) {
+	if (!rename(PW_TMPFILE, "/etc/passwd"))
 	    _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho);
-	    return PAM_SUCCESS;
+	else
+	    err = 1;
 	}
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+	if (setfscreatecon(prev_context)) {
+	    err = 1;
     }
-
+	if (prev_context)
+	    freecon(prev_context);
+	prev_context = NULL;
+    }
+#endif
+    if (!err) {
+	return PAM_SUCCESS;
+    } else {
     unlink(PW_TMPFILE);
     return found ? PAM_AUTHTOK_ERR : PAM_USER_UNKNOWN;
 }
+}
 
 static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
 {
@@ -463,36 +545,57 @@
 	return PAM_USER_UNKNOWN;
     }
     oldmask = umask(077);
+
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      security_context_t shadow_context=NULL;
+      if (getfilecon("/etc/shadow",&shadow_context)<0) {
+	return PAM_AUTHTOK_ERR;
+      };
+      if (getfscreatecon(&prev_context)<0) {
+	freecon(shadow_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      if (setfscreatecon(shadow_context)) {
+	freecon(shadow_context);
+	freecon(prev_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      freecon(shadow_context);
+    }
+#endif
     pwfile = fopen(SH_TMPFILE, "w");
     umask(oldmask);
     if (pwfile == NULL) {
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
     }
 
     opwfile = fopen("/etc/shadow", "r");
     if (opwfile == NULL) {
 	fclose(pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
     }
 
-    if (fstat (fileno (opwfile), &st) == -1)
-      {
+    if (fstat(fileno(opwfile), &st) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
 
-    if (fchown (fileno (pwfile), st.st_uid, st.st_gid) == -1)
-      {
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
-    if (fchmod (fileno (pwfile), st.st_mode) == -1)
-      {
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
 
     stmpent = fgetspent(opwfile);
@@ -521,16 +624,32 @@
 	err = 1;
     }
 
+done:
     if (!err) {
-	if (!rename(SH_TMPFILE, "/etc/shadow")) {
+	if (!rename(SH_TMPFILE, "/etc/shadow"))
 	    _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho);
-	    return PAM_SUCCESS;
+	else
+	    err = 1;
+    }
+
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+	if (setfscreatecon(prev_context)) {
+	    err = 1;
 	}
+	if (prev_context)
+	    freecon(prev_context);
+	prev_context = NULL;
     }
+#endif
 
+    if (!err) {
+	return PAM_SUCCESS;
+    } else {
     unlink(SH_TMPFILE);
     return found ? PAM_AUTHTOK_ERR : PAM_USER_UNKNOWN;
 }
+}
 
 static int _do_setpass(pam_handle_t* pamh, const char *forwho, char *fromwhat,
 		       char *towhat, unsigned int ctrl, int remember)
@@ -665,6 +784,8 @@
 		spwdent = getspnam(user);
 		endspent();
 
+		if (spwdent == NULL && SELINUX_ENABLED ) 
+		    spwdent = _unix_run_verify_binary(pamh, ctrl, user);
 		if (spwdent == NULL)
 			return PAM_AUTHINFO_UNAVAIL;
 	} else {
@@ -1088,6 +1209,16 @@
 			}
 		}
 
+		/* A null pointer here indicates a memory failure
+		   somewhere along the way; don't set the password to
+		   NULL! */
+		if (tpass == NULL) {
+			_log_err(LOG_CRIT, pamh,
+			         "out of memory for password");
+			pass_new = pass_old = NULL;	/* tidy up */
+			return PAM_BUF_ERR;
+		}
+
 		D(("password processed"));
 
 		/* update the password database(s) -- race conditions..? */
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/support.c Linux-PAM/modules/pam_unix/support.c
--- Linux-PAM-+patch56/modules/pam_unix/support.c	2006-10-19 16:44:47.000000000 -0500
+++ Linux-PAM/modules/pam_unix/support.c	2006-10-19 18:21:40.000000000 -0500
@@ -15,6 +15,7 @@
 #include <pwd.h>
 #include <shadow.h>
 #include <limits.h>
+#include <linux/limits.h>
 #include <utmp.h>
 #include <errno.h>
 #include <signal.h>
@@ -27,7 +28,12 @@
 
 #include "md5.h"
 #include "support.h"
-
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#define SELINUX_ENABLED is_selinux_enabled()>0
+#else
+#define SELINUX_ENABLED 0
+#endif
 extern char *crypt(const char *key, const char *salt);
 extern char *bigcrypt(const char *key, const char *salt);
 
@@ -591,18 +597,32 @@
     /* fork */
     child = fork();
     if (child == 0) {
+        int i=0;
+        struct rlimit rlim;
 	static char *envp[] = { NULL };
-	char *args[] = { NULL, NULL, NULL };
+	char *args[] = { NULL, NULL, NULL, NULL };
 
 	/* XXX - should really tidy up PAM here too */
 
+	close(0); close(1);
 	/* reopen stdin as pipe */
 	close(fds[1]);
 	dup2(fds[0], STDIN_FILENO);
 
+	if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
+	  for (i=2; i < rlim.rlim_max; i++) {
+		if (fds[0] != i)
+	  	   close(i);
+	  }	
+	}
 	/* exec binary helper */
 	args[0] = x_strdup(CHKPWD_HELPER);
 	args[1] = x_strdup(user);
+	if (off(UNIX__NONULL, ctrl)) {
+	    args[2] = x_strdup("nullok");
+	} else {
+	    args[2] = x_strdup("nonull");
+	}
 
 	execve(CHKPWD_HELPER, args, envp);
 
@@ -612,23 +632,25 @@
     } else if (child > 0) {
 	/* wait for child */
 	/* if the stored password is NULL */
-	if (off(UNIX__NONULL, ctrl)) {	/* this means we've succeeded */
-	    write(fds[1], "nullok\0\0", 8);
-	} else {
-	    write(fds[1], "nonull\0\0", 8);
-	}
-	if (passwd != NULL) {            /* send the password to the child */
-	    write(fds[1], passwd, strlen(passwd)+1);
-	    passwd = NULL;
-	} else {
-	    write(fds[1], "", 1);                        /* blank password */
-	}
+        int rc=0;
+	if (passwd)
+	    _pammodutil_write(fds[1], passwd, strlen(passwd)+1);
+	else
+	    _pammodutil_write(fds[1], "", 1);	/* blank password */
+
 	close(fds[0]);       /* close here to avoid possible SIGPIPE above */
 	close(fds[1]);
-	(void) waitpid(child, &retval, 0);  /* wait for helper to complete */
-	retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR;
+	rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
+	if (rc<0) {
+	  _log_err(LOG_ERR, pamh, "unix_chkpwd waitpid returned %d: %s", rc, strerror(errno));
+	  retval = PAM_AUTH_ERR;
+	} else {
+	  retval = WEXITSTATUS(retval);
+	}
     } else {
 	D(("fork failed"));
+	close(fds[0]);
+ 	close(fds[1]);
 	retval = PAM_AUTH_ERR;
     }
 
@@ -716,7 +739,8 @@
 
 	retval = PAM_SUCCESS;
 	if (pwd == NULL || salt == NULL || !strcmp(salt, "x") || ((salt[0] == '#') && (salt[1] == '#') && !strcmp(salt + 2, name))) {
-		if (geteuid()) {
+	  
+		if (geteuid() || SELINUX_ENABLED) {
 			/* we are not root perhaps this is the reason? Run helper */
 			D(("running helper binary"));
 			retval = _unix_run_helper_binary(pamh, p, ctrl, name);
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/support.h Linux-PAM/modules/pam_unix/support.h
--- Linux-PAM-+patch56/modules/pam_unix/support.h	2006-10-19 16:44:47.000000000 -0500
+++ Linux-PAM/modules/pam_unix/support.h	2006-10-19 18:22:09.000000000 -0500
@@ -165,4 +165,5 @@
 extern unsigned int pass_min_len;
 extern unsigned int pass_max_len;
 
+extern struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user);
 #endif /* _PAM_UNIX_SUPPORT_H */
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/unix_chkpwd.c Linux-PAM/modules/pam_unix/unix_chkpwd.c
--- Linux-PAM-+patch56/modules/pam_unix/unix_chkpwd.c	2006-10-19 16:41:44.000000000 -0500
+++ Linux-PAM/modules/pam_unix/unix_chkpwd.c	2006-10-19 18:22:34.000000000 -0500
@@ -28,12 +28,25 @@
 #include <syslog.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <pwd.h>
 #include <shadow.h>
 #include <signal.h>
+#include <time.h>
+
+static int selinux_enabled = -1;
+
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#define SELINUX_ENABLED (selinux_enabled != -1 ? selinux_enabled : (selinux_enabled = is_selinux_enabled() > 0))
+static security_context_t prev_context=NULL;
+#else
+#define SELINUX_ENABLED 0
+#endif
 
 #define MAXPASS		200	/* the maximum length of a password */
 
+#include <security/_pam_types.h>
 #include <security/_pam_macros.h>
 
 #include "md5.h"
@@ -41,9 +54,6 @@
 extern char *crypt(const char *key, const char *salt);
 extern char *bigcrypt(const char *key, const char *salt);
 
-#define UNIX_PASSED	0
-#define UNIX_FAILED	1
-
 /* syslogging function for errors and other information */
 
 static void _log_err(int err, const char *format,...)
@@ -112,13 +122,40 @@
 	(void) sigaction(SIGQUIT, &action, NULL);
 }
 
+static int _verify_account(const char * const uname)
+{
+	struct spwd *spent;
+	struct passwd *pwent;
+
+	pwent = getpwnam(uname);
+	if (!pwent) {
+		_log_err(LOG_ALERT, "could not identify user (from getpwnam(%s))", uname);
+		return PAM_USER_UNKNOWN;
+	}
+
+	spent = getspnam( uname );
+	if (!spent) {
+		_log_err(LOG_ALERT, "could not get username from shadow (%s))", uname);
+		return PAM_AUTHINFO_UNAVAIL;	/* Couldn't get username from shadow */
+	}
+	printf("%ld:%ld:%ld:%ld:%ld:%ld",
+		 spent->sp_lstchg, /* last password change */
+                 spent->sp_min, /* days until change allowed. */
+                 spent->sp_max, /* days before change required */
+                 spent->sp_warn, /* days warning for expiration */
+                 spent->sp_inact, /* days before account inactive */
+                 spent->sp_expire); /* date when account expires */
+              
+	return PAM_SUCCESS;
+}
+
 static int _unix_verify_password(const char *name, const char *p, int nullok)
 {
 	struct passwd *pwd = NULL;
 	struct spwd *spwdent = NULL;
 	char *salt = NULL;
 	char *pp = NULL;
-	int retval = UNIX_FAILED;
+	int retval = PAM_AUTH_ERR;
 	int salt_len;
 
 	/* UNIX passwords area */
@@ -156,14 +193,14 @@
 	if (pwd == NULL || salt == NULL) {
 		_log_err(LOG_WARNING, "check pass; user unknown");
 		p = NULL;
-		return retval;
+		return PAM_USER_UNKNOWN;
 	}
 
 	salt_len = strlen(salt);
 	if (salt_len == 0)
-		return (nullok == 0) ? UNIX_FAILED : UNIX_PASSED;
+		return (nullok == 0) ? PAM_AUTH_ERR : PAM_SUCCESS;
 	else if (p == NULL || strlen(p) == 0)
-		return UNIX_FAILED;
+		return PAM_AUTHTOK_ERR;
 
 	/* Hack off SysVR4 password aging */
 	{
@@ -173,18 +210,18 @@
 	}
 
 	/* the moment of truth -- do we agree with the password? */
-	retval = UNIX_FAILED;
+	retval = PAM_AUTH_ERR;
 	if (!strncmp(salt, "$1$", 3)) {
 		pp = Goodcrypt_md5(p, salt);
 		if (strcmp(pp, salt) == 0) {
-			retval = UNIX_PASSED;
+			retval = PAM_SUCCESS;
 		} else {
 			pp = Brokencrypt_md5(p, salt);
 			if (strcmp(pp, salt) == 0)
-				retval = UNIX_PASSED;
+				retval = PAM_SUCCESS;
 		}
 	} else if ((*salt == '*') || (salt_len < 13)) {
-	    retval = UNIX_FAILED;
+	    retval = PAM_AUTH_ERR;
 	} else {
 		pp = bigcrypt(p, salt);
 		/*
@@ -197,7 +234,7 @@
 		 * Bug 521314: the strncmp comparison is for legacy support.
 		 */
 		if (strncmp(pp, salt, salt_len) == 0) {
-			retval = UNIX_PASSED;
+			retval = PAM_SUCCESS;
 		}
 	}
 	p = NULL;		/* no longer needed here */
@@ -234,10 +271,10 @@
 int main(int argc, char *argv[])
 {
 	char pass[MAXPASS + 1];
-	char option[8];
+	char *option;
 	int npass, nullok;
 	int force_failure = 0;
-	int retval = UNIX_FAILED;
+	int retval = PAM_AUTH_ERR;
 	char *user;
 
 	/*
@@ -254,8 +291,7 @@
 	 * account).
 	 */
 
-	if (isatty(STDIN_FILENO)) {
-
+	if (isatty(STDIN_FILENO) || argc != 3 ) {
 		_log_err(LOG_NOTICE
 		      ,"inappropriate use of Unix helper binary [UID=%d]"
 			 ,getuid());
@@ -263,35 +299,36 @@
 		 ,"This binary is not designed for running in this way\n"
 		      "-- the system administrator has been informed\n");
 		sleep(10);	/* this should discourage/annoy the user */
-		return UNIX_FAILED;
+		return PAM_SYSTEM_ERR;
 	}
 
 	/*
-	 * determine the current user's name is
+	 * Determine the current user's name.
+	 * On a SELinux enabled system, policy will prevent third
+	 * parties from using unix_chkpwd as a password guesser.
+	 * Leaving the existing check prevents su from working, since
+	 * the current uid is the user's and the password is for root.
 	 */
+	if (SELINUX_ENABLED) {
+	    user = argv[1];
+	} else {
 	user = getuidname(getuid());
-	if (argc == 2) {
-	    /* if the caller specifies the username, verify that user
-	       matches it */
 	    if (strcmp(user, argv[1])) {
-		force_failure = 1;
+		return PAM_AUTH_ERR;
 	    }
 	}
 
-	/* read the nullok/nonull option */
+	option = argv[2];
 
-	npass = read(STDIN_FILENO, option, 8);
+	if (strncmp(argv[2], "verify", 8) == 0) {
+	    /* Get the account information from the shadow file */
+	    return _verify_account(argv[1]);
+	}
 
-	if (npass < 0 || option == NULL || option[0] == '\0') {
-		_log_err(LOG_DEBUG, "no option supplied");
-		return UNIX_FAILED;
-	} else {
-		option[7] = '\0';
 		if (strncmp(option, "nullok", 8) == 0)
 			nullok = 1;
 		else
 			nullok = 0;
-	}
 
 	/* read the password from stdin (a pipe from the pam_unix module) */
 
@@ -324,10 +361,10 @@
 
 	/* return pass or fail */
 
-	if ((retval != UNIX_PASSED) || force_failure) {
-	    return UNIX_FAILED;
+	if ((retval != PAM_SUCCESS) || force_failure) {
+	    return PAM_AUTH_ERR;
 	} else {
-	    return UNIX_PASSED;
+	    return PAM_SUCCESS;
 	}
 }
 
-- System Information:
Debian Release: testing/unstable
  APT prefers unstable
  APT policy: (990, 'unstable'), (500, 'stable'), (1, 'experimental')
Architecture: i386 (i686)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.18-mh1-skas3-v9-pre9-fremap
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8) (ignored: LC_ALL 
set to en_US.UTF-8)

Versions of packages libpam0g depends on:
ii  libc6                        2.3.6.ds1-6 GNU C Library: Shared libraries
ii  libpam-runtime               0.79-3.2    Runtime support for the PAM librar

libpam0g recommends no packages.

-- no debconf information

-- 
Chisolm's First Corollary to Murphy's Second Law: When things just
can't possibly get any worse, they will.
Manoj Srivastava <[EMAIL PROTECTED]> <http://www.debian.org/~srivasta/>
1024D/BF24424C print 4966 F272 D093 B493 410B  924B 21BA DABB BF24 424C
diff -uBbwr Linux-PAM-+patch56/configure.in Linux-PAM/configure.in
--- Linux-PAM-+patch56/configure.in	2006-10-19 16:44:22.000000000 -0500
+++ Linux-PAM/configure.in	2006-10-19 18:46:02.000000000 -0500
@@ -231,6 +231,13 @@
 if test $HAVE_LIBNSL = yes ; then
 	pwdblibs="$pwdblibs -lnsl"
 fi
+AC_CHECK_LIB(selinux, getfilecon, HAVE_LIBSELINUX=yes ; AC_DEFINE(HAVE_LIBSELINUX),
+	HAVE_LIBSELINUX=no)
+AC_SUBST(HAVE_LIBSELINUX)
+
+if test $HAVE_LIBSELINUX = yes ; then
+	pwdblibs="$pwdblibs -lselinux" 
+fi
 AC_CHECK_LIB(pwdb, pwdb_db_name, HAVE_LIBPWDB=yes ; AC_DEFINE(HAVE_LIBPWDB),
 	HAVE_LIBPWDB=no,$pwdblibs)
 AC_SUBST(HAVE_LIBPWDB)
diff -uBbwr Linux-PAM-+patch56/modules/pam_rootok/Makefile Linux-PAM/modules/pam_rootok/Makefile
--- Linux-PAM-+patch56/modules/pam_rootok/Makefile	2000-11-19 17:54:05.000000000 -0600
+++ Linux-PAM/modules/pam_rootok/Makefile	2006-10-19 18:46:26.000000000 -0500
@@ -13,3 +13,6 @@
 TITLE=pam_rootok
 
 include ../Simple.Rules
+
+CFLAGS += -DWITH_SELINUX
+LINK_PAMMODUTILS += -lselinux 
diff -uBbwr Linux-PAM-+patch56/modules/pam_rootok/pam_rootok.c Linux-PAM/modules/pam_rootok/pam_rootok.c
--- Linux-PAM-+patch56/modules/pam_rootok/pam_rootok.c	2002-05-26 18:00:28.000000000 -0500
+++ Linux-PAM/modules/pam_rootok/pam_rootok.c	2006-10-19 18:46:50.000000000 -0500
@@ -39,6 +39,11 @@
 }
 
 
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/av_permissions.h>
+#endif
+
 /* argument parsing */
 
 #define PAM_DEBUG_ARG       01
@@ -73,6 +78,9 @@
 
     ctrl = _pam_parse(argc, argv);
     if (getuid() == 0)
+#ifdef WITH_SELINUX
+      if (is_selinux_enabled()<1 || selinux_check_passwd_access(PASSWD__ROOTOK)==0)
+#endif
 	retval = PAM_SUCCESS;
 
     if (ctrl & PAM_DEBUG_ARG) {
Only in Linux-PAM/modules: pam_selinux
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/lckpwdf.-c Linux-PAM/modules/pam_unix/lckpwdf.-c
--- Linux-PAM-+patch56/modules/pam_unix/lckpwdf.-c	2000-06-20 17:12:02.000000000 -0500
+++ Linux-PAM/modules/pam_unix/lckpwdf.-c	2006-10-19 17:56:44.000000000 -0500
@@ -26,6 +26,9 @@
 
 #include <fcntl.h>
 #include <signal.h>
+#ifdef WITH_SELINUX
+#  include <selinux/selinux.h>
+#endif
 
 #define LOCKFILE "/etc/.pwd.lock"
 #define TIMEOUT 15
@@ -64,6 +67,28 @@
 	if (lockfd != -1)
 		return -1;
 
+#ifdef WITH_SELINUX
+       if (is_selinux_enabled() > 0)
+       {
+               lockfd = open(LOCKFILE, O_WRONLY);
+               if(lockfd == -1 && errno == ENOENT)
+               {
+                       security_context_t create_context;
+                       int rc;
+
+                       if(getfilecon("/etc/passwd", &create_context))
+                               return -1;
+                       rc = setfscreatecon(create_context);
+                       freecon(create_context);
+                       if(rc)
+                               return -1;
+                       lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
+                       if(setfscreatecon(NULL))
+                               return -1;
+               }
+       }
+       else
+#endif
 	lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
 	if (lockfd == -1)
 		return -1;
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/Makefile Linux-PAM/modules/pam_unix/Makefile
--- Linux-PAM-+patch56/modules/pam_unix/Makefile	2006-10-19 16:39:57.000000000 -0500
+++ Linux-PAM/modules/pam_unix/Makefile	2006-10-19 17:57:56.000000000 -0500
@@ -60,9 +60,9 @@
 ########################################################################
 
 CFLAGS += $(USE_CRACKLIB) $(USE_LCKPWDF) $(NEED_LCKPWDF) $(EXTRAS) \
-	 $(INCLUDE_PAMMODUTILS)
+	 $(INCLUDE_PAMMODUTILS) -DWITH_SELINUX
 
-LDLIBS = $(EXTRALS) $(LINK_PAMMODUTILS)
+LDLIBS = $(EXTRALS) $(LINK_PAMMODUTILS) -lselinux
 
 ifdef USE_CRACKLIB
 CRACKLIB = -lcrack
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/pam_unix_acct.c Linux-PAM/modules/pam_unix/pam_unix_acct.c
--- Linux-PAM-+patch56/modules/pam_unix/pam_unix_acct.c	2006-10-19 16:39:57.000000000 -0500
+++ Linux-PAM/modules/pam_unix/pam_unix_acct.c	2006-10-19 18:01:11.000000000 -0500
@@ -45,6 +45,15 @@
 #include <pwd.h>
 #include <shadow.h>
 #include <time.h>		/* for time() */
+#include <linux/limits.h>
+#include <errno.h>
+#include <sys/wait.h>
+#ifdef WITH_SELINUX
+#  include <selinux/selinux.h>
+#  define SELINUX_ENABLED is_selinux_enabled() > 0
+#else
+#  define SELINUX_ENABLED 0
+#endif
 
 #include <security/_pam_macros.h>
 
@@ -60,6 +69,112 @@
 #endif				/* LINUX_PAM */
 
 #include "support.h"
+struct spwd spwd;
+ 
+struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user)
+{
+  int retval=0, child, fds[2];
+  void (*sighandler)(int) = NULL;
+  D(("running verify_binary"));
+
+  /* create a pipe for the messages */
+  if (pipe(fds) != 0) {
+    D(("could not make pipe"));
+    _log_err(LOG_ERR, pamh, "Could not make pipe %s",strerror(errno));
+    return NULL;
+  }
+  D(("called."));
+  
+  if (off(UNIX_NOREAP, ctrl)) {
+    /*
+     * This code arranges that the demise of the child does not cause
+     * the application to receive a signal it is not expecting - which
+     * may kill the application or worse.
+     *
+     * The "noreap" module argument is provided so that the admin can
+     * override this behavior.
+     */
+    sighandler = signal(SIGCHLD, SIG_DFL);
+  }
+
+  /* fork */
+  child = fork();
+  if (child == 0) {
+    int i=0;
+    struct rlimit rlim;
+    static char *envp[] = { NULL };
+    char *args[] = { NULL, NULL, NULL, NULL };
+
+    close(0); close(1);
+    /* reopen stdin as pipe */
+    close(fds[0]);
+    dup2(fds[1], STDOUT_FILENO);
+    
+    /* XXX - should really tidy up PAM here too */
+    
+    if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
+      for (i=2; i < rlim.rlim_max; i++) {
+       if (fds[1] != i) {
+         close(i);
+       }
+      }        
+    }
+    /* exec binary helper */
+    args[0] = x_strdup(CHKPWD_HELPER);
+    args[1] = x_strdup(user);
+    args[2] = x_strdup("verify");
+    
+    execve(CHKPWD_HELPER, args, envp);
+    
+    _log_err(LOG_ERR, pamh, "helper binary execve failed: %s",strerror(errno));
+    /* should not get here: exit with error */
+    close (fds[1]);
+    D(("helper binary is not available"));
+    exit(PAM_AUTHINFO_UNAVAIL);
+  } else { 
+    close(fds[1]);
+    if (child > 0) {
+      char buf[1024];
+      int rc=0;
+      rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
+      if (rc<0) {
+       _log_err(LOG_ERR, pamh, "unix_chkpwd waitpid returned %d: %s", rc, strerror(errno));
+       retval = PAM_AUTH_ERR;
+      } else {
+       retval = WEXITSTATUS(retval);
+       if (retval != PAM_AUTHINFO_UNAVAIL) {
+          rc = _pammodutil_read(fds[0], buf, sizeof(buf) - 1);
+         if(rc > 0) {
+             buf[rc] = '\0';
+             if (sscanf(buf,"%ld:%ld:%ld:%ld:%ld:%ld",
+                    &spwd.sp_lstchg, /* last password change */
+                    &spwd.sp_min, /* days until change allowed. */
+                    &spwd.sp_max, /* days before change required */
+                    &spwd.sp_warn, /* days warning for expiration */
+                    &spwd.sp_inact, /* days before account inactive */
+                    &spwd.sp_expire) /* date when account expires */ != 6 ) retval = PAM_AUTH_ERR;
+           }           
+         else {
+           _log_err(LOG_ERR, pamh, " ERROR %d:%s \n",rc, strerror(errno)); retval = PAM_AUTH_ERR;
+         }
+       }
+      }
+    } else {
+      _log_err(LOG_ERR, pamh, "Fork failed %s \n",strerror(errno));
+      D(("fork failed"));
+      retval = PAM_AUTH_ERR;
+    }
+    close(fds[0]);
+  }
+  if (sighandler != NULL) {
+    (void) signal(SIGCHLD, sighandler);   /* restore old signal handler */
+  }
+  D(("Returning %d",retval));
+  if (retval != PAM_SUCCESS) {
+    return NULL;
+  } 
+  return &spwd;
+}      
  
 /*
  * PAM framework looks for this entry-point to pass control to the
@@ -128,6 +243,9 @@
 	else
 		return PAM_SUCCESS;
 
+       if (!spent && SELINUX_ENABLED ) 
+           spent = _unix_run_verify_binary(pamh, ctrl, uname);
+
 	if (!spent)
 		if (on(UNIX_BROKEN_SHADOW,ctrl))
 			return PAM_SUCCESS;
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/pam_unix_passwd.c Linux-PAM/modules/pam_unix/pam_unix_passwd.c
--- Linux-PAM-+patch56/modules/pam_unix/pam_unix_passwd.c	2006-10-19 16:44:14.000000000 -0500
+++ Linux-PAM/modules/pam_unix/pam_unix_passwd.c	2006-10-19 18:20:33.000000000 -0500
@@ -57,6 +57,19 @@
 #include <rpcsvc/yp_prot.h>
 #include <rpcsvc/ypclnt.h>
 
+#include <signal.h>
+#include <linux/limits.h>
+#include <errno.h>
+#include <sys/wait.h>
+#ifdef WITH_SELINUX
+static int selinux_enabled = -1;
+#include <selinux/selinux.h>
+static security_context_t prev_context = NULL;
+#define SELINUX_ENABLED (selinux_enabled != -1 ? selinux_enabled : (selinux_enabled = is_selinux_enabled() > 0))
+#else
+#define SELINUX_ENABLED 0
+#endif
+
 #ifdef USE_CRACKLIB
 #include <crack.h>
 #endif
@@ -273,36 +286,57 @@
     }
 
     oldmask = umask(077);
+
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      security_context_t passwd_context=NULL;
+      if (getfilecon("/etc/passwd",&passwd_context)<0) {
+        return PAM_AUTHTOK_ERR;
+      };
+      if (getfscreatecon(&prev_context)<0) {
+        freecon(passwd_context);
+        return PAM_AUTHTOK_ERR;
+      }
+      if (setfscreatecon(passwd_context)) {
+        freecon(passwd_context);
+        freecon(prev_context);
+        return PAM_AUTHTOK_ERR;
+      }
+      freecon(passwd_context);
+    }
+#endif
     pwfile = fopen(OPW_TMPFILE, "w");
     umask(oldmask);
     if (pwfile == NULL) {
-	return PAM_AUTHTOK_ERR;
+      err = 1;
+      goto done;
     }
 
     opwfile = fopen(OLD_PASSWORDS_FILE, "r");
     if (opwfile == NULL) {
 	fclose(pwfile);
-	return PAM_AUTHTOK_ERR;
+      err = 1;
+      goto done;
     }
 
-    if (fstat (fileno (opwfile), &st) == -1)
-      {
+    if (fstat(fileno(opwfile), &st) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
 
-    if (fchown (fileno (pwfile), st.st_uid, st.st_gid) == -1)
-      {
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
-    if (fchmod (fileno (pwfile), st.st_mode) == -1)
-      {
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
 
     while (fgets(buf, 16380, opwfile)) {
@@ -360,15 +394,28 @@
 	err = 1;
     }
 
+done:
     if (!err) {
-	if (!rename(OPW_TMPFILE, OLD_PASSWORDS_FILE)) {
-	    return PAM_SUCCESS;
+	if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
+	    err = 1;
+    }
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      if (setfscreatecon(prev_context)) {
+        err = 1;
 	}
+      if (prev_context)
+        freecon(prev_context);
+      prev_context=NULL;
     }
-
+#endif
+    if (!err) {
+	return PAM_SUCCESS;
+    } else {
     unlink(OPW_TMPFILE);
     return PAM_AUTHTOK_ERR;
 }
+}
 
 static int _update_passwd(pam_handle_t *pamh,
 			  const char *forwho, const char *towhat)
@@ -380,35 +427,56 @@
     int oldmask;
 
     oldmask = umask(077);
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      security_context_t passwd_context=NULL;
+      if (getfilecon("/etc/passwd",&passwd_context)<0) {
+	return PAM_AUTHTOK_ERR;
+      };
+      if (getfscreatecon(&prev_context)<0) {
+	freecon(passwd_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      if (setfscreatecon(passwd_context)) {
+	freecon(passwd_context);
+	freecon(prev_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      freecon(passwd_context);
+    }
+#endif
     pwfile = fopen(PW_TMPFILE, "w");
     umask(oldmask);
     if (pwfile == NULL) {
-	return PAM_AUTHTOK_ERR;
+      err = 1;
+      goto done;
     }
 
     opwfile = fopen("/etc/passwd", "r");
     if (opwfile == NULL) {
 	fclose(pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
     }
 
-    if (fstat (fileno (opwfile), &st) == -1)
-      {
+    if (fstat(fileno(opwfile), &st) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
 
-    if (fchown (fileno (pwfile), st.st_uid, st.st_gid) == -1)
-      {
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
-    if (fchmod (fileno (pwfile), st.st_mode) == -1)
-      {
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
+	err = 1;
+	goto done;
       }
 
     tmpent = fgetpwent (opwfile);
@@ -439,16 +507,30 @@
 	err = 1;
     }
 
+done:
     if (!err) {
-	if (!rename(PW_TMPFILE, "/etc/passwd")) {
+	if (!rename(PW_TMPFILE, "/etc/passwd"))
 	    _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho);
-	    return PAM_SUCCESS;
+	else
+	    err = 1;
 	}
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+	if (setfscreatecon(prev_context)) {
+	    err = 1;
     }
-
+	if (prev_context)
+	    freecon(prev_context);
+	prev_context = NULL;
+    }
+#endif
+    if (!err) {
+	return PAM_SUCCESS;
+    } else {
     unlink(PW_TMPFILE);
     return found ? PAM_AUTHTOK_ERR : PAM_USER_UNKNOWN;
 }
+}
 
 static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
 {
@@ -463,36 +545,57 @@
 	return PAM_USER_UNKNOWN;
     }
     oldmask = umask(077);
+
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+      security_context_t shadow_context=NULL;
+      if (getfilecon("/etc/shadow",&shadow_context)<0) {
+	return PAM_AUTHTOK_ERR;
+      };
+      if (getfscreatecon(&prev_context)<0) {
+	freecon(shadow_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      if (setfscreatecon(shadow_context)) {
+	freecon(shadow_context);
+	freecon(prev_context);
+	return PAM_AUTHTOK_ERR;
+      }
+      freecon(shadow_context);
+    }
+#endif
     pwfile = fopen(SH_TMPFILE, "w");
     umask(oldmask);
     if (pwfile == NULL) {
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
     }
 
     opwfile = fopen("/etc/shadow", "r");
     if (opwfile == NULL) {
 	fclose(pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
     }
 
-    if (fstat (fileno (opwfile), &st) == -1)
-      {
+    if (fstat(fileno(opwfile), &st) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
 
-    if (fchown (fileno (pwfile), st.st_uid, st.st_gid) == -1)
-      {
+    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
-    if (fchmod (fileno (pwfile), st.st_mode) == -1)
-      {
+    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
 	fclose (opwfile);
 	fclose (pwfile);
-	return PAM_AUTHTOK_ERR;
+	err = 1;
+	goto done;
       }
 
     stmpent = fgetspent(opwfile);
@@ -521,16 +624,32 @@
 	err = 1;
     }
 
+done:
     if (!err) {
-	if (!rename(SH_TMPFILE, "/etc/shadow")) {
+	if (!rename(SH_TMPFILE, "/etc/shadow"))
 	    _log_err(LOG_NOTICE, pamh, "password changed for %s", forwho);
-	    return PAM_SUCCESS;
+	else
+	    err = 1;
+    }
+
+#ifdef WITH_SELINUX
+    if (SELINUX_ENABLED) {
+	if (setfscreatecon(prev_context)) {
+	    err = 1;
 	}
+	if (prev_context)
+	    freecon(prev_context);
+	prev_context = NULL;
     }
+#endif
 
+    if (!err) {
+	return PAM_SUCCESS;
+    } else {
     unlink(SH_TMPFILE);
     return found ? PAM_AUTHTOK_ERR : PAM_USER_UNKNOWN;
 }
+}
 
 static int _do_setpass(pam_handle_t* pamh, const char *forwho, char *fromwhat,
 		       char *towhat, unsigned int ctrl, int remember)
@@ -665,6 +784,8 @@
 		spwdent = getspnam(user);
 		endspent();
 
+		if (spwdent == NULL && SELINUX_ENABLED ) 
+		    spwdent = _unix_run_verify_binary(pamh, ctrl, user);
 		if (spwdent == NULL)
 			return PAM_AUTHINFO_UNAVAIL;
 	} else {
@@ -1088,6 +1209,16 @@
 			}
 		}
 
+		/* A null pointer here indicates a memory failure
+		   somewhere along the way; don't set the password to
+		   NULL! */
+		if (tpass == NULL) {
+			_log_err(LOG_CRIT, pamh,
+			         "out of memory for password");
+			pass_new = pass_old = NULL;	/* tidy up */
+			return PAM_BUF_ERR;
+		}
+
 		D(("password processed"));
 
 		/* update the password database(s) -- race conditions..? */
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/support.c Linux-PAM/modules/pam_unix/support.c
--- Linux-PAM-+patch56/modules/pam_unix/support.c	2006-10-19 16:44:47.000000000 -0500
+++ Linux-PAM/modules/pam_unix/support.c	2006-10-19 18:21:40.000000000 -0500
@@ -15,6 +15,7 @@
 #include <pwd.h>
 #include <shadow.h>
 #include <limits.h>
+#include <linux/limits.h>
 #include <utmp.h>
 #include <errno.h>
 #include <signal.h>
@@ -27,7 +28,12 @@
 
 #include "md5.h"
 #include "support.h"
-
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#define SELINUX_ENABLED is_selinux_enabled()>0
+#else
+#define SELINUX_ENABLED 0
+#endif
 extern char *crypt(const char *key, const char *salt);
 extern char *bigcrypt(const char *key, const char *salt);
 
@@ -591,18 +597,32 @@
     /* fork */
     child = fork();
     if (child == 0) {
+        int i=0;
+        struct rlimit rlim;
 	static char *envp[] = { NULL };
-	char *args[] = { NULL, NULL, NULL };
+	char *args[] = { NULL, NULL, NULL, NULL };
 
 	/* XXX - should really tidy up PAM here too */
 
+	close(0); close(1);
 	/* reopen stdin as pipe */
 	close(fds[1]);
 	dup2(fds[0], STDIN_FILENO);
 
+	if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
+	  for (i=2; i < rlim.rlim_max; i++) {
+		if (fds[0] != i)
+	  	   close(i);
+	  }	
+	}
 	/* exec binary helper */
 	args[0] = x_strdup(CHKPWD_HELPER);
 	args[1] = x_strdup(user);
+	if (off(UNIX__NONULL, ctrl)) {
+	    args[2] = x_strdup("nullok");
+	} else {
+	    args[2] = x_strdup("nonull");
+	}
 
 	execve(CHKPWD_HELPER, args, envp);
 
@@ -612,23 +632,25 @@
     } else if (child > 0) {
 	/* wait for child */
 	/* if the stored password is NULL */
-	if (off(UNIX__NONULL, ctrl)) {	/* this means we've succeeded */
-	    write(fds[1], "nullok\0\0", 8);
-	} else {
-	    write(fds[1], "nonull\0\0", 8);
-	}
-	if (passwd != NULL) {            /* send the password to the child */
-	    write(fds[1], passwd, strlen(passwd)+1);
-	    passwd = NULL;
-	} else {
-	    write(fds[1], "", 1);                        /* blank password */
-	}
+        int rc=0;
+	if (passwd)
+	    _pammodutil_write(fds[1], passwd, strlen(passwd)+1);
+	else
+	    _pammodutil_write(fds[1], "", 1);	/* blank password */
+
 	close(fds[0]);       /* close here to avoid possible SIGPIPE above */
 	close(fds[1]);
-	(void) waitpid(child, &retval, 0);  /* wait for helper to complete */
-	retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR;
+	rc=waitpid(child, &retval, 0);  /* wait for helper to complete */
+	if (rc<0) {
+	  _log_err(LOG_ERR, pamh, "unix_chkpwd waitpid returned %d: %s", rc, strerror(errno));
+	  retval = PAM_AUTH_ERR;
+	} else {
+	  retval = WEXITSTATUS(retval);
+	}
     } else {
 	D(("fork failed"));
+	close(fds[0]);
+ 	close(fds[1]);
 	retval = PAM_AUTH_ERR;
     }
 
@@ -716,7 +739,8 @@
 
 	retval = PAM_SUCCESS;
 	if (pwd == NULL || salt == NULL || !strcmp(salt, "x") || ((salt[0] == '#') && (salt[1] == '#') && !strcmp(salt + 2, name))) {
-		if (geteuid()) {
+	  
+		if (geteuid() || SELINUX_ENABLED) {
 			/* we are not root perhaps this is the reason? Run helper */
 			D(("running helper binary"));
 			retval = _unix_run_helper_binary(pamh, p, ctrl, name);
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/support.h Linux-PAM/modules/pam_unix/support.h
--- Linux-PAM-+patch56/modules/pam_unix/support.h	2006-10-19 16:44:47.000000000 -0500
+++ Linux-PAM/modules/pam_unix/support.h	2006-10-19 18:22:09.000000000 -0500
@@ -165,4 +165,5 @@
 extern unsigned int pass_min_len;
 extern unsigned int pass_max_len;
 
+extern struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user);
 #endif /* _PAM_UNIX_SUPPORT_H */
diff -uBbwr Linux-PAM-+patch56/modules/pam_unix/unix_chkpwd.c Linux-PAM/modules/pam_unix/unix_chkpwd.c
--- Linux-PAM-+patch56/modules/pam_unix/unix_chkpwd.c	2006-10-19 16:41:44.000000000 -0500
+++ Linux-PAM/modules/pam_unix/unix_chkpwd.c	2006-10-19 18:22:34.000000000 -0500
@@ -28,12 +28,25 @@
 #include <syslog.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <pwd.h>
 #include <shadow.h>
 #include <signal.h>
+#include <time.h>
+
+static int selinux_enabled = -1;
+
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#define SELINUX_ENABLED (selinux_enabled != -1 ? selinux_enabled : (selinux_enabled = is_selinux_enabled() > 0))
+static security_context_t prev_context=NULL;
+#else
+#define SELINUX_ENABLED 0
+#endif
 
 #define MAXPASS		200	/* the maximum length of a password */
 
+#include <security/_pam_types.h>
 #include <security/_pam_macros.h>
 
 #include "md5.h"
@@ -41,9 +54,6 @@
 extern char *crypt(const char *key, const char *salt);
 extern char *bigcrypt(const char *key, const char *salt);
 
-#define UNIX_PASSED	0
-#define UNIX_FAILED	1
-
 /* syslogging function for errors and other information */
 
 static void _log_err(int err, const char *format,...)
@@ -112,13 +122,40 @@
 	(void) sigaction(SIGQUIT, &action, NULL);
 }
 
+static int _verify_account(const char * const uname)
+{
+	struct spwd *spent;
+	struct passwd *pwent;
+
+	pwent = getpwnam(uname);
+	if (!pwent) {
+		_log_err(LOG_ALERT, "could not identify user (from getpwnam(%s))", uname);
+		return PAM_USER_UNKNOWN;
+	}
+
+	spent = getspnam( uname );
+	if (!spent) {
+		_log_err(LOG_ALERT, "could not get username from shadow (%s))", uname);
+		return PAM_AUTHINFO_UNAVAIL;	/* Couldn't get username from shadow */
+	}
+	printf("%ld:%ld:%ld:%ld:%ld:%ld",
+		 spent->sp_lstchg, /* last password change */
+                 spent->sp_min, /* days until change allowed. */
+                 spent->sp_max, /* days before change required */
+                 spent->sp_warn, /* days warning for expiration */
+                 spent->sp_inact, /* days before account inactive */
+                 spent->sp_expire); /* date when account expires */
+              
+	return PAM_SUCCESS;
+}
+
 static int _unix_verify_password(const char *name, const char *p, int nullok)
 {
 	struct passwd *pwd = NULL;
 	struct spwd *spwdent = NULL;
 	char *salt = NULL;
 	char *pp = NULL;
-	int retval = UNIX_FAILED;
+	int retval = PAM_AUTH_ERR;
 	int salt_len;
 
 	/* UNIX passwords area */
@@ -156,14 +193,14 @@
 	if (pwd == NULL || salt == NULL) {
 		_log_err(LOG_WARNING, "check pass; user unknown");
 		p = NULL;
-		return retval;
+		return PAM_USER_UNKNOWN;
 	}
 
 	salt_len = strlen(salt);
 	if (salt_len == 0)
-		return (nullok == 0) ? UNIX_FAILED : UNIX_PASSED;
+		return (nullok == 0) ? PAM_AUTH_ERR : PAM_SUCCESS;
 	else if (p == NULL || strlen(p) == 0)
-		return UNIX_FAILED;
+		return PAM_AUTHTOK_ERR;
 
 	/* Hack off SysVR4 password aging */
 	{
@@ -173,18 +210,18 @@
 	}
 
 	/* the moment of truth -- do we agree with the password? */
-	retval = UNIX_FAILED;
+	retval = PAM_AUTH_ERR;
 	if (!strncmp(salt, "$1$", 3)) {
 		pp = Goodcrypt_md5(p, salt);
 		if (strcmp(pp, salt) == 0) {
-			retval = UNIX_PASSED;
+			retval = PAM_SUCCESS;
 		} else {
 			pp = Brokencrypt_md5(p, salt);
 			if (strcmp(pp, salt) == 0)
-				retval = UNIX_PASSED;
+				retval = PAM_SUCCESS;
 		}
 	} else if ((*salt == '*') || (salt_len < 13)) {
-	    retval = UNIX_FAILED;
+	    retval = PAM_AUTH_ERR;
 	} else {
 		pp = bigcrypt(p, salt);
 		/*
@@ -197,7 +234,7 @@
 		 * Bug 521314: the strncmp comparison is for legacy support.
 		 */
 		if (strncmp(pp, salt, salt_len) == 0) {
-			retval = UNIX_PASSED;
+			retval = PAM_SUCCESS;
 		}
 	}
 	p = NULL;		/* no longer needed here */
@@ -234,10 +271,10 @@
 int main(int argc, char *argv[])
 {
 	char pass[MAXPASS + 1];
-	char option[8];
+	char *option;
 	int npass, nullok;
 	int force_failure = 0;
-	int retval = UNIX_FAILED;
+	int retval = PAM_AUTH_ERR;
 	char *user;
 
 	/*
@@ -254,8 +291,7 @@
 	 * account).
 	 */
 
-	if (isatty(STDIN_FILENO)) {
-
+	if (isatty(STDIN_FILENO) || argc != 3 ) {
 		_log_err(LOG_NOTICE
 		      ,"inappropriate use of Unix helper binary [UID=%d]"
 			 ,getuid());
@@ -263,35 +299,36 @@
 		 ,"This binary is not designed for running in this way\n"
 		      "-- the system administrator has been informed\n");
 		sleep(10);	/* this should discourage/annoy the user */
-		return UNIX_FAILED;
+		return PAM_SYSTEM_ERR;
 	}
 
 	/*
-	 * determine the current user's name is
+	 * Determine the current user's name.
+	 * On a SELinux enabled system, policy will prevent third
+	 * parties from using unix_chkpwd as a password guesser.
+	 * Leaving the existing check prevents su from working, since
+	 * the current uid is the user's and the password is for root.
 	 */
+	if (SELINUX_ENABLED) {
+	    user = argv[1];
+	} else {
 	user = getuidname(getuid());
-	if (argc == 2) {
-	    /* if the caller specifies the username, verify that user
-	       matches it */
 	    if (strcmp(user, argv[1])) {
-		force_failure = 1;
+		return PAM_AUTH_ERR;
 	    }
 	}
 
-	/* read the nullok/nonull option */
+	option = argv[2];
 
-	npass = read(STDIN_FILENO, option, 8);
+	if (strncmp(argv[2], "verify", 8) == 0) {
+	    /* Get the account information from the shadow file */
+	    return _verify_account(argv[1]);
+	}
 
-	if (npass < 0 || option == NULL || option[0] == '\0') {
-		_log_err(LOG_DEBUG, "no option supplied");
-		return UNIX_FAILED;
-	} else {
-		option[7] = '\0';
 		if (strncmp(option, "nullok", 8) == 0)
 			nullok = 1;
 		else
 			nullok = 0;
-	}
 
 	/* read the password from stdin (a pipe from the pam_unix module) */
 
@@ -324,10 +361,10 @@
 
 	/* return pass or fail */
 
-	if ((retval != UNIX_PASSED) || force_failure) {
-	    return UNIX_FAILED;
+	if ((retval != PAM_SUCCESS) || force_failure) {
+	    return PAM_AUTH_ERR;
 	} else {
-	    return UNIX_PASSED;
+	    return PAM_SUCCESS;
 	}
 }
 

Reply via email to