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