]> granicus.if.org Git - linux-pam/blobdiff - modules/pam_unix/support.c
Relevant BUGIDs: debian#514437 rhbz#487216
[linux-pam] / modules / pam_unix / support.c
index 256e49998f65fcfc400db3ac02d003aee4b35d63..faec20dc4c879e92c5163eb00e947e2f8767c802 100644 (file)
-/* 
- * $Id$
- *
+/*
  * Copyright information at end of file.
  */
 
-#define _BSD_SOURCE
+#include "config.h"
 
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdarg.h>
+#include <stdio.h>
 #include <string.h>
 #include <malloc.h>
 #include <pwd.h>
 #include <shadow.h>
 #include <limits.h>
 #include <utmp.h>
+#include <errno.h>
+#include <signal.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <sys/resource.h>
+#include <rpcsvc/ypclnt.h>
 
 #include <security/_pam_macros.h>
 #include <security/pam_modules.h>
+#include <security/pam_ext.h>
+#include <security/pam_modutil.h>
 
-#include "md5.h"
 #include "support.h"
-
-extern char *crypt(const char *key, const char *salt);
-extern char *bigcrypt(const char *key, const char *salt);
-
-/* syslogging function for errors and other information */
-
-void _log_err(int err, const char *format,...)
-{
-       va_list args;
-
-       va_start(args, format);
-       openlog("PAM_unix", LOG_CONS | LOG_PID, LOG_AUTH);
-       vsyslog(err, format, args);
-       va_end(args);
-       closelog();
-}
+#include "passverify.h"
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#define SELINUX_ENABLED is_selinux_enabled()>0
+#else
+#define SELINUX_ENABLED 0
+#endif
 
 /* this is a front-end for module-application conversations */
 
-static int converse(pam_handle_t * pamh, int ctrl, int nargs
-                   ,struct pam_message **message
-                   ,struct pam_response **response)
-{
-       int retval;
-       struct pam_conv *conv;
-
-       D(("begin to converse"));
-
-       retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
-       if (retval == PAM_SUCCESS) {
-
-               retval = conv->conv(nargs, (const struct pam_message **) message
-                                   ,response, conv->appdata_ptr);
-
-               D(("returned from application's conversation function"));
-
-               if (retval != PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) {
-                       _log_err(LOG_DEBUG, "conversation failure [%s]"
-                                ,pam_strerror(pamh, retval));
-               }
-       } else if (retval != PAM_CONV_AGAIN) {
-               _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
-                        ,pam_strerror(pamh, retval));
-       }
-       D(("ready to return from module conversation"));
-
-       return retval;          /* propagate error status */
-}
-
-int _make_remark(pam_handle_t * pamh, unsigned int ctrl
-                      ,int type, const char *text)
+int _make_remark(pam_handle_t * pamh, unsigned int ctrl,
+                   int type, const char *text)
 {
        int retval = PAM_SUCCESS;
 
        if (off(UNIX__QUIET, ctrl)) {
-               struct pam_message *pmsg[1], msg[1];
-               struct pam_response *resp;
-
-               pmsg[0] = &msg[0];
-               msg[0].msg = text;
-               msg[0].msg_style = type;
-
-               resp = NULL;
-               retval = converse(pamh, ctrl, 1, pmsg, &resp);
-
-               if (resp) {
-                       _pam_drop_reply(resp, 1);
-               }
+               retval = pam_prompt(pamh, type, NULL, "%s", text);
        }
        return retval;
 }
 
-  /*
-   * Beacause getlogin() is fucked in a weird way, and
-   * sometimes it just don't work, we reimplement it here.
-   */
-char *PAM_getlogin(void)
-{
-       struct utmp *ut, line;
-       char *curr_tty, *retval;
-       static char curr_user[UT_NAMESIZE + 4];
-
-       retval = NULL;
-
-       curr_tty = ttyname(0);
-       if (curr_tty != NULL) {
-               D(("PAM_getlogin ttyname: %s", curr_tty));
-               curr_tty += 5;
-               setutent();
-               strncpy(line.ut_line, curr_tty, sizeof line.ut_line);
-               if ((ut = getutline(&line)) != NULL) {
-                       strncpy(curr_user, ut->ut_user, UT_NAMESIZE);
-                       retval = curr_user;
-               }
-               endutent();
-       }
-       D(("PAM_getlogin retval: %s", retval));
-
-       return retval;
-}
-
 /*
  * set the control flags for the UNIX module.
  */
 
-int _set_ctrl(int flags, int *remember, int argc, const char **argv)
+int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds,
+             int argc, const char **argv)
 {
        unsigned int ctrl;
 
@@ -148,10 +75,6 @@ int _set_ctrl(int flags, int *remember, int argc, const char **argv)
                D(("PRELIM_CHECK"));
                set(UNIX__PRELIM, ctrl);
        }
