static int _do_setpass(pam_handle_t* pamh, const char *forwho, const char *fromwhat, char *towhat, unsigned int ctrl, int remember) { struct passwd *pwd = NULL; int retval = 0; int unlocked = 0; char *master = NULL; 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 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; }
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; }
static int set_password(const char *forwho, const char *shadow, const char *remember) { struct passwd *pwd = NULL; int retval; char pass[MAXPASS + 1]; char towhat[MAXPASS + 1]; int npass = 0; /* we don't care about number format errors because the helper should be called internally only */ int doshadow = atoi(shadow); int nremember = atoi(remember); char *passwords[] = { pass, towhat }; /* read the password from stdin (a pipe from the pam_unix module) */ npass = read_passwords(STDIN_FILENO, 2, passwords); if (npass != 2) { /* is it a valid password? */ if (npass == 1) { helper_log_err(LOG_DEBUG, "no new password supplied"); memset(pass, '\0', MAXPASS); } else { helper_log_err(LOG_DEBUG, "no valid passwords supplied"); } return PAM_AUTHTOK_ERR; } if (lock_pwdf() != PAM_SUCCESS) return PAM_AUTHTOK_LOCK_BUSY; pwd = getpwnam(forwho); if (pwd == NULL) { retval = PAM_USER_UNKNOWN; goto done; } /* If real caller uid is not root we must verify that received old pass agrees with the current one. We always allow change from null pass. */ if (getuid()) { retval = helper_verify_password(forwho, pass, 1); if (retval != PAM_SUCCESS) { goto done; } } /* first, save old password */ if (save_old_password(forwho, pass, nremember)) { retval = PAM_AUTHTOK_ERR; goto done; } if (doshadow || is_pwd_shadowed(pwd)) { retval = unix_update_shadow(forwho, towhat); if (retval == PAM_SUCCESS) if (!is_pwd_shadowed(pwd)) retval = unix_update_passwd(forwho, "x"); } else { retval = unix_update_passwd(forwho, towhat); } done: memset(pass, '\0', MAXPASS); memset(towhat, '\0', MAXPASS); unlock_pwdf(); if (retval == PAM_SUCCESS) { return PAM_SUCCESS; } else { return PAM_AUTHTOK_ERR; } }