krb5/krb5-1.6.1-pam.patch

1024 lines
31 KiB
Diff

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.
When enabled, ftpd, krshd, and login.krb5 gain dependence on libpam.
--- krb5-1.6.1/src/appl/bsd/configure.in 2006-03-27 23:35:02.000000000 -0500
+++ krb5-1.6.1/src/appl/bsd/configure.in 2007-06-21 17:39: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.
--- krb5-1.6.1/src/appl/bsd/krshd.c 2006-10-15 03:50:16.000000000 -0400
+++ krb5-1.6.1/src/appl/bsd/krshd.c 2007-06-22 14:28:57.000000000 -0400
@@ -185,6 +185,10 @@ Key_schedule v4_schedule;
#include <arpa/nameser.h>
#endif
+#ifdef USE_PAM
+#include "pam.h"
+#endif
+
#ifndef MAXDNAME
#define MAXDNAME 256 /*per the rfc*/
#endif
@@ -205,6 +209,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;
@@ -1085,14 +1090,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
#if defined(KRB5_KRB4_COMPAT) && !defined(ALWAYS_V5_KUSEROK)
@@ -1151,11 +1148,49 @@ 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,
+ "",
+ 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)) {
@@ -1195,7 +1230,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;
@@ -1507,6 +1542,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
--- krb5-1.6.1/src/appl/bsd/Makefile.in 2006-10-06 17:17:56.000000000 -0400
+++ krb5-1.6.1/src/appl/bsd/Makefile.in 2007-06-21 17:39:57.000000000 -0400
@@ -14,13 +14,14 @@ LIBOBJS=@LIBOBJS@
V4RCP=@V4RCP@
V4RCPO=@V4RCPO@
KRSHDLIBS=@KRSHDLIBS@
+PAMOBJS=pam.o
SRCS= $(srcdir)/krcp.c $(srcdir)/krlogin.c $(srcdir)/krsh.c $(srcdir)/kcmd.c \
$(srcdir)/forward.c $(srcdir)/compat_recv.c \
$(srcdir)/login.c $(srcdir)/krshd.c $(srcdir)/krlogind.c \
$(srcdir)/v4rcp.c
OBJS= krcp.o krlogin.o krsh.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) \
- login.o krshd.o krlogind.o $(V4RCPO) $(LIBOBJS)
+ login.o krshd.o krlogind.o $(V4RCPO) $(LIBOBJS) $(PAMOBJS)
UCB_RLOGIN = @UCB_RLOGIN@
UCB_RSH = @UCB_RSH@
@@ -66,8 +67,8 @@ install::
${DESTDIR}$(CLIENT_MANDIR)/`echo $$f|sed '$(transform)'`.1; \
fi
-kshd: krshd.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) $(LIBOBJS) $(PTY_DEPLIB) $(KRB4COMPAT_DEPLIBS) $(APPUTILS_DEPLIB)
- $(CC_LINK) -o kshd krshd.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) $(LIBOBJS) $(KRSHDLIBS) $(PTY_LIB) $(UTIL_LIB) $(KRB4COMPAT_LIBS) $(APPUTILS_LIB)
+kshd: krshd.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) $(PAMOBJS) $(LIBOBJS) $(PTY_DEPLIB) $(KRB4COMPAT_DEPLIBS) $(APPUTILS_DEPLIB)
+ $(CC_LINK) -o kshd krshd.o kcmd.o forward.o compat_recv.o $(PAMOBJS) $(SETENVOBJ) $(LIBOBJS) $(KRSHDLIBS) $(PTY_LIB) $(UTIL_LIB) $(KRB4COMPAT_LIBS) $(PAM_LIBS) $(APPUTILS_LIB)
klogind: krlogind.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) $(LIBOBJS) $(PTY_DEPLIB) $(KRB4COMPAT_DEPLIBS) $(APPUTILS_DEPLIB)
$(CC_LINK) -o klogind krlogind.o kcmd.o forward.o compat_recv.o $(SETENVOBJ) $(LIBOBJS) $(PTY_LIB) $(UTIL_LIB) $(KRB4COMPAT_LIBS) $(APPUTILS_LIB)
@@ -84,8 +85,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) $(KRB4COMPAT_DEPLIBS)
- $(CC_LINK) -o login.krb5 login.o $(SETENVOBJ) $(LIBOBJS) $(LOGINLIBS) $(PTY_LIB) $(KRB4COMPAT_LIBS)
+login.krb5: login.o $(SETENVOBJ) $(PAMOBJS) $(LIBOBJS) $(PTY_DEPLIB) $(KRB4COMPAT_DEPLIBS)
+ $(CC_LINK) -o login.krb5 login.o $(SETENVOBJ) $(PAMOBJS) $(LIBOBJS) $(LOGINLIBS) $(PTY_LIB) $(KRB4COMPAT_LIBS) $(PAM_LIBS)
install::
$(INSTALL_PROGRAM) login.krb5 $(DESTDIR)$(SERVER_BINDIR)/login.krb5
--- krb5-1.6.1/src/appl/bsd/login.c 2006-08-08 15:26:40.000000000 -0400
+++ krb5-1.6.1/src/appl/bsd/login.c 2007-06-22 14:09:41.000000000 -0400
@@ -159,6 +159,11 @@ typedef sigtype (*handler)();
#include "osconf.h"
#endif /* KRB5_GET_TICKETS */
+#ifdef USE_PAM
+#include "pam.h"
+int login_use_pam = 1;
+#endif
+
#ifdef KRB4_KLOGIN
/* support for running under v4 klogind, -k -K flags */
#define KRB4
@@ -351,6 +356,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},
#endif
@@ -1292,6 +1294,18 @@ 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, "",
+ 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
/* we have several sets of code:
1) get v5 tickets alone -DKRB5_GET_TICKETS
2) get v4 tickets alone [** don't! only get them *with* v5 **]
@@ -1406,6 +1420,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, 0, username, "",
+ 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.
@@ -1446,6 +1478,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);
@@ -1792,6 +1839,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)
--- /dev/null 2007-06-22 10:29:46.741860805 -0400
+++ krb5-1.6.1/src/appl/bsd/pam.c 2007-06-22 14:22:10.000000000 -0400
@@ -0,0 +1,414 @@
+/*
+ * src/appl/bsd/pam.c
+ *
+ * Copyright 2007 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.
+ */
+
+#ifdef USE_PAM
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#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[strspn(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 *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 (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 *tty)
+{
+ int ret;
+ ret = appl_pam_start(service, interactive, login_username,
+ non_interactive_password, 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 *tty)
+{
+ int ret;
+ appl_pam_pwchange_required = 0;
+ ret = appl_pam_start(service, interactive, login_username,
+ non_interactive_password, 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 authentication token.\n");
+#endif
+ ret = pam_chauthtok(appl_pamh, 0);
+ }
+ 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
--- /dev/null 2007-06-22 10:29:46.741860805 -0400
+++ krb5-1.6.1/src/appl/bsd/pam.h 2007-06-22 14:27:05.000000000 -0400
@@ -0,0 +1,61 @@
+/*
+ * src/appl/bsd/pam.h
+ *
+ * Copyright 2007 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 <krb5.h>
+#ifdef HAVE_SECURITY_PAM_APPL_H
+#include <security/pam_appl.h>
+#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 *tty);
+int appl_pam_acct_mgmt(const char *service, int interactive,
+ const char *local_username,
+ const char *non_interactive_password,
+ 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
--- krb5-1.6.1/src/appl/gssftp/ftpd/Makefile.in 2006-12-01 19:10:25.000000000 -0500
+++ krb5-1.6.1/src/appl/gssftp/ftpd/Makefile.in 2007-06-21 17:39: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) @KRB4_INCLUDES@
all:: ftpd
ftpd: $(OBJS) $(PTY_DEPLIB) $(GSS_DEPLIBS) $(KRB4COMPAT_DEPLIBS)
- $(CC_LINK) -o $@ $(OBJS) $(FTPD_LIBS) $(PTY_LIB) $(UTIL_LIB) $(GSS_LIBS) $(KRB4COMPAT_LIBS)
+ $(CC_LINK) -o $@ $(OBJS) $(FTPD_LIBS) $(PTY_LIB) $(UTIL_LIB) $(GSS_LIBS) $(PAM_LIBS) $(KRB4COMPAT_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
--- krb5-1.6.1/src/appl/gssftp/ftpd/ftpd.c 2006-08-08 15:26:40.000000000 -0400
+++ krb5-1.6.1/src/appl/gssftp/ftpd/ftpd.c 2007-06-22 14:28:09.000000000 -0400
@@ -70,6 +70,9 @@ static char sccsid[] = "@(#)ftpd.c 5.40
#ifdef HAVE_SHADOW
#include <shadow.h>
#endif
+#ifdef USE_PAM
+#include "../../bsd/pam.h"
+#endif
#include <grp.h>
#include <setjmp.h>
#ifndef POSIX_SETJMP
@@ -903,6 +906,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);
@@ -1073,9 +1080,17 @@ 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,
+ 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) {
@@ -1092,6 +1107,17 @@ 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,
+ FTP_PAM_SERVICE) != 0) {
+ reply(530, "Login incorrect.");
+ return;
+ }
+ }
+#endif
+
login(passwd, 0);
return;
}
@@ -1110,6 +1136,18 @@ login(passwd, logincode)
chown(tkt_string(), 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);
@@ -2125,6 +2163,10 @@ dologout(status)
dest_tkt();
#endif
}
+#ifdef USE_PAM
+ if (appl_pam_enabled(kcontext, "ftpd"))
+ appl_pam_cleanup();
+#endif
/* beware of flushing buffers after a SIGPIPE */
_exit(status);
}
--- krb5-1.6.1/src/appl/gssftp/configure.in 2006-03-31 16:00:40.000000000 -0500
+++ krb5-1.6.1/src/appl/gssftp/configure.in 2007-06-21 17:39: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
--- krb5-1.6.1/src/configure.in 2007-06-21 17:39:57.000000000 -0400
+++ krb5-1.6.1/src/configure.in 2007-06-21 17:39:57.000000000 -0400
@@ -929,6 +929,8 @@ if false; then
AC_CONFIG_SUBDIRS(plugins/locate/python)
fi
+KRB5_WITH_PAM
+
AC_CONFIG_FILES(krb5-config, [chmod +x krb5-config])
mansysconfdir=$sysconfdir
--- krb5-1.6.1/src/config/pre.in 2007-06-21 17:39:57.000000000 -0400
+++ krb5-1.6.1/src/config/pre.in 2007-06-21 17:39:57.000000000 -0400
@@ -180,6 +180,7 @@ SRVLIBS = @SRVLIBS@
SRVDEPLIBS = @SRVDEPLIBS@
CLNTLIBS = @CLNTLIBS@
CLNTDEPLIBS = @CLNTDEPLIBS@
+PAM_LIBS = @PAM_LIBS@
INSTALL=@INSTALL@
INSTALL_STRIP=
--- krb5-1.6.1/src/aclocal.m4 2007-06-21 17:39:57.000000000 -0400
+++ krb5-1.6.1/src/aclocal.m4 2007-06-21 17:39:57.000000000 -0400
@@ -1823,3 +1823,82 @@ 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)
+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_RESULT([Using PAM.])
+ 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.])
+ 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