-       if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
-               D(("DISALLOW_NULL_AUTHTOK"));
-               set(UNIX__NONULL, ctrl);
-       }
        if (flags & PAM_SILENT) {
                D(("SILENT"));
                set(UNIX__QUIET, ctrl);
@@ -171,7 +94,8 @@ int _set_ctrl(int flags, int *remember, int argc, const char **argv)
                }
 
                if (j >= UNIX_CTRLS_) {
-                       _log_err(LOG_ERR, "unrecognized option [%s]", *argv);
+                       pam_syslog(pamh, LOG_ERR,
+                                "unrecognized option [%s]", *argv);
                } else {
                        ctrl &= unix_args[j].mask;      /* for turning things off */
                        ctrl |= unix_args[j].flag;      /* for turning things on  */
@@ -179,17 +103,44 @@ int _set_ctrl(int flags, int *remember, int argc, const char **argv)
                        if (remember != NULL) {
                                if (j == UNIX_REMEMBER_PASSWD) {
                                        *remember = strtol(*argv + 9, NULL, 10);
-                                       if ((*remember == LONG_MIN) || (*remember == LONG_MAX))
+                                       if ((*remember == INT_MIN) || (*remember == INT_MAX))
                                                *remember = -1;
                                        if (*remember > 400)
                                                *remember = 400;
                                }
                        }
+                       if (rounds != NULL && j == UNIX_ALGO_ROUNDS)
+                               *rounds = strtol(*argv + 7, NULL, 10);
                }
 
                ++argv;         /* step to next argument */
        }
 
+       if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
+               D(("DISALLOW_NULL_AUTHTOK"));
+               set(UNIX__NONULL, ctrl);
+       }
+       
+       /* Set default rounds for blowfish */
+       if (on(UNIX_BLOWFISH_PASS, ctrl) && off(UNIX_ALGO_ROUNDS, ctrl)) {
+               *rounds = 5;
+               set(UNIX_ALGO_ROUNDS, ctrl);
+       }
+       
+       /* Enforce sane "rounds" values */
+       if (on(UNIX_ALGO_ROUNDS, ctrl)) {
+               if (on(UNIX_BLOWFISH_PASS, ctrl)) {
+                       if (*rounds < 4 || *rounds > 31)
+                               *rounds = 5;
+               } else if (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl)) {
+                       if ((*rounds < 1000) || (*rounds == INT_MAX))
+                               /* don't care about bogus values */
+                               unset(UNIX_ALGO_ROUNDS, ctrl);
+                       if (*rounds >= 10000000)
+                               *rounds = 9999999;
+               }
+       }
+
        /* auditing is a more sensitive version of debug */
 
        if (on(UNIX_AUDIT, ctrl)) {
@@ -201,7 +152,7 @@ int _set_ctrl(int flags, int *remember, int argc, const char **argv)
        return ctrl;
 }
 
-static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
+static void _cleanup(pam_handle_t * pamh UNUSED, void *x, int error_status UNUSED)
 {
        _pam_delete(x);
 }
@@ -221,7 +172,8 @@ static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
 struct _pam_failed_auth {
        char *user;             /* user that's failed to be authenticated */
        char *name;             /* attempt from user with name */
-       int id;                 /* uid of name'd user */
+       int uid;                /* uid of calling user */
+       int euid;               /* euid of calling process */
        int count;              /* number of failures so far */
 };
 
@@ -232,7 +184,10 @@ struct _pam_failed_auth {
 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
 {
        int quiet;
-       const char *service = NULL;
+       const void *service = NULL;
+       const void *ruser = NULL;
+       const void *rhost = NULL;
+       const void *tty = NULL;
        struct _pam_failed_auth *failure;
 
        D(("called"));
@@ -247,23 +202,33 @@ static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
 
                        /* log the number of authentication failures */
                        if (failure->count > 1) {
-                               (void) pam_get_item(pamh, PAM_SERVICE
-                                             ,(const void **) &service);
-                               _log_err(LOG_NOTICE
-                                        ,"%d more authentication failure%s; %s(uid=%d) -> "
-                                        "%s for %s service"
-                                        ,failure->count - 1, failure->count == 2 ? "" : "s"
-                                        ,failure->name
-                                        ,failure->id
-                                        ,failure->user
-                               ,service == NULL ? "**unknown**" : service
-                                   );
+                               (void) pam_get_item(pamh, PAM_SERVICE,
+                                                   &service);
+                               (void) pam_get_item(pamh, PAM_RUSER,
+                                                   &ruser);
+                               (void) pam_get_item(pamh, PAM_RHOST,
+                                                   &rhost);
+                               (void) pam_get_item(pamh, PAM_TTY,
+                                                   &tty);
+                               pam_syslog(pamh, LOG_NOTICE,
+                                        "%d more authentication failure%s; "
+                                        "logname=%s uid=%d euid=%d "
+                                        "tty=%s ruser=%s rhost=%s "
+                                        "%s%s",
+                                        failure->count - 1, failure->count == 2 ? "" : "s",
+                                        failure->name, failure->uid, failure->euid,
+                                        tty ? (const char *)tty : "", ruser ? (const char *)ruser : "",
+                                        rhost ? (const char *)rhost : "",
+                                        (failure->user && failure->user[0] != '\0')
+                                         ? " user=" : "", failure->user
+                               );
+
                                if (failure->count > UNIX_MAX_RETRIES) {
-                                       _log_err(LOG_ALERT
-                                                ,"service(%s) ignoring max retries; %d > %d"
-                                                ,service == NULL ? "**unknown**" : service
-                                                ,failure->count
-                                                ,UNIX_MAX_RETRIES);
+                                       pam_syslog(pamh, LOG_ALERT,
+                                                "service(%s) ignoring max retries; %d > %d",
+                                                service == NULL ? "**unknown**" : (const char *)service,
+                                                failure->count,
+                                                UNIX_MAX_RETRIES);
                                }
                        }
                }
@@ -274,89 +239,162 @@ static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
 }
 
 /*
- * _unix_blankpasswd() is a quick check for a blank password
- *
- * returns TRUE if user does not have a password
- * - to avoid prompting for one in such cases (CG)
+ * _unix_getpwnam() searches only /etc/passwd and NIS to find user information
  */
