summaryrefslogtreecommitdiff
path: root/modules/pam_pwdb/pam_unix_passwd.-c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_pwdb/pam_unix_passwd.-c')
-rw-r--r--modules/pam_pwdb/pam_unix_passwd.-c404
1 files changed, 404 insertions, 0 deletions
diff --git a/modules/pam_pwdb/pam_unix_passwd.-c b/modules/pam_pwdb/pam_unix_passwd.-c
new file mode 100644
index 00000000..13801ba3
--- /dev/null
+++ b/modules/pam_pwdb/pam_unix_passwd.-c
@@ -0,0 +1,404 @@
+/* $Id$ */
+
+/*
+ * $Log$
+ * Revision 1.1 2000/06/20 22:11:50 agmorgan
+ * Initial revision
+ *
+ * Revision 1.2 1999/07/04 23:22:38 morgan
+ * Andrey's MD5 (bigendian) work around + cleanup to address problems with
+ * applications that let an (ab)user kill them off without giving PAM the
+ * opportunity to end. [Problem report from Tani Hosokawa on bugtraq.]
+ *
+ * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
+ * Linux PAM sources pre-0.66
+ *
+ * Revision 1.6 1997/04/05 06:31:06 morgan
+ * mostly a reformat.
+ *
+ * Revision 1.5 1996/12/01 03:05:54 morgan
+ * debugging with _pam_macros.h
+ *
+ * Revision 1.4 1996/11/10 21:04:51 morgan
+ * pwdb conversion
+ *
+ * Revision 1.3 1996/09/05 06:48:15 morgan
+ * A lot has changed. I'd recommend you study the diff.
+ *
+ * Revision 1.2 1996/09/01 16:33:27 morgan
+ * Cristian Gafton's changes
+ *
+ * Revision 1.1 1996/08/29 13:21:27 morgan
+ * Initial revision
+ *
+ */
+
+static const char rcsid_pass[] =
+"$Id$\n"
+" - PAM_PWDB password module <morgan@parc.power.net>"
+;
+
+#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;
+
+ /* <DO NOT free() THESE> */
+ const char *user;
+ const char *pass_old, *pass_new;
+ /* </DO NOT free() THESE> */
+
+ 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.
+ */
+
+ /*
+ * 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 <morgan@parc.power.net> 1996, 1997.
+ * Copyright (c) Cristian Gafton, <gafton@redhat.com> 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.
+ */