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