1 // SPDX-License-Identifier: GPL-2.0 1 2 /* 3 * SafeSetID Linux Security Module 4 * 5 * Author: Micah Morton <mortonm@chromium.org> 6 * 7 * Copyright (C) 2018 The Chromium OS Authors. 8 * 9 * This program is free software; you can redi 10 * it under the terms of the GNU General Publi 11 * published by the Free Software Foundation. 12 * 13 */ 14 15 #define pr_fmt(fmt) "SafeSetID: " fmt 16 17 #include <linux/security.h> 18 #include <linux/cred.h> 19 20 #include "lsm.h" 21 22 static DEFINE_MUTEX(uid_policy_update_lock); 23 static DEFINE_MUTEX(gid_policy_update_lock); 24 25 /* 26 * In the case the input buffer contains one o 27 * variables pointed to by @parent and @child 28 * function will return an error. 29 * Contents of @buf may be modified. 30 */ 31 static int parse_policy_line(struct file *file 32 struct setid_rule *rule) 33 { 34 char *child_str; 35 int ret; 36 u32 parsed_parent, parsed_child; 37 38 /* Format of |buf| string should be <U 39 child_str = strchr(buf, ':'); 40 if (child_str == NULL) 41 return -EINVAL; 42 *child_str = '\0'; 43 child_str++; 44 45 ret = kstrtou32(buf, 0, &parsed_parent 46 if (ret) 47 return ret; 48 49 ret = kstrtou32(child_str, 0, &parsed_ 50 if (ret) 51 return ret; 52 53 if (rule->type == UID){ 54 rule->src_id.uid = make_kuid(f 55 rule->dst_id.uid = make_kuid(f 56 if (!uid_valid(rule->src_id.ui 57 return -EINVAL; 58 } else if (rule->type == GID){ 59 rule->src_id.gid = make_kgid(f 60 rule->dst_id.gid = make_kgid(f 61 if (!gid_valid(rule->src_id.gi 62 return -EINVAL; 63 } else { 64 /* Error, rule->type is an inv 65 return -EINVAL; 66 } 67 return 0; 68 } 69 70 static void __release_ruleset(struct rcu_head 71 { 72 struct setid_ruleset *pol = 73 container_of(rcu, struct setid 74 int bucket; 75 struct setid_rule *rule; 76 struct hlist_node *tmp; 77 78 hash_for_each_safe(pol->rules, bucket, 79 kfree(rule); 80 kfree(pol->policy_str); 81 kfree(pol); 82 } 83 84 static void release_ruleset(struct setid_rules 85 call_rcu(&pol->rcu, __release_ruleset) 86 } 87 88 static void insert_rule(struct setid_ruleset * 89 { 90 if (pol->type == UID) 91 hash_add(pol->rules, &rule->ne 92 else if (pol->type == GID) 93 hash_add(pol->rules, &rule->ne 94 else /* Error, pol->type is neither UI 95 return; 96 } 97 98 static int verify_ruleset(struct setid_ruleset 99 { 100 int bucket; 101 struct setid_rule *rule, *nrule; 102 int res = 0; 103 104 hash_for_each(pol->rules, bucket, rule 105 if (_setid_policy_lookup(pol, 106 if (pol->type == UID) 107 pr_warn("insec 108 __kuid 109 __kuid 110 } else if (pol->type = 111 pr_warn("insec 112 __kgid 113 __kgid 114 } else { /* pol->type 115 res = -EINVAL; 116 return res; 117 } 118 res = -EINVAL; 119 120 /* fix it up */ 121 nrule = kmalloc(sizeof 122 if (!nrule) 123 return -ENOMEM 124 if (pol->type == UID){ 125 nrule->src_id. 126 nrule->dst_id. 127 nrule->type = 128 } else { /* pol->type 129 nrule->src_id. 130 nrule->dst_id. 131 nrule->type = 132 } 133 insert_rule(pol, nrule 134 } 135 } 136 return res; 137 } 138 139 static ssize_t handle_policy_update(struct fil 140 const char 141 { 142 struct setid_ruleset *pol; 143 char *buf, *p, *end; 144 int err; 145 146 pol = kmalloc(sizeof(struct setid_rule 147 if (!pol) 148 return -ENOMEM; 149 pol->policy_str = NULL; 150 pol->type = policy_type; 151 hash_init(pol->rules); 152 153 p = buf = memdup_user_nul(ubuf, len); 154 if (IS_ERR(buf)) { 155 err = PTR_ERR(buf); 156 goto out_free_pol; 157 } 158 pol->policy_str = kstrdup(buf, GFP_KER 159 if (pol->policy_str == NULL) { 160 err = -ENOMEM; 161 goto out_free_buf; 162 } 163 164 /* policy lines, including the last on 165 while (*p != '\0') { 166 struct setid_rule *rule; 167 168 end = strchr(p, '\n'); 169 if (end == NULL) { 170 err = -EINVAL; 171 goto out_free_buf; 172 } 173 *end = '\0'; 174 175 rule = kmalloc(sizeof(struct s 176 if (!rule) { 177 err = -ENOMEM; 178 goto out_free_buf; 179 } 180 181 rule->type = policy_type; 182 err = parse_policy_line(file, 183 if (err) 184 goto out_free_rule; 185 186 if (_setid_policy_lookup(pol, 187 pr_warn("bad policy: d 188 err = -EEXIST; 189 goto out_free_rule; 190 } 191 192 insert_rule(pol, rule); 193 p = end + 1; 194 continue; 195 196 out_free_rule: 197 kfree(rule); 198 goto out_free_buf; 199 } 200 201 err = verify_ruleset(pol); 202 /* bogus policy falls through after fi 203 if (err && err != -EINVAL) 204 goto out_free_buf; 205 206 /* 207 * Everything looks good, apply the po 208 * What we really want here is an xchg 209 * doesn't currently exist, just use a 210 */ 211 if (policy_type == UID) { 212 mutex_lock(&uid_policy_update_ 213 pol = rcu_replace_pointer(safe 214 lock 215 mutex_unlock(&uid_policy_updat 216 } else if (policy_type == GID) { 217 mutex_lock(&gid_policy_update_ 218 pol = rcu_replace_pointer(safe 219 lock 220 mutex_unlock(&gid_policy_updat 221 } else { 222 /* Error, policy type is neith 223 pr_warn("error: bad policy typ 224 } 225 err = len; 226 227 out_free_buf: 228 kfree(buf); 229 out_free_pol: 230 if (pol) 231 release_ruleset(pol); 232 return err; 233 } 234 235 static ssize_t safesetid_uid_file_write(struct 236 const char 237 size_t len 238 loff_t *pp 239 { 240 if (!file_ns_capable(file, &init_user_ 241 return -EPERM; 242 243 if (*ppos != 0) 244 return -EINVAL; 245 246 return handle_policy_update(file, buf, 247 } 248 249 static ssize_t safesetid_gid_file_write(struct 250 const char 251 size_t len 252 loff_t *pp 253 { 254 if (!file_ns_capable(file, &init_user_ 255 return -EPERM; 256 257 if (*ppos != 0) 258 return -EINVAL; 259 260 return handle_policy_update(file, buf, 261 } 262 263 static ssize_t safesetid_file_read(struct file 264 size_t len, 265 { 266 ssize_t res = 0; 267 struct setid_ruleset *pol; 268 const char *kbuf; 269 270 mutex_lock(policy_update_lock); 271 pol = rcu_dereference_protected(rulese 272 if (pol) { 273 kbuf = pol->policy_str; 274 res = simple_read_from_buffer( 275 276 } 277 mutex_unlock(policy_update_lock); 278 279 return res; 280 } 281 282 static ssize_t safesetid_uid_file_read(struct 283 size_t len, 284 { 285 return safesetid_file_read(file, buf, 286 &uid_policy 287 } 288 289 static ssize_t safesetid_gid_file_read(struct 290 size_t len, 291 { 292 return safesetid_file_read(file, buf, 293 &gid_policy 294 } 295 296 297 298 static const struct file_operations safesetid_ 299 .read = safesetid_uid_file_read, 300 .write = safesetid_uid_file_write, 301 }; 302 303 static const struct file_operations safesetid_ 304 .read = safesetid_gid_file_read, 305 .write = safesetid_gid_file_write, 306 }; 307 308 static int __init safesetid_init_securityfs(vo 309 { 310 int ret; 311 struct dentry *policy_dir; 312 struct dentry *uid_policy_file; 313 struct dentry *gid_policy_file; 314 315 if (!safesetid_initialized) 316 return 0; 317 318 policy_dir = securityfs_create_dir("sa 319 if (IS_ERR(policy_dir)) { 320 ret = PTR_ERR(policy_dir); 321 goto error; 322 } 323 324 uid_policy_file = securityfs_create_fi 325 policy_dir, NULL, &saf 326 if (IS_ERR(uid_policy_file)) { 327 ret = PTR_ERR(uid_policy_file) 328 goto error; 329 } 330 331 gid_policy_file = securityfs_create_fi 332 policy_dir, NULL, &saf 333 if (IS_ERR(gid_policy_file)) { 334 ret = PTR_ERR(gid_policy_file) 335 goto error; 336 } 337 338 339 return 0; 340 341 error: 342 securityfs_remove(policy_dir); 343 return ret; 344 } 345 fs_initcall(safesetid_init_securityfs); 346
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.