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 */