4 * Copyright information at end of file.
19 #include <security/_pam_macros.h>
20 #include <security/pam_modules.h>
25 extern char *crypt(const char *key, const char *salt);
26 extern char *bigcrypt(const char *key, const char *salt);
28 /* syslogging function for errors and other information */
30 void _log_err(int err, const char *format,...)
34 va_start(args, format);
35 openlog("PAM_unix", LOG_CONS | LOG_PID, LOG_AUTH);
36 vsyslog(err, format, args);
41 /* this is a front-end for module-application conversations */
43 static int converse(pam_handle_t * pamh, int ctrl, int nargs
44 ,struct pam_message **message
45 ,struct pam_response **response)
48 struct pam_conv *conv;
50 D(("begin to converse"));
52 retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
53 if (retval == PAM_SUCCESS) {
55 retval = conv->conv(nargs, (const struct pam_message **) message
56 ,response, conv->appdata_ptr);
58 D(("returned from application's conversation function"));
60 if (retval != PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) {
61 _log_err(LOG_DEBUG, "conversation failure [%s]"
62 ,pam_strerror(pamh, retval));
64 } else if (retval != PAM_CONV_AGAIN) {
65 _log_err(LOG_ERR, "couldn't obtain coversation function [%s]"
66 ,pam_strerror(pamh, retval));
68 D(("ready to return from module conversation"));
70 return retval; /* propagate error status */
73 int _make_remark(pam_handle_t * pamh, unsigned int ctrl
74 ,int type, const char *text)
76 int retval = PAM_SUCCESS;
78 if (off(UNIX__QUIET, ctrl)) {
79 struct pam_message *pmsg[1], msg[1];
80 struct pam_response *resp;
84 msg[0].msg_style = type;
87 retval = converse(pamh, ctrl, 1, pmsg, &resp);
90 _pam_drop_reply(resp, 1);
97 * Beacause getlogin() is fucked in a weird way, and
98 * sometimes it just don't work, we reimplement it here.
100 char *PAM_getlogin(void)
102 struct utmp *ut, line;
103 char *curr_tty, *retval;
104 static char curr_user[UT_NAMESIZE + 4];
108 curr_tty = ttyname(0);
109 if (curr_tty != NULL) {
110 D(("PAM_getlogin ttyname: %s", curr_tty));
113 strncpy(line.ut_line, curr_tty, sizeof line.ut_line);
114 if ((ut = getutline(&line)) != NULL) {
115 strncpy(curr_user, ut->ut_user, UT_NAMESIZE);
120 D(("PAM_getlogin retval: %s", retval));
126 * set the control flags for the UNIX module.
129 int _set_ctrl(int flags, int *remember, int argc, const char **argv)
135 ctrl = UNIX_DEFAULTS; /* the default selection of options */
137 /* set some flags manually */
139 if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
141 set(UNIX__IAMROOT, ctrl);
143 if (flags & PAM_UPDATE_AUTHTOK) {
144 D(("UPDATE_AUTHTOK"));
145 set(UNIX__UPDATE, ctrl);
147 if (flags & PAM_PRELIM_CHECK) {
149 set(UNIX__PRELIM, ctrl);
151 if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
152 D(("DISALLOW_NULL_AUTHTOK"));
153 set(UNIX__NONULL, ctrl);
155 if (flags & PAM_SILENT) {
157 set(UNIX__QUIET, ctrl);
159 /* now parse the arguments to this module */
164 D(("pam_unix arg: %s", *argv));
166 for (j = 0; j < UNIX_CTRLS_; ++j) {
167 if (unix_args[j].token
168 && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
173 if (j >= UNIX_CTRLS_) {
174 _log_err(LOG_ERR, "unrecognized option [%s]", *argv);
176 ctrl &= unix_args[j].mask; /* for turning things off */
177 ctrl |= unix_args[j].flag; /* for turning things on */
179 if (remember != NULL) {
180 if (j == UNIX_REMEMBER_PASSWD) {
181 *remember = strtol(*argv + 9, NULL, 10);
182 if ((*remember == LONG_MIN) || (*remember == LONG_MAX))
190 ++argv; /* step to next argument */
193 /* auditing is a more sensitive version of debug */
195 if (on(UNIX_AUDIT, ctrl)) {
196 set(UNIX_DEBUG, ctrl);
198 /* return the set of flags */
204 static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
209 /* ************************************************************** *
210 * Useful non-trivial functions *
211 * ************************************************************** */
214 * the following is used to keep track of the number of times a user fails
215 * to authenticate themself.
218 #define FAIL_PREFIX "-UN*X-FAIL-"
219 #define UNIX_MAX_RETRIES 3
221 struct _pam_failed_auth {
222 char *user; /* user that's failed to be authenticated */
223 char *name; /* attempt from user with name */
224 int id; /* uid of name'd user */
225 int count; /* number of failures so far */
228 #ifndef PAM_DATA_REPLACE
229 #error "Need to get an updated libpam 0.52 or better"
232 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
235 const char *service = NULL;
236 struct _pam_failed_auth *failure;
240 quiet = err & PAM_DATA_SILENT; /* should we log something? */
241 err &= PAM_DATA_REPLACE; /* are we just replacing data? */
242 failure = (struct _pam_failed_auth *) fl;
244 if (failure != NULL) {
246 if (!quiet && !err) { /* under advisement from Sun,may go away */
248 /* log the number of authentication failures */
249 if (failure->count > 1) {
250 (void) pam_get_item(pamh, PAM_SERVICE
251 ,(const void **) &service);
253 ,"%d more authentication failure%s; %s(uid=%d) -> "
255 ,failure->count - 1, failure->count == 2 ? "" : "s"
259 ,service == NULL ? "**unknown**" : service
261 if (failure->count > UNIX_MAX_RETRIES) {
263 ,"service(%s) ignoring max retries; %d > %d"
264 ,service == NULL ? "**unknown**" : service
270 _pam_delete(failure->user); /* tidy up */
271 _pam_delete(failure->name); /* tidy up */
277 * _unix_blankpasswd() is a quick check for a blank password
279 * returns TRUE if user does not have a password
280 * - to avoid prompting for one in such cases (CG)
283 int _unix_blankpasswd(unsigned int ctrl, const char *name)
285 struct passwd *pwd = NULL;
286 struct spwd *spwdent = NULL;
293 * This function does not have to be too smart if something goes
294 * wrong, return FALSE and let this case to be treated somewhere
298 if (on(UNIX__NONULL, ctrl))
299 return 0; /* will fail but don't let on yet */
301 /* UNIX passwords area */
302 pwd = getpwnam(name); /* Get password file entry... */
305 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
307 uid_t save_euid, save_uid;
309 save_euid = geteuid();
311 if (save_uid == pwd->pw_uid)
312 setreuid( save_euid, save_uid );
315 if (setreuid( -1, pwd->pw_uid ) == -1) {
318 if(setreuid( -1, pwd->pw_uid ) == -1)
319 /* Will fail elsewhere. */
324 spwdent = getspnam( name );
325 if (save_uid == pwd->pw_uid)
326 setreuid( save_uid, save_euid );
328 if (setreuid( -1, 0 ) == -1)
329 setreuid( save_uid, -1 );
330 setreuid( -1, save_euid );
332 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
334 * ...and shadow password file entry for this user,
335 * if shadowing is enabled
337 spwdent = getspnam(name);
340 salt = x_strdup(spwdent->sp_pwdp);
342 salt = x_strdup(pwd->pw_passwd);
344 /* Does this user have a password? */
348 if (strlen(salt) == 0)
363 * verify the password of a user
366 #include <sys/types.h>
367 #include <sys/wait.h>
369 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd, unsigned int ctrl)
371 int retval, child, fds[2];
374 /* create a pipe for the password */
375 if (pipe(fds) != 0) {
376 D(("could not make pipe"));
383 static char *args[] = { NULL, NULL };
384 static char *envp[] = { NULL };
386 /* XXX - should really tidy up PAM here too */
388 /* reopen stdin as pipe */
390 dup2(fds[0], STDIN_FILENO);
392 /* exec binary helper */
393 args[0] = x_strdup(CHKPWD_HELPER);
394 execve(CHKPWD_HELPER, args, envp);
396 /* should not get here: exit with error */
397 D(("helper binary is not available"));
398 exit(PAM_AUTHINFO_UNAVAIL);
399 } else if (child > 0) {
402 /* if the stored password is NULL */
403 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
404 write(fds[1], "nullok\0\0", 8);
406 write(fds[1], "nonull\0\0", 8);
408 if (passwd != NULL) { /* send the password to the child */
409 write(fds[1], passwd, strlen(passwd)+1);
412 write(fds[1], "", 1); /* blank password */
415 (void) waitpid(child, &retval, 0); /* wait for helper to complete */
416 retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR;
419 retval = PAM_AUTH_ERR;
422 D(("returning %d", retval));
426 int _unix_verify_password(pam_handle_t * pamh, const char *name
427 ,const char *p, unsigned int ctrl)
429 struct passwd *pwd = NULL;
430 struct spwd *spwdent = NULL;
438 #ifdef HAVE_PAM_FAIL_DELAY
439 if (off(UNIX_NODELAY, ctrl)) {
440 D(("setting delay"));
441 (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
445 /* locate the entry for this user */
447 D(("locating user's record"));
449 /* UNIX passwords area */
450 pwd = getpwnam(name); /* Get password file entry... */
453 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
455 uid_t save_euid, save_uid;
457 save_euid = geteuid();
459 if (save_uid == pwd->pw_uid)
460 setreuid( save_euid, save_uid );
463 if (setreuid( -1, pwd->pw_uid ) == -1) {
466 if(setreuid( -1, pwd->pw_uid ) == -1)
467 return PAM_CRED_INSUFFICIENT;
471 spwdent = getspnam( name );
472 if (save_uid == pwd->pw_uid)
473 setreuid( save_uid, save_euid );
475 if (setreuid( -1, 0 ) == -1)
476 setreuid( save_uid, -1 );
477 setreuid( -1, save_euid );
479 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
481 * ...and shadow password file entry for this user,
482 * if shadowing is enabled
484 spwdent = getspnam(name);
487 salt = x_strdup(spwdent->sp_pwdp);
489 salt = x_strdup(pwd->pw_passwd);
492 data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
493 if (data_name == NULL) {
494 _log_err(LOG_CRIT, "no memory for data-name");
496 strcpy(data_name, FAIL_PREFIX);
497 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
500 retval = PAM_SUCCESS;
501 if (pwd == NULL || salt == NULL || !strcmp(salt, "x")) {
503 /* we are not root perhaps this is the reason? Run helper */
504 D(("running helper binary"));
505 retval = _unix_run_helper_binary(pamh, p, ctrl);
507 D(("user's record unavailable"));
508 if (on(UNIX_AUDIT, ctrl)) {
509 /* this might be a typo and the user has given a password
510 instead of a username. Careful with this. */
511 _log_err(LOG_ALERT, "check pass; user (%s) unknown", name);
513 _log_err(LOG_ALERT, "check pass; user unknown");
516 retval = PAM_AUTHINFO_UNAVAIL;
520 /* the stored password is NULL */
521 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
522 D(("user has empty password - access granted"));
523 retval = PAM_SUCCESS;
525 D(("user has empty password - access denied"));
526 retval = PAM_AUTH_ERR;
529 retval = PAM_AUTH_ERR;
531 if (!strncmp(salt, "$1$", 3)) {
532 pp = Goodcrypt_md5(p, salt);
533 if (strcmp(pp, salt) != 0) {
534 pp = Brokencrypt_md5(p, salt);
537 pp = bigcrypt(p, salt);
539 p = NULL; /* no longer needed here */
541 /* the moment of truth -- do we agree with the password? */
542 D(("comparing state of pp[%s] and salt[%s]", pp, salt));
544 if (strcmp(pp, salt) == 0) {
545 retval = PAM_SUCCESS;
547 retval = PAM_AUTH_ERR;
552 if (retval == PAM_SUCCESS) {
553 if (data_name) /* reset failures */
554 pam_set_data(pamh, data_name, NULL, _cleanup_failures);
556 if (data_name != NULL) {
557 struct _pam_failed_auth *new = NULL;
558 const struct _pam_failed_auth *old = NULL;
560 /* get a failure recorder */
562 new = (struct _pam_failed_auth *)
563 malloc(sizeof(struct _pam_failed_auth));
567 new->user = x_strdup(name);
569 new->name = x_strdup(PAM_getlogin()? PAM_getlogin() : "");
571 /* any previous failures for this user ? */
572 pam_get_data(pamh, data_name, (const void **) &old);
575 new->count = old->count + 1;
576 if (new->count >= UNIX_MAX_RETRIES) {
577 retval = PAM_MAXTRIES;
580 const char *service=NULL;
581 (void) pam_get_item(pamh, PAM_SERVICE,
582 (const void **)&service);
584 ,"authentication failure; %s(uid=%d) -> "
589 ,service == NULL ? "**unknown**":service
594 pam_set_data(pamh, data_name, new, _cleanup_failures);
597 _log_err(LOG_CRIT, "no memory for failure recorder");
603 _pam_delete(data_name);
609 D(("done [%d].", retval));
615 * obtain a password from the user
618 int _unix_read_password(pam_handle_t * pamh
623 ,const char *data_name
634 * make sure nothing inappropriate gets returned
637 *pass = token = NULL;
640 * which authentication token are we getting?
643 authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
646 * should we obtain the password from a PAM item ?
649 if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
650 retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
651 if (retval != PAM_SUCCESS) {
654 ,"pam_get_item returned error to unix-read-password"
657 } else if (item != NULL) { /* we have a password! */
661 } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
662 return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
663 } else if (on(UNIX_USE_AUTHTOK, ctrl)
664 && off(UNIX__OLD_PASSWD, ctrl)) {
665 return PAM_AUTHTOK_RECOVER_ERR;
669 * getting here implies we will have to get the password from the
674 struct pam_message msg[3], *pmsg[3];
675 struct pam_response *resp;
678 /* prepare to converse */
680 if (comment != NULL && off(UNIX__QUIET, ctrl)) {
682 msg[0].msg_style = PAM_TEXT_INFO;
683 msg[0].msg = comment;
690 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
691 msg[i++].msg = prompt1;
694 if (prompt2 != NULL) {
696 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
697 msg[i++].msg = prompt2;
700 /* so call the conversation expecting i responses */
702 retval = converse(pamh, ctrl, i, pmsg, &resp);
706 /* interpret the response */
708 if (retval == PAM_SUCCESS) { /* a good conversation */
710 token = x_strdup(resp[i - replies].resp);
714 /* verify that password entered correctly */
715 if (!resp[i - 1].resp
716 || strcmp(token, resp[i - 1].resp)) {
717 _pam_delete(token); /* mistyped */
718 retval = PAM_AUTHTOK_RECOVER_ERR;
719 _make_remark(pamh, ctrl
720 ,PAM_ERROR_MSG, MISTYPED_PASS);
725 ,"could not recover authentication token");
730 * tidy up the conversation (resp_retcode) is ignored
731 * -- what is it for anyway? AGM
734 _pam_drop_reply(resp, i);
737 retval = (retval == PAM_SUCCESS)
738 ? PAM_AUTHTOK_RECOVER_ERR : retval;
742 if (retval != PAM_SUCCESS) {
743 if (on(UNIX_DEBUG, ctrl))
744 _log_err(LOG_DEBUG, "unable to obtain a password");
747 /* 'token' is the entered password */
749 if (off(UNIX_NOT_SET_PASS, ctrl)) {
751 /* we store this password as an item */
753 retval = pam_set_item(pamh, authtok_flag, token);
754 _pam_delete(token); /* clean it up */
755 if (retval != PAM_SUCCESS
756 || (retval = pam_get_item(pamh, authtok_flag
757 ,(const void **) &item))
760 _log_err(LOG_CRIT, "error manipulating password");
766 * then store it as data specific to this module. pam_end()
767 * will arrange to clean it up.
770 retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
771 if (retval != PAM_SUCCESS) {
772 _log_err(LOG_CRIT, "error manipulating password data [%s]"
773 ,pam_strerror(pamh, retval));
778 token = NULL; /* break link to password */
782 item = NULL; /* break link to password */
787 /* ****************************************************************** *
788 * Copyright (c) Jan Rêkorajski 1999.
789 * Copyright (c) Andrew G. Morgan 1996-8.
790 * Copyright (c) Alex O. Yuriev, 1996.
791 * Copyright (c) Cristian Gafton 1996.
793 * Redistribution and use in source and binary forms, with or without
794 * modification, are permitted provided that the following conditions
796 * 1. Redistributions of source code must retain the above copyright
797 * notice, and the entire permission notice in its entirety,
798 * including the disclaimer of warranties.
799 * 2. Redistributions in binary form must reproduce the above copyright
800 * notice, this list of conditions and the following disclaimer in the
801 * documentation and/or other materials provided with the distribution.
802 * 3. The name of the author may not be used to endorse or promote
803 * products derived from this software without specific prior
804 * written permission.
806 * ALTERNATIVELY, this product may be distributed under the terms of
807 * the GNU Public License, in which case the provisions of the GPL are
808 * required INSTEAD OF the above restrictions. (This clause is
809 * necessary due to a potential bad interaction between the GPL and
810 * the restrictions contained in a BSD-style copyright.)
812 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
813 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
814 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
815 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
816 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
817 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
818 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
819 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
820 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
821 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
822 * OF THE POSSIBILITY OF SUCH DAMAGE.