]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/support.c
Relevant BUGIDs: 521314
[linux-pam] / modules / pam_unix / support.c
1 /* 
2  * $Id$
3  *
4  * Copyright information at end of file.
5  */
6
7 #define _BSD_SOURCE
8
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #include <malloc.h>
14 #include <pwd.h>
15 #include <shadow.h>
16 #include <limits.h>
17 #include <utmp.h>
18 #include <errno.h>
19
20 #include <security/_pam_macros.h>
21 #include <security/pam_modules.h>
22
23 #include "md5.h"
24 #include "support.h"
25
26 extern char *crypt(const char *key, const char *salt);
27 extern char *bigcrypt(const char *key, const char *salt);
28
29 /* syslogging function for errors and other information */
30
31 void _log_err(int err, pam_handle_t *pamh, const char *format,...)
32 {
33         char *service = NULL;
34         char logname[256];
35         va_list args;
36
37         pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
38         if (service) {
39                 strncpy(logname, service, sizeof(logname));
40                 logname[sizeof(logname) - 1 - strlen("(pam_unix)")] = '\0';
41                 strncat(logname, "(pam_unix)", strlen("(pam_unix)"));
42         } else {
43                 strncpy(logname, "pam_unix", sizeof(logname) - 1);
44         }
45
46         va_start(args, format);
47         openlog(logname, LOG_CONS | LOG_PID, LOG_AUTH);
48         vsyslog(err, format, args);
49         va_end(args);
50         closelog();
51 }
52
53 /* this is a front-end for module-application conversations */
54
55 static int converse(pam_handle_t * pamh, int ctrl, int nargs
56                     ,struct pam_message **message
57                     ,struct pam_response **response)
58 {
59         int retval;
60         struct pam_conv *conv;
61
62         D(("begin to converse"));
63
64         retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
65         if (retval == PAM_SUCCESS) {
66
67                 retval = conv->conv(nargs, (const struct pam_message **) message
68                                     ,response, conv->appdata_ptr);
69
70                 D(("returned from application's conversation function"));
71
72                 if (retval != PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) {
73                         _log_err(LOG_DEBUG, pamh, "conversation failure [%s]"
74                                  ,pam_strerror(pamh, retval));
75                 }
76         } else if (retval != PAM_CONV_AGAIN) {
77                 _log_err(LOG_ERR, pamh
78                          ,"couldn't obtain coversation function [%s]"
79                          ,pam_strerror(pamh, retval));
80         }
81         D(("ready to return from module conversation"));
82
83         return retval;          /* propagate error status */
84 }
85
86 int _make_remark(pam_handle_t * pamh, unsigned int ctrl
87                        ,int type, const char *text)
88 {
89         int retval = PAM_SUCCESS;
90
91         if (off(UNIX__QUIET, ctrl)) {
92                 struct pam_message *pmsg[1], msg[1];
93                 struct pam_response *resp;
94
95                 pmsg[0] = &msg[0];
96                 msg[0].msg = text;
97                 msg[0].msg_style = type;
98
99                 resp = NULL;
100                 retval = converse(pamh, ctrl, 1, pmsg, &resp);
101
102                 if (resp) {
103                         _pam_drop_reply(resp, 1);
104                 }
105         }
106         return retval;
107 }
108
109   /*
110    * Beacause getlogin() is braindead and sometimes it just
111    * doesn't work, we reimplement it here.
112    */
113 char *PAM_getlogin(void)
114 {
115         struct utmp *ut, line;
116         char *curr_tty, *retval;
117         static char curr_user[sizeof(ut->ut_user) + 4];
118
119         retval = NULL;
120
121         curr_tty = ttyname(0);
122         if (curr_tty != NULL) {
123                 D(("PAM_getlogin ttyname: %s", curr_tty));
124                 curr_tty += 5;
125                 setutent();
126                 strncpy(line.ut_line, curr_tty, sizeof(line.ut_line));
127                 if ((ut = getutline(&line)) != NULL) {
128                         strncpy(curr_user, ut->ut_user, sizeof(ut->ut_user));
129                         curr_user[sizeof(curr_user) - 1] = '\0';
130                         retval = curr_user;
131                 }
132                 endutent();
133         }
134         D(("PAM_getlogin retval: %s", retval));
135
136         return retval;
137 }
138
139 /*
140  * set the control flags for the UNIX module.
141  */
142
143 int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
144               const char **argv)
145 {
146         unsigned int ctrl;
147
148         D(("called."));
149
150         ctrl = UNIX_DEFAULTS;   /* the default selection of options */
151
152         /* set some flags manually */
153
154         if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
155                 D(("IAMROOT"));
156                 set(UNIX__IAMROOT, ctrl);
157         }
158         if (flags & PAM_UPDATE_AUTHTOK) {
159                 D(("UPDATE_AUTHTOK"));
160                 set(UNIX__UPDATE, ctrl);
161         }
162         if (flags & PAM_PRELIM_CHECK) {
163                 D(("PRELIM_CHECK"));
164                 set(UNIX__PRELIM, ctrl);
165         }
166         if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
167                 D(("DISALLOW_NULL_AUTHTOK"));
168                 set(UNIX__NONULL, ctrl);
169         }
170         if (flags & PAM_SILENT) {
171                 D(("SILENT"));
172                 set(UNIX__QUIET, ctrl);
173         }
174         /* now parse the arguments to this module */
175
176         while (argc-- > 0) {
177                 int j;
178
179                 D(("pam_unix arg: %s", *argv));
180
181                 for (j = 0; j < UNIX_CTRLS_; ++j) {
182                         if (unix_args[j].token
183                             && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
184                                 break;
185                         }
186                 }
187
188                 if (j >= UNIX_CTRLS_) {
189                         _log_err(LOG_ERR, pamh,
190                                  "unrecognized option [%s]", *argv);
191                 } else {
192                         ctrl &= unix_args[j].mask;      /* for turning things off */
193                         ctrl |= unix_args[j].flag;      /* for turning things on  */
194
195                         if (remember != NULL) {
196                                 if (j == UNIX_REMEMBER_PASSWD) {
197                                         *remember = strtol(*argv + 9, NULL, 10);
198                                         if ((*remember == LONG_MIN) || (*remember == LONG_MAX))
199                                                 *remember = -1;
200                                         if (*remember > 400)
201                                                 *remember = 400;
202                                 }
203                         }
204                 }
205
206                 ++argv;         /* step to next argument */
207         }
208
209         /* auditing is a more sensitive version of debug */
210
211         if (on(UNIX_AUDIT, ctrl)) {
212                 set(UNIX_DEBUG, ctrl);
213         }
214         /* return the set of flags */
215
216         D(("done."));
217         return ctrl;
218 }
219
220 static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
221 {
222         _pam_delete(x);
223 }
224
225 /* ************************************************************** *
226  * Useful non-trivial functions                                   *
227  * ************************************************************** */
228
229   /*
230    * the following is used to keep track of the number of times a user fails
231    * to authenticate themself.
232    */
233
234 #define FAIL_PREFIX                   "-UN*X-FAIL-"
235 #define UNIX_MAX_RETRIES              3
236
237 struct _pam_failed_auth {
238         char *user;             /* user that's failed to be authenticated */
239         char *name;             /* attempt from user with name */
240         int uid;                /* uid of calling user */
241         int euid;               /* euid of calling process */
242         int count;              /* number of failures so far */
243 };
244
245 #ifndef PAM_DATA_REPLACE
246 #error "Need to get an updated libpam 0.52 or better"
247 #endif
248
249 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
250 {
251         int quiet;
252         const char *service = NULL;
253         const char *ruser = NULL;
254         const char *rhost = NULL;
255         const char *tty = NULL;
256         struct _pam_failed_auth *failure;
257
258         D(("called"));
259
260         quiet = err & PAM_DATA_SILENT;  /* should we log something? */
261         err &= PAM_DATA_REPLACE;        /* are we just replacing data? */
262         failure = (struct _pam_failed_auth *) fl;
263
264         if (failure != NULL) {
265
266                 if (!quiet && !err) {   /* under advisement from Sun,may go away */
267
268                         /* log the number of authentication failures */
269                         if (failure->count > 1) {
270                                 (void) pam_get_item(pamh, PAM_SERVICE,
271                                                     (const void **)&service);
272                                 (void) pam_get_item(pamh, PAM_RUSER,
273                                                     (const void **)&ruser);
274                                 (void) pam_get_item(pamh, PAM_RHOST,
275                                                     (const void **)&rhost);
276                                 (void) pam_get_item(pamh, PAM_TTY,
277                                                     (const void **)&tty);
278                                 _log_err(LOG_NOTICE, pamh,
279                                          "%d more authentication failure%s; "
280                                          "logname=%s uid=%d euid=%d "
281                                          "tty=%s ruser=%s rhost=%s "
282                                          "%s%s",
283                                          failure->count - 1, failure->count == 2 ? "" : "s",
284                                          failure->name, failure->uid, failure->euid,
285                                          tty ? tty : "", ruser ? ruser : "",
286                                          rhost ? rhost : "",
287                                          (failure->user && failure->user[0] != '\0')
288                                           ? " user=" : "", failure->user
289                                 );
290
291                                 if (failure->count > UNIX_MAX_RETRIES) {
292                                         _log_err(LOG_ALERT, pamh
293                                                  ,"service(%s) ignoring max retries; %d > %d"
294                                                  ,service == NULL ? "**unknown**" : service
295                                                  ,failure->count
296                                                  ,UNIX_MAX_RETRIES);
297                                 }
298                         }
299                 }
300                 _pam_delete(failure->user);     /* tidy up */
301                 _pam_delete(failure->name);     /* tidy up */
302                 free(failure);
303         }
304 }
305
306 /*
307  * _unix_blankpasswd() is a quick check for a blank password
308  *
309  * returns TRUE if user does not have a password
310  * - to avoid prompting for one in such cases (CG)
311  */
312
313 int _unix_blankpasswd(unsigned int ctrl, const char *name)
314 {
315         struct passwd *pwd = NULL;
316         struct spwd *spwdent = NULL;
317         char *salt = NULL;
318         int retval;
319 #if HAVE_GETPWNAM_R
320         char *buf = NULL;
321         int bufsize = 0;
322         struct passwd pwd_buf;
323
324         pwd = &pwd_buf;
325 #endif
326
327         D(("called"));
328
329         /*
330          * This function does not have to be too smart if something goes
331          * wrong, return FALSE and let this case to be treated somewhere
332          * else (CG)
333          */
334
335         if (on(UNIX__NONULL, ctrl))
336                 return 0;       /* will fail but don't let on yet */
337
338         /* UNIX passwords area */
339
340         /* Get password file entry... */
341 #if HAVE_GETPWNAM_R
342         bufsize = 1024;
343         buf = malloc(bufsize);
344
345         if ((retval = getpwnam_r(name, pwd, buf, bufsize, &pwd))) {
346                 pwd = NULL;
347         }
348         while (retval == ERANGE) {
349                 bufsize += 1024;
350                 buf = realloc(buf, bufsize);
351                 if ((retval = getpwnam_r(name, pwd, buf, bufsize, &pwd))) {
352                         pwd = NULL;
353                 }
354         }
355 #else
356         pwd = getpwnam(name);
357 #endif
358
359         if (pwd != NULL) {
360                 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
361                 { /* NIS+ */                 
362                         uid_t save_euid, save_uid;
363         
364                         save_euid = geteuid();
365                         save_uid = getuid();
366                         if (save_uid == pwd->pw_uid)
367                                 setreuid( save_euid, save_uid );
368                         else  {
369                                 setreuid( 0, -1 );
370                                 if (setreuid( -1, pwd->pw_uid ) == -1) {
371                                         setreuid( -1, 0 );
372                                         setreuid( 0, -1 );
373                                         if(setreuid( -1, pwd->pw_uid ) == -1)
374                                                 /* Will fail elsewhere. */
375 #if HAVE_GETPWNAM_R
376                                                 if (buf)
377                                                         free(buf);
378 #endif
379                                                 return 0;
380                                 }
381                         }
382         
383                         spwdent = getspnam( name );
384                         if (save_uid == pwd->pw_uid)
385                                 setreuid( save_uid, save_euid );
386                         else {
387                                 if (setreuid( -1, 0 ) == -1)
388                                 setreuid( save_uid, -1 );
389                                 setreuid( -1, save_euid );
390                         }
391                 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
392                         /*
393                          * ...and shadow password file entry for this user,
394                          * if shadowing is enabled
395                          */
396                         spwdent = getspnam(name);
397                 }
398                 if (spwdent)
399                         salt = x_strdup(spwdent->sp_pwdp);
400                 else
401                         salt = x_strdup(pwd->pw_passwd);
402         }
403         /* Does this user have a password? */
404         if (salt == NULL) {
405                 retval = 0;
406         } else {
407                 if (strlen(salt) == 0)
408                         retval = 1;
409                 else
410                         retval = 0;
411         }
412
413         /* tidy up */
414
415         if (salt)
416                 _pam_delete(salt);
417
418 #if HAVE_GETPWNAM_R
419         if (buf)
420                 free(buf);
421 #endif
422
423         return retval;
424 }
425
426 /*
427  * verify the password of a user
428  */
429
430 #include <sys/types.h>
431 #include <sys/wait.h>
432
433 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
434                                    unsigned int ctrl, const char *user)
435 {
436     int retval, child, fds[2];
437
438     D(("called."));
439     /* create a pipe for the password */
440     if (pipe(fds) != 0) {
441         D(("could not make pipe"));
442         return PAM_AUTH_ERR;
443     }
444
445     /* fork */
446     child = fork();
447     if (child == 0) {
448         static char *envp[] = { NULL };
449         char *args[] = { NULL, NULL, NULL };
450
451         /* XXX - should really tidy up PAM here too */
452
453         /* reopen stdin as pipe */
454         close(fds[1]);
455         dup2(fds[0], STDIN_FILENO);
456
457         /* exec binary helper */
458         args[0] = x_strdup(CHKPWD_HELPER);
459         args[1] = x_strdup(user);
460
461         execve(CHKPWD_HELPER, args, envp);
462
463         /* should not get here: exit with error */
464         D(("helper binary is not available"));
465         exit(PAM_AUTHINFO_UNAVAIL);
466     } else if (child > 0) {
467         /* wait for child */
468         /* if the stored password is NULL */
469         if (off(UNIX__NONULL, ctrl)) {  /* this means we've succeeded */
470             write(fds[1], "nullok\0\0", 8);
471         } else {
472             write(fds[1], "nonull\0\0", 8);
473         }
474         if (passwd != NULL) {            /* send the password to the child */
475             write(fds[1], passwd, strlen(passwd)+1);
476             passwd = NULL;
477         } else {
478             write(fds[1], "", 1);                        /* blank password */
479         }
480         close(fds[0]);       /* close here to avoid possible SIGPIPE above */
481         close(fds[1]);
482         (void) waitpid(child, &retval, 0);  /* wait for helper to complete */
483         retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR;
484     } else {
485         D(("fork failed"));
486         retval = PAM_AUTH_ERR;
487     }
488
489     D(("returning %d", retval));
490     return retval;
491 }
492
493 int _unix_verify_password(pam_handle_t * pamh, const char *name
494                           ,const char *p, unsigned int ctrl)
495 {
496         struct passwd *pwd = NULL;
497         struct spwd *spwdent = NULL;
498         char *salt = NULL;
499         char *pp = NULL;
500         char *data_name;
501         int retval;
502
503         D(("called"));
504
505 #ifdef HAVE_PAM_FAIL_DELAY
506         if (off(UNIX_NODELAY, ctrl)) {
507                 D(("setting delay"));
508                 (void) pam_fail_delay(pamh, 2000000);   /* 2 sec delay for on failure */
509         }
510 #endif
511
512         /* locate the entry for this user */
513
514         D(("locating user's record"));
515
516         /* UNIX passwords area */
517         pwd = getpwnam(name);   /* Get password file entry... */
518
519         if (pwd != NULL) {
520                 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
521                 { /* NIS+ */                 
522                         uid_t save_euid, save_uid;
523         
524                         save_euid = geteuid();
525                         save_uid = getuid();
526                         if (save_uid == pwd->pw_uid)
527                                 setreuid( save_euid, save_uid );
528                         else  {
529                                 setreuid( 0, -1 );
530                                 if (setreuid( -1, pwd->pw_uid ) == -1) {
531                                         setreuid( -1, 0 );
532                                         setreuid( 0, -1 );
533                                         if(setreuid( -1, pwd->pw_uid ) == -1)
534                                                 return PAM_CRED_INSUFFICIENT;
535                                 }
536                         }
537         
538                         spwdent = getspnam( name );
539                         if (save_uid == pwd->pw_uid)
540                                 setreuid( save_uid, save_euid );
541                         else {
542                                 if (setreuid( -1, 0 ) == -1)
543                                 setreuid( save_uid, -1 );
544                                 setreuid( -1, save_euid );
545                         }
546                 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
547                         /*
548                          * ...and shadow password file entry for this user,
549                          * if shadowing is enabled
550                          */
551                         spwdent = getspnam(name);
552                 }
553                 if (spwdent)
554                         salt = x_strdup(spwdent->sp_pwdp);
555                 else
556                         salt = x_strdup(pwd->pw_passwd);
557         }
558
559         data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
560         if (data_name == NULL) {
561                 _log_err(LOG_CRIT, pamh, "no memory for data-name");
562         } else {
563                 strcpy(data_name, FAIL_PREFIX);
564                 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
565         }
566
567         retval = PAM_SUCCESS;
568         if (pwd == NULL || salt == NULL || !strcmp(salt, "x")) {
569                 if (geteuid()) {
570                         /* we are not root perhaps this is the reason? Run helper */
571                         D(("running helper binary"));
572                         retval = _unix_run_helper_binary(pamh, p, ctrl, name);
573                         if (pwd == NULL && !on(UNIX_AUDIT,ctrl)
574                             && retval != PAM_SUCCESS)
575                         {
576                                 name = NULL;
577                         }
578                 } else {
579                         D(("user's record unavailable"));
580                         if (on(UNIX_AUDIT, ctrl)) {
581                                 /* this might be a typo and the user has given a password
582                                    instead of a username. Careful with this. */
583                                 _log_err(LOG_ALERT, pamh,
584                                          "check pass; user (%s) unknown", name);
585                         } else {
586                                 name = NULL;
587                                 _log_err(LOG_ALERT, pamh,
588                                          "check pass; user unknown");
589                         }
590                         p = NULL;
591                         retval = PAM_AUTHINFO_UNAVAIL;
592                 }
593         } else {
594                 if (!strlen(salt)) {
595                         /* the stored password is NULL */
596                         if (off(UNIX__NONULL, ctrl)) {  /* this means we've succeeded */
597                                 D(("user has empty password - access granted"));
598                                 retval = PAM_SUCCESS;
599                         } else {
600                                 D(("user has empty password - access denied"));
601                                 retval = PAM_AUTH_ERR;
602                         }
603                 } else if (!p) {
604                                 retval = PAM_AUTH_ERR;
605                 } else {
606                         if (!strncmp(salt, "$1$", 3)) {
607                                 pp = Goodcrypt_md5(p, salt);
608                                 if (strcmp(pp, salt) != 0) {
609                                         _pam_delete(pp);
610                                         pp = Brokencrypt_md5(p, salt);
611                                 }
612                         } else {
613                                 pp = bigcrypt(p, salt);
614                         }
615                         p = NULL;               /* no longer needed here */
616
617                         /* the moment of truth -- do we agree with the password? */
618                         D(("comparing state of pp[%s] and salt[%s]", pp, salt));
619
620                         /*
621                          * Note, we are comparing the bigcrypt of the password with
622                          * the contents of the password field. If the latter was
623                          * encrypted with regular crypt (and not bigcrypt) it will
624                          * have been truncated for storage relative to the output
625                          * of bigcrypt here. As such we need to compare only the
626                          * stored string with the subset of bigcrypt's result.
627                          * Bug 521314: The strncmp comparison is for legacy support.
628                          */
629                         if (strncmp(pp, salt, strlen(salt)) == 0) {
630                                 retval = PAM_SUCCESS;
631                         } else {
632                                 retval = PAM_AUTH_ERR;
633                         }
634                 }
635         }
636
637         if (retval == PAM_SUCCESS) {
638                 if (data_name)  /* reset failures */
639                         pam_set_data(pamh, data_name, NULL, _cleanup_failures);
640         } else {
641                 if (data_name != NULL) {
642                         struct _pam_failed_auth *new = NULL;
643                         const struct _pam_failed_auth *old = NULL;
644
645                         /* get a failure recorder */
646
647                         new = (struct _pam_failed_auth *)
648                             malloc(sizeof(struct _pam_failed_auth));
649
650                         if (new != NULL) {
651
652                                 new->user = x_strdup(name ? name : "");
653                                 new->uid = getuid();
654                                 new->euid = geteuid();
655                                 new->name = x_strdup(PAM_getlogin()? PAM_getlogin() : "");
656
657                                 /* any previous failures for this user ? */
658                                 pam_get_data(pamh, data_name, (const void **) &old);
659
660                                 if (old != NULL) {
661                                         new->count = old->count + 1;
662                                         if (new->count >= UNIX_MAX_RETRIES) {
663                                                 retval = PAM_MAXTRIES;
664                                         }
665                                 } else {
666                                         const char *service=NULL;
667                                         const char *ruser=NULL;
668                                         const char *rhost=NULL;
669                                         const char *tty=NULL;
670
671                                         (void) pam_get_item(pamh, PAM_SERVICE,
672                                                             (const void **)&service);
673                                         (void) pam_get_item(pamh, PAM_RUSER,
674                                                             (const void **)&ruser);
675                                         (void) pam_get_item(pamh, PAM_RHOST,
676                                                             (const void **)&rhost);
677                                         (void) pam_get_item(pamh, PAM_TTY,
678                                                             (const void **)&tty);
679
680                                         _log_err(LOG_NOTICE, pamh,
681                                                  "authentication failure; "
682                                                  "logname=%s uid=%d euid=%d "
683                                                  "tty=%s ruser=%s rhost=%s "
684                                                  "%s%s",
685                                                  new->name, new->uid, new->euid,
686                                                  tty ? tty : "",
687                                                  ruser ? ruser : "",
688                                                  rhost ? rhost : "",
689                                                  (new->user && new->user[0] != '\0')
690                                                   ? " user=" : "",
691                                                  new->user
692                                         );
693                                         new->count = 1;
694                                 }
695
696                                 pam_set_data(pamh, data_name, new, _cleanup_failures);
697
698                         } else {
699                                 _log_err(LOG_CRIT, pamh,
700                                          "no memory for failure recorder");
701                         }
702                 }
703         }
704
705         if (data_name)
706                 _pam_delete(data_name);
707         if (salt)
708                 _pam_delete(salt);
709         if (pp)
710                 _pam_delete(pp);
711
712         D(("done [%d].", retval));
713
714         return retval;
715 }
716
717 /*
718  * obtain a password from the user
719  */
720
721 int _unix_read_password(pam_handle_t * pamh
722                         ,unsigned int ctrl
723                         ,const char *comment
724                         ,const char *prompt1
725                         ,const char *prompt2
726                         ,const char *data_name
727                         ,const char **pass)
728 {
729         int authtok_flag;
730         int retval;
731         char *token;
732
733         D(("called"));
734
735         /*
736          * make sure nothing inappropriate gets returned
737          */
738
739         *pass = token = NULL;
740
741         /*
742          * which authentication token are we getting?
743          */
744
745         authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
746
747         /*
748          * should we obtain the password from a PAM item ?
749          */
750
751         if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
752                 retval = pam_get_item(pamh, authtok_flag, (const void **) pass);
753                 if (retval != PAM_SUCCESS) {
754                         /* very strange. */
755                         _log_err(LOG_ALERT, pamh
756                                  ,"pam_get_item returned error to unix-read-password"
757                             );
758                         return retval;
759                 } else if (*pass != NULL) {     /* we have a password! */
760                         return PAM_SUCCESS;
761                 } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
762                         return PAM_AUTHTOK_RECOVER_ERR;         /* didn't work */
763                 } else if (on(UNIX_USE_AUTHTOK, ctrl)
764                            && off(UNIX__OLD_PASSWD, ctrl)) {
765                         return PAM_AUTHTOK_RECOVER_ERR;
766                 }
767         }
768         /*
769          * getting here implies we will have to get the password from the
770          * user directly.
771          */
772
773         {
774                 struct pam_message msg[3], *pmsg[3];
775                 struct pam_response *resp;
776                 int i, replies;
777
778                 /* prepare to converse */
779
780                 if (comment != NULL && off(UNIX__QUIET, ctrl)) {
781                         pmsg[0] = &msg[0];
782                         msg[0].msg_style = PAM_TEXT_INFO;
783                         msg[0].msg = comment;
784                         i = 1;
785                 } else {
786                         i = 0;
787                 }
788
789                 pmsg[i] = &msg[i];
790                 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
791                 msg[i++].msg = prompt1;
792                 replies = 1;
793
794                 if (prompt2 != NULL) {
795                         pmsg[i] = &msg[i];
796                         msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
797                         msg[i++].msg = prompt2;
798                         ++replies;
799                 }
800                 /* so call the conversation expecting i responses */
801                 resp = NULL;
802                 retval = converse(pamh, ctrl, i, pmsg, &resp);
803
804                 if (resp != NULL) {
805
806                         /* interpret the response */
807
808                         if (retval == PAM_SUCCESS) {    /* a good conversation */
809
810                                 token = x_strdup(resp[i - replies].resp);
811                                 if (token != NULL) {
812                                         if (replies == 2) {
813
814                                                 /* verify that password entered correctly */
815                                                 if (!resp[i - 1].resp
816                                                     || strcmp(token, resp[i - 1].resp)) {
817                                                         _pam_delete(token);     /* mistyped */
818                                                         retval = PAM_AUTHTOK_RECOVER_ERR;
819                                                         _make_remark(pamh, ctrl
820                                                                     ,PAM_ERROR_MSG, MISTYPED_PASS);
821                                                 }
822                                         }
823                                 } else {
824                                         _log_err(LOG_NOTICE, pamh
825                                                  ,"could not recover authentication token");
826                                 }
827
828                         }
829                         /*
830                          * tidy up the conversation (resp_retcode) is ignored
831                          * -- what is it for anyway? AGM
832                          */
833
834                         _pam_drop_reply(resp, i);
835
836                 } else {
837                         retval = (retval == PAM_SUCCESS)
838                             ? PAM_AUTHTOK_RECOVER_ERR : retval;
839                 }
840         }
841
842         if (retval != PAM_SUCCESS) {
843                 if (on(UNIX_DEBUG, ctrl))
844                         _log_err(LOG_DEBUG, pamh,
845                                  "unable to obtain a password");
846                 return retval;
847         }
848         /* 'token' is the entered password */
849
850         if (off(UNIX_NOT_SET_PASS, ctrl)) {
851
852                 /* we store this password as an item */
853
854                 retval = pam_set_item(pamh, authtok_flag, token);
855                 _pam_delete(token);     /* clean it up */
856                 if (retval != PAM_SUCCESS
857                     || (retval = pam_get_item(pamh, authtok_flag
858                                               ,(const void **) pass))
859                     != PAM_SUCCESS) {
860
861                         *pass = NULL;
862                         _log_err(LOG_CRIT, pamh, "error manipulating password");
863                         return retval;
864
865                 }
866         } else {
867                 /*
868                  * then store it as data specific to this module. pam_end()
869                  * will arrange to clean it up.
870                  */
871
872                 retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
873                 if (retval != PAM_SUCCESS) {
874                         _log_err(LOG_CRIT, pamh
875                                  ,"error manipulating password data [%s]"
876                                  ,pam_strerror(pamh, retval));
877                         _pam_delete(token);
878                         return retval;
879                 }
880                 *pass = token;
881                 token = NULL;   /* break link to password */
882         }
883
884         return PAM_SUCCESS;
885 }
886
887 /* ****************************************************************** *
888  * Copyright (c) Jan Rêkorajski 1999.
889  * Copyright (c) Andrew G. Morgan 1996-8.
890  * Copyright (c) Alex O. Yuriev, 1996.
891  * Copyright (c) Cristian Gafton 1996.
892  *
893  * Redistribution and use in source and binary forms, with or without
894  * modification, are permitted provided that the following conditions
895  * are met:
896  * 1. Redistributions of source code must retain the above copyright
897  *    notice, and the entire permission notice in its entirety,
898  *    including the disclaimer of warranties.
899  * 2. Redistributions in binary form must reproduce the above copyright
900  *    notice, this list of conditions and the following disclaimer in the
901  *    documentation and/or other materials provided with the distribution.
902  * 3. The name of the author may not be used to endorse or promote
903  *    products derived from this software without specific prior
904  *    written permission.
905  * 
906  * ALTERNATIVELY, this product may be distributed under the terms of
907  * the GNU Public License, in which case the provisions of the GPL are
908  * required INSTEAD OF the above restrictions.  (This clause is
909  * necessary due to a potential bad interaction between the GPL and
910  * the restrictions contained in a BSD-style copyright.)
911  * 
912  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
913  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
914  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
915  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
916  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
917  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
918  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
919  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
920  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
921  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
922  * OF THE POSSIBILITY OF SUCH DAMAGE.
923  */