• Bug#1108497: marked as done (unblock: pam/1.7.0-5) (3/3)

    From Debian Bug Tracking System@21:1/5 to All on Sun Jul 6 19:40:01 2025
    [continued from previous message]

    ++ retval = inst_init(polyptr, "tmpfs", idata, 1);
    ++ goto cleanup;
    + }
    +
    +- if (stat(polyptr->dir, &statbuf) < 0) {
    +- pam_syslog(idata->pamh, LOG_ERR, "Error stating %s: %m",
    +- polyptr->dir);
    +- return PAM_SESSION_ERR;
    ++ if (fstat(dfd_pptrdir, &statbuf) < 0) {
    ++ pam_syslog(idata->pamh, LOG_ERR, "Error stating %s: %m", polyptr->dir);
    ++ goto error_out;
    + }
    +
    + /*
    +@@ -1693,15 +1933,16 @@ static int ns_setup(struct polydir_s *polyptr,
    + * security policy.
    + */
    + #ifdef WITH_SELINUX
    +- retval = poly_name(polyptr, &instname, &instcontext,
    +- &origcontext, idata);
    ++ retval = poly_name(polyptr, &instcontext, &origcontext, idata);
    + #else
    +- retval = poly_name(polyptr, &instname, idata);
    ++ retval = poly_name(polyptr, idata);
    + #endif
    +
    + if (retval != PAM_SUCCESS) {
    +- if (retval != PAM_IGNORE)
    ++ if (retval != PAM_IGNORE) {
    + pam_syslog(idata->pamh, LOG_ERR, "Error getting instance name");
    ++ goto error_out;
    ++ }
    + goto cleanup;
    + } else {
    + #ifdef WITH_SELINUX
    +@@ -1712,22 +1953,33 @@ static int ns_setup(struct polydir_s *polyptr,
    + #endif
    + }
    +
    +- if (asprintf(&inst_dir, "%s%s", polyptr->instance_prefix, instname) < 0) +- goto error_out;
    +-
    +- if (idata->flags & PAMNS_DEBUG)
    +- pam_syslog(idata->pamh, LOG_DEBUG, "instance_dir %s",
    +- inst_dir);
    ++ /*
    ++ * Gets a fd in a secure manner (we may be operating on a path under
    ++ * user control), and check it's compliant.
    ++ * Then, we should *always* operate on *this* fd and a relative path
    ++ * to be protected against race conditions.
    ++ */
    ++ dfd_iparent = secure_opendir(polyptr->instance_parent,
    ++ SECURE_OPENDIR_PROTECT | SECURE_OPENDIR_MKDIR, 0, idata);
    ++ if (dfd_iparent == -1) {
    ++ pam_syslog(idata->pamh, LOG_ERR,
    ++ "polyptr->instance_parent %s access error",
    ++ polyptr->instance_parent);
    ++ goto error_out;
    ++ }
    ++ if (check_inst_parent(dfd_iparent, idata)) {
    ++ goto error_out;
    ++ }
    +
    + /*
    + * Create instance directory with appropriate security
    + * contexts, owner, group and mode bits.
    + */
    + #ifdef WITH_SELINUX
    +- retval = create_instance(polyptr, inst_dir, &statbuf, instcontext,
    +- origcontext, idata);
    ++ retval = create_instance(polyptr, dfd_iparent, &statbuf, instcontext,
    ++ origcontext, idata);
    + #else
    +- retval = create_instance(polyptr, inst_dir, &statbuf, idata);
    ++ retval = create_instance(polyptr, dfd_iparent, &statbuf, idata);
    + #endif
    +
    + if (retval == PAM_IGNORE) {
    +@@ -1739,19 +1991,48 @@ static int ns_setup(struct polydir_s *polyptr,
    + goto error_out;
    + }
    +
    ++ /*
    ++ * Instead of getting a new secure fd, we reuse the fd opened on directory
    ++ * polyptr->instance_parent to ensure we are working on the same dir as ++ * previously, and thus ensure that previous checks (e.g. check_inst_parent())
    ++ * are still relevant.
    ++ */
    ++ dfd_ipath = openat(dfd_iparent, polyptr->instname,
    ++ O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
    ++ if (dfd_ipath == -1) {
    ++ pam_syslog(idata->pamh, LOG_ERR, "Error openat on %s, %m",
    ++ polyptr->instname);
    ++ goto error_out;
    ++ }
    ++
    ++ if (pam_sprintf(s_ipath, "/proc/self/fd/%d", dfd_ipath) < 0) {
    ++ pam_syslog(idata->pamh, LOG_ERR, "Error pam_sprintf s_ipath");
    ++ goto error_out;
    ++ }
    ++
    ++ if (pam_sprintf(s_pptrdir, "/proc/self/fd/%d", dfd_pptrdir) < 0) {
    ++ pam_syslog(idata->pamh, LOG_ERR, "Error pam_sprintf s_pptrdir");
    ++ goto error_out;
    ++ }
    ++
    + /*
    + * Bind mount instance directory on top of the polyinstantiated
    + * directory to provide an instance of polyinstantiated directory
    + * based on polyinstantiated method.
    ++ *
    ++ * Operates on magic links created from two fd obtained securely
    ++ * to protect against race conditions and symlink attacks. Indeed,
    ++ * the source and destination can be in a user controled path.
    + */
    +- if (mount(inst_dir, polyptr->dir, NULL, MS_BIND, NULL) < 0) {
    +- pam_syslog(idata->pamh, LOG_ERR, "Error mounting %s on %s, %m",
    +- inst_dir, polyptr->dir);
    ++ if(mount(s_ipath, s_pptrdir, NULL, MS_BIND, NULL) < 0) {
    ++ pam_syslog(idata->pamh, LOG_ERR,
    ++ "Error mounting %s on %s (%s on %s), %m",
    ++ s_ipath, s_pptrdir, polyptr->instance_absolute, polyptr->dir);
    + goto error_out;
    + }
    +
    + if (!(polyptr->flags & POLYDIR_NOINIT))
    +- retval = inst_init(polyptr, inst_dir, idata, newdir);
    ++ retval = inst_init(polyptr, polyptr->instance_absolute, idata, newdir); +
    + goto cleanup;
    +
    +@@ -1763,8 +2044,12 @@ error_out:
    + retval = PAM_SESSION_ERR;
    +
    + cleanup:
    +- free(inst_dir);
    +- free(instname);
    ++ if (dfd_iparent != -1)
    ++ close(dfd_iparent);
    ++ if (dfd_ipath != -1)
    ++ close(dfd_ipath);
    ++ if (dfd_pptrdir != -1)
    ++ close(dfd_pptrdir);
    + #ifdef WITH_SELINUX
    + freecon(instcontext);
    + freecon(origcontext);
    +@@ -1803,6 +2088,7 @@ static int cleanup_tmpdirs(struct instance_data *idata) + {
    + struct polydir_s *pptr;
    + pid_t rc, pid;
    ++ int dfd = -1;
    + struct sigaction newsa, oldsa;
    + int status;
    +
    +@@ -1814,7 +2100,17 @@ static int cleanup_tmpdirs(struct instance_data *idata) + }
    +
    + for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) {
    +- if (pptr->method == TMPDIR && access(pptr->instance_prefix, F_OK) == 0) {
    ++ if (pptr->method == TMPDIR) {
    ++
    ++ dfd = secure_opendir_stateless(pptr->instance_parent);
    ++ if (dfd == -1)
    ++ continue;
    ++
    ++ if (faccessat(dfd, pptr->instname, F_OK, AT_SYMLINK_NOFOLLOW) != 0) {
    ++ close(dfd);
    ++ continue;
    ++ }
    ++
    + pid = fork();
    + if (pid == 0) {
    + static char *envp[] = { NULL };
    +@@ -1824,10 +2120,21 @@ static int cleanup_tmpdirs(struct instance_data *idata)
    + _exit(1);
    + }
    + #endif
    ++ if (fchdir(dfd) == -1) {
    ++ pam_syslog(idata->pamh, LOG_ERR, "Failed fchdir to %s: %m",
    ++ pptr->instance_absolute);
    ++ _exit(1);
    ++ }
    ++
    + close_fds_pre_exec(idata);
    +- if (execle("/bin/rm", "/bin/rm", "-rf", pptr->instance_prefix, NULL, envp) < 0)
    +- _exit(1);
    ++
    ++ execle("/bin/rm", "/bin/rm", "-rf", pptr->instname, NULL, envp);
    ++ _exit(1);
    + } else if (pid > 0) {
    ++
    ++ if (dfd != -1)
    ++ close(dfd);
    ++
    + while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
    + (errno == EINTR));
    + if (rc == (pid_t)-1) {
    +@@ -1840,6 +2147,10 @@ static int cleanup_tmpdirs(struct instance_data *idata) + "Error removing %s", pptr->instance_prefix);
    + }
    + } else if (pid < 0) {
    ++
    ++ if (dfd != -1)
    ++ close(dfd);
    ++
    + pam_syslog(idata->pamh, LOG_ERR,
    + "Cannot fork to cleanup temporary directory, %m");
    + rc = PAM_SESSION_ERR;
    +@@ -1863,6 +2174,7 @@ out:
    + static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt) + {
    + int retval = 0, need_poly = 0, changing_dir = 0;
    ++ int dfd = -1;
    + char *cptr, *fptr, poly_parent[PATH_MAX];
    + struct polydir_s *pptr;
    +
    +@@ -1978,13 +2290,21 @@ static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt)
    + strcpy(poly_parent, "/");
    + else if (cptr)
    + *cptr = '\0';
    +- if (chdir(poly_parent) < 0) {
    ++
    ++ dfd = secure_opendir_stateless(poly_parent);
    ++ if (dfd == -1) {
    + pam_syslog(idata->pamh, LOG_ERR,
    +- "Can't chdir to %s, %m", poly_parent);
    ++ "Failed opening %s to fchdir: %m", poly_parent); + }
    ++ else if (fchdir(dfd) == -1) {
    ++ pam_syslog(idata->pamh, LOG_ERR,
    ++ "Failed fchdir to %s: %m", poly_parent);
    ++ }
    ++ if (dfd != -1)
    ++ close(dfd);
    + }
    +
    +- if (umount(pptr->rdir) < 0) {
    ++ if (secure_umount(pptr->rdir) < 0) {
    + int saved_errno = errno;
    + pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m",
    + pptr->rdir);
    +@@ -2054,7 +2374,7 @@ static int orig_namespace(struct instance_data *idata)
    + "Unmounting instance dir for user %d & dir %s",
    + idata->uid, pptr->dir);
    +
    +- if (umount(pptr->dir) < 0) {
    ++ if (secure_umount(pptr->dir) < 0) {
    + pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m", + pptr->dir);
    + return PAM_SESSION_ERR;
    +diff --git a/modules/pam_namespace/pam_namespace.h b/modules/pam_namespace/pam_namespace.h
    +index 1799938..8a7a66b 100644
    +--- a/modules/pam_namespace/pam_namespace.h
    ++++ b/modules/pam_namespace/pam_namespace.h
    +@@ -44,21 +44,17 @@
    + #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"
    +@@ -126,6 +122,13 @@
    + #define NAMESPACE_POLYDIR_DATA "pam_namespace:polydir_data"
    + #define NAMESPACE_PROTECT_DATA "pam_namespace:protect_data"
    +
    ++/*
    ++ * Operation mode for function secure_opendir()
    ++ */
    ++#define SECURE_OPENDIR_PROTECT 0x00000001
    ++#define SECURE_OPENDIR_MKDIR 0x00000002
    ++#define SECURE_OPENDIR_FULL_FD 0x00000004
    ++
    + /*
    + * Polyinstantiation method options, based on user, security context
    + * or both
    +@@ -163,6 +166,9 @@ struct polydir_s {
    + char dir[PATH_MAX]; /* directory to polyinstantiate */
    + char rdir[PATH_MAX]; /* directory to unmount (based on RUSER) */
    + char instance_prefix[PATH_MAX]; /* prefix for instance dir path name */ ++ char instance_absolute[PATH_MAX]; /* absolute path to the instance dir (instance_parent + instname) */
    ++ char instance_parent[PATH_MAX]; /* parent dir of the instance dir */
    ++ char *instname; /* last segment of the path to the instance dir */
    + enum polymethod method; /* method used to polyinstantiate */
    + unsigned int num_uids; /* number of override uids */
    + uid_t *uid; /* list of override uids */ diff --git a/debian/patches/upstream/0022-pam_access-rework-resolving-of-tokens-as-hostname.patch b/debian/patches/upstream/0022-pam_access-rework-resolving-of-tokens-as-hostname.patch
    new file mode 100644
    index 00000000..f08cf0ad
    --- /dev/null
    +++ b/debian/patches/upstream/0022-pam_access-rework-resolving-of-tokens-as-hostname.patch
    @@ -0,0 +1,223 @@
    +From: Thorsten Kukuk <[email protected]>
    +Date: Thu, 14 Nov 2024 10:27:28 +0100
    +Subject: pam_access: rework resolving of tokens as hostname
    +
    +* modules/pam_access/pam_access.c: separate resolving of IP addresses
    + from hostnames. Don't resolve TTYs or display variables as hostname
    + (#834).
    + Add "nodns" option to disallow resolving of tokens as hostname.
    +* modules/pam_access/pam_access.8.xml: document nodns option
    +* modules/pam_access/access.conf.5.xml: document that hostnames should
    + be written as FQHN.
    +
    +(cherry picked from commit 940747f88c16e029b69a74e80a2e94f65cb3e628)
    +---
    + modules/pam_access/access.conf.5.xml | 4 ++
    + modules/pam_access/pam_access.8.xml | 46 +++++++++++++++--------
    + modules/pam_access/pam_access.c | 72 +++++++++++++++++++++++++++++++++++-
    + 3 files changed, 105 insertions(+), 17 deletions(-)
    +
    +diff --git a/modules/pam_access