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 */
pkcs7_find_key(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo)28 static 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
set_file_ownerid(struct cs_info *cs_info, int path_type, struct pkcs7_signed_info *sinfo)70 static 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
find_matched_source(const struct x509_certificate *signer, bool is_debug)110 static 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
code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len, struct cs_info *cs_info, int *ret)124 void 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
217 untrusted:
218 code_sign_log_error("cert subject and issuer verify failed");
219 *ret = -EKEYREJECTED;
220 exit:
221 pkcs7_free_message(pkcs7);
222 }
223