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, pam_handle_t *pamh, const char *format,...)
36 pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
38 strncpy(logname, service, sizeof(logname));
39 logname[sizeof(logname) - 1 - strlen("(pam_unix)")] = '\0';
40 strncat(logname, "(pam_unix)", strlen("(pam_unix)"));
42 strncpy(logname, "pam_unix", sizeof(logname) - 1);
45 va_start(args, format);
46 openlog(logname, LOG_CONS | LOG_PID, LOG_AUTH);
47 vsyslog(err, format, args);
52 /* this is a front-end for module-application conversations */
54 static int converse(pam_handle_t * pamh, int ctrl, int nargs
55 ,struct pam_message **message
56 ,struct pam_response **response)
59 struct pam_conv *conv;
61 D(("begin to converse"));
63 retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv);
64 if (retval == PAM_SUCCESS) {
66 retval = conv->conv(nargs, (const struct pam_message **) message
67 ,response, conv->appdata_ptr);
69 D(("returned from application's conversation function"));
71 if (retval != PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) {
72 _log_err(LOG_DEBUG, pamh, "conversation failure [%s]"
73 ,pam_strerror(pamh, retval));
75 } else if (retval != PAM_CONV_AGAIN) {
76 _log_err(LOG_ERR, pamh
77 ,"couldn't obtain coversation function [%s]"
78 ,pam_strerror(pamh, retval));
80 D(("ready to return from module conversation"));
82 return retval; /* propagate error status */
85 int _make_remark(pam_handle_t * pamh, unsigned int ctrl
86 ,int type, const char *text)
88 int retval = PAM_SUCCESS;
90 if (off(UNIX__QUIET, ctrl)) {
91 struct pam_message *pmsg[1], msg[1];
92 struct pam_response *resp;
96 msg[0].msg_style = type;
99 retval = converse(pamh, ctrl, 1, pmsg, &resp);
102 _pam_drop_reply(resp, 1);
109 * Beacause getlogin() is braindead and sometimes it just
110 * doesn't work, we reimplement it here.
112 char *PAM_getlogin(void)
114 struct utmp *ut, line;
115 char *curr_tty, *retval;
116 static char curr_user[sizeof(ut->ut_user) + 4];
120 curr_tty = ttyname(0);
121 if (curr_tty != NULL) {
122 D(("PAM_getlogin ttyname: %s", curr_tty));
125 strncpy(line.ut_line, curr_tty, sizeof line.ut_line);
126 if ((ut = getutline(&line)) != NULL) {
127 strncpy(curr_user, ut->ut_user, sizeof(ut->ut_user));
132 D(("PAM_getlogin retval: %s", retval));
138 * set the control flags for the UNIX module.
141 int _set_ctrl(pam_handle_t *pamh, int flags, int *remember, int argc,
148 ctrl = UNIX_DEFAULTS; /* the default selection of options */
150 /* set some flags manually */
152 if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
154 set(UNIX__IAMROOT, ctrl);
156 if (flags & PAM_UPDATE_AUTHTOK) {
157 D(("UPDATE_AUTHTOK"));
158 set(UNIX__UPDATE, ctrl);
160 if (flags & PAM_PRELIM_CHECK) {
162 set(UNIX__PRELIM, ctrl);
164 if (flags & PAM_DISALLOW_NULL_AUTHTOK) {
165 D(("DISALLOW_NULL_AUTHTOK"));
166 set(UNIX__NONULL, ctrl);
168 if (flags & PAM_SILENT) {
170 set(UNIX__QUIET, ctrl);
172 /* now parse the arguments to this module */
177 D(("pam_unix arg: %s", *argv));
179 for (j = 0; j < UNIX_CTRLS_; ++j) {
180 if (unix_args[j].token
181 && !strncmp(*argv, unix_args[j].token, strlen(unix_args[j].token))) {
186 if (j >= UNIX_CTRLS_) {
187 _log_err(LOG_ERR, pamh,
188 "unrecognized option [%s]", *argv);
190 ctrl &= unix_args[j].mask; /* for turning things off */
191 ctrl |= unix_args[j].flag; /* for turning things on */
193 if (remember != NULL) {
194 if (j == UNIX_REMEMBER_PASSWD) {
195 *remember = strtol(*argv + 9, NULL, 10);
196 if ((*remember == LONG_MIN) || (*remember == LONG_MAX))
204 ++argv; /* step to next argument */
207 /* auditing is a more sensitive version of debug */
209 if (on(UNIX_AUDIT, ctrl)) {
210 set(UNIX_DEBUG, ctrl);
212 /* return the set of flags */
218 static void _cleanup(pam_handle_t * pamh, void *x, int error_status)
223 /* ************************************************************** *
224 * Useful non-trivial functions *
225 * ************************************************************** */
228 * the following is used to keep track of the number of times a user fails
229 * to authenticate themself.
232 #define FAIL_PREFIX "-UN*X-FAIL-"
233 #define UNIX_MAX_RETRIES 3
235 struct _pam_failed_auth {
236 char *user; /* user that's failed to be authenticated */
237 char *name; /* attempt from user with name */
238 int uid; /* uid of calling user */
239 int euid; /* euid of calling process */
240 int count; /* number of failures so far */
243 #ifndef PAM_DATA_REPLACE
244 #error "Need to get an updated libpam 0.52 or better"
247 static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err)
250 const char *service = NULL;
251 const char *ruser = NULL;
252 const char *rhost = NULL;
253 const char *tty = NULL;
254 struct _pam_failed_auth *failure;
258 quiet = err & PAM_DATA_SILENT; /* should we log something? */
259 err &= PAM_DATA_REPLACE; /* are we just replacing data? */
260 failure = (struct _pam_failed_auth *) fl;
262 if (failure != NULL) {
264 if (!quiet && !err) { /* under advisement from Sun,may go away */
266 /* log the number of authentication failures */
267 if (failure->count > 1) {
268 (void) pam_get_item(pamh, PAM_SERVICE,
269 (const void **)&service);
270 (void) pam_get_item(pamh, PAM_RUSER,
271 (const void **)&ruser);
272 (void) pam_get_item(pamh, PAM_RHOST,
273 (const void **)&rhost);
274 (void) pam_get_item(pamh, PAM_TTY,
275 (const void **)&tty);
276 _log_err(LOG_NOTICE, pamh,
277 "%d more authentication failure%s; "
278 "logname=%s uid=%d euid=%d "
279 "tty=%s ruser=%s rhost=%s "
281 failure->count - 1, failure->count == 2 ? "" : "s",
282 failure->name, failure->uid, failure->euid,
283 tty ? tty : "", ruser ? ruser : "",
285 (failure->user && failure->user[0] != '\0')
286 ? " user=" : "", failure->user
289 if (failure->count > UNIX_MAX_RETRIES) {
290 _log_err(LOG_ALERT, pamh
291 ,"service(%s) ignoring max retries; %d > %d"
292 ,service == NULL ? "**unknown**" : service
298 _pam_delete(failure->user); /* tidy up */
299 _pam_delete(failure->name); /* tidy up */
305 * _unix_blankpasswd() is a quick check for a blank password
307 * returns TRUE if user does not have a password
308 * - to avoid prompting for one in such cases (CG)
311 int _unix_blankpasswd(unsigned int ctrl, const char *name)
313 struct passwd *pwd = NULL;
314 struct spwd *spwdent = NULL;
321 * This function does not have to be too smart if something goes
322 * wrong, return FALSE and let this case to be treated somewhere
326 if (on(UNIX__NONULL, ctrl))
327 return 0; /* will fail but don't let on yet */
329 /* UNIX passwords area */
330 pwd = getpwnam(name); /* Get password file entry... */
333 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
335 uid_t save_euid, save_uid;
337 save_euid = geteuid();
339 if (save_uid == pwd->pw_uid)
340 setreuid( save_euid, save_uid );
343 if (setreuid( -1, pwd->pw_uid ) == -1) {
346 if(setreuid( -1, pwd->pw_uid ) == -1)
347 /* Will fail elsewhere. */
352 spwdent = getspnam( name );
353 if (save_uid == pwd->pw_uid)
354 setreuid( save_uid, save_euid );
356 if (setreuid( -1, 0 ) == -1)
357 setreuid( save_uid, -1 );
358 setreuid( -1, save_euid );
360 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
362 * ...and shadow password file entry for this user,
363 * if shadowing is enabled
365 spwdent = getspnam(name);
368 salt = x_strdup(spwdent->sp_pwdp);
370 salt = x_strdup(pwd->pw_passwd);
372 /* Does this user have a password? */
376 if (strlen(salt) == 0)
391 * verify the password of a user
394 #include <sys/types.h>
395 #include <sys/wait.h>
397 static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd, unsigned int ctrl)
399 int retval, child, fds[2];
402 /* create a pipe for the password */
403 if (pipe(fds) != 0) {
404 D(("could not make pipe"));
411 static char *args[] = { NULL, NULL };
412 static char *envp[] = { NULL };
414 /* XXX - should really tidy up PAM here too */
416 /* reopen stdin as pipe */
418 dup2(fds[0], STDIN_FILENO);
420 /* exec binary helper */
421 args[0] = x_strdup(CHKPWD_HELPER);
422 execve(CHKPWD_HELPER, args, envp);
424 /* should not get here: exit with error */
425 D(("helper binary is not available"));
426 exit(PAM_AUTHINFO_UNAVAIL);
427 } else if (child > 0) {
429 /* if the stored password is NULL */
430 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
431 write(fds[1], "nullok\0\0", 8);
433 write(fds[1], "nonull\0\0", 8);
435 if (passwd != NULL) { /* send the password to the child */
436 write(fds[1], passwd, strlen(passwd)+1);
439 write(fds[1], "", 1); /* blank password */
441 close(fds[0]); /* close here to avoid possible SIGPIPE above */
443 (void) waitpid(child, &retval, 0); /* wait for helper to complete */
444 retval = (retval == 0) ? PAM_SUCCESS:PAM_AUTH_ERR;
447 retval = PAM_AUTH_ERR;
450 D(("returning %d", retval));
454 int _unix_verify_password(pam_handle_t * pamh, const char *name
455 ,const char *p, unsigned int ctrl)
457 struct passwd *pwd = NULL;
458 struct spwd *spwdent = NULL;
466 #ifdef HAVE_PAM_FAIL_DELAY
467 if (off(UNIX_NODELAY, ctrl)) {
468 D(("setting delay"));
469 (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */
473 /* locate the entry for this user */
475 D(("locating user's record"));
477 /* UNIX passwords area */
478 pwd = getpwnam(name); /* Get password file entry... */
481 if (strcmp( pwd->pw_passwd, "*NP*" ) == 0)
483 uid_t save_euid, save_uid;
485 save_euid = geteuid();
487 if (save_uid == pwd->pw_uid)
488 setreuid( save_euid, save_uid );
491 if (setreuid( -1, pwd->pw_uid ) == -1) {
494 if(setreuid( -1, pwd->pw_uid ) == -1)
495 return PAM_CRED_INSUFFICIENT;
499 spwdent = getspnam( name );
500 if (save_uid == pwd->pw_uid)
501 setreuid( save_uid, save_euid );
503 if (setreuid( -1, 0 ) == -1)
504 setreuid( save_uid, -1 );
505 setreuid( -1, save_euid );
507 } else if (strcmp(pwd->pw_passwd, "x") == 0) {
509 * ...and shadow password file entry for this user,
510 * if shadowing is enabled
512 spwdent = getspnam(name);
515 salt = x_strdup(spwdent->sp_pwdp);
517 salt = x_strdup(pwd->pw_passwd);
520 data_name = (char *) malloc(sizeof(FAIL_PREFIX) + strlen(name));
521 if (data_name == NULL) {
522 _log_err(LOG_CRIT, pamh, "no memory for data-name");
524 strcpy(data_name, FAIL_PREFIX);
525 strcpy(data_name + sizeof(FAIL_PREFIX) - 1, name);
528 retval = PAM_SUCCESS;
529 if (pwd == NULL || salt == NULL || !strcmp(salt, "x")) {
531 /* we are not root perhaps this is the reason? Run helper */
532 D(("running helper binary"));
533 retval = _unix_run_helper_binary(pamh, p, ctrl);
534 if (pwd == NULL && !on(UNIX_AUDIT,ctrl)
535 && retval != PAM_SUCCESS)
540 D(("user's record unavailable"));
541 if (on(UNIX_AUDIT, ctrl)) {
542 /* this might be a typo and the user has given a password
543 instead of a username. Careful with this. */
544 _log_err(LOG_ALERT, pamh,
545 "check pass; user (%s) unknown", name);
548 _log_err(LOG_ALERT, pamh,
549 "check pass; user unknown");
552 retval = PAM_AUTHINFO_UNAVAIL;
556 /* the stored password is NULL */
557 if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */
558 D(("user has empty password - access granted"));
559 retval = PAM_SUCCESS;
561 D(("user has empty password - access denied"));
562 retval = PAM_AUTH_ERR;
565 retval = PAM_AUTH_ERR;
567 if (!strncmp(salt, "$1$", 3)) {
568 pp = Goodcrypt_md5(p, salt);
569 if (strcmp(pp, salt) != 0) {
570 pp = Brokencrypt_md5(p, salt);
573 pp = bigcrypt(p, salt);
575 p = NULL; /* no longer needed here */
577 /* the moment of truth -- do we agree with the password? */
578 D(("comparing state of pp[%s] and salt[%s]", pp, salt));
580 if (strcmp(pp, salt) == 0) {
581 retval = PAM_SUCCESS;
583 retval = PAM_AUTH_ERR;
588 if (retval == PAM_SUCCESS) {
589 if (data_name) /* reset failures */
590 pam_set_data(pamh, data_name, NULL, _cleanup_failures);
592 if (data_name != NULL) {
593 struct _pam_failed_auth *new = NULL;
594 const struct _pam_failed_auth *old = NULL;
596 /* get a failure recorder */
598 new = (struct _pam_failed_auth *)
599 malloc(sizeof(struct _pam_failed_auth));
603 new->user = x_strdup(name ? name : "");
605 new->euid = geteuid();
606 new->name = x_strdup(PAM_getlogin()? PAM_getlogin() : "");
608 /* any previous failures for this user ? */
609 pam_get_data(pamh, data_name, (const void **) &old);
612 new->count = old->count + 1;
613 if (new->count >= UNIX_MAX_RETRIES) {
614 retval = PAM_MAXTRIES;
617 const char *service=NULL;
618 const char *ruser=NULL;
619 const char *rhost=NULL;
620 const char *tty=NULL;
622 (void) pam_get_item(pamh, PAM_SERVICE,
623 (const void **)&service);
624 (void) pam_get_item(pamh, PAM_RUSER,
625 (const void **)&ruser);
626 (void) pam_get_item(pamh, PAM_RHOST,
627 (const void **)&rhost);
628 (void) pam_get_item(pamh, PAM_TTY,
629 (const void **)&tty);
631 _log_err(LOG_NOTICE, pamh,
632 "authentication failure; "
633 "logname=%s uid=%d euid=%d "
634 "tty=%s ruser=%s rhost=%s "
636 new->name, new->uid, new->euid,
640 (new->user && new->user[0] != '\0')
647 pam_set_data(pamh, data_name, new, _cleanup_failures);
650 _log_err(LOG_CRIT, pamh,
651 "no memory for failure recorder");
657 _pam_delete(data_name);
663 D(("done [%d].", retval));
669 * obtain a password from the user
672 int _unix_read_password(pam_handle_t * pamh
677 ,const char *data_name
688 * make sure nothing inappropriate gets returned
691 *pass = token = NULL;
694 * which authentication token are we getting?
697 authtok_flag = on(UNIX__OLD_PASSWD, ctrl) ? PAM_OLDAUTHTOK : PAM_AUTHTOK;
700 * should we obtain the password from a PAM item ?
703 if (on(UNIX_TRY_FIRST_PASS, ctrl) || on(UNIX_USE_FIRST_PASS, ctrl)) {
704 retval = pam_get_item(pamh, authtok_flag, (const void **) &item);
705 if (retval != PAM_SUCCESS) {
707 _log_err(LOG_ALERT, pamh
708 ,"pam_get_item returned error to unix-read-password"
711 } else if (item != NULL) { /* we have a password! */
715 } else if (on(UNIX_USE_FIRST_PASS, ctrl)) {
716 return PAM_AUTHTOK_RECOVER_ERR; /* didn't work */
717 } else if (on(UNIX_USE_AUTHTOK, ctrl)
718 && off(UNIX__OLD_PASSWD, ctrl)) {
719 return PAM_AUTHTOK_RECOVER_ERR;
723 * getting here implies we will have to get the password from the
728 struct pam_message msg[3], *pmsg[3];
729 struct pam_response *resp;
732 /* prepare to converse */
734 if (comment != NULL && off(UNIX__QUIET, ctrl)) {
736 msg[0].msg_style = PAM_TEXT_INFO;
737 msg[0].msg = comment;
744 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
745 msg[i++].msg = prompt1;
748 if (prompt2 != NULL) {
750 msg[i].msg_style = PAM_PROMPT_ECHO_OFF;
751 msg[i++].msg = prompt2;
754 /* so call the conversation expecting i responses */
756 retval = converse(pamh, ctrl, i, pmsg, &resp);
760 /* interpret the response */
762 if (retval == PAM_SUCCESS) { /* a good conversation */
764 token = x_strdup(resp[i - replies].resp);
768 /* verify that password entered correctly */
769 if (!resp[i - 1].resp
770 || strcmp(token, resp[i - 1].resp)) {
771 _pam_delete(token); /* mistyped */
772 retval = PAM_AUTHTOK_RECOVER_ERR;
773 _make_remark(pamh, ctrl
774 ,PAM_ERROR_MSG, MISTYPED_PASS);
778 _log_err(LOG_NOTICE, pamh
779 ,"could not recover authentication token");
784 * tidy up the conversation (resp_retcode) is ignored
785 * -- what is it for anyway? AGM
788 _pam_drop_reply(resp, i);
791 retval = (retval == PAM_SUCCESS)
792 ? PAM_AUTHTOK_RECOVER_ERR : retval;
796 if (retval != PAM_SUCCESS) {
797 if (on(UNIX_DEBUG, ctrl))
798 _log_err(LOG_DEBUG, pamh,
799 "unable to obtain a password");
802 /* 'token' is the entered password */
804 if (off(UNIX_NOT_SET_PASS, ctrl)) {
806 /* we store this password as an item */
808 retval = pam_set_item(pamh, authtok_flag, token);
809 _pam_delete(token); /* clean it up */
810 if (retval != PAM_SUCCESS
811 || (retval = pam_get_item(pamh, authtok_flag
812 ,(const void **) &item))
815 _log_err(LOG_CRIT, pamh, "error manipulating password");
821 * then store it as data specific to this module. pam_end()
822 * will arrange to clean it up.
825 retval = pam_set_data(pamh, data_name, (void *) token, _cleanup);
826 if (retval != PAM_SUCCESS) {
827 _log_err(LOG_CRIT, pamh
828 ,"error manipulating password data [%s]"
829 ,pam_strerror(pamh, retval));
834 token = NULL; /* break link to password */
838 item = NULL; /* break link to password */
843 /* ****************************************************************** *
844 * Copyright (c) Jan Rêkorajski 1999.
845 * Copyright (c) Andrew G. Morgan 1996-8.
846 * Copyright (c) Alex O. Yuriev, 1996.
847 * Copyright (c) Cristian Gafton 1996.
849 * Redistribution and use in source and binary forms, with or without
850 * modification, are permitted provided that the following conditions
852 * 1. Redistributions of source code must retain the above copyright
853 * notice, and the entire permission notice in its entirety,
854 * including the disclaimer of warranties.
855 * 2. Redistributions in binary form must reproduce the above copyright
856 * notice, this list of conditions and the following disclaimer in the
857 * documentation and/or other materials provided with the distribution.
858 * 3. The name of the author may not be used to endorse or promote
859 * products derived from this software without specific prior
860 * written permission.
862 * ALTERNATIVELY, this product may be distributed under the terms of
863 * the GNU Public License, in which case the provisions of the GPL are
864 * required INSTEAD OF the above restrictions. (This clause is
865 * necessary due to a potential bad interaction between the GPL and
866 * the restrictions contained in a BSD-style copyright.)
868 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
869 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
870 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
871 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
872 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
873 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
874 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
875 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
876 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
877 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
878 * OF THE POSSIBILITY OF SUCH DAMAGE.