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