summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorTomas Mraz <tm@t8m.info>2008-02-01 16:22:23 +0000
committerTomas Mraz <tm@t8m.info>2008-02-01 16:22:23 +0000
commit2535f925c1a6049e5ad9ee4f313bcaa79131932b (patch)
treeb0f1ae461c7b4650ccd8c5211e57f24c114eb79c /modules
parent538dad819245deb53f1d55109130dce2199c6730 (diff)
Relevant BUGIDs:
Purpose of commit: new feature Commit summary: --------------- 2008-02-01 Tomas Mraz <t8m@centrum.cz> * modules/pam_namespace/namespace.conf.5.xml: Add documentation for tmpfs and tmpdir polyinst and for ~ user list modifier. * modules/pam_namespace/namespace.init: Add documentation for the new init parameter. Add home directory initialization script. * modules/pam_namespace/pam_namespace.8.xml: Document the new init parameter of the namespace.init script. * modules/pam_namespace/pam_namespace.c(copy_ent): Copy exclusive flag. (cleanup_data): New function. (process_line): Set exclusive flag. Add tmpfs and tmpdir methods. (ns_override): Change behavior on the exclusive flag. (poly_name): Process tmpfs and tmpdir methods. (inst_init): Add flag for new directory initialization. (create_dirs): Process the tmpdir method, add the new directory flag. (ns_setup): Remove unused code. Process the tmpfs method. (cleanup_tmpdirs): New function. (setup_namespace): Set data for proper cleanup. Cleanup the tmpdirs on failures. (pam_sm_close_session): Instead of parsing the config file again use the previously set data for cleanup. * modules/pam_namespace/pam_namespace.h: Add TMPFS and TMPDIR methods and exclusive flag.
Diffstat (limited to 'modules')
-rw-r--r--modules/pam_namespace/namespace.conf.5.xml12
-rwxr-xr-xmodules/pam_namespace/namespace.init40
-rw-r--r--modules/pam_namespace/pam_namespace.8.xml4
-rw-r--r--modules/pam_namespace/pam_namespace.c211
-rw-r--r--modules/pam_namespace/pam_namespace.h4
5 files changed, 198 insertions, 73 deletions
diff --git a/modules/pam_namespace/namespace.conf.5.xml b/modules/pam_namespace/namespace.conf.5.xml
index db48cdcb..9fbefc49 100644
--- a/modules/pam_namespace/namespace.conf.5.xml
+++ b/modules/pam_namespace/namespace.conf.5.xml
@@ -72,10 +72,13 @@
<para>
The third field, <replaceable>method</replaceable>, is the method
- used for polyinstantiation. It can take 3 different values; "user"
+ used for polyinstantiation. It can take these values; "user"
for polyinstantiation based on user name, "level" for
- polyinstantiation based on process MLS level and user name, and "context" for
- polyinstantiation based on process security context and user name
+ polyinstantiation based on process MLS level and user name, "context" for
+ polyinstantiation based on process security context and user name,
+ "tmpfs" for mounting tmpfs filesystem as an instance dir, and
+ "tmpdir" for creating temporary directory as an instance dir which is
+ removed when the user's session is closed.
Methods "context" and "level" are only available with SELinux. This
field cannot be blank.
</para>
@@ -84,7 +87,8 @@
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.
+ for all users. If the list is preceded with a single "~" character,
+ polyinstantiation is performed only for users in the list.
</para>
<para>
diff --git a/modules/pam_namespace/namespace.init b/modules/pam_namespace/namespace.init
index 0e9be68f..424c6d0c 100755
--- a/modules/pam_namespace/namespace.init
+++ b/modules/pam_namespace/namespace.init
@@ -1,24 +1,24 @@
#!/bin/sh -p
-# This is only a boilerplate for the instance initialization script.
-# It receives polydir path as $1 and the instance path as $2.
+# It receives polydir path as $1, the instance path as $2,
+# a flag whether the instance dir was newly created (0 - no, 1 - yes) in $3,
+# and user name in $4.
#
-# 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
+# The following section will copy the contents of /etc/skel if this is a
+# newly created home directory.
+if [ "$3" = 1 ]; then
+ # This line will fix the labeling on all newly created directories
+ [ -x /sbin/restorecon ] && /sbin/restorecon "$1"
+ user="$4"
+ passwd=$(getent passwd "$user")
+ homedir=$(echo "$passwd" | cut -f6 -d":")
+ if [ "$1" = "$homedir" ]; then
+ gid=$(echo "$passwd" | cut -f4 -d":")
+ cp -rT /etc/skel "$homedir"
+ chown -R "$user":"$gid" "$homedir"
+ mode=$(awk '/^UMASK/{gsub("#.*$", "", $2); printf "%o", and(0777,compl(strtonum("0" $2))); exit}' /etc/login.defs)
+ chmod ${mode:-700} "$homedir"
+ [ -x /sbin/restorecon ] && /sbin/restorecon -R "$homedir"
+ fi
+fi
exit 0
diff --git a/modules/pam_namespace/pam_namespace.8.xml b/modules/pam_namespace/pam_namespace.8.xml
index e1b307ae..f47bb81b 100644
--- a/modules/pam_namespace/pam_namespace.8.xml
+++ b/modules/pam_namespace/pam_namespace.8.xml
@@ -60,7 +60,9 @@
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.
+ directory path, the instance directory path, flag whether the instance
+ directory was newly created (0 for no, 1 for yes), and the user name
+ as its arguments.
</para>
<para>
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
index d3612f59..a47b0698 100644
--- a/modules/pam_namespace/pam_namespace.c
+++ b/modules/pam_namespace/pam_namespace.c
@@ -43,6 +43,7 @@ static int copy_ent(const struct polydir_s *ent, struct polydir_s *pent)
strcpy(pent->instance_prefix, ent->instance_prefix);
pent->method = ent->method;
pent->num_uids = ent->num_uids;
+ pent->exclusive = ent->exclusive;
if (ent->num_uids) {
uid_t *pptr, *eptr;
@@ -120,6 +121,10 @@ static void del_polydir_list(struct polydir_s *polydirs_ptr)
}
}
+static void cleanup_data(pam_handle_t *pamh UNUSED , void *data, int err UNUSED)
+{
+ del_polydir_list(data);
+}
/*
* Called from parse_config_file, this function processes a single line
@@ -140,6 +145,7 @@ static int process_line(char *line, const char *home,
poly.uid = NULL;
poly.num_uids = 0;
+ poly.exclusive = 0;
/*
* skip the leading white space
@@ -223,24 +229,13 @@ static int process_line(char *line, const char *home,
}
/*
- * 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");
+ goto skipping;
}
strcpy(poly.dir, dir);
strcpy(poly.instance_prefix, instance_prefix);
@@ -248,6 +243,18 @@ static int process_line(char *line, const char *home,
poly.method = NONE;
if (strcmp(method, "user") == 0)
poly.method = USER;
+
+ if (strcmp(method, "tmpdir") == 0) {
+ poly.method = TMPDIR;
+ if (sizeof(poly.instance_prefix) - strlen(poly.instance_prefix) < 7) {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
+ goto skipping;
+ }
+ strcat(poly.instance_prefix, "XXXXXX");
+ }
+
+ if (strcmp(method, "tmpfs") == 0)
+ poly.method = TMPFS;
#ifdef WITH_SELINUX
if (strcmp(method, "level") == 0) {
@@ -266,12 +273,24 @@ static int process_line(char *line, const char *home,
#endif
- if ( poly.method == NONE) {
+ if (poly.method == NONE) {
pam_syslog(idata->pamh, LOG_NOTICE, "Illegal method");
goto skipping;
}
/*
+ * Ensure that all pathnames are absolute path names.
+ */
+ if ((dir[0] != '/') || (poly.method != TMPFS && 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;
+ }
+
+ /*
* 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
@@ -281,7 +300,11 @@ static int process_line(char *line, const char *home,
uid_t *uidptr;
const char *ustr, *sstr;
int count, i;
-
+
+ if (*uids == '~') {
+ poly.exclusive = 1;
+ uids++;
+ }
for (count = 0, ustr = sstr = uids; sstr; ustr = sstr + 1, count++)
sstr = strchr(ustr, ',');
@@ -419,6 +442,7 @@ static int parse_config_file(struct instance_data *idata)
* 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.
+ * If exclusive is set the returned values are opposite.
*/
static int ns_override(struct polydir_s *polyptr, struct instance_data *idata,
uid_t uid)
@@ -432,11 +456,11 @@ static int ns_override(struct polydir_s *polyptr, struct instance_data *idata,
for (i = 0; i < polyptr->num_uids; i++) {
if (uid == polyptr->uid[i]) {
- return 1;
+ return !polyptr->exclusive;
}
}
- return 0;
+ return polyptr->exclusive;
}
/*
@@ -623,6 +647,12 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name,
#endif /* WITH_SELINUX */
+ case TMPDIR:
+ case TMPFS:
+ if ((*i_name=strdup("")) == NULL)
+ goto fail;
+ return PAM_SUCCESS;
+
default:
if (idata->flags & PAMNS_DEBUG)
pam_syslog(idata->pamh, LOG_ERR, "Unknown method");
@@ -643,7 +673,7 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name,
hash = NULL;
} else {
char *newname;
- if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-strlen(hash),
+ if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-(int)strlen(hash),
*i_name, hash) < 0) {
goto fail;
}
@@ -726,8 +756,8 @@ static int check_inst_parent(char *ipath, struct instance_data *idata)
* execute it and pass directory to polyinstantiate and instance
* directory as arguments.
*/
-static int inst_init(const struct polydir_s *polyptr, char *ipath,
- struct instance_data *idata)
+static int inst_init(const struct polydir_s *polyptr, const char *ipath,
+ struct instance_data *idata, int newdir)
{
pid_t rc, pid;
sighandler_t osighand = NULL;
@@ -757,7 +787,7 @@ static int inst_init(const struct polydir_s *polyptr, char *ipath,
}
#endif
if (execl(NAMESPACE_INIT_SCRIPT, NAMESPACE_INIT_SCRIPT,
- polyptr->dir, ipath, (char *)NULL) < 0)
+ polyptr->dir, ipath, newdir?"1":"0", idata->user, (char *)NULL) < 0)
exit(1);
} else if (pid > 0) {
while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
@@ -792,16 +822,17 @@ out:
* Create polyinstantiated instance directory (ipath).
*/
#ifdef WITH_SELINUX
-static int create_dirs(const struct polydir_s *polyptr, char *ipath,
+static int create_dirs(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,
+static int create_dirs(struct polydir_s *polyptr, char *ipath,
struct instance_data *idata)
#endif
{
struct stat statbuf, newstatbuf;
int rc, fd;
+ int newdir = 0;
/*
* stat the directory to polyinstantiate, so its owner-group-mode
@@ -835,7 +866,17 @@ static int create_dirs(const struct polydir_s *polyptr, char *ipath,
* attributes to match that of the original directory that is being
* polyinstantiated.
*/
- if (mkdir(ipath, S_IRUSR) < 0) {
+
+ if (polyptr->method == TMPDIR) {
+ if (mkdtemp(polyptr->instance_prefix) == NULL) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error creating temporary instance %s, %m",
+ polyptr->instance_prefix);
+ polyptr->method = NONE; /* do not clean up! */
+ return PAM_SESSION_ERR;
+ }
+ /* copy the actual directory name to ipath */
+ strcpy(ipath, polyptr->instance_prefix);
+ } else if (mkdir(ipath, S_IRUSR) < 0) {
if (errno == EEXIST)
goto inst_init;
else {
@@ -845,6 +886,7 @@ static int create_dirs(const struct polydir_s *polyptr, char *ipath,
}
}
+ newdir = 1;
/* Open a descriptor to it to prevent races */
fd = open(ipath, O_DIRECTORY | O_RDONLY);
if (fd < 0) {
@@ -909,7 +951,7 @@ static int create_dirs(const struct polydir_s *polyptr, char *ipath,
*/
inst_init:
- rc = inst_init(polyptr, ipath, idata);
+ rc = inst_init(polyptr, ipath, idata, newdir);
return rc;
}
@@ -921,13 +963,12 @@ inst_init:
* security attributes, and performs bind mount to setup the process
* namespace.
*/
-static int ns_setup(const struct polydir_s *polyptr,
+static int ns_setup(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
@@ -936,9 +977,15 @@ static int ns_setup(const struct polydir_s *polyptr,
pam_syslog(idata->pamh, LOG_DEBUG,
"Set namespace for directory %s", polyptr->dir);
- dir = strrchr(polyptr->dir, '/');
- if (dir && strlen(dir) > 1)
- dir++;
+ if (polyptr->method == TMPFS) {
+ if (mount("tmpfs", polyptr->dir, "tmpfs", 0, NULL) < 0) {
+ pam_syslog(idata->pamh, LOG_ERR, "Error mounting tmpfs on %s, %m",
+ polyptr->dir);
+ return PAM_SESSION_ERR;
+ }
+ /* we must call inst_init after the mount in this case */
+ return inst_init(polyptr, "tmpfs", idata, 1);
+ }
/*
* Obtain the name of instance pathname based on the
@@ -1044,6 +1091,58 @@ static int cwd_in(char *dir, struct instance_data *idata)
return retval;
}
+static int cleanup_tmpdirs(struct instance_data *idata)
+{
+ struct polydir_s *pptr;
+ pid_t rc, pid;
+ sighandler_t osighand = NULL;
+ int status;
+
+ osighand = signal(SIGCHLD, SIG_DFL);
+ if (osighand == SIG_ERR) {
+ pam_syslog(idata->pamh, LOG_ERR, "Cannot set signal value");
+ rc = PAM_SESSION_ERR;
+ goto out;
+ }
+
+ for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
+ if (pptr->method == TMPDIR && access(pptr->instance_prefix, F_OK) == 0) {
+ pid = fork();
+ if (pid == 0) {
+#ifdef WITH_SELINUX
+ if (idata->flags & PAMNS_SELINUX_ENABLED) {
+ if (setexeccon(NULL) < 0)
+ exit(1);
+ }
+#endif
+ if (execl("/bin/rm", "/bin/rm", "-rf", pptr->instance_prefix, (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");
+ rc = PAM_SESSION_ERR;
+ goto out;
+ }
+ if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) {
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Error removing %s", pptr->instance_prefix);
+ }
+ } else if (pid < 0) {
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Cannot fork to run namespace init script, %m");
+ rc = PAM_SESSION_ERR;
+ goto out;
+ }
+ }
+ }
+
+ rc = PAM_SUCCESS;
+out:
+ signal(SIGCHLD, osighand);
+ return rc;
+}
/*
* This function checks to see if polyinstantiation is needed for any
@@ -1112,13 +1211,22 @@ static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
* disassociate from the parent namespace.
*/
if (need_poly) {
+ if (pam_set_data(idata->pamh, NAMESPACE_POLYDIR_DATA, idata->polydirs_ptr,
+ cleanup_data) != PAM_SUCCESS) {
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Unable to set namespace data");
+ return PAM_SYSTEM_ERR;
+ }
if (unshare(CLONE_NEWNS) < 0) {
- pam_syslog(idata->pamh, LOG_ERR,
+ pam_set_data(idata->pamh, NAMESPACE_POLYDIR_DATA, NULL, NULL);
+ pam_syslog(idata->pamh, LOG_ERR,
"Unable to unshare from parent namespace, %m");
return PAM_SESSION_ERR;
}
- } else
+ } else {
+ del_polydir_list(idata->polydirs_ptr);
return PAM_SUCCESS;
+ }
/*
* Again cycle through all polyinstantiated directories, this time,
@@ -1145,7 +1253,8 @@ static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
* umount
*/
if ((changing_dir = cwd_in(pptr->dir, idata)) < 0) {
- return PAM_SESSION_ERR;
+ retval = PAM_SESSION_ERR;
+ goto out;
} else if (changing_dir) {
if (idata->flags & PAMNS_DEBUG)
pam_syslog(idata->pamh, LOG_DEBUG, "changing cwd");
@@ -1173,8 +1282,10 @@ static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
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;
+ if (saved_errno != EINVAL) {
+ retval = PAM_SESSION_ERR;
+ goto out;
+ }
} else if (idata->flags & PAMNS_DEBUG)
pam_syslog(idata->pamh, LOG_DEBUG, "Umount succeeded %s",
pptr->dir);
@@ -1186,7 +1297,9 @@ static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
break;
}
}
-
+out:
+ if (retval != PAM_SUCCESS)
+ cleanup_tmpdirs(idata);
return retval;
}
@@ -1225,8 +1338,10 @@ static int orig_namespace(struct instance_data *idata)
} else if (idata->flags & PAMNS_DEBUG)
pam_syslog(idata->pamh, LOG_DEBUG, "Unmount of %s succeeded",
pptr->dir);
- }
+ }
}
+
+ cleanup_tmpdirs(idata);
return 0;
}
@@ -1351,7 +1466,8 @@ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
} else if (idata.flags & PAMNS_DEBUG)
pam_syslog(idata.pamh, LOG_DEBUG, "Nothing to polyinstantiate");
- del_polydir_list(idata.polydirs_ptr);
+ if (retval != PAM_SUCCESS)
+ del_polydir_list(idata.polydirs_ptr);
return retval;
}
@@ -1366,6 +1482,7 @@ PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
struct instance_data idata;
char *user_name;
struct passwd *pwd;
+ void *polyptr;
/* init instance data */
idata.flags = 0;
@@ -1429,16 +1546,12 @@ PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
strncat(idata.user, user_name, sizeof(idata.user) - 1);
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;
- }
+ retval = pam_get_data(idata.pamh, NAMESPACE_POLYDIR_DATA, (const void **)&polyptr);
+ if (retval != PAM_SUCCESS || polyptr == NULL)
+ /* nothing to reset */
+ return PAM_SUCCESS;
+
+ idata.polydirs_ptr = polyptr;
if (idata.flags & PAMNS_DEBUG)
pam_syslog(idata.pamh, LOG_DEBUG, "Resetting namespace for pid %d",
@@ -1453,7 +1566,9 @@ PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED,
pam_syslog(idata.pamh, LOG_DEBUG,
"resetting namespace ok for pid %d", getpid());
}
- del_polydir_list(idata.polydirs_ptr);
+
+ pam_set_data(idata.pamh, NAMESPACE_POLYDIR_DATA, NULL, NULL);
+
return PAM_SUCCESS;
}
diff --git a/modules/pam_namespace/pam_namespace.h b/modules/pam_namespace/pam_namespace.h
index 0847ec08..4b438899 100644
--- a/modules/pam_namespace/pam_namespace.h
+++ b/modules/pam_namespace/pam_namespace.h
@@ -90,6 +90,7 @@
#define PAMNS_NO_UNMOUNT_ON_CLOSE 0x00010000 /* no unmount at session close */
#define NAMESPACE_MAX_DIR_LEN 80
+#define NAMESPACE_POLYDIR_DATA "pam_namespace:polydir_data"
/*
* Polyinstantiation method options, based on user, security context
@@ -100,6 +101,8 @@ enum polymethod {
USER,
CONTEXT,
LEVEL,
+ TMPDIR,
+ TMPFS
};
/*
@@ -128,6 +131,7 @@ struct polydir_s {
enum polymethod method; /* method used to polyinstantiate */
unsigned int num_uids; /* number of override uids */
uid_t *uid; /* list of override uids */
+ int exclusive; /* polyinstatiate exclusively for override uids */
struct polydir_s *next; /* pointer to the next polydir entry */
};