PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags,int argc, const char **argv) {
  int ctrl = _set_ctrl(pamh, flags, argc, argv, NULL);

  const char *name = NULL;
  int retval = pam_get_user(pamh, &name, NULL);
  if (retval != PAM_SUCCESS) {
    if (retval == PAM_CONV_AGAIN)
      retval = PAM_INCOMPLETE;
    return retval;
  }

  if (on(UNIX__NULLOK, ctrl) && _unix_blankpasswd(pamh, name)) {
    name = NULL;
    retval = PAM_SUCCESS;
    return retval;
  }

  char *p;
  retval = _unix_read_password(pamh, ctrl, PAM_AUTHTOK, "Password: "******"auth could not identify password for [%s]", name);
    name = NULL;
    return retval;
  }

  retval = _unix_verify_password(pamh, name, p, ctrl);
  name = p = NULL;

  return retval;
}
PAM_EXTERN int pam_sm_close_session(pam_handle_t * pamh, int flags,
				    int argc, const char **argv)
{
	char *user_name, *service;
	unsigned int ctrl;
	int retval;

	D(("called."));

	ctrl = _set_ctrl(pamh, flags, NULL, argc, argv);

	retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
	if (user_name == NULL || retval != PAM_SUCCESS) {
		_log_err(LOG_CRIT, pamh,
		         "close_session - error recovering username");
		return PAM_SESSION_ERR;		/* How did we get authenticated with
						   no username?! */
	}
	retval = pam_get_item(pamh, PAM_SERVICE, (void *) &service);
	if (service == NULL || retval != PAM_SUCCESS) {
		_log_err(LOG_CRIT, pamh,
		         "close_session - error recovering service");
		return PAM_SESSION_ERR;
	}
	_log_err(LOG_INFO, pamh, "session closed for user %s"
		 ,user_name);

	return PAM_SUCCESS;
}
Exemple #3
0
int
pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
	char *user_name, *service;
	unsigned int ctrl;
	int retval;
    const char *login_name;

	D(("called."));

	ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv);

	retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
	if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
		pam_syslog(pamh, LOG_CRIT,
		         "open_session - error recovering username");
		return PAM_SESSION_ERR;		/* How did we get authenticated with
						   no username?! */
	}
	retval = pam_get_item(pamh, PAM_SERVICE, (void *) &service);
	if (service == NULL || *service == '\0' || retval != PAM_SUCCESS) {
		pam_syslog(pamh, LOG_CRIT,
		         "open_session - error recovering service");
		return PAM_SESSION_ERR;
	}
	login_name = pam_modutil_getlogin(pamh);
	if (login_name == NULL) {
	    login_name = "";
	}
	if (off (UNIX_QUIET, ctrl))
	  pam_syslog(pamh, LOG_INFO, "session opened for user %s by %s(uid=%lu)",
		     user_name, login_name, (unsigned long)getuid());

	return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_close_session(pam_handle_t * pamh, int flags,
				    int argc, const char **argv)
{
	char *user_name, *service;
	const char *login_username;
	unsigned int ctrl;
	int retval;

        /* pam_syslog(pamh, LOG_DEBUG, "pam_sm_close_session()"); */

	D(("called."));

	ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);

	retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
	if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
		pam_syslog(pamh, LOG_CRIT,
		         "close_session - error recovering username");
		return PAM_SESSION_ERR;		/* How did we get authenticated with
						   no username?! */
	}
	retval = pam_get_item(pamh, PAM_SERVICE, (void *) &service);
	if (service == NULL || *service == '\0' || retval != PAM_SUCCESS) {
		pam_syslog(pamh, LOG_CRIT,
		         "close_session - error recovering service");
		return PAM_SESSION_ERR;
	}

	login_username = pam_getenv(pamh, "LOGIN_USER");
	if (login_username && (!user_name ||
                               (strcmp(login_username, user_name) != 0))) {
	    pam_syslog(pamh, LOG_INFO, "session closed for user %s (%s)"
                       ,login_username ,user_name);
	}
        else {
            pam_syslog(pamh, LOG_INFO, "session closed for user %s",
                       user_name);
        }

	return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_open_session(pam_handle_t * pamh, int flags,
				   int argc, const char **argv)
{
	char *user_name = NULL, *service = NULL;
	unsigned int ctrl = 0;
	int retval = 0;
	const char *login_name = NULL;
	const char *login_username = NULL;
	const char *old_tai = NULL;
	int names_different = 0;
        lpc_auth_method auth_method = lpcam_none;
        const char *user_remote = NULL, *user_local = NULL;

        /* pam_syslog(pamh, LOG_DEBUG, "pam_sm_open_session()"); */

	D(("called."));

	ctrl = _set_ctrl(pamh, flags, NULL, NULL, argc, argv);

	retval = pam_get_item(pamh, PAM_USER, (void *) &user_name);
	if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) {
		pam_syslog(pamh, LOG_CRIT,
		         "open_session - error recovering username");
		return PAM_SESSION_ERR;		/* How did we get authenticated with
						   no username?! */
	}
	retval = pam_get_item(pamh, PAM_SERVICE, (void *) &service);
	if (service == NULL || *service == '\0' || retval != PAM_SUCCESS) {
		pam_syslog(pamh, LOG_CRIT,
		         "open_session - error recovering service");
		return PAM_SESSION_ERR;
	}
	login_name = pam_modutil_getlogin(pamh);
	if (login_name == NULL) {
	    login_name = "";
	}

	login_username = pam_getenv(pamh, "LOGIN_USER");
	if (login_username && (!user_name || 
                               (strcmp(login_username, user_name) != 0))) {
            names_different = 1;
	    pam_syslog(pamh, LOG_INFO, "session opened for user %s (%s) by "
                       "%s(uid=%lu)"
                       ,login_username, user_name
                       ,login_name, (unsigned long) getuid());
	}
	else {
	    names_different = 0;
	    pam_syslog(pamh, LOG_INFO, "session opened for user %s by %s(uid=%lu)",
		       user_name, login_name, (unsigned long)getuid());
	}

	/*
	 * Make sure TRUSTED_AUTH_INFO is set.  The cases are:
	 * 
	 *   1. Set to the magic string PAM_TRUSTED_AUTH_INFO_SET_STR.
         *      This is how sshd signals us that it wants us to set it
         *      ourselves; probably it means the user authenticated in some
         *      non-PAM way, like with an ssh authorized key.
	 *
	 *      (a) If user_name and login_name match, we'll assume that
	 *	    their authentication was "local", and report it that way.
	 *	    (This could be remote authentication if someone
         *          introduced a new PAM module, but we'll stick with 
         *          Occam's Razor...)
	 *
	 *      (b) If user_name and login_name do not match, this is
	 *	    unexpected.  This suggests it was a remote authentication,
	 *	    but by a module that's not one we have modified to support
	 *	    TRUSTED_AUTH_INFO.  Since we don't know the situation,
         *          leave it alone.
         *
         *   2. Set to something else.  We'll presume it's set to some
         *      real value here, and leave it alone.
         *
         *   3. Not set at all.  We're not sure how this happened, but
         *      leave it alone.
	 */
	old_tai = pam_getenv(pamh, "TRUSTED_AUTH_INFO");

	if (old_tai == NULL) { /* Case 3 */
            /*
             * Leave it unset.
             */
	    pam_syslog(pamh, LOG_DEBUG, "TRUSTED_AUTH_INFO: not set, "
                       "leaving it alone");
        }
        else if (!strcmp(old_tai, PAM_TRUSTED_AUTH_INFO_SET_STR)) { /*Case 1*/
            if (login_username && user_name) {
                user_remote = login_username;
                user_local = user_name;
            }
            else {
                /* We know from above test that user_name is non-NULL */
                user_remote = user_name;
                user_local = user_name;
            }

	    if (names_different) { /* Case 1b */
		pam_syslog(pamh, LOG_WARNING, "TRUSTED_AUTH_INFO: set "
                           "requested, but two different usernames?  "
                           "(LOGIN_USER = '******', AUTH_USER = '******')  "
                           "Leaving it alone", user_remote, user_local);
	    }
	    else { /* Case 1a */
		pam_syslog(pamh, LOG_DEBUG, "TRUSTED_AUTH_INFO: set "
                           "requested, names match, presuming non-PAM local "
			   "authentication, e.g. ssh authorized key");
                auth_method = lpcam_local;
	    }

            if (auth_method != lpcam_none) {
                pam_unix_set_trusted_auth_info(pamh, auth_method,
                                               user_remote, user_local);
            }
	}
        else { /* Case 2 */
            /*
             * Leave it unset.
             */
	    pam_syslog(pamh, LOG_DEBUG, "TRUSTED_AUTH_INFO: already set, "
                       "leaving it alone");
        }

	return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
				int argc, const char **argv)
{
	unsigned int ctrl, lctrl;
	int retval, i;
	int remember = -1;

	/* <DO NOT free() THESE> */
	const char *user;
	char *pass_old, *pass_new;
	/* </DO NOT free() THESE> */

	D(("called."));

#ifdef USE_LCKPWDF
	/* our current locking system requires that we lock the
	   entire password database.  This avoids both livelock
	   and deadlock. */
	/* These values for the number of attempts and the sleep time
	   are, of course, completely arbitrary.
	   My reading of the PAM docs is that, once pam_chauthtok() has been
	   called with PAM_UPDATE_AUTHTOK, we are obliged to take any
	   reasonable steps to make sure the token is updated; so retrying
	   for 1/10 sec. isn't overdoing it.
	   The other possibility is to call lckpwdf() on the first
	   pam_chauthtok() pass, and hold the lock until released in the
	   second pass--but is this guaranteed to work? -SRL */
	i=0;
	while((retval = lckpwdf()) != 0 && i < 100) {
		usleep(1000);
	}
	if(retval != 0) {
		return PAM_AUTHTOK_LOCK_BUSY;
	}
#endif
	ctrl = _set_ctrl(pamh, flags, &remember, argc, argv);

	/*
	 * First get the name of a user
	 */
	retval = pam_get_user(pamh, &user, "Username: "******"bad username [%s]", user);
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return PAM_USER_UNKNOWN;
		}
		if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl))
			_log_err(LOG_DEBUG, pamh, "username [%s] obtained",
			         user);
	} else {
		if (on(UNIX_DEBUG, ctrl))
			_log_err(LOG_DEBUG, pamh,
			         "password - could not identify user");
#ifdef USE_LCKPWDF
		ulckpwdf();
#endif
		return retval;
	}

	D(("Got username of %s", user));

	/*
	 * 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.
		 */
		char *Announce;

		D(("prelim check"));

		if (_unix_blankpasswd(ctrl, user)) {
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return PAM_SUCCESS;
		} else if (off(UNIX__IAMROOT, ctrl)) {

			/* instruct user what is happening */
#define greeting "Changing password for "
			Announce = (char *) malloc(sizeof(greeting) + strlen(user));
			if (Announce == NULL) {
				_log_err(LOG_CRIT, pamh,
				         "password - out of memory");
#ifdef USE_LCKPWDF
				ulckpwdf();
#endif
				return PAM_BUF_ERR;
			}
			(void) strcpy(Announce, greeting);
			(void) strcpy(Announce + sizeof(greeting) - 1, user);
#undef greeting

			lctrl = ctrl;
			set(UNIX__OLD_PASSWD, lctrl);
			retval = _unix_read_password(pamh, lctrl
						     ,Announce
					     ,"(current) UNIX password: "******"password - (old) token not obtained");
#ifdef USE_LCKPWDF
				ulckpwdf();
#endif
				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;
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return retval;
		}
		retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
		pass_old = NULL;
		if (retval != PAM_SUCCESS) {
			_log_err(LOG_CRIT, pamh,
			         "failed to set PAM_OLDAUTHTOK");
		}
		retval = _unix_verify_shadow(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].
		 */

		if (off(UNIX_NOT_SET_PASS, ctrl)) {
			retval = pam_get_item(pamh, PAM_OLDAUTHTOK
					      ,(const void **) &pass_old);
		} else {
			retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
					      ,(const void **) &pass_old);
			if (retval == PAM_NO_MODULE_DATA) {
				retval = PAM_SUCCESS;
				pass_old = NULL;
			}
		}
		D(("pass_old [%s]", pass_old));

		if (retval != PAM_SUCCESS) {
			_log_err(LOG_NOTICE, pamh, "user not authenticated");
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return retval;
		}
		retval = _unix_verify_shadow(user, ctrl);
		if (retval != PAM_SUCCESS) {
			_log_err(LOG_NOTICE, pamh, "user not authenticated 2");
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return retval;
		}
		D(("get new password now"));

		lctrl = ctrl;

		if (on(UNIX_USE_AUTHTOK, lctrl)) {
			set(UNIX_USE_FIRST_PASS, lctrl);
		}
		retry = 0;
		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 = _unix_read_password(pamh, lctrl
						     ,NULL
					     ,"Enter new UNIX password: "******"Retype new UNIX password: "******"password - new password not obtained");
				}
				pass_old = NULL;	/* tidy up */
