1419b0af8Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2419b0af8Sopenharmony_ci/*
3419b0af8Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
4419b0af8Sopenharmony_ci */
5419b0af8Sopenharmony_ci
6419b0af8Sopenharmony_ci#include <linux/cred.h>
7419b0af8Sopenharmony_ci#include <linux/key.h>
8419b0af8Sopenharmony_ci#include <linux/slab.h>
9419b0af8Sopenharmony_ci#include <linux/version.h>
10419b0af8Sopenharmony_ci#include <linux/verification.h>
11419b0af8Sopenharmony_ci#include <crypto/pkcs7.h>
12419b0af8Sopenharmony_ci#include "objsec.h"
13419b0af8Sopenharmony_ci#include "code_sign_ext.h"
14419b0af8Sopenharmony_ci#include "code_sign_ioctl.h"
15419b0af8Sopenharmony_ci#include "code_sign_log.h"
16419b0af8Sopenharmony_ci#include "verify_cert_chain.h"
17419b0af8Sopenharmony_ci
18419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_XPM
19419b0af8Sopenharmony_ci#include "dsmm_developer.h"
20419b0af8Sopenharmony_ci#endif
21419b0af8Sopenharmony_ci
22419b0af8Sopenharmony_ci/*
23419b0af8Sopenharmony_ci * Find the key (X.509 certificate) to use to verify a PKCS#7 message.  PKCS#7
24419b0af8Sopenharmony_ci * uses the issuer's name and the issuing certificate serial number for
25419b0af8Sopenharmony_ci * matching purposes.  These must match the certificate issuer's name (not
26419b0af8Sopenharmony_ci * subject's name) and the certificate serial number [RFC 2315 6.7].
27419b0af8Sopenharmony_ci */
28419b0af8Sopenharmony_cistatic int pkcs7_find_key(struct pkcs7_message *pkcs7,
29419b0af8Sopenharmony_ci			  struct pkcs7_signed_info *sinfo)
30419b0af8Sopenharmony_ci{
31419b0af8Sopenharmony_ci	struct x509_certificate *cert;
32419b0af8Sopenharmony_ci	unsigned certix = 1;
33419b0af8Sopenharmony_ci
34419b0af8Sopenharmony_ci	kenter("%u", sinfo->index);
35419b0af8Sopenharmony_ci	code_sign_log_info("sinfo->index %u", sinfo->index);
36419b0af8Sopenharmony_ci
37419b0af8Sopenharmony_ci	cert = pkcs7->certs;
38419b0af8Sopenharmony_ci	while (cert) {
39419b0af8Sopenharmony_ci		if (asymmetric_key_id_same(cert->id, sinfo->sig->auth_ids[0])) {
40419b0af8Sopenharmony_ci			if (strcmp(cert->pub->pkey_algo, sinfo->sig->pkey_algo) != 0
41419b0af8Sopenharmony_ci					&& (strncmp(cert->pub->pkey_algo, "ecdsa-", 6) != 0
42419b0af8Sopenharmony_ci					|| strcmp(cert->sig->pkey_algo, "ecdsa") != 0)) {
43419b0af8Sopenharmony_ci				code_sign_log_warn("sig %u: X.509 algo and PKCS#7 sig algo don't match", sinfo->index);
44419b0af8Sopenharmony_ci				cert = cert->next;
45419b0af8Sopenharmony_ci				certix++;
46419b0af8Sopenharmony_ci				continue;
47419b0af8Sopenharmony_ci			}
48419b0af8Sopenharmony_ci		} else {
49419b0af8Sopenharmony_ci			code_sign_log_warn("sig %u: X.509->id and PKCS#7 sinfo->sig->auth_ids[0] don't match",
50419b0af8Sopenharmony_ci				sinfo->index, cert->id, sinfo->sig->auth_ids[0]);
51419b0af8Sopenharmony_ci			cert = cert->next;
52419b0af8Sopenharmony_ci			certix++;
53419b0af8Sopenharmony_ci			continue;
54419b0af8Sopenharmony_ci		}
55419b0af8Sopenharmony_ci
56419b0af8Sopenharmony_ci		// cert is found
57419b0af8Sopenharmony_ci		sinfo->signer = cert;
58419b0af8Sopenharmony_ci		return 0;
59419b0af8Sopenharmony_ci	}
60419b0af8Sopenharmony_ci
61419b0af8Sopenharmony_ci	/* The relevant X.509 cert isn't found here, but it might be found in
62419b0af8Sopenharmony_ci	 * the trust keyring.
63419b0af8Sopenharmony_ci	 */
64419b0af8Sopenharmony_ci	code_sign_log_info("Sig %u: Issuing X.509 cert not found (#%*phN)",
65419b0af8Sopenharmony_ci		 sinfo->index,
66419b0af8Sopenharmony_ci		 sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data);
67419b0af8Sopenharmony_ci	return 0;
68419b0af8Sopenharmony_ci}
69419b0af8Sopenharmony_ci
70419b0af8Sopenharmony_cistatic void set_file_ownerid(struct cs_info *cs_info, int path_type,
71419b0af8Sopenharmony_ci	struct pkcs7_signed_info *sinfo)
72419b0af8Sopenharmony_ci{
73419b0af8Sopenharmony_ci	if (cs_info == NULL)
74419b0af8Sopenharmony_ci		return;
75419b0af8Sopenharmony_ci
76419b0af8Sopenharmony_ci	/* Mark a debug file as OWNERID_DEBUG */
77419b0af8Sopenharmony_ci	if((path_type > DEBUG_CODE_START) && (path_type < DEBUG_CODE_END)) {
78419b0af8Sopenharmony_ci		code_sign_set_ownerid(cs_info, FILE_OWNERID_DEBUG, NULL, 0);
79419b0af8Sopenharmony_ci		return;
80419b0af8Sopenharmony_ci	}
81419b0af8Sopenharmony_ci
82419b0af8Sopenharmony_ci	/* Mark the file as OWNERID_COMPAT, if its ownerid is empty */
83419b0af8Sopenharmony_ci	if(!sinfo->ownerid) {
84419b0af8Sopenharmony_ci		code_sign_set_ownerid(cs_info, FILE_OWNERID_COMPAT, NULL, 0);
85419b0af8Sopenharmony_ci		return;
86419b0af8Sopenharmony_ci	}
87419b0af8Sopenharmony_ci
88419b0af8Sopenharmony_ci	/* Mark the file as OWNERID_SHARED, if the file is shareable */
89419b0af8Sopenharmony_ci	if((sinfo->ownerid_len == strlen(OWNERID_SHARED_TAG)) &&
90419b0af8Sopenharmony_ci		!memcmp(sinfo->ownerid, OWNERID_SHARED_TAG,
91419b0af8Sopenharmony_ci			sinfo->ownerid_len)) {
92419b0af8Sopenharmony_ci		code_sign_set_ownerid(cs_info, FILE_OWNERID_SHARED, NULL, 0);
93419b0af8Sopenharmony_ci		return;
94419b0af8Sopenharmony_ci	}
95419b0af8Sopenharmony_ci
96419b0af8Sopenharmony_ci	/* If this code is signed on the device, check whether it is DEBUG_ID */
97419b0af8Sopenharmony_ci	if((path_type = MAY_LOCAL_CODE) &&
98419b0af8Sopenharmony_ci		(sinfo->ownerid_len == strlen(OWNERID_DEBUG_TAG)) &&
99419b0af8Sopenharmony_ci		!memcmp(sinfo->ownerid, OWNERID_DEBUG_TAG,
100419b0af8Sopenharmony_ci			sinfo->ownerid_len)) {
101419b0af8Sopenharmony_ci		code_sign_set_ownerid(cs_info, FILE_OWNERID_DEBUG, NULL, 0);
102419b0af8Sopenharmony_ci		return;
103419b0af8Sopenharmony_ci	}
104419b0af8Sopenharmony_ci
105419b0af8Sopenharmony_ci	/* Mark the file OWNERID_APP in other cases */
106419b0af8Sopenharmony_ci	code_sign_set_ownerid(cs_info, FILE_OWNERID_APP,
107419b0af8Sopenharmony_ci		sinfo->ownerid, sinfo->ownerid_len);
108419b0af8Sopenharmony_ci}
109419b0af8Sopenharmony_ci
110419b0af8Sopenharmony_cistatic struct cert_source *find_matched_source(const struct x509_certificate *signer, bool is_debug)
111419b0af8Sopenharmony_ci{
112419b0af8Sopenharmony_ci	int block_type = is_debug ? DEBUG_BLOCK_CODE: RELEASE_BLOCK_CODE;
113419b0af8Sopenharmony_ci	struct cert_source *source = find_match(signer->subject, signer->issuer, is_debug);
114419b0af8Sopenharmony_ci
115419b0af8Sopenharmony_ci	if (source == NULL) {
116419b0af8Sopenharmony_ci		source = find_match("ALL", signer->issuer, is_debug);
117419b0af8Sopenharmony_ci	} else if (source->path_type == block_type) {
118419b0af8Sopenharmony_ci		code_sign_log_error("signer certificate's type not trusted");
119419b0af8Sopenharmony_ci		return NULL;
120419b0af8Sopenharmony_ci	}
121419b0af8Sopenharmony_ci	return source;
122419b0af8Sopenharmony_ci}
123419b0af8Sopenharmony_ci
124419b0af8Sopenharmony_civoid code_sign_verify_certchain(const void *raw_pkcs7, size_t pkcs7_len,
125419b0af8Sopenharmony_ci	struct cs_info *cs_info, int *ret)
126419b0af8Sopenharmony_ci{
127419b0af8Sopenharmony_ci	struct pkcs7_message *pkcs7;
128419b0af8Sopenharmony_ci	struct pkcs7_signed_info *sinfo;
129419b0af8Sopenharmony_ci
130419b0af8Sopenharmony_ci	pkcs7 = pkcs7_parse_message(raw_pkcs7, pkcs7_len);
131419b0af8Sopenharmony_ci	if (IS_ERR(pkcs7)) {
132419b0af8Sopenharmony_ci		code_sign_log_error("parse pkcs7 message failed");
133419b0af8Sopenharmony_ci		*ret = PTR_ERR(pkcs7);
134419b0af8Sopenharmony_ci		return;
135419b0af8Sopenharmony_ci	}
136419b0af8Sopenharmony_ci
137419b0af8Sopenharmony_ci	if (!pkcs7->signed_infos) {
138419b0af8Sopenharmony_ci		code_sign_log_error("signed info not found in pkcs7");
139419b0af8Sopenharmony_ci		goto untrusted;
140419b0af8Sopenharmony_ci	}
141419b0af8Sopenharmony_ci
142419b0af8Sopenharmony_ci	// no cert chain, verify by certificates in keyring
143419b0af8Sopenharmony_ci	if (!pkcs7->certs) {
144419b0af8Sopenharmony_ci		code_sign_log_warn("no certs in pkcs7, might be found in trust keyring");
145419b0af8Sopenharmony_ci		*ret = MAY_LOCAL_CODE;
146419b0af8Sopenharmony_ci		goto exit;
147419b0af8Sopenharmony_ci	}
148419b0af8Sopenharmony_ci
149419b0af8Sopenharmony_ci	bool is_dev_mode = false;
150419b0af8Sopenharmony_ci
151419b0af8Sopenharmony_ci#ifdef CONFIG_SECURITY_XPM
152419b0af8Sopenharmony_ci	// developer mode && developer proc
153419b0af8Sopenharmony_ci	if (get_developer_mode_state() == STATE_ON) {
154419b0af8Sopenharmony_ci		code_sign_log_info("developer mode on");
155419b0af8Sopenharmony_ci		is_dev_mode = true;
156419b0af8Sopenharmony_ci	}
157419b0af8Sopenharmony_ci#endif
158419b0af8Sopenharmony_ci
159419b0af8Sopenharmony_ci	for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
160419b0af8Sopenharmony_ci		/* Find the key for the signature if there is one */
161419b0af8Sopenharmony_ci		*ret = pkcs7_find_key(pkcs7, sinfo);
162419b0af8Sopenharmony_ci		if (*ret) {
163419b0af8Sopenharmony_ci			code_sign_log_error("key not find in pkcs7");
164419b0af8Sopenharmony_ci			goto exit;
165419b0af8Sopenharmony_ci		}
166419b0af8Sopenharmony_ci
167419b0af8Sopenharmony_ci		const struct x509_certificate *signer = sinfo->signer;
168419b0af8Sopenharmony_ci		if (!signer) {
169419b0af8Sopenharmony_ci			code_sign_log_error("signer cert not found in pkcs7");
170419b0af8Sopenharmony_ci			*ret = -EINVAL;
171419b0af8Sopenharmony_ci			goto exit;
172419b0af8Sopenharmony_ci		}
173419b0af8Sopenharmony_ci
174419b0af8Sopenharmony_ci		struct cert_source *source = find_matched_source(signer, false);
175419b0af8Sopenharmony_ci		if (!source) {
176419b0af8Sopenharmony_ci			if (is_dev_mode) {
177419b0af8Sopenharmony_ci				// find on dev trusted list
178419b0af8Sopenharmony_ci				source = find_matched_source(signer, true);
179419b0af8Sopenharmony_ci				if (!source)
180419b0af8Sopenharmony_ci					goto untrusted;
181419b0af8Sopenharmony_ci			} else {
182419b0af8Sopenharmony_ci				goto untrusted;
183419b0af8Sopenharmony_ci			}
184419b0af8Sopenharmony_ci		}
185419b0af8Sopenharmony_ci
186419b0af8Sopenharmony_ci		// cal cert chain depth
187419b0af8Sopenharmony_ci		int cert_chain_depth_without_root = 1;
188419b0af8Sopenharmony_ci		char *issuer = signer->issuer;
189419b0af8Sopenharmony_ci		struct x509_certificate* cert = pkcs7->certs;
190419b0af8Sopenharmony_ci		while(cert) {
191419b0af8Sopenharmony_ci			// if issuer cert is found
192419b0af8Sopenharmony_ci			if (cert->subject && (strcmp(cert->subject, issuer) == 0)) {
193419b0af8Sopenharmony_ci				// reach root CA, end search
194419b0af8Sopenharmony_ci				if (strcmp(cert->subject, cert->issuer) == 0) {
195419b0af8Sopenharmony_ci					break;
196419b0af8Sopenharmony_ci				}
197419b0af8Sopenharmony_ci				cert_chain_depth_without_root++;
198419b0af8Sopenharmony_ci				// search again for current issuer's issuer
199419b0af8Sopenharmony_ci				issuer = cert->issuer;
200419b0af8Sopenharmony_ci				cert = pkcs7->certs;
201419b0af8Sopenharmony_ci			} else {
202419b0af8Sopenharmony_ci				// move to next certificate
203419b0af8Sopenharmony_ci				cert = cert->next;
204419b0af8Sopenharmony_ci			}
205419b0af8Sopenharmony_ci		}
206419b0af8Sopenharmony_ci		if (cert_chain_depth_without_root == (source->max_path_depth - 1)) {
207419b0af8Sopenharmony_ci			code_sign_log_info("cert subject and issuer trusted");
208419b0af8Sopenharmony_ci			set_file_ownerid(cs_info, source->path_type, pkcs7->signed_infos);
209419b0af8Sopenharmony_ci			*ret = source->path_type;
210419b0af8Sopenharmony_ci			goto exit;
211419b0af8Sopenharmony_ci		} else {
212419b0af8Sopenharmony_ci			code_sign_log_error("depth mismatch: cert chain depth without root is %d, max_path_depth is %d",
213419b0af8Sopenharmony_ci				cert_chain_depth_without_root, source->max_path_depth);
214419b0af8Sopenharmony_ci		}
215419b0af8Sopenharmony_ci	}
216419b0af8Sopenharmony_ci
217419b0af8Sopenharmony_ciuntrusted:
218419b0af8Sopenharmony_ci	code_sign_log_error("cert subject and issuer verify failed");
219419b0af8Sopenharmony_ci	*ret = -EKEYREJECTED;
220419b0af8Sopenharmony_ciexit:
221419b0af8Sopenharmony_ci	pkcs7_free_message(pkcs7);
222419b0af8Sopenharmony_ci}
223