2 * Copyright information at end of file.
21 #include <sys/resource.h>
22 #include <rpcsvc/ypclnt.h>
24 #include <security/_pam_macros.h>
25 #include <security/pam_modules.h>
26 #include <security/pam_ext.h>
27 #include <security/pam_modutil.h>
30 #include "passverify.h"
32 #include <selinux/selinux.h>
33 #define SELINUX_ENABLED is_selinux_enabled()>0
35 #define SELINUX_ENABLED 0
38 /* this is a front-end for module-application conversations */
40 int _make_remark(pam_handle_t * pamh, unsigned int ctrl,
41 int type, const char *text)
43 int retval = PAM_SUCCESS;
45 if (off(UNIX__QUIET, ctrl)) {
46 retval = pam_prompt(pamh, type, NULL, "%s", text);
52 * set the control flags for the UNIX module.
55 int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int *rounds,
56 int argc, const char **argv)
62 ctrl = UNIX_DEFAULTS; /* the default selection of options */
64 /* set some flags manually */
66 if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
68 set(UNIX__IAMROOT, ctrl);
70 if (flags & PAM_UPDATE_AUTHTOK) {
71 D(("UPDATE_AUTHTOK"));
72 set(UNIX__UPDATE, ctrl);
74 if (flags & PAM_PRELIM_CHECK) {
76 set(UNIX__PRELIM, ctrl);
78 if (flags & PAM_SILENT) {
80 set(UNIX__QUIET, ctrl);
82 /* now parse the arguments to this module */
87 D(("pam_unix arg: %s", *argv));
89 for (j = 0; j < UNIX_CTRLS_; ++j) {
90 if (unix_args[j].token
91 && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
96 if (j >= UNIX_CTRLS_) {
97 pam_syslog(pamh, LOG_ERR,
98 "unrecognized option [%s]", *argv);
100 ctrl &= unix_args[j].mask; /* for turning things off */
101 ctrl |= unix_args[j].flag; /* for turning things on */
103 if (remember != NULL) {
104 if (j == UNIX_REMEMBER_PASSWD) {
105 *remember = strtol(*argv + 9, NULL, 10);
106 if ((*remember == INT_MIN) || (*remember == INT_MAX))
112 if (rounds != NULL) {
113 if (j == UNIX_ALGO_ROUNDS) {
114 *rounds = strtol(*argv + 7, NULL, 10);
115 if ((*rounds < 1000) || (*rounds == INT_MAX))
116 /* don't care about bogus values */
117 unset(UNIX_ALGO_ROUNDS, ctrl);
118 if (*rounds >= 10000000)
124 ++argv; /* step to next argument */
127 if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
128 D(("DISALLOW_NULL_AUTHTOK"));
129 set(UNIX__NONULL, ctrl);
132 /* auditing is a more sensitive version of debug */
134 if (on(UNIX_AUDIT, ctrl)) {
135 set(UNIX_DEBUG, ctrl);
137 /* return the set of flags */
143 static void _cleanup(pam_handle_t * pamh UNUSED, void *x, int error_status UNUSED)
148 /* ************************************************************** *
149 * Useful non-trivial functions *
150 * ************************************************************** */
153 * the following is used to keep track of the number of times a user fails
154 * to authenticate themself.
157 #define FAIL_PREFIX "-UN*X-FAIL-"
158 #define UNIX_MAX_RETRIES 3
160 struct _pam_failed_auth {
161 char *user; /* user that's failed to be authenticated */
162 char *name; /* attempt from user with name */
163 int uid; /* uid of calling user */
164 int euid; /* euid of calling process */
165 int count; /* number of failures so far */
168 #ifndef PAM_DATA_REPLACE
169 #error "Need to get an updated libpam 0.52 or better"
172 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
175 const void *service = NULL;
176 const void *ruser = NULL;
177 const void *rhost = NULL;
178 const void *tty = NULL;
179 struct _pam_failed_auth *failure;
183 quiet = err & PAM_DATA_SILENT; /* should we log something? */
184 err &= PAM_DATA_REPLACE; /* are we just replacing data? */
185 failure = (struct _pam_failed_auth *) fl;
187 if (failure != NULL) {
189 if (!quiet && !err) { /* under advisement from Sun,may go away */
191 /* log the number of authentication failures */
192 if (failure->count > 1) {
193 (void) pam_get_item(pamh, PAM_SERVICE,
195 (void) pam_get_item(pamh, PAM_RUSER,
197 (void) pam_get_item(pamh, PAM_RHOST,
199 (void) pam_get_item(pamh, PAM_TTY,
201 pam_syslog(pamh, LOG_NOTICE,
202 "%d more authentication failure%s; "
203 "logname=%s uid=%d euid=%d "
204 "tty=%s ruser=%s rhost=%s "
206 failure->count - 1, failure->count == 2 ? "" : "s",
207 failure->name, failure->uid, failure->euid,
208 tty ? (const char *)tty : "", ruser ? (const char *)ruser : "",
209 rhost ? (const char *)rhost : "",
210 (failure->user && failure->user[0] != '\0')
211 ? " user=" : "", failure->user
214 if (failure->count > UNIX_MAX_RETRIES) {
215 pam_syslog(pamh, LOG_ALERT,
216 "service(%s) ignoring max retries; %d > %d",
217 service == NULL ? "**unknown**" : (const char *)service,
223 _pam_delete(failure->user); /* tidy up */
224 _pam_delete(failure->name); /* tidy up */
230 * _unix_getpwnam() searches only /etc/passwd and NIS to find user information
232 static void _unix_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED)
237 int _unix_getpwnam(pam_handle_t *pamh, const char *name,
238 int files, int nis, struct passwd **ret)
242 int matched = 0, buflen;
243 char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p;
245 memset(buf, 0, sizeof(buf));
247 if (!matched && files) {
248 int userlen = strlen(name);
249 passwd = fopen("/etc/passwd", "r");
250 if (passwd != NULL) {
251 while (fgets(buf, sizeof(buf), passwd) != NULL) {
252 if ((buf[userlen] == ':') &&
253 (strncmp(name, buf, userlen) == 0)) {
254 p = buf + strlen(buf) - 1;
255 while (isspace(*p) && (p >= buf)) {
266 if (!matched && nis) {
267 char *userinfo = NULL, *domain = NULL;
269 len = yp_get_default_domain(&domain);
270 if (len == YPERR_SUCCESS) {
271 len = yp_bind(domain);
273 if (len == YPERR_SUCCESS) {
274 i = yp_match(domain, "passwd.byname", name,
275 strlen(name), &userinfo, &len);
277 if ((i == YPERR_SUCCESS) && ((size_t)len < sizeof(buf))) {
278 strncpy(buf, userinfo, sizeof(buf) - 1);
279 buf[sizeof(buf) - 1] = '\0';
285 if (matched && (ret != NULL)) {
290 spasswd = strchr(slogin, ':');
291 if (spasswd == NULL) {
296 suid = strchr(spasswd, ':');
302 sgid = strchr(suid, ':');
308 sgecos = strchr(sgid, ':');
309 if (sgecos == NULL) {
314 shome = strchr(sgecos, ':');
320 sshell = strchr(shome, ':');
321 if (sshell == NULL) {
326 buflen = sizeof(struct passwd) +
328 strlen(spasswd) + 1 +
334 *ret = malloc(buflen);
338 memset(*ret, '\0', buflen);
340 (*ret)->pw_uid = strtol(suid, &p, 10);
341 if ((strlen(suid) == 0) || (*p != '\0')) {
347 (*ret)->pw_gid = strtol(sgid, &p, 10);
348 if ((strlen(sgid) == 0) || (*p != '\0')) {
354 p = ((char*)(*ret)) + sizeof(struct passwd);
355 (*ret)->pw_name = strcpy(p, slogin);
357 (*ret)->pw_passwd = strcpy(p, spasswd);
359 (*ret)->pw_gecos = strcpy(p, sgecos);
361 (*ret)->pw_dir = strcpy(p, shome);
363 (*ret)->pw_shell = strcpy(p, sshell);
365 snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name);
367 if (pam_set_data(pamh, buf,
368 *ret, _unix_cleanup) != PAM_SUCCESS) {
378 * _unix_comsefromsource() is a quick check to see if information about a given
379 * user comes from a particular source (just files and nis for now)
382 int _unix_comesfromsource(pam_handle_t *pamh,
383 const char *name, int files, int nis)
385 return _unix_getpwnam(pamh, name, files, nis, NULL);
389 * verify the password of a user
392 #include <sys/types.h>
393 #include <sys/wait.h>
395 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
396 unsigned int ctrl, const char *user)
398 int retval, child, fds[2];
399 void (*sighandler)(int) = NULL;
402 /* create a pipe for the password */
403 if (pipe(fds) != 0) {
404 D(("could not make pipe"));
408 if (off(UNIX_NOREAP, ctrl)) {
410 * This code arranges that the demise of the child does not cause
411 * the application to receive a signal it is not expecting - which
412 * may kill the application or worse.
414 * The "noreap" module argument is provided so that the admin can
415 * override this behavior.
417 sighandler = signal(SIGCHLD, SIG_DFL);
425 static char *envp[] = { NULL };
426 char *args[] = { NULL, NULL, NULL, NULL };
428 /* XXX - should really tidy up PAM here too */
431 /* reopen stdin as pipe */
433 dup2(fds[0], STDIN_FILENO);
435 if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
436 for (i=2; i < (int)rlim.rlim_max; i++) {
442 if (geteuid() == 0) {
443 /* must set the real uid to 0 so the helper will not error
444 out if pam is called from setuid binary (su, sudo...) */
448 /* exec binary helper */
449 args[0] = strdup(CHKPWD_HELPER);
450 args[1] = x_strdup(user);
451 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
452 args[2]=strdup("nullok");
454 args[2]=strdup("nonull");
457 execve(CHKPWD_HELPER, args, envp);
459 /* should not get here: exit with error */
460 D(("helper binary is not available"));
461 exit(PAM_AUTHINFO_UNAVAIL);
462 } else if (child > 0) {
464 /* if the stored password is NULL */
466 if (passwd != NULL) { /* send the password to the child */
467 write(fds[1], passwd, strlen(passwd)+1);
470 write(fds[1], "", 1); /* blank password */
472 close(fds[0]); /* close here to avoid possible SIGPIPE above */
474 rc=waitpid(child, &retval, 0); /* wait for helper to complete */
476 pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
477 retval = PAM_AUTH_ERR;
479 retval = WEXITSTATUS(retval);
485 retval = PAM_AUTH_ERR;
488 if (sighandler != SIG_ERR) {
489 (void) signal(SIGCHLD, sighandler); /* restore old signal handler */
492 D(("returning %d", retval));
497 * _unix_blankpasswd() is a quick check for a blank password
499 * returns TRUE if user does not have a password
500 * - to avoid prompting for one in such cases (CG)
504 _unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
506 struct passwd *pwd = NULL;
513 * This function does not have to be too smart if something goes
514 * wrong, return FALSE and let this case to be treated somewhere
518 if (on(UNIX__NONULL, ctrl))
519 return 0; /* will fail but don't let on yet */
521 /* UNIX passwords area */
523 retval = get_pwd_hash(pamh, name, &pwd, &salt);
525 if (retval == PAM_UNIX_RUN_HELPER) {
526 /* salt will not be set here so we can return immediately */
527 if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS)
533 /* Does this user have a password? */
537 if (strlen(salt) == 0)
551 int _unix_verify_password(pam_handle_t * pamh, const char *name
552 ,const char *p, unsigned int ctrl)
554 struct passwd *pwd = NULL;
562 #ifdef HAVE_PAM_FAIL_DELAY
563 if (off(UNIX_NODELAY, ctrl)) {
564 D(("setting delay"));
565 (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
569 /* locate the entry for this user */
571 D(("locating user's record"));
573 retval = get_pwd_hash(pamh, name, &pwd, &salt);
575 data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
576 if (data_name == NULL) {
577 pam_syslog(pamh, LOG_CRIT, "no memory for data-name");
579 strcpy(data_name, FAIL_PREFIX);
580 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
583 if (retval != PAM_SUCCESS) {
584 if (retval == PAM_UNIX_RUN_HELPER) {
585 D(("running helper binary"));
586 retval = _unix_run_helper_binary(pamh, p, ctrl, name);
588 D(("user's record unavailable"));
590 if (on(UNIX_AUDIT, ctrl)) {
591 /* this might be a typo and the user has given a password
592 instead of a username. Careful with this. */
593 pam_syslog(pamh, LOG_WARNING,
594 "check pass; user (%s) unknown", name);
597 if (on(UNIX_DEBUG, ctrl) || pwd == NULL) {
598 pam_syslog(pamh, LOG_WARNING,
599 "check pass; user unknown");
601 /* don't log failure as another pam module can succeed */
607 retval = verify_pwd_hash(p, salt, off(UNIX__NONULL, ctrl));
610 if (retval == PAM_SUCCESS) {
611 if (data_name) /* reset failures */
612 pam_set_data(pamh, data_name, NULL, _cleanup_failures);
614 if (data_name != NULL) {
615 struct _pam_failed_auth *new = NULL;
616 const struct _pam_failed_auth *old = NULL;
618 /* get a failure recorder */
620 new = (struct _pam_failed_auth *)
621 malloc(sizeof(struct _pam_failed_auth));
625 const char *login_name;
626 const void *void_old;
629 login_name = pam_modutil_getlogin(pamh);
630 if (login_name == NULL) {
634 new->user = x_strdup(name ? name : "");
636 new->euid = geteuid();
637 new->name = x_strdup(login_name);
639 /* any previous failures for this user ? */
640 if (pam_get_data(pamh, data_name, &void_old)
647 new->count = old->count + 1;
648 if (new->count >= UNIX_MAX_RETRIES) {
649 retval = PAM_MAXTRIES;
652 const void *service=NULL;
653 const void *ruser=NULL;
654 const void *rhost=NULL;
655 const void *tty=NULL;
657 (void) pam_get_item(pamh, PAM_SERVICE,
659 (void) pam_get_item(pamh, PAM_RUSER,
661 (void) pam_get_item(pamh, PAM_RHOST,
663 (void) pam_get_item(pamh, PAM_TTY,
666 pam_syslog(pamh, LOG_NOTICE,
667 "authentication failure; "
668 "logname=%s uid=%d euid=%d "
669 "tty=%s ruser=%s rhost=%s "
671 new->name, new->uid, new->euid,
672 tty ? (const char *)tty : "",
673 ruser ? (const char *)ruser : "",
674 rhost ? (const char *)rhost : "",
675 (new->user && new->user[0] != '\0')
682 pam_set_data(pamh, data_name, new, _cleanup_failures);
685 pam_syslog(pamh, LOG_CRIT,
686 "no memory for failure recorder");
693 _pam_delete(data_name);
697 D(("done [%d].", retval));
703 * obtain a password from the user
706 int _unix_read_password(pam_handle_t * pamh
711 ,const char *data_name
715 int retval = PAM_SUCCESS;
721 * make sure nothing inappropriate gets returned
724 *pass = token = NULL;
727 * which authentication token are we getting?
730 authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
733 * should we obtain the password from a PAM item ?
736 if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
737 retval = pam_get_item(pamh, authtok_flag, pass);
738 if (retval != PAM_SUCCESS) {
740 pam_syslog(pamh, LOG_ALERT,
741 "pam_get_item returned error to unix-read-password"
744 } else if (*pass != NULL) { /* we have a password! */
746 } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
747 return PAM_AUTHTOK_RECOVERY_ERR; /* didn't work */
748 } else if (on(UNIX_USE_AUTHTOK, ctrl)
749 && off(UNIX__OLD_PASSWD, ctrl)) {
750 return PAM_AUTHTOK_ERR;
754 * getting here implies we will have to get the password from the
760 char *resp[2] = { NULL, NULL };
762 if (comment != NULL && off(UNIX__QUIET, ctrl)) {
763 retval = pam_info(pamh, "%s", comment);
766 if (retval == PAM_SUCCESS) {
767 retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
768 &resp[0], "%s", prompt1);
770 if (retval == PAM_SUCCESS && prompt2 != NULL) {
771 retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
772 &resp[1], "%s", prompt2);
777 if (resp[0] != NULL && resp[replies-1] != NULL) {
778 /* interpret the response */
780 if (retval == PAM_SUCCESS) { /* a good conversation */
785 /* verify that password entered correctly */
786 if (strcmp(token, resp[replies - 1])) {
788 retval = PAM_AUTHTOK_RECOVERY_ERR;
789 _make_remark(pamh, ctrl,
790 PAM_ERROR_MSG, MISTYPED_PASS);
794 pam_syslog(pamh, LOG_NOTICE,
795 "could not recover authentication token");
801 retval = (retval == PAM_SUCCESS)
802 ? PAM_AUTHTOK_RECOVERY_ERR : retval;
807 _pam_delete(resp[1]);
810 if (retval != PAM_SUCCESS) {
813 if (on(UNIX_DEBUG, ctrl))
814 pam_syslog(pamh, LOG_DEBUG,
815 "unable to obtain a password");
818 /* 'token' is the entered password */
820 if (off(UNIX_NOT_SET_PASS, ctrl)) {
822 /* we store this password as an item */
824 retval = pam_set_item(pamh, authtok_flag, token);
825 _pam_delete(token); /* clean it up */
826 if (retval != PAM_SUCCESS
827 || (retval = pam_get_item(pamh, authtok_flag, pass))
831 pam_syslog(pamh, LOG_CRIT, "error manipulating password");
837 * then store it as data specific to this module. pam_end()
838 * will arrange to clean it up.
841 retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
842 if (retval != PAM_SUCCESS) {
843 pam_syslog(pamh, LOG_CRIT,
844 "error manipulating password data [%s]",
845 pam_strerror(pamh, retval));
850 token = NULL; /* break link to password */
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.
863 * Redistribution and use in source and binary forms, with or without
864 * modification, are permitted provided that the following conditions
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.
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.)
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.