Compare commits

...

5 Commits

8 changed files with 1863 additions and 1 deletions

View File

@@ -0,0 +1,36 @@
From 8ae228fa76ff9ef1d8d6b2199582d9206f1830c6 Mon Sep 17 00:00:00 2001
From: Stanislav Brabec <sbrabec@suse.cz>
Date: Mon, 22 Jul 2024 23:18:16 +0200
Subject: [PATCH] libpam_misc: Use ECHOCTL in the terminal input
Use the canonical terminal mode (line mode) and set ECHOCTL to prevent
cursor escape from the login prompt using arrows or escape sequences.
ICANON is the default in most cases anyway. ECHOCTL is default on tty, but
for example not on pty, allowing cursor to escape.
Stanislav Brabec <sbrabec@suse.com>
---
libpam_misc/misc_conv.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libpam_misc/misc_conv.c b/libpam_misc/misc_conv.c
index 7410e929..6b839b48 100644
--- a/libpam_misc/misc_conv.c
+++ b/libpam_misc/misc_conv.c
@@ -145,9 +145,10 @@ static int read_string(int echo, const char *prompt, char **retstr)
return -1;
}
memcpy(&term_tmp, &term_before, sizeof(term_tmp));
- if (!echo) {
+ if (echo)
+ term_tmp.c_lflag |= ICANON | ECHOCTL;
+ else
term_tmp.c_lflag &= ~(ECHO);
- }
have_term = 1;
/*
--
2.45.2

View File

@@ -1,3 +1,35 @@
-------------------------------------------------------------------
Thu Jun 12 18:36:14 UTC 2025 - Valentin Lefebvre <valentin.lefebvre@suse.com>
- pam_namespace: convert functions that may operate on a user-controlled path
to operate on file descriptors instead of absolute path. And keep the
bind-mount protection from protect_mount() as a defense in depthmeasure.
[bsc#1244509, CVE-2025-6020,
pam_inline-introduce-pam_asprintf-pam_snprintf-and-p.patch,
pam_namespace-fix-potential-privilege-escalation.patch,
pam_namespace-add-flags-to-indicate-path-safety.patch,
pam_namespace-secure_opendir-do-not-look-at-the-grou.patch]
- pam_namespace-fix-potential-privilege-escalation.patch adapted and includes
changes from upstream commits: ds6242a, bc856cd.
* pam_namespace fix logic in return value handling
* pam_namespace move functions around
-------------------------------------------------------------------
Thu Dec 5 12:44:33 UTC 2024 - Valentin Lefebvre <valentin.lefebvre@suse.com>
- pam_access: rework resolving of tokens as hostname
- separate resolving of IP addresses from hostnames. Don't resolve TTYs or
display variables as hostname.
- Add "nodns" option to disallow resolving of tokens as hostname.
- [pam_access-rework-resolving-of-tokens-as-hostname.patch, bsc#1233078,
CVE-2024-10963]
-------------------------------------------------------------------
Wed Aug 7 14:44:56 UTC 2024 - Stanislav Brabec <sbrabec@suse.com>
- Prevent cursor escape from the login prompt [bsc#1194818]
* Added: pam-bsc1194818-cursor-escape.patch
-------------------------------------------------------------------
Wed Apr 10 07:12:02 UTC 2024 - Thorsten Kukuk <kukuk@suse.com>
@@ -1183,7 +1215,6 @@ Wed Feb 23 12:45:03 UTC 2011 - vcizek@novell.com
* correct parsing of "quiet" option
-------------------------------------------------------------------
Wed Feb 23 10:00:22 UTC 2011 - vcizek@novell.com
- fix for bnc#673826 (pam_listfile)

View File

@@ -96,6 +96,14 @@ Source22: postlogin-account.pamd
Source23: postlogin-password.pamd
Source24: postlogin-session.pamd
Patch1: pam-limit-nproc.patch
Patch2: pam-bsc1194818-cursor-escape.patch
# https://github.com/linux-pam/linux-pam/pull/854
Patch3: pam_access-rework-resolving-of-tokens-as-hostname.patch
# CVE-2025-6020
Patch4: pam_inline-introduce-pam_asprintf-pam_snprintf-and-p.patch
Patch5: pam_namespace-fix-potential-privilege-escalation.patch
Patch6: pam_namespace-add-flags-to-indicate-path-safety.patch
Patch7: pam_namespace-secure_opendir-do-not-look-at-the-grou.patch
BuildRequires: audit-devel
BuildRequires: bison
BuildRequires: flex

View File

@@ -0,0 +1,225 @@
From 940747f88c16e029b69a74e80a2e94f65cb3e628 Mon Sep 17 00:00:00 2001
From: Thorsten Kukuk <kukuk@suse.com>
Date: Thu, 14 Nov 2024 10:27:28 +0100
Subject: [PATCH] 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.
---
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/access.conf.5.xml b/modules/pam_access/access.conf.5.xml
index 0b93db00..10b8ba92 100644
--- a/modules/pam_access/access.conf.5.xml
+++ b/modules/pam_access/access.conf.5.xml
@@ -233,6 +233,10 @@
An IPv6 link local host address must contain the interface
identifier. IPv6 link local network/netmask is not supported.
</para>
+ <para>
+ Hostnames should be written as Fully-Qualified Host Name (FQHN) to avoid
+ confusion with device names or PAM service names.
+ </para>
</refsect1>
<refsect1 xml:id="access.conf-see_also">
diff --git a/modules/pam_access/pam_access.8.xml b/modules/pam_access/pam_access.8.xml
index c991d7a0..71a4f7ee 100644
--- a/modules/pam_access/pam_access.8.xml
+++ b/modules/pam_access/pam_access.8.xml
@@ -22,11 +22,14 @@
<arg choice="opt" rep="norepeat">
debug
</arg>
+ <arg choice="opt" rep="norepeat">
+ noaudit
+ </arg>
<arg choice="opt" rep="norepeat">
nodefgroup
</arg>
<arg choice="opt" rep="norepeat">
- noaudit
+ nodns
</arg>
<arg choice="opt" rep="norepeat">
quiet_log
@@ -132,6 +135,33 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ nodefgroup
+ </term>
+ <listitem>
+ <para>
+ User tokens which are not enclosed in parentheses will not be
+ matched against the group database. The backwards compatible default is
+ to try the group database match even for tokens not enclosed
+ in parentheses.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ nodns
+ </term>
+ <listitem>
+ <para>
+ Do not try to resolve tokens as hostnames, only IPv4 and IPv6
+ addresses will be resolved. Which means to allow login from a
+ remote host, the IP addresses need to be specified in <filename>access.conf</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term>
quiet_log
@@ -185,20 +215,6 @@
</listitem>
</varlistentry>
- <varlistentry>
- <term>
- nodefgroup
- </term>
- <listitem>
- <para>
- User tokens which are not enclosed in parentheses will not be
- matched against the group database. The backwards compatible default is
- to try the group database match even for tokens not enclosed
- in parentheses.
- </para>
- </listitem>
- </varlistentry>
-
</variablelist>
</refsect1>
diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c
index 48e7c7e9..109115e9 100644
--- a/modules/pam_access/pam_access.c
+++ b/modules/pam_access/pam_access.c
@@ -100,6 +100,7 @@ struct login_info {
int only_new_group_syntax; /* Only allow group entries of the form "(xyz)" */
int noaudit; /* Do not audit denials */
int quiet_log; /* Do not log denials */
+ int nodns; /* Do not try to resolve tokens as hostnames */
const char *fs; /* field separator */
const char *sep; /* list-element separator */
int from_remote_host; /* If PAM_RHOST was used for from */
@@ -154,6 +155,8 @@ parse_args(pam_handle_t *pamh, struct login_info *loginfo,
loginfo->noaudit = YES;
} else if (strcmp (argv[i], "quiet_log") == 0) {
loginfo->quiet_log = YES;
+ } else if (strcmp (argv[i], "nodns") == 0) {
+ loginfo->nodns = YES;
} else {
pam_syslog(pamh, LOG_ERR, "unrecognized option [%s]", argv[i]);
}
@@ -820,7 +823,7 @@ remote_match (pam_handle_t *pamh, char *tok, struct login_info *item)
if ((str_len = strlen(string)) > tok_len
&& strcasecmp(tok, string + str_len - tok_len) == 0)
return YES;
- } else if (tok[tok_len - 1] == '.') { /* internet network numbers (end with ".") */
+ } else if (tok[tok_len - 1] == '.') { /* internet network numbers/subnet (end with ".") */
struct addrinfo hint;
memset (&hint, '\0', sizeof (hint));
@@ -895,6 +898,39 @@ string_match (pam_handle_t *pamh, const char *tok, const char *string,
}
+static int
+is_device (pam_handle_t *pamh, const char *tok)
+{
+ struct stat st;
+ const char *dev = "/dev/";
+ char *devname;
+
+ devname = malloc (strlen(dev) + strlen (tok) + 1);
+ if (devname == NULL) {
+ pam_syslog(pamh, LOG_ERR, "Cannot allocate memory for device name: %m");
+ /*
+ * We should return an error and abort, but pam_access has no good
+ * error handling.
+ */
+ return NO;
+ }
+
+ char *cp = stpcpy (devname, dev);
+ strcpy (cp, tok);
+
+ if (lstat(devname, &st) != 0)
+ {
+ free (devname);
+ return NO;
+ }
+ free (devname);
+
+ if (S_ISCHR(st.st_mode))
+ return YES;
+
+ return NO;
+}
+
/* network_netmask_match - match a string against one token
* where string is a hostname or ip (v4,v6) address and tok
* represents either a hostname, a single ip (v4,v6) address
@@ -956,10 +992,42 @@ network_netmask_match (pam_handle_t *pamh,
return NO;
}
}
+ else if (isipaddr(tok, NULL, NULL) == YES)
+ {
+ if (getaddrinfo (tok, NULL, NULL, &ai) != 0)
+ {
+ if (item->debug)
+ pam_syslog(pamh, LOG_DEBUG, "cannot resolve IP address \"%s\"", tok);
+
+ return NO;
+ }
+ netmask_ptr = NULL;
+ }
+ else if (item->nodns)
+ {
+ /* Only hostnames are left, which we would need to resolve via DNS */
+ return NO;
+ }
else
{
+ /* Bail out on X11 Display entries and ttys. */
+ if (tok[0] == ':')
+ {
+ if (item->debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "network_netmask_match: tok=%s is X11 display", tok);
+ return NO;
+ }
+ if (is_device (pamh, tok))
+ {
+ if (item->debug)
+ pam_syslog (pamh, LOG_DEBUG,
+ "network_netmask_match: tok=%s is a TTY", tok);
+ return NO;
+ }
+
/*
- * It is either an IP address or a hostname.
+ * It is most likely a hostname.
* Let getaddrinfo sort everything out
*/
if (getaddrinfo (tok, NULL, NULL, &ai) != 0)
--
2.47.0

View File

@@ -0,0 +1,69 @@
Index: Linux-PAM-1.6.1/libpam/include/pam_cc_compat.h
===================================================================
--- Linux-PAM-1.6.1.orig/libpam/include/pam_cc_compat.h
+++ Linux-PAM-1.6.1/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"); \
Index: Linux-PAM-1.6.1/libpam/include/pam_inline.h
===================================================================
--- Linux-PAM-1.6.1.orig/libpam/include/pam_inline.h
+++ Linux-PAM-1.6.1/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>
@@ -125,6 +127,38 @@ pam_drop_response(struct pam_response *r
free(reply);
}
+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)

