From 2535f925c1a6049e5ad9ee4f313bcaa79131932b Mon Sep 17 00:00:00 2001 From: Tomas Mraz Date: Fri, 1 Feb 2008 16:22:23 +0000 Subject: Relevant BUGIDs: Purpose of commit: new feature Commit summary: --------------- 2008-02-01 Tomas Mraz * 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. --- modules/pam_namespace/namespace.conf.5.xml | 12 +- modules/pam_namespace/namespace.init | 40 +++--- modules/pam_namespace/pam_namespace.8.xml | 4 +- modules/pam_namespace/pam_namespace.c | 211 ++++++++++++++++++++++------- modules/pam_namespace/pam_namespace.h | 4 + 5 files changed, 198 insertions(+), 73 deletions(-) (limited to 'modules/pam_namespace') 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 @@ The third field, method, 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. @@ -84,7 +87,8 @@ 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. + for all users. If the list is preceded with a single "~" character, + polyinstantiation is performed only for users in the list. 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 /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. + 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. 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 @@ -222,18 +228,6 @@ static int process_line(char *line, const char *home, 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. @@ -241,6 +235,7 @@ static int process_line(char *line, const char *home, 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,11 +273,23 @@ 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 @@ -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 */ }; -- cgit v1.2.3