+static void _unix_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED)
+{
+       free(data);
+}
 
-int _unix_blankpasswd(unsigned int ctrl, const char *name)
+int _unix_getpwnam(pam_handle_t *pamh, const char *name,
+                  int files, int nis, struct passwd **ret)
 {
-       struct passwd *pwd = NULL;
-       struct spwd *spwdent = NULL;
-       char *salt = NULL;
-       int retval;
+       FILE *passwd;
+       char buf[16384];
+       int matched = 0, buflen;
+       char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p;
+
+       memset(buf, 0, sizeof(buf));
+
+       if (!matched && files) {
+               int userlen = strlen(name);
+               passwd = fopen("/etc/passwd", "r");
+               if (passwd != NULL) {
+                       while (fgets(buf, sizeof(buf), passwd) != NULL) {
+                               if ((buf[userlen] == ':') &&
+                                   (strncmp(name, buf, userlen) == 0)) {
+                                       p = buf + strlen(buf) - 1;
+                                       while (isspace(*p) && (p >= buf)) {
+                                               *p-- = '\0';
+                                       }
+                                       matched = 1;
+                                       break;
+                               }
+                       }
+                       fclose(passwd);
+               }
+       }
 
-       D(("called"));
+       if (!matched && nis) {
+               char *userinfo = NULL, *domain = NULL;
+               int len = 0, i;
+               len = yp_get_default_domain(&domain);
+               if (len == YPERR_SUCCESS) {
+                       len = yp_bind(domain);
+               }
+               if (len == YPERR_SUCCESS) {
+                       i = yp_match(domain, "passwd.byname", name,
+                                    strlen(name), &userinfo, &len);
+                       yp_unbind(domain);
+                       if ((i == YPERR_SUCCESS) && ((size_t)len < sizeof(buf))) {
+                               strncpy(buf, userinfo, sizeof(buf) - 1);
+                               buf[sizeof(buf) - 1] = '\0';
+                               matched = 1;
+                       }
+               }
+       }
 
-       /*
-        * This function does not have to be too smart if something goes
-        * wrong, return FALSE and let this case to be treated somewhere
-        * else (CG)
-        */
+       if (matched && (ret != NULL)) {
+               *ret = NULL;
 
-       if (on(UNIX__NONULL, ctrl))
-               return 0;       /* will fail but don't let on yet */
+               slogin = buf;
 
-       /* UNIX passwords area */
-       pwd = getpwnam(name);   /* Get password file entry... */
+               spasswd = strchr(slogin, ':');
+               if (spasswd == NULL) {
+                       return matched;
+               }
+               *spasswd++ = '\0';
 
-       if (pwd != NULL) {
-               if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
-               { /* NIS+ */                 
-                       uid_t save_euid, save_uid;
-       
-                       save_euid = geteuid();
-                       save_uid = getuid();
-                       if (save_uid == pwd->pw_uid)
-                               setreuid( save_euid, save_uid );
-                       else  {
-                               setreuid( 0, -1 );
-                               if (setreuid( -1, pwd->pw_uid ) == -1) {
-                                       setreuid( -1, 0 );
-                                       setreuid( 0, -1 );
-                                       if(setreuid( -1, pwd->pw_uid ) == -1)
-                                               /* Will fail elsewhere. */
-                                               return 0;
-                               }
-                       }
-       
-                       spwdent = getspnam( name );
-                       if (save_uid == pwd->pw_uid)
-                               setreuid( save_uid, save_euid );
-                       else {
-                               if (setreuid( -1, 0 ) == -1)
-                               setreuid( save_uid, -1 );
-                               setreuid( -1, save_euid );
-                       }
-               } else if (strcmp(pwd->pw_passwd, "x") == 0) {
-                       /*
-                        * ...and shadow password file entry for this user,
-                        * if shadowing is enabled
-                        */
-                       spwdent = getspnam(name);
+               suid = strchr(spasswd, ':');
+               if (suid == NULL) {
+                       return matched;
                }
-               if (spwdent)
-                       salt = x_strdup(spwdent->sp_pwdp);
-               else
-                       salt = x_strdup(pwd->pw_passwd);
-       }
-       /* Does this user have a password? */
-       if (salt == NULL) {
-               retval = 0;
-       } else {
-               if (strlen(salt) == 0)
-                       retval = 1;
-               else
-                       retval = 0;
-       }
+               *suid++ = '\0';
 
-       /* tidy up */
+               sgid = strchr(suid, ':');
+               if (sgid == NULL) {
+                       return matched;
+               }
+               *sgid++ = '\0';
 
-       if (salt)
-               _pam_delete(salt);
+               sgecos = strchr(sgid, ':');
+               if (sgecos == NULL) {
+                       return matched;
+               }
+               *sgecos++ = '\0';
 
-       return retval;
+               shome = strchr(sgecos, ':');
+               if (shome == NULL) {
+                       return matched;
+               }
+               *shome++ = '\0';
+
+               sshell = strchr(shome, ':');
+               if (sshell == NULL) {
+                       return matched;
+               }
+               *sshell++ = '\0';
+
+               buflen = sizeof(struct passwd) +
+                        strlen(slogin) + 1 +
+                        strlen(spasswd) + 1 +
+                        strlen(suid) + 1 +
+                        strlen(sgid) + 1 +
+                        strlen(sgecos) + 1 +
+                        strlen(shome) + 1 +
+                        strlen(sshell) + 1;
+               *ret = malloc(buflen);
+               if (*ret == NULL) {
+                       return matched;
+               }
+               memset(*ret, '\0', buflen);
+
+               (*ret)->pw_uid = strtol(suid, &p, 10);
+               if ((strlen(suid) == 0) || (*p != '\0')) {
+                       free(*ret);
+                       *ret = NULL;
+                       return matched;
+               }
+
+               (*ret)->pw_gid = strtol(sgid, &p, 10);
+               if ((strlen(sgid) == 0) || (*p != '\0')) {
+                       free(*ret);
+                       *ret = NULL;
+                       return matched;
+               }
+
+               p = ((char*)(*ret)) + sizeof(struct passwd);
+               (*ret)->pw_name = strcpy(p, slogin);
+               p += strlen(p) + 1;
+               (*ret)->pw_passwd = strcpy(p, spasswd);
+               p += strlen(p) + 1;
+               (*ret)->pw_gecos = strcpy(p, sgecos);
+               p += strlen(p) + 1;
+               (*ret)->pw_dir = strcpy(p, shome);
+               p += strlen(p) + 1;
+               (*ret)->pw_shell = strcpy(p, sshell);
+
+               snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name);
+
+               if (pam_set_data(pamh, buf,
+                                *ret, _unix_cleanup) != PAM_SUCCESS) {
+                       free(*ret);
+                       *ret = NULL;
+               }
+       }
+
+       return matched;
+}
+
+/*
+ * _unix_comsefromsource() is a quick check to see if information about a given
+ * user comes from a particular source (just files and nis for now)
+ *
+ */
+int _unix_comesfromsource(pam_handle_t *pamh,
+                         const char *name, int files, int nis)
+{
+       return _unix_getpwnam(pamh, name, files, nis, NULL);
 }
 
 /*
@@ -366,9 +404,11 @@ int _unix_blankpasswd(unsigned int ctrl, const char *name)
 #include <sys/types.h>
 #include <sys/wait.h>
 
-static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd, unsigned int ctrl)
+static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
+                                  unsigned int ctrl, const char *user)
 {
     int retval, child, fds[2];
+    void (*sighandler)(int) = NULL;
 
     D(("called."));
     /* create a pipe for the password */
