mirror of
https://github.com/openharmony/kernel_linux_common_modules.git
synced 2026-08-27 01:31:36 -04:00
feat: common modules 代码签名模块
Signed-off-by: Hongjin Li <lihongjin1@huawei.com>
This commit is contained in:
@@ -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.
|
||||
@@ -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)
|
||||
Executable
+28
@@ -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
|
||||
@@ -0,0 +1,183 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/compat.h>
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
#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 */
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,49 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/hck/lite_hck_code_sign.h>
|
||||
|
||||
#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");
|
||||
@@ -0,0 +1,144 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/cred.h>
|
||||
#include <linux/key.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/verification.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#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;
|
||||
}
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user