summaryrefslogtreecommitdiff
path: root/0009-2243-Refactor-capbilities-specs.patch
diff options
context:
space:
mode:
Diffstat (limited to '0009-2243-Refactor-capbilities-specs.patch')
-rw-r--r--0009-2243-Refactor-capbilities-specs.patch1056
1 files changed, 1056 insertions, 0 deletions
diff --git a/0009-2243-Refactor-capbilities-specs.patch b/0009-2243-Refactor-capbilities-specs.patch
new file mode 100644
index 0000000..116a30f
--- /dev/null
+++ b/0009-2243-Refactor-capbilities-specs.patch
@@ -0,0 +1,1056 @@
+From e84112fd7128e05a1a4a380d8242c672d1f539f9 Mon Sep 17 00:00:00 2001
+From: xuxuepeng <xuxuepeng1@huawei.com>
+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 <stdint.h>
++#include <stdio.h>
++#include <isula_libutils/log.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
++#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 <stdbool.h>
++#include <stddef.h>
++#include <linux/capability.h>
++
++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 <sys/stat.h>
+ #include <errno.h>
+ #include <fcntl.h>
+-#include <linux/capability.h>
+ #include <stdio.h>
+ #include <strings.h>
+
+@@ -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 <gmock/gmock.h>
+ #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
+