@@ -377,20 +417,55 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd, unsig
        return PAM_AUTH_ERR;
     }
 
+    if (off(UNIX_NOREAP, ctrl)) {
+       /*
+        * This code arranges that the demise of the child does not cause
+        * the application to receive a signal it is not expecting - which
+        * may kill the application or worse.
+        *
+        * The "noreap" module argument is provided so that the admin can
+        * override this behavior.
+        */
+       sighandler = signal(SIGCHLD, SIG_DFL);
+    }
+
     /* fork */
     child = fork();
     if (child == 0) {
-       static char *args[] = { NULL, NULL };
+        int i=0;
+        struct rlimit rlim;
        static char *envp[] = { NULL };
+       char *args[] = { NULL, NULL, NULL, NULL };
 
        /* XXX - should really tidy up PAM here too */
 
        /* reopen stdin as pipe */
-       close(fds[1]);
        dup2(fds[0], STDIN_FILENO);
 
+       if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
+          if (rlim.rlim_max >= MAX_FD_NO)
+                rlim.rlim_max = MAX_FD_NO;
+         for (i=0; i < (int)rlim.rlim_max; i++) {
+               if (i != STDIN_FILENO)
+                  close(i);
+         }
+       }
+
+       if (geteuid() == 0) {
+          /* must set the real uid to 0 so the helper will not error
+            out if pam is called from setuid binary (su, sudo...) */
+         setuid(0);
+       }
+
        /* exec binary helper */
