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