]> granicus.if.org Git - linux-pam/blob - modules/pam_pwdb/pam_unix_passwd.-c
Relevant BUGIDs: 115055
[linux-pam] / modules / pam_pwdb / pam_unix_passwd.-c
1 /* $Id$ */
2
3 /*
4  * $Log$
5  * Revision 1.1  2000/06/20 22:11:50  agmorgan
6  * Initial revision
7  *
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.]
12  *
13  * Revision 1.1.1.1  1998/07/12 05:17:16  morgan
14  * Linux PAM sources pre-0.66
15  *
16  * Revision 1.6  1997/04/05 06:31:06  morgan
17  * mostly a reformat.
18  *
19  * Revision 1.5  1996/12/01 03:05:54  morgan
20  * debugging with _pam_macros.h
21  *
22  * Revision 1.4  1996/11/10 21:04:51  morgan
23  * pwdb conversion
24  *
25  * Revision 1.3  1996/09/05 06:48:15  morgan
26  * A lot has changed. I'd recommend you study the diff.
27  *
28  * Revision 1.2  1996/09/01 16:33:27  morgan
29  * Cristian Gafton's changes
30  *
31  * Revision 1.1  1996/08/29 13:21:27  morgan
32  * Initial revision
33  *
34  */
35
36 static const char rcsid_pass[] =
37 "$Id$\n"
38 " - PAM_PWDB password module <morgan@parc.power.net>"
39 ;
40
41 #include "pam_unix_pwupd.-c"
42
43 /* passwd/salt conversion macros */
44
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)+'.')
47
48 /* data tokens */
49
50 #define _UNIX_OLD_AUTHTOK  "-UN*X-OLD-PASS"
51 #define _UNIX_NEW_AUTHTOK  "-UN*X-NEW-PASS"
52
53 /* Implementation */
54
55 /*
56  * i64c - convert an integer to a radix 64 character
57  */
58 static int i64c(int i)
59 {
60     if (i < 0)
61         return ('.');
62     else if (i > 63)
63         return ('z');
64     if (i == 0)
65         return ('.');
66     if (i == 1)
67         return ('/');
68     if (i >= 2 && i <= 11)
69         return ('0' - 2 + i);
70     if (i >= 12 && i <= 37)
71         return ('A' - 12 + i);
72     if (i >= 38 && i <= 63)
73         return ('a' - 38 + i);
74     return ('\0');
75 }
76
77 /*
78  * FUNCTION: _pam_unix_chauthtok() 
79  *
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.
86  *
87  * Having obtained a new password. The function updates the
88  * /etc/passwd (and optionally the /etc/shadow) file(s).
89  *
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.
95  */
96
97 static int _unix_chauthtok(pam_handle_t *pamh, unsigned int ctrl)
98 {
99     int retval;
100     unsigned int lctrl;
101
102     /* <DO NOT free() THESE> */
103     const char *user;
104     const char *pass_old, *pass_new;
105     /* </DO NOT free() THESE> */
106
107     D(("called"));
108
109     /*
110      * First get the name of a user
111      */
112
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");
117         }
118         return retval;
119     }
120
121     if ( on(UNIX__PRELIM, ctrl) ) {
122         /*
123          * obtain and verify the current password (OLDAUTHTOK) for
124          * the user.
125          */
126
127         char *Announce;
128
129         D(("prelim check"));
130
131         if ( _unix_blankpasswd(ctrl, user) ) {
132
133             return PAM_SUCCESS;
134
135         } else if ( off(UNIX__IAMROOT, ctrl) ) {
136
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");
142                 return PAM_BUF_ERR;
143             }
144             (void) strcpy(Announce, greeting);
145             (void) strcpy(Announce+sizeof(greeting)-1, user);
146 #undef greeting
147
148             lctrl = ctrl;
149             set(UNIX__OLD_PASSWD, lctrl);
150             retval = _unix_read_password( pamh, lctrl
151                                           , Announce
152                                           , "(current) UNIX password: "
153                                           , NULL
154                                           , _UNIX_OLD_AUTHTOK
155                                           , &pass_old );
156             free(Announce);
157
158             if ( retval != PAM_SUCCESS ) {
159                 _log_err(LOG_NOTICE
160                          , "password - (old) token not obtained");
161                 return retval;
162             }
163
164             /* verify that this is the password for this user */
165
166             retval = _unix_verify_password(pamh, user, pass_old, ctrl);
167         } else {
168             D(("process run by root so do nothing this time around"));
169             pass_old = NULL;
170             retval = PAM_SUCCESS;           /* root doesn't have too */
171         }
172
173         if ( retval != PAM_SUCCESS ) {
174             D(("Authentication failed"));
175             pass_old = NULL;
176             return retval;
177         }
178
179         retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
180         pass_old = NULL;
181         if ( retval != PAM_SUCCESS ) {
182             _log_err(LOG_CRIT, "failed to set PAM_OLDAUTHTOK");
183         }
184
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. */
188
189         char *tpass=NULL;     
190
191         /*
192          * obtain the proposed password
193          */
194
195         D(("do update"));
196
197         /*
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].
201          */
202
203         if ( off(UNIX_NOT_SET_PASS, ctrl) ) {
204             retval = pam_get_item(pamh, PAM_OLDAUTHTOK
205                                   , (const void **)&pass_old);
206         } else {
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;
211                 pass_old = NULL;
212             }
213         }
214
215         if (retval != PAM_SUCCESS) {
216             _log_err(LOG_NOTICE, "user not authenticated");
217             return retval;
218         }
219
220         D(("get new password now"));
221
222         lctrl = ctrl;
223
224         /*
225          * use_authtok is to force the use of a previously entered
226          * password -- needed for pluggable password strength checking
227          */
228
229         if ( on(UNIX_USE_AUTHTOK, lctrl) ) {
230             set(UNIX_USE_FIRST_PASS, lctrl);
231         }
232
233         retval = _unix_read_password( pamh, lctrl
234                                       , NULL
235                                       , "Enter new UNIX password: "
236                                       , "Retype new UNIX password: "
237                                       , _UNIX_NEW_AUTHTOK
238                                       , &pass_new );
239
240         if ( retval != PAM_SUCCESS ) {
241             if ( on(UNIX_DEBUG,ctrl) ) {
242                 _log_err(LOG_ALERT
243                          , "password - new password not obtained");
244             }
245             pass_old = NULL;                               /* tidy up */
246             return retval;
247         }
248
249         D(("returned to _unix_chauthtok"));
250
251         /*
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.
255          */
256
257         if (pass_new[0] == '\0') {     /* "\0" password = NULL */
258             pass_new = NULL;
259         }
260
261         retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new);
262
263         if (retval != PAM_SUCCESS) {
264             _log_err(LOG_NOTICE, "new password not acceptable");
265             pass_new = pass_old = NULL;               /* tidy up */
266             return retval;
267         }
268
269         /*
270          * By reaching here we have approved the passwords and must now
271          * rebuild the password database file.
272          */
273
274         /*
275          * First we encrypt the new password.
276          *
277          * XXX - this is where we might need some code for RADIUS types
278          *       of password handling... no encryption needed..
279          */
280
281         if ( on(UNIX_MD5_PASS, ctrl) ) {
282
283             /*
284              * Code lifted from Marek Michalkiewicz's shadow suite. (CG)
285              * removed use of static variables (AGM)
286              */
287
288             struct timeval tv;
289             MD5_CTX ctx;
290             unsigned char result[16];
291             char *cp = (char *)result;
292             unsigned char tmp[16];
293             int i;
294
295             GoodMD5Init(&ctx);
296             gettimeofday(&tv, (struct timezone *) 0);
297             GoodMD5Update(&ctx, (void *) &tv, sizeof tv);
298             i = getpid();
299             GoodMD5Update(&ctx, (void *) &i, sizeof i);
300             i = clock();
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 */
305             cp += strlen(cp);
306             for (i = 0; i < 8; i++)
307                 *cp++ = i64c(tmp[i] & 077);
308             *cp = '\0';
309
310             /* no longer need cleartext */
311             pass_new = tpass = _pam_md(pass_new, (const char *)result);
312
313         } else {
314             /*
315              * Salt manipulation is stolen from Rick Faith's passwd
316              * program.  Sorry Rick :) -- alex
317              */
318
319             time_t tm;
320             char salt[3];
321
322             time(&tm);
323             salt[0] = bin_to_ascii(tm & 0x3f);
324             salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
325             salt[2] = '\0';
326
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);
331
332                 if (temp == NULL) {
333                     _log_err(LOG_CRIT, "out of memory for password");
334                     pass_new = pass_old = NULL;          /* tidy up */
335                     return PAM_BUF_ERR;
336                 }
337
338                 /* copy first 8 bytes of password */
339                 strncpy(temp, pass_new, 8);
340                 temp[8] = '\0';
341
342                 /* no longer need cleartext */
343                 pass_new = tpass = _pam_md( temp, salt );
344
345                 _pam_delete(temp);                       /* tidy up */
346             } else {
347                 /* no longer need cleartext */
348                 pass_new = tpass = _pam_md( pass_new, salt );
349             }
350         }
351
352         D(("password processed"));
353
354         /* update the password database(s) -- race conditions..? */
355
356         retval = unix_update_db(pamh, ctrl, user, pass_old, pass_new);
357         pass_old = pass_new = NULL;
358
359     } else {            /* something has broken with the module */
360
361         _log_err(LOG_ALERT, "password received unknown request");
362         retval = PAM_ABORT;
363
364     }
365
366     return retval;
367 }
368
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.
373  *
374  * Redistribution and use in source and binary forms, with or without
375  * modification, are permitted provided that the following conditions
376  * are met:
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.
386  * 
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.)
392  * 
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.
404  */