diff --git a/pam-0.78-unix-hpux-aging.patch b/pam-0.78-unix-hpux-aging.patch
deleted file mode 100644
index 7dbd34d..0000000
--- a/pam-0.78-unix-hpux-aging.patch
+++ /dev/null
@@ -1,76 +0,0 @@
-o For non-extensible-style hashes, strip off anything after the 13th character
- which would not be valid as part of a hash. On HP/UX, this clips off a comma
- followed by encoded aging information.
-
- The real problem is a complete lack of any standard for storing password
- aging information (actually, for anything having to do with password aging)
- for users across operating systems, but there's nothing we can do about that
- here.
-
---- Linux-PAM-0.78/modules/pam_unix/support.c.unix-hpux-aging 2004-10-06 16:05:17.000000000 +0200
-+++ Linux-PAM-0.78/modules/pam_unix/support.c 2004-11-23 14:55:27.885063264 +0100
-@@ -611,6 +611,21 @@
- return retval;
- }
-
-+static void strip_hpux_aging(char *p)
-+{
-+ const char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-+ "abcdefghijklmnopqrstuvwxyz"
-+ "0123456789./";
-+ if ((*p != '$') && (strlen(p) > 13)) {
-+ for (p += 13; *p != '\0'; p++) {
-+ if (strchr(valid, *p) == NULL) {
-+ *p = '\0';
-+ break;
-+ }
-+ }
-+ }
-+}
-+
- int _unix_verify_password(pam_handle_t * pamh, const char *name
- ,const char *p, unsigned int ctrl)
- {
-@@ -712,7 +727,9 @@
- retval = PAM_AUTHINFO_UNAVAIL;
- }
- } else {
-- int salt_len = strlen(salt);
-+ int salt_len;
-+ strip_hpux_aging(salt);
-+ salt_len = strlen(salt);
- if (!salt_len) {
- /* the stored password is NULL */
- if (off(UNIX__NONULL, ctrl)) {/* this means we've succeeded */
---- Linux-PAM-0.78/modules/pam_unix/unix_chkpwd.c.unix-hpux-aging 2004-11-18 14:41:20.000000000 +0100
-+++ Linux-PAM-0.78/modules/pam_unix/unix_chkpwd.c 2004-11-23 15:03:43.979169586 +0100
-@@ -112,6 +112,21 @@
- (void) sigaction(SIGQUIT, &action, NULL);
- }
-
-+static void strip_hpux_aging(char *p)
-+{
-+ const char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-+ "abcdefghijklmnopqrstuvwxyz"
-+ "0123456789./";
-+ if ((*p != '$') && (strlen(p) > 13)) {
-+ for (p += 13; *p != '\0'; p++) {
-+ if (strchr(valid, *p) == NULL) {
-+ *p = '\0';
-+ break;
-+ }
-+ }
-+ }
-+}
-+
- static int _unix_verify_password(const char *name, const char *p, int nullok)
- {
- struct passwd *pwd = NULL;
-@@ -159,6 +174,7 @@
- return retval;
- }
-
-+ strip_hpux_aging(salt);
- salt_len = strlen(salt);
- if (salt_len == 0)
- return (nullok == 0) ? UNIX_FAILED : UNIX_PASSED;
diff --git a/pam-0.99.6.2-namespace-docfix.patch b/pam-0.99.6.2-namespace-docfix.patch
new file mode 100644
index 0000000..66620c8
--- /dev/null
+++ b/pam-0.99.6.2-namespace-docfix.patch
@@ -0,0 +1,18 @@
+--- Linux-PAM-0.99.6.2/modules/pam_namespace/namespace.conf.5.xml.docfix 2007-04-03 17:51:29.000000000 +0200
++++ Linux-PAM-0.99.6.2/modules/pam_namespace/namespace.conf.5.xml 2007-04-23 19:04:10.000000000 +0200
+@@ -86,6 +86,15 @@
+ for all users.
+
+
++
++ In case of context or level polyinstantiation the SELinux context
++ which is used for polyinstantiation is the context used for executing
++ a new process as obtained by getexeccon. This context must be set
++ by the calling application or pam_selinux.so
++ module. If this context is not set the polyinstatiation will be
++ based just on user name.
++
++
+
+
+
diff --git a/pam-0.99.6.2-namespace-temp-logon.patch b/pam-0.99.6.2-namespace-temp-logon.patch
new file mode 100644
index 0000000..dc28fb2
--- /dev/null
+++ b/pam-0.99.6.2-namespace-temp-logon.patch
@@ -0,0 +1,433 @@
+--- Linux-PAM-0.99.6.2/modules/pam_namespace/pam_namespace.h.temp-logon 2007-05-31 17:04:17.000000000 +0200
++++ Linux-PAM-0.99.6.2/modules/pam_namespace/pam_namespace.h 2007-05-31 17:04:18.000000000 +0200
+@@ -90,6 +90,7 @@
+ #define PAMNS_NO_UNMOUNT_ON_CLOSE 0x00010000 /* no unmount at session close */
+
+ #define NAMESPACE_MAX_DIR_LEN 80
++#define NAMESPACE_POLYDIR_DATA "pam_namespace:polydir_data"
+
+ /*
+ * Polyinstantiation method options, based on user, security context
+@@ -100,6 +101,8 @@
+ USER,
+ CONTEXT,
+ LEVEL,
++ TMPDIR,
++ TMPFS
+ };
+
+ /*
+@@ -128,6 +131,7 @@
+ enum polymethod method; /* method used to polyinstantiate */
+ unsigned int num_uids; /* number of override uids */
+ uid_t *uid; /* list of override uids */
++ int exclusive; /* polyinstatiate exclusively for override uids */
+ struct polydir_s *next; /* pointer to the next polydir entry */
+ };
+
+--- Linux-PAM-0.99.6.2/modules/pam_namespace/pam_namespace.c.temp-logon 2007-05-31 17:04:18.000000000 +0200
++++ Linux-PAM-0.99.6.2/modules/pam_namespace/pam_namespace.c 2007-05-31 17:54:14.000000000 +0200
+@@ -43,6 +43,7 @@
+ strcpy(pent->instance_prefix, ent->instance_prefix);
+ pent->method = ent->method;
+ pent->num_uids = ent->num_uids;
++ pent->exclusive = ent->exclusive;
+ if (ent->num_uids) {
+ uid_t *pptr, *eptr;
+
+@@ -120,6 +121,10 @@
+ }
+ }
+
++static void cleanup_data(pam_handle_t *pamh, void *data, int err)
++{
++ del_polydir_list(data);
++}
+
+ /*
+ * Called from parse_config_file, this function processes a single line
+@@ -140,6 +145,7 @@
+
+ poly.uid = NULL;
+ poly.num_uids = 0;
++ poly.exclusive = 0;
+
+ /*
+ * skip the leading white space
+@@ -223,24 +229,13 @@
+ }
+
+ /*
+- * Ensure that all pathnames are absolute path names.
+- */
+- if ((dir[0] != '/') || (instance_prefix[0] != '/')) {
+- pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must start with '/'");
+- goto skipping;
+- }
+- if (strstr(dir, "..") || strstr(instance_prefix, "..")) {
+- pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must not contain '..'");
+- goto skipping;
+- }
+-
+- /*
+ * Populate polyinstantiated directory structure with appropriate
+ * pathnames and the method with which to polyinstantiate.
+ */
+ if (strlen(dir) >= sizeof(poly.dir)
+ || strlen(instance_prefix) >= sizeof(poly.instance_prefix)) {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
++ goto skipping;
+ }
+ strcpy(poly.dir, dir);
+ strcpy(poly.instance_prefix, instance_prefix);
+@@ -248,6 +243,18 @@
+ poly.method = NONE;
+ if (strcmp(method, "user") == 0)
+ poly.method = USER;
++
++ if (strcmp(method, "tmpdir") == 0) {
++ poly.method = TMPDIR;
++ if (sizeof(poly.instance_prefix) - strlen(poly.instance_prefix) < 7) {
++ pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
++ goto skipping;
++ }
++ strcat(poly.instance_prefix, "XXXXXX");
++ }
++
++ if (strcmp(method, "tmpfs") == 0)
++ poly.method = TMPFS;
+
+ #ifdef WITH_SELINUX
+ if (strcmp(method, "level") == 0) {
+@@ -266,12 +273,24 @@
+
+ #endif
+
+- if ( poly.method == NONE) {
++ if (poly.method == NONE) {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Illegal method");
+ goto skipping;
+ }
+
+ /*
++ * Ensure that all pathnames are absolute path names.
++ */
++ if ((dir[0] != '/') || (poly.method != TMPFS && instance_prefix[0] != '/')) {
++ pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames must start with '/'");
++ goto skipping;
++ }
++ if (strstr(dir, "..") || strstr(instance_prefix, "..")) {
++ pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames must not contain '..'");
++ goto skipping;
++ }
++
++ /*
+ * If the line in namespace.conf for a directory to polyinstantiate
+ * contains a list of override users (users for whom polyinstantiation
+ * is not performed), read the user ids, convert names into uids, and
+@@ -281,7 +300,11 @@
+ uid_t *uidptr;
+ const char *ustr, *sstr;
+ int count, i;
+-
++
++ if (*uids == '~') {
++ poly.exclusive = 1;
++ uids++;
++ }
+ for (count = 0, ustr = sstr = uids; sstr; ustr = sstr + 1, count++)
+ sstr = strchr(ustr, ',');
+
+@@ -419,6 +442,7 @@
+ * directory's list of override uids. If the uid is one of the override
+ * uids for the polyinstantiated directory, polyinstantiation is not
+ * performed for that user for that directory.
++ * If exclusive is set the returned values are opposite.
+ */
+ static int ns_override(struct polydir_s *polyptr, struct instance_data *idata,
+ uid_t uid)
+@@ -432,11 +456,11 @@
+
+ for (i = 0; i < polyptr->num_uids; i++) {
+ if (uid == polyptr->uid[i]) {
+- return 1;
++ return !polyptr->exclusive;
+ }
+ }
+
+- return 0;
++ return polyptr->exclusive;
+ }
+
+ /*
+@@ -622,6 +646,12 @@
+
+ #endif /* WITH_SELINUX */
+
++ case TMPDIR:
++ case TMPFS:
++ if ((*i_name=strdup("")) == NULL)
++ goto fail;
++ return PAM_SUCCESS;
++
+ default:
+ if (idata->flags & PAMNS_DEBUG)
+ pam_syslog(idata->pamh, LOG_ERR, "Unknown method");
+@@ -725,7 +755,7 @@
+ * execute it and pass directory to polyinstantiate and instance
+ * directory as arguments.
+ */
+-static int inst_init(const struct polydir_s *polyptr, char *ipath,
++static int inst_init(const struct polydir_s *polyptr, const char *ipath,
+ struct instance_data *idata)
+ {
+ pid_t rc, pid;
+@@ -791,11 +821,11 @@
+ * Create polyinstantiated instance directory (ipath).
+ */
+ #ifdef WITH_SELINUX
+-static int create_dirs(const struct polydir_s *polyptr, char *ipath,
++static int create_dirs(struct polydir_s *polyptr, char *ipath,
+ security_context_t icontext, security_context_t ocontext,
+ struct instance_data *idata)
+ #else
+-static int create_dirs(const struct polydir_s *polyptr, char *ipath,
++static int create_dirs(struct polydir_s *polyptr, char *ipath,
+ struct instance_data *idata)
+ #endif
+ {
+@@ -834,7 +864,17 @@
+ * attributes to match that of the original directory that is being
+ * polyinstantiated.
+ */
+- if (mkdir(ipath, S_IRUSR) < 0) {
++
++ if (polyptr->method == TMPDIR) {
++ if (mkdtemp(polyptr->instance_prefix) == NULL) {
++ pam_syslog(idata->pamh, LOG_ERR, "Error creating temporary instance %s, %m",
++ polyptr->instance_prefix);
++ polyptr->method = NONE; /* do not clean up! */
++ return PAM_SESSION_ERR;
++ }
++ /* copy the actual directory name to ipath */
++ strcpy(ipath, polyptr->instance_prefix);
++ } else if (mkdir(ipath, S_IRUSR) < 0) {
+ if (errno == EEXIST)
+ goto inst_init;
+ else {
+@@ -920,13 +960,12 @@
+ * security attributes, and performs bind mount to setup the process
+ * namespace.
+ */
+-static int ns_setup(const struct polydir_s *polyptr,
++static int ns_setup(struct polydir_s *polyptr,
+ struct instance_data *idata)
+ {
+ int retval = 0;
+ char *inst_dir = NULL;
+ char *instname = NULL;
+- char *dir;
+ #ifdef WITH_SELINUX
+ security_context_t instcontext = NULL, origcontext = NULL;
+ #endif
+@@ -935,9 +974,15 @@
+ pam_syslog(idata->pamh, LOG_DEBUG,
+ "Set namespace for directory %s", polyptr->dir);
+
+- dir = strrchr(polyptr->dir, '/');
+- if (dir && strlen(dir) > 1)
+- dir++;
++ if (polyptr->method == TMPFS) {
++ if (mount("tmpfs", polyptr->dir, "tmpfs", 0, NULL) < 0) {
++ pam_syslog(idata->pamh, LOG_ERR, "Error mounting tmpfs on %s, %m",
++ polyptr->dir);
++ return PAM_SESSION_ERR;
++ }
++ /* we must call inst_init after the mount in this case */
++ return inst_init(polyptr, "tmpfs", idata);
++ }
+
+ /*
+ * Obtain the name of instance pathname based on the
+@@ -1043,6 +1088,58 @@
+ return retval;
+ }
+
++static int cleanup_tmpdirs(struct instance_data *idata)
++{
++ struct polydir_s *pptr;
++ pid_t rc, pid;
++ sighandler_t osighand = NULL;
++ int status;
++
++ osighand = signal(SIGCHLD, SIG_DFL);
++ if (osighand == SIG_ERR) {
++ pam_syslog(idata->pamh, LOG_ERR, "Cannot set signal value");
++ rc = PAM_SESSION_ERR;
++ goto out;
++ }
++
++ for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
++ if (pptr->method == TMPDIR && access(pptr->instance_prefix, F_OK) == 0) {
++ pid = fork();
++ if (pid == 0) {
++#ifdef WITH_SELINUX
++ if (idata->flags & PAMNS_SELINUX_ENABLED) {
++ if (setexeccon(NULL) < 0)
++ exit(1);
++ }
++#endif
++ if (execl("/bin/rm", "/bin/rm", "-rf", pptr->instance_prefix, (char *)NULL) < 0)
++ exit(1);
++ } else if (pid > 0) {
++ while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
++ (errno == EINTR));
++ if (rc == (pid_t)-1) {
++ pam_syslog(idata->pamh, LOG_ERR, "waitpid failed- %m");
++ rc = PAM_SESSION_ERR;
++ goto out;
++ }
++ if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) {
++ pam_syslog(idata->pamh, LOG_ERR,
++ "Error removing %s", pptr->instance_prefix);
++ }
++ } else if (pid < 0) {
++ pam_syslog(idata->pamh, LOG_ERR,
++ "Cannot fork to run namespace init script, %m");
++ rc = PAM_SESSION_ERR;
++ goto out;
++ }
++ }
++ }
++
++ rc = PAM_SUCCESS;
++out:
++ signal(SIGCHLD, osighand);
++ return rc;
++}
+
+ /*
+ * This function checks to see if polyinstantiation is needed for any
+@@ -1111,13 +1208,22 @@
+ * disassociate from the parent namespace.
+ */
+ if (need_poly) {
++ if (pam_set_data(idata->pamh, NAMESPACE_POLYDIR_DATA, idata->polydirs_ptr,
++ cleanup_data) != PAM_SUCCESS) {
++ pam_syslog(idata->pamh, LOG_ERR,
++ "Unable to set namespace data");
++ return PAM_SYSTEM_ERR;
++ }
+ if (unshare(CLONE_NEWNS) < 0) {
+- pam_syslog(idata->pamh, LOG_ERR,
++ pam_set_data(idata->pamh, NAMESPACE_POLYDIR_DATA, NULL, NULL);
++ pam_syslog(idata->pamh, LOG_ERR,
+ "Unable to unshare from parent namespace, %m");
+ return PAM_SESSION_ERR;
+ }
+- } else
++ } else {
++ del_polydir_list(idata->polydirs_ptr);
+ return PAM_SUCCESS;
++ }
+
+ /*
+ * Again cycle through all polyinstantiated directories, this time,
+@@ -1144,7 +1250,8 @@
+ * umount
+ */
+ if ((changing_dir = cwd_in(pptr->dir, idata)) < 0) {
+- return PAM_SESSION_ERR;
++ retval = PAM_SESSION_ERR;
++ goto out;
+ } else if (changing_dir) {
+ if (idata->flags & PAMNS_DEBUG)
+ pam_syslog(idata->pamh, LOG_DEBUG, "changing cwd");
+@@ -1172,8 +1279,10 @@
+ int saved_errno = errno;
+ pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
+ pptr->dir);
+- if (saved_errno != EINVAL)
+- return PAM_SESSION_ERR;
++ if (saved_errno != EINVAL) {
++ retval = PAM_SESSION_ERR;
++ goto out;
++ }
+ } else if (idata->flags & PAMNS_DEBUG)
+ pam_syslog(idata->pamh, LOG_DEBUG, "Umount succeeded %s",
+ pptr->dir);
+@@ -1185,7 +1294,9 @@
+ break;
+ }
+ }
+-
++out:
++ if (retval != PAM_SUCCESS)
++ cleanup_tmpdirs(idata);
+ return retval;
+ }
+
+@@ -1224,8 +1335,10 @@
+ } else if (idata->flags & PAMNS_DEBUG)
+ pam_syslog(idata->pamh, LOG_DEBUG, "Unmount of %s succeeded",
+ pptr->dir);
+- }
++ }
+ }
++
++ cleanup_tmpdirs(idata);
+ return 0;
+ }
+
+@@ -1349,7 +1462,8 @@
+ } else if (idata.flags & PAMNS_DEBUG)
+ pam_syslog(idata.pamh, LOG_DEBUG, "Nothing to polyinstantiate");
+
+- del_polydir_list(idata.polydirs_ptr);
++ if (retval != PAM_SUCCESS)
++ del_polydir_list(idata.polydirs_ptr);
+ return retval;
+ }
+
+@@ -1364,6 +1478,7 @@
+ struct instance_data idata;
+ char *user_name;
+ struct passwd *pwd;
++ const void *polyptr;
+
+ /* init instance data */
+ idata.flags = 0;
+@@ -1425,16 +1540,12 @@
+ idata.user = user_name;
+ idata.uid = pwd->pw_uid;
+
+- /*
+- * Parse namespace configuration file which lists directories that
+- * are polyinstantiated, directories where instance directories are
+- * created and the method used for polyinstantiation.
+- */
+- retval = parse_config_file(&idata);
+- if ((retval != PAM_SUCCESS) || !idata.polydirs_ptr) {
+- del_polydir_list(idata.polydirs_ptr);
+- return PAM_SESSION_ERR;
+- }
++ retval = pam_get_data(idata.pamh, NAMESPACE_POLYDIR_DATA, &polyptr);
++ if (retval != PAM_SUCCESS || polyptr == NULL)
++ /* nothing to reset */
++ return PAM_SUCCESS;
++
++ idata.polydirs_ptr = polyptr;
+
+ if (idata.flags & PAMNS_DEBUG)
+ pam_syslog(idata.pamh, LOG_DEBUG, "Resetting namespace for pid %d",
+@@ -1449,7 +1560,9 @@
+ pam_syslog(idata.pamh, LOG_DEBUG,
+ "resetting namespace ok for pid %d", getpid());
+ }
+- del_polydir_list(idata.polydirs_ptr);
++
++ pam_set_data(idata.pamh, NAMESPACE_POLYDIR_DATA, NULL, NULL);
++
+ return PAM_SUCCESS;
+ }
+
diff --git a/pam-0.99.7.1-unix-bigcrypt.patch b/pam-0.99.7.1-unix-bigcrypt.patch
index e5f53d6..f7bdbed 100644
--- a/pam-0.99.7.1-unix-bigcrypt.patch
+++ b/pam-0.99.7.1-unix-bigcrypt.patch
@@ -1,15 +1,15 @@
---- Linux-PAM-0.99.7.1/modules/pam_unix/support.c.bigcrypt 2007-02-21 20:30:24.000000000 +0100
-+++ Linux-PAM-0.99.7.1/modules/pam_unix/support.c 2007-02-21 21:17:29.000000000 +0100
-@@ -694,7 +694,7 @@
+--- Linux-PAM-0.99.7.1/modules/pam_unix/support.c.bigcrypt 2007-01-23 10:41:21.000000000 +0100
++++ Linux-PAM-0.99.7.1/modules/pam_unix/support.c 2007-06-01 15:11:51.000000000 +0200
+@@ -679,7 +679,7 @@
}
}
} else {
-- int salt_len;
-+ size_t salt_len;
- strip_hpux_aging(salt);
- salt_len = strlen(salt);
+- int salt_len = strlen(salt);
++ size_t salt_len = strlen(salt);
if (!salt_len) {
-@@ -706,19 +706,19 @@
+ /* the stored password is NULL */
+ if (off(UNIX__NONULL, ctrl)) {/* this means we've succeeded */
+@@ -689,19 +689,19 @@
D(("user has empty password - access denied"));
retval = PAM_AUTH_ERR;
}
@@ -33,7 +33,7 @@
}
} else {
/*
-@@ -732,7 +732,7 @@
+@@ -715,7 +715,7 @@
/* the moment of truth -- do we agree with the password? */
D(("comparing state of pp[%s] and salt[%s]", pp, salt));
@@ -42,9 +42,9 @@
retval = PAM_SUCCESS;
} else {
retval = PAM_AUTH_ERR;
---- Linux-PAM-0.99.7.1/modules/pam_unix/unix_chkpwd.c.bigcrypt 2007-02-21 20:30:24.000000000 +0100
-+++ Linux-PAM-0.99.7.1/modules/pam_unix/unix_chkpwd.c 2007-02-21 21:18:57.000000000 +0100
-@@ -159,7 +159,7 @@
+--- Linux-PAM-0.99.7.1/modules/pam_unix/unix_chkpwd.c.bigcrypt 2006-10-24 12:01:49.000000000 +0200
++++ Linux-PAM-0.99.7.1/modules/pam_unix/unix_chkpwd.c 2007-06-01 15:08:46.000000000 +0200
+@@ -144,7 +144,7 @@
char *salt = NULL;
char *pp = NULL;
int retval = PAM_AUTH_ERR;
@@ -53,7 +53,7 @@
/* UNIX passwords area */
setpwent();
-@@ -205,6 +205,8 @@
+@@ -189,6 +189,8 @@
return (nullok == 0) ? PAM_AUTH_ERR : PAM_SUCCESS;
}
if (p == NULL || strlen(p) == 0) {
@@ -62,7 +62,7 @@
return PAM_AUTHTOK_ERR;
}
-@@ -212,11 +214,13 @@
+@@ -196,11 +198,13 @@
retval = PAM_AUTH_ERR;
if (!strncmp(salt, "$1$", 3)) {
pp = Goodcrypt_md5(p, salt);
@@ -78,7 +78,7 @@
retval = PAM_SUCCESS;
}
} else if (*salt == '$') {
-@@ -225,10 +229,10 @@
+@@ -209,10 +213,10 @@
* libcrypt nows about it? We should try it.
*/
pp = x_strdup (crypt(p, salt));
@@ -91,7 +91,7 @@
retval = PAM_AUTH_ERR;
} else {
pp = bigcrypt(p, salt);
-@@ -239,24 +243,21 @@
+@@ -223,24 +227,21 @@
* have been truncated for storage relative to the output
* of bigcrypt here. As such we need to compare only the
* stored string with the subset of bigcrypt's result.
diff --git a/pam-0.99.7.1-unix-hpux-aging.patch b/pam-0.99.7.1-unix-hpux-aging.patch
new file mode 100644
index 0000000..11d5274
--- /dev/null
+++ b/pam-0.99.7.1-unix-hpux-aging.patch
@@ -0,0 +1,77 @@
+o For non-extensible-style hashes, strip off anything after the 13th character
+ which would not be valid as part of a hash. On HP/UX, this clips off a comma
+ followed by encoded aging information.
+
+ The real problem is a complete lack of any standard for storing password
+ aging information (actually, for anything having to do with password aging)
+ for users across operating systems, but there's nothing we can do about that
+ here.
+
+--- Linux-PAM-0.99.7.1/modules/pam_unix/support.c.unix-hpux-aging 2007-06-01 15:21:08.000000000 +0200
++++ Linux-PAM-0.99.7.1/modules/pam_unix/support.c 2007-06-01 15:24:32.000000000 +0200
+@@ -573,6 +573,21 @@
+ return retval;
+ }
+
++static void strip_hpux_aging(char *p)
++{
++ const char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
++ "abcdefghijklmnopqrstuvwxyz"
++ "0123456789./";
++ if ((*p != '$') && (strlen(p) > 13)) {
++ for (p += 13; *p != '\0'; p++) {
++ if (strchr(valid, *p) == NULL) {
++ *p = '\0';
++ break;
++ }
++ }
++ }
++}
++
+ int _unix_verify_password(pam_handle_t * pamh, const char *name
+ ,const char *p, unsigned int ctrl)
+ {
+@@ -679,7 +694,9 @@
+ }
+ }
+ } else {
+- size_t salt_len = strlen(salt);
++ size_t salt_len;
++ strip_hpux_aging(salt);
++ salt_len = strlen(salt);
+ if (!salt_len) {
+ /* the stored password is NULL */
+ if (off(UNIX__NONULL, ctrl)) {/* this means we've succeeded */
+--- Linux-PAM-0.99.7.1/modules/pam_unix/passverify.c.unix-hpux-aging 2007-06-01 15:21:08.000000000 +0200
++++ Linux-PAM-0.99.7.1/modules/pam_unix/passverify.c 2007-06-01 15:26:26.000000000 +0200
+@@ -146,6 +146,22 @@
+ return i;
+ }
+
++static void
++strip_hpux_aging(char *p)
++{
++ const char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
++ "abcdefghijklmnopqrstuvwxyz"
++ "0123456789./";
++ if ((*p != '$') && (strlen(p) > 13)) {
++ for (p += 13; *p != '\0'; p++) {
++ if (strchr(valid, *p) == NULL) {
++ *p = '\0';
++ break;
++ }
++ }
++ }
++}
++
+ int
+ _unix_verify_password(const char *name, const char *p, int nullok)
+ {
+@@ -194,6 +210,7 @@
+ return PAM_USER_UNKNOWN;
+ }
+
++ strip_hpux_aging(salt);
+ salt_len = strlen(salt);
+ if (salt_len == 0) {
+ return (nullok == 0) ? PAM_AUTH_ERR : PAM_SUCCESS;
diff --git a/pam-0.99.7.1-unix-update-helper.patch b/pam-0.99.7.1-unix-update-helper.patch
new file mode 100644
index 0000000..b8d253c
--- /dev/null
+++ b/pam-0.99.7.1-unix-update-helper.patch
@@ -0,0 +1,2548 @@
+--- /dev/null 2007-05-28 11:10:34.936447748 +0200
++++ Linux-PAM-0.99.7.1/modules/pam_unix/passupdate.c 2007-06-01 15:13:57.000000000 +0200
+@@ -0,0 +1,560 @@
++/*
++ * Main coding by Elliot Lee , Red Hat Software.
++ * Copyright (C) 1996.
++ * Copyright (c) Jan R�korajski, 1999.
++ * Copyright (c) Red Hat, Inc., 2007
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, and the entire permission notice in its entirety,
++ * including the disclaimer of warranties.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote
++ * products derived from this software without specific prior
++ * written permission.
++ *
++ * ALTERNATIVELY, this product may be distributed under the terms of
++ * the GNU Public License, in which case the provisions of the GPL are
++ * required INSTEAD OF the above restrictions. (This clause is
++ * necessary due to a potential bad interaction between the GPL and
++ * the restrictions contained in a BSD-style copyright.)
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++ * OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++/* this will be included from module and update helper */
++
++#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
++# include "./lckpwdf.-c"
++#endif
++
++/* passwd/salt conversion macros */
++
++#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
++#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
++
++#define PW_TMPFILE "/etc/npasswd"
++#define SH_TMPFILE "/etc/nshadow"
++#define OPW_TMPFILE "/etc/security/nopasswd"
++#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
++
++/*
++ * i64c - convert an integer to a radix 64 character
++ */
++static int i64c(int i)
++{
++ if (i < 0)
++ return ('.');
++ else if (i > 63)
++ return ('z');
++ if (i == 0)
++ return ('.');
++ if (i == 1)
++ return ('/');
++ if (i >= 2 && i <= 11)
++ return ('0' - 2 + i);
++ if (i >= 12 && i <= 37)
++ return ('A' - 12 + i);
++ if (i >= 38 && i <= 63)
++ return ('a' - 38 + i);
++ return ('\0');
++}
++
++static char *crypt_md5_wrapper(const char *pass_new)
++{
++ /*
++ * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
++ * removed use of static variables (AGM)
++ */
++
++ struct timeval tv;
++ MD5_CTX ctx;
++ unsigned char result[16];
++ char *cp = (char *) result;
++ unsigned char tmp[16];
++ int i;
++ char *x = NULL;
++
++ GoodMD5Init(&ctx);
++ gettimeofday(&tv, (struct timezone *) 0);
++ GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
++ i = getpid();
++ GoodMD5Update(&ctx, (void *) &i, sizeof i);
++ i = clock();
++ GoodMD5Update(&ctx, (void *) &i, sizeof i);
++ GoodMD5Update(&ctx, result, sizeof result);
++ GoodMD5Final(tmp, &ctx);
++ strcpy(cp, "$1$"); /* magic for the MD5 */
++ cp += strlen(cp);
++ for (i = 0; i < 8; i++)
++ *cp++ = i64c(tmp[i] & 077);
++ *cp = '\0';
++
++ /* no longer need cleartext */
++ x = Goodcrypt_md5(pass_new, (const char *) result);
++
++ return x;
++}
++
++#ifdef USE_LCKPWDF
++static int lock_pwdf(void)
++{
++ int i;
++ int retval;
++
++#ifndef HELPER_COMPILE
++ if (selinux_confined()) {
++ return PAM_SUCCESS;
++ }
++#endif
++ /* These values for the number of attempts and the sleep time
++ are, of course, completely arbitrary.
++ My reading of the PAM docs is that, once pam_chauthtok() has been
++ called with PAM_UPDATE_AUTHTOK, we are obliged to take any
++ reasonable steps to make sure the token is updated; so retrying
++ for 1/10 sec. isn't overdoing it. */
++ i=0;
++ while((retval = lckpwdf()) != 0 && i < 100) {
++ usleep(1000);
++ i++;
++ }
++ if(retval != 0) {
++ return PAM_AUTHTOK_LOCK_BUSY;
++ }
++ return PAM_SUCCESS;
++}
++
++static void unlock_pwdf(void)
++{
++#ifndef HELPER_COMPILE
++ if (selinux_confined()) {
++ return;
++ }
++#endif
++ ulckpwdf();
++}
++#endif
++
++static int
++save_old_password(const char *forwho, const char *oldpass,
++ int howmany)
++{
++ static char buf[16384];
++ static char nbuf[16384];
++ char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
++ int npas;
++ FILE *pwfile, *opwfile;
++ int err = 0;
++ int oldmask;
++ int found = 0;
++ struct passwd *pwd = NULL;
++ struct stat st;
++
++ if (howmany < 0) {
++ return PAM_SUCCESS;
++ }
++
++ if (oldpass == NULL) {
++ return PAM_SUCCESS;
++ }
++
++ 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) {
++ err = 1;
++ goto done;
++ }
++
++ opwfile = fopen(OLD_PASSWORDS_FILE, "r");
++ if (opwfile == NULL) {
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++
++ if (fstat(fileno(opwfile), &st) == -1) {
++ fclose(opwfile);
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++
++ if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
++ fclose(opwfile);
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++ if (fchmod(fileno(pwfile), st.st_mode) == -1) {
++ fclose(opwfile);
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++
++ while (fgets(buf, 16380, opwfile)) {
++ if (!strncmp(buf, forwho, strlen(forwho))) {
++ char *sptr = NULL;
++ found = 1;
++ if (howmany == 0)
++ continue;
++ buf[strlen(buf) - 1] = '\0';
++ s_luser = strtok_r(buf, ":", &sptr);
++ s_uid = strtok_r(NULL, ":", &sptr);
++ s_npas = strtok_r(NULL, ":", &sptr);
++ s_pas = strtok_r(NULL, ":", &sptr);
++ npas = strtol(s_npas, NULL, 10) + 1;
++ while (npas > howmany) {
++ s_pas = strpbrk(s_pas, ",");
++ if (s_pas != NULL)
++ s_pas++;
++ npas--;
++ }
++ pass = crypt_md5_wrapper(oldpass);
++ if (s_pas == NULL)
++ snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
++ s_luser, s_uid, npas, pass);
++ else
++ snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
++ s_luser, s_uid, npas, s_pas, pass);
++ _pam_delete(pass);
++ if (fputs(nbuf, pwfile) < 0) {
++ err = 1;
++ break;
++ }
++ } else if (fputs(buf, pwfile) < 0) {
++ err = 1;
++ break;
++ }
++ }
++ fclose(opwfile);
++
++ if (!found) {
++ pwd = getpwnam(forwho);
++ if (pwd == NULL) {
++ err = 1;
++ } else {
++ pass = crypt_md5_wrapper(oldpass);
++ snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
++ forwho, (unsigned long)pwd->pw_uid, pass);
++ _pam_delete(pass);
++ if (fputs(nbuf, pwfile) < 0) {
++ err = 1;
++ }
++ }
++ }
++
++ if (fclose(pwfile)) {
++ D(("error writing entries to old passwords file: %m"));
++ err = 1;
++ }
++
++done:
++ if (!err) {
++ 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;
++ }
++}
++
++#ifdef HELPER_COMPILE
++static int
++_update_passwd(const char *forwho, const char *towhat)
++#else
++static int
++_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat)
++#endif
++{
++ struct passwd *tmpent = NULL;
++ struct stat st;
++ FILE *pwfile, *opwfile;
++ int err = 1;
++ 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) {
++ err = 1;
++ goto done;
++ }
++
++ opwfile = fopen("/etc/passwd", "r");
++ if (opwfile == NULL) {
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++
++ if (fstat(fileno(opwfile), &st) == -1) {
++ fclose(opwfile);
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++
++ if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
++ fclose(opwfile);
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++ if (fchmod(fileno(pwfile), st.st_mode) == -1) {
++ fclose(opwfile);
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++
++ tmpent = fgetpwent(opwfile);
++ while (tmpent) {
++ if (!strcmp(tmpent->pw_name, forwho)) {
++ /* To shut gcc up */
++ union {
++ const char *const_charp;
++ char *charp;
++ } assigned_passwd;
++ assigned_passwd.const_charp = towhat;
++
++ tmpent->pw_passwd = assigned_passwd.charp;
++ err = 0;
++ }
++ if (putpwent(tmpent, pwfile)) {
++ D(("error writing entry to password file: %m"));
++ err = 1;
++ break;
++ }
++ tmpent = fgetpwent(opwfile);
++ }
++ fclose(opwfile);
++
++ if (fclose(pwfile)) {
++ D(("error writing entries to password file: %m"));
++ err = 1;
++ }
++
++done:
++ if (!err) {
++ if (!rename(PW_TMPFILE, "/etc/passwd"))
++#ifdef HELPER_COMPILE
++ _log_err(
++#else
++ pam_syslog(pamh,
++#endif
++ LOG_NOTICE, "password changed for %s", forwho);
++ 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 PAM_AUTHTOK_ERR;
++ }
++}
++
++#ifdef HELPER_COMPILE
++static int
++_update_shadow(const char *forwho, char *towhat)
++#else
++static int
++_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
++#endif
++{
++ struct spwd *spwdent = NULL, *stmpent = NULL;
++ struct stat st;
++ FILE *pwfile, *opwfile;
++ int err = 1;
++ int oldmask;
++
++ spwdent = getspnam(forwho);
++ if (spwdent == NULL) {
++ 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) {
++ err = 1;
++ goto done;
++ }
++
++ opwfile = fopen("/etc/shadow", "r");
++ if (opwfile == NULL) {
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++
++ if (fstat(fileno(opwfile), &st) == -1) {
++ fclose(opwfile);
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++
++ if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
++ fclose(opwfile);
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++ if (fchmod(fileno(pwfile), st.st_mode) == -1) {
++ fclose(opwfile);
++ fclose(pwfile);
++ err = 1;
++ goto done;
++ }
++
++ stmpent = fgetspent(opwfile);
++ while (stmpent) {
++
++ if (!strcmp(stmpent->sp_namp, forwho)) {
++ stmpent->sp_pwdp = towhat;
++ stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
++ err = 0;
++ D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
++ }
++
++ if (putspent(stmpent, pwfile)) {
++ D(("error writing entry to shadow file: %m"));
++ err = 1;
++ break;
++ }
++
++ stmpent = fgetspent(opwfile);
++ }
++ fclose(opwfile);
++
++ if (fclose(pwfile)) {
++ D(("error writing entries to shadow file: %m"));
++ err = 1;
++ }
++
++ done:
++ if (!err) {
++ if (!rename(SH_TMPFILE, "/etc/shadow"))
++#ifdef HELPER_COMPILE
++ _log_err(
++#else
++ pam_syslog(pamh,
++#endif
++ LOG_NOTICE, "password changed for %s", forwho);
++ 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 PAM_AUTHTOK_ERR;
++ }
++}
+--- Linux-PAM-0.99.7.1/modules/pam_unix/pam_unix_acct.c.update-helper 2006-06-27 10:38:14.000000000 +0200
++++ Linux-PAM-0.99.7.1/modules/pam_unix/pam_unix_acct.c 2007-06-01 15:13:57.000000000 +0200
+@@ -124,11 +124,11 @@
+ }
+
+ /* exec binary helper */
+- args[0] = x_strdup(CHKPWD_HELPER);
++ args[0] = x_strdup(UPDATE_HELPER);
+ args[1] = x_strdup(user);
+ args[2] = x_strdup("verify");
+
+- execve(CHKPWD_HELPER, args, envp);
++ execve(UPDATE_HELPER, args, envp);
+
+ pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %m");
+ /* should not get here: exit with error */
+@@ -142,11 +142,11 @@
+ int rc=0;
+ rc=waitpid(child, &retval, 0); /* wait for helper to complete */
+ if (rc<0) {
+- pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
++ pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
+ retval = PAM_AUTH_ERR;
+ } else {
+ retval = WEXITSTATUS(retval);
+- if (retval != PAM_AUTHINFO_UNAVAIL) {
++ if (retval == PAM_SUCCESS) {
+ rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
+ if(rc > 0) {
+ buf[rc] = '\0';
+@@ -157,15 +157,15 @@
+ &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 {
+- pam_syslog(pamh, LOG_ERR, " ERROR %d: %m", rc); retval = PAM_AUTH_ERR;
++ } else {
++ pam_syslog(pamh, LOG_ERR, "read failed: %m"); retval = PAM_AUTH_ERR;
+ }
++ } else {
++ pam_syslog(pamh, LOG_ERR, "unix_update returned error %d", retval);
+ }
+ }
+ } else {
+- pam_syslog(pamh, LOG_ERR, "Fork failed: %m");
+- D(("fork failed"));
++ pam_syslog(pamh, LOG_ERR, "fork failed: %m");
+ retval = PAM_AUTH_ERR;
+ }
+ close(fds[0]);
+@@ -247,6 +247,8 @@
+ setreuid( -1, save_euid );
+ }
+
++ } else if (geteuid() != 0) { /* cannot read shadow when non root */
++ return PAM_IGNORE;
+ } else if (_unix_shadowed (pwent))
+ spent = pam_modutil_getspnam (pamh, uname);
+ else
+--- Linux-PAM-0.99.7.1/modules/pam_unix/pam_unix_passwd.c.update-helper 2007-06-01 15:13:57.000000000 +0200
++++ Linux-PAM-0.99.7.1/modules/pam_unix/pam_unix_passwd.c 2007-06-01 15:13:57.000000000 +0200
+@@ -2,6 +2,7 @@
+ * Main coding by Elliot Lee , Red Hat Software.
+ * Copyright (C) 1996.
+ * Copyright (c) Jan R�korajski, 1999.
++ * Copyright (c) Red Hat, Inc., 2007.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+@@ -92,15 +93,6 @@
+ #endif /* GNU libc 2.1 */
+
+ /*
+- * PAM framework looks for these entry-points to pass control to the
+- * password changing module.
+- */
+-
+-#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
+-# include "./lckpwdf.-c"
+-#endif
+-
+-/*
+ How it works:
+ Gets in username (has to be done) from the calling program
+ Does authentication of user (only if we are not running as root)
+@@ -108,82 +100,15 @@
+ Sets it.
+ */
+
+-/* passwd/salt conversion macros */
+-
+-#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+-#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+-
+ /* data tokens */
+
+ #define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS"
+ #define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS"
+
+ #define MAX_PASSWD_TRIES 3
+-#define PW_TMPFILE "/etc/npasswd"
+-#define SH_TMPFILE "/etc/nshadow"
+ #ifndef CRACKLIB_DICTS
+ #define CRACKLIB_DICTS NULL
+ #endif
+-#define OPW_TMPFILE "/etc/security/nopasswd"
+-#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
+-
+-/*
+- * i64c - convert an integer to a radix 64 character
+- */
+-static int i64c(int i)
+-{
+- if (i < 0)
+- return ('.');
+- else if (i > 63)
+- return ('z');
+- if (i == 0)
+- return ('.');
+- if (i == 1)
+- return ('/');
+- if (i >= 2 && i <= 11)
+- return ('0' - 2 + i);
+- if (i >= 12 && i <= 37)
+- return ('A' - 12 + i);
+- if (i >= 38 && i <= 63)
+- return ('a' - 38 + i);
+- return ('\0');
+-}
+-
+-static char *crypt_md5_wrapper(const char *pass_new)
+-{
+- /*
+- * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
+- * removed use of static variables (AGM)
+- */
+-
+- struct timeval tv;
+- MD5_CTX ctx;
+- unsigned char result[16];
+- char *cp = (char *) result;
+- unsigned char tmp[16];
+- int i;
+- char *x = NULL;
+-
+- GoodMD5Init(&ctx);
+- gettimeofday(&tv, (struct timezone *) 0);
+- GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
+- i = getpid();
+- GoodMD5Update(&ctx, (void *) &i, sizeof i);
+- i = clock();
+- GoodMD5Update(&ctx, (void *) &i, sizeof i);
+- GoodMD5Update(&ctx, result, sizeof result);
+- GoodMD5Final(tmp, &ctx);
+- strcpy(cp, "$1$"); /* magic for the MD5 */
+- cp += strlen(cp);
+- for (i = 0; i < 8; i++)
+- *cp++ = i64c(tmp[i] & 077);
+- *cp = '\0';
+-
+- /* no longer need cleartext */
+- x = Goodcrypt_md5(pass_new, (const char *) result);
+-
+- return x;
+-}
+
+ static char *getNISserver(pam_handle_t *pamh)
+ {
+@@ -217,7 +142,8 @@
+
+ #ifdef WITH_SELINUX
+
+-static int _unix_run_shadow_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user, const char *fromwhat, const char *towhat)
++static int _unix_run_update_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user,
++ const char *fromwhat, const char *towhat, int remember)
+ {
+ int retval, child, fds[2];
+ void (*sighandler)(int) = NULL;
+@@ -247,7 +173,8 @@
+ size_t i=0;
+ struct rlimit rlim;
+ static char *envp[] = { NULL };
+- char *args[] = { NULL, NULL, NULL, NULL };
++ char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
++ char buffer[16];
+
+ /* XXX - should really tidy up PAM here too */
+
+@@ -270,11 +197,18 @@
+ }
+
+ /* exec binary helper */
+- args[0] = x_strdup(CHKPWD_HELPER);
++ args[0] = x_strdup(UPDATE_HELPER);
+ args[1] = x_strdup(user);
+- args[2] = x_strdup("shadow");
++ args[2] = x_strdup("update");
++ if (on(UNIX_SHADOW, ctrl))
++ args[3] = x_strdup("1");
++ else
++ args[3] = x_strdup("0");
+
+- execve(CHKPWD_HELPER, args, envp);
++ snprintf(buffer, sizeof(buffer), "%d", remember);
++ args[4] = x_strdup(buffer);
++
++ execve(UPDATE_HELPER, args, envp);
+
+ /* should not get here: exit with error */
+ D(("helper binary is not available"));
+@@ -297,7 +231,7 @@
+ close(fds[1]);
+ rc=waitpid(child, &retval, 0); /* wait for helper to complete */
+ if (rc<0) {
+- pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
++ pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
+ retval = PAM_AUTH_ERR;
+ } else {
+ retval = WEXITSTATUS(retval);
+@@ -315,8 +249,56 @@
+
+ return retval;
+ }
++
++static int selinux_confined(void)
++{
++ static int confined = -1;
++ int fd;
++ char tempfile[]="/etc/.pwdXXXXXX";
++
++ if (confined != -1)
++ return confined;
++
++ /* cannot be confined without SELinux enabled */
++ if (!SELINUX_ENABLED){
++ confined = 0;
++ return confined;
++ }
++
++ /* let's try opening shadow read only */
++ if ((fd=open("/etc/shadow", O_RDONLY)) != -1) {
++ close(fd);
++ confined = 0;
++ return confined;
++ }
++
++ if (errno == EACCES) {
++ confined = 1;
++ return confined;
++ }
++
++ /* shadow opening failed because of other reasons let's try
++ creating a file in /etc */
++ if ((fd=mkstemp(tempfile)) != -1) {
++ unlink(tempfile);
++ close(fd);
++ confined = 0;
++ return confined;
++ }
++
++ confined = 1;
++ return confined;
++}
++
++#else
++static int selinux_confined(void)
++{
++ return 0;
++}
+ #endif
+
++#include "passupdate.c"
++
+ static int check_old_password(const char *forwho, const char *newpass)
+ {
+ static char buf[16384];
+@@ -353,392 +335,6 @@
+ return retval;
+ }
+
+-static int save_old_password(pam_handle_t *pamh,
+- const char *forwho, const char *oldpass,
+- int howmany)
+-{
+- static char buf[16384];
+- static char nbuf[16384];
+- char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
+- int npas;
+- FILE *pwfile, *opwfile;
+- int err = 0;
+- int oldmask;
+- int found = 0;
+- struct passwd *pwd = NULL;
+- struct stat st;
+-
+- if (howmany < 0) {
+- return PAM_SUCCESS;
+- }
+-
+- if (oldpass == NULL) {
+- return PAM_SUCCESS;
+- }
+-
+- 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) {
+- err = 1;
+- goto done;
+- }
+-
+- opwfile = fopen(OLD_PASSWORDS_FILE, "r");
+- if (opwfile == NULL) {
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- if (fstat(fileno(opwfile), &st) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- while (fgets(buf, 16380, opwfile)) {
+- if (!strncmp(buf, forwho, strlen(forwho))) {
+- buf[strlen(buf) - 1] = '\0';
+- s_luser = strtok(buf, ":");
+- s_uid = strtok(NULL, ":");
+- s_npas = strtok(NULL, ":");
+- s_pas = strtok(NULL, ":");
+- npas = strtol(s_npas, NULL, 10) + 1;
+- while (npas > howmany) {
+- s_pas = strpbrk(s_pas, ",");
+- if (s_pas != NULL)
+- s_pas++;
+- npas--;
+- }
+- pass = crypt_md5_wrapper(oldpass);
+- if (s_pas == NULL)
+- snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
+- s_luser, s_uid, npas, pass);
+- else
+- snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
+- s_luser, s_uid, npas, s_pas, pass);
+- _pam_delete(pass);
+- if (fputs(nbuf, pwfile) < 0) {
+- err = 1;
+- break;
+- }
+- found = 1;
+- } else if (fputs(buf, pwfile) < 0) {
+- err = 1;
+- break;
+- }
+- }
+- fclose(opwfile);
+-
+- if (!found) {
+- pwd = pam_modutil_getpwnam(pamh, forwho);
+- if (pwd == NULL) {
+- err = 1;
+- } else {
+- pass = crypt_md5_wrapper(oldpass);
+- snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
+- forwho, (unsigned long)pwd->pw_uid, pass);
+- _pam_delete(pass);
+- if (fputs(nbuf, pwfile) < 0) {
+- err = 1;
+- }
+- }
+- }
+-
+- if (fclose(pwfile)) {
+- D(("error writing entries to old passwords file: %m"));
+- err = 1;
+- }
+-
+-done:
+- if (!err) {
+- 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)
+-{
+- struct passwd *tmpent = NULL;
+- struct stat st;
+- FILE *pwfile, *opwfile;
+- int err = 1;
+- 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) {
+- err = 1;
+- goto done;
+- }
+-
+- opwfile = fopen("/etc/passwd", "r");
+- if (opwfile == NULL) {
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- if (fstat(fileno(opwfile), &st) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- tmpent = fgetpwent(opwfile);
+- while (tmpent) {
+- if (!strcmp(tmpent->pw_name, forwho)) {
+- /* To shut gcc up */
+- union {
+- const char *const_charp;
+- char *charp;
+- } assigned_passwd;
+- assigned_passwd.const_charp = towhat;
+-
+- tmpent->pw_passwd = assigned_passwd.charp;
+- err = 0;
+- }
+- if (putpwent(tmpent, pwfile)) {
+- D(("error writing entry to password file: %m"));
+- err = 1;
+- break;
+- }
+- tmpent = fgetpwent(opwfile);
+- }
+- fclose(opwfile);
+-
+- if (fclose(pwfile)) {
+- D(("error writing entries to password file: %m"));
+- err = 1;
+- }
+-
+-done:
+- if (!err) {
+- if (!rename(PW_TMPFILE, "/etc/passwd"))
+- pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
+- 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 PAM_AUTHTOK_ERR;
+- }
+-}
+-
+-static int _update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
+-{
+- struct spwd *spwdent = NULL, *stmpent = NULL;
+- struct stat st;
+- FILE *pwfile, *opwfile;
+- int err = 1;
+- int oldmask;
+-
+- spwdent = getspnam(forwho);
+- if (spwdent == NULL) {
+- 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) {
+- err = 1;
+- goto done;
+- }
+-
+- opwfile = fopen("/etc/shadow", "r");
+- if (opwfile == NULL) {
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- if (fstat(fileno(opwfile), &st) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- stmpent = fgetspent(opwfile);
+- while (stmpent) {
+-
+- if (!strcmp(stmpent->sp_namp, forwho)) {
+- stmpent->sp_pwdp = towhat;
+- stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
+- err = 0;
+- D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
+- }
+-
+- if (putspent(stmpent, pwfile)) {
+- D(("error writing entry to shadow file: %m"));
+- err = 1;
+- break;
+- }
+-
+- stmpent = fgetspent(opwfile);
+- }
+- fclose(opwfile);
+-
+- if (fclose(pwfile)) {
+- D(("error writing entries to shadow file: %m"));
+- err = 1;
+- }
+-
+- done:
+- if (!err) {
+- if (!rename(SH_TMPFILE, "/etc/shadow"))
+- pam_syslog(pamh, LOG_NOTICE, "password changed for %s", forwho);
+- 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 PAM_AUTHTOK_ERR;
+- }
+-}
+-
+ static int _do_setpass(pam_handle_t* pamh, const char *forwho,
+ const char *fromwhat,
+ char *towhat, unsigned int ctrl, int remember)
+@@ -767,7 +363,7 @@
+
+ /* Unlock passwd file to avoid deadlock */
+ #ifdef USE_LCKPWDF
+- ulckpwdf();
++ unlock_pwdf();
+ #endif
+ unlocked = 1;
+
+@@ -830,33 +426,22 @@
+ if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
+ #ifdef USE_LCKPWDF
+ if(unlocked) {
+- int i = 0;
+- /* These values for the number of attempts and the sleep time
+- are, of course, completely arbitrary.
+- My reading of the PAM docs is that, once pam_chauthtok() has been
+- called with PAM_UPDATE_AUTHTOK, we are obliged to take any
+- reasonable steps to make sure the token is updated; so retrying
+- for 1/10 sec. isn't overdoing it. */
+- while((retval = lckpwdf()) != 0 && i < 100) {
+- usleep(1000);
+- i++;
+- }
+- if(retval != 0) {
++ if (lock_pwdf() != PAM_SUCCESS) {
+ return PAM_AUTHTOK_LOCK_BUSY;
+ }
+ }
+ #endif
++#ifdef WITH_SELINUX
++ if (selinux_confined())
++ return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember);
++#endif
+ /* first, save old password */
+- if (save_old_password(pamh, forwho, fromwhat, remember)) {
++ if (save_old_password(forwho, fromwhat, remember)) {
+ retval = PAM_AUTHTOK_ERR;
+ goto done;
+ }
+ if (on(UNIX_SHADOW, ctrl) || _unix_shadowed(pwd)) {
+ retval = _update_shadow(pamh, forwho, towhat);
+-#ifdef WITH_SELINUX
+- if (retval != PAM_SUCCESS && SELINUX_ENABLED)
+- retval = _unix_run_shadow_binary(pamh, ctrl, forwho, fromwhat, towhat);
+-#endif
+ if (retval == PAM_SUCCESS)
+ if (!_unix_shadowed(pwd))
+ retval = _update_passwd(pamh, forwho, "x");
+@@ -868,7 +453,7 @@
+
+ done:
+ #ifdef USE_LCKPWDF
+- ulckpwdf();
++ unlock_pwdf();
+ #endif
+
+ return retval;
+@@ -889,13 +474,17 @@
+ if (_unix_shadowed(pwd)) {
+ /* ...and shadow password file entry for this user, if shadowing
+ is enabled */
+- setspent();
+- spwdent = getspnam(user);
+- endspent();
+-
+ #ifdef WITH_SELINUX
+- if (spwdent == NULL && SELINUX_ENABLED )
+- spwdent = _unix_run_verify_binary(pamh, ctrl, user);
++ if (selinux_confined())
++ spwdent = _unix_run_verify_binary(pamh, ctrl, user);
++ else
++ {
++#endif
++ setspent();
++ spwdent = getspnam(user);
++ endspent();
++#ifdef WITH_SELINUX
++ }
+ #endif
+ if (spwdent == NULL)
+ return PAM_AUTHINFO_UNAVAIL;
+@@ -1018,7 +607,7 @@
+ int argc, const char **argv)
+ {
+ unsigned int ctrl, lctrl;
+- int retval, i;
++ int retval;
+ int remember = -1;
+
+ /* */
+@@ -1238,49 +827,40 @@
+ return retval;
+ }
+ #ifdef USE_LCKPWDF
+- /* These values for the number of attempts and the sleep time
+- are, of course, completely arbitrary.
+- My reading of the PAM docs is that, once pam_chauthtok() has been
+- called with PAM_UPDATE_AUTHTOK, we are obliged to take any
+- reasonable steps to make sure the token is updated; so retrying
+- for 1/10 sec. isn't overdoing it. */
+- i=0;
+- while((retval = lckpwdf()) != 0 && i < 100) {
+- usleep(1000);
+- i++;
+- }
+- if(retval != 0) {
++ if (lock_pwdf() != PAM_SUCCESS) {
+ return PAM_AUTHTOK_LOCK_BUSY;
+ }
+ #endif
+
+- if (pass_old) {
++ if (!selinux_confined() && pass_old) {
+ retval = _unix_verify_password(pamh, user, pass_old, ctrl);
+ if (retval != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_NOTICE, "user password changed by another process");
+ #ifdef USE_LCKPWDF
+- ulckpwdf();
++ unlock_pwdf();
+ #endif
+ return retval;
+ }
+ }
+
+- retval = _unix_verify_shadow(pamh, user, ctrl);
+- if (retval != PAM_SUCCESS) {
++
++ if (!selinux_confined() &&
++ (retval=_unix_verify_shadow(pamh, user, ctrl)) != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_NOTICE, "user not authenticated 2");
+ #ifdef USE_LCKPWDF
+- ulckpwdf();
++ unlock_pwdf();
+ #endif
+ return retval;
+ }
+
+- retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
+- if (retval != PAM_SUCCESS) {
++
++ if (!selinux_confined() &&
++ (retval=_pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new)) != PAM_SUCCESS) {
+ pam_syslog(pamh, LOG_NOTICE,
+ "new password not acceptable 2");
+ pass_new = pass_old = NULL; /* tidy up */
+ #ifdef USE_LCKPWDF
+- ulckpwdf();
++ unlock_pwdf();
+ #endif
+ return retval;
+ }
+@@ -1324,7 +904,7 @@
+ "out of memory for password");
+ pass_new = pass_old = NULL; /* tidy up */
+ #ifdef USE_LCKPWDF
+- ulckpwdf();
++ unlock_pwdf();
+ #endif
+ return PAM_BUF_ERR;
+ }
+@@ -1347,7 +927,7 @@
+
+ retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
+ remember);
+- /* _do_setpass has called ulckpwdf for us */
++ /* _do_setpass has called unlock_pwdf for us */
+
+ _pam_delete(tpass);
+ pass_old = pass_new = NULL;
+--- /dev/null 2007-05-28 11:10:34.936447748 +0200
++++ Linux-PAM-0.99.7.1/modules/pam_unix/passverify.h 2007-06-01 15:13:57.000000000 +0200
+@@ -0,0 +1,60 @@
++/*
++ * This program is designed to run setuid(root) or with sufficient
++ * privilege to read all of the unix password databases. It is designed
++ * to provide a mechanism for the current user (defined by this
++ * process' uid) to verify their own password.
++ *
++ * The password is read from the standard input. The exit status of
++ * this program indicates whether the user is authenticated or not.
++ *
++ * Copyright information is located at the end of the file.
++ *
++ */
++
++#define MAXPASS 200 /* the maximum length of a password */
++
++void _log_err(int err, const char *format,...);
++
++void setup_signals(void);
++
++int read_passwords(int fd, int npass, char **passwords);
++
++int _unix_verify_password(const char *name, const char *p, int nullok);
++
++char *getuidname(uid_t uid);
++
++/*
++ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
++ * Copyright (c) Red Hat, Inc. 2007. All rights reserved
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, and the entire permission notice in its entirety,
++ * including the disclaimer of warranties.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote
++ * products derived from this software without specific prior
++ * written permission.
++ *
++ * ALTERNATIVELY, this product may be distributed under the terms of
++ * the GNU Public License, in which case the provisions of the GPL are
++ * required INSTEAD OF the above restrictions. (This clause is
++ * necessary due to a potential bad interaction between the GPL and
++ * the restrictions contained in a BSD-style copyright.)
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++ * OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
+--- Linux-PAM-0.99.7.1/modules/pam_unix/unix_chkpwd.c.update-helper 2007-06-01 15:13:57.000000000 +0200
++++ Linux-PAM-0.99.7.1/modules/pam_unix/unix_chkpwd.c 2007-06-01 15:16:00.000000000 +0200
+@@ -41,386 +41,7 @@
+
+ #include "md5.h"
+ #include "bigcrypt.h"
+-
+-/* syslogging function for errors and other information */
+-
+-static void _log_err(int err, const char *format,...)
+-{
+- va_list args;
+-
+- va_start(args, format);
+- openlog("unix_chkpwd", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
+- vsyslog(err, format, args);
+- va_end(args);
+- closelog();
+-}
+-
+-static int _unix_shadowed(const struct passwd *pwd)
+-{
+- char hashpass[1024];
+- if (pwd != NULL) {
+- if (strcmp(pwd->pw_passwd, "x") == 0) {
+- return 1;
+- }
+- if (strlen(pwd->pw_name) < sizeof(hashpass) - 2) {
+- strcpy(hashpass, "##");
+- strcpy(hashpass + 2, pwd->pw_name);
+- if (strcmp(pwd->pw_passwd, hashpass) == 0) {
+- return 1;
+- }
+- }
+- }
+- return 0;
+-}
+-
+-static void su_sighandler(int sig)
+-{
+-#ifndef SA_RESETHAND
+- /* emulate the behaviour of the SA_RESETHAND flag */
+- if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV )
+- signal(sig, SIG_DFL);
+-#endif
+- if (sig > 0) {
+- _log_err(LOG_NOTICE, "caught signal %d.", sig);
+- exit(sig);
+- }
+-}
+-
+-static void setup_signals(void)
+-{
+- struct sigaction action; /* posix signal structure */
+-
+- /*
+- * Setup signal handlers
+- */
+- (void) memset((void *) &action, 0, sizeof(action));
+- action.sa_handler = su_sighandler;
+-#ifdef SA_RESETHAND
+- action.sa_flags = SA_RESETHAND;
+-#endif
+- (void) sigaction(SIGILL, &action, NULL);
+- (void) sigaction(SIGTRAP, &action, NULL);
+- (void) sigaction(SIGBUS, &action, NULL);
+- (void) sigaction(SIGSEGV, &action, NULL);
+- action.sa_handler = SIG_IGN;
+- action.sa_flags = 0;
+- (void) sigaction(SIGTERM, &action, NULL);
+- (void) sigaction(SIGHUP, &action, NULL);
+- (void) sigaction(SIGINT, &action, NULL);
+- (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 = PAM_AUTH_ERR;
+- size_t salt_len;
+-
+- /* UNIX passwords area */
+- setpwent();
+- pwd = getpwnam(name); /* Get password file entry... */
+- endpwent();
+- if (pwd != NULL) {
+- if (_unix_shadowed(pwd)) {
+- /*
+- * ...and shadow password file entry for this user,
+- * if shadowing is enabled
+- */
+- setspent();
+- spwdent = getspnam(name);
+- endspent();
+- if (spwdent != NULL)
+- salt = x_strdup(spwdent->sp_pwdp);
+- else
+- pwd = NULL;
+- } else {
+- if (strcmp(pwd->pw_passwd, "*NP*") == 0) { /* NIS+ */
+- uid_t save_uid;
+-
+- save_uid = geteuid();
+- seteuid(pwd->pw_uid);
+- spwdent = getspnam(name);
+- seteuid(save_uid);
+-
+- salt = x_strdup(spwdent->sp_pwdp);
+- } else {
+- salt = x_strdup(pwd->pw_passwd);
+- }
+- }
+- }
+- if (pwd == NULL || salt == NULL) {
+- _log_err(LOG_ALERT, "check pass; user unknown");
+- p = NULL;
+- return PAM_USER_UNKNOWN;
+- }
+-
+- salt_len = strlen(salt);
+- if (salt_len == 0) {
+- return (nullok == 0) ? PAM_AUTH_ERR : PAM_SUCCESS;
+- }
+- if (p == NULL || strlen(p) == 0) {
+- _pam_overwrite(salt);
+- _pam_drop(salt);
+- return PAM_AUTHTOK_ERR;
+- }
+-
+- /* the moment of truth -- do we agree with the password? */
+- retval = PAM_AUTH_ERR;
+- if (!strncmp(salt, "$1$", 3)) {
+- pp = Goodcrypt_md5(p, salt);
+- if (pp && strcmp(pp, salt) == 0) {
+- retval = PAM_SUCCESS;
+- } else {
+- _pam_overwrite(pp);
+- _pam_drop(pp);
+- pp = Brokencrypt_md5(p, salt);
+- if (pp && strcmp(pp, salt) == 0)
+- retval = PAM_SUCCESS;
+- }
+- } else if (*salt == '$') {
+- /*
+- * Ok, we don't know the crypt algorithm, but maybe
+- * libcrypt nows about it? We should try it.
+- */
+- pp = x_strdup (crypt(p, salt));
+- if (pp && strcmp(pp, salt) == 0) {
+- retval = PAM_SUCCESS;
+- }
+- } else if (*salt == '*' || *salt == '!' || salt_len < 13) {
+- retval = PAM_AUTH_ERR;
+- } else {
+- pp = bigcrypt(p, salt);
+- /*
+- * Note, we are comparing the bigcrypt of the password with
+- * the contents of the password field. If the latter was
+- * encrypted with regular crypt (and not bigcrypt) it will
+- * have been truncated for storage relative to the output
+- * of bigcrypt here. As such we need to compare only the
+- * stored string with the subset of bigcrypt's result.
+- * Bug 521314.
+- */
+- if (pp && salt_len == 13 && strlen(pp) > salt_len) {
+- _pam_overwrite(pp+salt_len);
+- }
+-
+- if (pp && strcmp(pp, salt) == 0) {
+- retval = PAM_SUCCESS;
+- }
+- }
+- p = NULL; /* no longer needed here */
+-
+- /* clean up */
+- _pam_overwrite(pp);
+- _pam_drop(pp);
+-
+- return retval;
+-}
+-
+-static char *getuidname(uid_t uid)
+-{
+- struct passwd *pw;
+- static char username[32];
+-
+- pw = getpwuid(uid);
+- if (pw == NULL)
+- return NULL;
+-
+- strncpy(username, pw->pw_name, sizeof(username));
+- username[sizeof(username) - 1] = '\0';
+-
+- return username;
+-}
+-
+-#define SH_TMPFILE "/etc/nshadow"
+-static int _update_shadow(const char *forwho)
+-{
+- struct spwd *spwdent = NULL, *stmpent = NULL;
+- FILE *pwfile, *opwfile;
+- int err = 1;
+- int oldmask;
+- struct stat st;
+- char pass[MAXPASS + 1];
+- char towhat[MAXPASS + 1];
+- int npass=0;
+-
+- /* read the password from stdin (a pipe from the pam_unix module) */
+-
+- npass = read(STDIN_FILENO, pass, MAXPASS);
+-
+- if (npass < 0) { /* is it a valid password? */
+-
+- _log_err(LOG_DEBUG, "no password supplied");
+- return PAM_AUTHTOK_ERR;
+-
+- } else if (npass >= MAXPASS) {
+-
+- _log_err(LOG_DEBUG, "password too long");
+- return PAM_AUTHTOK_ERR;
+-
+- } else {
+- /* does pass agree with the official one? */
+- int retval=0;
+- pass[npass] = '\0'; /* NUL terminate */
+- retval = _unix_verify_password(forwho, pass, 0);
+- if (retval != PAM_SUCCESS) {
+- return retval;
+- }
+- }
+-
+- /* read the password from stdin (a pipe from the pam_unix module) */
+-
+- npass = read(STDIN_FILENO, towhat, MAXPASS);
+-
+- if (npass < 0) { /* is it a valid password? */
+-
+- _log_err(LOG_DEBUG, "no new password supplied");
+- return PAM_AUTHTOK_ERR;
+-
+- } else if (npass >= MAXPASS) {
+-
+- _log_err(LOG_DEBUG, "new password too long");
+- return PAM_AUTHTOK_ERR;
+-
+- }
+-
+- towhat[npass] = '\0'; /* NUL terminate */
+- spwdent = getspnam(forwho);
+- if (spwdent == NULL) {
+- 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) {
+- err = 1;
+- goto done;
+- }
+-
+- opwfile = fopen("/etc/shadow", "r");
+- if (opwfile == NULL) {
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- if (fstat(fileno(opwfile), &st) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+- if (fchmod(fileno(pwfile), st.st_mode) == -1) {
+- fclose(opwfile);
+- fclose(pwfile);
+- err = 1;
+- goto done;
+- }
+-
+- stmpent = fgetspent(opwfile);
+- while (stmpent) {
+-
+- if (!strcmp(stmpent->sp_namp, forwho)) {
+- stmpent->sp_pwdp = towhat;
+- stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24);
+- err = 0;
+- D(("Set password %s for %s", stmpent->sp_pwdp, forwho));
+- }
+-
+- if (putspent(stmpent, pwfile)) {
+- D(("error writing entry to shadow file: %m"));
+- err = 1;
+- break;
+- }
+-
+- stmpent = fgetspent(opwfile);
+- }
+- fclose(opwfile);
+-
+- if (fclose(pwfile)) {
+- D(("error writing entries to shadow file: %m"));
+- err = 1;
+- }
+-
+- done:
+- if (!err) {
+- if (rename(SH_TMPFILE, "/etc/shadow"))
+- 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 PAM_AUTHTOK_ERR;
+- }
+-}
++#include "passverify.h"
+
+ int main(int argc, char *argv[])
+ {
+@@ -430,6 +51,7 @@
+ int force_failure = 0;
+ int retval = PAM_AUTH_ERR;
+ char *user;
++ char *passwords[] = { pass };
+
+ /*
+ * Catch or ignore as many signal as possible.
+@@ -476,49 +98,24 @@
+
+ option=argv[2];
+
+- if (strncmp(argv[2], "verify", 8) == 0) {
+- /* Get the account information from the shadow file */
+- return _verify_account(argv[1]);
+- }
+-
+- if (strncmp(option, "shadow", 8) == 0) {
+- /* Attempting to change the password */
+- return _update_shadow(argv[1]);
+- }
+-
+ /* read the nullok/nonull option */
+ if (strncmp(option, "nullok", 8) == 0)
+ nullok = 1;
+- else
++ else if (strncmp(option, "nonull", 8) == 0)
+ nullok = 0;
++ else
++ return PAM_SYSTEM_ERR;
+
+ /* read the password from stdin (a pipe from the pam_unix module) */
+
+- npass = read(STDIN_FILENO, pass, MAXPASS);
+-
+- if (npass < 0) { /* is it a valid password? */
+-
+- _log_err(LOG_DEBUG, "no password supplied");
++ npass = read_passwords(STDIN_FILENO, 1, passwords);
+
+- } else if (npass >= MAXPASS) {
+-
+- _log_err(LOG_DEBUG, "password too long");
+-
+- } else {
+- if (npass == 0) {
+- /* the password is NULL */
+-
+- retval = _unix_verify_password(user, NULL, nullok);
+-
+- } else {
+- /* does pass agree with the official one? */
+-
+- pass[npass] = '\0'; /* NUL terminate */
+- retval = _unix_verify_password(user, pass, nullok);
+-
+- }
++ if (npass != 1) { /* is it a valid password? */
++ _log_err(LOG_DEBUG, "no valid password supplied");
+ }
+
++ retval = _unix_verify_password(user, pass, nullok);
++
+ memset(pass, '\0', MAXPASS); /* clear memory of the password */
+
+ /* return pass or fail */
+@@ -533,6 +130,7 @@
+
+ /*
+ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
++ * Copyright (c) Red Hat, Inc., 2007. All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+--- /dev/null 2007-05-28 11:10:34.936447748 +0200
++++ Linux-PAM-0.99.7.1/modules/pam_unix/unix_update.c 2007-06-01 15:13:57.000000000 +0200
+@@ -0,0 +1,262 @@
++/*
++ * This program is designed to run setuid(root) or with sufficient
++ * privilege to read all of the unix password databases. It is designed
++ * to provide a mechanism for the current user (defined by this
++ * process' uid) to verify their own password.
++ *
++ * The password is read from the standard input. The exit status of
++ * this program indicates whether the user is authenticated or not.
++ *
++ * Copyright information is located at the end of the file.
++ *
++ */
++
++#include "config.h"
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#ifdef WITH_SELINUX
++#include
++#define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
++static security_context_t prev_context=NULL;
++static int selinux_enabled=-1;
++#else
++#define SELINUX_ENABLED 0
++#endif
++
++#define MAXPASS 200 /* the maximum length of a password */
++
++#include
++#include
++
++#include "md5.h"
++#include "bigcrypt.h"
++#include "passverify.h"
++
++#define _pam_delete(xx) \
++{ \
++ _pam_overwrite(xx); \
++ _pam_drop(xx); \
++}
++
++static int
++_unix_shadowed(const struct passwd *pwd)
++{
++ if (pwd != NULL) {
++ if (strcmp(pwd->pw_passwd, "x") == 0) {
++ return 1;
++ }
++ if ((pwd->pw_passwd[0] == '#') &&
++ (pwd->pw_passwd[1] == '#') &&
++ (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
++ return 1;
++ }
++ }
++ return 0;
++}
++
++#define HELPER_COMPILE
++#include "passupdate.c"
++
++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
++set_password(const char *forwho, const char *shadow, const char *remember)
++{
++ struct passwd *pwd = NULL;
++ int retval;
++ char pass[MAXPASS + 1];
++ char towhat[MAXPASS + 1];
++ int npass = 0;
++ /* we don't care about number format errors because the helper
++ should be called internally only */
++ int doshadow = atoi(shadow);
++ int nremember = atoi(remember);
++ char *passwords[] = { pass, towhat };
++
++ /* read the password from stdin (a pipe from the pam_unix module) */
++
++ npass = read_passwords(STDIN_FILENO, 2, passwords);
++
++ if (npass != 2) { /* is it a valid password? */
++ if (npass == 1) {
++ _log_err(LOG_DEBUG, "no new password supplied");
++ memset(pass, '\0', MAXPASS);
++ } else {
++ _log_err(LOG_DEBUG, "no valid passwords supplied");
++ }
++ return PAM_AUTHTOK_ERR;
++ }
++
++#ifdef USE_LCKPWDF
++ if (lock_pwdf() != PAM_SUCCESS)
++ return PAM_AUTHTOK_LOCK_BUSY;
++#endif
++
++ pwd = getpwnam(forwho);
++
++ if (pwd == NULL) {
++ retval = PAM_USER_UNKNOWN;
++ goto done;
++ }
++
++ /* does pass agree with the official one?
++ we always allow change from null pass */
++ retval = _unix_verify_password(forwho, pass, 1);
++ if (retval != PAM_SUCCESS) {
++ goto done;
++ }
++
++ /* first, save old password */
++ if (save_old_password(forwho, pass, nremember)) {
++ retval = PAM_AUTHTOK_ERR;
++ goto done;
++ }
++
++ if (doshadow || _unix_shadowed(pwd)) {
++ retval = _update_shadow(forwho, towhat);
++ if (retval == PAM_SUCCESS)
++ if (!_unix_shadowed(pwd))
++ retval = _update_passwd(forwho, "x");
++ } else {
++ retval = _update_passwd(forwho, towhat);
++ }
++
++done:
++ memset(pass, '\0', MAXPASS);
++ memset(towhat, '\0', MAXPASS);
++
++#ifdef USE_LCKPWDF
++ unlock_pwdf();
++#endif
++
++ if (retval == PAM_SUCCESS) {
++ return PAM_SUCCESS;
++ } else {
++ return PAM_AUTHTOK_ERR;
++ }
++}
++
++int main(int argc, char *argv[])
++{
++ char *option;
++
++ /*
++ * Catch or ignore as many signal as possible.
++ */
++ setup_signals();
++
++ /*
++ * we establish that this program is running with non-tty stdin.
++ * this is to discourage casual use. It does *NOT* prevent an
++ * intruder from repeatadly running this program to determine the
++ * password of the current user (brute force attack, but one for
++ * which the attacker must already have gained access to the user's
++ * account).
++ */
++
++ if (isatty(STDIN_FILENO) || argc < 3 ) {
++ _log_err(LOG_NOTICE
++ ,"inappropriate use of Unix helper binary [UID=%d]"
++ ,getuid());
++ fprintf(stderr
++ ,"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 PAM_SYSTEM_ERR;
++ }
++
++ /* We must be root to read/update shadow.
++ */
++ if (geteuid() != 0) {
++ return PAM_AUTH_ERR;
++ }
++
++ option = argv[2];
++
++ if (strncmp(option, "verify", 8) == 0) {
++ /* Get the account information from the shadow file */
++ return verify_account(argv[1]);
++ }
++
++ if (strncmp(option, "update", 8) == 0) {
++ if (argc == 5)
++ /* Attempting to change the password */
++ return set_password(argv[1], argv[3], argv[4]);
++ }
++
++ return PAM_SYSTEM_ERR;
++}
++
++/*
++ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
++ * Copyright (c) Red Hat, Inc., 2007. All rights reserved
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, and the entire permission notice in its entirety,
++ * including the disclaimer of warranties.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote
++ * products derived from this software without specific prior
++ * written permission.
++ *
++ * ALTERNATIVELY, this product may be distributed under the terms of
++ * the GNU Public License, in which case the provisions of the GPL are
++ * required INSTEAD OF the above restrictions. (This clause is
++ * necessary due to a potential bad interaction between the GPL and
++ * the restrictions contained in a BSD-style copyright.)
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++ * OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
+--- /dev/null 2007-05-28 11:10:34.936447748 +0200
++++ Linux-PAM-0.99.7.1/modules/pam_unix/passverify.c 2007-06-01 15:13:57.000000000 +0200
+@@ -0,0 +1,308 @@
++/*
++ * This program is designed to run setuid(root) or with sufficient
++ * privilege to read all of the unix password databases. It is designed
++ * to provide a mechanism for the current user (defined by this
++ * process' uid) to verify their own password.
++ *
++ * The password is read from the standard input. The exit status of
++ * this program indicates whether the user is authenticated or not.
++ *
++ * Copyright information is located at the end of the file.
++ *
++ */
++
++#include "config.h"
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include
++#include
++
++#include "md5.h"
++#include "bigcrypt.h"
++
++#include "passverify.h"
++
++/* syslogging function for errors and other information */
++
++void
++_log_err(int err, const char *format,...)
++{
++ va_list args;
++
++ va_start(args, format);
++ openlog("unix_chkpwd", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
++ vsyslog(err, format, args);
++ va_end(args);
++ closelog();
++}
++
++static int
++_unix_shadowed(const struct passwd *pwd)
++{
++ char hashpass[1024];
++ if (pwd != NULL) {
++ if (strcmp(pwd->pw_passwd, "x") == 0) {
++ return 1;
++ }
++ if (strlen(pwd->pw_name) < sizeof(hashpass) - 2) {
++ strcpy(hashpass, "##");
++ strcpy(hashpass + 2, pwd->pw_name);
++ if (strcmp(pwd->pw_passwd, hashpass) == 0) {
++ return 1;
++ }
++ }
++ }
++ return 0;
++}
++
++static void
++su_sighandler(int sig)
++{
++#ifndef SA_RESETHAND
++ /* emulate the behaviour of the SA_RESETHAND flag */
++ if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV )
++ signal(sig, SIG_DFL);
++#endif
++ if (sig > 0) {
++ _exit(sig);
++ }
++}
++
++void
++setup_signals(void)
++{
++ struct sigaction action; /* posix signal structure */
++
++ /*
++ * Setup signal handlers
++ */
++ (void) memset((void *) &action, 0, sizeof(action));
++ action.sa_handler = su_sighandler;
++#ifdef SA_RESETHAND
++ action.sa_flags = SA_RESETHAND;
++#endif
++ (void) sigaction(SIGILL, &action, NULL);
++ (void) sigaction(SIGTRAP, &action, NULL);
++ (void) sigaction(SIGBUS, &action, NULL);
++ (void) sigaction(SIGSEGV, &action, NULL);
++ action.sa_handler = SIG_IGN;
++ action.sa_flags = 0;
++ (void) sigaction(SIGTERM, &action, NULL);
++ (void) sigaction(SIGHUP, &action, NULL);
++ (void) sigaction(SIGINT, &action, NULL);
++ (void) sigaction(SIGQUIT, &action, NULL);
++}
++
++int
++read_passwords(int fd, int npass, char **passwords)
++{
++ int rbytes = 0;
++ int offset = 0;
++ int i = 0;
++ char *pptr;
++ while (npass > 0) {
++ rbytes = read(fd, passwords[i]+offset, MAXPASS-offset);
++
++ if (rbytes < 0) {
++ if (errno == EINTR) continue;
++ break;
++ }
++ if (rbytes == 0)
++ break;
++
++ while (npass > 0 && (pptr=memchr(passwords[i]+offset, '\0', rbytes))
++ != NULL) {
++ rbytes -= pptr - (passwords[i]+offset) + 1;
++ i++;
++ offset = 0;
++ npass--;
++ if (rbytes > 0) {
++ if (npass > 0)
++ memcpy(passwords[i], pptr+1, rbytes);
++ memset(pptr+1, '\0', rbytes);
++ }
++ }
++ offset += rbytes;
++ }
++
++ /* clear up */
++ if (offset > 0 && npass > 0) {
++ memset(passwords[i], '\0', offset);
++ }
++
++ return i;
++}
++
++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 = PAM_AUTH_ERR;
++ size_t salt_len;
++
++ /* UNIX passwords area */
++ setpwent();
++ pwd = getpwnam(name); /* Get password file entry... */
++ endpwent();
++ if (pwd != NULL) {
++ if (_unix_shadowed(pwd)) {
++ /*
++ * ...and shadow password file entry for this user,
++ * if shadowing is enabled
++ */
++ setspent();
++ spwdent = getspnam(name);
++ endspent();
++ if (spwdent != NULL)
++ salt = x_strdup(spwdent->sp_pwdp);
++ else
++ pwd = NULL;
++ } else {
++ if (strcmp(pwd->pw_passwd, "*NP*") == 0) { /* NIS+ */
++ uid_t save_uid;
++
++ save_uid = geteuid();
++ seteuid(pwd->pw_uid);
++ spwdent = getspnam(name);
++ seteuid(save_uid);
++
++ salt = x_strdup(spwdent->sp_pwdp);
++ } else {
++ salt = x_strdup(pwd->pw_passwd);
++ }
++ }
++ }
++ if (pwd == NULL || salt == NULL) {
++ _log_err(LOG_ALERT, "check pass; user unknown");
++ p = NULL;
++ return PAM_USER_UNKNOWN;
++ }
++
++ salt_len = strlen(salt);
++ if (salt_len == 0) {
++ return (nullok == 0) ? PAM_AUTH_ERR : PAM_SUCCESS;
++ }
++ if (p == NULL || strlen(p) == 0) {
++ _pam_overwrite(salt);
++ _pam_drop(salt);
++ return PAM_AUTHTOK_ERR;
++ }
++
++ /* the moment of truth -- do we agree with the password? */
++ retval = PAM_AUTH_ERR;
++ if (!strncmp(salt, "$1$", 3)) {
++ pp = Goodcrypt_md5(p, salt);
++ if (pp && strcmp(pp, salt) == 0) {
++ retval = PAM_SUCCESS;
++ } else {
++ _pam_overwrite(pp);
++ _pam_drop(pp);
++ pp = Brokencrypt_md5(p, salt);
++ if (pp && strcmp(pp, salt) == 0)
++ retval = PAM_SUCCESS;
++ }
++ } else if (*salt == '$') {
++ /*
++ * Ok, we don't know the crypt algorithm, but maybe
++ * libcrypt nows about it? We should try it.
++ */
++ pp = x_strdup (crypt(p, salt));
++ if (pp && strcmp(pp, salt) == 0) {
++ retval = PAM_SUCCESS;
++ }
++ } else if (*salt == '*' || *salt == '!' || salt_len < 13) {
++ retval = PAM_AUTH_ERR;
++ } else {
++ pp = bigcrypt(p, salt);
++ /*
++ * Note, we are comparing the bigcrypt of the password with
++ * the contents of the password field. If the latter was
++ * encrypted with regular crypt (and not bigcrypt) it will
++ * have been truncated for storage relative to the output
++ * of bigcrypt here. As such we need to compare only the
++ * stored string with the subset of bigcrypt's result.
++ * Bug 521314.
++ */
++ if (pp && salt_len == 13 && strlen(pp) > salt_len) {
++ _pam_overwrite(pp+salt_len);
++ }
++
++ if (pp && strcmp(pp, salt) == 0) {
++ retval = PAM_SUCCESS;
++ }
++ }
++ p = NULL; /* no longer needed here */
++
++ /* clean up */
++ _pam_overwrite(pp);
++ _pam_drop(pp);
++
++ return retval;
++}
++
++char *
++getuidname(uid_t uid)
++{
++ struct passwd *pw;
++ static char username[256];
++
++ pw = getpwuid(uid);
++ if (pw == NULL)
++ return NULL;
++
++ strncpy(username, pw->pw_name, sizeof(username));
++ username[sizeof(username) - 1] = '\0';
++
++ return username;
++}
++/*
++ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
++ * Copyright (c) Red Hat, Inc. 2007. All rights reserved
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, and the entire permission notice in its entirety,
++ * including the disclaimer of warranties.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ * 3. The name of the author may not be used to endorse or promote
++ * products derived from this software without specific prior
++ * written permission.
++ *
++ * ALTERNATIVELY, this product may be distributed under the terms of
++ * the GNU Public License, in which case the provisions of the GPL are
++ * required INSTEAD OF the above restrictions. (This clause is
++ * necessary due to a potential bad interaction between the GPL and
++ * the restrictions contained in a BSD-style copyright.)
++ *
++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++ * OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
+--- Linux-PAM-0.99.7.1/modules/pam_unix/Makefile.am.update-helper 2006-12-18 19:50:50.000000000 +0100
++++ Linux-PAM-0.99.7.1/modules/pam_unix/Makefile.am 2007-06-01 15:15:04.000000000 +0200
+@@ -16,7 +16,8 @@
+ secureconfdir = $(SCONFIGDIR)
+
+ AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
+- -DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\"
++ -DCHKPWD_HELPER=\"$(sbindir)/unix_chkpwd\" \
++ -DUPDATE_HELPER=\"$(sbindir)/unix_update\"
+
+ if HAVE_LIBSELINUX
+ AM_CFLAGS += -D"WITH_SELINUX"
+@@ -34,9 +35,9 @@
+
+ securelib_LTLIBRARIES = pam_unix.la
+
+-noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h
++noinst_HEADERS = md5.h support.h yppasswd.h bigcrypt.h passverify.h
+
+-sbin_PROGRAMS = unix_chkpwd
++sbin_PROGRAMS = unix_chkpwd unix_update
+
+ noinst_PROGRAMS = bigcrypt
+
+@@ -48,11 +49,16 @@
+ bigcrypt_CFLAGS = $(AM_CFLAGS)
+ bigcrypt_LDFLAGS = @LIBCRYPT@
+
+-unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c
++unix_chkpwd_SOURCES = unix_chkpwd.c passverify.c md5_good.c md5_broken.c bigcrypt.c
+ unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
+ unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@ -L$(top_builddir)/libpam -lpam \
+ @LIBCRYPT@ @LIBSELINUX@
+
++unix_update_SOURCES = unix_update.c passverify.c md5_good.c md5_broken.c bigcrypt.c
++unix_update_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
++unix_update_LDFLAGS = @PIE_LDFLAGS@ -L$(top_builddir)/libpam -lpam \
++ @LIBCRYPT@ @LIBSELINUX@
++
+ if ENABLE_REGENERATE_MAN
+ noinst_DATA = README
+ README: pam_unix.8.xml
diff --git a/pam.spec b/pam.spec
index aa3c21d..c7caaed 100644
--- a/pam.spec
+++ b/pam.spec
@@ -11,7 +11,7 @@
Summary: A security tool which provides authentication for applications
Name: pam
Version: 0.99.7.1
-Release: 5%{?dist}
+Release: 6%{?dist}
License: GPL or BSD
Group: System Environment/Base
Source0: http://ftp.us.kernel.org/pub/linux/libs/pam/pre/library/Linux-PAM-%{version}.tar.bz2
@@ -27,9 +27,10 @@ Source10: config-util.5
Patch1: pam-0.99.7.0-redhat-modules.patch
Patch2: pam-0.99.7.1-console-more-displays.patch
Patch3: pam-0.99.7.1-console-decrement.patch
-Patch21: pam-0.78-unix-hpux-aging.patch
Patch22: pam-0.99.7.1-unix-allow-pwmodify.patch
Patch23: pam-0.99.7.1-unix-bigcrypt.patch
+Patch24: pam-0.99.7.1-unix-update-helper.patch
+Patch25: pam-0.99.7.1-unix-hpux-aging.patch
Patch34: pam-0.99.7.0-dbpam.patch
Patch70: pam-0.99.2.1-selinux-nofail.patch
Patch80: pam-0.99.6.2-selinux-drop-multiple.patch
@@ -45,6 +46,8 @@ Patch95: pam-0.99.6.2-selinux-use-current-range.patch
Patch96: pam-0.99.6.2-namespace-dirnames.patch
Patch97: pam-0.99.7.1-namespace-unknown-user.patch
Patch98: pam-0.99.6.2-selinux-audit-context.patch
+Patch99: pam-0.99.6.2-namespace-docfix.patch
+Patch100: pam-0.99.7.1-namespace-temp-logon.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: cracklib, cracklib-dicts >= 2.8
@@ -100,9 +103,10 @@ cp %{SOURCE7} .
%patch1 -p1 -b .redhat-modules
%patch2 -p1 -b .displays
%patch3 -p1 -b .decrement
-%patch21 -p1 -b .unix-hpux-aging
%patch22 -p1 -b .pwmodify
%patch23 -p1 -b .bigcrypt
+%patch24 -p1 -b .update-helper
+%patch25 -p1 -b .unix-hpux-aging
%patch34 -p1 -b .dbpam
%patch70 -p1 -b .nofail
%patch80 -p1 -b .drop-multiple
@@ -118,6 +122,8 @@ cp %{SOURCE7} .
%patch96 -p1 -b .dirnames
%patch97 -p1 -b .unknown-user
%patch98 -p1 -b .audit-context
+%patch99 -p1 -b .docfix
+%patch100 -p1 -b .temp-logon
autoreconf
@@ -319,6 +325,7 @@ fi
%{_sbindir}/pam_tally2
%attr(4755,root,root) %{_sbindir}/pam_timestamp_check
%attr(4755,root,root) %{_sbindir}/unix_chkpwd
+%attr(0700,root,root) %{_sbindir}/unix_update
%if %{_lib} != lib
%dir /lib/security
%endif
@@ -406,6 +413,11 @@ fi
%doc doc/adg/*.txt doc/adg/html
%changelog
+* Thu Apr 26 2007 Tomas Mraz 0.99.7.1-6
+- pam_namespace: better document behavior on failure (#237249)
+- pam_unix: split out passwd change to a new helper binary (#236316)
+- pam_namespace: add support for temporary logons (#241226)
+
* Fri Apr 13 2007 Tomas Mraz 0.99.7.1-5
- pam_selinux: improve context change auditing (#234781)
- pam_namespace: fix parsing config file with unknown users (#234513)