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