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