]> granicus.if.org Git - linux-pam/commitdiff
Relevant BUGIDs:
authorTomas Mraz <tm@t8m.info>
Tue, 27 Jun 2006 12:34:07 +0000 (12:34 +0000)
committerTomas Mraz <tm@t8m.info>
Tue, 27 Jun 2006 12:34:07 +0000 (12:34 +0000)
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.

ChangeLog
configure.in
modules/Makefile.am
modules/pam_keyinit/Makefile.am [new file with mode: 0644]
modules/pam_keyinit/README [new file with mode: 0644]
modules/pam_keyinit/README.xml [new file with mode: 0644]
modules/pam_keyinit/pam_keyinit.8 [new file with mode: 0644]
modules/pam_keyinit/pam_keyinit.8.xml [new file with mode: 0644]
modules/pam_keyinit/pam_keyinit.c [new file with mode: 0644]
modules/pam_keyinit/tst-pam_keyinit [new file with mode: 0755]

index 106430d042156003ad17c8232bcaf525977f9afa..ec179c53eadff5493c846959a87eac6ce4ec3d97 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2006-06-27  David Howells <dhowells@redhat.com>
+
+       * 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.
+
 2006-06-27  Thorsten Kukuk <kukuk@thkukuk.de>
 
        * m4/libprelude.m4: Sync with upstream.
