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 +#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 #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" @@ -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 */