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; idebug = 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 @@ ++ ++ ++ ++
++ ++ ++ ++ ++ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" ++ href="pam_tty_audit.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_tty_audit-name"]/*)'/> ++ ++ ++ ++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
++ ++
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)