mirror of
https://gitlab.com/buildroot.org/buildroot.git
synced 2026-08-18 21:33:39 -09:00
package/linux-pam: patch CVE-2025-6020
- CVE-2025-6020:
A flaw was found in linux-pam. The module pam_namespace may use access
user-controlled paths without proper protection, allowing local users
to elevate their privileges to root via multiple symlink attacks and
race conditions.
For more information, see:
- https://www.cve.org/CVERecord?id=CVE-2025-6020
This fix was backported based on the work of the OpenEmbedded
community. See the link to the original patches in the patch header.
(cherry picked from 30e38505e4)
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
This commit is contained in:
103
package/linux-pam/0003-inline-pam-asprintf.patch
Normal file
103
package/linux-pam/0003-inline-pam-asprintf.patch
Normal file
@@ -0,0 +1,103 @@
|
||||
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: https://github.com/linux-pam/linux-pam/commit/10b80543807e3fc5af5f8bcfd8bb6e219bb3cecc
|
||||
|
||||
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
|
||||
Upstream: https://github.com/openembedded/openembedded-core/blob/scarthgap/meta/recipes-extended/pam/libpam/0001-pam-inline-pam-asprintf.patch
|
||||
[thomas: backport from OpenEmbedded patch to 1.6.1]
|
||||
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
|
||||
---
|
||||
libpam/include/pam_cc_compat.h | 6 ++++++
|
||||
libpam/include/pam_inline.h | 36 ++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 42 insertions(+)
|
||||
|
||||
diff --git a/libpam/include/pam_cc_compat.h b/libpam/include/pam_cc_compat.h
|
||||
index 0a6e32d..af05428 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 7721c0b..ec0497c 100644
|
||||
--- a/libpam/include/pam_inline.h
|
||||
+++ b/libpam/include/pam_inline.h
|
||||
@@ -9,6 +9,8 @@
|
||||
#define PAM_INLINE_H
|
||||
|
||||
#include "pam_cc_compat.h"
|
||||
+#include <stdarg.h>
|
||||
+#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@@ -126,6 +128,40 @@ pam_drop_response(struct pam_response *reply, int replies)
|
||||
}
|
||||
|
||||
|
||||
+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.49.0
|
||||
564
package/linux-pam/0004-pam-namespace-rebase.patch
Normal file
564
package/linux-pam/0004-pam-namespace-rebase.patch
Normal file
@@ -0,0 +1,564 @@
|
||||
From df1dab1a1a7900650ad4be157fea1a002048cc49 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-rebase
|
||||
|
||||
Refresh the pam-namespace.
|
||||
|
||||
Upstream: https://github.com/linux-pam/linux-pam/commit/a8b4dce7b53d73de372e150028c970ee0a2a2e97
|
||||
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
|
||||
Upstream: https://github.com/openembedded/openembedded-core/blob/scarthgap/meta/recipes-extended/pam/libpam/0002-pam-namespace-rebase.patch
|
||||
[thomas: backport from OpenEmbedded patch to 1.6.1]
|
||||
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
|
||||
---
|
||||
modules/pam_namespace/pam_namespace.c | 444 +++++++++++++-------------
|
||||
modules/pam_namespace/pam_namespace.h | 7 +-
|
||||
2 files changed, 224 insertions(+), 227 deletions(-)
|
||||
|
||||
diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c
|
||||
index b026861..166bfce 100644
|
||||
--- a/modules/pam_namespace/pam_namespace.c
|
||||
+++ b/modules/pam_namespace/pam_namespace.c
|
||||
@@ -63,6 +63,147 @@ static void close_fds_pre_exec(struct instance_data *idata)
|
||||
}
|
||||
}
|
||||
|
||||
+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
|
||||
@@ -137,6 +278,7 @@ static char **read_namespace_dir(struct instance_data *idata)
|
||||
return file_list;
|
||||
}
|
||||
|
||||
+
|
||||
/*
|
||||
* Adds an entry for a polyinstantiated directory to the linked list of
|
||||
* polyinstantiated directories. It is called from process_line() while
|
||||
@@ -319,8 +461,7 @@ static int parse_iscript_params(char *params, struct polydir_s *poly)
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -482,6 +623,7 @@ 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;
|
||||
@@ -490,7 +632,6 @@ static int process_line(char *line, const char *home, const char *rhome,
|
||||
static const char *const var_names[] = {"HOME", "USER", NULL};
|
||||
const char *var_values[] = {home, idata->user};
|
||||
const char *rvar_values[] = {rhome, idata->ruser};
|
||||
- size_t len;
|
||||
|
||||
/*
|
||||
* skip the leading white space
|
||||
@@ -531,22 +672,19 @@ static int process_line(char *line, const char *home, const char *rhome,
|
||||
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");
|
||||
- dir = 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;
|
||||
}
|
||||
|
||||
@@ -561,19 +699,16 @@ static int process_line(char *line, const char *home, const char *rhome,
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@@ -583,15 +718,8 @@ static int process_line(char *line, const char *home, const char *rhome,
|
||||
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");
|
||||
@@ -1003,6 +1131,7 @@ static int form_context(const struct polydir_s *polyptr,
|
||||
return rc;
|
||||
}
|
||||
/* Should never get here */
|
||||
+ freecon(scon);
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
@@ -1064,10 +1193,8 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name,
|
||||
|
||||
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
|
||||
@@ -1077,17 +1204,12 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name,
|
||||
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 */
|
||||
@@ -1117,11 +1239,12 @@ static int poly_name(const struct polydir_s *polyptr, char **i_name,
|
||||
*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;
|
||||
}
|
||||
@@ -1146,137 +1269,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;
|
||||
@@ -1380,8 +1372,8 @@ static int inst_init(const struct polydir_s *polyptr, const char *ipath,
|
||||
|
||||
close_fds_pre_exec(idata);
|
||||
|
||||
- if (execle(init_script, init_script,
|
||||
- polyptr->dir, ipath, newdir?"1":"0", idata->user, NULL, envp) < 0)
|
||||
+ 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) &&
|
||||
@@ -1432,7 +1424,9 @@ static int create_polydir(struct polydir_s *polyptr,
|
||||
|
||||
#ifdef WITH_SELINUX
|
||||
if (idata->flags & PAMNS_SELINUX_ENABLED) {
|
||||
- getfscreatecon_raw(&oldcon_raw);
|
||||
+ if (getfscreatecon_raw(&oldcon_raw) != 0)
|
||||
+ pam_syslog(idata->pamh, LOG_NOTICE,
|
||||
+ "Error retrieving fs create context: %m");
|
||||
|
||||
label_handle = selabel_open(SELABEL_CTX_FILE, NULL, 0);
|
||||
if (!label_handle) {
|
||||
@@ -1461,6 +1455,9 @@ static int create_polydir(struct polydir_s *polyptr,
|
||||
if (rc == -1) {
|
||||
pam_syslog(idata->pamh, LOG_ERR,
|
||||
"Error creating directory %s: %m", dir);
|
||||
+#ifdef WITH_SELINUX
|
||||
+ freecon(oldcon_raw);
|
||||
+#endif
|
||||
return PAM_SESSION_ERR;
|
||||
}
|
||||
|
||||
@@ -1648,16 +1645,14 @@ static int ns_setup(struct polydir_s *polyptr,
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -1706,7 +1701,7 @@ static int ns_setup(struct polydir_s *polyptr,
|
||||
#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)
|
||||
diff --git a/modules/pam_namespace/pam_namespace.h b/modules/pam_namespace/pam_namespace.h
|
||||
index a991b4c..180e042 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"
|
||||
--
|
||||
2.49.0
|
||||
1131
package/linux-pam/0005-cve-2025-6020-01.patch
Normal file
1131
package/linux-pam/0005-cve-2025-6020-01.patch
Normal file
File diff suppressed because it is too large
Load Diff
37
package/linux-pam/0006-cve-2025-6020-02.patch
Normal file
37
package/linux-pam/0006-cve-2025-6020-02.patch
Normal file
@@ -0,0 +1,37 @@
|
||||
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: https://github.com/linux-pam/linux-pam/commit/976c20079358d133514568fc7fd95c02df8b5773
|
||||
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
|
||||
Upstream: https://github.com/openembedded/openembedded-core/blob/scarthgap/meta/recipes-extended/pam/libpam/CVE-2025-6020-02.patch
|
||||
CVE: CVE-2025-6020
|
||||
[thomas: backport from OpenEmbedded patch to 1.6.1]
|
||||
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
|
||||
---
|
||||
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 4c8153b..791dd07 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.49.0
|
||||
189
package/linux-pam/0007-cve-2025-6020-03.patch
Normal file
189
package/linux-pam/0007-cve-2025-6020-03.patch
Normal file
@@ -0,0 +1,189 @@
|
||||
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: https://github.com/linux-pam/linux-pam/commit/592d84e1265d04c3104acee815a503856db503a1
|
||||
|
||||
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
|
||||
Upstream: https://github.com/openembedded/openembedded-core/blob/scarthgap/meta/recipes-extended/pam/libpam/CVE-2025-6020-03.patch
|
||||
CVE: CVE-2025-6020
|
||||
[thomas: backport from OpenEmbedded patch to 1.6.1]
|
||||
Signed-off-by: Thomas Perale <thomas.perale@mind.be>
|
||||
---
|
||||
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 d9053a1..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=$(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
|
||||
|
||||
+# 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 9d993d4..4c8153b 100644
|
||||
--- a/modules/pam_namespace/pam_namespace.c
|
||||
+++ b/modules/pam_namespace/pam_namespace.c
|
||||
@@ -1466,6 +1466,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
|
||||
@@ -1524,7 +1597,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.49.0
|
||||
@@ -28,6 +28,11 @@ LINUX_PAM_CPE_ID_VENDOR = linux-pam
|
||||
# 0002-pam_access-rework-resolving-of-tokens-as-hostname.patch
|
||||
LINUX_PAM_IGNORE_CVES += CVE-2024-10963
|
||||
|
||||
# 0003-inline-pam-asprintf.patch 0004-pam-namespace-rebase.patch
|
||||
# 0005-cve-2025-6020-01.patch 0006-cve-2025-6020-02.patch
|
||||
# 0007-cve-2025-6020-03.patch
|
||||
LINUX_PAM_IGNORE_CVES += CVE-2025-6020
|
||||
|
||||
ifeq ($(BR2_TOOLCHAIN_HAS_LIBATOMIC),y)
|
||||
LINUX_PAM_LIBS += -latomic
|
||||
endif
|
||||
|
||||
Reference in New Issue
Block a user