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