1 /*
2 * Copyright information at end of file.
3 */
4
5 #include "config.h"
6
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <malloc.h>
13 #include <pwd.h>
14 #include <shadow.h>
15 #include <limits.h>
16 #include <utmp.h>
17 #include <errno.h>
18 #include <signal.h>
19 #include <ctype.h>
20 #include <syslog.h>
21 #include <sys/resource.h>
22 #ifdef HAVE_NIS
23 #include <rpcsvc/ypclnt.h>
24 #endif
25
26 #include <security/_pam_macros.h>
27 #include <security/pam_modules.h>
28 #include <security/pam_ext.h>
29 #include <security/pam_modutil.h>
30
31 #include "pam_cc_compat.h"
32 #include "pam_inline.h"
33 #include "support.h"
34 #include "passverify.h"
35
36 /* this is a front-end for module-application conversations */
37
38 int _make_remark(pam_handle_t * pamh, unsigned long long ctrl,
39 int type, const char *text)
40 {
41 int retval = PAM_SUCCESS;
42
43 if (off(UNIX__QUIET, ctrl)) {
44 retval = pam_prompt(pamh, type, NULL, "%s", text);
45 }
46 return retval;
47 }
48
49 /*
50 * set the control flags for the UNIX module.
51 */
52
53 unsigned long long _set_ctrl(pam_handle_t *pamh, int flags, int *remember,
54 int *rounds, int *pass_min_len, int argc,
55 const char **argv)
56 {
57 unsigned long long ctrl;
58 char *val;
59 int j;
60
61 D(("called."));
62
63 ctrl = UNIX_DEFAULTS; /* the default selection of options */
64
65 /* set some flags manually */
66
67 if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
68 D(("IAMROOT"));
69 set(UNIX__IAMROOT, ctrl);
70 }
71 if (flags & PAM_UPDATE_AUTHTOK) {
72 D(("UPDATE_AUTHTOK"));
73 set(UNIX__UPDATE, ctrl);
74 }
75 if (flags & PAM_PRELIM_CHECK) {
76 D(("PRELIM_CHECK"));
77 set(UNIX__PRELIM, ctrl);
78 }
79 if (flags & PAM_SILENT) {
80 D(("SILENT"));
81 set(UNIX__QUIET, ctrl);
82 }
83
84 /* preset encryption method with value from /etc/login.defs */
85 val = pam_modutil_search_key(pamh, LOGIN_DEFS, "ENCRYPT_METHOD");
86 if (val) {
87 for (j = 0; j < UNIX_CTRLS_; ++j) {
88 if (unix_args[j].token && unix_args[j].is_hash_algo
89 && !strncasecmp(val, unix_args[j].token, strlen(unix_args[j].token))) {
90 break;
91 }
92 }
93 if (j >= UNIX_CTRLS_) {
94 pam_syslog(pamh, LOG_WARNING, "unrecognized ENCRYPT_METHOD value [%s]", val);
95 } else {
96 ctrl &= unix_args[j].mask; /* for turning things off */
97 ctrl |= unix_args[j].flag; /* for turning things on */
98 }
99 free (val);
100
101 /* read number of rounds for crypt algo */
102 if (rounds && (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl))) {
103 val = pam_modutil_search_key(pamh, LOGIN_DEFS, "SHA_CRYPT_MAX_ROUNDS");
104
105 if (val) {
106 *rounds = strtol(val, NULL, 10);
107 set(UNIX_ALGO_ROUNDS, ctrl);
108 free (val);
109 }
110 }
111 }
112
113 /* now parse the arguments to this module */
114
115 for (; argc-- > 0; ++argv) {
116 const char *str = NULL;
117
118 D(("pam_unix arg: %s", *argv));
119
120 for (j = 0; j < UNIX_CTRLS_; ++j) {
121 if (unix_args[j].token
122 && (str = pam_str_skip_prefix_len(*argv,
123 unix_args[j].token,
124 strlen(unix_args[j].token))) != NULL) {
125 break;
126 }
127 }
128
129 if (str == NULL) {
130 pam_syslog(pamh, LOG_ERR,
131 "unrecognized option [%s]", *argv);
132 } else {
133 /* special cases */
134 if (j == UNIX_REMEMBER_PASSWD) {
135 if (remember == NULL) {
136 pam_syslog(pamh, LOG_ERR,
137 "option remember not allowed for this module type");
138 continue;
139 }
140 *remember = strtol(str, NULL, 10);
141 if ((*remember == INT_MIN) || (*remember == INT_MAX))
142 *remember = -1;
143 if (*remember > 400)
144 *remember = 400;
145 } else if (j == UNIX_MIN_PASS_LEN) {
146 if (pass_min_len == NULL) {
147 pam_syslog(pamh, LOG_ERR,
148 "option minlen not allowed for this module type");
149 continue;
150 }
151 *pass_min_len = atoi(str);
152 } else if (j == UNIX_ALGO_ROUNDS) {
153 if (rounds == NULL) {
154 pam_syslog(pamh, LOG_ERR,
155 "option rounds not allowed for this module type");
156 continue;
157 }
158 *rounds = strtol(str, NULL, 10);
159 }
160
161 ctrl &= unix_args[j].mask; /* for turning things off */
162 ctrl |= unix_args[j].flag; /* for turning things on */
163 }
164 }
165
166 if (UNIX_DES_CRYPT(ctrl)
167 && pass_min_len && *pass_min_len > 8)
168 {
169 pam_syslog (pamh, LOG_NOTICE, "Password minlen reset to 8 characters");
170 *pass_min_len = 8;
171 }
172
173 if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
174 D(("DISALLOW_NULL_AUTHTOK"));
175 set(UNIX__NONULL, ctrl);
176 }
177
178 /* Set default rounds for blowfish, gost-yescrypt and yescrypt */
179 if (off(UNIX_ALGO_ROUNDS, ctrl) && rounds != NULL) {
180 if (on(UNIX_BLOWFISH_PASS, ctrl) ||
181 on(UNIX_GOST_YESCRYPT_PASS, ctrl) ||
182 on(UNIX_YESCRYPT_PASS, ctrl)) {
183 *rounds = 5;
184 set(UNIX_ALGO_ROUNDS, ctrl);
185 }
186 }
187
188 /* Enforce sane "rounds" values */
189 if (on(UNIX_ALGO_ROUNDS, ctrl)) {
190 if (on(UNIX_GOST_YESCRYPT_PASS, ctrl) ||
191 on(UNIX_YESCRYPT_PASS, ctrl)) {
192 if (*rounds < 3 || *rounds > 11)
193 *rounds = 5;
194 } else if (on(UNIX_BLOWFISH_PASS, ctrl)) {
195 if (*rounds < 4 || *rounds > 31)
196 *rounds = 5;
197 } else if (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl)) {
198 if ((*rounds < 1000) || (*rounds == INT_MAX)) {
199 /* don't care about bogus values */
200 *rounds = 0;
201 unset(UNIX_ALGO_ROUNDS, ctrl);
202 } else if (*rounds >= 10000000) {
203 *rounds = 9999999;
204 }
205 }
206 }
207
208 /* auditing is a more sensitive version of debug */
209
210 if (on(UNIX_AUDIT, ctrl)) {
211 set(UNIX_DEBUG, ctrl);
212 }
213 /* return the set of flags */
214
215 D(("done."));
216 return ctrl;
217 }
218
219 /* ************************************************************** *
220 * Useful non-trivial functions *
221 * ************************************************************** */
222
223 /*
224 * the following is used to keep track of the number of times a user fails
225 * to authenticate themself.
226 */
227
228 #define FAIL_PREFIX "-UN*X-FAIL-"
229 #define UNIX_MAX_RETRIES 3
230
231 struct _pam_failed_auth {
232 char *user; /* user that's failed to be authenticated */
233 char *name; /* attempt from user with name */
234 int uid; /* uid of calling user */
235 int euid; /* euid of calling process */
236 int count; /* number of failures so far */
237 };
238
239 #ifndef PAM_DATA_REPLACE
240 #error "Need to get an updated libpam 0.52 or better"
241 #endif
242
243 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
244 {
245 int quiet;
246 const void *service = NULL;
247 const void *ruser = NULL;
248 const void *rhost = NULL;
249 const void *tty = NULL;
250 struct _pam_failed_auth *failure;
251
252 D(("called"));
253
254 quiet = err & PAM_DATA_SILENT; /* should we log something? */
255 err &= PAM_DATA_REPLACE; /* are we just replacing data? */
256 failure = (struct _pam_failed_auth *) fl;
257
258 if (failure != NULL) {
259
260 if (!quiet && !err) { /* under advisement from Sun,may go away */
261
262 /* log the number of authentication failures */
263 if (failure->count > 1) {
264 (void) pam_get_item(pamh, PAM_SERVICE,
265 &service);
266 (void) pam_get_item(pamh, PAM_RUSER,
267 &ruser);
268 (void) pam_get_item(pamh, PAM_RHOST,
269 &rhost);
270 (void) pam_get_item(pamh, PAM_TTY,
271 &tty);
272 pam_syslog(pamh, LOG_NOTICE,
273 "%d more authentication failure%s; "
274 "logname=%s uid=%d euid=%d "
275 "tty=%s ruser=%s rhost=%s "
276 "%s%s",
277 failure->count - 1, failure->count == 2 ? "" : "s",
278 failure->name, failure->uid, failure->euid,
279 tty ? (const char *)tty : "", ruser ? (const char *)ruser : "",
280 rhost ? (const char *)rhost : "",
281 (failure->user && failure->user[0] != '\0')
282 ? " user=" : "", failure->user
283 );
284
285 if (failure->count > UNIX_MAX_RETRIES) {
286 pam_syslog(pamh, LOG_NOTICE,
287 "service(%s) ignoring max retries; %d > %d",
288 service == NULL ? "**unknown**" : (const char *)service,
289 failure->count,
290 UNIX_MAX_RETRIES);
291 }
292 }
293 }
294 _pam_delete(failure->user); /* tidy up */
295 _pam_delete(failure->name); /* tidy up */
296 free(failure);
297 }
298 }
299
300 /*
301 * _unix_getpwnam() searches only /etc/passwd and NIS to find user information
302 */
303 static void _unix_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED)
304 {
305 free(data);
306 }
307
308 int _unix_getpwnam(pam_handle_t *pamh, const char *name,
309 int files, int nis, struct passwd **ret)
310 {
311 FILE *passwd;
312 char buf[16384];
313 int matched = 0, buflen;
314 char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p;
315
316 memset(buf, 0, sizeof(buf));
317
318 if (!matched && files) {
319 int userlen = strlen(name);
320 passwd = fopen("/etc/passwd", "r");
321 if (passwd != NULL) {
322 while (fgets(buf, sizeof(buf), passwd) != NULL) {
323 if ((buf[userlen] == ':') &&
324 (strncmp(name, buf, userlen) == 0)) {
325 p = buf + strlen(buf) - 1;
326 while (isspace(*p) && (p >= buf)) {
327 *p-- = '\0';
328 }
329 matched = 1;
330 break;
331 }
332 }
333 fclose(passwd);
334 }
335 }
336
337 #if defined(HAVE_YP_GET_DEFAULT_DOMAIN) && defined (HAVE_YP_BIND) && defined (HAVE_YP_MATCH) && defined (HAVE_YP_UNBIND)
338 if (!matched && nis) {
339 char *userinfo = NULL, *domain = NULL;
340 int len = 0, i;
341 len = yp_get_default_domain(&domain);
342 if (len == YPERR_SUCCESS) {
343 len = yp_bind(domain);
344 }
345 if (len == YPERR_SUCCESS) {
346 i = yp_match(domain, "passwd.byname", name,
347 strlen(name), &userinfo, &len);
348 yp_unbind(domain);
349 if ((i == YPERR_SUCCESS) && ((size_t)len < sizeof(buf))) {
350 strncpy(buf, userinfo, sizeof(buf) - 1);
351 buf[sizeof(buf) - 1] = '\0';
352 matched = 1;
353 }
354 }
355 }
356 #else
357 /* we don't have NIS support, make compiler happy. */
358 (void) nis;
359 #endif
360
361 if (matched && (ret != NULL)) {
362 *ret = NULL;
363
364 slogin = buf;
365
366 spasswd = strchr(slogin, ':');
367 if (spasswd == NULL) {
368 return matched;
369 }
370 *spasswd++ = '\0';
371
372 suid = strchr(spasswd, ':');
373 if (suid == NULL) {
374 return matched;
375 }
376 *suid++ = '\0';
377
378 sgid = strchr(suid, ':');
379 if (sgid == NULL) {
380 return matched;
381 }
382 *sgid++ = '\0';
383
384 sgecos = strchr(sgid, ':');
385 if (sgecos == NULL) {
386 return matched;
387 }
388 *sgecos++ = '\0';
389
390 shome = strchr(sgecos, ':');
391 if (shome == NULL) {
392 return matched;
393 }
394 *shome++ = '\0';
395
396 sshell = strchr(shome, ':');
397 if (sshell == NULL) {
398 return matched;
399 }
400 *sshell++ = '\0';
401
402 buflen = sizeof(struct passwd) +
403 strlen(slogin) + 1 +
404 strlen(spasswd) + 1 +
405 strlen(sgecos) + 1 +
406 strlen(shome) + 1 +
407 strlen(sshell) + 1;
408 *ret = malloc(buflen);
409 if (*ret == NULL) {
410 return matched;
411 }
412 memset(*ret, '\0', buflen);
413
414 (*ret)->pw_uid = strtol(suid, &p, 10);
415 if ((strlen(suid) == 0) || (*p != '\0')) {
416 free(*ret);
417 *ret = NULL;
418 return matched;
419 }
420
421 (*ret)->pw_gid = strtol(sgid, &p, 10);
422 if ((strlen(sgid) == 0) || (*p != '\0')) {
423 free(*ret);
424 *ret = NULL;
425 return matched;
426 }
427
428 p = ((char*)(*ret)) + sizeof(struct passwd);
429 (*ret)->pw_name = strcpy(p, slogin);
430 p += strlen(p) + 1;
431 (*ret)->pw_passwd = strcpy(p, spasswd);
432 p += strlen(p) + 1;
433 (*ret)->pw_gecos = strcpy(p, sgecos);
434 p += strlen(p) + 1;
435 (*ret)->pw_dir = strcpy(p, shome);
436 p += strlen(p) + 1;
437 (*ret)->pw_shell = strcpy(p, sshell);
438
439 snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name);
440
441 if (pam_set_data(pamh, buf,
442 *ret, _unix_cleanup) != PAM_SUCCESS) {
443 free(*ret);
444 *ret = NULL;
445 }
446 }
447
448 return matched;
449 }
450
451 /*
452 * _unix_comsefromsource() is a quick check to see if information about a given
453 * user comes from a particular source (just files and nis for now)
454 *
455 */
456 int _unix_comesfromsource(pam_handle_t *pamh,
457 const char *name, int files, int nis)
458 {
459 return _unix_getpwnam(pamh, name, files, nis, NULL);
460 }
461
462 /*
463 * verify the password of a user
464 */
465
466 #include <sys/types.h>
467 #include <sys/wait.h>
468
469 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
470 unsigned long long ctrl, const char *user)
471 {
472 int retval, child, fds[2];
473 struct sigaction newsa, oldsa;
474
475 D(("called."));
476 /* create a pipe for the password */
477 if (pipe(fds) != 0) {
478 D(("could not make pipe"));
479 return PAM_AUTH_ERR;
480 }
481
482 if (off(UNIX_NOREAP, ctrl)) {
483 /*
484 * This code arranges that the demise of the child does not cause
485 * the application to receive a signal it is not expecting - which
486 * may kill the application or worse.
487 *
488 * The "noreap" module argument is provided so that the admin can
489 * override this behavior.
490 */
491 memset(&newsa, '\0', sizeof(newsa));
492 newsa.sa_handler = SIG_DFL;
493 sigaction(SIGCHLD, &newsa, &oldsa);
494 }
495
496 /* fork */
497 child = fork();
498 if (child == 0) {
499 static char *envp[] = { NULL };
500 const char *args[] = { NULL, NULL, NULL, NULL };
501
502 /* XXX - should really tidy up PAM here too */
503
504 /* reopen stdin as pipe */
505 if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) {
506 pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin");
507 _exit(PAM_AUTHINFO_UNAVAIL);
508 }
509
510 if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD,
511 PAM_MODUTIL_PIPE_FD,
512 PAM_MODUTIL_PIPE_FD) < 0) {
513 _exit(PAM_AUTHINFO_UNAVAIL);
514 }
515
516 if (geteuid() == 0) {
517 /* must set the real uid to 0 so the helper will not error
518 out if pam is called from setuid binary (su, sudo...) */
519 if (setuid(0) == -1) {
520 D(("setuid failed"));
521 _exit(PAM_AUTHINFO_UNAVAIL);
522 }
523 }
524
525 /* exec binary helper */
526 args[0] = CHKPWD_HELPER;
527 args[1] = user;
528 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
529 args[2]="nullok";
530 } else {
531 args[2]="nonull";
532 }
533
534 DIAG_PUSH_IGNORE_CAST_QUAL;
535 execve(CHKPWD_HELPER, (char *const *) args, envp);
536 DIAG_POP_IGNORE_CAST_QUAL;
537
538 /* should not get here: exit with error */
539 D(("helper binary is not available"));
540 _exit(PAM_AUTHINFO_UNAVAIL);
541 } else if (child > 0) {
542 /* wait for child */
543 /* if the stored password is NULL */
544 int rc=0;
545 if (passwd != NULL) { /* send the password to the child */
546 int len = strlen(passwd);
547
548 if (len > PAM_MAX_RESP_SIZE)
549 len = PAM_MAX_RESP_SIZE;
550 if (write(fds[1], passwd, len) == -1 ||
551 write(fds[1], "", 1) == -1) {
552 pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m");
553 retval = PAM_AUTH_ERR;
554 }
555 passwd = NULL;
556 } else { /* blank password */
557 if (write(fds[1], "", 1) == -1) {
558 pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m");
559 retval = PAM_AUTH_ERR;
560 }
561 }
562 close(fds[0]); /* close here to avoid possible SIGPIPE above */
563 close(fds[1]);
564 /* wait for helper to complete: */
565 while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR);
566 if (rc<0) {
567 pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
568 retval = PAM_AUTH_ERR;
569 } else if (!WIFEXITED(retval)) {
570 pam_syslog(pamh, LOG_ERR, "unix_chkpwd abnormal exit: %d", retval);
571 retval = PAM_AUTH_ERR;
572 } else {
573 retval = WEXITSTATUS(retval);
574 }
575 } else {
576 D(("fork failed"));
577 close(fds[0]);
578 close(fds[1]);
579 retval = PAM_AUTH_ERR;
580 }
581
582 if (off(UNIX_NOREAP, ctrl)) {
583 sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
584 }
585
586 D(("returning %d", retval));
587 return retval;
588 }
589
590 /*
591 * _unix_blankpasswd() is a quick check for a blank password
592 *
593 * returns TRUE if user does not have a password
594 * - to avoid prompting for one in such cases (CG)
595 */
596
597 int
598 _unix_blankpasswd (pam_handle_t *pamh, unsigned long long ctrl, const char *name)
599 {
600 struct passwd *pwd = NULL;
601 char *salt = NULL;
602 int daysleft;
603 int retval;
604 int blank = 0;
605 int execloop;
606 int nonexistent_check = 1;
607
608 D(("called"));
609
610 /*
611 * This function does not have to be too smart if something goes
612 * wrong, return FALSE and let this case to be treated somewhere
613 * else (CG)
614 */
615
616 if (on(UNIX_NULLRESETOK, ctrl)) {
617 retval = _unix_verify_user(pamh, ctrl, name, &daysleft);
618 if (retval == PAM_NEW_AUTHTOK_REQD) {
619 /* password reset is enforced, allow authentication with empty password */
620 pam_syslog(pamh, LOG_DEBUG, "user [%s] has expired blank password, enabling nullok", name);
621 set(UNIX__NULLOK, ctrl);
622 }
623 }
624
625 if (on(UNIX__NONULL, ctrl))
626 return 0; /* will fail but don't let on yet */
627
628 /* UNIX passwords area */
629
630 /*
631 * Execute this loop twice: one checking the password hash of an existing
632 * user and another one for a non-existing user. This way the runtimes
633 * are equal, making it more difficult to differentiate existing from
634 * non-existing users.
635 */
636 for (execloop = 0; execloop < 2; ++execloop) {
637 retval = get_pwd_hash(pamh, name, &pwd, &salt);
638
639 if (retval == PAM_UNIX_RUN_HELPER) {
640 if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS)
641 blank = nonexistent_check;
642 } else if (retval == PAM_USER_UNKNOWN) {
643 name = "root";
644 nonexistent_check = 0;
645 continue;
646 } else if (salt != NULL) {
647 if (strlen(salt) == 0)
648 blank = nonexistent_check;
649 }
650 name = "pam_unix_non_existent:";
651 /* non-existent user check will not affect the blank value */
652 }
653
654 /* tidy up */
655 if (salt)
656 _pam_delete(salt);
657
658 return blank;
659 }
660
661 int _unix_verify_password(pam_handle_t * pamh, const char *name
662 ,const char *p, unsigned long long ctrl)
663 {
664 struct passwd *pwd = NULL;
665 char *salt = NULL;
666 char *data_name;
667 char pw[PAM_MAX_RESP_SIZE + 1];
668 int retval;
669
670
671 D(("called"));
672
673 #ifdef HAVE_PAM_FAIL_DELAY
674 if (off(UNIX_NODELAY, ctrl)) {
675 D(("setting delay"));
676 (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
677 }
678 #endif
679
680 /* locate the entry for this user */
681
682 D(("locating user's record"));
683
684 retval = get_pwd_hash(pamh, name, &pwd, &salt);
685
686 data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
687 if (data_name == NULL) {
688 pam_syslog(pamh, LOG_CRIT, "no memory for data-name");
689 } else {
690 strcpy(data_name, FAIL_PREFIX);
691 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
692 }
693
694 if (p != NULL && strlen(p) > PAM_MAX_RESP_SIZE) {
695 memset(pw, 0, sizeof(pw));
696 p = strncpy(pw, p, sizeof(pw) - 1);
697 }
698
699 if (retval != PAM_SUCCESS) {
700 if (retval == PAM_UNIX_RUN_HELPER) {
701 D(("running helper binary"));
702 retval = _unix_run_helper_binary(pamh, p, ctrl, name);
703 } else {
704 D(("user's record unavailable"));
705 p = NULL;
706 if (on(UNIX_AUDIT, ctrl)) {
707 /* this might be a typo and the user has given a password
708 instead of a username. Careful with this. */
709 pam_syslog(pamh, LOG_NOTICE,
710 "check pass; user (%s) unknown", name);
711 } else {
712 name = NULL;
713 if (on(UNIX_DEBUG, ctrl) || pwd == NULL) {
714 pam_syslog(pamh, LOG_NOTICE,
715 "check pass; user unknown");
716 } else {
717 /* don't log failure as another pam module can succeed */
718 goto cleanup;
719 }
720 }
721 }
722 } else {
723 retval = verify_pwd_hash(pamh, p, salt, off(UNIX__NONULL, ctrl));
724 }
725
726 if (retval == PAM_SUCCESS) {
727 if (data_name) /* reset failures */
728 pam_set_data(pamh, data_name, NULL, _cleanup_failures);
729 } else {
730 if (data_name != NULL) {
731 struct _pam_failed_auth *new = NULL;
732 const struct _pam_failed_auth *old = NULL;
733
734 /* get a failure recorder */
735
736 new = (struct _pam_failed_auth *)
737 malloc(sizeof(struct _pam_failed_auth));
738
739 if (new != NULL) {
740
741 const char *login_name;
742 const void *void_old;
743
744
745 login_name = pam_modutil_getlogin(pamh);
746 if (login_name == NULL) {
747 login_name = "";
748 }
749
750 new->user = strdup(name ? name : "");
751 new->uid = getuid();
752 new->euid = geteuid();
753 new->name = strdup(login_name);
754
755 /* any previous failures for this user ? */
756 if (pam_get_data(pamh, data_name, &void_old)
757 == PAM_SUCCESS)
758 old = void_old;
759 else
760 old = NULL;
761
762 if (old != NULL) {
763 new->count = old->count + 1;
764 if (new->count >= UNIX_MAX_RETRIES) {
765 retval = PAM_MAXTRIES;
766 }
767 } else {
768 const void *service=NULL;
769 const void *ruser=NULL;
770 const void *rhost=NULL;
771 const void *tty=NULL;
772
773 (void) pam_get_item(pamh, PAM_SERVICE,
774 &service);
775 (void) pam_get_item(pamh, PAM_RUSER,
776 &ruser);
777 (void) pam_get_item(pamh, PAM_RHOST,
778 &rhost);
779 (void) pam_get_item(pamh, PAM_TTY,
780 &tty);
781
782 pam_syslog(pamh, LOG_NOTICE,
783 "authentication failure; "
784 "logname=%s uid=%d euid=%d "
785 "tty=%s ruser=%s rhost=%s "
786 "%s%s",
787 new->name, new->uid, new->euid,
788 tty ? (const char *)tty : "",
789 ruser ? (const char *)ruser : "",
790 rhost ? (const char *)rhost : "",
791 (new->user && new->user[0] != '\0')
792 ? " user=" : "",
793 new->user
794 );
795 new->count = 1;
796 }
797
798 pam_set_data(pamh, data_name, new, _cleanup_failures);
799
800 } else {
801 pam_syslog(pamh, LOG_CRIT,
802 "no memory for failure recorder");
803 }
804 }
805 }
806
807 cleanup:
808 pam_overwrite_array(pw); /* clear memory of the password */
809 if (data_name)
810 _pam_delete(data_name);
811 if (salt)
812 _pam_delete(salt);
813
814 D(("done [%d].", retval));
815
816 return retval;
817 }
818
819 int
820 _unix_verify_user(pam_handle_t *pamh,
821 unsigned long long ctrl,
822 const char *name,
823 int *daysleft)
824 {
825 int retval;
826 struct spwd *spent;
827 struct passwd *pwent;
828
829 retval = get_account_info(pamh, name, &pwent, &spent);
830 if (retval == PAM_USER_UNKNOWN) {
831 pam_syslog(pamh, LOG_ERR,
832 "could not identify user (from getpwnam(%s))",
833 name);
834 return retval;
835 }
836
837 if (retval == PAM_SUCCESS && spent == NULL)
838 return PAM_SUCCESS;
839
840 if (retval == PAM_UNIX_RUN_HELPER) {
841 retval = _unix_run_verify_binary(pamh, ctrl, name, daysleft);
842 if (retval == PAM_AUTHINFO_UNAVAIL &&
843 on(UNIX_BROKEN_SHADOW, ctrl))
844 return PAM_SUCCESS;
845 } else if (retval != PAM_SUCCESS) {
846 if (on(UNIX_BROKEN_SHADOW,ctrl))
847 return PAM_SUCCESS;
848 else
849 return retval;
850 } else
851 retval = check_shadow_expiry(pamh, spent, daysleft);
852
853 return retval;
854 }
855
856 /* ****************************************************************** *
857 * Copyright (c) Jan Rękorajski 1999.
858 * Copyright (c) Andrew G. Morgan 1996-8.
859 * Copyright (c) Alex O. Yuriev, 1996.
860 * Copyright (c) Cristian Gafton 1996.
861 * Copyright (c) Red Hat, Inc. 2007.
862 *
863 * Redistribution and use in source and binary forms, with or without
864 * modification, are permitted provided that the following conditions
865 * are met:
866 * 1. Redistributions of source code must retain the above copyright
867 * notice, and the entire permission notice in its entirety,
868 * including the disclaimer of warranties.
869 * 2. Redistributions in binary form must reproduce the above copyright
870 * notice, this list of conditions and the following disclaimer in the
871 * documentation and/or other materials provided with the distribution.
872 * 3. The name of the author may not be used to endorse or promote
873 * products derived from this software without specific prior
874 * written permission.
875 *
876 * ALTERNATIVELY, this product may be distributed under the terms of
877 * the GNU Public License, in which case the provisions of the GPL are
878 * required INSTEAD OF the above restrictions. (This clause is
879 * necessary due to a potential bad interaction between the GPL and
880 * the restrictions contained in a BSD-style copyright.)
881 *
882 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
883 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
884 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
885 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
886 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
887 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
888 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
889 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
890 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
891 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
892 * OF THE POSSIBILITY OF SUCH DAMAGE.
893 */