5 * Revision 1.1 2000/06/20 22:11:50 agmorgan
8 * Revision 1.2 1999/07/04 23:22:38 morgan
9 * Andrey's MD5 (bigendian) work around + cleanup to address problems with
10 * applications that let an (ab)user kill them off without giving PAM the
11 * opportunity to end. [Problem report from Tani Hosokawa on bugtraq.]
13 * Revision 1.1.1.1 1998/07/12 05:17:16 morgan
14 * Linux PAM sources pre-0.66
16 * Revision 1.6 1997/04/05 06:31:06 morgan
19 * Revision 1.5 1996/12/01 03:05:54 morgan
20 * debugging with _pam_macros.h
22 * Revision 1.4 1996/11/10 21:04:51 morgan
25 * Revision 1.3 1996/09/05 06:48:15 morgan
26 * A lot has changed. I'd recommend you study the diff.
28 * Revision 1.2 1996/09/01 16:33:27 morgan
29 * Cristian Gafton's changes
31 * Revision 1.1 1996/08/29 13:21:27 morgan
36 static const char rcsid_pass[] =
38 " - PAM_PWDB password module <morgan@parc.power.net>"
41 #include "pam_unix_pwupd.-c"
43 /* passwd/salt conversion macros */
45 #define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
46 #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
50 #define _UNIX_OLD_AUTHTOK "-UN*X-OLD-PASS"
51 #define _UNIX_NEW_AUTHTOK "-UN*X-NEW-PASS"
56 * i64c - convert an integer to a radix 64 character
58 static int i64c(int i)
68 if (i >= 2 && i <= 11)
70 if (i >= 12 && i <= 37)
71 return ('A' - 12 + i);
72 if (i >= 38 && i <= 63)
73 return ('a' - 38 + i);
78 * FUNCTION: _pam_unix_chauthtok()
80 * this function works in two passes. The first, when UNIX__PRELIM is
81 * set, obtains the previous password. It sets the PAM_OLDAUTHTOK item
82 * or stores it as a data item. The second function obtains a new
83 * password (verifying if necessary, that the user types it the same a
84 * second time.) depending on the 'ctrl' flags this new password may
85 * be stored in the PAM_AUTHTOK item or a private data item.
87 * Having obtained a new password. The function updates the
88 * /etc/passwd (and optionally the /etc/shadow) file(s).
90 * Provision is made for the creation of a blank shadow file if none
91 * is available, but one is required to update the shadow file -- the
92 * intention being for shadow passwords to be seamlessly implemented
93 * from the generic UNIX scheme. -- THIS BIT IS PRE-ALPHA.. and included
94 * in this release (.52) mostly for the purpose of discussion.
97 static int _unix_chauthtok(pam_handle_t *pamh, unsigned int ctrl)
102 /* <DO NOT free() THESE> */
104 const char *pass_old, *pass_new;
105 /* </DO NOT free() THESE> */
110 * First get the name of a user
113 retval = _unix_get_user( pamh, ctrl, "Username: ", &user );
114 if ( retval != PAM_SUCCESS ) {
115 if ( on(UNIX_DEBUG,ctrl) ) {
116 _log_err(LOG_DEBUG, "password - could not identify user");
121 if ( on(UNIX__PRELIM, ctrl) ) {
123 * obtain and verify the current password (OLDAUTHTOK) for
131 if ( _unix_blankpasswd(ctrl, user) ) {
135 } else if ( off(UNIX__IAMROOT, ctrl) ) {
137 /* instruct user what is happening */
138 #define greeting "Changing password for "
139 Announce = (char *) malloc(sizeof(greeting)+strlen(user));
140 if (Announce == NULL) {
141 _log_err(LOG_CRIT, "password - out of memory");
144 (void) strcpy(Announce, greeting);
145 (void) strcpy(Announce+sizeof(greeting)-1, user);
149 set(UNIX__OLD_PASSWD, lctrl);
150 retval = _unix_read_password( pamh, lctrl
152 , "(current) UNIX password: "
158 if ( retval != PAM_SUCCESS ) {
160 , "password - (old) token not obtained");
164 /* verify that this is the password for this user */
166 retval = _unix_verify_password(pamh, user, pass_old, ctrl);
168 D(("process run by root so do nothing this time around"));
170 retval = PAM_SUCCESS; /* root doesn't have too */
173 if ( retval != PAM_SUCCESS ) {
174 D(("Authentication failed"));
179 retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
181 if ( retval != PAM_SUCCESS ) {
182 _log_err(LOG_CRIT, "failed to set PAM_OLDAUTHTOK");
185 } else if ( on( UNIX__UPDATE, ctrl ) ) {
186 /* tpass is used below to store the _pam_md() return; it
187 * should be _pam_delete()'d. */
192 * obtain the proposed password
198 * get the old token back. NULL was ok only if root [at this
199 * point we assume that this has already been enforced on a
200 * previous call to this function].
203 if ( off(UNIX_NOT_SET_PASS, ctrl) ) {
204 retval = pam_get_item(pamh, PAM_OLDAUTHTOK
205 , (const void **)&pass_old);
207 retval = pam_get_data(pamh, _UNIX_OLD_AUTHTOK
208 , (const void **)&pass_old);
209 if (retval == PAM_NO_MODULE_DATA) {
210 retval = PAM_SUCCESS;
215 if (retval != PAM_SUCCESS) {
216 _log_err(LOG_NOTICE, "user not authenticated");
220 D(("get new password now"));
225 * use_authtok is to force the use of a previously entered
226 * password -- needed for pluggable password strength checking
229 if ( on(UNIX_USE_AUTHTOK, lctrl) ) {
230 set(UNIX_USE_FIRST_PASS, lctrl);
233 retval = _unix_read_password( pamh, lctrl
235 , "Enter new UNIX password: "
236 , "Retype new UNIX password: "
240 if ( retval != PAM_SUCCESS ) {
241 if ( on(UNIX_DEBUG,ctrl) ) {
243 , "password - new password not obtained");
245 pass_old = NULL; /* tidy up */
249 D(("returned to _unix_chauthtok"));
252 * At this point we know who the user is and what they
253 * propose as their new password. Verify that the new
254 * password is acceptable.
257 if (pass_new[0] == '\0') { /* "\0" password = NULL */
261 retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
263 if (retval != PAM_SUCCESS) {
264 _log_err(LOG_NOTICE, "new password not acceptable");
265 pass_new = pass_old = NULL; /* tidy up */
270 * By reaching here we have approved the passwords and must now
271 * rebuild the password database file.
275 * First we encrypt the new password.
277 * XXX - this is where we might need some code for RADIUS types
278 * of password handling... no encryption needed..
281 if ( on(UNIX_MD5_PASS, ctrl) ) {
284 * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
285 * removed use of static variables (AGM)
290 unsigned char result[16];
291 char *cp = (char *)result;
292 unsigned char tmp[16];
296 gettimeofday(&tv, (struct timezone *) 0);
297 GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
299 GoodMD5Update(&ctx, (void *) &i, sizeof i);
301 GoodMD5Update(&ctx, (void *) &i, sizeof i);
302 GoodMD5Update(&ctx, result, sizeof result);
303 GoodMD5Final(tmp, &ctx);
304 strcpy(cp, "$1$"); /* magic for the MD5 */
306 for (i = 0; i < 8; i++)
307 *cp++ = i64c(tmp[i] & 077);
310 /* no longer need cleartext */
311 pass_new = tpass = _pam_md(pass_new, (const char *)result);
315 * Salt manipulation is stolen from Rick Faith's passwd
316 * program. Sorry Rick :) -- alex
323 salt[0] = bin_to_ascii(tm & 0x3f);
324 salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
327 if ( off(UNIX_BIGCRYPT, ctrl) && strlen(pass_new) > 8 ) {
328 /* to avoid using the _extensions_ of the bigcrypt()
329 function we truncate the newly entered password */
330 char *temp = malloc(9);
333 _log_err(LOG_CRIT, "out of memory for password");
334 pass_new = pass_old = NULL; /* tidy up */
338 /* copy first 8 bytes of password */
339 strncpy(temp, pass_new, 8);
342 /* no longer need cleartext */
343 pass_new = tpass = _pam_md( temp, salt );
345 _pam_delete(temp); /* tidy up */
347 /* no longer need cleartext */
348 pass_new = tpass = _pam_md( pass_new, salt );
352 D(("password processed"));
354 /* update the password database(s) -- race conditions..? */
356 retval = unix_update_db(pamh, ctrl, user, pass_old, pass_new);
357 pass_old = pass_new = NULL;
359 } else { /* something has broken with the module */
361 _log_err(LOG_ALERT, "password received unknown request");
369 /* ******************************************************************
370 * Copyright (c) Alexander O. Yuriev (alex@bach.cis.temple.edu), 1996.
371 * Copyright (c) Andrew Morgan <morgan@parc.power.net> 1996, 1997.
372 * Copyright (c) Cristian Gafton, <gafton@redhat.com> 1996, 1997.
374 * Redistribution and use in source and binary forms, with or without
375 * modification, are permitted provided that the following conditions
377 * 1. Redistributions of source code must retain the above copyright
378 * notice, and the entire permission notice in its entirety,
379 * including the disclaimer of warranties.
380 * 2. Redistributions in binary form must reproduce the above copyright
381 * notice, this list of conditions and the following disclaimer in the
382 * documentation and/or other materials provided with the distribution.
383 * 3. The name of the author may not be used to endorse or promote
384 * products derived from this software without specific prior
385 * written permission.
387 * ALTERNATIVELY, this product may be distributed under the terms of
388 * the GNU Public License, in which case the provisions of the GPL are
389 * required INSTEAD OF the above restrictions. (This clause is
390 * necessary due to a potential bad interaction between the GPL and
391 * the restrictions contained in a BSD-style copyright.)
393 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
394 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
395 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
396 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
397 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
398 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
399 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
400 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
401 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
402 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
403 * OF THE POSSIBILITY OF SUCH DAMAGE.