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.c217
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);