Index: lib/libpam/modules/pam_unix/pam_unix.c =================================================================== --- lib/libpam/modules/pam_unix/pam_unix.c +++ lib/libpam/modules/pam_unix/pam_unix.c @@ -42,6 +42,8 @@ #include #include #include +#include +#include #include #include @@ -69,6 +71,8 @@ #include #include +#define AUTHHELPER "/usr/libexec/pam_unix-helper" + #define PASSWORD_HASH "md5" #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ #define SALTSIZE 32 @@ -83,6 +87,56 @@ #define PAM_OPT_LOCAL_PASS "local_pass" #define PAM_OPT_NIS_PASS "nis_pass" +static int +helper_checkpw(const char *user, const char *pass, int nullok, int emptyok) +{ + int authpipe[2]; + int status; + pid_t pid; + char helper[] = AUTHHELPER; + char userarg[MAXLOGNAME] = { '\0' }; + char eflag[] = "-e"; + char nflag[] = "-n"; + char *argv[5] = { helper, NULL }; + char *envp[1] = { NULL }; + int argc = 1; + + if (pipe(authpipe) < 0) return (-1); + + if (emptyok) argv[argc++] = eflag; + if (nullok) argv[argc++] = nflag; + (void)strncpy(userarg, user, MAXLOGNAME-1); + argv[argc++] = userarg; + + pid = fork(); + if (pid < 0) { + close(authpipe[0]); + close(authpipe[1]); + return (-1); + } + + if (pid == 0) { + close(authpipe[1]); + if (dup2(authpipe[0], STDIN_FILENO) < 0) + exit(1); + close(authpipe[0]); + if (execve(helper, argv, envp) < 0) + exit(1); + } + + close(authpipe[0]); + (void)write(authpipe[1], pass, strlen(pass)); + close(authpipe[1]); + + if (waitpid(pid, &status, WEXITED) < 0) + return (-1); + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + return (0); + + return (-1); +} + /* * authentication management */ @@ -95,6 +149,9 @@ int retval; const char *pass, *user, *realpw, *prompt; const char *emptypasswd = ""; + int usehelper = 0; + int hlpnullok = 0; + int hlpemptyok = 0; if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) { user = getlogin(); @@ -110,22 +167,35 @@ if (pwd != NULL) { PAM_LOG("Doing real authentication"); realpw = pwd->pw_passwd; - if (realpw[0] == '\0') { + if (geteuid() != 0 && getuid() == pwd->pw_uid) + usehelper = 1; + if (usehelper) { if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) && openpam_get_option(pamh, PAM_OPT_NULLOK)) + hlpnullok = 1; + if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) && + openpam_get_option(pamh, PAM_OPT_EMPTYOK)) + hlpemptyok = 1; + } else { + if (realpw[0] == '\0') { + if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) && + openpam_get_option(pamh, PAM_OPT_NULLOK)) + return (PAM_SUCCESS); + PAM_LOG("Password is empty, " + "using fake password"); + realpw = "*"; + } + /* + * Check whether the saved password hash matches the + * one generated from an empty password - as opposed + * to empty saved password hash, which is handled + * above. + */ + if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) && + openpam_get_option(pamh, PAM_OPT_EMPTYOK) && + strcmp(crypt(emptypasswd, realpw), realpw) == 0) return (PAM_SUCCESS); - PAM_LOG("Password is empty, using fake password"); - realpw = "*"; } - /* - * Check whether the saved password hash matches the one - * generated from an empty password - as opposed to empty - * saved password hash, which is handled above. - */ - if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) && - openpam_get_option(pamh, PAM_OPT_EMPTYOK) && - strcmp(crypt(emptypasswd, realpw), realpw) == 0) - return (PAM_SUCCESS); lc = login_getpwclass(pwd); } else { PAM_LOG("Doing dummy authentication"); @@ -138,12 +208,17 @@ if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Got password"); - if (strnlen(pass, _PASSWORD_LEN + 1) > _PASSWORD_LEN) { - PAM_LOG("Password is too long, using fake password"); - realpw = "*"; + if (usehelper) { + if (helper_checkpw(user, pass, hlpnullok, hlpemptyok) == 0) + return (PAM_SUCCESS); + } else { + if (strnlen(pass, _PASSWORD_LEN + 1) > _PASSWORD_LEN) { + PAM_LOG("Password is too long, using fake password"); + realpw = "*"; + } + if (strcmp(crypt(pass, realpw), realpw) == 0) + return (PAM_SUCCESS); } - if (strcmp(crypt(pass, realpw), realpw) == 0) - return (PAM_SUCCESS); PAM_VERBOSE_ERROR("UNIX authentication refused"); return (PAM_AUTH_ERR);