libpam: fix CVE-2025-6020

Upstream-Status: Backport from 475bd60c55 && 592d84e126 && 976c200793

(From OE-Core rev: 4ff5111d2a758bacb803de981177799a8ac7fd0b)

Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
This commit is contained in:
Hitendra Prajapati 2025-07-24 10:02:52 +05:30 committed by Steve Sakoman
parent 875170d8f8
commit a485d82c25
6 changed files with 1959 additions and 0 deletions

View File

@ -0,0 +1,102 @@
From 10b80543807e3fc5af5f8bcfd8bb6e219bb3cecc Mon Sep 17 00:00:00 2001
From: "Dmitry V. Levin" <ldv@strace.io>
Date: Tue, 18 Feb 2025 08:00:00 +0000
Subject: [PATCH] pam_inline: introduce pam_asprintf(), pam_snprintf(), and
pam_sprintf()
pam_asprintf() is essentially asprintf() with the following semantic
difference: it returns the string itself instead of its length.
pam_snprintf() is essentially snprintf() with the following semantic
difference: it returns -1 in case of truncation.
pam_sprintf() is essentially snprintf() but with a check that the buffer
is an array, and with an automatically calculated buffer size.
Use of these helpers would make error checking simpler.
(cherry picked from commit 10b80543807e3fc5af5f8bcfd8bb6e219bb3cecc)
Signed-off-by: Dmitry V. Levin <ldv@strace.io>
Upstream-Status: Backport [https://github.com/linux-pam/linux-pam/commit/10b80543807e3fc5af5f8bcfd8bb6e219bb3cecc]
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
libpam/include/pam_cc_compat.h | 6 ++++++
libpam/include/pam_inline.h | 37 ++++++++++++++++++++++++++++++++++
2 files changed, 43 insertions(+)
diff --git a/libpam/include/pam_cc_compat.h b/libpam/include/pam_cc_compat.h
index 6919036..45c74b5 100644
--- a/libpam/include/pam_cc_compat.h
+++ b/libpam/include/pam_cc_compat.h
@@ -21,6 +21,12 @@
# define PAM_ATTRIBUTE_ALIGNED(arg) /* empty */
#endif
+#if PAM_GNUC_PREREQ(3, 0)
+# define PAM_ATTRIBUTE_MALLOC __attribute__((__malloc__))
+#else
+# define PAM_ATTRIBUTE_MALLOC /* empty */
+#endif
+
#if PAM_GNUC_PREREQ(4, 6)
# define DIAG_PUSH_IGNORE_CAST_QUAL \
_Pragma("GCC diagnostic push"); \
diff --git a/libpam/include/pam_inline.h b/libpam/include/pam_inline.h
index ec2f3bf..666a028 100644
--- a/libpam/include/pam_inline.h
+++ b/libpam/include/pam_inline.h
@@ -9,6 +9,9 @@
#define PAM_INLINE_H
#include "pam_cc_compat.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
@@ -66,6 +69,40 @@ pam_str_skip_icase_prefix_len(const char *str, const char *prefix, size_t prefix
#define pam_str_skip_icase_prefix(str_, prefix_) \
pam_str_skip_icase_prefix_len((str_), (prefix_), sizeof(prefix_) - 1 + PAM_MUST_BE_ARRAY(prefix_))
+static inline char * PAM_FORMAT((printf, 1, 2)) PAM_NONNULL((1)) PAM_ATTRIBUTE_MALLOC
+pam_asprintf(const char *fmt, ...)
+{
+ int rc;
+ char *res;
+ va_list ap;
+
+ va_start(ap, fmt);
+ rc = vasprintf(&res, fmt, ap);
+ va_end(ap);
+
+ return rc < 0 ? NULL : res;
+}
+
+static inline int PAM_FORMAT((printf, 3, 4)) PAM_NONNULL((3))
+pam_snprintf(char *str, size_t size, const char *fmt, ...)
+{
+ int rc;
+ va_list ap;
+
+ va_start(ap, fmt);
+ rc = vsnprintf(str, size, fmt, ap);
+ va_end(ap);
+
+ if (rc < 0 || (unsigned int) rc >= size)
+ return -1;
+ return rc;
+}
+
+#define pam_sprintf(str_, fmt_, ...) \
+ pam_snprintf((str_), sizeof(str_) + PAM_MUST_BE_ARRAY(str_), (fmt_), \
+ ##__VA_ARGS__)
+
+
static inline int
pam_read_passwords(int fd, int npass, char **passwords)
{
--
2.50.1

View File

@ -0,0 +1,42 @@
From cc9d40b7cdbd3e15ccaa324a0dda1680ef9dea13 Mon Sep 17 00:00:00 2001
From: Jacob Heider <jacob@pkgx.dev>
Date: Wed, 17 Jan 2024 11:49:26 -0500
Subject: [PATCH] pam_namespace: include stdint.h
pam_namespace.c makes use of SIZE_MAX but doesn't include stdint.h,
resulting in the following build failures on 1.6.0:
pam_namespace.c: In function 'process_line':
pam_namespace.c:649:41: error: 'SIZE_MAX' undeclared (first use in this function)
649 | if (count > UINT_MAX || count > SIZE_MAX / sizeof(uid_t)) {
| ^~~~~~~~
pam_namespace.c:41:1: note: 'SIZE_MAX' is defined in header '<stdint.h>'; did you forget to '#include <stdint.h>'?
40 | #include "argv_parse.h"
+++ |+#include <stdint.h>
41 |
pam_namespace.c:649:41: note: each undeclared identifier is reported only once for each function it appears in
649 | if (count > UINT_MAX || count > SIZE_MAX / sizeof(uid_t)) {
| ^~~~~~~~
Fixes: v1.6.0~100 ("pam_namespace: validate amount of uids in config")
Resolves: https://github.com/linux-pam/linux-pam/issues/733
Upstream-Status: Backport [https://github.com/linux-pam/linux-pam/commit/cc9d40b7cdbd3e15ccaa324a0dda1680ef9dea13]
Signed-off-by: Khem Raj <raj.khem@gmail.com>
---
modules/pam_namespace/pam_namespace.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
index f72d67189..b16731c22 100644
--- a/modules/pam_namespace/pam_namespace.c
+++ b/modules/pam_namespace/pam_namespace.c
@@ -34,6 +34,8 @@
#define _ATFILE_SOURCE
+#include "config.h"
+#include <stdint.h>
#include "pam_cc_compat.h"
#include "pam_inline.h"
#include "pam_namespace.h"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,187 @@
From 592d84e1265d04c3104acee815a503856db503a1 Mon Sep 17 00:00:00 2001
From: Olivier Bal-Petre <olivier.bal-petre@ssi.gouv.fr>
Date: Tue, 4 Mar 2025 14:37:02 +0100
Subject: [PATCH] pam_namespace: add flags to indicate path safety
Add two flags in the script to indicate if the paths to the polydir
and the instance directories are safe (root owned and writable by
root only).
Signed-off-by: Olivier Bal-Petre <olivier.bal-petre@ssi.gouv.fr>
Signed-off-by: Dmitry V. Levin <ldv@strace.io>
Upstream-Status: Backport [https://github.com/linux-pam/linux-pam/commit/592d84e1265d04c3104acee815a503856db503a1]
CVE: CVE-2025-6020
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
modules/pam_namespace/namespace.init | 56 ++++++++++++-------
modules/pam_namespace/pam_namespace.c | 79 ++++++++++++++++++++++++++-
2 files changed, 115 insertions(+), 20 deletions(-)
diff --git a/modules/pam_namespace/namespace.init b/modules/pam_namespace/namespace.init
index 67d4aa2..8782178 100755
--- a/modules/pam_namespace/namespace.init
+++ b/modules/pam_namespace/namespace.init
@@ -1,25 +1,43 @@
#!/bin/sh
-# It receives polydir path as $1, the instance path as $2,
-# a flag whether the instance dir was newly created (0 - no, 1 - yes) in $3,
-# and user name in $4.
+# It receives as arguments:
+# - $1 polydir path (see WARNING below)
+# - $2 instance path (see WARNING below)
+# - $3 flag whether the instance dir was newly created (0 - no, 1 - yes)
+# - $4 user name
+# - $5 flag whether the polydir path ($1) is safe (0 - unsafe, 1 -safe)
+# - $6 flag whether the instance path ($2) is safe (0 - unsafe, 1 - safe)
+#
+# WARNING: This script is invoked with full root privileges. Accessing
+# the polydir ($1) and the instance ($2) directories in this context may be
+# extremely dangerous as those can be under user control. The flags $5 and $6
+# are provided to let you know if all the segments part of the path (except the
+# last one) are owned by root and are writable by root only. If the path does
+# not meet these criteria, you expose yourself to possible symlink attacks when
+# accessing these path.
+# However, even if the path components are safe, the content of the
+# directories may still be owned/writable by a user, so care must be taken!
#
# The following section will copy the contents of /etc/skel if this is a
# newly created home directory.
-if [ "$3" = 1 ]; then
- # This line will fix the labeling on all newly created directories
- [ -x /sbin/restorecon ] && /sbin/restorecon "$1"
- user="$4"
- passwd=$(getent passwd "$user")
- homedir=$(echo "$passwd" | cut -f6 -d":")
- if [ "$1" = "$homedir" ]; 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)))
- chmod ${mode:-700} "$homedir"
- [ -x /sbin/restorecon ] && /sbin/restorecon -R "$homedir"
- fi
-fi
+# Executes only if the polydir path is safe
+if [ "$5" = 1 ]; then
+
+ if [ "$3" = 1 ]; then
+ # This line will fix the labeling on all newly created directories
+ [ -x /sbin/restorecon ] && /sbin/restorecon "$1"
+ user="$4"
+ passwd=$(getent passwd "$user")
+ homedir=$(echo "$passwd" | cut -f6 -d":")
+ if [ "$1" = "$homedir" ]; then
+ gid=$(echo "$passwd" | cut -f4 -d":")
+ cp -rT /etc/skel "$homedir"
+ chown -R "$user":"$gid" "$homedir"
+ 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
+ fi
+fi
exit 0
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
index 22d8445..8cba036 100644
--- a/modules/pam_namespace/pam_namespace.c
+++ b/modules/pam_namespace/pam_namespace.c
@@ -1390,6 +1390,79 @@ static int check_inst_parent(int dfd, struct instance_data *idata)
return PAM_SUCCESS;
}
+/*
+ * Check for a given absolute path that all segments except the last one are:
+ * 1. a directory owned by root and not writable by group or others
+ * 2. a symlink owned by root and referencing a directory respecting 1.
+ * Returns 0 if safe, -1 is unsafe.
+ * If the path is not accessible (does not exist, hidden under a mount...),
+ * returns -1 (unsafe).
+ */
+static int check_safe_path(const char *path, struct instance_data *idata)
+{
+ char *p = strdup(path);
+ char *d;
+ char *dir = p;
+ struct stat st;
+
+ if (p == NULL)
+ return -1;
+
+ /* Check path is absolute */
+ if (p[0] != '/')
+ goto error;
+
+ strip_trailing_slashes(p);
+
+ /* Last segment of the path may be owned by the user */
+ if ((d = strrchr(dir, '/')) != NULL)
+ *d = '\0';
+
+ while ((d=strrchr(dir, '/')) != NULL) {
+
+ /* Do not follow symlinks */
+ if (lstat(dir, &st) != 0)
+ goto error;
+
+ if (S_ISLNK(st.st_mode)) {
+ if (st.st_uid != 0) {
+ if (idata->flags & PAMNS_DEBUG)
+ pam_syslog(idata->pamh, LOG_DEBUG,
+ "Path deemed unsafe: Symlink %s should be owned by root", dir);
+ goto error;
+ }
+
+ /* Follow symlinks */
+ if (stat(dir, &st) != 0)
+ goto error;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ if (idata->flags & PAMNS_DEBUG)
+ pam_syslog(idata->pamh, LOG_DEBUG,
+ "Path deemed unsafe: %s is expected to be a directory", dir);
+ goto error;
+ }
+
+ if (st.st_uid != 0 ||
+ ((st.st_mode & (S_IWGRP|S_IWOTH)) && !(st.st_mode & S_ISVTX))) {
+ if (idata->flags & PAMNS_DEBUG)
+ pam_syslog(idata->pamh, LOG_DEBUG,
+ "Path deemed unsafe: %s should be owned by root, and not be writable by group or others", dir);
+ goto error;
+ }
+
+ *d = '\0';
+ }
+
+ free(p);
+ return 0;
+
+error:
+ free(p);
+ return -1;
+}
+
/*
* Check to see if there is a namespace initialization script in
* the /etc/security directory. If such a script exists
@@ -1438,7 +1511,11 @@ static int inst_init(const struct polydir_s *polyptr, const char *ipath,
close_fds_pre_exec(idata);
execle(init_script, init_script,
- polyptr->dir, ipath, newdir?"1":"0", idata->user, NULL, envp);
+ polyptr->dir, ipath,
+ newdir ? "1":"0", idata->user,
+ (check_safe_path(polyptr->dir, idata) == -1) ? "0":"1",
+ (check_safe_path(ipath, idata) == -1) ? "0":"1",
+ NULL, envp);
_exit(1);
} else if (pid > 0) {
while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&
--
2.50.1

View File

@ -0,0 +1,35 @@
From 976c20079358d133514568fc7fd95c02df8b5773 Mon Sep 17 00:00:00 2001
From: "Dmitry V. Levin" <ldv@strace.io>
Date: Tue, 27 May 2025 08:00:00 +0000
Subject: [PATCH] pam_namespace: secure_opendir: do not look at the group
ownership
When the directory is not group-writable, the group ownership does
not matter, and when it is group-writable, there should not be any
exceptions for the root group as there is no guarantee that the root
group does not include non-root users.
Upstream-Status: Backport [https://github.com/linux-pam/linux-pam/commit/976c20079358d133514568fc7fd95c02df8b5773]
CVE: CVE-2025-6020
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
modules/pam_namespace/pam_namespace.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
index 8cba036..630cf0a 100644
--- a/modules/pam_namespace/pam_namespace.c
+++ b/modules/pam_namespace/pam_namespace.c
@@ -215,8 +215,7 @@ static int secure_opendir(const char *path, int opm, mode_t mode,
if (dfd_next == -1)
goto error;
} else if (st.st_uid != 0
- || (st.st_gid != 0 && (st.st_mode & S_IWGRP))
- || (st.st_mode & S_IWOTH)) {
+ || (st.st_mode & (S_IWGRP|S_IWOTH))) {
/* do not follow symlinks on subdirectories */
flags |= O_NOFOLLOW;
}
--
2.50.1

View File

@ -29,6 +29,11 @@ SRC_URI = "https://github.com/linux-pam/linux-pam/releases/download/v${PV}/Linux
file://CVE-2024-22365.patch \
file://CVE-2024-10041-1.patch \
file://CVE-2024-10041-2.patch \
file://0001-pam_namespace-include-stdint-h.patch \
file://0001-pam_inline-introduce-pam_asprint.patch \
file://CVE-2025-6020-01.patch \
file://CVE-2025-6020-02.patch \
file://CVE-2025-6020-03.patch \
"
SRC_URI[sha256sum] = "e4ec7131a91da44512574268f493c6d8ca105c87091691b8e9b56ca685d4f94d"