1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2023 Huawei Device Co., Ltd. 4 */ 5 6#include <linux/cred.h> 7#include <linux/key.h> 8#include <linux/slab.h> 9#include <linux/version.h> 10#include <linux/verification.h> 11#include <crypto/pkcs7.h> 12#include "objsec.h" 13#include "code_sign_ext.h" 14#include "code_sign_ioctl.h" 15#include "code_sign_log.h" 16#include "verify_cert_chain.h" 17 18#ifdef CONFIG_SECURITY_XPM 19#include "dsmm_developer.h" 20#endif 21 22/* 23 * Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7 24 * uses the issuer's name and the issuing certificate serial number for 25 * matching purposes. These must match the certificate issuer's name (not 26 * subject's name) and the certificate serial number [RFC 2315 6.7]. 27 */ 28static int pkcs7_find_key(struct pkcs7_message *pkcs7, 29 struct pkcs7_signed_info *sinfo) 30{ 31 struct x509_certificate *cert; 32 unsigned certix = 1; 33 34 kenter("%u", sinfo->index); 35 code_sign_log_info("sinfo->index %u", sinfo->index); 36 37 cert = pkcs7->certs; 38 while (cert) { 39 if (asymmetric_key_id_same(cert->id, sinfo->sig->auth_ids[0])) { 40 if (strcmp(cert->pub->pkey_algo, sinfo->sig->pkey_algo) != 0 41 && (strncmp(cert->pub->pkey_algo, "ecdsa-", 6) != 0 42 || strcmp(cert->sig->pkey_algo, "ecdsa") != 0)) { 43 code_sign_log_warn("sig %u: X.509 algo and PKCS#7 sig algo don't match", sinfo->index); 44 cert = cert->next; 45 certix++; 46 continue; 47 } 48 } else { 49 code_sign_log_warn("sig %u: X.509->id and PKCS#7 sinfo->sig->auth_ids[0] don't match", 50 sinfo->index, cert->id, sinfo->sig->auth_ids[0]); 51 cert = cert->next; 52 certix++; 53 continue; 54 } 55 56 // cert is found 57 sinfo->signer = cert; 58 return 0; 59 } 60 61 /* The relevant X.509 cert isn't found here, but it might be found in 62 * the trust keyring. 63 */ 64 code_sign_log_info("Sig %u: Issuing X.509 cert not found (#%*phN)", 65 sinfo->index, 66 sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data); 67 return 0; 68} 69 70static void set_file_ownerid(struct cs_info *cs_info, int path_type, 71 struct pkcs7_signed_info *sinfo) 72{ 73 if (cs_info == NULL) 74 return; 75 76 /* Mark a debug file as OWNERID_DEBUG */ 77 if((path_type > DEBUG_CODE_START) && (path_type < DEBUG_CODE_END)) { 78 code_sign_set_ownerid(cs_info, FILE_OWNERID_DEBUG, NULL, 0); 79 return; 80 } 81 82 /* Mark the file as OWNERID_COMPAT, if its ownerid is empty */ 83 if(!sinfo->ownerid) { 84 code_sign_set_ownerid(cs_info, FILE_OWNERID_COMPAT, NULL, 0); 85 return; 86 } 87 88 /* Mark the file as OWNERID_SHARED, if the file is shareable */ 89 if((sinfo->ownerid_len == strlen(OWNERID_SHARED_TAG)) && 90 !memcmp(sinfo->ownerid, OWNERID_SHARED_TAG, 91 sinfo->ownerid_len)) { 92 code_sign_set_ownerid(cs_info, FILE_OWNERID_SHARED, NULL, 0); 93 return; 94 } 95 96 /* If this code is signed on the device, check whether it is DEBUG_ID */ 97 if((path_type = MAY_LOCAL_CODE) && 98 (sinfo->ownerid_len == strlen(OWNERID_DEBUG_TAG)) && 99 !memcmp(sinfo->ownerid, OWNERID_DEBUG_TAG, 100 sinfo->ownerid_len)) { 101 code_sign_set_ownerid(cs_info, FILE_OWNERID_DEBUG, NULL, 0); 102 return; 103 } 104 105 /* Mark the file OWNERID_APP in other cases */ 106 code_sign_set_ownerid(cs_info, FILE_OWNERID_APP, 107 sinfo->ownerid, sinfo->ownerid_len); 108} 109 110static struct cert_source *find_matched_source(const struct x509_certificate *signer, bool is_debug) 111{ 112 int block_type = is_debug ? DEBUG_BLOCK_CODE: RELEASE_BLOCK_CODE; 113 struct cert_source *source = find_match(signer->subject, signer->issuer, is_debug); 114 115 if (source == NULL) { 116 source = find_match("ALL", signer->issuer, is_debug); 117 } else if (source->path_type == block_type) { 118 code_sign_log_error("signer certificate's type not trusted"); 119 return NULL; 120 } 121 return source; 122} 123 124void code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, 125 struct cs_info *cs_info, int *ret) 126{ 127 struct pkcs7_message *pkcs7; 128 struct pkcs7_signed_info *sinfo; 129 130 pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len); 131 if (IS_ERR(pkcs7)) { 132 code_sign_log_error("parse pkcs7 message failed"); 133 *ret = PTR_ERR(pkcs7); 134 return; 135 } 136 137 if (!pkcs7->signed_infos) { 138 code_sign_log_error("signed info not found in pkcs7"); 139 goto untrusted; 140 } 141 142 // no cert chain, verify by certificates in keyring 143 if (!pkcs7->certs) { 144 code_sign_log_warn("no certs in pkcs7, might be found in trust keyring"); 145 *ret = MAY_LOCAL_CODE; 146 goto exit; 147 } 148 149 bool is_dev_mode = false; 150 151#ifdef CONFIG_SECURITY_XPM 152 // developer mode && developer proc 153 if (get_developer_mode_state() == STATE_ON) { 154 code_sign_log_info("developer mode on"); 155 is_dev_mode = true; 156 } 157#endif 158 159 for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { 160 /* Find the key for the signature if there is one */ 161 *ret = pkcs7_find_key(pkcs7, sinfo); 162 if (*ret) { 163 code_sign_log_error("key not find in pkcs7"); 164 goto exit; 165 } 166 167 const struct x509_certificate *signer = sinfo->signer; 168 if (!signer) { 169 code_sign_log_error("signer cert not found in pkcs7"); 170 *ret = -EINVAL; 171 goto exit; 172 } 173 174 struct cert_source *source = find_matched_source(signer, false); 175 if (!source) { 176 if (is_dev_mode) { 177 // find on dev trusted list 178 source = find_matched_source(signer, true); 179 if (!source) 180 goto untrusted; 181 } else { 182 goto untrusted; 183 } 184 } 185 186 // cal cert chain depth 187 int cert_chain_depth_without_root = 1; 188 char *issuer = signer->issuer; 189 struct x509_certificate* cert = pkcs7->certs; 190 while(cert) { 191 // if issuer cert is found 192 if (cert->subject && (strcmp(cert->subject, issuer) == 0)) { 193 // reach root CA, end search 194 if (strcmp(cert->subject, cert->issuer) == 0) { 195 break; 196 } 197 cert_chain_depth_without_root++; 198 // search again for current issuer's issuer 199 issuer = cert->issuer; 200 cert = pkcs7->certs; 201 } else { 202 // move to next certificate 203 cert = cert->next; 204 } 205 } 206 if (cert_chain_depth_without_root == (source->max_path_depth - 1)) { 207 code_sign_log_info("cert subject and issuer trusted"); 208 set_file_ownerid(cs_info, source->path_type, pkcs7->signed_infos); 209 *ret = source->path_type; 210 goto exit; 211 } else { 212 code_sign_log_error("depth mismatch: cert chain depth without root is %d, max_path_depth is %d", 213 cert_chain_depth_without_root, source->max_path_depth); 214 } 215 } 216 217untrusted: 218 code_sign_log_error("cert subject and issuer verify failed"); 219 *ret = -EKEYREJECTED; 220exit: 221 pkcs7_free_message(pkcs7); 222} 223