From 09b44afcb6ee0ea15e2baf1a6422197159b641c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Mr=C3=A1z?= Date: Mon, 4 Jun 2007 14:22:15 +0000 Subject: [PATCH] - 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) --- pam-0.99.6.2-namespace-docfix.patch | 18 + pam-0.99.6.2-namespace-temp-logon.patch | 433 +++ pam-0.99.7.1-unix-bigcrypt.patch | 32 +- ...atch => pam-0.99.7.1-unix-hpux-aging.patch | 39 +- pam-0.99.7.1-unix-update-helper.patch | 2548 +++++++++++++++++ pam.spec | 18 +- 6 files changed, 3050 insertions(+), 38 deletions(-) create mode 100644 pam-0.99.6.2-namespace-docfix.patch create mode 100644 pam-0.99.6.2-namespace-temp-logon.patch rename pam-0.78-unix-hpux-aging.patch => pam-0.99.7.1-unix-hpux-aging.patch (63%) create mode 100644 pam-0.99.7.1-unix-update-helper.patch 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.78-unix-hpux-aging.patch b/pam-0.99.7.1-unix-hpux-aging.patch similarity index 63% rename from pam-0.78-unix-hpux-aging.patch rename to pam-0.99.7.1-unix-hpux-aging.patch index 7dbd34d..11d5274 100644 --- a/pam-0.78-unix-hpux-aging.patch +++ b/pam-0.99.7.1-unix-hpux-aging.patch @@ -6,10 +6,10 @@ o For non-extensible-style hashes, strip off anything after the 13th character 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 @@ + +--- 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; } @@ -31,24 +31,25 @@ o For non-extensible-style hashes, strip off anything after the 13th character 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; +@@ -679,7 +694,9 @@ + } } } else { -- int salt_len = strlen(salt); -+ int salt_len; +- 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.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); +--- 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) ++static void ++strip_hpux_aging(char *p) +{ + const char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" @@ -63,14 +64,14 @@ o For non-extensible-style hashes, strip off anything after the 13th character + } +} + - static int _unix_verify_password(const char *name, const char *p, int nullok) + int + _unix_verify_password(const char *name, const char *p, int nullok) { - struct passwd *pwd = NULL; -@@ -159,6 +174,7 @@ - return retval; +@@ -194,6 +210,7 @@ + return PAM_USER_UNKNOWN; } + strip_hpux_aging(salt); salt_len = strlen(salt); - if (salt_len == 0) - return (nullok == 0) ? UNIX_FAILED : UNIX_PASSED; + 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)