From: Valentine Krasnobaeva Date: Fri, 21 Jun 2024 22:15:20 +0000 (+0200) Subject: MINOR: capabilities: prepare support for version 3 X-Git-Tag: v3.1-dev2~23 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e2e756a67d3ee9ad75dc0bee5ce9030569139d42;p=thirdparty%2Fhaproxy.git MINOR: capabilities: prepare support for version 3 Commit e338d263a76a ("Add 64-bit capability support to the kernel") introduces in the kernel _LINUX_CAPABILITY_VERSION_1 and _LINUX_CAPABILITY_VERSION_2 and its corresponded magic numbers "1" (_LINUX_CAPABILITY_U32S_1) and "2" (_LINUX_CAPABILITY_VERSION_2). Capabilities sets, since this commit, are composed as an arrays of __user_cap_data_struct with length defined in version's magic number (e.g. struct __user_cap_data_struct kdata[_LINUX_CAPABILITY_U32S_1]). These magic numbers also help the kernel to figure out how many data (in __user_cap_data_struct "units") it needs to copy_from/to_user in capset/capget syscalls. In order to use _LINUX_CAPABILITY_VERSION_3 in the next commit (it has the same functionality as version 2), let's follow the kernel code and let's allocate memory to store 32-capabilities as an array of __user_cap_data_struct with the length of 1 (_LINUX_CAPABILITY_U32S_1). --- diff --git a/src/linuxcap.c b/src/linuxcap.c index 1e87aed693..6c2e5eb650 100644 --- a/src/linuxcap.c +++ b/src/linuxcap.c @@ -82,7 +82,12 @@ static uint32_t caplist; */ int prepare_caps_from_permitted_set(int from_uid, int to_uid, const char *program_name) { - struct __user_cap_data_struct start_cap_data = { }; + /* _LINUX_CAPABILITY_U32S_1 = 1 and corresponds to version 1, which is three + * 32-bit integers set. So kernel in capset()/capget() will copy_from/to_user + * only _LINUX_CAPABILITY_U32S_1 * (sizeof(struct __user_cap_data_struct)), + * i.e. only the __user_cap_data_struct[0]. + */ + struct __user_cap_data_struct start_cap_data[_LINUX_CAPABILITY_U32S_1] = { }; /* started as root */ if (!from_uid) @@ -97,7 +102,7 @@ int prepare_caps_from_permitted_set(int from_uid, int to_uid, const char *progra * these capabilities and the file effective bit on haproxy binary via * setcap, see capabilities man page for details. */ - if (capget(&cap_hdr_haproxy, &start_cap_data) == -1) { + if (capget(&cap_hdr_haproxy, start_cap_data) == -1) { if (global.last_checks & (LSTCHK_NETADM | LSTCHK_SYSADM)) ha_diag_warning("Failed to get process capabilities using capget(): %s. " "Can't use capabilities that might be set on %s binary " @@ -105,12 +110,12 @@ int prepare_caps_from_permitted_set(int from_uid, int to_uid, const char *progra return 0; } - if (start_cap_data.effective & ((1 << CAP_NET_ADMIN)|(1 << CAP_NET_RAW))) { + if (start_cap_data[0].effective & ((1 << CAP_NET_ADMIN)|(1 << CAP_NET_RAW))) { global.last_checks &= ~LSTCHK_NETADM; return 0; } - if (start_cap_data.effective & ((1 << CAP_SYS_ADMIN))) { + if (start_cap_data[0].effective & ((1 << CAP_SYS_ADMIN))) { global.last_checks &= ~LSTCHK_SYSADM; return 0; } @@ -120,9 +125,9 @@ int prepare_caps_from_permitted_set(int from_uid, int to_uid, const char *progra * set, if it is in the caplist and also presented in the binary * permitted set. */ - if (caplist && start_cap_data.permitted & caplist) { - start_cap_data.effective |= start_cap_data.permitted & caplist; - if (capset(&cap_hdr_haproxy, &start_cap_data) == 0) { + if (caplist && start_cap_data[0].permitted & caplist) { + start_cap_data[0].effective |= start_cap_data[0].permitted & caplist; + if (capset(&cap_hdr_haproxy, start_cap_data) == 0) { if (caplist & ((1 << CAP_NET_ADMIN)|(1 << CAP_NET_RAW))) global.last_checks &= ~LSTCHK_NETADM; if (caplist & (1 << CAP_SYS_ADMIN)) @@ -152,7 +157,12 @@ int prepare_caps_from_permitted_set(int from_uid, int to_uid, const char *progra */ int prepare_caps_for_setuid(int from_uid, int to_uid) { - struct __user_cap_data_struct cap_data = { }; + /* _LINUX_CAPABILITY_U32S_1 = 1 and corresponds to version 1, which is three + * 32-bit integers set. So kernel in capset()/capget() will copy_from/to_user + * only _LINUX_CAPABILITY_U32S_1 * (sizeof(struct __user_cap_data_struct)), + * i.e. only the __user_cap_data_struct[0]. + */ + struct __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_1] = { }; if (from_uid != 0) return 0; @@ -168,8 +178,8 @@ int prepare_caps_for_setuid(int from_uid, int to_uid) return -1; } - cap_data.effective = cap_data.permitted = caplist | (1 << CAP_SETUID); - if (capset(&cap_hdr_haproxy, &cap_data) == -1) { + cap_data[0].effective = cap_data[0].permitted = caplist | (1 << CAP_SETUID); + if (capset(&cap_hdr_haproxy, cap_data) == -1) { ha_alert("Failed to preset the capabilities to preserve using capset(): %s\n", strerror(errno)); return -1; } @@ -179,8 +189,8 @@ int prepare_caps_for_setuid(int from_uid, int to_uid) return -1; } - cap_data.effective = cap_data.permitted = caplist | (1 << CAP_SETUID); - if (capset(&cap_hdr_haproxy, &cap_data) == -1) { + cap_data[0].effective = cap_data[0].permitted = caplist | (1 << CAP_SETUID); + if (capset(&cap_hdr_haproxy, cap_data) == -1) { ha_alert("Failed to set the final capabilities using capset(): %s\n", strerror(errno)); return -1; } @@ -201,7 +211,7 @@ int prepare_caps_for_setuid(int from_uid, int to_uid) */ int finalize_caps_after_setuid(int from_uid, int to_uid) { - struct __user_cap_data_struct cap_data = { }; + struct __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_1] = { }; if (from_uid != 0) return 0; @@ -212,11 +222,12 @@ int finalize_caps_after_setuid(int from_uid, int to_uid) if (!caplist) return 0; - cap_data.effective = cap_data.permitted = caplist; - if (capset(&cap_hdr_haproxy, &cap_data) == -1) { + cap_data[0].effective = cap_data[0].permitted = caplist; + if (capset(&cap_hdr_haproxy, cap_data) == -1) { ha_alert("Failed to drop the setuid capability using capset(): %s\n", strerror(errno)); return -1; } + /* all's good */ return 0; }