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