index 09869e3cfa0f12f3794de1c22f72c9c50a0de077..315c89cc80f3752dc5bf7e38f77052b6b35677ee 100644 (file)
@@ -451,6 +451,29 @@ AH_VERBATIM([_ZZENABLE_NLS],
 #define N_(msgid) msgid
 #endif /* ENABLE_NLS */])
 
+dnl
+dnl Check for the availability of the kernel key management facility
+dnl - The pam_keyinit module only requires the syscalls, not the error codes
+dnl
+have_key_syscalls=0
+AC_CHECK_DECL(__NR_keyctl, [have_key_syscalls=1],,[#include <sys/syscall.h>])
+have_key_errors=0
+AC_CHECK_DECL(ENOKEY, [have_key_errors=1])
+
+HAVE_KEY_MANAGEMENT=0
+if test $have_key_syscalls$have_key_errors = 11
+then
+       HAVE_KEY_MANAGEMENT=1
+fi
+
+if test $HAVE_KEY_MANAGEMENT = 1; then
+   AC_DEFINE([HAVE_KEY_MANAGEMENT], 1,
+            [Defined if the kernel key management facility is available])
+fi
+AC_SUBST([HAVE_KEY_MANAGEMENT], $HAVE_KEY_MANAGEMENT)
+
+AM_CONDITIONAL([HAVE_KEY_MANAGEMENT], [test ! -z "$have_key_syscalls"])
+
 dnl Files to be created from when we run configure
 AC_OUTPUT(Makefile libpam/Makefile libpamc/Makefile libpamc/test/Makefile \
        libpam_misc/Makefile conf/Makefile conf/pam_conv1/Makefile \
@@ -461,7 +484,8 @@ AC_OUTPUT(Makefile libpam/Makefile libpamc/Makefile libpamc/test/Makefile \
        modules/pam_echo/Makefile modules/pam_env/Makefile \
        modules/pam_filter/Makefile modules/pam_filter/upperLOWER/Makefile \
        modules/pam_ftp/Makefile modules/pam_group/Makefile \
-       modules/pam_issue/Makefile modules/pam_lastlog/Makefile \
+       modules/pam_issue/Makefile \
+       modules/pam_keyinit/Makefile modules/pam_lastlog/Makefile \
        modules/pam_limits/Makefile modules/pam_listfile/Makefile \
        modules/pam_localuser/Makefile modules/pam_mail/Makefile \
        modules/pam_mkhomedir/Makefile modules/pam_motd/Makefile \
index 075eb58dda8d75f8d0828a015f5152143adf191d..9d7a6fcb8ce9260dc4678400d4ec97152173a531 100644 (file)
@@ -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 (file)
index 0000000..e75106f
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 2006 David Howells <dhowells@redhat.com>
+#
+
+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 (file)
index 0000000..a27077b
--- /dev/null
@@ -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 (file)
index 0000000..47659e8
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.docbook.org/xml/4.3/docbookx.dtd"
+[
+<!--
+<!ENTITY pamaccess SYSTEM "pam_keyinit.8.xml">
+-->
+]>
+
+<article>
+
+  <articleinfo>
+
+    <title>
+      <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+      href="pam_keyinit.8.xml" xpointer='xpointer(//refnamediv[@id = "pam_keyinit-name"]/*)'/>
+    </title>
+
+  </articleinfo>
+
+  <section>
+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+      href="pam_keyinit.8.xml" xpointer='xpointer(//refsect1[@id = "pam_keyinit-description"]/*)'/>
+  </section>
+
+  <section>
+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+      href="pam_keyinit.8.xml" xpointer='xpointer(//refsect1[@id = "pam_keyinit-options"]/*)'/>
+  </section>
+
+  <section>
+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+      href="pam_keyinit.8.xml" xpointer='xpointer(//refsect1[@id = "pam_keyinit-examples"]/*)'/>
+  </section>
+
+  <section>
+    <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
+      href="pam_keyinit.8.xml" xpointer='xpointer(//refsect1[@id = "pam_keyinit-author"]/*)'/>
+  </section>
+
+</article>
diff --git a/modules/pam_keyinit/pam_keyinit.8 b/modules/pam_keyinit/pam_keyinit.8
new file mode 100644 (file)
index 0000000..40b1e12
--- /dev/null
@@ -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, <dhowells@redhat\&.com>\&.
+
diff --git a/modules/pam_keyinit/pam_keyinit.8.xml b/modules/pam_keyinit/pam_keyinit.8.xml
new file mode 100644 (file)
index 0000000..c7dddf5
--- /dev/null
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding='UTF-8'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+       "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+
+<refentry id="pam_keyinit">
+
+  <refmeta>
+    <refentrytitle>pam_keyinit</refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id="pam_keyinit-name">
+    <refname>pam_keyinit</refname>
+    <refpurpose>Kernel session keyring initialiser module</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis id="pam_keyinit-cmdsynopsis">
+      <command>pam_keyinit.so</command>
+      <arg choice="opt">
+       debug
+      </arg>
+      <arg choice="opt">
+       force
+      </arg>
+      <arg choice="opt">
+       revoke
+      </arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1 id="pam_keyinit-description">
+    <title>DESCRIPTION</title>
+    <para>
+      The pam_keyinit PAM module ensures that the invoking process has a
+      session keyring other than the user default session keyring.
+    </para>
+    <para>
+      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.
+    </para>
+    <para>
+      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.
+    </para>
+    <para>
+      The session keyring of the invoking process will thenceforth be inherited
+      by all its children unless they override it.
+    </para>
+    <para>
+      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.
+    </para>
+    <para>
+      This module should not, generally, be invoked by programs like
+      <emphasis remap='B'>su</emphasis>, 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.
+    </para>
+    <para>
+      This module should be included as early as possible in a PAM
+      configuration, so that other PAM modules can attach tokens to the
+      keyring.
+    </para>
+    <para>
+      The keyutils package is used to manipulate keys more directly.  This
+      can be obtained from:
+    </para>
+    <para>
+      <ulink url="http://people.redhat.com/~dhowells/keyutils/">
+       Keyutils
+      </ulink>
+    </para>
+  </refsect1>
+
+  <refsect1 id="pam_keyinit-options">
+    <title>OPTIONS</title>
+    <variablelist>
+      <varlistentry>
+        <term>
+          <option>debug</option>
+        </term>
+        <listitem>
+          <para>
+            Log debug information with <citerefentry>
+           <refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum>
+            </citerefentry>.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>force</option>
+        </term>
+        <listitem>
+          <para>
+           Causes the session keyring of the invoking process to be replaced
+           unconditionally.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>revoke</option>
+        </term>
+        <listitem>
+          <para>
+           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.
+          </para>
+        </listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id="pam_keyinit-services">
+    <title>MODULE SERVICES PROVIDED</title>
+    <para>
+      Only the <emphasis remap='B'>session</emphasis> service is supported.
+    </para>
+  </refsect1>
+
+  <refsect1 id='pam_keyinit-return_values'>
+    <title>RETURN VALUES</title>
+    <variablelist>
+      <varlistentry>
+       <term>PAM_SUCCESS</term>
+       <listitem>
+         <para>
+           This module will usually return this value
+         </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>PAM_AUTH_ERR</term>
+        <listitem>
+           <para>
+             Authentication failure.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>PAM_BUF_ERR</term>
+        <listitem>
+           <para>
+             Memory buffer error.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>PAM_IGNORE</term>
+        <listitem>
+          <para>
+            The return value should be ignored by PAM dispatch.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>PAM_SERVICE_ERR</term>
+        <listitem>
+          <para>
+           Cannot determine the user name.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>PAM_SESSION_ERR</term>
+       <listitem>
+         <para>
+           This module will return this value if its arguments are invalid or
+           if a system error such as ENOMEM occurs.
+         </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>PAM_USER_UNKNOWN</term>
+        <listitem>
+          <para>
+            User not known.
+          </para>
+        </listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id='pam_keyinit-examples'>
+    <title>EXAMPLES</title>
+    <para>
+      Add this line to your login entries to start each login session with its
+      own session keyring:
+      <programlisting>
+session  required  pam_keyinit.so
+      </programlisting>
+    </para>
+    <para>
+      This will prevent keys from one session leaking into another session for
+      the same user.
+    </para>
+  </refsect1>
+
+  <refsect1 id='pam_keyinit-see_also'>
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+       <refentrytitle>pam.conf</refentrytitle><manvolnum>5</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>pam.d</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+       <refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>
+      <citerefentry>
+       <refentrytitle>keyctl</refentrytitle><manvolnum>1</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+
+  <refsect1 id='pam_keyinit-author'>
+    <title>AUTHOR</title>
+      <para>
+        pam_keyinit was written by David Howells, &lt;dhowells@redhat.com&gt;.
+      </para>
+  </refsect1>
+
+</refentry>
diff --git a/modules/pam_keyinit/pam_keyinit.c b/modules/pam_keyinit/pam_keyinit.c
new file mode 100644 (file)
index 0000000..363adb5
--- /dev/null
@@ -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 <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <errno.h>
+#include <security/pam_modules.h>
+#include <security/pam_modutil.h>
+#include <security/pam_ext.h>
+#include <sys/syscall.h>
+
+#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 (executable)
index 0000000..f0a7b9b
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+../../tests/tst-dlopen .libs/pam_keyinit.so