diff --git a/LICENSE b/LICENSE
index 97c58d4662c37e15a56f149eedfb98c00e604414..0113e4b173a77583bc17bbbf213595fd9c905121 100644
--- a/LICENSE
+++ b/LICENSE
@@ -3,5 +3,6 @@
./xpm/
./qos_auth/
./ucollection/
+ ./code_sign
As for the specific use of the licenses, please refer to the relevant description in the documents.
diff --git a/OAT.xml b/OAT.xml
index 55a93aaac5d9767f9e9f8ec15add42c0fa350918..13a8f09121676c0990873ef3de5d57c3413a2969 100644
--- a/OAT.xml
+++ b/OAT.xml
@@ -61,10 +61,12 @@ Note:If the text contains special characters, please escape them according to th
+
+
diff --git a/code_sign/Kconfig b/code_sign/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..0c6a9aae8358853c3ea4a4ce04f79bdd90ebdf1b
--- /dev/null
+++ b/code_sign/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2023 Huawei Device Co., Ltd.
+#
+config SECURITY_CODE_SIGN
+ bool "Advanced code signing feature based on FS Verity"
+ depends on FS_VERITY
+ default n
+ help
+ This option enables additional code signing verify features
+ based on fs-verity, including verify if a certificate's subject
+ and issuer can be trusted, etc.
+
+ If unsure, say N.
diff --git a/code_sign/Makefile b/code_sign/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..ab3b9826b20de2b84a83b86f2994c2fa91ba88e4
--- /dev/null
+++ b/code_sign/Makefile
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2023 Huawei Device Co., Ltd.
+#
+obj-$(CONFIG_SECURITY_CODE_SIGN) += \
+ code_sign_misc.o \
+ verify_cert_chain.o \
+ code_sign_ioctl.o
+
+ccflags-$(CONFIG_SECURITY_CODE_SIGN) += \
+ -I$(srctree)/fs/code_sign \
+ -I$(srctree)/security/selinux/include \
+ -I$(srctree)/security/selinux
+
+$(addprefix $(obj)/,$(obj-y)): $(obj)/flask.h
+
+quiet_cmd_flask = GEN $(obj)/flask.h $(obj)/av_permissions.h
+ cmd_flask = scripts/selinux/genheaders/genheaders $(obj)/flask.h $(obj)/av_permissions.h
+
+targets += flask.h av_permissions.h
+$(obj)/flask.h: $(srctree)/security/selinux/include/classmap.h FORCE
+ $(call if_changed,flask)
diff --git a/code_sign/apply_code_sign.sh b/code_sign/apply_code_sign.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6098a16de610d3d626dcca024835ccd29f51cd7a
--- /dev/null
+++ b/code_sign/apply_code_sign.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Huawei Device Co., Ltd.
+#
+
+set -e
+
+OHOS_SOURCE_ROOT=$1
+KERNEL_BUILD_ROOT=$2
+PRODUCT_NAME=$3
+KERNEL_VERSION=$4
+CODE_SIGN_SOURCE_ROOT=$OHOS_SOURCE_ROOT/kernel/linux/common_modules/code_sign
+
+function main()
+{
+ pushd .
+
+ if [ ! -d "$KERNEL_BUILD_ROOT/fs/code_sign" ]; then
+ mkdir $KERNEL_BUILD_ROOT/fs/code_sign
+ fi
+
+ cd $KERNEL_BUILD_ROOT/fs/code_sign
+ ln -s -f $(realpath --relative-to=$KERNEL_BUILD_ROOT/fs/code_sign $CODE_SIGN_SOURCE_ROOT)/* ./
+
+ popd
+}
+
+main
diff --git a/code_sign/code_sign_ioctl.c b/code_sign/code_sign_ioctl.c
new file mode 100644
index 0000000000000000000000000000000000000000..bbd3510d8d1709b008a77585c2ca0c60e5e58e59
--- /dev/null
+++ b/code_sign/code_sign_ioctl.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 Huawei Device Co., Ltd.
+ */
+
+#include
+#include
+#include
+#include
+#include "avc.h"
+#include "objsec.h"
+#include "code_sign_ioctl.h"
+#include "code_sign_log.h"
+
+struct rb_root cert_chain_tree = RB_ROOT;
+
+struct cert_source *cert_chain_search(struct rb_root *root, struct x509_certificate *cert)
+{
+ struct rb_node **cur_node = &(root->rb_node);
+
+ while (*cur_node) {
+ struct cert_source *cur_cert = container_of(*cur_node, struct cert_source, node);
+ int result = strcmp(cert->subject, cur_cert->subject);
+
+ if (result < 0) {
+ cur_node = &((*cur_node)->rb_left);
+ } else if (result > 0) {
+ cur_node = &((*cur_node)->rb_right);
+ } else {
+ result = strcmp(cert->issuer, cur_cert->issuer);
+ if (result < 0) {
+ cur_node = &((*cur_node)->rb_left);
+ } else if (result > 0) {
+ cur_node = &((*cur_node)->rb_right);
+ } else {
+ code_sign_log_info("cert found");
+ return cur_cert;
+ }
+ }
+ }
+ code_sign_log_error("cert not found");
+ return NULL;
+}
+
+struct cert_source *find_match(struct x509_certificate *cert)
+{
+ return cert_chain_search(&cert_chain_tree, cert);
+}
+
+void cert_chain_insert(struct rb_root *root, struct cert_source *cert)
+{
+ struct rb_node **new = &(root->rb_node), *parent = NULL;
+
+ while (*new) {
+ struct cert_source *this = container_of(*new, struct cert_source, node);
+ int result = strcmp(cert->subject, this->subject);
+
+ parent = *new;
+ if (result < 0) {
+ new = &((*new)->rb_left);
+ } else if (result > 0) {
+ new = &((*new)->rb_right);
+ } else {
+ result = strcmp(cert->issuer, this->issuer);
+ if (result < 0) {
+ new = &((*new)->rb_left);
+ } else if (result > 0) {
+ new = &((*new)->rb_right);
+ } else {
+ code_sign_log_info("cert already exist in trust sources");
+ return;
+ }
+ }
+ }
+
+ // add new node
+ rb_link_node(&cert->node, parent, new);
+ rb_insert_color(&cert->node, root);
+}
+
+int code_sign_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+int code_sign_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+int code_sign_avc_has_perm(u16 tclass, u32 requested)
+{
+ struct av_decision avd;
+ u32 sid = current_sid();
+ int rc, rc2;
+
+ rc = avc_has_perm_noaudit(&selinux_state, sid, sid, tclass, requested,
+ AVC_STRICT, &avd);
+ rc2 = avc_audit(&selinux_state, sid, sid, tclass, requested, &avd, rc,
+ NULL, AVC_STRICT);
+ if (rc2)
+ return rc2;
+
+ return rc;
+}
+
+long code_sign_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
+{
+ int ret = 0;
+
+ if (code_sign_avc_has_perm(SECCLASS_CODE_SIGN, CODE_SIGN__ADD_CERT_CHAIN)) {
+ code_sign_log_error("selinux check failed, no permission to add cert chain");
+ return -EPERM;
+ }
+
+ if (cmd != WRITE_CERT_CHAIN) {
+ code_sign_log_error("code_sign cmd error, cmd: %d", cmd);
+ return -EINVAL;
+ }
+
+ struct cert_source *source = kzalloc(sizeof(struct cert_source), GFP_KERNEL);
+
+ if (!source)
+ return -ENOMEM;
+
+
+ struct cert_chain_info info;
+
+ if (copy_from_user(&info, args, sizeof(struct cert_chain_info))) {
+ code_sign_log_error("cmd copy_from_user failed");
+ ret = -ENOMEM;
+ goto copy_source_failed;
+ }
+
+ if (info.path_len > CERT_CHAIN_PATH_LEN_MAX) {
+ code_sign_log_error("invalid path len: %d", info.path_len);
+ ret = -EINVAL;
+ goto copy_source_failed;
+ }
+
+ source->subject = kzalloc(info.signing_length, GFP_KERNEL);
+ if (!source->subject) {
+ ret = -ENOMEM;
+ goto copy_source_failed;
+ }
+
+ if (copy_from_user(source->subject, u64_to_user_ptr(info.signing_ptr), info.signing_length)) {
+ code_sign_log_error("copy_from_user get signing failed");
+ ret = -EFAULT;
+ goto copy_subject_failed;
+ }
+
+ source->issuer = kzalloc(info.issuer_length, GFP_KERNEL);
+ if (!source->issuer) {
+ ret = -ENOMEM;
+ goto copy_subject_failed;
+ }
+
+ ret = copy_from_user(source->issuer, u64_to_user_ptr(info.issuer_ptr), info.issuer_length);
+ if (ret) {
+ code_sign_log_error("copy_from_user get issuer failed");
+ ret = -EFAULT;
+ goto copy_issuer_failed;
+ }
+
+ source->max_path_depth = info.path_len;
+
+ code_sign_log_info("add trusted cert: subject = '%s', issuer = '%s', max_path_depth = %d",
+ source->subject, source->issuer, source->max_path_depth);
+
+ // insert rb_tree
+ cert_chain_insert(&cert_chain_tree, source);
+
+ return ret;
+
+copy_issuer_failed:
+ kfree(source->issuer);
+copy_subject_failed:
+ kfree(source->subject);
+copy_source_failed:
+ kfree(source);
+ return ret;
+}
diff --git a/code_sign/code_sign_ioctl.h b/code_sign/code_sign_ioctl.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b2980c19206df0b22b7e6c0519c048f187ceb02
--- /dev/null
+++ b/code_sign/code_sign_ioctl.h
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 Huawei Device Co., Ltd.
+ */
+
+#include
+#include <../../crypto/asymmetric_keys/pkcs7_parser.h>
+
+#ifndef _CODE_SIGN_H
+#define _CODE_SIGN_H
+
+struct cert_chain_info {
+ __u32 signing_length;
+ __u32 issuer_length;
+ __u64 signing_ptr;
+ __u64 issuer_ptr;
+ __u32 path_len;
+ __u8 __reserved[36];
+};
+
+struct cert_source {
+ char *subject;
+ char *issuer;
+ int max_path_depth;
+ struct rb_node node;
+};
+
+#define WRITE_CERT_CHAIN _IOW('k', 1, struct cert_chain_info)
+
+#define CERT_CHAIN_PATH_LEN_MAX 3
+
+/*
+ * cert_chain.c
+ */
+struct cert_source *find_match(struct x509_certificate *cert);
+
+int code_sign_open(struct inode *inode, struct file *filp);
+
+int code_sign_release(struct inode *inode, struct file *filp);
+
+long code_sign_ioctl(struct file *filp, unsigned int cmd, unsigned long args);
+
+#endif /* _CODE_SIGN_H */
diff --git a/code_sign/code_sign_log.h b/code_sign/code_sign_log.h
new file mode 100644
index 0000000000000000000000000000000000000000..06619d64980d5477ec7ca5413b2a6c1f3c138b08
--- /dev/null
+++ b/code_sign/code_sign_log.h
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 Huawei Device Co., Ltd.
+ */
+
+#ifndef _CODE_SIGN_LOG_H
+#define _CODE_SIGN_LOG_H
+
+#define CODE_SIGN_TAG "code_sign_kernel"
+#define CODE_SIGN_INFO_TAG "I"
+#define CODE_SIGN_ERROR_TAG "E"
+#define CODE_SIGN_WARN_TAG "W"
+
+#define code_sign_log_info(fmt, args...) pr_info("[%s/%s]%s: " fmt "\n", \
+ CODE_SIGN_INFO_TAG, CODE_SIGN_TAG, __func__, ##args)
+
+#define code_sign_log_error(fmt, args...) pr_err("[%s/%s]%s: " fmt "\n", \
+ CODE_SIGN_ERROR_TAG, CODE_SIGN_TAG, __func__, ##args)
+
+#define code_sign_log_warn(fmt, args...) pr_warn("[%s/%s]%s: " fmt "\n", \
+ CODE_SIGN_WARN_TAG, CODE_SIGN_TAG, __func__, ##args)
+
+#endif /* _CODE_SIGN_LOG_H */
\ No newline at end of file
diff --git a/code_sign/code_sign_misc.c b/code_sign/code_sign_misc.c
new file mode 100644
index 0000000000000000000000000000000000000000..c2237bd4fe3e9ee3d2ab9809a630d97f859d5577
--- /dev/null
+++ b/code_sign/code_sign_misc.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 Huawei Device Co., Ltd.
+ */
+
+#include
+#include
+#include
+#include
+
+#include "code_sign_ioctl.h"
+#include "code_sign_log.h"
+
+static const struct file_operations code_sign_ops = {
+ .owner = THIS_MODULE,
+ .open = code_sign_open,
+ .release = code_sign_release,
+ .unlocked_ioctl = code_sign_ioctl,
+ .compat_ioctl = code_sign_ioctl,
+};
+
+static struct miscdevice code_sign_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "code_sign",
+ .fops = &code_sign_ops,
+};
+
+static void code_sign_register_hck_hooks(void)
+{
+ REGISTER_HCK_LITE_HOOK(code_sign_verify_certchain_lhck, code_sign_verify_certchain);
+}
+
+static int __init code_sign_init(void)
+{
+ code_sign_log_info("INIT");
+ code_sign_register_hck_hooks();
+ return misc_register(&code_sign_misc);
+}
+
+static void __exit code_sign_exit(void)
+{
+ misc_deregister(&code_sign_misc);
+ code_sign_log_info("EXIT");
+}
+
+module_init(code_sign_init);
+module_exit(code_sign_exit);
+
+MODULE_LICENSE("GPL");
\ No newline at end of file
diff --git a/code_sign/verify_cert_chain.c b/code_sign/verify_cert_chain.c
new file mode 100644
index 0000000000000000000000000000000000000000..1141e0e8e637c1d783e57c57604d1585ba3f6516
--- /dev/null
+++ b/code_sign/verify_cert_chain.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 Huawei Device Co., Ltd.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include "code_sign_ioctl.h"
+#include "code_sign_log.h"
+#include "verify_cert_chain.h"
+
+/*
+ * Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7
+ * uses the issuer's name and the issuing certificate serial number for
+ * matching purposes. These must match the certificate issuer's name (not
+ * subject's name) and the certificate serial number [RFC 2315 6.7].
+ */
+static int pkcs7_find_key(struct pkcs7_message *pkcs7,
+ struct pkcs7_signed_info *sinfo)
+{
+ struct x509_certificate *cert;
+ unsigned certix = 1;
+
+ kenter("%u", sinfo->index);
+ code_sign_log_info("sinfo->index %u", sinfo->index);
+
+ cert = pkcs7->certs;
+ while (cert) {
+ if (asymmetric_key_id_same(cert->id, sinfo->sig->auth_ids[0])) {
+ if (strcmp(cert->pub->pkey_algo, sinfo->sig->pkey_algo) != 0
+ && (strncmp(cert->pub->pkey_algo, "ecdsa-", 6) != 0
+ || strcmp(cert->sig->pkey_algo, "ecdsa") != 0)) {
+ code_sign_log_warn("sig %u: X.509 algo and PKCS#7 sig algo don't match", sinfo->index);
+ cert = cert->next;
+ certix++;
+ continue;
+ }
+ } else {
+ code_sign_log_warn("sig %u: X.509->id and PKCS#7 sinfo->sig->auth_ids[0] don't match",
+ sinfo->index, cert->id, sinfo->sig->auth_ids[0]);
+ cert = cert->next;
+ certix++;
+ continue;
+ }
+
+ // cert is found
+ sinfo->signer = cert;
+ return 0;
+ }
+
+ /* The relevant X.509 cert isn't found here, but it might be found in
+ * the trust keyring.
+ */
+ code_sign_log_info("Sig %u: Issuing X.509 cert not found (#%*phN)",
+ sinfo->index,
+ sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data);
+ return 0;
+}
+
+void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, int *ret)
+{
+ struct pkcs7_message *pkcs7;
+ struct pkcs7_signed_info *sinfo;
+
+ pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
+ if (IS_ERR(pkcs7)) {
+ code_sign_log_error("parse pkcs7 message failed");
+ *ret = PTR_ERR(pkcs7);
+ return;
+ }
+
+ // no cert chain, verify by certificates in keyring
+ if (!pkcs7->certs) {
+ code_sign_log_warn("no certs in pkcs7, might be found in trust keyring");
+ *ret = 0;
+ return;
+ }
+
+ if (!pkcs7->signed_infos) {
+ code_sign_log_error("signed info not found in pkcs7");
+ *ret = -EKEYREJECTED;
+ return;
+ }
+
+ for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
+ /* Find the key for the signature if there is one */
+ *ret = pkcs7_find_key(pkcs7, sinfo);
+ if (*ret) {
+ code_sign_log_error("key not find in pkcs7");
+ return;
+ }
+
+ struct x509_certificate *signer = sinfo->signer;
+
+ if (!signer) {
+ code_sign_log_error("signer cert not found in pkcs7");
+ *ret = -EINVAL;
+ return;
+ }
+
+ struct cert_source *source = find_match(signer);
+ if (source == NULL) {
+ code_sign_log_error("signer certificate's subject and issuer not trusted");
+ *ret = -EKEYREJECTED;
+ return;
+ }
+
+ // cal cert chain depth
+ int cert_chain_depth_without_root = 1;
+ char *issuer = signer->issuer;
+ struct x509_certificate* cert = pkcs7->certs;
+ while(cert) {
+ // if issuer cert is found
+ if (cert->subject && (strcmp(cert->subject, issuer) == 0)) {
+ // reach root CA, end search
+ if (strcmp(cert->subject, cert->issuer) == 0) {
+ break;
+ }
+ cert_chain_depth_without_root++;
+ // search agains for current issuer's issuer
+ issuer = cert->issuer;
+ cert = pkcs7->certs;
+ } else {
+ // move to next certificate
+ cert = cert->next;
+ }
+ }
+ if (cert_chain_depth_without_root == (source->max_path_depth - 1)) {
+ code_sign_log_info("cert subject and issuer trusted");
+ *ret = 0;
+ return;
+ } else {
+ code_sign_log_error("depth mismatch: cert chain depth without root is %d, max_path_depth is %d",
+ cert_chain_depth_without_root, source->max_path_depth);
+ }
+ }
+
+ code_sign_log_error("cert subject and issuer verify failed");
+ *ret = -EKEYREJECTED;
+ return;
+}
diff --git a/code_sign/verify_cert_chain.h b/code_sign/verify_cert_chain.h
new file mode 100644
index 0000000000000000000000000000000000000000..49b8d6d97a7fb292f03749aa847853a79fe030c5
--- /dev/null
+++ b/code_sign/verify_cert_chain.h
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2023 Huawei Device Co., Ltd.
+ */
+
+#ifndef _VERIFY_CERT_CHAIN_H
+#define _VERIFY_CERT_CHAIN_H
+
+/*
+ * verify_cert_chain.c
+ */
+
+void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, int *ret);
+
+#endif /* _VERIFY_CERT_CHAIN_H */