• 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/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, 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, 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 	int is_dev_mode = 0;
259 
260 	if (code > RELEASE_CODE_START && code < RELEASE_CODE_END)
261 		return is_dev_mode;
262 
263 	// developer mode
264 	if (get_developer_mode_state() == STATE_ON) {
265 		code_sign_log_debug("developer mode on");
266 		is_dev_mode = 1;
267 	}
268 
269 	if (is_dev_mode && (code > DEBUG_CODE_START && code < DEBUG_CODE_END))
270 		return is_dev_mode;
271 
272 	code_sign_log_error("cert type %x is invalid", code);
273 	return -EINVAL;
274 }
275 
code_sign_ioctl(struct file * filp,unsigned int cmd,unsigned long args)276 long code_sign_ioctl(struct file *filp, unsigned int cmd, unsigned long args)
277 {
278 	int ret = 0;
279 	struct cert_source *source;
280 
281 	switch (cmd) {
282 		case ADD_CERT_CHAIN:
283 			if (code_sign_avc_has_perm(SECCLASS_CODE_SIGN, CODE_SIGN__ADD_CERT_CHAIN)) {
284 				code_sign_log_error("selinux check failed, no permission to add cert chain");
285 				return -EPERM;
286 			}
287 
288 			ret = parse_cert_source(args, &source);
289 			if (ret)
290 				return ret;
291 
292 			// insert rb_tree
293 			ret = code_sign_check_code(source->path_type);
294 			if (ret < 0)
295 				return ret;
296 
297 			if (ret) {
298 				// developer cert
299 				code_sign_log_debug("add developer cert");
300 				ret = cert_chain_insert(&dev_cert_chain_tree, source);
301 			} else {
302 				code_sign_log_debug("add release cert");
303 				ret = cert_chain_insert(&cert_chain_tree, source);
304 			}
305 			break;
306 		case REMOVE_CERT_CHAIN:
307 			if (code_sign_avc_has_perm(SECCLASS_CODE_SIGN, CODE_SIGN__REMOVE_CERT_CHAIN)) {
308 				code_sign_log_error("selinux check failed, no permission to remove cert chain");
309 				return -EPERM;
310 			}
311 
312 			ret = parse_cert_source(args, &source);
313 			if (ret)
314 				return ret;
315 
316 			// delete rb_tree
317 			ret = code_sign_check_code(source->path_type);
318 			if (ret < 0)
319 				return ret;
320 
321 			if (ret) {
322 				// developer cert
323 				code_sign_log_debug("remove developer cert");
324 				ret = cert_chain_remove(&dev_cert_chain_tree, source);
325 			} else {
326 				code_sign_log_debug("remove release cert");
327 				ret = cert_chain_remove(&cert_chain_tree, source);
328 			}
329 			if (ret) {
330 				code_sign_log_error("remove cert failed.");
331 			}
332 			break;
333 		default:
334 			code_sign_log_error("code_sign cmd error, cmd: %d", cmd);
335 			ret = -EINVAL;
336 			break;
337 	}
338 
339 	return ret;
340 }
341