4 * Copyright information at end of file.
21 #include <security/_pam_macros.h>
22 #include <security/pam_modules.h>
27 extern char *crypt(const char *key, const char *salt);
28 extern char *bigcrypt(const char *key, const char *salt);
30 /* syslogging function for errors and other information */
32 void _log_err(int err, pam_handle_t *pamh, const char *format,...)
38 pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
40 strncpy(logname, service, sizeof(logname));
41 logname[sizeof(logname) - 1 - strlen("(pam_unix)")] = '\0';
42 strncat(logname, "(pam_unix)", strlen("(pam_unix)"));
44 strncpy(logname, "pam_unix", sizeof(logname) - 1);
47 va_start(args, format);
48 openlog(logname, LOG_CONS | LOG_PID, LOG_AUTH);
49 vsyslog(err, format, args);
54 /* this is a front-end for module-application conversations */
56 static int converse(pam_handle_t * pamh, int ctrl, int nargs
57 ,struct pam_message **message
58 ,struct pam_response **response)
61 struct pam_conv *conv;
63 D(("begin to converse"));
65 retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
66 if (retval == PAM_SUCCESS) {
68 retval = conv->conv(nargs, (const struct pam_message **) message
69 ,response, conv->appdata_ptr);
71 D(("returned from application's conversation function"));
73 if (retval != PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) {
74 _log_err(LOG_DEBUG, pamh, "conversation failure [%s]"
75 ,pam_strerror(pamh, retval));
77 } else if (retval != PAM_CONV_AGAIN) {
78 _log_err(LOG_ERR, pamh
79 ,"couldn't obtain coversation function [%s]"
80 ,pam_strerror(pamh, retval));
82 D(("ready to return from module conversation"));
84 return retval; /* propagate error status */
87 int _make_remark(pam_handle_t * pamh, unsigned int ctrl
88 ,int type, const char *text)
90 int retval = PAM_SUCCESS;
92 if (off(UNIX__QUIET, ctrl)) {
93 struct pam_message *pmsg[1], msg[1];
94 struct pam_response *resp;
98 msg[0].msg_style = type;
101 retval = converse(pamh, ctrl, 1, pmsg, &resp);
104 _pam_drop_reply(resp, 1);
111 * Beacause getlogin() is braindead and sometimes it just
112 * doesn't work, we reimplement it here.
114 char *PAM_getlogin(void)
116 struct utmp *ut, line;
117 char *curr_tty, *retval;
118 static char curr_user[sizeof(ut->ut_user) + 4];
122 curr_tty = ttyname(0);
123 if (curr_tty != NULL) {
124 D(("PAM_getlogin ttyname: %s", curr_tty));
127 strncpy(line.ut_line, curr_tty, sizeof(line.ut_line));
128 if ((ut = getutline(&line)) != NULL) {
129 strncpy(curr_user, ut->ut_user, sizeof(ut->ut_user));
130 curr_user[sizeof(curr_user) - 1] = '\0';
135 D(("PAM_getlogin retval: %s", retval));
141 * set the control flags for the UNIX module.
144 int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
151 ctrl = UNIX_DEFAULTS; /* the default selection of options */
153 /* set some flags manually */
155 if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
157 set(UNIX__IAMROOT, ctrl);
159 if (flags & PAM_UPDATE_AUTHTOK) {
160 D(("UPDATE_AUTHTOK"));
161 set(UNIX__UPDATE, ctrl);
163 if (flags & PAM_PRELIM_CHECK) {
165 set(UNIX__PRELIM, ctrl);
167 if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
168 D(("DISALLOW_NULL_AUTHTOK"));
169 set(UNIX__NONULL, ctrl);
171 if (flags & PAM_SILENT) {
173 set(UNIX__QUIET, ctrl);
175 /* now parse the arguments to this module */
180 D(("pam_unix arg: %s", *argv));
182 for (j = 0; j < UNIX_CTRLS_; ++j) {
183 if (unix_args[j].token
184 && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
189 if (j >= UNIX_CTRLS_) {
190 _log_err(LOG_ERR, pamh,
191 "unrecognized option [%s]", *argv);
193 ctrl &= unix_args[j].mask; /* for turning things off */
194 ctrl |= unix_args[j].flag; /* for turning things on */
196 if (remember != NULL) {
197 if (j == UNIX_REMEMBER_PASSWD) {
198 *remember = strtol(*argv + 9, NULL, 10);
199 if ((*remember == LONG_MIN) || (*remember == LONG_MAX))
207 ++argv; /* step to next argument */
210 /* auditing is a more sensitive version of debug */
212 if (on(UNIX_AUDIT, ctrl)) {
213 set(UNIX_DEBUG, ctrl);
215 /* return the set of flags */
221 static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
226 /* ************************************************************** *
227 * Useful non-trivial functions *
228 * ************************************************************** */
231 * the following is used to keep track of the number of times a user fails
232 * to authenticate themself.
235 #define FAIL_PREFIX "-UN*X-FAIL-"
236 #define UNIX_MAX_RETRIES 3
238 struct _pam_failed_auth {
239 char *user; /* user that's failed to be authenticated */
240 char *name; /* attempt from user with name */
241 int uid; /* uid of calling user */
242 int euid; /* euid of calling process */
243 int count; /* number of failures so far */
246 #ifndef PAM_DATA_REPLACE
247 #error "Need to get an updated libpam 0.52 or better"
250 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
253 const char *service = NULL;
254 const char *ruser = NULL;
255 const char *rhost = NULL;
256 const char *tty = NULL;
257 struct _pam_failed_auth *failure;
261 quiet = err & PAM_DATA_SILENT; /* should we log something? */
262 err &= PAM_DATA_REPLACE; /* are we just replacing data? */
263 failure = (struct _pam_failed_auth *) fl;
265 if (failure != NULL) {
267 if (!quiet && !err) { /* under advisement from Sun,may go away */
269 /* log the number of authentication failures */
270 if (failure->count > 1) {
271 (void) pam_get_item(pamh, PAM_SERVICE,
272 (const void **)&service);
273 (void) pam_get_item(pamh, PAM_RUSER,
274 (const void **)&ruser);
275 (void) pam_get_item(pamh, PAM_RHOST,
276 (const void **)&rhost);
277 (void) pam_get_item(pamh, PAM_TTY,
278 (const void **)&tty);
279 _log_err(LOG_NOTICE, pamh,
280 "%d more authentication failure%s; "
281 "logname=%s uid=%d euid=%d "
282 "tty=%s ruser=%s rhost=%s "
284 failure->count - 1, failure->count == 2 ? "" : "s",
285 failure->name, failure->uid, failure->euid,
286 tty ? tty : "", ruser ? ruser : "",
288 (failure->user && failure->user[0] != '\0')
289 ? " user=" : "", failure->user
292 if (failure->count > UNIX_MAX_RETRIES) {
293 _log_err(LOG_ALERT, pamh
294 ,"service(%s) ignoring max retries; %d > %d"
295 ,service == NULL ? "**unknown**" : service
301 _pam_delete(failure->user); /* tidy up */
302 _pam_delete(failure->name); /* tidy up */
308 * _unix_blankpasswd() is a quick check for a blank password
310 * returns TRUE if user does not have a password
311 * - to avoid prompting for one in such cases (CG)
314 int _unix_blankpasswd(unsigned int ctrl, const char *name)
316 struct passwd *pwd = NULL;
317 struct spwd *spwdent = NULL;
323 struct passwd pwd_buf;
331 * This function does not have to be too smart if something goes
332 * wrong, return FALSE and let this case to be treated somewhere
336 if (on(UNIX__NONULL, ctrl))
337 return 0; /* will fail but don't let on yet */
339 /* UNIX passwords area */
341 /* Get password file entry... */
344 buf = malloc(bufsize);
346 if ((retval = getpwnam_r(name, pwd, buf, bufsize, &pwd))) {
349 while (retval == ERANGE) {
351 buf = realloc(buf, bufsize);
352 if ((retval = getpwnam_r(name, pwd, buf, bufsize, &pwd))) {
357 pwd = getpwnam(name);
361 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
363 uid_t save_euid, save_uid;
365 save_euid = geteuid();
367 if (save_uid == pwd->pw_uid)
368 setreuid( save_euid, save_uid );
371 if (setreuid( -1, pwd->pw_uid ) == -1) {
374 if(setreuid( -1, pwd->pw_uid ) == -1)
375 /* Will fail elsewhere. */
384 spwdent = getspnam( name );
385 if (save_uid == pwd->pw_uid)
386 setreuid( save_uid, save_euid );
388 if (setreuid( -1, 0 ) == -1)
389 setreuid( save_uid, -1 );
390 setreuid( -1, save_euid );
392 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
394 * ...and shadow password file entry for this user,
395 * if shadowing is enabled
397 spwdent = getspnam(name);
400 salt = x_strdup(spwdent->sp_pwdp);
402 salt = x_strdup(pwd->pw_passwd);
404 /* Does this user have a password? */
408 if (strlen(salt) == 0)
428 * verify the password of a user
431 #include <sys/types.h>
432 #include <sys/wait.h>
434 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
435 unsigned int ctrl, const char *user)
437 int retval, child, fds[2];
438 void (*sighandler)(int) = NULL;
441 /* create a pipe for the password */
442 if (pipe(fds) != 0) {
443 D(("could not make pipe"));
447 if (off(UNIX_NOREAP, ctrl)) {
449 * This code arranges that the demise of the child does not cause
450 * the application to receive a signal it is not expecting - which
451 * may kill the application or worse.
453 * The "noreap" module argument is provided so that the admin can
454 * override this behavior.
456 sighandler = signal(SIGCHLD, SIG_IGN);
462 static char *envp[] = { NULL };
463 char *args[] = { NULL, NULL, NULL };
465 /* XXX - should really tidy up PAM here too */
467 /* reopen stdin as pipe */
469 dup2(fds[0], STDIN_FILENO);
471 /* exec binary helper */
472 args[0] = x_strdup(CHKPWD_HELPER);
473 args[1] = x_strdup(user);
475 execve(CHKPWD_HELPER, args, envp);
477 /* should not get here: exit with error */
478 D(("helper binary is not available"));
479 exit(PAM_AUTHINFO_UNAVAIL);
480 } else if (child > 0) {
482 /* if the stored password is NULL */
483 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
484 write(fds[1], "nullok\0\0", 8);
486 write(fds[1], "nonull\0\0", 8);
488 if (passwd != NULL) { /* send the password to the child */
489 write(fds[1], passwd, strlen(passwd)+1);
492 write(fds[1], "", 1); /* blank password */
494 close(fds[0]); /* close here to avoid possible SIGPIPE above */
496 (void) waitpid(child, &retval, 0); /* wait for helper to complete */
497 retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR;
500 retval = PAM_AUTH_ERR;
503 if (sighandler != NULL) {
504 (void) signal(SIGCHLD, sighandler); /* restore old signal handler */
507 D(("returning %d", retval));
511 int _unix_verify_password(pam_handle_t * pamh, const char *name
512 ,const char *p, unsigned int ctrl)
514 struct passwd *pwd = NULL;
515 struct spwd *spwdent = NULL;
523 #ifdef HAVE_PAM_FAIL_DELAY
524 if (off(UNIX_NODELAY, ctrl)) {
525 D(("setting delay"));
526 (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
530 /* locate the entry for this user */
532 D(("locating user's record"));
534 /* UNIX passwords area */
535 pwd = getpwnam(name); /* Get password file entry... */
538 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
540 uid_t save_euid, save_uid;
542 save_euid = geteuid();
544 if (save_uid == pwd->pw_uid)
545 setreuid( save_euid, save_uid );
548 if (setreuid( -1, pwd->pw_uid ) == -1) {
551 if(setreuid( -1, pwd->pw_uid ) == -1)
552 return PAM_CRED_INSUFFICIENT;
556 spwdent = getspnam( name );
557 if (save_uid == pwd->pw_uid)
558 setreuid( save_uid, save_euid );
560 if (setreuid( -1, 0 ) == -1)
561 setreuid( save_uid, -1 );
562 setreuid( -1, save_euid );
564 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
566 * ...and shadow password file entry for this user,
567 * if shadowing is enabled
569 spwdent = getspnam(name);
572 salt = x_strdup(spwdent->sp_pwdp);
574 salt = x_strdup(pwd->pw_passwd);
577 data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
578 if (data_name == NULL) {
579 _log_err(LOG_CRIT, pamh, "no memory for data-name");
581 strcpy(data_name, FAIL_PREFIX);
582 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
585 retval = PAM_SUCCESS;
586 if (pwd == NULL || salt == NULL || !strcmp(salt, "x")) {
588 /* we are not root perhaps this is the reason? Run helper */
589 D(("running helper binary"));
590 retval = _unix_run_helper_binary(pamh, p, ctrl, name);
591 if (pwd == NULL && !on(UNIX_AUDIT,ctrl)
592 && retval != PAM_SUCCESS)
597 D(("user's record unavailable"));
598 if (on(UNIX_AUDIT, ctrl)) {
599 /* this might be a typo and the user has given a password
600 instead of a username. Careful with this. */
601 _log_err(LOG_ALERT, pamh,
602 "check pass; user (%s) unknown", name);
605 _log_err(LOG_ALERT, pamh,
606 "check pass; user unknown");
609 retval = PAM_AUTHINFO_UNAVAIL;
613 /* the stored password is NULL */
614 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
615 D(("user has empty password - access granted"));
616 retval = PAM_SUCCESS;
618 D(("user has empty password - access denied"));
619 retval = PAM_AUTH_ERR;
622 retval = PAM_AUTH_ERR;
624 if (!strncmp(salt, "$1$", 3)) {
625 pp = Goodcrypt_md5(p, salt);
626 if (strcmp(pp, salt) != 0) {
628 pp = Brokencrypt_md5(p, salt);
631 pp = bigcrypt(p, salt);
633 p = NULL; /* no longer needed here */
635 /* the moment of truth -- do we agree with the password? */
636 D(("comparing state of pp[%s] and salt[%s]", pp, salt));
639 * Note, we are comparing the bigcrypt of the password with
640 * the contents of the password field. If the latter was
641 * encrypted with regular crypt (and not bigcrypt) it will
642 * have been truncated for storage relative to the output
643 * of bigcrypt here. As such we need to compare only the
644 * stored string with the subset of bigcrypt's result.
645 * Bug 521314: The strncmp comparison is for legacy support.
647 if (strncmp(pp, salt, strlen(salt)) == 0) {
648 retval = PAM_SUCCESS;
650 retval = PAM_AUTH_ERR;
655 if (retval == PAM_SUCCESS) {
656 if (data_name) /* reset failures */
657 pam_set_data(pamh, data_name, NULL, _cleanup_failures);
659 if (data_name != NULL) {
660 struct _pam_failed_auth *new = NULL;
661 const struct _pam_failed_auth *old = NULL;
663 /* get a failure recorder */
665 new = (struct _pam_failed_auth *)
666 malloc(sizeof(struct _pam_failed_auth));
670 new->user = x_strdup(name ? name : "");
672 new->euid = geteuid();
673 new->name = x_strdup(PAM_getlogin()? PAM_getlogin() : "");
675 /* any previous failures for this user ? */
676 pam_get_data(pamh, data_name, (const void **) &old);
679 new->count = old->count + 1;
680 if (new->count >= UNIX_MAX_RETRIES) {
681 retval = PAM_MAXTRIES;
684 const char *service=NULL;
685 const char *ruser=NULL;
686 const char *rhost=NULL;
687 const char *tty=NULL;
689 (void) pam_get_item(pamh, PAM_SERVICE,
690 (const void **)&service);
691 (void) pam_get_item(pamh, PAM_RUSER,
692 (const void **)&ruser);
693 (void) pam_get_item(pamh, PAM_RHOST,
694 (const void **)&rhost);
695 (void) pam_get_item(pamh, PAM_TTY,
696 (const void **)&tty);
698 _log_err(LOG_NOTICE, pamh,
699 "authentication failure; "
700 "logname=%s uid=%d euid=%d "
701 "tty=%s ruser=%s rhost=%s "
703 new->name, new->uid, new->euid,
707 (new->user && new->user[0] != '\0')
714 pam_set_data(pamh, data_name, new, _cleanup_failures);
717 _log_err(LOG_CRIT, pamh,
718 "no memory for failure recorder");
724 _pam_delete(data_name);
730 D(("done [%d].", retval));
736 * obtain a password from the user
739 int _unix_read_password(pam_handle_t * pamh
744 ,const char *data_name
754 * make sure nothing inappropriate gets returned
757 *pass = token = NULL;
760 * which authentication token are we getting?
763 authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
766 * should we obtain the password from a PAM item ?
769 if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
770 retval = pam_get_item(pamh, authtok_flag, (const void **) pass);
771 if (retval != PAM_SUCCESS) {
773 _log_err(LOG_ALERT, pamh
774 ,"pam_get_item returned error to unix-read-password"
777 } else if (*pass != NULL) { /* we have a password! */
779 } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
780 return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
781 } else if (on(UNIX_USE_AUTHTOK, ctrl)
782 && off(UNIX__OLD_PASSWD, ctrl)) {
783 return PAM_AUTHTOK_RECOVER_ERR;
787 * getting here implies we will have to get the password from the
792 struct pam_message msg[3], *pmsg[3];
793 struct pam_response *resp;
796 /* prepare to converse */
798 if (comment != NULL && off(UNIX__QUIET, ctrl)) {
800 msg[0].msg_style = PAM_TEXT_INFO;
801 msg[0].msg = comment;
808 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
809 msg[i++].msg = prompt1;
812 if (prompt2 != NULL) {
814 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
815 msg[i++].msg = prompt2;
818 /* so call the conversation expecting i responses */
820 retval = converse(pamh, ctrl, i, pmsg, &resp);
824 /* interpret the response */
826 if (retval == PAM_SUCCESS) { /* a good conversation */
828 token = x_strdup(resp[i - replies].resp);
832 /* verify that password entered correctly */
833 if (!resp[i - 1].resp
834 || strcmp(token, resp[i - 1].resp)) {
835 _pam_delete(token); /* mistyped */
836 retval = PAM_AUTHTOK_RECOVER_ERR;
837 _make_remark(pamh, ctrl
838 ,PAM_ERROR_MSG, MISTYPED_PASS);
842 _log_err(LOG_NOTICE, pamh
843 ,"could not recover authentication token");
848 * tidy up the conversation (resp_retcode) is ignored
849 * -- what is it for anyway? AGM
852 _pam_drop_reply(resp, i);
855 retval = (retval == PAM_SUCCESS)
856 ? PAM_AUTHTOK_RECOVER_ERR : retval;
860 if (retval != PAM_SUCCESS) {
861 if (on(UNIX_DEBUG, ctrl))
862 _log_err(LOG_DEBUG, pamh,
863 "unable to obtain a password");
866 /* 'token' is the entered password */
868 if (off(UNIX_NOT_SET_PASS, ctrl)) {
870 /* we store this password as an item */
872 retval = pam_set_item(pamh, authtok_flag, token);
873 _pam_delete(token); /* clean it up */
874 if (retval != PAM_SUCCESS
875 || (retval = pam_get_item(pamh, authtok_flag
876 ,(const void **) pass))
880 _log_err(LOG_CRIT, pamh, "error manipulating password");
886 * then store it as data specific to this module. pam_end()
887 * will arrange to clean it up.
890 retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
891 if (retval != PAM_SUCCESS) {
892 _log_err(LOG_CRIT, pamh
893 ,"error manipulating password data [%s]"
894 ,pam_strerror(pamh, retval));
899 token = NULL; /* break link to password */
905 /* ****************************************************************** *
906 * Copyright (c) Jan Rêkorajski 1999.
907 * Copyright (c) Andrew G. Morgan 1996-8.
908 * Copyright (c) Alex O. Yuriev, 1996.
909 * Copyright (c) Cristian Gafton 1996.
911 * Redistribution and use in source and binary forms, with or without
912 * modification, are permitted provided that the following conditions
914 * 1. Redistributions of source code must retain the above copyright
915 * notice, and the entire permission notice in its entirety,
916 * including the disclaimer of warranties.
917 * 2. Redistributions in binary form must reproduce the above copyright
918 * notice, this list of conditions and the following disclaimer in the
919 * documentation and/or other materials provided with the distribution.
920 * 3. The name of the author may not be used to endorse or promote
921 * products derived from this software without specific prior
922 * written permission.
924 * ALTERNATIVELY, this product may be distributed under the terms of
925 * the GNU Public License, in which case the provisions of the GPL are
926 * required INSTEAD OF the above restrictions. (This clause is
927 * necessary due to a potential bad interaction between the GPL and
928 * the restrictions contained in a BSD-style copyright.)
930 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
931 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
932 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
933 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
934 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
935 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
936 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
937 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
938 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
939 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
940 * OF THE POSSIBILITY OF SUCH DAMAGE.