--- /dev/null 2007-07-08 21:11:04.052436262 +0200 +++ Linux-PAM-0.99.8.1/modules/pam_unix/passupdate.c 2007-07-23 13:40:56.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.8.1/modules/pam_unix/pam_unix_acct.c.update-helper 2006-06-27 10:38:14.000000000 +0200 +++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_acct.c 2007-07-23 13:40:56.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.8.1/modules/pam_unix/pam_unix_passwd.c.update-helper 2007-04-30 12:47:30.000000000 +0200 +++ Linux-PAM-0.99.8.1/modules/pam_unix/pam_unix_passwd.c 2007-07-23 13:54:55.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]; @@ -354,393 +336,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))) { - char *sptr; - 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; - } - 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) @@ -769,7 +364,7 @@ /* Unlock passwd file to avoid deadlock */ #ifdef USE_LCKPWDF - ulckpwdf(); + unlock_pwdf(); #endif unlocked = 1; @@ -832,33 +427,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"); @@ -870,7 +454,7 @@ done: #ifdef USE_LCKPWDF - ulckpwdf(); + unlock_pwdf(); #endif return retval; @@ -891,13 +475,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; @@ -1020,7 +608,7 @@ int argc, const char **argv) { unsigned int ctrl, lctrl; - int retval, i; + int retval; int remember = -1; /* */ @@ -1240,49 +828,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; } @@ -1326,7 +905,7 @@ "out of memory for password"); pass_new = pass_old = NULL; /* tidy up */ #ifdef USE_LCKPWDF - ulckpwdf(); + unlock_pwdf(); #endif return PAM_BUF_ERR; } @@ -1349,7 +928,7 @@ 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-07-08 21:11:04.052436262 +0200 +++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.h 2007-07-23 13:40:56.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.8.1/modules/pam_unix/unix_chkpwd.c.update-helper 2007-03-12 15:35:14.000000000 +0100 +++ Linux-PAM-0.99.8.1/modules/pam_unix/unix_chkpwd.c 2007-07-23 13:40:56.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-07-08 21:11:04.052436262 +0200 +++ Linux-PAM-0.99.8.1/modules/pam_unix/unix_update.c 2007-07-23 13:40:56.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-07-08 21:11:04.052436262 +0200 +++ Linux-PAM-0.99.8.1/modules/pam_unix/passverify.c 2007-07-23 13:40:56.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.8.1/modules/pam_unix/Makefile.am.update-helper 2006-12-18 19:50:50.000000000 +0100 +++ Linux-PAM-0.99.8.1/modules/pam_unix/Makefile.am 2007-07-23 13:40:56.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