add DELETE_CERT_CHAIN & support developer mode

Change-Id: I1becd4f785ced67870ae995eeed80add6287d59b
Signed-off-by: zengsiyu <zengsiyu3@huawei.com>
This commit is contained in:
zengsiyu
2023-11-04 21:02:10 +08:00
parent 2099969b4e
commit b5be187cdb
4 changed files with 196 additions and 37 deletions
+153 -28
View File
@@ -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;
}
+13 -4
View File
@@ -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);
+4
View File
@@ -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)
+26 -5
View File
@@ -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",