From bc7424a26860cc26630a80e5500284f3af36b940 Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Tue, 27 Jun 2006 13:07:42 +0000 Subject: Relevant BUGIDs: 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. --- modules/Makefile.am | 2 +- modules/pam_namespace/Makefile.am | 38 + modules/pam_namespace/README | 122 +++ modules/pam_namespace/md5.c | 260 ++++++ modules/pam_namespace/md5.h | 28 + modules/pam_namespace/namespace.conf | 30 + modules/pam_namespace/namespace.conf.5 | 96 ++ modules/pam_namespace/namespace.conf.5.xml | 151 +++ modules/pam_namespace/namespace.init | 24 + modules/pam_namespace/pam_namespace.8 | 141 +++ modules/pam_namespace/pam_namespace.8.xml | 317 +++++++ modules/pam_namespace/pam_namespace.c | 1386 ++++++++++++++++++++++++++++ 12 files changed, 2594 insertions(+), 1 deletion(-) create mode 100644 modules/pam_namespace/Makefile.am create mode 100644 modules/pam_namespace/README create mode 100644 modules/pam_namespace/md5.c create mode 100644 modules/pam_namespace/md5.h create mode 100644 modules/pam_namespace/namespace.conf create mode 100644 modules/pam_namespace/namespace.conf.5 create mode 100644 modules/pam_namespace/namespace.conf.5.xml create mode 100755 modules/pam_namespace/namespace.init create mode 100644 modules/pam_namespace/pam_namespace.8 create mode 100644 modules/pam_namespace/pam_namespace.8.xml create mode 100644 modules/pam_namespace/pam_namespace.c (limited to 'modules') diff --git a/modules/Makefile.am b/modules/Makefile.am index 9d7a6fcb..300f1955 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -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 index 00000000..e1e8931f --- /dev/null +++ b/modules/pam_namespace/Makefile.am @@ -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 index 00000000..7edfe55e --- /dev/null +++ b/modules/pam_namespace/README @@ -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: + + + +Where: + - 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. + + - is the string prefix used to build the pathname for the + instantiation of . 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 to provide an instance of + based on the column. The special string $HOME is replaced with + the user's home directory, and $USER with the username. + This field cannot be blank. + + - 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. + + - 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 s you need polyinstantiation (login for example) + put the following line in /etc/pam.d/ 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 index 00000000..3094a130 --- /dev/null +++ b/modules/pam_namespace/md5.c @@ -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 +#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<>(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 index 00000000..73f85833 --- /dev/null +++ b/modules/pam_namespace/md5.h @@ -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 index 00000000..c7305ffe --- /dev/null +++ b/modules/pam_namespace/namespace.conf @@ -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 index 00000000..ff325a21 --- /dev/null +++ b/modules/pam_namespace/namespace.conf.5 @@ -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 \&. 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 to provide an instance of based on the 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 + +.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 s you need polyinstantiation (login for example) put the following line in /etc/pam\&.d/ 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 \&. + diff --git a/modules/pam_namespace/namespace.conf.5.xml b/modules/pam_namespace/namespace.conf.5.xml new file mode 100644 index 00000000..36a1a085 --- /dev/null +++ b/modules/pam_namespace/namespace.conf.5.xml @@ -0,0 +1,151 @@ + + + + + + + namespace.conf + 5 + Linux-PAM Manual + + + + namespace.conf + the namespace configuration file + + + + + DESCRIPTION + + + 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 /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. + + + + The /etc/security/namespace.conf 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. + + + + When someone logs in, the file namespace.conf is + scanned where each non comment line represents one polyinstantiated + directory with space separated fields as follows: + + + + polydir instance_prefix method list_of_uids + + + + The first field, 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. + + + + The second field, 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. 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> + + + + The third field, 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. + + + + The fourth field, 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. + + + + + + EXAMPLES + + These are some example lines which might be specified in + /etc/security/namespace.conf. + + + + # 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 + + + + 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. + + + + + + SEE ALSO + + pam_namespace8, + pam.d5, + pam8 + + + + + AUTHORS + + The namespace.conf manual page was written by Janak Desai <janak@us.ibm.com>. + + + diff --git a/modules/pam_namespace/namespace.init b/modules/pam_namespace/namespace.init new file mode 100755 index 00000000..62f8e6e4 --- /dev/null +++ b/modules/pam_namespace/namespace.init @@ -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 index 00000000..88594304 --- /dev/null +++ b/modules/pam_namespace/pam_namespace.8 @@ -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 s you need polyinstantiation (login for example) put the following line in /etc/pam\&.d/ 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 , Chad Sellers and Steve Grubb \&. + diff --git a/modules/pam_namespace/pam_namespace.8.xml b/modules/pam_namespace/pam_namespace.8.xml new file mode 100644 index 00000000..4c93ecf0 --- /dev/null +++ b/modules/pam_namespace/pam_namespace.8.xml @@ -0,0 +1,317 @@ + + + + + + + pam_namespace + 8 + Linux-PAM Manual + + + + pam_namespace + + PAM module for configuring namespace for a session + + + + + + + + pam_namespace.so + + debug + + + unmnt_remnt + + + unmnt_only + + + require_selinux + + + gen_hash + + + ignore_config_error + + + ignore_instance_parent_mode + + + + + + + DESCRIPTION + + 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 /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. + + + + 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. + + + + + + OPTIONS + + + + + + + + + A lot of debug information is logged using syslog + + + + + + + + + + + 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 + + + + + + + + + + + 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 + + + + + + + + + + + If selinux is not enabled, return failure + + + + + + + + + + + Instead of using the security context string + for the instance name, generate and use its + md5 hash. + + + + + + + + + + + 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. + + + + + + + + + + + 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. + + + + + + + + + MODULE SERVICES PROVIDED + + The service is supported. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + Namespace setup was successful. + + + + + PAM_SERVICE_ERR + + + Unexpected system error occurred while setting up namespace. + + + + + PAM_SESSION_ERR + + + Unexpected namespace configuration error occurred. + + + + + + + + FILES + + + /etc/security/namespace.conf + + Configuration file + + + + + + + EXAMPLES + + + 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] + + + + To use polyinstantiation with graphical display manager gdm, insert the + following line, before exit 0, in /etc/gdm/PostSession/Default: + + + + /usr/sbin/gdm-safe-restart + + + + 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 + /etc/security/namespace.init 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 + /etc/security/namespace.init. In addition, + perform the following changes to use graphical environment with + polyinstantiation of /tmp: + + + + + 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. + + + + + + + SEE ALSO + + + namespace.conf5 + , + + pam.d8 + , + + mount8 + , + + pam8 + . + + + + + AUTHORS + + 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.c b/modules/pam_namespace/pam_namespace.c new file mode 100644 index 00000000..cd6ef07c --- /dev/null +++ b/modules/pam_namespace/pam_namespace.c @@ -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 + * With Revisions by: Steve Grubb + * Derived from a namespace setup patch by Chad Sellers + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "security/pam_modules.h" +#include "security/pam_modutil.h" +#include "security/pam_ext.h" +#include "md5.h" + +#ifdef WITH_SELINUX +#include +#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 + -- cgit v1.2.3