From 7d8ba5e5958cb2bb25e58d6dbe39e2762a9a5241 Mon Sep 17 00:00:00 2001 From: Thorsten Kukuk Date: Fri, 28 Jul 2006 11:59:28 +0000 Subject: Relevant BUGIDs: Purpose of commit: cleanup Commit summary: --------------- 2006-07-24 David Quigley * modules/pam_namespace/Makefile.am: Add pam_namespace.h. * modules/pam_namespace/pam_namespace.c: Move includes and data structure definitions from here ... * modules/pam_namespace/pam_namespace.h: ... here. New file. * modules/pam_namespace/pam_namespace.c: Move large sections of code into new functions. Acked by Janak Desai --- ChangeLog | 10 + modules/pam_namespace/Makefile.am | 2 +- modules/pam_namespace/pam_namespace.c | 474 +++++++++++++++------------------- modules/pam_namespace/pam_namespace.h | 135 ++++++++++ 4 files changed, 359 insertions(+), 262 deletions(-) create mode 100644 modules/pam_namespace/pam_namespace.h diff --git a/ChangeLog b/ChangeLog index df745d43..e4adc8c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2006-07-24 David Quigley + + * modules/pam_namespace/Makefile.am: Add pam_namespace.h. + * modules/pam_namespace/pam_namespace.c: Move includes and + data structure definitions from here ... + * modules/pam_namespace/pam_namespace.h: ... here. New file. + + * modules/pam_namespace/pam_namespace.c: Move large sections + of code into new functions. + 2006-07-24 Thorsten Kukuk * doc/adg/Makefile.am: Add uninstall and distclean rules. diff --git a/modules/pam_namespace/Makefile.am b/modules/pam_namespace/Makefile.am index fd7d4e2a..9c7c2f73 100644 --- a/modules/pam_namespace/Makefile.am +++ b/modules/pam_namespace/Makefile.am @@ -32,7 +32,7 @@ endif if HAVE_UNSHARE securelib_LTLIBRARIES = pam_namespace.la -pam_namespace_la_SOURCES = pam_namespace.c md5.c md5.h +pam_namespace_la_SOURCES = pam_namespace.c pam_namespace.h md5.c md5.h secureconf_DATA = namespace.conf secureconf_SCRIPTS = namespace.init diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c index cd6ef07c..4e777be3 100644 --- a/modules/pam_namespace/pam_namespace.c +++ b/modules/pam_namespace/pam_namespace.c @@ -30,109 +30,33 @@ * 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, -}; +#include "pam_namespace.h" /* - * Structure that holds information about a directory to polyinstantiate + * Copies the contents of ent into pent */ -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 */ -}; +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 @@ -143,7 +67,7 @@ static int add_polydir_entry(struct instance_data *idata, const struct polydir_s *ent) { struct polydir_s *pent; - unsigned int i; + int rc = 0; /* * Allocate an entry to hold information about a directory to @@ -152,27 +76,14 @@ static int add_polydir_entry(struct instance_data *idata, * directories. */ pent = (struct polydir_s *) malloc(sizeof(struct polydir_s)); - if (!pent) - return -1; - + if (!pent) { + rc = -1; + goto out; + } /* 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; + rc = copy_ent(ent,pent); + if(rc < 0) + goto out_clean; /* Now attach to linked list */ pent->next = NULL; @@ -186,8 +97,11 @@ static int add_polydir_entry(struct instance_data *idata, tail = tail->next; tail->next = pent; } - - return 0; + goto out; +out_clean: + free(pent); +out: + return rc; } @@ -515,6 +429,54 @@ static int ns_override(struct polydir_s *polyptr, struct instance_data *idata) 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 @@ -532,49 +494,10 @@ 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); - } + rc = form_context(polyptr, i_context, origcon, idata); #endif rc = PAM_SUCCESS; @@ -618,6 +541,122 @@ static int poly_name(const struct polydir_s *polyptr, char **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). @@ -631,16 +670,14 @@ 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; + 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); @@ -655,49 +692,12 @@ static int create_dirs(const struct polydir_s *polyptr, char *ipath, 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); + + /* + * 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 @@ -779,56 +779,8 @@ static int create_dirs(const struct polydir_s *polyptr, char *ipath, */ 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; + rc = inst_init(polyptr, ipath, idata); + return rc; } diff --git a/modules/pam_namespace/pam_namespace.h b/modules/pam_namespace/pam_namespace.h new file mode 100644 index 00000000..c918cff3 --- /dev/null +++ b/modules/pam_namespace/pam_namespace.h @@ -0,0 +1,135 @@ +/****************************************************************************** + * 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 */ +}; -- cgit v1.2.3