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