View File

@@ -0,0 +1,179 @@
Adapted from commit:
From a195c0daaba325aa09180b9492bf78ba03c5af89 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 2/3] 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>
Signed-off-by: Valentin Lefebvre <valentin.lefebvre@suse.com>
Index: Linux-PAM-1.6.1/modules/pam_namespace/namespace.init
===================================================================
--- Linux-PAM-1.6.1.orig/modules/pam_namespace/namespace.init
+++ Linux-PAM-1.6.1/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
Index: Linux-PAM-1.6.1/modules/pam_namespace/pam_namespace.c
===================================================================
--- Linux-PAM-1.6.1.orig/modules/pam_namespace/pam_namespace.c
+++ Linux-PAM-1.6.1/modules/pam_namespace/pam_namespace.c
@@ -1478,6 +1478,79 @@ static int check_inst_parent(int dfd, st
}
/*
+ * 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
* execute it and pass directory to polyinstantiate and instance
@@ -1535,7 +1608,11 @@ static int inst_init(const struct polydi
close_fds_pre_exec(idata);
if (execle(init_script, init_script,
- polyptr->dir, ipath, newdir?"1":"0", idata->user, NULL, envp) < 0)
+ 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) < 0)
_exit(1);
} else if (pid > 0) {
while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) &&

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
Adapted from commit:
From 2c978bab94a0a62e5b8bc0d52a777dca394d90cb 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 3/3] 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.
Signed-off-by: Valentin Lefebvre <valentin.lefebvre@suse.com>
Index: Linux-PAM-1.3.0/modules/pam_namespace/pam_namespace.c
===================================================================
--- Linux-PAM-1.3.0.orig/modules/pam_namespace/pam_namespace.c
+++ Linux-PAM-1.3.0/modules/pam_namespace/pam_namespace.c
@@ -1139,8 +1139,7 @@ static int secure_opendir(const char *pa
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;
}