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

selinux: remove the runtime disable functionality

After working with the larger SELinux-based distros for several
years, we're finally at a place where we can disable the SELinux
runtime disable functionality. The existing kernel deprecation
notice explains the functionality and why we want to remove it:

The selinuxfs "disable" node allows SELinux to be disabled at
runtime prior to a policy being loaded into the kernel. If
disabled via this mechanism, SELinux will remain disabled until
the system is rebooted.

The preferred method of disabling SELinux is via the "selinux=0"
boot parameter, but the selinuxfs "disable" node was created to
make it easier for systems with primitive bootloaders that did not
allow for easy modification of the kernel command line.
Unfortunately, allowing for SELinux to be disabled at runtime makes
it difficult to secure the kernel's LSM hooks using the
"__ro_after_init" feature.

It is that last sentence, mentioning the '__ro_after_init' hardening,
which is the real motivation for this change, and if you look at the
diffstat you'll see that the impact of this patch reaches across all
the different LSMs, helping prevent tampering at the LSM hook level.

From a SELinux perspective, it is important to note that if you
continue to disable SELinux via "/etc/selinux/config" it may appear
that SELinux is disabled, but it is simply in an uninitialized state.
If you load a policy with `load_policy -i`, you will see SELinux
come alive just as if you had loaded the policy during early-boot.

It is also worth noting that the "/sys/fs/selinux/disable" file is
always writable now, regardless of the Kconfig settings, but writing
to the file has no effect on the system, other than to display an
error on the console if a non-zero/true value is written.

Finally, in the several years where we have been working on
deprecating this functionality, there has only been one instance of
someone mentioning any user visible breakage. In this particular
case it was an individual's kernel test system, and the workaround
documented in the deprecation notice ("selinux=0" on the kernel
command line) resolved the issue without problem.

Acked-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>

