pam/SOURCES/pam-1.3.1-pam-namespace-rebase.patch

1157 lines
34 KiB
Diff

diff -up Linux-PAM-1.3.1/modules/pam_namespace/namespace.init.pam-namespace-rebase Linux-PAM-1.3.1/modules/pam_namespace/namespace.init
--- Linux-PAM-1.3.1/modules/pam_namespace/namespace.init.pam-namespace-rebase 2017-02-10 11:10:15.000000000 +0100
+++ Linux-PAM-1.3.1/modules/pam_namespace/namespace.init 2025-06-17 12:50:04.962542096 +0200
@@ -15,8 +15,8 @@ if [ "$3" = 1 ]; then
gid=$(echo "$passwd" | cut -f4 -d":")
cp -rT /etc/skel "$homedir"
chown -R "$user":"$gid" "$homedir"
- mask=$(awk '/^UMASK/{gsub("#.*$", "", $2); print $2; exit}' /etc/login.defs)
- mode=$(printf "%o" $((0777 & ~$mask)))
+ mask=$(sed -E -n 's/^UMASK[[:space:]]+([^#[:space:]]+).*/\1/p' /etc/login.defs)
+ mode=$(printf "%o" $((0777 & ~mask)))
chmod ${mode:-700} "$homedir"
[ -x /sbin/restorecon ] && /sbin/restorecon -R "$homedir"
fi
diff -up Linux-PAM-1.3.1/modules/pam_namespace/pam_namespace.c.pam-namespace-rebase Linux-PAM-1.3.1/modules/pam_namespace/pam_namespace.c
--- Linux-PAM-1.3.1/modules/pam_namespace/pam_namespace.c.pam-namespace-rebase 2025-06-17 12:50:04.954195973 +0200
+++ Linux-PAM-1.3.1/modules/pam_namespace/pam_namespace.c 2025-06-17 12:51:08.445616548 +0200
@@ -34,9 +34,250 @@
#define _ATFILE_SOURCE
+#include "config.h"
+#include <stdint.h>
+#include "pam_cc_compat.h"
+#include "pam_inline.h"
#include "pam_namespace.h"
#include "argv_parse.h"
+/* --- evaluating 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));
+}
+
+static void close_fds_pre_exec(struct instance_data *idata)
+{
+ if (pam_modutil_sanitize_helper_fds(idata->pamh, PAM_MODUTIL_IGNORE_FD,
+ PAM_MODUTIL_IGNORE_FD, PAM_MODUTIL_IGNORE_FD) < 0) {
+ _exit(1);
+ }
+}
+
+static void
+strip_trailing_slashes(char *str)
+{
+ char *p = str + strlen(str);
+
+ while (--p > str && *p == '/')
+ *p = '\0';
+}
+
+static int protect_mount(int dfd, const char *path, struct instance_data *idata)
+{
+ struct protect_dir_s *dir = idata->protect_dirs;
+ char tmpbuf[64];
+
+ while (dir != NULL) {
+ if (strcmp(path, dir->dir) == 0) {
+ return 0;
+ }
+ dir = dir->next;
+ }
+
+ if (pam_sprintf(tmpbuf, "/proc/self/fd/%d", dfd) < 0)
+ return -1;
+
+ dir = calloc(1, sizeof(*dir));
+
+ if (dir == NULL) {
+ return -1;
+ }
+
+ dir->dir = strdup(path);
+
+ if (dir->dir == NULL) {
+ free(dir);
+ return -1;
+ }
+
+ if (idata->flags & PAMNS_DEBUG) {
+ pam_syslog(idata->pamh, LOG_INFO,
+ "Protect mount of %s over itself", path);
+ }
+
+ if (mount(tmpbuf, tmpbuf, NULL, MS_BIND, NULL) != 0) {
+ int save_errno = errno;
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Protect mount of %s failed: %m", tmpbuf);
+ free(dir->dir);
+ free(dir);
+ errno = save_errno;
+ return -1;
+ }
+
+ dir->next = idata->protect_dirs;
+ idata->protect_dirs = dir;
+
+ return 0;
+}
+
+static int protect_dir(const char *path, mode_t mode, int do_mkdir,
+ struct instance_data *idata)
+{
+ char *p = strdup(path);
+ char *d;
+ char *dir = p;
+ int dfd = AT_FDCWD;
+ int dfd_next;
+ int save_errno;
+ int flags = O_RDONLY | O_DIRECTORY;
+ int rv = -1;
+ struct stat st;
+
+ if (p == NULL) {
+ return -1;
+ }
+
+ if (*dir == '/') {
+ dfd = open("/", flags);
+ if (dfd == -1) {
+ goto error;
+ }
+ dir++; /* assume / is safe */
+ }
+
+ while ((d=strchr(dir, '/')) != NULL) {
+ *d = '\0';
+ dfd_next = openat(dfd, dir, flags);
+ if (dfd_next == -1) {
+ goto error;
+ }
+
+ if (dfd != AT_FDCWD)
+ close(dfd);
+ dfd = dfd_next;
+
+ if (fstat(dfd, &st) != 0) {
+ goto error;
+ }
+
+ if (flags & O_NOFOLLOW) {
+ /* we are inside user-owned dir - protect */
+ if (protect_mount(dfd, p, idata) == -1)
+ goto error;
+ } else if (st.st_uid != 0 || st.st_gid != 0 ||
+ (st.st_mode & S_IWOTH)) {
+ /* do not follow symlinks on subdirectories */
+ flags |= O_NOFOLLOW;
+ }
+
+ *d = '/';
+ dir = d + 1;
+ }
+
+ rv = openat(dfd, dir, flags);
+
+ if (rv == -1) {
+ if (!do_mkdir || mkdirat(dfd, dir, mode) != 0) {
+ goto error;
+ }
+ rv = openat(dfd, dir, flags);
+ }
+
+ if (flags & O_NOFOLLOW) {
+ /* we are inside user-owned dir - protect */
+ if (protect_mount(rv, p, idata) == -1) {
+ save_errno = errno;
+ close(rv);
+ rv = -1;
+ errno = save_errno;
+ }
+ }
+
+error:
+ save_errno = errno;
+ free(p);
+ if (dfd != AT_FDCWD && dfd >= 0)
+ close(dfd);
+ errno = save_errno;
+
+ return rv;
+}
+
+/* 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
@@ -106,7 +347,7 @@ static void cleanup_protect_data(pam_han
unprotect_dirs(data);
}
-static char *expand_variables(const char *orig, const char *var_names[], const char *var_values[])
+static char *expand_variables(const char *orig, const char *const var_names[], const char *var_values[])
{
const char *src = orig;
char *dst;
@@ -117,7 +358,7 @@ static char *expand_variables(const char
if (*src == '$') {
int i;
for (i = 0; var_names[i]; i++) {
- int namelen = strlen(var_names[i]);
+ size_t namelen = strlen(var_names[i]);
if (strncmp(var_names[i], src+1, namelen) == 0) {
dstlen += strlen(var_values[i]) - 1; /* $ */
src += namelen;
@@ -135,7 +376,7 @@ static char *expand_variables(const char
if (c == '$') {
int i;
for (i = 0; var_names[i]; i++) {
- int namelen = strlen(var_names[i]);
+ size_t namelen = strlen(var_names[i]);
if (strncmp(var_names[i], src+1, namelen) == 0) {
dst = stpcpy(dst, var_values[i]);
--dst;
@@ -219,8 +460,7 @@ static int parse_iscript_params(char *pa
if (*params != '\0') {
if (*params != '/') { /* path is relative to NAMESPACE_D_DIR */
- if (asprintf(&poly->init_script, "%s%s", NAMESPACE_D_DIR, params) == -1)
- return -1;
+ poly->init_script = pam_asprintf("%s%s", NAMESPACE_D_DIR, params);
} else {
poly->init_script = strdup(params);
}
@@ -259,7 +499,7 @@ static int filter_mntopts(const char *op
do {
size_t len;
- int i;
+ unsigned int i;
end = strchr(opts, ',');
if (end == NULL) {
@@ -268,7 +508,7 @@ static int filter_mntopts(const char *op
len = end - opts;
}
- for (i = 0; i < (int)(sizeof(mntflags)/sizeof(mntflags[0])); i++) {
+ for (i = 0; i < PAM_ARRAY_SIZE(mntflags); i++) {
if (mntflags[i].len != len)
continue;
if (memcmp(mntflags[i].name, opts, len) == 0) {
@@ -302,9 +542,9 @@ static int parse_method(char *method, st
{
enum polymethod pm;
char *sptr = NULL;
- static const char *method_names[] = { "user", "context", "level", "tmpdir",
+ static const char *const method_names[] = { "user", "context", "level", "tmpdir",
"tmpfs", NULL };
- static const char *flag_names[] = { "create", "noinit", "iscript",
+ static const char *const flag_names[] = { "create", "noinit", "iscript",
"shared", "mntopts", NULL };
static const unsigned int flag_values[] = { POLYDIR_CREATE, POLYDIR_NOINIT,
POLYDIR_ISCRIPT, POLYDIR_SHARED, POLYDIR_MNTOPTS };
@@ -329,7 +569,7 @@ static int parse_method(char *method, st
while ((flag=strtok_r(NULL, ":", &sptr)) != NULL) {
for (i = 0; flag_names[i]; i++) {
- int namelen = strlen(flag_names[i]);
+ size_t namelen = strlen(flag_names[i]);
if (strncmp(flag, flag_names[i], namelen) == 0) {
poly->flags |= flag_values[i];
@@ -375,27 +615,27 @@ static int parse_method(char *method, st
* 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
+ * polyinstantiated 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, const char *rhome,
struct instance_data *idata)
{
char *dir = NULL, *instance_prefix = NULL, *rdir = NULL;
+ const char *config_dir, *config_instance_prefix;
char *method, *uids;
char *tptr;
struct polydir_s *poly;
int retval = 0;
char **config_options = NULL;
- static const char *var_names[] = {"HOME", "USER", NULL};
+ static const char *const var_names[] = {"HOME", "USER", NULL};
const char *var_values[] = {home, idata->user};
const char *rvar_values[] = {rhome, idata->ruser};
- int len;
/*
* skip the leading white space
*/
- while (*line && isspace(*line))
+ while (*line && isspace((unsigned char)*line))
line++;
/*
@@ -431,22 +671,19 @@ static int process_line(char *line, cons
goto erralloc;
}
- dir = config_options[0];
- if (dir == NULL) {
+ config_dir = config_options[0];
+ if (config_dir == NULL) {
pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing polydir");
goto skipping;
}
- instance_prefix = config_options[1];
- if (instance_prefix == NULL) {
+ config_instance_prefix = config_options[1];
+ if (config_instance_prefix == NULL) {
pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing instance_prefix");
- instance_prefix = NULL;
goto skipping;
}
method = config_options[2];
if (method == NULL) {
pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing method");
- instance_prefix = NULL;
- dir = NULL;
goto skipping;
}
@@ -461,19 +698,16 @@ static int process_line(char *line, cons
/*
* Expand $HOME and $USER in poly dir and instance dir prefix
*/
- if ((rdir=expand_variables(dir, var_names, rvar_values)) == NULL) {
- instance_prefix = NULL;
- dir = NULL;
+ if ((rdir = expand_variables(config_dir, var_names, rvar_values)) == NULL) {
goto erralloc;
}
- if ((dir=expand_variables(dir, var_names, var_values)) == NULL) {
- instance_prefix = NULL;
+ if ((dir = expand_variables(config_dir, var_names, var_values)) == NULL) {
goto erralloc;
}
- if ((instance_prefix=expand_variables(instance_prefix, var_names, var_values))
- == NULL) {
+ if ((instance_prefix = expand_variables(config_instance_prefix,
+ var_names, var_values)) == NULL) {
goto erralloc;
}
@@ -483,15 +717,8 @@ static int process_line(char *line, cons
pam_syslog(idata->pamh, LOG_DEBUG, "Expanded instance prefix: '%s'", instance_prefix);
}
- len = strlen(dir);
- if (len > 0 && dir[len-1] == '/') {
- dir[len-1] = '\0';
- }
-
- len = strlen(rdir);
- if (len > 0 && rdir[len-1] == '/') {
- rdir[len-1] = '\0';
- }
+ strip_trailing_slashes(dir);
+ strip_trailing_slashes(rdir);
if (dir[0] == '\0' || rdir[0] == '\0') {
pam_syslog(idata->pamh, LOG_NOTICE, "Invalid polydir");
@@ -502,26 +729,19 @@ static int process_line(char *line, cons
* Populate polyinstantiated directory structure with appropriate
* pathnames and the method with which to polyinstantiate.
*/
- if (strlen(dir) >= sizeof(poly->dir)
- || strlen(rdir) >= sizeof(poly->rdir)
- || strlen(instance_prefix) >= sizeof(poly->instance_prefix)) {
- pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
- goto skipping;
- }
- strcpy(poly->dir, dir);
- strcpy(poly->rdir, rdir);
- strcpy(poly->instance_prefix, instance_prefix);
-
if (parse_method(method, poly, idata) != 0) {
goto skipping;
}
- if (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");
+#define COPY_STR(dst, src, apd) \
+ pam_sprintf((dst), "%s%s", (src), (apd))
+
+ if (COPY_STR(poly->dir, dir, "") < 0
+ || COPY_STR(poly->rdir, rdir, "") < 0
+ || COPY_STR(poly->instance_prefix, instance_prefix,
+ poly->method == TMPDIR ? "XXXXXX" : "") < 0) {
+ pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long");
+ goto skipping;
}
/*
@@ -545,7 +765,7 @@ static int process_line(char *line, cons
if (uids) {
uid_t *uidptr;
const char *ustr, *sstr;
- int count, i;
+ size_t count, i;
if (*uids == '~') {
poly->flags |= POLYDIR_EXCLUSIVE;
@@ -554,8 +774,13 @@ static int process_line(char *line, cons
for (count = 0, ustr = sstr = uids; sstr; ustr = sstr + 1, count++)
sstr = strchr(ustr, ',');
+ if (count > UINT_MAX || count > SIZE_MAX / sizeof(uid_t)) {
+ pam_syslog(idata->pamh, LOG_ERR, "Too many uids encountered in configuration");
+ goto skipping;
+ }
+
poly->num_uids = count;
- poly->uid = (uid_t *) malloc(count * sizeof (uid_t));
+ poly->uid = malloc(count * sizeof (uid_t));
uidptr = poly->uid;
if (uidptr == NULL) {
goto erralloc;
@@ -622,8 +847,6 @@ static int parse_config_file(struct inst
char *line;
int retval;
size_t len = 0;
- glob_t globbuf;
- const char *oldlocale;
size_t n;
/*
@@ -662,13 +885,16 @@ static int parse_config_file(struct inst
* 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)
@@ -678,7 +904,6 @@ static int parse_config_file(struct inst
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;
@@ -696,7 +921,6 @@ static int parse_config_file(struct inst
"Error processing conf file %s line %s", confname, line);
fclose(fil);
free(line);
- globfree(&globbuf);
free(rhome);
free(home);
return PAM_SERVICE_ERR;
@@ -705,14 +929,18 @@ static int parse_config_file(struct inst
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);
@@ -738,7 +966,7 @@ static int parse_config_file(struct inst
/*
- * This funtion returns true if a given uid is present in the polyinstantiated
+ * This function 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.
@@ -795,11 +1023,11 @@ static char *md5hash(const char *instnam
#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;
/*
@@ -842,6 +1070,12 @@ static int form_context(const struct pol
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) {
@@ -878,7 +1112,7 @@ static int form_context(const struct pol
goto fail;
}
if (context_range_set(fcontext, context_range_get(scontext)) != 0) {
- pam_syslog(idata->pamh, LOG_ERR, "Unable to set MLS Componant of context");
+ pam_syslog(idata->pamh, LOG_ERR, "Unable to set MLS Component of context");
goto fail;
}
*i_context=strdup(context_str(fcontext));
@@ -895,6 +1129,7 @@ static int form_context(const struct pol
return rc;
}
/* Should never get here */
+ freecon(scon);
return PAM_SUCCESS;
}
#endif
@@ -908,7 +1143,7 @@ static int form_context(const struct pol
*/
#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,
@@ -919,7 +1154,7 @@ static int poly_name(const struct polydi
char *hash = NULL;
enum polymethod pm;
#ifdef WITH_SELINUX
- security_context_t rawcon = NULL;
+ char *rawcon = NULL;
#endif
*i_name = NULL;
@@ -956,10 +1191,8 @@ static int poly_name(const struct polydi
switch (pm) {
case USER:
- if (asprintf(i_name, "%s", idata->user) < 0) {
- *i_name = NULL;
+ if ((*i_name = strdup(idata->user)) == NULL)
goto fail;
- }
break;
#ifdef WITH_SELINUX
@@ -969,17 +1202,12 @@ static int poly_name(const struct polydi
pam_syslog(idata->pamh, LOG_ERR, "Error translating directory context");
goto fail;
}
- if (polyptr->flags & POLYDIR_SHARED) {
- if (asprintf(i_name, "%s", rawcon) < 0) {
- *i_name = NULL;
- goto fail;
- }
- } else {
- if (asprintf(i_name, "%s_%s", rawcon, idata->user) < 0) {
- *i_name = NULL;
- goto fail;
- }
- }
+ if (polyptr->flags & POLYDIR_SHARED)
+ *i_name = strdup(rawcon);
+ else
+ *i_name = pam_asprintf("%s_%s", rawcon, idata->user);
+ if (*i_name == NULL)
+ goto fail;
break;
#endif /* WITH_SELINUX */
@@ -1009,11 +1237,12 @@ static int poly_name(const struct polydi
*i_name = hash;
hash = NULL;
} else {
- char *newname;
- if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-(int)strlen(hash),
- *i_name, hash) < 0) {
+ char *newname =
+ pam_asprintf("%.*s_%s",
+ NAMESPACE_MAX_DIR_LEN - 1 - (int)strlen(hash),
+ *i_name, hash);
+ if (newname == NULL)
goto fail;
- }
free(*i_name);
*i_name = newname;
}
@@ -1038,137 +1267,6 @@ fail:
return rc;
}
-static int protect_mount(int dfd, const char *path, struct instance_data *idata)
-{
- struct protect_dir_s *dir = idata->protect_dirs;
- char tmpbuf[64];
-
- while (dir != NULL) {
- if (strcmp(path, dir->dir) == 0) {
- return 0;
- }
- dir = dir->next;
- }
-
- dir = calloc(1, sizeof(*dir));
-
- if (dir == NULL) {
- return -1;
- }
-
- dir->dir = strdup(path);
-
- if (dir->dir == NULL) {
- free(dir);
- return -1;
- }
-
- snprintf(tmpbuf, sizeof(tmpbuf), "/proc/self/fd/%d", dfd);
-
- if (idata->flags & PAMNS_DEBUG) {
- pam_syslog(idata->pamh, LOG_INFO,
- "Protect mount of %s over itself", path);
- }
-
- if (mount(tmpbuf, tmpbuf, NULL, MS_BIND, NULL) != 0) {
- int save_errno = errno;
- pam_syslog(idata->pamh, LOG_ERR,
- "Protect mount of %s failed: %m", tmpbuf);
- free(dir->dir);
- free(dir);
- errno = save_errno;
- return -1;
- }
-
- dir->next = idata->protect_dirs;
- idata->protect_dirs = dir;
-
- return 0;
-}
-
-static int protect_dir(const char *path, mode_t mode, int do_mkdir,
- struct instance_data *idata)
-{
- char *p = strdup(path);
- char *d;
- char *dir = p;
- int dfd = AT_FDCWD;
- int dfd_next;
- int save_errno;
- int flags = O_RDONLY | O_DIRECTORY;
- int rv = -1;
- struct stat st;
-
- if (p == NULL) {
- goto error;
- }
-
- if (*dir == '/') {
- dfd = open("/", flags);
- if (dfd == -1) {
- goto error;
- }
- dir++; /* assume / is safe */
- }
-
- while ((d=strchr(dir, '/')) != NULL) {
- *d = '\0';
- dfd_next = openat(dfd, dir, flags);
- if (dfd_next == -1) {
- goto error;
- }
-
- if (dfd != AT_FDCWD)
- close(dfd);
- dfd = dfd_next;
-
- if (fstat(dfd, &st) != 0) {
- goto error;
- }
-
- if (flags & O_NOFOLLOW) {
- /* we are inside user-owned dir - protect */
- if (protect_mount(dfd, p, idata) == -1)
- goto error;
- } else if (st.st_uid != 0 || st.st_gid != 0 ||
- (st.st_mode & S_IWOTH)) {
- /* do not follow symlinks on subdirectories */
- flags |= O_NOFOLLOW;
- }
-
- *d = '/';
- dir = d + 1;
- }
-
- rv = openat(dfd, dir, flags);
-
- if (rv == -1) {
- if (!do_mkdir || mkdirat(dfd, dir, mode) != 0) {
- goto error;
- }
- rv = openat(dfd, dir, flags);
- }
-
- if (flags & O_NOFOLLOW) {
- /* we are inside user-owned dir - protect */
- if (protect_mount(rv, p, idata) == -1) {
- save_errno = errno;
- close(rv);
- rv = -1;
- errno = save_errno;
- }
- }
-
-error:
- save_errno = errno;
- free(p);
- if (dfd != AT_FDCWD && dfd >= 0)
- close(dfd);
- errno = save_errno;
-
- return rv;
-}
-
static int check_inst_parent(char *ipath, struct instance_data *idata)
{
struct stat instpbuf;
@@ -1180,13 +1278,12 @@ static int check_inst_parent(char *ipath
* admin explicitly instructs to ignore the instance parent
* mode by the "ignore_instance_parent_mode" argument).
*/
- inst_parent = (char *) malloc(strlen(ipath)+1);
+ inst_parent = strdup(ipath);
if (!inst_parent) {
pam_syslog(idata->pamh, LOG_CRIT, "Error allocating pathname string");
return PAM_SESSION_ERR;
}
- strcpy(inst_parent, ipath);
trailing_slash = strrchr(inst_parent, '/');
if (trailing_slash)
*trailing_slash = '\0';
@@ -1226,72 +1323,83 @@ static int inst_init(const struct polydi
struct instance_data *idata, int newdir)
{
pid_t rc, pid;
- struct sigaction newsa, oldsa;
int status;
const char *init_script = NAMESPACE_INIT_SCRIPT;
+#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;
+
+ if (access(init_script, F_OK) != 0)
+ return PAM_SUCCESS;
+
+ if (access(init_script, X_OK) < 0) {
+ if (idata->flags & PAMNS_DEBUG)
+ pam_syslog(idata->pamh, LOG_ERR,
+ "Namespace init script not executable");
+ return PAM_SESSION_ERR;
+ }
+
+ struct sigaction newsa, oldsa;
+
memset(&newsa, '\0', sizeof(newsa));
- newsa.sa_handler = SIG_DFL;
+ newsa.sa_handler = SIG_DFL;
if (sigaction(SIGCHLD, &newsa, &oldsa) == -1) {
- pam_syslog(idata->pamh, LOG_ERR, "Cannot set signal value");
+ pam_syslog(idata->pamh, LOG_ERR, "failed to reset SIGCHLD handler");
return PAM_SESSION_ERR;
}
- if ((polyptr->flags & POLYDIR_ISCRIPT) && polyptr->init_script)
- init_script = polyptr->init_script;
+ pid = fork();
+ if (pid == 0) {
+ static char *envp[] = { NULL };
+#ifdef WITH_SELINUX
+ if (idata->flags & PAMNS_SELINUX_ENABLED) {
+ if (setexeccon(NULL) < 0)
+ _exit(1);
+ }
+#endif
+ /* Pass maximum privs when we exec() */
+ if (setuid(geteuid()) < 0) {
+ /* ignore failures, they don't matter */
+ }
- if (access(init_script, F_OK) == 0) {
- if (access(init_script, X_OK) < 0) {
- if (idata->flags & PAMNS_DEBUG)
- pam_syslog(idata->pamh, LOG_ERR,
- "Namespace init script not executable");
+ close_fds_pre_exec(idata);
+
+ execle(init_script, init_script,
+ polyptr->dir, ipath, newdir?"1":"0", idata->user, NULL, envp);
+ _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 {
- pid = fork();
- if (pid == 0) {
- static char *envp[] = { NULL };
-#ifdef WITH_SELINUX
- if (idata->flags & PAMNS_SELINUX_ENABLED) {
- if (setexeccon(NULL) < 0)
- _exit(1);
- }
-#endif
- /* Pass maximum privs when we exec() */
- if (setuid(geteuid()) < 0) {
- /* ignore failures, they don't matter */
- }
-
- if (execle(init_script, init_script,
- polyptr->dir, ipath, newdir?"1":"0", idata->user, NULL, envp) < 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;
- }
}
+ } 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) sigaction(SIGCHLD, &oldsa, NULL);
-
- return rc;
+ (void) sigaction(SIGCHLD, &oldsa, NULL);
+ return rc;
}
static int create_polydir(struct polydir_s *polyptr,
@@ -1395,7 +1503,7 @@ static int create_polydir(struct polydir
*/
#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,
@@ -1513,7 +1621,7 @@ static int ns_setup(struct polydir_s *po
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)
@@ -1522,16 +1630,14 @@ static int ns_setup(struct polydir_s *po
retval = protect_dir(polyptr->dir, 0, 0, idata);
- if (retval < 0 && errno != ENOENT) {
- pam_syslog(idata->pamh, LOG_ERR, "Polydir %s access error: %m",
- polyptr->dir);
- return PAM_SESSION_ERR;
- }
-
if (retval < 0) {
- if ((polyptr->flags & POLYDIR_CREATE) &&
- create_polydir(polyptr, idata) != PAM_SUCCESS)
- return PAM_SESSION_ERR;
+ if (errno != ENOENT || !(polyptr->flags & POLYDIR_CREATE)) {
+ pam_syslog(idata->pamh, LOG_ERR, "Polydir %s access error: %m",
+ polyptr->dir);
+ return PAM_SESSION_ERR;
+ }
+ if (create_polydir(polyptr, idata) != PAM_SUCCESS)
+ return PAM_SESSION_ERR;
} else {
close(retval);
}
@@ -1580,7 +1686,7 @@ static int ns_setup(struct polydir_s *po
#endif
}
- if (asprintf(&inst_dir, "%s%s", polyptr->instance_prefix, instname) < 0)
+ if ((inst_dir = pam_asprintf("%s%s", polyptr->instance_prefix, instname)) == NULL)
goto error_out;
if (idata->flags & PAMNS_DEBUG)
@@ -1692,8 +1798,9 @@ static int cleanup_tmpdirs(struct instan
_exit(1);
}
#endif
- if (execle("/bin/rm", "/bin/rm", "-rf", pptr->instance_prefix, NULL, envp) < 0)
- _exit(1);
+ close_fds_pre_exec(idata);
+ execle("/bin/rm", "/bin/rm", "-rf", pptr->instance_prefix, NULL, envp);
+ _exit(1);
} else if (pid > 0) {
while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
(errno == EINTR));
@@ -1708,7 +1815,7 @@ static int cleanup_tmpdirs(struct instan
}
} else if (pid < 0) {
pam_syslog(idata->pamh, LOG_ERR,
- "Cannot fork to run namespace init script, %m");
+ "Cannot fork to cleanup temporary directory, %m");
rc = PAM_SESSION_ERR;
goto out;
}
@@ -1948,7 +2055,7 @@ static int orig_namespace(struct instanc
*/
static int ctxt_based_inst_needed(void)
{
- security_context_t scon = NULL;
+ char *scon = NULL;
int rc = 0;
rc = getexeccon(&scon);
@@ -1994,7 +2101,7 @@ static int root_shared(void)
break;
if (i == 6) {
- if (strncmp(tok, "shared:", 7) == 0)
+ if (pam_str_skip_prefix(tok, "shared:") != NULL)
/* there might be more / mounts, the last one counts */
rv = 1;
else
@@ -2162,7 +2269,7 @@ int pam_sm_close_session(pam_handle_t *p
{
int i, retval;
struct instance_data idata;
- void *polyptr;
+ const void *polyptr;
/* init instance data */
idata.flags = 0;
@@ -2202,7 +2309,7 @@ int pam_sm_close_session(pam_handle_t *p
pam_set_data(idata.pamh, NAMESPACE_PROTECT_DATA, NULL, NULL);
if (idata.flags & PAMNS_DEBUG)
- pam_syslog(idata.pamh, LOG_DEBUG, "close_session - sucessful");
+ pam_syslog(idata.pamh, LOG_DEBUG, "close_session - successful");
return PAM_SUCCESS;
}
@@ -2210,12 +2317,14 @@ int pam_sm_close_session(pam_handle_t *p
if (retval != PAM_SUCCESS)
return retval;
- retval = pam_get_data(idata.pamh, NAMESPACE_POLYDIR_DATA, (const void **)&polyptr);
+ retval = pam_get_data(idata.pamh, NAMESPACE_POLYDIR_DATA, &polyptr);
if (retval != PAM_SUCCESS || polyptr == NULL)
/* nothing to reset */
return PAM_SUCCESS;
- idata.polydirs_ptr = polyptr;
+ DIAG_PUSH_IGNORE_CAST_QUAL;
+ idata.polydirs_ptr = (void *)polyptr;
+ DIAG_POP_IGNORE_CAST_QUAL;
if (idata.flags & PAMNS_DEBUG)
pam_syslog(idata.pamh, LOG_DEBUG, "Resetting namespace for pid %d",
diff -up Linux-PAM-1.3.1/modules/pam_namespace/pam_namespace.h.pam-namespace-rebase Linux-PAM-1.3.1/modules/pam_namespace/pam_namespace.h
--- Linux-PAM-1.3.1/modules/pam_namespace/pam_namespace.h.pam-namespace-rebase 2025-06-17 12:50:04.921165460 +0200
+++ Linux-PAM-1.3.1/modules/pam_namespace/pam_namespace.h 2025-06-17 12:50:04.963237346 +0200
@@ -30,7 +30,7 @@
* DEALINGS IN THE SOFTWARE.
*/
-#if !(defined(linux))
+#ifndef __linux__
#error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
#endif
@@ -44,21 +44,16 @@
#include <stdlib.h>
#include <errno.h>
#include <syslog.h>
-#include <dlfcn.h>
-#include <stdarg.h>
#include <pwd.h>
#include <grp.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <sys/resource.h>
#include <sys/mount.h>
#include <sys/wait.h>
-#include <libgen.h>
#include <fcntl.h>
#include <sched.h>
#include <glob.h>
-#include <locale.h>
#include "security/pam_modules.h"
#include "security/pam_modutil.h"
#include "security/pam_ext.h"
@@ -111,7 +106,7 @@
#define PAMNS_MOUNT_PRIVATE 0x00080000 /* Make the polydir mounts private */
/* polydir flags */
-#define POLYDIR_EXCLUSIVE 0x00000001 /* polyinstatiate exclusively for override uids */
+#define POLYDIR_EXCLUSIVE 0x00000001 /* polyinstantiate exclusively for override uids */
#define POLYDIR_CREATE 0x00000002 /* create the polydir */
#define POLYDIR_NOINIT 0x00000004 /* no init script */
#define POLYDIR_SHARED 0x00000008 /* share context/level instances among users */