diff options
Diffstat (limited to 'modules/pam_namespace/pam_namespace.c')
-rw-r--r-- | modules/pam_namespace/pam_namespace.c | 217 |
1 files changed, 166 insertions, 51 deletions
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c index 63b5c665..f34ce934 100644 --- a/modules/pam_namespace/pam_namespace.c +++ b/modules/pam_namespace/pam_namespace.c @@ -39,6 +39,94 @@ #include "pam_namespace.h" #include "argv_parse.h" +/* --- evaluting all files in VENDORDIR/security/namespace.d and /etc/security/namespace.d --- */ +static const char *base_name(const char *path) +{ + const char *base = strrchr(path, '/'); + return base ? base+1 : path; +} + +static int +compare_filename(const void *a, const void *b) +{ + return strcmp(base_name(* (char * const *) a), + base_name(* (char * const *) b)); +} + +/* Evaluating a list of files which have to be parsed in the right order: + * + * - If etc/security/namespace.d/@filename@.conf exists, then + * %vendordir%/security/namespace.d/@filename@.conf should not be used. + * - All files in both namespace.d directories are sorted by their @filename@.conf in + * lexicographic order regardless of which of the directories they reside in. */ +static char **read_namespace_dir(struct instance_data *idata) +{ + glob_t globbuf; + size_t i=0; + int glob_rv = glob(NAMESPACE_D_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); + char **file_list; + size_t file_list_size = glob_rv == 0 ? globbuf.gl_pathc : 0; + +#ifdef VENDOR_NAMESPACE_D_GLOB + glob_t globbuf_vendor; + int glob_rv_vendor = glob(VENDOR_NAMESPACE_D_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf_vendor); + if (glob_rv_vendor == 0) + file_list_size += globbuf_vendor.gl_pathc; +#endif + file_list = malloc((file_list_size + 1) * sizeof(char*)); + if (file_list == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "Cannot allocate memory for file list: %m"); +#ifdef VENDOR_NAMESPACE_D_GLOB + if (glob_rv_vendor == 0) + globfree(&globbuf_vendor); +#endif + if (glob_rv == 0) + globfree(&globbuf); + return NULL; + } + + if (glob_rv == 0) { + for (i = 0; i < globbuf.gl_pathc; i++) { + file_list[i] = strdup(globbuf.gl_pathv[i]); + if (file_list[i] == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "strdup failed: %m"); + break; + } + } + } +#ifdef VENDOR_NAMESPACE_D_GLOB + if (glob_rv_vendor == 0) { + for (size_t j = 0; j < globbuf_vendor.gl_pathc; j++) { + if (glob_rv == 0 && globbuf.gl_pathc > 0) { + int double_found = 0; + for (size_t k = 0; k < globbuf.gl_pathc; k++) { + if (strcmp(base_name(globbuf.gl_pathv[k]), + base_name(globbuf_vendor.gl_pathv[j])) == 0) { + double_found = 1; + break; + } + } + if (double_found) + continue; + } + file_list[i] = strdup(globbuf_vendor.gl_pathv[j]); + if (file_list[i] == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "strdup failed: %m"); + break; + } + i++; + } + globfree(&globbuf_vendor); + } +#endif + file_list[i] = NULL; + qsort(file_list, i, sizeof(char *), compare_filename); + if (glob_rv == 0) + globfree(&globbuf); + + return file_list; +} + /* * Adds an entry for a polyinstantiated directory to the linked list of * polyinstantiated directories. It is called from process_line() while @@ -624,8 +712,6 @@ static int parse_config_file(struct instance_data *idata) char *line; int retval; size_t len = 0; - glob_t globbuf; - const char *oldlocale; size_t n; /* @@ -664,13 +750,16 @@ static int parse_config_file(struct instance_data *idata) * process_line to process each line. */ - memset(&globbuf, '\0', sizeof(globbuf)); - oldlocale = setlocale(LC_COLLATE, "C"); - glob(NAMESPACE_D_GLOB, 0, NULL, &globbuf); - if (oldlocale != NULL) - setlocale(LC_COLLATE, oldlocale); - confname = PAM_NAMESPACE_CONFIG; +#ifdef VENDOR_PAM_NAMESPACE_CONFIG + /* Check whether PAM_NAMESPACE_CONFIG file is available. + * If it does not exist, fall back to VENDOR_PAM_NAMESPACE_CONFIG file. */ + struct stat buffer; + if (stat(confname, &buffer) != 0 && errno == ENOENT) { + confname = VENDOR_PAM_NAMESPACE_CONFIG; + } +#endif + char **filename_list = read_namespace_dir(idata); n = 0; for (;;) { if (idata->flags & PAMNS_DEBUG) @@ -680,7 +769,6 @@ static int parse_config_file(struct instance_data *idata) if (fil == NULL) { pam_syslog(idata->pamh, LOG_ERR, "Error opening config file %s", confname); - globfree(&globbuf); free(rhome); free(home); return PAM_SERVICE_ERR; @@ -698,7 +786,6 @@ static int parse_config_file(struct instance_data *idata) "Error processing conf file %s line %s", confname, line); fclose(fil); free(line); - globfree(&globbuf); free(rhome); free(home); return PAM_SERVICE_ERR; @@ -707,14 +794,18 @@ static int parse_config_file(struct instance_data *idata) fclose(fil); free(line); - if (n >= globbuf.gl_pathc) + if (filename_list == NULL || filename_list[n] == NULL) break; - confname = globbuf.gl_pathv[n]; - n++; + confname = filename_list[n++]; + } + + if (filename_list != NULL) { + for (size_t i = 0; filename_list[i] != NULL; i++) + free(filename_list[i]); + free(filename_list); } - globfree(&globbuf); free(rhome); free(home); @@ -797,11 +888,11 @@ static char *md5hash(const char *instname, struct instance_data *idata) #ifdef WITH_SELINUX static int form_context(const struct polydir_s *polyptr, - security_context_t *i_context, security_context_t *origcon, + char **i_context, char **origcon, struct instance_data *idata) { int rc = PAM_SUCCESS; - security_context_t scon = NULL; + char *scon = NULL; security_class_t tclass; /* @@ -844,6 +935,12 @@ static int form_context(const struct polydir_s *polyptr, if (polyptr->method == CONTEXT) { tclass = string_to_security_class("dir"); + if (tclass == 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Error getting dir security class"); + freecon(scon); + return PAM_SESSION_ERR; + } if (security_compute_member(scon, *origcon, tclass, i_context) < 0) { @@ -910,7 +1007,7 @@ static int form_context(const struct polydir_s *polyptr, */ #ifdef WITH_SELINUX static int poly_name(const struct polydir_s *polyptr, char **i_name, - security_context_t *i_context, security_context_t *origcon, + char **i_context, char **origcon, struct instance_data *idata) #else static int poly_name(const struct polydir_s *polyptr, char **i_name, @@ -921,7 +1018,7 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name, char *hash = NULL; enum polymethod pm; #ifdef WITH_SELINUX - security_context_t rawcon = NULL; + char *rawcon = NULL; #endif *i_name = NULL; @@ -1244,16 +1341,17 @@ static int inst_init(const struct polydir_s *polyptr, const char *ipath, struct instance_data *idata, int newdir) { pid_t rc, pid; - struct sigaction newsa, oldsa; int status; const char *init_script = NAMESPACE_INIT_SCRIPT; - memset(&newsa, '\0', sizeof(newsa)); - newsa.sa_handler = SIG_DFL; - if (sigaction(SIGCHLD, &newsa, &oldsa) == -1) { - pam_syslog(idata->pamh, LOG_ERR, "Cannot set signal value"); - return PAM_SESSION_ERR; +#ifdef VENDOR_NAMESPACE_INIT_SCRIPT + /* Check whether NAMESPACE_INIT_SCRIPT file is available. + * If it does not exist, fall back to VENDOR_NAMESPACE_INIT_SCRIPT file. */ + struct stat buffer; + if (stat(init_script, &buffer) != 0 && errno == ENOENT) { + init_script = VENDOR_NAMESPACE_INIT_SCRIPT; } +#endif if ((polyptr->flags & POLYDIR_ISCRIPT) && polyptr->init_script) init_script = polyptr->init_script; @@ -1263,9 +1361,17 @@ static int inst_init(const struct polydir_s *polyptr, const char *ipath, if (idata->flags & PAMNS_DEBUG) pam_syslog(idata->pamh, LOG_ERR, "Namespace init script not executable"); - rc = PAM_SESSION_ERR; - goto out; + return PAM_SESSION_ERR; } else { + struct sigaction newsa, oldsa; + + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + if (sigaction(SIGCHLD, &newsa, &oldsa) == -1) { + pam_syslog(idata->pamh, LOG_ERR, "failed to reset SIGCHLD handler"); + return PAM_SESSION_ERR; + } + pid = fork(); if (pid == 0) { static char *envp[] = { NULL }; @@ -1303,13 +1409,13 @@ static int inst_init(const struct polydir_s *polyptr, const char *ipath, rc = PAM_SESSION_ERR; goto out; } + rc = PAM_SUCCESS; +out: + (void) sigaction(SIGCHLD, &oldsa, NULL); + return rc; } } - rc = PAM_SUCCESS; -out: - (void) sigaction(SIGCHLD, &oldsa, NULL); - - return rc; + return PAM_SUCCESS; } static int create_polydir(struct polydir_s *polyptr, @@ -1318,7 +1424,8 @@ static int create_polydir(struct polydir_s *polyptr, mode_t mode; int rc; #ifdef WITH_SELINUX - security_context_t dircon, oldcon = NULL; + char *dircon_raw, *oldcon_raw = NULL; + struct selabel_handle *label_handle; #endif const char *dir = polyptr->dir; uid_t uid; @@ -1331,21 +1438,28 @@ static int create_polydir(struct polydir_s *polyptr, #ifdef WITH_SELINUX if (idata->flags & PAMNS_SELINUX_ENABLED) { - getfscreatecon(&oldcon); - rc = matchpathcon(dir, S_IFDIR, &dircon); - if (rc) { - pam_syslog(idata->pamh, LOG_NOTICE, - "Unable to get default context for directory %s, check your policy: %m", dir); - } else { - if (idata->flags & PAMNS_DEBUG) - pam_syslog(idata->pamh, LOG_DEBUG, - "Polydir %s context: %s", dir, (char *)dircon); - if (setfscreatecon(dircon) != 0) + getfscreatecon_raw(&oldcon_raw); + + label_handle = selabel_open(SELABEL_CTX_FILE, NULL, 0); + if (!label_handle) { + pam_syslog(idata->pamh, LOG_NOTICE, + "Unable to initialize SELinux labeling handle: %m"); + } else { + rc = selabel_lookup_raw(label_handle, &dircon_raw, dir, S_IFDIR); + if (rc) { pam_syslog(idata->pamh, LOG_NOTICE, - "Error setting context for directory %s: %m", dir); - freecon(dircon); - } - matchpathcon_fini(); + "Unable to get default context for directory %s, check your policy: %m", dir); + } else { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "Polydir %s context: %s", dir, dircon_raw); + if (setfscreatecon_raw(dircon_raw) != 0) + pam_syslog(idata->pamh, LOG_NOTICE, + "Error setting context for directory %s: %m", dir); + freecon(dircon_raw); + } + selabel_close(label_handle); + } } #endif @@ -1358,10 +1472,10 @@ static int create_polydir(struct polydir_s *polyptr, #ifdef WITH_SELINUX if (idata->flags & PAMNS_SELINUX_ENABLED) { - if (setfscreatecon(oldcon) != 0) + if (setfscreatecon_raw(oldcon_raw) != 0) pam_syslog(idata->pamh, LOG_NOTICE, "Error resetting fs create context: %m"); - freecon(oldcon); + freecon(oldcon_raw); } #endif @@ -1413,7 +1527,7 @@ static int create_polydir(struct polydir_s *polyptr, */ #ifdef WITH_SELINUX static int create_instance(struct polydir_s *polyptr, char *ipath, struct stat *statbuf, - security_context_t icontext, security_context_t ocontext, + const char *icontext, const char *ocontext, struct instance_data *idata) #else static int create_instance(struct polydir_s *polyptr, char *ipath, struct stat *statbuf, @@ -1488,6 +1602,7 @@ static int create_instance(struct polydir_s *polyptr, char *ipath, struct stat * if (fstat(fd, &newstatbuf) < 0) { pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m", ipath); + close(fd); rmdir(ipath); return PAM_SESSION_ERR; } @@ -1530,7 +1645,7 @@ static int ns_setup(struct polydir_s *polyptr, char *instname = NULL; struct stat statbuf; #ifdef WITH_SELINUX - security_context_t instcontext = NULL, origcontext = NULL; + char *instcontext = NULL, *origcontext = NULL; #endif if (idata->flags & PAMNS_DEBUG) @@ -1965,7 +2080,7 @@ static int orig_namespace(struct instance_data *idata) */ static int ctxt_based_inst_needed(void) { - security_context_t scon = NULL; + char *scon = NULL; int rc = 0; rc = getexeccon(&scon); |