4 * Copyright information at end of file.
21 #include <security/_pam_macros.h>
22 #include <security/pam_modules.h>
23 #include <security/_pam_modutil.h>
28 extern char *crypt(const char *key, const char *salt);
29 extern char *bigcrypt(const char *key, const char *salt);
31 /* syslogging function for errors and other information */
33 void _log_err(int err, pam_handle_t *pamh, const char *format,...)
39 pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
41 strncpy(logname, service, sizeof(logname));
42 logname[sizeof(logname) - 1 - strlen("(pam_unix)")] = '\0';
43 strncat(logname, "(pam_unix)", strlen("(pam_unix)"));
45 strncpy(logname, "pam_unix", sizeof(logname) - 1);
48 va_start(args, format);
49 openlog(logname, LOG_CONS | LOG_PID, LOG_AUTH);
50 vsyslog(err, format, args);
55 /* this is a front-end for module-application conversations */
57 static int converse(pam_handle_t * pamh, int ctrl, int nargs
58 ,struct pam_message **message
59 ,struct pam_response **response)
62 struct pam_conv *conv;
64 D(("begin to converse"));
66 retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
67 if (retval == PAM_SUCCESS) {
69 retval = conv->conv(nargs, (const struct pam_message **) message
70 ,response, conv->appdata_ptr);
72 D(("returned from application's conversation function"));
74 if (retval != PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) {
75 _log_err(LOG_DEBUG, pamh, "conversation failure [%s]"
76 ,pam_strerror(pamh, retval));
78 } else if (retval != PAM_CONV_AGAIN) {
79 _log_err(LOG_ERR, pamh
80 ,"couldn't obtain coversation function [%s]"
81 ,pam_strerror(pamh, retval));
83 D(("ready to return from module conversation"));
85 return retval; /* propagate error status */
88 int _make_remark(pam_handle_t * pamh, unsigned int ctrl
89 ,int type, const char *text)
91 int retval = PAM_SUCCESS;
93 if (off(UNIX__QUIET, ctrl)) {
94 struct pam_message *pmsg[1], msg[1];
95 struct pam_response *resp;
99 msg[0].msg_style = type;
102 retval = converse(pamh, ctrl, 1, pmsg, &resp);
105 _pam_drop_reply(resp, 1);
112 * set the control flags for the UNIX module.
115 int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
122 ctrl = UNIX_DEFAULTS; /* the default selection of options */
124 /* set some flags manually */
126 if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
128 set(UNIX__IAMROOT, ctrl);
130 if (flags & PAM_UPDATE_AUTHTOK) {
131 D(("UPDATE_AUTHTOK"));
132 set(UNIX__UPDATE, ctrl);
134 if (flags & PAM_PRELIM_CHECK) {
136 set(UNIX__PRELIM, ctrl);
138 if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
139 D(("DISALLOW_NULL_AUTHTOK"));
140 set(UNIX__NONULL, ctrl);
142 if (flags & PAM_SILENT) {
144 set(UNIX__QUIET, ctrl);
146 /* now parse the arguments to this module */
151 D(("pam_unix arg: %s", *argv));
153 for (j = 0; j < UNIX_CTRLS_; ++j) {
154 if (unix_args[j].token
155 && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
160 if (j >= UNIX_CTRLS_) {
161 _log_err(LOG_ERR, pamh,
162 "unrecognized option [%s]", *argv);
164 ctrl &= unix_args[j].mask; /* for turning things off */
165 ctrl |= unix_args[j].flag; /* for turning things on */
167 if (remember != NULL) {
168 if (j == UNIX_REMEMBER_PASSWD) {
169 *remember = strtol(*argv + 9, NULL, 10);
170 if ((*remember == INT_MIN) || (*remember == INT_MAX))
178 ++argv; /* step to next argument */
181 /* auditing is a more sensitive version of debug */
183 if (on(UNIX_AUDIT, ctrl)) {
184 set(UNIX_DEBUG, ctrl);
186 /* return the set of flags */
192 static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
197 /* ************************************************************** *
198 * Useful non-trivial functions *
199 * ************************************************************** */
202 * the following is used to keep track of the number of times a user fails
203 * to authenticate themself.
206 #define FAIL_PREFIX "-UN*X-FAIL-"
207 #define UNIX_MAX_RETRIES 3
209 struct _pam_failed_auth {
210 char *user; /* user that's failed to be authenticated */
211 char *name; /* attempt from user with name */
212 int uid; /* uid of calling user */
213 int euid; /* euid of calling process */
214 int count; /* number of failures so far */
217 #ifndef PAM_DATA_REPLACE
218 #error "Need to get an updated libpam 0.52 or better"
221 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
224 const char *service = NULL;
225 const char *ruser = NULL;
226 const char *rhost = NULL;
227 const char *tty = NULL;
228 struct _pam_failed_auth *failure;
232 quiet = err & PAM_DATA_SILENT; /* should we log something? */
233 err &= PAM_DATA_REPLACE; /* are we just replacing data? */
234 failure = (struct _pam_failed_auth *) fl;
236 if (failure != NULL) {
238 if (!quiet && !err) { /* under advisement from Sun,may go away */
240 /* log the number of authentication failures */
241 if (failure->count > 1) {
242 (void) pam_get_item(pamh, PAM_SERVICE,
243 (const void **)&service);
244 (void) pam_get_item(pamh, PAM_RUSER,
245 (const void **)&ruser);
246 (void) pam_get_item(pamh, PAM_RHOST,
247 (const void **)&rhost);
248 (void) pam_get_item(pamh, PAM_TTY,
249 (const void **)&tty);
250 _log_err(LOG_NOTICE, pamh,
251 "%d more authentication failure%s; "
252 "logname=%s uid=%d euid=%d "
253 "tty=%s ruser=%s rhost=%s "
255 failure->count - 1, failure->count == 2 ? "" : "s",
256 failure->name, failure->uid, failure->euid,
257 tty ? tty : "", ruser ? ruser : "",
259 (failure->user && failure->user[0] != '\0')
260 ? " user=" : "", failure->user
263 if (failure->count > UNIX_MAX_RETRIES) {
264 _log_err(LOG_ALERT, pamh
265 ,"service(%s) ignoring max retries; %d > %d"
266 ,service == NULL ? "**unknown**" : service
272 _pam_delete(failure->user); /* tidy up */
273 _pam_delete(failure->name); /* tidy up */
279 * _unix_blankpasswd() is a quick check for a blank password
281 * returns TRUE if user does not have a password
282 * - to avoid prompting for one in such cases (CG)
285 int _unix_blankpasswd(unsigned int ctrl, const char *name)
287 struct passwd *pwd = NULL;
288 struct spwd *spwdent = NULL;
294 struct passwd pwd_buf;
302 * This function does not have to be too smart if something goes
303 * wrong, return FALSE and let this case to be treated somewhere
307 if (on(UNIX__NONULL, ctrl))
308 return 0; /* will fail but don't let on yet */
310 /* UNIX passwords area */
312 /* Get password file entry... */
315 buf = malloc(bufsize);
317 if ((retval = getpwnam_r(name, pwd, buf, bufsize, &pwd))) {
320 while (retval == ERANGE) {
322 buf = realloc(buf, bufsize);
323 if ((retval = getpwnam_r(name, pwd, buf, bufsize, &pwd))) {
328 pwd = getpwnam(name);
332 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
334 uid_t save_euid, save_uid;
336 save_euid = geteuid();
338 if (save_uid == pwd->pw_uid)
339 setreuid( save_euid, save_uid );
342 if (setreuid( -1, pwd->pw_uid ) == -1) {
345 if(setreuid( -1, pwd->pw_uid ) == -1)
346 /* Will fail elsewhere. */
355 spwdent = getspnam( name );
356 if (save_uid == pwd->pw_uid)
357 setreuid( save_uid, save_euid );
359 if (setreuid( -1, 0 ) == -1)
360 setreuid( save_uid, -1 );
361 setreuid( -1, save_euid );
363 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
365 * ...and shadow password file entry for this user,
366 * if shadowing is enabled
368 spwdent = getspnam(name);
371 salt = x_strdup(spwdent->sp_pwdp);
373 salt = x_strdup(pwd->pw_passwd);
375 /* Does this user have a password? */
379 if (strlen(salt) == 0)
399 * verify the password of a user
402 #include <sys/types.h>
403 #include <sys/wait.h>
405 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd,
406 unsigned int ctrl, const char *user)
408 int retval, child, fds[2];
409 void (*sighandler)(int) = NULL;
412 /* create a pipe for the password */
413 if (pipe(fds) != 0) {
414 D(("could not make pipe"));
418 if (off(UNIX_NOREAP, ctrl)) {
420 * This code arranges that the demise of the child does not cause
421 * the application to receive a signal it is not expecting - which
422 * may kill the application or worse.
424 * The "noreap" module argument is provided so that the admin can
425 * override this behavior.
427 sighandler = signal(SIGCHLD, SIG_IGN);
433 static char *envp[] = { NULL };
434 char *args[] = { NULL, NULL, NULL };
436 /* XXX - should really tidy up PAM here too */
438 /* reopen stdin as pipe */
440 dup2(fds[0], STDIN_FILENO);
442 /* exec binary helper */
443 args[0] = x_strdup(CHKPWD_HELPER);
444 args[1] = x_strdup(user);
446 execve(CHKPWD_HELPER, args, envp);
448 /* should not get here: exit with error */
449 D(("helper binary is not available"));
450 exit(PAM_AUTHINFO_UNAVAIL);
451 } else if (child > 0) {
453 /* if the stored password is NULL */
454 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
455 write(fds[1], "nullok\0\0", 8);
457 write(fds[1], "nonull\0\0", 8);
459 if (passwd != NULL) { /* send the password to the child */
460 write(fds[1], passwd, strlen(passwd)+1);
463 write(fds[1], "", 1); /* blank password */
465 close(fds[0]); /* close here to avoid possible SIGPIPE above */
467 (void) waitpid(child, &retval, 0); /* wait for helper to complete */
468 retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR;
471 retval = PAM_AUTH_ERR;
474 if (sighandler != NULL) {
475 (void) signal(SIGCHLD, sighandler); /* restore old signal handler */
478 D(("returning %d", retval));
482 int _unix_verify_password(pam_handle_t * pamh, const char *name
483 ,const char *p, unsigned int ctrl)
485 struct passwd *pwd = NULL;
486 struct spwd *spwdent = NULL;
494 #ifdef HAVE_PAM_FAIL_DELAY
495 if (off(UNIX_NODELAY, ctrl)) {
496 D(("setting delay"));
497 (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
501 /* locate the entry for this user */
503 D(("locating user's record"));
505 /* UNIX passwords area */
506 pwd = getpwnam(name); /* Get password file entry... */
509 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
511 uid_t save_euid, save_uid;
513 save_euid = geteuid();
515 if (save_uid == pwd->pw_uid)
516 setreuid( save_euid, save_uid );
519 if (setreuid( -1, pwd->pw_uid ) == -1) {
522 if(setreuid( -1, pwd->pw_uid ) == -1)
523 return PAM_CRED_INSUFFICIENT;
527 spwdent = getspnam( name );
528 if (save_uid == pwd->pw_uid)
529 setreuid( save_uid, save_euid );
531 if (setreuid( -1, 0 ) == -1)
532 setreuid( save_uid, -1 );
533 setreuid( -1, save_euid );
535 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
537 * ...and shadow password file entry for this user,
538 * if shadowing is enabled
540 spwdent = getspnam(name);
543 salt = x_strdup(spwdent->sp_pwdp);
545 salt = x_strdup(pwd->pw_passwd);
548 data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
549 if (data_name == NULL) {
550 _log_err(LOG_CRIT, pamh, "no memory for data-name");
552 strcpy(data_name, FAIL_PREFIX);
553 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
556 retval = PAM_SUCCESS;
557 if (pwd == NULL || salt == NULL || !strcmp(salt, "x")) {
559 /* we are not root perhaps this is the reason? Run helper */
560 D(("running helper binary"));
561 retval = _unix_run_helper_binary(pamh, p, ctrl, name);
562 if (pwd == NULL && !on(UNIX_AUDIT,ctrl)
563 && retval != PAM_SUCCESS)
568 D(("user's record unavailable"));
569 if (on(UNIX_AUDIT, ctrl)) {
570 /* this might be a typo and the user has given a password
571 instead of a username. Careful with this. */
572 _log_err(LOG_ALERT, pamh,
573 "check pass; user (%s) unknown", name);
576 _log_err(LOG_ALERT, pamh,
577 "check pass; user unknown");
580 retval = PAM_AUTHINFO_UNAVAIL;
583 int salt_len = strlen(salt);
585 /* the stored password is NULL */
586 if (off(UNIX__NONULL, ctrl)) {/* this means we've succeeded */
587 D(("user has empty password - access granted"));
588 retval = PAM_SUCCESS;
590 D(("user has empty password - access denied"));
591 retval = PAM_AUTH_ERR;
593 } else if (!p || (*salt == '*') || (salt_len < 13)) {
594 retval = PAM_AUTH_ERR;
596 if (!strncmp(salt, "$1$", 3)) {
597 pp = Goodcrypt_md5(p, salt);
598 if (strcmp(pp, salt) != 0) {
600 pp = Brokencrypt_md5(p, salt);
603 pp = bigcrypt(p, salt);
605 p = NULL; /* no longer needed here */
607 /* the moment of truth -- do we agree with the password? */
608 D(("comparing state of pp[%s] and salt[%s]", pp, salt));
611 * Note, we are comparing the bigcrypt of the password with
612 * the contents of the password field. If the latter was
613 * encrypted with regular crypt (and not bigcrypt) it will
614 * have been truncated for storage relative to the output
615 * of bigcrypt here. As such we need to compare only the
616 * stored string with the subset of bigcrypt's result.
617 * Bug 521314: The strncmp comparison is for legacy support.
619 if (strncmp(pp, salt, salt_len) == 0) {
620 retval = PAM_SUCCESS;
622 retval = PAM_AUTH_ERR;
627 if (retval == PAM_SUCCESS) {
628 if (data_name) /* reset failures */
629 pam_set_data(pamh, data_name, NULL, _cleanup_failures);
631 if (data_name != NULL) {
632 struct _pam_failed_auth *new = NULL;
633 const struct _pam_failed_auth *old = NULL;
635 /* get a failure recorder */
637 new = (struct _pam_failed_auth *)
638 malloc(sizeof(struct _pam_failed_auth));
642 const char *login_name;
644 login_name = _pammodutil_getlogin(pamh);
645 if (login_name == NULL) {
649 new->user = x_strdup(name ? name : "");
651 new->euid = geteuid();
652 new->name = x_strdup(login_name);
654 /* any previous failures for this user ? */
655 pam_get_data(pamh, data_name, (const void **) &old);
658 new->count = old->count + 1;
659 if (new->count >= UNIX_MAX_RETRIES) {
660 retval = PAM_MAXTRIES;
663 const char *service=NULL;
664 const char *ruser=NULL;
665 const char *rhost=NULL;
666 const char *tty=NULL;
668 (void) pam_get_item(pamh, PAM_SERVICE,
669 (const void **)&service);
670 (void) pam_get_item(pamh, PAM_RUSER,
671 (const void **)&ruser);
672 (void) pam_get_item(pamh, PAM_RHOST,
673 (const void **)&rhost);
674 (void) pam_get_item(pamh, PAM_TTY,
675 (const void **)&tty);
677 _log_err(LOG_NOTICE, pamh,
678 "authentication failure; "
679 "logname=%s uid=%d euid=%d "
680 "tty=%s ruser=%s rhost=%s "
682 new->name, new->uid, new->euid,
686 (new->user && new->user[0] != '\0')
693 pam_set_data(pamh, data_name, new, _cleanup_failures);
696 _log_err(LOG_CRIT, pamh,
697 "no memory for failure recorder");
703 _pam_delete(data_name);
709 D(("done [%d].", retval));
715 * obtain a password from the user
718 int _unix_read_password(pam_handle_t * pamh
723 ,const char *data_name
733 * make sure nothing inappropriate gets returned
736 *pass = token = NULL;
739 * which authentication token are we getting?
742 authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
745 * should we obtain the password from a PAM item ?
748 if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
749 retval = pam_get_item(pamh, authtok_flag, (const void **) pass);
750 if (retval != PAM_SUCCESS) {
752 _log_err(LOG_ALERT, pamh
753 ,"pam_get_item returned error to unix-read-password"
756 } else if (*pass != NULL) { /* we have a password! */
758 } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
759 return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
760 } else if (on(UNIX_USE_AUTHTOK, ctrl)
761 && off(UNIX__OLD_PASSWD, ctrl)) {
762 return PAM_AUTHTOK_RECOVER_ERR;
766 * getting here implies we will have to get the password from the
771 struct pam_message msg[3], *pmsg[3];
772 struct pam_response *resp;
775 /* prepare to converse */
777 if (comment != NULL && off(UNIX__QUIET, ctrl)) {
779 msg[0].msg_style = PAM_TEXT_INFO;
780 msg[0].msg = comment;
787 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
788 msg[i++].msg = prompt1;
791 if (prompt2 != NULL) {
793 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
794 msg[i++].msg = prompt2;
797 /* so call the conversation expecting i responses */
799 retval = converse(pamh, ctrl, i, pmsg, &resp);
803 /* interpret the response */
805 if (retval == PAM_SUCCESS) { /* a good conversation */
807 token = x_strdup(resp[i - replies].resp);
811 /* verify that password entered correctly */
812 if (!resp[i - 1].resp
813 || strcmp(token, resp[i - 1].resp)) {
814 _pam_delete(token); /* mistyped */
815 retval = PAM_AUTHTOK_RECOVER_ERR;
816 _make_remark(pamh, ctrl
817 ,PAM_ERROR_MSG, MISTYPED_PASS);
821 _log_err(LOG_NOTICE, pamh
822 ,"could not recover authentication token");
827 * tidy up the conversation (resp_retcode) is ignored
828 * -- what is it for anyway? AGM
831 _pam_drop_reply(resp, i);
834 retval = (retval == PAM_SUCCESS)
835 ? PAM_AUTHTOK_RECOVER_ERR : retval;
839 if (retval != PAM_SUCCESS) {
840 if (on(UNIX_DEBUG, ctrl))
841 _log_err(LOG_DEBUG, pamh,
842 "unable to obtain a password");
845 /* 'token' is the entered password */
847 if (off(UNIX_NOT_SET_PASS, ctrl)) {
849 /* we store this password as an item */
851 retval = pam_set_item(pamh, authtok_flag, token);
852 _pam_delete(token); /* clean it up */
853 if (retval != PAM_SUCCESS
854 || (retval = pam_get_item(pamh, authtok_flag
855 ,(const void **) pass))
859 _log_err(LOG_CRIT, pamh, "error manipulating password");
865 * then store it as data specific to this module. pam_end()
866 * will arrange to clean it up.
869 retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
870 if (retval != PAM_SUCCESS) {
871 _log_err(LOG_CRIT, pamh
872 ,"error manipulating password data [%s]"
873 ,pam_strerror(pamh, retval));
878 token = NULL; /* break link to password */
884 /* ****************************************************************** *
885 * Copyright (c) Jan Rêkorajski 1999.
886 * Copyright (c) Andrew G. Morgan 1996-8.
887 * Copyright (c) Alex O. Yuriev, 1996.
888 * Copyright (c) Cristian Gafton 1996.
890 * Redistribution and use in source and binary forms, with or without
891 * modification, are permitted provided that the following conditions
893 * 1. Redistributions of source code must retain the above copyright
894 * notice, and the entire permission notice in its entirety,
895 * including the disclaimer of warranties.
896 * 2. Redistributions in binary form must reproduce the above copyright
897 * notice, this list of conditions and the following disclaimer in the
898 * documentation and/or other materials provided with the distribution.
899 * 3. The name of the author may not be used to endorse or promote
900 * products derived from this software without specific prior
901 * written permission.
903 * ALTERNATIVELY, this product may be distributed under the terms of
904 * the GNU Public License, in which case the provisions of the GPL are
905 * required INSTEAD OF the above restrictions. (This clause is
906 * necessary due to a potential bad interaction between the GPL and
907 * the restrictions contained in a BSD-style copyright.)
909 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
910 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
911 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
912 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
913 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
914 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
915 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
916 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
917 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
918 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
919 * OF THE POSSIBILITY OF SUCH DAMAGE.