Fix local privilege escalation via cifs.spnego
- resolves: RHEL-185759 - Fix CVE-2026-12505 Signed-off-by: Paulo Alcantara <paalcant@redhat.com>
This commit is contained in:
parent
b8989baea7
commit
8a813204d2
@ -3,7 +3,7 @@
|
||||
|
||||
Name: cifs-utils
|
||||
Version: 7.0
|
||||
Release: 3%{pre_release}%{?dist}
|
||||
Release: 4%{pre_release}%{?dist}
|
||||
Summary: Utilities for mounting and managing CIFS mounts
|
||||
|
||||
Group: System Environment/Daemons
|
||||
@ -22,6 +22,8 @@ Patch1: 0001-Use-explicit-usr-bin-python3.patch
|
||||
Patch2: mount.cifs.rst-add-missing-reference-for-sssd.patch
|
||||
Patch3: mount.cifs.rst-update-section-about-xattr-acl-suppor.patch
|
||||
Patch4: docs-update-echo_interval-description.patch
|
||||
Patch5: cifs.upcall-remove-getpwuid-dependency.patch
|
||||
Patch6: cifs.upcall-fix-compiler-warning-with-Wvla.patch
|
||||
|
||||
%description
|
||||
The SMB/CIFS protocol is a standard file sharing protocol widely deployed
|
||||
@ -60,6 +62,8 @@ provide these credentials to the kernel automatically at login.
|
||||
%patch2 -p1
|
||||
%patch3 -p1
|
||||
%patch4 -p1
|
||||
%patch5 -p1
|
||||
%patch6 -p1
|
||||
|
||||
%build
|
||||
autoreconf -i
|
||||
@ -119,6 +123,11 @@ fi
|
||||
%{_mandir}/man8/pam_cifscreds.8.gz
|
||||
|
||||
%changelog
|
||||
* Thu Jun 25 2026 Paulo Alcantara <paalcant@redhat.com> - 7.0-4
|
||||
- cifs.upcall: remove getpwuid dependency
|
||||
- cifs.upcall: fix compiler warning with -Wvla
|
||||
- Resolves: RHEL-185759 - Fix CVE-2026-12505
|
||||
|
||||
* Thu Feb 19 2026 Paulo Alcantara <paalcant@redhat.com> - 7.0-3
|
||||
- docs: update echo_interval description
|
||||
- Resolves: RHEL-80397
|
||||
|
||||
46
cifs.upcall-fix-compiler-warning-with-Wvla.patch
Normal file
46
cifs.upcall-fix-compiler-warning-with-Wvla.patch
Normal file
@ -0,0 +1,46 @@
|
||||
From d2f39a20d68aa55023b63e575f06941721e644c6 Mon Sep 17 00:00:00 2001
|
||||
From: Paulo Alcantara <pc@manguebit.org>
|
||||
Date: Tue, 23 Jun 2026 15:06:20 -0300
|
||||
Subject: [PATCH] cifs.upcall: fix compiler warning with -Wvla
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The length value for @path array in get_uidgid() needs to be evaluated
|
||||
at compile time, so replace strlen() with sizeof() when defining
|
||||
PROC_PID_PATH_MAXLEN and then fix the following warning:
|
||||
|
||||
cifs.upcall.c: In function ‘get_uidgid’:
|
||||
cifs.upcall.c:1400:9: warning: ISO C90 forbids array ‘path’ whose size
|
||||
cannot be evaluated [-Wvla]
|
||||
1400 | char path[PROC_PID_PATH_MAXLEN] = {}, buf[256];
|
||||
| ^~~~
|
||||
|
||||
Fixes: 972c5b5ff95e ("cifs.upcall: remove getpwuid() dependency")
|
||||
Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
|
||||
Reviewed-by: David Howells <dhowells@redhat.com>
|
||||
Cc: Enzo Matsumiya <ematsumiya@suse.de>
|
||||
Cc: linux-cifs@vger.kernel.org
|
||||
Signed-off-by: Steve French <stfrench@microsoft.com>
|
||||
---
|
||||
cifs.upcall.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/cifs.upcall.c b/cifs.upcall.c
|
||||
index 01690dfcade1..11dbc6186a74 100644
|
||||
--- a/cifs.upcall.c
|
||||
+++ b/cifs.upcall.c
|
||||
@@ -1375,8 +1375,8 @@ static int ip_to_fqdn(const char *addrstr, char *host, size_t hostlen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-/* cover worst case/impossible scenarios, + 1 for NUL */
|
||||
-#define PROC_PID_PATH_MAXLEN ((int)strlen("/proc/2147483647/status") + 1)
|
||||
+/* cover worst case/impossible scenarios */
|
||||
+#define PROC_PID_PATH_MAXLEN ((int)sizeof("/proc/2147483647/status"))
|
||||
/* max valid UID/GID is (UINT_MAX - 1) */
|
||||
#define INVALID_UIDGID UINT_MAX
|
||||
|
||||
--
|
||||
2.54.0
|
||||
|
||||
251
cifs.upcall-remove-getpwuid-dependency.patch
Normal file
251
cifs.upcall-remove-getpwuid-dependency.patch
Normal file
@ -0,0 +1,251 @@
|
||||
From 972c5b5ff95e3e812bc8daa72d0383654ab0dba7 Mon Sep 17 00:00:00 2001
|
||||
From: Enzo Matsumiya <ematsumiya@suse.de>
|
||||
Date: Mon, 15 Jun 2026 14:25:22 -0300
|
||||
Subject: [PATCH] cifs.upcall: remove getpwuid() dependency
|
||||
|
||||
This patch removes getpwuid() and resort to getting UID/GID from
|
||||
/proc/pid/status
|
||||
|
||||
Changes:
|
||||
- add get_uidgid() helper
|
||||
- drop supplementary groups (i.e. move setgroups(0, NULL)) before even
|
||||
decoding spnego key -- those are never needed in any case
|
||||
|
||||
Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
|
||||
Reported-by: Shaomin Chen <eeesssooo020@gmail.com>
|
||||
Acked-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
|
||||
Acked-by: Pavel Shilovskiy <pshilovskiy@microsoft.com>
|
||||
Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
|
||||
Signed-off-by: Steve French <stfrench@microsoft.com>
|
||||
---
|
||||
cifs.upcall.c | 180 ++++++++++++++++++++++++++++++++++++++++----------
|
||||
1 file changed, 145 insertions(+), 35 deletions(-)
|
||||
|
||||
diff --git a/cifs.upcall.c b/cifs.upcall.c
|
||||
index 52c03280dbe0..ae3294dd61f0 100644
|
||||
--- a/cifs.upcall.c
|
||||
+++ b/cifs.upcall.c
|
||||
@@ -51,7 +51,6 @@
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
-#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
@@ -1265,6 +1264,121 @@ static int ip_to_fqdn(const char *addrstr, char *host, size_t hostlen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/* cover worst case/impossible scenarios, + 1 for NUL */
|
||||
+#define PROC_PID_PATH_MAXLEN ((int)strlen("/proc/2147483647/status") + 1)
|
||||
+/* max valid UID/GID is (UINT_MAX - 1) */
|
||||
+#define INVALID_UIDGID UINT_MAX
|
||||
+
|
||||
+/*
|
||||
+ * get_uidgid - Get @pid's (real) UID and/or GID.
|
||||
+ * @pid: process to get UID/GID from
|
||||
+ * @uidp: pointer to store @pid's UID (can be NULL)
|
||||
+ * @gidp: pointer to store @pid's GID (can be NULL)
|
||||
+ *
|
||||
+ * Extract "Uid:" and "Gid:" fields from /proc/@pid/status.
|
||||
+ * Do so based on whether @uidp or @gidp are NULL.
|
||||
+ *
|
||||
+ * This function assumes we're on the same namespace as @pid.
|
||||
+ *
|
||||
+ * Return: 0 on success, -1 otherwise (errno set).
|
||||
+ *
|
||||
+ * On errors, *@uidp and *@gidp are set to INVALID_UIDGID.
|
||||
+ */
|
||||
+static int get_uidgid(pid_t pid, uid_t *uidp, gid_t *gidp)
|
||||
+{
|
||||
+ char path[PROC_PID_PATH_MAXLEN] = {}, buf[256];
|
||||
+ FILE *fp = NULL;
|
||||
+ int ret;
|
||||
+
|
||||
+ errno = 0;
|
||||
+ if (pid < 0 || (!uidp && !gidp)) {
|
||||
+ errno = EINVAL;
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ if (uidp)
|
||||
+ *uidp = INVALID_UIDGID;
|
||||
+
|
||||
+ if (gidp)
|
||||
+ *gidp = INVALID_UIDGID;
|
||||
+
|
||||
+ ret = snprintf(path, PROC_PID_PATH_MAXLEN, "/proc/%d/status", pid);
|
||||
+ if (ret < 0 || ret >= PROC_PID_PATH_MAXLEN) {
|
||||
+ if (!errno)
|
||||
+ errno = ENAMETOOLONG;
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ fp = fopen(path, "r");
|
||||
+ if (!fp) {
|
||||
+ ret = -1;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ /* Parse /proc/pid/status fields */
|
||||
+ errno = 0;
|
||||
+ ret = -1;
|
||||
+ while (fgets(buf, 256, fp)) {
|
||||
+ unsigned long long val;
|
||||
+
|
||||
+ errno = ENODATA;
|
||||
+ if ((!uidp || strncmp(buf, "Uid:", 4)) && (!gidp || strncmp(buf, "Gid:", 4)))
|
||||
+ continue;
|
||||
+
|
||||
+ errno = 0;
|
||||
+
|
||||
+ /*
|
||||
+ * Example line format (same for both Uid/Gid):
|
||||
+ * "Uid:\t%u\t%u\%u\%u"
|
||||
+ *
|
||||
+ * Where the numbers represents:
|
||||
+ * <real> <effective> <saved> <fsuid>
|
||||
+ *
|
||||
+ * We're only interested in the <real> value.
|
||||
+ *
|
||||
+ * (field names "Uid:"/"Gid:" parsed above, skip it)
|
||||
+ */
|
||||
+ ret = sscanf(&buf[0] + 4, "%llu", &val);
|
||||
+ if (ret != 1) {
|
||||
+ ret = -1;
|
||||
+ if (errno)
|
||||
+ break;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ ret = -1;
|
||||
+ if (val >= UINT_MAX) {
|
||||
+ errno = EINVAL;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (uidp && !strncmp(buf, "Uid:", 4))
|
||||
+ *uidp = (uid_t)val;
|
||||
+ else
|
||||
+ *gidp = (gid_t)val;
|
||||
+
|
||||
+ if ((!uidp || *uidp != INVALID_UIDGID) && (!gidp || *gidp != INVALID_UIDGID)) {
|
||||
+ errno = 0;
|
||||
+ ret = 0;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+out:
|
||||
+ if (fp)
|
||||
+ fclose(fp);
|
||||
+
|
||||
+ if (ret) {
|
||||
+ syslog(LOG_DEBUG, "%s(pid=%d): %s", __func__, pid, strerror(errno));
|
||||
+ if (uidp)
|
||||
+ *uidp = INVALID_UIDGID;
|
||||
+
|
||||
+ if (gidp)
|
||||
+ *gidp = INVALID_UIDGID;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
/* walk a string and lowercase it in-place */
|
||||
static void
|
||||
lowercase_string(char *c)
|
||||
@@ -1306,10 +1420,10 @@ int main(const int argc, char *const argv[])
|
||||
struct decoded_args *arg = NULL;
|
||||
const char *oid;
|
||||
uid_t uid;
|
||||
+ gid_t gid;
|
||||
char *keytab_name = NULL;
|
||||
char *env_cachename = NULL;
|
||||
krb5_ccache ccache = NULL;
|
||||
- struct passwd *pw;
|
||||
unsigned expire_time = DNS_RESOLVER_DEFAULT_TIMEOUT;
|
||||
const char *key_descr = NULL;
|
||||
|
||||
@@ -1396,6 +1510,17 @@ int main(const int argc, char *const argv[])
|
||||
* Otherwise, it's a spnego key request
|
||||
*/
|
||||
|
||||
+ /*
|
||||
+ * We don't need supplementary groups, ever.
|
||||
+ * Drop them ASAP.
|
||||
+ */
|
||||
+ rc = setgroups(0, NULL);
|
||||
+ if (rc == -1) {
|
||||
+ syslog(LOG_ERR, "setgroups: %s", strerror(errno));
|
||||
+ rc = 1;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
rc = decode_key_description(buf, &arg);
|
||||
free(buf);
|
||||
if (rc) {
|
||||
@@ -1450,38 +1575,6 @@ int main(const int argc, char *const argv[])
|
||||
if (trim_capabilities(env_probe))
|
||||
goto out;
|
||||
|
||||
- /*
|
||||
- * The kernel doesn't pass down the gid, so we resort here to scraping
|
||||
- * one out of the passwd nss db. Note that this might not reflect the
|
||||
- * actual gid of the process that initiated the upcall. While we could
|
||||
- * scrape that out of /proc, relying on that is a bit more risky.
|
||||
- */
|
||||
- pw = getpwuid(uid);
|
||||
- if (!pw) {
|
||||
- syslog(LOG_ERR, "Unable to find pw entry for uid %d: %s\n",
|
||||
- uid, strerror(errno));
|
||||
- rc = 1;
|
||||
- goto out;
|
||||
- }
|
||||
-
|
||||
- /*
|
||||
- * The kernel should send down a zero-length grouplist already, but
|
||||
- * just to be on the safe side...
|
||||
- */
|
||||
- rc = setgroups(0, NULL);
|
||||
- if (rc == -1) {
|
||||
- syslog(LOG_ERR, "setgroups: %s", strerror(errno));
|
||||
- rc = 1;
|
||||
- goto out;
|
||||
- }
|
||||
-
|
||||
- rc = setgid(pw->pw_gid);
|
||||
- if (rc == -1) {
|
||||
- syslog(LOG_ERR, "setgid: %s", strerror(errno));
|
||||
- rc = 1;
|
||||
- goto out;
|
||||
- }
|
||||
-
|
||||
/*
|
||||
* We can't reasonably do this for root. When mounting a DFS share,
|
||||
* for instance we can end up with creds being overridden, but the env
|
||||
@@ -1490,6 +1583,24 @@ int main(const int argc, char *const argv[])
|
||||
if (uid == 0)
|
||||
env_probe = false;
|
||||
|
||||
+ /*
|
||||
+ * FIXME: this only works if we haven't switched PID namespaces.
|
||||
+ * If we did, /proc/arg->pid/ might not exist, or worse, point to something else.
|
||||
+ */
|
||||
+ rc = get_uidgid(arg->pid, &uid, &gid);
|
||||
+ if (rc) {
|
||||
+ syslog(LOG_ERR, "get_uidgid (NS): %s", strerror(errno));
|
||||
+ rc = 1;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ rc = setgid(gid);
|
||||
+ if (rc) {
|
||||
+ syslog(LOG_ERR, "setgid: %s", strerror(errno));
|
||||
+ rc = 1;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* Must do this before setuid, as we need elevated capabilities to
|
||||
* look at the environ file.
|
||||
Loading…
Reference in New Issue
Block a user