Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

LSM: add SafeSetID module that gates setid calls

SafeSetID gates the setid family of syscalls to restrict UID/GID
transitions from a given UID/GID to only those approved by a
system-wide whitelist. These restrictions also prohibit the given
UIDs/GIDs from obtaining auxiliary privileges associated with
CAP_SET{U/G}ID, such as allowing a user to set up user namespace UID
mappings. For now, only gating the set*uid family of syscalls is
supported, with support for set*gid coming in a future patch set.

Signed-off-by: Micah Morton <mortonm@chromium.org>
Acked-by: Kees Cook <keescook@chromium.org>
Signed-off-by: James Morris <james.morris@microsoft.com>

authored by

Micah Morton and committed by
James Morris
aeca4e2c 40852275

+634 -1
+107
Documentation/admin-guide/LSM/SafeSetID.rst
··· 1 + ========= 2 + SafeSetID 3 + ========= 4 + SafeSetID is an LSM module that gates the setid family of syscalls to restrict 5 + UID/GID transitions from a given UID/GID to only those approved by a 6 + system-wide whitelist. These restrictions also prohibit the given UIDs/GIDs 7 + from obtaining auxiliary privileges associated with CAP_SET{U/G}ID, such as 8 + allowing a user to set up user namespace UID mappings. 9 + 10 + 11 + Background 12 + ========== 13 + In absence of file capabilities, processes spawned on a Linux system that need 14 + to switch to a different user must be spawned with CAP_SETUID privileges. 15 + CAP_SETUID is granted to programs running as root or those running as a non-root 16 + user that have been explicitly given the CAP_SETUID runtime capability. It is 17 + often preferable to use Linux runtime capabilities rather than file 18 + capabilities, since using file capabilities to run a program with elevated 19 + privileges opens up possible security holes since any user with access to the 20 + file can exec() that program to gain the elevated privileges. 21 + 22 + While it is possible to implement a tree of processes by giving full 23 + CAP_SET{U/G}ID capabilities, this is often at odds with the goals of running a 24 + tree of processes under non-root user(s) in the first place. Specifically, 25 + since CAP_SETUID allows changing to any user on the system, including the root 26 + user, it is an overpowered capability for what is needed in this scenario, 27 + especially since programs often only call setuid() to drop privileges to a 28 + lesser-privileged user -- not elevate privileges. Unfortunately, there is no 29 + generally feasible way in Linux to restrict the potential UIDs that a user can 30 + switch to through setuid() beyond allowing a switch to any user on the system. 31 + This SafeSetID LSM seeks to provide a solution for restricting setid 32 + capabilities in such a way. 33 + 34 + The main use case for this LSM is to allow a non-root program to transition to 35 + other untrusted uids without full blown CAP_SETUID capabilities. The non-root 36 + program would still need CAP_SETUID to do any kind of transition, but the 37 + additional restrictions imposed by this LSM would mean it is a "safer" version 38 + of CAP_SETUID since the non-root program cannot take advantage of CAP_SETUID to 39 + do any unapproved actions (e.g. setuid to uid 0 or create/enter new user 40 + namespace). The higher level goal is to allow for uid-based sandboxing of system 41 + services without having to give out CAP_SETUID all over the place just so that 42 + non-root programs can drop to even-lesser-privileged uids. This is especially 43 + relevant when one non-root daemon on the system should be allowed to spawn other 44 + processes as different uids, but its undesirable to give the daemon a 45 + basically-root-equivalent CAP_SETUID. 46 + 47 + 48 + Other Approaches Considered 49 + =========================== 50 + 51 + Solve this problem in userspace 52 + ------------------------------- 53 + For candidate applications that would like to have restricted setid capabilities 54 + as implemented in this LSM, an alternative option would be to simply take away 55 + setid capabilities from the application completely and refactor the process 56 + spawning semantics in the application (e.g. by using a privileged helper program 57 + to do process spawning and UID/GID transitions). Unfortunately, there are a 58 + number of semantics around process spawning that would be affected by this, such 59 + as fork() calls where the program doesn???t immediately call exec() after the 60 + fork(), parent processes specifying custom environment variables or command line 61 + args for spawned child processes, or inheritance of file handles across a 62 + fork()/exec(). Because of this, as solution that uses a privileged helper in 63 + userspace would likely be less appealing to incorporate into existing projects 64 + that rely on certain process-spawning semantics in Linux. 65 + 66 + Use user namespaces 67 + ------------------- 68 + Another possible approach would be to run a given process tree in its own user 69 + namespace and give programs in the tree setid capabilities. In this way, 70 + programs in the tree could change to any desired UID/GID in the context of their 71 + own user namespace, and only approved UIDs/GIDs could be mapped back to the 72 + initial system user namespace, affectively preventing privilege escalation. 73 + Unfortunately, it is not generally feasible to use user namespaces in isolation, 74 + without pairing them with other namespace types, which is not always an option. 75 + Linux checks for capabilities based off of the user namespace that ???owns??? some 76 + entity. For example, Linux has the notion that network namespaces are owned by 77 + the user namespace in which they were created. A consequence of this is that 78 + capability checks for access to a given network namespace are done by checking 79 + whether a task has the given capability in the context of the user namespace 80 + that owns the network namespace -- not necessarily the user namespace under 81 + which the given task runs. Therefore spawning a process in a new user namespace 82 + effectively prevents it from accessing the network namespace owned by the 83 + initial namespace. This is a deal-breaker for any application that expects to 84 + retain the CAP_NET_ADMIN capability for the purpose of adjusting network 85 + configurations. Using user namespaces in isolation causes problems regarding 86 + other system interactions, including use of pid namespaces and device creation. 87 + 88 + Use an existing LSM 89 + ------------------- 90 + None of the other in-tree LSMs have the capability to gate setid transitions, or 91 + even employ the security_task_fix_setuid hook at all. SELinux says of that hook: 92 + "Since setuid only affects the current process, and since the SELinux controls 93 + are not based on the Linux identity attributes, SELinux does not need to control 94 + this operation." 95 + 96 + 97 + Directions for use 98 + ================== 99 + This LSM hooks the setid syscalls to make sure transitions are allowed if an 100 + applicable restriction policy is in place. Policies are configured through 101 + securityfs by writing to the safesetid/add_whitelist_policy and 102 + safesetid/flush_whitelist_policies files at the location where securityfs is 103 + mounted. The format for adding a policy is '<UID>:<UID>', using literal 104 + numbers, such as '123:456'. To flush the policies, any write to the file is 105 + sufficient. Again, configuring a policy for a UID will prevent that UID from 106 + obtaining auxiliary setid privileges, such as allowing a user to set up user 107 + namespace UID mappings.
+1
Documentation/admin-guide/LSM/index.rst
··· 46 46 Smack 47 47 tomoyo 48 48 Yama 49 + SafeSetID
+2 -1
security/Kconfig
··· 236 236 source "security/apparmor/Kconfig" 237 237 source "security/loadpin/Kconfig" 238 238 source "security/yama/Kconfig" 239 + source "security/safesetid/Kconfig" 239 240 240 241 source "security/integrity/Kconfig" 241 242 242 243 config LSM 243 244 string "Ordered list of enabled LSMs" 244 - default "yama,loadpin,integrity,selinux,smack,tomoyo,apparmor" 245 + default "yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" 245 246 help 246 247 A comma-separated list of LSMs, in initialization order. 247 248 Any LSMs left off this list will be ignored. This can be
+2
security/Makefile
··· 10 10 subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor 11 11 subdir-$(CONFIG_SECURITY_YAMA) += yama 12 12 subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin 13 + subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid 13 14 14 15 # always enable default capabilities 15 16 obj-y += commoncap.o ··· 26 25 obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/ 27 26 obj-$(CONFIG_SECURITY_YAMA) += yama/ 28 27 obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/ 28 + obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/ 29 29 obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o 30 30 31 31 # Object integrity file lists
+12
security/safesetid/Kconfig
··· 1 + config SECURITY_SAFESETID 2 + bool "Gate setid transitions to limit CAP_SET{U/G}ID capabilities" 3 + default n 4 + help 5 + SafeSetID is an LSM module that gates the setid family of syscalls to 6 + restrict UID/GID transitions from a given UID/GID to only those 7 + approved by a system-wide whitelist. These restrictions also prohibit 8 + the given UIDs/GIDs from obtaining auxiliary privileges associated 9 + with CAP_SET{U/G}ID, such as allowing a user to set up user namespace 10 + UID mappings. 11 + 12 + If you are unsure how to answer this question, answer N.
+7
security/safesetid/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Makefile for the safesetid LSM. 4 + # 5 + 6 + obj-$(CONFIG_SECURITY_SAFESETID) := safesetid.o 7 + safesetid-y := lsm.o securityfs.o
+277
security/safesetid/lsm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 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 redistribute it and/or modify 10 + * it under the terms of the GNU General Public License version 2, as 11 + * published by the Free Software Foundation. 12 + * 13 + */ 14 + 15 + #define pr_fmt(fmt) "SafeSetID: " fmt 16 + 17 + #include <asm/syscall.h> 18 + #include <linux/hashtable.h> 19 + #include <linux/lsm_hooks.h> 20 + #include <linux/module.h> 21 + #include <linux/ptrace.h> 22 + #include <linux/sched/task_stack.h> 23 + #include <linux/security.h> 24 + 25 + /* Flag indicating whether initialization completed */ 26 + int safesetid_initialized; 27 + 28 + #define NUM_BITS 8 /* 128 buckets in hash table */ 29 + 30 + static DEFINE_HASHTABLE(safesetid_whitelist_hashtable, NUM_BITS); 31 + 32 + /* 33 + * Hash table entry to store safesetid policy signifying that 'parent' user 34 + * can setid to 'child' user. 35 + */ 36 + struct entry { 37 + struct hlist_node next; 38 + struct hlist_node dlist; /* for deletion cleanup */ 39 + uint64_t parent_kuid; 40 + uint64_t child_kuid; 41 + }; 42 + 43 + static DEFINE_SPINLOCK(safesetid_whitelist_hashtable_spinlock); 44 + 45 + static bool check_setuid_policy_hashtable_key(kuid_t parent) 46 + { 47 + struct entry *entry; 48 + 49 + rcu_read_lock(); 50 + hash_for_each_possible_rcu(safesetid_whitelist_hashtable, 51 + entry, next, __kuid_val(parent)) { 52 + if (entry->parent_kuid == __kuid_val(parent)) { 53 + rcu_read_unlock(); 54 + return true; 55 + } 56 + } 57 + rcu_read_unlock(); 58 + 59 + return false; 60 + } 61 + 62 + static bool check_setuid_policy_hashtable_key_value(kuid_t parent, 63 + kuid_t child) 64 + { 65 + struct entry *entry; 66 + 67 + rcu_read_lock(); 68 + hash_for_each_possible_rcu(safesetid_whitelist_hashtable, 69 + entry, next, __kuid_val(parent)) { 70 + if (entry->parent_kuid == __kuid_val(parent) && 71 + entry->child_kuid == __kuid_val(child)) { 72 + rcu_read_unlock(); 73 + return true; 74 + } 75 + } 76 + rcu_read_unlock(); 77 + 78 + return false; 79 + } 80 + 81 + static int safesetid_security_capable(const struct cred *cred, 82 + struct user_namespace *ns, 83 + int cap, 84 + unsigned int opts) 85 + { 86 + if (cap == CAP_SETUID && 87 + check_setuid_policy_hashtable_key(cred->uid)) { 88 + if (!(opts & CAP_OPT_INSETID)) { 89 + /* 90 + * Deny if we're not in a set*uid() syscall to avoid 91 + * giving powers gated by CAP_SETUID that are related 92 + * to functionality other than calling set*uid() (e.g. 93 + * allowing user to set up userns uid mappings). 94 + */ 95 + pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions", 96 + __kuid_val(cred->uid)); 97 + return -1; 98 + } 99 + } 100 + return 0; 101 + } 102 + 103 + static int check_uid_transition(kuid_t parent, kuid_t child) 104 + { 105 + if (check_setuid_policy_hashtable_key_value(parent, child)) 106 + return 0; 107 + pr_warn("UID transition (%d -> %d) blocked", 108 + __kuid_val(parent), 109 + __kuid_val(child)); 110 + /* 111 + * Kill this process to avoid potential security vulnerabilities 112 + * that could arise from a missing whitelist entry preventing a 113 + * privileged process from dropping to a lesser-privileged one. 114 + */ 115 + force_sig(SIGKILL, current); 116 + return -EACCES; 117 + } 118 + 119 + /* 120 + * Check whether there is either an exception for user under old cred struct to 121 + * set*uid to user under new cred struct, or the UID transition is allowed (by 122 + * Linux set*uid rules) even without CAP_SETUID. 123 + */ 124 + static int safesetid_task_fix_setuid(struct cred *new, 125 + const struct cred *old, 126 + int flags) 127 + { 128 + 129 + /* Do nothing if there are no setuid restrictions for this UID. */ 130 + if (!check_setuid_policy_hashtable_key(old->uid)) 131 + return 0; 132 + 133 + switch (flags) { 134 + case LSM_SETID_RE: 135 + /* 136 + * Users for which setuid restrictions exist can only set the 137 + * real UID to the real UID or the effective UID, unless an 138 + * explicit whitelist policy allows the transition. 139 + */ 140 + if (!uid_eq(old->uid, new->uid) && 141 + !uid_eq(old->euid, new->uid)) { 142 + return check_uid_transition(old->uid, new->uid); 143 + } 144 + /* 145 + * Users for which setuid restrictions exist can only set the 146 + * effective UID to the real UID, the effective UID, or the 147 + * saved set-UID, unless an explicit whitelist policy allows 148 + * the transition. 149 + */ 150 + if (!uid_eq(old->uid, new->euid) && 151 + !uid_eq(old->euid, new->euid) && 152 + !uid_eq(old->suid, new->euid)) { 153 + return check_uid_transition(old->euid, new->euid); 154 + } 155 + break; 156 + case LSM_SETID_ID: 157 + /* 158 + * Users for which setuid restrictions exist cannot change the 159 + * real UID or saved set-UID unless an explicit whitelist 160 + * policy allows the transition. 161 + */ 162 + if (!uid_eq(old->uid, new->uid)) 163 + return check_uid_transition(old->uid, new->uid); 164 + if (!uid_eq(old->suid, new->suid)) 165 + return check_uid_transition(old->suid, new->suid); 166 + break; 167 + case LSM_SETID_RES: 168 + /* 169 + * Users for which setuid restrictions exist cannot change the 170 + * real UID, effective UID, or saved set-UID to anything but 171 + * one of: the current real UID, the current effective UID or 172 + * the current saved set-user-ID unless an explicit whitelist 173 + * policy allows the transition. 174 + */ 175 + if (!uid_eq(new->uid, old->uid) && 176 + !uid_eq(new->uid, old->euid) && 177 + !uid_eq(new->uid, old->suid)) { 178 + return check_uid_transition(old->uid, new->uid); 179 + } 180 + if (!uid_eq(new->euid, old->uid) && 181 + !uid_eq(new->euid, old->euid) && 182 + !uid_eq(new->euid, old->suid)) { 183 + return check_uid_transition(old->euid, new->euid); 184 + } 185 + if (!uid_eq(new->suid, old->uid) && 186 + !uid_eq(new->suid, old->euid) && 187 + !uid_eq(new->suid, old->suid)) { 188 + return check_uid_transition(old->suid, new->suid); 189 + } 190 + break; 191 + case LSM_SETID_FS: 192 + /* 193 + * Users for which setuid restrictions exist cannot change the 194 + * filesystem UID to anything but one of: the current real UID, 195 + * the current effective UID or the current saved set-UID 196 + * unless an explicit whitelist policy allows the transition. 197 + */ 198 + if (!uid_eq(new->fsuid, old->uid) && 199 + !uid_eq(new->fsuid, old->euid) && 200 + !uid_eq(new->fsuid, old->suid) && 201 + !uid_eq(new->fsuid, old->fsuid)) { 202 + return check_uid_transition(old->fsuid, new->fsuid); 203 + } 204 + break; 205 + default: 206 + pr_warn("Unknown setid state %d\n", flags); 207 + force_sig(SIGKILL, current); 208 + return -EINVAL; 209 + } 210 + return 0; 211 + } 212 + 213 + int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child) 214 + { 215 + struct entry *new; 216 + 217 + /* Return if entry already exists */ 218 + if (check_setuid_policy_hashtable_key_value(parent, child)) 219 + return 0; 220 + 221 + new = kzalloc(sizeof(struct entry), GFP_KERNEL); 222 + if (!new) 223 + return -ENOMEM; 224 + new->parent_kuid = __kuid_val(parent); 225 + new->child_kuid = __kuid_val(child); 226 + spin_lock(&safesetid_whitelist_hashtable_spinlock); 227 + hash_add_rcu(safesetid_whitelist_hashtable, 228 + &new->next, 229 + __kuid_val(parent)); 230 + spin_unlock(&safesetid_whitelist_hashtable_spinlock); 231 + return 0; 232 + } 233 + 234 + void flush_safesetid_whitelist_entries(void) 235 + { 236 + struct entry *entry; 237 + struct hlist_node *hlist_node; 238 + unsigned int bkt_loop_cursor; 239 + HLIST_HEAD(free_list); 240 + 241 + /* 242 + * Could probably use hash_for_each_rcu here instead, but this should 243 + * be fine as well. 244 + */ 245 + spin_lock(&safesetid_whitelist_hashtable_spinlock); 246 + hash_for_each_safe(safesetid_whitelist_hashtable, bkt_loop_cursor, 247 + hlist_node, entry, next) { 248 + hash_del_rcu(&entry->next); 249 + hlist_add_head(&entry->dlist, &free_list); 250 + } 251 + spin_unlock(&safesetid_whitelist_hashtable_spinlock); 252 + synchronize_rcu(); 253 + hlist_for_each_entry_safe(entry, hlist_node, &free_list, dlist) { 254 + hlist_del(&entry->dlist); 255 + kfree(entry); 256 + } 257 + } 258 + 259 + static struct security_hook_list safesetid_security_hooks[] = { 260 + LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid), 261 + LSM_HOOK_INIT(capable, safesetid_security_capable) 262 + }; 263 + 264 + static int __init safesetid_security_init(void) 265 + { 266 + security_add_hooks(safesetid_security_hooks, 267 + ARRAY_SIZE(safesetid_security_hooks), "safesetid"); 268 + 269 + /* Report that SafeSetID successfully initialized */ 270 + safesetid_initialized = 1; 271 + 272 + return 0; 273 + } 274 + 275 + DEFINE_LSM(safesetid_security_init) = { 276 + .init = safesetid_security_init, 277 + };
+33
security/safesetid/lsm.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 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 redistribute it and/or modify 10 + * it under the terms of the GNU General Public License version 2, as 11 + * published by the Free Software Foundation. 12 + * 13 + */ 14 + #ifndef _SAFESETID_H 15 + #define _SAFESETID_H 16 + 17 + #include <linux/types.h> 18 + 19 + /* Flag indicating whether initialization completed */ 20 + extern int safesetid_initialized; 21 + 22 + /* Function type. */ 23 + enum safesetid_whitelist_file_write_type { 24 + SAFESETID_WHITELIST_ADD, /* Add whitelist policy. */ 25 + SAFESETID_WHITELIST_FLUSH, /* Flush whitelist policies. */ 26 + }; 27 + 28 + /* Add entry to safesetid whitelist to allow 'parent' to setid to 'child'. */ 29 + int add_safesetid_whitelist_entry(kuid_t parent, kuid_t child); 30 + 31 + void flush_safesetid_whitelist_entries(void); 32 + 33 + #endif /* _SAFESETID_H */
+193
security/safesetid/securityfs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 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 redistribute it and/or modify 10 + * it under the terms of the GNU General Public License version 2, as 11 + * published by the Free Software Foundation. 12 + * 13 + */ 14 + #include <linux/security.h> 15 + #include <linux/cred.h> 16 + 17 + #include "lsm.h" 18 + 19 + static struct dentry *safesetid_policy_dir; 20 + 21 + struct safesetid_file_entry { 22 + const char *name; 23 + enum safesetid_whitelist_file_write_type type; 24 + struct dentry *dentry; 25 + }; 26 + 27 + static struct safesetid_file_entry safesetid_files[] = { 28 + {.name = "add_whitelist_policy", 29 + .type = SAFESETID_WHITELIST_ADD}, 30 + {.name = "flush_whitelist_policies", 31 + .type = SAFESETID_WHITELIST_FLUSH}, 32 + }; 33 + 34 + /* 35 + * In the case the input buffer contains one or more invalid UIDs, the kuid_t 36 + * variables pointed to by 'parent' and 'child' will get updated but this 37 + * function will return an error. 38 + */ 39 + static int parse_safesetid_whitelist_policy(const char __user *buf, 40 + size_t len, 41 + kuid_t *parent, 42 + kuid_t *child) 43 + { 44 + char *kern_buf; 45 + char *parent_buf; 46 + char *child_buf; 47 + const char separator[] = ":"; 48 + int ret; 49 + size_t first_substring_length; 50 + long parsed_parent; 51 + long parsed_child; 52 + 53 + /* Duplicate string from user memory and NULL-terminate */ 54 + kern_buf = memdup_user_nul(buf, len); 55 + if (IS_ERR(kern_buf)) 56 + return PTR_ERR(kern_buf); 57 + 58 + /* 59 + * Format of |buf| string should be <UID>:<UID>. 60 + * Find location of ":" in kern_buf (copied from |buf|). 61 + */ 62 + first_substring_length = strcspn(kern_buf, separator); 63 + if (first_substring_length == 0 || first_substring_length == len) { 64 + ret = -EINVAL; 65 + goto free_kern; 66 + } 67 + 68 + parent_buf = kmemdup_nul(kern_buf, first_substring_length, GFP_KERNEL); 69 + if (!parent_buf) { 70 + ret = -ENOMEM; 71 + goto free_kern; 72 + } 73 + 74 + ret = kstrtol(parent_buf, 0, &parsed_parent); 75 + if (ret) 76 + goto free_both; 77 + 78 + child_buf = kern_buf + first_substring_length + 1; 79 + ret = kstrtol(child_buf, 0, &parsed_child); 80 + if (ret) 81 + goto free_both; 82 + 83 + *parent = make_kuid(current_user_ns(), parsed_parent); 84 + if (!uid_valid(*parent)) { 85 + ret = -EINVAL; 86 + goto free_both; 87 + } 88 + 89 + *child = make_kuid(current_user_ns(), parsed_child); 90 + if (!uid_valid(*child)) { 91 + ret = -EINVAL; 92 + goto free_both; 93 + } 94 + 95 + free_both: 96 + kfree(parent_buf); 97 + free_kern: 98 + kfree(kern_buf); 99 + return ret; 100 + } 101 + 102 + static ssize_t safesetid_file_write(struct file *file, 103 + const char __user *buf, 104 + size_t len, 105 + loff_t *ppos) 106 + { 107 + struct safesetid_file_entry *file_entry = 108 + file->f_inode->i_private; 109 + kuid_t parent; 110 + kuid_t child; 111 + int ret; 112 + 113 + if (!ns_capable(current_user_ns(), CAP_MAC_ADMIN)) 114 + return -EPERM; 115 + 116 + if (*ppos != 0) 117 + return -EINVAL; 118 + 119 + switch (file_entry->type) { 120 + case SAFESETID_WHITELIST_FLUSH: 121 + flush_safesetid_whitelist_entries(); 122 + break; 123 + case SAFESETID_WHITELIST_ADD: 124 + ret = parse_safesetid_whitelist_policy(buf, len, &parent, 125 + &child); 126 + if (ret) 127 + return ret; 128 + 129 + ret = add_safesetid_whitelist_entry(parent, child); 130 + if (ret) 131 + return ret; 132 + break; 133 + default: 134 + pr_warn("Unknown securityfs file %d\n", file_entry->type); 135 + break; 136 + } 137 + 138 + /* Return len on success so caller won't keep trying to write */ 139 + return len; 140 + } 141 + 142 + static const struct file_operations safesetid_file_fops = { 143 + .write = safesetid_file_write, 144 + }; 145 + 146 + static void safesetid_shutdown_securityfs(void) 147 + { 148 + int i; 149 + 150 + for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) { 151 + struct safesetid_file_entry *entry = 152 + &safesetid_files[i]; 153 + securityfs_remove(entry->dentry); 154 + entry->dentry = NULL; 155 + } 156 + 157 + securityfs_remove(safesetid_policy_dir); 158 + safesetid_policy_dir = NULL; 159 + } 160 + 161 + static int __init safesetid_init_securityfs(void) 162 + { 163 + int i; 164 + int ret; 165 + 166 + if (!safesetid_initialized) 167 + return 0; 168 + 169 + safesetid_policy_dir = securityfs_create_dir("safesetid", NULL); 170 + if (!safesetid_policy_dir) { 171 + ret = PTR_ERR(safesetid_policy_dir); 172 + goto error; 173 + } 174 + 175 + for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) { 176 + struct safesetid_file_entry *entry = 177 + &safesetid_files[i]; 178 + entry->dentry = securityfs_create_file( 179 + entry->name, 0200, safesetid_policy_dir, 180 + entry, &safesetid_file_fops); 181 + if (IS_ERR(entry->dentry)) { 182 + ret = PTR_ERR(entry->dentry); 183 + goto error; 184 + } 185 + } 186 + 187 + return 0; 188 + 189 + error: 190 + safesetid_shutdown_securityfs(); 191 + return ret; 192 + } 193 + fs_initcall(safesetid_init_securityfs);