#ifdef USE_LCKPWDF
				ulckpwdf();
#endif
				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 (pass_new[0] == '\0') {	/* "\0" password = NULL */
				pass_new = NULL;
			}
			retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
		}

		if (retval != PAM_SUCCESS) {
			_log_err(LOG_NOTICE, pamh,
			         "new password not acceptable");
			_pam_overwrite(pass_new);
			_pam_overwrite(pass_old);
			pass_new = pass_old = NULL;	/* tidy up */
#ifdef USE_LCKPWDF
			ulckpwdf();
#endif
			return retval;
		}
		/*
		 * By reaching here we have approved the passwords and must now
		 * rebuild the password database file.
		 */

		/*
		 * First we encrypt the new password.
		 */

		if (on(UNIX_MD5_PASS, ctrl)) {
			tpass = crypt_md5_wrapper(pass_new);
		} else {
			/*
			 * Salt manipulation is stolen from Rick Faith's passwd
			 * program.  Sorry Rick :) -- alex
			 */

			time_t tm;
			char salt[3];

			time(&tm);
			salt[0] = bin_to_ascii(tm & 0x3f);
			salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
			salt[2] = '\0';

			if (off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8) {
				/* 
				 * to avoid using the _extensions_ of the bigcrypt()
				 * function we truncate the newly entered password
				 */
				char *temp = malloc(9);
				char *e;

				if (temp == NULL) {
					_log_err(LOG_CRIT, pamh,
					         "out of memory for password");
					_pam_overwrite(pass_new);
					_pam_overwrite(pass_old);
					pass_new = pass_old = NULL;	/* tidy up */
#ifdef USE_LCKPWDF
					ulckpwdf();
#endif
					return PAM_BUF_ERR;
				}
				/* copy first 8 bytes of password */
				strncpy(temp, pass_new, 8);
				temp[8] = '\0';

				/* no longer need cleartext */
				e = bigcrypt(temp, salt);
				tpass = x_strdup(e);

				_pam_overwrite(e);
				_pam_delete(temp);	/* tidy up */
			} else {
				char *e;

				/* no longer need cleartext */
				e = bigcrypt(pass_new, salt);
				tpass = x_strdup(e);

				_pam_overwrite(e);
			}
		}

		D(("password processed"));

		/* update the password database(s) -- race conditions..? */

		retval = _do_setpass(pamh, user, pass_old, tpass, ctrl,
		                     remember);
		_pam_overwrite(pass_new);
		_pam_overwrite(pass_old);
		_pam_delete(tpass);
		pass_old = pass_new = NULL;
	} else {		/* something has broken with the module */
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
				int argc, const char **argv)
{
	unsigned int ctrl, lctrl;
	int retval;
	int remember = -1;
	int rounds = -1;
	int pass_min_len = 0;

	/* <DO NOT free() THESE> */
	const char *user;
	const void *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 == NULL || user[0] == '-' || user[0] == '+') {
			pam_syslog(pamh, LOG_ERR, "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.
		 */
		char *Announce;

		D(("prelim check"));

		if (_unix_blankpasswd(pamh, ctrl, user)) {
			return PAM_SUCCESS;
		} else if (off(UNIX__IAMROOT, ctrl)) {
			/* instruct user what is happening */
			if (asprintf(&Announce, _("Changing password for %s."),
				user) < 0) {
				pam_syslog(pamh, LOG_CRIT,
				         "password - out of memory");
				return PAM_BUF_ERR;
			}

			lctrl = ctrl;
			set(UNIX__OLD_PASSWD, lctrl);
			retval = _unix_read_password(pamh, lctrl
						     ,Announce
					     ,_("(current) UNIX password: "******"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;
		}
		retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
		pass_old = NULL;
		if (retval != PAM_SUCCESS) {
			pam_syslog(pamh, LOG_CRIT,
			         "failed to set PAM_OLDAUTHTOK");
		}
		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].
		 */

		if (off(UNIX_NOT_SET_PASS, ctrl)) {
			retval = pam_get_item(pamh, PAM_OLDAUTHTOK
					      ,&pass_old);
		} else {
			retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
					      ,&pass_old);
			if (retval == PAM_NO_MODULE_DATA) {
				retval = PAM_SUCCESS;
				pass_old = NULL;
			}
		}
		D(("pass_old [%s]", pass_old));

		if (retval != PAM_SUCCESS) {
			pam_syslog(pamh, LOG_NOTICE, "user not authenticated");
			return retval;
		}

		D(("get new password now"));

		lctrl = ctrl;

		if (on(UNIX_USE_AUTHTOK, lctrl)) {
			set(UNIX_USE_FIRST_PASS, lctrl);
		}
		retry = 0;
		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 = _unix_read_password(pamh, lctrl
						     ,NULL
					     ,_("Enter new UNIX password: "******"Retype new UNIX password: "******"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 && off(UNIX_NOT_SET_PASS, ctrl)) {
				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,
				"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_ALERT,
		         "password received unknown request");
		retval = PAM_ABORT;
	}

	D(("retval was %d", retval));

	return retval;
}