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