From abf8754ad5c98462b2134aa339271b52960569c0 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Tue, 27 Jun 2006 12:34:07 +0000 Subject: Relevant BUGIDs: Purpose of commit: new feature Commit summary: --------------- * modules/pam_keyinit/pam_keyinit.c: New module. * modules/pam_keyinit/pam_keyinit.8: New. * modules/pam_keyinit/pam_keyinit.8.xml: New. * modules/pam_keyinit/README: New. * modules/pam_keyinit/README.xml: New. * modules/pam_keyinit/Makefile.am: New. * modules/pam_keyinit/tst_pam_keyinit: New. * modules/Makefile.am: Added pam_keyinit. * configure.in: Added test for the key mgmt syscall. --- modules/pam_keyinit/pam_keyinit.c | 209 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 modules/pam_keyinit/pam_keyinit.c (limited to 'modules/pam_keyinit/pam_keyinit.c') diff --git a/modules/pam_keyinit/pam_keyinit.c b/modules/pam_keyinit/pam_keyinit.c new file mode 100644 index 00000000..363adb58 --- /dev/null +++ b/modules/pam_keyinit/pam_keyinit.c @@ -0,0 +1,209 @@ +/* pam_keyinit.c: Initialise the session keyring on login through a PAM module + * + * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KEY_SPEC_SESSION_KEYRING -3 /* ID for session keyring */ +#define KEY_SPEC_USER_KEYRING -4 /* ID for UID-specific keyring */ +#define KEY_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session keyring */ + +#define KEYCTL_GET_KEYRING_ID 0 /* ask for a keyring's ID */ +#define KEYCTL_JOIN_SESSION_KEYRING 1 /* start named session keyring */ +#define KEYCTL_REVOKE 3 /* revoke a key */ +#define KEYCTL_LINK 8 /* link a key into a keyring */ + +static int my_session_keyring; +static int do_revoke; +static int xdebug = 1; + +static void debug(pam_handle_t *pamh, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +static void debug(pam_handle_t *pamh, const char *fmt, ...) +{ + va_list va; + + if (xdebug) { + va_start(va, fmt); + pam_vsyslog(pamh, LOG_DEBUG, fmt, va); + va_end(va); + } +} + +static int error(pam_handle_t *pamh, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +static int error(pam_handle_t *pamh, const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + pam_vsyslog(pamh, LOG_ERR, fmt, va); + va_end(va); + + return PAM_SESSION_ERR; +} + +/* + * initialise the session keyring for this process + */ +static int init_keyrings(pam_handle_t *pamh, int force) +{ + int session, usession, ret; + + if (!force) { + /* get the IDs of the session keyring and the user session + * keyring */ + session = syscall(__NR_keyctl, + KEYCTL_GET_KEYRING_ID, + KEY_SPEC_SESSION_KEYRING, + 0); + debug(pamh, "GET SESSION = %d", session); + if (session < 0) { + /* don't worry about keyrings if facility not + * installed */ + if (errno == ENOSYS) + return PAM_SUCCESS; + return PAM_SESSION_ERR; + } + + usession = syscall(__NR_keyctl, + KEYCTL_GET_KEYRING_ID, + KEY_SPEC_USER_SESSION_KEYRING, + 0); + debug(pamh, "GET SESSION = %d", usession); + if (usession < 0) + return PAM_SESSION_ERR; + + /* if the user session keyring is our keyring, then we don't + * need to do anything if we're not forcing */ + if (session != usession) { + do_revoke = 0; + return PAM_SUCCESS; + } + } + + /* create a session keyring, discarding the old one */ + ret = syscall(__NR_keyctl, + KEYCTL_JOIN_SESSION_KEYRING, + NULL); + debug(pamh, "JOIN = %d", ret); + if (ret < 0) + return PAM_SESSION_ERR; + + my_session_keyring = ret; + + /* make a link from the session keyring to the user keyring */ + ret = syscall(__NR_keyctl, + KEYCTL_LINK, + KEY_SPEC_USER_KEYRING, + KEY_SPEC_SESSION_KEYRING); + + return ret < 0 ? PAM_SESSION_ERR : PAM_SUCCESS; +} + +/* + * revoke the session keyring for this process + */ +static void kill_keyrings(pam_handle_t *pamh) +{ + /* revoke the session keyring we created earlier */ + if (my_session_keyring > 0) { + debug(pamh, "REVOKE %d", my_session_keyring); + + syscall(__NR_keyctl, + KEYCTL_REVOKE, + my_session_keyring); + } +} + +/* + * open a PAM session by making sure there's a session keyring + */ +PAM_EXTERN +int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + struct passwd *pw; + const char *username; + int ret, old_uid, uid, old_gid, gid, loop, force = 0; + + for (loop = 0; loop < argc; loop++) { + if (strcmp(argv[loop], "force") == 0) + force = 1; + else if (strcmp(argv[loop], "debug") == 0) + xdebug = 1; + else if (strcmp(argv[loop], "revoke") == 0) + do_revoke = 1; + } + + /* look up the target UID */ + ret = pam_get_user(pamh, &username, "key user"); + if (ret != PAM_SUCCESS) + return ret; + + pw = pam_modutil_getpwnam(pamh, username); + if (!pw) { + error(pamh, "Unable to look up user \"%s\"\n", username); + return PAM_USER_UNKNOWN; + } + + uid = pw->pw_uid; + old_uid = getuid(); + gid = pw->pw_gid; + old_gid = getgid(); + debug(pamh, "UID:%d [%d] GID:%d [%d]", uid, old_uid, gid, old_gid); + + /* switch to the real UID and GID so that the keyring ends up owned by + * the right user */ + if (uid != old_uid && setreuid(uid, -1) < 0) + return error(pamh, "Unable to change UID to %d temporarily\n", uid); + + if (gid != old_gid && setregid(gid, -1) < 0) { + error(pamh, "Unable to change GID to %d temporarily\n", gid); + setreuid(old_uid, -1); + return PAM_SESSION_ERR; + } + + ret = init_keyrings(pamh, force); + + /* return to the orignal UID and GID (probably root) */ + if (uid != old_uid && setreuid(old_uid, -1) < 0) + ret = error(pamh, "Unable to change UID back to %d\n", old_uid); + + if (gid != old_gid && setregid(old_gid, -1) < 0) + ret = error(pamh, "Unable to change GID back to %d\n", old_gid); + + return ret; +} + +/* + * close a PAM session by revoking the session keyring if requested + */ +PAM_EXTERN +int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + if (do_revoke) + kill_keyrings(pamh); + + return PAM_SUCCESS; +} -- cgit v1.2.3