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/Makefile.am | 7 +- modules/pam_keyinit/Makefile.am | 33 +++++ modules/pam_keyinit/README | 24 ++++ modules/pam_keyinit/README.xml | 41 ++++++ modules/pam_keyinit/pam_keyinit.8 | 133 +++++++++++++++++++ modules/pam_keyinit/pam_keyinit.8.xml | 241 ++++++++++++++++++++++++++++++++++ modules/pam_keyinit/pam_keyinit.c | 209 +++++++++++++++++++++++++++++ modules/pam_keyinit/tst-pam_keyinit | 2 + 8 files changed, 686 insertions(+), 4 deletions(-) create mode 100644 modules/pam_keyinit/Makefile.am create mode 100644 modules/pam_keyinit/README create mode 100644 modules/pam_keyinit/README.xml create mode 100644 modules/pam_keyinit/pam_keyinit.8 create mode 100644 modules/pam_keyinit/pam_keyinit.8.xml create mode 100644 modules/pam_keyinit/pam_keyinit.c create mode 100755 modules/pam_keyinit/tst-pam_keyinit (limited to 'modules') diff --git a/modules/Makefile.am b/modules/Makefile.am index 075eb58d..9d7a6fcb 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -3,14 +3,13 @@ # SUBDIRS = pam_access pam_cracklib pam_debug pam_deny pam_echo \ - pam_env pam_filter pam_ftp pam_group pam_issue pam_lastlog \ - pam_limits pam_listfile pam_localuser pam_mail pam_mkhomedir \ - pam_motd pam_nologin pam_permit pam_rhosts pam_rootok \ + pam_env pam_filter pam_ftp pam_group pam_issue pam_keyinit \ + pam_lastlog pam_limits pam_listfile pam_localuser pam_mail \ + pam_mkhomedir pam_motd pam_nologin pam_permit pam_rhosts pam_rootok \ pam_securetty pam_selinux pam_shells pam_stress pam_succeed_if \ pam_tally pam_time pam_umask pam_unix pam_userdb pam_warn \ pam_wheel pam_xauth pam_exec - CLEANFILES = *~ EXTRA_DIST = modules.map diff --git a/modules/pam_keyinit/Makefile.am b/modules/pam_keyinit/Makefile.am new file mode 100644 index 00000000..e75106f6 --- /dev/null +++ b/modules/pam_keyinit/Makefile.am @@ -0,0 +1,33 @@ +# +# Copyright (c) 2006 David Howells +# + +CLEANFILES = *~ + +EXTRA_DIST = README tst-pam_keyinit +XMLS = README.xml pam_keyinit.8.xml + +if HAVE_KEY_MANAGEMENT + man_MANS = pam_keyinit.8 + TESTS = tst-pam_keyinit +endif + +if ENABLE_REGENERATE_MAN +noinst_DATA = README +README: pam_keyinit.8.xml +-include $(top_srcdir)/Make.xml.rules +endif + +securelibdir = $(SECUREDIR) +secureconfdir = $(SCONFIGDIR) + +AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include +AM_LDFLAGS = -no-undefined -avoid-version -module \ + -L$(top_builddir)/libpam -lpam +if HAVE_VERSIONING + AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map +endif + +if HAVE_KEY_MANAGEMENT + securelib_LTLIBRARIES = pam_keyinit.la +endif \ No newline at end of file diff --git a/modules/pam_keyinit/README b/modules/pam_keyinit/README new file mode 100644 index 00000000..a27077b3 --- /dev/null +++ b/modules/pam_keyinit/README @@ -0,0 +1,24 @@ +# $Id$ -*- text -*- +# + +This module makes sure the calling process has its own session keyring rather +than using the default per-user session keyring. + +The following words may be supplied as arguments to the module through the PAM +configuration scripts: + + (*) "force" + + This will cause the process's current session keyring to be replaced with + a new one. If this isn't supplied, a session keyring will only be created + if the process doesn't already have its own. + + (*) "revoke" + + If the module actually created a keyring, this will cause that keyring to + be revoked on session closure. + + (*) "debug" + + This will cause the module to write some debugging information to the + syslog. diff --git a/modules/pam_keyinit/README.xml b/modules/pam_keyinit/README.xml new file mode 100644 index 00000000..47659e89 --- /dev/null +++ b/modules/pam_keyinit/README.xml @@ -0,0 +1,41 @@ + + +--> +]> + +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" + href="pam_keyinit.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_keyinit-name"]/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
diff --git a/modules/pam_keyinit/pam_keyinit.8 b/modules/pam_keyinit/pam_keyinit.8 new file mode 100644 index 00000000..40b1e125 --- /dev/null +++ b/modules/pam_keyinit/pam_keyinit.8 @@ -0,0 +1,133 @@ +.\"Generated by db2man.xsl. Don't modify this, modify the source. +.de Sh \" Subsection +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Ip \" List item +.br +.ie \\n(.$>=3 .ne \\$3 +.el .ne 3 +.IP "\\$1" \\$2 +.. +.TH "PAM_KEYINIT" 8 "" "" "" +.SH NAME +pam_keyinit \- Kernel session keyring initialiser module +.SH "SYNOPSIS" +.ad l +.hy 0 +.HP 15 +\fBpam_keyinit\&.so\fR [debug] [force] [revoke] +.ad +.hy + +.SH "DESCRIPTION" + +.PP +The pam_keyinit PAM module ensures that the invoking process has a session keyring other than the user default session keyring\&. + +.PP +The session component of the module checks to see if the process's session keyring is the user default, and, if it is, creates a new anonymous session keyring with which to replace it\&. + +.PP +If a new session keyring is created, it will install a link to the user common keyring in the session keyring so that keys common to the user will be automatically accessible through it\&. + +.PP +The session keyring of the invoking process will thenceforth be inherited by all its children unless they override it\&. + +.PP +This module is intended primarily for use by login processes\&. Be aware that after the session keyring has been replaced, the old session keyring and the keys it contains will no longer be accessible\&. + +.PP +This module should not, generally, be invoked by programs like \fIsu\fR, since it is usually desirable for the key set to percolate through to the alternate context\&. The keys have their own permissions system to manage this\&. + +.PP +This module should be included as early as possible in a PAM configuration, so that other PAM modules can attach tokens to the keyring\&. + +.PP +The keyutils package is used to manipulate keys more directly\&. This included in the Fedora Extras 5+ and Red Hat Enterprise Linux 4 U2+ and can also be obtained from: + +.PP + Keyutils : \fIhttp://people.redhat.com/~dhowells/keyutils/\fR + +.SH "OPTIONS" + +.TP +\fBdebug\fR +Log debug information with \fBsyslog\fR(3)\&. + +.TP +\fBforce\fR +Causes the session keyring of the invoking process to be replaced unconditionally\&. + +.TP +\fBrevoke\fR +Causes the session keyring of the invoking process to be revoked when the invoking process exits if the session keyring was created for this process in the first place\&. + +.SH "MODULE SERVICES PROVIDED" + +.PP +Only the \fIsession\fR service is supported\&. + +.SH "RETURN VALUES" + +.TP +PAM_SUCCESS +This module will usually return this value + +.TP +PAM_AUTH_ERR +Authentication failure\&. + +.TP +PAM_BUF_ERR +Memory buffer error\&. + +.TP +PAM_IGNORE +The return value should be ignored by PAM dispatch\&. + +.TP +PAM_SERVICE_ERR +Cannot determine the user name\&. + +.TP +PAM_SESSION_ERR +This module will return this value if its arguments are invalid or if a system error such as ENOMEM occurs\&. + +.TP +PAM_USER_UNKNOWN +User not known\&. + +.SH "EXAMPLES" + +.PP +Add this line to your login entries to start each login session with its own session keyring: + +.nf + +session required pam_keyinit\&.so + +.fi + + +.PP +This will prevent keys from one session leaking into another session for the same user\&. + +.SH "SEE ALSO" + +.PP + \fBpam\&.conf\fR(5), \fBpam\&.d\fR(8), \fBpam\fR(8) \fBkeyctl\fR(1) + +.SH "AUTHOR" + +.PP +pam_keyinit was written by David Howells, \&. + diff --git a/modules/pam_keyinit/pam_keyinit.8.xml b/modules/pam_keyinit/pam_keyinit.8.xml new file mode 100644 index 00000000..c7dddf54 --- /dev/null +++ b/modules/pam_keyinit/pam_keyinit.8.xml @@ -0,0 +1,241 @@ + + + + + + + pam_keyinit + 8 + Linux-PAM Manual + + + + pam_keyinit + Kernel session keyring initialiser module + + + + + pam_keyinit.so + + debug + + + force + + + revoke + + + + + + DESCRIPTION + + The pam_keyinit PAM module ensures that the invoking process has a + session keyring other than the user default session keyring. + + + The session component of the module checks to see if the process's + session keyring is the user default, and, if it is, creates a new + anonymous session keyring with which to replace it. + + + If a new session keyring is created, it will install a link to the user + common keyring in the session keyring so that keys common to the user + will be automatically accessible through it. + + + The session keyring of the invoking process will thenceforth be inherited + by all its children unless they override it. + + + This module is intended primarily for use by login processes. Be aware + that after the session keyring has been replaced, the old session keyring + and the keys it contains will no longer be accessible. + + + This module should not, generally, be invoked by programs like + su, since it is usually desirable for the + key set to percolate through to the alternate context. The keys have + their own permissions system to manage this. + + + This module should be included as early as possible in a PAM + configuration, so that other PAM modules can attach tokens to the + keyring. + + + The keyutils package is used to manipulate keys more directly. This + can be obtained from: + + + + Keyutils + + + + + + OPTIONS + + + + + + + + Log debug information with + syslog3 + . + + + + + + + + + + + Causes the session keyring of the invoking process to be replaced + unconditionally. + + + + + + + + + + + Causes the session keyring of the invoking process to be revoked + when the invoking process exits if the session keyring was created + for this process in the first place. + + + + + + + + + MODULE SERVICES PROVIDED + + Only the session service is supported. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + This module will usually return this value + + + + + + PAM_AUTH_ERR + + + Authentication failure. + + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + + PAM_IGNORE + + + The return value should be ignored by PAM dispatch. + + + + + + PAM_SERVICE_ERR + + + Cannot determine the user name. + + + + + + PAM_SESSION_ERR + + + This module will return this value if its arguments are invalid or + if a system error such as ENOMEM occurs. + + + + + + PAM_USER_UNKNOWN + + + User not known. + + + + + + + + + EXAMPLES + + Add this line to your login entries to start each login session with its + own session keyring: + +session required pam_keyinit.so + + + + This will prevent keys from one session leaking into another session for + the same user. + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d8 + , + + pam8 + + + keyctl1 + + + + + + AUTHOR + + pam_keyinit was written by David Howells, <dhowells@redhat.com>. + + + + 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; +} diff --git a/modules/pam_keyinit/tst-pam_keyinit b/modules/pam_keyinit/tst-pam_keyinit new file mode 100755 index 00000000..f0a7b9bc --- /dev/null +++ b/modules/pam_keyinit/tst-pam_keyinit @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_keyinit.so -- cgit v1.2.3