From ee4c5d45ab9097ebdc56296d6f5553a38e3ace33 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Mon, 23 Feb 2026 13:25:31 -0800 Subject: [PATCH 1/4] Add CAP_CHOWN to permitted capability set Add CAP_CHOWN to the permitted capability set retained after privilege drop. This allows plugins that perform cert file backup writes to set root ownership on newly created files when certs are restricted to root:root 600. Like CAP_DAC_OVERRIDE, CAP_CHOWN is held in the permitted set only and must be explicitly promoted to the effective set before use. It is not active during normal operation. --- src/tscore/ink_cap.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tscore/ink_cap.cc b/src/tscore/ink_cap.cc index f464daad3b1..6f12d0e5b3a 100644 --- a/src/tscore/ink_cap.cc +++ b/src/tscore/ink_cap.cc @@ -273,7 +273,7 @@ RestrictCapabilities() cap_t caps_orig = cap_get_proc(); // Capabilities we need. - cap_value_t perm_list[] = {CAP_NET_ADMIN, CAP_NET_BIND_SERVICE, CAP_IPC_LOCK, CAP_DAC_OVERRIDE, CAP_FOWNER}; + cap_value_t perm_list[] = {CAP_NET_ADMIN, CAP_NET_BIND_SERVICE, CAP_IPC_LOCK, CAP_DAC_OVERRIDE, CAP_FOWNER, CAP_CHOWN}; static int const PERM_CAP_COUNT = sizeof(perm_list) / sizeof(*perm_list); cap_value_t eff_list[] = {CAP_NET_ADMIN, CAP_NET_BIND_SERVICE, CAP_IPC_LOCK}; static int const EFF_CAP_COUNT = sizeof(eff_list) / sizeof(*eff_list); From 8bf8f83274097850e8f545395b01e9b182a68335 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Tue, 3 Mar 2026 08:34:11 -0800 Subject: [PATCH 2/4] Add CHOWN_PRIVILEGE to ElevateAccess Add CHOWN_PRIVILEGE (0x10u) to the ElevateAccess privilege_level enum and wire up CAP_CHOWN in acquirePrivilege() so plugins can elevate file ownership capability through the standard ATS privilege API. --- include/tscore/ink_cap.h | 3 ++- src/tscore/ink_cap.cc | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/tscore/ink_cap.h b/include/tscore/ink_cap.h index 86a8f31f4d5..f1e50b9f749 100644 --- a/include/tscore/ink_cap.h +++ b/include/tscore/ink_cap.h @@ -81,8 +81,9 @@ class ElevateAccess FILE_PRIVILEGE = 0x1u, ///< Access filesystem objects with privilege TRACE_PRIVILEGE = 0x2u, ///< Trace other processes with privilege LOW_PORT_PRIVILEGE = 0x4u, ///< Bind to privilege ports. - OWNER_PRIVILEGE = 0x8u ///< Bypass permission checks on operations that normally require + OWNER_PRIVILEGE = 0x8u, ///< Bypass permission checks on operations that normally require /// filesystem UID & process UID to match + CHOWN_PRIVILEGE = 0x10u ///< Change file ownership }; ElevateAccess(unsigned level = FILE_PRIVILEGE); diff --git a/src/tscore/ink_cap.cc b/src/tscore/ink_cap.cc index 6f12d0e5b3a..6841c462b02 100644 --- a/src/tscore/ink_cap.cc +++ b/src/tscore/ink_cap.cc @@ -436,7 +436,7 @@ void ElevateAccess::acquirePrivilege(unsigned priv_mask) { unsigned cap_count = 0; - cap_value_t cap_list[3]; + cap_value_t cap_list[4]; cap_t new_cap_state; Dbg(dbg_ctl_privileges, "[acquirePrivilege] level= %x", level); @@ -463,6 +463,11 @@ ElevateAccess::acquirePrivilege(unsigned priv_mask) ++cap_count; } + if (priv_mask & ElevateAccess::CHOWN_PRIVILEGE) { + cap_list[cap_count] = CAP_CHOWN; + ++cap_count; + } + ink_release_assert(cap_count <= sizeof(cap_list)); if (cap_count > 0) { From 95182960046e81e5c482fa5cd1214f7777a7c186 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Thu, 12 Mar 2026 15:40:29 -0700 Subject: [PATCH 3/4] Fix bounds-check assertion in acquirePrivilege() The assertion compared cap_count against sizeof(cap_list) which returns the byte size (16), not the element count (4). This could never catch an actual array overflow. --- include/tscore/ink_cap.h | 2 +- src/tscore/ink_cap.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/tscore/ink_cap.h b/include/tscore/ink_cap.h index f1e50b9f749..d8987929e8b 100644 --- a/include/tscore/ink_cap.h +++ b/include/tscore/ink_cap.h @@ -83,7 +83,7 @@ class ElevateAccess LOW_PORT_PRIVILEGE = 0x4u, ///< Bind to privilege ports. OWNER_PRIVILEGE = 0x8u, ///< Bypass permission checks on operations that normally require /// filesystem UID & process UID to match - CHOWN_PRIVILEGE = 0x10u ///< Change file ownership + CHOWN_PRIVILEGE = 0x10u ///< Change file ownership }; ElevateAccess(unsigned level = FILE_PRIVILEGE); diff --git a/src/tscore/ink_cap.cc b/src/tscore/ink_cap.cc index 6841c462b02..1e208f34cfb 100644 --- a/src/tscore/ink_cap.cc +++ b/src/tscore/ink_cap.cc @@ -468,7 +468,7 @@ ElevateAccess::acquirePrivilege(unsigned priv_mask) ++cap_count; } - ink_release_assert(cap_count <= sizeof(cap_list)); + ink_release_assert(cap_count <= sizeof(cap_list) / sizeof(cap_list[0])); if (cap_count > 0) { this->cap_state = cap_get_proc(); // save current capabilities From 30c86c766e15d0300a54582f45c1e09cf19cdad5 Mon Sep 17 00:00:00 2001 From: Bryan Call Date: Thu, 12 Mar 2026 15:46:20 -0700 Subject: [PATCH 4/4] Simplify OWNER_PRIVILEGE comment to single line Replace the two-line comment with a concise description that covers the full scope of CAP_FOWNER, not just chmod. --- include/tscore/ink_cap.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/tscore/ink_cap.h b/include/tscore/ink_cap.h index d8987929e8b..a18c3449ac1 100644 --- a/include/tscore/ink_cap.h +++ b/include/tscore/ink_cap.h @@ -81,8 +81,7 @@ class ElevateAccess FILE_PRIVILEGE = 0x1u, ///< Access filesystem objects with privilege TRACE_PRIVILEGE = 0x2u, ///< Trace other processes with privilege LOW_PORT_PRIVILEGE = 0x4u, ///< Bind to privilege ports. - OWNER_PRIVILEGE = 0x8u, ///< Bypass permission checks on operations that normally require - /// filesystem UID & process UID to match + OWNER_PRIVILEGE = 0x8u, ///< Owner-only operations on unowned files (CAP_FOWNER) CHOWN_PRIVILEGE = 0x10u ///< Change file ownership };