From cfe6f0cfe8a3fedff47f0b6af4df61e75a65d4c8 Mon Sep 17 00:00:00 2001 From: zengsiyu Date: Sat, 4 Nov 2023 21:02:10 +0800 Subject: [PATCH] add DELETE_CERT_CHAIN & support developer mode Change-Id: I1becd4f785ced67870ae995eeed80add6287d59b Signed-off-by: zengsiyu --- code_sign/code_sign_ioctl.c | 181 ++++++++++++++++++++++++++++------ code_sign/code_sign_ioctl.h | 17 +++- code_sign/code_sign_log.h | 4 + code_sign/verify_cert_chain.c | 31 +++++- 4 files changed, 196 insertions(+), 37 deletions(-) diff --git a/code_sign/code_sign_ioctl.c b/code_sign/code_sign_ioctl.c index bbd3510..15f2405 100644 --- a/code_sign/code_sign_ioctl.c +++ b/code_sign/code_sign_ioctl.c @@ -9,25 +9,27 @@ #include #include "avc.h" #include "objsec.h" +#include "../../security/xpm/include/dsmm_developer.h" #include "code_sign_ioctl.h" #include "code_sign_log.h" struct rb_root cert_chain_tree = RB_ROOT; +struct rb_root dev_cert_chain_tree = RB_ROOT; -struct cert_source *cert_chain_search(struct rb_root *root, struct x509_certificate *cert) +struct cert_source *cert_chain_search(struct rb_root *root, char *subject, char *issuer) { 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); + int result = strcmp(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); + result = strcmp(issuer, cur_cert->issuer); if (result < 0) { cur_node = &((*cur_node)->rb_left); } else if (result > 0) { @@ -42,13 +44,40 @@ struct cert_source *cert_chain_search(struct rb_root *root, struct x509_certific return NULL; } -struct cert_source *find_match(struct x509_certificate *cert) +struct cert_source *find_match(struct x509_certificate *cert, bool is_dev) { - return cert_chain_search(&cert_chain_tree, cert); + if (is_dev) + return cert_chain_search(&dev_cert_chain_tree, cert->subject, cert->issuer); + else + return cert_chain_search(&cert_chain_tree, cert->subject, cert->issuer); } -void cert_chain_insert(struct rb_root *root, struct cert_source *cert) +int code_sign_check_caller(char *caller) { + u32 sid = current_sid(), context_len; + char *context = NULL; + int rc; + + rc = security_sid_to_context(&selinux_state, sid, &context, &context_len); + if (rc) + return rc; + + code_sign_log_debug("sid=%d, context=%s", sid, context); + if (strncmp(caller, context, strlen(caller))) + return 0; + else + return -EKEYREJECTED; +} + +int cert_chain_insert(struct rb_root *root, struct cert_source *cert) +{ + // procs except key_enable are only allowed to insert developer_code + if (!code_sign_check_caller(KEY_ENABLE_CTX) && !(cert->path_type == RELEASE_DEVELOPER_CODE + || cert->path_type == DEBUG_DEVELOPER_CODE)) { + code_sign_log_error("no permission to insert code %d", cert->path_type); + return -EKEYREJECTED; + } + struct rb_node **new = &(root->rb_node), *parent = NULL; while (*new) { @@ -67,15 +96,43 @@ void cert_chain_insert(struct rb_root *root, struct cert_source *cert) } else if (result > 0) { new = &((*new)->rb_right); } else { + this->cnt++; code_sign_log_info("cert already exist in trust sources"); - return; + return 0; } } } // add new node + cert->cnt++; rb_link_node(&cert->node, parent, new); rb_insert_color(&cert->node, root); + + code_sign_log_info("add trusted cert: subject = '%s', issuer = '%s', max_path_depth = %d", + cert->subject, cert->issuer, cert->max_path_depth); + return 0; +} + +int cert_chain_remove(struct rb_root *root, struct cert_source *cert) +{ + struct cert_source *matched_cert = cert_chain_search(root, cert->subject, cert->issuer); + + if (!matched_cert) + return -EINVAL; + + if (matched_cert->path_type == RELEASE_DEVELOPER_CODE + || matched_cert->path_type == DEBUG_DEVELOPER_CODE) { + --matched_cert->cnt; + if (matched_cert->cnt > 0) + return 0; + rb_erase(&matched_cert->node, root); + code_sign_log_info("remove trusted cert: subject = '%s', issuer = '%s', max_path_depth = %d", + cert->subject, cert->issuer, cert->max_path_depth); + return 0; + } + + code_sign_log_error("can not remove cert type %x", cert->path_type); + return -EKEYREJECTED; } int code_sign_open(struct inode *inode, struct file *filp) @@ -104,26 +161,14 @@ int code_sign_avc_has_perm(u16 tclass, u32 requested) return rc; } -long code_sign_ioctl(struct file *filp, unsigned int cmd, unsigned long args) +int parse_cert_source(unsigned long args, struct cert_source **_source) { 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))) { @@ -132,8 +177,8 @@ long code_sign_ioctl(struct file *filp, unsigned int cmd, unsigned long args) goto copy_source_failed; } - if (info.path_len > CERT_CHAIN_PATH_LEN_MAX) { - code_sign_log_error("invalid path len: %d", info.path_len); + if (info.path_len > CERT_CHAIN_PATH_LEN_MAX || info.issuer_length == 0 || info.signing_length == 0) { + code_sign_log_error("invalid path len or subject or issuer"); ret = -EINVAL; goto copy_source_failed; } @@ -164,13 +209,9 @@ long code_sign_ioctl(struct file *filp, unsigned int cmd, unsigned long args) } source->max_path_depth = info.path_len; + source->path_type = info.cert_type; - 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); - + *_source = source; return ret; copy_issuer_failed: @@ -181,3 +222,87 @@ copy_source_failed: kfree(source); return ret; } + +int code_sign_check_code(int code) +{ + int is_dev_mode = 0; + + if (code > RELEASE_CODE_START && code < RELEASE_CODE_END) + return is_dev_mode; + + // developer mode + if (!strcmp(developer_mode_state(), DEVELOPER_STATUS_ON)) { + code_sign_log_debug("developer mode on"); + is_dev_mode = 1; + } + + if (is_dev_mode && (code > DEBUG_CODE_START && code < DEBUG_CODE_END)) + return is_dev_mode; + + code_sign_log_error("cert type %x is invalid", code); + return -EINVAL; +} + +long code_sign_ioctl(struct file *filp, unsigned int cmd, unsigned long args) +{ + int ret = 0; + struct cert_source *source; + + switch (cmd) { + case ADD_CERT_CHAIN: + 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; + } + + ret = parse_cert_source(args, &source); + if (ret) + return ret; + + // insert rb_tree + ret = code_sign_check_code(source->path_type); + if (ret < 0) + return ret; + + if (ret) { + // developer cert + code_sign_log_debug("add developer cert"); + source->cnt++; + ret = cert_chain_insert(&dev_cert_chain_tree, source); + } else { + code_sign_log_debug("add release cert"); + ret = cert_chain_insert(&cert_chain_tree, source); + } + break; + case REMOVE_CERT_CHAIN: + if (code_sign_avc_has_perm(SECCLASS_CODE_SIGN, CODE_SIGN__REMOVE_CERT_CHAIN)) { + code_sign_log_error("selinux check failed, no permission to remove cert chain"); + return -EPERM; + } + + ret = parse_cert_source(args, &source); + if (ret) + return ret; + + // delete rb_tree + ret = code_sign_check_code(source->path_type); + if (ret < 0) + return ret; + + if (ret) { + // developer cert + code_sign_log_debug("remove developer cert"); + cert_chain_remove(&dev_cert_chain_tree, source); + } else { + code_sign_log_debug("remove release cert"); + cert_chain_remove(&cert_chain_tree, source); + } + break; + default: + code_sign_log_error("code_sign cmd error, cmd: %d", cmd); + ret = -EINVAL; + break; + } + + return ret; +} diff --git a/code_sign/code_sign_ioctl.h b/code_sign/code_sign_ioctl.h index 8b2980c..ef4a70e 100644 --- a/code_sign/code_sign_ioctl.h +++ b/code_sign/code_sign_ioctl.h @@ -3,6 +3,7 @@ * Copyright (c) 2023 Huawei Device Co., Ltd. */ +#include #include #include <../../crypto/asymmetric_keys/pkcs7_parser.h> @@ -15,24 +16,32 @@ struct cert_chain_info { __u64 signing_ptr; __u64 issuer_ptr; __u32 path_len; - __u8 __reserved[36]; + __s32 cert_type; + __u8 __reserved[32]; }; struct cert_source { char *subject; char *issuer; - int max_path_depth; + unsigned int max_path_depth; + int path_type; + unsigned int cnt; struct rb_node node; }; -#define WRITE_CERT_CHAIN _IOW('k', 1, struct cert_chain_info) +#define ADD_CERT_CHAIN _IOW('k', 1, struct cert_chain_info) +#define REMOVE_CERT_CHAIN _IOW('k', 2, struct cert_chain_info) #define CERT_CHAIN_PATH_LEN_MAX 3 +#define KEY_ENABLE_CTX "u:r:key_enable:" + /* * cert_chain.c */ -struct cert_source *find_match(struct x509_certificate *cert); +struct cert_source *find_match(struct x509_certificate *cert, bool is_dev); + +int code_sign_avc_has_perm(u16 tclass, u32 requested); int code_sign_open(struct inode *inode, struct file *filp); diff --git a/code_sign/code_sign_log.h b/code_sign/code_sign_log.h index 06619d6..a7224fc 100644 --- a/code_sign/code_sign_log.h +++ b/code_sign/code_sign_log.h @@ -7,10 +7,14 @@ #define _CODE_SIGN_LOG_H #define CODE_SIGN_TAG "code_sign_kernel" +#define CODE_SIGN_DEBUG_TAG "D" #define CODE_SIGN_INFO_TAG "I" #define CODE_SIGN_ERROR_TAG "E" #define CODE_SIGN_WARN_TAG "W" +#define code_sign_log_debug(fmt, args...) pr_debug("[%s/%s]%s: " fmt "\n", \ + CODE_SIGN_DEBUG_TAG, CODE_SIGN_TAG, __func__, ##args) + #define code_sign_log_info(fmt, args...) pr_info("[%s/%s]%s: " fmt "\n", \ CODE_SIGN_INFO_TAG, CODE_SIGN_TAG, __func__, ##args) diff --git a/code_sign/verify_cert_chain.c b/code_sign/verify_cert_chain.c index 1141e0e..605e9a4 100644 --- a/code_sign/verify_cert_chain.c +++ b/code_sign/verify_cert_chain.c @@ -8,6 +8,8 @@ #include #include #include +#include "objsec.h" +#include "../../security/xpm/include/dsmm_developer.h" #include "code_sign_ioctl.h" #include "code_sign_log.h" #include "verify_cert_chain.h" @@ -75,7 +77,7 @@ void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, int *re // 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; + *ret = MAY_LOCAL_CODE; return; } @@ -85,6 +87,17 @@ void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, int *re return; } + bool is_dev_mode = false, is_dev_proc = false; + + // developer mode && developer proc + if (!strcmp(developer_mode_state(), DEVELOPER_STATUS_ON)) { + code_sign_log_info("developer mode on"); + is_dev_mode = true; + if (!code_sign_avc_has_perm(SECCLASS_XPM, XPM__EXEC_NO_SIGN)) { + is_dev_proc = true; + } + } + 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); @@ -101,9 +114,17 @@ void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, int *re return; } - struct cert_source *source = find_match(signer); + struct cert_source *source = find_match(signer, is_dev_proc); if (source == NULL) { - code_sign_log_error("signer certificate's subject and issuer not trusted"); + signer->subject = "ALL"; + source = find_match(signer, is_dev_proc); + if (source == NULL) { + code_sign_log_error("signer certificate's subject and issuer not trusted"); + *ret = -EKEYREJECTED; + return; + } + } else if (source->path_type == RELEASE_BLOCK_CODE || source->path_type == DEBUG_BLOCK_CODE) { + code_sign_log_error("signer certificate's type not trusted"); *ret = -EKEYREJECTED; return; } @@ -120,7 +141,7 @@ void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, int *re break; } cert_chain_depth_without_root++; - // search agains for current issuer's issuer + // search again for current issuer's issuer issuer = cert->issuer; cert = pkcs7->certs; } else { @@ -130,7 +151,7 @@ void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, int *re } if (cert_chain_depth_without_root == (source->max_path_depth - 1)) { code_sign_log_info("cert subject and issuer trusted"); - *ret = 0; + *ret = source->path_type; return; } else { code_sign_log_error("depth mismatch: cert chain depth without root is %d, max_path_depth is %d", -- Gitee