aboutsummaryrefslogtreecommitdiff
path: root/modules/pam_namespace/pam_namespace.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/pam_namespace/pam_namespace.c')
-rw-r--r--modules/pam_namespace/pam_namespace.c1337
1 files changed, 0 insertions, 1337 deletions
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
deleted file mode 100644
index e4e4a5d8..00000000
--- a/modules/pam_namespace/pam_namespace.c
+++ /dev/null
@@ -1,1337 +0,0 @@
-/******************************************************************************
- * A module for Linux-PAM that will set the default namespace after
- * establishing a session via PAM.
- *
- * (C) Copyright IBM Corporation 2005
- * (C) Copyright Red Hat 2006
- * All Rights Reserved.
- *
- * Written by: Janak Desai <janak@us.ibm.com>
- * With Revisions by: Steve Grubb <sgrubb@redhat.com>
- * Derived from a namespace setup patch by Chad Sellers <cdselle@tycho.nsa.gov>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * on the rights to use, copy, modify, merge, publish, distribute, sub
- * license, and/or sell copies of the Software, and to permit persons to whom
- * the Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#include "pam_namespace.h"
-
-/*
- * Copies the contents of ent into pent
- */
-static int copy_ent(const struct polydir_s *ent, struct polydir_s *pent)
-{
- unsigned int i;
-
- 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)) {
- return -1;
- }
- for (i = 0, pptr = pent->uid, eptr = ent->uid; i < ent->num_uids;
- i++, eptr++, pptr++)
- *pptr = *eptr;
- } else
- pent->uid = NULL;
- return 0;
-}
-
-/*
- * 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;
- int rc = 0;
-
- /*
- * 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) {
- rc = -1;
- goto out;
- }
- /* Make copy */
- rc = copy_ent(ent,pent);
- if(rc < 0)
- goto out_clean;
-
- /* 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;
- }
- goto out;
-out_clean:
- free(pent);
-out:
- return rc;
-}
-
-
-/*
- * 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 = pam_modutil_getpwnam(idata->pamh, 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 = pam_modutil_getpwnam(idata->pamh, 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;
-}
-
-#ifdef WITH_SELINUX
-static int form_context(const struct polydir_s *polyptr,
- security_context_t *i_context, security_context_t *origcon,
- struct instance_data *idata)
-{
- int rc = PAM_SUCCESS;
- security_context_t scon = NULL;
- security_class_t tclass;
-
- /*
- * 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);
- }
- return PAM_SUCCESS;
-}
-#endif
-
-/*
- * 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
-{
- int rc;
-
-# ifdef WITH_SELINUX
- rc = form_context(polyptr, i_context, origcon, idata);
-#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;
-}
-
-static int check_inst_parent(char *ipath, struct instance_data *idata)
-{
- struct stat instpbuf;
- char *inst_parent, *trailing_slash;
- /*
- * 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);
- return PAM_SUCCESS;
-}
-
-/*
-* 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.
-*/
-static int inst_init(const struct polydir_s *polyptr, char *ipath,
- struct instance_data *idata)
-{
- 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;
- }
-
- 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");
- rc = PAM_SESSION_ERR;
- goto out;
- } 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");
- rc = PAM_SESSION_ERR;
- goto out;
- }
- if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) {
- pam_syslog(idata->pamh, LOG_ERR,
- "Error initializing instance");
- rc = PAM_SESSION_ERR;
- goto out;
- }
- } 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:
- (void) signal(SIGCHLD, osighand);
-
- 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;
- int rc, fd;
-
- /*
- * stat the directory to polyinstantiate, so its owner-group-mode
- * can be propagated to instance directory
- */
- rc = PAM_SUCCESS;
- 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;
- }
-
- /*
- * Check to make sure instance parent is valid.
- */
- if (check_inst_parent(ipath, idata))
- return PAM_SESSION_ERR;
-
- /*
- * 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:
- rc = inst_init(polyptr, ipath, idata);
- return rc;
-}
-
-
-/*
- * 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 = pam_modutil_getpwnam(idata.pamh, 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) - 1);
- 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 = pam_modutil_getpwnam(idata.pamh, 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) - 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;
- }
-
- 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