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 && j == UNIX_ALGO_ROUNDS)
113 *rounds = strtol(*argv + 7, NULL, 10);
116 ++argv; /* step to next argument */
119 if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
120 D(("DISALLOW_NULL_AUTHTOK"));
121 set(UNIX__NONULL, ctrl);
124 /* Set default rounds for blowfish */
125 if (on(UNIX_BLOWFISH_PASS, ctrl) && off(UNIX_ALGO_ROUNDS, ctrl)) {
127 set(UNIX_ALGO_ROUNDS, ctrl);
130 /* Enforce sane "rounds" values */
131 if (on(UNIX_ALGO_ROUNDS, ctrl)) {
132 if (on(UNIX_BLOWFISH_PASS, ctrl)) {
133 if (*rounds < 4 || *rounds > 31)
135 } else if (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl)) {
136 if ((*rounds < 1000) || (*rounds == INT_MAX))
137 /* don't care about bogus values */
138 unset(UNIX_ALGO_ROUNDS, ctrl);
139 if (*rounds >= 10000000)
144 /* auditing is a more sensitive version of debug */
146 if (on(UNIX_AUDIT, ctrl)) {
147 set(UNIX_DEBUG, ctrl);
149 /* return the set of flags */
155 static void _cleanup(pam_handle_t * pamh UNUSED, void *x, int error_status UNUSED)
160 /* ************************************************************** *
161 * Useful non-trivial functions *
162 * ************************************************************** */
165 * the following is used to keep track of the number of times a user fails
166 * to authenticate themself.
169 #define FAIL_PREFIX "-UN*X-FAIL-"
170 #define UNIX_MAX_RETRIES 3
172 struct _pam_failed_auth {
173 char *user; /* user that's failed to be authenticated */
174 char *name; /* attempt from user with name */
175 int uid; /* uid of calling user */
176 int euid; /* euid of calling process */
177 int count; /* number of failures so far */
180 #ifndef PAM_DATA_REPLACE
181 #error "Need to get an updated libpam 0.52 or better"
184 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
187 const void *service = NULL;
188 const void *ruser = NULL;
189 const void *rhost = NULL;
190 const void *tty = NULL;
191 struct _pam_failed_auth *failure;
195 quiet = err & PAM_DATA_SILENT; /* should we log something? */
196 err &= PAM_DATA_REPLACE; /* are we just replacing data? */
197 failure = (struct _pam_failed_auth *) fl;
199 if (failure != NULL) {
201 if (!quiet && !err) { /* under advisement from Sun,may go away */
203 /* log the number of authentication failures */
204 if (failure->count > 1) {
205 (void) pam_get_item(pamh, PAM_SERVICE,
207 (void) pam_get_item(pamh, PAM_RUSER,
209 (void) pam_get_item(pamh, PAM_RHOST,
211 (void) pam_get_item(pamh, PAM_TTY,
213 pam_syslog(pamh, LOG_NOTICE,
214 "%d more authentication failure%s; "
215 "logname=%s uid=%d euid=%d "
216 "tty=%s ruser=%s rhost=%s "
218 failure->count - 1, failure->count == 2 ? "" : "s",
219 failure->name, failure->uid, failure->euid,
220 tty ? (const char *)tty : "", ruser ? (const char *)ruser : "",
221 rhost ? (const char *)rhost : "",
222 (failure->user && failure->user[0] != '\0')
223 ? " user=" : "", failure->user
226 if (failure->count > UNIX_MAX_RETRIES) {
227 pam_syslog(pamh, LOG_ALERT,
228 "service(%s) ignoring max retries; %d > %d",
229 service == NULL ? "**unknown**" : (const char *)service,
235 _pam_delete(failure->user); /* tidy up */
236 _pam_delete(failure->name); /* tidy up */
242 * _unix_getpwnam() searches only /etc/passwd and NIS to find user information
244 static void _unix_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED)
249 int _unix_getpwnam(pam_handle_t *pamh, const char *name,
250 int files, int nis, struct passwd **ret)
254 int matched = 0, buflen;
255 char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p;
257 memset(buf, 0, sizeof(buf));
259 if (!matched && files) {
260 int userlen = strlen(name);
261 passwd = fopen("/etc/passwd", "r");
262 if (passwd != NULL) {
263 while (fgets(buf, sizeof(buf), passwd) != NULL) {
264 if ((buf[userlen] == ':') &&
265 (strncmp(name, buf, userlen) == 0)) {
266 p = buf + strlen(buf) - 1;
267 while (isspace(*p) && (p >= buf)) {
278 if (!matched && nis) {
279 char *userinfo = NULL, *domain = NULL;
281 len = yp_get_default_domain(&domain);
282 if (len == YPERR_SUCCESS) {
283 len = yp_bind(domain);
285 if (len == YPERR_SUCCESS) {
286 i = yp_match(domain, "passwd.byname", name,
287 strlen(name), &userinfo, &len);
289 if ((i == YPERR_SUCCESS) && ((size_t)len < sizeof(buf))) {
290 strncpy(buf, userinfo, sizeof(buf) - 1);
291 buf[sizeof(buf) - 1] = '\0';
297 if (matched && (ret != NULL)) {
302 spasswd = strchr(slogin, ':');
303 if (spasswd == NULL) {
308 suid = strchr(spasswd, ':');
314 sgid = strchr(suid, ':');
320 sgecos = strchr(sgid, ':');
321 if (sgecos == NULL) {
326 shome = strchr(sgecos, ':');
332 sshell = strchr(shome, ':');
333 if (sshell == NULL) {
338 buflen = sizeof(struct passwd) +
340 strlen(spasswd) + 1 +
346 *ret = malloc(buflen);
350 memset(*ret, '\0', buflen);
352 (*ret)->pw_uid = strtol(suid, &p, 10);
353 if ((strlen(suid) == 0) || (*p != '\0')) {
359 (*ret)->pw_gid = strtol(sgid, &p, 10);
360 if ((strlen(sgid) == 0) || (*p != '\0')) {
366 p = ((char*)(*ret)) + sizeof(struct passwd);
367 (*ret)->pw_name = strcpy(p, slogin);
369 (*ret)->pw_passwd = strcpy(p, spasswd);
371 (*ret)->pw_gecos = strcpy(p, sgecos);
373 (*ret)->pw_dir = strcpy(p, shome);
375 (*ret)->pw_shell = strcpy(p, sshell);
377 snprintf(buf, sizeof(buf), "_pam_unix_getpwnam_%s", name);
379 if (pam_set_data(pamh, buf,
380 *ret, _unix_cleanup) != PAM_SUCCESS) {
390 * _unix_comsefromsource() is a quick check to see if information about a given
391 * user comes from a particular source (just files and nis for now)
394 int _unix_comesfromsource(pam_handle_t *pamh,
395 const char *name, int files, int nis)
397 return _unix_getpwnam(pamh, name, files, nis, NULL);
401 * verify the password of a user
404 #include <sys/types.h>
405 #include <sys/wait.h>
407 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
408 unsigned int ctrl, const char *user)
410 int retval, child, fds[2];
411 struct sigaction newsa, oldsa;
414 /* create a pipe for the password */
415 if (pipe(fds) != 0) {
416 D(("could not make pipe"));
420 if (off(UNIX_NOREAP, ctrl)) {
422 * This code arranges that the demise of the child does not cause
423 * the application to receive a signal it is not expecting - which
424 * may kill the application or worse.
426 * The "noreap" module argument is provided so that the admin can
427 * override this behavior.
429 memset(&newsa, '\0', sizeof(newsa));
430 newsa.sa_handler = SIG_DFL;
431 sigaction(SIGCHLD, &newsa, &oldsa);
439 static char *envp[] = { NULL };
440 char *args[] = { NULL, NULL, NULL, NULL };
442 /* XXX - should really tidy up PAM here too */
444 /* reopen stdin as pipe */
445 dup2(fds[0], STDIN_FILENO);
447 if (getrlimit(RLIMIT_NOFILE,&rlim)==0) {
448 if (rlim.rlim_max >= MAX_FD_NO)
449 rlim.rlim_max = MAX_FD_NO;
450 for (i=0; i < (int)rlim.rlim_max; i++) {
451 if (i != STDIN_FILENO)
456 if (geteuid() == 0) {
457 /* must set the real uid to 0 so the helper will not error
458 out if pam is called from setuid binary (su, sudo...) */
462 /* exec binary helper */
463 args[0] = strdup(CHKPWD_HELPER);
464 args[1] = x_strdup(user);
465 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
466 args[2]=strdup("nullok");
468 args[2]=strdup("nonull");
471 execve(CHKPWD_HELPER, args, envp);
473 /* should not get here: exit with error */
474 D(("helper binary is not available"));
475 exit(PAM_AUTHINFO_UNAVAIL);
476 } else if (child > 0) {
478 /* if the stored password is NULL */
480 if (passwd != NULL) { /* send the password to the child */
481 write(fds[1], passwd, strlen(passwd)+1);
484 write(fds[1], "", 1); /* blank password */
486 close(fds[0]); /* close here to avoid possible SIGPIPE above */
488 rc=waitpid(child, &retval, 0); /* wait for helper to complete */
490 pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc);
491 retval = PAM_AUTH_ERR;
493 retval = WEXITSTATUS(retval);
499 retval = PAM_AUTH_ERR;
502 if (off(UNIX_NOREAP, ctrl)) {
503 sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */
506 D(("returning %d", retval));
511 * _unix_blankpasswd() is a quick check for a blank password
513 * returns TRUE if user does not have a password
514 * - to avoid prompting for one in such cases (CG)
518 _unix_blankpasswd (pam_handle_t *pamh, unsigned int ctrl, const char *name)
520 struct passwd *pwd = NULL;
527 * This function does not have to be too smart if something goes
528 * wrong, return FALSE and let this case to be treated somewhere
532 if (on(UNIX__NONULL, ctrl))
533 return 0; /* will fail but don't let on yet */
535 /* UNIX passwords area */
537 retval = get_pwd_hash(pamh, name, &pwd, &salt);
539 if (retval == PAM_UNIX_RUN_HELPER) {
540 /* salt will not be set here so we can return immediately */
541 if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS)
547 /* Does this user have a password? */
551 if (strlen(salt) == 0)
565 int _unix_verify_password(pam_handle_t * pamh, const char *name
566 ,const char *p, unsigned int ctrl)
568 struct passwd *pwd = NULL;
576 #ifdef HAVE_PAM_FAIL_DELAY
577 if (off(UNIX_NODELAY, ctrl)) {
578 D(("setting delay"));
579 (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
583 /* locate the entry for this user */
585 D(("locating user's record"));
587 retval = get_pwd_hash(pamh, name, &pwd, &salt);
589 data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
590 if (data_name == NULL) {
591 pam_syslog(pamh, LOG_CRIT, "no memory for data-name");
593 strcpy(data_name, FAIL_PREFIX);
594 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
597 if (retval != PAM_SUCCESS) {
598 if (retval == PAM_UNIX_RUN_HELPER) {
599 D(("running helper binary"));
600 retval = _unix_run_helper_binary(pamh, p, ctrl, name);
602 D(("user's record unavailable"));
604 if (on(UNIX_AUDIT, ctrl)) {
605 /* this might be a typo and the user has given a password
606 instead of a username. Careful with this. */
607 pam_syslog(pamh, LOG_WARNING,
608 "check pass; user (%s) unknown", name);
611 if (on(UNIX_DEBUG, ctrl) || pwd == NULL) {
612 pam_syslog(pamh, LOG_WARNING,
613 "check pass; user unknown");
615 /* don't log failure as another pam module can succeed */
621 retval = verify_pwd_hash(p, salt, off(UNIX__NONULL, ctrl));
624 if (retval == PAM_SUCCESS) {
625 if (data_name) /* reset failures */
626 pam_set_data(pamh, data_name, NULL, _cleanup_failures);
628 if (data_name != NULL) {
629 struct _pam_failed_auth *new = NULL;
630 const struct _pam_failed_auth *old = NULL;
632 /* get a failure recorder */
634 new = (struct _pam_failed_auth *)
635 malloc(sizeof(struct _pam_failed_auth));
639 const char *login_name;
640 const void *void_old;
643 login_name = pam_modutil_getlogin(pamh);
644 if (login_name == NULL) {
648 new->user = x_strdup(name ? name : "");
650 new->euid = geteuid();
651 new->name = x_strdup(login_name);
653 /* any previous failures for this user ? */
654 if (pam_get_data(pamh, data_name, &void_old)
661 new->count = old->count + 1;
662 if (new->count >= UNIX_MAX_RETRIES) {
663 retval = PAM_MAXTRIES;
666 const void *service=NULL;
667 const void *ruser=NULL;
668 const void *rhost=NULL;
669 const void *tty=NULL;
671 (void) pam_get_item(pamh, PAM_SERVICE,
673 (void) pam_get_item(pamh, PAM_RUSER,
675 (void) pam_get_item(pamh, PAM_RHOST,
677 (void) pam_get_item(pamh, PAM_TTY,
680 pam_syslog(pamh, LOG_NOTICE,
681 "authentication failure; "
682 "logname=%s uid=%d euid=%d "
683 "tty=%s ruser=%s rhost=%s "
685 new->name, new->uid, new->euid,
686 tty ? (const char *)tty : "",
687 ruser ? (const char *)ruser : "",
688 rhost ? (const char *)rhost : "",
689 (new->user && new->user[0] != '\0')
696 pam_set_data(pamh, data_name, new, _cleanup_failures);
699 pam_syslog(pamh, LOG_CRIT,
700 "no memory for failure recorder");
707 _pam_delete(data_name);
711 D(("done [%d].", retval));
717 * obtain a password from the user
720 int _unix_read_password(pam_handle_t * pamh
725 ,const char *data_name
729 int retval = PAM_SUCCESS;
735 * make sure nothing inappropriate gets returned
738 *pass = token = NULL;
741 * which authentication token are we getting?
744 authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
747 * should we obtain the password from a PAM item ?
750 if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
751 retval = pam_get_item(pamh, authtok_flag, pass);
752 if (retval != PAM_SUCCESS) {
754 pam_syslog(pamh, LOG_ALERT,
755 "pam_get_item returned error to unix-read-password"
758 } else if (*pass != NULL) { /* we have a password! */
760 } else if (on(UNIX_USE_AUTHTOK, ctrl)
761 && off(UNIX__OLD_PASSWD, ctrl)) {
762 return PAM_AUTHTOK_ERR;
763 } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
764 return PAM_AUTHTOK_RECOVERY_ERR; /* didn't work */
768 * getting here implies we will have to get the password from the
774 char *resp[2] = { NULL, NULL };
776 if (comment != NULL && off(UNIX__QUIET, ctrl)) {
777 retval = pam_info(pamh, "%s", comment);
780 if (retval == PAM_SUCCESS) {
781 retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
782 &resp[0], "%s", prompt1);
784 if (retval == PAM_SUCCESS && prompt2 != NULL) {
785 retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
786 &resp[1], "%s", prompt2);
791 if (resp[0] != NULL && resp[replies-1] != NULL) {
792 /* interpret the response */
794 if (retval == PAM_SUCCESS) { /* a good conversation */
799 /* verify that password entered correctly */
800 if (strcmp(token, resp[replies - 1])) {
802 retval = PAM_AUTHTOK_RECOVERY_ERR;
803 _make_remark(pamh, ctrl,
804 PAM_ERROR_MSG, MISTYPED_PASS);
808 pam_syslog(pamh, LOG_NOTICE,
809 "could not recover authentication token");
815 retval = (retval == PAM_SUCCESS)
816 ? PAM_AUTHTOK_RECOVERY_ERR : retval;
821 _pam_delete(resp[1]);
824 if (retval != PAM_SUCCESS) {
827 if (on(UNIX_DEBUG, ctrl))
828 pam_syslog(pamh, LOG_DEBUG,
829 "unable to obtain a password");
832 /* 'token' is the entered password */
834 if (off(UNIX_NOT_SET_PASS, ctrl)) {
836 /* we store this password as an item */
838 retval = pam_set_item(pamh, authtok_flag, token);
839 _pam_delete(token); /* clean it up */
840 if (retval != PAM_SUCCESS
841 || (retval = pam_get_item(pamh, authtok_flag, pass))
845 pam_syslog(pamh, LOG_CRIT, "error manipulating password");
851 * then store it as data specific to this module. pam_end()
852 * will arrange to clean it up.
855 retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
856 if (retval != PAM_SUCCESS) {
857 pam_syslog(pamh, LOG_CRIT,
858 "error manipulating password data [%s]",
859 pam_strerror(pamh, retval));
864 token = NULL; /* break link to password */
870 /* ****************************************************************** *
871 * Copyright (c) Jan Rêkorajski 1999.
872 * Copyright (c) Andrew G. Morgan 1996-8.
873 * Copyright (c) Alex O. Yuriev, 1996.
874 * Copyright (c) Cristian Gafton 1996.
875 * Copyright (c) Red Hat, Inc. 2007.
877 * Redistribution and use in source and binary forms, with or without
878 * modification, are permitted provided that the following conditions
880 * 1. Redistributions of source code must retain the above copyright
881 * notice, and the entire permission notice in its entirety,
882 * including the disclaimer of warranties.
883 * 2. Redistributions in binary form must reproduce the above copyright
884 * notice, this list of conditions and the following disclaimer in the
885 * documentation and/or other materials provided with the distribution.
886 * 3. The name of the author may not be used to endorse or promote
887 * products derived from this software without specific prior
888 * written permission.
890 * ALTERNATIVELY, this product may be distributed under the terms of
891 * the GNU Public License, in which case the provisions of the GPL are
892 * required INSTEAD OF the above restrictions. (This clause is
893 * necessary due to a potential bad interaction between the GPL and
894 * the restrictions contained in a BSD-style copyright.)
896 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
897 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
898 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
899 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
900 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
901 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
902 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
903 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
904 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
905 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
906 * OF THE POSSIBILITY OF SUCH DAMAGE.