pcp/pcp-7.0.3-pmdaroot-peer-credentials.patch
2026-08-19 05:34:07 -04:00

112 lines
2.7 KiB
Diff

From 2a4bd9f81a Mon Sep 17 00:00:00 2001
From: Nathan Scott <nathans@redhat.com>
Subject: [PATCH] pmdaroot: add peer credential verification on Unix socket (CWE-403)
Defense-in-depth for the FD_CLOEXEC fix: verify the UID of connecting
clients on the pmdaroot Unix socket using SO_PEERCRED (Linux) or
getpeereid (macOS/FreeBSD). Only root (UID 0) and the PCP service
user (typically 'pcp') are permitted to connect. Connections from
other UIDs are rejected with a log message.
This prevents exploitation even if the pmdaroot socket fd were to
leak to an unprivileged process through a path not covered by
FD_CLOEXEC (e.g., direct socket file access).
The PCP service UID is resolved once at startup via pmGetUsername()
and getpwnam(), cached in a static for use in the accept path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---
diff --git a/src/pmdas/root/root.c b/src/pmdas/root/root.c
--- a/src/pmdas/root/root.c
+++ b/src/pmdas/root/root.c
@@ -23,6 +23,9 @@
#include "docker.h"
#include "podman.h"
#include "domain.h"
+#if defined(HAVE_PWD_H)
+#include <pwd.h>
+#endif
#ifndef S_IRWXU
/*
@@ -37,6 +40,7 @@
static __pmSockAddr *socket_addr;
static int socket_fd = -1;
static int pmcd_fd = -1;
+static uid_t pcp_uid;
static __pmFdSet connected_fds;
int root_maximum_fd;
@@ -461,6 +465,42 @@
exit(1);
}
}
+#if defined(HAVE_STRUCT_UCRED)
+ {
+ struct ucred cred;
+ __pmSockLen len = sizeof(cred);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == 0) {
+ if (cred.uid != 0 && cred.uid != pcp_uid) {
+ pmNotifyErr(LOG_ERR,
+ "root_accept_client: rejected uid=%d (expected root or pcp[%d])\n",
+ cred.uid, pcp_uid);
+ close(fd);
+ root_client[i].fd = -1;
+ root_delete_client(&root_client[i]);
+ return NULL;
+ }
+ }
+ }
+#elif defined(HAVE_GETPEEREID)
+ {
+ uid_t uid;
+ gid_t gid;
+
+ if (getpeereid(fd, &uid, &gid) == 0) {
+ if (uid != 0 && uid != pcp_uid) {
+ pmNotifyErr(LOG_ERR,
+ "root_accept_client: rejected uid=%d (expected root or pcp[%d])\n",
+ uid, pcp_uid);
+ close(fd);
+ root_client[i].fd = -1;
+ root_delete_client(&root_client[i]);
+ return NULL;
+ }
+ }
+ }
+#endif
+
if (fd > root_maximum_fd)
root_maximum_fd = fd;
__pmFD_SET(fd, &connected_fds);
@@ -797,6 +837,19 @@
}
static void
+root_get_pcp_uid(void)
+{
+#if defined(HAVE_PWD_H)
+ char *username;
+ struct passwd *pw;
+
+ pmGetUsername(&username);
+ if ((pw = getpwnam(username)) != NULL)
+ pcp_uid = pw->pw_uid;
+#endif
+}
+
+static void
root_check_user(void)
{
#ifdef HAVE_GETUID
@@ -815,6 +868,7 @@
root_prep(void)
{
root_check_user();
+ root_get_pcp_uid();
root_setup_socket();
atexit(root_close_socket);
}