From e84112fd7128e05a1a4a380d8242c672d1f539f9 Mon Sep 17 00:00:00 2001 From: xuxuepeng Date: Mon, 13 Nov 2023 08:44:15 +0000 Subject: [PATCH 09/14] !2243 Refactor capbilities specs * Refactor capbilities specs --- src/cmd/isula/isula_host_spec.c | 1 + src/daemon/modules/spec/specs.c | 12 +- src/daemon/modules/spec/specs_security.c | 140 ++++---- src/utils/cutils/utils_cap.c | 119 +++++++ src/utils/cutils/utils_cap.h | 39 +++ src/utils/cutils/utils_verify.c | 81 ----- src/utils/cutils/utils_verify.h | 6 - test/cutils/utils_verify/utils_verify_ut.cc | 14 - .../image/oci/oci_config_merge/CMakeLists.txt | 1 + test/image/oci/storage/images/CMakeLists.txt | 1 + test/specs/specs/CMakeLists.txt | 1 + test/specs/specs/oci_runtime_spec.json | 32 ++ test/specs/specs/specs_ut.cc | 315 ++++++++++++++++++ test/specs/specs_extend/CMakeLists.txt | 1 + 14 files changed, 573 insertions(+), 190 deletions(-) create mode 100644 src/utils/cutils/utils_cap.c create mode 100644 src/utils/cutils/utils_cap.h diff --git a/src/cmd/isula/isula_host_spec.c b/src/cmd/isula/isula_host_spec.c index 6f39588d..09dea271 100644 --- a/src/cmd/isula/isula_host_spec.c +++ b/src/cmd/isula/isula_host_spec.c @@ -36,6 +36,7 @@ #include "utils_file.h" #include "utils_string.h" #include "utils_verify.h" +#include "utils_cap.h" #include "opt_ulimit.h" static bool parse_restart_policy(const char *policy, host_config_restart_policy **rp) diff --git a/src/daemon/modules/spec/specs.c b/src/daemon/modules/spec/specs.c index 95346603..cc49d85f 100644 --- a/src/daemon/modules/spec/specs.c +++ b/src/daemon/modules/spec/specs.c @@ -49,6 +49,7 @@ #include "utils_file.h" #include "utils_string.h" #include "utils_verify.h" +#include "utils_cap.h" #ifndef CLONE_NEWUTS #define CLONE_NEWUTS 0x04000000 @@ -814,15 +815,16 @@ static int adapt_settings_for_privileged(oci_runtime_spec *oci_spec, bool privil { int ret = 0; size_t all_caps_len = 0; + const char **all_caps = NULL; if (!privileged) { return 0; } - all_caps_len = util_get_all_caps_len(); - if (oci_spec == NULL) { - ret = -1; - goto out; + all_caps = util_get_all_caps(&all_caps_len); + if (all_caps == NULL) { + ERROR("Failed to get all capabilities"); + return -1; } clean_correlated_items(oci_spec); @@ -838,7 +840,7 @@ static int adapt_settings_for_privileged(oci_runtime_spec *oci_spec, bool privil goto out; } - ret = refill_oci_process_capabilities(&oci_spec->process->capabilities, g_all_caps, all_caps_len); + ret = refill_oci_process_capabilities(&oci_spec->process->capabilities, all_caps, all_caps_len); if (ret != 0) { ERROR("Failed to copy all capabilities"); ret = -1; diff --git a/src/daemon/modules/spec/specs_security.c b/src/daemon/modules/spec/specs_security.c index e78cc744..b34aec7c 100644 --- a/src/daemon/modules/spec/specs_security.c +++ b/src/daemon/modules/spec/specs_security.c @@ -37,6 +37,7 @@ #include "utils_array.h" #include "utils_string.h" #include "utils_verify.h" +#include "utils_cap.h" #define MAX_CAP_LEN 32 @@ -104,41 +105,31 @@ static int tweak_drops_capabilities(char ***new_caps, size_t *new_caps_len, char size_t i = 0; int ret = 0; - if (util_strings_in_slice((const char **)drops, drops_len, "all")) { - goto out; + if (basic_caps == NULL || basic_caps_len == 0) { + *new_caps = NULL; + *new_caps_len = 0; + return 0; } - for (i = 0; (basic_caps != NULL && i < basic_caps_len); i++) { - // skip `all` already handled above - if (!basic_caps[i] || !strcasecmp(basic_caps[i], "all")) { - continue; - } - - // if we don't drop `all`, add back all the non-dropped caps + for (i = 0; i < basic_caps_len; i++) { if (!util_strings_in_slice((const char **)drops, drops_len, basic_caps[i] + strlen("CAP_"))) { ret = append_capability(new_caps, new_caps_len, basic_caps[i]); if (ret != 0) { ERROR("Failed to append capabilities"); - ret = -1; - goto out; + return -1; } } } -out: - return ret; + return 0; } static int tweak_adds_capabilities(char ***new_caps, size_t *new_caps_len, const char **adds, size_t adds_len) { size_t i = 0; - int ret = 0; int nret = 0; - size_t all_caps_len = 0; char tmpcap[MAX_CAP_LEN] = { 0 }; - all_caps_len = util_get_all_caps_len(); - for (i = 0; i < adds_len; i++) { // skip `all` already handled above if (strcasecmp(adds[i], "all") == 0) { @@ -148,111 +139,92 @@ static int tweak_adds_capabilities(char ***new_caps, size_t *new_caps_len, const nret = snprintf(tmpcap, sizeof(tmpcap), "CAP_%s", adds[i]); if (nret < 0 || (size_t)nret >= sizeof(tmpcap)) { ERROR("Failed to print string"); - ret = -1; - goto out; - } - if (!util_strings_in_slice(g_all_caps, all_caps_len, tmpcap)) { - ERROR("Unknown capability to add: '%s'", tmpcap); - ret = -1; - goto out; + return -1; } // add cap if not already in the list if (!util_strings_in_slice((const char **)*new_caps, *new_caps_len, tmpcap)) { - ret = append_capability(new_caps, new_caps_len, tmpcap); - if (ret != 0) { + nret = append_capability(new_caps, new_caps_len, tmpcap); + if (nret != 0) { ERROR("Failed to append capabilities"); - ret = -1; - goto out; + return -1; } } } -out: - return ret; -} - -static bool valid_drops_cap(const char **drops, size_t drops_len) -{ - int nret = 0; - size_t i; - size_t all_caps_len = 0; - char tmpcap[MAX_CAP_LEN] = { 0 }; - - all_caps_len = util_get_all_caps_len(); - // look for invalid cap in the drop list - for (i = 0; i < drops_len; i++) { - if (strcasecmp(drops[i], "all") == 0) { - continue; - } - - nret = snprintf(tmpcap, sizeof(tmpcap), "CAP_%s", drops[i]); - if (nret < 0 || (size_t)nret >= sizeof(tmpcap)) { - ERROR("Failed to print string"); - return false; - } - if (!util_strings_in_slice(g_all_caps, all_caps_len, tmpcap)) { - ERROR("Unknown capability to drop: '%s'", drops[i]); - return false; - } - } - - return true; + return 0; } // tweak_capabilities can tweak capabilities by adding or dropping capabilities -// based on the basic capabilities. +// based on the basic capabilities. The following are the priorities of the tweaks: +// 1. if adds contains "all", then the basic capabilities will be ignored, and all capabilities will be added. +// 2. if drops contains "all", all capabilities will be dropped. +// 3. add individual capabilities in adds +// 4. drop individual capabilities in drops. +// The reason why we handle "all" first is that we can avoid the case that the individual capabilities are +// not included by "all". static int tweak_capabilities(char ***caps, size_t *caps_len, const char **adds, size_t adds_len, const char **drops, size_t drops_len) { - size_t i; - size_t all_caps_len = 0; int ret = 0; char **new_caps = NULL; char **basic_caps = NULL; + const char **all_caps = NULL; size_t new_caps_len = 0; size_t basic_caps_len = 0; + size_t all_caps_len = 0; + bool add_all = false; + bool drop_all = false; - all_caps_len = util_get_all_caps_len(); - if (!valid_drops_cap(drops, drops_len)) { + all_caps = util_get_all_caps(&all_caps_len); + if (all_caps == NULL) { + ERROR("Failed to get all capabilities"); return -1; } - if (util_strings_in_slice((const char **)adds, adds_len, "all")) { - ret = copy_capabilities(&basic_caps, &basic_caps_len, g_all_caps, all_caps_len); - } else { + add_all = util_strings_in_slice((const char **)adds, adds_len, "all"); + drop_all = util_strings_in_slice((const char **)drops, drops_len, "all"); + + + if (!add_all && !drop_all) { + // if neither add_all nor drop_all, we start with the default capabilities ret = copy_capabilities(&basic_caps, &basic_caps_len, (const char **)*caps, *caps_len); - } - if (ret != 0) { - ERROR("Failed to copy capabilities"); - ret = -1; - goto free_out; + if (ret != 0) { + ERROR("Failed to copy capabilities"); + ret = -1; + goto free_out; + } + } else if (drop_all) { + // if drop_all, we start with an empty set + basic_caps = NULL; + basic_caps_len = 0; + } else { + // if not drop_all but add_all, we start with all capabilities + ret = copy_capabilities(&basic_caps, &basic_caps_len, all_caps, all_caps_len); + if (ret != 0) { + ERROR("Failed to copy all capabilities"); + ret = -1; + goto free_out; + } } - ret = tweak_drops_capabilities(&new_caps, &new_caps_len, basic_caps, basic_caps_len, drops, drops_len); + // Add capabilities to the basic capabilities + ret = tweak_adds_capabilities(&basic_caps, &basic_caps_len, adds, adds_len); if (ret != 0) { ret = -1; goto free_out; } - ret = tweak_adds_capabilities(&new_caps, &new_caps_len, adds, adds_len); + // Drop capabilities from the basic capabilities + ret = tweak_drops_capabilities(&new_caps, &new_caps_len, basic_caps, basic_caps_len, drops, drops_len); if (ret != 0) { ret = -1; goto free_out; } free_out: - for (i = 0; i < basic_caps_len; i++) { - free(basic_caps[i]); - } - free(basic_caps); - - // free old caps - for (i = 0; i < *caps_len; i++) { - free((*caps)[i]); - (*caps)[i] = NULL; - } - free(*caps); + util_free_array_by_len(basic_caps, basic_caps_len); + util_free_array_by_len(*caps, *caps_len); // set new caps *caps = new_caps; diff --git a/src/utils/cutils/utils_cap.c b/src/utils/cutils/utils_cap.c new file mode 100644 index 00000000..6473df45 --- /dev/null +++ b/src/utils/cutils/utils_cap.c @@ -0,0 +1,119 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2018-2022. All rights reserved. + * iSulad licensed under the Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v2 for more details. + * Author: xuxuepeng + * Create: 2023-11-08 + * Description: provide capbilities utils functions + *******************************************************************************/ + +#define _GNU_SOURCE + +#include "utils_cap.h" + +#include +#include +#include + +#include "utils_string.h" + +const char *g_all_caps[] = { + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_DAC_READ_SEARCH", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETPCAP", + "CAP_LINUX_IMMUTABLE", + "CAP_NET_BIND_SERVICE", + "CAP_NET_BROADCAST", + "CAP_NET_ADMIN", + "CAP_NET_RAW", + "CAP_IPC_LOCK", + "CAP_IPC_OWNER", + "CAP_SYS_MODULE", + "CAP_SYS_RAWIO", + "CAP_SYS_CHROOT", + "CAP_SYS_PTRACE", + "CAP_SYS_PACCT", + "CAP_SYS_ADMIN", + "CAP_SYS_BOOT", + "CAP_SYS_NICE", + "CAP_SYS_RESOURCE", + "CAP_SYS_TIME", + "CAP_SYS_TTY_CONFIG", + "CAP_MKNOD", + "CAP_LEASE", +#ifdef CAP_AUDIT_WRITE + "CAP_AUDIT_WRITE", +#endif +#ifdef CAP_AUDIT_CONTROL + "CAP_AUDIT_CONTROL", +#endif + "CAP_SETFCAP", + "CAP_MAC_OVERRIDE", + "CAP_MAC_ADMIN", +#ifdef CAP_SYSLOG + "CAP_SYSLOG", +#endif +#ifdef CAP_WAKE_ALARM + "CAP_WAKE_ALARM", +#endif +#ifdef CAP_BLOCK_SUSPEND + "CAP_BLOCK_SUSPEND", +#endif +#ifdef CAP_AUDIT_READ + "CAP_AUDIT_READ", +#endif +#ifdef CAP_PERFMON + "CAP_PERFMON", +#endif +#ifdef CAP_BPF + "CAP_BPF", +#endif +#ifdef CAP_CHECKPOINT_RESTORE + "CAP_CHECKPOINT_RESTORE", +#endif +}; + +static inline size_t util_get_all_caps_len() +{ + return sizeof(g_all_caps) / sizeof(char *); +} + +bool util_valid_cap(const char *cap) +{ + int nret = 0; + char tmpcap[32] = { 0 }; + size_t all_caps_len = util_get_all_caps_len(); + + if (cap == NULL) { + return false; + } + + nret = snprintf(tmpcap, sizeof(tmpcap), "CAP_%s", cap); + if (nret < 0 || (size_t)nret >= sizeof(tmpcap)) { + ERROR("Failed to print string"); + return false; + } + if (!util_strings_in_slice(g_all_caps, all_caps_len, tmpcap)) { + return false; + } + + return true; +} + +const char **util_get_all_caps(size_t *cap_len) +{ + *cap_len = util_get_all_caps_len(); + return g_all_caps; +} diff --git a/src/utils/cutils/utils_cap.h b/src/utils/cutils/utils_cap.h new file mode 100644 index 00000000..de63d070 --- /dev/null +++ b/src/utils/cutils/utils_cap.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2018-2022. All rights reserved. + * iSulad licensed under the Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v2 for more details. + * Author: xuxuepeng + * Create: 2023-11-08 + * Description: provide capbilities utils functions + *******************************************************************************/ + +#ifndef UTILS_CUTILS_UTILS_CAP_H +#define UTILS_CUTILS_UTILS_CAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +bool util_valid_cap(const char *cap); + +/** + * Get all supported capabilities for linux, + * note that the returned strings are unmutable + */ +const char **util_get_all_caps(size_t *cap_len); + +#ifdef __cplusplus +} +#endif + +#endif // UTILS_CUTILS_UTILS_CAP_H diff --git a/src/utils/cutils/utils_verify.c b/src/utils/cutils/utils_verify.c index 2f10f278..f4ce3199 100644 --- a/src/utils/cutils/utils_verify.c +++ b/src/utils/cutils/utils_verify.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -32,59 +31,6 @@ #include "utils_array.h" #include "utils_string.h" -const char *g_all_caps[] = { - "CAP_CHOWN", - "CAP_DAC_OVERRIDE", - "CAP_DAC_READ_SEARCH", - "CAP_FOWNER", - "CAP_FSETID", - "CAP_KILL", - "CAP_SETGID", - "CAP_SETUID", - "CAP_SETPCAP", - "CAP_LINUX_IMMUTABLE", - "CAP_NET_BIND_SERVICE", - "CAP_NET_BROADCAST", - "CAP_NET_ADMIN", - "CAP_NET_RAW", - "CAP_IPC_LOCK", - "CAP_IPC_OWNER", - "CAP_SYS_MODULE", - "CAP_SYS_RAWIO", - "CAP_SYS_CHROOT", - "CAP_SYS_PTRACE", - "CAP_SYS_PACCT", - "CAP_SYS_ADMIN", - "CAP_SYS_BOOT", - "CAP_SYS_NICE", - "CAP_SYS_RESOURCE", - "CAP_SYS_TIME", - "CAP_SYS_TTY_CONFIG", - "CAP_MKNOD", - "CAP_LEASE", -#ifdef CAP_AUDIT_WRITE - "CAP_AUDIT_WRITE", -#endif -#ifdef CAP_AUDIT_CONTROL - "CAP_AUDIT_CONTROL", -#endif - "CAP_SETFCAP", - "CAP_MAC_OVERRIDE", - "CAP_MAC_ADMIN", -#ifdef CAP_SYSLOG - "CAP_SYSLOG", -#endif -#ifdef CAP_WAKE_ALARM - "CAP_WAKE_ALARM", -#endif -#ifdef CAP_BLOCK_SUSPEND - "CAP_BLOCK_SUSPEND", -#endif -#ifdef CAP_AUDIT_READ - "CAP_AUDIT_READ", -#endif -}; - bool util_valid_cmd_arg(const char *arg) { return (arg != NULL) && (strchr(arg, '|') == NULL) && (strchr(arg, '`') == NULL) && (strchr(arg, '&')) == NULL && @@ -215,33 +161,6 @@ bool util_valid_str(const char *str) return (str != NULL && str[0] != '\0') ? true : false; } -size_t util_get_all_caps_len() -{ - return sizeof(g_all_caps) / sizeof(char *); -} - -bool util_valid_cap(const char *cap) -{ - int nret = 0; - char tmpcap[32] = { 0 }; - size_t all_caps_len = util_get_all_caps_len(); - - if (cap == NULL) { - return false; - } - - nret = snprintf(tmpcap, sizeof(tmpcap), "CAP_%s", cap); - if (nret < 0 || (size_t)nret >= sizeof(tmpcap)) { - ERROR("Failed to print string"); - return false; - } - if (!util_strings_in_slice(g_all_caps, all_caps_len, tmpcap)) { - return false; - } - - return true; -} - bool util_valid_container_id(const char *id) { char *patten = "^[a-f0-9]{1,64}$"; diff --git a/src/utils/cutils/utils_verify.h b/src/utils/cutils/utils_verify.h index ad4466ef..54d1ce71 100644 --- a/src/utils/cutils/utils_verify.h +++ b/src/utils/cutils/utils_verify.h @@ -38,8 +38,6 @@ extern "C" { #define VALID_VOLUME_NAME "[a-zA-Z0-9][a-zA-Z0-9_.-]{1,63}" -extern const char *g_all_caps[]; - bool util_valid_cmd_arg(const char *arg); bool util_valid_signal(int sig); @@ -54,10 +52,6 @@ bool util_valid_device_mode(const char *mode); bool util_valid_str(const char *str); -size_t util_get_all_caps_len(); - -bool util_valid_cap(const char *cap); - bool util_valid_time_tz(const char *time); bool util_valid_embedded_image_name(const char *name); diff --git a/test/cutils/utils_verify/utils_verify_ut.cc b/test/cutils/utils_verify/utils_verify_ut.cc index 99775d09..79670ec1 100644 --- a/test/cutils/utils_verify/utils_verify_ut.cc +++ b/test/cutils/utils_verify/utils_verify_ut.cc @@ -98,20 +98,6 @@ TEST(utils_verify, test_util_valid_str) ASSERT_EQ(util_valid_str(nullptr), false); } -TEST(utils_verify, test_util_get_all_caps_len) -{ - ASSERT_NE(util_get_all_caps_len(), 0); -} - -TEST(utils_verify, test_util_valid_cap) -{ - ASSERT_EQ(util_valid_cap("DAC_READ_SEARCH"), true); - - ASSERT_EQ(util_valid_cap(nullptr), false); - ASSERT_EQ(util_valid_cap(""), false); - ASSERT_EQ(util_valid_cap("DA_READ_SEARCH"), false); -} - TEST(utils_verify, test_util_valid_time_tz) { ASSERT_EQ(util_valid_time_tz("2022-10-04T18:22:45.289257759Z"), true); diff --git a/test/image/oci/oci_config_merge/CMakeLists.txt b/test/image/oci/oci_config_merge/CMakeLists.txt index ce4df5ba..90809080 100644 --- a/test/image/oci/oci_config_merge/CMakeLists.txt +++ b/test/image/oci/oci_config_merge/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(${EXE} ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/utils_file.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/utils_timestamp.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/utils_fs.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/utils_cap.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/map/map.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/map/rb_tree.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../../src/utils/cutils/util_atomic.c diff --git a/test/image/oci/storage/images/CMakeLists.txt b/test/image/oci/storage/images/CMakeLists.txt index 8446ebba..28e0b505 100644 --- a/test/image/oci/storage/images/CMakeLists.txt +++ b/test/image/oci/storage/images/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable(${EXE} ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/utils_convert.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/utils_file.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/utils_base64.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/utils_cap.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/util_atomic.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/sha256/sha256.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src/utils/cutils/path.c diff --git a/test/specs/specs/CMakeLists.txt b/test/specs/specs/CMakeLists.txt index 1d627e37..a9dbc52c 100644 --- a/test/specs/specs/CMakeLists.txt +++ b/test/specs/specs/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(${EXE} ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/util_atomic.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_mount_spec.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_fs.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_cap.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/sha256/sha256.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/path.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/map/map.c diff --git a/test/specs/specs/oci_runtime_spec.json b/test/specs/specs/oci_runtime_spec.json index 0223fd6f..efd5da35 100644 --- a/test/specs/specs/oci_runtime_spec.json +++ b/test/specs/specs/oci_runtime_spec.json @@ -154,6 +154,38 @@ "CAP_SYS_CHROOT", "CAP_KILL", "CAP_AUDIT_WRITE" + ], + "effective": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "permitted": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" ] } }, diff --git a/test/specs/specs/specs_ut.cc b/test/specs/specs/specs_ut.cc index 96aa1c63..ad903a3f 100644 --- a/test/specs/specs/specs_ut.cc +++ b/test/specs/specs/specs_ut.cc @@ -20,6 +20,7 @@ #include "isula_libutils/oci_runtime_spec.h" #include "specs_api.h" #include "specs_namespace.h" +#include "specs_security.h" #include "isula_libutils/host_config.h" #include "isula_libutils/container_config.h" #include "oci_ut_common.h" @@ -27,6 +28,7 @@ #include #include "isulad_config_mock.h" #include "utils.h" +#include "utils_cap.h" using ::testing::Args; using ::testing::ByRef; @@ -344,3 +346,316 @@ TEST_F(SpecsUnitTest, test_merge_container_cgroups_path_5) testing::Mock::VerifyAndClearExpectations(&m_isulad_conf); } + +/********************************* UT for merge caps *******************************************/ +struct capabilities_lens { + size_t bounding_len; + size_t effective_len; + size_t inheritable_len; + size_t permitted_len; + size_t ambient_len; +}; + +void check_capabilities_len(defs_process_capabilities *cap, struct capabilities_lens *lens) +{ + lens->bounding_len = cap->bounding_len; + lens->effective_len = cap->effective_len; + lens->inheritable_len = cap->inheritable_len; + lens->permitted_len = cap->permitted_len; + lens->ambient_len = cap->ambient_len; +} + +void validate_capabilities_len(defs_process_capabilities *cap, struct capabilities_lens *lens, ssize_t len_diff) +{ + ASSERT_EQ((ssize_t)cap->bounding_len, (ssize_t)lens->bounding_len + len_diff); + ASSERT_EQ((ssize_t)cap->effective_len, (ssize_t)lens->effective_len + len_diff); + ASSERT_EQ((ssize_t)cap->permitted_len, (ssize_t)lens->permitted_len + len_diff); + // Currently we don't support inheritable and ambient capabilities +} + +TEST(merge_capability_ut, test_merge_caps_without_adds_drops) +{ + oci_runtime_spec *oci_spec = nullptr; + int ret = 0; + char *err = nullptr; + struct capabilities_lens old_lens = { 0 }; + char *oci_config_file = nullptr; + + oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE); + ASSERT_TRUE(oci_config_file != nullptr); + + + oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err); + ASSERT_TRUE(oci_spec != nullptr); + free(err); + + check_capabilities_len(oci_spec->process->capabilities, &old_lens); + + ret = merge_caps(oci_spec, nullptr, 0, nullptr, 0); + ASSERT_EQ(ret, 0); + + validate_capabilities_len(oci_spec->process->capabilities, &old_lens, 0); + + free_oci_runtime_spec(oci_spec); +} + +TEST(merge_capability_ut, test_merge_caps_adds_without_drops) +{ + oci_runtime_spec *oci_spec = nullptr; + int ret = 0; + char *err = nullptr; + struct capabilities_lens old_lens = { 0 }; + char *oci_config_file = nullptr; + /* All of below capabilities are not in oci_config_file */ + const char *adds[] = { "NET_ADMIN", "SYS_ADMIN", "SYS_TTY_CONFIG", "SYS_PTRACE" }; + const char *drops[] = {}; + size_t adds_len = sizeof(adds) / sizeof(adds[0]); + size_t drops_len = sizeof(drops) / sizeof(drops[0]); + + oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE); + ASSERT_TRUE(oci_config_file != nullptr); + + + oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err); + ASSERT_TRUE(oci_spec != nullptr); + free(err); + + check_capabilities_len(oci_spec->process->capabilities, &old_lens); + + ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len); + ASSERT_EQ(ret, 0); + + /* All of capabilities in adds are added */ + validate_capabilities_len(oci_spec->process->capabilities, &old_lens, adds_len); + + free_oci_runtime_spec(oci_spec); +} + +TEST(merge_capability_ut, test_merge_caps_adds_existing_without_drops) +{ + oci_runtime_spec *oci_spec = nullptr; + int ret = 0; + char *err = nullptr; + struct capabilities_lens old_lens = { 0 }; + char *oci_config_file = nullptr; + /* CHOWN already exits in oci_config_file */ + const char *adds[] = { "CHOWN", "SYS_ADMIN", "SYS_TTY_CONFIG", "SYS_PTRACE" }; + const char *drops[] = {}; + size_t adds_len = sizeof(adds) / sizeof(adds[0]); + size_t drops_len = sizeof(drops) / sizeof(drops[0]); + + oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE); + ASSERT_TRUE(oci_config_file != nullptr); + + + oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err); + ASSERT_TRUE(oci_spec != nullptr); + free(err); + + check_capabilities_len(oci_spec->process->capabilities, &old_lens); + + ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len); + ASSERT_EQ(ret, 0); + + /* CHOWN is not added, since it already exits in the default list */ + validate_capabilities_len(oci_spec->process->capabilities, &old_lens, adds_len - 1); + + free_oci_runtime_spec(oci_spec); +} + +TEST(merge_capability_ut, test_merge_caps_drops_without_adds) +{ + oci_runtime_spec *oci_spec = nullptr; + int ret = 0; + char *err = nullptr; + struct capabilities_lens old_lens = { 0 }; + char *oci_config_file = nullptr; + const char *adds[] = {}; + /* Below capabilities are not in the oci_config_file */ + const char *drops[] = { "SYS_TTY_CONFIG", "SYS_PTRACE" }; + size_t adds_len = sizeof(adds) / sizeof(adds[0]); + size_t drops_len = sizeof(drops) / sizeof(drops[0]); + + oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE); + ASSERT_TRUE(oci_config_file != nullptr); + + + oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err); + ASSERT_TRUE(oci_spec != nullptr); + free(err); + + check_capabilities_len(oci_spec->process->capabilities, &old_lens); + + ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len); + ASSERT_EQ(ret, 0); + + /* Nothing dropped */ + validate_capabilities_len(oci_spec->process->capabilities, &old_lens, 0); + + free_oci_runtime_spec(oci_spec); +} + +TEST(merge_capability_ut, test_merge_caps_drops_existing_without_adds) +{ + oci_runtime_spec *oci_spec = nullptr; + int ret = 0; + char *err = nullptr; + struct capabilities_lens old_lens = { 0 }; + char *oci_config_file = nullptr; + const char *adds[] = {}; + /* Below capabilities are in the oci_config_file */ + const char *drops[] = { "CHOWN", "MKNOD" }; + size_t adds_len = sizeof(adds) / sizeof(adds[0]); + size_t drops_len = sizeof(drops) / sizeof(drops[0]); + + oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE); + ASSERT_TRUE(oci_config_file != nullptr); + + + oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err); + ASSERT_TRUE(oci_spec != nullptr); + free(err); + + check_capabilities_len(oci_spec->process->capabilities, &old_lens); + + ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len); + ASSERT_EQ(ret, 0); + + /* All dropped */ + validate_capabilities_len(oci_spec->process->capabilities, &old_lens, adds_len - drops_len); + + free_oci_runtime_spec(oci_spec); +} + +TEST(merge_capability_ut, test_merge_caps_adds_drops) +{ + oci_runtime_spec *oci_spec = nullptr; + int ret = 0; + char *err = nullptr; + struct capabilities_lens old_lens = { 0 }; + char *oci_config_file = nullptr; + /* All of below capabilities are not in oci_config_file */ + const char *adds[] = { "NET_ADMIN", "SYS_ADMIN", "SYS_TTY_CONFIG", "SYS_PTRACE" }; + const char *drops[] = { "SYS_TTY_CONFIG", "SYS_PTRACE" }; + size_t adds_len = sizeof(adds) / sizeof(adds[0]); + size_t drops_len = sizeof(drops) / sizeof(drops[0]); + + oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE); + ASSERT_TRUE(oci_config_file != nullptr); + + + oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err); + ASSERT_TRUE(oci_spec != nullptr); + free(err); + + check_capabilities_len(oci_spec->process->capabilities, &old_lens); + + ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len); + ASSERT_EQ(ret, 0); + + validate_capabilities_len(oci_spec->process->capabilities, &old_lens, adds_len - drops_len); + + free_oci_runtime_spec(oci_spec); +} + +TEST(merge_capability_ut, test_merge_caps_adds_all_without_drops) +{ + oci_runtime_spec *oci_spec = nullptr; + int ret = 0; + char *err = nullptr; + struct capabilities_lens old_lens = { 0 }; + char *oci_config_file = nullptr; + /* NET_ADMIN is in all */ + const char *adds[] = { "ALL", "NET_ADMIN" }; + const char *drops[] = {}; + size_t adds_len = sizeof(adds) / sizeof(adds[0]); + size_t drops_len = sizeof(drops) / sizeof(drops[0]); + size_t all_caps_len = 0; + util_get_all_caps(&all_caps_len); + + oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE); + ASSERT_TRUE(oci_config_file != nullptr); + + oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err); + ASSERT_TRUE(oci_spec != nullptr); + free(err); + + check_capabilities_len(oci_spec->process->capabilities, &old_lens); + + ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len); + ASSERT_EQ(ret, 0); + + ASSERT_EQ(oci_spec->process->capabilities->bounding_len, all_caps_len); + ASSERT_EQ(oci_spec->process->capabilities->effective_len, all_caps_len); + ASSERT_EQ(oci_spec->process->capabilities->permitted_len, all_caps_len); + + free_oci_runtime_spec(oci_spec); +} + +TEST(merge_capability_ut, test_merge_caps_adds_all_and_extra_without_drops) +{ + oci_runtime_spec *oci_spec = nullptr; + int ret = 0; + char *err = nullptr; + struct capabilities_lens old_lens = { 0 }; + char *oci_config_file = nullptr; + /* ABC is not in all */ + const char *adds[] = { "ALL", "ABC" }; + const char *drops[] = {}; + size_t adds_len = sizeof(adds) / sizeof(adds[0]); + size_t drops_len = sizeof(drops) / sizeof(drops[0]); + size_t all_caps_len = 0; + util_get_all_caps(&all_caps_len); + + oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE); + ASSERT_TRUE(oci_config_file != nullptr); + + oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err); + ASSERT_TRUE(oci_spec != nullptr); + free(err); + + check_capabilities_len(oci_spec->process->capabilities, &old_lens); + + ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len); + ASSERT_EQ(ret, 0); + + ASSERT_EQ(oci_spec->process->capabilities->bounding_len, all_caps_len + 1); + ASSERT_EQ(oci_spec->process->capabilities->effective_len, all_caps_len + 1); + ASSERT_EQ(oci_spec->process->capabilities->permitted_len, all_caps_len + 1); + + free_oci_runtime_spec(oci_spec); +} + +TEST(merge_capability_ut, test_merge_caps_adds_all_drops_all) +{ + oci_runtime_spec *oci_spec = nullptr; + int ret = 0; + char *err = nullptr; + struct capabilities_lens old_lens = { 0 }; + char *oci_config_file = nullptr; + /* ABC, EFG is not in all */ + const char *adds[] = { "ALL", "ABC", "EFG"}; + const char *drops[] = { "ALL", "ABC" }; + size_t adds_len = sizeof(adds) / sizeof(adds[0]); + size_t drops_len = sizeof(drops) / sizeof(drops[0]); + size_t all_caps_len = 0; + util_get_all_caps(&all_caps_len); + + oci_config_file = json_path(OCI_RUNTIME_SPEC_FILE); + ASSERT_TRUE(oci_config_file != nullptr); + + oci_spec = oci_runtime_spec_parse_file(oci_config_file, nullptr, &err); + ASSERT_TRUE(oci_spec != nullptr); + free(err); + + check_capabilities_len(oci_spec->process->capabilities, &old_lens); + + ret = merge_caps(oci_spec, adds, adds_len, drops, drops_len); + ASSERT_EQ(ret, 0); + + ASSERT_EQ(oci_spec->process->capabilities->bounding_len, 1); + ASSERT_EQ(oci_spec->process->capabilities->effective_len, 1); + ASSERT_EQ(oci_spec->process->capabilities->permitted_len, 1); + + free_oci_runtime_spec(oci_spec); +} diff --git a/test/specs/specs_extend/CMakeLists.txt b/test/specs/specs_extend/CMakeLists.txt index 294690e8..bf4b378e 100644 --- a/test/specs/specs_extend/CMakeLists.txt +++ b/test/specs/specs_extend/CMakeLists.txt @@ -14,6 +14,7 @@ add_executable(${EXE} ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/util_atomic.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_mount_spec.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_fs.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_cap.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/sha256/sha256.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/path.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/map/map.c -- 2.42.0