From b34e03d1e6f4e0254851ae9b4e366be75bca5d7f Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Thu, 3 Aug 2006 01:25:59 +0000 Subject: [PATCH] pam-aklog-20060802 A PAM module to call setpag and run aklog. Based on ideas by Sam Hartman and Doug Engert. Probably doesn't yet do everything that we want and needs more eyes, but it at least builds. --- src/pam/.cvsignore | 1 + src/pam/Makefile.in | 6 +- src/pam/pam_aklog.c | 352 ++++++++++++++++++++++++++++++++++++++++++ src/pam/pam_aklog.hp | 4 + src/pam/pam_aklog.map | 10 ++ 5 files changed, 372 insertions(+), 1 deletion(-) create mode 100644 src/pam/pam_aklog.c create mode 100644 src/pam/pam_aklog.hp create mode 100644 src/pam/pam_aklog.map diff --git a/src/pam/.cvsignore b/src/pam/.cvsignore index 2acfe898e..10327f260 100644 --- a/src/pam/.cvsignore +++ b/src/pam/.cvsignore @@ -2,4 +2,5 @@ AFS_component_version_number.c Makefile pam_afs.krb.so.1 pam_afs.so.1 +pam_aklog.so.1 test_pam diff --git a/src/pam/Makefile.in b/src/pam/Makefile.in index 07156e88e..20327e0fb 100644 --- a/src/pam/Makefile.in +++ b/src/pam/Makefile.in @@ -27,7 +27,7 @@ LDFLAGS = ${SHLIB_LDFLAGS} INCLUDES=-I${TOP_OBJDIR}/src/config -I${TOP_INCDIR} CFLAGS = ${PAM_DBG} ${PAM_OPTMZ} ${INCLUDES} ${PAM_CFLAGS} -all: test_pam pam_afs.so.1 pam_afs.krb.so.1 +all: test_pam pam_afs.so.1 pam_afs.krb.so.1 pam_aklog.so.1 afs_setcred.o: afs_setcred.c afs_pam_msg.h afs_message.h afs_util.h ${CC} ${CFLAGS} -c ${srcdir}/afs_setcred.c -o afs_setcred.o @@ -56,6 +56,10 @@ pam_afs.krb.so.1: $(SHOBJS) afs_setcred_krb.o afs_auth_krb.o afs_util_krb.o afs_setcred_krb.o afs_auth_krb.o afs_util_krb.o \ ${SHOBJS} ${KLIBS} +pam_aklog.so.1: pam_aklog.o + ../config/shlib-build -d $(srcdir) -f pam_aklog.so.1 -l pam_aklog -- \ + ${TOP_LIBDIR}/libkopenafs.a ${PAM_LIBS} + test_pam: test_pam.o set -x; \ case "$(SYS_NAME)" in \ diff --git a/src/pam/pam_aklog.c b/src/pam/pam_aklog.c new file mode 100644 index 000000000..d7f149ae8 --- /dev/null +++ b/src/pam/pam_aklog.c @@ -0,0 +1,352 @@ +/* + * Copyright 2000, International Business Machines Corporation and others. + * All Rights Reserved. + * + * This software has been released under the terms of the IBM Public + * License. For details, see the LICENSE file in the top-level source + * directory or online at http://www.openafs.org/dl/license10.html + */ + +/* + * This is a PAM module that calls setpag when creating a new session or + * establishing credentials and then forks an external aklog program. + * Eventually, it should call aklog inline without forking it. + */ + +#include +#include + +RCSID + ("$Header$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* This should really be set by the Makefile. */ +#ifndef AKLOG_PATH +# define AKLOG_PATH "/usr/bin/aklog" +#endif + +/* Warn about a problem. */ +#define WARN(A, B) \ + syslog(LOG_WARNING, "pam_afs_session: %s: %s", (A), (B)); + +/* Log a debug message. */ +#define DLOG(A, B) \ + if (debug) \ + syslog(LOG_DEBUG, "pam_afs_session:%d: %s: %s", __LINE__, (A), (B)) + +/* + * We store in the PAM environment whether aklog has already been run by + * setting afs_aklog. The value doesn't matter, but we need a pointer to use + * as the data value. + */ +static int afs_aklog_flag; + + +/* + * Given a cache template and a struct passwd, generate the cache environment + * string and return it in newly allocated space. + * + * This is unfortunately necessary on HP-UX, since HP-UX's PAM implementation + * apparently lacks any way to recover the KRB5CCNAME that's set in the PAM + * environment; hence, this module has to be told how to reconstruct the + * path. + */ +static char * +build_cache_env(const char *cache_template, struct passwd *pwd) +{ + char *cache, *out; + const char *p; + char scratch[BUFSIZ]; + size_t length; + + length = 0; + for (p = cache_template; *p != '\0'; p++) { + if (*p == '%') { + p++; + if (*p == 'u') { + sprintf(scratch, "%ld", (long) pwd->pw_uid); + length += strlen(scratch); + } else if (*p == 'p') { + sprintf(scratch, "%ld", (long) getpid()); + length += strlen(scratch); + } else { + length++; + } + } else { + length++; + } + } + cache = malloc(length + strlen("KRB5CCNAME=") + 1); + if (cache == NULL) + return NULL; + strcpy(cache, "KRB5CCNAME="); + out = cache + strlen(cache); + for (p = cache_template, out = cache; *p != '\0'; p++) { + if (*p == '%') { + p++; + if (*p == 'u') { + sprintf(out, "%ld", (long) pwd->pw_uid); + out += strlen(out); + } else if (*p == 'p') { + sprintf(out, "%ld", (long) getpid()); + out += strlen(out); + } else { + *out = *p; + out++; + } + } else { + *out = *p; + out++; + } + } + return cache; +} + + +/* + * Call aklog with the appropriate environment. Takes the PAM handle (so that + * we can get the environment), the path to aklog, and the path to the ticket + * cache (possibly a template). Returns a PAM status value. + */ +static int +run_aklog(pam_handle_t *pamh, const char *aklog, const char *cache_template) +{ + int status, result; + char *cache, scratch[BUFSIZ]; + char *username = NULL; + char **env; + struct passwd *pwd; + pid_t child; + + DEBUG("run_aklog" "called"); + + if (pam_get_item(pamh, PAM_USER, (void *) &username)) { + status = PAM_SERVICE_ERR; + goto done; + } + pwd = getpwnam(username); + if (pwd == NULL) { + status = PAM_USER_UNKNOWN; + DEBUG("user unknown", username); + goto done; + } + + /* + * HP-UX doesn't have pam_getenvlist, so for it we'll create a special + * environment that contains only PATH and KRB5CCNAME. + */ +#ifdef __hpux + env = malloc(sizeof(char *) * 3); + if (env == NULL) { + status = PAM_SERVICE_ERR; + goto done; + } + if (cache_template == NULL) + cache_template = "FILE:/tmp/krb5cc_%u"; + cache = build_cache_path(cache_template, pwd); + if (cache == NULL) { + status = PAM_SERVICE_ERR; + goto done; + } + env[0] = cache; + env[1] = getenv("PATH"); + env[2] = NULL; +#else + env = pam_getenvlist(pamh); +#endif + + /* Fork off a copy of aklog. */ + child = fork(); + if (child < 0) { + status = PAM_SERVICE_ERR; + goto done; + } else if (child == 0) { + if (setuid(pwd->pw_uid) < 0) { + WARN("cannot setuid to user", strerror(errno)); + exit(1); + } + execle(aklog, aklog, NULL, env); + WARN("cannot exec aklog", strerror(errno)); + exit(1); + } + if (waitpid(child, &result, 0) && WIFEXITED(result)) + status = PAM_SUCCESS; + else + status = PAM_SESSION_ERR; + +done: + return status; +} + + +/* + * Open a new session. Create a new PAG with k_setpag and then fork the aklog + * binary as the user. A Kerberos v5 PAM module should have previously run to + * obtain Kerberos tickets (or ticket forwarding should have already + * happened). + */ +int +pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, + const char *argv[]) +{ + int i, status; + int debug = 0, no_unlog = 0; + int do_setpag = 1; + const char *cache_template = NULL; + const char *path = AKLOG_PATH; + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) + debug++; + else if (strncmp(argv[i], "aklog=", strlen("aklog=")) == 0) + path = &argv[i][strlen("aklog=")]; + else if (strcmp(argv[i], "nopag") == 0) + do_setpag = 0; + else if (strcmp(argv[i], "no_unlog") == 0) + no_unlog = 1; +#ifdef __hpux + else if (strncmp(argv[i], "ccache=", strlen("ccache=")) == 0) + cache_template = &argv[i][strlen("ccache=")]; +#endif + else + WARN("unknown option", argv[i]); + } + + if (!k_hasafs()) { + WARN("skipping AFS session initialization", "AFS not running"); + return PAM_SUCCESS; + } + if (do_setpag && k_setpag() != 0) { + WARN("setpag failed", strerror(errno)); + return PAM_SESSION_ERR; + } + status = run_aklog(pamh, path, cache_template); + if (status != PAM_SUCCESS) + status = PAM_SESSION_ERR; + return status; +} + + +/* + * Don't do anything for authenticate. We're only an auth module so that we + * can supply a pam_setcred implementation. + */ +int +pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, + const char *argv[]) +{ + return PAM_SUCCESS; +} + + +/* + * Calling pam_setcred with PAM_ESTABLISH_CRED is equivalent to opening a new + * session for our purposes. With PAM_REFRESH_CRED, we don't call setpag, + * just run aklog again. PAM_DELETE_CRED calls unlog. + */ +int +pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, + const char *argv[]) +{ + int debug = 0, no_unlog = 0; + int do_setpag = 1; + int i, status; + const char *cache_template = NULL; + const char *path = AKLOG_PATH; + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) + debug++; + else if (strncmp(argv[i], "aklog=", strlen("aklog=")) == 0) + path = &argv[i][strlen("aklog=")]; + else if (strcmp(argv[i], "nopag") == 0) + do_setpag = 0; + else if (strcmp(argv[i], "no_unlog") == 0) + no_unlog = 1; +#ifdef __hpux + else if (strncmp(argv[i], "ccache=", strlen("ccache=")) == 0) + cache_template = &argv[i][strlen("ccache=")]; +#endif + else + WARN("unknown option", argv[i]); + } + + if (!k_hasafs()) { + WARN("skipping AFS session initialization", "AFS not running"); + return PAM_SUCCESS; + } + if (flags & PAM_DELETE_CRED) { + if (k_unlog() != 0) { + WARN("unable to delete credentials", strerror(errno)); + return PAM_CRED_ERR; + } + return PAM_SUCCESS; + } + + if (flags & (PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED)) + do_setpag = 0; + if (do_setpag && k_setpag() != 0) { + WARN("setpag failed", strerror(errno)); + return PAM_CRED_ERR; + } + status = run_aklog(pamh, path, cache_template); + if (status != PAM_SUCCESS) + status = PAM_CRED_ERR; + return status; +} + + +/* + * Close a session. Normally, what we do here is call unlog, but we can be + * configured not to do so. + */ +int +pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, + const char *argv[]) +{ + int debug = 0, no_unlog = 0; + int do_setpag = 1; + int i; + const char *cache_template = NULL; + const char *path = AKLOG_PATH; + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) + debug++; + else if (strncmp(argv[i], "aklog=", strlen("aklog=")) == 0) + path = &argv[i][strlen("aklog=")]; + else if (strcmp(argv[i], "nopag") == 0) + do_setpag = 0; + else if (strcmp(argv[i], "no_unlog") == 0) + no_unlog = 1; +#ifdef __hpux + else if (strncmp(argv[i], "ccache=", strlen("ccache=")) == 0) + cache_template = &argv[i][strlen("ccache=")]; +#endif + else + WARN("unknown option", argv[i]); + } + + if (!k_hasafs()) { + WARN("skipping AFS session initialization", "AFS not running"); + return PAM_SUCCESS; + } + if (k_unlog() != 0) { + WARN("unable to delete credentials", strerror(errno)); + return PAM_CRED_ERR; + } + return PAM_SUCCESS; +} diff --git a/src/pam/pam_aklog.hp b/src/pam/pam_aklog.hp new file mode 100644 index 000000000..264a58e83 --- /dev/null +++ b/src/pam/pam_aklog.hp @@ -0,0 +1,4 @@ ++e pam_sm_authenticate ++e pam_sm_setcred ++e pam_sm_open_session ++e pam_sm_close_session diff --git a/src/pam/pam_aklog.map b/src/pam/pam_aklog.map new file mode 100644 index 000000000..c0280a1e5 --- /dev/null +++ b/src/pam/pam_aklog.map @@ -0,0 +1,10 @@ +pam_aklog.so.1 { + global: + # Published PAM service module interfaces + pam_sm_authenticate; + pam_sm_setcred; + pam_sm_open_session; + pam_sm_close_session; + local: + *; +}; -- 2.39.5