-       args[0] = x_strdup(CHKPWD_HELPER);
+       args[0] = strdup(CHKPWD_HELPER);
+       args[1] = x_strdup(user);
+       if (off(UNIX__NONULL, ctrl)) {  /* this means we've succeeded */
+         args[2]=strdup("nullok");
+       } else {
+         args[2]=strdup("nonull");
+       }
+
        execve(CHKPWD_HELPER, args, envp);
 
        /* should not get here: exit with error */
@@ -398,41 +473,102 @@ static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd, unsig
        exit(PAM_AUTHINFO_UNAVAIL);
     } else if (child > 0) {
        /* wait for child */
-       close(fds[0]);
        /* if the stored password is NULL */
-       if (off(UNIX__NONULL, ctrl)) {  /* this means we've succeeded */
-           write(fds[1], "nullok\0\0", 8);
-       } else {
-           write(fds[1], "nonull\0\0", 8);
-       }
+        int rc=0;
        if (passwd != NULL) {            /* send the password to the child */
            write(fds[1], passwd, strlen(passwd)+1);
            passwd = NULL;
        } else {
            write(fds[1], "", 1);                        /* blank password */
        }
+       close(fds[0]);       /* close here to avoid possible SIGPIPE above */
        close(fds[1]);
-       (void) waitpid(child, &retval, 0);  /* wait for helper to complete */
-       retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR;
+       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);
+         retval = PAM_AUTH_ERR;
+       } else {
+         retval = WEXITSTATUS(retval);
+       }
     } else {
        D(("fork failed"));
+       close(fds[0]);
+       close(fds[1]);
        retval = PAM_AUTH_ERR;
     }
 
+    if (sighandler != SIG_ERR) {
+        (void) signal(SIGCHLD, sighandler);   /* restore old signal handler */
+    }
+
     D(("returning %d", retval));
     return retval;
 }
 
