1419b0af8Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2419b0af8Sopenharmony_ci/* 3419b0af8Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd. 4419b0af8Sopenharmony_ci */ 5419b0af8Sopenharmony_ci 6419b0af8Sopenharmony_ci#include <linux/cred.h> 7419b0af8Sopenharmony_ci#include <linux/key.h> 8419b0af8Sopenharmony_ci#include <linux/slab.h> 9419b0af8Sopenharmony_ci#include <linux/version.h> 10419b0af8Sopenharmony_ci#include <linux/verification.h> 11419b0af8Sopenharmony_ci#include <crypto/pkcs7.h> 12419b0af8Sopenharmony_ci#include "objsec.h" 13419b0af8Sopenharmony_ci#include "code_sign_ext.h" 14419b0af8Sopenharmony_ci#include "code_sign_ioctl.h" 15419b0af8Sopenharmony_ci#include "code_sign_log.h" 16419b0af8Sopenharmony_ci#include "verify_cert_chain.h" 17419b0af8Sopenharmony_ci 18419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_XPM 19419b0af8Sopenharmony_ci#include "dsmm_developer.h" 20419b0af8Sopenharmony_ci#endif 21419b0af8Sopenharmony_ci 22419b0af8Sopenharmony_ci/* 23419b0af8Sopenharmony_ci * Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7 24419b0af8Sopenharmony_ci * uses the issuer's name and the issuing certificate serial number for 25419b0af8Sopenharmony_ci * matching purposes. These must match the certificate issuer's name (not 26419b0af8Sopenharmony_ci * subject's name) and the certificate serial number [RFC 2315 6.7]. 27419b0af8Sopenharmony_ci */ 28419b0af8Sopenharmony_cistatic int pkcs7_find_key(struct pkcs7_message *pkcs7, 29419b0af8Sopenharmony_ci struct pkcs7_signed_info *sinfo) 30419b0af8Sopenharmony_ci{ 31419b0af8Sopenharmony_ci struct x509_certificate *cert; 32419b0af8Sopenharmony_ci unsigned certix = 1; 33419b0af8Sopenharmony_ci 34419b0af8Sopenharmony_ci kenter("%u", sinfo->index); 35419b0af8Sopenharmony_ci code_sign_log_info("sinfo->index %u", sinfo->index); 36419b0af8Sopenharmony_ci 37419b0af8Sopenharmony_ci cert = pkcs7->certs; 38419b0af8Sopenharmony_ci while (cert) { 39419b0af8Sopenharmony_ci if (asymmetric_key_id_same(cert->id, sinfo->sig->auth_ids[0])) { 40419b0af8Sopenharmony_ci if (strcmp(cert->pub->pkey_algo, sinfo->sig->pkey_algo) != 0 41419b0af8Sopenharmony_ci && (strncmp(cert->pub->pkey_algo, "ecdsa-", 6) != 0 42419b0af8Sopenharmony_ci || strcmp(cert->sig->pkey_algo, "ecdsa") != 0)) { 43419b0af8Sopenharmony_ci code_sign_log_warn("sig %u: X.509 algo and PKCS#7 sig algo don't match", sinfo->index); 44419b0af8Sopenharmony_ci cert = cert->next; 45419b0af8Sopenharmony_ci certix++; 46419b0af8Sopenharmony_ci continue; 47419b0af8Sopenharmony_ci } 48419b0af8Sopenharmony_ci } else { 49419b0af8Sopenharmony_ci code_sign_log_warn("sig %u: X.509->id and PKCS#7 sinfo->sig->auth_ids[0] don't match", 50419b0af8Sopenharmony_ci sinfo->index, cert->id, sinfo->sig->auth_ids[0]); 51419b0af8Sopenharmony_ci cert = cert->next; 52419b0af8Sopenharmony_ci certix++; 53419b0af8Sopenharmony_ci continue; 54419b0af8Sopenharmony_ci } 55419b0af8Sopenharmony_ci 56419b0af8Sopenharmony_ci // cert is found 57419b0af8Sopenharmony_ci sinfo->signer = cert; 58419b0af8Sopenharmony_ci return 0; 59419b0af8Sopenharmony_ci } 60419b0af8Sopenharmony_ci 61419b0af8Sopenharmony_ci /* The relevant X.509 cert isn't found here, but it might be found in 62419b0af8Sopenharmony_ci * the trust keyring. 63419b0af8Sopenharmony_ci */ 64419b0af8Sopenharmony_ci code_sign_log_info("Sig %u: Issuing X.509 cert not found (#%*phN)", 65419b0af8Sopenharmony_ci sinfo->index, 66419b0af8Sopenharmony_ci sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data); 67419b0af8Sopenharmony_ci return 0; 68419b0af8Sopenharmony_ci} 69419b0af8Sopenharmony_ci 70419b0af8Sopenharmony_cistatic void set_file_ownerid(struct cs_info *cs_info, int path_type, 71419b0af8Sopenharmony_ci struct pkcs7_signed_info *sinfo) 72419b0af8Sopenharmony_ci{ 73419b0af8Sopenharmony_ci if (cs_info == NULL) 74419b0af8Sopenharmony_ci return; 75419b0af8Sopenharmony_ci 76419b0af8Sopenharmony_ci /* Mark a debug file as OWNERID_DEBUG */ 77419b0af8Sopenharmony_ci if((path_type > DEBUG_CODE_START) && (path_type < DEBUG_CODE_END)) { 78419b0af8Sopenharmony_ci code_sign_set_ownerid(cs_info, FILE_OWNERID_DEBUG, NULL, 0); 79419b0af8Sopenharmony_ci return; 80419b0af8Sopenharmony_ci } 81419b0af8Sopenharmony_ci 82419b0af8Sopenharmony_ci /* Mark the file as OWNERID_COMPAT, if its ownerid is empty */ 83419b0af8Sopenharmony_ci if(!sinfo->ownerid) { 84419b0af8Sopenharmony_ci code_sign_set_ownerid(cs_info, FILE_OWNERID_COMPAT, NULL, 0); 85419b0af8Sopenharmony_ci return; 86419b0af8Sopenharmony_ci } 87419b0af8Sopenharmony_ci 88419b0af8Sopenharmony_ci /* Mark the file as OWNERID_SHARED, if the file is shareable */ 89419b0af8Sopenharmony_ci if((sinfo->ownerid_len == strlen(OWNERID_SHARED_TAG)) && 90419b0af8Sopenharmony_ci !memcmp(sinfo->ownerid, OWNERID_SHARED_TAG, 91419b0af8Sopenharmony_ci sinfo->ownerid_len)) { 92419b0af8Sopenharmony_ci code_sign_set_ownerid(cs_info, FILE_OWNERID_SHARED, NULL, 0); 93419b0af8Sopenharmony_ci return; 94419b0af8Sopenharmony_ci } 95419b0af8Sopenharmony_ci 96419b0af8Sopenharmony_ci /* If this code is signed on the device, check whether it is DEBUG_ID */ 97419b0af8Sopenharmony_ci if((path_type = MAY_LOCAL_CODE) && 98419b0af8Sopenharmony_ci (sinfo->ownerid_len == strlen(OWNERID_DEBUG_TAG)) && 99419b0af8Sopenharmony_ci !memcmp(sinfo->ownerid, OWNERID_DEBUG_TAG, 100419b0af8Sopenharmony_ci sinfo->ownerid_len)) { 101419b0af8Sopenharmony_ci code_sign_set_ownerid(cs_info, FILE_OWNERID_DEBUG, NULL, 0); 102419b0af8Sopenharmony_ci return; 103419b0af8Sopenharmony_ci } 104419b0af8Sopenharmony_ci 105419b0af8Sopenharmony_ci /* Mark the file OWNERID_APP in other cases */ 106419b0af8Sopenharmony_ci code_sign_set_ownerid(cs_info, FILE_OWNERID_APP, 107419b0af8Sopenharmony_ci sinfo->ownerid, sinfo->ownerid_len); 108419b0af8Sopenharmony_ci} 109419b0af8Sopenharmony_ci 110419b0af8Sopenharmony_cistatic struct cert_source *find_matched_source(const struct x509_certificate *signer, bool is_debug) 111419b0af8Sopenharmony_ci{ 112419b0af8Sopenharmony_ci int block_type = is_debug ? DEBUG_BLOCK_CODE: RELEASE_BLOCK_CODE; 113419b0af8Sopenharmony_ci struct cert_source *source = find_match(signer->subject, signer->issuer, is_debug); 114419b0af8Sopenharmony_ci 115419b0af8Sopenharmony_ci if (source == NULL) { 116419b0af8Sopenharmony_ci source = find_match("ALL", signer->issuer, is_debug); 117419b0af8Sopenharmony_ci } else if (source->path_type == block_type) { 118419b0af8Sopenharmony_ci code_sign_log_error("signer certificate's type not trusted"); 119419b0af8Sopenharmony_ci return NULL; 120419b0af8Sopenharmony_ci } 121419b0af8Sopenharmony_ci return source; 122419b0af8Sopenharmony_ci} 123419b0af8Sopenharmony_ci 124419b0af8Sopenharmony_civoid code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, 125419b0af8Sopenharmony_ci struct cs_info *cs_info, int *ret) 126419b0af8Sopenharmony_ci{ 127419b0af8Sopenharmony_ci struct pkcs7_message *pkcs7; 128419b0af8Sopenharmony_ci struct pkcs7_signed_info *sinfo; 129419b0af8Sopenharmony_ci 130419b0af8Sopenharmony_ci pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len); 131419b0af8Sopenharmony_ci if (IS_ERR(pkcs7)) { 132419b0af8Sopenharmony_ci code_sign_log_error("parse pkcs7 message failed"); 133419b0af8Sopenharmony_ci *ret = PTR_ERR(pkcs7); 134419b0af8Sopenharmony_ci return; 135419b0af8Sopenharmony_ci } 136419b0af8Sopenharmony_ci 137419b0af8Sopenharmony_ci if (!pkcs7->signed_infos) { 138419b0af8Sopenharmony_ci code_sign_log_error("signed info not found in pkcs7"); 139419b0af8Sopenharmony_ci goto untrusted; 140419b0af8Sopenharmony_ci } 141419b0af8Sopenharmony_ci 142419b0af8Sopenharmony_ci // no cert chain, verify by certificates in keyring 143419b0af8Sopenharmony_ci if (!pkcs7->certs) { 144419b0af8Sopenharmony_ci code_sign_log_warn("no certs in pkcs7, might be found in trust keyring"); 145419b0af8Sopenharmony_ci *ret = MAY_LOCAL_CODE; 146419b0af8Sopenharmony_ci goto exit; 147419b0af8Sopenharmony_ci } 148419b0af8Sopenharmony_ci 149419b0af8Sopenharmony_ci bool is_dev_mode = false; 150419b0af8Sopenharmony_ci 151419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_XPM 152419b0af8Sopenharmony_ci // developer mode && developer proc 153419b0af8Sopenharmony_ci if (get_developer_mode_state() == STATE_ON) { 154419b0af8Sopenharmony_ci code_sign_log_info("developer mode on"); 155419b0af8Sopenharmony_ci is_dev_mode = true; 156419b0af8Sopenharmony_ci } 157419b0af8Sopenharmony_ci#endif 158419b0af8Sopenharmony_ci 159419b0af8Sopenharmony_ci for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { 160419b0af8Sopenharmony_ci /* Find the key for the signature if there is one */ 161419b0af8Sopenharmony_ci *ret = pkcs7_find_key(pkcs7, sinfo); 162419b0af8Sopenharmony_ci if (*ret) { 163419b0af8Sopenharmony_ci code_sign_log_error("key not find in pkcs7"); 164419b0af8Sopenharmony_ci goto exit; 165419b0af8Sopenharmony_ci } 166419b0af8Sopenharmony_ci 167419b0af8Sopenharmony_ci const struct x509_certificate *signer = sinfo->signer; 168419b0af8Sopenharmony_ci if (!signer) { 169419b0af8Sopenharmony_ci code_sign_log_error("signer cert not found in pkcs7"); 170419b0af8Sopenharmony_ci *ret = -EINVAL; 171419b0af8Sopenharmony_ci goto exit; 172419b0af8Sopenharmony_ci } 173419b0af8Sopenharmony_ci 174419b0af8Sopenharmony_ci struct cert_source *source = find_matched_source(signer, false); 175419b0af8Sopenharmony_ci if (!source) { 176419b0af8Sopenharmony_ci if (is_dev_mode) { 177419b0af8Sopenharmony_ci // find on dev trusted list 178419b0af8Sopenharmony_ci source = find_matched_source(signer, true); 179419b0af8Sopenharmony_ci if (!source) 180419b0af8Sopenharmony_ci goto untrusted; 181419b0af8Sopenharmony_ci } else { 182419b0af8Sopenharmony_ci goto untrusted; 183419b0af8Sopenharmony_ci } 184419b0af8Sopenharmony_ci } 185419b0af8Sopenharmony_ci 186419b0af8Sopenharmony_ci // cal cert chain depth 187419b0af8Sopenharmony_ci int cert_chain_depth_without_root = 1; 188419b0af8Sopenharmony_ci char *issuer = signer->issuer; 189419b0af8Sopenharmony_ci struct x509_certificate* cert = pkcs7->certs; 190419b0af8Sopenharmony_ci while(cert) { 191419b0af8Sopenharmony_ci // if issuer cert is found 192419b0af8Sopenharmony_ci if (cert->subject && (strcmp(cert->subject, issuer) == 0)) { 193419b0af8Sopenharmony_ci // reach root CA, end search 194419b0af8Sopenharmony_ci if (strcmp(cert->subject, cert->issuer) == 0) { 195419b0af8Sopenharmony_ci break; 196419b0af8Sopenharmony_ci } 197419b0af8Sopenharmony_ci cert_chain_depth_without_root++; 198419b0af8Sopenharmony_ci // search again for current issuer's issuer 199419b0af8Sopenharmony_ci issuer = cert->issuer; 200419b0af8Sopenharmony_ci cert = pkcs7->certs; 201419b0af8Sopenharmony_ci } else { 202419b0af8Sopenharmony_ci // move to next certificate 203419b0af8Sopenharmony_ci cert = cert->next; 204419b0af8Sopenharmony_ci } 205419b0af8Sopenharmony_ci } 206419b0af8Sopenharmony_ci if (cert_chain_depth_without_root == (source->max_path_depth - 1)) { 207419b0af8Sopenharmony_ci code_sign_log_info("cert subject and issuer trusted"); 208419b0af8Sopenharmony_ci set_file_ownerid(cs_info, source->path_type, pkcs7->signed_infos); 209419b0af8Sopenharmony_ci *ret = source->path_type; 210419b0af8Sopenharmony_ci goto exit; 211419b0af8Sopenharmony_ci } else { 212419b0af8Sopenharmony_ci code_sign_log_error("depth mismatch: cert chain depth without root is %d, max_path_depth is %d", 213419b0af8Sopenharmony_ci cert_chain_depth_without_root, source->max_path_depth); 214419b0af8Sopenharmony_ci } 215419b0af8Sopenharmony_ci } 216419b0af8Sopenharmony_ci 217419b0af8Sopenharmony_ciuntrusted: 218419b0af8Sopenharmony_ci code_sign_log_error("cert subject and issuer verify failed"); 219419b0af8Sopenharmony_ci *ret = -EKEYREJECTED; 220419b0af8Sopenharmony_ciexit: 221419b0af8Sopenharmony_ci pkcs7_free_message(pkcs7); 222419b0af8Sopenharmony_ci} 223