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