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