123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875 |
- /*
- * pam_unix password management
- *
- * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
- * Copyright (C) 1996.
- * Copyright (c) Jan Rękorajski, 1999.
- * Copyright (c) Red Hat, Inc., 2007, 2008.
- *
- * 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.
- */
- #include "config.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <string.h>
- #include <malloc.h>
- #include <unistd.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <pwd.h>
- #include <syslog.h>
- #include <shadow.h>
- #include <time.h> /* for time() */
- #include <fcntl.h>
- #include <ctype.h>
- #include <sys/time.h>
- #include <sys/stat.h>
- #include <signal.h>
- #include <sys/wait.h>
- #include <sys/resource.h>
- #include <security/_pam_macros.h>
- #include <security/pam_modules.h>
- #include <security/pam_ext.h>
- #include <security/pam_modutil.h>
- #include "pam_cc_compat.h"
- #include "md5.h"
- #include "support.h"
- #include "passverify.h"
- #include "bigcrypt.h"
- #if (HAVE_YP_GET_DEFAULT_DOMAIN || HAVE_GETDOMAINNAME) && HAVE_YP_MASTER
- # define HAVE_NIS
- #endif
- #ifdef HAVE_NIS
- # include <rpc/rpc.h>
- # if HAVE_RPCSVC_YP_PROT_H
- # include <rpcsvc/yp_prot.h>
- # endif
- # if HAVE_RPCSVC_YPCLNT_H
- # include <rpcsvc/ypclnt.h>
- # endif
- # include "yppasswd.h"
- # if !HAVE_DECL_GETRPCPORT &&!HAVE_RPCB_GETADDR
- extern int getrpcport(const char *host, unsigned long prognum,
- unsigned long versnum, unsigned int proto);
- # endif /* GNU libc 2.1 */
- #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)
- Gets new password/checks for sanity
- Sets it.
- */
- #define MAX_PASSWD_TRIES 3
- #ifdef HAVE_NIS
- #ifdef HAVE_RPCB_GETADDR
- static unsigned short
- __taddr2port (const struct netconfig *nconf, const struct netbuf *nbuf)
- {
- unsigned short port = 0;
- struct __rpc_sockinfo si;
- struct sockaddr_in *sin;
- struct sockaddr_in6 *sin6;
- if (!__rpc_nconf2sockinfo(nconf, &si))
- return 0;
- switch (si.si_af)
- {
- case AF_INET:
- sin = nbuf->buf;
- port = sin->sin_port;
- break;
- case AF_INET6:
- sin6 = nbuf->buf;
- port = sin6->sin6_port;
- break;
- default:
- break;
- }
- return htons (port);
- }
- #endif
- static char *getNISserver(pam_handle_t *pamh, unsigned long long ctrl)
- {
- char *master;
- char *domainname;
- int port, err;
- #if defined(HAVE_RPCB_GETADDR)
- struct netconfig *nconf;
- struct netbuf svcaddr;
- char addrbuf[INET6_ADDRSTRLEN];
- void *handle;
- int found;
- #endif
- #ifdef HAVE_YP_GET_DEFAULT_DOMAIN
- if ((err = yp_get_default_domain(&domainname)) != 0) {
- pam_syslog(pamh, LOG_WARNING, "can't get local yp domain: %s",
- yperr_string(err));
- return NULL;
- }
- #elif defined(HAVE_GETDOMAINNAME)
- char domainname_res[256];
- if (getdomainname (domainname_res, sizeof (domainname_res)) == 0)
- {
- if (strcmp (domainname_res, "(none)") == 0)
- {
- /* If domainname is not set, some systems will return "(none)" */
- domainname_res[0] = '\0';
- }
- domainname = domainname_res;
- }
- else domainname = NULL;
- #endif
- if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
- pam_syslog(pamh, LOG_WARNING, "can't find the master ypserver: %s",
- yperr_string(err));
- return NULL;
- }
- #ifdef HAVE_RPCB_GETADDR
- svcaddr.len = 0;
- svcaddr.maxlen = sizeof (addrbuf);
- svcaddr.buf = addrbuf;
- port = 0;
- found = 0;
- handle = setnetconfig();
- while ((nconf = getnetconfig(handle)) != NULL) {
- if (!strcmp(nconf->nc_proto, "udp")) {
- if (rpcb_getaddr(YPPASSWDPROG, YPPASSWDPROC_UPDATE,
- nconf, &svcaddr, master)) {
- port = __taddr2port (nconf, &svcaddr);
- endnetconfig (handle);
- found=1;
- break;
- }
- if (rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {
- clnt_pcreateerror (master);
- pam_syslog (pamh, LOG_ERR,
- "rpcb_getaddr (%s) failed!", master);
- return NULL;
- }
- }
- }
- if (!found) {
- pam_syslog (pamh, LOG_ERR,
- "Cannot find suitable transport for protocol 'udp'");
- return NULL;
- }
- #else
- port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
- #endif
- if (port == 0) {
- pam_syslog(pamh, LOG_WARNING,
- "yppasswdd not running on NIS master host");
- return NULL;
- }
- if (port >= IPPORT_RESERVED) {
- pam_syslog(pamh, LOG_WARNING,
- "yppasswd daemon running on illegal port");
- return NULL;
- }
- if (on(UNIX_DEBUG, ctrl)) {
- pam_syslog(pamh, LOG_DEBUG, "Use NIS server on %s with port %d",
- master, port);
- }
- return master;
- }
- #endif
- #ifdef WITH_SELINUX
- static int _unix_run_update_binary(pam_handle_t *pamh, unsigned long long ctrl, const char *user,
- const char *fromwhat, const char *towhat, int remember)
- {
- int retval, child, fds[2];
- struct sigaction newsa, oldsa;
- D(("called."));
- /* create a pipe for the password */
- if (pipe(fds) != 0) {
- D(("could not make pipe"));
- 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.
- */
- memset(&newsa, '\0', sizeof(newsa));
- newsa.sa_handler = SIG_DFL;
- sigaction(SIGCHLD, &newsa, &oldsa);
- }
- /* fork */
- child = fork();
- if (child == 0) {
- static char *envp[] = { NULL };
- const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
- char buffer[16];
- /* XXX - should really tidy up PAM here too */
- /* reopen stdin as pipe */
- if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) {
- pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin");
- _exit(PAM_AUTHINFO_UNAVAIL);
- }
- if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD,
- PAM_MODUTIL_PIPE_FD,
- PAM_MODUTIL_PIPE_FD) < 0) {
- _exit(PAM_AUTHINFO_UNAVAIL);
- }
- /* exec binary helper */
- args[0] = UPDATE_HELPER;
- args[1] = user;
- args[2] = "update";
- if (on(UNIX_SHADOW, ctrl))
- args[3] = "1";
- else
- args[3] = "0";
- snprintf(buffer, sizeof(buffer), "%d", remember);
- args[4] = buffer;
- DIAG_PUSH_IGNORE_CAST_QUAL;
- execve(UPDATE_HELPER, (char *const *) args, envp);
- DIAG_POP_IGNORE_CAST_QUAL;
- /* should not get here: exit with error */
- D(("helper binary is not available"));
- _exit(PAM_AUTHINFO_UNAVAIL);
- } else if (child > 0) {
- /* wait for child */
- /* if the stored password is NULL */
- int rc=0;
- if (fromwhat) {
- int len = strlen(fromwhat);
- if (len > PAM_MAX_RESP_SIZE)
- len = PAM_MAX_RESP_SIZE;
- pam_modutil_write(fds[1], fromwhat, len);
- }
- pam_modutil_write(fds[1], "", 1);
- if (towhat) {
- int len = strlen(towhat);
- if (len > PAM_MAX_RESP_SIZE)
- len = PAM_MAX_RESP_SIZE;
- pam_modutil_write(fds[1], towhat, len);
- }
- pam_modutil_write(fds[1], "", 1);
- close(fds[0]); /* close here to avoid possible SIGPIPE above */
- close(fds[1]);
- /* wait for helper to complete: */
- while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
- if (rc<0) {
- pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
- retval = PAM_AUTHTOK_ERR;
- } else if (!WIFEXITED(retval)) {
- pam_syslog(pamh, LOG_ERR, "unix_update abnormal exit: %d", retval);
- retval = PAM_AUTHTOK_ERR;
- } else {
- retval = WEXITSTATUS(retval);
- }
- } else {
- D(("fork failed"));
- close(fds[0]);
- close(fds[1]);
- retval = PAM_AUTH_ERR;
- }
- if (off(UNIX_NOREAP, ctrl)) {
- sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
- }
- return retval;
- }
- #endif
- static int check_old_password(const char *forwho, const char *newpass)
- {
- static char buf[16384];
- char *s_pas;
- int retval = PAM_SUCCESS;
- FILE *opwfile;
- size_t len = strlen(forwho);
- opwfile = fopen(OLD_PASSWORDS_FILE, "r");
- if (opwfile == NULL)
- return PAM_ABORT;
- while (fgets(buf, 16380, opwfile)) {
- if (!strncmp(buf, forwho, len) && (buf[len] == ':' ||
- buf[len] == ',')) {
- 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);
- while (s_pas != NULL) {
- char *md5pass = Goodcrypt_md5(newpass, s_pas);
- if (md5pass == NULL || !strcmp(md5pass, s_pas)) {
- _pam_delete(md5pass);
- retval = PAM_AUTHTOK_ERR;
- break;
- }
- s_pas = strtok_r(NULL, ":,", &sptr);
- _pam_delete(md5pass);
- }
- break;
- }
- }
- fclose(opwfile);
- return retval;
- }
- static int _do_setpass(pam_handle_t* pamh, const char *forwho,
- const char *fromwhat,
- char *towhat, unsigned long long ctrl, int remember)
- {
- struct passwd *pwd = NULL;
- int retval = 0;
- int unlocked = 0;
- D(("called"));
- pwd = getpwnam(forwho);
- if (pwd == NULL) {
- retval = PAM_AUTHTOK_ERR;
- goto done;
- }
- if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) {
- #ifdef HAVE_NIS
- char *master;
- if ((master=getNISserver(pamh, ctrl)) != NULL) {
- struct timeval timeout;
- struct yppasswd yppwd;
- CLIENT *clnt;
- int status;
- enum clnt_stat err;
- /* Unlock passwd file to avoid deadlock */
- unlock_pwdf();
- unlocked = 1;
- /* Initialize password information */
- yppwd.newpw.pw_passwd = pwd->pw_passwd;
- yppwd.newpw.pw_name = pwd->pw_name;
- yppwd.newpw.pw_uid = pwd->pw_uid;
- yppwd.newpw.pw_gid = pwd->pw_gid;
- yppwd.newpw.pw_gecos = pwd->pw_gecos;
- yppwd.newpw.pw_dir = pwd->pw_dir;
- yppwd.newpw.pw_shell = pwd->pw_shell;
- yppwd.oldpass = fromwhat ? strdup (fromwhat) : strdup ("");
- yppwd.newpw.pw_passwd = towhat;
- D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
- /* The yppasswd.x file said `unix authentication required',
- * so I added it. This is the only reason it is in here.
- * My yppasswdd doesn't use it, but maybe some others out there
- * do. --okir
- */
- clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
- clnt->cl_auth = authunix_create_default();
- memset((char *) &status, '\0', sizeof(status));
- timeout.tv_sec = 25;
- timeout.tv_usec = 0;
- err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
- (xdrproc_t) xdr_yppasswd, (char *) &yppwd,
- (xdrproc_t) xdr_int, (char *) &status,
- timeout);
- free (yppwd.oldpass);
- if (err) {
- _make_remark(pamh, ctrl, PAM_TEXT_INFO,
- clnt_sperrno(err));
- } else if (status) {
- D(("Error while changing NIS password.\n"));
- }
- D(("The password has%s been changed on %s.",
- (err || status) ? " not" : "", master));
- pam_syslog(pamh, LOG_NOTICE, "password%s changed for %s on %s",
- (err || status) ? " not" : "", pwd->pw_name, master);
- auth_destroy(clnt->cl_auth);
- clnt_destroy(clnt);
- if (err || status) {
- _make_remark(pamh, ctrl, PAM_TEXT_INFO,
- _("NIS password could not be changed."));
- retval = PAM_TRY_AGAIN;
- }
- #ifdef PAM_DEBUG
- sleep(5);
- #endif
- } else {
- retval = PAM_TRY_AGAIN;
- }
- #else
- if (on(UNIX_DEBUG, ctrl)) {
- pam_syslog(pamh, LOG_DEBUG, "No NIS support available");
- }
- retval = PAM_TRY_AGAIN;
- #endif
- }
- if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
- if(unlocked) {
- if (lock_pwdf() != PAM_SUCCESS) {
- return PAM_AUTHTOK_LOCK_BUSY;
- }
- }
- #ifdef WITH_SELINUX
- if (unix_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)) {
- retval = PAM_AUTHTOK_ERR;
- goto done;
- }
- if (on(UNIX_SHADOW, ctrl) || is_pwd_shadowed(pwd)) {
- retval = unix_update_shadow(pamh, forwho, towhat);
- if (retval == PAM_SUCCESS)
- if (!is_pwd_shadowed(pwd))
- retval = unix_update_passwd(pamh, forwho, "x");
- } else {
- retval = unix_update_passwd(pamh, forwho, towhat);
- }
- }
- done:
- unlock_pwdf();
- return retval;
- }
- static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned long long ctrl)
- {
- struct passwd *pwent = NULL; /* Password and shadow password */
- struct spwd *spent = NULL; /* file entries for the user */
- int daysleft;
- int retval;
- retval = get_account_info(pamh, user, &pwent, &spent);
- if (retval == PAM_USER_UNKNOWN) {
- return retval;
- }
- if (retval == PAM_SUCCESS && spent == NULL)
- return PAM_SUCCESS;
- if (retval == PAM_UNIX_RUN_HELPER) {
- retval = _unix_run_verify_binary(pamh, ctrl, user, &daysleft);
- if (retval == PAM_AUTH_ERR || retval == PAM_USER_UNKNOWN)
- return retval;
- }
- else if (retval == PAM_SUCCESS)
- retval = check_shadow_expiry(pamh, spent, &daysleft);
- if (on(UNIX__IAMROOT, ctrl) || retval == PAM_NEW_AUTHTOK_REQD)
- return PAM_SUCCESS;
- return retval;
- }
- static int _pam_unix_approve_pass(pam_handle_t * pamh
- ,unsigned long long ctrl
- ,const char *pass_old
- ,const char *pass_new,
- int pass_min_len)
- {
- const void *user;
- const char *remark = NULL;
- int retval = PAM_SUCCESS;
- D(("&new=%p, &old=%p", pass_old, pass_new));
- D(("new=[%s]", pass_new));
- D(("old=[%s]", pass_old));
- if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
- if (on(UNIX_DEBUG, ctrl)) {
- pam_syslog(pamh, LOG_DEBUG, "bad authentication token");
- }
- _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
- _("No password has been supplied.") :
- _("The password has not been changed."));
- return PAM_AUTHTOK_ERR;
- }
- /*
- * if one wanted to hardwire authentication token strength
- * checking this would be the place - AGM
- */
- retval = pam_get_item(pamh, PAM_USER, &user);
- if (retval != PAM_SUCCESS) {
- if (on(UNIX_DEBUG, ctrl)) {
- pam_syslog(pamh, LOG_ERR, "Can not get username");
- return PAM_AUTHTOK_ERR;
- }
- }
- if (strlen(pass_new) > PAM_MAX_RESP_SIZE) {
- remark = _("You must choose a shorter password.");
- D(("length exceeded [%s]", remark));
- } else if (off(UNIX__IAMROOT, ctrl)) {
- if ((int)strlen(pass_new) < pass_min_len)
- remark = _("You must choose a longer password.");
- D(("length check [%s]", remark));
- if (on(UNIX_REMEMBER_PASSWD, ctrl)) {
- if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR)
- remark = _("Password has been already used. Choose another.");
- if (retval == PAM_ABORT) {
- pam_syslog(pamh, LOG_ERR, "can't open %s file to check old passwords",
- OLD_PASSWORDS_FILE);
- return retval;
- }
- }
- }
- if (remark) {
- _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
- retval = PAM_AUTHTOK_ERR;
- }
- return retval;
- }
- int
- pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
- {
- unsigned long long ctrl, lctrl;
- int retval;
- int remember = -1;
- int rounds = 0;
- int pass_min_len = 0;
- /* <DO NOT free() THESE> */
- const char *user;
- const void *item;
- const char *pass_old, *pass_new;
- /* </DO NOT free() THESE> */
- D(("called."));
- ctrl = _set_ctrl(pamh, flags, &remember, &rounds, &pass_min_len,
- argc, argv);
- /*
- * First get the name of a user
- */
- retval = pam_get_user(pamh, &user, NULL);
- if (retval == PAM_SUCCESS) {
- /*
- * Various libraries at various times have had bugs related to
- * '+' or '-' as the first character of a user name. Don't
- * allow them.
- */
- if (user[0] == '-' || user[0] == '+') {
- pam_syslog(pamh, LOG_NOTICE, "bad username [%s]", user);
- return PAM_USER_UNKNOWN;
- }
- if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
- pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained",
- user);
- } else {
- if (on(UNIX_DEBUG, ctrl))
- pam_syslog(pamh, LOG_DEBUG,
- "password - could not identify user");
- return retval;
- }
- D(("Got username of %s", user));
- /*
- * Before we do anything else, check to make sure that the user's
- * info is in one of the databases we can modify from this module,
- * which currently is 'files' and 'nis'. We have to do this because
- * getpwnam() doesn't tell you *where* the information it gives you
- * came from, nor should it. That's our job.
- */
- if (_unix_comesfromsource(pamh, user, 1, on(UNIX_NIS, ctrl)) == 0) {
- pam_syslog(pamh, LOG_DEBUG,
- "user \"%s\" does not exist in /etc/passwd%s",
- user, on(UNIX_NIS, ctrl) ? " or NIS" : "");
- return PAM_USER_UNKNOWN;
- } else {
- struct passwd *pwd;
- _unix_getpwnam(pamh, user, 1, 1, &pwd);
- if (pwd == NULL) {
- pam_syslog(pamh, LOG_DEBUG,
- "user \"%s\" has corrupted passwd entry",
- user);
- return PAM_USER_UNKNOWN;
- }
- }
- /*
- * This is not an AUTH module!
- */
- if (on(UNIX__NONULL, ctrl))
- set(UNIX__NULLOK, ctrl);
- if (on(UNIX__PRELIM, ctrl)) {
- /*
- * obtain and verify the current password (OLDAUTHTOK) for
- * the user.
- */
- D(("prelim check"));
- if (_unix_blankpasswd(pamh, ctrl, user)) {
- return PAM_SUCCESS;
- } else if (off(UNIX__IAMROOT, ctrl) ||
- (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, user, 0, 1))) {
- /* instruct user what is happening */
- if (off(UNIX__QUIET, ctrl)) {
- retval = pam_info(pamh, _("Changing password for %s."), user);
- if (retval != PAM_SUCCESS)
- return retval;
- }
- retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &pass_old, NULL);
- if (retval != PAM_SUCCESS) {
- pam_syslog(pamh, LOG_NOTICE,
- "password - (old) token not obtained");
- return retval;
- }
- /* verify that this is the password for this user */
- retval = _unix_verify_password(pamh, user, pass_old, ctrl);
- } else {
- D(("process run by root so do nothing this time around"));
- pass_old = NULL;
- retval = PAM_SUCCESS; /* root doesn't have too */
- }
- if (retval != PAM_SUCCESS) {
- D(("Authentication failed"));
- pass_old = NULL;
- return retval;
- }
- pass_old = NULL;
- retval = _unix_verify_shadow(pamh,user, ctrl);
- if (retval == PAM_AUTHTOK_ERR) {
- if (off(UNIX__IAMROOT, ctrl))
- _make_remark(pamh, ctrl, PAM_ERROR_MSG,
- _("You must wait longer to change your password."));
- else
- retval = PAM_SUCCESS;
- }
- } else if (on(UNIX__UPDATE, ctrl)) {
- /*
- * tpass is used below to store the _pam_md() return; it
- * should be _pam_delete()'d.
- */
- char *tpass = NULL;
- int retry = 0;
- /*
- * obtain the proposed password
- */
- D(("do update"));
- /*
- * get the old token back. NULL was ok only if root [at this
- * point we assume that this has already been enforced on a
- * previous call to this function].
- */
- retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &item);
- if (retval != PAM_SUCCESS) {
- pam_syslog(pamh, LOG_NOTICE, "user not authenticated");
- return retval;
- }
- pass_old = item;
- D(("pass_old [%s]", pass_old));
- D(("get new password now"));
- lctrl = ctrl;
- if (on(UNIX_USE_AUTHTOK, lctrl)) {
- set(UNIX_USE_FIRST_PASS, lctrl);
- }
- if (on(UNIX_USE_FIRST_PASS, lctrl)) {
- retry = MAX_PASSWD_TRIES-1;
- }
- retval = PAM_AUTHTOK_ERR;
- while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
- /*
- * use_authtok is to force the use of a previously entered
- * password -- needed for pluggable password strength checking
- */
- retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass_new, NULL);
- if (retval != PAM_SUCCESS) {
- if (on(UNIX_DEBUG, ctrl)) {
- pam_syslog(pamh, LOG_ERR,
- "password - new password not obtained");
- }
- pass_old = NULL; /* tidy up */
- return retval;
- }
- D(("returned to _unix_chauthtok"));
- /*
- * At this point we know who the user is and what they
- * propose as their new password. Verify that the new
- * password is acceptable.
- */
- if (*(const char *)pass_new == '\0') { /* "\0" password = NULL */
- pass_new = NULL;
- }
- retval = _pam_unix_approve_pass(pamh, ctrl, pass_old,
- pass_new, pass_min_len);
- if (retval != PAM_SUCCESS) {
- pam_set_item(pamh, PAM_AUTHTOK, NULL);
- }
- }
- if (retval != PAM_SUCCESS) {
- pam_syslog(pamh, LOG_NOTICE,
- "new password not acceptable");
- pass_new = pass_old = NULL; /* tidy up */
- return retval;
- }
- if (lock_pwdf() != PAM_SUCCESS) {
- return PAM_AUTHTOK_LOCK_BUSY;
- }
- if (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");
- unlock_pwdf();
- return retval;
- }
- }
- retval = _unix_verify_shadow(pamh, user, ctrl);
- if (retval != PAM_SUCCESS) {
- pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired");
- unlock_pwdf();
- return retval;
- }
- retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new,
- pass_min_len);
- if (retval != PAM_SUCCESS) {
- pam_syslog(pamh, LOG_NOTICE,
- "new password not acceptable 2");
- pass_new = pass_old = NULL; /* tidy up */
- unlock_pwdf();
- return retval;
- }
- /*
- * By reaching here we have approved the passwords and must now
- * rebuild the password database file.
- */
- /*
- * First we encrypt the new password.
- */
- tpass = create_password_hash(pamh, pass_new, ctrl, rounds);
- if (tpass == NULL) {
- pam_syslog(pamh, LOG_CRIT,
- "crypt() failure or out of memory for password");
- pass_new = pass_old = NULL; /* tidy up */
- unlock_pwdf();
- return PAM_BUF_ERR;
- }
- D(("password processed"));
- /* update the password database(s) -- race conditions..? */
- retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
- remember);
- /* _do_setpass has called unlock_pwdf for us */
- _pam_delete(tpass);
- pass_old = pass_new = NULL;
- } else { /* something has broken with the module */
- pam_syslog(pamh, LOG_CRIT,
- "password received unknown request");
- retval = PAM_ABORT;
- }
- D(("retval was %d", retval));
- return retval;
- }
|