mirror of
https://github.com/openharmony/kernel_linux_common_modules.git
synced 2026-08-27 01:31:36 -04:00
add DELETE_CERT_CHAIN & support developer mode
Change-Id: I1becd4f785ced67870ae995eeed80add6287d59b Signed-off-by: zengsiyu <zengsiyu3@huawei.com>
This commit is contained in:
+153
-28
@@ -9,25 +9,27 @@
|
||||
#include <linux/compat.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/code_sign.h>
|
||||
#include <linux/rbtree.h>
|
||||
#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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/verification.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#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",
|
||||
|
||||
Reference in New Issue
Block a user