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