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