From d231a8e5f8ea1c8a5584fdc8baa579b18d1b6a19 Mon Sep 17 00:00:00 2001
From: zhongtao <zhongtao17@huawei.com>
Date: Wed, 29 Mar 2023 03:26:45 +0800
Subject: [PATCH 38/46] support to config selinux label in cri

Signed-off-by: zhongtao <zhongtao17@huawei.com>
---
 .../cri/cri_container_manager_service_impl.cc | 47 +++++----
 .../cri/cri_container_manager_service_impl.h  |  1 +
 src/daemon/entry/cri/cri_helpers.cc           | 95 ++++++++++++++++++-
 src/daemon/entry/cri/cri_helpers.h            | 17 +++-
 .../cri_pod_sandbox_manager_service_impl.cc   | 47 ++++-----
 5 files changed, 149 insertions(+), 58 deletions(-)

diff --git a/src/daemon/entry/cri/cri_container_manager_service_impl.cc b/src/daemon/entry/cri/cri_container_manager_service_impl.cc
index a64d222c..6278512f 100644
--- a/src/daemon/entry/cri/cri_container_manager_service_impl.cc
+++ b/src/daemon/entry/cri/cri_container_manager_service_impl.cc
@@ -98,30 +98,21 @@ auto ContainerManagerServiceImpl::PackCreateContainerHostConfigSecurityContext(
     // security Opt Separator Change Version : k8s v1.23.0 (Corresponds to docker 1.11.x)
     // New version '=' , old version ':', iSulad cri is based on v18.09, so iSulad cri use new version separator
     const char securityOptSep { '=' };
-    std::vector<std::string> securityOpts = CRIHelpers::GetSecurityOpts(
-                                                containerConfig.linux().security_context().seccomp_profile_path(), securityOptSep, error);
+    const ::runtime::v1alpha2::LinuxContainerSecurityContext &context = containerConfig.linux().security_context();
+    CRIHelpers::commonSecurityContext commonContext = {
+        .hasSELinuxOption = context.has_selinux_options(),
+        .selinuxOption = context.selinux_options(),
+        .seccompProfile = context.seccomp_profile_path(),
+    };
+    std::vector<std::string> securityOpts = CRIHelpers::GetSecurityOpts(commonContext, securityOptSep, error);
     if (error.NotEmpty()) {
-        error.Errorf("failed to generate security options for container %s", containerConfig.metadata().name().c_str());
+        error.Errorf("Failed to generate security options for container %s", containerConfig.metadata().name().c_str());
         return -1;
     }
-    if (!securityOpts.empty()) {
-        char **tmp_security_opt = nullptr;
-        if (securityOpts.size() > (SIZE_MAX / sizeof(char *)) - hostconfig->security_opt_len) {
-            error.Errorf("Out of memory");
-            return -1;
-        }
-        size_t newSize = (hostconfig->security_opt_len + securityOpts.size()) * sizeof(char *);
-        size_t oldSize = hostconfig->security_opt_len * sizeof(char *);
-        int ret = util_mem_realloc((void **)(&tmp_security_opt), newSize, (void *)hostconfig->security_opt, oldSize);
-        if (ret != 0) {
-            error.Errorf("Out of memory");
-            return -1;
-        }
-        hostconfig->security_opt = tmp_security_opt;
-        for (const auto &securityOpt : securityOpts) {
-            hostconfig->security_opt[hostconfig->security_opt_len] = util_strdup_s(securityOpt.c_str());
-            hostconfig->security_opt_len++;
-        }
+    CRIHelpers::AddSecurityOptsToHostConfig(securityOpts, hostconfig, error);
+    if (error.NotEmpty()) {
+        error.Errorf("Failed to add securityOpts to hostconfig for container %s", containerConfig.metadata().name().c_str());
+        return -1;
     }
     return 0;
 }
@@ -148,7 +139,7 @@ auto ContainerManagerServiceImpl::DoUsePodLevelSELinuxConfig(const runtime::v1al
     }
 
     tmp_str = std::string(inspect->process_label);
-    selinuxLabelOpts = CRIHelpers::GetSELinuxLabelOpts(tmp_str, error);
+    selinuxLabelOpts = CRIHelpers::GetPodSELinuxLabelOpts(tmp_str, error);
     if (error.NotEmpty()) {
         ERROR("Failed to get SELinuxLabelOpts for container %s", containerConfig.metadata().name().c_str());
         goto cleanup;
@@ -181,7 +172,14 @@ cleanup:
     return ret;
 }
 
-
+auto ContainerManagerServiceImpl::IsSELinuxLabelEmpty(const ::runtime::v1alpha2::SELinuxOption &selinuxOption) -> bool
+{
+    if (selinuxOption.user().length() == 0 && selinuxOption.role().length() == 0 && selinuxOption.type().length() == 0 &&
+        selinuxOption.level().length() == 0) {
+        return true;
+    }
+    return false;
+}
 
 auto ContainerManagerServiceImpl::GenerateCreateContainerHostConfig(
     const runtime::v1alpha2::ContainerConfig &containerConfig,
@@ -219,7 +217,8 @@ auto ContainerManagerServiceImpl::GenerateCreateContainerHostConfig(
 
     // If selinux label is not specified in container config, use pod level SELinux config
     if (!containerConfig.linux().has_security_context() ||
-        !containerConfig.linux().security_context().has_selinux_options()) {
+        !containerConfig.linux().security_context().has_selinux_options() ||
+        IsSELinuxLabelEmpty(containerConfig.linux().security_context().selinux_options())) {
         if (DoUsePodLevelSELinuxConfig(containerConfig, hostconfig, realPodSandboxID, error) != 0) {
             error.SetError("Failed to security context to host config");
             goto cleanup;
diff --git a/src/daemon/entry/cri/cri_container_manager_service_impl.h b/src/daemon/entry/cri/cri_container_manager_service_impl.h
index dcb2cd44..ad53a65f 100644
--- a/src/daemon/entry/cri/cri_container_manager_service_impl.h
+++ b/src/daemon/entry/cri/cri_container_manager_service_impl.h
@@ -84,6 +84,7 @@ private:
                                         Errors &error) -> container_create_request *;
     auto GenerateCreateContainerHostConfig(const runtime::v1alpha2::ContainerConfig &containerConfig,
                                            const std::string &realPodSandboxID, Errors &error) -> host_config *;
+    auto IsSELinuxLabelEmpty(const ::runtime::v1alpha2::SELinuxOption &selinuxOption) -> bool;
     auto GenerateCreateContainerCustomConfig(const std::string &containerName, const std::string &realPodSandboxID,
                                              const runtime::v1alpha2::ContainerConfig &containerConfig,
                                              const runtime::v1alpha2::PodSandboxConfig &podSandboxConfig,
diff --git a/src/daemon/entry/cri/cri_helpers.cc b/src/daemon/entry/cri/cri_helpers.cc
index 2bc6bed7..ec14d0b2 100644
--- a/src/daemon/entry/cri/cri_helpers.cc
+++ b/src/daemon/entry/cri/cri_helpers.cc
@@ -68,6 +68,8 @@ const std::string Constants::CNI_MUTL_NET_EXTENSION_KEY { "extension.network.kub
 const std::string Constants::CNI_MUTL_NET_EXTENSION_ARGS_KEY { "CNI_MUTLINET_EXTENSION" };
 const std::string Constants::CNI_ARGS_EXTENSION_PREFIX_KEY { "extension.network.kubernetes.io/cniargs/" };
 const std::string Constants::IMAGE_NAME_ANNOTATION_KEY { "io.kubernetes.cri.image-name" };
+// Usually, the format of level is "s0:c60,c525" or "s0-s0:c40.c23"
+const std::string Constants::SELINUX_LABEL_LEVEL_PATTERN { "^s[0-9](-s[0-9])?(:c[0-9]{1,4}(\\.c[0-9]{1,4})?(,c[0-9]{1,4}(\\.c[0-9]{1,4})?)*)?$" };
 
 const char *InternalLabelKeys[] = { CRIHelpers::Constants::CONTAINER_TYPE_LABEL_KEY.c_str(),
                                     CRIHelpers::Constants::CONTAINER_LOGPATH_LABEL_KEY.c_str(),
@@ -598,6 +600,34 @@ auto GetSeccompiSuladOpts(const std::string &seccompProfile, Errors &error) -> s
     return ret;
 }
 
+
+auto GetSelinuxiSuladOpts(const ::runtime::v1alpha2::SELinuxOption &selinux, Errors &error)-> std::vector<iSuladOpt>
+{
+    std::vector<iSuladOpt> selinuxOpts { };
+    // LabeSep is consistent with the separator used when parsing labels
+    const char labeSep { ':' };
+
+    if (selinux.level().length() != 0 &&
+        util_reg_match(CRIHelpers::Constants::SELINUX_LABEL_LEVEL_PATTERN.c_str(), selinux.level().c_str()) != 0) {
+        error.Errorf("The format of 'level' %s is not correct", selinux.level().c_str());
+        return selinuxOpts;
+    }
+
+    if (selinux.user().length() > 0) {
+        selinuxOpts.push_back({ "label", std::string("user") + std::string(1, labeSep) + selinux.user(), "" });
+    }
+    if (selinux.role().length() > 0) {
+        selinuxOpts.push_back({ "label", std::string("role") + std::string(1, labeSep) + selinux.role(), "" });
+    }
+    if (selinux.type().length() > 0) {
+        selinuxOpts.push_back({ "label", std::string("type") + std::string(1, labeSep) + selinux.type(), "" });
+    }
+    if (selinux.level().length() > 0) {
+        selinuxOpts.push_back({ "label", std::string("level") + std::string(1, labeSep) + selinux.level(), "" });
+    }
+    return selinuxOpts;
+}
+
 auto GetSeccompSecurityOpts(const std::string &seccompProfile, const char &separator, Errors &error)
 -> std::vector<std::string>
 {
@@ -609,17 +639,44 @@ auto GetSeccompSecurityOpts(const std::string &seccompProfile, const char &separ
     return fmtiSuladOpts(seccompOpts, separator);
 }
 
-auto GetSecurityOpts(const std::string &seccompProfile, const char &separator, Errors &error)
+auto GetSELinuxLabelOpts(const bool hasSELinuxOption, const ::runtime::v1alpha2::SELinuxOption &selinux,
+                         const char &separator, Errors &error)
 -> std::vector<std::string>
 {
-    std::vector<std::string> seccompSecurityOpts = GetSeccompSecurityOpts(seccompProfile, separator, error);
+    if (!hasSELinuxOption) {
+        return std::vector<std::string>();
+    }
+
+    std::vector<iSuladOpt> selinuxOpts = GetSelinuxiSuladOpts(selinux, error);
     if (error.NotEmpty()) {
-        error.Errorf("failed to generate seccomp security options for container: %s", error.GetMessage().c_str());
+        return std::vector<std::string>();
     }
-    return seccompSecurityOpts;
+
+    return fmtiSuladOpts(selinuxOpts, separator);
 }
 
-auto GetSELinuxLabelOpts(const std::string &selinuxLabel, Errors &error)
+auto GetSecurityOpts(const commonSecurityContext &context, const char &separator, Errors &error)
+-> std::vector<std::string>
+{
+    std::vector<std::string> securityOpts;
+    std::vector<std::string> seccompSecurityOpts = GetSeccompSecurityOpts(context.seccompProfile, separator, error);
+    if (error.NotEmpty()) {
+        error.Errorf("Failed to generate seccomp security options for container: %s", error.GetMessage().c_str());
+        return securityOpts;
+    }
+
+    std::vector<std::string> selinuxOpts = CRIHelpers::GetSELinuxLabelOpts(context.hasSELinuxOption,
+                                                                           context.selinuxOption, separator, error);
+    if (error.NotEmpty()) {
+        error.Errorf("Failed to generate SELinuxLabel options for container %s", error.GetMessage().c_str());
+        return securityOpts;
+    }
+    securityOpts.insert(securityOpts.end(), seccompSecurityOpts.begin(), seccompSecurityOpts.end());
+    securityOpts.insert(securityOpts.end(), selinuxOpts.begin(), selinuxOpts.end());
+    return securityOpts;
+}
+
+auto GetPodSELinuxLabelOpts(const std::string &selinuxLabel, Errors &error)
 -> std::vector<std::string>
 {
     // security Opt Separator Change Version : k8s v1.23.0 (Corresponds to docker 1.11.x)
@@ -650,6 +707,34 @@ auto GetSELinuxLabelOpts(const std::string &selinuxLabel, Errors &error)
     return fmtiSuladOpts(selinuxOpts, securityOptSep);
 }
 
+void AddSecurityOptsToHostConfig(std::vector<std::string> &securityOpts, host_config *hostconfig, Errors &error)
+{
+    if (securityOpts.empty()) {
+        return;
+    }
+
+    char **tmp_security_opt = nullptr;
+    if (securityOpts.size() > (SIZE_MAX / sizeof(char *)) - hostconfig->security_opt_len) {
+        error.Errorf("Too many securityOpts");
+        ERROR("Too many securityOpts");
+        return;
+    }
+    size_t newSize = (hostconfig->security_opt_len + securityOpts.size()) * sizeof(char *);
+    size_t oldSize = hostconfig->security_opt_len * sizeof(char *);
+    int ret = util_mem_realloc((void **)(&tmp_security_opt), newSize, (void *)hostconfig->security_opt, oldSize);
+    if (ret != 0) {
+        error.Errorf("Out of memory");
+        ERROR("Out of memory");
+        return;
+    }
+    hostconfig->security_opt = tmp_security_opt;
+    for (const auto &securityOpt : securityOpts) {
+        hostconfig->security_opt[hostconfig->security_opt_len] = util_strdup_s(securityOpt.c_str());
+        hostconfig->security_opt_len++;
+    }
+
+}
+
 auto CreateCheckpoint(CRI::PodSandboxCheckpoint &checkpoint, Errors &error) -> std::string
 {
     cri_checkpoint *criCheckpoint { nullptr };
diff --git a/src/daemon/entry/cri/cri_helpers.h b/src/daemon/entry/cri/cri_helpers.h
index 4ef227d7..a5d6cf61 100644
--- a/src/daemon/entry/cri/cri_helpers.h
+++ b/src/daemon/entry/cri/cri_helpers.h
@@ -67,10 +67,17 @@ public:
     static const std::string CNI_MUTL_NET_EXTENSION_KEY;
     static const std::string CNI_MUTL_NET_EXTENSION_ARGS_KEY;
     static const std::string CNI_ARGS_EXTENSION_PREFIX_KEY;
+    static const std::string SELINUX_LABEL_LEVEL_PATTERN;
 
     static const std::string IMAGE_NAME_ANNOTATION_KEY;
 };
 
+struct commonSecurityContext {
+    const bool hasSELinuxOption;
+    const ::runtime::v1alpha2::SELinuxOption selinuxOption;
+    const std::string seccompProfile;
+};
+
 auto GetDefaultSandboxImage(Errors &err) -> std::string;
 
 auto MakeLabels(const google::protobuf::Map<std::string, std::string> &mapLabels, Errors &error)
@@ -124,12 +131,18 @@ auto ValidateCheckpointKey(const std::string &key, Errors &error) -> bool;
 
 auto ToIsuladContainerStatus(const runtime::v1alpha2::ContainerStateValue &state) -> std::string;
 
-auto GetSecurityOpts(const std::string &seccompProfile, const char &separator, Errors &error)
+auto GetSELinuxLabelOpts(const bool hasSELinuxOption, const ::runtime::v1alpha2::SELinuxOption &selinux,
+                         const char &separator, Errors &error)
 -> std::vector<std::string>;
 
-auto GetSELinuxLabelOpts(const std::string &selinuxLabel, Errors &error)
+auto GetSecurityOpts(const commonSecurityContext &context, const char &separator, Errors &error)
 -> std::vector<std::string>;
 
+auto GetPodSELinuxLabelOpts(const std::string &selinuxLabel, Errors &error)
+-> std::vector<std::string>;
+
+void AddSecurityOptsToHostConfig(std::vector<std::string> &securityOpts, host_config *hostconfig, Errors &error);
+
 auto CreateCheckpoint(CRI::PodSandboxCheckpoint &checkpoint, Errors &error) -> std::string;
 
 void GetCheckpoint(const std::string &jsonCheckPoint, CRI::PodSandboxCheckpoint &checkpoint, Errors &error);
diff --git a/src/daemon/entry/cri/cri_pod_sandbox_manager_service_impl.cc b/src/daemon/entry/cri/cri_pod_sandbox_manager_service_impl.cc
index 7ff545db..1cb3254d 100644
--- a/src/daemon/entry/cri/cri_pod_sandbox_manager_service_impl.cc
+++ b/src/daemon/entry/cri/cri_pod_sandbox_manager_service_impl.cc
@@ -197,34 +197,27 @@ void PodSandboxManagerServiceImpl::MakeSandboxIsuladConfig(const runtime::v1alph
     const char securityOptSep = '=';
 
     // Security Opts
-    if (c.linux().has_security_context()) {
-        std::vector<std::string> securityOpts =
-            CRIHelpers::GetSecurityOpts(c.linux().security_context().seccomp_profile_path(), securityOptSep, error);
-        if (error.NotEmpty()) {
-            error.Errorf("failed to generate security options for sandbox %s: %s",
-                         c.metadata().name().c_str(), error.GetMessage().c_str());
-            return;
-        }
-        if (!securityOpts.empty()) {
-            char **tmp_security_opt = nullptr;
+    if (!c.linux().has_security_context()) {
+        return;
+    }
 
-            if (securityOpts.size() > (SIZE_MAX / sizeof(char *)) - hc->security_opt_len) {
-                error.Errorf("Out of memory");
-                return;
-            }
-            size_t newSize = (hc->security_opt_len + securityOpts.size()) * sizeof(char *);
-            size_t oldSize = hc->security_opt_len * sizeof(char *);
-            int ret = util_mem_realloc((void **)(&tmp_security_opt), newSize, (void *)hc->security_opt, oldSize);
-            if (ret != 0) {
-                error.Errorf("Out of memory");
-                return;
-            }
-            hc->security_opt = tmp_security_opt;
-            for (const auto &securityOpt : securityOpts) {
-                hc->security_opt[hc->security_opt_len] = util_strdup_s(securityOpt.c_str());
-                hc->security_opt_len++;
-            }
-        }
+    CRIHelpers::commonSecurityContext commonContext = {
+        .hasSELinuxOption = c.linux().security_context().has_selinux_options(),
+        .selinuxOption = c.linux().security_context().selinux_options(),
+        .seccompProfile = c.linux().security_context().seccomp_profile_path(),
+    };
+
+    std::vector<std::string> securityOpts = CRIHelpers::GetSecurityOpts(commonContext, securityOptSep, error);
+    if (error.NotEmpty()) {
+        error.Errorf("Failed to generate security options for sandbox %s: %s",
+                     c.metadata().name().c_str(), error.GetMessage().c_str());
+        return;
+    }
+    CRIHelpers::AddSecurityOptsToHostConfig(securityOpts, hc, error);
+    if (error.NotEmpty()) {
+        error.Errorf("Failed to add securityOpts to hostconfig for sandbox %s: %s", c.metadata().name().c_str(),
+                     error.GetMessage().c_str());
+        return;
     }
 }
 
-- 
2.25.1

