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