/* $Id$ */ static const char rcsid_pass[] = "$Id$\n" " - PAM_PWDB password module " ; #include "pam_unix_pwupd.-c" /* passwd/salt conversion macros */ #define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.') #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') /* data tokens */ #define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS" #define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS" /* Implementation */ /* * i64c - convert an integer to a radix 64 character */ static int i64c(int i) { if (i < 0) return ('.'); else if (i > 63) return ('z'); if (i == 0) return ('.'); if (i == 1) return ('/'); if (i >= 2 && i <= 11) return ('0' - 2 + i); if (i >= 12 && i <= 37) return ('A' - 12 + i); if (i >= 38 && i <= 63) return ('a' - 38 + i); return ('\0'); } /* * FUNCTION: _pam_unix_chauthtok() * * this function works in two passes. The first, when UNIX__PRELIM is * set, obtains the previous password. It sets the PAM_OLDAUTHTOK item * or stores it as a data item. The second function obtains a new * password (verifying if necessary, that the user types it the same a * second time.) depending on the 'ctrl' flags this new password may * be stored in the PAM_AUTHTOK item or a private data item. * * Having obtained a new password. The function updates the * /etc/passwd (and optionally the /etc/shadow) file(s). * * Provision is made for the creation of a blank shadow file if none * is available, but one is required to update the shadow file -- the * intention being for shadow passwords to be seamlessly implemented * from the generic UNIX scheme. -- THIS BIT IS PRE-ALPHA.. and included * in this release (.52) mostly for the purpose of discussion. */ static int _unix_chauthtok(pam_handle_t *pamh, unsigned int ctrl) { int retval; unsigned int lctrl; /* */ const char *user; const char *pass_old, *pass_new; /* */ D(("called")); /* * First get the name of a user */ retval = _unix_get_user( pamh, ctrl, "Username: ", &user ); if ( retval != PAM_SUCCESS ) { if ( on(UNIX_DEBUG,ctrl) ) { _log_err(LOG_DEBUG, "password - could not identify user"); } return retval; } 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) ) { 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, "password - out of memory"); 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: " , NULL , _UNIX_OLD_AUTHTOK , &pass_old ); free(Announce); if ( retval != PAM_SUCCESS ) { _log_err(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; } retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old); pass_old = NULL; if ( retval != PAM_SUCCESS ) { _log_err(LOG_CRIT, "failed to set PAM_OLDAUTHTOK"); } } 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; /* * 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; } } if (retval != PAM_SUCCESS) { _log_err(LOG_NOTICE, "user not authenticated"); return retval; } D(("get new password now")); lctrl = ctrl; /* * use_authtok is to force the use of a previously entered * password -- needed for pluggable password strength checking */ if ( on(UNIX_USE_AUTHTOK, lctrl) ) { set(UNIX_USE_FIRST_PASS, lctrl); } retval = _unix_read_password( pamh, lctrl , NULL , "Enter new UNIX password: " , "Retype new UNIX password: " , _UNIX_NEW_AUTHTOK , &pass_new ); if ( retval != PAM_SUCCESS ) { if ( on(UNIX_DEBUG,ctrl) ) { _log_err(LOG_ALERT , "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 (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, "new password not acceptable"); pass_new = pass_old = NULL; /* tidy up */ return retval; } /* * By reaching here we have approved the passwords and must now * rebuild the password database file. * * This includes the fact that the password is _not_ NULL. */ /* * First we encrypt the new password. * * XXX - this is where we might need some code for RADIUS types * of password handling... no encryption needed.. */ if ( on(UNIX_MD5_PASS, ctrl) ) { /* * Code lifted from Marek Michalkiewicz's shadow suite. (CG) * removed use of static variables (AGM) */ struct timeval tv; MD5_CTX ctx; unsigned char result[16]; char *cp = (char *)result; unsigned char tmp[16]; int i; GoodMD5Init(&ctx); gettimeofday(&tv, (struct timezone *) 0); GoodMD5Update(&ctx, (void *) &tv, sizeof tv); i = getpid(); GoodMD5Update(&ctx, (void *) &i, sizeof i); i = clock(); GoodMD5Update(&ctx, (void *) &i, sizeof i); GoodMD5Update(&ctx, result, sizeof result); GoodMD5Final(tmp, &ctx); strcpy(cp, "$1$"); /* magic for the MD5 */ cp += strlen(cp); for (i = 0; i < 8; i++) *cp++ = i64c(tmp[i] & 077); *cp = '\0'; /* no longer need cleartext */ pass_new = tpass = _pam_md(pass_new, (const char *)result); } 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); if (temp == NULL) { _log_err(LOG_CRIT, "out of memory for password"); pass_new = pass_old = NULL; /* tidy up */ return PAM_BUF_ERR; } /* copy first 8 bytes of password */ strncpy(temp, pass_new, 8); temp[8] = '\0'; /* no longer need cleartext */ pass_new = tpass = _pam_md( temp, salt ); _pam_delete(temp); /* tidy up */ } else { /* no longer need cleartext */ pass_new = tpass = _pam_md( pass_new, salt ); } } D(("password processed")); /* update the password database(s) -- race conditions..? */ retval = unix_update_db(pamh, ctrl, user, pass_old, pass_new); pass_old = pass_new = NULL; } else { /* something has broken with the module */ _log_err(LOG_ALERT, "password received unknown request"); retval = PAM_ABORT; } return retval; } /* ****************************************************************** * Copyright (c) Alexander O. Yuriev (alex@bach.cis.temple.edu), 1996. * Copyright (c) Andrew Morgan 1996, 1997. * Copyright (c) Cristian Gafton, 1996, 1997. * * 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. */