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