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