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