• 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 <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 
18 DEFINE_SPINLOCK(cert_chain_tree_lock);
19 struct rb_root cert_chain_tree = RB_ROOT;
20 struct rb_root dev_cert_chain_tree = RB_ROOT;
21 
matched_cert_search(struct rb_root * root,const char * subject,const char * issuer)22 struct 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 
cert_chain_search(struct rb_root * root,const char * subject,const char * issuer,bool has_locked)50 struct 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 
find_match(const char * subject,const char * issuer,bool is_dev)62 struct 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 
code_sign_check_caller(char * caller)70 int 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 
cert_chain_insert(struct rb_root * root,struct cert_source * cert)90 int 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);
138 out:
139 	spin_unlock(&cert_chain_tree_lock);
140 	return 0;
141 }
142 
cert_chain_remove(struct rb_root * root,struct cert_source * cert)143 int 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;
167 out:
168 	spin_unlock(&cert_chain_tree_lock);
169 	return ret;
170 }
171 
code_sign_open(struct inode * inode,struct file * filp)172 int code_sign_open(struct inode *inode, struct file *filp)
173 {
174 	return 0;
175 }
176 
code_sign_release(struct inode * inode,struct file * filp)177 int code_sign_release(struct inode *inode, struct file *filp)
178 {
179 	return 0;
180 }
181 
code_sign_avc_has_perm(u16 tclass,u32 requested)182 int 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 
parse_cert_source(unsigned long args,struct cert_source ** _source)204 int 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 
258 copy_issuer_failed:
259 	kfree(source->issuer);
260 copy_subject_failed:
261 	kfree(source->subject);
262 copy_source_failed:
263 	kfree(source);
264 	return ret;
265 }
266 
code_sign_check_code(int code)267 int 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 
code_sign_ioctl(struct file * filp,unsigned int cmd,unsigned long args)279 long 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