diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/Kconfig.hardening | 45 | ||||
-rw-r--r-- | security/apparmor/Kconfig | 3 | ||||
-rw-r--r-- | security/apparmor/crypto.c | 85 | ||||
-rw-r--r-- | security/apparmor/file.c | 2 | ||||
-rw-r--r-- | security/inode.c | 62 | ||||
-rw-r--r-- | security/integrity/evm/evm_secfs.c | 15 | ||||
-rw-r--r-- | security/integrity/ima/ima_fs.c | 137 | ||||
-rw-r--r-- | security/ipe/fs.c | 32 | ||||
-rw-r--r-- | security/ipe/policy_fs.c | 4 | ||||
-rw-r--r-- | security/landlock/fs.c | 1 | ||||
-rw-r--r-- | security/landlock/id.c | 69 | ||||
-rw-r--r-- | security/landlock/syscalls.c | 1 | ||||
-rw-r--r-- | security/security.c | 32 | ||||
-rw-r--r-- | security/selinux/hooks.c | 28 | ||||
-rw-r--r-- | security/selinux/include/avc.h | 4 | ||||
-rw-r--r-- | security/selinux/include/objsec.h | 8 | ||||
-rw-r--r-- | security/selinux/include/security.h | 4 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 3 | ||||
-rw-r--r-- | security/selinux/ss/hashtab.c | 3 | ||||
-rw-r--r-- | security/selinux/ss/policydb.c | 19 | ||||
-rw-r--r-- | security/selinux/ss/policydb.h | 2 | ||||
-rw-r--r-- | security/selinux/ss/services.c | 20 |
22 files changed, 273 insertions, 306 deletions
diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index fd1238753cad..b9a5bc3430aa 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -82,10 +82,13 @@ choice endchoice -config GCC_PLUGIN_STACKLEAK +config CC_HAS_SANCOV_STACK_DEPTH_CALLBACK + def_bool $(cc-option,-fsanitize-coverage-stack-depth-callback-min=1) + +config KSTACK_ERASE bool "Poison kernel stack before returning from syscalls" - depends on GCC_PLUGINS - depends on HAVE_ARCH_STACKLEAK + depends on HAVE_ARCH_KSTACK_ERASE + depends on GCC_PLUGINS || CC_HAS_SANCOV_STACK_DEPTH_CALLBACK help This option makes the kernel erase the kernel stack before returning from system calls. This has the effect of leaving @@ -103,6 +106,10 @@ config GCC_PLUGIN_STACKLEAK are advised to test this feature on your expected workload before deploying it. +config GCC_PLUGIN_STACKLEAK + def_bool KSTACK_ERASE + depends on GCC_PLUGINS + help This plugin was ported from grsecurity/PaX. More information at: * https://grsecurity.net/ * https://pax.grsecurity.net/ @@ -117,37 +124,37 @@ config GCC_PLUGIN_STACKLEAK_VERBOSE instrumented. This is useful for comparing coverage between builds. -config STACKLEAK_TRACK_MIN_SIZE - int "Minimum stack frame size of functions tracked by STACKLEAK" +config KSTACK_ERASE_TRACK_MIN_SIZE + int "Minimum stack frame size of functions tracked by KSTACK_ERASE" default 100 range 0 4096 - depends on GCC_PLUGIN_STACKLEAK + depends on KSTACK_ERASE help - The STACKLEAK gcc plugin instruments the kernel code for tracking + The KSTACK_ERASE option instruments the kernel code for tracking the lowest border of the kernel stack (and for some other purposes). - It inserts the stackleak_track_stack() call for the functions with - a stack frame size greater than or equal to this parameter. + It inserts the __sanitizer_cov_stack_depth() call for the functions + with a stack frame size greater than or equal to this parameter. If unsure, leave the default value 100. -config STACKLEAK_METRICS - bool "Show STACKLEAK metrics in the /proc file system" - depends on GCC_PLUGIN_STACKLEAK +config KSTACK_ERASE_METRICS + bool "Show KSTACK_ERASE metrics in the /proc file system" + depends on KSTACK_ERASE depends on PROC_FS help - If this is set, STACKLEAK metrics for every task are available in - the /proc file system. In particular, /proc/<pid>/stack_depth + If this is set, KSTACK_ERASE metrics for every task are available + in the /proc file system. In particular, /proc/<pid>/stack_depth shows the maximum kernel stack consumption for the current and previous syscalls. Although this information is not precise, it - can be useful for estimating the STACKLEAK performance impact for - your workloads. + can be useful for estimating the KSTACK_ERASE performance impact + for your workloads. -config STACKLEAK_RUNTIME_DISABLE +config KSTACK_ERASE_RUNTIME_DISABLE bool "Allow runtime disabling of kernel stack erasing" - depends on GCC_PLUGIN_STACKLEAK + depends on KSTACK_ERASE help This option provides 'stack_erasing' sysctl, which can be used in runtime to control kernel stack erasing for kernels built with - CONFIG_GCC_PLUGIN_STACKLEAK. + CONFIG_KSTACK_ERASE. config INIT_ON_ALLOC_DEFAULT_ON bool "Enable heap memory zeroing on allocation by default" diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 64cc3044a42c..1e3bd44643da 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -59,8 +59,7 @@ config SECURITY_APPARMOR_INTROSPECT_POLICY config SECURITY_APPARMOR_HASH bool "Enable introspection of sha256 hashes for loaded profiles" depends on SECURITY_APPARMOR_INTROSPECT_POLICY - select CRYPTO - select CRYPTO_SHA256 + select CRYPTO_LIB_SHA256 default y help This option selects whether introspection of loaded policy diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c index aad486b2fca6..227d47c14907 100644 --- a/security/apparmor/crypto.c +++ b/security/apparmor/crypto.c @@ -11,113 +11,52 @@ * it should be. */ -#include <crypto/hash.h> +#include <crypto/sha2.h> #include "include/apparmor.h" #include "include/crypto.h" -static unsigned int apparmor_hash_size; - -static struct crypto_shash *apparmor_tfm; - unsigned int aa_hash_size(void) { - return apparmor_hash_size; + return SHA256_DIGEST_SIZE; } char *aa_calc_hash(void *data, size_t len) { - SHASH_DESC_ON_STACK(desc, apparmor_tfm); char *hash; - int error; - - if (!apparmor_tfm) - return NULL; - hash = kzalloc(apparmor_hash_size, GFP_KERNEL); + hash = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL); if (!hash) return ERR_PTR(-ENOMEM); - desc->tfm = apparmor_tfm; - - error = crypto_shash_init(desc); - if (error) - goto fail; - error = crypto_shash_update(desc, (u8 *) data, len); - if (error) - goto fail; - error = crypto_shash_final(desc, hash); - if (error) - goto fail; - + sha256(data, len, hash); return hash; - -fail: - kfree(hash); - - return ERR_PTR(error); } int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, size_t len) { - SHASH_DESC_ON_STACK(desc, apparmor_tfm); - int error; + struct sha256_ctx sctx; __le32 le32_version = cpu_to_le32(version); if (!aa_g_hash_policy) return 0; - if (!apparmor_tfm) - return 0; - - profile->hash = kzalloc(apparmor_hash_size, GFP_KERNEL); + profile->hash = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL); if (!profile->hash) return -ENOMEM; - desc->tfm = apparmor_tfm; - - error = crypto_shash_init(desc); - if (error) - goto fail; - error = crypto_shash_update(desc, (u8 *) &le32_version, 4); - if (error) - goto fail; - error = crypto_shash_update(desc, (u8 *) start, len); - if (error) - goto fail; - error = crypto_shash_final(desc, profile->hash); - if (error) - goto fail; - + sha256_init(&sctx); + sha256_update(&sctx, (u8 *)&le32_version, 4); + sha256_update(&sctx, (u8 *)start, len); + sha256_final(&sctx, profile->hash); return 0; - -fail: - kfree(profile->hash); - profile->hash = NULL; - - return error; } static int __init init_profile_hash(void) { - struct crypto_shash *tfm; - - if (!apparmor_initialized) - return 0; - - tfm = crypto_alloc_shash("sha256", 0, 0); - if (IS_ERR(tfm)) { - int error = PTR_ERR(tfm); - AA_ERROR("failed to setup profile sha256 hashing: %d\n", error); - return error; - } - apparmor_tfm = tfm; - apparmor_hash_size = crypto_shash_digestsize(apparmor_tfm); - - aa_info_message("AppArmor sha256 policy hashing enabled"); - + if (apparmor_initialized) + aa_info_message("AppArmor sha256 policy hashing enabled"); return 0; } - late_initcall(init_profile_hash); diff --git a/security/apparmor/file.c b/security/apparmor/file.c index d52a5b14dad4..f494217112c9 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -604,7 +604,7 @@ int aa_file_perm(const char *op, const struct cred *subj_cred, rcu_read_unlock(); /* TODO: label cross check */ - if (file->f_path.mnt && path_mediated_fs(file->f_path.dentry)) + if (path_mediated_fs(file->f_path.dentry)) error = __file_path_perm(op, subj_cred, label, flabel, file, request, denied, in_atomic); diff --git a/security/inode.c b/security/inode.c index 3913501621fa..43382ef8896e 100644 --- a/security/inode.c +++ b/security/inode.c @@ -112,18 +112,20 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode, struct dentry *dentry; struct inode *dir, *inode; int error; + bool pinned = false; if (!(mode & S_IFMT)) mode = (mode & S_IALLUGO) | S_IFREG; pr_debug("securityfs: creating file '%s'\n",name); - error = simple_pin_fs(&fs_type, &mount, &mount_count); - if (error) - return ERR_PTR(error); - - if (!parent) + if (!parent) { + error = simple_pin_fs(&fs_type, &mount, &mount_count); + if (error) + return ERR_PTR(error); + pinned = true; parent = mount->mnt_root; + } dir = d_inode(parent); @@ -159,7 +161,6 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode, inode->i_fop = fops; } d_instantiate(dentry, inode); - dget(dentry); inode_unlock(dir); return dentry; @@ -168,7 +169,8 @@ out1: dentry = ERR_PTR(error); out: inode_unlock(dir); - simple_release_fs(&mount, &mount_count); + if (pinned) + simple_release_fs(&mount, &mount_count); return dentry; } @@ -279,6 +281,12 @@ struct dentry *securityfs_create_symlink(const char *name, } EXPORT_SYMBOL_GPL(securityfs_create_symlink); +static void remove_one(struct dentry *victim) +{ + if (victim->d_parent == victim->d_sb->s_root) + simple_release_fs(&mount, &mount_count); +} + /** * securityfs_remove - removes a file or directory from the securityfs filesystem * @@ -291,43 +299,11 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink); * This function is required to be called in order for the file to be * removed. No automatic cleanup of files will happen when a module is * removed; you are responsible here. - */ -void securityfs_remove(struct dentry *dentry) -{ - struct inode *dir; - - if (IS_ERR_OR_NULL(dentry)) - return; - - dir = d_inode(dentry->d_parent); - inode_lock(dir); - if (simple_positive(dentry)) { - if (d_is_dir(dentry)) - simple_rmdir(dir, dentry); - else - simple_unlink(dir, dentry); - dput(dentry); - } - inode_unlock(dir); - simple_release_fs(&mount, &mount_count); -} -EXPORT_SYMBOL_GPL(securityfs_remove); - -static void remove_one(struct dentry *victim) -{ - simple_release_fs(&mount, &mount_count); -} - -/** - * securityfs_recursive_remove - recursively removes a file or directory - * - * @dentry: a pointer to a the dentry of the file or directory to be removed. * - * This function recursively removes a file or directory in securityfs that was - * previously created with a call to another securityfs function (like - * securityfs_create_file() or variants thereof.) + * AV: when applied to directory it will take all children out; no need to call + * it for descendents if ancestor is getting killed. */ -void securityfs_recursive_remove(struct dentry *dentry) +void securityfs_remove(struct dentry *dentry) { if (IS_ERR_OR_NULL(dentry)) return; @@ -336,7 +312,7 @@ void securityfs_recursive_remove(struct dentry *dentry) simple_recursive_removal(dentry, remove_one); simple_release_fs(&mount, &mount_count); } -EXPORT_SYMBOL_GPL(securityfs_recursive_remove); +EXPORT_SYMBOL_GPL(securityfs_remove); #ifdef CONFIG_SECURITY static struct dentry *lsm_dentry; diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index 9b907c2fee60..b0d2aad27850 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -17,7 +17,6 @@ #include "evm.h" static struct dentry *evm_dir; -static struct dentry *evm_init_tpm; static struct dentry *evm_symlink; #ifdef CONFIG_EVM_ADD_XATTRS @@ -286,7 +285,7 @@ static int evm_init_xattrs(void) { evm_xattrs = securityfs_create_file("evm_xattrs", 0660, evm_dir, NULL, &evm_xattr_ops); - if (!evm_xattrs || IS_ERR(evm_xattrs)) + if (IS_ERR(evm_xattrs)) return -EFAULT; return 0; @@ -301,21 +300,22 @@ static int evm_init_xattrs(void) int __init evm_init_secfs(void) { int error = 0; + struct dentry *dentry; evm_dir = securityfs_create_dir("evm", integrity_dir); - if (!evm_dir || IS_ERR(evm_dir)) + if (IS_ERR(evm_dir)) return -EFAULT; - evm_init_tpm = securityfs_create_file("evm", 0660, - evm_dir, NULL, &evm_key_ops); - if (!evm_init_tpm || IS_ERR(evm_init_tpm)) { + dentry = securityfs_create_file("evm", 0660, + evm_dir, NULL, &evm_key_ops); + if (IS_ERR(dentry)) { error = -EFAULT; goto out; } evm_symlink = securityfs_create_symlink("evm", NULL, "integrity/evm/evm", NULL); - if (!evm_symlink || IS_ERR(evm_symlink)) { + if (IS_ERR(evm_symlink)) { error = -EFAULT; goto out; } @@ -328,7 +328,6 @@ int __init evm_init_secfs(void) return 0; out: securityfs_remove(evm_symlink); - securityfs_remove(evm_init_tpm); securityfs_remove(evm_dir); return error; } diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index e4a79a9b2d58..87045b09f120 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -116,28 +116,6 @@ void ima_putc(struct seq_file *m, void *data, int datalen) seq_putc(m, *(char *)data++); } -static struct dentry **ascii_securityfs_measurement_lists __ro_after_init; -static struct dentry **binary_securityfs_measurement_lists __ro_after_init; -static int securityfs_measurement_list_count __ro_after_init; - -static void lookup_template_data_hash_algo(int *algo_idx, enum hash_algo *algo, - struct seq_file *m, - struct dentry **lists) -{ - struct dentry *dentry; - int i; - - dentry = file_dentry(m->file); - - for (i = 0; i < securityfs_measurement_list_count; i++) { - if (dentry == lists[i]) { - *algo_idx = i; - *algo = ima_algo_array[i].algo; - break; - } - } -} - /* print format: * 32bit-le=pcr# * char[n]=template digest @@ -160,9 +138,10 @@ int ima_measurements_show(struct seq_file *m, void *v) algo_idx = ima_sha1_idx; algo = HASH_ALGO_SHA1; - if (m->file != NULL) - lookup_template_data_hash_algo(&algo_idx, &algo, m, - binary_securityfs_measurement_lists); + if (m->file != NULL) { + algo_idx = (unsigned long)file_inode(m->file)->i_private; + algo = ima_algo_array[algo_idx].algo; + } /* get entry */ e = qe->entry; @@ -256,9 +235,10 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) algo_idx = ima_sha1_idx; algo = HASH_ALGO_SHA1; - if (m->file != NULL) - lookup_template_data_hash_algo(&algo_idx, &algo, m, - ascii_securityfs_measurement_lists); + if (m->file != NULL) { + algo_idx = (unsigned long)file_inode(m->file)->i_private; + algo = ima_algo_array[algo_idx].algo; + } /* get entry */ e = qe->entry; @@ -396,11 +376,6 @@ out: static struct dentry *ima_dir; static struct dentry *ima_symlink; -static struct dentry *binary_runtime_measurements; -static struct dentry *ascii_runtime_measurements; -static struct dentry *runtime_measurements_count; -static struct dentry *violations; -static struct dentry *ima_policy; enum ima_fs_flags { IMA_FS_BUSY, @@ -417,64 +392,33 @@ static const struct seq_operations ima_policy_seqops = { }; #endif -static void __init remove_securityfs_measurement_lists(struct dentry **lists) -{ - int i; - - if (lists) { - for (i = 0; i < securityfs_measurement_list_count; i++) - securityfs_remove(lists[i]); - - kfree(lists); - } -} - static int __init create_securityfs_measurement_lists(void) { - char file_name[NAME_MAX + 1]; - struct dentry *dentry; - u16 algo; - int i; - - securityfs_measurement_list_count = NR_BANKS(ima_tpm_chip); + int count = NR_BANKS(ima_tpm_chip); if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip)) - securityfs_measurement_list_count++; + count++; - ascii_securityfs_measurement_lists = - kcalloc(securityfs_measurement_list_count, sizeof(struct dentry *), - GFP_KERNEL); - if (!ascii_securityfs_measurement_lists) - return -ENOMEM; - - binary_securityfs_measurement_lists = - kcalloc(securityfs_measurement_list_count, sizeof(struct dentry *), - GFP_KERNEL); - if (!binary_securityfs_measurement_lists) - return -ENOMEM; - - for (i = 0; i < securityfs_measurement_list_count; i++) { - algo = ima_algo_array[i].algo; + for (int i = 0; i < count; i++) { + u16 algo = ima_algo_array[i].algo; + char file_name[NAME_MAX + 1]; + struct dentry *dentry; sprintf(file_name, "ascii_runtime_measurements_%s", hash_algo_name[algo]); dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, - ima_dir, NULL, + ima_dir, (void *)(uintptr_t)i, &ima_ascii_measurements_ops); if (IS_ERR(dentry)) return PTR_ERR(dentry); - ascii_securityfs_measurement_lists[i] = dentry; - sprintf(file_name, "binary_runtime_measurements_%s", hash_algo_name[algo]); dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP, - ima_dir, NULL, + ima_dir, (void *)(uintptr_t)i, &ima_measurements_ops); if (IS_ERR(dentry)) return PTR_ERR(dentry); - - binary_securityfs_measurement_lists[i] = dentry; } return 0; @@ -533,8 +477,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) ima_update_policy(); #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY) - securityfs_remove(ima_policy); - ima_policy = NULL; + securityfs_remove(file->f_path.dentry); #elif defined(CONFIG_IMA_WRITE_POLICY) clear_bit(IMA_FS_BUSY, &ima_fs_flags); #elif defined(CONFIG_IMA_READ_POLICY) @@ -553,11 +496,9 @@ static const struct file_operations ima_measure_policy_ops = { int __init ima_fs_init(void) { + struct dentry *dentry; int ret; - ascii_securityfs_measurement_lists = NULL; - binary_securityfs_measurement_lists = NULL; - ima_dir = securityfs_create_dir("ima", integrity_dir); if (IS_ERR(ima_dir)) return PTR_ERR(ima_dir); @@ -573,57 +514,45 @@ int __init ima_fs_init(void) if (ret != 0) goto out; - binary_runtime_measurements = - securityfs_create_symlink("binary_runtime_measurements", ima_dir, + dentry = securityfs_create_symlink("binary_runtime_measurements", ima_dir, "binary_runtime_measurements_sha1", NULL); - if (IS_ERR(binary_runtime_measurements)) { - ret = PTR_ERR(binary_runtime_measurements); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); goto out; } - ascii_runtime_measurements = - securityfs_create_symlink("ascii_runtime_measurements", ima_dir, + dentry = securityfs_create_symlink("ascii_runtime_measurements", ima_dir, "ascii_runtime_measurements_sha1", NULL); - if (IS_ERR(ascii_runtime_measurements)) { - ret = PTR_ERR(ascii_runtime_measurements); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); goto out; } - runtime_measurements_count = - securityfs_create_file("runtime_measurements_count", + dentry = securityfs_create_file("runtime_measurements_count", S_IRUSR | S_IRGRP, ima_dir, NULL, &ima_measurements_count_ops); - if (IS_ERR(runtime_measurements_count)) { - ret = PTR_ERR(runtime_measurements_count); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); goto out; } - violations = - securityfs_create_file("violations", S_IRUSR | S_IRGRP, + dentry = securityfs_create_file("violations", S_IRUSR | S_IRGRP, ima_dir, NULL, &ima_htable_violations_ops); - if (IS_ERR(violations)) { - ret = PTR_ERR(violations); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); goto out; } - ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS, + dentry = securityfs_create_file("policy", POLICY_FILE_FLAGS, ima_dir, NULL, &ima_measure_policy_ops); - if (IS_ERR(ima_policy)) { - ret = PTR_ERR(ima_policy); + if (IS_ERR(dentry)) { + ret = PTR_ERR(dentry); goto out; } return 0; out: - securityfs_remove(ima_policy); - securityfs_remove(violations); - securityfs_remove(runtime_measurements_count); - securityfs_remove(ascii_runtime_measurements); - securityfs_remove(binary_runtime_measurements); - remove_securityfs_measurement_lists(ascii_securityfs_measurement_lists); - remove_securityfs_measurement_lists(binary_securityfs_measurement_lists); - securityfs_measurement_list_count = 0; securityfs_remove(ima_symlink); securityfs_remove(ima_dir); diff --git a/security/ipe/fs.c b/security/ipe/fs.c index f40e50bfd2e7..0bb9468b8026 100644 --- a/security/ipe/fs.c +++ b/security/ipe/fs.c @@ -12,11 +12,8 @@ #include "policy.h" #include "audit.h" -static struct dentry *np __ro_after_init; static struct dentry *root __ro_after_init; struct dentry *policy_root __ro_after_init; -static struct dentry *audit_node __ro_after_init; -static struct dentry *enforce_node __ro_after_init; /** * setaudit() - Write handler for the securityfs node, "ipe/success_audit" @@ -200,27 +197,26 @@ static int __init ipe_init_securityfs(void) { int rc = 0; struct ipe_policy *ap; + struct dentry *dentry; if (!ipe_enabled) return -EOPNOTSUPP; root = securityfs_create_dir("ipe", NULL); - if (IS_ERR(root)) { - rc = PTR_ERR(root); - goto err; - } + if (IS_ERR(root)) + return PTR_ERR(root); - audit_node = securityfs_create_file("success_audit", 0600, root, + dentry = securityfs_create_file("success_audit", 0600, root, NULL, &audit_fops); - if (IS_ERR(audit_node)) { - rc = PTR_ERR(audit_node); + if (IS_ERR(dentry)) { + rc = PTR_ERR(dentry); goto err; } - enforce_node = securityfs_create_file("enforce", 0600, root, NULL, + dentry = securityfs_create_file("enforce", 0600, root, NULL, &enforce_fops); - if (IS_ERR(enforce_node)) { - rc = PTR_ERR(enforce_node); + if (IS_ERR(dentry)) { + rc = PTR_ERR(dentry); goto err; } @@ -237,18 +233,14 @@ static int __init ipe_init_securityfs(void) goto err; } - np = securityfs_create_file("new_policy", 0200, root, NULL, &np_fops); - if (IS_ERR(np)) { - rc = PTR_ERR(np); + dentry = securityfs_create_file("new_policy", 0200, root, NULL, &np_fops); + if (IS_ERR(dentry)) { + rc = PTR_ERR(dentry); goto err; } return 0; err: - securityfs_remove(np); - securityfs_remove(policy_root); - securityfs_remove(enforce_node); - securityfs_remove(audit_node); securityfs_remove(root); return rc; } diff --git a/security/ipe/policy_fs.c b/security/ipe/policy_fs.c index db26032ccbe1..9d92d8a14b13 100644 --- a/security/ipe/policy_fs.c +++ b/security/ipe/policy_fs.c @@ -438,7 +438,7 @@ static const struct ipefs_file policy_subdir[] = { */ void ipe_del_policyfs_node(struct ipe_policy *p) { - securityfs_recursive_remove(p->policyfs); + securityfs_remove(p->policyfs); p->policyfs = NULL; } @@ -485,6 +485,6 @@ int ipe_new_policyfs_node(struct ipe_policy *p) return 0; err: - securityfs_recursive_remove(policyfs); + securityfs_remove(policyfs); return rc; } diff --git a/security/landlock/fs.c b/security/landlock/fs.c index 6fee7c20f64d..c04f8879ad03 100644 --- a/security/landlock/fs.c +++ b/security/landlock/fs.c @@ -895,6 +895,7 @@ static bool is_access_to_paths_allowed( /* Stops when a rule from each layer grants access. */ if (allowed_parent1 && allowed_parent2) break; + jump_up: if (walker_path.dentry == walker_path.mnt->mnt_root) { if (follow_up(&walker_path)) { diff --git a/security/landlock/id.c b/security/landlock/id.c index 56f7cc0fc744..838c3ed7bb82 100644 --- a/security/landlock/id.c +++ b/security/landlock/id.c @@ -119,6 +119,12 @@ static u64 get_id_range(size_t number_of_ids, atomic64_t *const counter, #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST +static u8 get_random_u8_positive(void) +{ + /* max() evaluates its arguments once. */ + return max(1, get_random_u8()); +} + static void test_range1_rand0(struct kunit *const test) { atomic64_t counter; @@ -127,9 +133,10 @@ static void test_range1_rand0(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(1, &counter, 0), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 1); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 1); } static void test_range1_rand1(struct kunit *const test) @@ -140,9 +147,10 @@ static void test_range1_rand1(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(1, &counter, 1), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 2); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 2); } static void test_range1_rand15(struct kunit *const test) @@ -153,9 +161,10 @@ static void test_range1_rand15(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(1, &counter, 15), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 16); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 16); } static void test_range1_rand16(struct kunit *const test) @@ -166,9 +175,10 @@ static void test_range1_rand16(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(1, &counter, 16), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 1); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 1); } static void test_range2_rand0(struct kunit *const test) @@ -179,9 +189,10 @@ static void test_range2_rand0(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 0), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 2); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 2); } static void test_range2_rand1(struct kunit *const test) @@ -192,9 +203,10 @@ static void test_range2_rand1(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 1), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 3); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 3); } static void test_range2_rand2(struct kunit *const test) @@ -205,9 +217,10 @@ static void test_range2_rand2(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 2), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 4); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 4); } static void test_range2_rand15(struct kunit *const test) @@ -218,9 +231,10 @@ static void test_range2_rand15(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 15), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 17); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 17); } static void test_range2_rand16(struct kunit *const test) @@ -231,9 +245,10 @@ static void test_range2_rand16(struct kunit *const test) init = get_random_u32(); atomic64_set(&counter, init); KUNIT_EXPECT_EQ(test, get_id_range(2, &counter, 16), init); - KUNIT_EXPECT_EQ( - test, get_id_range(get_random_u8(), &counter, get_random_u8()), - init + 2); + KUNIT_EXPECT_EQ(test, + get_id_range(get_random_u8_positive(), &counter, + get_random_u8()), + init + 2); } #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */ diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c index 33eafb71e4f3..0116e9f93ffe 100644 --- a/security/landlock/syscalls.c +++ b/security/landlock/syscalls.c @@ -303,7 +303,6 @@ static int get_path_from_fd(const s32 fd, struct path *const path) if ((fd_file(f)->f_op == &ruleset_fops) || (fd_file(f)->f_path.mnt->mnt_flags & MNT_INTERNAL) || (fd_file(f)->f_path.dentry->d_sb->s_flags & SB_NOUSER) || - d_is_negative(fd_file(f)->f_path.dentry) || IS_PRIVATE(d_backing_inode(fd_file(f)->f_path.dentry))) return -EBADFD; diff --git a/security/security.c b/security/security.c index 596d41818577..ad163f06bf7a 100644 --- a/security/security.c +++ b/security/security.c @@ -2181,7 +2181,7 @@ int security_inode_symlink(struct inode *dir, struct dentry *dentry, } /** - * security_inode_mkdir() - Check if creation a new director is allowed + * security_inode_mkdir() - Check if creating a new directory is allowed * @dir: parent directory * @dentry: new directory * @mode: new directory mode @@ -2623,6 +2623,36 @@ void security_inode_post_removexattr(struct dentry *dentry, const char *name) } /** + * security_inode_file_setattr() - check if setting fsxattr is allowed + * @dentry: file to set filesystem extended attributes on + * @fa: extended attributes to set on the inode + * + * Called when file_setattr() syscall or FS_IOC_FSSETXATTR ioctl() is called on + * inode + * + * Return: Returns 0 if permission is granted. + */ +int security_inode_file_setattr(struct dentry *dentry, struct file_kattr *fa) +{ + return call_int_hook(inode_file_setattr, dentry, fa); +} + +/** + * security_inode_file_getattr() - check if retrieving fsxattr is allowed + * @dentry: file to retrieve filesystem extended attributes from + * @fa: extended attributes to get + * + * Called when file_getattr() syscall or FS_IOC_FSGETXATTR ioctl() is called on + * inode + * + * Return: Returns 0 if permission is granted. + */ +int security_inode_file_getattr(struct dentry *dentry, struct file_kattr *fa) +{ + return call_int_hook(inode_file_getattr, dentry, fa); +} + +/** * security_inode_need_killpriv() - Check if security_inode_killpriv() required * @dentry: associated dentry * diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 595ceb314aeb..c95a5874bf7d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3181,6 +3181,8 @@ static inline void task_avdcache_update(struct task_security_struct *tsec, tsec->avdcache.dir[spot].audited = audited; tsec->avdcache.dir[spot].allowed = avd->allowed; tsec->avdcache.dir[spot].permissive = avd->flags & AVD_FLAGS_PERMISSIVE; + tsec->avdcache.permissive_neveraudit = + (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT)); } /** @@ -3207,10 +3209,13 @@ static int selinux_inode_permission(struct inode *inode, int requested) if (!mask) return 0; + tsec = selinux_cred(current_cred()); + if (task_avdcache_permnoaudit(tsec)) + return 0; + isec = inode_security_rcu(inode, requested & MAY_NOT_BLOCK); if (IS_ERR(isec)) return PTR_ERR(isec); - tsec = selinux_cred(current_cred()); perms = file_mask_to_av(inode->i_mode, mask); rc = task_avdcache_search(tsec, isec, &avdc); @@ -3274,6 +3279,13 @@ static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, static int selinux_inode_getattr(const struct path *path) { + struct task_security_struct *tsec; + + tsec = selinux_cred(current_cred()); + + if (task_avdcache_permnoaudit(tsec)) + return 0; + return path_has_perm(current_cred(), path, FILE__GETATTR); } @@ -3480,6 +3492,18 @@ static int selinux_inode_removexattr(struct mnt_idmap *idmap, return -EACCES; } +static int selinux_inode_file_setattr(struct dentry *dentry, + struct file_kattr *fa) +{ + return dentry_has_perm(current_cred(), dentry, FILE__SETATTR); +} + +static int selinux_inode_file_getattr(struct dentry *dentry, + struct file_kattr *fa) +{ + return dentry_has_perm(current_cred(), dentry, FILE__GETATTR); +} + static int selinux_path_notify(const struct path *path, u64 mask, unsigned int obj_type) { @@ -7350,6 +7374,8 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr), LSM_HOOK_INIT(inode_listxattr, selinux_inode_listxattr), LSM_HOOK_INIT(inode_removexattr, selinux_inode_removexattr), + LSM_HOOK_INIT(inode_file_getattr, selinux_inode_file_getattr), + LSM_HOOK_INIT(inode_file_setattr, selinux_inode_file_setattr), LSM_HOOK_INIT(inode_set_acl, selinux_inode_set_acl), LSM_HOOK_INIT(inode_get_acl, selinux_inode_get_acl), LSM_HOOK_INIT(inode_remove_acl, selinux_inode_remove_acl), diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 281f40103663..01b5167fee1a 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -65,6 +65,10 @@ static inline u32 avc_audit_required(u32 requested, struct av_decision *avd, int result, u32 auditdeny, u32 *deniedp) { u32 denied, audited; + + if (avd->flags & AVD_FLAGS_NEVERAUDIT) + return 0; + denied = requested & ~avd->allowed; if (unlikely(denied)) { audited = denied & avd->auditdeny; diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 6ee7dc4dfd6e..1d7ac59015a1 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -49,9 +49,17 @@ struct task_security_struct { u32 seqno; /* AVC sequence number */ unsigned int dir_spot; /* dir cache index to check first */ struct avdc_entry dir[TSEC_AVDC_DIR_SIZE]; /* dir entries */ + bool permissive_neveraudit; /* permissive and neveraudit */ } avdcache; } __randomize_layout; +static inline bool task_avdcache_permnoaudit(struct task_security_struct *tsec) +{ + return (tsec->avdcache.permissive_neveraudit && + tsec->sid == tsec->avdcache.sid && + tsec->avdcache.seqno == avc_policy_seqno()); +} + enum label_initialized { LABEL_INVALID, /* invalid or not initialized */ LABEL_INITIALIZED, /* initialized */ diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 278c144c22d6..8201e6a3ac0f 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -47,10 +47,11 @@ #define POLICYDB_VERSION_GLBLUB 32 #define POLICYDB_VERSION_COMP_FTRANS 33 /* compressed filename transitions */ #define POLICYDB_VERSION_COND_XPERMS 34 /* extended permissions in conditional policies */ +#define POLICYDB_VERSION_NEVERAUDIT 35 /* neveraudit types */ /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_COND_XPERMS +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_NEVERAUDIT /* Mask for just the mount related flags */ #define SE_MNTMASK 0x0f @@ -260,6 +261,7 @@ struct extended_perms { /* definitions of av_decision.flags */ #define AVD_FLAGS_PERMISSIVE 0x0001 +#define AVD_FLAGS_NEVERAUDIT 0x0002 void security_compute_av(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd, diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index e67a8ce4b64c..9aa1d03ab612 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1072,6 +1072,7 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) pr_warn_ratelimited("SELinux: %s (%d) wrote to /sys/fs/selinux/user!" " This will not be supported in the future; please update your" " userspace.\n", current->comm, current->pid); + ssleep(5); length = avc_has_perm(current_sid(), SECINITSID_SECURITY, SECCLASS_SECURITY, SECURITY__COMPUTE_USER, @@ -2097,8 +2098,6 @@ err: pr_err("SELinux: %s: failed while creating inodes\n", __func__); - selinux_fs_info_free(sb); - return ret; } diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 383fd2d70878..1382eb3bfde1 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -40,7 +40,8 @@ int hashtab_init(struct hashtab *h, u32 nel_hint) h->htable = NULL; if (size) { - h->htable = kcalloc(size, sizeof(*h->htable), GFP_KERNEL); + h->htable = kcalloc(size, sizeof(*h->htable), + GFP_KERNEL | __GFP_NOWARN); if (!h->htable) return -ENOMEM; h->size = size; diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 9ea971943713..91df3db6a88c 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -160,6 +160,11 @@ static const struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_NEVERAUDIT, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static const struct policydb_compat_info * @@ -531,6 +536,7 @@ static void policydb_init(struct policydb *p) ebitmap_init(&p->filename_trans_ttypes); ebitmap_init(&p->policycaps); ebitmap_init(&p->permissive_map); + ebitmap_init(&p->neveraudit_map); } /* @@ -852,6 +858,7 @@ void policydb_destroy(struct policydb *p) ebitmap_destroy(&p->filename_trans_ttypes); ebitmap_destroy(&p->policycaps); ebitmap_destroy(&p->permissive_map); + ebitmap_destroy(&p->neveraudit_map); } /* @@ -2538,6 +2545,12 @@ int policydb_read(struct policydb *p, struct policy_file *fp) goto bad; } + if (p->policyvers >= POLICYDB_VERSION_NEVERAUDIT) { + rc = ebitmap_read(&p->neveraudit_map, fp); + if (rc) + goto bad; + } + rc = -EINVAL; info = policydb_lookup_compat(p->policyvers); if (!info) { @@ -3723,6 +3736,12 @@ int policydb_write(struct policydb *p, struct policy_file *fp) return rc; } + if (p->policyvers >= POLICYDB_VERSION_NEVERAUDIT) { + rc = ebitmap_write(&p->neveraudit_map, fp); + if (rc) + return rc; + } + num_syms = info->sym_num; for (i = 0; i < num_syms; i++) { struct policy_data pd; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 25650224b6e7..89a180b1742f 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -300,6 +300,8 @@ struct policydb { struct ebitmap permissive_map; + struct ebitmap neveraudit_map; + /* length of this policy when it was loaded */ size_t len; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index d185754c2786..713130bd43c4 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1153,6 +1153,14 @@ void security_compute_av(u32 ssid, if (ebitmap_get_bit(&policydb->permissive_map, scontext->type)) avd->flags |= AVD_FLAGS_PERMISSIVE; + /* neveraudit domain? */ + if (ebitmap_get_bit(&policydb->neveraudit_map, scontext->type)) + avd->flags |= AVD_FLAGS_NEVERAUDIT; + + /* both permissive and neveraudit => allow */ + if (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT)) + goto allow; + tcontext = sidtab_search(sidtab, tsid); if (!tcontext) { pr_err("SELinux: %s: unrecognized SID %d\n", @@ -1172,6 +1180,8 @@ void security_compute_av(u32 ssid, policydb->allow_unknown); out: rcu_read_unlock(); + if (avd->flags & AVD_FLAGS_NEVERAUDIT) + avd->auditallow = avd->auditdeny = 0; return; allow: avd->allowed = 0xffffffff; @@ -1208,6 +1218,14 @@ void security_compute_av_user(u32 ssid, if (ebitmap_get_bit(&policydb->permissive_map, scontext->type)) avd->flags |= AVD_FLAGS_PERMISSIVE; + /* neveraudit domain? */ + if (ebitmap_get_bit(&policydb->neveraudit_map, scontext->type)) + avd->flags |= AVD_FLAGS_NEVERAUDIT; + + /* both permissive and neveraudit => allow */ + if (avd->flags == (AVD_FLAGS_PERMISSIVE|AVD_FLAGS_NEVERAUDIT)) + goto allow; + tcontext = sidtab_search(sidtab, tsid); if (!tcontext) { pr_err("SELinux: %s: unrecognized SID %d\n", @@ -1225,6 +1243,8 @@ void security_compute_av_user(u32 ssid, NULL); out: rcu_read_unlock(); + if (avd->flags & AVD_FLAGS_NEVERAUDIT) + avd->auditallow = avd->auditdeny = 0; return; allow: avd->allowed = 0xffffffff; |