]> granicus.if.org Git - linux-pam/commitdiff
Relevant BUGIDs:
authorTomas Mraz <tm@t8m.info>
Tue, 27 Jun 2006 13:07:42 +0000 (13:07 +0000)
committerTomas Mraz <tm@t8m.info>
Tue, 27 Jun 2006 13:07:42 +0000 (13:07 +0000)
Purpose of commit: new feature

Commit summary:
---------------
        * modules/pam_namespace/pam_namespace.c: New module
        originally written by Janak Desai.
        * modules/pam_namespace/Makefile.am: New.
        * modules/pam_namespace/README: New.
        * modules/pam_namespace/md5.c: New.
        * modules/pam_namespace/md5.h: New.
        * modules/pam_namespace/namespace.conf: New.
        * modules/pam_namespace/namespace.conf.5: New.
        * modules/pam_namespace/namespace.conf.5.xml: New.
        * modules/pam_namespace/namespace.init: New.
        * modules/pam_namespace/pam_namespace.8: New.
        * modules/pam_namespace/pam_namespace.8.xml: New.
        * modules/Makefile.am: Added pam_namespace.
        * configure.in: Added pam_namespace, test for unshare
        library call.

14 files changed:
ChangeLog
configure.in
modules/Makefile.am
modules/pam_namespace/Makefile.am [new file with mode: 0644]
modules/pam_namespace/README [new file with mode: 0644]
modules/pam_namespace/md5.c [new file with mode: 0644]
modules/pam_namespace/md5.h [new file with mode: 0644]
modules/pam_namespace/namespace.conf [new file with mode: 0644]
modules/pam_namespace/namespace.conf.5 [new file with mode: 0644]
modules/pam_namespace/namespace.conf.5.xml [new file with mode: 0644]
modules/pam_namespace/namespace.init [new file with mode: 0755]
modules/pam_namespace/pam_namespace.8 [new file with mode: 0644]
modules/pam_namespace/pam_namespace.8.xml [new file with mode: 0644]
modules/pam_namespace/pam_namespace.c [new file with mode: 0644]

index ec179c53eadff5493c846959a87eac6ce4ec3d97..f7ae8ce723f0cbed2674f2c1768a547d376a24ae 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2006-06-27  Tomas Mraz <t8m@centrum.cz>
+
+       * modules/pam_namespace/pam_namespace.c: New module
+       originally written by Janak Desai.
+       * modules/pam_namespace/Makefile.am: New.
+       * modules/pam_namespace/README: New.
+       * modules/pam_namespace/md5.c: New.
+       * modules/pam_namespace/md5.h: New.
+       * modules/pam_namespace/namespace.conf: New.
+       * modules/pam_namespace/namespace.conf.5: New.
+       * modules/pam_namespace/namespace.conf.5.xml: New.
+       * modules/pam_namespace/namespace.init: New.
+       * modules/pam_namespace/pam_namespace.8: New.
+       * modules/pam_namespace/pam_namespace.8.xml: New.
+       * modules/Makefile.am: Added pam_namespace.
+       * configure.in: Added pam_namespace, test for unshare
+       library call.
+
 2006-06-27  David Howells <dhowells@redhat.com>
 
        * modules/pam_keyinit/pam_keyinit.c: New module.
index 315c89cc80f3752dc5bf7e38f77052b6b35677ee..6242e48d8d93fdd16870bd3ab5094d0f87057d86 100644 (file)
@@ -355,6 +355,9 @@ AC_SUBST(LIBNSL)
 AC_CHECK_LIB([selinux],[getfilecon], LIBSELINUX="-lselinux", LIBSELINUX="")
 AC_SUBST(LIBSELINUX)
 AM_CONDITIONAL([HAVE_LIBSELINUX], [test ! -z "$LIBSELINUX"])
+if test ! -z "$LIBSELINUX" ; then
+    AC_DEFINE([WITH_SELINUX], 1, [Defined if SE Linux support is compiled in])
+fi
 
 dnl Checks for header files.
 AC_HEADER_DIRENT
@@ -388,6 +391,8 @@ AC_CHECK_FUNCS(strcspn strdup strspn strstr strtol uname)
 AC_CHECK_FUNCS(getpwnam_r getpwuid_r getgrnam_r getgrgid_r getspnam_r)
 AC_CHECK_FUNCS(getgrouplist getline getdelim)
 AC_CHECK_FUNCS(inet_ntop inet_pton)
+AC_CHECK_FUNCS(unshare, [UNSHARE=yes], [UNSHARE=no])
+AM_CONDITIONAL([HAVE_UNSHARE], [test "$UNSHARE" = yes])
 
 dnl Checks for programs/utilities
 AC_CHECK_PROG(SGML2PS, sgml2ps, yes, no)
