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