]> granicus.if.org Git - shadow/blob - lib/pwauth.c
[svn-upgrade] Integrating new upstream version, shadow (20000826)
[shadow] / lib / pwauth.c
1 /*
2  * Copyright 1992 - 1994, Julianne Frances Haugh
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <config.h>
31
32 #include "rcsid.h"
33 RCSID("$Id: pwauth.c,v 1.11 2000/08/26 18:27:17 marekm Exp $")
34
35 #include <sys/types.h>
36 #include <signal.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <errno.h>
40 #include "prototypes.h"
41 #include "defines.h"
42 #include "pwauth.h"
43 #include "getdef.h"
44
45 #ifdef SKEY
46 #include <skey.h>
47 #endif
48
49 #ifdef OPIE
50 #include <opie.h>
51 #endif
52
53 #ifdef __linux__  /* standard password prompt by default */
54 static const char *PROMPT = gettext_noop("Password: ");
55 #else
56 static const char *PROMPT = gettext_noop("%s's Password: ");
57 #endif
58
59 extern char *getpass();
60 extern char *getpass_with_echo();
61
62 #ifdef AUTH_METHODS
63 /*
64  * Look-up table for bound-in methods.  Put the name that the
65  * method is known by in the password field as "name" and a
66  * pointer to the function
67  */
68
69 struct  method  {
70         char    *name;
71         int     (*func)(const char *, int, const char *);
72 };
73
74 #ifdef PAD_AUTH
75 int pad_auth();
76 #endif
77 static struct method methods[] = {
78 #ifdef PAD_AUTH
79         { "pad", pad_auth },
80 #endif
81         { "",   0 }
82 };
83 #endif  /* AUTH_METHODS */
84
85 int wipe_clear_pass = 1;
86 char *clear_pass = NULL;
87
88 /*
89  * _old_auth - perform getpass/crypt authentication
90  *
91  *      _old_auth gets the user's cleartext password and encrypts it
92  *      using the salt in the encrypted password.  The results are
93  *      compared.
94  */
95
96 static int
97 _old_auth(const char *cipher, const char *user, int reason, const char *input)
98 {
99         char    prompt[1024];
100         char    *clear = NULL;
101         const char *cp;
102         int     retval;
103 #ifdef  SKEY
104         int     use_skey = 0;
105         char    challenge_info[40];
106         struct  skey    skey;
107 #endif
108
109 #ifdef OPIE
110         int use_opie = 0;
111         char o_challenge_info[OPIE_CHALLENGE_MAX + 1];
112         struct opie opie;
113         /*
114          * This implementation is based almost entirely on the SKEY code
115          * above. Thus the opie struct is called skey, etc. I am unaware
116          * if the system works at the same time, but I cannot imagine why
117          * anyone would want to do this....
118          * -- A.R.
119          * Mod: 5/14/98 A.R.
120          * Made the OPIE code separate from the S/Key code. Now
121          * (conceivably) both can be compiled in and function apart from
122          * one another (assuming a sysadmin really wants to maintain OPIE
123          * and an S/Key databases....).
124          *
125          * Also cleaned up the code a bit. Will be adding second-prompt
126          * support (the traditional Echo-on S/Key/OPIE-only prompts to let
127          * the users see the one-time passwords they are typing/pasting
128          * in....
129          * -- A.R.
130          */
131 #endif
132
133         /*
134          * There are programs for adding and deleting authentication data.
135          */
136
137         if (reason == PW_ADD || reason == PW_DELETE)
138                 return 0;
139
140         /*
141          * There are even programs for changing the user name ...
142          */
143
144         if (reason == PW_CHANGE && input != (char *) 0)
145                 return 0;
146
147         /*
148          * WARNING:
149          *
150          * When we change a password and we are root, we don't prompt.
151          * This is so root can change any password without having to
152          * know it.  This is a policy decision that might have to be
153          * revisited.
154          */
155
156         if (reason == PW_CHANGE && getuid () == 0)
157                 return 0;
158
159         /*
160          * WARNING:
161          *
162          * When we are logging in a user with no ciphertext password,
163          * we don't prompt for the password or anything.  In reality
164          * the user could just hit <ENTER>, so it doesn't really
165          * matter.
166          */
167
168         if (cipher == (char *) 0 || *cipher == '\0')
169                 return 0;
170
171 #ifdef  SKEY
172         /*
173          * If the user has an S/KEY entry show them the pertinent info
174          * and then we can try validating the created cyphertext and the SKEY.
175          * If there is no SKEY information we default to not using SKEY.
176          */
177
178         if (skeychallenge (&skey, user, challenge_info) == 0)
179                 use_skey = 1;
180 #endif
181
182 #ifdef OPIE
183         /*
184          * Ditto above, for OPIE passwords.
185          * -- AR
186          */
187
188         o_challenge_info[0] = '\0';
189         if (opiechallenge(&opie, user, o_challenge_info) == 0)
190                 use_opie = 1;
191
192         if (use_opie == 0)
193                 opieverify(&opie, (char *)NULL);
194         /*
195          * This call to opieverify is necessary within OPIE's interface:
196          * Every call to opiechallenge(), which checks to see if the user
197          * has an OPIE password, and if so get the challenge, must be
198          * accompanied by exactly one call to opieverify, which clears
199          * any outstanding locks, and otherwise cleans up.
200          * -- AR
201          */
202 #endif
203
204         /*
205          * Prompt for the password as required.  FTPD and REXECD both
206          * get the cleartext password for us.
207          */
208
209         if (reason != PW_FTP && reason != PW_REXEC && !input) {
210                 if (! (cp = getdef_str ("LOGIN_STRING")))
211                         cp = _(PROMPT);
212 #ifdef  SKEY
213                 if (use_skey)
214                         printf ("[%s]\n", challenge_info);
215 #endif
216
217 #ifdef OPIE
218                 if (use_opie)
219                         printf("[ %s ]\n", o_challenge_info);
220 #endif
221
222                 snprintf(prompt, sizeof prompt, cp, user);
223                 clear = getpass(prompt);
224                 if (!clear) {
225                         static char c[1];
226                         c[0] = '\0';
227                         clear = c;
228                 }
229                 input = clear;
230         }
231
232         /*
233          * Convert the cleartext password into a ciphertext string.
234          * If the two match, the return value will be zero, which is
235          * SUCCESS.  Otherwise we see if SKEY is being used and check
236          * the results there as well.
237          */
238
239         retval = strcmp(pw_encrypt(input, cipher), cipher);
240
241 #ifdef OPIE
242         /*
243          * This is required because using OPIE, opieverify() MUST be called
244          * opiechallenge() above even if OPIE isn't being used in this case,
245          * so locks get released, etc.
246          * -- AR
247          */
248
249         if ((retval == 0) && use_opie)
250                 opieverify(&opie, (char *)NULL);
251 #endif
252
253 #if (defined(SKEY) || defined(OPIE))
254         /*
255          * If (1) The password fails to match, and
256          * (2) The password is empty and
257          * (3) We are using OPIE or S/Key, then
258          * ...Re-prompt, with echo on.
259          * -- AR 8/22/1999
260          */
261         if (retval && !input[0] &&
262             (0
263 #ifdef SKEY
264              || use_skey
265 #endif
266 #ifdef OPIE
267              || use_opie
268 #endif
269              )) {
270                 strncat(prompt, _("(Echo on) "),
271                         (sizeof(prompt) - strlen(prompt)));
272                 clear = getpass_with_echo(prompt);
273                 if (!clear) {
274                         static char c[1];
275                         c[0] = '\0';
276                         clear = c;
277                 }
278                 input = clear;
279         }
280 #endif
281
282 #ifdef  SKEY
283         if (retval && use_skey) {
284                 int passcheck = -1;
285
286 #if 0  /* some skey libs don't have skey_passcheck.  --marekm */
287                 passcheck = skey_passcheck(user, input);
288 #else
289                 if (skeyverify(&skey, input) == 0)
290                         passcheck = skey.n;
291 #endif /* if 0 */
292                 if (passcheck > 0)
293                         retval = 0;
294         }
295 #endif
296
297 #ifdef OPIE
298         if (retval && use_opie) {
299                 if (opieverify(&opie, input) == 0)
300                         retval = 0;
301         }
302 #endif /* OPIE */
303
304         /*
305          * Things like RADIUS authentication may need the password -
306          * if the external variable wipe_clear_pass is zero, we will
307          * not wipe it (the caller should wipe clear_pass when it is
308          * no longer needed).  --marekm
309          */
310
311         clear_pass = clear;
312         if (wipe_clear_pass && clear && *clear)
313                 strzero(clear);
314         return retval;
315 }
316
317 #ifdef AUTH_METHODS
318 /*
319  * _pw_auth - perform alternate password authentication
320  *
321  *      pw_auth executes the alternate password authentication method
322  *      described in the user's password entry.  _pw_auth does the real
323  *      work, pw_auth splits the authentication string into individual
324  *      command names.
325  */
326
327 static int
328 _pw_auth(const char *command, const char *user, int reason, const char *input)
329 {
330         RETSIGTYPE (*sigint)();
331         RETSIGTYPE (*sigquit)();
332 #ifdef  SIGTSTP
333         RETSIGTYPE      (*sigtstp)();
334 #endif
335         int     pid;
336         int     status;
337         int     i;
338         char    * const argv[5];
339         int     argc = 0;
340         int     pipes[2];
341         char    *empty_env = NULL;
342         int     use_pipe;
343
344         /*
345          * Start with a quick sanity check.  ALL command names must
346          * be fully-qualified path names.
347          */
348
349         if (command[0] != '/')
350                 return -1;
351
352         /*
353          * Set the keyboard signals to be ignored.  When the user kills
354          * the child we don't want the parent dying as well.
355          */
356
357         sigint = signal (SIGINT, SIG_IGN);
358         sigquit = signal (SIGQUIT, SIG_IGN);
359 #ifdef  SIGTSTP
360         sigtstp = signal (SIGTSTP, SIG_IGN);
361 #endif
362
363         /* 
364          * FTP and REXEC reasons don't give the program direct access
365          * to the user.  This means that the program can only get input
366          * from this function.  So we set up a pipe for that purpose.
367          */
368
369         use_pipe = (reason == PW_FTP || reason == PW_REXEC);
370         if (use_pipe)
371                 if (pipe (pipes))
372                         return -1;
373
374         /*
375          * The program will be forked off with the parent process waiting
376          * on the child to tell it how successful it was.
377          */
378
379         switch (pid = fork ()) {
380
381                 /*
382                  * The fork() failed completely.  Clean up as needed and
383                  * return to the caller.
384                  */
385                 case -1:
386                         if (use_pipe) {
387                                 close (pipes[0]);
388                                 close (pipes[1]);
389                         }
390                         return -1;
391                 case 0:
392
393                         /*
394                          * Let the child catch the SIGINT and SIGQUIT
395                          * signals.  The parent, however, will continue
396                          * to ignore them.
397                          */
398                         signal (SIGINT, SIG_DFL);
399                         signal (SIGQUIT, SIG_DFL);
400
401                         /*
402                          * Set up the command line.  The first argument is
403                          * the name of the command being executed.  The
404                          * second is the command line option for the reason,
405                          * and the third is the user name.
406                          */
407                         argv[argc++] = command;
408                         switch (reason) {
409                                 case PW_SU:     argv[argc++] = "-s"; break;
410                                 case PW_LOGIN:  argv[argc++] = "-l"; break;
411                                 case PW_ADD:    argv[argc++] = "-a"; break;
412                                 case PW_CHANGE: argv[argc++] = "-c"; break;
413                                 case PW_DELETE: argv[argc++] = "-d"; break;
414                                 case PW_TELNET: argv[argc++] = "-t"; break;
415                                 case PW_RLOGIN: argv[argc++] = "-r"; break;
416                                 case PW_FTP:    argv[argc++] = "-f"; break;
417                                 case PW_REXEC:  argv[argc++] = "-x"; break;
418                         }
419                         if (reason == PW_CHANGE && input)
420                                 argv[argc++] = input;
421
422                         argv[argc++] = user;
423                         argv[argc] = (char *) 0;
424
425                         /*
426                          * The FTP and REXEC reasons use a pipe to communicate
427                          * with the parent.  The other standard I/O descriptors
428                          * are closed and re-opened as /dev/null.
429                          */
430                         if (use_pipe) {
431                                 close (0);
432                                 close (1);
433                                 close (2);
434
435                                 if (dup (pipes[0]) != 0)
436                                         exit (1);
437
438                                 close (pipes[0]);
439                                 close (pipes[1]);
440
441                                 if (open ("/dev/null", O_WRONLY) != 1)
442                                         exit (1);
443
444                                 if (open ("/dev/null", O_WRONLY) != 2)
445                                         exit (1);
446                         }
447
448                         /*
449                          * Now we execute the command directly.
450                          * Do it with empty environment for safety.  --marekm
451                          */
452                         execve(command, argv, &empty_env);
453                         _exit((errno == ENOENT) ? 127 : 126);
454                         /*NOTREACHED*/
455                 default:
456                         /* 
457                          * FTP and REXEC cause a single line of text to be
458                          * sent to the child over a pipe that was set up
459                          * earlier.
460                          */
461                         if (use_pipe) {
462                                 close (pipes[0]);
463
464                                 if (input)
465                                         write (pipes[1], input, strlen (input));
466
467                                 write (pipes[1], "\n", 1);
468                                 close (pipes[1]);
469                         }
470
471                         /*
472                          * Wait on the child to die.  When it does you will
473                          * get the exit status and use that to determine if
474                          * the authentication program was successful.
475                          */
476                         while ((i = wait (&status)) != pid && i != -1)
477                                 ;
478
479                         /*
480                          * Re-set the signals to their earlier values.
481                          */
482                         signal (SIGINT, sigint);
483                         signal (SIGQUIT, sigquit);
484 #ifdef  SIGTSTP
485                         signal (SIGTSTP, sigtstp);
486 #endif
487
488                         /*
489                          * Make sure we found the right process!
490                          */
491                         if (i == -1)
492                                 return -1;
493
494                         if (status == 0)
495                                 return 0;
496                         else
497                                 return -1;
498         }
499         /*NOTREACHED*/
500 }
501
502 /*
503  * _builtin_auth - lookup routine in table and execute
504  */
505
506 static int
507 _builtin_auth(const char *command, const char *user, int reason, const char *input)
508 {
509         int     i;
510
511         /*
512          * Scan the table, looking for a match.  If we fall off
513          * the end, it must mean that this method isn't supported,
514          * so we fail the authentication.
515          */
516
517         for (i = 0;methods[i].name[0];i++) {
518                 if (! strcmp (command, methods[i].name))
519                         break;
520         }
521         if (methods[i].name[0] == '\0')
522                 return -1;
523
524         /*
525          * Call the pointed to function with the other three
526          * arguments.
527          */
528
529         return (methods[i].func) (user, reason, input);
530 }
531 #endif  /* AUTH_METHODS */
532
533 /*
534  * This function does the real work.  It splits the list of program names
535  * up into individual programs and executes them one at a time.
536  */
537
538 int
539 pw_auth(const char *command, const char *user, int reason, const char *input)
540 {
541 #ifdef AUTH_METHODS
542         char    buf[256];
543         char    *cmd, *end;
544         int     rc;
545
546         /* 
547          * Quick little sanity check ...
548          */
549
550         if (strlen (command) >= sizeof buf)
551                 return -1;
552
553         strcpy(buf, command); /* safe (because of the above check) --marekm */
554
555         /*
556          * Find each command and make sure it is NUL-terminated.  Then
557          * invoke _pw_auth to actually run the program.  The first
558          * failing program ends the whole mess.
559          */
560
561         for (cmd = buf;cmd;cmd = end) {
562                 if ((end = strchr (cmd, ';')))
563                         *end++ = '\0';
564
565                 if (cmd[0] != '@')
566                         rc = _old_auth (cmd, user, reason, input);
567                 else if (cmd[1] == '/')
568                         rc = _pw_auth (cmd + 1, user, reason, input);
569                 else
570                         rc = _builtin_auth (cmd + 1, user, reason, input);
571                 if (rc)
572                         return -1;
573         }
574         return 0;
575 #else
576         return _old_auth(command, user, reason, input);
577 #endif
578 }