@@ -489,6 +494,7 @@ AC_OUTPUT(Makefile libpam/Makefile libpamc/Makefile libpamc/test/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 \
+       modules/pam_namespace/Makefile \
        modules/pam_nologin/Makefile modules/pam_permit/Makefile \
        modules/pam_rhosts/Makefile \
        modules/pam_rootok/Makefile modules/pam_exec/Makefile \
index 9d7a6fcb8ce9260dc4678400d4ec97152173a531..300f1955c5936a6ba5e4315e69eca7e5d9e6046e 100644 (file)
@@ -8,7 +8,7 @@ SUBDIRS = pam_access pam_cracklib pam_debug pam_deny pam_echo \
        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
+       pam_wheel pam_xauth pam_exec pam_namespace
 
 CLEANFILES = *~
 
diff --git a/modules/pam_namespace/Makefile.am b/modules/pam_namespace/Makefile.am
new file mode 100644 (file)
index 0000000..e1e8931
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2006 Red Hat, Inc. 
+#
+
+CLEANFILES = *~
+MAN5 = namespace.conf.5
+MAN8 = pam_namespace.8
+
+man_MANS = $(MAN5) $(MAN8)
+
+XMLS = namespace.conf.5.xml pam_namespace.8.xml
+
+if ENABLE_REGENERATE_MAN
+-include $(top_srcdir)/Make.xml.rules
+endif
+
+EXTRA_DIST = README namespace.conf $(man_MANS) $(XMLS)
+
+noinst_HEADERS = md5.h
+
+securelibdir = $(SECUREDIR)
+secureconfdir = $(SCONFIGDIR)
+
+AM_CFLAGS = -I$(top_srcdir)/libpam/include -I$(top_srcdir)/libpamc/include \
+        -DPAM_NAMESPACE_CONFIG=\"$(SCONFIGDIR)/namespace.conf\"
+AM_LDFLAGS =  -no-undefined -avoid-version -module \
+        -L$(top_builddir)/libpam -lpam @LIBSELINUX@
+if HAVE_VERSIONING
+  AM_LDFLAGS += -Wl,--version-script=$(srcdir)/../modules.map
+endif
+
+if HAVE_UNSHARE
+securelib_LTLIBRARIES = pam_namespace.la
+pam_namespace_la_SOURCES = pam_namespace.c md5.c md5.h
+
+secureconf_DATA = namespace.conf
+secureconf_SCRIPTS = namespace.init
+endif
diff --git a/modules/pam_namespace/README b/modules/pam_namespace/README
new file mode 100644 (file)
index 0000000..7edfe55
--- /dev/null
@@ -0,0 +1,122 @@
+
+pam_namespace module:
+Setup a private namespace with polyinstantiated directories.
+
+THEORY OF OPERATION:
+The pam namespace module consults /etc/security/namespace.conf
+configuration file and sets up a private namespace with polyinstantiated
+directories for a session managed by PAM. A skeleton namespace.conf
+installed by default provides example for polyinstantiating /tmp, /var/tmp
+and users' home directory.
+
+If an executable script /etc/security/namespace.init exists, it
+is used to initialize the namespace every time a new instance directory
+is setup. The script receives the polyinstantiated directory path
+and the instance directory path as its arguments.
+
+Each line in namespace.conf describes a limit for a user in the form:
+
+<polydir>  <instance_prefix>  <method>  <list_of_uids>
+
+Where:
+<polydir> - is the absolute pathname of the directory to polyinstantiate
+      Special entry $HOME is supported to designate user's home directory.
+      This field cannot be blank.
+
+<instance_prefix> - is the string prefix used to build the pathname for the
+       instantiation of <polydir>. The directory security context, or
+       optionally its md5sum string (32 hex characters), is appended to
+        the prefix to generate the final instance directory path.
+        This directory is created if it did not exist already, and is then
+       bind mounted on the <polydir> to provide an instance of <polydir>
+       based on the <method> column. The special string $HOME is replaced with
+       the user's home directory, and $USER with the username.
+       This field cannot be blank.
+
+<method> - is the method used for polyinstantiation. It can take 3 different
+       values; "user" for polyinstantiation based on user name, "context"
+       for polyinstantiation based on process security context, and "both"
+       for polyinstantiation based on both user name and security context.
+       Methods "context" and "both" are only available with SELinux. This
+       field cannot be blank.
+
+<list_of_uids> - is a comma separated list of user names for whom the
+       polyinstantiation is not performed. If left blank, polyinstantiation
+       will be performed for all users.
+
+EXAMPLE /etc/security/namespace.conf configuration file:
+=======================================================
+# Following three lines will polyinstantiate /tmp, /var/tmp and user's home
+# directories. /tmp and /var/tmp will be polyinstantiated based on both
+# security context as well as user name, whereas home directory will
+# be polyinstantiated based on security context only. Polyinstantiation
+# will not be performed for user root and adm for directories /tmp and
+# /var/tmp, whereas home directories will be polyinstantiated for all
+# users. The user name and/or context is appended to the instance prefix.
+#
+# Note that instance directories do not have to reside inside the
+# polyinstantiated directory. In the examples below, instances of /tmp
+# will be created in /tmp-inst directory, where as instances of /var/tmp
+# and users home directories will reside within the directories that
+# are being polyinstantiated.
+#
+# Instance parent directories must exist for the polyinstantiation
+# mechanism to work. By default, they should be created with the mode
+# of 000. pam_namespace module will enforce this mode unless it
+# is explicitly called with an argument to ignore the mode of the
+# instance parent. System administrators should use this argument with
+# caution, as it will reduce security and isolation achieved by
+# polyinstantiation.
+#
+/tmp     /tmp-inst/            both      root,adm
+/var/tmp /var/tmp/tmp-inst/    both      root,adm
+$HOME    $HOME/$USER.inst/inst- context
+
+ARGUMENTS RECOGNIZED:
+    debug
+       Verbose logging by syslog
+
+    unmnt_remnt
+       For programs such as su and newrole, the login session has
+       already setup a polyinstantiated namespace. For these programs,
+       polyinstantiation is performed based on new user id or security
+       context, however the command first needs to undo the
+       polyinstantiation performed by login. This argument instructs
+       the command to first undo previous polyinstantiation before
+       proceeding with new polyinstantiation based on new id/context.
+
+    unmnt_only
+       For trusted programs that want to undo any existing bind mounts
+       and process instance directories on their own, this argument
+       allows them to unmount currently mounted instance directories.
+
+    require_selinux
+       If selinux is not enabled, return failure.
+
+    gen_hash
+       Instead of using the security context string for the instance
+       name, generate and use its md5 hash.
+
+    ignore_config_error
+       If a line in the configuration file corresponding to a
+       polyinstantiated directory contains format error, skip that
+       line process the next line. Without this option, pam will return
+       an error to the calling program resulting in termination
+       of the session.
+
+    ignore_instance_parent_mode
+       Instance parent directories by default are expected to have
+       the restrictive mode of 000. Using this option, an administrator
+       can choose to ignore the mode of the instance parent.
+
+MODULE SERVICES PROVIDED:
+       session            open_session and close_session
+
+USAGE:
+       For the <service>s you need polyinstantiation (login for example)
+       put the following line in /etc/pam.d/<service> as the last line for 
+       session group:
+
+       session  required  pam_namespace.so [arguments]
+
+       This module also depends on pam_selinux.so setting the context.
diff --git a/modules/pam_namespace/md5.c b/modules/pam_namespace/md5.c
new file mode 100644 (file)
index 0000000..3094a13
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * $Id$
+ *
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ */
+
+#include <string.h>
+#include "md5.h"
+
+#define MD5Name(x) x
+
+#if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
+#define byteReverse(buf, len)  /* Nothing */
+#else
+static void byteReverse(unsigned char *buf, unsigned longs);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+       uint32 t;
+       do {
+               t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+                   ((unsigned) buf[1] << 8 | buf[0]);
+               *(uint32 *) buf = t;
+               buf += 4;
+       } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Name(MD5Init)(struct MD5Context *ctx)
+{
+       ctx->buf[0] = 0x67452301U;
+       ctx->buf[1] = 0xefcdab89U;
+       ctx->buf[2] = 0x98badcfeU;
+       ctx->buf[3] = 0x10325476U;
+
+       ctx->bits[0] = 0;
+       ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Name(MD5Update)(struct MD5Context *ctx, unsigned const char *buf, unsigned len)
+{
+       uint32 t;
+
+       /* Update bitcount */
+
+       t = ctx->bits[0];
+       if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+               ctx->bits[1]++; /* Carry from low to high */
+       ctx->bits[1] += len >> 29;
+
+       t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */
+
+       /* Handle any leading odd-sized chunks */
+
+       if (t) {
+               unsigned char *p = (unsigned char *) ctx->in + t;
+
+               t = 64 - t;
+               if (len < t) {
+                       memcpy(p, buf, len);
+                       return;
+               }
+               memcpy(p, buf, t);
+               byteReverse(ctx->in, 16);
+               MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+               buf += t;
+               len -= t;
+       }
+       /* Process data in 64-byte chunks */
+
+       while (len >= 64) {
+               memcpy(ctx->in, buf, 64);
+               byteReverse(ctx->in, 16);
+               MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+               buf += 64;
+               len -= 64;
+       }
+
+       /* Handle any remaining bytes of data. */
+
+       memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx)
+{
+       unsigned count;
+       unsigned char *p;
+
+       /* Compute number of bytes mod 64 */
+       count = (ctx->bits[0] >> 3) & 0x3F;
+
+       /* Set the first char of padding to 0x80.  This is safe since there is
+          always at least one byte free */
+       p = ctx->in + count;
+       *p++ = 0x80;
+
+       /* Bytes of padding needed to make 64 bytes */
+       count = 64 - 1 - count;
+
+       /* Pad out to 56 mod 64 */
+       if (count < 8) {
+               /* Two lots of padding:  Pad the first block to 64 bytes */
+               memset(p, 0, count);
+               byteReverse(ctx->in, 16);
+               MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+
+               /* Now fill the next block with 56 bytes */
+               memset(ctx->in, 0, 56);
+       } else {
+               /* Pad block to 56 bytes */
+               memset(p, 0, count - 8);
+       }
+       byteReverse(ctx->in, 14);
+
+       /* Append length in bits and transform */
+       ((uint32 *) ctx->in)[14] = ctx->bits[0];
+       ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+       MD5Name(MD5Transform)(ctx->buf, (uint32 *) ctx->in);
+       byteReverse((unsigned char *) ctx->buf, 4);
+       memcpy(digest, ctx->buf, 16);
+       memset(ctx, 0, sizeof(ctx));    /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Name(MD5Transform)(uint32 buf[4], uint32 const in[16])
+{
+       register uint32 a, b, c, d;
+
+       a = buf[0];
+       b = buf[1];
+       c = buf[2];
+       d = buf[3];
+
+       MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478U, 7);
+       MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756U, 12);
+       MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbU, 17);
+       MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22);
+       MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU, 7);
+       MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aU, 12);
+       MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613U, 17);
+       MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501U, 22);
+       MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8U, 7);
+       MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afU, 12);
+       MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17);
+       MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22);
+       MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U, 7);
+       MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12);
+       MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17);
+       MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22);
+
+       MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562U, 5);
+       MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340U, 9);
+       MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14);
+       MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20);
+       MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dU, 5);
+       MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U, 9);
+       MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14);
+       MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20);
+       MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6U, 5);
+       MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U, 9);
+       MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87U, 14);
+       MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edU, 20);
+       MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U, 5);
+       MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9);
+       MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9U, 14);
+       MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20);
+
+       MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942U, 4);
+       MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681U, 11);
+       MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16);
+       MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23);
+       MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44U, 4);
+       MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11);
+       MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16);
+       MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23);
+       MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U, 4);
+       MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faU, 11);
+       MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085U, 16);
+       MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05U, 23);
+       MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039U, 4);
+       MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11);
+       MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16);
+       MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665U, 23);
+
+       MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244U, 6);
+       MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97U, 10);
+       MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15);
+       MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039U, 21);
+       MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U, 6);
+       MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10);
+       MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15);
+       MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1U, 21);
+       MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6);
+       MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10);
+       MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314U, 15);
+       MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21);
+       MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82U, 6);
+       MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10);
+       MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15);
+       MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391U, 21);
+
+       buf[0] += a;
+       buf[1] += b;
+       buf[2] += c;
+       buf[3] += d;
+}
+
+void MD5Name(MD5)(unsigned const char *buf, unsigned len, unsigned char digest[16])
+{
+       struct MD5Context ctx;
+       MD5Name(MD5Init)(&ctx);
+       MD5Name(MD5Update)(&ctx, buf, len);
+       MD5Name(MD5Final)(digest, &ctx);
+}
diff --git a/modules/pam_namespace/md5.h b/modules/pam_namespace/md5.h
new file mode 100644 (file)
index 0000000..73f8583
--- /dev/null
@@ -0,0 +1,28 @@
+
+#ifndef MD5_H
+#define MD5_H
+
+typedef unsigned int uint32;
+
+struct MD5Context {
+       uint32 buf[4];
+       uint32 bits[2];
+       unsigned char in[64];
+};
+
+#define MD5_DIGEST_LENGTH 16
+
+void MD5Init(struct MD5Context *);
+void MD5Update(struct MD5Context *, unsigned const char *, unsigned);
+void MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], struct MD5Context *);
+void MD5Transform(uint32 buf[4], uint32 const in[MD5_DIGEST_LENGTH]);
+void MD5(unsigned const char *, unsigned, unsigned char digest[MD5_DIGEST_LENGTH]);
+
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+
+typedef struct MD5Context MD5_CTX;
+
+#endif                         /* MD5_H */
diff --git a/modules/pam_namespace/namespace.conf b/modules/pam_namespace/namespace.conf
new file mode 100644 (file)
index 0000000..c7305ff
--- /dev/null
@@ -0,0 +1,30 @@
+# /etc/security/namespace.conf
+#
+# See /usr/share/doc/pam-*/txts/README.pam_namespace for more information.
+#
+# Uncommenting the following three lines will polyinstantiate
+# /tmp, /var/tmp and user's home directories. /tmp and /var/tmp will
+# be polyinstantiated based on both security context as well as user
+# name, whereas home directory will be polyinstantiated based on
+# security context only. Polyinstantion will not be performed for
+# user root and adm for directories /tmp and /var/tmp, whereas home
+# directories will be polyinstantiated for all users. The user name
+# and/or context is appended to the instance prefix.
+#
+# Note that instance directories do not have to reside inside the
+# polyinstantiated directory. In the examples below, instances of /tmp
+# will be created in /tmp-inst directory, where as instances of /var/tmp
+# and users home directories will reside within the directories that
+# are being polyinstantiated.
+#
+# Instance parent directories must exist for the polyinstantiation
+# mechanism to work. By default, they should be created with the mode
+# of 000. pam_namespace module will enforce this mode unless it
+# is explicitly called with an argument to ignore the mode of the
+# instance parent. System administrators should use this argument with
+# caution, as it will reduce security and isolation achieved by
+# polyinstantiation.
+#
+#/tmp     /tmp-inst/           both      root,adm
+#/var/tmp /var/tmp/tmp-inst/           both      root,adm
+#$HOME    $HOME/$USER.inst/inst- context
diff --git a/modules/pam_namespace/namespace.conf.5 b/modules/pam_namespace/namespace.conf.5
new file mode 100644 (file)
index 0000000..ff325a2
--- /dev/null
@@ -0,0 +1,96 @@
+.\"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 "NAMESPACE.CONF" 5 "" "" ""
+.SH NAME
+namespace.conf \- the namespace configuration file
+.SH "DESCRIPTION"
+
+.PP
+This module allows setup of private namespaces with polyinstantiated directories\&. Directories can be polyinstantiated based on user name or, in the case of SELinux, user name, security context or both\&. If an executable script \fI/etc/security/namespace\&.init\fR exists, it is used to initialize the namespace every time a new instance directory is setup\&. The script receives the polyinstantiated directory path and the instance directory path as its arguments\&.
+
+.PP
+The \fI/etc/security/namespace\&.conf\fR file specifies which directories are polyinstantiated, how they are polyinstantiated, how instance directories would be named, and any users for whom polyinstantiation would not be performed\&.
+
+.PP
+When someone logs in, the file \fInamespace\&.conf\fR is scanned where each non comment line represents one polyinstantiated directory with space separated fields as follows:
+
+.PP
+ \fIpolydir\fR  \fI instance_prefix\fR  \fI method\fR  \fI list_of_uids\fR 
+
+.PP
+The first field, \fIpolydir\fR, is the absolute pathname of the directory to polyinstantiate\&. Special entry $HOME is supported to designate user's home directory\&. This field cannot be blank\&.
+
+.PP
+The second field, \fIinstance_prefix\fR is the string prefix used to build the pathname for the instantiation of <polydir>\&. The directory security context, or optionally its md5sum string (32 hex characters), is appended to the prefix to generate the final instance directory path\&. This directory is created if it did not exist already, and is then bind mounted on the <polydir> to provide an instance of <polydir> based on the <method> column\&. The special string $HOME is replaced with the user's home directory, and $USER with the username\&. This field cannot be blank\&. The directory where polyinstantiated instances are to be created, must exist and must have, by default, the mode of 000\&. The requirement that the instance parent be of mode 000 can be overridden with the command line option <ignore_instance_parent_mode>
+
+.PP
+The third field, \fImethod\fR, is the method used for polyinstantiation\&. It can take 3 different values; "user" for polyinstantiation based on user name, "context" for polyinstantiation based on process security context, and "both" for polyinstantiation based on both user name and security context\&. Methods "context" and "both" are only available with SELinux\&. This field cannot be blank\&.
+
+.PP
+The fourth field, \fIlist_of_uids\fR, is a comma separated list of user names for whom the polyinstantiation is not performed\&. If left blank, polyinstantiation will be performed for all users\&.
+
+.SH "EXAMPLES"
+
+.PP
+These are some example lines which might be specified in \fI/etc/security/namespace\&.conf\fR\&.
+
+.nf
+
+      # The following three lines will polyinstantiate /tmp,
+      # /var/tmp and user's home directories\&. /tmp and /var/tmp
+      # will be polyinstantiated based on both security context
+      # as well as user name, whereas home directory will be
+      # polyinstantiated based on security context only\&.
+      # Polyinstantiation will not be performed for user root
+      # and adm for directories /tmp and /var/tmp, whereas home
+      # directories will be polyinstantiated for all users\&.
+      #
+      # Note that instance directories do not have to reside inside
+      # the polyinstantiated directory\&. In the examples below,
+      # instances of /tmp will be created in /tmp\-inst directory,
+      # where as instances of /var/tmp and users home directories
+      # will reside within the directories that are being
+      # polyinstantiated\&.
+      #
+      /tmp     /tmp\-inst/             both      root,adm
+      /var/tmp /var/tmp/tmp\-inst/     both      root,adm
+      $HOME    $HOME/$USER\&.inst/inst\- context
+    
+.fi
+
+.PP
+For the <service>s you need polyinstantiation (login for example) put the following line in /etc/pam\&.d/<service> as the last line for session group:
+
+.PP
+session required pam_namespace\&.so [arguments]
+
+.PP
+This module also depends on pam_selinux\&.so setting the context\&.
+
+.SH "SEE ALSO"
+
+.PP
+ \fBpam_namespace\fR(8), \fBpam\&.d\fR(5), \fBpam\fR(8) 
+
+.SH "AUTHORS"
+
+.PP
+The namespace\&.conf manual page was written by Janak Desai <janak@us\&.ibm\&.com>\&.
+
diff --git a/modules/pam_namespace/namespace.conf.5.xml b/modules/pam_namespace/namespace.conf.5.xml
new file mode 100644 (file)
index 0000000..36a1a08
--- /dev/null
@@ -0,0 +1,151 @@
+<?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="namespace.conf">
+
+  <refmeta>
+    <refentrytitle>namespace.conf</refentrytitle>
+    <manvolnum>5</manvolnum>
+    <refmiscinfo class="sectdesc">Linux-PAM Manual</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>namespace.conf</refname>
+    <refpurpose>the namespace configuration file</refpurpose>
+  </refnamediv>
+
+
+  <refsect1 id='namespace.conf-description'>
+    <title>DESCRIPTION</title>
+
+    <para>
+      This module allows setup of private namespaces with polyinstantiated
+      directories. Directories can be polyinstantiated based on user name
+      or, in the case of SELinux, user name, security context or both.  If an
+      executable script <filename>/etc/security/namespace.init</filename>
+      exists, it is used to initialize the namespace every time a new instance
+      directory is setup. The script receives the polyinstantiated
+      directory path and the instance directory path as its arguments.
+    </para>
+
+    <para>
+      The <filename>/etc/security/namespace.conf</filename> file specifies
+      which directories are polyinstantiated, how they are polyinstantiated,
+      how instance directories would be named, and any users for whom
+      polyinstantiation would not be performed.
+    </para>
+
+    <para>
+      When someone logs in, the file <filename>namespace.conf</filename> is
+      scanned where each non comment line represents one polyinstantiated
+      directory with space separated fields as follows:
+   </para>
+
+    <para>
+      <replaceable>polydir</replaceable> <replaceable> instance_prefix</replaceable> <replaceable> method</replaceable> <replaceable> list_of_uids</replaceable>
+    </para>
+
+    <para>
+      The first field, <replaceable>polydir</replaceable>, is the absolute
+      pathname of the directory to polyinstantiate. Special entry $HOME is
+      supported to designate user's home directory. This field cannot be
+      blank.
+    </para>
+
+    <para>
+      The second field, <replaceable>instance_prefix</replaceable> is
+      the string prefix used to build the pathname for the instantiation
+      of &lt;polydir&gt;. The directory security context, or optionally its
+      md5sum string (32 hex characters), is appended to the prefix to
+      generate the final instance directory path. This directory is
+      created if it did not exist already, and is then bind mounted on the
+      &lt;polydir&gt; to provide an instance of &lt;polydir&gt; based on the
+      &lt;method&gt; column. The special string $HOME is replaced with the
+      user's home directory, and $USER with the username. This field cannot
+      be blank. The directory where polyinstantiated instances are to be
+      created, must exist and must have, by default, the mode of 000.  The
+      requirement that the instance parent be of mode 000 can be overridden
+      with the command line option &lt;ignore_instance_parent_mode&gt;
+    </para>
+
+    <para>
+      The third field, <replaceable>method</replaceable>, is the method
+      used for polyinstantiation. It can take 3 different values; "user"
+      for polyinstantiation based on user name, "context" for
+      polyinstantiation based on process security context, and "both"
+      for polyinstantiation based on both user name and security context.
+      Methods "context" and "both" are only available with SELinux. This
+      field cannot be blank.
+    </para>
+
+    <para>
+      The fourth field, <replaceable>list_of_uids</replaceable>, is
+      a comma separated list of user names for whom the polyinstantiation
+      is not performed. If left blank, polyinstantiation will be performed
+      for all users.
+    </para>
+
+  </refsect1>
+
+  <refsect1 id="namespace.conf-examples">
+    <title>EXAMPLES</title>
+    <para>
+      These are some example lines which might be specified in
+      <filename>/etc/security/namespace.conf</filename>.
+    </para>
+
+    <literallayout>
+      # The following three lines will polyinstantiate /tmp,
+      # /var/tmp and user's home directories. /tmp and /var/tmp
+      # will be polyinstantiated based on both security context
+      # as well as user name, whereas home directory will be
+      # polyinstantiated based on security context only.
+      # Polyinstantiation will not be performed for user root
+      # and adm for directories /tmp and /var/tmp, whereas home
+      # directories will be polyinstantiated for all users.
+      #
+      # Note that instance directories do not have to reside inside
+      # the polyinstantiated directory. In the examples below,
+      # instances of /tmp will be created in /tmp-inst directory,
+      # where as instances of /var/tmp and users home directories
+      # will reside within the directories that are being
+      # polyinstantiated.
+      #
+      /tmp     /tmp-inst/              both      root,adm
+      /var/tmp /var/tmp/tmp-inst/      both      root,adm
+      $HOME    $HOME/$USER.inst/inst- context
+    </literallayout>
+
+    <para>
+      For the &lt;service&gt;s you need polyinstantiation (login for example)
+      put the following line in /etc/pam.d/&lt;service&gt; as the last line for
+      session group:
+    </para>
+
+    <para>
+      session  required  pam_namespace.so [arguments]
+    </para>
+
+    <para>
+      This module also depends on pam_selinux.so setting the context.
+    </para>
+
+  </refsect1>
+
+  <refsect1 id="namespace.conf-see_also">
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry><refentrytitle>pam_namespace</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>pam.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+  <refsect1 id="namespace.conf-author">
+    <title>AUTHORS</title>
+    <para>
+      The namespace.conf manual page was written by Janak Desai &lt;janak@us.ibm.com&gt;.
+    </para>
+  </refsect1>
+</refentry>
diff --git a/modules/pam_namespace/namespace.init b/modules/pam_namespace/namespace.init
new file mode 100755 (executable)
index 0000000..62f8e6e
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+# This is only a boilerplate for the instance initialization script.
+# It receives polydir path as $1 and the instance path as $2.
+#
+# If you intend to polyinstantiate /tmp and you also want to use the X windows
+# environment, you will have to use this script to bind mount the socket that
+# is used by the X server to communicate with its clients. X server places
+# this socket in /tmp/.X11-unix directory, which will get obscured by
+# polyinstantiation. Uncommenting the following lines will bind mount
+# the relevant directory at an alternative location (/.tmp/.X11-unix) such
+# that the X server, window manager and X clients, can still find the
+# socket X0 at the polyinstanted /tmp/.X11-unix.
+#
+#if [ $1 = /tmp ]; then
+#      if [ ! -f /.tmp/.X11-unix ]; then
+#              mkdir -p /.tmp/.X11-unix
+#      fi
+#      mount --bind /tmp/.X11-unix /.tmp/.X11-unix
+#      cp -fp -- /tmp/.X0-lock "$2/.X0-lock"
+#      mkdir -- "$2/.X11-unix"
+#      ln -fs -- /.tmp/.X11-unix/X0 "$2/.X11-unix/X0"
+#fi
+
+exit 0
diff --git a/modules/pam_namespace/pam_namespace.8 b/modules/pam_namespace/pam_namespace.8
new file mode 100644 (file)
index 0000000..8859430
--- /dev/null
@@ -0,0 +1,141 @@
+.\"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_NAMESPACE" 8 "" "" ""
+.SH NAME
+pam_namespace \- PAM module for configuring namespace for a session
+.SH "SYNOPSIS"
+.ad l
+.hy 0
+.HP 17
+\fBpam_namespace\&.so\fR [debug] [unmnt_remnt] [unmnt_only] [require_selinux] [gen_hash] [ignore_config_error] [ignore_instance_parent_mode]
+.ad
+.hy
+
+.SH "DESCRIPTION"
+
+.PP
+The pam_namespace PAM module sets up a private namespace for a session with polyinstantiated directories\&. A polyinstantiated directory provides a different instance of itself based on user name, or when using SELinux, user name, security context or both\&. If an executable script \fI/etc/security/namespace\&.init\fR exists, it is used to initialize the namespace every time a new instance directory is setup\&. The script receives the polyinstantiated directory path and the instance directory path as its arguments\&.
+
+.PP
+The pam_namespace module disassociates the session namespace from the parent namespace\&. Any mounts/unmounts performed in the parent namespace, such as mounting of devices, are not reflected in the session namespace\&. To propagate selected mount/unmount events from the parent namespace into the disassociated session namespace, an administrator may use the special shared\-subtree feature\&. For additional information on shared\-subtree feature, please refer to the mount(8) man page and the shared\-subtree description at http://lwn\&.net/Articles/159077 and http://lwn\&.net/Articles/159092\&.
+
+.SH "OPTIONS"
+
+.TP
+\fBdebug\fR
+A lot of debug information is logged using syslog
+
+.TP
+\fBunmnt_remnt\fR
+For programs such as su and newrole, the login session has already setup a polyinstantiated namespace\&. For these programs, polyinstantiation is performed based on new user id or security context, however the command first needs to undo the polyinstantiation performed by login\&. This argument instructs the command to first undo previous polyinstantiation before proceeding with new polyinstantiation based on new id/context
+
+.TP
+\fBunmnt_only\fR
+For trusted programs that want to undo any existing bind mounts and process instance directories on their own, this argument allows them to unmount currently mounted instance directories
+
+.TP
+\fBrequire_selinux\fR
+If selinux is not enabled, return failure
+
+.TP
+\fBgen_hash\fR
+Instead of using the security context string for the instance name, generate and use its md5 hash\&.
+
+.TP
+\fBignore_config_error\fR
+If a line in the configuration file corresponding to a polyinstantiated directory contains format error, skip that line process the next line\&. Without this option, pam will return an error to the calling program resulting in termination of the session\&.
+
+.TP
+\fBignore_instance_parent_mode\fR
+Instance parent directories by default are expected to have the restrictive mode of 000\&. Using this option, an administrator can choose to ignore the mode of the instance parent\&. This option should be used with caution as it will reduce security and isolation goals of the polyinstantiation mechanism\&.
+
+.SH "MODULE SERVICES PROVIDED"
+
+.PP
+The \fBsession\fR service is supported\&.
+
+.SH "RETURN VALUES"
+
+.TP
+PAM_SUCCESS
+Namespace setup was successful\&.
+
+.TP
+PAM_SERVICE_ERR
+Unexpected system error occurred while setting up namespace\&.
+
+.TP
+PAM_SESSION_ERR
+Unexpected namespace configuration error occurred\&.
+
+.SH "FILES"
+
+.TP
+\fI/etc/security/namespace\&.conf\fR
+Configuration file
+
+.SH "EXAMPLES"
+
+.PP
+For the <service>s you need polyinstantiation (login for example) put the following line in /etc/pam\&.d/<service> as the last line for session group:
+
+.PP
+session required pam_namespace\&.so [arguments]
+
+.PP
+To use polyinstantiation with graphical display manager gdm, insert the following line, before exit 0, in /etc/gdm/PostSession/Default:
+
+.PP
+/usr/sbin/gdm\-safe\-restart
+
+.PP
+This allows gdm to restart after each session and appropriately adjust namespaces of display manager and the X server\&. If polyinstantiation of /tmp is desired along with the graphical environment, then additional configuration changes are needed to address the interaction of X server and font server namespaces with their use of /tmp to create communication sockets\&. Please use the initialization script \fI/etc/security/namespace\&.init\fR to ensure that the X server and its clients can appropriately access the communication socket X0\&. Please refer to the sample instructions provided in the comment section of the instance initialization script \fI/etc/security/namespace\&.init\fR\&. In addition, perform the following changes to use graphical environment with polyinstantiation of /tmp:
+
+.PP
+
+.nf
+
+      1\&. Disable the use of font server by commenting out "FontPath"
+         line in /etc/X11/xorg\&.conf\&. If you do want to use the font server
+         then you will have to augment the instance initialization
+         script to appropriately provide /tmp/\&.font\-unix from the
+         polyinstantiated /tmp\&.
+      2\&. Ensure that the gdm service is setup to use pam_namespace,
+         as described above, by modifying /etc/pam\&.d/gdm\&.
+      3\&. Ensure that the display manager is configured to restart X server
+         with each new session\&. This default setup can be verified by
+         making sure that /usr/share/gdm/defaults\&.conf contains
+         "AlwaysRestartServer=true", and it is not overridden by
+         /etc/gdm/custom\&.conf\&.
+    
+.fi
+
+.SH "SEE ALSO"
+
+.PP
+ \fBnamespace\&.conf\fR(5), \fBpam\&.d\fR(8), \fBmount\fR(8), \fBpam\fR(8)\&.
+
+.SH "AUTHORS"
+
+.PP
+The namespace setup scheme was designed by Stephen Smalley, Janak Desai and Chad Sellers\&. The pam_namespace PAM module was developed by Janak Desai <janak@us\&.ibm\&.com>, Chad Sellers <csellers@tresys\&.com> and Steve Grubb <sgrubb@redhat\&.com>\&.
+
diff --git a/modules/pam_namespace/pam_namespace.8.xml b/modules/pam_namespace/pam_namespace.8.xml
new file mode 100644 (file)
index 0000000..4c93ecf
--- /dev/null
@@ -0,0 +1,317 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+                   "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+
+<refentry id='pam_namespace'>
+
+  <refmeta>
+    <refentrytitle>pam_namespace</refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo class='setdesc'>Linux-PAM Manual</refmiscinfo>
+  </refmeta>
+
+  <refnamediv id='pam_namespace-name'>
+    <refname>pam_namespace</refname>
+    <refpurpose>
+      PAM module for configuring namespace for a session
+    </refpurpose>
+  </refnamediv>
+
+<!-- body begins here -->
+
+  <refsynopsisdiv>
+    <cmdsynopsis id="pam_namespace-cmdsynopsis">
+      <command>pam_namespace.so</command>
+      <arg choice="opt">
+        debug
+      </arg>
+      <arg choice="opt">
+        unmnt_remnt
+      </arg>
+      <arg choice="opt">
+        unmnt_only
+      </arg>
+      <arg choice="opt">
+        require_selinux
+      </arg>
+      <arg choice="opt">
+        gen_hash
+      </arg>
+      <arg choice="opt">
+        ignore_config_error
+      </arg>
+      <arg choice="opt">
+        ignore_instance_parent_mode
+      </arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+
+  <refsect1 id="pam_namespace-description">
+    <title>DESCRIPTION</title>
+    <para>
+      The pam_namespace PAM module sets up a private namespace for a session
+      with polyinstantiated directories. A polyinstantiated directory
+      provides a different instance of itself based on user name, or when
+      using SELinux, user name, security context or both.  If an executable
+      script <filename>/etc/security/namespace.init</filename> exists, it
+      is used to initialize the namespace every time a new instance
+      directory is setup. The script receives the polyinstantiated
+      directory path and the instance directory path as its arguments.
+    </para>
+
+    <para>
+      The pam_namespace module disassociates the session namespace from
+      the parent namespace. Any mounts/unmounts performed in the parent
+      namespace, such as mounting of devices, are not reflected in the
+      session namespace. To propagate selected mount/unmount events from
+      the parent namespace into the disassociated session namespace, an
+      administrator may use the special shared-subtree feature. For
+      additional information on shared-subtree feature, please refer to
+      the mount(8) man page and the shared-subtree description at
+      http://lwn.net/Articles/159077 and http://lwn.net/Articles/159092.
+    </para>
+
+  </refsect1>
+
+  <refsect1 id="pam_namespace-options">
+    <title>OPTIONS</title>
+    <variablelist>
+
+      <varlistentry>
+        <term>
+          <option>debug</option>
+        </term>
+        <listitem>
+          <para>
+            A lot of debug information is logged using syslog
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>unmnt_remnt</option>
+        </term>
+        <listitem>
+          <para>
+            For programs such as su and newrole, the login
+            session has already setup a polyinstantiated
+            namespace. For these programs, polyinstantiation
+            is performed based on new user id or security
+            context, however the command first needs to
+            undo the polyinstantiation performed by login.
+            This argument instructs the command to
+            first undo previous polyinstantiation before
+            proceeding with new polyinstantiation based on
+            new id/context
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>unmnt_only</option>
+        </term>
+        <listitem>
+          <para>
+            For trusted programs that want to undo any
+            existing bind mounts and process instance
+            directories on their own, this argument allows
+            them to unmount currently mounted instance
+            directories
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>require_selinux</option>
+        </term>
+        <listitem>
+          <para>
+            If selinux is not enabled, return failure
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>gen_hash</option>
+        </term>
+        <listitem>
+          <para>
+            Instead of using the security context string
+            for the instance name, generate and use its
+            md5 hash.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>ignore_config_error</option>
+        </term>
+        <listitem>
+          <para>
+            If a line in the configuration file corresponding
+            to a polyinstantiated directory contains format
+            error, skip that line process the next line.
+            Without this option, pam will return an error
+            to the calling program resulting in termination
+            of the session.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+          <option>ignore_instance_parent_mode</option>
+        </term>
+        <listitem>
+          <para>
+           Instance parent directories by default are expected to have
+           the restrictive mode of 000. Using this option, an administrator
+           can choose to ignore the mode of the instance parent. This option
+            should be used with caution as it will reduce security and
+            isolation goals of the polyinstantiation mechanism.
+          </para>
+        </listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id="pam_namespace-services">
+    <title>MODULE SERVICES PROVIDED</title>
+    <para>
+      The <option>session</option> service is supported.
+    </para>
+  </refsect1>
+
+  <refsect1 id="pam_namespace-return_values">
+    <title>RETURN VALUES</title>
+    <variablelist>
+      <varlistentry>
+        <term>PAM_SUCCESS</term>
+        <listitem>
+           <para>
+             Namespace setup was successful.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>PAM_SERVICE_ERR</term>
+        <listitem>
+           <para>
+             Unexpected system error occurred while setting up namespace.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>PAM_SESSION_ERR</term>
+        <listitem>
+           <para>
+             Unexpected namespace configuration error occurred.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id="pam_namespace-files">
+    <title>FILES</title>
+    <variablelist>
+      <varlistentry>
+        <term><filename>/etc/security/namespace.conf</filename></term>
+        <listitem>
+          <para>Configuration file</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id="pam_namespace-examples">
+    <title>EXAMPLES</title>
+
+    <para>
+      For the &lt;service&gt;s you need polyinstantiation (login for example)
+      put the following line in /etc/pam.d/&lt;service&gt; as the last line for
+      session group:
+    </para>
+
+    <para>
+      session  required  pam_namespace.so [arguments]
+    </para>
+
+    <para>
+      To use polyinstantiation with graphical display manager gdm, insert the
+      following line, before exit 0, in /etc/gdm/PostSession/Default:
+    </para>
+
+    <para>
+      /usr/sbin/gdm-safe-restart
+    </para>
+
+    <para>
+      This allows gdm to restart after each session and appropriately adjust
+      namespaces of display manager and the X server. If polyinstantiation
+      of /tmp is desired along with the graphical environment, then additional
+      configuration changes are needed to address the interaction of X server
+      and font server namespaces with their use of /tmp to create
+      communication sockets. Please use the initialization script
+      <filename>/etc/security/namespace.init</filename> to ensure that
+      the X server and its clients can appropriately access the
+      communication socket X0. Please refer to the sample instructions
+      provided in the comment section of the instance initialization script
+      <filename>/etc/security/namespace.init</filename>. In addition,
+      perform the following changes to use graphical environment with
+      polyinstantiation of /tmp:
+    </para>
+
+    <para>
+    <literallayout>
+      1. Disable the use of font server by commenting out "FontPath"
+         line in /etc/X11/xorg.conf. If you do want to use the font server
+         then you will have to augment the instance initialization
+         script to appropriately provide /tmp/.font-unix from the
+         polyinstantiated /tmp.
+      2. Ensure that the gdm service is setup to use pam_namespace,
+         as described above, by modifying /etc/pam.d/gdm.
+      3. Ensure that the display manager is configured to restart X server
+         with each new session. This default setup can be verified by
+         making sure that /usr/share/gdm/defaults.conf contains
+         "AlwaysRestartServer=true", and it is not overridden by
+         /etc/gdm/custom.conf.
+    </literallayout>
+    </para>
+
+  </refsect1>
+
+  <refsect1 id="pam_namespace-see_also">
+    <title>SEE ALSO</title>
+    <para>
+      <citerefentry>
+        <refentrytitle>namespace.conf</refentrytitle><manvolnum>5</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>pam.d</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
+        <refentrytitle>pam</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1 id="pam_namespace-authors">
+    <title>AUTHORS</title>
+    <para>
+      The namespace setup scheme was designed by Stephen Smalley, Janak Desai
+      and Chad Sellers.
+      The pam_namespace PAM module was developed by Janak Desai &lt;janak@us.ibm.com&gt;, Chad Sellers &lt;csellers@tresys.com&gt; and Steve Grubb &lt;sgrubb@redhat.com&gt;.
+    </para>
+  </refsect1>
+</refentry>
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
new file mode 100644 (file)
index 0000000..cd6ef07
--- /dev/null
@@ -0,0 +1,1386 @@
+/******************************************************************************
+ * A module for Linux-PAM that will set the default namespace after 
+ * establishing a session via PAM.
+ *
+ * (C) Copyright IBM Corporation 2005
+ * (C) Copyright Red Hat 2006
+ * All Rights Reserved.
+ *
+ * Written by: Janak Desai <janak@us.ibm.com>
+ * With Revisions by: Steve Grubb <sgrubb@redhat.com>
+ * Derived from a namespace setup patch by Chad Sellers <cdselle@tycho.nsa.gov>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
+ * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#if !(defined(linux))
+#error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
+#endif
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <syslog.h>
+#include <dlfcn.h>
+#include <stdarg.h>
+#include <pwd.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <sched.h>
+#include "security/pam_modules.h"
+#include "security/pam_modutil.h"
+#include "security/pam_ext.h"
+#include "md5.h"
+
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+#ifndef CLONE_NEWNS
+#define CLONE_NEWNS 0x00020000 /* Flag to create new namespace */
+#endif
+
+/*
+ * Module defines
+ */
+#ifndef PAM_NAMESPACE_CONFIG
+#define PAM_NAMESPACE_CONFIG "/etc/security/namespace.conf"
+#endif
+
+#ifndef NAMESPACE_INIT_SCRIPT
+#define NAMESPACE_INIT_SCRIPT "/etc/security/namespace.init"
+#endif
+
+#define PAMNS_DEBUG           0x00000100 /* Running in debug mode */
+#define PAMNS_SELINUX_ENABLED 0x00000400 /* SELinux is enabled */
+#define PAMNS_CTXT_BASED_INST 0x00000800 /* Context based instance needed */
+#define PAMNS_GEN_HASH        0x00002000 /* Generate md5 hash for inst names */
+#define PAMNS_IGN_CONFIG_ERR  0x00004000 /* Ignore format error in conf file */
+#define PAMNS_IGN_INST_PARENT_MODE  0x00008000 /* Ignore instance parent mode */
+
+/*
+ * Polyinstantiation method options, based on user, security context
+ * or both
+ */
+enum polymethod {
+    USER,
+    CONTEXT,
+    BOTH,
+};
+
+/*
+ * Depending on the application using this namespace module, we
+ * may need to unmount priviously bind mounted instance directory.
+ * Applications such as login and sshd, that establish a new 
+ * session unmount of instance directory is not needed. For applications
+ * such as su and newrole, that switch the identity, this module 
+ * has to unmount previous instance directory first and re-mount
+ * based on the new indentity. For other trusted applications that
+ * just want to undo polyinstantiation, only unmount of previous
+ * instance directory is needed.
+ */
+enum unmnt_op {
+    NO_UNMNT,
+    UNMNT_REMNT,
+    UNMNT_ONLY,
+};
+
+/*
+ * Structure that holds information about a directory to polyinstantiate
+ */
+struct polydir_s {
+    char dir[PATH_MAX];                /* directory to polyinstantiate */
+    char instance_prefix[PATH_MAX];    /* prefix for instance dir path name */
+    enum polymethod method;            /* method used to polyinstantiate */
+    unsigned int num_uids;             /* number of override uids */
+    uid_t *uid;                                /* list of override uids */
+    struct polydir_s *next;            /* pointer to the next polydir entry */
+};
+
+struct instance_data {
+    pam_handle_t *pamh;                /* The pam handle for this instance */
+    struct polydir_s *polydirs_ptr; /* The linked list pointer */
+    char user[LOGIN_NAME_MAX]; /* User name */
+    uid_t uid;                 /* The uid of the user */
+    unsigned long flags;               /* Flags for debug, selinux etc */
+};
+
+/*
+ * Adds an entry for a polyinstantiated directory to the linked list of
+ * polyinstantiated directories. It is called from process_line() while
+ * parsing the namespace configuration file.
+ */
+static int add_polydir_entry(struct instance_data *idata, 
+       const struct polydir_s *ent)
+{
+    struct polydir_s *pent;
+    unsigned int i;
+
+    /*
+     * Allocate an entry to hold information about a directory to
+     * polyinstantiate, populate it with information from 2nd argument
+     * and add the entry to the linked list of polyinstantiated
+     * directories.
+     */
+    pent = (struct polydir_s *) malloc(sizeof(struct polydir_s));
+    if (!pent) 
+        return -1;
+
+    /* Make copy */
+    strcpy(pent->dir, ent->dir);
+    strcpy(pent->instance_prefix, ent->instance_prefix);
+    pent->method = ent->method;
+    pent->num_uids = ent->num_uids;
+    if (ent->num_uids) {
+        uid_t *pptr, *eptr;
+
+        pent->uid = (uid_t *) malloc(ent->num_uids * sizeof(uid_t));
+        if (!(pent->uid)) {
+            free(pent);
+            return -1;
+        }
+        for (i = 0, pptr = pent->uid, eptr = ent->uid; i < ent->num_uids;
+                 i++, eptr++, pptr++)
+             *pptr = *eptr;
+    } else 
+        pent->uid = NULL;
+
+    /* Now attach to linked list */
+    pent->next = NULL;
+    if (idata->polydirs_ptr == NULL)
+        idata->polydirs_ptr = pent;
+    else {
+        struct polydir_s *tail;
+
+        tail = idata->polydirs_ptr;
+        while (tail->next)
+            tail = tail->next;
+        tail->next = pent;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Deletes all the entries in the linked list.
+ */
+static void del_polydir_list(struct polydir_s *polydirs_ptr)
+{
+        struct polydir_s *dptr = polydirs_ptr;
+
+       while (dptr) {
+               struct polydir_s *tptr = dptr;
+               dptr = dptr->next;
+               free(tptr->uid);
+               free(tptr);
+       }
+}
+
+
+/*
+ * Called from parse_config_file, this function processes a single line
+ * of the namespace configuration file. It skips over comments and incomplete
+ * or malformed lines. It processes a valid line with information on
+ * polyinstantiating a directory by populating appropriate fields of a
+ * polyinstatiated directory structure and then calling add_polydir_entry to
+ * add that entry to the linked list of polyinstantiated directories.
+ */
+static int process_line(char *line, const char *home, 
+                       struct instance_data *idata)
+{
+    const char *dir, *instance_prefix;
+    const char *method, *uids;
+    char *tptr;
+    struct polydir_s poly;
+    int retval = 0;
+
+    poly.uid = NULL;
+    poly.num_uids = 0;
+
+    /*
+     * skip the leading white space
+     */
+    while (*line && isspace(*line))
+        line++;
+
+    /*
+     * Rip off the comments
+     */
+    tptr = strchr(line,'#');
+    if (tptr)
+        *tptr = '\0';
+
+    /*
+     * Rip off the newline char
+     */
+    tptr = strchr(line,'\n');
+    if (tptr)
+        *tptr = '\0';
+
+    /*
+     * Anything left ?
+     */
+    if (line[0] == 0)
+        return 0;
+
+    /*
+     * Initialize and scan the five strings from the line from the
+     * namespace configuration file.
+     */
+    dir = strtok_r(line, " \t", &tptr);
+    if (dir == NULL) {
+        pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing polydir");
+        goto skipping;
+    }
+    instance_prefix = strtok_r(NULL, " \t", &tptr);
+    if (instance_prefix == NULL) {
+        pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing instance_prefix");
+        goto skipping;
+    }
+    method = strtok_r(NULL, " \t", &tptr);
+    if (method == NULL) {
+        pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing method");
+        goto skipping;
+    }
+
+    /*
+     * Only the uids field is allowed to be blank, to indicate no
+     * override users for polyinstantiation of that directory. If
+     * any of the other fields are blank, the line is incomplete so
+     * skip it.
+     */
+    uids = strtok_r(NULL, " \t", &tptr);
+
+    /*
+     * If the directory being polyinstantiated is the home directory
+     * of the user who is establishing a session, we have to swap
+     * the "$HOME" string with the user's home directory that is
+     * passed in as an argument.
+     */
+    if (strcmp(dir, "$HOME") == 0) {
+       dir = home;
+    }
+
+    /*
+     * Expand $HOME and $USER in instance dir prefix
+     */
+    if ((tptr = strstr(instance_prefix, "$USER")) != 0) {
+       /* FIXME: should only support this if method is USER or BOTH */
+       char *expanded = alloca(strlen(idata->user) + strlen(instance_prefix)-5+1);
+       *tptr = 0;
+       sprintf(expanded, "%s%s%s", instance_prefix, idata->user, tptr+5);
+       instance_prefix = expanded;
+    }
+    if ((tptr = strstr(instance_prefix, "$HOME")) != 0) {
+       char *expanded = alloca(strlen(home)+strlen(instance_prefix)-5+1);
+       *tptr = 0;
+       sprintf(expanded, "%s%s%s", instance_prefix, home, tptr+5);
+       instance_prefix = expanded;
+    }
+
+    /*
+     * Ensure that all pathnames are absolute path names.
+     */
+    if ((dir[0] != '/') || (instance_prefix[0] != '/')) {
+        pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must start with '/'");
+        goto skipping;
+    }
+    if (strstr(dir, "..") || strstr(instance_prefix, "..")) {
+        pam_syslog(idata->pamh, LOG_NOTICE,"Pathnames must not contain '..'");
+        goto skipping;
+    }
+
+    /*
+     * Populate polyinstantiated directory structure with appropriate
+     * pathnames and the method with which to polyinstantiate.
+     */
+    if (strlen(dir) >= sizeof(poly.dir)
+       || strlen(instance_prefix) >= sizeof(poly.instance_prefix)) {
+       pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
+    }
+    strcpy(poly.dir, dir);
+    strcpy(poly.instance_prefix, instance_prefix);
+    if (strcmp(method, "user") == 0)
+        poly.method = USER;
+#ifdef WITH_SELINUX
+    else if (strcmp(method, "context") == 0) {
+        if (idata->flags & PAMNS_CTXT_BASED_INST)
+            poly.method = CONTEXT;
+       else
+            poly.method = USER;
+    } else if (strcmp(method, "both") == 0) {
+        if (idata->flags & PAMNS_CTXT_BASED_INST)
+            poly.method = BOTH;
+       else
+            poly.method = USER;
+    }
+
+#endif
+    else {
+        pam_syslog(idata->pamh, LOG_NOTICE, "Illegal method");
+        goto skipping;
+    }
+
+    /*
+     * If the line in namespace.conf for a directory to polyinstantiate
+     * contains a list of override users (users for whom polyinstantiation
+     * is not performed), read the user ids, convert names into uids, and
+     * add to polyinstantiated directory structure.
+     */
+    if (uids) {
+        uid_t *uidptr;
+        const char *ustr, *sstr;
+        int count, i;
+
+        for (count = 0, ustr = sstr = uids; sstr; ustr = sstr + 1, count++)
+           sstr = strchr(ustr, ',');
+
+        poly.num_uids = count;
+        poly.uid = (uid_t *) malloc(count * sizeof (uid_t));
+        uidptr = poly.uid;
+        if (uidptr == NULL) {
+            pam_syslog(idata->pamh, LOG_NOTICE, "out of memory");
+            goto skipping;
+        }
+
+        ustr = uids;
+        for (i = 0; i < count; i++) {
+            struct passwd *pwd;
+
+            tptr = strchr(ustr, ',');
+            if (tptr)
+                *tptr = '\0';
+
+            pwd = getpwnam(ustr);
+            *uidptr = pwd->pw_uid;
+            if (i < count - 1) {
+                ustr = tptr + 1;
+                uidptr++;
+            }
+        }
+    }
+
+    /*
+     * Add polyinstantiated directory structure to the linked list
+     * of all polyinstantiated directory structures.
+     */
+    if (add_polydir_entry(idata, &poly) < 0) {
+        pam_syslog(idata->pamh, LOG_ERR, "Allocation Error");
+        retval = PAM_SERVICE_ERR;
+    }
+    free(poly.uid);
+
+    goto out;
+
+skipping:
+    if (idata->flags & PAMNS_IGN_CONFIG_ERR)
+        retval = 0;
+    else
+        retval = PAM_SERVICE_ERR;
+out:
+    return retval;
+}
+
+
+/*
+ * Parses /etc/security/namespace.conf file to build a linked list of
+ * polyinstantiated directory structures of type polydir_s. Each entry
+ * in the linked list contains information needed to polyinstantiate
+ * one directory.
+ */
+static int parse_config_file(struct instance_data *idata)
+{
+    FILE *fil;
+    char *home;
+    struct passwd *cpwd;
+    char *line = NULL;
+    int retval;
+    size_t len = 0;
+
+    if (idata->flags & PAMNS_DEBUG)
+        pam_syslog(idata->pamh, LOG_DEBUG, "Parsing config file %s", 
+               PAM_NAMESPACE_CONFIG);
+
+    /*
+     * Extract the user's home directory to resolve $HOME entries
+     * in the namespace configuration file.
+     */
+    cpwd = getpwnam(idata->user);
+    if (!cpwd) {
+        pam_syslog(idata->pamh, LOG_ERR,
+               "Error getting home dir for '%s'", idata->user);
+        return PAM_SESSION_ERR;
+    }
+    home = strdupa(cpwd->pw_dir);
+
+    /*
+     * Open configuration file, read one line at a time and call
+     * process_line to process each line.
+     */
+    fil = fopen(PAM_NAMESPACE_CONFIG, "r");
+    if (fil == NULL) {
+        pam_syslog(idata->pamh, LOG_ERR, "Error opening config file");
+        return PAM_SERVICE_ERR;
+    }
+
+    /* Use unlocked IO */
+    __fsetlocking(fil, FSETLOCKING_BYCALLER);
+
+    /* loop reading the file */
+    while (getline(&line, &len, fil) > 0) {
+        retval = process_line(line, home, idata);
+        if (retval) {
+            pam_syslog(idata->pamh, LOG_ERR,
+               "Error processing conf file line %s", line);
+            fclose(fil);
+            free(line);
+            return PAM_SERVICE_ERR;
+        }
+    }
+    fclose(fil);
+    free(line);
+
+    /* All done...just some debug stuff */
+    if (idata->flags & PAMNS_DEBUG) {
+        struct polydir_s *dptr = idata->polydirs_ptr;
+        uid_t *iptr;
+        uid_t i;
+
+        pam_syslog(idata->pamh, LOG_DEBUG,
+           dptr?"Configured poly dirs:":"No configured poly dirs");
+        while (dptr) {
+            pam_syslog(idata->pamh, LOG_DEBUG, "dir='%s' iprefix='%s' meth=%d",
+                  dptr->dir, dptr->instance_prefix, dptr->method);
+            for (i = 0, iptr = dptr->uid; i < dptr->num_uids; i++, iptr++)
+                pam_syslog(idata->pamh, LOG_DEBUG, "override user %d ", *iptr);
+            dptr = dptr->next;
+        }
+    }
+
+    return PAM_SUCCESS;
+}
+
+
+/*
+ * This funtion returns true if a given uid is present in the polyinstantiated
+ * directory's list of override uids. If the uid is one of the override
+ * uids for the polyinstantiated directory, polyinstantiation is not
+ * performed for that user for that directory.
+ */
+static int ns_override(struct polydir_s *polyptr, struct instance_data *idata)
+{
+    unsigned int i;
+
+    if (idata->flags & PAMNS_DEBUG)
+       pam_syslog(idata->pamh, LOG_DEBUG,
+               "Checking for ns override in dir %s for uid %d",
+               polyptr->dir, idata->uid);
+
+    for (i = 0; i < polyptr->num_uids; i++) {
+        if (idata->uid == polyptr->uid[i]) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+
+/*
+ * poly_name returns the name of the polyinstantiated instance directory
+ * based on the method used for polyinstantiation (user, context or both)
+ * In addition, the function also returns the security contexts of the
+ * original directory to polyinstantiate and the polyinstantiated instance
+ * directory.
+ */
+#ifdef WITH_SELINUX
+static int poly_name(const struct polydir_s *polyptr, char **i_name,
+       security_context_t *i_context, security_context_t *origcon,
+        struct instance_data *idata)
+#else
+static int poly_name(const struct polydir_s *polyptr, char **i_name, 
+       struct instance_data *idata)
+#endif
+{
+#ifdef WITH_SELINUX
+    security_context_t scon = NULL;
+    security_class_t tclass;
+#endif
+    int rc;
+
+# ifdef WITH_SELINUX
+    /*
+     * Get the security context of the directory to polyinstantiate.
+     */
+    rc = getfilecon(polyptr->dir, origcon);
+    if (rc < 0 || *origcon == NULL) {
+       pam_syslog(idata->pamh, LOG_ERR,
+               "Error getting poly dir context, %m");
+       return PAM_SESSION_ERR;
+    }
+
+    /*
+     * If polyinstantiating based on security context, get current
+     * process security context, get security class for directories,
+     * and ask the policy to provide security context of the
+     * polyinstantiated instance directory.
+     */
+    if ((polyptr->method == CONTEXT) || (polyptr->method == BOTH)) {
+        rc = getexeccon(&scon);
+        if (rc < 0 || scon == NULL) {
+            pam_syslog(idata->pamh, LOG_ERR, 
+               "Error getting exec context, %m");
+            return PAM_SESSION_ERR;
+       }
+        tclass = string_to_security_class("dir");
+
+        if (security_compute_member(scon, *origcon, tclass,
+                                               i_context) < 0) {
+           pam_syslog(idata->pamh, LOG_ERR,
+                       "Error computing poly dir member context");
+           freecon(scon);
+           return PAM_SESSION_ERR;
+        } else if (idata->flags & PAMNS_DEBUG)
+           pam_syslog(idata->pamh, LOG_DEBUG, 
+                   "member context returned by policy %s", *i_context);
+       freecon(scon);
+    }
+#endif
+    rc = PAM_SUCCESS;
+
+    /*
+     * Set the name of the polyinstantiated instance dir based on the
+     * polyinstantiation method.
+     */
+    switch (polyptr->method) {
+        case USER:
+           if (asprintf(i_name, "%s", idata->user) < 0) {
+               *i_name = NULL;
+               rc = PAM_SESSION_ERR;
+           }
+           break;
+
+#ifdef WITH_SELINUX
+        case CONTEXT:
+           if (asprintf(i_name, "%s", *i_context) < 0) {
+               *i_name = NULL;
+               rc = PAM_SESSION_ERR;
+           }
+           break;
+
+       case BOTH:
+           if (asprintf(i_name, "%s_%s", *i_context, idata->user) < 0) {
+               *i_name = NULL;
+               rc = PAM_SESSION_ERR;
+           }
+           break;
+#endif /* WITH_SELINUX */
+
+       default:
+           if (idata->flags & PAMNS_DEBUG)
+               pam_syslog(idata->pamh, LOG_ERR, "Unknown method");
+           rc = PAM_SESSION_ERR;
+    }
+
+    if ((idata->flags & PAMNS_DEBUG) && rc == PAM_SUCCESS)
+        pam_syslog(idata->pamh, LOG_DEBUG, "poly_name %s", *i_name);
+
+    return rc;
+}
+
+
+/*
+ * Create polyinstantiated instance directory (ipath).
+ */
+#ifdef WITH_SELINUX
+static int create_dirs(const struct polydir_s *polyptr, char *ipath, 
+        security_context_t icontext, security_context_t ocontext,
+       struct instance_data *idata)
+#else
+static int create_dirs(const struct polydir_s *polyptr, char *ipath,
+       struct instance_data *idata)
+#endif
+{
+    struct stat statbuf, newstatbuf, instpbuf;
+    int fd, status;
+    char *inst_parent, *trailing_slash;
+    pid_t rc, pid;
+    sighandler_t osighand = NULL;
+
+    /*
+     * stat the directory to polyinstantiate, so its owner-group-mode
+     * can be propagated to instance directory
+     */
+    if (stat(polyptr->dir, &statbuf) < 0) {
+        pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
+               polyptr->dir);
+        return PAM_SESSION_ERR;
+    }
+
+    /*
+     * Make sure we are dealing with a directory
+     */
+    if (!S_ISDIR(statbuf.st_mode)) {
+       pam_syslog(idata->pamh, LOG_ERR, "poly dir %s is not a dir",
+               polyptr->dir);
+        return PAM_SESSION_ERR;
+    }
+
+    /*
+     * stat the instance parent path to make sure it exists
+     * and is a directory. Check that its mode is 000 (unless the
+     * admin explicitly instructs to ignore the instance parent
+     * mode by the "ignore_instance_parent_mode" argument).
+     */
+    inst_parent = (char *) malloc(strlen(ipath)+1);
+    if (!inst_parent) {
+       pam_syslog(idata->pamh, LOG_ERR, "Error allocating pathname string");
+        return PAM_SESSION_ERR;
+    }
+
+    strcpy(inst_parent, ipath);
+    trailing_slash = strrchr(inst_parent, '/');
+    if (trailing_slash)
+        *trailing_slash = '\0';
+
+    if (stat(inst_parent, &instpbuf) < 0) {
+        pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m", inst_parent);
+        free(inst_parent);
+        return PAM_SESSION_ERR;
+    }
+
+    /*
+     * Make sure we are dealing with a directory
+     */
+    if (!S_ISDIR(instpbuf.st_mode)) {
+       pam_syslog(idata->pamh, LOG_ERR, "Instance parent %s is not a dir",
+               inst_parent);
+        free(inst_parent);
+        return PAM_SESSION_ERR;
+    }
+
+    if ((idata->flags & PAMNS_IGN_INST_PARENT_MODE) == 0) {
+        if (instpbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) {
+           pam_syslog(idata->pamh, LOG_ERR, "Mode of inst parent %s not 000",
+                   inst_parent);
+            free(inst_parent);
+            return PAM_SESSION_ERR;
+        }
+    }
+    free(inst_parent);
+
+    /*
+     * Create instance directory and set its security context to the context
+     * returned by the security policy. Set its mode and ownership
+     * attributes to match that of the original directory that is being
+     * polyinstantiated.
+     */
+    if (mkdir(ipath, S_IRUSR) < 0) {
+        if (errno == EEXIST)
+            goto inst_init;
+        else {
+            pam_syslog(idata->pamh, LOG_ERR, "Error creating %s, %m",
+                       ipath);
+            return PAM_SESSION_ERR;
+        }
+    }
+
+    /* Open a descriptor to it to prevent races */
+    fd = open(ipath, O_DIRECTORY | O_RDONLY);
+    if (fd < 0) {
+       pam_syslog(idata->pamh, LOG_ERR, "Error opening %s, %m", ipath);
+       rmdir(ipath);
+       return PAM_SESSION_ERR;
+    }
+#ifdef WITH_SELINUX
+    /* If SE Linux is disabled, no need to label it */
+    if (idata->flags & PAMNS_SELINUX_ENABLED) {
+        /* If method is USER, icontext is NULL */
+        if (icontext) {
+            if (fsetfilecon(fd, icontext) < 0) {
+                pam_syslog(idata->pamh, LOG_ERR, 
+                       "Error setting context of %s to %s", ipath, icontext);
+                close(fd);
+               rmdir(ipath);
+                return PAM_SESSION_ERR;
+            }
+        } else {
+            if (fsetfilecon(fd, ocontext) < 0) {
+                pam_syslog(idata->pamh, LOG_ERR,
+                       "Error setting context of %s to %s", ipath, ocontext);
+               close(fd);
+               rmdir(ipath);
+                return PAM_SESSION_ERR;
+            }
+        }
+    }
+#endif
+    if (fstat(fd, &newstatbuf) < 0) {
+        pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m",
+               ipath);
+       rmdir(ipath);
+        return PAM_SESSION_ERR;
+    }
+    if (newstatbuf.st_uid != statbuf.st_uid ||
+                        newstatbuf.st_gid != statbuf.st_gid) {
+        if (fchown(fd, statbuf.st_uid, statbuf.st_gid) < 0) {
+            pam_syslog(idata->pamh, LOG_ERR,
+                       "Error changing owner for %s, %m",
+                       ipath);
+           close(fd);
+           rmdir(ipath);
+            return PAM_SESSION_ERR;
+        }
+    }
+    if (fchmod(fd, statbuf.st_mode & 07777) < 0) {
+        pam_syslog(idata->pamh, LOG_ERR, "Error changing mode for %s, %m",
+                       ipath);
+       close(fd);
+       rmdir(ipath);
+        return PAM_SESSION_ERR;
+    }
+    close(fd);
+
+    /*
+     * Check to see if there is a namespace initialization script in
+     * the /etc/security directory. If such a script exists
+     * execute it and pass directory to polyinstantiate and instance
+     * directory as arguments.
+     */
+
+inst_init:
+    osighand = signal(SIGCHLD, SIG_DFL);
+    if (osighand == SIG_ERR) {
+        pam_syslog(idata->pamh, LOG_ERR, "Cannot set signal value");
+        return PAM_SESSION_ERR;
+    }
+
+    if (access(NAMESPACE_INIT_SCRIPT, F_OK) == 0) {
+        if (access(NAMESPACE_INIT_SCRIPT, X_OK) < 0) {
+            if (idata->flags & PAMNS_DEBUG)
+                pam_syslog(idata->pamh, LOG_ERR,
+                           "Namespace init script not executable");
+            (void) signal(SIGCHLD, osighand);
+            return PAM_SESSION_ERR;
+        } else {
+            pid = fork();
+           if (pid == 0) {
+#ifdef WITH_SELINUX
+               if (idata->flags & PAMNS_SELINUX_ENABLED) {
+                   if (setexeccon(NULL) < 0)
+                       exit(1);
+               }
+#endif
+               if (execl(NAMESPACE_INIT_SCRIPT, NAMESPACE_INIT_SCRIPT,
+                         polyptr->dir, ipath, (char *)NULL) < 0)
+                   exit(1);
+            } else if (pid > 0) {
+                while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
+                       (errno == EINTR));
+                if (rc == (pid_t)-1) {
+                    pam_syslog(idata->pamh, LOG_ERR, "waitpid failed- %m");
+                    (void) signal(SIGCHLD, osighand);
+                    return PAM_SESSION_ERR;
+                }
+                if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) {
+                    pam_syslog(idata->pamh, LOG_ERR,
+                               "Error initializing instance");
+                    (void) signal(SIGCHLD, osighand);
+                    return PAM_SESSION_ERR;
+                }
+           } else if (pid < 0) {
+                pam_syslog(idata->pamh, LOG_ERR,
+                           "Cannot fork to run namespace init script, %m");
+                (void) signal(SIGCHLD, osighand);
+                return PAM_SESSION_ERR;
+           }
+        }
+    }
+
+    (void) signal(SIGCHLD, osighand);
+    return PAM_SUCCESS;
+}
+
+
+/*
+ * md5hash generates a hash of the passed in instance directory name.
+ */
+static int md5hash(char **instname, struct instance_data *idata)
+{
+    int i;
+    char *md5inst = NULL;
+    char *to;
+    unsigned char inst_digest[MD5_DIGEST_LENGTH];
+
+    /*
+     * Create MD5 hashes for instance pathname.
+     */
+
+    MD5((unsigned char *)*instname, strlen(*instname), inst_digest);
+
+    if ((md5inst = malloc(MD5_DIGEST_LENGTH * 2 + 1)) == NULL) {
+        pam_syslog(idata->pamh, LOG_ERR, "Unable to allocate buffer");
+        return PAM_SESSION_ERR;
+    }
+
+    to = md5inst;
+    for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
+        snprintf(to, 3, "%02x", (unsigned int)inst_digest[i]);
+        to += 3;
+    }
+
+    free(*instname);
+    *instname = md5inst;
+
+    return PAM_SUCCESS;
+}
+
+/*
+ * This function performs the namespace setup for a particular directory
+ * that is being polyinstantiated. It creates an MD5 hash of instance 
+ * directory, calls create_dirs to create it with appropriate
+ * security attributes, and performs bind mount to setup the process
+ * namespace.
+ */
+static int ns_setup(const struct polydir_s *polyptr,
+       struct instance_data *idata)
+{
+    int retval = 0;
+    char *inst_dir = NULL;
+    char *instname = NULL;
+    char *dir;
+#ifdef WITH_SELINUX
+    security_context_t instcontext = NULL, origcontext = NULL;
+#endif
+
+    if (idata->flags & PAMNS_DEBUG)
+        pam_syslog(idata->pamh, LOG_DEBUG,
+               "Set namespace for directory %s", polyptr->dir);
+
+    dir = strrchr(polyptr->dir, '/');
+    if (dir && strlen(dir) > 1)
+        dir++;
+
+    /*
+     * Obtain the name of instance pathname based on the
+     * polyinstantiation method and instance context returned by
+     * security policy.
+     */
+#ifdef WITH_SELINUX
+    retval = poly_name(polyptr, &instname, &instcontext,
+                       &origcontext, idata);
+#else
+    retval = poly_name(polyptr, &instname, idata);
+#endif
+
+    if (retval) {
+        pam_syslog(idata->pamh, LOG_ERR, "Error getting instance name");
+        goto error_out;
+    } else {
+#ifdef WITH_SELINUX
+        if ((idata->flags & PAMNS_DEBUG) &&
+            (idata->flags & PAMNS_SELINUX_ENABLED))
+            pam_syslog(idata->pamh, LOG_DEBUG, "Inst ctxt %s Orig ctxt %s",
+                instcontext, origcontext);
+#endif
+    }
+
+    if (idata->flags & PAMNS_GEN_HASH) {
+        retval = md5hash(&instname, idata);
+        if (retval < 0) {
+            pam_syslog(idata->pamh, LOG_ERR, "Error generating md5 hash");
+            goto error_out;
+        }
+    }
+
+    if (asprintf(&inst_dir, "%s%s", polyptr->instance_prefix, instname) < 0)
+       goto error_out;
+
+    if (idata->flags & PAMNS_DEBUG)
+        pam_syslog(idata->pamh, LOG_DEBUG, "instance_dir %s",
+               inst_dir);
+
+    /*
+     * Create instance directory with appropriate security
+     * contexts, owner, group and mode bits.
+     */
+#ifdef WITH_SELINUX
+    retval = create_dirs(polyptr, inst_dir, instcontext,
+                        origcontext, idata);
+#else
+    retval = create_dirs(polyptr, inst_dir, idata);
+#endif
+
+    if (retval < 0) {
+        pam_syslog(idata->pamh, LOG_ERR, "Error creating instance dir");
+        goto error_out;
+    }
+
+    /*
+     * Bind mount instance directory on top of the polyinstantiated
+     * directory to provide an instance of polyinstantiated directory
+     * based on polyinstantiated method.
+     */
+    if (mount(inst_dir, polyptr->dir, NULL, MS_BIND, NULL) < 0) {
+        pam_syslog(idata->pamh, LOG_ERR, "Error mounting %s on %s, %m",
+                   inst_dir, polyptr->dir);
+        goto error_out;
+    }
+
+    goto cleanup;
+
+    /*
+     * various error exit points. Free allocated memory and set return
+     * value to indicate a pam session error.
+     */
+error_out:
+    retval = PAM_SESSION_ERR;
+
+cleanup:
+    free(inst_dir);
+    free(instname);
+#ifdef WITH_SELINUX
+    freecon(instcontext);
+    freecon(origcontext);
+#endif
+    return retval;
+}
+
+
+/*
+ * This function checks to see if the current working directory is
+ * inside the directory passed in as the first argument.
+ */
+static int cwd_in(char *dir, struct instance_data *idata)
+{
+    int retval = 0;
+    char cwd[PATH_MAX];
+
+    if (getcwd(cwd, PATH_MAX) == NULL) {
+        pam_syslog(idata->pamh, LOG_ERR, "Can't get current dir, %m");
+        return -1;
+    }
+
+    if (strncmp(cwd, dir, strlen(dir)) == 0) {
+        if (idata->flags & PAMNS_DEBUG)
+            pam_syslog(idata->pamh, LOG_DEBUG, "cwd is inside %s", dir);
+        retval = 1;
+    } else {
+        if (idata->flags & PAMNS_DEBUG)
+            pam_syslog(idata->pamh, LOG_DEBUG, "cwd is outside %s", dir);
+    }
+
+    return retval;
+}
+
+
+/*
+ * This function checks to see if polyinstantiation is needed for any
+ * of the directories listed in the configuration file. If needed,
+ * cycles through all polyinstantiated directory entries and calls
+ * ns_setup to setup polyinstantiation for each one of them.
+ */
+static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
+{
+    int retval = 0, need_poly = 0, changing_dir = 0;
+    char *cptr, *fptr, poly_parent[PATH_MAX];
+    struct polydir_s *pptr;
+
+    if (idata->flags & PAMNS_DEBUG)
+        pam_syslog(idata->pamh, LOG_DEBUG, "Set up namespace for pid %d",
+               getpid());
+
+    /*
+     * Cycle through all polyinstantiated directory entries to see if
+     * polyinstantiation is needed at all.
+     */
+    for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
+        if (ns_override(pptr, idata)) {
+            if (idata->flags & PAMNS_DEBUG)
+                pam_syslog(idata->pamh, LOG_DEBUG, 
+                       "Overriding poly for user %d for dir %s",
+                       idata->uid, pptr->dir);
+            continue;
+        } else {
+            if (idata->flags & PAMNS_DEBUG)
+                pam_syslog(idata->pamh, LOG_DEBUG, 
+                       "Need poly ns for user %d for dir %s",
+                       idata->uid, pptr->dir);
+            need_poly = 1;
+            break;
+        }
+    }
+
+    /*
+     * If polyinstnatiation is needed, call the unshare system call to
+     * disassociate from the parent namespace.
+     */
+    if (need_poly) {
+        if (unshare(CLONE_NEWNS) < 0) {
+            pam_syslog(idata->pamh, LOG_ERR,
+               "Unable to unshare from parent namespace, %m");
+            return PAM_SESSION_ERR;
+        }
+    } else
+        return PAM_SUCCESS;
+
+    /*
+     * Again cycle through all polyinstantiated directories, this time,
+     * call ns_setup to setup polyinstantiation for a particular entry.
+     */
+    for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
+        if (ns_override(pptr, idata))
+            continue;
+        else {
+            if (idata->flags & PAMNS_DEBUG)
+                pam_syslog(idata->pamh, LOG_DEBUG,
+                       "Setting poly ns for user %d for dir %s",
+                      idata->uid, pptr->dir);
+
+            if ((unmnt == UNMNT_REMNT) || (unmnt == UNMNT_ONLY)) {
+                /*
+                 * Check to see if process current directory is in the
+                 * bind mounted instance_parent directory that we are trying to
+                 * umount
+                 */
+                if ((changing_dir = cwd_in(pptr->dir, idata)) < 0) {
+                    return PAM_SESSION_ERR;
+                } else if (changing_dir) {
+                    if (idata->flags & PAMNS_DEBUG)
+                        pam_syslog(idata->pamh, LOG_DEBUG, "changing cwd");
+
+                    /*
+                     * Change current working directory to the parent of
+                     * the mount point, that is parent of the orig
+                     * directory where original contents of the polydir
+                     * are available from
+                     */
+                    strcpy(poly_parent, pptr->dir);
+                   fptr = strchr(poly_parent, '/');
+                   cptr = strrchr(poly_parent, '/');
+                   if (fptr && cptr && (fptr == cptr))
+                       strcpy(poly_parent, "/");
+                   else if (cptr)
+                       *cptr = '\0';
+                    if (chdir(poly_parent) < 0) {
+                        pam_syslog(idata->pamh, LOG_ERR, 
+                               "Can't chdir to %s, %m", poly_parent);
+                    }
+                }
+
+                if (umount(pptr->dir) < 0) {
+                   int saved_errno = errno;
+                   pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
+                       pptr->dir);
+                   if (saved_errno != EINVAL)
+                       return PAM_SESSION_ERR;
+                } else if (idata->flags & PAMNS_DEBUG)
+                    pam_syslog(idata->pamh, LOG_DEBUG, "Umount succeeded %s",
+                               pptr->dir);
+            }
+
+           if (unmnt != UNMNT_ONLY) {
+                retval = ns_setup(pptr, idata);
+                if (retval != PAM_SUCCESS) 
+                     break;
+           }
+        }
+    }
+
+    return retval;
+}
+
+
+/*
+ * Orig namespace. This function is called from when closing a pam
+ * session. If authorized, it unmounts instance directory.
+ */
+static int orig_namespace(struct instance_data *idata)
+{
+    struct polydir_s *pptr;
+
+    if (idata->flags & PAMNS_DEBUG)
+        pam_syslog(idata->pamh, LOG_DEBUG, "orig namespace for pid %d", 
+               getpid());
+
+    /*
+     * Cycle through all polyinstantiated directories from the namespace
+     * configuration file to see if polyinstantiation was performed for
+     * this user for each of the entry. If it was, try and unmount
+     * appropriate polyinstantiated instance directories.
+     */
+    for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
+        if (ns_override(pptr, idata))
+            continue;
+        else {
+            if (idata->flags & PAMNS_DEBUG)
+                pam_syslog(idata->pamh, LOG_DEBUG, 
+                       "Unmounting instance dir for user %d & dir %s",
+                       idata->uid, pptr->dir);
+
+            if (umount(pptr->dir) < 0) {
+                pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
+                       pptr->dir);
+                return PAM_SESSION_ERR;
+            } else if (idata->flags & PAMNS_DEBUG)
+                pam_syslog(idata->pamh, LOG_DEBUG, "Unmount of %s succeeded",
+                       pptr->dir);
+        }
+    }
+    return 0;
+}
+
+
+#ifdef WITH_SELINUX
+/*
+ * This function checks if the calling program has requested context
+ * change by calling setexeccon(). If context change is not requested
+ * then it does not make sense to polyinstantiate based on context.
+ * The return value from this function is used when selecting the 
+ * polyinstantiation method. If context change is not requested then
+ * the polyinstantiation method is set to USER, even if the configuration
+ * file lists the method as "context" or "both".
+ */
+static int ctxt_based_inst_needed(void)
+{
+    security_context_t scon = NULL;
+    int rc = 0;
+
+    rc = getexeccon(&scon);
+    if (rc < 0 || scon == NULL)
+        return 0;
+    else {
+        freecon(scon);
+        return 1;
+    }
+}
+#endif
+
+
+/*
+ * Entry point from pam_open_session call.
+ */
+PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
+                                   int argc, const char **argv)
+{
+    int i, retval;
+    struct instance_data idata;
+    char *user_name;
+    struct passwd *pwd;
+    enum unmnt_op unmnt = NO_UNMNT;
+
+    /* init instance data */
+    idata.flags = 0;
+    idata.polydirs_ptr = NULL;
+    idata.pamh = pamh;
+#ifdef WITH_SELINUX
+    if (is_selinux_enabled())
+        idata.flags |= PAMNS_SELINUX_ENABLED;
+    if (ctxt_based_inst_needed())
+        idata.flags |= PAMNS_CTXT_BASED_INST;
+#endif
+
+    /* Parse arguments. */
+    for (i = 0; i < argc; i++) {
+        if (strcmp(argv[i], "debug") == 0)
+            idata.flags |= PAMNS_DEBUG;
+        if (strcmp(argv[i], "gen_hash") == 0)
+            idata.flags |= PAMNS_GEN_HASH;
+        if (strcmp(argv[i], "ignore_config_error") == 0)
+            idata.flags |= PAMNS_IGN_CONFIG_ERR;
+        if (strcmp(argv[i], "ignore_instance_parent_mode") == 0)
+            idata.flags |= PAMNS_IGN_INST_PARENT_MODE;
+        if (strcmp(argv[i], "unmnt_remnt") == 0)
+            unmnt = UNMNT_REMNT;
+        if (strcmp(argv[i], "unmnt_only") == 0)
+            unmnt = UNMNT_ONLY;
+       if (strcmp(argv[i], "require_selinux") == 0) {
+               if (~(idata.flags & PAMNS_SELINUX_ENABLED)) {
+                       pam_syslog(idata.pamh, LOG_ERR, 
+                   "selinux_required option given and selinux is disabled");
+                       return PAM_SESSION_ERR;
+               }
+       }
+    }
+    if (idata.flags & PAMNS_DEBUG)
+        pam_syslog(idata.pamh, LOG_DEBUG, "open_session - start");
+
+    /* 
+     * Lookup user and fill struct items
+     */
+    retval = pam_get_item(idata.pamh, PAM_USER, (void*) &user_name );
+    if ( user_name == NULL || retval != PAM_SUCCESS ) {
+        pam_syslog(idata.pamh, LOG_ERR, "Error recovering pam user name");
+        return PAM_SESSION_ERR;
+    }
+
+    pwd = getpwnam(user_name);
+    if (!pwd) {
+        pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
+        return PAM_SESSION_ERR;
+    }
+
+    /*
+     * Add the user info to the instance data so we can refer to them later.
+     */
+    idata.user[0] = 0;
+    strncat(idata.user, user_name, sizeof(idata.user));
+    idata.uid = pwd->pw_uid;
+
+    /*
+     * Parse namespace configuration file which lists directories to
+     * polyinstantiate, directory where instance directories are to
+     * be created and the method used for polyinstantiation.
+     */
+    retval = parse_config_file(&idata);
+    if (retval != PAM_SUCCESS) {
+       del_polydir_list(idata.polydirs_ptr);
+        return PAM_SESSION_ERR;
+    }
+
+    if (idata.polydirs_ptr) {
+        retval = setup_namespace(&idata, unmnt);
+        if (idata.flags & PAMNS_DEBUG) {
+            if (retval)
+                pam_syslog(idata.pamh, LOG_DEBUG,
+                       "namespace setup failed for pid %d", getpid());
+            else
+                pam_syslog(idata.pamh, LOG_DEBUG,
+                       "namespace setup ok for pid %d", getpid());
+        }
+    } else if (idata.flags & PAMNS_DEBUG)
+        pam_syslog(idata.pamh, LOG_DEBUG, "Nothing to polyinstantiate");
+
+    del_polydir_list(idata.polydirs_ptr);
+    return retval;
+}
+
+
+/*
+ * Entry point from pam_close_session call.
+ */
+PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
+                                    int argc, const char **argv)
+{
+    int i, retval;
+    struct instance_data idata;
+    char *user_name;
+    struct passwd *pwd;
+
+    /* init instance data */
+    idata.flags = 0;
+    idata.polydirs_ptr = NULL;
+    idata.pamh = pamh;
+#ifdef WITH_SELINUX
+    if (is_selinux_enabled())
+        idata.flags |= PAMNS_SELINUX_ENABLED;
+    if (ctxt_based_inst_needed())
+        idata.flags |= PAMNS_CTXT_BASED_INST;
+#endif
+
+    /* Parse arguments. */
+    for (i = 0; i < argc; i++) {
+        if (strcmp(argv[i], "debug") == 0) 
+            idata.flags |= PAMNS_DEBUG;
+        if (strcmp(argv[i], "ignore_config_error") == 0)
+            idata.flags |= PAMNS_IGN_CONFIG_ERR;
+    }
+
+    if (idata.flags & PAMNS_DEBUG)
+        pam_syslog(idata.pamh, LOG_DEBUG, "close_session - start");
+
+    /* 
+     * Lookup user and fill struct items
+     */
+    retval = pam_get_item(idata.pamh, PAM_USER, (void*) &user_name );
+    if ( user_name == NULL || retval != PAM_SUCCESS ) {
+        pam_syslog(idata.pamh, LOG_ERR, "Error recovering pam user name");
+        return PAM_SESSION_ERR;
+    }
+
+    pwd = getpwnam(user_name);
+    if (!pwd) {
+        pam_syslog(idata.pamh, LOG_ERR, "user unknown '%s'", user_name);
+        return PAM_SESSION_ERR;
+    }
+
+    /*
+     * Add the user info to the instance data so we can refer to them later.
+     */
+    idata.user[0] = 0;
+    strncat(idata.user, user_name, sizeof(idata.user));
+    idata.uid = pwd->pw_uid;
+
+    /*
+     * Parse namespace configuration file which lists directories that
+     * are polyinstantiated, directories where instance directories are
+     * created and the method used for polyinstantiation.
+     */
+    retval = parse_config_file(&idata);
+    if ((retval != PAM_SUCCESS) || !idata.polydirs_ptr) {
+       del_polydir_list(idata.polydirs_ptr);
+        return PAM_SESSION_ERR;
+    }
+
+    if (idata.flags & PAMNS_DEBUG)
+        pam_syslog(idata.pamh, LOG_DEBUG, "Resetting namespace for pid %d",
+               getpid());
+
+    retval = orig_namespace(&idata);
+    if (idata.flags & PAMNS_DEBUG) {
+        if (retval) 
+            pam_syslog(idata.pamh, LOG_DEBUG,
+               "resetting namespace failed for pid %d", getpid());
+        else 
+            pam_syslog(idata.pamh, LOG_DEBUG, 
+               "resetting namespace ok for pid %d", getpid());
+    }
+    del_polydir_list(idata.polydirs_ptr);
+    return PAM_SUCCESS;
+}
+
+#ifdef PAM_STATIC
+
+/* static module data */
+
+struct pam_module _pam_namespace_modstruct = {
+     "pam_namespace",
+     NULL,
+     NULL,
+     NULL,
+     pam_sm_open_session,
+     pam_sm_close_session,
+     NULL
+};
+#endif
+