+/*
+ * _unix_blankpasswd() is a quick check for a blank password
+ *
+ * returns TRUE if user does not have a password
+ * - to avoid prompting for one in such cases (CG)
+ */
+
+int
+_unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
+{
+       struct passwd *pwd = NULL;
+       char *salt = NULL;
+       int retval;
+
+       D(("called"));
+
+       /*
+        * This function does not have to be too smart if something goes
+        * wrong, return FALSE and let this case to be treated somewhere
+        * else (CG)
+        */
+
+       if (on(UNIX__NONULL, ctrl))
+               return 0;       /* will fail but don't let on yet */
+
+       /* UNIX passwords area */
+
+       retval = get_pwd_hash(pamh, name, &pwd, &salt);
+
+       if (retval == PAM_UNIX_RUN_HELPER) {
+               /* salt will not be set here so we can return immediately */
+               if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS)
+                       return 1;
+               else
+                       return 0;
+       }
+
+       /* Does this user have a password? */
+       if (salt == NULL) {
+               retval = 0;
+       } else {
+               if (strlen(salt) == 0)
+                       retval = 1;
+               else
+                       retval = 0;
+       }
+
+       /* tidy up */
+
+       if (salt)
+               _pam_delete(salt);
+
+       return retval;
+}
+
 int _unix_verify_password(pam_handle_t * pamh, const char *name
                          ,const char *p, unsigned int ctrl)
 {
        struct passwd *pwd = NULL;
-       struct spwd *spwdent = NULL;
        char *salt = NULL;
-       char *pp = NULL;
        char *data_name;
        int retval;
 
+
        D(("called"));
 
 #ifdef HAVE_PAM_FAIL_DELAY
@@ -446,107 +582,41 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name
 
        D(("locating user's record"));
 
-       /* UNIX passwords area */
-       pwd = getpwnam(name);   /* Get password file entry... */
-
-       if (pwd != NULL) {
-               if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
-               { /* NIS+ */                 
-                       uid_t save_euid, save_uid;
-       
-                       save_euid = geteuid();
-                       save_uid = getuid();
-                       if (save_uid == pwd->pw_uid)
-                               setreuid( save_euid, save_uid );
-                       else  {
-                               setreuid( 0, -1 );
-                               if (setreuid( -1, pwd->pw_uid ) == -1) {
-                                       setreuid( -1, 0 );
-                                       setreuid( 0, -1 );
-                                       if(setreuid( -1, pwd->pw_uid ) == -1)
-                                               return PAM_CRED_INSUFFICIENT;
-                               }
-                       }
-       
-                       spwdent = getspnam( name );
-                       if (save_uid == pwd->pw_uid)
-                               setreuid( save_uid, save_euid );
-                       else {
-                               if (setreuid( -1, 0 ) == -1)
-                               setreuid( save_uid, -1 );
-                               setreuid( -1, save_euid );
-                       }
-               } else if (strcmp(pwd->pw_passwd, "x") == 0) {
-                       /*
-                        * ...and shadow password file entry for this user,
-                        * if shadowing is enabled
-                        */
-                       spwdent = getspnam(name);
-               }
-               if (spwdent)
-                       salt = x_strdup(spwdent->sp_pwdp);
-               else
-                       salt = x_strdup(pwd->pw_passwd);
-       }
+       retval = get_pwd_hash(pamh, name, &pwd, &salt);
 
        data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
        if (data_name == NULL) {
-               _log_err(LOG_CRIT, "no memory for data-name");
+               pam_syslog(pamh, LOG_CRIT, "no memory for data-name");
        } else {
                strcpy(data_name, FAIL_PREFIX);
                strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
        }
 
-       retval = PAM_SUCCESS;
-       if (pwd == NULL || salt == NULL || !strcmp(salt, "x")) {
-               if (geteuid()) {
-                       /* we are not root perhaps this is the reason? Run helper */
+       if (retval != PAM_SUCCESS) {
+               if (retval == PAM_UNIX_RUN_HELPER) {
                        D(("running helper binary"));
-                       retval = _unix_run_helper_binary(pamh, p, ctrl);
+                       retval = _unix_run_helper_binary(pamh, p, ctrl, name);
                } else {
                        D(("user's record unavailable"));
+                       p = NULL;
                        if (on(UNIX_AUDIT, ctrl)) {
                                /* this might be a typo and the user has given a password
                                   instead of a username. Careful with this. */
-                               _log_err(LOG_ALERT, "check pass; user (%s) unknown", name);
-                       } else {
-                               _log_err(LOG_ALERT, "check pass; user unknown");
-                       }
-                       p = NULL;
-                       retval = PAM_AUTHINFO_UNAVAIL;
-               }
-       } else {
-               if (!strlen(salt)) {
-                       /* the stored password is NULL */
-                       if (off(UNIX__NONULL, ctrl)) {  /* this means we've succeeded */
-                               D(("user has empty password - access granted"));
-                               retval = PAM_SUCCESS;
+                               pam_syslog(pamh, LOG_WARNING,
+                                        "check pass; user (%s) unknown", name);
                        } else {
-                               D(("user has empty password - access denied"));
-                               retval = PAM_AUTH_ERR;
-                       }
-               } else if (!p) {
-                               retval = PAM_AUTH_ERR;
-               } else {
-                       if (!strncmp(salt, "$1$", 3)) {
-                               pp = Goodcrypt_md5(p, salt);
-                               if (strcmp(pp, salt) != 0) {
-                                       pp = Brokencrypt_md5(p, salt);
+                               name = NULL;
+                               if (on(UNIX_DEBUG, ctrl) || pwd == NULL) {
+                                   pam_syslog(pamh, LOG_WARNING,
+                                           "check pass; user unknown");
+                               } else {
+                                   /* don't log failure as another pam module can succeed */
+                                   goto cleanup;
                                }
-                       } else {
-                               pp = bigcrypt(p, salt);
-                       }
-                       p = NULL;               /* no longer needed here */
-
-                       /* the moment of truth -- do we agree with the password? */
-                       D(("comparing state of pp[%s] and salt[%s]", pp, salt));
-
-                       if (strcmp(pp, salt) == 0) {
-                               retval = PAM_SUCCESS;
-                       } else {
-                               retval = PAM_AUTH_ERR;
                        }
                }
+       } else {
+               retval = verify_pwd_hash(p, salt, off(UNIX__NONULL, ctrl));
        }
 
        if (retval == PAM_SUCCESS) {
@@ -564,12 +634,26 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name
 
                        if (new != NULL) {
 
-                               new->user = x_strdup(name);
-                               new->id = getuid();
-                               new->name = x_strdup(PAM_getlogin()? PAM_getlogin() : "");
+                           const char *login_name;
+                           const void *void_old;
+
+
+                           login_name = pam_modutil_getlogin(pamh);
+                           if (login_name == NULL) {
+                               login_name = "";
+                           }
+
+                               new->user = x_strdup(name ? name : "");
+                               new->uid = getuid();
+                               new->euid = geteuid();
+                               new->name = x_strdup(login_name);
 
                                /* any previous failures for this user ? */
-                               pam_get_data(pamh, data_name, (const void **) &old);
+                               if (pam_get_data(pamh, data_name, &void_old)
+                                   == PAM_SUCCESS)
+                                       old = void_old;
+                               else
+                                       old = NULL;
 
                                if (old != NULL) {
                                        new->count = old->count + 1;
@@ -577,34 +661,50 @@ int _unix_verify_password(pam_handle_t * pamh, const char *name
                                                retval = PAM_MAXTRIES;
                                        }
                                } else {
-                                       const char *service=NULL;
+                                       const void *service=NULL;
+                                       const void *ruser=NULL;
+                                       const void *rhost=NULL;
+                                       const void *tty=NULL;
+
                                        (void) pam_get_item(pamh, PAM_SERVICE,
-                                                           (const void **)&service);
-                                       _log_err(LOG_NOTICE
-                                                       ,"authentication failure; %s(uid=%d) -> "
-                                                        "%s for %s service"
-                                                       ,new->name
-                                                       ,new->id
-                                                       ,new->user
-                                                       ,service == NULL ? "**unknown**":service
-                                                       );
+                                                           &service);
+                                       (void) pam_get_item(pamh, PAM_RUSER,
+                                                           &ruser);
+                                       (void) pam_get_item(pamh, PAM_RHOST,
+                                                           &rhost);
+                                       (void) pam_get_item(pamh, PAM_TTY,
+                                                           &tty);
+
+                                       pam_syslog(pamh, LOG_NOTICE,
+                                                "authentication failure; "
+                                                "logname=%s uid=%d euid=%d "
+                                                "tty=%s ruser=%s rhost=%s "
+                                                "%s%s",
+                                                new->name, new->uid, new->euid,
+                                                tty ? (const char *)tty : "",
+                                                ruser ? (const char *)ruser : "",
+                                                rhost ? (const char *)rhost : "",
+                                                (new->user && new->user[0] != '\0')
+                                                 ? " user=" : "",
+                                                new->user
+                                       );
                                        new->count = 1;
                                }
 
                                pam_set_data(pamh, data_name, new, _cleanup_failures);
 
                        } else {
-                               _log_err(LOG_CRIT, "no memory for failure recorder");
+                               pam_syslog(pamh, LOG_CRIT,
+                                        "no memory for failure recorder");
                        }
                }
        }
 
+cleanup:
        if (data_name)
                _pam_delete(data_name);
        if (salt)
                _pam_delete(salt);
-       if (pp)
-               _pam_overwrite(pp);
 
        D(("done [%d].", retval));
 
@@ -621,11 +721,10 @@ int _unix_read_password(pam_handle_t * pamh
                        ,const char *prompt1
                        ,const char *prompt2
                        ,const char *data_name
-                       ,const char **pass)
+                       ,const void **pass)
 {
        int authtok_flag;
-       int retval;
-       const char *item;
+       int retval = PAM_SUCCESS;
        char *token;
 
        D(("called"));
@@ -647,22 +746,20 @@ int _unix_read_password(pam_handle_t * pamh
         */
 
        if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
-               retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
+               retval = pam_get_item(pamh, authtok_flag, pass);
                if (retval != PAM_SUCCESS) {
                        /* very strange. */
-                       _log_err(LOG_ALERT
-                                ,"pam_get_item returned error to unix-read-password"
+                       pam_syslog(pamh, LOG_ALERT,
+                                "pam_get_item returned error to unix-read-password"
                            );
                        return retval;
-               } else if (item != NULL) {      /* we have a password! */
-                       *pass = item;
-                       item = NULL;
+               } else if (*pass != NULL) {     /* we have a password! */
                        return PAM_SUCCESS;
-               } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
-                       return PAM_AUTHTOK_RECOVER_ERR;         /* didn't work */
                } else if (on(UNIX_USE_AUTHTOK, ctrl)
                           && off(UNIX__OLD_PASSWD, ctrl)) {
-                       return PAM_AUTHTOK_RECOVER_ERR;
+                       return PAM_AUTHTOK_ERR;
+               } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
+                       return PAM_AUTHTOK_RECOVERY_ERR;          /* didn't work */
                }
        }
        /*
@@ -671,77 +768,63 @@ int _unix_read_password(pam_handle_t * pamh
         */
 
        {
-               struct pam_message msg[3], *pmsg[3];
-               struct pam_response *resp;
-               int i, replies;
-
-               /* prepare to converse */
+               int replies=1;
+               char *resp[2] = { NULL, NULL };
 
                if (comment != NULL && off(UNIX__QUIET, ctrl)) {
-                       pmsg[0] = &msg[0];
-                       msg[0].msg_style = PAM_TEXT_INFO;
-                       msg[0].msg = comment;
-                       i = 1;
-               } else {
-                       i = 0;
+                       retval = pam_info(pamh, "%s", comment);
                }
 
-               pmsg[i] = &msg[i];
-               msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
-               msg[i++].msg = prompt1;
-               replies = 1;
+               if (retval == PAM_SUCCESS) {
+                       retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
+                           &resp[0], "%s", prompt1);
 
-               if (prompt2 != NULL) {
-                       pmsg[i] = &msg[i];
-                       msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
-                       msg[i++].msg = prompt2;
-                       ++replies;
+                       if (retval == PAM_SUCCESS && prompt2 != NULL) {
+                               retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
+                                   &resp[1], "%s", prompt2);
+                               ++replies;
+                       }
                }
-               /* so call the conversation expecting i responses */
-               resp = NULL;
-               retval = converse(pamh, ctrl, i, pmsg, &resp);
-
-               if (resp != NULL) {
 
+               if (resp[0] != NULL && resp[replies-1] != NULL) {
                        /* interpret the response */
 
                        if (retval == PAM_SUCCESS) {    /* a good conversation */
 
-                               token = x_strdup(resp[i - replies].resp);
+                               token = resp[0];
                                if (token != NULL) {
                                        if (replies == 2) {
-
                                                /* verify that password entered correctly */
-                                               if (!resp[i - 1].resp
-                                                   || strcmp(token, resp[i - 1].resp)) {
-                                                       _pam_delete(token);     /* mistyped */
-                                                       retval = PAM_AUTHTOK_RECOVER_ERR;
-                                                       _make_remark(pamh, ctrl
-                                                                   ,PAM_ERROR_MSG, MISTYPED_PASS);
+                                               if (strcmp(token, resp[replies - 1])) {
+                                                       /* mistyped */
+                                                       retval = PAM_AUTHTOK_RECOVERY_ERR;
+                                                       _make_remark(pamh, ctrl,
+                                                           PAM_ERROR_MSG, MISTYPED_PASS);
                                                }
                                        }
                                } else {
-                                       _log_err(LOG_NOTICE
-                                                ,"could not recover authentication token");
+                                       pam_syslog(pamh, LOG_NOTICE,
+                                                   "could not recover authentication token");
                                }
 
                        }
-                       /*
-                        * tidy up the conversation (resp_retcode) is ignored
-                        * -- what is it for anyway? AGM
-                        */
-
-                       _pam_drop_reply(resp, i);
 
                } else {
                        retval = (retval == PAM_SUCCESS)
-                           ? PAM_AUTHTOK_RECOVER_ERR : retval;
+                           ? PAM_AUTHTOK_RECOVERY_ERR : retval;
                }
+
+               resp[0] = NULL;
+               if (replies > 1)
+                       _pam_delete(resp[1]);
        }
 
        if (retval != PAM_SUCCESS) {
+               _pam_delete(token);
+
                if (on(UNIX_DEBUG, ctrl))
-                       _log_err(LOG_DEBUG, "unable to obtain a password");
+                       pam_syslog(pamh, LOG_DEBUG,
+                                "unable to obtain a password");
                return retval;
        }
        /* 'token' is the entered password */
@@ -753,11 +836,11 @@ int _unix_read_password(pam_handle_t * pamh
                retval = pam_set_item(pamh, authtok_flag, token);
                _pam_delete(token);     /* clean it up */
                if (retval != PAM_SUCCESS
-                   || (retval = pam_get_item(pamh, authtok_flag
-                                             ,(const void **) &item))
+                   || (retval = pam_get_item(pamh, authtok_flag, pass))
                    != PAM_SUCCESS) {
 
-                       _log_err(LOG_CRIT, "error manipulating password");
+                       *pass = NULL;
+                       pam_syslog(pamh, LOG_CRIT, "error manipulating password");
                        return retval;
 
                }
@@ -769,18 +852,16 @@ int _unix_read_password(pam_handle_t * pamh
 
                retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
                if (retval != PAM_SUCCESS) {
-                       _log_err(LOG_CRIT, "error manipulating password data [%s]"
-                                ,pam_strerror(pamh, retval));
+                       pam_syslog(pamh, LOG_CRIT,
+                                "error manipulating password data [%s]",
+                                pam_strerror(pamh, retval));
                        _pam_delete(token);
                        return retval;
                }
-               item = token;
+               *pass = token;
                token = NULL;   /* break link to password */
        }
 
-       *pass = item;
-       item = NULL;            /* break link to password */
-
        return PAM_SUCCESS;
 }
 
@@ -789,6 +870,7 @@ int _unix_read_password(pam_handle_t * pamh
  * Copyright (c) Andrew G. Morgan 1996-8.
  * Copyright (c) Alex O. Yuriev, 1996.
  * Copyright (c) Cristian Gafton 1996.
+ * Copyright (c) Red Hat, Inc. 2007.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -802,13 +884,13 @@ int _unix_read_password(pam_handle_t * pamh
  * 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