1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
4 */
5
6#include <linux/fs.h>
7#include <linux/slab.h>
8#include <linux/spinlock.h>
9#include <linux/types.h>
10#include <linux/compat.h>
11#include <linux/version.h>
12#include "avc.h"
13#include "objsec.h"
14#include "code_sign_ioctl.h"
15#include "code_sign_log.h"
16#define  MAX_SIGNING_LENGTH 2048
17
18DEFINE_SPINLOCK(cert_chain_tree_lock);
19struct rb_root cert_chain_tree = RB_ROOT;
20struct rb_root dev_cert_chain_tree = RB_ROOT;
21
22struct cert_source *matched_cert_search(struct rb_root *root, const char *subject, const char *issuer)
23{
24	struct rb_node **cur_node = &(root->rb_node);
25
26	while (*cur_node) {
27		struct cert_source *cur_cert = container_of(*cur_node, struct cert_source, node);
28		int result = strcmp(subject, cur_cert->subject);
29
30		if (result < 0) {
31			cur_node = &((*cur_node)->rb_left);
32		} else if (result > 0) {
33			cur_node = &((*cur_node)->rb_right);
34		} else {
35			result = strcmp(issuer, cur_cert->issuer);
36			if (result < 0) {
37				cur_node = &((*cur_node)->rb_left);
38			} else if (result > 0) {
39				cur_node = &((*cur_node)->rb_right);
40			} else {
41				code_sign_log_info("cert found");
42				return cur_cert;
43			}
44		}
45	}
46	code_sign_log_error("cert not found");
47	return NULL;
48}
49
50struct cert_source *cert_chain_search(struct rb_root *root, const char *subject, const char *issuer, bool has_locked)
51{
52	if (has_locked)
53		return matched_cert_search(root, subject, issuer);
54	else {
55		spin_lock(&cert_chain_tree_lock);
56		struct cert_source *matched_cert = matched_cert_search(root, subject, issuer);
57		spin_unlock(&cert_chain_tree_lock);
58		return matched_cert;
59	}
60}
61
62struct cert_source *find_match(const char *subject, const char *issuer, bool is_dev)
63{
64	if (is_dev)
65		return cert_chain_search(&dev_cert_chain_tree, subject, issuer, false);
66	else
67		return cert_chain_search(&cert_chain_tree, subject, issuer, false);
68}
69
70int code_sign_check_caller(char *caller)
71{
72	u32 sid = current_sid(), context_len;
73	char *context = NULL;
74	int rc;
75#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
76	rc = security_sid_to_context(&selinux_state, sid, &context, &context_len);
77#else
78	rc = security_sid_to_context(sid, &context, &context_len);
79#endif
80	if (rc)
81		return -EINVAL;
82
83	code_sign_log_debug("sid=%d, context=%s", sid, context);
84	if (!strncmp(caller, context, strlen(caller)))
85		return 0;
86
87	return -EPERM;
88}
89
90int cert_chain_insert(struct rb_root *root, struct cert_source *cert)
91{
92	int ret = code_sign_check_caller(KEY_ENABLE_CTX);
93	if (ret == -EINVAL) {
94		code_sign_log_error("load SELinux context failed");
95		return -EINVAL;
96	} else if (ret == -EPERM) {
97		// procs except key_enable are only allowed to insert developer_code
98		if (!(cert->path_type == RELEASE_DEVELOPER_CODE
99			|| cert->path_type == DEBUG_DEVELOPER_CODE)) {
100			code_sign_log_error("no permission to insert code %d", cert->path_type);
101			return -EPERM;
102		}
103	}
104
105	spin_lock(&cert_chain_tree_lock);
106	struct rb_node **new = &(root->rb_node), *parent = NULL;
107
108	while (*new) {
109		struct cert_source *this = container_of(*new, struct cert_source, node);
110		int result = strcmp(cert->subject, this->subject);
111
112		parent = *new;
113		if (result < 0) {
114			new = &((*new)->rb_left);
115		} else if (result > 0) {
116			new = &((*new)->rb_right);
117		} else {
118			result = strcmp(cert->issuer, this->issuer);
119			if (result < 0) {
120				new = &((*new)->rb_left);
121			} else if (result > 0) {
122				new = &((*new)->rb_right);
123			} else {
124				this->cnt++;
125				code_sign_log_info("cert already exist in trust sources");
126				goto out;
127			}
128		}
129	}
130
131	// add new node
132	cert->cnt++;
133	rb_link_node(&cert->node, parent, new);
134	rb_insert_color(&cert->node, root);
135
136	code_sign_log_info("add trusted cert: subject = '%s', issuer = '%s', max_path_depth = %d",
137		cert->subject, cert->issuer, cert->max_path_depth);
138out:
139	spin_unlock(&cert_chain_tree_lock);
140	return 0;
141}
142
143int cert_chain_remove(struct rb_root *root, struct cert_source *cert)
144{
145	spin_lock(&cert_chain_tree_lock);
146	struct cert_source *matched_cert = cert_chain_search(root, cert->subject, cert->issuer, true);
147
148	int ret = 0;
149	if (!matched_cert) {
150		ret = -EINVAL;
151		goto out;
152	}
153
154	if (matched_cert->path_type == RELEASE_DEVELOPER_CODE
155		|| matched_cert->path_type == DEBUG_DEVELOPER_CODE) {
156		--matched_cert->cnt;
157		if (matched_cert->cnt > 0)
158			goto out;
159		rb_erase(&matched_cert->node, root);
160		code_sign_log_info("remove trusted cert: subject = '%s', issuer = '%s', max_path_depth = %d",
161			cert->subject, cert->issuer, cert->max_path_depth);
162		goto out;
163	}
164
165	code_sign_log_error("can not remove cert type %x", cert->path_type);
166	ret = -EKEYREJECTED;
167out:
168	spin_unlock(&cert_chain_tree_lock);
169	return ret;
170}
171
172int code_sign_open(struct inode *inode, struct file *filp)
173{
174	return 0;
175}
176
177int code_sign_release(struct inode *inode, struct file *filp)
178{
179	return 0;
180}
181
182int code_sign_avc_has_perm(u16 tclass, u32 requested)
183{
184	struct av_decision avd;
185	u32 sid = current_sid();
186	int rc, rc2;
187#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
188	rc = avc_has_perm_noaudit(&selinux_state, sid, sid, tclass, requested,
189		AVC_STRICT, &avd);
190	rc2 = avc_audit(&selinux_state, sid, sid, tclass, requested, &avd, rc,
191		NULL, AVC_STRICT);
192#else
193	rc = avc_has_perm_noaudit(sid, sid, tclass, requested,
194		AVC_STRICT, &avd);
195	rc2 = avc_audit(sid, sid, tclass, requested, &avd, rc,
196		NULL);
197#endif
198	if (rc2)
199		return rc2;
200
201	return rc;
202}
203
204int parse_cert_source(unsigned long args, struct cert_source **_source)
205{
206	int ret = 0;
207	struct cert_source *source = kzalloc(sizeof(struct cert_source), GFP_KERNEL);
208
209	if (!source)
210		return -ENOMEM;
211
212	struct cert_chain_info info;
213
214	if (copy_from_user(&info, args, sizeof(struct cert_chain_info))) {
215		code_sign_log_error("cmd copy_from_user failed");
216		ret = -ENOMEM;
217		goto copy_source_failed;
218	}
219
220	if (info.path_len > CERT_CHAIN_PATH_LEN_MAX || info.issuer_length == 0 || info.signing_length == 0
221		|| info.issuer_length > MAX_SIGNING_LENGTH || info.signing_length > MAX_SIGNING_LENGTH) {
222		code_sign_log_error("invalid path len or subject or issuer");
223		ret = -EINVAL;
224		goto copy_source_failed;
225	}
226
227	source->subject = kzalloc(info.signing_length + 1, GFP_KERNEL);
228	if (!source->subject) {
229		ret = -ENOMEM;
230		goto copy_source_failed;
231	}
232
233	if (copy_from_user(source->subject, u64_to_user_ptr(info.signing_ptr), info.signing_length)) {
234		code_sign_log_error("copy_from_user get signing failed");
235		ret = -EFAULT;
236		goto copy_subject_failed;
237	}
238
239	source->issuer = kzalloc(info.issuer_length + 1, GFP_KERNEL);
240	if (!source->issuer) {
241		ret = -ENOMEM;
242		goto copy_subject_failed;
243	}
244
245	ret = copy_from_user(source->issuer, u64_to_user_ptr(info.issuer_ptr), info.issuer_length);
246	if (ret) {
247		code_sign_log_error("copy_from_user get issuer failed");
248		ret = -EFAULT;
249		goto copy_issuer_failed;
250	}
251
252	source->max_path_depth = info.path_len;
253	source->path_type = info.cert_type;
254
255	*_source = source;
256	return ret;
257
258copy_issuer_failed:
259	kfree(source->issuer);
260copy_subject_failed:
261	kfree(source->subject);
262copy_source_failed:
263	kfree(source);
264	return ret;
265}
266
267int code_sign_check_code(int code)
268{
269	if (code > RELEASE_CODE_START && code < RELEASE_CODE_END)
270		return 0;
271
272	if (code > DEBUG_CODE_START && code < DEBUG_CODE_END)
273		return 1;
274
275	code_sign_log_error("cert type %x is invalid", code);
276	return -EINVAL;
277}
278
279long code_sign_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
280{
281	int ret = 0;
282	struct cert_source *source;
283
284	switch (cmd) {
285		case ADD_CERT_CHAIN:
286			if (code_sign_avc_has_perm(SECCLASS_CODE_SIGN, CODE_SIGN__ADD_CERT_CHAIN)) {
287				code_sign_log_error("selinux check failed, no permission to add cert chain");
288				return -EPERM;
289			}
290
291			ret = parse_cert_source(args, &source);
292			if (ret)
293				return ret;
294
295			// insert rb_tree
296			ret = code_sign_check_code(source->path_type);
297			if (ret < 0)
298				return ret;
299
300			if (ret == 1) {
301				// developer cert
302				code_sign_log_debug("add developer cert");
303				ret = cert_chain_insert(&dev_cert_chain_tree, source);
304			} else {
305				code_sign_log_debug("add release cert");
306				ret = cert_chain_insert(&cert_chain_tree, source);
307			}
308			break;
309		case REMOVE_CERT_CHAIN:
310			if (code_sign_avc_has_perm(SECCLASS_CODE_SIGN, CODE_SIGN__REMOVE_CERT_CHAIN)) {
311				code_sign_log_error("selinux check failed, no permission to remove cert chain");
312				return -EPERM;
313			}
314
315			ret = parse_cert_source(args, &source);
316			if (ret)
317				return ret;
318
319			// delete rb_tree
320			ret = code_sign_check_code(source->path_type);
321			if (ret < 0)
322				return ret;
323
324			if (ret == 1) {
325				// developer cert
326				code_sign_log_debug("remove developer cert");
327				ret = cert_chain_remove(&dev_cert_chain_tree, source);
328			} else {
329				code_sign_log_debug("remove release cert");
330				ret = cert_chain_remove(&cert_chain_tree, source);
331			}
332			if (ret) {
333				code_sign_log_error("remove cert failed.");
334			}
335			break;
336		default:
337			code_sign_log_error("code_sign cmd error, cmd: %d", cmd);
338			ret = -EINVAL;
339			break;
340	}
341
342	return ret;
343}
344