• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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