+32 -170
+3
Documentation/ABI/obsolete/sysfs-selinux-disable Documentation/ABI/removed/sysfs-selinux-disable
··· 4 4 Contact: selinux@vger.kernel.org 5 5 Description: 6 6 7 + REMOVAL UPDATE: The SELinux runtime disable functionality was removed 8 + in March 2023, the original deprecation notice is shown below. 9 + 7 10 The selinuxfs "disable" node allows SELinux to be disabled at runtime 8 11 prior to a policy being loaded into the kernel. If disabled via this 9 12 mechanism, SELinux will remain disabled until the system is rebooted.
-7
include/linux/lsm_hooks.h
··· 1763 1763 } 1764 1764 #endif /* CONFIG_SECURITY_SELINUX_DISABLE */ 1765 1765 1766 - /* Currently required to handle SELinux runtime hook disable. */ 1767 - #ifdef CONFIG_SECURITY_WRITABLE_HOOKS 1768 - #define __lsm_ro_after_init 1769 - #else 1770 - #define __lsm_ro_after_init __ro_after_init 1771 - #endif /* CONFIG_SECURITY_WRITABLE_HOOKS */ 1772 - 1773 1766 extern int lsm_inode_alloc(struct inode *inode); 1774 1767 1775 1768 #endif /* ! __LINUX_LSM_HOOKS_H */
-5
security/Kconfig
··· 32 32 33 33 If you are unsure how to answer this question, answer N. 34 34 35 - config SECURITY_WRITABLE_HOOKS 36 - depends on SECURITY 37 - bool 38 - default n 39 - 40 35 config SECURITYFS 41 36 bool "Enable the securityfs filesystem" 42 37 help
+3 -3
security/apparmor/lsm.c
··· 1209 1209 /* 1210 1210 * The cred blob is a pointer to, not an instance of, an aa_label. 1211 1211 */ 1212 - struct lsm_blob_sizes apparmor_blob_sizes __lsm_ro_after_init = { 1212 + struct lsm_blob_sizes apparmor_blob_sizes __ro_after_init = { 1213 1213 .lbs_cred = sizeof(struct aa_label *), 1214 1214 .lbs_file = sizeof(struct aa_file_ctx), 1215 1215 .lbs_task = sizeof(struct aa_task_ctx), 1216 1216 }; 1217 1217 1218 - static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { 1218 + static struct security_hook_list apparmor_hooks[] __ro_after_init = { 1219 1219 LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), 1220 1220 LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), 1221 1221 LSM_HOOK_INIT(capget, apparmor_capget), ··· 1427 1427 .get = param_get_aaintbool 1428 1428 }; 1429 1429 /* Boot time disable flag */ 1430 - static int apparmor_enabled __lsm_ro_after_init = 1; 1430 + static int apparmor_enabled __ro_after_init = 1; 1431 1431 module_param_named(enabled, apparmor_enabled, aaintbool, 0444); 1432 1432 1433 1433 static int __init apparmor_enabled_setup(char *str)
+2 -2
security/bpf/hooks.c
··· 6 6 #include <linux/lsm_hooks.h> 7 7 #include <linux/bpf_lsm.h> 8 8 9 - static struct security_hook_list bpf_lsm_hooks[] __lsm_ro_after_init = { 9 + static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = { 10 10 #define LSM_HOOK(RET, DEFAULT, NAME, ...) \ 11 11 LSM_HOOK_INIT(NAME, bpf_lsm_##NAME), 12 12 #include <linux/lsm_hook_defs.h> ··· 22 22 return 0; 23 23 } 24 24 25 - struct lsm_blob_sizes bpf_lsm_blob_sizes __lsm_ro_after_init = { 25 + struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = { 26 26 .lbs_inode = sizeof(struct bpf_storage_blob), 27 27 .lbs_task = sizeof(struct bpf_storage_blob), 28 28 };
+1 -1
security/commoncap.c
··· 1440 1440 1441 1441 #ifdef CONFIG_SECURITY 1442 1442 1443 - static struct security_hook_list capability_hooks[] __lsm_ro_after_init = { 1443 + static struct security_hook_list capability_hooks[] __ro_after_init = { 1444 1444 LSM_HOOK_INIT(capable, cap_capable), 1445 1445 LSM_HOOK_INIT(settime, cap_settime), 1446 1446 LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check),
+1 -1
security/landlock/cred.c
··· 34 34 landlock_put_ruleset_deferred(dom); 35 35 } 36 36 37 - static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { 37 + static struct security_hook_list landlock_hooks[] __ro_after_init = { 38 38 LSM_HOOK_INIT(cred_prepare, hook_cred_prepare), 39 39 LSM_HOOK_INIT(cred_free, hook_cred_free), 40 40 };
+1 -1
security/landlock/fs.c
··· 1280 1280 return -EACCES; 1281 1281 } 1282 1282 1283 - static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { 1283 + static struct security_hook_list landlock_hooks[] __ro_after_init = { 1284 1284 LSM_HOOK_INIT(inode_free_security, hook_inode_free_security), 1285 1285 1286 1286 LSM_HOOK_INIT(sb_delete, hook_sb_delete),
+1 -1
security/landlock/ptrace.c
··· 108 108 return task_ptrace(parent, current); 109 109 } 110 110 111 - static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = { 111 + static struct security_hook_list landlock_hooks[] __ro_after_init = { 112 112 LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check), 113 113 LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme), 114 114 };
+2 -2
security/landlock/setup.c
··· 15 15 #include "ptrace.h" 16 16 #include "setup.h" 17 17 18 - bool landlock_initialized __lsm_ro_after_init = false; 18 + bool landlock_initialized __ro_after_init = false; 19 19 20 - struct lsm_blob_sizes landlock_blob_sizes __lsm_ro_after_init = { 20 + struct lsm_blob_sizes landlock_blob_sizes __ro_after_init = { 21 21 .lbs_cred = sizeof(struct landlock_cred_security), 22 22 .lbs_file = sizeof(struct landlock_file_security), 23 23 .lbs_inode = sizeof(struct landlock_inode_security),
+1 -1
security/loadpin/loadpin.c
··· 214 214 return loadpin_check(NULL, (enum kernel_read_file_id) id); 215 215 } 216 216 217 - static struct security_hook_list loadpin_hooks[] __lsm_ro_after_init = { 217 + static struct security_hook_list loadpin_hooks[] __ro_after_init = { 218 218 LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security), 219 219 LSM_HOOK_INIT(kernel_read_file, loadpin_read_file), 220 220 LSM_HOOK_INIT(kernel_load_data, loadpin_load_data),
+1 -1
security/lockdown/lockdown.c
··· 71 71 return 0; 72 72 } 73 73 74 - static struct security_hook_list lockdown_hooks[] __lsm_ro_after_init = { 74 + static struct security_hook_list lockdown_hooks[] __ro_after_init = { 75 75 LSM_HOOK_INIT(locked_down, lockdown_is_locked_down), 76 76 }; 77 77
+2 -2
security/security.c
··· 74 74 [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality", 75 75 }; 76 76 77 - struct security_hook_heads security_hook_heads __lsm_ro_after_init; 77 + struct security_hook_heads security_hook_heads __ro_after_init; 78 78 static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain); 79 79 80 80 static struct kmem_cache *lsm_file_cache; 81 81 static struct kmem_cache *lsm_inode_cache; 82 82 83 83 char *lsm_names; 84 - static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init; 84 + static struct lsm_blob_sizes blob_sizes __ro_after_init; 85 85 86 86 /* Boot-time LSM user choice */ 87 87 static __initdata const char *chosen_lsm_order;
-24
security/selinux/Kconfig
··· 23 23 24 24 If you are unsure how to answer this question, answer N. 25 25 26 - config SECURITY_SELINUX_DISABLE 27 - bool "NSA SELinux runtime disable" 28 - depends on SECURITY_SELINUX 29 - select SECURITY_WRITABLE_HOOKS 30 - default n 31 - help 32 - This option enables writing to a selinuxfs node 'disable', which 33 - allows SELinux to be disabled at runtime prior to the policy load. 34 - SELinux will then remain disabled until the next boot. 35 - This option is similar to the selinux=0 boot parameter, but is to 36 - support runtime disabling of SELinux, e.g. from /sbin/init, for 37 - portability across platforms where boot parameters are difficult 38 - to employ. 39 - 40 - NOTE: selecting this option will disable the '__ro_after_init' 41 - kernel hardening feature for security hooks. Please consider 42 - using the selinux=0 boot parameter instead of enabling this 43 - option. 44 - 45 - WARNING: this option is deprecated and will be removed in a future 46 - kernel release. 47 - 48 - If you are unsure how to answer this question, answer N. 49 - 50 26 config SECURITY_SELINUX_DEVELOP 51 27 bool "NSA SELinux Development Support" 52 28 depends on SECURITY_SELINUX
+2 -55
security/selinux/hooks.c
··· 6769 6769 } 6770 6770 #endif 6771 6771 6772 - struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = { 6772 + struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = { 6773 6773 .lbs_cred = sizeof(struct task_security_struct), 6774 6774 .lbs_file = sizeof(struct file_security_struct), 6775 6775 .lbs_inode = sizeof(struct inode_security_struct), ··· 6905 6905 * safely. Breaking the ordering rules above might lead to NULL pointer derefs 6906 6906 * when disabling SELinux at runtime. 6907 6907 */ 6908 - static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { 6908 + static struct security_hook_list selinux_hooks[] __ro_after_init = { 6909 6909 LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr), 6910 6910 LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction), 6911 6911 LSM_HOOK_INIT(binder_transfer_binder, selinux_binder_transfer_binder), ··· 7253 7253 }; 7254 7254 7255 7255 #if defined(CONFIG_NETFILTER) 7256 - 7257 7256 static const struct nf_hook_ops selinux_nf_ops[] = { 7258 7257 { 7259 7258 .hook = selinux_ip_postroute, ··· 7327 7328 return 0; 7328 7329 } 7329 7330 __initcall(selinux_nf_ip_init); 7330 - 7331 - #ifdef CONFIG_SECURITY_SELINUX_DISABLE 7332 - static void selinux_nf_ip_exit(void) 7333 - { 7334 - pr_debug("SELinux: Unregistering netfilter hooks\n"); 7335 - 7336 - unregister_pernet_subsys(&selinux_net_ops); 7337 - } 7338 - #endif 7339 - 7340 - #else /* CONFIG_NETFILTER */ 7341 - 7342 - #ifdef CONFIG_SECURITY_SELINUX_DISABLE 7343 - #define selinux_nf_ip_exit() 7344 - #endif 7345 - 7346 7331 #endif /* CONFIG_NETFILTER */ 7347 - 7348 - #ifdef CONFIG_SECURITY_SELINUX_DISABLE 7349 - int selinux_disable(void) 7350 - { 7351 - if (selinux_initialized()) { 7352 - /* Not permitted after initial policy load. */ 7353 - return -EINVAL; 7354 - } 7355 - 7356 - if (selinux_disabled()) { 7357 - /* Only do this once. */ 7358 - return -EINVAL; 7359 - } 7360 - 7361 - selinux_mark_disabled(); 7362 - 7363 - pr_info("SELinux: Disabled at runtime.\n"); 7364 - 7365 - /* 7366 - * Unregister netfilter hooks. 7367 - * Must be done before security_delete_hooks() to avoid breaking 7368 - * runtime disable. 7369 - */ 7370 - selinux_nf_ip_exit(); 7371 - 7372 - security_delete_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks)); 7373 - 7374 - /* Try to destroy the avc node cache */ 7375 - avc_disable(); 7376 - 7377 - /* Unregister selinuxfs. */ 7378 - exit_sel_fs(); 7379 - 7380 - return 0; 7381 - } 7382 - #endif
-21
security/selinux/include/security.h
··· 89 89 struct selinux_policy; 90 90 91 91 struct selinux_state { 92 - #ifdef CONFIG_SECURITY_SELINUX_DISABLE 93 - bool disabled; 94 - #endif 95 92 #ifdef CONFIG_SECURITY_SELINUX_DEVELOP 96 93 bool enforcing; 97 94 #endif ··· 144 147 /* non-zero/true checkreqprot values are no longer supported */ 145 148 return 0; 146 149 } 147 - 148 - #ifdef CONFIG_SECURITY_SELINUX_DISABLE 149 - static inline bool selinux_disabled(void) 150 - { 151 - return READ_ONCE(selinux_state.disabled); 152 - } 153 - 154 - static inline void selinux_mark_disabled(void) 155 - { 156 - WRITE_ONCE(selinux_state.disabled, true); 157 - } 158 - #else 159 - static inline bool selinux_disabled(void) 160 - { 161 - return false; 162 - } 163 - #endif 164 150 165 151 static inline bool selinux_policycap_netpeer(void) 166 152 { ··· 384 404 extern void selinux_status_update_setenforce(int enforcing); 385 405 extern void selinux_status_update_policyload(int seqno); 386 406 extern void selinux_complete_init(void); 387 - extern int selinux_disable(void); 388 407 extern void exit_sel_fs(void); 389 408 extern struct path selinux_null; 390 409 extern void selnl_notify_setenforce(int val);
+6 -37
security/selinux/selinuxfs.c
··· 267 267 .llseek = generic_file_llseek, 268 268 }; 269 269 270 - #ifdef CONFIG_SECURITY_SELINUX_DISABLE 271 270 static ssize_t sel_write_disable(struct file *file, const char __user *buf, 272 271 size_t count, loff_t *ppos) 273 272 ··· 274 275 char *page; 275 276 ssize_t length; 276 277 int new_value; 277 - int enforcing; 278 - 279 - /* NOTE: we are now officially considering runtime disable as 280 - * deprecated, and using it will become increasingly painful 281 - * (e.g. sleeping/blocking) as we progress through future 282 - * kernel releases until eventually it is removed 283 - */ 284 - pr_err("SELinux: Runtime disable is deprecated, use selinux=0 on the kernel cmdline.\n"); 285 - pr_err("SELinux: https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-runtime-disable\n"); 286 - ssleep(15); 287 278 288 279 if (count >= PAGE_SIZE) 289 280 return -ENOMEM; ··· 286 297 if (IS_ERR(page)) 287 298 return PTR_ERR(page); 288 299 289 - length = -EINVAL; 290 - if (sscanf(page, "%d", &new_value) != 1) 300 + if (sscanf(page, "%d", &new_value) != 1) { 301 + length = -EINVAL; 291 302 goto out; 303 + } 304 + length = count; 292 305 293 306 if (new_value) { 294 - enforcing = enforcing_enabled(); 295 - length = selinux_disable(); 296 - if (length) 297 - goto out; 298 - audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, 299 - "enforcing=%d old_enforcing=%d auid=%u ses=%u" 300 - " enabled=0 old-enabled=1 lsm=selinux res=1", 301 - enforcing, enforcing, 302 - from_kuid(&init_user_ns, audit_get_loginuid(current)), 303 - audit_get_sessionid(current)); 307 + pr_err("SELinux: https://github.com/SELinuxProject/selinux-kernel/wiki/DEPRECATE-runtime-disable\n"); 308 + pr_err("SELinux: Runtime disable is not supported, use selinux=0 on the kernel cmdline.\n"); 304 309 } 305 310 306 - length = count; 307 311 out: 308 312 kfree(page); 309 313 return length; 310 314 } 311 - #else 312 - #define sel_write_disable NULL 313 - #endif 314 315 315 316 static const struct file_operations sel_disable_ops = { 316 317 .write = sel_write_disable, ··· 2173 2194 } 2174 2195 2175 2196 __initcall(init_sel_fs); 2176 - 2177 - #ifdef CONFIG_SECURITY_SELINUX_DISABLE 2178 - void exit_sel_fs(void) 2179 - { 2180 - sysfs_remove_mount_point(fs_kobj, "selinux"); 2181 - dput(selinux_null.dentry); 2182 - kern_unmount(selinuxfs_mount); 2183 - unregister_filesystem(&sel_fs_type); 2184 - } 2185 - #endif
+2 -2
security/smack/smack_lsm.c
··· 4847 4847 4848 4848 #endif /* CONFIG_IO_URING */ 4849 4849 4850 - struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = { 4850 + struct lsm_blob_sizes smack_blob_sizes __ro_after_init = { 4851 4851 .lbs_cred = sizeof(struct task_smack), 4852 4852 .lbs_file = sizeof(struct smack_known *), 4853 4853 .lbs_inode = sizeof(struct inode_smack), ··· 4856 4856 .lbs_superblock = sizeof(struct superblock_smack), 4857 4857 }; 4858 4858 4859 - static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { 4859 + static struct security_hook_list smack_hooks[] __ro_after_init = { 4860 4860 LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check), 4861 4861 LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme), 4862 4862 LSM_HOOK_INIT(syslog, smack_syslog),
+3 -3
security/tomoyo/tomoyo.c
··· 499 499 return tomoyo_socket_sendmsg_permission(sock, msg, size); 500 500 } 501 501 502 - struct lsm_blob_sizes tomoyo_blob_sizes __lsm_ro_after_init = { 502 + struct lsm_blob_sizes tomoyo_blob_sizes __ro_after_init = { 503 503 .lbs_task = sizeof(struct tomoyo_task), 504 504 }; 505 505 ··· 546 546 * tomoyo_security_ops is a "struct security_operations" which is used for 547 547 * registering TOMOYO. 548 548 */ 549 - static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = { 549 + static struct security_hook_list tomoyo_hooks[] __ro_after_init = { 550 550 LSM_HOOK_INIT(cred_prepare, tomoyo_cred_prepare), 551 551 LSM_HOOK_INIT(bprm_committed_creds, tomoyo_bprm_committed_creds), 552 552 LSM_HOOK_INIT(task_alloc, tomoyo_task_alloc), ··· 583 583 /* Lock for GC. */ 584 584 DEFINE_SRCU(tomoyo_ss); 585 585 586 - int tomoyo_enabled __lsm_ro_after_init = 1; 586 + int tomoyo_enabled __ro_after_init = 1; 587 587 588 588 /** 589 589 * tomoyo_init - Register TOMOYO Linux as a LSM module.
+1 -1
security/yama/yama_lsm.c
··· 421 421 return rc; 422 422 } 423 423 424 - static struct security_hook_list yama_hooks[] __lsm_ro_after_init = { 424 + static struct security_hook_list yama_hooks[] __ro_after_init = { 425 425 LSM_HOOK_INIT(ptrace_access_check, yama_ptrace_access_check), 426 426 LSM_HOOK_INIT(ptrace_traceme, yama_ptrace_traceme), 427 427 LSM_HOOK_INIT(task_prctl, yama_task_prctl),