Backport applicable private-pcp security fixes to PCP 6.3.7 for RHEL 9.9. CVE-2026-16531 is not applicable because the pmproxy logger servlet is absent in this release. Resolves: RHEL-213747 CVE-2026-16530 Resolves: RHEL-213736 CVE-2026-16529 Resolves: RHEL-213711 CVE-2026-16527 Resolves: RHEL-213687 CVE-2026-16526 Resolves: RHEL-213658 CVE-2026-16524 Co-authored-by: Cursor <cursoragent@cursor.com>
208 lines
5.8 KiB
Diff
208 lines
5.8 KiB
Diff
From 7e27614006 Mon Sep 17 00:00:00 2001
|
|
From: Nathan Scott <nathans@redhat.com>
|
|
Subject: [PATCH] libpcp, libpcp_pmda: set FD_CLOEXEC on AF_UNIX sockets (CWE-403)
|
|
|
|
The __pmInitSocket() function returns early for AF_UNIX sockets,
|
|
skipping all subsequent socket hardening including FD_CLOEXEC.
|
|
This causes the pmdaroot Unix socket fd to be inherited by child
|
|
processes spawned via popen()/fork(), enabling privilege escalation
|
|
when combined with the linux_sockets command injection (vuln 3):
|
|
an attacker's popen() child inherits the pmdaroot fd and can send
|
|
a PDUROOT_STARTPMDA_REQ to execute commands as root.
|
|
|
|
Fix:
|
|
- Set FD_CLOEXEC on AF_UNIX sockets in __pmInitSocket() before the
|
|
early return, matching the behavior TCP sockets get via
|
|
__pmConnectRestoreFlags()
|
|
- Set FD_CLOEXEC on pmdarootfd in pmdaRootConnect() after connect()
|
|
succeeds, as belt-and-suspenders for this critical fd
|
|
- Add qa/src/check_cloexec.c and qa/2104 verifying FD_CLOEXEC is set
|
|
on sockets created by __pmCreateUnixSocket()
|
|
|
|
Note: SO_PEERCRED peer credential verification on the pmdaroot server
|
|
side is a separate hardening measure to be addressed as a follow-up.
|
|
|
|
Reported-by: Francisco Alisson Bezerra, TIM Security Red Team
|
|
Reported-by: Lucas Gabriel Alves, TIM Security Red Team
|
|
Reported-by: Massimiliano Brolli, TIM Security Red Team
|
|
|
|
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
|
|
|
Resolves: RHEL-213687 CVE-2026-16526
|
|
---
|
|
diff --git a/qa/2104 b/qa/2104
|
|
new file mode 100755
|
|
index 0000000000..0b2b0b602e
|
|
--- /dev/null
|
|
+++ b/qa/2104
|
|
@@ -0,0 +1,31 @@
|
|
+#!/bin/sh
|
|
+# PCP QA Test No. 2104
|
|
+# Verify AF_UNIX sockets have FD_CLOEXEC set (CWE-403 fix)
|
|
+#
|
|
+# Copyright (c) 2026 Red Hat. All Rights Reserved.
|
|
+#
|
|
+
|
|
+seq=`basename $0`
|
|
+echo "QA output created by $seq"
|
|
+
|
|
+# get standard environment, filters and checks
|
|
+. ./common.product
|
|
+. ./common.filter
|
|
+. ./common.check
|
|
+
|
|
+[ -f src/check_cloexec ] || _notrun "check_cloexec not built"
|
|
+
|
|
+_cleanup()
|
|
+{
|
|
+ cd $here
|
|
+ $sudo rm -rf $tmp $tmp.*
|
|
+}
|
|
+
|
|
+status=0 # success is the default!
|
|
+trap "_cleanup; exit \$status" 0 1 2 3 15
|
|
+
|
|
+# real QA test starts here
|
|
+src/check_cloexec
|
|
+
|
|
+# success, all done
|
|
+exit
|
|
diff --git a/qa/2104.out b/qa/2104.out
|
|
new file mode 100644
|
|
index 0000000000..900a95ec75
|
|
--- /dev/null
|
|
+++ b/qa/2104.out
|
|
@@ -0,0 +1,2 @@
|
|
+QA output created by 2104
|
|
+FD_CLOEXEC is set
|
|
diff --git a/qa/group b/qa/group
|
|
index e538db58e6..c7db6290b2 100644
|
|
--- a/qa/group
|
|
+++ b/qa/group
|
|
@@ -2218,4 +2218,5 @@ pmcd.pdu
|
|
1992 pmda.uwsgi local
|
|
2105 libpcp local security
|
|
2100 pmproxy local security
|
|
+2104 libpcp local security
|
|
4751 libpcp threads valgrind local pcp helgrind
|
|
diff --git a/qa/src/GNUlocaldefs b/qa/src/GNUlocaldefs
|
|
index f838260680..6ee74c7f16 100644
|
|
--- a/qa/src/GNUlocaldefs
|
|
+++ b/qa/src/GNUlocaldefs
|
|
@@ -54,7 +54,8 @@ CFILES = disk_test.c exercise.c context_test.c chkoptfetch.c \
|
|
stampconv.c time_stamp.c archend.c scandata.c wait_for_values.c \
|
|
dumpstack.c usergroup.c derived_help.c ready-or-not.c cleanmapdir.c \
|
|
throttle.c throttle_timeout.c y2038.c bigpmcdpmids.c pdu-gadget.c \
|
|
- newcontext.c
|
|
+ newcontext.c \
|
|
+ check_cloexec.c
|
|
|
|
ifeq ($(shell test -f ../localconfig && echo 1), 1)
|
|
include ../localconfig
|
|
@@ -574,6 +575,11 @@ sortinst: sortinst.c
|
|
# --- need libpcp_import
|
|
#
|
|
|
|
+check_cloexec: check_cloexec.c
|
|
+ rm -f $@
|
|
+ $(CCF) $(CDEFS) -o $@ $@.c $(LDLIBS)
|
|
+ $(LINKER_MAKERULE)
|
|
+
|
|
check_import: check_import.c
|
|
rm -f $@
|
|
$(CCF) $(CDEFS) -o $@ $@.c $(LDLIBS) -lpcp_import
|
|
@@ -880,6 +886,8 @@ xmktime.o: libpcp.h
|
|
xxx.o: libpcp.h
|
|
y2038.o: libpcp.h
|
|
|
|
+check_cloexec.o: libpcp.h
|
|
+
|
|
bozo:
|
|
@echo CFILES_TARGETS=$(CFILES_TARGETS)
|
|
@echo "patsubst ->" $(patsubst %.c,%,$(CFILES_TARGETS))
|
|
diff --git a/qa/src/check_cloexec.c b/qa/src/check_cloexec.c
|
|
new file mode 100644
|
|
index 0000000000..ebc438d301
|
|
--- /dev/null
|
|
+++ b/qa/src/check_cloexec.c
|
|
@@ -0,0 +1,38 @@
|
|
+/*
|
|
+ * Verify that AF_UNIX sockets created by __pmCreateUnixSocket()
|
|
+ * have FD_CLOEXEC set.
|
|
+ */
|
|
+
|
|
+#include <pcp/pmapi.h>
|
|
+#include "libpcp.h"
|
|
+#include <fcntl.h>
|
|
+
|
|
+int
|
|
+main(int argc, char **argv)
|
|
+{
|
|
+ int fd, flags;
|
|
+
|
|
+ pmSetProgname(argv[0]);
|
|
+
|
|
+ fd = __pmCreateUnixSocket();
|
|
+ if (fd < 0) {
|
|
+ fprintf(stderr, "Error: __pmCreateUnixSocket failed: %s\n",
|
|
+ pmErrStr(fd));
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ flags = fcntl(fd, F_GETFD);
|
|
+ if (flags < 0) {
|
|
+ fprintf(stderr, "Error: fcntl F_GETFD failed\n");
|
|
+ close(fd);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ if (flags & FD_CLOEXEC)
|
|
+ printf("FD_CLOEXEC is set\n");
|
|
+ else
|
|
+ printf("FAIL: FD_CLOEXEC is NOT set\n");
|
|
+
|
|
+ close(fd);
|
|
+ return 0;
|
|
+}
|
|
diff --git a/src/libpcp/src/auxconnect.c b/src/libpcp/src/auxconnect.c
|
|
index 62f6f54517..67227e3c6b 100644
|
|
--- a/src/libpcp/src/auxconnect.c
|
|
+++ b/src/libpcp/src/auxconnect.c
|
|
@@ -516,8 +516,12 @@ __pmInitSocket(int fd, int family)
|
|
}
|
|
|
|
#if defined(HAVE_STRUCT_SOCKADDR_UN)
|
|
- if (family == AF_UNIX)
|
|
+ if (family == AF_UNIX) {
|
|
+ int fdFlags;
|
|
+ if ((fdFlags = __pmGetFileDescriptorFlags(fd)) >= 0)
|
|
+ __pmSetFileDescriptorFlags(fd, fdFlags | FD_CLOEXEC);
|
|
return fd;
|
|
+ }
|
|
#endif
|
|
|
|
/* Avoid 200 ms delay. This option is not supported for unix domain sockets. */
|
|
diff --git a/src/libpcp_pmda/src/root.c b/src/libpcp_pmda/src/root.c
|
|
index 1d3223b572..3950e4f1ec 100644
|
|
--- a/src/libpcp_pmda/src/root.c
|
|
+++ b/src/libpcp_pmda/src/root.c
|
|
@@ -32,7 +32,7 @@ pmdaRootConnect(const char *path)
|
|
char *tmpdir;
|
|
char socketpath[MAXPATHLEN];
|
|
char errmsg[PM_MAXERRMSGLEN];
|
|
- int fd, sts, version, features;
|
|
+ int fd, sts, version, features, fdFlags;
|
|
|
|
/* Initialize the socket address. */
|
|
if ((addr = __pmSockAddrAlloc()) == NULL)
|
|
@@ -72,6 +72,9 @@ pmdaRootConnect(const char *path)
|
|
return sts;
|
|
}
|
|
|
|
+ if ((fdFlags = __pmGetFileDescriptorFlags(fd)) >= 0)
|
|
+ __pmSetFileDescriptorFlags(fd, fdFlags | FD_CLOEXEC);
|
|
+
|
|
/* Check server connection information */
|
|
if ((sts = __pmdaRecvRootPDUInfo(fd, &version, &features)) < 0) {
|
|
pmNotifyErr(LOG_ERR,
|