diff --git a/pam-0.99.7.1-unix-hpux-aging.patch b/pam-0.99.7.1-unix-hpux-aging.patch
deleted file mode 100644
index 11d5274..0000000
--- a/pam-0.99.7.1-unix-hpux-aging.patch
+++ /dev/null
@@ -1,77 +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.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.8.1-audit-failed.patch b/pam-0.99.8.1-audit-failed.patch
new file mode 100644
index 0000000..33580b1
--- /dev/null
+++ b/pam-0.99.8.1-audit-failed.patch
@@ -0,0 +1,838 @@
+diff -up Linux-PAM-0.99.8.1/libpam/libpam.map.audit-failed Linux-PAM-0.99.8.1/libpam/libpam.map
+--- Linux-PAM-0.99.8.1/libpam/libpam.map.audit-failed 2006-06-14 17:28:44.000000000 +0200
++++ Linux-PAM-0.99.8.1/libpam/libpam.map 2008-01-22 22:24:05.000000000 +0100
+@@ -45,3 +45,7 @@ LIBPAM_MODUTIL_1.0 {
+ pam_modutil_read;
+ pam_modutil_write;
+ };
++LIBPAM_MODUTIL_1.1 {
++ global:
++ pam_modutil_audit_write;
++} LIBPAM_MODUTIL_1.0;
+diff -up Linux-PAM-0.99.8.1/libpam/pam_audit.c.audit-failed Linux-PAM-0.99.8.1/libpam/pam_audit.c
+--- Linux-PAM-0.99.8.1/libpam/pam_audit.c.audit-failed 2008-01-22 22:24:05.000000000 +0100
++++ Linux-PAM-0.99.8.1/libpam/pam_audit.c 2008-01-22 22:24:05.000000000 +0100
+@@ -42,39 +42,53 @@ _pam_audit_writelog(pam_handle_t *pamh,
+ best to fix it. */
+ errno = -rc;
+
++ if (rc < 0 && errno != old_errno)
++ {
++ old_errno = errno;
++ pam_syslog (pamh, LOG_CRIT, "audit_log_acct_message() failed: %m");
++ }
++
+ pamh->audit_state |= PAMAUDIT_LOGGED;
+
+- if (rc < 0) {
+- if (rc == -EPERM && getuid() != 0)
+- return 0;
+- if (errno != old_errno) {
+- old_errno = errno;
+- pam_syslog (pamh, LOG_CRIT, "audit_log_acct_message() failed: %m");
+- }
+- }
+- return rc;
++ if (rc == -EPERM && getuid () != 0)
++ return 0;
++ else
++ return rc;
+ }
+
+-int
+-_pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags)
++static int
++_pam_audit_open(pam_handle_t *pamh)
+ {
+- const char *message;
+- int type;
+ int audit_fd;
+-
+ audit_fd = audit_open();
+ if (audit_fd < 0) {
+ /* You get these error codes only when the kernel doesn't have
+ * audit compiled in. */
+ if (errno == EINVAL || errno == EPROTONOSUPPORT ||
+ errno == EAFNOSUPPORT)
+- return retval;
++ return -2;
+
+ /* this should only fail in case of extreme resource shortage,
+ * need to prevent login in that case for CAPP compliance.
+ */
+ pam_syslog(pamh, LOG_CRIT, "audit_open() failed: %m");
++ return -1;
++ }
++
++ return audit_fd;
++}
++
++int
++_pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags)
++{
++ const char *message;
++ int type;
++ int audit_fd;
++
++ if ((audit_fd=_pam_audit_open(pamh)) == -1) {
+ return PAM_SYSTEM_ERR;
++ } else if (audit_fd == -2) {
++ return retval;
+ }
+
+ switch (action) {
+@@ -141,4 +155,30 @@ _pam_audit_end(pam_handle_t *pamh, int s
+ return 0;
+ }
+
++int
++pam_modutil_audit_write(pam_handle_t *pamh, int type,
++ const char *message, int retval)
++{
++ int audit_fd;
++ int rc;
++
++ if ((audit_fd=_pam_audit_open(pamh)) == -1) {
++ return PAM_SYSTEM_ERR;
++ } else if (audit_fd == -2) {
++ return retval;
++ }
++
++ rc = _pam_audit_writelog(pamh, audit_fd, type, message, retval);
++
++ audit_close(audit_fd);
++
++ return rc < 0 ? PAM_SYSTEM_ERR : PAM_SUCCESS;
++}
++
++#else
++int pam_modutil_audit_write(pam_handle_t *pamh UNUSED, int type UNUSED,
++ const char *message UNUSED, int retval UNUSED)
++{
++ return PAM_SUCCESS;
++}
+ #endif /* HAVE_LIBAUDIT */
+diff -up Linux-PAM-0.99.8.1/modules/pam_access/pam_access.8.xml.audit-failed Linux-PAM-0.99.8.1/modules/pam_access/pam_access.8.xml
+--- Linux-PAM-0.99.8.1/modules/pam_access/pam_access.8.xml.audit-failed 2007-06-22 10:03:29.000000000 +0200
++++ Linux-PAM-0.99.8.1/modules/pam_access/pam_access.8.xml 2008-01-22 22:24:05.000000000 +0100
+@@ -29,6 +29,9 @@
+ nodefgroup
+
+
++ noaudit
++
++
+ accessfile=file
+
+
+@@ -54,6 +57,10 @@
+ /etc/security/access.conf if you don't specify
+ another file.
+
++
++ If Linux PAM is compiled with audit support the module will report
++ when it denies access based on origin (host or tty).
++
+
+
+
+@@ -87,6 +94,17 @@
+
+
+
++
++
++
++
++ Do not report logins from disallowed hosts and ttys to the audit subsystem.
++
++
++
++
++
++
+
+
+
+diff -up Linux-PAM-0.99.8.1/modules/pam_access/pam_access.c.audit-failed Linux-PAM-0.99.8.1/modules/pam_access/pam_access.c
+--- Linux-PAM-0.99.8.1/modules/pam_access/pam_access.c.audit-failed 2007-06-25 11:59:11.000000000 +0200
++++ Linux-PAM-0.99.8.1/modules/pam_access/pam_access.c 2008-01-22 22:24:05.000000000 +0100
+@@ -46,6 +46,10 @@
+ #include
+ #include
+
++#ifdef HAVE_LIBAUDIT
++#include
++#endif
++
+ /*
+ * here, we make definitions for the externally accessible functions
+ * in this file (these definitions are required for static modules
+@@ -81,17 +85,11 @@
+
+ /* Delimiters for fields and for lists of users, ttys or hosts. */
+
+-static const char *fs = ":"; /* field separator */
+-static const char *sep = ", \t"; /* list-element separator */
+-
+- /* Constants to be used in assignments only, not in comparisons... */
+
++#define ALL 2
+ #define YES 1
+ #define NO 0
+
+-/* Only allow group entries of the form "(xyz)" */
+-static int only_new_group_syntax = NO;
+-
+ /*
+ * A structure to bundle up all login-related information to keep the
+ * functional interfaces as generic as possible.
+@@ -100,12 +98,13 @@ struct login_info {
+ const struct passwd *user;
+ const char *from;
+ const char *config_file;
++ int debug; /* Print debugging messages. */
++ int only_new_group_syntax; /* Only allow group entries of the form "(xyz)" */
++ int noaudit; /* Do not audit denials */
++ const char *fs; /* field separator */
++ const char *sep; /* list-element separator */
+ };
+
+-/* Print debugging messages.
+- Default is NO which means don't print debugging messages. */
+-static char pam_access_debug = NO;
+-
+ /* Parse module config arguments */
+
+ static int
+@@ -113,17 +112,22 @@ parse_args(pam_handle_t *pamh, struct lo
+ int argc, const char **argv)
+ {
+ int i;
+-
++
++ loginfo->noaudit = NO;
++ loginfo->debug = NO;
++ loginfo->only_new_group_syntax = NO;
++ loginfo->fs = ":";
++ loginfo->sep = ", \t";
+ for (i=0; ifs = argv[i]+9;
+
+ } else if (!strncmp("listsep=", argv[i], 8)) {
+
+ /* the admin wants to override the default list separators */
+- sep = argv[i]+8;
++ loginfo->sep = argv[i]+8;
+
+ } else if (!strncmp("accessfile=", argv[i], 11)) {
+ FILE *fp = fopen(11 + argv[i], "r");
+@@ -138,9 +142,11 @@ parse_args(pam_handle_t *pamh, struct lo
+ }
+
+ } else if (strcmp (argv[i], "debug") == 0) {
+- pam_access_debug = YES;
++ loginfo->debug = YES;
+ } else if (strcmp (argv[i], "nodefgroup") == 0) {
+- only_new_group_syntax = YES;
++ loginfo->only_new_group_syntax = YES;
++ } else if (strcmp (argv[i], "noaudit") == 0) {
++ loginfo->noaudit = YES;
+ } else {
+ pam_syslog(pamh, LOG_ERR, "unrecognized option [%s]", argv[i]);
+ }
+@@ -153,13 +159,13 @@ parse_args(pam_handle_t *pamh, struct lo
+
+ typedef int match_func (pam_handle_t *, char *, struct login_info *);
+
+-static int list_match (pam_handle_t *, char *, struct login_info *,
++static int list_match (pam_handle_t *, char *, char *, struct login_info *,
+ match_func *);
+ static int user_match (pam_handle_t *, char *, struct login_info *);
+-static int group_match (pam_handle_t *, const char *, const char *);
++static int group_match (pam_handle_t *, const char *, const char *, int);
+ static int from_match (pam_handle_t *, char *, struct login_info *);
+-static int string_match (pam_handle_t *, const char *, const char *);
+-static int network_netmask_match (pam_handle_t *, const char *, const char *);
++static int string_match (pam_handle_t *, const char *, const char *, int);
++static int network_netmask_match (pam_handle_t *, const char *, const char *, int);
+
+
+ /* isipaddr - find out if string provided is an IP address or not */
+@@ -325,11 +331,12 @@ login_access (pam_handle_t *pamh, struct
+ char *users; /* becomes list of login names */
+ char *froms; /* becomes list of terminals or hosts */
+ int match = NO;
++ int nonall_match = NO;
+ int end;
+ int lineno = 0; /* for diagnostics */
+ char *sptr;
+
+- if (pam_access_debug)
++ if (item->debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "login_access: user=%s, from=%s, file=%s",
+ item->user->pw_name,
+@@ -361,8 +368,8 @@ login_access (pam_handle_t *pamh, struct
+ continue;
+
+ /* Allow field seperator in last field of froms */
+- if (!(perm = strtok_r(line, fs, &sptr))
+- || !(users = strtok_r(NULL, fs, &sptr))
++ if (!(perm = strtok_r(line, item->fs, &sptr))
++ || !(users = strtok_r(NULL, item->fs, &sptr))
+ || !(froms = strtok_r(NULL, "\n", &sptr))) {
+ pam_syslog(pamh, LOG_ERR, "%s: line %d: bad field count",
+ item->config_file, lineno);
+@@ -373,17 +380,22 @@ login_access (pam_handle_t *pamh, struct
+ item->config_file, lineno);
+ continue;
+ }
+- if (pam_access_debug)
++ if (item->debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "line %d: %s : %s : %s", lineno, perm, users, froms);
+- match = list_match(pamh, froms, item, from_match);
+- if (pam_access_debug)
+- pam_syslog (pamh, LOG_DEBUG,
+- "from_match=%d, \"%s\"", match, item->from);
+- match = match && list_match (pamh, users, item, user_match);
+- if (pam_access_debug)
++ match = list_match(pamh, users, NULL, item, user_match);
++ if (item->debug)
+ pam_syslog (pamh, LOG_DEBUG, "user_match=%d, \"%s\"",
+ match, item->user->pw_name);
++ if (match) {
++ match = list_match(pamh, froms, NULL, item, from_match);
++ if (!match && perm[0] == '+') {
++ nonall_match = YES;
++ }
++ if (item->debug)
++ pam_syslog (pamh, LOG_DEBUG,
++ "from_match=%d, \"%s\"", match, item->from);
++ }
+ }
+ (void) fclose(fp);
+ } else if (errno == ENOENT) {
+@@ -394,20 +406,27 @@ login_access (pam_handle_t *pamh, struct
+ pam_syslog(pamh, LOG_ERR, "cannot open %s: %m", item->config_file);
+ return NO;
+ }
++#ifdef HAVE_LIBAUDIT
++ if (!item->noaudit && line[0] == '-' && (match == YES || (match == ALL &&
++ nonall_match == YES))) {
++ pam_modutil_audit_write(pamh, AUDIT_ANOM_LOGIN_LOCATION,
++ "pam_access", 0);
++ }
++#endif
+ return (match == NO || (line[0] == '+'));
+ }
+
+
+ /* list_match - match an item against a list of tokens with exceptions */
+
+-static int list_match(pam_handle_t *pamh,
+- char *list, struct login_info *item, match_func *match_fn)
++static int
++list_match(pam_handle_t *pamh, char *list, char *sptr,
++ struct login_info *item, match_func *match_fn)
+ {
+ char *tok;
+ int match = NO;
+- char *sptr;
+
+- if (pam_access_debug)
++ if (item->debug && list != NULL)
+ pam_syslog (pamh, LOG_DEBUG,
+ "list_match: list=%s, item=%s", list, item->user->pw_name);
+
+@@ -418,8 +437,8 @@ static int list_match(pam_handle_t *pamh
+ * the match is affected by any exceptions.
+ */
+
+- for (tok = strtok_r(list, sep, &sptr); tok != 0;
+- tok = strtok_r(NULL, sep, &sptr)) {
++ for (tok = strtok_r(list, item->sep, &sptr); tok != 0;
++ tok = strtok_r(NULL, item->sep, &sptr)) {
+ if (strcasecmp(tok, "EXCEPT") == 0) /* EXCEPT: give up */
+ break;
+ if ((match = (*match_fn) (pamh, tok, item))) /* YES */
+@@ -428,10 +447,12 @@ static int list_match(pam_handle_t *pamh
+ /* Process exceptions to matches. */
+
+ if (match != NO) {
+- while ((tok = strtok_r(NULL, sep, &sptr)) && strcasecmp(tok, "EXCEPT"))
++ while ((tok = strtok_r(NULL, item->sep, &sptr)) && strcasecmp(tok, "EXCEPT"))
+ /* VOID */ ;
+- if (tok == 0 || list_match(pamh, sptr, item, match_fn) == NO)
+- return (match);
++ if (tok == 0)
++ return match;
++ if (list_match(pamh, NULL, sptr, item, match_fn) == NO)
++ return YES; /* drop special meaning of ALL */
+ }
+ return (NO);
+ }
+@@ -453,7 +474,7 @@ static char *myhostname(void)
+
+ static int
+ netgroup_match (pam_handle_t *pamh, const char *netgroup,
+- const char *machine, const char *user)
++ const char *machine, const char *user, int debug)
+ {
+ char *mydomain = NULL;
+ int retval;
+@@ -462,7 +483,7 @@ netgroup_match (pam_handle_t *pamh, cons
+
+
+ retval = innetgr (netgroup, machine, user, mydomain);
+- if (pam_access_debug == YES)
++ if (debug == YES)
+ pam_syslog (pamh, LOG_DEBUG,
+ "netgroup_match: %d (netgroup=%s, machine=%s, user=%s, domain=%s)",
+ retval, netgroup ? netgroup : "NULL",
+@@ -480,8 +501,9 @@ user_match (pam_handle_t *pamh, char *to
+ char *string = item->user->pw_name;
+ struct login_info fake_item;
+ char *at;
++ int rv;
+
+- if (pam_access_debug)
++ if (item->debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "user_match: tok=%s, item=%s", tok, string);
+
+@@ -500,12 +522,12 @@ user_match (pam_handle_t *pamh, char *to
+ return (user_match (pamh, tok, item) &&
+ from_match (pamh, at + 1, &fake_item));
+ } else if (tok[0] == '@') /* netgroup */
+- return (netgroup_match (pamh, tok + 1, (char *) 0, string));
++ return (netgroup_match (pamh, tok + 1, (char *) 0, string, item->debug));
+ else if (tok[0] == '(' && tok[strlen(tok) - 1] == ')')
+- return (group_match (pamh, tok, string));
+- else if (string_match (pamh, tok, string)) /* ALL or exact match */
+- return YES;
+- else if (only_new_group_syntax == NO &&
++ return (group_match (pamh, tok, string, item->debug));
++ else if ((rv=string_match (pamh, tok, string, item->debug)) != NO) /* ALL or exact match */
++ return rv;
++ else if (item->only_new_group_syntax == NO &&
+ pam_modutil_user_in_group_nam_nam (pamh,
+ item->user->pw_name, tok))
+ /* try group membership */
+@@ -518,11 +540,12 @@ user_match (pam_handle_t *pamh, char *to
+ /* group_match - match a username against token named group */
+
+ static int
+-group_match (pam_handle_t *pamh, const char *tok, const char* usr)
++group_match (pam_handle_t *pamh, const char *tok, const char* usr,
++ int debug)
+ {
+ char grptok[BUFSIZ];
+
+- if (pam_access_debug)
++ if (debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "group_match: grp=%s, user=%s", grptok, usr);
+
+@@ -548,8 +571,9 @@ from_match (pam_handle_t *pamh UNUSED, c
+ const char *string = item->from;
+ int tok_len;
+ int str_len;
++ int rv;
+
+- if (pam_access_debug)
++ if (item->debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "from_match: tok=%s, item=%s", tok, string);
+
+@@ -565,10 +589,10 @@ from_match (pam_handle_t *pamh UNUSED, c
+ if (string == NULL) {
+ return NO;
+ } else if (tok[0] == '@') { /* netgroup */
+- return (netgroup_match (pamh, tok + 1, string, (char *) 0));
+- } else if (string_match(pamh, tok, string)) {
++ return (netgroup_match (pamh, tok + 1, string, (char *) 0, item->debug));
++ } else if ((rv = string_match(pamh, tok, string, item->debug)) != NO) {
+ /* ALL or exact match */
+- return (YES);
++ return rv;
+ } else if (tok[0] == '.') { /* domain: match last fields */
+ if ((str_len = strlen(string)) > (tok_len = strlen(tok))
+ && strcasecmp(tok, string + str_len - tok_len) == 0)
+@@ -614,7 +638,7 @@ from_match (pam_handle_t *pamh UNUSED, c
+ }
+ } else if (isipaddr(string, NULL, NULL) == YES) {
+ /* Assume network/netmask with a IP of a host. */
+- if (network_netmask_match(pamh, tok, string))
++ if (network_netmask_match(pamh, tok, string, item->debug))
+ return YES;
+ } else {
+ /* Assume network/netmask with a name of a host. */
+@@ -641,7 +665,7 @@ from_match (pam_handle_t *pamh UNUSED, c
+ : (void *) &((struct sockaddr_in6 *) runp->ai_addr)->sin6_addr,
+ buf, sizeof (buf));
+
+- if (network_netmask_match(pamh, tok, buf))
++ if (network_netmask_match(pamh, tok, buf, item->debug))
+ {
+ freeaddrinfo (res);
+ return YES;
+@@ -658,10 +682,11 @@ from_match (pam_handle_t *pamh UNUSED, c
+ /* string_match - match a string against one token */
+
+ static int
+-string_match (pam_handle_t *pamh, const char *tok, const char *string)
++string_match (pam_handle_t *pamh, const char *tok, const char *string,
++ int debug)
+ {
+
+- if (pam_access_debug)
++ if (debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "string_match: tok=%s, item=%s", tok, string);
+
+@@ -672,7 +697,7 @@ string_match (pam_handle_t *pamh, const
+ */
+
+ if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */
+- return (YES);
++ return (ALL);
+ } else if (string != NULL) {
+ if (strcasecmp(tok, string) == 0) { /* try exact match */
+ return (YES);
+@@ -690,9 +715,9 @@ string_match (pam_handle_t *pamh, const
+ */
+ static int
+ network_netmask_match (pam_handle_t *pamh,
+- const char *tok, const char *string)
++ const char *tok, const char *string, int debug)
+ {
+- if (pam_access_debug)
++ if (debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "network_netmask_match: tok=%s, item=%s", tok, string);
+
+@@ -771,6 +796,22 @@ pam_sm_authenticate (pam_handle_t *pamh,
+ return PAM_USER_UNKNOWN;
+ }
+
++ if ((user_pw=pam_modutil_getpwnam(pamh, user))==NULL)
++ return (PAM_USER_UNKNOWN);
++
++ /*
++ * Bundle up the arguments to avoid unnecessary clumsiness later on.
++ */
++ loginfo.user = user_pw;
++ loginfo.config_file = PAM_ACCESS_CONFIG;
++
++ /* parse the argument list */
++
++ if (!parse_args(pamh, &loginfo, argc, argv)) {
++ pam_syslog(pamh, LOG_ERR, "failed to parse the module arguments");
++ return PAM_ABORT;
++ }
++
+ /* remote host name */
+
+ if (pam_get_item(pamh, PAM_RHOST, &void_from)
+@@ -799,7 +840,7 @@ pam_sm_authenticate (pam_handle_t *pamh,
+ return PAM_ABORT;
+ }
+ from = void_from;
+- if (pam_access_debug)
++ if (loginfo.debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "cannot determine tty or remote hostname, using service %s",
+ from);
+@@ -817,22 +858,7 @@ pam_sm_authenticate (pam_handle_t *pamh,
+ }
+ }
+
+- if ((user_pw=pam_modutil_getpwnam(pamh, user))==NULL)
+- return (PAM_USER_UNKNOWN);
+-
+- /*
+- * Bundle up the arguments to avoid unnecessary clumsiness later on.
+- */
+- loginfo.user = user_pw;
+ loginfo.from = from;
+- loginfo.config_file = PAM_ACCESS_CONFIG;
+-
+- /* parse the argument list */
+-
+- if (!parse_args(pamh, &loginfo, argc, argv)) {
+- pam_syslog(pamh, LOG_ERR, "failed to parse the module arguments");
+- return PAM_ABORT;
+- }
+
+ if (login_access(pamh, &loginfo)) {
+ return (PAM_SUCCESS);
+diff -up Linux-PAM-0.99.8.1/modules/pam_time/pam_time.c.audit-failed Linux-PAM-0.99.8.1/modules/pam_time/pam_time.c
+--- Linux-PAM-0.99.8.1/modules/pam_time/pam_time.c.audit-failed 2006-06-16 08:35:16.000000000 +0200
++++ Linux-PAM-0.99.8.1/modules/pam_time/pam_time.c 2008-01-22 22:24:05.000000000 +0100
+@@ -22,9 +22,16 @@
+ #include
+ #include
+
++#ifdef HAVE_LIBAUDIT
++#include
++#endif
++
+ #define PAM_TIME_BUFLEN 1000
+ #define FIELD_SEPARATOR ';' /* this is new as of .02 */
+
++#define PAM_DEBUG_ARG 0x0001
++#define PAM_NO_AUDIT 0x0002
++
+ #ifndef TRUE
+ # define TRUE 1
+ #endif
+@@ -46,6 +53,29 @@ typedef enum { AND, OR } operator;
+ #include
+ #include
+ #include
++#include
++
++static int
++_pam_parse (const pam_handle_t *pamh, int argc, const char **argv)
++{
++ int ctrl = 0;
++
++ /* step through arguments */
++ for (; argc-- > 0; ++argv) {
++
++ /* generic options */
++
++ if (!strcmp(*argv, "debug")) {
++ ctrl |= PAM_DEBUG_ARG;
++ } else if (!strcmp(*argv, "noaudit")) {
++ ctrl |= PAM_NO_AUDIT;
++ } else {
++ pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
++ }
++ }
++
++ return ctrl;
++}
+
+ /* --- static functions for checking whether the user should be let in --- */
+
+@@ -59,7 +89,7 @@ shift_bytes(char *mem, int from, int by)
+ }
+
+ static int
+-read_field(pam_handle_t *pamh, int fd, char **buf, int *from, int *to)
++read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *to)
+ {
+ /* is buf set ? */
+
+@@ -137,6 +167,7 @@ read_field(pam_handle_t *pamh, int fd, c
+ switch ((*buf)[i]) {
+ int j,c;
+ case '#':
++ c = 0;
+ for (j=i; j < *to && (c = (*buf)[j]) != '\n'; ++j);
+ if (j >= *to) {
+ (*buf)[*to = ++i] = '\0';
+@@ -324,6 +355,13 @@ is_same(pam_handle_t *pamh UNUSED, const
+ return FALSE;
+ }
+ }
++
++ /* Ok, we know that b is a substring from A and does not contain
++ wildcards, but now the length of both strings must be the same,
++ too. */
++ if (strlen (a) != strlen(b))
++ return FALSE;
++
+ return ( !len );
+ }
+
+@@ -559,11 +597,15 @@ check_account(pam_handle_t *pamh, const
+
+ PAM_EXTERN int
+ pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED,
+- int argc UNUSED, const char **argv UNUSED)
++ int argc, const char **argv)
+ {
+ const void *service=NULL, *void_tty=NULL;
+ const char *tty;
+ const char *user=NULL;
++ int ctrl;
++ int rv;
++
++ ctrl = _pam_parse(pamh, argc, argv);
+
+ /* set service name */
+
+@@ -612,7 +654,19 @@ pam_sm_acct_mgmt(pam_handle_t *pamh, int
+ D(("user=%s", user));
+ D(("tty=%s", tty));
+
+- return check_account(pamh, service, tty, user);
++ rv = check_account(pamh, service, tty, user);
++ if (rv != PAM_SUCCESS) {
++#ifdef HAVE_LIBAUDIT
++ if (!(ctrl & PAM_NO_AUDIT)) {
++ pam_modutil_audit_write(pamh, AUDIT_ANOM_LOGIN_TIME,
++ "pam_time", rv); /* ignore return value as we fail anyway */
++ }
++#endif
++ if (ctrl & PAM_DEBUG_ARG) {
++ pam_syslog(pamh, LOG_DEBUG, "user %s rejected", user);
++ }
++ }
++ return rv;
+ }
+
+ /* end of module definition */
+diff -up Linux-PAM-0.99.8.1/modules/pam_time/pam_time.8.xml.audit-failed Linux-PAM-0.99.8.1/modules/pam_time/pam_time.8.xml
+--- Linux-PAM-0.99.8.1/modules/pam_time/pam_time.8.xml.audit-failed 2006-06-22 21:44:30.000000000 +0200
++++ Linux-PAM-0.99.8.1/modules/pam_time/pam_time.8.xml 2008-01-22 22:24:05.000000000 +0100
+@@ -22,6 +22,12 @@
+
+
+ pam_time.so
++
++ debug
++
++
++ noaudit
++
+
+
+
+@@ -41,11 +47,40 @@
+ By default rules for time/port access are taken from config file
+ /etc/security/time.conf.
+
++
++ If Linux PAM is compiled with audit support the module will report
++ when it denies access.
++
+
+
+
+ OPTIONS
+- This module does not recognice any options.
++
++
++
++
++
++
++
++
++ Some debug informations are printed with
++ syslog3.
++
++
++
++
++
++
++
++
++
++
++ Do not report logins at disallowed time to the audit subsystem.
++
++
++
++
++
+
+
+
+diff -up Linux-PAM-0.99.8.1/modules/pam_limits/pam_limits.c.audit-failed Linux-PAM-0.99.8.1/modules/pam_limits/pam_limits.c
+--- Linux-PAM-0.99.8.1/modules/pam_limits/pam_limits.c.audit-failed 2007-07-10 12:10:56.000000000 +0200
++++ Linux-PAM-0.99.8.1/modules/pam_limits/pam_limits.c 2008-01-22 22:41:40.000000000 +0100
+@@ -41,6 +41,10 @@
+ #include
+ #include
+
++#ifdef HAVE_LIBAUDIT
++#include
++#endif
++
+ /* Module defines */
+ #define LINE_LENGTH 1024
+
+@@ -101,6 +105,7 @@ struct pam_limit_s {
+ #define PAM_DEBUG_ARG 0x0001
+ #define PAM_DO_SETREUID 0x0002
+ #define PAM_UTMP_EARLY 0x0004
++#define PAM_NO_AUDIT 0x0008
+
+ /* Limits from globbed files. */
+ #define LIMITS_CONF_GLOB LIMITS_FILE_DIR
+@@ -126,6 +131,8 @@ _pam_parse (const pam_handle_t *pamh, in
+ ctrl |= PAM_DO_SETREUID;
+ } else if (!strcmp(*argv,"utmp_early")) {
+ ctrl |= PAM_UTMP_EARLY;
++ } else if (!strcmp(*argv,"noaudit")) {
++ ctrl |= PAM_NO_AUDIT;
+ } else {
+ pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv);
+ }
+@@ -599,6 +606,13 @@ static int setup_limits(pam_handle_t *pa
+ D(("skip login limit check for uid=0"));
+ } else if (pl->login_limit > 0) {
+ if (check_logins(pamh, uname, pl->login_limit, ctrl, pl) == LOGIN_ERR) {
++#ifdef HAVE_LIBAUDIT
++ if (!(ctrl & PAM_NO_AUDIT)) {
++ pam_modutil_audit_write(pamh, AUDIT_ANOM_LOGIN_SESSIONS,
++ "pam_limits", PAM_PERM_DENIED);
++ /* ignore return value as we fail anyway */
++ }
++#endif
+ retval |= LOGIN_ERR;
+ }
+ } else if (pl->login_limit == 0) {
+diff -up Linux-PAM-0.99.8.1/modules/pam_limits/pam_limits.8.xml.audit-failed Linux-PAM-0.99.8.1/modules/pam_limits/pam_limits.8.xml
+--- Linux-PAM-0.99.8.1/modules/pam_limits/pam_limits.8.xml.audit-failed 2007-04-30 12:47:26.000000000 +0200
++++ Linux-PAM-0.99.8.1/modules/pam_limits/pam_limits.8.xml 2008-01-22 22:24:05.000000000 +0100
+@@ -34,6 +34,9 @@
+
+ utmp_early
+
++
++ noaudit
++
+
+
+
+@@ -57,6 +60,11 @@
+
+ The module must not be called by a multithreaded application.
+
++
++ If Linux PAM is compiled with audit support the module will report
++ when it denies access based on limit of maximum number of concurrent
++ login sessions.
++
+
+
+
+@@ -111,6 +119,16 @@
+
+
+
++
++
++
++
++
++
++ Do not report exceeded maximum logins count to the audit subsystem.
++
++
++
+
+
+
diff --git a/pam-0.99.8.1-sepermit-kill-user.patch b/pam-0.99.8.1-sepermit-kill-user.patch
new file mode 100644
index 0000000..b33d81a
--- /dev/null
+++ b/pam-0.99.8.1-sepermit-kill-user.patch
@@ -0,0 +1,318 @@
+diff -up Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.c.kill-user Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.c
+--- Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.c.kill-user 2008-01-28 18:34:18.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.c 2008-01-28 18:34:18.000000000 +0100
+@@ -1,7 +1,7 @@
+ /******************************************************************************
+ * A module for Linux-PAM that allows/denies acces based on SELinux state.
+ *
+- * Copyright (c) 2007 Red Hat, Inc.
++ * Copyright (c) 2007, 2008 Red Hat, Inc.
+ * Written by Tomas Mraz
+ *
+ * Redistribution and use in source and binary forms, with or without
+@@ -46,6 +46,14 @@
+ #include
+ #include
+ #include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
+
+ #define PAM_SM_AUTH
+ #define PAM_SM_ACCOUNT
+@@ -57,6 +65,165 @@
+
+ #include
+
++#define MODULE "pam_selinux_permit"
++#define OPT_DELIM ":"
++
++struct lockfd {
++ uid_t uid;
++ int fd;
++ int debug;
++};
++
++#define PROC_BASE "/proc"
++#define MAX_NAMES (int)(sizeof(unsigned long)*8)
++
++static int
++match_process_uid(pid_t pid, uid_t uid)
++{
++ char buf[128];
++ uid_t puid;
++ FILE *f;
++ int re = 0;
++
++ snprintf (buf, sizeof buf, PROC_BASE "/%d/status", pid);
++ if (!(f = fopen (buf, "r")))
++ return 0;
++
++ while (fgets(buf, sizeof buf, f)) {
++ if (sscanf (buf, "Uid:\t%d", &puid)) {
++ re = uid == puid;
++ break;
++ }
++ }
++ fclose(f);
++ return re;
++}
++
++static int
++check_running (pam_handle_t *pamh, uid_t uid, int killall, int debug)
++{
++ DIR *dir;
++ struct dirent *de;
++ pid_t *pid_table, pid, self;
++ int i;
++ int pids, max_pids;
++ int running = 0;
++ self = getpid();
++ if (!(dir = opendir(PROC_BASE))) {
++ pam_syslog(pamh, LOG_ERR, "Failed to open proc directory file %s:", PROC_BASE);
++ return -1;
++ }
++ max_pids = 256;
++ pid_table = malloc(max_pids * sizeof (pid_t));
++ if (!pid_table) {
++ pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
++ return -1;
++ }
++ pids = 0;
++ while ((de = readdir (dir)) != NULL) {
++ if (!(pid = (pid_t)atoi(de->d_name)) || pid == self)
++ continue;
++
++ if (pids == max_pids) {
++ if (!(pid_table = realloc(pid_table, 2*pids*sizeof(pid_t)))) {
++ pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
++ return -1;
++ }
++ max_pids *= 2;
++ }
++ pid_table[pids++] = pid;
++ }
++
++ (void)closedir(dir);
++
++ for (i = 0; i < pids; i++) {
++ pid_t id;
++
++ if (match_process_uid(pid_table[i], uid) == 0)
++ continue;
++ id = pid_table[i];
++
++ if (killall) {
++ if (debug)
++ pam_syslog(pamh, LOG_NOTICE, "Attempting to kill %d", id);
++ kill(id, SIGKILL);
++ }
++ running++;
++ }
++
++ free(pid_table);
++ return running;
++}
++
++static void
++sepermit_unlock(pam_handle_t *pamh, void *plockfd, int error_status UNUSED)
++{
++ struct lockfd *lockfd = plockfd;
++ struct flock fl;
++
++ memset(&fl, 0, sizeof(fl));
++ fl.l_type = F_UNLCK;
++ fl.l_whence = SEEK_SET;
++
++ if (lockfd->debug)
++ pam_syslog(pamh, LOG_ERR, "Unlocking fd: %d uid: %d", lockfd->fd, lockfd->uid);
++
++ /* Don't kill uid==0 */
++ if (lockfd->uid)
++ /* This is a DOS but it prevents an app from forking to prevent killing */
++ while(check_running(pamh, lockfd->uid, 1, lockfd->debug) > 0)
++ continue;
++
++ fcntl(lockfd->fd, F_SETLK, &fl);
++ close(lockfd->fd);
++ free(lockfd);
++}
++
++static int
++sepermit_lock(pam_handle_t *pamh, const char *user, int debug)
++{
++ char buf[PATH_MAX];
++ struct flock fl;
++
++ memset(&fl, 0, sizeof(fl));
++ fl.l_type = F_WRLCK;
++ fl.l_whence = SEEK_SET;
++
++ struct passwd *pw = pam_modutil_getpwnam( pamh, user );
++ if (!pw) {
++ pam_syslog(pamh, LOG_ERR, "Unable to find uid for user %s", user);
++ return -1;
++ }
++ if (check_running(pamh, pw->pw_uid, 0, debug) > 0) {
++ pam_syslog(pamh, LOG_ERR, "User %s processes are running. Exclusive login not allowed", user);
++ return -1;
++ }
++
++ snprintf(buf, sizeof(buf), "%s/%d.lock", SEPERMIT_LOCKDIR, pw->pw_uid);
++ int fd = open(buf, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
++ if (fd < 0) {
++ pam_syslog(pamh, LOG_ERR, "Unable to open lock file %s/%d.lock", SEPERMIT_LOCKDIR, pw->pw_uid);
++ return -1;
++ }
++
++ if (fcntl(fd, F_SETLK, &fl) == -1) {
++ pam_syslog(pamh, LOG_ERR, "User %s with exclusive login already logged in", user);
++ close(fd);
++ return -1;
++ }
++ struct lockfd *lockfd=calloc(1, sizeof(struct lockfd));
++ if (!lockfd) {
++ close(fd);
++ pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
++ return -1;
++ }
++ lockfd->uid = pw->pw_uid;
++ lockfd->debug = debug;
++ lockfd->fd=fd;
++ pam_set_data(pamh, "pam_selinux_permit", lockfd, sepermit_unlock);
++ return 0;
++}
++
+ /* return 0 when matched, -1 when unmatched, pam error otherwise */
+ static int
+ sepermit_match(pam_handle_t *pamh, const char *cfgfile, const char *user,
+@@ -67,6 +234,7 @@ sepermit_match(pam_handle_t *pamh, const
+ char *start;
+ size_t len = 0;
+ int matched = 0;
++ int exclusive = 0;
+
+ f = fopen(cfgfile, "r");
+
+@@ -77,6 +245,8 @@ sepermit_match(pam_handle_t *pamh, const
+
+ while (!matched && getline(&line, &len, f) != -1) {
+ size_t n;
++ char *sptr;
++ char *opt;
+
+ if (line[0] == '#')
+ continue;
+@@ -92,6 +262,7 @@ sepermit_match(pam_handle_t *pamh, const
+ continue;
+
+ start[n] = '\0';
++ start = strtok_r(start, OPT_DELIM, &sptr);
+
+ switch (start[0]) {
+ case '@':
+@@ -117,11 +288,22 @@ sepermit_match(pam_handle_t *pamh, const
+ matched = 1;
+ }
+ }
++ if (matched)
++ while ((opt=strtok_r(NULL, OPT_DELIM, &sptr)) != NULL) {
++ if (strcmp(opt, "exclusive") == 0)
++ exclusive = 1;
++ else if (debug) {
++ pam_syslog(pamh, LOG_NOTICE, "Unknown user option: %s", opt);
++ }
++ }
+ }
+
+ free(line);
+ fclose(f);
+- return matched ? 0 : -1;
++ if (matched)
++ return exclusive ? sepermit_lock(pamh, user, debug) : 0;
++ else
++ return -1;
+ }
+
+ PAM_EXTERN int
+diff -up Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.8.xml.kill-user Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.8.xml
+--- Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.8.xml.kill-user 2008-01-28 18:34:18.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_selinux/pam_selinux_permit.8.xml 2008-01-28 18:34:18.000000000 +0100
+@@ -30,8 +30,8 @@
+
+ DESCRIPTION
+
+- The pam_selinux module allows or denies login depending on SELinux enforcement
+- state.
++ The pam_selinux_permit module allows or denies login depending on SELinux
++ enforcement state.
+
+
+ When the user which is logging in matches an entry in the config file
+@@ -41,14 +41,21 @@
+
+
+ The config file contains a simple list of user names one per line. If the
+- name is prefixed with @ character it means that all
++ name is prefixed with @ character it means that all
+ users in the group name match. If it is prefixed
+- with a % character the SELinux user is used to match against the name
++ with a % character the SELinux user is used to match against the name
+ instead of the account name. Note that when SELinux is disabled the
+ SELinux user assigned to the account cannot be determined. This means that
+ such entries are never matched when SELinux is disabled and pam_selinux_permit
+ will return PAM_IGNORE.
+
++
++ Each user name in the configuration file can have optional arguments separated
++ by : character. The only currently recognized argument is exclusive.
++ The pam_selinux_permit module will allow only single concurrent user session for
++ the user with this argument specified and it will attempt to kill all processes
++ of the user after logout.
++
+
+
+
+diff -up Linux-PAM-0.99.8.1/modules/pam_selinux/Makefile.am.kill-user Linux-PAM-0.99.8.1/modules/pam_selinux/Makefile.am
+--- Linux-PAM-0.99.8.1/modules/pam_selinux/Makefile.am.kill-user 2008-01-28 18:34:18.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_selinux/Makefile.am 2008-01-28 18:35:01.000000000 +0100
+@@ -16,10 +16,13 @@ XMLS = README.xml pam_selinux.8.xml pam_
+
+ securelibdir = $(SECUREDIR)
+ secureconfdir = $(SCONFIGDIR)
++sepermitlockdir = /var/run/sepermit
+
+ AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
+ -I$(top_srcdir)/libpam_misc/include \
+- -D SEPERMIT_CONF_FILE=\"$(SCONFIGDIR)/sepermit.conf\"
++ -D SEPERMIT_CONF_FILE=\"$(SCONFIGDIR)/sepermit.conf\" \
++ -D SEPERMIT_LOCKDIR=\"$(sepermitlockdir)\"
++
+ AM_LDFLAGS = -no-undefined \
+ -L$(top_builddir)/libpam -lpam @LIBSELINUX@
+
+@@ -34,6 +37,7 @@ endif
+ pam_selinux_permit_la_LDFLAGS= $(pam_selinux_la_LDFLAGS)
+
+ secureconf_DATA = sepermit.conf
++sepermitlock_DATA =
+
+ if HAVE_LIBSELINUX
+ securelib_LTLIBRARIES = pam_selinux.la pam_selinux_permit.la
+diff -up Linux-PAM-0.99.8.1/modules/pam_selinux/sepermit.conf.kill-user Linux-PAM-0.99.8.1/modules/pam_selinux/sepermit.conf
+--- Linux-PAM-0.99.8.1/modules/pam_selinux/sepermit.conf.kill-user 2008-01-28 18:34:18.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_selinux/sepermit.conf 2008-01-28 18:34:18.000000000 +0100
+@@ -4,3 +4,8 @@
+ # - an user name
+ # - a group name, with @group syntax
+ # - a SELinux user name, with %seuser syntax
++# Each line can contain optional arguments separated by :
++# The possible arguments are:
++# - exclusive - only single login session will
++# be allowed for the user and the user's processes
++# will be killed on logout
diff --git a/pam-0.99.8.1-setkeycreatecon.patch b/pam-0.99.8.1-setkeycreatecon.patch
new file mode 100644
index 0000000..a9bedff
--- /dev/null
+++ b/pam-0.99.8.1-setkeycreatecon.patch
@@ -0,0 +1,31 @@
+diff -up Linux-PAM-0.99.8.1/configure.in.setkeycreatecon Linux-PAM-0.99.8.1/configure.in
+--- Linux-PAM-0.99.8.1/configure.in.setkeycreatecon 2008-01-28 17:22:40.000000000 +0100
++++ Linux-PAM-0.99.8.1/configure.in 2008-01-28 17:26:25.000000000 +0100
+@@ -379,6 +379,7 @@ AC_SUBST(LIBDB)
+ AM_CONDITIONAL([HAVE_LIBDB], [test ! -z "$LIBDB"])
+
+ AC_CHECK_LIB([nsl],[yp_get_default_domain], LIBNSL="-lnsl", LIBNSL="")
++BACKUP_LIBS=$LIBS
+ LIBS="$LIBS $LIBNSL"
+ AC_CHECK_FUNCS(yp_get_default_domain)
+ LIBS=$BACKUP_LIBS
+@@ -396,6 +397,10 @@ AC_SUBST(LIBSELINUX)
+ AM_CONDITIONAL([HAVE_LIBSELINUX], [test ! -z "$LIBSELINUX"])
+ if test ! -z "$LIBSELINUX" ; then
+ AC_DEFINE([WITH_SELINUX], 1, [Defined if SE Linux support is compiled in])
++ BACKUP_LIBS=$LIBS
++ LIBS="$LIBS $LIBSELINUX"
++ AC_CHECK_FUNCS(setkeycreatecon)
++ LIBS=$BACKUP_LIBS
+ fi
+
+ dnl Checks for header files.
+@@ -428,7 +433,7 @@ AC_CHECK_FUNCS(fseeko gethostname gettim
+ AC_CHECK_FUNCS(strcspn strdup strspn strstr strtol uname)
+ AC_CHECK_FUNCS(getpwnam_r getpwuid_r getgrnam_r getgrgid_r getspnam_r)
+ AC_CHECK_FUNCS(getgrouplist getline getdelim)
+-AC_CHECK_FUNCS(inet_ntop inet_pton ruserok_af setkeycreatecon)
++AC_CHECK_FUNCS(inet_ntop inet_pton ruserok_af)
+
+ AC_CHECK_FUNCS(unshare, [UNSHARE=yes], [UNSHARE=no])
+ AM_CONDITIONAL([HAVE_UNSHARE], [test "$UNSHARE" = yes])
diff --git a/pam-0.99.8.1-substack.patch b/pam-0.99.8.1-substack.patch
new file mode 100644
index 0000000..912df83
--- /dev/null
+++ b/pam-0.99.8.1-substack.patch
@@ -0,0 +1,784 @@
+Index: libpam/pam_dispatch.c
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/libpam/pam_dispatch.c,v
+retrieving revision 1.11
+diff -u -p -r1.11 pam_dispatch.c
+--- libpam/pam_dispatch.c 1 Aug 2006 08:54:57 -0000 1.11
++++ libpam/pam_dispatch.c 12 Oct 2007 16:23:37 -0000
+@@ -34,7 +34,8 @@
+ static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
+ _pam_boolean resumed, int use_cached_chain)
+ {
+- int depth, impression, status, skip_depth;
++ int depth, impression, status, skip_depth, prev_level, stack_level;
++ struct _pam_substack_state *substates = NULL;
+
+ IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR);
+
+@@ -54,27 +55,51 @@ static int _pam_dispatch_aux(pam_handle_
+ skip_depth = pamh->former.depth;
+ status = pamh->former.status;
+ impression = pamh->former.impression;
++ substates = pamh->former.substates;
+ /* forget all that */
+ pamh->former.impression = _PAM_UNDEF;
+ pamh->former.status = PAM_MUST_FAIL_CODE;
+ pamh->former.depth = 0;
++ pamh->former.substates = NULL;
+ } else {
+ skip_depth = 0;
+- impression = _PAM_UNDEF;
+- status = PAM_MUST_FAIL_CODE;
++ substates = malloc(PAM_SUBSTACK_MAX_LEVEL * sizeof(*substates));
++ if (substates == NULL) {
++ pam_syslog(pamh, LOG_CRIT,
++ "_pam_dispatch_aux: no memory for substack states");
++ return PAM_BUF_ERR;
++ }
++ substates[0].impression = impression = _PAM_UNDEF;
++ substates[0].status = status = PAM_MUST_FAIL_CODE;
+ }
+
++ prev_level = 0;
++
+ /* Loop through module logic stack */
+- for (depth=0 ; h != NULL ; h = h->next, ++depth) {
++ for (depth=0 ; h != NULL ; prev_level = stack_level, h = h->next, ++depth) {
+ int retval, cached_retval, action;
+
++ stack_level = h->stack_level;
++
+ /* skip leading modules if they have already returned */
+ if (depth < skip_depth) {
+ continue;
+ }
+
++ /* remember state if we are entering a substack */
++ if (prev_level < stack_level) {
++ substates[stack_level].impression = impression;
++ substates[stack_level].status = status;
++ }
++
+ /* attempt to call the module */
+- if (h->func == NULL) {
++ if (h->handler_type == PAM_HT_MUST_FAIL) {
++ D(("module poorly listed in PAM config; forcing failure"));
++ retval = PAM_MUST_FAIL_CODE;
++ } else if (h->handler_type == PAM_HT_SUBSTACK) {
++ D(("skipping substack handler"));
++ continue;
++ } else if (h->func == NULL) {
+ D(("module function is not defined, indicating failure"));
+ retval = PAM_MODULE_UNKNOWN;
+ } else {
+@@ -83,10 +108,6 @@ static int _pam_dispatch_aux(pam_handle_
+ retval = h->func(pamh, flags, h->argc, h->argv);
+ pamh->mod_name=NULL;
+ D(("module returned: %s", pam_strerror(pamh, retval)));
+- if (h->must_fail) {
+- D(("module poorly listed in PAM config; forcing failure"));
+- retval = PAM_MUST_FAIL_CODE;
+- }
+ }
+
+ /*
+@@ -100,6 +121,7 @@ static int _pam_dispatch_aux(pam_handle_
+ pamh->former.impression = impression;
+ pamh->former.status = status;
+ pamh->former.depth = depth;
++ pamh->former.substates = substates;
+
+ D(("module %d returned PAM_INCOMPLETE", depth));
+ return retval;
+@@ -176,8 +198,8 @@ static int _pam_dispatch_aux(pam_handle_
+ switch (action) {
+ case _PAM_ACTION_RESET:
+
+- impression = _PAM_UNDEF;
+- status = PAM_MUST_FAIL_CODE;
++ impression = substates[stack_level].impression;
++ status = substates[stack_level].status;
+ break;
+
+ case _PAM_ACTION_OK:
+@@ -244,9 +266,13 @@ static int _pam_dispatch_aux(pam_handle_
+ }
+
+ /* this means that we need to skip #action stacked modules */
+- do {
+- h = h->next;
+- } while ( --action > 0 && h != NULL );
++ while (h->next != NULL && h->next->stack_level >= stack_level && action > 0) {
++ do {
++ h = h->next;
++ ++depth;
++ } while (h->next != NULL && h->next->stack_level > stack_level);
++ --action;
++ }
+
+ /* note if we try to skip too many modules action is
+ still non-zero and we snag the next if. */
+@@ -254,14 +280,19 @@ static int _pam_dispatch_aux(pam_handle_
+
+ /* this case is a syntax error: we can't succeed */
+ if (action) {
+- D(("action syntax error"));
++ pam_syslog(pamh, LOG_ERR, "bad jump in stack");
+ impression = _PAM_NEGATIVE;
+ status = PAM_MUST_FAIL_CODE;
+ }
+ }
+- }
+-
++ continue;
++
+ decision_made: /* by getting here we have made a decision */
++ while (h->next != NULL && h->next->stack_level >= stack_level) {
++ h = h->next;
++ ++depth;
++ }
++ }
+
+ /* Sanity check */
+ if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) {
+@@ -269,6 +300,7 @@ decision_made: /* by getting here w
+ status = PAM_MUST_FAIL_CODE;
+ }
+
++ free(substates);
+ /* We have made a decision about the modules executed */
+ return status;
+ }
+Index: libpam/pam_end.c
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/libpam/pam_end.c,v
+retrieving revision 1.4
+diff -u -p -r1.4 pam_end.c
+--- libpam/pam_end.c 12 Jan 2006 10:06:49 -0000 1.4
++++ libpam/pam_end.c 12 Oct 2007 16:23:37 -0000
+@@ -71,6 +71,8 @@ int pam_end(pam_handle_t *pamh, int pam_
+ _pam_drop(pamh->pam_conversation);
+ pamh->fail_delay.delay_fn_ptr = NULL;
+
++ _pam_drop(pamh->former.substates);
++
+ /* and finally liberate the memory for the pam_handle structure */
+
+ _pam_drop(pamh);
+Index: libpam/pam_handlers.c
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/libpam/pam_handlers.c,v
+retrieving revision 1.24
+diff -u -p -r1.24 pam_handlers.c
+--- libpam/pam_handlers.c 14 Jun 2006 11:41:47 -0000 1.24
++++ libpam/pam_handlers.c 12 Oct 2007 16:23:37 -0000
+@@ -18,7 +18,7 @@
+
+ #define BUF_SIZE 1024
+ #define MODULE_CHUNK 4
+-#define UNKNOWN_MODULE_PATH "<*unknown module path*>"
++#define UNKNOWN_MODULE "<*unknown module*>"
+ #ifndef _PAM_ISA
+ #define _PAM_ISA "."
+ #endif
+@@ -28,7 +28,7 @@ static int _pam_assemble_line(FILE *f, c
+ static void _pam_free_handlers_aux(struct handler **hp);
+
+ static int _pam_add_handler(pam_handle_t *pamh
+- , int must_fail, int other, int type
++ , int must_fail, int other, int stack_level, int type
+ , int *actions, const char *mod_path
+ , int argc, char **argv, int argvlen);
+
+@@ -43,6 +43,7 @@ static int _pam_add_handler(pam_handle_t
+ static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name
+ , const char *service /* specific file */
+ , int module_type /* specific type */
++ , int stack_level /* level of substack */
+ #ifdef PAM_READ_BOTH_CONFS
+ , int not_other
+ #endif /* PAM_READ_BOTH_CONFS */
+@@ -51,6 +52,7 @@ static int _pam_load_conf_file(pam_handl
+ static int _pam_parse_conf_file(pam_handle_t *pamh, FILE *f
+ , const char *known_service /* specific file */
+ , int requested_module_type /* specific type */
++ , int stack_level /* level of substack */
+ #ifdef PAM_READ_BOTH_CONFS
+ , int not_other
+ #endif /* PAM_READ_BOTH_CONFS */
+@@ -68,7 +70,7 @@ static int _pam_parse_conf_file(pam_hand
+ int module_type, actions[_PAM_RETURN_VALUES];
+ int other; /* set if module is for PAM_DEFAULT_SERVICE */
+ int res; /* module added successfully? */
+- int must_fail=0; /* a badly formatted line must fail when used */
++ int handler_type = PAM_HT_MODULE; /* regular handler from a module */
+ int argc;
+ char **argv;
+ int argvlen;
+@@ -92,6 +94,7 @@ static int _pam_parse_conf_file(pam_hand
+ /* accept "service name" or PAM_DEFAULT_SERVICE modules */
+ if (!strcasecmp(this_service, pamh->service_name) || other) {
+ int pam_include = 0;
++ int substack = 0;
+
+ /* This is a service we are looking for */
+ D(("_pam_init_handlers: Found PAM config entry for: %s"
+@@ -105,7 +108,7 @@ static int _pam_parse_conf_file(pam_hand
+ "(%s) empty module type", this_service);
+ module_type = (requested_module_type != PAM_T_ANY) ?
+ requested_module_type : PAM_T_AUTH; /* most sensitive */
+- must_fail = 1; /* install as normal but fail when dispatched */
++ handler_type = PAM_HT_MUST_FAIL; /* install as normal but fail when dispatched */
+ } else if (!strcasecmp("auth", tok)) {
+ module_type = PAM_T_AUTH;
+ } else if (!strcasecmp("session", tok)) {
+@@ -121,9 +124,9 @@ static int _pam_parse_conf_file(pam_hand
+ this_service, tok);
+ module_type = (requested_module_type != PAM_T_ANY) ?
+ requested_module_type : PAM_T_AUTH; /* most sensitive */
+- must_fail = 1; /* install as normal but fail when dispatched */
++ handler_type = PAM_HT_MUST_FAIL; /* install as normal but fail when dispatched */
+ }
+- D(("Using %s config entry: %s", must_fail?"BAD ":"", tok));
++ D(("Using %s config entry: %s", handler_type?"BAD ":"", tok));
+ if (requested_module_type != PAM_T_ANY &&
+ module_type != requested_module_type) {
+ D(("Skipping config entry: %s (requested=%d, found=%d)",
+@@ -145,7 +148,7 @@ static int _pam_parse_conf_file(pam_hand
+ pam_syslog(pamh, LOG_ERR,
+ "(%s) no control flag supplied", this_service);
+ _pam_set_default_control(actions, _PAM_ACTION_BAD);
+- must_fail = 1;
++ handler_type = PAM_HT_MUST_FAIL;
+ } else if (!strcasecmp("required", tok)) {
+ D(("*PAM_F_REQUIRED*"));
+ actions[PAM_SUCCESS] = _PAM_ACTION_OK;
+@@ -171,6 +174,11 @@ static int _pam_parse_conf_file(pam_hand
+ } else if (!strcasecmp("include", tok)) {
+ D(("*PAM_F_INCLUDE*"));
+ pam_include = 1;
++ substack = 0;
++ } else if (!strcasecmp("substack", tok)) {
++ D(("*PAM_F_SUBSTACK*"));
++ pam_include = 1;
++ substack = 1;
+ } else {
+ D(("will need to parse %s", tok));
+ _pam_parse_control(actions, tok);
+@@ -180,7 +188,18 @@ static int _pam_parse_conf_file(pam_hand
+
+ tok = _pam_StrTok(NULL, " \n\t", &nexttok);
+ if (pam_include) {
+- if (_pam_load_conf_file(pamh, tok, this_service, module_type
++ if (substack) {
++ res = _pam_add_handler(pamh, PAM_HT_SUBSTACK, other,
++ stack_level, module_type, actions, tok,
++ 0, NULL, 0);
++ if (res != PAM_SUCCESS) {
++ pam_syslog(pamh, LOG_ERR, "error adding substack %s", tok);
++ D(("failed to load module - aborting"));
++ return PAM_ABORT;
++ }
++ }
++ if (_pam_load_conf_file(pamh, tok, this_service, module_type,
++ stack_level + substack
+ #ifdef PAM_READ_BOTH_CONFS
+ , !other
+ #endif /* PAM_READ_BOTH_CONFS */
+@@ -188,7 +207,7 @@ static int _pam_parse_conf_file(pam_hand
+ continue;
+ _pam_set_default_control(actions, _PAM_ACTION_BAD);
+ mod_path = NULL;
+- must_fail = 1;
++ handler_type = PAM_HT_MUST_FAIL;
+ nexttok = NULL;
+ } else if (tok != NULL) {
+ mod_path = tok;
+@@ -199,7 +218,7 @@ static int _pam_parse_conf_file(pam_hand
+ pam_syslog(pamh, LOG_ERR,
+ "(%s) no module name supplied", this_service);
+ mod_path = NULL;
+- must_fail = 1;
++ handler_type = PAM_HT_MUST_FAIL;
+ }
+
+ /* nexttok points to remaining arguments... */
+@@ -219,7 +238,7 @@ static int _pam_parse_conf_file(pam_hand
+ int y;
+
+ D(("CONF%s: %s%s %d %s %d"
+- , must_fail?"<*will fail*>":""
++ , handler_type==PAM_HT_MUST_FAIL?"<*will fail*>":""
+ , this_service, other ? "(backup)":""
+ , module_type
+ , mod_path, argc));
+@@ -235,7 +254,7 @@ static int _pam_parse_conf_file(pam_hand
+ }
+ #endif
+
+- res = _pam_add_handler(pamh, must_fail, other
++ res = _pam_add_handler(pamh, handler_type, other, stack_level
+ , module_type, actions, mod_path
+ , argc, argv, argvlen);
+ if (res != PAM_SUCCESS) {
+@@ -252,6 +271,7 @@ static int _pam_parse_conf_file(pam_hand
+ static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name
+ , const char *service /* specific file */
+ , int module_type /* specific type */
++ , int stack_level /* level of substack */
+ #ifdef PAM_READ_BOTH_CONFS
+ , int not_other
+ #endif /* PAM_READ_BOTH_CONFS */
+@@ -263,6 +283,12 @@ static int _pam_load_conf_file(pam_handl
+
+ D(("_pam_load_conf_file called"));
+
++ if (stack_level >= PAM_SUBSTACK_MAX_LEVEL) {
++ D(("maximum level of substacks reached"));
++ pam_syslog(pamh, LOG_ERR, "maximum level of substacks reached");
++ return PAM_ABORT;
++ }
++
+ if (config_name == NULL) {
+ D(("no config file supplied"));
+ pam_syslog(pamh, LOG_ERR, "(%s) no config file supplied", service);
+@@ -280,7 +306,7 @@ static int _pam_load_conf_file(pam_handl
+ D(("opening %s", config_name));
+ f = fopen(config_name, "r");
+ if (f != NULL) {
+- retval = _pam_parse_conf_file(pamh, f, service, module_type
++ retval = _pam_parse_conf_file(pamh, f, service, module_type, stack_level
+ #ifdef PAM_READ_BOTH_CONFS
+ , not_other
+ #endif /* PAM_READ_BOTH_CONFS */
+@@ -379,7 +405,8 @@ int _pam_init_handlers(pam_handle_t *pam
+ f = fopen(filename, "r");
+ if (f != NULL) {
+ /* would test magic here? */
+- retval = _pam_parse_conf_file(pamh, f, pamh->service_name, PAM_T_ANY
++ retval = _pam_parse_conf_file(pamh, f, pamh->service_name,
++ PAM_T_ANY, 0
+ #ifdef PAM_READ_BOTH_CONFS
+ , 0
+ #endif /* PAM_READ_BOTH_CONFS */
+@@ -400,7 +427,7 @@ int _pam_init_handlers(pam_handle_t *pam
+ D(("checking %s", PAM_CONFIG));
+
+ if ((f = fopen(PAM_CONFIG,"r")) != NULL) {
+- retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 1);
++ retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 0, 1);
+ fclose(f);
+ } else
+ #endif /* PAM_READ_BOTH_CONFS */
+@@ -419,9 +446,8 @@ int _pam_init_handlers(pam_handle_t *pam
+ f = fopen(PAM_DEFAULT_SERVICE_FILE, "r");
+ if (f != NULL) {
+ /* would test magic here? */
+- retval = _pam_parse_conf_file(pamh, f
+- , PAM_DEFAULT_SERVICE
+- , PAM_T_ANY
++ retval = _pam_parse_conf_file(pamh, f, PAM_DEFAULT_SERVICE,
++ PAM_T_ANY, 0
+ #ifdef PAM_READ_BOTH_CONFS
+ , 0
+ #endif /* PAM_READ_BOTH_CONFS */
+@@ -454,7 +480,7 @@ int _pam_init_handlers(pam_handle_t *pam
+ return PAM_ABORT;
+ }
+
+- retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY
++ retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 0
+ #ifdef PAM_READ_BOTH_CONFS
+ , 0
+ #endif /* PAM_READ_BOTH_CONFS */
+@@ -581,46 +607,19 @@ extract_modulename(const char *mod_path)
+ return retval;
+ }
+
+-int _pam_add_handler(pam_handle_t *pamh
+- , int must_fail, int other, int type
+- , int *actions, const char *mod_path
+- , int argc, char **argv, int argvlen)
++static struct loaded_module *
++_pam_load_module(pam_handle_t *pamh, const char *mod_path)
+ {
+- struct loaded_module *mod;
+ int x = 0;
+- struct handler **handler_p;
+- struct handler **handler_p2;
+- struct handlers *the_handlers;
+- const char *sym, *sym2;
+- char *mod_full_path=NULL;
++ int success;
+ #ifndef PAM_STATIC
+ char *mod_full_isa_path=NULL, *isa=NULL;
+ #endif
+- servicefn func, func2;
+- int success;
+-
+- D(("called."));
+- IF_NO_PAMH("_pam_add_handler",pamh,PAM_SYSTEM_ERR);
+-
+- /* if NULL set to something that can be searched for */
+- switch (mod_path != NULL) {
+- default:
+- if (mod_path[0] == '/') {
+- break;
+- }
+- if (asprintf(&mod_full_path, "%s%s",
+- DEFAULT_MODULE_PATH, mod_path) >= 0) {
+- mod_path = mod_full_path;
+- break;
+- }
+- mod_full_path = NULL;
+- pam_syslog(pamh, LOG_CRIT, "cannot malloc full mod path");
+- case 0:
+- mod_path = UNKNOWN_MODULE_PATH;
+- }
++ struct loaded_module *mod;
+
+- D(("_pam_add_handler: adding type %d, module `%s'",type,mod_path));
+- mod = pamh->handlers.module;
++ D(("_pam_load_module: loading module `%s'", mod_path));
++
++ mod = pamh->handlers.module;
+
+ /* First, ensure the module is loaded */
+ while (x < pamh->handlers.modules_used) {
+@@ -639,9 +638,8 @@ int _pam_add_handler(pam_handle_t *pamh
+ if (tmp == NULL) {
+ D(("cannot enlarge module pointer memory"));
+ pam_syslog(pamh, LOG_ERR,
+- "realloc returned NULL in _pam_add_handler");
+- _pam_drop(mod_full_path);
+- return PAM_ABORT;
++ "realloc returned NULL in _pam_load_module");
++ return NULL;
+ }
+ pamh->handlers.module = tmp;
+ pamh->handlers.modules_allocated += MODULE_CHUNK;
+@@ -654,10 +652,10 @@ int _pam_add_handler(pam_handle_t *pamh
+ /* Only load static function if function was not found dynamically.
+ * This code should work even if no dynamic loading is available. */
+ if (success != PAM_SUCCESS) {
+- D(("_pam_add_handler: open static handler %s", mod_path));
++ D(("_pam_load_module: open static handler %s", mod_path));
+ mod->dl_handle = _pam_open_static_handler(pamh, mod_path);
+ if (mod->dl_handle == NULL) {
+- D(("_pam_add_handler: unable to find static handler %s",
++ D(("_pam_load_module: unable to find static handler %s",
+ mod_path));
+ pam_syslog(pamh, LOG_ERR,
+ "unable to open static handler %s", mod_path);
+@@ -670,15 +668,15 @@ int _pam_add_handler(pam_handle_t *pamh
+ }
+ }
+ #else
+- D(("_pam_add_handler: _pam_dlopen(%s)", mod_path));
++ D(("_pam_load_module: _pam_dlopen(%s)", mod_path));
+ mod->dl_handle = _pam_dlopen(mod_path);
+- D(("_pam_add_handler: _pam_dlopen'ed"));
+- D(("_pam_add_handler: dlopen'ed"));
++ D(("_pam_load_module: _pam_dlopen'ed"));
++ D(("_pam_load_module: dlopen'ed"));
+ if (mod->dl_handle == NULL) {
+ if (strstr(mod_path, "$ISA")) {
+ mod_full_isa_path = malloc(strlen(mod_path) + strlen(_PAM_ISA) + 1);
+ if (mod_full_isa_path == NULL) {
+- D(("_pam_handler: couldn't get memory for mod_path"));
++ D(("_pam_load_module: couldn't get memory for mod_path"));
+ pam_syslog(pamh, LOG_ERR, "no memory for module path");
+ success = PAM_ABORT;
+ } else {
+@@ -694,9 +692,9 @@ int _pam_add_handler(pam_handle_t *pamh
+ }
+ }
+ if (mod->dl_handle == NULL) {
+- D(("_pam_add_handler: _pam_dlopen(%s) failed", mod_path));
+- pam_syslog(pamh, LOG_ERR, "unable to dlopen(%s)", mod_path);
+- pam_syslog(pamh, LOG_ERR, "[error: %s]", _pam_dlerror());
++ D(("_pam_load_module: _pam_dlopen(%s) failed", mod_path));
++ pam_syslog(pamh, LOG_ERR, "unable to dlopen(%s): %s", mod_path,
++ _pam_dlerror());
+ /* Don't abort yet; static code may be able to find function.
+ * But defaults to abort if nothing found below... */
+ } else {
+@@ -717,7 +715,7 @@ int _pam_add_handler(pam_handle_t *pamh
+
+ /* indicate its name - later we will search for it by this */
+ if ((mod->name = _pam_strdup(mod_path)) == NULL) {
+- D(("_pam_handler: couldn't get memory for mod_path"));
++ D(("_pam_load_module: couldn't get memory for mod_path"));
+ pam_syslog(pamh, LOG_ERR, "no memory for module path");
+ success = PAM_ABORT;
+ }
+@@ -726,18 +724,54 @@ int _pam_add_handler(pam_handle_t *pamh
+ mod += x; /* the located module */
+ success = PAM_SUCCESS;
+ }
++ return success == PAM_SUCCESS ? mod : NULL;
++}
++
++int _pam_add_handler(pam_handle_t *pamh
++ , int handler_type, int other, int stack_level, int type
++ , int *actions, const char *mod_path
++ , int argc, char **argv, int argvlen)
++{
++ struct loaded_module *mod = NULL;
++ struct handler **handler_p;
++ struct handler **handler_p2;
++ struct handlers *the_handlers;
++ const char *sym, *sym2;
++ char *mod_full_path;
++ servicefn func, func2;
++ int mod_type = PAM_MT_FAULTY_MOD;
++
++ D(("called."));
++ IF_NO_PAMH("_pam_add_handler",pamh,PAM_SYSTEM_ERR);
+
+- _pam_drop(mod_full_path);
+- mod_path = NULL; /* no longer needed or trusted */
++ D(("_pam_add_handler: adding type %d, handler_type %d, module `%s'",
++ type, handler_type, mod_path));
+
+- /* Now return error if necessary after trying all possible ways... */
+- if (success != PAM_SUCCESS)
+- return(success);
++ if (handler_type == PAM_HT_MODULE && mod_path != NULL) {
++ if (mod_path[0] == '/') {
++ mod = _pam_load_module(pamh, mod_path);
++ } else if (asprintf(&mod_full_path, "%s%s",
++ DEFAULT_MODULE_PATH, mod_path) >= 0) {
++ mod = _pam_load_module(pamh, mod_full_path);
++ _pam_drop(mod_full_path);
++ } else {
++ pam_syslog(pamh, LOG_CRIT, "cannot malloc full mod path");
++ return PAM_ABORT;
++ }
++
++ if (mod == NULL) {
++ /* if we get here with NULL it means allocation error */
++ return PAM_ABORT;
++ }
++
++ mod_type = mod->type;
++ }
++
++ if (mod_path == NULL)
++ mod_path = UNKNOWN_MODULE;
+
+ /*
+- * At this point 'mod' points to the stored/loaded module. If its
+- * dl_handle is unknown, then we must be able to indicate dispatch
+- * failure with 'must_fail'
++ * At this point 'mod' points to the stored/loaded module.
+ */
+
+ /* Now define the handler(s) based on mod->dlhandle and type */
+@@ -780,43 +814,43 @@ int _pam_add_handler(pam_handle_t *pamh
+ /* are the modules reliable? */
+ if (
+ #ifdef PAM_STATIC
+- mod->type != PAM_MT_STATIC_MOD
++ mod_type != PAM_MT_STATIC_MOD
+ &&
+ #else
+- mod->type != PAM_MT_DYNAMIC_MOD
++ mod_type != PAM_MT_DYNAMIC_MOD
+ &&
+ #endif
+- mod->type != PAM_MT_FAULTY_MOD
++ mod_type != PAM_MT_FAULTY_MOD
+ ) {
+- D(("_pam_add_handlers: illegal module library type; %d", mod->type));
++ D(("_pam_add_handlers: illegal module library type; %d", mod_type));
+ pam_syslog(pamh, LOG_ERR,
+ "internal error: module library type not known: %s;%d",
+- sym, mod->type);
++ sym, mod_type);
+ return PAM_ABORT;
+ }
+
+ /* now identify this module's functions - for non-faulty modules */
+
+ #ifdef PAM_STATIC
+- if ((mod->type == PAM_MT_STATIC_MOD) &&
++ if ((mod_type == PAM_MT_STATIC_MOD) &&
+ (func = (servicefn)_pam_get_static_sym(mod->dl_handle, sym)) == NULL) {
+ pam_syslog(pamh, LOG_ERR, "unable to resolve static symbol: %s", sym);
+ }
+ #else
+- if ((mod->type == PAM_MT_DYNAMIC_MOD) &&
++ if ((mod_type == PAM_MT_DYNAMIC_MOD) &&
+ !(func = _pam_dlsym(mod->dl_handle, sym)) ) {
+ pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym);
+ }
+ #endif
+ if (sym2) {
+ #ifdef PAM_STATIC
+- if ((mod->type == PAM_MT_STATIC_MOD) &&
++ if ((mod_type == PAM_MT_STATIC_MOD) &&
+ (func2 = (servicefn)_pam_get_static_sym(mod->dl_handle, sym2))
+ == NULL) {
+ pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym2);
+ }
+ #else
+- if ((mod->type == PAM_MT_DYNAMIC_MOD) &&
++ if ((mod_type == PAM_MT_DYNAMIC_MOD) &&
+ !(func2 = _pam_dlsym(mod->dl_handle, sym2)) ) {
+ pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym2);
+ }
+@@ -835,14 +869,15 @@ int _pam_add_handler(pam_handle_t *pamh
+ return (PAM_ABORT);
+ }
+
+- (*handler_p)->must_fail = must_fail; /* failure forced? */
++ (*handler_p)->handler_type = handler_type;
++ (*handler_p)->stack_level = stack_level;
+ (*handler_p)->func = func;
+ memcpy((*handler_p)->actions,actions,sizeof((*handler_p)->actions));
+ (*handler_p)->cached_retval = _PAM_INVALID_RETVAL;
+ (*handler_p)->cached_retval_p = &((*handler_p)->cached_retval);
+ (*handler_p)->argc = argc;
+ (*handler_p)->argv = argv; /* not a copy */
+- (*handler_p)->mod_name = extract_modulename(mod->name);
++ (*handler_p)->mod_name = extract_modulename(mod_path);
+ (*handler_p)->next = NULL;
+
+ /* some of the modules have a second calling function */
+@@ -857,7 +892,8 @@ int _pam_add_handler(pam_handle_t *pamh
+ return (PAM_ABORT);
+ }
+
+- (*handler_p2)->must_fail = must_fail; /* failure forced? */
++ (*handler_p2)->handler_type = handler_type;
++ (*handler_p2)->stack_level = stack_level;
+ (*handler_p2)->func = func2;
+ memcpy((*handler_p2)->actions,actions,sizeof((*handler_p2)->actions));
+ (*handler_p2)->cached_retval = _PAM_INVALID_RETVAL; /* ignored */
+@@ -873,7 +909,7 @@ int _pam_add_handler(pam_handle_t *pamh
+ } else {
+ (*handler_p2)->argv = NULL; /* no arguments */
+ }
+- (*handler_p2)->mod_name = extract_modulename(mod->name);
++ (*handler_p2)->mod_name = extract_modulename(mod_path);
+ (*handler_p2)->next = NULL;
+ }
+
+Index: libpam/pam_private.h
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/libpam/pam_private.h,v
+retrieving revision 1.19
+diff -u -p -r1.19 pam_private.h
+--- libpam/pam_private.h 24 Jul 2006 15:47:40 -0000 1.19
++++ libpam/pam_private.h 12 Oct 2007 16:23:37 -0000
+@@ -44,7 +44,7 @@
+ #define _PAM_INVALID_RETVAL -1 /* default value for cached_retval */
+
+ struct handler {
+- int must_fail;
++ int handler_type;
+ int (*func)(pam_handle_t *pamh, int flags, int argc, char **argv);
+ int actions[_PAM_RETURN_VALUES];
+ /* set by authenticate, open_session, chauthtok(1st)
+@@ -54,8 +54,13 @@ struct handler {
+ char **argv;
+ struct handler *next;
+ char *mod_name;
++ int stack_level;
+ };
+
++#define PAM_HT_MODULE 0
++#define PAM_HT_MUST_FAIL 1
++#define PAM_HT_SUBSTACK 2
++
+ struct loaded_module {
+ char *name;
+ int type; /* PAM_STATIC_MOD or PAM_DYNAMIC_MOD */
+@@ -76,7 +81,7 @@ struct handlers {
+ };
+
+ struct service {
+- struct loaded_module *module; /* Only used for dynamic loading */
++ struct loaded_module *module; /* Array of modules */
+ int modules_allocated;
+ int modules_used;
+ int handlers_loaded;
+@@ -111,6 +116,12 @@ struct _pam_fail_delay {
+ const void *delay_fn_ptr;
+ };
+
++/* initial state in substack */
++struct _pam_substack_state {
++ int impression;
++ int status;
++};
++
+ struct _pam_former_state {
+ /* this is known and set by _pam_dispatch() */
+ int choice; /* which flavor of module function did we call? */
+@@ -119,6 +130,7 @@ struct _pam_former_state {
+ int depth; /* how deep in the stack were we? */
+ int impression; /* the impression at that time */
+ int status; /* the status before returning incomplete */
++ struct _pam_substack_state *substates; /* array of initial substack states */
+
+ /* state info used by pam_get_user() function */
+ int fail_user;
+@@ -175,6 +187,8 @@ struct pam_handle {
+ #define _PAM_ACTION_UNDEF -6 /* this is treated as an error
+ ( = _PAM_ACTION_BAD) */
+
++#define PAM_SUBSTACK_MAX_LEVEL 16 /* maximum level of substacks */
++
+ /* character tables for parsing config files */
+ extern const char * const _pam_token_actions[-_PAM_ACTION_UNDEF];
+ extern const char * const _pam_token_returns[_PAM_RETURN_VALUES+1];
+Index: libpam/pam_start.c
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/libpam/pam_start.c,v
+retrieving revision 1.9
+diff -u -p -r1.9 pam_start.c
+--- libpam/pam_start.c 24 Jul 2006 15:47:40 -0000 1.9
++++ libpam/pam_start.c 12 Oct 2007 16:23:37 -0000
+@@ -88,6 +88,7 @@ int pam_start (
+ (*pamh)->oldauthtok = NULL;
+ (*pamh)->fail_delay.delay_fn_ptr = NULL;
+ (*pamh)->former.choice = PAM_NOT_STACKED;
++ (*pamh)->former.substates = NULL;
+ #ifdef HAVE_LIBAUDIT
+ (*pamh)->audit_state = 0;
+ #endif
+Index: doc/man/pam.conf-syntax.xml
+===================================================================
+RCS file: /cvsroot/pam/Linux-PAM/doc/man/pam.conf-syntax.xml,v
+retrieving revision 1.4
+retrieving revision 1.5
+diff -u -r1.4 -r1.5
+--- doc/man/pam.conf-syntax.xml 26 Aug 2007 22:44:51 -0000 1.4
++++ doc/man/pam.conf-syntax.xml 19 Oct 2007 17:06:30 -0000 1.5
+@@ -180,6 +180,24 @@
+
+
+
++
++ substack
++
++
++ include all lines of given type from the configuration
++ file specified as an argument to this control. This differs from
++ include in that evaluation of the
++ done and die actions
++ in a substack does not cause skipping the rest of the complete
++ module stack, but only of the substack. Jumps in a substack
++ also can not make evaluation jump out of it, and the whole substack
++ is counted as one module when the jump is done in a parent stack.
++ The reset action will reset the state of a
++ module stack to the state it was in as of beginning of the substack
++ evaluation.
++
++
++
+
+
+
diff --git a/pam-0.99.8.1-tty-audit.patch b/pam-0.99.8.1-tty-audit.patch
new file mode 100644
index 0000000..ead4837
--- /dev/null
+++ b/pam-0.99.8.1-tty-audit.patch
@@ -0,0 +1,590 @@
+Written-by: Miloslav Trmac
+Reviewed-by: Tomas Mraz
+diff -urN Linux-PAM/configure.in Linux-PAM-0.99.8.1/configure.in
+--- Linux-PAM/configure.in 2007-11-28 13:41:14.000000000 +0100
++++ Linux-PAM-0.99.8.1/configure.in 2007-11-28 14:35:30.000000000 +0100
+@@ -331,7 +331,11 @@
+ WITH_LIBAUDIT=$enableval, WITH_LIBAUDIT=yes)
+ if test x"$WITH_LIBAUDIT" != xno ; then
+ AC_CHECK_HEADER([libaudit.h],
+- [AC_CHECK_LIB(audit, audit_log_acct_message, LIBAUDIT=-laudit, LIBAUDIT="")]
++ [AC_CHECK_LIB(audit, audit_log_acct_message, LIBAUDIT=-laudit, LIBAUDIT="")
++ AC_CHECK_TYPE([struct audit_tty_status],
++ [HAVE_AUDIT_TTY_STATUS=yes],
++ [HAVE_AUDIT_TTY_STATUS=""],
++ [#include ])]
+ )
+ if test ! -z "$LIBAUDIT" -a "ac_cv_header_libaudit_h" != "no" ; then
+ AC_DEFINE([HAVE_LIBAUDIT], 1, [Defined if audit support should be compiled in])
+@@ -340,6 +344,8 @@
+ LIBAUDIT=""
+ fi
+ AC_SUBST(LIBAUDIT)
++AM_CONDITIONAL([HAVE_AUDIT_TTY_STATUS],
++ [test "x$HAVE_AUDIT_TTY_STATUS" = xyes])
+
+ BACKUP_LIBS=$LIBS
+ AC_SEARCH_LIBS([crypt],[xcrypt crypt], LIBCRYPT="-l$ac_lib", LIBCRYPT="")
+@@ -517,7 +523,8 @@
+ modules/pam_securetty/Makefile modules/pam_selinux/Makefile \
+ modules/pam_shells/Makefile modules/pam_stress/Makefile \
+ modules/pam_succeed_if/Makefile modules/pam_tally/Makefile \
+- modules/pam_time/Makefile modules/pam_umask/Makefile \
++ modules/pam_time/Makefile modules/pam_tty_audit/Makefile \
++ modules/pam_umask/Makefile \
+ modules/pam_unix/Makefile modules/pam_userdb/Makefile \
+ modules/pam_warn/Makefile modules/pam_wheel/Makefile \
+ modules/pam_xauth/Makefile doc/Makefile doc/specs/Makefile \
+diff -urN Linux-PAM/modules/Makefile.am Linux-PAM-0.99.8.1/modules/Makefile.am
+--- Linux-PAM/modules/Makefile.am 2007-11-28 13:41:13.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/Makefile.am 2007-11-28 14:02:48.000000000 +0100
+@@ -9,8 +9,8 @@
+ pam_lastlog pam_limits pam_listfile pam_localuser pam_mail \
+ pam_mkhomedir pam_motd pam_nologin pam_permit pam_rhosts pam_rootok \
+ pam_securetty pam_selinux pam_shells pam_stress pam_succeed_if \
+- pam_tally pam_time pam_umask pam_unix pam_userdb pam_warn \
+- pam_wheel pam_xauth pam_exec pam_namespace pam_loginuid \
++ pam_tally pam_time pam_tty_audit pam_umask pam_unix pam_userdb \
++ pam_warn pam_wheel pam_xauth pam_exec pam_namespace pam_loginuid \
+ pam_faildelay
+
+ CLEANFILES = *~
+diff -urN Linux-PAM/modules/pam_tty_audit/Makefile.am Linux-PAM-0.99.8.1/modules/pam_tty_audit/Makefile.am
+--- Linux-PAM/modules/pam_tty_audit/Makefile.am 1970-01-01 01:00:00.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_tty_audit/Makefile.am 2007-11-28 16:05:00.000000000 +0100
+@@ -0,0 +1,30 @@
++#
++# Copyright (c) 2005, 2006 Thorsten Kukuk
++#
++
++CLEANFILES = *~
++
++EXTRA_DIST = README ${MANS} $(XMLS)
++
++man_MANS = pam_tty_audit.8
++XMLS = README.xml pam_tty_audit.8.xml
++
++securelibdir = $(SECUREDIR)
++
++AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include
++AM_LDFLAGS = -no-undefined -avoid-version -module \
++ -L$(top_builddir)/libpam -lpam
++if HAVE_VERSIONING
++ AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
++endif
++
++if HAVE_AUDIT_TTY_STATUS
++ securelib_LTLIBRARIES = pam_tty_audit.la
++endif
++
++if ENABLE_REGENERATE_MAN
++noinst_DATA = README
++README: pam_tty_audit.8.xml
++-include $(top_srcdir)/Make.xml.rules
++endif
++
+diff -urN Linux-PAM/modules/pam_tty_audit/pam_tty_audit.c Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.c
+--- Linux-PAM/modules/pam_tty_audit/pam_tty_audit.c 1970-01-01 01:00:00.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.c 2007-11-28 16:10:43.000000000 +0100
+@@ -0,0 +1,332 @@
++/* Copyright © 2007 Red Hat, Inc. All rights reserved.
++ Red Hat author: Miloslav Trmač
++
++ Redistribution and use in source and binary forms of Linux-PAM, with
++ or without modification, are permitted provided that the following
++ conditions are met:
++
++ 1. Redistributions of source code must retain any existing copyright
++ notice, and this entire permission notice in its entirety,
++ including the disclaimer of warranties.
++
++ 2. Redistributions in binary form must reproduce all prior and current
++ copyright notices, this list of conditions, and the following
++ disclaimer in the documentation and/or other materials provided
++ with the distribution.
++
++ 3. The name of any author may not be used to endorse or promote
++ products derived from this software without their specific prior
++ written permission.
++
++ ALTERNATIVELY, this product may be distributed under the terms of the
++ GNU General Public License, in which case the provisions of the GNU
++ GPL are required INSTEAD OF the above restrictions. (This clause is
++ necessary due to a potential conflict between the GNU 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(S) 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. */
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include
++#include
++#include
++#include
++#include
++
++#define DATANAME "pam_tty_audit_last_state"
++
++/* Open an audit netlink socket */
++static int
++nl_open (void)
++{
++ return socket (AF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
++}
++
++static int
++nl_send (int fd, unsigned type, unsigned flags, const void *data, size_t size)
++{
++ struct sockaddr_nl addr;
++ struct msghdr msg;
++ struct nlmsghdr nlm;
++ struct iovec iov[2];
++ ssize_t res;
++
++ nlm.nlmsg_len = NLMSG_LENGTH (size);
++ nlm.nlmsg_type = type;
++ nlm.nlmsg_flags = NLM_F_REQUEST | flags;
++ nlm.nlmsg_seq = 0;
++ nlm.nlmsg_pid = 0;
++ iov[0].iov_base = &nlm;
++ iov[0].iov_len = sizeof (nlm);
++ iov[1].iov_base = (void *)data;
++ iov[1].iov_len = size;
++ addr.nl_family = AF_NETLINK;
++ addr.nl_pid = 0;
++ addr.nl_groups = 0;
++ msg.msg_name = &addr;
++ msg.msg_namelen = sizeof (addr);
++ msg.msg_iov = iov;
++ msg.msg_iovlen = 2;
++ msg.msg_control = NULL;
++ msg.msg_controllen = 0;
++ msg.msg_flags = 0;
++ res = sendmsg (fd, &msg, 0);
++ if (res == -1)
++ return -1;
++ if ((size_t)res != nlm.nlmsg_len)
++ {
++ errno = EIO;
++ return -1;
++ }
++ return 0;
++}
++
++static int
++nl_recv (int fd, unsigned type, void *buf, size_t size)
++{
++ struct sockaddr_nl addr;
++ struct msghdr msg;
++ struct nlmsghdr nlm;
++ struct iovec iov[2];
++ ssize_t res;
++
++ again:
++ iov[0].iov_base = &nlm;
++ iov[0].iov_len = sizeof (nlm);
++ msg.msg_name = &addr;
++ msg.msg_namelen = sizeof (addr);
++ msg.msg_iov = iov;
++ msg.msg_iovlen = 1;
++ msg.msg_control = NULL;
++ msg.msg_controllen = 0;
++ if (type != NLMSG_ERROR)
++ {
++ res = recvmsg (fd, &msg, MSG_PEEK);
++ if (res == -1)
++ return -1;
++ if (res != NLMSG_LENGTH (0))
++ {
++ errno = EIO;
++ return -1;
++ }
++ if (nlm.nlmsg_type == NLMSG_ERROR)
++ {
++ struct nlmsgerr err;
++
++ iov[1].iov_base = &err;
++ iov[1].iov_len = sizeof (err);
++ msg.msg_iovlen = 2;
++ res = recvmsg (fd, &msg, 0);
++ if (res == -1)
++ return -1;
++ if ((size_t)res != NLMSG_LENGTH (sizeof (err))
++ || nlm.nlmsg_type != NLMSG_ERROR)
++ {
++ errno = EIO;
++ return -1;
++ }
++ if (err.error == 0)
++ goto again;
++ errno = -err.error;
++ return -1;
++ }
++ }
++ if (size != 0)
++ {
++ iov[1].iov_base = buf;
++ iov[1].iov_len = size;
++ msg.msg_iovlen = 2;
++ }
++ res = recvmsg (fd, &msg, 0);
++ if (res == -1)
++ return -1;
++ if ((size_t)res != NLMSG_LENGTH (size)
++ || nlm.nlmsg_type != type)
++ {
++ errno = EIO;
++ return -1;
++ }
++ return 0;
++}
++
++static int
++nl_recv_ack (int fd)
++{
++ struct nlmsgerr err;
++
++ if (nl_recv (fd, NLMSG_ERROR, &err, sizeof (err)) != 0)
++ return -1;
++ if (err.error != 0)
++ {
++ errno = -err.error;
++ return -1;
++ }
++ return 0;
++}
++
++static void
++cleanup_old_status (pam_handle_t *pamh, void *data, int error_status)
++{
++ (void)pamh;
++ (void)error_status;
++ free (data);
++}
++
++int
++pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, const char **argv)
++{
++ enum command { CMD_NONE, CMD_ENABLE, CMD_DISABLE };
++
++ enum command command;
++ struct audit_tty_status *old_status, new_status;
++ const char *user;
++ uid_t user_uid;
++ struct passwd *pwd;
++ int i, fd;
++
++ (void)flags;
++
++ if (pam_get_user (pamh, &user, NULL) != PAM_SUCCESS)
++ {
++ pam_syslog (pamh, LOG_ERR, "error determining target user's name");
++ return PAM_SESSION_ERR;
++ }
++ pwd = pam_modutil_getpwnam (pamh, user);
++ if (pwd == NULL)
++ {
++ pam_syslog (pamh, LOG_ERR, "error determining target user's UID: %m");
++ return PAM_SESSION_ERR;
++ }
++ user_uid = pwd->pw_uid;
++
++ command = CMD_NONE;
++ for (i = 0; i < argc; i++)
++ {
++ if (strncmp (argv[i], "enable=", 7) == 0
++ || strncmp (argv[i], "disable=", 8) == 0)
++ {
++ enum command this_command;
++ char *copy, *tok_data, *tok;
++
++ this_command = *argv[i] == 'e' ? CMD_ENABLE : CMD_DISABLE;
++ copy = strdup (strchr (argv[i], '=') + 1);
++ if (copy == NULL)
++ return PAM_SESSION_ERR;
++ for (tok = strtok_r (copy, ",", &tok_data); tok != NULL;
++ tok = strtok_r (NULL, ",", &tok_data))
++ {
++ pwd = pam_modutil_getpwnam (pamh, tok);
++ if (pwd == NULL)
++ {
++ pam_syslog (pamh, LOG_WARNING, "unknown user %s", tok);
++ continue;
++ }
++ if (pwd->pw_uid == user_uid)
++ {
++ command = this_command;
++ break;
++ }
++ }
++ free (copy);
++ }
++ }
++ if (command == CMD_NONE)
++ return PAM_SUCCESS;
++
++ old_status = malloc (sizeof (*old_status));
++ if (old_status == NULL)
++ return PAM_SESSION_ERR;
++
++ fd = nl_open ();
++ if (fd == -1
++ || nl_send (fd, AUDIT_TTY_GET, 0, NULL, 0) != 0
++ || nl_recv (fd, AUDIT_TTY_GET, old_status, sizeof (*old_status)) != 0)
++ {
++ pam_syslog (pamh, LOG_ERR, "error reading current audit status: %m");
++ if (fd != -1)
++ close (fd);
++ free (old_status);
++ return PAM_SESSION_ERR;
++ }
++
++ if (old_status->enabled == (command == CMD_ENABLE ? 1 : 0))
++ {
++ free (old_status);
++ goto ok_fd;
++ }
++
++ if (pam_set_data (pamh, DATANAME, old_status, cleanup_old_status)
++ != PAM_SUCCESS)
++ {
++ pam_syslog (pamh, LOG_ERR, "error saving old audit status");
++ close (fd);
++ free (old_status);
++ return PAM_SESSION_ERR;
++ }
++
++ new_status.enabled = (command == CMD_ENABLE ? 1 : 0);
++ if (nl_send (fd, AUDIT_TTY_SET, NLM_F_ACK, &new_status,
++ sizeof (new_status)) != 0
++ || nl_recv_ack (fd) != 0)
++ {
++ pam_syslog (pamh, LOG_ERR, "error setting current audit status: %m");
++ close (fd);
++ return PAM_SESSION_ERR;
++ }
++ /* Fall through */
++ ok_fd:
++ close (fd);
++ pam_syslog (pamh, LOG_DEBUG, "changed status from %d to %d",
++ old_status->enabled, new_status.enabled);
++ return PAM_SUCCESS;
++}
++
++int
++pam_sm_close_session (pam_handle_t *pamh, int flags, int argc,
++ const char **argv)
++{
++ const void *status_;
++
++ (void)flags;
++ (void)argc;
++ (void)argv;
++ if (pam_get_data (pamh, DATANAME, &status_) == PAM_SUCCESS)
++ {
++ const struct audit_tty_status *status;
++ int fd;
++
++ status = status_;
++
++ fd = nl_open ();
++ if (fd == -1
++ || nl_send (fd, AUDIT_TTY_SET, NLM_F_ACK, status,
++ sizeof (*status)) != 0
++ || nl_recv_ack (fd) != 0)
++ {
++ pam_syslog (pamh, LOG_ERR, "error restoring audit status: %m");
++ if (fd != -1)
++ close (fd);
++ return PAM_SESSION_ERR;
++ }
++ close (fd);
++ pam_syslog (pamh, LOG_ERR, "restored status to %d", status->enabled);
++ }
++ return PAM_SUCCESS;
++}
+diff -urN Linux-PAM/modules/pam_tty_audit/pam_tty_audit.8.xml Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.8.xml
+--- Linux-PAM/modules/pam_tty_audit/pam_tty_audit.8.xml 1970-01-01 01:00:00.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.8.xml 2007-11-28 15:50:22.000000000 +0100
+@@ -0,0 +1,125 @@
++
++
++
++
++
++
++ pam_tty_audit
++ 8
++ Linux-PAM Manual
++
++
++
++ pam_tty_audit
++ Enable or disable TTY auditing for specified users
++
++
++
++
++ pam_tty_audit.so
++
++ disable=usernames
++
++
++ enable=usernames
++
++
++
++
++
++ DESCRIPTION
++
++ The pam_tty_audit PAM module is used to enable or disable TTY auditing.
++ By default, the kernel does not audit input on any TTY.
++
++
++
++
++ OPTIONS
++
++
++
++
++
++
++
++ For each user matching one of comma-separated
++ , disable
++ TTY auditing. This overrides any older
++ option for the same user name.
++
++
++
++
++
++
++
++
++
++ For each user matching one of comma-separated
++ , enable
++ TTY auditing. This overrides any older
++ option for the same user name.
++
++
++
++
++
++
++
++ MODULE SERVICES PROVIDED
++
++ Only the session service is supported.
++
++
++
++
++ RETURN VALUES
++
++
++ PAM_SESSION_ERR
++
++
++ Error reading or modifying the TTY audit flag. See the system log
++ for more details.
++
++
++
++
++
++ PAM_SUCCESS
++
++
++ Success.
++
++
++
++
++
++
++
++
++ EXAMPLES
++
++ Audit all administrative actions.
++
++login root required pam_tty_audit.so enable=root
++su root required pam_tty_audit.so enable=root
++su-l root required pam_tty_audit.so enable=root
++sudo root required pam_tty_audit.so enable=root
++sudo-l root required pam_tty_audit.so enable=root
++sshd root required pam_tty_audit.so enable=root
++
++
++
++
++
++ AUTHOR
++
++ pam_tty_audit was written by Miloslav Trmač
++ <mitr@redhat.com>.
++
++
++
++
+diff -urN Linux-PAM/modules/pam_tty_audit/README.xml Linux-PAM-0.99.8.1/modules/pam_tty_audit/README.xml
+--- Linux-PAM/modules/pam_tty_audit/README.xml 1970-01-01 01:00:00.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_tty_audit/README.xml 2007-11-28 15:52:50.000000000 +0100
+@@ -0,0 +1,36 @@
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
diff --git a/pam-0.99.8.1-tty-audit2.patch b/pam-0.99.8.1-tty-audit2.patch
new file mode 100644
index 0000000..4978913
--- /dev/null
+++ b/pam-0.99.8.1-tty-audit2.patch
@@ -0,0 +1,233 @@
+Written-by: Miloslav Trmac
+Reviewed-by: Tomas Mraz
+diff -up Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.8.xml.tty-audit2 Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.8.xml
+--- Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.8.xml.tty-audit2 2008-01-02 11:28:26.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.8.xml 2008-01-02 11:29:55.000000000 +0100
+@@ -19,10 +19,10 @@
+
+ pam_tty_audit.so
+
+- disable=usernames
++ disable=patterns
+
+
+- enable=usernames
++ enable=patterns
+
+
+
+@@ -40,27 +40,40 @@
+
+
+
+-
++
+
+
+
+- For each user matching one of comma-separated
+- , disable
+- TTY auditing. This overrides any older
+- option for the same user name.
++ For each user matching one of comma-separated glob
++ , disable
++ TTY auditing. This overrides any previous
++ option matchin the same user name on the command line.
+
+
+
+
+
+-
++
+
+
+
+- For each user matching one of comma-separated
+- , enable
+- TTY auditing. This overrides any older
+- option for the same user name.
++ For each user matching one of comma-separated glob
++ , enable
++ TTY auditing. This overrides any previous
++ option matching the same user name on the command line.
++
++
++
++
++
++
++
++
++
++ Set the TTY audit flag when opening the session, but do not restore
++ it when closing the session. Using this option is necessary for
++ some services that don't fork() to run the
++ authenticated session, such as sudo.
+
+
+
+@@ -99,17 +112,24 @@
+
+
+
++
++ NOTES
++
++ When TTY auditing is enabled, it is inherited by all processes started by
++ that user. In particular, daemons restarted by an user will still have
++ TTY auditing enabled, and audit TTY input even by other users unless
++ auditing for these users is explicitly disabled. Therefore, it is
++ recommended to use as the first option for
++ most daemons using PAM.
++
++
++
+
+ EXAMPLES
+
+ Audit all administrative actions.
+
+-login root required pam_tty_audit.so enable=root
+-su root required pam_tty_audit.so enable=root
+-su-l root required pam_tty_audit.so enable=root
+-sudo root required pam_tty_audit.so enable=root
+-sudo-l root required pam_tty_audit.so enable=root
+-sshd root required pam_tty_audit.so enable=root
++session required pam_tty_audit.so disable=* enable=root
+
+
+
+diff -up Linux-PAM-0.99.8.1/modules/pam_tty_audit/README.xml.tty-audit2 Linux-PAM-0.99.8.1/modules/pam_tty_audit/README.xml
+--- Linux-PAM-0.99.8.1/modules/pam_tty_audit/README.xml.tty-audit2 2008-01-02 11:28:26.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_tty_audit/README.xml 2008-01-02 11:28:26.000000000 +0100
+@@ -25,6 +25,11 @@
+
+
++
++
+
+diff -up Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.c.tty-audit2 Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.c
+--- Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.c.tty-audit2 2008-01-02 11:28:26.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_tty_audit/pam_tty_audit.c 2008-01-02 11:28:26.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* Copyright © 2007 Red Hat, Inc. All rights reserved.
++/* Copyright © 2007, 2008 Red Hat, Inc. All rights reserved.
+ Red Hat author: Miloslav Trmač
+
+ Redistribution and use in source and binary forms of Linux-PAM, with
+@@ -37,7 +37,7 @@
+ DAMAGE. */
+
+ #include
+-#include
++#include
+ #include
+ #include
+ #include
+@@ -197,9 +197,7 @@ pam_sm_open_session (pam_handle_t *pamh,
+ enum command command;
+ struct audit_tty_status *old_status, new_status;
+ const char *user;
+- uid_t user_uid;
+- struct passwd *pwd;
+- int i, fd;
++ int i, fd, open_only;
+
+ (void)flags;
+
+@@ -208,15 +206,9 @@ pam_sm_open_session (pam_handle_t *pamh,
+ pam_syslog (pamh, LOG_ERR, "error determining target user's name");
+ return PAM_SESSION_ERR;
+ }
+- pwd = pam_modutil_getpwnam (pamh, user);
+- if (pwd == NULL)
+- {
+- pam_syslog (pamh, LOG_ERR, "error determining target user's UID: %m");
+- return PAM_SESSION_ERR;
+- }
+- user_uid = pwd->pw_uid;
+
+ command = CMD_NONE;
++ open_only = 0;
+ for (i = 0; i < argc; i++)
+ {
+ if (strncmp (argv[i], "enable=", 7) == 0
+@@ -232,13 +224,7 @@ pam_sm_open_session (pam_handle_t *pamh,
+ for (tok = strtok_r (copy, ",", &tok_data); tok != NULL;
+ tok = strtok_r (NULL, ",", &tok_data))
+ {
+- pwd = pam_modutil_getpwnam (pamh, tok);
+- if (pwd == NULL)
+- {
+- pam_syslog (pamh, LOG_WARNING, "unknown user %s", tok);
+- continue;
+- }
+- if (pwd->pw_uid == user_uid)
++ if (fnmatch (tok, user, 0) == 0)
+ {
+ command = this_command;
+ break;
+@@ -246,6 +232,13 @@ pam_sm_open_session (pam_handle_t *pamh,
+ }
+ free (copy);
+ }
++ else if (strcmp (argv[i], "open_only") == 0)
++ open_only = 1;
++ else
++ {
++ pam_syslog (pamh, LOG_ERR, "unknown option `%s'", argv[i]);
++ return PAM_SESSION_ERR;
++ }
+ }
+ if (command == CMD_NONE)
+ return PAM_SUCCESS;
+@@ -266,13 +259,15 @@ pam_sm_open_session (pam_handle_t *pamh,
+ return PAM_SESSION_ERR;
+ }
+
+- if (old_status->enabled == (command == CMD_ENABLE ? 1 : 0))
++ new_status.enabled = (command == CMD_ENABLE ? 1 : 0);
++ if (old_status->enabled == new_status.enabled)
+ {
+ free (old_status);
+ goto ok_fd;
+ }
+
+- if (pam_set_data (pamh, DATANAME, old_status, cleanup_old_status)
++ if (open_only == 0
++ && pam_set_data (pamh, DATANAME, old_status, cleanup_old_status)
+ != PAM_SUCCESS)
+ {
+ pam_syslog (pamh, LOG_ERR, "error saving old audit status");
+@@ -281,13 +276,14 @@ pam_sm_open_session (pam_handle_t *pamh,
+ return PAM_SESSION_ERR;
+ }
+
+- new_status.enabled = (command == CMD_ENABLE ? 1 : 0);
+ if (nl_send (fd, AUDIT_TTY_SET, NLM_F_ACK, &new_status,
+ sizeof (new_status)) != 0
+ || nl_recv_ack (fd) != 0)
+ {
+ pam_syslog (pamh, LOG_ERR, "error setting current audit status: %m");
+ close (fd);
++ if (open_only != 0)
++ free (old_status);
+ return PAM_SESSION_ERR;
+ }
+ /* Fall through */
+@@ -295,6 +291,8 @@ pam_sm_open_session (pam_handle_t *pamh,
+ close (fd);
+ pam_syslog (pamh, LOG_DEBUG, "changed status from %d to %d",
+ old_status->enabled, new_status.enabled);
++ if (open_only != 0)
++ free (old_status);
+ return PAM_SUCCESS;
+ }
+
diff --git a/pam-0.99.8.1-unix-blankpass.patch b/pam-0.99.8.1-unix-blankpass.patch
deleted file mode 100644
index 9a1c054..0000000
--- a/pam-0.99.8.1-unix-blankpass.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-diff -up Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.blankpass Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c
---- Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.blankpass 2007-09-18 13:50:40.000000000 +0200
-+++ Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c 2007-09-18 13:50:40.000000000 +0200
-@@ -50,7 +50,7 @@ int main(int argc, char *argv[])
- char pass[MAXPASS + 1];
- char *option;
- int npass, nullok;
-- int force_failure = 0;
-+ int blankpass = 0;
- int retval = PAM_AUTH_ERR;
- char *user;
- char *passwords[] = { pass };
-@@ -115,6 +115,10 @@ int main(int argc, char *argv[])
- if (npass != 1) { /* is it a valid password? */
- _log_err(LOG_DEBUG, "no valid password supplied");
- }
-+
-+ if (*pass == '\0') {
-+ blankpass = 1;
-+ }
-
- retval = _unix_verify_password(user, pass, nullok);
-
-@@ -122,8 +126,11 @@ int main(int argc, char *argv[])
-
- /* return pass or fail */
-
-- if ((retval != PAM_SUCCESS) || force_failure) {
-- _log_err(LOG_NOTICE, "password check failed for user (%s)", user);
-+ if (retval != PAM_SUCCESS) {
-+ /* don't log if it is a test for blank password */
-+ if (!blankpass) {
-+ _log_err(LOG_NOTICE, "password check failed for user (%s)", user);
-+ }
- return PAM_AUTH_ERR;
- } else {
- return PAM_SUCCESS;
-diff -up Linux-PAM-0.99.8.1/modules/pam_unix/support.c.blankpass Linux-PAM-0.99.8.1/modules/pam_unix/support.c
---- Linux-PAM-0.99.8.1/modules/pam_unix/support.c.blankpass 2007-09-18 13:50:40.000000000 +0200
-+++ Linux-PAM-0.99.8.1/modules/pam_unix/support.c 2007-09-18 17:56:57.000000000 +0200
-@@ -38,6 +38,9 @@
-
- const char app_name[]="pam_unix";
-
-+static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
-+ unsigned int ctrl, const char *user);
-+
- /* this is a front-end for module-application conversations */
-
- int _make_remark(pam_handle_t * pamh, unsigned int ctrl,
-@@ -442,6 +445,13 @@ _unix_blankpasswd (pam_handle_t *pamh, u
- * ...and shadow password file entry for this user,
- * if shadowing is enabled
- */
-+ if (geteuid() || SELINUX_ENABLED) {
-+ /* We do not have direct access to shadow. Run helper. */
-+ D(("running helper binary"));
-+ if (_unix_run_helper_binary(pamh, "", ctrl, name) == PAM_SUCCESS)
-+ return 1;
-+ return 0;
-+ }
- spwdent = pam_modutil_getspnam(pamh, name);
- }
- if (spwdent)
diff --git a/pam-0.99.8.1-unix-hpux-aging.patch b/pam-0.99.8.1-unix-hpux-aging.patch
new file mode 100644
index 0000000..72966dc
--- /dev/null
+++ b/pam-0.99.8.1-unix-hpux-aging.patch
@@ -0,0 +1,50 @@
+diff -up Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h.unix-hpux-aging Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h
+--- Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h.unix-hpux-aging 2008-01-08 14:43:36.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h 2008-01-08 15:49:43.000000000 +0100
+@@ -13,7 +13,7 @@
+ #define OLD_PASSWORDS_FILE "/etc/security/opasswd"
+
+ int
+-verify_pwd_hash(const char *p, const char *hash, unsigned int nullok);
++verify_pwd_hash(const char *p, char *hash, unsigned int nullok);
+
+ int
+ is_pwd_shadowed(const struct passwd *pwd);
+diff -up Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c.unix-hpux-aging Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c
+--- Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c.unix-hpux-aging 2008-01-08 14:43:36.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c 2008-01-08 15:49:02.000000000 +0100
+@@ -44,14 +44,32 @@
+ # include "./lckpwdf.-c"
+ #endif
+
++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
+-verify_pwd_hash(const char *p, const char *hash, unsigned int nullok)
++verify_pwd_hash(const char *p, char *hash, unsigned int nullok)
+ {
+- size_t hash_len = strlen(hash);
++ size_t hash_len;
+ char *pp = NULL;
+ int retval;
+ D(("called"));
+
++ strip_hpux_aging(hash);
++ hash_len = strlen(hash);
+ if (!hash_len) {
+ /* the stored password is NULL */
+ if (nullok) { /* this means we've succeeded */
diff --git a/pam-0.99.8.1-unix-update-helper.patch b/pam-0.99.8.1-unix-update-helper.patch
index 93baab4..d02a2f6 100644
--- a/pam-0.99.8.1-unix-update-helper.patch
+++ b/pam-0.99.8.1-unix-update-helper.patch
@@ -1,14 +1,51 @@
+diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c
+--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c.update-helper 2006-06-17 18:44:58.000000000 +0200
++++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_sess.c 2008-01-07 16:39:07.000000000 +0100
+@@ -73,7 +73,7 @@ PAM_EXTERN int pam_sm_open_session(pam_h
+
+ D(("called."));
+
+- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
++ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
+
+ retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
+ if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
+@@ -107,7 +107,7 @@ PAM_EXTERN int pam_sm_close_session(pam_
+
+ D(("called."));
+
+- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
++ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
+
+ retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
+ if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c
--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper 2007-04-30 12:47:30.000000000 +0200
-+++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c 2007-09-18 09:52:43.000000000 +0200
++++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c 2008-01-08 16:17:32.000000000 +0100
@@ -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.
++ * Copyright (c) Red Hat, Inc., 2007, 2008.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
+@@ -63,7 +64,6 @@
+ #ifdef WITH_SELINUX
+ static int selinux_enabled=-1;
+ #include
+-static security_context_t prev_context=NULL;
+ #define SELINUX_ENABLED (selinux_enabled!=-1 ? selinux_enabled : (selinux_enabled=is_selinux_enabled()>0))
+ #endif
+
+@@ -84,6 +84,7 @@ static security_context_t prev_context=N
+ #include "yppasswd.h"
+ #include "md5.h"
+ #include "support.h"
++#include "passverify.h"
+ #include "bigcrypt.h"
+
+ #if !((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 1))
@@ -92,15 +93,6 @@ extern int getrpcport(const char *host,
#endif /* GNU libc 2.1 */
@@ -159,64 +196,7 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper Lin
retval = PAM_AUTH_ERR;
} else {
retval = WEXITSTATUS(retval);
-@@ -315,8 +249,56 @@ static int _unix_run_shadow_binary(pam_h
-
- 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];
-@@ -354,393 +336,6 @@ static int check_old_password(const char
+@@ -354,393 +288,6 @@ static int check_old_password(const char
return retval;
}
@@ -610,18 +590,22 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper Lin
static int _do_setpass(pam_handle_t* pamh, const char *forwho,
const char *fromwhat,
char *towhat, unsigned int ctrl, int remember)
-@@ -769,7 +364,7 @@ static int _do_setpass(pam_handle_t* pam
+@@ -768,9 +315,7 @@ static int _do_setpass(pam_handle_t* pam
+ enum clnt_stat err;
/* Unlock passwd file to avoid deadlock */
- #ifdef USE_LCKPWDF
+-#ifdef USE_LCKPWDF
- ulckpwdf();
+-#endif
+ unlock_pwdf();
- #endif
unlocked = 1;
-@@ -832,33 +427,22 @@ static int _do_setpass(pam_handle_t* pam
+ /* Initialize password information */
+@@ -830,129 +375,63 @@ static int _do_setpass(pam_handle_t* pam
+ }
+
if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
- #ifdef USE_LCKPWDF
+-#ifdef USE_LCKPWDF
if(unlocked) {
- int i = 0;
- /* These values for the number of attempts and the sleep time
@@ -639,72 +623,169 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper Lin
return PAM_AUTHTOK_LOCK_BUSY;
}
}
- #endif
+#ifdef WITH_SELINUX
-+ if (selinux_confined())
++ if (unix_selinux_confined())
+ return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember);
-+#endif
+ #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);
+- 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 (on(UNIX_SHADOW, ctrl) || is_pwd_shadowed(pwd)) {
++ retval = unix_update_shadow(pamh, forwho, towhat);
if (retval == PAM_SUCCESS)
- if (!_unix_shadowed(pwd))
- retval = _update_passwd(pamh, forwho, "x");
-@@ -870,7 +454,7 @@ static int _do_setpass(pam_handle_t* pam
+- if (!_unix_shadowed(pwd))
+- retval = _update_passwd(pamh, forwho, "x");
++ if (!is_pwd_shadowed(pwd))
++ retval = unix_update_passwd(pamh, forwho, "x");
+ } else {
+- retval = _update_passwd(pamh, forwho, towhat);
++ retval = unix_update_passwd(pamh, forwho, towhat);
+ }
+ }
+
done:
- #ifdef USE_LCKPWDF
+-#ifdef USE_LCKPWDF
- ulckpwdf();
+-#endif
+ unlock_pwdf();
- #endif
return retval;
-@@ -891,13 +475,17 @@ static int _unix_verify_shadow(pam_handl
- if (_unix_shadowed(pwd)) {
- /* ...and shadow password file entry for this user, if shadowing
- is enabled */
+ }
+
+ static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned int ctrl)
+ {
+- struct passwd *pwd = NULL; /* Password and shadow password */
+- struct spwd *spwdent = NULL; /* file entries for the user */
+- time_t curdays;
+- int retval = PAM_SUCCESS;
++ struct passwd *pwent = NULL; /* Password and shadow password */
++ struct spwd *spent = NULL; /* file entries for the user */
++ int daysleft;
++ int retval;
+
+- /* UNIX passwords area */
+- pwd = getpwnam(user); /* Get password file entry... */
+- if (pwd == NULL)
+- return PAM_AUTHINFO_UNAVAIL; /* We don't need to do the rest... */
+-
+- if (_unix_shadowed(pwd)) {
+- /* ...and shadow password file entry for this user, if shadowing
+- is enabled */
- setspent();
- spwdent = getspnam(user);
- endspent();
--
- #ifdef WITH_SELINUX
++ retval = get_account_info(pamh, user, &pwent, &spent);
++ if (retval == PAM_USER_UNKNOWN) {
++ return retval;
++ }
+
+-#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;
-@@ -1020,7 +608,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
+-#endif
+- if (spwdent == NULL)
+- return PAM_AUTHINFO_UNAVAIL;
+- } else {
+- if (strcmp(pwd->pw_passwd,"*NP*") == 0) { /* NIS+ */
+- uid_t save_uid;
++ if (retval == PAM_SUCCESS && spent == NULL)
++ return PAM_SUCCESS;
+
+- save_uid = geteuid();
+- seteuid (pwd->pw_uid);
+- spwdent = getspnam( user );
+- seteuid (save_uid);
+-
+- if (spwdent == NULL)
+- return PAM_AUTHINFO_UNAVAIL;
+- } else
+- spwdent = NULL;
++ if (retval == PAM_UNIX_RUN_HELPER) {
++ retval = _unix_run_verify_binary(pamh, ctrl, user, &daysleft);
++ if (retval == PAM_AUTH_ERR || retval == PAM_USER_UNKNOWN)
++ return retval;
+ }
++ else if (retval == PAM_SUCCESS)
++ retval = check_shadow_expiry(pamh, spent, &daysleft);
++
++ if (on(UNIX__IAMROOT, ctrl) || retval == PAM_NEW_AUTHTOK_REQD)
++ return PAM_SUCCESS;
+
+- if (spwdent != NULL) {
+- /* We have the user's information, now let's check if their account
+- has expired (60 * 60 * 24 = number of seconds in a day) */
+-
+- if (off(UNIX__IAMROOT, ctrl)) {
+- /* Get the current number of days since 1970 */
+- curdays = time(NULL) / (60 * 60 * 24);
+- if (curdays < spwdent->sp_lstchg) {
+- pam_syslog(pamh, LOG_DEBUG,
+- "account %s has password changed in future",
+- user);
+- curdays = spwdent->sp_lstchg;
+- }
+- if ((curdays - spwdent->sp_lstchg < spwdent->sp_min)
+- && (spwdent->sp_min != -1))
+- /*
+- * The last password change was too recent.
+- */
+- retval = PAM_AUTHTOK_ERR;
+- else if ((curdays - spwdent->sp_lstchg > spwdent->sp_max)
+- && (curdays - spwdent->sp_lstchg > spwdent->sp_inact)
+- && (curdays - spwdent->sp_lstchg >
+- spwdent->sp_max + spwdent->sp_inact)
+- && (spwdent->sp_max != -1) && (spwdent->sp_inact != -1)
+- && (spwdent->sp_lstchg != 0))
+- /*
+- * Their password change has been put off too long,
+- */
+- retval = PAM_ACCT_EXPIRED;
+- else if ((curdays > spwdent->sp_expire) && (spwdent->sp_expire != -1)
+- && (spwdent->sp_lstchg != 0))
+- /*
+- * OR their account has just plain expired
+- */
+- retval = PAM_ACCT_EXPIRED;
+- }
+- }
+ return retval;
+ }
+
+@@ -1020,8 +499,9 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
int argc, const char **argv)
{
unsigned int ctrl, lctrl;
- int retval, i;
+ int retval;
int remember = -1;
++ int rounds = -1;
/* */
-@@ -1240,49 +828,40 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
+ const char *user;
+@@ -1030,7 +510,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
+
+ D(("called."));
+
+- ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);
++ ctrl = _set_ctrl(pamh, flags, &remember, &rounds, argc, argv);
+
+ /*
+ * First get the name of a user
+@@ -1239,40 +719,23 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
+ pass_new = pass_old = NULL; /* tidy up */
return retval;
}
- #ifdef USE_LCKPWDF
+-#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
@@ -720,58 +801,102 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper Lin
+ if (lock_pwdf() != PAM_SUCCESS) {
return PAM_AUTHTOK_LOCK_BUSY;
}
- #endif
+-#endif
-- if (pass_old) {
-+ if (!selinux_confined() && pass_old) {
+ if (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
+-#ifdef USE_LCKPWDF
- ulckpwdf();
+-#endif
+ 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
+ retval = _unix_verify_shadow(pamh, user, ctrl);
+ if (retval != PAM_SUCCESS) {
+- pam_syslog(pamh, LOG_NOTICE, "user not authenticated 2");
+-#ifdef USE_LCKPWDF
- ulckpwdf();
+-#endif
++ pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired");
+ 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) {
+@@ -1281,9 +744,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
pam_syslog(pamh, LOG_NOTICE,
"new password not acceptable 2");
pass_new = pass_old = NULL; /* tidy up */
- #ifdef USE_LCKPWDF
+-#ifdef USE_LCKPWDF
- ulckpwdf();
+-#endif
+ unlock_pwdf();
- #endif
return retval;
}
-@@ -1326,7 +905,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
- "out of memory for password");
- pass_new = pass_old = NULL; /* tidy up */
- #ifdef USE_LCKPWDF
+
+@@ -1296,51 +757,13 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
+ * First we encrypt the new password.
+ */
+
+- if (on(UNIX_MD5_PASS, ctrl)) {
+- tpass = crypt_md5_wrapper(pass_new);
+- } else {
+- /*
+- * Salt manipulation is stolen from Rick Faith's passwd
+- * program. Sorry Rick :) -- alex
+- */
+-
+- time_t tm;
+- char salt[3];
+-
+- time(&tm);
+- salt[0] = bin_to_ascii(tm & 0x3f);
+- salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
+- salt[2] = '\0';
+-
+- if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
+- /*
+- * to avoid using the _extensions_ of the bigcrypt()
+- * function we truncate the newly entered password
+- * [Problems that followed from this are fixed as per
+- * Bug 521314.]
+- */
+- char *temp = malloc(9);
+-
+- if (temp == NULL) {
+- pam_syslog(pamh, LOG_CRIT,
+- "out of memory for password");
+- pass_new = pass_old = NULL; /* tidy up */
+-#ifdef USE_LCKPWDF
- ulckpwdf();
-+ unlock_pwdf();
- #endif
- return PAM_BUF_ERR;
- }
-@@ -1349,7 +928,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
+-#endif
+- return PAM_BUF_ERR;
+- }
+- /* copy first 8 bytes of password */
+- strncpy(temp, pass_new, 8);
+- temp[8] = '\0';
+-
+- /* no longer need cleartext */
+- tpass = bigcrypt(temp, salt);
+-
+- _pam_delete(temp); /* tidy up */
+- } else {
+- tpass = bigcrypt(pass_new, salt);
+- }
++ tpass = create_password_hash(pass_new, ctrl, rounds);
++ if (tpass == NULL) {
++ pam_syslog(pamh, LOG_CRIT,
++ "out of memory for password");
++ pass_new = pass_old = NULL; /* tidy up */
++ unlock_pwdf();
++ return PAM_BUF_ERR;
+ }
+
+ D(("password processed"));
+@@ -1349,7 +772,7 @@ PAM_EXTERN int pam_sm_chauthtok(pam_hand
retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
remember);
@@ -782,13 +907,36 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper Lin
pass_old = pass_new = NULL;
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c
--- Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper 2007-03-12 15:35:14.000000000 +0100
-+++ Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c 2007-09-18 10:15:54.000000000 +0200
-@@ -41,386 +41,9 @@ static int selinux_enabled=-1;
++++ Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c 2008-01-08 16:17:32.000000000 +0100
+@@ -13,7 +13,6 @@
- #include "md5.h"
- #include "bigcrypt.h"
-+#include "passverify.h"
+ #include "config.h"
+
+-#include
+ #include
+ #include
+ #include
+@@ -25,401 +24,34 @@
+ #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"
+-
-/* syslogging function for errors and other information */
-
-static void _log_err(int err, const char *format,...)
@@ -856,12 +1004,16 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper Linux-P
- (void) sigaction(SIGINT, &action, NULL);
- (void) sigaction(SIGQUIT, &action, NULL);
-}
--
++#include "passverify.h"
+
-static int _verify_account(const char * const uname)
--{
-- struct spwd *spent;
-- struct passwd *pwent;
--
++static int _check_expiry(const char *uname)
+ {
+ struct spwd *spent;
+ struct passwd *pwent;
++ int retval;
++ int daysleft;
+
- pwent = getpwnam(uname);
- if (!pwent) {
- _log_err(LOG_ALERT, "could not identify user (from getpwnam(%s))", uname);
@@ -983,9 +1135,19 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper Linux-P
- if (pp && strcmp(pp, salt) == 0) {
- retval = PAM_SUCCESS;
- }
-- }
++ retval = get_account_info(uname, &pwent, &spent);
++ if (retval != PAM_SUCCESS) {
++ helper_log_err(LOG_ALERT, "could not obtain user info (%s)", uname);
++ printf("-1\n");
++ return retval;
++ }
++
++ if (spent == NULL) {
++ printf("-1\n");
++ return retval;
+ }
- p = NULL; /* no longer needed here */
--
+
- /* clean up */
- _pam_overwrite(pp);
- _pam_drop(pp);
@@ -1040,7 +1202,9 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper Linux-P
- pass[npass] = '\0'; /* NUL terminate */
- retval = _unix_verify_password(forwho, pass, 0);
- if (retval != PAM_SUCCESS) {
-- return retval;
++ retval = check_shadow_expiry(spent, &daysleft);
++ printf("%d\n", daysleft);
+ return retval;
- }
- }
-
@@ -1167,20 +1331,44 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper Linux-P
- unlink(SH_TMPFILE);
- return PAM_AUTHTOK_ERR;
- }
--}
-+const char app_name[] = "unix_chkpwd";
+ }
int main(int argc, char *argv[])
- {
-@@ -430,6 +53,7 @@ int main(int argc, char *argv[])
- int force_failure = 0;
+@@ -427,9 +59,10 @@ int main(int argc, char *argv[])
+ char pass[MAXPASS + 1];
+ char *option;
+ int npass, nullok;
+- int force_failure = 0;
++ int blankpass = 0;
int retval = PAM_AUTH_ERR;
char *user;
+ char *passwords[] = { pass };
/*
* Catch or ignore as many signal as possible.
-@@ -476,49 +100,24 @@ int main(int argc, char *argv[])
+@@ -446,7 +79,7 @@ int main(int argc, char *argv[])
+ */
+
+ if (isatty(STDIN_FILENO) || argc != 3 ) {
+- _log_err(LOG_NOTICE
++ helper_log_err(LOG_NOTICE
+ ,"inappropriate use of Unix helper binary [UID=%d]"
+ ,getuid());
+ fprintf(stderr
+@@ -458,11 +91,9 @@ int main(int argc, char *argv[])
+
+ /*
+ * Determine what the current user's name is.
+- * On a SELinux enabled system with a strict policy leaving the
+- * existing check prevents shadow password authentication from working.
+ * We must thus skip the check if the real uid is 0.
+ */
+- if (SELINUX_ENABLED && getuid() == 0) {
++ if (getuid() == 0) {
+ user=argv[1];
+ }
+ else {
+@@ -476,63 +107,49 @@ int main(int argc, char *argv[])
option=argv[2];
@@ -1194,11 +1382,15 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper Linux-P
- return _update_shadow(argv[1]);
- }
-
++ if (strcmp(option, "chkexpiry") == 0)
++ /* Check account information from the shadow file */
++ return _check_expiry(argv[1]);
/* read the nullok/nonull option */
- if (strncmp(option, "nullok", 8) == 0)
+- if (strncmp(option, "nullok", 8) == 0)
++ else if (strcmp(option, "nullok") == 0)
nullok = 1;
- else
-+ else if (strncmp(option, "nonull", 8) == 0)
++ else if (strcmp(option, "nonull") == 0)
nullok = 0;
+ else
+ return PAM_SYSTEM_ERR;
@@ -1227,59 +1419,193 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c.update-helper Linux-P
-
- 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");
++ helper_log_err(LOG_DEBUG, "no password supplied");
++ *pass = '\0';
++ }
+
+- }
++ if (*pass == '\0') {
++ blankpass = 1;
}
-+ retval = _unix_verify_password(user, pass, nullok);
++ retval = helper_verify_password(user, pass, nullok);
+
memset(pass, '\0', MAXPASS); /* clear memory of the password */
/* return pass or fail */
-@@ -533,6 +132,7 @@ int main(int argc, char *argv[])
+
+- if ((retval != PAM_SUCCESS) || force_failure) {
+- _log_err(LOG_NOTICE, "password check failed for user (%s)", user);
+- return PAM_AUTH_ERR;
++ if (retval != PAM_SUCCESS) {
++ if (!nullok || !blankpass)
++ /* no need to log blank pass test */
++ helper_log_err(LOG_NOTICE, "password check failed for user (%s)", user);
++ return PAM_AUTH_ERR;
+ } else {
+- return PAM_SUCCESS;
++ return PAM_SUCCESS;
+ }
+ }
/*
* Copyright (c) Andrew G. Morgan, 1996. All rights reserved
-+ * Copyright (c) Red Hat, Inc., 2007. All rights reserved
++ * Copyright (c) Red Hat, Inc., 2007,2008. All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
+diff -up Linux-PAM-0.99.8.1/modules/pam_unix/support.h.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/support.h
+--- Linux-PAM-0.99.8.1/modules/pam_unix/support.h.update-helper 2007-01-23 10:30:23.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_unix/support.h 2008-01-08 16:17:32.000000000 +0100
+@@ -1,5 +1,5 @@
+ /*
+- * $Id: support.h,v 1.12 2005/09/26 14:27:09 t8m Exp $
++ * $Id: support.h,v 1.13.2.1 2008/01/04 14:41:06 t8m Exp $
+ */
+
+ #ifndef _PAM_UNIX_SUPPORT_H
+@@ -84,8 +84,12 @@ typedef struct {
+ #define UNIX_NOREAP 21 /* don't reap child process */
+ #define UNIX_BROKEN_SHADOW 22 /* ignore errors reading password aging
+ * information during acct management */
++#define UNIX_SHA256_PASS 23 /* new password hashes will use SHA256 */
++#define UNIX_SHA512_PASS 24 /* new password hashes will use SHA512 */
++#define UNIX_ALGO_ROUNDS 25 /* optional number of rounds for new
++ password hash algorithms */
+ /* -------------- */
+-#define UNIX_CTRLS_ 23 /* number of ctrl arguments defined */
++#define UNIX_CTRLS_ 26 /* number of ctrl arguments defined */
+
+
+ static const UNIX_Ctrls unix_args[UNIX_CTRLS_] =
+@@ -116,6 +120,9 @@ static const UNIX_Ctrls unix_args[UNIX_C
+ /* UNIX_REMEMBER_PASSWD */ {"remember=", _ALL_ON_, 02000000},
+ /* UNIX_NOREAP */ {"noreap", _ALL_ON_, 04000000},
+ /* UNIX_BROKEN_SHADOW */ {"broken_shadow", _ALL_ON_, 010000000},
++/* UNIX_SHA256_PASS */ {"sha256", _ALL_ON_^(040420000), 020000000},
++/* UNIX_SHA512_PASS */ {"sha512", _ALL_ON_^(020420000), 040000000},
++/* UNIX_ALGO_ROUNDS */ {"rounds=", _ALL_ON_, 0100000000},
+ };
+
+ #define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag)
+@@ -131,8 +138,8 @@ static const UNIX_Ctrls unix_args[UNIX_C
+
+ extern int _make_remark(pam_handle_t * pamh, unsigned int ctrl
+ ,int type, const char *text);
+-extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int argc,
+- const char **argv);
++extern int _set_ctrl(pam_handle_t * pamh, int flags, int *remember, int *rounds,
++ int argc, const char **argv);
+ extern int _unix_getpwnam (pam_handle_t *pamh,
+ const char *name, int files, int nis,
+ struct passwd **ret);
+@@ -149,7 +156,7 @@ extern int _unix_read_password(pam_handl
+ ,const char *prompt2
+ ,const char *data_name
+ ,const void **pass);
+-extern int _unix_shadowed(const struct passwd *pwd);
+
+-extern struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user);
++extern int _unix_run_verify_binary(pam_handle_t *pamh,
++ unsigned int ctrl, const char *user, int *daysleft);
+ #endif /* _PAM_UNIX_SUPPORT_H */
diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h
---- /dev/null 2007-09-17 08:57:19.474470099 +0200
-+++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h 2007-09-18 10:13:25.000000000 +0200
-@@ -0,0 +1,62 @@
+--- /dev/null 2008-01-05 20:21:31.621001512 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h 2008-01-08 16:17:32.000000000 +0100
+@@ -0,0 +1,124 @@
+/*
-+ * 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.
-+ *
++ * Copyright information at end of file.
+ */
+
++#include
++#include
++#include
++
++#define PAM_UNIX_RUN_HELPER PAM_CRED_INSUFFICIENT
++
+#define MAXPASS 200 /* the maximum length of a password */
+
-+extern const char app_name[];
++#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
+
-+void _log_err(int err, const char *format,...);
++int
++verify_pwd_hash(const char *p, const char *hash, unsigned int nullok);
+
-+void setup_signals(void);
++int
++is_pwd_shadowed(const struct passwd *pwd);
+
-+int read_passwords(int fd, int npass, char **passwords);
++char *
++crypt_md5_wrapper(const char *pass_new);
+
-+int _unix_verify_password(const char *name, const char *p, int nullok);
++char *
++create_password_hash(const char *password, unsigned int ctrl, int rounds);
+
-+char *getuidname(uid_t uid);
++int
++unix_selinux_confined(void);
+
-+/*
-+ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
-+ * Copyright (c) Red Hat, Inc. 2007. All rights reserved
++int
++lock_pwdf(void);
++
++void
++unlock_pwdf(void);
++
++int
++save_old_password(const char *forwho, const char *oldpass,
++ int howmany);
++
++#ifdef HELPER_COMPILE
++void
++helper_log_err(int err, const char *format,...);
++
++int
++helper_verify_password(const char *name, const char *p, int nullok);
++
++void
++setup_signals(void);
++
++char *
++getuidname(uid_t uid);
++
++int
++read_passwords(int fd, int npass, char **passwords);
++
++int
++get_account_info(const char *name,
++ struct passwd **pwd, struct spwd **spwdent);
++
++int
++get_pwd_hash(const char *name,
++ struct passwd **pwd, char **hash);
++
++int
++check_shadow_expiry(struct spwd *spent, int *daysleft);
++
++int
++unix_update_passwd(const char *forwho, const char *towhat);
++
++int
++unix_update_shadow(const char *forwho, char *towhat);
++#else
++int
++get_account_info(pam_handle_t *pamh, const char *name,
++ struct passwd **pwd, struct spwd **spwdent);
++
++int
++get_pwd_hash(pam_handle_t *pamh, const char *name,
++ struct passwd **pwd, char **hash);
++
++int
++check_shadow_expiry(pam_handle_t *pamh, struct spwd *spent, int *daysleft);
++
++int
++unix_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat);
++
++int
++unix_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat);
++#endif
++
++/* ****************************************************************** *
++ * 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
@@ -1314,90 +1640,503 @@ diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h
+ */
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c
--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c.update-helper 2006-06-27 10:38:14.000000000 +0200
-+++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c 2007-09-18 13:02:52.000000000 +0200
-@@ -124,11 +124,11 @@ struct spwd *_unix_run_verify_binary(pam
- }
++++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c 2008-01-08 16:17:32.000000000 +0100
+@@ -47,10 +47,6 @@
+ #include /* for time() */
+ #include
+ #include
+-#ifdef WITH_SELINUX
+-#include
+-#define SELINUX_ENABLED is_selinux_enabled()>0
+-#endif
- /* 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");
+ #include
-- execve(CHKPWD_HELPER, args, envp);
-+ execve(UPDATE_HELPER, args, envp);
+@@ -63,12 +59,10 @@
+ #include
- pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %m");
- /* should not get here: exit with error */
-@@ -142,11 +142,11 @@ struct spwd *_unix_run_verify_binary(pam
- 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");
+ #include "support.h"
++#include "passverify.h"
+
+-#ifdef WITH_SELINUX
+-
+-struct spwd spwd;
+-
+-struct spwd *_unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl, const char *user)
++int _unix_run_verify_binary(pam_handle_t *pamh, unsigned int ctrl,
++ const char *user, int *daysleft)
+ {
+ int retval=0, child, fds[2];
+ void (*sighandler)(int) = NULL;
+@@ -78,7 +72,7 @@ struct spwd *_unix_run_verify_binary(pam
+ if (pipe(fds) != 0) {
+ D(("could not make pipe"));
+ pam_syslog(pamh, LOG_ERR, "Could not make pipe: %m");
+- return NULL;
++ return PAM_AUTH_ERR;
+ }
+ D(("called."));
+
+@@ -117,7 +111,7 @@ struct spwd *_unix_run_verify_binary(pam
+ }
+ }
+
+- if (SELINUX_ENABLED && geteuid() == 0) {
++ if (geteuid() == 0) {
+ /* must set the real uid to 0 so the helper will not error
+ out if pam is called from setuid binary (su, sudo...) */
+ setuid(0);
+@@ -126,7 +120,7 @@ struct spwd *_unix_run_verify_binary(pam
+ /* exec binary helper */
+ args[0] = x_strdup(CHKPWD_HELPER);
+ args[1] = x_strdup(user);
+- args[2] = x_strdup("verify");
++ args[2] = x_strdup("chkexpiry");
+
+ execve(CHKPWD_HELPER, args, envp);
+
+@@ -134,11 +128,12 @@ struct spwd *_unix_run_verify_binary(pam
+ /* should not get here: exit with error */
+ close (fds[1]);
+ D(("helper binary is not available"));
++ printf("-1\n");
+ exit(PAM_AUTHINFO_UNAVAIL);
+ } else {
+ close(fds[1]);
+ if (child > 0) {
+- char buf[1024];
++ char buf[32];
+ int rc=0;
+ rc=waitpid(child, &retval, 0); /* wait for helper to complete */
+ if (rc<0) {
+@@ -146,22 +141,16 @@ struct spwd *_unix_run_verify_binary(pam
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) {
+- rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
+- if(rc > 0) {
++ rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1);
++ if(rc > 0) {
buf[rc] = '\0';
-@@ -157,15 +157,15 @@ struct spwd *_unix_run_verify_binary(pam
- &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;
-- }
+- 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;
++ if (sscanf(buf,"%d", daysleft) != 1 )
++ 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, "read unix_chkpwd output error %d: %m", rc);
++ 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,15 +247,17 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_hand
- setreuid( -1, save_euid );
- }
+ pam_syslog(pamh, LOG_ERR, "Fork failed: %m");
+@@ -174,15 +163,9 @@ struct spwd *_unix_run_verify_binary(pam
+ (void) signal(SIGCHLD, sighandler); /* restore old signal handler */
+ }
+ D(("Returning %d",retval));
+- if (retval != PAM_SUCCESS) {
+- return NULL;
+- }
+- return &spwd;
++ return retval;
+ }
+
+-#endif
+-
+-
+ /*
+ * PAM framework looks for this entry-point to pass control to the
+ * account management module.
+@@ -195,14 +178,13 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_hand
+ const void *void_uname;
+ const char *uname;
+ int retval, daysleft;
+- time_t curdays;
+ struct spwd *spent;
+ struct passwd *pwent;
+ char buf[256];
+
+ D(("called."));
+
+- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
++ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
+
+ retval = pam_get_item(pamh, PAM_USER, &void_uname);
+ uname = void_uname;
+@@ -214,134 +196,90 @@ PAM_EXTERN int pam_sm_acct_mgmt(pam_hand
+ return PAM_USER_UNKNOWN;
+ }
+- pwent = pam_modutil_getpwnam(pamh, uname);
+- if (!pwent) {
++ retval = get_account_info(pamh, uname, &pwent, &spent);
++ if (retval == PAM_USER_UNKNOWN) {
+ pam_syslog(pamh, LOG_ALERT,
+ "could not identify user (from getpwnam(%s))",
+ uname);
+- return PAM_USER_UNKNOWN;
++ return retval;
+ }
+
+- if (!strcmp( pwent->pw_passwd, "*NP*" )) { /* NIS+ */
+- uid_t save_euid, save_uid;
+-
+- save_euid = geteuid();
+- save_uid = getuid();
+- if (save_uid == pwent->pw_uid)
+- setreuid( save_euid, save_uid );
+- else {
+- setreuid( 0, -1 );
+- if (setreuid( -1, pwent->pw_uid ) == -1) {
+- setreuid( -1, 0 );
+- setreuid( 0, -1 );
+- if(setreuid( -1, pwent->pw_uid ) == -1)
+- return PAM_CRED_INSUFFICIENT;
+- }
+- }
+- spent = pam_modutil_getspnam (pamh, uname);
+- if (save_uid == pwent->pw_uid)
+- setreuid( save_uid, save_euid );
+- else {
+- if (setreuid( -1, 0 ) == -1)
+- setreuid( save_uid, -1 );
+- setreuid( -1, save_euid );
+- }
+-
- } else if (_unix_shadowed (pwent))
- spent = pam_modutil_getspnam (pamh, uname);
- else
-- return PAM_SUCCESS;
--
-+ } else if (geteuid() != 0) { /* cannot read shadow when non root */
-+ return PAM_IGNORE;
-+ } else if (_unix_shadowed (pwent)) {
- #ifdef WITH_SELINUX
++ if (retval == PAM_SUCCESS && spent == NULL)
+ return PAM_SUCCESS;
+
+-#ifdef WITH_SELINUX
- if (!spent && SELINUX_ENABLED )
- spent = _unix_run_verify_binary(pamh, ctrl, uname);
-+ if (SELINUX_ENABLED)
-+ spent = _unix_run_verify_binary(pamh, ctrl, uname);
+-#endif
+-
+- if (!spent)
++ if (retval == PAM_UNIX_RUN_HELPER) {
++ retval = _unix_run_verify_binary(pamh, ctrl, uname, &daysleft);
++ if (retval == PAM_AUTHINFO_UNAVAIL &&
++ on(UNIX_BROKEN_SHADOW, ctrl))
++ return PAM_SUCCESS;
++ } else if (retval != PAM_SUCCESS) {
+ if (on(UNIX_BROKEN_SHADOW,ctrl))
+ return PAM_SUCCESS;
+ else
- #endif
-+ spent = pam_modutil_getspnam (pamh, uname);
++ return retval;
+ } else
-+ return PAM_SUCCESS;
++ retval = check_shadow_expiry(pamh, spent, &daysleft);
- if (!spent)
- if (on(UNIX_BROKEN_SHADOW,ctrl))
-diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c
---- /dev/null 2007-09-17 08:57:19.474470099 +0200
-+++ Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c 2007-09-18 09:52:43.000000000 +0200
-@@ -0,0 +1,560 @@
+- if (!spent)
+- return PAM_AUTHINFO_UNAVAIL; /* Couldn't get username from shadow */
+-
+- curdays = time(NULL) / (60 * 60 * 24);
+- D(("today is %d, last change %d", curdays, spent->sp_lstchg));
+- if ((curdays > spent->sp_expire) && (spent->sp_expire != -1)) {
++ switch (retval) {
++ case PAM_ACCT_EXPIRED:
+ pam_syslog(pamh, LOG_NOTICE,
+- "account %s has expired (account expired)",
+- uname);
++ "account %s has expired (account expired)",
++ uname);
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+- _("Your account has expired; please contact your system administrator"));
+- D(("account expired"));
+- return PAM_ACCT_EXPIRED;
+- }
+- if (spent->sp_lstchg == 0) {
+- pam_syslog(pamh, LOG_NOTICE,
+- "expired password for user %s (root enforced)",
+- uname);
+- _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+- _("You are required to change your password immediately (root enforced)"));
+- D(("need a new password"));
+- return PAM_NEW_AUTHTOK_REQD;
+- }
+- if (curdays < spent->sp_lstchg) {
+- pam_syslog(pamh, LOG_DEBUG,
+- "account %s has password changed in future",
+- uname);
+- return PAM_SUCCESS;
+- }
+- if ((curdays - spent->sp_lstchg > spent->sp_max)
+- && (curdays - spent->sp_lstchg > spent->sp_inact)
+- && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact)
+- && (spent->sp_max != -1) && (spent->sp_inact != -1)) {
++ _("Your account has expired; please contact your system administrator"));
++ break;
++ case PAM_NEW_AUTHTOK_REQD:
++ if (daysleft == 0) {
++ pam_syslog(pamh, LOG_NOTICE,
++ "expired password for user %s (root enforced)",
++ uname);
++ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
++ _("You are required to change your password immediately (root enforced)"));
++ } else {
++ pam_syslog(pamh, LOG_DEBUG,
++ "expired password for user %s (password aged)",
++ uname);
++ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
++ _("You are required to change your password immediately (password aged)"));
++ }
++ break;
++ case PAM_AUTHTOK_EXPIRED:
+ pam_syslog(pamh, LOG_NOTICE,
+- "account %s has expired (failed to change password)",
+- uname);
+- _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+- _("Your account has expired; please contact your system administrator"));
+- D(("account expired 2"));
+- return PAM_ACCT_EXPIRED;
+- }
+- if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
+- pam_syslog(pamh, LOG_DEBUG,
+- "expired password for user %s (password aged)",
+- uname);
++ "account %s has expired (failed to change password)",
++ uname);
+ _make_remark(pamh, ctrl, PAM_ERROR_MSG,
+- _("You are required to change your password immediately (password aged)"));
+- D(("need a new password 2"));
+- return PAM_NEW_AUTHTOK_REQD;
+- }
+- if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn)
+- && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
+- daysleft = (spent->sp_lstchg + spent->sp_max) - curdays;
+- pam_syslog(pamh, LOG_DEBUG,
+- "password for user %s will expire in %d days",
+- uname, daysleft);
+-#ifdef HAVE_DNGETTEXT
+- snprintf (buf, sizeof (buf),
+- dngettext(PACKAGE,
+- "Warning: your password will expire in %d day",
+- "Warning: your password will expire in %d days",
+- daysleft),
+- daysleft);
++ _("Your account has expired; please contact your system administrator"));
++ break;
++ case PAM_SUCCESS:
++ if (daysleft >= 0) {
++ pam_syslog(pamh, LOG_DEBUG,
++ "password for user %s will expire in %d days",
++ uname, daysleft);
++#if defined HAVE_DNGETTEXT && defined ENABLE_NLS
++ snprintf (buf, sizeof (buf),
++ dngettext(PACKAGE,
++ "Warning: your password will expire in %d day",
++ "Warning: your password will expire in %d days",
++ daysleft),
++ daysleft);
+ #else
+- if (daysleft == 1)
+- snprintf(buf, sizeof (buf),
+- _("Warning: your password will expire in %d day"),
+- daysleft);
+- else
+- snprintf(buf, sizeof (buf),
+- /* TRANSLATORS: only used if dngettext is not support
+-ed */
+- _("Warning: your password will expire in %d days"),
+- daysleft);
++ if (daysleft == 1)
++ snprintf(buf, sizeof (buf),
++ _("Warning: your password will expire in %d day"),
++ daysleft);
++ else
++ snprintf(buf, sizeof (buf),
++ /* TRANSLATORS: only used if dngettext is not supported */
++ _("Warning: your password will expire in %d days"),
++ daysleft);
+ #endif
+- _make_remark(pamh, ctrl, PAM_TEXT_INFO, buf);
++ _make_remark(pamh, ctrl, PAM_TEXT_INFO, buf);
++ }
+ }
+
+ D(("all done"));
+
+- return PAM_SUCCESS;
++ return retval;
+ }
+
+
+diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/unix_update.c
+--- /dev/null 2008-01-05 20:21:31.621001512 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_unix/unix_update.c 2008-01-08 16:17:32.000000000 +0100
+@@ -0,0 +1,194 @@
++/*
++ * 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 int selinux_enabled=-1;
++#else
++#define SELINUX_ENABLED 0
++#endif
++
++#include
++#include
++
++#include "passverify.h"
++
++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) {
++ helper_log_err(LOG_DEBUG, "no new password supplied");
++ memset(pass, '\0', MAXPASS);
++ } else {
++ helper_log_err(LOG_DEBUG, "no valid passwords supplied");
++ }
++ return PAM_AUTHTOK_ERR;
++ }
++
++ if (lock_pwdf() != PAM_SUCCESS)
++ return PAM_AUTHTOK_LOCK_BUSY;
++
++ 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 = helper_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 || is_pwd_shadowed(pwd)) {
++ retval = unix_update_shadow(forwho, towhat);
++ if (retval == PAM_SUCCESS)
++ if (!is_pwd_shadowed(pwd))
++ retval = unix_update_passwd(forwho, "x");
++ } else {
++ retval = unix_update_passwd(forwho, towhat);
++ }
++
++done:
++ memset(pass, '\0', MAXPASS);
++ memset(towhat, '\0', MAXPASS);
++
++ unlock_pwdf();
++
++ 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 != 5 ) {
++ helper_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_CRED_INSUFFICIENT;
++ }
++
++ option = argv[2];
++
++ if (strcmp(option, "update") == 0) {
++ /* Attempting to change the password */
++ return set_password(argv[1], argv[3], argv[4]);
++ }
++
++ return PAM_SYSTEM_ERR;
++}
++
+/*
-+ * Main coding by Elliot Lee , Red Hat Software.
-+ * Copyright (C) 1996.
-+ * Copyright (c) Jan R�korajski, 1999.
-+ * Copyright (c) Red Hat, Inc., 2007
++ * Copyright (c) Andrew G. Morgan, 1996. All rights reserved
++ * Copyright (c) Red Hat, Inc., 2007, 2008. All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
@@ -1430,121 +2169,508 @@ diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c
+--- /dev/null 2008-01-05 20:21:31.621001512 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c 2008-01-08 16:17:32.000000000 +0100
+@@ -0,0 +1,1083 @@
++/*
++ * Copyright information at end of file.
++ */
++#include "config.h"
++#include
++#include
++#include "support.h"
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include "md5.h"
++#include "bigcrypt.h"
++#include "passverify.h"
++
++#ifdef WITH_SELINUX
++#include
++#define SELINUX_ENABLED is_selinux_enabled()>0
++#else
++#define SELINUX_ENABLED 0
++#endif
+
-+/* this will be included from module and update helper */
++#ifdef HELPER_COMPILE
++#define pam_modutil_getpwnam(h,n) getpwnam(n)
++#define pam_modutil_getspnam(h,n) getspnam(n)
++#define pam_syslog(h,a,b,c) helper_log_err(a,b,c)
++#else
++#include
++#include
++#endif
+
+#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF)
+# include "./lckpwdf.-c"
+#endif
+
-+/* passwd/salt conversion macros */
++int
++verify_pwd_hash(const char *p, const char *hash, unsigned int nullok)
++{
++ size_t hash_len = strlen(hash);
++ char *pp = NULL;
++ int retval;
++ D(("called"));
++
++ if (!hash_len) {
++ /* the stored password is NULL */
++ if (nullok) { /* this means we've succeeded */
++ D(("user has empty password - access granted"));
++ retval = PAM_SUCCESS;
++ } else {
++ D(("user has empty password - access denied"));
++ retval = PAM_AUTH_ERR;
++ }
++ } else if (!p || *hash == '*' || *hash == '!') {
++ retval = PAM_AUTH_ERR;
++ } else {
++ if (!strncmp(hash, "$1$", 3)) {
++ pp = Goodcrypt_md5(p, hash);
++ if (pp && strcmp(pp, hash) != 0) {
++ _pam_delete(pp);
++ pp = Brokencrypt_md5(p, hash);
++ }
++ } else if (*hash != '$' && hash_len >= 13) {
++ pp = bigcrypt(p, hash);
++ if (pp && hash_len == 13 && strlen(pp) > hash_len) {
++ _pam_overwrite(pp + hash_len);
++ }
++ } else {
++ /*
++ * Ok, we don't know the crypt algorithm, but maybe
++ * libcrypt nows about it? We should try it.
++ */
++ pp = x_strdup(crypt(p, hash));
++ }
++ p = NULL; /* no longer needed here */
+
-+#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)+'.')
++ /* the moment of truth -- do we agree with the password? */
++ D(("comparing state of pp[%s] and salt[%s]", pp, salt));
+
-+#define PW_TMPFILE "/etc/npasswd"
-+#define SH_TMPFILE "/etc/nshadow"
-+#define OPW_TMPFILE "/etc/security/nopasswd"
-+#define OLD_PASSWORDS_FILE "/etc/security/opasswd"
++ if (pp && strcmp(pp, hash) == 0) {
++ retval = PAM_SUCCESS;
++ } else {
++ retval = PAM_AUTH_ERR;
++ }
++ }
++
++ if (pp)
++ _pam_delete(pp);
++ D(("done [%d].", retval));
+
-+/*
-+ * i64c - convert an integer to a radix 64 character
-+ */
-+static int i64c(int i)
++ return retval;
++}
++
++int
++is_pwd_shadowed(const struct passwd *pwd)
+{
-+ 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');
++ 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;
+}
+
-+static char *crypt_md5_wrapper(const char *pass_new)
++#ifdef HELPER_COMPILE
++int
++get_account_info(const char *name,
++ struct passwd **pwd, struct spwd **spwdent)
++#else
++int
++get_account_info(pam_handle_t *pamh, const char *name,
++ struct passwd **pwd, struct spwd **spwdent)
++#endif
+{
-+ /*
-+ * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
-+ * removed use of static variables (AGM)
-+ */
++ /* UNIX passwords area */
++ *pwd = pam_modutil_getpwnam(pamh, name); /* Get password file entry... */
++ *spwdent = NULL;
++
++ if (*pwd != NULL) {
++ if (strcmp((*pwd)->pw_passwd, "*NP*") == 0)
++ { /* NIS+ */
++#ifdef HELPER_COMPILE
++ uid_t save_euid, save_uid;
++
++ save_euid = geteuid();
++ save_uid = getuid();
++ if (save_uid == (*pwd)->pw_uid)
++ setreuid(save_euid, save_uid);
++ else {
++ setreuid(0, -1);
++ if (setreuid(-1, (*pwd)->pw_uid) == -1) {
++ setreuid(-1, 0);
++ setreuid(0, -1);
++ if(setreuid(-1, (*pwd)->pw_uid) == -1)
++ return PAM_CRED_INSUFFICIENT;
++ }
++ }
++
++ *spwdent = pam_modutil_getspnam(pamh, name);
++ if (save_uid == (*pwd)->pw_uid)
++ setreuid(save_uid, save_euid);
++ else {
++ setreuid(-1, 0);
++ setreuid(save_uid, -1);
++ setreuid(-1, save_euid);
++ }
+
-+ 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;
++ if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
++ return PAM_AUTHINFO_UNAVAIL;
++#else
++ /* we must run helper for NIS+ passwords */
++ return PAM_UNIX_RUN_HELPER;
++#endif
++ } else if (is_pwd_shadowed(*pwd)) {
++ /*
++ * ...and shadow password file entry for this user,
++ * if shadowing is enabled
++ */
++#ifndef HELPER_COMPILE
++ if (geteuid() || SELINUX_ENABLED)
++ return PAM_UNIX_RUN_HELPER;
++#endif
++ *spwdent = pam_modutil_getspnam(pamh, name);
++ if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
++ return PAM_AUTHINFO_UNAVAIL;
++ }
++ } else {
++ return PAM_USER_UNKNOWN;
++ }
++ return PAM_SUCCESS;
+}
+
-+#ifdef USE_LCKPWDF
-+static int lock_pwdf(void)
++#ifdef HELPER_COMPILE
++int
++get_pwd_hash(const char *name,
++ struct passwd **pwd, char **hash)
++#else
++int
++get_pwd_hash(pam_handle_t *pamh, const char *name,
++ struct passwd **pwd, char **hash)
++#endif
+{
-+ int i;
+ int retval;
++ struct spwd *spwdent = NULL;
+
-+#ifndef HELPER_COMPILE
-+ if (selinux_confined()) {
++#ifdef HELPER_COMPILE
++ retval = get_account_info(name, pwd, &spwdent);
++#else
++ retval = get_account_info(pamh, name, pwd, &spwdent);
++#endif
++ if (retval != PAM_SUCCESS) {
++ return retval;
++ }
++
++ if (spwdent)
++ *hash = x_strdup(spwdent->sp_pwdp);
++ else
++ *hash = x_strdup((*pwd)->pw_passwd);
++ if (*hash == NULL)
++ return PAM_BUF_ERR;
++
++ return PAM_SUCCESS;
++}
++
++#ifdef HELPER_COMPILE
++int
++check_shadow_expiry(struct spwd *spent, int *daysleft)
++#else
++int
++check_shadow_expiry(pam_handle_t *pamh, struct spwd *spent, int *daysleft)
++#endif
++{
++ long int curdays;
++ *daysleft = -1;
++ curdays = (long int)(time(NULL) / (60 * 60 * 24));
++ D(("today is %d, last change %d", curdays, spent->sp_lstchg));
++ if ((curdays > spent->sp_expire) && (spent->sp_expire != -1)) {
++ D(("account expired"));
++ return PAM_ACCT_EXPIRED;
++ }
++ if (spent->sp_lstchg == 0) {
++ D(("need a new password"));
++ *daysleft = 0;
++ return PAM_NEW_AUTHTOK_REQD;
++ }
++ if (curdays < spent->sp_lstchg) {
++ pam_syslog(pamh, LOG_DEBUG,
++ "account %s has password changed in future",
++ spent->sp_namp);
+ return PAM_SUCCESS;
+ }
++ if ((curdays - spent->sp_lstchg > spent->sp_max)
++ && (curdays - spent->sp_lstchg > spent->sp_inact)
++ && (curdays - spent->sp_lstchg > spent->sp_max + spent->sp_inact)
++ && (spent->sp_max != -1) && (spent->sp_inact != -1)) {
++ *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
++ D(("authtok expired"));
++ return PAM_AUTHTOK_EXPIRED;
++ }
++ if ((curdays - spent->sp_lstchg > spent->sp_max) && (spent->sp_max != -1)) {
++ D(("need a new password 2"));
++ return PAM_NEW_AUTHTOK_REQD;
++ }
++ if ((curdays - spent->sp_lstchg > spent->sp_max - spent->sp_warn)
++ && (spent->sp_max != -1) && (spent->sp_warn != -1)) {
++ *daysleft = (int)((spent->sp_lstchg + spent->sp_max) - curdays);
++ D(("warn before expiry"));
++ }
++ return PAM_SUCCESS;
++
++}
++
++/* passwd/salt conversion macros */
++
++#define PW_TMPFILE "/etc/npasswd"
++#define SH_TMPFILE "/etc/nshadow"
++#define OPW_TMPFILE "/etc/security/nopasswd"
++
++/*
++ * 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 void
++crypt_make_salt(char *where, int length)
++{
++ struct timeval tv;
++ MD5_CTX ctx;
++ unsigned char tmp[16];
++ unsigned char *src = (unsigned char *)where;
++ int i;
++#ifdef PATH_RANDOMDEV
++ int fd;
++ int rv;
++
++ if ((rv = fd = open (PATH_RANDOMDEV, O_RDONLY)) != -1) {
++ while ((rv = read(fd, where, length)) != length && errno == EINTR);
++ close (fd);
++ }
++ if (rv != length) {
++#endif
++ /*
++ * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
++ * removed use of static variables (AGM)
++ *
++ * will work correctly only for length <= 16 */
++ src = tmp;
++ 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, src, length);
++ GoodMD5Final(tmp, &ctx);
++#ifdef PATH_RANDOMDEV
++ }
+#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++;
++ for (i = 0; i < length; i++)
++ *where++ = i64c(src[i] & 077);
++ *where = '\0';
++}
++
++char *
++crypt_md5_wrapper(const char *pass_new)
++{
++ unsigned char result[16];
++ char *cp = (char *) result;
++
++ cp = stpcpy(cp, "$1$"); /* magic for the MD5 */
++ crypt_make_salt(cp, 9);
++
++ /* no longer need cleartext */
++ cp = Goodcrypt_md5(pass_new, (const char *) result);
++ pass_new = NULL;
++
++ return cp;
++}
++
++char *
++create_password_hash(const char *password, unsigned int ctrl, int rounds)
++{
++ const char *algoid;
++ char salt[64]; /* contains rounds number + max 16 bytes of salt + algo id */
++ char *sp;
++
++ if (on(UNIX_MD5_PASS, ctrl)) {
++ return crypt_md5_wrapper(password);
++ }
++ if (on(UNIX_SHA256_PASS, ctrl)) {
++ algoid = "$5$";
++ } else if (on(UNIX_SHA512_PASS, ctrl)) {
++ algoid = "$6$";
++ } else { /* must be crypt/bigcrypt */
++ char tmppass[9];
++ char *crypted;
++
++ crypt_make_salt(salt, 2);
++ if (off(UNIX_BIGCRYPT, ctrl) && strlen(password) > 8) {
++ strncpy(tmppass, password, sizeof(tmppass)-1);
++ tmppass[sizeof(tmppass)-1] = '\0';
++ password = tmppass;
++ }
++ crypted = bigcrypt(password, salt);
++ memset(tmppass, '\0', sizeof(tmppass));
++ password = NULL;
++ return crypted;
+ }
-+ if(retval != 0) {
-+ return PAM_AUTHTOK_LOCK_BUSY;
++
++ sp = stpcpy(salt, algoid);
++ if (on(UNIX_ALGO_ROUNDS, ctrl)) {
++ sp += snprintf(sp, sizeof(salt) - 3, "rounds=%u$", rounds);
+ }
++ crypt_make_salt(sp, 8);
++ /* For now be conservative so the resulting hashes
++ * are not too long. 8 bytes of salt prevents dictionary
++ * attacks well enough. */
++ return x_strdup(crypt(password, salt));
++}
++
++#ifdef WITH_SELINUX
++int
++unix_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
++int
++unix_selinux_confined(void)
++{
++ return 0;
++}
++#endif
++
++#ifdef USE_LCKPWDF
++int
++lock_pwdf(void)
++{
++ int i;
++ int retval;
++
++#ifndef HELPER_COMPILE
++ if (unix_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;
++}
++
++void
++unlock_pwdf(void)
++{
++#ifndef HELPER_COMPILE
++ if (unix_selinux_confined()) {
++ return;
++ }
++#endif
++ ulckpwdf();
++}
++#else
++int
++lock_pwdf(void)
++{
+ return PAM_SUCCESS;
+}
+
-+static void unlock_pwdf(void)
++void
++unlock_pwdf(void)
+{
-+#ifndef HELPER_COMPILE
-+ if (selinux_confined()) {
-+ return;
-+ }
-+#endif
-+ ulckpwdf();
++ return;
+}
+#endif
+
-+static int
++int
+save_old_password(const char *forwho, const char *oldpass,
+ int howmany)
+{
@@ -1558,6 +2684,7 @@ diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c
+ int found = 0;
+ struct passwd *pwd = NULL;
+ struct stat st;
++ security_context_t prev_context=NULL;
+
+ if (howmany < 0) {
+ return PAM_SUCCESS;
@@ -1702,11 +2829,11 @@ diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c
+}
+
+#ifdef HELPER_COMPILE
-+static int
-+_update_passwd(const char *forwho, const char *towhat)
++int
++unix_update_passwd(const char *forwho, const char *towhat)
+#else
-+static int
-+_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat)
++int
++unix_update_passwd(pam_handle_t *pamh, const char *forwho, const char *towhat)
+#endif
+{
+ struct passwd *tmpent = NULL;
@@ -1714,6 +2841,7 @@ diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c
+ FILE *pwfile, *opwfile;
+ int err = 1;
+ int oldmask;
++ security_context_t prev_context=NULL;
+
+ oldmask = umask(077);
+#ifdef WITH_SELINUX
@@ -1799,7 +2927,7 @@ diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c
+ if (!err) {
+ if (!rename(PW_TMPFILE, "/etc/passwd"))
+#ifdef HELPER_COMPILE
-+ _log_err(
++ helper_log_err(
+#else
+ pam_syslog(pamh,
+#endif
@@ -1826,11 +2954,11 @@ diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c
+}
+
+#ifdef HELPER_COMPILE
-+static int
-+_update_shadow(const char *forwho, char *towhat)
++int
++unix_update_shadow(const char *forwho, char *towhat)
+#else
-+static int
-+_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
++int
++unix_update_shadow(pam_handle_t *pamh, const char *forwho, char *towhat)
+#endif
+{
+ struct spwd *spwdent = NULL, *stmpent = NULL;
@@ -1838,6 +2966,7 @@ diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c
+ FILE *pwfile, *opwfile;
+ int err = 1;
+ int oldmask;
++ security_context_t prev_context=NULL;
+
+ spwdent = getspnam(forwho);
+ if (spwdent == NULL) {
@@ -1926,7 +3055,7 @@ diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c
+ if (!err) {
+ if (!rename(SH_TMPFILE, "/etc/shadow"))
+#ifdef HELPER_COMPILE
-+ _log_err(
++ helper_log_err(
+#else
+ pam_syslog(pamh,
+#endif
@@ -1953,554 +3082,148 @@ diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c
+ return PAM_AUTHTOK_ERR;
+ }
+}
-diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/unix_update.c
---- /dev/null 2007-09-17 08:57:19.474470099 +0200
-+++ Linux-PAM-0.99.8.1/modules/pam_unix/unix_update.c 2007-09-18 10:16:36.000000000 +0200
-@@ -0,0 +1,264 @@
-+/*
-+ * 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); \
-+}
-+
-+const char app_name[] = "unix_update";
-+
-+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.
-+ */
-diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c
---- /dev/null 2007-09-17 08:57:19.474470099 +0200
-+++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c 2007-09-18 10:15:19.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
++#ifdef HELPER_COMPILE
+
-+#include "md5.h"
-+#include "bigcrypt.h"
++int
++helper_verify_password(const char *name, const char *p, int nullok)
++{
++ struct passwd *pwd = NULL;
++ char *salt = NULL;
++ int retval;
+
-+#include "passverify.h"
++ retval = get_pwd_hash(name, &pwd, &salt);
++
++ if (pwd == NULL || salt == NULL) {
++ helper_log_err(LOG_WARNING, "check pass; user unknown");
++ retval = PAM_USER_UNKNOWN;
++ } else {
++ retval = verify_pwd_hash(p, salt, nullok);
++ }
++
++ if (salt) {
++ _pam_overwrite(salt);
++ _pam_drop(salt);
++ }
++
++ p = NULL; /* no longer needed here */
+
-+/* syslogging function for errors and other information */
++ return retval;
++}
+
+void
-+_log_err(int err, const char *format,...)
++helper_log_err(int err, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
-+ openlog(app_name, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
++ openlog(HELPER_COMPILE, 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);
++ /* 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);
-+ }
++ if (sig > 0) {
++ _exit(sig);
++ }
+}
+
+void
+setup_signals(void)
+{
-+ struct sigaction action; /* posix signal structure */
++ struct sigaction action; /* posix signal structure */
+
-+ /*
-+ * Setup signal handlers
-+ */
-+ (void) memset((void *) &action, 0, sizeof(action));
-+ action.sa_handler = su_sighandler;
++ /*
++ * Setup signal handlers
++ */
++ (void) memset((void *) &action, 0, sizeof(action));
++ action.sa_handler = su_sighandler;
+#ifdef SA_RESETHAND
-+ action.sa_flags = 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);
++ (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)
++char *
++getuidname(uid_t uid)
+{
-+ 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;
-+ }
++ struct passwd *pw;
++ static char username[256];
+
-+ /* clear up */
-+ if (offset > 0 && npass > 0) {
-+ memset(passwords[i], '\0', offset);
-+ }
++ pw = getpwuid(uid);
++ if (pw == NULL)
++ return NULL;
++
++ strncpy(username, pw->pw_name, sizeof(username));
++ username[sizeof(username) - 1] = '\0';
+
-+ return i;
++ return username;
+}
+
+int
-+_unix_verify_password(const char *name, const char *p, int nullok)
++read_passwords(int fd, int npass, char **passwords)
+{
-+ 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 */
++ 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;
++ }
+
-+ /* clean up */
-+ _pam_overwrite(pp);
-+ _pam_drop(pp);
++ /* clear up */
++ if (offset > 0 && npass > 0) {
++ memset(passwords[i], '\0', offset);
++ }
+
-+ return retval;
++ return i;
+}
+
-+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
++#endif
++/* ****************************************************************** *
++ * Copyright (c) Jan Rêkorajski 1999.
++ * Copyright (c) Andrew G. Morgan 1996-8.
++ * Copyright (c) Alex O. Yuriev, 1996.
++ * Copyright (c) Cristian Gafton 1996.
++ * Copyright (c) Red Hat, Inc. 1996, 2007, 2008.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
@@ -2535,48 +3258,418 @@ diff -up /dev/null Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c
+ */
diff -up Linux-PAM-0.99.8.1/modules/pam_unix/support.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/support.c
--- Linux-PAM-0.99.8.1/modules/pam_unix/support.c.update-helper 2007-02-06 17:06:45.000000000 +0100
-+++ Linux-PAM-0.99.8.1/modules/pam_unix/support.c 2007-09-18 12:05:31.000000000 +0200
-@@ -36,6 +36,8 @@
- #define SELINUX_ENABLED 0
- #endif
++++ Linux-PAM-0.99.8.1/modules/pam_unix/support.c 2008-01-08 16:17:32.000000000 +0100
+@@ -26,9 +26,8 @@
+ #include
+ #include
-+const char app_name[]="pam_unix";
-+
- /* this is a front-end for module-application conversations */
+-#include "md5.h"
+ #include "support.h"
+-#include "bigcrypt.h"
++#include "passverify.h"
+ #ifdef WITH_SELINUX
+ #include
+ #define SELINUX_ENABLED is_selinux_enabled()>0
+@@ -53,8 +52,8 @@ int _make_remark(pam_handle_t * pamh, un
+ * set the control flags for the UNIX module.
+ */
+
+-int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
+- const char **argv)
++int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds,
++ int argc, const char **argv)
+ {
+ unsigned int ctrl;
- int _make_remark(pam_handle_t * pamh, unsigned int ctrl,
-@@ -627,7 +629,7 @@ int _unix_verify_password(pam_handle_t *
- setreuid( save_uid, -1 );
- setreuid( -1, save_euid );
+@@ -110,6 +109,16 @@ int _set_ctrl(pam_handle_t *pamh, int fl
+ *remember = 400;
+ }
}
++ if (rounds != NULL) {
++ if (j == UNIX_ALGO_ROUNDS) {
++ *rounds = strtol(*argv + 7, NULL, 10);
++ if ((*rounds < 1000) || (*rounds == INT_MAX))
++ /* don't care about bogus values */
++ unset(UNIX_ALGO_ROUNDS, ctrl);
++ if (*rounds >= 10000000)
++ *rounds = 9999999;
++ }
++ }
+ }
+
+ ++argv; /* step to next argument */
+@@ -377,95 +386,6 @@ int _unix_comesfromsource(pam_handle_t *
+ }
+
+ /*
+- * _unix_blankpasswd() is a quick check for a blank password
+- *
+- * returns TRUE if user does not have a password
+- * - to avoid prompting for one in such cases (CG)
+- */
+-
+-int
+-_unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
+-{
+- struct passwd *pwd = NULL;
+- struct spwd *spwdent = NULL;
+- char *salt = NULL;
+- int retval;
+-
+- D(("called"));
+-
+- /*
+- * This function does not have to be too smart if something goes
+- * wrong, return FALSE and let this case to be treated somewhere
+- * else (CG)
+- */
+-
+- if (on(UNIX__NONULL, ctrl))
+- return 0; /* will fail but don't let on yet */
+-
+- /* UNIX passwords area */
+-
+- /* Get password file entry... */
+- pwd = pam_modutil_getpwnam (pamh, name);
+-
+- if (pwd != NULL) {
+- if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
+- { /* NIS+ */
+- uid_t save_euid, save_uid;
+-
+- save_euid = geteuid();
+- save_uid = getuid();
+- if (save_uid == pwd->pw_uid)
+- setreuid( save_euid, save_uid );
+- else {
+- setreuid( 0, -1 );
+- if (setreuid( -1, pwd->pw_uid ) == -1) {
+- setreuid( -1, 0 );
+- setreuid( 0, -1 );
+- if(setreuid( -1, pwd->pw_uid ) == -1)
+- /* Will fail elsewhere. */
+- return 0;
+- }
+- }
+-
+- spwdent = pam_modutil_getspnam (pamh, name);
+- if (save_uid == pwd->pw_uid)
+- setreuid( save_uid, save_euid );
+- else {
+- if (setreuid( -1, 0 ) == -1)
+- setreuid( save_uid, -1 );
+- setreuid( -1, save_euid );
+- }
- } else if (_unix_shadowed(pwd)) {
-+ } else if (_unix_shadowed(pwd) && !SELINUX_ENABLED) {
- /*
- * ...and shadow password file entry for this user,
- * if shadowing is enabled
-diff -up Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am
---- Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am.update-helper 2006-12-18 19:50:50.000000000 +0100
-+++ Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am 2007-09-18 12:54:44.000000000 +0200
-@@ -4,7 +4,7 @@
+- /*
+- * ...and shadow password file entry for this user,
+- * if shadowing is enabled
+- */
+- spwdent = pam_modutil_getspnam(pamh, name);
+- }
+- if (spwdent)
+- salt = x_strdup(spwdent->sp_pwdp);
+- else
+- salt = x_strdup(pwd->pw_passwd);
+- }
+- /* Does this user have a password? */
+- if (salt == NULL) {
+- retval = 0;
+- } else {
+- if (strlen(salt) == 0)
+- retval = 1;
+- else
+- retval = 0;
+- }
+-
+- /* tidy up */
+-
+- if (salt)
+- _pam_delete(salt);
+-
+- return retval;
+-}
+-
+-/*
+ * verify the password of a user
+ */
+
+@@ -519,7 +439,7 @@ static int _unix_run_helper_binary(pam_h
+ }
+ }
+
+- if (SELINUX_ENABLED && geteuid() == 0) {
++ if (geteuid() == 0) {
+ /* must set the real uid to 0 so the helper will not error
+ out if pam is called from setuid binary (su, sudo...) */
+ setuid(0);
+@@ -573,13 +493,66 @@ static int _unix_run_helper_binary(pam_h
+ return retval;
+ }
+
++/*
++ * _unix_blankpasswd() is a quick check for a blank password
++ *
++ * returns TRUE if user does not have a password
++ * - to avoid prompting for one in such cases (CG)
++ */
++
++int
++_unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
++{
++ struct passwd *pwd = NULL;
++ char *salt = NULL;
++ int retval;
++
++ D(("called"));
++
++ /*
++ * This function does not have to be too smart if something goes
++ * wrong, return FALSE and let this case to be treated somewhere
++ * else (CG)
++ */
++
++ if (on(UNIX__NONULL, ctrl))
++ return 0; /* will fail but don't let on yet */
++
++ /* UNIX passwords area */
++
++ retval = get_pwd_hash(pamh, name, &pwd, &salt);
++
++ if (retval == PAM_UNIX_RUN_HELPER) {
++ /* salt will not be set here so we can return immediately */
++ if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS)
++ return 1;
++ else
++ return 0;
++ }
++
++ /* Does this user have a password? */
++ if (salt == NULL) {
++ retval = 0;
++ } else {
++ if (strlen(salt) == 0)
++ retval = 1;
++ else
++ retval = 0;
++ }
++
++ /* tidy up */
++
++ if (salt)
++ _pam_delete(salt);
++
++ return retval;
++}
++
+ int _unix_verify_password(pam_handle_t * pamh, const char *name
+ ,const char *p, unsigned int ctrl)
+ {
+ struct passwd *pwd = NULL;
+- struct spwd *spwdent = NULL;
+ char *salt = NULL;
+- char *pp = NULL;
+ char *data_name;
+ int retval;
+
+@@ -597,48 +570,7 @@ int _unix_verify_password(pam_handle_t *
+
+ D(("locating user's record"));
+
+- /* UNIX passwords area */
+- pwd = pam_modutil_getpwnam (pamh, name); /* Get password file entry... */
+-
+- if (pwd != NULL) {
+- if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
+- { /* NIS+ */
+- uid_t save_euid, save_uid;
+-
+- save_euid = geteuid();
+- save_uid = getuid();
+- if (save_uid == pwd->pw_uid)
+- setreuid( save_euid, save_uid );
+- else {
+- setreuid( 0, -1 );
+- if (setreuid( -1, pwd->pw_uid ) == -1) {
+- setreuid( -1, 0 );
+- setreuid( 0, -1 );
+- if(setreuid( -1, pwd->pw_uid ) == -1)
+- return PAM_CRED_INSUFFICIENT;
+- }
+- }
+-
+- spwdent = pam_modutil_getspnam (pamh, name);
+- if (save_uid == pwd->pw_uid)
+- setreuid( save_uid, save_euid );
+- else {
+- if (setreuid( -1, 0 ) == -1)
+- setreuid( save_uid, -1 );
+- setreuid( -1, save_euid );
+- }
+- } else if (_unix_shadowed(pwd)) {
+- /*
+- * ...and shadow password file entry for this user,
+- * if shadowing is enabled
+- */
+- spwdent = pam_modutil_getspnam (pamh, name);
+- }
+- if (spwdent)
+- salt = x_strdup(spwdent->sp_pwdp);
+- else
+- salt = x_strdup(pwd->pw_passwd);
+- }
++ retval = get_pwd_hash(pamh, name, &pwd, &salt);
+
+ data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
+ if (data_name == NULL) {
+@@ -648,29 +580,22 @@ int _unix_verify_password(pam_handle_t *
+ strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
+ }
+
+- retval = PAM_SUCCESS;
+- if (pwd == NULL || salt == NULL || !strcmp(salt, "x") || ((salt[0] == '#') && (salt[1] == '#') && !strcmp(salt + 2, name))) {
+-
+- if (pwd != NULL && (geteuid() || SELINUX_ENABLED)) {
+- /* we are not root perhaps this is the reason? Run helper */
++ if (retval != PAM_SUCCESS) {
++ if (retval == PAM_UNIX_RUN_HELPER) {
+ D(("running helper binary"));
+ retval = _unix_run_helper_binary(pamh, p, ctrl, name);
+ } else {
+ D(("user's record unavailable"));
+ p = NULL;
+- if (pwd == NULL)
+- retval = PAM_USER_UNKNOWN;
+- else
+- retval = PAM_AUTHINFO_UNAVAIL;
+ if (on(UNIX_AUDIT, ctrl)) {
+ /* this might be a typo and the user has given a password
+ instead of a username. Careful with this. */
+- pam_syslog(pamh, LOG_ALERT,
++ pam_syslog(pamh, LOG_WARNING,
+ "check pass; user (%s) unknown", name);
+ } else {
+ name = NULL;
+ if (on(UNIX_DEBUG, ctrl) || pwd == NULL) {
+- pam_syslog(pamh, LOG_ALERT,
++ pam_syslog(pamh, LOG_WARNING,
+ "check pass; user unknown");
+ } else {
+ /* don't log failure as another pam module can succeed */
+@@ -679,48 +604,7 @@ int _unix_verify_password(pam_handle_t *
+ }
+ }
+ } else {
+- size_t salt_len = strlen(salt);
+- if (!salt_len) {
+- /* the stored password is NULL */
+- if (off(UNIX__NONULL, ctrl)) {/* this means we've succeeded */
+- D(("user has empty password - access granted"));
+- retval = PAM_SUCCESS;
+- } else {
+- D(("user has empty password - access denied"));
+- retval = PAM_AUTH_ERR;
+- }
+- } else if (!p || *salt == '*' || *salt == '!') {
+- retval = PAM_AUTH_ERR;
+- } else {
+- if (!strncmp(salt, "$1$", 3)) {
+- pp = Goodcrypt_md5(p, salt);
+- if (pp && strcmp(pp, salt) != 0) {
+- _pam_delete(pp);
+- pp = Brokencrypt_md5(p, salt);
+- }
+- } else if (*salt != '$' && salt_len >= 13) {
+- pp = bigcrypt(p, salt);
+- if (pp && salt_len == 13 && strlen(pp) > salt_len) {
+- _pam_overwrite(pp + salt_len);
+- }
+- } else {
+- /*
+- * Ok, we don't know the crypt algorithm, but maybe
+- * libcrypt nows about it? We should try it.
+- */
+- pp = x_strdup (crypt(p, salt));
+- }
+- p = NULL; /* no longer needed here */
+-
+- /* the moment of truth -- do we agree with the password? */
+- D(("comparing state of pp[%s] and salt[%s]", pp, salt));
+-
+- if (pp && strcmp(pp, salt) == 0) {
+- retval = PAM_SUCCESS;
+- } else {
+- retval = PAM_AUTH_ERR;
+- }
+- }
++ retval = verify_pwd_hash(p, salt, off(UNIX__NONULL, ctrl));
+ }
+
+ if (retval == PAM_SUCCESS) {
+@@ -809,8 +693,6 @@ cleanup:
+ _pam_delete(data_name);
+ if (salt)
+ _pam_delete(salt);
+- if (pp)
+- _pam_delete(pp);
- CLEANFILES = *~
+ D(("done [%d].", retval));
--EXTRA_DIST = README md5.c md5_crypt.c lckpwdf.-c $(MANS) CHANGELOG \
-+EXTRA_DIST = README md5.c md5_crypt.c lckpwdf.-c passupdate.c $(MANS) CHANGELOG \
- tst-pam_unix $(XMLS)
+@@ -971,26 +853,12 @@ int _unix_read_password(pam_handle_t * p
+ return PAM_SUCCESS;
+ }
- man_MANS = pam_unix.8 unix_chkpwd.8
-@@ -16,7 +16,8 @@ securelibdir = $(SECUREDIR)
+-int _unix_shadowed(const struct passwd *pwd)
+-{
+- if (pwd != NULL) {
+- if (strcmp(pwd->pw_passwd, "x") == 0) {
+- return 1;
+- }
+- if ((pwd->pw_passwd[0] == '#') &&
+- (pwd->pw_passwd[1] == '#') &&
+- (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) {
+- return 1;
+- }
+- }
+- return 0;
+-}
+-
+ /* ****************************************************************** *
+ * Copyright (c) Jan R�korajski 1999.
+ * Copyright (c) Andrew G. Morgan 1996-8.
+ * Copyright (c) Alex O. Yuriev, 1996.
+ * Copyright (c) Cristian Gafton 1996.
++ * 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
+diff -up Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am
+--- Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am.update-helper 2006-12-18 19:50:50.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am 2008-01-08 16:17:32.000000000 +0100
+@@ -16,7 +16,9 @@ securelibdir = $(SECUREDIR)
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\"
++ -DUPDATE_HELPER=\"$(sbindir)/unix_update\" \
++ -DPATH_RANDOMDEV=\"/dev/urandom\"
if HAVE_LIBSELINUX
AM_CFLAGS += -D"WITH_SELINUX"
-@@ -34,9 +35,9 @@ endif
+@@ -25,33 +27,40 @@ if HAVE_LIBCRACK
+ AM_CFLAGS += -D"USE_CRACKLIB"
+ endif
+
+-pam_unix_la_LDFLAGS = -no-undefined -avoid-version -module \
+- @LIBCRACK@ @LIBNSL@ -L$(top_builddir)/libpam -lpam \
+- @LIBCRYPT@ @LIBSELINUX@
++pam_unix_la_LDFLAGS = -no-undefined -avoid-version -module
+ if HAVE_VERSIONING
+ pam_unix_la_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
+ endif
++pam_unix_la_LIBADD = @LIBCRACK@ @LIBNSL@ -L$(top_builddir)/libpam -lpam \
++ @LIBCRYPT@ @LIBSELINUX@
securelib_LTLIBRARIES = pam_unix.la
@@ -2588,21 +3681,43 @@ diff -up Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am.update-helper Linux-PAM
noinst_PROGRAMS = bigcrypt
-@@ -48,11 +49,16 @@ bigcrypt_SOURCES = bigcrypt.c bigcrypt_m
+ pam_unix_la_SOURCES = bigcrypt.c pam_unix_acct.c \
+ pam_unix_auth.c pam_unix_passwd.c pam_unix_sess.c support.c \
+- yppasswd_xdr.c md5_good.c md5_broken.c
++ passverify.c yppasswd_xdr.c md5_good.c md5_broken.c
+
+ bigcrypt_SOURCES = bigcrypt.c bigcrypt_main.c
bigcrypt_CFLAGS = $(AM_CFLAGS)
- bigcrypt_LDFLAGS = @LIBCRYPT@
+-bigcrypt_LDFLAGS = @LIBCRYPT@
++bigcrypt_LDADD = @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@
-+
+-unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@
+-unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@ -L$(top_builddir)/libpam -lpam \
+- @LIBCRYPT@ @LIBSELINUX@
++unix_chkpwd_SOURCES = unix_chkpwd.c md5_good.c md5_broken.c bigcrypt.c \
++ passverify.c
++unix_chkpwd_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_chkpwd\"
++unix_chkpwd_LDFLAGS = @PIE_LDFLAGS@
++unix_chkpwd_LDADD = @LIBCRYPT@ @LIBSELINUX@
++
++unix_update_SOURCES = unix_update.c md5_good.c md5_broken.c bigcrypt.c \
++ passverify.c
++unix_update_CFLAGS = $(AM_CFLAGS) @PIE_CFLAGS@ -DHELPER_COMPILE=\"unix_update\"
++unix_update_LDFLAGS = @PIE_LDFLAGS@
++unix_update_LDADD = @LIBCRYPT@ @LIBSELINUX@
+
if ENABLE_REGENERATE_MAN
noinst_DATA = README
- README: pam_unix.8.xml
+diff -up Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c.update-helper Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c
+--- Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c.update-helper 2006-12-20 15:52:55.000000000 +0100
++++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_auth.c 2008-01-07 16:38:50.000000000 +0100
+@@ -111,7 +111,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_h
+
+ D(("called."));
+
+- ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);
++ ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);
+
+ /* Get a few bytes so we can pass our return value to
+ pam_sm_setcred(). */
diff --git a/pam.spec b/pam.spec
index ae2d4cf..c9240bc 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.8.1
-Release: 10%{?dist}
+Release: 17%{?dist}
# The library is BSD licensed with option to relicense as GPLv2+ - this option is redundant
# as the BSD license allows that anyway. pam_timestamp and pam_console modules are GPLv2+,
# pam_rhosts_auth module is BSD with advertising
@@ -32,8 +32,7 @@ Patch2: db-4.6.18-glibc.patch
Patch4: pam-0.99.8.1-dbpam.patch
Patch5: pam-0.99.8.1-audit-no-log.patch
Patch24: pam-0.99.8.1-unix-update-helper.patch
-Patch25: pam-0.99.7.1-unix-hpux-aging.patch
-Patch26: pam-0.99.8.1-unix-blankpass.patch
+Patch25: pam-0.99.8.1-unix-hpux-aging.patch
Patch31: pam-0.99.3.0-cracklib-try-first-pass.patch
Patch32: pam-0.99.3.0-tally-fail-close.patch
Patch40: pam-0.99.7.1-namespace-temp-logon.patch
@@ -44,11 +43,16 @@ Patch44: pam-0.99.7.1-namespace-homedir.patch
Patch45: pam-0.99.8.1-selinux-permit.patch
Patch46: pam-0.99.8.1-succif-in-operator.patch
Patch47: pam-0.99.8.1-xauth-no-free.patch
+Patch48: pam-0.99.8.1-substack.patch
+Patch49: pam-0.99.8.1-tty-audit.patch
+Patch50: pam-0.99.8.1-tty-audit2.patch
+Patch51: pam-0.99.8.1-audit-failed.patch
+Patch52: pam-0.99.8.1-setkeycreatecon.patch
+Patch53: pam-0.99.8.1-sepermit-kill-user.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: cracklib, cracklib-dicts >= 2.8
-Requires(pre): grep, coreutils
-Requires(post): mktemp, sed, coreutils, /sbin/ldconfig
+Requires(post): coreutils, /sbin/ldconfig
BuildRequires: autoconf >= 2.60
BuildRequires: automake, libtool
BuildRequires: bison, flex, sed
@@ -71,6 +75,8 @@ URL: http://www.us.kernel.org/pub/linux/libs/pam/index.html
# We internalize libdb to get a non-threaded copy, but we should at least try
# to coexist with the system's copy of libdb, which will be used to make the
# files for use by pam_userdb (either by db_load or Perl's DB_File module).
+# The non-threaded db4 is necessary so we do not break single threaded
+# services when they call pam_userdb.so module.
Conflicts: db4 >= %{db_conflicting_version}
%description
@@ -92,9 +98,6 @@ PAM-aware applications and modules for use with PAM.
%prep
%setup -q -n Linux-PAM-%{version} -a 2 -a 4
-cp %{SOURCE5} .
-cp %{SOURCE6} .
-cp %{SOURCE7} .
%patch1 -p1 -b .redhat-modules
pushd db-%{db_version}
@@ -104,7 +107,6 @@ popd
%patch5 -p1 -b .no-log
%patch24 -p1 -b .update-helper
%patch25 -p1 -b .unix-hpux-aging
-%patch26 -p1 -b .blankpass
%patch31 -p1 -b .try-first-pass
%patch32 -p1 -b .fail-close
%patch40 -p1 -b .temp-logon
@@ -115,6 +117,12 @@ popd
%patch45 -p1 -b .permit
%patch46 -p1 -b .in-operator
%patch47 -p1 -b .no-free
+%patch48 -p0 -b .substack
+%patch49 -p1 -b .tty-audit
+%patch50 -p1 -b .tty-audit2
+%patch51 -p1 -b .audit-failed
+%patch52 -p1 -b .setkeycreatecon
+%patch53 -p1 -b .kill-user
autoreconf
@@ -156,6 +164,7 @@ LDFLAGS=-L${topdir}/%{_lib} ; export LDFLAGS
--enable-isadir=../../%{_lib}/security \
--with-db-uniquename=_pam
make
+# we do not use _smp_mflags because the build of sources in yacc/flex fails
%install
rm -rf $RPM_BUILD_ROOT
@@ -175,20 +184,42 @@ rm -f $RPM_BUILD_ROOT%{_sysconfdir}/environment
# Install default configuration files.
install -d -m 755 $RPM_BUILD_ROOT%{_sysconfdir}/pam.d
-install -m 644 other.pamd $RPM_BUILD_ROOT%{_sysconfdir}/pam.d/other
-install -m 644 system-auth.pamd $RPM_BUILD_ROOT%{_sysconfdir}/pam.d/system-auth
-install -m 644 config-util.pamd $RPM_BUILD_ROOT%{_sysconfdir}/pam.d/config-util
+install -m 644 %{SOURCE5} $RPM_BUILD_ROOT%{_sysconfdir}/pam.d/other
+install -m 644 %{SOURCE6} $RPM_BUILD_ROOT%{_sysconfdir}/pam.d/system-auth
+install -m 644 %{SOURCE7} $RPM_BUILD_ROOT%{_sysconfdir}/pam.d/config-util
install -m 600 /dev/null $RPM_BUILD_ROOT%{_sysconfdir}/security/opasswd
install -d -m 755 $RPM_BUILD_ROOT/var/log
install -m 600 /dev/null $RPM_BUILD_ROOT/var/log/faillog
install -m 600 /dev/null $RPM_BUILD_ROOT/var/log/tallylog
-# Forcibly strip binaries.
-strip $RPM_BUILD_ROOT%{_sbindir}/* ||:
-
# Install man pages.
install -m 644 %{SOURCE9} %{SOURCE10} $RPM_BUILD_ROOT%{_mandir}/man5/
+for phase in auth acct passwd session ; do
+ ln -sf pam_unix.so $RPM_BUILD_ROOT/%{_lib}/security/pam_unix_${phase}.so
+done
+
+# Remove .la files and make new .so links -- this depends on the value
+# of _libdir not changing, and *not* being /usr/lib.
+install -d -m 755 $RPM_BUILD_ROOT%{_libdir}
+for lib in libpam libpamc libpam_misc ; do
+pushd $RPM_BUILD_ROOT%{_libdir}
+ln -sf ../../%{_lib}/${lib}.so.*.* ${lib}.so
+popd
+rm -f $RPM_BUILD_ROOT/%{_lib}/${lib}.so
+rm -f $RPM_BUILD_ROOT/%{_lib}/${lib}.la
+done
+rm -f $RPM_BUILD_ROOT/%{_lib}/security/*.la
+
+# Duplicate doc file sets.
+rm -fr $RPM_BUILD_ROOT/usr/share/doc/pam
+
+# Create /lib/security in case it isn't the same as /%{_lib}/security.
+install -m755 -d $RPM_BUILD_ROOT/lib/security
+
+%find_lang Linux-PAM
+
+%check
# Make sure every module subdirectory gave us a module. Yes, this is hackish.
for dir in modules/pam_* ; do
if [ -d ${dir} ] ; then
@@ -204,7 +235,7 @@ done
/sbin/ldconfig -n $RPM_BUILD_ROOT/%{_lib}
for module in $RPM_BUILD_ROOT/%{_lib}/security/pam*.so ; do
if ! env LD_LIBRARY_PATH=$RPM_BUILD_ROOT/%{_lib} \
- $RPM_SOURCE_DIR/dlopen.sh -ldl -lpam -L$RPM_BUILD_ROOT/%{_lib} ${module} ; then
+ %{SOURCE8} -ldl -lpam -L$RPM_BUILD_ROOT/%{_libdir} ${module} ; then
echo ERROR module: ${module} cannot be loaded.
exit 1
fi
@@ -212,84 +243,17 @@ for module in $RPM_BUILD_ROOT/%{_lib}/security/pam*.so ; do
# libraries, which if loaded in a non-threaded application, can cause Very
# Bad Things to happen.
if env LD_LIBRARY_PATH=$RPM_BUILD_ROOT/%{_lib} \
- LD_PRELOAD=$RPM_BUILD_ROOT/%{_lib}/libpam.so ldd -r ${module} | fgrep -q libpthread ; then
+ LD_PRELOAD=$RPM_BUILD_ROOT%{_libdir}/libpam.so ldd -r ${module} | fgrep -q libpthread ; then
echo ERROR module: ${module} pulls threading libraries.
exit 1
fi
done
-for phase in auth acct passwd session ; do
- ln -sf pam_unix.so $RPM_BUILD_ROOT/%{_lib}/security/pam_unix_${phase}.so
-done
-
-# Remove .la files and make new .so links -- this depends on the value
-# of _libdir not changing, and *not* being /usr/lib.
-install -d -m 755 $RPM_BUILD_ROOT%{_libdir}
-for lib in libpam libpamc libpam_misc ; do
-pushd $RPM_BUILD_ROOT%{_libdir}
-ln -sf ../../%{_lib}/${lib}.so.*.* ${lib}.so
-popd
-rm -f $RPM_BUILD_ROOT/%{_lib}/${lib}.so
-rm -f $RPM_BUILD_ROOT/%{_lib}/${lib}.la
-done
-rm -f $RPM_BUILD_ROOT/%{_lib}/security/*.la
-
-# Duplicate doc file sets.
-rm -fr $RPM_BUILD_ROOT/usr/share/doc/pam
-
-# Create /lib/security in case it isn't the same as /%{_lib}/security.
-install -m755 -d $RPM_BUILD_ROOT/lib/security
-
-%find_lang Linux-PAM
-
%clean
rm -rf $RPM_BUILD_ROOT
-%pre
-# Figure whether or not we're using shadow/md5 passwords if we're upgrading.
-if [ -f %{_sysconfdir}/pam.d/other ] ; then
- USEMD5=
- if [ -f /etc/sysconfig/authconfig ] ; then
- . /etc/sysconfig/authconfig
- fi
- if [ -z "$USEMD5" ] ; then
- if [ -f /etc/shadow ] ; then
- passwdfiles="/etc/passwd /etc/shadow"
- else
- passwdfiles="/etc/passwd"
- fi
- if cut -f2 -d: $passwdfiles | grep -q '^\$1\$' ; then
- echo USEMD5=yes >> /etc/sysconfig/authconfig
- USEMD5=yes
- else
- echo USEMD5=no >> /etc/sysconfig/authconfig
- USEMD5=no
- fi
- fi
-fi
-exit 0
-
%post
/sbin/ldconfig
-if [ ! -f /etc/shadow ] ; then
- tmp=`mktemp /etc/pam.d/pam-post.XXXXXX`
- if [ -n "$tmp" ] ; then
- sed 's| shadow||g' /etc/pam.d/system-auth > $tmp && \
- cat $tmp > /etc/pam.d/system-auth
- rm -f $tmp
- fi
-fi
-if [ -f /etc/sysconfig/authconfig ] ; then
- . /etc/sysconfig/authconfig
-fi
-if [ "$USEMD5" = "no" ] ; then
- tmp=`mktemp /etc/pam.d/pam-post.XXXXXX`
- if [ -n "$tmp" ] ; then
- sed 's| md5||g' /etc/pam.d/system-auth > $tmp && \
- cat $tmp > /etc/pam.d/system-auth
- rm -f $tmp
- fi
-fi
if [ ! -a /var/log/faillog ] ; then
install -m 600 /dev/null /var/log/faillog
fi
@@ -363,6 +327,7 @@ fi
/%{_lib}/security/pam_tally2.so
/%{_lib}/security/pam_time.so
/%{_lib}/security/pam_timestamp.so
+/%{_lib}/security/pam_tty_audit.so
/%{_lib}/security/pam_umask.so
/%{_lib}/security/pam_unix.so
/%{_lib}/security/pam_unix_acct.so
@@ -391,6 +356,7 @@ fi
%dir %{_sysconfdir}/security/console.perms.d
%config %{_sysconfdir}/security/console.perms.d/50-default.perms
%dir /var/run/console
+%dir /var/run/sepermit
%ghost %verify(not md5 size mtime) /var/log/faillog
%ghost %verify(not md5 size mtime) /var/log/tallylog
%{_mandir}/man5/*
@@ -407,6 +373,31 @@ fi
%doc doc/adg/*.txt doc/adg/html
%changelog
+* Mon Jan 28 2008 Tomas Mraz 0.99.8.1-17
+- test for setkeycreatecon correctly
+- add exclusive login mode of operation to pam_selinux_permit (original
+ patch by Dan Walsh)
+
+* Tue Jan 22 2008 Tomas Mraz 0.99.8.1-16
+- add auditing to pam_access, pam_limits, and pam_time
+- moved sanity testing code to check script
+
+* Mon Jan 14 2008 Tomas Mraz 0.99.8.1-15
+- merge review fixes (#226228)
+
+* Wed Jan 8 2008 Tomas Mraz 0.99.8.1-14
+- support for sha256 and sha512 password hashes
+- account expiry checks moved to unix_chkpwd helper
+
+* Wed Jan 2 2008 Tomas Mraz 0.99.8.1-13
+- wildcard match support in pam_tty_audit (by Miloslav Trmač)
+
+* Thu Nov 29 2007 Tomas Mraz 0.99.8.1-12
+- add pam_tty_audit module (#244352) - written by Miloslav Trmač
+
+* Wed Nov 7 2007 Tomas Mraz 0.99.8.1-11
+- add substack support
+
* Tue Sep 25 2007 Tomas Mraz 0.99.8.1-10
- update db4 to 4.6.19 (#274661)