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