(root)/
Linux-PAM-1.5.3/
modules/
pam_unix/
pam_unix_passwd.c
       1  /*
       2   * pam_unix password management
       3   *
       4   * Main coding by Elliot Lee <sopwith@redhat.com>, Red Hat Software.
       5   * Copyright (C) 1996.
       6   * Copyright (c) Jan Rękorajski, 1999.
       7   * Copyright (c) Red Hat, Inc., 2007, 2008.
       8   *
       9   * Redistribution and use in source and binary forms, with or without
      10   * modification, are permitted provided that the following conditions
      11   * are met:
      12   * 1. Redistributions of source code must retain the above copyright
      13   *    notice, and the entire permission notice in its entirety,
      14   *    including the disclaimer of warranties.
      15   * 2. Redistributions in binary form must reproduce the above copyright
      16   *    notice, this list of conditions and the following disclaimer in the
      17   *    documentation and/or other materials provided with the distribution.
      18   * 3. The name of the author may not be used to endorse or promote
      19   *    products derived from this software without specific prior
      20   *    written permission.
      21   *
      22   * ALTERNATIVELY, this product may be distributed under the terms of
      23   * the GNU Public License, in which case the provisions of the GPL are
      24   * required INSTEAD OF the above restrictions.  (This clause is
      25   * necessary due to a potential bad interaction between the GPL and
      26   * the restrictions contained in a BSD-style copyright.)
      27   *
      28   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
      29   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      30   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
      31   * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
      32   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      33   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      34   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      35   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      36   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      37   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      38   * OF THE POSSIBILITY OF SUCH DAMAGE.
      39   */
      40  
      41  #include "config.h"
      42  
      43  #include <stdio.h>
      44  #include <stdlib.h>
      45  #include <stdarg.h>
      46  #include <string.h>
      47  #include <malloc.h>
      48  #include <unistd.h>
      49  #include <errno.h>
      50  #include <sys/types.h>
      51  #include <pwd.h>
      52  #include <syslog.h>
      53  #include <shadow.h>
      54  #include <time.h>		/* for time() */
      55  #include <fcntl.h>
      56  #include <ctype.h>
      57  #include <sys/time.h>
      58  #include <sys/stat.h>
      59  
      60  #include <signal.h>
      61  #include <sys/wait.h>
      62  #include <sys/resource.h>
      63  
      64  #include <security/_pam_macros.h>
      65  #include <security/pam_modules.h>
      66  #include <security/pam_ext.h>
      67  #include <security/pam_modutil.h>
      68  
      69  #include "pam_inline.h"
      70  #include "pam_cc_compat.h"
      71  #include "md5.h"
      72  #include "support.h"
      73  #include "passverify.h"
      74  #include "bigcrypt.h"
      75  
      76  #ifdef HAVE_NIS
      77  # include <rpc/rpc.h>
      78  # include <rpcsvc/yp_prot.h>
      79  # include <rpcsvc/ypclnt.h>
      80  
      81  # include "yppasswd.h"
      82  
      83  # if !defined(HAVE_DECL_GETRPCPORT) &&!defined(HAVE_RPCB_GETADDR)
      84  extern int getrpcport(const char *host, unsigned long prognum,
      85  		      unsigned long versnum, unsigned int proto);
      86  # endif				/* GNU libc 2.1 */
      87  #endif
      88  
      89  /*
      90     How it works:
      91     Gets in username (has to be done) from the calling program
      92     Does authentication of user (only if we are not running as root)
      93     Gets new password/checks for sanity
      94     Sets it.
      95   */
      96  
      97  #define MAX_PASSWD_TRIES	3
      98  
      99  #ifdef HAVE_NIS
     100  #ifdef HAVE_RPCB_GETADDR
     101  static unsigned short
     102  __taddr2port (const struct netconfig *nconf, const struct netbuf *nbuf)
     103  {
     104    unsigned short port = 0;
     105    struct __rpc_sockinfo si;
     106    struct sockaddr_in *sin;
     107    struct sockaddr_in6 *sin6;
     108    if (!__rpc_nconf2sockinfo(nconf, &si))
     109      return 0;
     110  
     111    switch (si.si_af)
     112      {
     113      case AF_INET:
     114        sin = nbuf->buf;
     115        port = sin->sin_port;
     116        break;
     117      case AF_INET6:
     118        sin6 = nbuf->buf;
     119        port = sin6->sin6_port;
     120        break;
     121      default:
     122        break;
     123      }
     124  
     125    return htons (port);
     126  }
     127  #endif
     128  
     129  static char *getNISserver(pam_handle_t *pamh, unsigned long long ctrl)
     130  {
     131  	char *master;
     132  	char *domainname;
     133  	int port, err;
     134  #if defined(HAVE_RPCB_GETADDR)
     135  	struct netconfig *nconf;
     136  	struct netbuf svcaddr;
     137  	char addrbuf[INET6_ADDRSTRLEN];
     138  	void *handle;
     139  	int found;
     140  #endif
     141  
     142  
     143  #ifdef HAVE_YP_GET_DEFAULT_DOMAIN
     144  	if ((err = yp_get_default_domain(&domainname)) != 0) {
     145  		pam_syslog(pamh, LOG_WARNING, "can't get local yp domain: %s",
     146  			 yperr_string(err));
     147  		return NULL;
     148  	}
     149  #elif defined(HAVE_GETDOMAINNAME)
     150  	char domainname_res[256];
     151  
     152  	if (getdomainname (domainname_res, sizeof (domainname_res)) == 0)
     153  	  {
     154  	    if (strcmp (domainname_res, "(none)") == 0)
     155  	      {
     156  		/* If domainname is not set, some systems will return "(none)" */
     157  		domainname_res[0] = '\0';
     158  	      }
     159  	    domainname = domainname_res;
     160  	  }
     161  	else domainname = NULL;
     162  #endif
     163  
     164  	if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) {
     165  		pam_syslog(pamh, LOG_WARNING, "can't find the master ypserver: %s",
     166  			 yperr_string(err));
     167  		return NULL;
     168  	}
     169  #ifdef HAVE_RPCB_GETADDR
     170  	svcaddr.len = 0;
     171  	svcaddr.maxlen = sizeof (addrbuf);
     172  	svcaddr.buf = addrbuf;
     173  	port = 0;
     174  	found = 0;
     175  
     176  	handle = setnetconfig();
     177  	while ((nconf = getnetconfig(handle)) != NULL) {
     178  	  if (!strcmp(nconf->nc_proto, "udp")) {
     179  	    if (rpcb_getaddr(YPPASSWDPROG, YPPASSWDPROC_UPDATE,
     180  			     nconf, &svcaddr, master)) {
     181                port = __taddr2port (nconf, &svcaddr);
     182                endnetconfig (handle);
     183                found=1;
     184                break;
     185              }
     186  
     187  	    if (rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {
     188  	      clnt_pcreateerror (master);
     189                pam_syslog (pamh, LOG_ERR,
     190  			  "rpcb_getaddr (%s) failed!", master);
     191                return NULL;
     192              }
     193  	  }
     194  	}
     195  
     196  	if (!found) {
     197  	  pam_syslog (pamh, LOG_ERR,
     198  		      "Cannot find suitable transport for protocol 'udp'");
     199  	  return NULL;
     200  	}
     201  #else
     202  	port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP);
     203  #endif
     204  	if (port == 0) {
     205  		pam_syslog(pamh, LOG_WARNING,
     206  		         "yppasswdd not running on NIS master host");
     207  		return NULL;
     208  	}
     209  	if (port >= IPPORT_RESERVED) {
     210  		pam_syslog(pamh, LOG_WARNING,
     211  		         "yppasswd daemon running on illegal port");
     212  		return NULL;
     213  	}
     214  	if (on(UNIX_DEBUG, ctrl)) {
     215  	  pam_syslog(pamh, LOG_DEBUG, "Use NIS server on %s with port %d",
     216  		     master, port);
     217  	}
     218  	return master;
     219  }
     220  #endif
     221  
     222  #ifdef WITH_SELINUX
     223  
     224  static int _unix_run_update_binary(pam_handle_t *pamh, unsigned long long ctrl, const char *user,
     225      const char *fromwhat, const char *towhat, int remember)
     226  {
     227      int retval, child, fds[2];
     228      struct sigaction newsa, oldsa;
     229  
     230      D(("called."));
     231      /* create a pipe for the password */
     232      if (pipe(fds) != 0) {
     233  	D(("could not make pipe"));
     234  	return PAM_AUTH_ERR;
     235      }
     236  
     237      if (off(UNIX_NOREAP, ctrl)) {
     238  	/*
     239  	 * This code arranges that the demise of the child does not cause
     240  	 * the application to receive a signal it is not expecting - which
     241  	 * may kill the application or worse.
     242  	 *
     243  	 * The "noreap" module argument is provided so that the admin can
     244  	 * override this behavior.
     245  	 */
     246          memset(&newsa, '\0', sizeof(newsa));
     247          newsa.sa_handler = SIG_DFL;
     248          sigaction(SIGCHLD, &newsa, &oldsa);
     249      }
     250  
     251      /* fork */
     252      child = fork();
     253      if (child == 0) {
     254  	static char *envp[] = { NULL };
     255  	const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL };
     256          char buffer[16];
     257  
     258  	/* XXX - should really tidy up PAM here too */
     259  
     260  	/* reopen stdin as pipe */
     261  	if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) {
     262  		pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin");
     263  		_exit(PAM_AUTHINFO_UNAVAIL);
     264  	}
     265  
     266  	if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD,
     267  					    PAM_MODUTIL_PIPE_FD,
     268  					    PAM_MODUTIL_PIPE_FD) < 0) {
     269  		_exit(PAM_AUTHINFO_UNAVAIL);
     270  	}
     271  
     272  	/* exec binary helper */
     273  	args[0] = UPDATE_HELPER;
     274  	args[1] = user;
     275  	args[2] = "update";
     276  	if (on(UNIX_SHADOW, ctrl))
     277  		args[3] = "1";
     278  	else
     279  		args[3] = "0";
     280  
     281          snprintf(buffer, sizeof(buffer), "%d", remember);
     282          args[4] = buffer;
     283  
     284  	DIAG_PUSH_IGNORE_CAST_QUAL;
     285  	execve(UPDATE_HELPER, (char *const *) args, envp);
     286  	DIAG_POP_IGNORE_CAST_QUAL;
     287  
     288  	/* should not get here: exit with error */
     289  	D(("helper binary is not available"));
     290  	_exit(PAM_AUTHINFO_UNAVAIL);
     291      } else if (child > 0) {
     292  	/* wait for child */
     293  	/* if the stored password is NULL */
     294          int rc=0;
     295  	if (fromwhat) {
     296  	    int len = strlen(fromwhat);
     297  
     298  	    if (len > PAM_MAX_RESP_SIZE)
     299  	      len = PAM_MAX_RESP_SIZE;
     300  	    pam_modutil_write(fds[1], fromwhat, len);
     301  	}
     302          pam_modutil_write(fds[1], "", 1);
     303  	if (towhat) {
     304  	    int len = strlen(towhat);
     305  
     306  	    if (len > PAM_MAX_RESP_SIZE)
     307  	      len = PAM_MAX_RESP_SIZE;
     308  	    pam_modutil_write(fds[1], towhat, len);
     309          }
     310          pam_modutil_write(fds[1], "", 1);
     311  
     312  	close(fds[0]);       /* close here to avoid possible SIGPIPE above */
     313  	close(fds[1]);
     314  	/* wait for helper to complete: */
     315  	while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
     316  	if (rc<0) {
     317  	  pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m");
     318  	  retval = PAM_AUTHTOK_ERR;
     319  	} else if (!WIFEXITED(retval)) {
     320            pam_syslog(pamh, LOG_ERR, "unix_update abnormal exit: %d", retval);
     321            retval = PAM_AUTHTOK_ERR;
     322          } else {
     323  	  retval = WEXITSTATUS(retval);
     324  	}
     325      } else {
     326  	D(("fork failed"));
     327  	close(fds[0]);
     328  	close(fds[1]);
     329  	retval = PAM_AUTH_ERR;
     330      }
     331  
     332      if (off(UNIX_NOREAP, ctrl)) {
     333          sigaction(SIGCHLD, &oldsa, NULL);   /* restore old signal handler */
     334      }
     335  
     336      return retval;
     337  }
     338  #endif
     339  
     340  static int check_old_password(const char *forwho, const char *newpass)
     341  {
     342  	static char buf[16384];
     343  	char *s_pas;
     344  	int retval = PAM_SUCCESS;
     345  	FILE *opwfile;
     346  	size_t len = strlen(forwho);
     347  
     348  	opwfile = fopen(OLD_PASSWORDS_FILE, "r");
     349  	if (opwfile == NULL)
     350  		return PAM_ABORT;
     351  
     352  	while (fgets(buf, 16380, opwfile)) {
     353  		if (!strncmp(buf, forwho, len) && (buf[len] == ':' ||
     354  			buf[len] == ',')) {
     355  			char *sptr;
     356  			buf[strlen(buf) - 1] = '\0';
     357  			/* s_luser = */ strtok_r(buf, ":,", &sptr);
     358  			/* s_uid = */ strtok_r(NULL, ":,", &sptr);
     359  			/* s_npas = */ strtok_r(NULL, ":,", &sptr);
     360  			s_pas = strtok_r(NULL, ":,", &sptr);
     361  			while (s_pas != NULL) {
     362  				char *md5pass = Goodcrypt_md5(newpass, s_pas);
     363  				if (md5pass == NULL || !strcmp(md5pass, s_pas)) {
     364  					_pam_delete(md5pass);
     365  					retval = PAM_AUTHTOK_ERR;
     366  					break;
     367  				}
     368  				s_pas = strtok_r(NULL, ":,", &sptr);
     369  				_pam_delete(md5pass);
     370  			}
     371  			break;
     372  		}
     373  	}
     374  	fclose(opwfile);
     375  
     376  	return retval;
     377  }
     378  
     379  static int _do_setpass(pam_handle_t* pamh, const char *forwho,
     380  		       const char *fromwhat,
     381  		       char *towhat, unsigned long long ctrl, int remember)
     382  {
     383  	struct passwd *pwd = NULL;
     384  	int retval = 0;
     385  	int unlocked = 0;
     386  
     387  	D(("called"));
     388  
     389  	pwd = getpwnam(forwho);
     390  
     391  	if (pwd == NULL) {
     392  		retval = PAM_AUTHTOK_ERR;
     393  		goto done;
     394  	}
     395  
     396  	if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) {
     397  #ifdef HAVE_NIS
     398  	  char *master;
     399  
     400  	  if ((master=getNISserver(pamh, ctrl)) != NULL) {
     401  		struct timeval timeout;
     402  		struct yppasswd yppwd;
     403  		CLIENT *clnt;
     404  		int status;
     405  		enum clnt_stat err;
     406  
     407  		/* Unlock passwd file to avoid deadlock */
     408  		unlock_pwdf();
     409  		unlocked = 1;
     410  
     411  		/* Initialize password information */
     412  		yppwd.newpw.pw_passwd = pwd->pw_passwd;
     413  		yppwd.newpw.pw_name = pwd->pw_name;
     414  		yppwd.newpw.pw_uid = pwd->pw_uid;
     415  		yppwd.newpw.pw_gid = pwd->pw_gid;
     416  		yppwd.newpw.pw_gecos = pwd->pw_gecos;
     417  		yppwd.newpw.pw_dir = pwd->pw_dir;
     418  		yppwd.newpw.pw_shell = pwd->pw_shell;
     419  		yppwd.oldpass = fromwhat ? strdup (fromwhat) : strdup ("");
     420  		yppwd.newpw.pw_passwd = towhat;
     421  
     422  		D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho));
     423  
     424  		/* The yppasswd.x file said `unix authentication required',
     425  		 * so I added it. This is the only reason it is in here.
     426  		 * My yppasswdd doesn't use it, but maybe some others out there
     427  		 * do.                                        --okir
     428  		 */
     429  		clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
     430  		clnt->cl_auth = authunix_create_default();
     431  		memset((char *) &status, '\0', sizeof(status));
     432  		timeout.tv_sec = 25;
     433  		timeout.tv_usec = 0;
     434  		err = clnt_call(clnt, YPPASSWDPROC_UPDATE,
     435  				(xdrproc_t) xdr_yppasswd, (char *) &yppwd,
     436  				(xdrproc_t) xdr_int, (char *) &status,
     437  				timeout);
     438  
     439  		free (yppwd.oldpass);
     440  
     441  		if (err) {
     442  			_make_remark(pamh, ctrl, PAM_TEXT_INFO,
     443  				clnt_sperrno(err));
     444  		} else if (status) {
     445  			D(("Error while changing NIS password.\n"));
     446  		}
     447  		D(("The password has%s been changed on %s.",
     448  		   (err || status) ? " not" : "", master));
     449  		pam_syslog(pamh, LOG_NOTICE, "password%s changed for %s on %s",
     450  			 (err || status) ? " not" : "", pwd->pw_name, master);
     451  
     452  		auth_destroy(clnt->cl_auth);
     453  		clnt_destroy(clnt);
     454  		if (err || status) {
     455  			_make_remark(pamh, ctrl, PAM_TEXT_INFO,
     456  				_("NIS password could not be changed."));
     457  			retval = PAM_TRY_AGAIN;
     458  		}
     459  #ifdef PAM_DEBUG
     460  		sleep(5);
     461  #endif
     462  	    } else {
     463  		    retval = PAM_TRY_AGAIN;
     464  	    }
     465  #else
     466            if (on(UNIX_DEBUG, ctrl)) {
     467              pam_syslog(pamh, LOG_DEBUG, "No NIS support available");
     468            }
     469  
     470            retval = PAM_TRY_AGAIN;
     471  #endif
     472  	}
     473  
     474  	if (_unix_comesfromsource(pamh, forwho, 1, 0)) {
     475  		if(unlocked) {
     476  			if (lock_pwdf() != PAM_SUCCESS) {
     477  				return PAM_AUTHTOK_LOCK_BUSY;
     478  			}
     479  		}
     480  #ifdef WITH_SELINUX
     481  	        if (unix_selinux_confined())
     482  			  return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember);
     483  #endif
     484  		/* first, save old password */
     485  		if (save_old_password(pamh, forwho, fromwhat, remember)) {
     486  			retval = PAM_AUTHTOK_ERR;
     487  			goto done;
     488  		}
     489  		if (on(UNIX_SHADOW, ctrl) || is_pwd_shadowed(pwd)) {
     490  			retval = unix_update_shadow(pamh, forwho, towhat);
     491  			if (retval == PAM_SUCCESS)
     492  				if (!is_pwd_shadowed(pwd))
     493  					retval = unix_update_passwd(pamh, forwho, "x");
     494  		} else {
     495  			retval = unix_update_passwd(pamh, forwho, towhat);
     496  		}
     497  	}
     498  
     499  
     500  done:
     501  	unlock_pwdf();
     502  
     503  	return retval;
     504  }
     505  
     506  static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned long long ctrl)
     507  {
     508  	struct passwd *pwent = NULL;	/* Password and shadow password */
     509  	struct spwd *spent = NULL;	/* file entries for the user */
     510  	int daysleft;
     511  	int retval;
     512  
     513  	retval = get_account_info(pamh, user, &pwent, &spent);
     514  	if (retval == PAM_USER_UNKNOWN) {
     515  		return retval;
     516  	}
     517  
     518  	if (retval == PAM_SUCCESS && spent == NULL)
     519  		return PAM_SUCCESS;
     520  
     521  	if (retval == PAM_UNIX_RUN_HELPER) {
     522  		retval = _unix_run_verify_binary(pamh, ctrl, user, &daysleft);
     523  		if (retval == PAM_AUTH_ERR || retval == PAM_USER_UNKNOWN)
     524  			return retval;
     525  	}
     526  	else if (retval == PAM_SUCCESS)
     527  		retval = check_shadow_expiry(pamh, spent, &daysleft);
     528  
     529  	if (on(UNIX__IAMROOT, ctrl) || retval == PAM_NEW_AUTHTOK_REQD)
     530  		return PAM_SUCCESS;
     531  
     532  	return retval;
     533  }
     534  
     535  static int _pam_unix_approve_pass(pam_handle_t * pamh
     536  				  ,unsigned long long ctrl
     537  				  ,const char *pass_old
     538  				  ,const char *pass_new,
     539                                    int pass_min_len)
     540  {
     541  	const void *user;
     542  	const char *remark = NULL;
     543  	int retval = PAM_SUCCESS;
     544  
     545  	D(("&new=%p, &old=%p", pass_old, pass_new));
     546  	D(("new=[%s]", pass_new));
     547  	D(("old=[%s]", pass_old));
     548  
     549  	if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) {
     550  		if (on(UNIX_DEBUG, ctrl)) {
     551  			pam_syslog(pamh, LOG_DEBUG, "bad authentication token");
     552  		}
     553  		_make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ?
     554  			_("No password has been supplied.") :
     555  			_("The password has not been changed."));
     556  		return PAM_AUTHTOK_ERR;
     557  	}
     558  	/*
     559  	 * if one wanted to hardwire authentication token strength
     560  	 * checking this would be the place - AGM
     561  	 */
     562  
     563  	retval = pam_get_item(pamh, PAM_USER, &user);
     564  	if (retval != PAM_SUCCESS) {
     565  		if (on(UNIX_DEBUG, ctrl)) {
     566  			pam_syslog(pamh, LOG_ERR, "Can not get username");
     567  			return PAM_AUTHTOK_ERR;
     568  		}
     569  	}
     570  
     571  	if (strlen(pass_new) > PAM_MAX_RESP_SIZE) {
     572  		remark = _("You must choose a shorter password.");
     573  		D(("length exceeded [%s]", remark));
     574  	} else if (off(UNIX__IAMROOT, ctrl)) {
     575  		if ((int)strlen(pass_new) < pass_min_len)
     576  		  remark = _("You must choose a longer password.");
     577  		D(("length check [%s]", remark));
     578  		if (on(UNIX_REMEMBER_PASSWD, ctrl)) {
     579  			if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR)
     580  			  remark = _("Password has been already used. Choose another.");
     581  			if (retval == PAM_ABORT) {
     582  				pam_syslog(pamh, LOG_ERR, "can't open %s file to check old passwords",
     583  					OLD_PASSWORDS_FILE);
     584  				return retval;
     585  			}
     586  		}
     587  	}
     588  	if (remark) {
     589  		_make_remark(pamh, ctrl, PAM_ERROR_MSG, remark);
     590  		retval = PAM_AUTHTOK_ERR;
     591  	}
     592  	return retval;
     593  }
     594  
     595  int
     596  pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
     597  {
     598  	unsigned long long ctrl, lctrl;
     599  	int retval;
     600  	int remember = -1;
     601  	int rounds = 0;
     602  	int pass_min_len = 0;
     603  
     604  	/* <DO NOT free() THESE> */
     605  	const char *user;
     606  	const void *item;
     607  	const char *pass_old, *pass_new;
     608  	/* </DO NOT free() THESE> */
     609  
     610  	D(("called."));
     611  
     612  	ctrl = _set_ctrl(pamh, flags, &remember, &rounds, &pass_min_len,
     613  	                 argc, argv);
     614  
     615  	/*
     616  	 * First get the name of a user
     617  	 */
     618  	retval = pam_get_user(pamh, &user, NULL);
     619  	if (retval == PAM_SUCCESS) {
     620  		/*
     621  		 * Various libraries at various times have had bugs related to
     622  		 * '+' or '-' as the first character of a user name. Don't
     623  		 * allow them.
     624  		 */
     625  		if (user[0] == '-' || user[0] == '+') {
     626  			pam_syslog(pamh, LOG_NOTICE, "bad username [%s]", user);
     627  			return PAM_USER_UNKNOWN;
     628  		}
     629  		if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
     630  			pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained",
     631  			         user);
     632  	} else {
     633  		if (on(UNIX_DEBUG, ctrl))
     634  			pam_syslog(pamh, LOG_DEBUG,
     635  			         "password - could not identify user");
     636  		return retval;
     637  	}
     638  
     639  	D(("Got username of %s", user));
     640  
     641  	/*
     642  	 * Before we do anything else, check to make sure that the user's
     643  	 * info is in one of the databases we can modify from this module,
     644  	 * which currently is 'files' and 'nis'.  We have to do this because
     645  	 * getpwnam() doesn't tell you *where* the information it gives you
     646  	 * came from, nor should it.  That's our job.
     647  	 */
     648  	if (_unix_comesfromsource(pamh, user, 1, on(UNIX_NIS, ctrl)) == 0) {
     649  		pam_syslog(pamh, LOG_DEBUG,
     650  			 "user \"%s\" does not exist in /etc/passwd%s",
     651  			 user, on(UNIX_NIS, ctrl) ? " or NIS" : "");
     652  		return PAM_USER_UNKNOWN;
     653  	} else {
     654  		struct passwd *pwd;
     655  		_unix_getpwnam(pamh, user, 1, 1, &pwd);
     656  		if (pwd == NULL) {
     657  			pam_syslog(pamh, LOG_DEBUG,
     658  				"user \"%s\" has corrupted passwd entry",
     659  				user);
     660  			return PAM_USER_UNKNOWN;
     661  		}
     662  	}
     663  
     664  	/*
     665  	 * This is not an AUTH module!
     666  	 */
     667  	if (on(UNIX__NONULL, ctrl))
     668  		set(UNIX__NULLOK, ctrl);
     669  
     670  	if (on(UNIX__PRELIM, ctrl)) {
     671  		/*
     672  		 * obtain and verify the current password (OLDAUTHTOK) for
     673  		 * the user.
     674  		 */
     675  		D(("prelim check"));
     676  
     677  		if (_unix_blankpasswd(pamh, ctrl, user)) {
     678  			return PAM_SUCCESS;
     679  		} else if (off(UNIX__IAMROOT, ctrl) ||
     680  			   (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, user, 0, 1))) {
     681  			/* instruct user what is happening */
     682  			if (off(UNIX__QUIET, ctrl)) {
     683  				retval = pam_info(pamh, _("Changing password for %s."), user);
     684  				if (retval != PAM_SUCCESS)
     685  					return retval;
     686  			}
     687  			retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &pass_old, NULL);
     688  
     689  			if (retval != PAM_SUCCESS) {
     690  				pam_syslog(pamh, LOG_NOTICE,
     691  				    "password - (old) token not obtained");
     692  				return retval;
     693  			}
     694  			/* verify that this is the password for this user */
     695  
     696  			retval = _unix_verify_password(pamh, user, pass_old, ctrl);
     697  		} else {
     698  			D(("process run by root so do nothing this time around"));
     699  			pass_old = NULL;
     700  			retval = PAM_SUCCESS;	/* root doesn't have too */
     701  		}
     702  
     703  		if (retval != PAM_SUCCESS) {
     704  			D(("Authentication failed"));
     705  			pass_old = NULL;
     706  			return retval;
     707  		}
     708  		pass_old = NULL;
     709  		retval = _unix_verify_shadow(pamh,user, ctrl);
     710  		if (retval == PAM_AUTHTOK_ERR) {
     711  			if (off(UNIX__IAMROOT, ctrl))
     712  				_make_remark(pamh, ctrl, PAM_ERROR_MSG,
     713  					     _("You must wait longer to change your password."));
     714  			else
     715  				retval = PAM_SUCCESS;
     716  		}
     717  	} else if (on(UNIX__UPDATE, ctrl)) {
     718  		/*
     719  		 * tpass is used below to store the _pam_md() return; it
     720  		 * should be _pam_delete()'d.
     721  		 */
     722  
     723  		char *tpass = NULL;
     724  		int retry = 0;
     725  
     726  		/*
     727  		 * obtain the proposed password
     728  		 */
     729  
     730  		D(("do update"));
     731  
     732  		/*
     733  		 * get the old token back. NULL was ok only if root [at this
     734  		 * point we assume that this has already been enforced on a
     735  		 * previous call to this function].
     736  		 */
     737  
     738  		retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &item);
     739  
     740  		if (retval != PAM_SUCCESS) {
     741  			pam_syslog(pamh, LOG_NOTICE, "user not authenticated");
     742  			return retval;
     743  		}
     744  		pass_old = item;
     745  		D(("pass_old [%s]", pass_old));
     746  
     747  		D(("get new password now"));
     748  
     749  		lctrl = ctrl;
     750  
     751  		if (on(UNIX_USE_AUTHTOK, lctrl)) {
     752  			set(UNIX_USE_FIRST_PASS, lctrl);
     753  		}
     754  		if (on(UNIX_USE_FIRST_PASS, lctrl)) {
     755  			retry = MAX_PASSWD_TRIES-1;
     756  		}
     757  		retval = PAM_AUTHTOK_ERR;
     758  		while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) {
     759  			/*
     760  			 * use_authtok is to force the use of a previously entered
     761  			 * password -- needed for pluggable password strength checking
     762  			 */
     763  
     764  			retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass_new, NULL);
     765  
     766  			if (retval != PAM_SUCCESS) {
     767  				if (on(UNIX_DEBUG, ctrl)) {
     768  					pam_syslog(pamh, LOG_ERR,
     769  						 "password - new password not obtained");
     770  				}
     771  				pass_old = NULL;	/* tidy up */
     772  				return retval;
     773  			}
     774  			D(("returned to _unix_chauthtok"));
     775  
     776  			/*
     777  			 * At this point we know who the user is and what they
     778  			 * propose as their new password. Verify that the new
     779  			 * password is acceptable.
     780  			 */
     781  
     782  			if (*(const char *)pass_new == '\0') {	/* "\0" password = NULL */
     783  				pass_new = NULL;
     784  			}
     785  			retval = _pam_unix_approve_pass(pamh, ctrl, pass_old,
     786  			                                pass_new, pass_min_len);
     787  
     788  			if (retval != PAM_SUCCESS) {
     789  				pam_set_item(pamh, PAM_AUTHTOK, NULL);
     790  			}
     791  		}
     792  
     793  		if (retval != PAM_SUCCESS) {
     794  			pam_syslog(pamh, LOG_NOTICE,
     795  			         "new password not acceptable");
     796  			pass_new = pass_old = NULL;	/* tidy up */
     797  			return retval;
     798  		}
     799  		if (lock_pwdf() != PAM_SUCCESS) {
     800  			return PAM_AUTHTOK_LOCK_BUSY;
     801  		}
     802  
     803  		if (pass_old) {
     804  			retval = _unix_verify_password(pamh, user, pass_old, ctrl);
     805  			if (retval != PAM_SUCCESS) {
     806  				pam_syslog(pamh, LOG_NOTICE, "user password changed by another process");
     807  				unlock_pwdf();
     808  				return retval;
     809  			}
     810  		}
     811  
     812  		retval = _unix_verify_shadow(pamh, user, ctrl);
     813  		if (retval != PAM_SUCCESS) {
     814  			pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired");
     815  			unlock_pwdf();
     816  			return retval;
     817  		}
     818  
     819  		retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new,
     820  		                                pass_min_len);
     821  		if (retval != PAM_SUCCESS) {
     822  			pam_syslog(pamh, LOG_NOTICE,
     823  			         "new password not acceptable 2");
     824  			pass_new = pass_old = NULL;	/* tidy up */
     825  			unlock_pwdf();
     826  			return retval;
     827  		}
     828  
     829  		/*
     830  		 * By reaching here we have approved the passwords and must now
     831  		 * rebuild the password database file.
     832  		 */
     833  
     834  		/*
     835  		 * First we encrypt the new password.
     836  		 */
     837  
     838  		tpass = create_password_hash(pamh, pass_new, ctrl, rounds);
     839  		if (tpass == NULL) {
     840  			pam_syslog(pamh, LOG_CRIT,
     841  				"crypt() failure or out of memory for password");
     842  			pass_new = pass_old = NULL;	/* tidy up */
     843  			unlock_pwdf();
     844  			return PAM_BUF_ERR;
     845  		}
     846  
     847  		D(("password processed"));
     848  
     849  		/* update the password database(s) -- race conditions..? */
     850  
     851  		retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
     852  		                     remember);
     853  	        /* _do_setpass has called unlock_pwdf for us */
     854  
     855  		_pam_delete(tpass);
     856  		pass_old = pass_new = NULL;
     857  	} else {		/* something has broken with the module */
     858  		pam_syslog(pamh, LOG_CRIT,
     859  		         "password received unknown request");
     860  		retval = PAM_ABORT;
     861  	}
     862  
     863  	D(("retval was %d", retval));
     864  
     865  	return retval;
     866  }