Here's an updated version which should apply to 1.7 and the current trunk. * It now sets PAM_RHOST and PAM_TTY when applicable, and performs account and session management in ksu. * It passes the flag which signals that we're attempting to change an expired password when we attempt to change an expired password. * It allows attempts to change an expired password when called from login, which by virtue of having a controlling terminal can actually interact with the user correctly. -- Modify krshd so that it performs PAM account and session management. It must now always fork so that it can always clean up the session. The PAM session is opened and credentials initialized after any forwarded credentials are stored to disk and before access to the user's home directory is attempted. The default service name is "kshell" or "ekshell", depending on whether or not encryption is in use, to avoid a dependency or conflict on the plain rsh server's configuration file. At run-time, krshd's behavior can be reset to the earlier, non-PAM behavior by setting "use_pam" to false in the [rshd] section of /etc/krb5.conf. Modify ftpd so that authentication with a plaintext password goes through PAM, and it performs PAM account and session management. The PAM session is opened and credentials initialized after any forwarded credentials are stored to disk. The default service name is "gssftp", mainly to avoid conflicts with other FTP servers' configuration files. At run-time, krshd's behavior can be reset to the earlier, non-PAM behavior by setting "use_pam" to false in the [ftpd] section of /etc/krb5.conf. Modify login so that instead of directly obtaining v5 or v4 credentials or running aklog, it calls PAM for authentication if strong authentication hasn't already been performed, so that it performs account management using PAM (prompting for a password change if need be), and that it performs session management. The PAM session is opened and credentials initialized after any forwarded credentials are stored to disk. The default service name is "login", because its configuration is pretty much always going to be there. At run-time, login's behavior can be reset to the earlier, non-PAM behavior by setting "use_pam" to false in the [login] section of /etc/krb5.conf. Modify ksu so that it performs account and session management for the target user account, mimicking the action of regular su. The default service name is "ksu", because on Fedora at least the configuration used is determined by whether or not a login shell is being opened, and so this may need to vary, too. At run-time, ksu's behavior can be reset to the earlier, non-PAM behavior by setting "use_pam" to false in the [ksu] section of /etc/krb5.conf. When enabled, ftpd, krshd, login.krb5, and ksu gain dependence on libpam. diff -up krb5-1.7/src/aclocal.m4 krb5-1.7/src/aclocal.m4 --- krb5-1.7/src/aclocal.m4 2009-02-18 19:38:17.000000000 -0500 +++ krb5-1.7/src/aclocal.m4 2009-06-04 13:45:57.000000000 -0400 @@ -1771,3 +1771,86 @@ AC_DEFUN(KRB5_AC_KEYRING_CCACHE,[ ])) ])dnl dnl +dnl +dnl Use PAM instead of local crypt() compare for checking local passwords, +dnl and perform PAM account, session management, and password-changing where +dnl appropriate. +dnl +AC_DEFUN(KRB5_WITH_PAM,[ +AC_ARG_WITH(pam,[AC_HELP_STRING(--with-pam,[compile with PAM support])], + withpam="$withval",withpam=auto) +AC_ARG_WITH(pam-login-service,[AC_HELP_STRING(--with-login-service,[PAM service name for login ["login"]])], + withloginpamservice="$withval",withloginpamservice=login) +AC_ARG_WITH(pam-kshell-service,[AC_HELP_STRING(--with-kshell-service,[PAM service name for unencrypted rsh ["kshell"]])], + withkshellpamservice="$withval",withkshellpamservice=kshell) +AC_ARG_WITH(pam-ekshell-service,[AC_HELP_STRING(--with-ekshell-service,[PAM service name for encrypted rsh ["ekshell"]])], + withekshellpamservice="$withval",withekshellpamservice=ekshell) +AC_ARG_WITH(pam-ftp-service,[AC_HELP_STRING(--with-ftp-service,[PAM service name for ftpd ["gssftp"]])], + withftppamservice="$withval",withftppamservice=gssftp) +AC_ARG_WITH(pam-ksu-service,[AC_HELP_STRING(--with-ksu-service,[PAM service name for ksu ["ksu"]])], + withksupamservice="$withval",withksupamservice=ksu) +old_LIBS="$LIBS" +if test "$withpam" != no ; then + AC_MSG_RESULT([checking for PAM...]) + PAM_LIBS= + + AC_CHECK_HEADERS(security/pam_appl.h) + if test "x$ac_cv_header_security_pam_appl_h" != xyes ; then + if test "$withpam" = auto ; then + AC_MSG_RESULT([Unable to locate security/pam_appl.h.]) + withpam=no + else + AC_MSG_ERROR([Unable to locate security/pam_appl.h.]) + fi + fi + + LIBS= + unset ac_cv_func_pam_start + AC_CHECK_FUNCS(putenv pam_start) + if test "x$ac_cv_func_pam_start" = xno ; then + unset ac_cv_func_pam_start + AC_CHECK_LIB(dl,dlopen) + AC_CHECK_FUNCS(pam_start) + if test "x$ac_cv_func_pam_start" = xno ; then + AC_CHECK_LIB(pam,pam_start) + unset ac_cv_func_pam_start + unset ac_cv_func_pam_getenvlist + AC_CHECK_FUNCS(pam_start pam_getenvlist) + if test "x$ac_cv_func_pam_start" = xyes ; then + PAM_LIBS="$LIBS" + else + if test "$withpam" = auto ; then + AC_MSG_RESULT([Unable to locate libpam.]) + withpam=no + else + AC_MSG_ERROR([Unable to locate libpam.]) + fi + fi + fi + fi + if test "$withpam" != no ; then + AC_MSG_NOTICE([building with PAM support]) + AC_DEFINE(USE_PAM,1,[Define if Kerberos-aware tools should support PAM]) + AC_DEFINE_UNQUOTED(LOGIN_PAM_SERVICE,"$withloginpamservice", + [Define to the name of the PAM service name to be used by login.]) + AC_DEFINE_UNQUOTED(KSHELL_PAM_SERVICE,"$withkshellpamservice", + [Define to the name of the PAM service name to be used by rshd for unencrypted sessions.]) + AC_DEFINE_UNQUOTED(EKSHELL_PAM_SERVICE,"$withekshellpamservice", + [Define to the name of the PAM service name to be used by rshd for encrypted sessions.]) + AC_DEFINE_UNQUOTED(FTP_PAM_SERVICE,"$withftppamservice", + [Define to the name of the PAM service name to be used by ftpd.]) + AC_DEFINE_UNQUOTED(KSU_PAM_SERVICE,"$withksupamservice", + [Define to the name of the PAM service name to be used by ksu.]) + PAM_LIBS="$LIBS" + NON_PAM_MAN=".\\\" " + PAM_MAN= + else + PAM_MAN=".\\\" " + NON_PAM_MAN= + fi +fi +LIBS="$old_LIBS" +AC_SUBST(PAM_LIBS) +AC_SUBST(PAM_MAN) +AC_SUBST(NON_PAM_MAN) +])dnl diff -up krb5-1.7/src/appl/bsd/configure.in krb5-1.7/src/appl/bsd/configure.in --- krb5-1.7/src/appl/bsd/configure.in 2008-12-15 15:29:01.000000000 -0500 +++ krb5-1.7/src/appl/bsd/configure.in 2009-06-04 13:45:57.000000000 -0400 @@ -24,6 +24,7 @@ AC_CHECK_LIB(odm,main, AC_CHECK_LIB(cfg,main, LOGINLIBS="$LOGINLIBS -lodm -ls -lcfg" ))) +KRB5_WITH_PAM dnl dnl Make our operating system-specific security checks and definitions for dnl login. diff -up krb5-1.7/src/appl/bsd/krshd.c krb5-1.7/src/appl/bsd/krshd.c --- krb5-1.7/src/appl/bsd/krshd.c 2009-04-15 16:07:15.000000000 -0400 +++ krb5-1.7/src/appl/bsd/krshd.c 2009-06-04 13:45:57.000000000 -0400 @@ -161,6 +161,10 @@ char copyright[] = #include #endif +#ifdef USE_PAM +#include "pam.h" +#endif + #ifndef MAXDNAME #define MAXDNAME 256 /*per the rfc*/ #endif @@ -181,6 +185,7 @@ void fatal(int, const char *); int require_encrypt = 0; int do_encrypt = 0; +int force_fork = 0; int anyport = 0; char *kprogdir = KPROGDIR; int netf; @@ -1030,14 +1035,6 @@ void doit(f, fromp) } #endif /*CRAY*/ - if (chdir(pwd->pw_dir) < 0) { - if(chdir("/") < 0) { - error("No remote directory.\n"); - goto signout_please; - } - pwd->pw_dir = "/"; - } - #ifdef KERBEROS /* krb5_kuserok returns 1 if OK */ if (!krb5_kuserok(bsd_context, client, locuser)){ @@ -1067,11 +1064,51 @@ void doit(f, fromp) goto signout_please; } +#ifdef USE_PAM + if (appl_pam_enabled(bsd_context, "rshd")) { + if (appl_pam_acct_mgmt(do_encrypt ? + EKSHELL_PAM_SERVICE : + KSHELL_PAM_SERVICE, + 0, + locuser, + "", + hostname, + NULL, + do_encrypt ? + EKSHELL_PAM_SERVICE : + KSHELL_PAM_SERVICE) != 0) { + error("Login denied.\n"); + goto signout_please; + } + if (appl_pam_requires_chauthtok()) { + error("Password change required, but not possible over rsh.\n"); + goto signout_please; + } + force_fork = 1; + appl_pam_set_forwarded_ccname(getenv("KRB5CCNAME")); + if (appl_pam_session_open() != 0) { + error("Login failure.\n"); + goto signout_please; + } + if (appl_pam_cred_init()) { + error("Login failure.\n"); + goto signout_please; + } + } else +#endif if (pwd->pw_uid && !access(NOLOGIN, F_OK)) { error("Logins currently disabled.\n"); goto signout_please; } + if (chdir(pwd->pw_dir) < 0) { + if (chdir("/") < 0) { + error("No remote directory.\n"); + goto signout_please; + } + pwd->pw_dir = "/"; + } + /* Log access to account */ pwd = (struct passwd *) getpwnam(locuser); if (pwd && (pwd->pw_uid == 0)) { @@ -1111,7 +1148,7 @@ void doit(f, fromp) (void) write(2, "", 1); - if (port||do_encrypt) { + if (port||do_encrypt||force_fork) { if (port&&(pipe(pv) < 0)) { error("Can't make pipe.\n"); goto signout_please; @@ -1416,6 +1453,15 @@ void doit(f, fromp) environ = envinit; +#ifdef USE_PAM + if (appl_pam_enabled(bsd_context, "rshd")) { + if (appl_pam_setenv() != 0) { + error("Login failure.\n"); + goto signout_please; + } + } +#endif + #ifdef KERBEROS /* To make Kerberos rcp work correctly, we must ensure that we invoke Kerberos rcp on this end, not normal rcp, even if the diff -up krb5-1.7/src/appl/bsd/login.c krb5-1.7/src/appl/bsd/login.c --- krb5-1.7/src/appl/bsd/login.c 2008-12-15 15:29:01.000000000 -0500 +++ krb5-1.7/src/appl/bsd/login.c 2009-06-04 13:45:57.000000000 -0400 @@ -145,6 +145,11 @@ typedef sigtype (*handler)(); #include "osconf.h" #endif /* KRB5_GET_TICKETS */ +#ifdef USE_PAM +#include "pam.h" +int login_use_pam = 1; +#endif + #ifndef __STDC__ #ifndef volatile #define volatile @@ -294,6 +299,9 @@ static struct login_confs { char *flagname; int *flag; } login_conf_set[] = { +#ifdef USE_PAM + {USE_PAM_CONFIGURATION_KEYWORD, &login_use_pam}, +#endif #ifdef KRB5_GET_TICKETS {"krb5_get_tickets", &login_krb5_get_tickets}, {"krb_run_aklog", &login_krb_run_aklog}, @@ -933,6 +941,21 @@ int main(argc, argv) if (!unix_needs_passwd()) break; +#ifdef USE_PAM + if (login_use_pam) { + if (appl_pam_authenticate(LOGIN_PAM_SERVICE, 1, username, "", + hostname, + NULL, + ttyname(STDIN_FILENO)) == PAM_SUCCESS) { + break; + } else { + /* the goto target label is in a different nesting scope, but + * it's roughly where we want to land */ + goto bad_login; + } + } +#endif + #ifdef KRB5_GET_TICKETS if (login_krb5_get_tickets) { /* rename these to something more verbose */ @@ -1020,6 +1043,24 @@ int main(argc, argv) /* committed to login -- turn off timeout */ (void) alarm((u_int) 0); +#ifdef USE_PAM + if (login_use_pam) { + if (appl_pam_acct_mgmt(LOGIN_PAM_SERVICE, 1, username, "", + hostname, NULL, ttyname(STDIN_FILENO)) != 0) { + printf("Login incorrect\n"); + sleepexit(1); + } + if (appl_pam_requires_chauthtok()) { + if (appl_pam_chauthtok() != 0) { + printf("Failed to change password.\n"); + sleepexit(1); + } + } + } else { + /* the "else" here is the non-PAM behavior which continues until the + * next ifdef USE_PAM block, as of this writing more or less + * duplicating the work of pam_securetty and an OQUOTA check */ +#endif /* * If valid so far and root is logging in, see if root logins on * this terminal are permitted. @@ -1060,6 +1101,21 @@ int main(argc, argv) sleepexit(0); } #endif +#ifdef USE_PAM + } +#endif /* USE_PAM */ + +#ifdef USE_PAM + if (login_use_pam) { + appl_pam_set_forwarded_ccname(getenv("KRB5CCNAME")); + if (appl_pam_session_open() != 0) { + sleepexit(1); + } + if (appl_pam_cred_init() != 0) { + sleepexit(1); + } + } +#endif /* USE_PAM */ if (chdir(pwd->pw_dir) < 0) { printf("No directory %s!\n", pwd->pw_dir); @@ -1347,6 +1403,11 @@ int main(argc, argv) } #endif /* KRB5_GET_TICKETS */ +#ifdef USE_PAM + if (login_use_pam) + appl_pam_setenv(); +#endif + if (tty[sizeof("tty")-1] == 'd') syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); if (pwd->pw_uid == 0) diff -up krb5-1.7/src/appl/bsd/Makefile.in krb5-1.7/src/appl/bsd/Makefile.in --- krb5-1.7/src/appl/bsd/Makefile.in 2009-01-05 15:27:53.000000000 -0500 +++ krb5-1.7/src/appl/bsd/Makefile.in 2009-06-04 13:45:57.000000000 -0400 @@ -11,12 +11,13 @@ SETENVOBJ=@SETENVOBJ@ LOGINLIBS=@LOGINLIBS@ LIBOBJS=@LIBOBJS@ KRSHDLIBS=@KRSHDLIBS@ +PAMOBJS=pam.o SRCS= $(srcdir)/krcp.c $(srcdir)/krlogin.c $(srcdir)/krsh.c $(srcdir)/kcmd.c \ $(srcdir)/forward.c $(srcdir)/login.c $(srcdir)/krshd.c \ $(srcdir)/krlogind.c OBJS= krcp.o krlogin.o krsh.o kcmd.o forward.o $(SETENVOBJ) login.o krshd.o \ - krlogind.o $(LIBOBJS) + krlogind.o $(LIBOBJS) $(PAMOBJS) UCB_RLOGIN = @UCB_RLOGIN@ UCB_RSH = @UCB_RSH@ @@ -53,8 +54,8 @@ install:: ) || exit 1; \ done -kshd: krshd.o kcmd.o forward.o $(SETENVOBJ) $(LIBOBJS) $(PTY_DEPLIB) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) - $(CC_LINK) -o kshd krshd.o kcmd.o forward.o $(SETENVOBJ) $(LIBOBJS) $(KRSHDLIBS) $(PTY_LIB) $(UTIL_LIB) $(KRB5_BASE_LIBS) $(APPUTILS_LIB) +kshd: krshd.o kcmd.o forward.o $(PAMOBJS) $(SETENVOBJ) $(LIBOBJS) $(PTY_DEPLIB) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) + $(CC_LINK) -o kshd krshd.o kcmd.o forward.o $(PAMOBJS) $(SETENVOBJ) $(LIBOBJS) $(KRSHDLIBS) $(PTY_LIB) $(UTIL_LIB) $(KRB5_BASE_LIBS) $(PAM_LIBS) $(APPUTILS_LIB) klogind: krlogind.o kcmd.o forward.o $(SETENVOBJ) $(LIBOBJS) $(PTY_DEPLIB) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) $(CC_LINK) -o klogind krlogind.o kcmd.o forward.o $(SETENVOBJ) $(LIBOBJS) $(PTY_LIB) $(UTIL_LIB) $(KRB5_BASE_LIBS) $(APPUTILS_LIB) @@ -71,8 +72,8 @@ install:: # No program name transformation is done with login.krb5 since it is directly # referenced by klogind. # -login.krb5: login.o $(SETENVOBJ) $(LIBOBJS) $(PTY_DEPLIB) $(KRB5_BASE_DEPLIBS) - $(CC_LINK) -o login.krb5 login.o $(SETENVOBJ) $(LIBOBJS) $(LOGINLIBS) $(PTY_LIB) $(KRB5_BASE_LIBS) +login.krb5: login.o $(SETENVOBJ) $(PAMOBJS) $(LIBOBJS) $(PTY_DEPLIB) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o login.krb5 login.o $(SETENVOBJ) $(PAMOBJS) $(LIBOBJS) $(LOGINLIBS) $(PTY_LIB) $(KRB5_BASE_LIBS) $(PAM_LIBS) install:: $(INSTALL_PROGRAM) login.krb5 $(DESTDIR)$(SERVER_BINDIR)/login.krb5 diff -up /dev/null krb5-1.7/src/appl/bsd/pam.c --- /dev/null 2009-06-04 10:34:55.169007373 -0400 +++ krb5-1.7/src/appl/bsd/pam.c 2009-06-04 13:45:57.000000000 -0400 @@ -0,0 +1,433 @@ +/* + * src/appl/bsd/pam.c + * + * Copyright 2007,2009 Red Hat, Inc. + * + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Red Hat, Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Convenience wrappers for using PAM. + */ + +#include "autoconf.h" +#ifdef USE_PAM +#include +#include +#include +#include +#include +#include "k5-int.h" +#include "pam.h" + +#ifndef MAXPWSIZE +#define MAXPWSIZE 128 +#endif + +static int appl_pam_started; +static pid_t appl_pam_starter = -1; +static int appl_pam_session_opened; +static int appl_pam_creds_initialized; +static int appl_pam_pwchange_required; +static pam_handle_t *appl_pamh; +static struct pam_conv appl_pam_conv; +static char *appl_pam_user; +struct appl_pam_non_interactive_args { + const char *user; + const char *password; +}; + +int +appl_pam_enabled(krb5_context context, const char *section) +{ + int enabled = 1; + if ((context != NULL) && (context->profile != NULL)) { + if (profile_get_boolean(context->profile, + section, + USE_PAM_CONFIGURATION_KEYWORD, + NULL, + enabled, &enabled) != 0) { + enabled = 1; + } + } + return enabled; +} + +void +appl_pam_cleanup(void) +{ + if (getpid() != appl_pam_starter) { + return; + } +#ifdef DEBUG + printf("Called to clean up PAM.\n"); +#endif + if (appl_pam_creds_initialized) { +#ifdef DEBUG + printf("Deleting PAM credentials.\n"); +#endif + pam_setcred(appl_pamh, PAM_DELETE_CRED); + appl_pam_creds_initialized = 0; + } + if (appl_pam_session_opened) { +#ifdef DEBUG + printf("Closing PAM session.\n"); +#endif + pam_close_session(appl_pamh, 0); + appl_pam_session_opened = 0; + } + appl_pam_pwchange_required = 0; + if (appl_pam_started) { +#ifdef DEBUG + printf("Shutting down PAM.\n"); +#endif + pam_end(appl_pamh, 0); + appl_pam_started = 0; + appl_pam_starter = -1; + free(appl_pam_user); + appl_pam_user = NULL; + } +} +static int +appl_pam_interactive_converse(int num_msg, const struct pam_message **msg, + struct pam_response **presp, void *appdata_ptr) +{ + const struct pam_message *message; + struct pam_response *resp; + int i, code; + char *pwstring, pwbuf[MAXPWSIZE]; + unsigned int pwsize; + resp = malloc(sizeof(struct pam_response) * num_msg); + if (resp == NULL) { + return PAM_BUF_ERR; + } + memset(resp, 0, sizeof(struct pam_response) * num_msg); + code = PAM_SUCCESS; + for (i = 0; i < num_msg; i++) { + message = &(msg[0][i]); /* XXX */ + message = msg[i]; /* XXX */ + pwstring = NULL; + switch (message->msg_style) { + case PAM_TEXT_INFO: + case PAM_ERROR_MSG: + printf("[%s]\n", message->msg ? message->msg : ""); + fflush(stdout); + resp[i].resp = NULL; + resp[i].resp_retcode = PAM_SUCCESS; + break; + case PAM_PROMPT_ECHO_ON: + case PAM_PROMPT_ECHO_OFF: + if (message->msg_style == PAM_PROMPT_ECHO_ON) { + if (fgets(pwbuf, sizeof(pwbuf), + stdin) != NULL) { + pwbuf[strcspn(pwbuf, "\r\n")] = '\0'; + pwstring = pwbuf; + } + } else { + pwstring = getpass(message->msg ? + message->msg : + ""); + } + if ((pwstring != NULL) && (pwstring[0] != '\0')) { + pwsize = strlen(pwstring); + resp[i].resp = malloc(pwsize + 1); + if (resp[i].resp == NULL) { + resp[i].resp_retcode = PAM_BUF_ERR; + } else { + memcpy(resp[i].resp, pwstring, pwsize); + resp[i].resp[pwsize] = '\0'; + resp[i].resp_retcode = PAM_SUCCESS; + } + } else { + resp[i].resp_retcode = PAM_CONV_ERR; + code = PAM_CONV_ERR; + } + break; + default: + break; + } + } + *presp = resp; + return code; +} +static int +appl_pam_non_interactive_converse(int num_msg, + const struct pam_message **msg, + struct pam_response **presp, + void *appdata_ptr) +{ + const struct pam_message *message; + struct pam_response *resp; + int i, code; + unsigned int pwsize; + struct appl_pam_non_interactive_args *args; + const char *pwstring; + resp = malloc(sizeof(struct pam_response) * num_msg); + if (resp == NULL) { + return PAM_BUF_ERR; + } + args = appdata_ptr; + memset(resp, 0, sizeof(struct pam_response) * num_msg); + code = PAM_SUCCESS; + for (i = 0; i < num_msg; i++) { + message = &((*msg)[i]); + message = msg[i]; + pwstring = NULL; + switch (message->msg_style) { + case PAM_TEXT_INFO: + case PAM_ERROR_MSG: + break; + case PAM_PROMPT_ECHO_ON: + case PAM_PROMPT_ECHO_OFF: + if (message->msg_style == PAM_PROMPT_ECHO_ON) { + /* assume "user" */ + pwstring = args->user; + } else { + /* assume "password" */ + pwstring = args->password; + } + if ((pwstring != NULL) && (pwstring[0] != '\0')) { + pwsize = strlen(pwstring); + resp[i].resp = malloc(pwsize + 1); + if (resp[i].resp == NULL) { + resp[i].resp_retcode = PAM_BUF_ERR; + } else { + memcpy(resp[i].resp, pwstring, pwsize); + resp[i].resp[pwsize] = '\0'; + resp[i].resp_retcode = PAM_SUCCESS; + } + } else { + resp[i].resp_retcode = PAM_CONV_ERR; + code = PAM_CONV_ERR; + } + break; + default: + break; + } + } + *presp = resp; + return code; +} +void +appl_pam_set_forwarded_ccname(const char *ccname) +{ + char *ccname2; + if (appl_pam_started && (ccname != NULL) && (strlen(ccname) > 0)) { + ccname2 = malloc(strlen(KRB5_ENV_CCNAME) + strlen(ccname) + 2); + if (ccname2 != NULL) { +#ifdef DEBUG + printf("Setting %s to \"%s\" in PAM environment.\n", + KRB5_ENV_CCNAME, ccname); +#endif + sprintf(ccname2, "%s=%s", KRB5_ENV_CCNAME, ccname); + pam_putenv(appl_pamh, ccname2); + } + } +} +static int +appl_pam_start(const char *service, int interactive, + const char *login_username, + const char *non_interactive_password, + const char *hostname, + const char *ruser, + const char *tty) +{ + static int exit_handler_registered; + static struct appl_pam_non_interactive_args args; + int ret = 0; + if (appl_pam_started && + (strcmp(login_username, appl_pam_user) != 0)) { + appl_pam_cleanup(); + appl_pam_user = NULL; + } + if (!appl_pam_started) { +#ifdef DEBUG + printf("Starting PAM up (service=\"%s\",user=\"%s\").\n", + service, login_username); +#endif + memset(&appl_pam_conv, 0, sizeof(appl_pam_conv)); + appl_pam_conv.conv = interactive ? + &appl_pam_interactive_converse : + &appl_pam_non_interactive_converse; + memset(&args, 0, sizeof(args)); + args.user = strdup(login_username); + args.password = non_interactive_password ? + strdup(non_interactive_password) : + NULL; + appl_pam_conv.appdata_ptr = &args; + ret = pam_start(service, login_username, + &appl_pam_conv, &appl_pamh); + if (ret == 0) { + if (hostname != NULL) { +#ifdef DEBUG + printf("Setting PAM_RHOST to \"%s\".\n", hostname); +#endif + pam_set_item(appl_pamh, PAM_RHOST, hostname); + } + if (ruser != NULL) { +#ifdef DEBUG + printf("Setting PAM_RUSER to \"%s\".\n", ruser); +#endif + pam_set_item(appl_pamh, PAM_RUSER, ruser); + } + if (tty != NULL) { +#ifdef DEBUG + printf("Setting PAM_TTY to \"%s\".\n", tty); +#endif + pam_set_item(appl_pamh, PAM_TTY, tty); + } + if (!exit_handler_registered && + (atexit(appl_pam_cleanup) != 0)) { + pam_end(appl_pamh, 0); + appl_pamh = NULL; + ret = -1; + } else { + appl_pam_started = 1; + appl_pam_starter = getpid(); + appl_pam_user = strdup(login_username); + exit_handler_registered = 1; + } + } + } + return ret; +} +int +appl_pam_authenticate(const char *service, int interactive, + const char *login_username, + const char *non_interactive_password, + const char *hostname, + const char *ruser, + const char *tty) +{ + int ret; + ret = appl_pam_start(service, interactive, login_username, + non_interactive_password, hostname, ruser, tty); + if (ret == 0) { + ret = pam_authenticate(appl_pamh, 0); + } + return ret; +} +int +appl_pam_acct_mgmt(const char *service, int interactive, + const char *login_username, + const char *non_interactive_password, + const char *hostname, + const char *ruser, + const char *tty) +{ + int ret; + appl_pam_pwchange_required = 0; + ret = appl_pam_start(service, interactive, login_username, + non_interactive_password, hostname, ruser, tty); + if (ret == 0) { +#ifdef DEBUG + printf("Calling pam_acct_mgmt().\n"); +#endif + ret = pam_acct_mgmt(appl_pamh, 0); + switch (ret) { + case PAM_IGNORE: + ret = 0; + break; + case PAM_NEW_AUTHTOK_REQD: + appl_pam_pwchange_required = 1; + ret = 0; + break; + default: + break; + } + } + return ret; +} +int +appl_pam_requires_chauthtok(void) +{ + return appl_pam_pwchange_required; +} +int +appl_pam_chauthtok(void) +{ + int ret = 0; + if (appl_pam_started) { +#ifdef DEBUG + printf("Changing PAM expired authentication token.\n"); +#endif + ret = pam_chauthtok(appl_pamh, PAM_CHANGE_EXPIRED_AUTHTOK); + } + return ret; +} +int +appl_pam_session_open(void) +{ + int ret = 0; + if (appl_pam_started) { +#ifdef DEBUG + printf("Opening PAM session.\n"); +#endif + ret = pam_open_session(appl_pamh, 0); + if (ret == 0) { + appl_pam_session_opened = 1; + } + } + return ret; +} +int +appl_pam_setenv(void) +{ + int ret = 0; +#ifdef HAVE_PAM_GETENVLIST +#ifdef HAVE_PUTENV + int i; + char **list; + if (appl_pam_started) { + list = pam_getenvlist(appl_pamh); + for (i = 0; ((list != NULL) && (list[i] != NULL)); i++) { +#ifdef DEBUG + printf("Setting \"%s\" in environment.\n", list[i]); +#endif + putenv(list[i]); + } + } +#endif +#endif + return ret; +} +int +appl_pam_cred_init(void) +{ + int ret = 0; + if (appl_pam_started) { +#ifdef DEBUG + printf("Initializing PAM credentials.\n"); +#endif + ret = pam_setcred(appl_pamh, PAM_ESTABLISH_CRED); + if (ret == 0) { + appl_pam_creds_initialized = 1; + } + } + return ret; +} +#endif diff -up /dev/null krb5-1.7/src/appl/bsd/pam.h --- /dev/null 2009-06-04 10:34:55.169007373 -0400 +++ krb5-1.7/src/appl/bsd/pam.h 2009-06-04 13:45:57.000000000 -0400 @@ -0,0 +1,65 @@ +/* + * src/appl/bsd/pam.h + * + * Copyright 2007,2009 Red Hat, Inc. + * + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of Red Hat, Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Convenience wrappers for using PAM. + */ + +#include +#ifdef HAVE_SECURITY_PAM_APPL_H +#include +#endif + +#define USE_PAM_CONFIGURATION_KEYWORD "use_pam" + +#ifdef USE_PAM +int appl_pam_enabled(krb5_context context, const char *section); +int appl_pam_authenticate(const char *service, int interactive, + const char *local_username, + const char *non_interactive_password, + const char *hostname, + const char *ruser, + const char *tty); +int appl_pam_acct_mgmt(const char *service, int interactive, + const char *local_username, + const char *non_interactive_password, + const char *hostname, + const char *ruser, + const char *tty); +int appl_pam_requires_chauthtok(void); +int appl_pam_chauthtok(void); +void appl_pam_set_forwarded_ccname(const char *ccname); +int appl_pam_session_open(void); +int appl_pam_setenv(void); +int appl_pam_cred_init(void); +void appl_pam_cleanup(void); +#endif diff -up krb5-1.7/src/appl/gssftp/configure.in krb5-1.7/src/appl/gssftp/configure.in --- krb5-1.7/src/appl/gssftp/configure.in 2006-03-31 16:00:40.000000000 -0500 +++ krb5-1.7/src/appl/gssftp/configure.in 2009-06-04 13:45:57.000000000 -0400 @@ -17,6 +17,7 @@ DECLARE_SYS_ERRLIST AC_REPLACE_FUNCS(getdtablesize) AC_CHECK_FUNCS(getcwd getdtablesize getusershell seteuid setreuid setresuid strerror getenv) AC_CHECK_LIB(crypt,crypt) dnl +KRB5_WITH_PAM KRB5_AC_LIBUTIL dnl dnl copied from appl/bsd/configure.in diff -up krb5-1.7/src/appl/gssftp/ftpd/ftpd.c krb5-1.7/src/appl/gssftp/ftpd/ftpd.c --- krb5-1.7/src/appl/gssftp/ftpd/ftpd.c 2009-01-28 00:42:11.000000000 -0500 +++ krb5-1.7/src/appl/gssftp/ftpd/ftpd.c 2009-06-04 13:45:57.000000000 -0400 @@ -67,6 +67,9 @@ static char sccsid[] = "@(#)ftpd.c 5.40 #ifdef HAVE_SHADOW #include #endif +#ifdef USE_PAM +#include "../../bsd/pam.h" +#endif #include #include #ifndef POSIX_SETJMP @@ -745,6 +748,22 @@ user(name) name); } #endif /* GSSAPI */ +#ifdef USE_PAM + if (appl_pam_enabled(kcontext, "ftpd")) { + if (appl_pam_acct_mgmt(FTP_PAM_SERVICE, 0, + pw->pw_name, "", + hostname, + NULL, + FTP_PAM_SERVICE) != 0) { + reply(530, "Login incorrect."); + return; + } + if (appl_pam_requires_chauthtok()) { + reply(530, "Password change required."); + return; + } + } +#endif if (!authorized && authlevel == AUTHLEVEL_AUTHORIZE) { strncat(buf, "; Access denied.", @@ -846,6 +865,10 @@ end_login() (void) krb5_seteuid((uid_t)0); if (logged_in) pty_logwtmp(ttyline, "", ""); +#ifdef USE_PAM + if (appl_pam_enabled(kcontext, "ftpd")) + appl_pam_cleanup(); +#endif if (have_creds) { #ifdef GSSAPI krb5_cc_destroy(kcontext, ccache); @@ -954,9 +977,19 @@ pass(passwd) * kpass fails and the user has no local password * kpass fails and the provided password doesn't match pw */ - if (pw == NULL || (!kpass(pw->pw_name, passwd) && - (want_creds || !*pw->pw_passwd || - strcmp(xpasswd, pw->pw_passwd)))) { + if ((pw == NULL) || +#ifdef USE_PAM + appl_pam_enabled(kcontext, "ftpd") ? + (appl_pam_authenticate(FTP_PAM_SERVICE, 0, + pw->pw_name, passwd, + hostname, + NULL, + FTP_PAM_SERVICE) != 0) : +#endif + (!kpass(pw->pw_name, passwd) && + (want_creds || + !*pw->pw_passwd || + strcmp(xpasswd, pw->pw_passwd)))) { pw = NULL; sleep(5); if (++login_attempts >= 3) { @@ -973,6 +1006,23 @@ pass(passwd) } login_attempts = 0; /* this time successful */ +#ifdef USE_PAM + if (appl_pam_enabled(kcontext, "ftpd")) { + if (appl_pam_acct_mgmt(FTP_PAM_SERVICE, 0, + pw->pw_name, passwd, + hostname, + NULL, + FTP_PAM_SERVICE) != 0) { + reply(530, "Login incorrect."); + return; + } + if (appl_pam_requires_chauthtok()) { + reply(530, "Password change required."); + return; + } + } +#endif + login(passwd, 0); return; } @@ -988,6 +1038,18 @@ login(passwd, logincode) chown(ccname, pw->pw_uid, pw->pw_gid); #endif } +#ifdef USE_PAM + if (appl_pam_enabled(kcontext, "ftpd")) { + if (appl_pam_session_open() != 0) { + reply(550, "Can't open PAM session."); + goto bad; + } + if (appl_pam_cred_init() != 0) { + reply(550, "Can't establish PAM credentials."); + goto bad; + } + } +#endif (void) krb5_setegid((gid_t)pw->pw_gid); (void) initgroups(pw->pw_name, pw->pw_gid); @@ -1960,6 +2022,10 @@ dologout(status) krb5_cc_destroy(kcontext, ccache); #endif } +#ifdef USE_PAM + if (appl_pam_enabled(kcontext, "ftpd")) + appl_pam_cleanup(); +#endif /* beware of flushing buffers after a SIGPIPE */ _exit(status); } diff -up krb5-1.7/src/appl/gssftp/ftpd/Makefile.in krb5-1.7/src/appl/gssftp/ftpd/Makefile.in --- krb5-1.7/src/appl/gssftp/ftpd/Makefile.in 2009-01-05 15:27:53.000000000 -0500 +++ krb5-1.7/src/appl/gssftp/ftpd/Makefile.in 2009-06-04 13:45:57.000000000 -0400 @@ -14,23 +14,25 @@ SETENVOBJ=@SETENVOBJ@ LIBOBJS=@LIBOBJS@ COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a FTPD_LIBS=@FTPD_LIBS@ +PAM_LIBS=@PAM_LIBS@ SRCS = $(srcdir)/ftpd.c ftpcmd.c $(srcdir)/popen.c \ $(srcdir)/vers.c \ $(srcdir)/../ftp/glob.c \ $(srcdir)/../ftp/radix.c \ $(srcdir)/../ftp/secure.c \ + $(srcdir)/../../bsd/pam.c \ $(srcdir)/../../bsd/getdtablesize.c $(SETENVSRC) OBJS = ftpd.o ftpcmd.o glob.o popen.o vers.o radix.o \ - secure.o $(LIBOBJS) $(SETENVOBJ) + secure.o pam.o getdtablesize.o $(LIBOBJS) $(SETENVOBJ) LOCALINCLUDES = -I$(srcdir)/.. -I$(srcdir) all:: ftpd ftpd: $(OBJS) $(PTY_DEPLIB) $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) - $(CC_LINK) -o $@ $(OBJS) $(FTPD_LIBS) $(PTY_LIB) $(UTIL_LIB) $(GSS_LIBS) $(KRB5_BASE_LIBS) + $(CC_LINK) -o $@ $(OBJS) $(FTPD_LIBS) $(PTY_LIB) $(UTIL_LIB) $(GSS_LIBS) $(KRB5_BASE_LIBS) $(PAM_LIBS) generate-files-mac: ftpcmd.c @@ -62,6 +64,8 @@ secure.o: $(srcdir)/../ftp/secure.c getdtablesize.o: $(srcdir)/../../bsd/getdtablesize.c $(CC) -c $(ALL_CFLAGS) $(srcdir)/../../bsd/getdtablesize.c +pam.o: $(srcdir)/../../bsd/pam.c + $(CC) -c $(ALL_CFLAGS) $(srcdir)/../../bsd/pam.c setenv.o: $(srcdir)/../../bsd/setenv.c $(CC) -c $(ALL_CFLAGS) $(srcdir)/../../bsd/setenv.c diff -up krb5-1.7/src/clients/ksu/main.c krb5-1.7/src/clients/ksu/main.c --- krb5-1.7/src/clients/ksu/main.c 2008-12-01 12:09:59.000000000 -0500 +++ krb5-1.7/src/clients/ksu/main.c 2009-06-04 13:45:57.000000000 -0400 @@ -25,6 +25,7 @@ * KSU was writen by: Ari Medvinsky, ari@isi.edu */ +#include "autoconf.h" #include "ksu.h" #include "adm_proto.h" #include @@ -32,6 +33,11 @@ #include #include +#ifdef USE_PAM +#include "../../appl/bsd/pam.h" +int force_fork = 0; +#endif + /* globals */ char * prog_name; int auth_debug =0; @@ -791,7 +797,24 @@ main (argc, argv) fprintf(stderr, "program to be execed %s\n",params[0]); } - if( keep_target_cache ) { +#ifdef USE_PAM + if (appl_pam_enabled(ksu_context, "ksu")) { + if (appl_pam_acct_mgmt(KSU_PAM_SERVICE, 1, target_user, NULL, + NULL, source_user, ttyname(STDERR_FILENO)) != 0) { + fprintf(stderr, "Access denied for %s.\n", target_user); + sweep_up(ksu_context, cc_target); + exit(1); + } + if (appl_pam_requires_chauthtok()) { + fprintf(stderr, "Password change required for %s.\n", target_user); + sweep_up(ksu_context, cc_target); + exit(1); + } + force_fork++; + } +#endif + + if( keep_target_cache && !force_fork ) { execv(params[0], params); com_err(prog_name, errno, "while trying to execv %s", params[0]); @@ -799,6 +822,33 @@ main (argc, argv) exit(1); }else{ statusp = 1; + +#ifdef USE_PAM + if (appl_pam_enabled(ksu_context, "ksu")) { + if (appl_pam_session_open() != 0) { + fprintf(stderr, "Error opening session for %s.\n", target_user); + sweep_up(ksu_context, cc_target); + exit(1); + } +#ifdef DEBUG + if (auth_debug){ + printf(" Opened PAM session.\n"); + } +#endif + if (appl_pam_cred_init()) { + fprintf(stderr, "Error initializing credentials for %s.\n", + target_user); + sweep_up(ksu_context, cc_target); + exit(1); + } +#ifdef DEBUG + if (auth_debug){ + printf(" Initialized PAM credentials.\n"); + } +#endif + } +#endif + switch ((child_pid = fork())) { default: if (auth_debug){ @@ -822,15 +872,34 @@ main (argc, argv) if (ret_pid == -1) { com_err(prog_name, errno, "while calling waitpid"); } - sweep_up(ksu_context, cc_target); + if( !keep_target_cache ) { + sweep_up(ksu_context, cc_target); + } exit (statusp); case -1: com_err(prog_name, errno, "while trying to fork."); sweep_up(ksu_context, cc_target); exit (1); case 0: +#ifdef USE_PAM + if (appl_pam_enabled(ksu_context, "ksu")) { + if (appl_pam_setenv() != 0) { + fprintf(stderr, "Error setting up environment for %s.\n", + target_user); + exit (1); + } +#ifdef DEBUG + if (auth_debug){ + printf(" Set up PAM environment.\n"); + } +#endif + } +#endif execv(params[0], params); com_err(prog_name, errno, "while trying to execv %s", params[0]); + if( keep_target_cache ) { + sweep_up(ksu_context, cc_target); + } exit (1); } } diff -up krb5-1.7/src/clients/ksu/Makefile.in krb5-1.7/src/clients/ksu/Makefile.in --- krb5-1.7/src/clients/ksu/Makefile.in 2009-01-05 15:27:53.000000000 -0500 +++ krb5-1.7/src/clients/ksu/Makefile.in 2009-06-04 13:45:57.000000000 -0400 @@ -15,6 +15,7 @@ SRCS = \ $(srcdir)/ccache.c \ $(srcdir)/authorization.c \ $(srcdir)/main.c \ + $(srcdir)/../../appl/bsd/pam.c \ $(srcdir)/heuristic.c \ $(srcdir)/xmalloc.c \ $(srcdir)/setenv.c @@ -23,13 +24,17 @@ OBJS = \ ccache.o \ authorization.o \ main.o \ + pam.o \ heuristic.o \ xmalloc.o @SETENVOBJ@ all:: ksu ksu: $(OBJS) $(KRB5_BASE_DEPLIBS) - $(CC_LINK) -o $@ $(OBJS) $(KRB5_BASE_LIBS) $(KSU_LIBS) + $(CC_LINK) -o $@ $(OBJS) $(KRB5_BASE_LIBS) $(KSU_LIBS) $(PAM_LIBS) + +pam.o: $(srcdir)/../../appl/bsd/pam.c + $(CC) $(ALL_CFLAGS) -c $< clean:: $(RM) ksu diff -up krb5-1.7/src/config/pre.in krb5-1.7/src/config/pre.in --- krb5-1.7/src/config/pre.in 2009-04-15 16:06:35.000000000 -0400 +++ krb5-1.7/src/config/pre.in 2009-06-04 13:45:57.000000000 -0400 @@ -181,6 +181,7 @@ LD_UNRESOLVED_PREFIX = @LD_UNRESOLVED_PR LD_SHLIBDIR_PREFIX = @LD_SHLIBDIR_PREFIX@ LDARGS = @LDARGS@ LIBS = @LIBS@ +PAM_LIBS = @PAM_LIBS@ INSTALL=@INSTALL@ INSTALL_STRIP= diff -up krb5-1.7/src/configure.in krb5-1.7/src/configure.in --- krb5-1.7/src/configure.in 2009-04-15 16:07:03.000000000 -0400 +++ krb5-1.7/src/configure.in 2009-06-04 13:45:57.000000000 -0400 @@ -1040,6 +1040,8 @@ fi dnl AC_CONFIG_SUBDIRS(appl/libpty appl/bsd appl/gssftp appl/telnet) +KRB5_WITH_PAM + AC_CONFIG_FILES(krb5-config, [chmod +x krb5-config]) V5_AC_OUTPUT_MAKEFILE(.