summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig50
-rw-r--r--security/Kconfig.hardening5
-rw-r--r--security/Makefile3
-rw-r--r--security/apparmor/apparmorfs.c7
-rw-r--r--security/apparmor/audit.c10
-rw-r--r--security/apparmor/capability.c19
-rw-r--r--security/apparmor/domain.c67
-rw-r--r--security/apparmor/file.c13
-rw-r--r--security/apparmor/include/audit.h4
-rw-r--r--security/apparmor/include/cred.h20
-rw-r--r--security/apparmor/include/label.h28
-rw-r--r--security/apparmor/include/lib.h1
-rw-r--r--security/apparmor/include/match.h8
-rw-r--r--security/apparmor/include/net.h3
-rw-r--r--security/apparmor/include/perms.h3
-rw-r--r--security/apparmor/include/policy.h1
-rw-r--r--security/apparmor/include/secid.h6
-rw-r--r--security/apparmor/label.c33
-rw-r--r--security/apparmor/lib.c84
-rw-r--r--security/apparmor/lsm.c57
-rw-r--r--security/apparmor/match.c99
-rw-r--r--security/apparmor/mount.c2
-rw-r--r--security/apparmor/net.c2
-rw-r--r--security/apparmor/path.c2
-rw-r--r--security/apparmor/policy.c11
-rw-r--r--security/apparmor/policy_unpack.c50
-rw-r--r--security/apparmor/policy_unpack_test.c15
-rw-r--r--security/apparmor/secid.c55
-rw-r--r--security/bpf/hooks.c2
-rw-r--r--security/commoncap.c101
-rw-r--r--security/inode.c27
-rw-r--r--security/integrity/Makefile1
-rw-r--r--security/integrity/digsig_asymmetric.c26
-rw-r--r--security/integrity/evm/Kconfig1
-rw-r--r--security/integrity/evm/evm.h27
-rw-r--r--security/integrity/evm/evm_crypto.c31
-rw-r--r--security/integrity/evm/evm_main.c288
-rw-r--r--security/integrity/iint.c197
-rw-r--r--security/integrity/ima/Kconfig1
-rw-r--r--security/integrity/ima/Makefile2
-rw-r--r--security/integrity/ima/ima.h168
-rw-r--r--security/integrity/ima/ima_api.c61
-rw-r--r--security/integrity/ima/ima_appraise.c103
-rw-r--r--security/integrity/ima/ima_crypto.c7
-rw-r--r--security/integrity/ima/ima_fs.c133
-rw-r--r--security/integrity/ima/ima_iint.c138
-rw-r--r--security/integrity/ima/ima_init.c10
-rw-r--r--security/integrity/ima/ima_kexec.c4
-rw-r--r--security/integrity/ima/ima_main.c303
-rw-r--r--security/integrity/ima/ima_policy.c43
-rw-r--r--security/integrity/ima/ima_queue.c44
-rw-r--r--security/integrity/ima/ima_template_lib.c41
-rw-r--r--security/integrity/integrity.h96
-rw-r--r--security/ipe/.gitignore2
-rw-r--r--security/ipe/Kconfig116
-rw-r--r--security/ipe/Makefile31
-rw-r--r--security/ipe/audit.c292
-rw-r--r--security/ipe/audit.h19
-rw-r--r--security/ipe/digest.c118
-rw-r--r--security/ipe/digest.h26
-rw-r--r--security/ipe/eval.c393
-rw-r--r--security/ipe/eval.h70
-rw-r--r--security/ipe/fs.c247
-rw-r--r--security/ipe/fs.h16
-rw-r--r--security/ipe/hooks.c314
-rw-r--r--security/ipe/hooks.h52
-rw-r--r--security/ipe/ipe.c98
-rw-r--r--security/ipe/ipe.h26
-rw-r--r--security/ipe/policy.c239
-rw-r--r--security/ipe/policy.h98
-rw-r--r--security/ipe/policy_fs.c472
-rw-r--r--security/ipe/policy_parser.c559
-rw-r--r--security/ipe/policy_parser.h11
-rw-r--r--security/ipe/policy_tests.c297
-rw-r--r--security/keys/encrypted-keys/encrypted.c1
-rw-r--r--security/keys/gc.c12
-rw-r--r--security/keys/key.c47
-rw-r--r--security/keys/keyctl.c13
-rw-r--r--security/keys/keyring.c7
-rw-r--r--security/keys/sysctl.c3
-rw-r--r--security/keys/trusted-keys/Kconfig18
-rw-r--r--security/keys/trusted-keys/Makefile2
-rw-r--r--security/keys/trusted-keys/trusted_core.c7
-rw-r--r--security/keys/trusted-keys/trusted_dcp.c356
-rw-r--r--security/keys/trusted-keys/trusted_tpm1.c23
-rw-r--r--security/keys/trusted-keys/trusted_tpm2.c163
-rw-r--r--security/landlock/.kunitconfig4
-rw-r--r--security/landlock/Kconfig15
-rw-r--r--security/landlock/Makefile2
-rw-r--r--security/landlock/access.h77
-rw-r--r--security/landlock/common.h2
-rw-r--r--security/landlock/cred.c11
-rw-r--r--security/landlock/cred.h2
-rw-r--r--security/landlock/fs.c647
-rw-r--r--security/landlock/fs.h8
-rw-r--r--security/landlock/limits.h7
-rw-r--r--security/landlock/net.c38
-rw-r--r--security/landlock/ptrace.c120
-rw-r--r--security/landlock/ruleset.c39
-rw-r--r--security/landlock/ruleset.h132
-rw-r--r--security/landlock/setup.c4
-rw-r--r--security/landlock/syscalls.c150
-rw-r--r--security/landlock/task.c325
-rw-r--r--security/landlock/task.h (renamed from security/landlock/ptrace.h)8
-rw-r--r--security/loadpin/Kconfig3
-rw-r--r--security/loadpin/loadpin.c11
-rw-r--r--security/lockdown/lockdown.c4
-rw-r--r--security/lsm_audit.c13
-rw-r--r--security/lsm_syscalls.c10
-rw-r--r--security/min_addr.c2
-rw-r--r--security/safesetid/securityfs.c3
-rw-r--r--security/security.c1698
-rw-r--r--security/selinux/.gitignore1
-rw-r--r--security/selinux/Makefile14
-rw-r--r--security/selinux/avc.c89
-rw-r--r--security/selinux/genheaders.c154
-rw-r--r--security/selinux/hooks.c535
-rw-r--r--security/selinux/include/audit.h51
-rw-r--r--security/selinux/include/avc.h5
-rw-r--r--security/selinux/include/classmap.h21
-rw-r--r--security/selinux/include/conditional.h2
-rw-r--r--security/selinux/include/initial_sid_to_string.h4
-rw-r--r--security/selinux/include/objsec.h28
-rw-r--r--security/selinux/include/policycap.h1
-rw-r--r--security/selinux/include/policycap_names.h1
-rw-r--r--security/selinux/include/security.h16
-rw-r--r--security/selinux/netlabel.c48
-rw-r--r--security/selinux/nlmsgtab.c297
-rw-r--r--security/selinux/selinuxfs.c58
-rw-r--r--security/selinux/ss/avtab.c121
-rw-r--r--security/selinux/ss/avtab.h82
-rw-r--r--security/selinux/ss/conditional.c100
-rw-r--r--security/selinux/ss/conditional.h31
-rw-r--r--security/selinux/ss/constraint.h67
-rw-r--r--security/selinux/ss/context.c4
-rw-r--r--security/selinux/ss/context.h45
-rw-r--r--security/selinux/ss/ebitmap.c116
-rw-r--r--security/selinux/ss/ebitmap.h87
-rw-r--r--security/selinux/ss/hashtab.c29
-rw-r--r--security/selinux/ss/hashtab.h37
-rw-r--r--security/selinux/ss/mls.c89
-rw-r--r--security/selinux/ss/mls.h58
-rw-r--r--security/selinux/ss/mls_types.h32
-rw-r--r--security/selinux/ss/policydb.c546
-rw-r--r--security/selinux/ss/policydb.h210
-rw-r--r--security/selinux/ss/services.c186
-rw-r--r--security/selinux/ss/services.h3
-rw-r--r--security/selinux/ss/sidtab.c75
-rw-r--r--security/selinux/ss/sidtab.h36
-rw-r--r--security/selinux/ss/symtab.c22
-rw-r--r--security/selinux/ss/symtab.h9
-rw-r--r--security/selinux/xfrm.c7
-rw-r--r--security/smack/smack.h13
-rw-r--r--security/smack/smack_access.c10
-rw-r--r--security/smack/smack_lsm.c435
-rw-r--r--security/smack/smack_netfilter.c8
-rw-r--r--security/smack/smackfs.c32
-rw-r--r--security/tomoyo/Kconfig2
-rw-r--r--security/tomoyo/Makefile2
-rw-r--r--security/tomoyo/common.c119
-rw-r--r--security/tomoyo/domain.c20
-rw-r--r--security/tomoyo/securityfs_if.c6
-rw-r--r--security/tomoyo/tomoyo.c5
-rw-r--r--security/yama/yama_lsm.c10
164 files changed, 10104 insertions, 3871 deletions
diff --git a/security/Kconfig b/security/Kconfig
index 52c9af08ad35..f10dbf15c294 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -19,6 +19,38 @@ config SECURITY_DMESG_RESTRICT
If you are unsure how to answer this question, answer N.
+choice
+ prompt "Allow /proc/pid/mem access override"
+ default PROC_MEM_ALWAYS_FORCE
+ help
+ Traditionally /proc/pid/mem allows users to override memory
+ permissions for users like ptrace, assuming they have ptrace
+ capability.
+
+ This allows people to limit that - either never override, or
+ require actual active ptrace attachment.
+
+ Defaults to the traditional behavior (for now)
+
+config PROC_MEM_ALWAYS_FORCE
+ bool "Traditional /proc/pid/mem behavior"
+ help
+ This allows /proc/pid/mem accesses to override memory mapping
+ permissions if you have ptrace access rights.
+
+config PROC_MEM_FORCE_PTRACE
+ bool "Require active ptrace() use for access override"
+ help
+ This allows /proc/pid/mem accesses to override memory mapping
+ permissions for active ptracers like gdb.
+
+config PROC_MEM_NO_FORCE
+ bool "Never"
+ help
+ Never override memory mapping permissions
+
+endchoice
+
config SECURITY
bool "Enable different security models"
depends on SYSFS
@@ -32,6 +64,11 @@ config SECURITY
If you are unsure how to answer this question, answer N.
+config HAS_SECURITY_AUDIT
+ def_bool y
+ depends on AUDIT
+ depends on SECURITY
+
config SECURITYFS
bool "Enable the securityfs filesystem"
help
@@ -142,8 +179,6 @@ config HARDENED_USERCOPY
config FORTIFY_SOURCE
bool "Harden common str/mem functions against buffer overflows"
depends on ARCH_HAS_FORTIFY_SOURCE
- # https://bugs.llvm.org/show_bug.cgi?id=41459
- depends on !CC_IS_CLANG || CLANG_VERSION >= 120001
# https://github.com/llvm/llvm-project/issues/53645
depends on !CC_IS_CLANG || !X86_32
help
@@ -194,6 +229,7 @@ source "security/yama/Kconfig"
source "security/safesetid/Kconfig"
source "security/lockdown/Kconfig"
source "security/landlock/Kconfig"
+source "security/ipe/Kconfig"
source "security/integrity/Kconfig"
@@ -233,11 +269,11 @@ endchoice
config LSM
string "Ordered list of enabled LSMs"
- default "landlock,lockdown,yama,loadpin,safesetid,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
- default "landlock,lockdown,yama,loadpin,safesetid,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
- default "landlock,lockdown,yama,loadpin,safesetid,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
- default "landlock,lockdown,yama,loadpin,safesetid,bpf" if DEFAULT_SECURITY_DAC
- default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,bpf"
+ default "landlock,lockdown,yama,loadpin,safesetid,smack,selinux,tomoyo,apparmor,ipe,bpf" if DEFAULT_SECURITY_SMACK
+ default "landlock,lockdown,yama,loadpin,safesetid,apparmor,selinux,smack,tomoyo,ipe,bpf" if DEFAULT_SECURITY_APPARMOR
+ default "landlock,lockdown,yama,loadpin,safesetid,tomoyo,ipe,bpf" if DEFAULT_SECURITY_TOMOYO
+ default "landlock,lockdown,yama,loadpin,safesetid,ipe,bpf" if DEFAULT_SECURITY_DAC
+ default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,bpf"
help
A comma-separated list of LSMs, in initialization order.
Any LSMs left off this list, except for those with order
diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening
index 2cff851ebfd7..b56e001e0c6a 100644
--- a/security/Kconfig.hardening
+++ b/security/Kconfig.hardening
@@ -127,6 +127,7 @@ choice
repeating for all types and padding except float and double
which use 0xFF repeating (-NaN). Clang on 32-bit uses 0xFF
repeating for all types and padding.
+ GCC uses 0xFE repeating for all types, and zero for padding.
config INIT_STACK_ALL_ZERO
bool "zero-init everything (strongest and safest)"
@@ -340,7 +341,7 @@ choice
config RANDSTRUCT_FULL
bool "Fully randomize structure layout"
depends on CC_HAS_RANDSTRUCT || GCC_PLUGINS
- select MODVERSIONS if MODULES
+ select MODVERSIONS if MODULES && !COMPILE_TEST
help
Fully randomize the member layout of sensitive
structures as much as possible, which may have both a
@@ -356,7 +357,7 @@ choice
config RANDSTRUCT_PERFORMANCE
bool "Limit randomization of structure layout to cache-lines"
depends on GCC_PLUGINS
- select MODVERSIONS if MODULES
+ select MODVERSIONS if MODULES && !COMPILE_TEST
help
Randomization of sensitive kernel structures will make a
best effort at restricting randomization to cacheline-sized
diff --git a/security/Makefile b/security/Makefile
index 59f238490665..22ff4c8bd8ce 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -15,7 +15,7 @@ obj-$(CONFIG_SECURITY) += security.o
obj-$(CONFIG_SECURITYFS) += inode.o
obj-$(CONFIG_SECURITY_SELINUX) += selinux/
obj-$(CONFIG_SECURITY_SMACK) += smack/
-obj-$(CONFIG_SECURITY) += lsm_audit.o
+obj-$(CONFIG_HAS_SECURITY_AUDIT) += lsm_audit.o
obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/
obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
obj-$(CONFIG_SECURITY_YAMA) += yama/
@@ -25,6 +25,7 @@ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/
obj-$(CONFIG_CGROUPS) += device_cgroup.o
obj-$(CONFIG_BPF_LSM) += bpf/
obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/
+obj-$(CONFIG_SECURITY_IPE) += ipe/
# Object integrity file lists
obj-$(CONFIG_INTEGRITY) += integrity/
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index bcfea073e3f2..c07d150685d7 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -1692,6 +1692,10 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
struct aa_profile *p;
p = aa_deref_parent(profile);
dent = prof_dir(p);
+ if (!dent) {
+ error = -ENOENT;
+ goto fail2;
+ }
/* adding to parent that previously didn't have children */
dent = aafs_create_dir("profiles", dent);
if (IS_ERR(dent))
@@ -2362,6 +2366,7 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = {
AA_SFS_FILE_U64("outofband", MAX_OOB_SUPPORTED),
AA_SFS_FILE_U64("permstable32_version", 1),
AA_SFS_FILE_STRING("permstable32", PERMS32STR),
+ AA_SFS_FILE_U64("state32", 1),
AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined),
{ }
};
@@ -2607,7 +2612,7 @@ static int policy_readlink(struct dentry *dentry, char __user *buffer,
res = snprintf(name, sizeof(name), "%s:[%lu]", AAFS_NAME,
d_inode(dentry)->i_ino);
if (res > 0 && res < sizeof(name))
- res = readlink_copy(buffer, buflen, name);
+ res = readlink_copy(buffer, buflen, name, strlen(name));
else
res = -ENOENT;
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index 45beb1c5f747..73087d76f649 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -217,7 +217,7 @@ void aa_audit_rule_free(void *vrule)
}
}
-int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
+int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule, gfp_t gfp)
{
struct aa_audit_rule *rule;
@@ -230,14 +230,14 @@ int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
return -EINVAL;
}
- rule = kzalloc(sizeof(struct aa_audit_rule), GFP_KERNEL);
+ rule = kzalloc(sizeof(struct aa_audit_rule), gfp);
if (!rule)
return -ENOMEM;
/* Currently rules are treated as coming from the root ns */
rule->label = aa_label_parse(&root_ns->unconfined->label, rulestr,
- GFP_KERNEL, true, false);
+ gfp, true, false);
if (IS_ERR(rule->label)) {
int err = PTR_ERR(rule->label);
aa_audit_rule_free(rule);
@@ -264,13 +264,13 @@ int aa_audit_rule_known(struct audit_krule *rule)
return 0;
}
-int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule)
+int aa_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *vrule)
{
struct aa_audit_rule *rule = vrule;
struct aa_label *label;
int found = 0;
- label = aa_secid_to_label(sid);
+ label = prop->apparmor.label;
if (!label)
return -ENOENT;
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c
index 9934df16c843..7ca489ee1054 100644
--- a/security/apparmor/capability.c
+++ b/security/apparmor/capability.c
@@ -12,6 +12,7 @@
#include <linux/errno.h>
#include <linux/gfp.h>
#include <linux/security.h>
+#include <linux/timekeeping.h>
#include "include/apparmor.h"
#include "include/capability.h"
@@ -30,8 +31,9 @@ struct aa_sfs_entry aa_sfs_entry_caps[] = {
};
struct audit_cache {
- struct aa_profile *profile;
- kernel_cap_t caps;
+ const struct cred *ad_subj_cred;
+ /* Capabilities go from 0 to CAP_LAST_CAP */
+ u64 ktime_ns_expiration[CAP_LAST_CAP+1];
};
static DEFINE_PER_CPU(struct audit_cache, audit_cache);
@@ -64,6 +66,8 @@ static void audit_cb(struct audit_buffer *ab, void *va)
static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile,
int cap, int error)
{
+ const u64 AUDIT_CACHE_TIMEOUT_NS = 1000*1000*1000; /* 1 second */
+
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct audit_cache *ent;
@@ -89,15 +93,16 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile
/* Do simple duplicate message elimination */
ent = &get_cpu_var(audit_cache);
- if (profile == ent->profile && cap_raised(ent->caps, cap)) {
+ /* If the capability was never raised the timestamp check would also catch that */
+ if (ad->subj_cred == ent->ad_subj_cred && ktime_get_ns() <= ent->ktime_ns_expiration[cap]) {
put_cpu_var(audit_cache);
if (COMPLAIN_MODE(profile))
return complain_error(error);
return error;
} else {
- aa_put_profile(ent->profile);
- ent->profile = aa_get_profile(profile);
- cap_raise(ent->caps, cap);
+ put_cred(ent->ad_subj_cred);
+ ent->ad_subj_cred = get_cred(ad->subj_cred);
+ ent->ktime_ns_expiration[cap] = ktime_get_ns() + AUDIT_CACHE_TIMEOUT_NS;
}
put_cpu_var(audit_cache);
@@ -109,7 +114,7 @@ static int audit_caps(struct apparmor_audit_data *ad, struct aa_profile *profile
* @profile: profile being enforced (NOT NULL, NOT unconfined)
* @cap: capability to test if allowed
* @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated
- * @ad: audit data (MAY BE NULL indicating no auditing)
+ * @ad: audit data (NOT NULL)
*
* Returns: 0 if allowed else -EPERM
*/
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 571158ec6188..5939bd9a9b9b 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -9,7 +9,6 @@
*/
#include <linux/errno.h>
-#include <linux/fdtable.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
@@ -637,6 +636,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
struct aa_ruleset *rules = list_first_entry(&profile->rules,
typeof(*rules), list);
struct aa_label *new = NULL;
+ struct aa_profile *new_profile = NULL;
const char *info = NULL, *name = NULL, *target = NULL;
aa_state_t state = rules->file->start[AA_CLASS_FILE];
struct aa_perms perms = {};
@@ -681,15 +681,18 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
/* hack ix fallback - improve how this is detected */
goto audit;
} else if (!new) {
- error = -EACCES;
info = "profile transition not found";
- /* remove MAY_EXEC to audit as failure */
+ /* remove MAY_EXEC to audit as failure or complaint */
perms.allow &= ~MAY_EXEC;
+ if (COMPLAIN_MODE(profile)) {
+ /* create null profile instead of failing */
+ goto create_learning_profile;
+ }
+ error = -EACCES;
}
} else if (COMPLAIN_MODE(profile)) {
+create_learning_profile:
/* no exec permission - learning mode */
- struct aa_profile *new_profile = NULL;
-
new_profile = aa_new_learning_profile(profile, false, name,
GFP_KERNEL);
if (!new_profile) {
@@ -710,8 +713,8 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
if (!(perms.xindex & AA_X_UNSAFE)) {
if (DEBUG_ON) {
- dbg_printk("apparmor: scrubbing environment variables"
- " for %s profile=", name);
+ dbg_printk("apparmor: setting AT_SECURE for %s profile=",
+ name);
aa_label_printk(new, GFP_KERNEL);
dbg_printk("\n");
}
@@ -790,8 +793,8 @@ static int profile_onexec(const struct cred *subj_cred,
if (!(perms.xindex & AA_X_UNSAFE)) {
if (DEBUG_ON) {
- dbg_printk("apparmor: scrubbing environment "
- "variables for %s label=", xname);
+ dbg_printk("apparmor: setting AT_SECURE for %s label=",
+ xname);
aa_label_printk(onexec, GFP_KERNEL);
dbg_printk("\n");
}
@@ -822,33 +825,19 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
AA_BUG(!bprm);
AA_BUG(!buffer);
- if (!stack) {
- error = fn_for_each_in_ns(label, profile,
- profile_onexec(subj_cred, profile, onexec, stack,
- bprm, buffer, cond, unsafe));
- if (error)
- return ERR_PTR(error);
- new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
- aa_get_newest_label(onexec),
- profile_transition(subj_cred, profile, bprm,
- buffer,
- cond, unsafe));
-
- } else {
- /* TODO: determine how much we want to loosen this */
- error = fn_for_each_in_ns(label, profile,
- profile_onexec(subj_cred, profile, onexec, stack, bprm,
- buffer, cond, unsafe));
- if (error)
- return ERR_PTR(error);
- new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
- aa_label_merge(&profile->label, onexec,
- GFP_KERNEL),
- profile_transition(subj_cred, profile, bprm,
- buffer,
- cond, unsafe));
- }
+ /* TODO: determine how much we want to loosen this */
+ error = fn_for_each_in_ns(label, profile,
+ profile_onexec(subj_cred, profile, onexec, stack,
+ bprm, buffer, cond, unsafe));
+ if (error)
+ return ERR_PTR(error);
+ new = fn_label_build_in_ns(label, profile, GFP_KERNEL,
+ stack ? aa_label_merge(&profile->label, onexec,
+ GFP_KERNEL)
+ : aa_get_newest_label(onexec),
+ profile_transition(subj_cred, profile, bprm,
+ buffer, cond, unsafe));
if (new)
return new;
@@ -961,8 +950,8 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
if (unsafe) {
if (DEBUG_ON) {
- dbg_printk("scrubbing environment variables for %s "
- "label=", bprm->filename);
+ dbg_printk("setting AT_SECURE for %s label=",
+ bprm->filename);
aa_label_printk(new, GFP_KERNEL);
dbg_printk("\n");
}
@@ -972,8 +961,8 @@ int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
if (label->proxy != new->proxy) {
/* when transitioning clear unsafe personality bits */
if (DEBUG_ON) {
- dbg_printk("apparmor: clearing unsafe personality "
- "bits. %s label=", bprm->filename);
+ dbg_printk("apparmor: clearing unsafe personality bits. %s label=",
+ bprm->filename);
aa_label_printk(new, GFP_KERNEL);
dbg_printk("\n");
}
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index c03eb7c19f16..d52a5b14dad4 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -144,19 +144,6 @@ int aa_audit_file(const struct cred *subj_cred,
return aa_audit(type, profile, &ad, file_audit_cb);
}
-/**
- * is_deleted - test if a file has been completely unlinked
- * @dentry: dentry of file to test for deletion (NOT NULL)
- *
- * Returns: true if deleted else false
- */
-static inline bool is_deleted(struct dentry *dentry)
-{
- if (d_unlinked(dentry) && d_backing_inode(dentry)->i_nlink == 0)
- return true;
- return false;
-}
-
static int path_name(const char *op, const struct cred *subj_cred,
struct aa_label *label,
const struct path *path, int flags, char *buffer,
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
index acbb03b9bd25..e27229349abb 100644
--- a/security/apparmor/include/audit.h
+++ b/security/apparmor/include/audit.h
@@ -200,8 +200,8 @@ static inline int complain_error(int error)
}
void aa_audit_rule_free(void *vrule);
-int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule);
+int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule, gfp_t gfp);
int aa_audit_rule_known(struct audit_krule *rule);
-int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule);
+int aa_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *vrule);
#endif /* __AA_AUDIT_H */
diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
index 58fdc72af664..7265d2f81dd5 100644
--- a/security/apparmor/include/cred.h
+++ b/security/apparmor/include/cred.h
@@ -63,6 +63,26 @@ static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred)
return aa_get_newest_label(aa_cred_raw_label(cred));
}
+static inline struct aa_label *aa_get_newest_cred_label_condref(const struct cred *cred,
+ bool *needput)
+{
+ struct aa_label *l = aa_cred_raw_label(cred);
+
+ if (unlikely(label_is_stale(l))) {
+ *needput = true;
+ return aa_get_newest_label(l);
+ }
+
+ *needput = false;
+ return l;
+}
+
+static inline void aa_put_label_condref(struct aa_label *l, bool needput)
+{
+ if (unlikely(needput))
+ aa_put_label(l);
+}
+
/**
* aa_current_raw_label - find the current tasks confining label
*
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
index 2a72e6b17d68..93290ae300bb 100644
--- a/security/apparmor/include/label.h
+++ b/security/apparmor/include/label.h
@@ -160,31 +160,7 @@ int aa_label_next_confined(struct aa_label *l, int i);
#define label_for_each_cont(I, L, P) \
for (++((I).i); ((P) = (L)->vec[(I).i]); ++((I).i))
-#define next_comb(I, L1, L2) \
-do { \
- (I).j++; \
- if ((I).j >= (L2)->size) { \
- (I).i++; \
- (I).j = 0; \
- } \
-} while (0)
-
-/* for each combination of P1 in L1, and P2 in L2 */
-#define label_for_each_comb(I, L1, L2, P1, P2) \
-for ((I).i = (I).j = 0; \
- ((P1) = (L1)->vec[(I).i]) && ((P2) = (L2)->vec[(I).j]); \
- (I) = next_comb(I, L1, L2))
-
-#define fn_for_each_comb(L1, L2, P1, P2, FN) \
-({ \
- struct label_it i; \
- int __E = 0; \
- label_for_each_comb(i, (L1), (L2), (P1), (P2)) { \
- last_error(__E, (FN)); \
- } \
- __E; \
-})
/* for each profile that is enforcing confinement in a label */
#define label_for_each_confined(I, L, P) \
@@ -291,8 +267,6 @@ bool aa_label_replace(struct aa_label *old, struct aa_label *new);
bool aa_label_make_newest(struct aa_labelset *ls, struct aa_label *old,
struct aa_label *new);
-struct aa_label *aa_label_find(struct aa_label *l);
-
struct aa_profile *aa_label_next_in_merge(struct label_it *I,
struct aa_label *a,
struct aa_label *b);
@@ -320,8 +294,6 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
struct aa_label *label, int flags, gfp_t gfp);
void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
gfp_t gfp);
-void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp);
-void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp);
void aa_label_printk(struct aa_label *label, gfp_t gfp);
struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str,
diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h
index d7a894b1031f..f11a0db7f51d 100644
--- a/security/apparmor/include/lib.h
+++ b/security/apparmor/include/lib.h
@@ -59,7 +59,6 @@ extern int apparmor_initialized;
/* fn's in lib */
const char *skipn_spaces(const char *str, size_t n);
-char *aa_split_fqname(char *args, char **ns_name);
const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
size_t *ns_len);
void aa_info_message(const char *str);
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h
index 4bb0405c9190..536ce3abd598 100644
--- a/security/apparmor/include/match.h
+++ b/security/apparmor/include/match.h
@@ -87,10 +87,12 @@ struct table_header {
char td_data[];
};
-#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF]->td_data))
+#define TABLE_DATAU16(TABLE) ((u16 *)((TABLE)->td_data))
+#define TABLE_DATAU32(TABLE) ((u32 *)((TABLE)->td_data))
+#define DEFAULT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_DEF]->td_data))
#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data))
-#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT]->td_data))
-#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK]->td_data))
+#define NEXT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_NXT]->td_data))
+#define CHECK_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_CHK]->td_data))
#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC]->td_data))
#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT]->td_data))
#define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2]->td_data))
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
index 67bf888c3bd6..c42ed8a73f1c 100644
--- a/security/apparmor/include/net.h
+++ b/security/apparmor/include/net.h
@@ -51,10 +51,9 @@ struct aa_sk_ctx {
struct aa_label *peer;
};
-#define SK_CTX(X) ((X)->sk_security)
static inline struct aa_sk_ctx *aa_sock(const struct sock *sk)
{
- return sk->sk_security;
+ return sk->sk_security + apparmor_blob_sizes.lbs_sock;
}
#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \
diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h
index 0f7e913c3fc2..bbaa7d39a39a 100644
--- a/security/apparmor/include/perms.h
+++ b/security/apparmor/include/perms.h
@@ -213,9 +213,6 @@ void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend);
void aa_profile_match_label(struct aa_profile *profile,
struct aa_ruleset *rules, struct aa_label *label,
int type, u32 request, struct aa_perms *perms);
-int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
- u32 request, int type, u32 *deny,
- struct apparmor_audit_data *ad);
int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
u32 request, struct apparmor_audit_data *ad,
void (*cb)(struct audit_buffer *, void *));
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index 75088cc310b6..757e3c232c57 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -264,7 +264,6 @@ void aa_free_profile(struct aa_profile *profile);
struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name);
struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
size_t n);
-struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name);
struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
const char *fqname, size_t n);
diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h
index a912a5d5d04f..6025d3849cf8 100644
--- a/security/apparmor/include/secid.h
+++ b/security/apparmor/include/secid.h
@@ -25,13 +25,13 @@ struct aa_label;
extern int apparmor_display_secid_mode;
struct aa_label *aa_secid_to_label(u32 secid);
-int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
+int apparmor_secid_to_secctx(u32 secid, struct lsm_context *cp);
+int apparmor_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp);
int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
-void apparmor_release_secctx(char *secdata, u32 seclen);
+void apparmor_release_secctx(struct lsm_context *cp);
int aa_alloc_secid(struct aa_label *label, gfp_t gfp);
void aa_free_secid(u32 secid);
-void aa_secid_update(u32 secid, struct aa_label *label);
#endif /* __AA_SECID_H */
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
index c71e4615dd46..91483ecacc16 100644
--- a/security/apparmor/label.c
+++ b/security/apparmor/label.c
@@ -899,23 +899,6 @@ struct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len,
return vec_create_and_insert_label(vec, len, gfp);
}
-/**
- * aa_label_find - find label @label in label set
- * @label: label to find (NOT NULL)
- *
- * Requires: caller to hold a valid ref on l
- *
- * Returns: refcounted @label if @label is in tree
- * refcounted label that is equiv to @label in tree
- * else NULL if @label or equiv is not in tree
- */
-struct aa_label *aa_label_find(struct aa_label *label)
-{
- AA_BUG(!label);
-
- return vec_find(label->vec, label->size);
-}
-
/**
* aa_label_insert - insert label @label into @ls or return existing label
@@ -1811,22 +1794,6 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
pr_info("%s", label->hname);
}
-void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp)
-{
- struct aa_ns *ns = aa_get_current_ns();
-
- aa_label_xaudit(ab, ns, label, FLAG_VIEW_SUBNS, gfp);
- aa_put_ns(ns);
-}
-
-void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp)
-{
- struct aa_ns *ns = aa_get_current_ns();
-
- aa_label_seq_xprint(f, ns, label, FLAG_VIEW_SUBNS, gfp);
- aa_put_ns(ns);
-}
-
void aa_label_printk(struct aa_label *label, gfp_t gfp)
{
struct aa_ns *ns = aa_get_current_ns();
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index cd569fbbfe36..7db62213e352 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -46,44 +46,6 @@ void aa_free_str_table(struct aa_str_table *t)
}
/**
- * aa_split_fqname - split a fqname into a profile and namespace name
- * @fqname: a full qualified name in namespace profile format (NOT NULL)
- * @ns_name: pointer to portion of the string containing the ns name (NOT NULL)
- *
- * Returns: profile name or NULL if one is not specified
- *
- * Split a namespace name from a profile name (see policy.c for naming
- * description). If a portion of the name is missing it returns NULL for
- * that portion.
- *
- * NOTE: may modify the @fqname string. The pointers returned point
- * into the @fqname string.
- */
-char *aa_split_fqname(char *fqname, char **ns_name)
-{
- char *name = strim(fqname);
-
- *ns_name = NULL;
- if (name[0] == ':') {
- char *split = strchr(&name[1], ':');
- *ns_name = skip_spaces(&name[1]);
- if (split) {
- /* overwrite ':' with \0 */
- *split++ = 0;
- if (strncmp(split, "//", 2) == 0)
- split += 2;
- name = skip_spaces(split);
- } else
- /* a ns name without a following profile is allowed */
- name = NULL;
- }
- if (name && *name == 0)
- name = NULL;
-
- return name;
-}
-
-/**
* skipn_spaces - Removes leading whitespace from @str.
* @str: The string to be stripped.
* @n: length of str to parse, will stop at \0 if encountered before n
@@ -276,33 +238,6 @@ void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
}
/**
- * aa_audit_perms_cb - generic callback fn for auditing perms
- * @ab: audit buffer (NOT NULL)
- * @va: audit struct to audit values of (NOT NULL)
- */
-static void aa_audit_perms_cb(struct audit_buffer *ab, void *va)
-{
- struct common_audit_data *sa = va;
- struct apparmor_audit_data *ad = aad(sa);
-
- if (ad->request) {
- audit_log_format(ab, " requested_mask=");
- aa_audit_perm_mask(ab, ad->request, aa_file_perm_chrs,
- PERMS_CHRS_MASK, aa_file_perm_names,
- PERMS_NAMES_MASK);
- }
- if (ad->denied) {
- audit_log_format(ab, "denied_mask=");
- aa_audit_perm_mask(ab, ad->denied, aa_file_perm_chrs,
- PERMS_CHRS_MASK, aa_file_perm_names,
- PERMS_NAMES_MASK);
- }
- audit_log_format(ab, " peer=");
- aa_label_xaudit(ab, labels_ns(ad->subj_label), ad->peer,
- FLAGS_NONE, GFP_ATOMIC);
-}
-
-/**
* aa_apply_modes_to_perms - apply namespace and profile flags to perms
* @profile: that perms where computed from
* @perms: perms to apply mode modifiers to
@@ -349,25 +284,6 @@ void aa_profile_match_label(struct aa_profile *profile,
}
-/* currently unused */
-int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target,
- u32 request, int type, u32 *deny,
- struct apparmor_audit_data *ad)
-{
- struct aa_ruleset *rules = list_first_entry(&profile->rules,
- typeof(*rules), list);
- struct aa_perms perms;
-
- ad->peer = &target->label;
- ad->request = request;
-
- aa_profile_match_label(profile, rules, &target->label, type, request,
- &perms);
- aa_apply_modes_to_perms(profile, &perms);
- *deny |= request & perms.deny;
- return aa_check_perms(profile, &perms, request, ad, aa_audit_perms_cb);
-}
-
/**
* aa_check_perms - do audit mode selection based on perms set
* @profile: profile being checked
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 9a3dcaafb5b1..9b6c2f157f83 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -461,6 +461,7 @@ static int apparmor_file_open(struct file *file)
struct aa_file_ctx *fctx = file_ctx(file);
struct aa_label *label;
int error = 0;
+ bool needput;
if (!path_mediated_fs(file->f_path.dentry))
return 0;
@@ -477,7 +478,7 @@ static int apparmor_file_open(struct file *file)
return 0;
}
- label = aa_get_newest_cred_label(file->f_cred);
+ label = aa_get_newest_cred_label_condref(file->f_cred, &needput);
if (!unconfined(label)) {
struct mnt_idmap *idmap = file_mnt_idmap(file);
struct inode *inode = file_inode(file);
@@ -494,7 +495,7 @@ static int apparmor_file_open(struct file *file)
/* todo cache full allowed permissions set and state */
fctx->allow = aa_map_file_to_perms(file);
}
- aa_put_label(label);
+ aa_put_label_condref(label, needput);
return error;
}
@@ -779,7 +780,7 @@ static int apparmor_sb_pivotroot(const struct path *old_path,
}
static int apparmor_getselfattr(unsigned int attr, struct lsm_ctx __user *lx,
- size_t *size, u32 flags)
+ u32 *size, u32 flags)
{
int error = -ENOENT;
struct aa_task_ctx *ctx = task_ctx(current);
@@ -924,7 +925,7 @@ fail:
}
static int apparmor_setselfattr(unsigned int attr, struct lsm_ctx *ctx,
- size_t size, u32 flags)
+ u32 size, u32 flags)
{
int rc;
@@ -981,17 +982,20 @@ static void apparmor_bprm_committed_creds(const struct linux_binprm *bprm)
return;
}
-static void apparmor_current_getsecid_subj(u32 *secid)
+static void apparmor_current_getlsmprop_subj(struct lsm_prop *prop)
{
struct aa_label *label = __begin_current_label_crit_section();
- *secid = label->secid;
+
+ prop->apparmor.label = label;
__end_current_label_crit_section(label);
}
-static void apparmor_task_getsecid_obj(struct task_struct *p, u32 *secid)
+static void apparmor_task_getlsmprop_obj(struct task_struct *p,
+ struct lsm_prop *prop)
{
struct aa_label *label = aa_get_task_label(p);
- *secid = label->secid;
+
+ prop->apparmor.label = label;
aa_put_label(label);
}
@@ -1057,27 +1061,12 @@ static int apparmor_userns_create(const struct cred *cred)
return error;
}
-static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags)
-{
- struct aa_sk_ctx *ctx;
-
- ctx = kzalloc(sizeof(*ctx), flags);
- if (!ctx)
- return -ENOMEM;
-
- sk->sk_security = ctx;
-
- return 0;
-}
-
static void apparmor_sk_free_security(struct sock *sk)
{
struct aa_sk_ctx *ctx = aa_sock(sk);
- sk->sk_security = NULL;
aa_put_label(ctx->label);
aa_put_label(ctx->peer);
- kfree(ctx);
}
/**
@@ -1124,7 +1113,7 @@ static int apparmor_socket_create(int family, int type, int protocol, int kern)
* @sock: socket that is being setup
* @family: family of socket being created
* @type: type of the socket
- * @ptotocol: protocol of the socket
+ * @protocol: protocol of the socket
* @kern: socket is a special kernel socket
*
* Note:
@@ -1304,6 +1293,13 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (!skb->secmark)
return 0;
+ /*
+ * If reach here before socket_post_create hook is called, in which
+ * case label is null, drop the packet.
+ */
+ if (!ctx->label)
+ return -EACCES;
+
return apparmor_secmark_check(ctx->label, OP_RECVMSG, AA_MAY_RECEIVE,
skb->secmark, sk);
}
@@ -1425,6 +1421,7 @@ struct lsm_blob_sizes apparmor_blob_sizes __ro_after_init = {
.lbs_cred = sizeof(struct aa_label *),
.lbs_file = sizeof(struct aa_file_ctx),
.lbs_task = sizeof(struct aa_task_ctx),
+ .lbs_sock = sizeof(struct aa_sk_ctx),
};
static const struct lsm_id apparmor_lsmid = {
@@ -1470,7 +1467,6 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
- LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security),
LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security),
LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security),
@@ -1510,8 +1506,9 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
LSM_HOOK_INIT(task_free, apparmor_task_free),
LSM_HOOK_INIT(task_alloc, apparmor_task_alloc),
- LSM_HOOK_INIT(current_getsecid_subj, apparmor_current_getsecid_subj),
- LSM_HOOK_INIT(task_getsecid_obj, apparmor_task_getsecid_obj),
+ LSM_HOOK_INIT(current_getlsmprop_subj,
+ apparmor_current_getlsmprop_subj),
+ LSM_HOOK_INIT(task_getlsmprop_obj, apparmor_task_getlsmprop_obj),
LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
LSM_HOOK_INIT(task_kill, apparmor_task_kill),
LSM_HOOK_INIT(userns_create, apparmor_userns_create),
@@ -1524,6 +1521,7 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
#endif
LSM_HOOK_INIT(secid_to_secctx, apparmor_secid_to_secctx),
+ LSM_HOOK_INIT(lsmprop_to_secctx, apparmor_lsmprop_to_secctx),
LSM_HOOK_INIT(secctx_to_secid, apparmor_secctx_to_secid),
LSM_HOOK_INIT(release_secctx, apparmor_release_secctx),
@@ -2029,7 +2027,7 @@ static int __init alloc_buffers(void)
}
#ifdef CONFIG_SYSCTL
-static int apparmor_dointvec(struct ctl_table *table, int write,
+static int apparmor_dointvec(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
if (!aa_current_policy_admin_capable(NULL))
@@ -2040,7 +2038,7 @@ static int apparmor_dointvec(struct ctl_table *table, int write,
return proc_dointvec(table, write, buffer, lenp, ppos);
}
-static struct ctl_table apparmor_sysctl_table[] = {
+static const struct ctl_table apparmor_sysctl_table[] = {
#ifdef CONFIG_USER_NS
{
.procname = "unprivileged_userns_apparmor_policy",
@@ -2064,7 +2062,6 @@ static struct ctl_table apparmor_sysctl_table[] = {
.mode = 0600,
.proc_handler = apparmor_dointvec,
},
- { }
};
static int __init apparmor_init_sysctl(void)
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 517d77d3c34c..f2d9c57f8794 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -247,6 +247,42 @@ void aa_dfa_free_kref(struct kref *kref)
dfa_free(dfa);
}
+
+
+/**
+ * remap_data16_to_data32 - remap u16 @old table to a u32 based table
+ * @old: table to remap
+ *
+ * Returns: new table with u32 entries instead of u16.
+ *
+ * Note: will free @old so caller does not have to
+ */
+static struct table_header *remap_data16_to_data32(struct table_header *old)
+{
+ struct table_header *new;
+ size_t tsize;
+ u32 i;
+
+ tsize = table_size(old->td_lolen, YYTD_DATA32);
+ new = kvzalloc(tsize, GFP_KERNEL);
+ if (!new) {
+ kvfree(old);
+ return NULL;
+ }
+ new->td_id = old->td_id;
+ new->td_flags = YYTD_DATA32;
+ new->td_lolen = old->td_lolen;
+
+ for (i = 0; i < old->td_lolen; i++)
+ TABLE_DATAU32(new)[i] = (u32) TABLE_DATAU16(old)[i];
+
+ kvfree(old);
+ if (is_vmalloc_addr(new))
+ vm_unmap_aliases();
+
+ return new;
+}
+
/**
* aa_dfa_unpack - unpack the binary tables of a serialized dfa
* @blob: aligned serialized stream of data to unpack (NOT NULL)
@@ -326,8 +362,10 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
case YYTD_ID_DEF:
case YYTD_ID_NXT:
case YYTD_ID_CHK:
- if (table->td_flags != YYTD_DATA16)
+ if (!(table->td_flags == YYTD_DATA16 ||
+ table->td_flags == YYTD_DATA32)) {
goto fail;
+ }
break;
case YYTD_ID_EC:
if (table->td_flags != YYTD_DATA8)
@@ -342,6 +380,23 @@ struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
dfa->tables[table->td_id] = table;
data += table_size(table->td_lolen, table->td_flags);
size -= table_size(table->td_lolen, table->td_flags);
+
+ /*
+ * this remapping has to be done after incrementing data above
+ * for now straight remap, later have dfa support both
+ */
+ switch (table->td_id) {
+ case YYTD_ID_DEF:
+ case YYTD_ID_NXT:
+ case YYTD_ID_CHK:
+ if (table->td_flags == YYTD_DATA16) {
+ table = remap_data16_to_data32(table);
+ if (!table)
+ goto fail;
+ }
+ dfa->tables[table->td_id] = table;
+ break;
+ }
table = NULL;
}
error = verify_table_headers(dfa->tables, flags);
@@ -395,10 +450,10 @@ do { \
aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
const char *str, int len)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
aa_state_t state = start;
if (state == DFA_NOMATCH)
@@ -434,10 +489,10 @@ aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
*/
aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
aa_state_t state = start;
if (state == DFA_NOMATCH)
@@ -472,10 +527,10 @@ aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
*/
aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
/* current state is <state>, matching character *str */
if (dfa->tables[YYTD_ID_EC]) {
@@ -490,10 +545,10 @@ aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c)
aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
u32 b = (base)[(state)];
if (!(b & MATCH_FLAG_OOB_TRANSITION))
@@ -521,10 +576,10 @@ aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state)
aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
const char *str, const char **retpos)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
u32 *accept = ACCEPT_TABLE(dfa);
aa_state_t state = start, pos;
@@ -582,10 +637,10 @@ aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
const char *str, int n, const char **retpos)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
u32 *accept = ACCEPT_TABLE(dfa);
aa_state_t state = start, pos;
@@ -658,10 +713,10 @@ static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start,
const char *str, struct match_workbuf *wb,
unsigned int *count)
{
- u16 *def = DEFAULT_TABLE(dfa);
+ u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
- u16 *next = NEXT_TABLE(dfa);
- u16 *check = CHECK_TABLE(dfa);
+ u32 *next = NEXT_TABLE(dfa);
+ u32 *check = CHECK_TABLE(dfa);
aa_state_t state = start, pos;
AA_BUG(!dfa);
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
index 49fe8da6fea4..bf8863253e07 100644
--- a/security/apparmor/mount.c
+++ b/security/apparmor/mount.c
@@ -44,6 +44,8 @@ static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
audit_log_format(ab, ", mand");
if (flags & MS_DIRSYNC)
audit_log_format(ab, ", dirsync");
+ if (flags & MS_NOSYMFOLLOW)
+ audit_log_format(ab, ", nosymfollow");
if (flags & MS_NOATIME)
audit_log_format(ab, ", noatime");
if (flags & MS_NODIRATIME)
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
index 87e934b2b548..77413a519117 100644
--- a/security/apparmor/net.c
+++ b/security/apparmor/net.c
@@ -151,7 +151,7 @@ static int aa_label_sk_perm(const struct cred *subj_cred,
const char *op, u32 request,
struct sock *sk)
{
- struct aa_sk_ctx *ctx = SK_CTX(sk);
+ struct aa_sk_ctx *ctx = aa_sock(sk);
int error = 0;
AA_BUG(!label);
diff --git a/security/apparmor/path.c b/security/apparmor/path.c
index 45ec994b558d..d6c74c357ffd 100644
--- a/security/apparmor/path.c
+++ b/security/apparmor/path.c
@@ -130,7 +130,7 @@ static int d_namespace_path(const struct path *path, char *buf, char **name,
/* handle error conditions - and still allow a partial path to
* be returned.
*/
- if (!res || IS_ERR(res)) {
+ if (IS_ERR_OR_NULL(res)) {
if (PTR_ERR(res) == -ENAMETOOLONG) {
error = -ENAMETOOLONG;
*name = buf;
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 957654d253dd..d0244fab0653 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -103,8 +103,7 @@ static void aa_free_pdb(struct aa_policydb *pdb)
{
if (pdb) {
aa_put_dfa(pdb->dfa);
- if (pdb->perms)
- kvfree(pdb->perms);
+ kvfree(pdb->perms);
aa_free_str_table(&pdb->trans);
kfree(pdb);
}
@@ -225,7 +224,7 @@ static void aa_free_data(void *ptr, void *arg)
{
struct aa_data *data = ptr;
- kfree_sensitive(data->data);
+ kvfree_sensitive(data->data, data->size);
kfree_sensitive(data->key);
kfree_sensitive(data);
}
@@ -580,11 +579,6 @@ struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname,
return profile;
}
-struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *hname)
-{
- return aa_lookupn_profile(ns, hname, strlen(hname));
-}
-
struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
const char *fqname, size_t n)
{
@@ -626,6 +620,7 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
/* TODO: ideally we should inherit abi from parent */
profile->label.flags |= FLAG_NULL;
+ profile->attach.xmatch = aa_get_pdb(nullpdb);
rules = list_first_entry(&profile->rules, typeof(*rules), list);
rules->file = aa_get_pdb(nullpdb);
rules->policy = aa_get_pdb(nullpdb);
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 5e578ef0ddff..992b74c50d64 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -13,7 +13,7 @@
* All policy is validated before it is used.
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <kunit/visibility.h>
#include <linux/ctype.h>
#include <linux/errno.h>
@@ -645,10 +645,13 @@ fail:
static bool unpack_perm(struct aa_ext *e, u32 version, struct aa_perms *perm)
{
+ u32 reserved;
+
if (version != 1)
return false;
- return aa_unpack_u32(e, &perm->allow, NULL) &&
+ /* reserved entry is for later expansion, discard for now */
+ return aa_unpack_u32(e, &reserved, NULL) &&
aa_unpack_u32(e, &perm->allow, NULL) &&
aa_unpack_u32(e, &perm->deny, NULL) &&
aa_unpack_u32(e, &perm->subtree, NULL) &&
@@ -747,34 +750,42 @@ static int unpack_pdb(struct aa_ext *e, struct aa_policydb **policy,
*info = "missing required dfa";
goto fail;
}
- goto out;
+ } else {
+ /*
+ * only unpack the following if a dfa is present
+ *
+ * sadly start was given different names for file and policydb
+ * but since it is optional we can try both
+ */
+ if (!aa_unpack_u32(e, &pdb->start[0], "start"))
+ /* default start state */
+ pdb->start[0] = DFA_START;
+ if (!aa_unpack_u32(e, &pdb->start[AA_CLASS_FILE], "dfa_start")) {
+ /* default start state for xmatch and file dfa */
+ pdb->start[AA_CLASS_FILE] = DFA_START;
+ } /* setup class index */
+ for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) {
+ pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0],
+ i);
+ }
}
/*
- * only unpack the following if a dfa is present
- *
- * sadly start was given different names for file and policydb
- * but since it is optional we can try both
+ * Unfortunately due to a bug in earlier userspaces, a
+ * transition table may be present even when the dfa is
+ * not. For compatibility reasons unpack and discard.
*/
- if (!aa_unpack_u32(e, &pdb->start[0], "start"))
- /* default start state */
- pdb->start[0] = DFA_START;
- if (!aa_unpack_u32(e, &pdb->start[AA_CLASS_FILE], "dfa_start")) {
- /* default start state for xmatch and file dfa */
- pdb->start[AA_CLASS_FILE] = DFA_START;
- } /* setup class index */
- for (i = AA_CLASS_FILE + 1; i <= AA_CLASS_LAST; i++) {
- pdb->start[i] = aa_dfa_next(pdb->dfa, pdb->start[0],
- i);
- }
if (!unpack_trans_table(e, &pdb->trans) && required_trans) {
*info = "failed to unpack profile transition table";
goto fail;
}
+ if (!pdb->dfa && pdb->trans.table)
+ aa_free_str_table(&pdb->trans);
+
/* TODO: move compat mapping here, requires dfa merging first */
/* TODO: move verify here, it has to be done after compat mappings */
-out:
+
*policy = pdb;
return 0;
@@ -1071,6 +1082,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
if (rhashtable_insert_fast(profile->data, &data->head,
profile->data->p)) {
+ kvfree_sensitive(data->data, data->size);
kfree_sensitive(data->key);
kfree_sensitive(data);
info = "failed to insert data to table";
diff --git a/security/apparmor/policy_unpack_test.c b/security/apparmor/policy_unpack_test.c
index 5c9bde25e56d..5b2ba88ae9e2 100644
--- a/security/apparmor/policy_unpack_test.c
+++ b/security/apparmor/policy_unpack_test.c
@@ -44,7 +44,7 @@
#define TEST_ARRAY_BUF_OFFSET \
(TEST_NAMED_ARRAY_BUF_OFFSET + 3 + strlen(TEST_ARRAY_NAME) + 1)
-MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING);
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
struct policy_unpack_fixture {
struct aa_ext *e;
@@ -80,14 +80,14 @@ static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf,
*(buf + 1) = strlen(TEST_U32_NAME) + 1;
strscpy(buf + 3, TEST_U32_NAME, e->end - (void *)(buf + 3));
*(buf + 3 + strlen(TEST_U32_NAME) + 1) = AA_U32;
- *((u32 *)(buf + 3 + strlen(TEST_U32_NAME) + 2)) = TEST_U32_DATA;
+ *((__le32 *)(buf + 3 + strlen(TEST_U32_NAME) + 2)) = cpu_to_le32(TEST_U32_DATA);
buf = e->start + TEST_NAMED_U64_BUF_OFFSET;
*buf = AA_NAME;
*(buf + 1) = strlen(TEST_U64_NAME) + 1;
strscpy(buf + 3, TEST_U64_NAME, e->end - (void *)(buf + 3));
*(buf + 3 + strlen(TEST_U64_NAME) + 1) = AA_U64;
- *((u64 *)(buf + 3 + strlen(TEST_U64_NAME) + 2)) = TEST_U64_DATA;
+ *((__le64 *)(buf + 3 + strlen(TEST_U64_NAME) + 2)) = cpu_to_le64(TEST_U64_DATA);
buf = e->start + TEST_NAMED_BLOB_BUF_OFFSET;
*buf = AA_NAME;
@@ -103,7 +103,7 @@ static struct aa_ext *build_aa_ext_struct(struct policy_unpack_fixture *puf,
*(buf + 1) = strlen(TEST_ARRAY_NAME) + 1;
strscpy(buf + 3, TEST_ARRAY_NAME, e->end - (void *)(buf + 3));
*(buf + 3 + strlen(TEST_ARRAY_NAME) + 1) = AA_ARRAY;
- *((u16 *)(buf + 3 + strlen(TEST_ARRAY_NAME) + 2)) = TEST_ARRAY_SIZE;
+ *((__le16 *)(buf + 3 + strlen(TEST_ARRAY_NAME) + 2)) = cpu_to_le16(TEST_ARRAY_SIZE);
return e;
}
@@ -281,6 +281,8 @@ static void policy_unpack_test_unpack_strdup_with_null_name(struct kunit *test)
((uintptr_t)puf->e->start <= (uintptr_t)string)
&& ((uintptr_t)string <= (uintptr_t)puf->e->end));
KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA);
+
+ kfree(string);
}
static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test)
@@ -296,6 +298,8 @@ static void policy_unpack_test_unpack_strdup_with_name(struct kunit *test)
((uintptr_t)puf->e->start <= (uintptr_t)string)
&& ((uintptr_t)string <= (uintptr_t)puf->e->end));
KUNIT_EXPECT_STREQ(test, string, TEST_STRING_DATA);
+
+ kfree(string);
}
static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test)
@@ -313,6 +317,8 @@ static void policy_unpack_test_unpack_strdup_out_of_bounds(struct kunit *test)
KUNIT_EXPECT_EQ(test, size, 0);
KUNIT_EXPECT_NULL(test, string);
KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, start);
+
+ kfree(string);
}
static void policy_unpack_test_unpack_nameX_with_null_name(struct kunit *test)
@@ -604,4 +610,5 @@ static struct kunit_suite apparmor_policy_unpack_test_module = {
kunit_test_suite(apparmor_policy_unpack_test_module);
+MODULE_DESCRIPTION("KUnit tests for AppArmor's policy unpack");
MODULE_LICENSE("GPL");
diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c
index 83d3d1e6d9dc..28caf66b9033 100644
--- a/security/apparmor/secid.c
+++ b/security/apparmor/secid.c
@@ -39,20 +39,6 @@ int apparmor_display_secid_mode;
* TODO: use secid_update in label replace
*/
-/**
- * aa_secid_update - update a secid mapping to a new label
- * @secid: secid to update
- * @label: label the secid will now map to
- */
-void aa_secid_update(u32 secid, struct aa_label *label)
-{
- unsigned long flags;
-
- xa_lock_irqsave(&aa_secids, flags);
- __xa_store(&aa_secids, secid, label, 0);
- xa_unlock_irqrestore(&aa_secids, flags);
-}
-
/*
* see label for inverse aa_label_to_secid
*/
@@ -61,23 +47,21 @@ struct aa_label *aa_secid_to_label(u32 secid)
return xa_load(&aa_secids, secid);
}
-int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+static int apparmor_label_to_secctx(struct aa_label *label,
+ struct lsm_context *cp)
{
/* TODO: cache secctx and ref count so we don't have to recreate */
- struct aa_label *label = aa_secid_to_label(secid);
int flags = FLAG_VIEW_SUBNS | FLAG_HIDDEN_UNCONFINED | FLAG_ABS_ROOT;
int len;
- AA_BUG(!seclen);
-
if (!label)
return -EINVAL;
if (apparmor_display_secid_mode)
flags |= FLAG_SHOW_MODE;
- if (secdata)
- len = aa_label_asxprint(secdata, root_ns, label,
+ if (cp)
+ len = aa_label_asxprint(&cp->context, root_ns, label,
flags, GFP_ATOMIC);
else
len = aa_label_snxprint(NULL, 0, root_ns, label, flags);
@@ -85,9 +69,28 @@ int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
if (len < 0)
return -ENOMEM;
- *seclen = len;
+ if (cp) {
+ cp->len = len;
+ cp->id = LSM_ID_APPARMOR;
+ }
- return 0;
+ return len;
+}
+
+int apparmor_secid_to_secctx(u32 secid, struct lsm_context *cp)
+{
+ struct aa_label *label = aa_secid_to_label(secid);
+
+ return apparmor_label_to_secctx(label, cp);
+}
+
+int apparmor_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp)
+{
+ struct aa_label *label;
+
+ label = prop->apparmor.label;
+
+ return apparmor_label_to_secctx(label, cp);
}
int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
@@ -103,9 +106,13 @@ int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
return 0;
}
-void apparmor_release_secctx(char *secdata, u32 seclen)
+void apparmor_release_secctx(struct lsm_context *cp)
{
- kfree(secdata);
+ if (cp->id == LSM_ID_APPARMOR) {
+ kfree(cp->context);
+ cp->context = NULL;
+ cp->id = LSM_ID_UNDEF;
+ }
}
/**
diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
index 57b9ffd53c98..db759025abe1 100644
--- a/security/bpf/hooks.c
+++ b/security/bpf/hooks.c
@@ -13,7 +13,6 @@ static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = {
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
LSM_HOOK_INIT(inode_free_security, bpf_inode_storage_free),
- LSM_HOOK_INIT(task_free, bpf_task_storage_free),
};
static const struct lsm_id bpf_lsmid = {
@@ -31,7 +30,6 @@ static int __init bpf_lsm_init(void)
struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = {
.lbs_inode = sizeof(struct bpf_storage_blob),
- .lbs_task = sizeof(struct bpf_storage_blob),
};
DEFINE_LSM(bpf) = {
diff --git a/security/commoncap.c b/security/commoncap.c
index 162d96b3a676..58a0c1c3e409 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -27,6 +27,9 @@
#include <linux/mnt_idmapping.h>
#include <uapi/linux/lsm.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/capability.h>
+
/*
* If a non-root user executes a setuid-root binary in
* !secure(SECURE_NOROOT) mode, then we raise capabilities.
@@ -50,24 +53,24 @@ static void warn_setuid_and_fcaps_mixed(const char *fname)
}
/**
- * cap_capable - Determine whether a task has a particular effective capability
+ * cap_capable_helper - Determine whether a task has a particular effective
+ * capability.
* @cred: The credentials to use
- * @targ_ns: The user namespace in which we need the capability
+ * @target_ns: The user namespace of the resource being accessed
+ * @cred_ns: The user namespace of the credentials
* @cap: The capability to check for
- * @opts: Bitmask of options defined in include/linux/security.h
*
* Determine whether the nominated task has the specified capability amongst
* its effective set, returning 0 if it does, -ve if it does not.
*
- * NOTE WELL: cap_has_capability() cannot be used like the kernel's capable()
- * and has_capability() functions. That is, it has the reverse semantics:
- * cap_has_capability() returns 0 when a task has a capability, but the
- * kernel's capable() and has_capability() returns 1 for this case.
+ * See cap_capable for more details.
*/
-int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
- int cap, unsigned int opts)
+static inline int cap_capable_helper(const struct cred *cred,
+ struct user_namespace *target_ns,
+ const struct user_namespace *cred_ns,
+ int cap)
{
- struct user_namespace *ns = targ_ns;
+ struct user_namespace *ns = target_ns;
/* See if cred has the capability in the target user namespace
* by examining the target user namespace and all of the target
@@ -75,21 +78,21 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
*/
for (;;) {
/* Do we have the necessary capabilities? */
- if (ns == cred->user_ns)
+ if (likely(ns == cred_ns))
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/*
* If we're already at a lower level than we're looking for,
* we're done searching.
*/
- if (ns->level <= cred->user_ns->level)
+ if (ns->level <= cred_ns->level)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
- if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
+ if ((ns->parent == cred_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
@@ -103,6 +106,31 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
}
/**
+ * cap_capable - Determine whether a task has a particular effective capability
+ * @cred: The credentials to use
+ * @target_ns: The user namespace of the resource being accessed
+ * @cap: The capability to check for
+ * @opts: Bitmask of options defined in include/linux/security.h (unused)
+ *
+ * Determine whether the nominated task has the specified capability amongst
+ * its effective set, returning 0 if it does, -ve if it does not.
+ *
+ * NOTE WELL: cap_has_capability() cannot be used like the kernel's capable()
+ * and has_capability() functions. That is, it has the reverse semantics:
+ * cap_has_capability() returns 0 when a task has a capability, but the
+ * kernel's capable() and has_capability() returns 1 for this case.
+ */
+int cap_capable(const struct cred *cred, struct user_namespace *target_ns,
+ int cap, unsigned int opts)
+{
+ const struct user_namespace *cred_ns = cred->user_ns;
+ int ret = cap_capable_helper(cred, target_ns, cred_ns, cap);
+
+ trace_cap_capable(cred, target_ns, cred_ns, cap, ret);
+ return ret;
+}
+
+/**
* cap_settime - Determine whether the current process may set the system clock
* @ts: The time to set
* @tz: The timezone to set
@@ -1302,21 +1330,38 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
& (old->securebits ^ arg2)) /*[1]*/
|| ((old->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
|| (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
- || (cap_capable(current_cred(),
- current_cred()->user_ns,
- CAP_SETPCAP,
- CAP_OPT_NONE) != 0) /*[4]*/
/*
* [1] no changing of bits that are locked
* [2] no unlocking of locks
* [3] no setting of unsupported bits
- * [4] doing anything requires privilege (go read about
- * the "sendmail capabilities bug")
*/
)
/* cannot change a locked bit */
return -EPERM;
+ /*
+ * Doing anything requires privilege (go read about the
+ * "sendmail capabilities bug"), except for unprivileged bits.
+ * Indeed, the SECURE_ALL_UNPRIVILEGED bits are not
+ * restrictions enforced by the kernel but by user space on
+ * itself.
+ */
+ if (cap_capable(current_cred(), current_cred()->user_ns,
+ CAP_SETPCAP, CAP_OPT_NONE) != 0) {
+ const unsigned long unpriv_and_locks =
+ SECURE_ALL_UNPRIVILEGED |
+ SECURE_ALL_UNPRIVILEGED << 1;
+ const unsigned long changed = old->securebits ^ arg2;
+
+ /* For legacy reason, denies non-change. */
+ if (!changed)
+ return -EPERM;
+
+ /* Denies privileged changes. */
+ if (changed & ~unpriv_and_locks)
+ return -EPERM;
+ }
+
new = prepare_creds();
if (!new)
return -ENOMEM;
@@ -1396,17 +1441,12 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
* Determine whether the allocation of a new virtual mapping by the current
* task is permitted.
*
- * Return: 1 if permission is granted, 0 if not.
+ * Return: 0 if permission granted, negative error code if not.
*/
int cap_vm_enough_memory(struct mm_struct *mm, long pages)
{
- int cap_sys_admin = 0;
-
- if (cap_capable(current_cred(), &init_user_ns,
- CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) == 0)
- cap_sys_admin = 1;
-
- return cap_sys_admin;
+ return cap_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN,
+ CAP_OPT_NOAUDIT);
}
/**
@@ -1433,12 +1473,6 @@ int cap_mmap_addr(unsigned long addr)
return ret;
}
-int cap_mmap_file(struct file *file, unsigned long reqprot,
- unsigned long prot, unsigned long flags)
-{
- return 0;
-}
-
#ifdef CONFIG_SECURITY
static const struct lsm_id capability_lsmid = {
@@ -1458,7 +1492,6 @@ static struct security_hook_list capability_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv),
LSM_HOOK_INIT(inode_getsecurity, cap_inode_getsecurity),
LSM_HOOK_INIT(mmap_addr, cap_mmap_addr),
- LSM_HOOK_INIT(mmap_file, cap_mmap_file),
LSM_HOOK_INIT(task_fix_setuid, cap_task_fix_setuid),
LSM_HOOK_INIT(task_prctl, cap_task_prctl),
LSM_HOOK_INIT(task_setscheduler, cap_task_setscheduler),
diff --git a/security/inode.c b/security/inode.c
index 9e7cde913667..da3ab44c8e57 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -296,7 +296,7 @@ void securityfs_remove(struct dentry *dentry)
{
struct inode *dir;
- if (!dentry || IS_ERR(dentry))
+ if (IS_ERR_OR_NULL(dentry))
return;
dir = d_inode(dentry->d_parent);
@@ -313,6 +313,31 @@ void securityfs_remove(struct dentry *dentry)
}
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.)
+ */
+void securityfs_recursive_remove(struct dentry *dentry)
+{
+ if (IS_ERR_OR_NULL(dentry))
+ return;
+
+ simple_pin_fs(&fs_type, &mount, &mount_count);
+ simple_recursive_removal(dentry, remove_one);
+ simple_release_fs(&mount, &mount_count);
+}
+EXPORT_SYMBOL_GPL(securityfs_recursive_remove);
+
#ifdef CONFIG_SECURITY
static struct dentry *lsm_dentry;
static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count,
diff --git a/security/integrity/Makefile b/security/integrity/Makefile
index d0ffe37dc1d6..92b63039c654 100644
--- a/security/integrity/Makefile
+++ b/security/integrity/Makefile
@@ -18,5 +18,6 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o
integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \
platform_certs/load_powerpc.o \
platform_certs/keyring_handler.o
+# The relative order of the 'ima' and 'evm' LSMs depends on the order below.
obj-$(CONFIG_IMA) += ima/
obj-$(CONFIG_EVM) += evm/
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 895f4b9ce8c6..457c0a396caf 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -114,8 +114,7 @@ int asymmetric_verify(struct key *keyring, const char *sig,
} else if (!strncmp(pk->pkey_algo, "ecdsa-", 6)) {
/* edcsa-nist-p192 etc. */
pks.encoding = "x962";
- } else if (!strcmp(pk->pkey_algo, "ecrdsa") ||
- !strcmp(pk->pkey_algo, "sm2")) {
+ } else if (!strcmp(pk->pkey_algo, "ecrdsa")) {
pks.encoding = "raw";
} else {
ret = -ENOPKG;
@@ -132,26 +131,3 @@ out:
pr_debug("%s() = %d\n", __func__, ret);
return ret;
}
-
-/**
- * integrity_kernel_module_request - prevent crypto-pkcs1pad(rsa,*) requests
- * @kmod_name: kernel module name
- *
- * We have situation, when public_key_verify_signature() in case of RSA
- * algorithm use alg_name to store internal information in order to
- * construct an algorithm on the fly, but crypto_larval_lookup() will try
- * to use alg_name in order to load kernel module with same name.
- * Since we don't have any real "crypto-pkcs1pad(rsa,*)" kernel modules,
- * we are safe to fail such module request from crypto_larval_lookup().
- *
- * In this way we prevent modprobe execution during digsig verification
- * and avoid possible deadlock if modprobe and/or it's dependencies
- * also signed with digsig.
- */
-int integrity_kernel_module_request(char *kmod_name)
-{
- if (strncmp(kmod_name, "crypto-pkcs1pad(rsa,", 20) == 0)
- return -EINVAL;
-
- return 0;
-}
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
index fba9ee359bc9..861b3bacab82 100644
--- a/security/integrity/evm/Kconfig
+++ b/security/integrity/evm/Kconfig
@@ -6,6 +6,7 @@ config EVM
select CRYPTO_HMAC
select CRYPTO_SHA1
select CRYPTO_HASH_INFO
+ select SECURITY_PATH
default n
help
EVM protects a file's security extended attributes against
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index 53bd7fec93fa..51aba5a54275 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -32,6 +32,26 @@ struct xattr_list {
bool enabled;
};
+#define EVM_NEW_FILE 0x00000001
+#define EVM_IMMUTABLE_DIGSIG 0x00000002
+
+/* EVM integrity metadata associated with an inode */
+struct evm_iint_cache {
+ unsigned long flags;
+ enum integrity_status evm_status:4;
+ struct integrity_inode_attributes metadata_inode;
+};
+
+extern struct lsm_blob_sizes evm_blob_sizes;
+
+static inline struct evm_iint_cache *evm_iint_inode(const struct inode *inode)
+{
+ if (unlikely(!inode->i_security))
+ return NULL;
+
+ return inode->i_security + evm_blob_sizes.lbs_inode;
+}
+
extern int evm_initialized;
#define EVM_ATTR_FSUUID 0x0001
@@ -42,7 +62,7 @@ extern int evm_hmac_attrs;
extern struct list_head evm_config_xattrnames;
struct evm_digest {
- struct ima_digest_data hdr;
+ struct ima_digest_data_hdr hdr;
char digest[IMA_MAX_DIGEST_SIZE];
} __packed;
@@ -55,11 +75,12 @@ int evm_update_evmxattr(struct dentry *dentry,
size_t req_xattr_value_len);
int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value,
- size_t req_xattr_value_len, struct evm_digest *data);
+ size_t req_xattr_value_len, struct evm_digest *data,
+ struct evm_iint_cache *iint);
int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len, char type,
- struct evm_digest *data);
+ struct evm_digest *data, struct evm_iint_cache *iint);
int evm_init_hmac(struct inode *inode, const struct xattr *xattrs,
char *hmac_val);
int evm_init_secfs(void);
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index b1ffd4cc0b44..a5e730ffda57 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -180,7 +180,7 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
}
/*
- * Dump large security xattr values as a continuous ascii hexademical string.
+ * Dump large security xattr values as a continuous ascii hexadecimal string.
* (pr_debug is limited to 64 bytes.)
*/
static void dump_security_xattr_l(const char *prefix, const void *src,
@@ -221,9 +221,10 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
const char *req_xattr_name,
const char *req_xattr_value,
size_t req_xattr_value_len,
- uint8_t type, struct evm_digest *data)
+ uint8_t type, struct evm_digest *data,
+ struct evm_iint_cache *iint)
{
- struct inode *inode = d_backing_inode(dentry);
+ struct inode *inode = d_inode(d_real(dentry, D_REAL_METADATA));
struct xattr_list *xattr;
struct shash_desc *desc;
size_t xattr_size = 0;
@@ -231,6 +232,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
int error;
int size, user_space_size;
bool ima_present = false;
+ u64 i_version = 0;
if (!(inode->i_opflags & IOP_XATTR) ||
inode->i_sb->s_user_ns != &init_user_ns)
@@ -294,6 +296,13 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
}
hmac_add_misc(desc, inode, type, data->digest);
+ if (inode != d_backing_inode(dentry) && iint) {
+ if (IS_I_VERSION(inode))
+ i_version = inode_query_iversion(inode);
+ integrity_inode_attrs_store(&iint->metadata_inode, i_version,
+ inode);
+ }
+
/* Portable EVM signatures must include an IMA hash */
if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present)
error = -EPERM;
@@ -305,27 +314,28 @@ out:
int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len,
- struct evm_digest *data)
+ struct evm_digest *data, struct evm_iint_cache *iint)
{
return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value,
- req_xattr_value_len, EVM_XATTR_HMAC, data);
+ req_xattr_value_len, EVM_XATTR_HMAC, data,
+ iint);
}
int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
const char *req_xattr_value, size_t req_xattr_value_len,
- char type, struct evm_digest *data)
+ char type, struct evm_digest *data, struct evm_iint_cache *iint)
{
return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value,
- req_xattr_value_len, type, data);
+ req_xattr_value_len, type, data, iint);
}
static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
{
const struct evm_ima_xattr_data *xattr_data = NULL;
- struct integrity_iint_cache *iint;
+ struct evm_iint_cache *iint;
int rc = 0;
- iint = integrity_iint_find(inode);
+ iint = evm_iint_inode(inode);
if (iint && (iint->flags & EVM_IMMUTABLE_DIGSIG))
return 1;
@@ -357,6 +367,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
const char *xattr_value, size_t xattr_value_len)
{
struct inode *inode = d_backing_inode(dentry);
+ struct evm_iint_cache *iint = evm_iint_inode(inode);
struct evm_digest data;
int rc = 0;
@@ -372,7 +383,7 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
data.hdr.algo = HASH_ALGO_SHA1;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
- xattr_value_len, &data);
+ xattr_value_len, &data, iint);
if (rc == 0) {
data.hdr.xattr.sha1.type = EVM_XATTR_HMAC;
rc = __vfs_setxattr_noperm(&nop_mnt_idmap, dentry,
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index cc7956d7878b..0add782e73ba 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -151,11 +151,11 @@ static int evm_find_protected_xattrs(struct dentry *dentry)
return count;
}
-static int is_unsupported_fs(struct dentry *dentry)
+static int is_unsupported_hmac_fs(struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
- if (inode->i_sb->s_iflags & SB_I_EVM_UNSUPPORTED) {
+ if (inode->i_sb->s_iflags & SB_I_EVM_HMAC_UNSUPPORTED) {
pr_info_once("%s not supported\n", inode->i_sb->s_type->name);
return 1;
}
@@ -169,7 +169,7 @@ static int is_unsupported_fs(struct dentry *dentry)
* and compare it against the stored security.evm xattr.
*
* For performance:
- * - use the previoulsy retrieved xattr value and length to calculate the
+ * - use the previously retrieved xattr value and length to calculate the
* HMAC.)
* - cache the verification result in the iint, when available.
*
@@ -178,21 +178,26 @@ static int is_unsupported_fs(struct dentry *dentry)
static enum integrity_status evm_verify_hmac(struct dentry *dentry,
const char *xattr_name,
char *xattr_value,
- size_t xattr_value_len,
- struct integrity_iint_cache *iint)
+ size_t xattr_value_len)
{
struct evm_ima_xattr_data *xattr_data = NULL;
struct signature_v2_hdr *hdr;
enum integrity_status evm_status = INTEGRITY_PASS;
struct evm_digest digest;
- struct inode *inode;
+ struct inode *inode = d_backing_inode(dentry);
+ struct evm_iint_cache *iint = evm_iint_inode(inode);
int rc, xattr_len, evm_immutable = 0;
if (iint && (iint->evm_status == INTEGRITY_PASS ||
iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
return iint->evm_status;
- if (is_unsupported_fs(dentry))
+ /*
+ * On unsupported filesystems without EVM_INIT_X509 enabled, skip
+ * signature verification.
+ */
+ if (!(evm_initialized & EVM_INIT_X509) &&
+ is_unsupported_hmac_fs(dentry))
return INTEGRITY_UNKNOWN;
/* if status is not PASS, try to check again - against -ENOMEM */
@@ -226,7 +231,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
digest.hdr.algo = HASH_ALGO_SHA1;
rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
- xattr_value_len, &digest);
+ xattr_value_len, &digest, iint);
if (rc)
break;
rc = crypto_memneq(xattr_data->data, digest.digest,
@@ -247,22 +252,22 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
hdr = (struct signature_v2_hdr *)xattr_data;
digest.hdr.algo = hdr->hash_algo;
rc = evm_calc_hash(dentry, xattr_name, xattr_value,
- xattr_value_len, xattr_data->type, &digest);
+ xattr_value_len, xattr_data->type, &digest,
+ iint);
if (rc)
break;
rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
(const char *)xattr_data, xattr_len,
digest.digest, digest.hdr.length);
if (!rc) {
- inode = d_backing_inode(dentry);
-
if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) {
if (iint)
iint->flags |= EVM_IMMUTABLE_DIGSIG;
evm_status = INTEGRITY_PASS_IMMUTABLE;
} else if (!IS_RDONLY(inode) &&
!(inode->i_sb->s_readonly_remount) &&
- !IS_IMMUTABLE(inode)) {
+ !IS_IMMUTABLE(inode) &&
+ !is_unsupported_hmac_fs(dentry)) {
evm_update_evmxattr(dentry, xattr_name,
xattr_value,
xattr_value_len);
@@ -403,7 +408,6 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
* @xattr_name: requested xattr
* @xattr_value: requested xattr value
* @xattr_value_len: requested xattr value length
- * @iint: inode integrity metadata
*
* Calculate the HMAC for the given dentry and verify it against the stored
* security.evm xattr. For performance, use the xattr value and length
@@ -416,22 +420,13 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer,
*/
enum integrity_status evm_verifyxattr(struct dentry *dentry,
const char *xattr_name,
- void *xattr_value, size_t xattr_value_len,
- struct integrity_iint_cache *iint)
+ void *xattr_value, size_t xattr_value_len)
{
if (!evm_key_loaded() || !evm_protected_xattr(xattr_name))
return INTEGRITY_UNKNOWN;
- if (is_unsupported_fs(dentry))
- return INTEGRITY_UNKNOWN;
-
- if (!iint) {
- iint = integrity_iint_find(d_backing_inode(dentry));
- if (!iint)
- return INTEGRITY_UNKNOWN;
- }
return evm_verify_hmac(dentry, xattr_name, xattr_value,
- xattr_value_len, iint);
+ xattr_value_len);
}
EXPORT_SYMBOL_GPL(evm_verifyxattr);
@@ -448,7 +443,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
if (!evm_key_loaded() || !S_ISREG(inode->i_mode) || evm_fixmode)
return INTEGRITY_PASS;
- return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
+ return evm_verify_hmac(dentry, NULL, NULL, 0);
}
/*
@@ -508,12 +503,12 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,
if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if (is_unsupported_fs(dentry))
+ if (is_unsupported_hmac_fs(dentry))
return -EPERM;
} else if (!evm_protected_xattr(xattr_name)) {
if (!posix_xattr_acl(xattr_name))
return 0;
- if (is_unsupported_fs(dentry))
+ if (is_unsupported_hmac_fs(dentry))
return 0;
evm_status = evm_verify_current_integrity(dentry);
@@ -521,19 +516,19 @@ static int evm_protect_xattr(struct mnt_idmap *idmap,
(evm_status == INTEGRITY_NOXATTRS))
return 0;
goto out;
- } else if (is_unsupported_fs(dentry))
+ } else if (is_unsupported_hmac_fs(dentry))
return 0;
evm_status = evm_verify_current_integrity(dentry);
if (evm_status == INTEGRITY_NOXATTRS) {
- struct integrity_iint_cache *iint;
+ struct evm_iint_cache *iint;
/* Exception if the HMAC is not going to be calculated. */
if (evm_hmac_disabled())
return 0;
- iint = integrity_iint_find(d_backing_inode(dentry));
- if (iint && (iint->flags & IMA_NEW_FILE))
+ iint = evm_iint_inode(d_backing_inode(dentry));
+ if (iint && (iint->flags & EVM_NEW_FILE))
return 0;
/* exception for pseudo filesystems */
@@ -581,6 +576,7 @@ out:
* @xattr_name: pointer to the affected extended attribute name
* @xattr_value: pointer to the new extended attribute value
* @xattr_value_len: pointer to the new extended attribute value length
+ * @flags: flags to pass into filesystem operations
*
* Before allowing the 'security.evm' protected xattr to be updated,
* verify the existing value is valid. As only the kernel should have
@@ -588,9 +584,9 @@ out:
* userspace from writing HMAC value. Writing 'security.evm' requires
* requires CAP_SYS_ADMIN privileges.
*/
-int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
- const char *xattr_name, const void *xattr_value,
- size_t xattr_value_len)
+static int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name, const void *xattr_value,
+ size_t xattr_value_len, int flags)
{
const struct evm_ima_xattr_data *xattr_data = xattr_value;
@@ -620,8 +616,8 @@ int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
* Removing 'security.evm' requires CAP_SYS_ADMIN privileges and that
* the current value is valid.
*/
-int evm_inode_removexattr(struct mnt_idmap *idmap,
- struct dentry *dentry, const char *xattr_name)
+static int evm_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name)
{
/* Policy permits modification of the protected xattrs even though
* there's no HMAC key loaded
@@ -671,9 +667,11 @@ static inline int evm_inode_set_acl_change(struct mnt_idmap *idmap,
* Prevent modifying posix acls causing the EVM HMAC to be re-calculated
* and 'security.evm' xattr updated, unless the existing 'security.evm' is
* valid.
+ *
+ * Return: zero on success, -EPERM on failure.
*/
-int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
- const char *acl_name, struct posix_acl *kacl)
+static int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *acl_name, struct posix_acl *kacl)
{
enum integrity_status evm_status;
@@ -712,16 +710,59 @@ int evm_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
return -EPERM;
}
+/**
+ * evm_inode_remove_acl - Protect the EVM extended attribute from posix acls
+ * @idmap: idmap of the mount
+ * @dentry: pointer to the affected dentry
+ * @acl_name: name of the posix acl
+ *
+ * Prevent removing posix acls causing the EVM HMAC to be re-calculated
+ * and 'security.evm' xattr updated, unless the existing 'security.evm' is
+ * valid.
+ *
+ * Return: zero on success, -EPERM on failure.
+ */
+static int evm_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *acl_name)
+{
+ return evm_inode_set_acl(idmap, dentry, acl_name, NULL);
+}
+
static void evm_reset_status(struct inode *inode)
{
- struct integrity_iint_cache *iint;
+ struct evm_iint_cache *iint;
- iint = integrity_iint_find(inode);
+ iint = evm_iint_inode(inode);
if (iint)
iint->evm_status = INTEGRITY_UNKNOWN;
}
/**
+ * evm_metadata_changed: Detect changes to the metadata
+ * @inode: a file's inode
+ * @metadata_inode: metadata inode
+ *
+ * On a stacked filesystem detect whether the metadata has changed. If this is
+ * the case reset the evm_status associated with the inode that represents the
+ * file.
+ */
+bool evm_metadata_changed(struct inode *inode, struct inode *metadata_inode)
+{
+ struct evm_iint_cache *iint = evm_iint_inode(inode);
+ bool ret = false;
+
+ if (iint) {
+ ret = (!IS_I_VERSION(metadata_inode) ||
+ integrity_inode_attrs_changed(&iint->metadata_inode,
+ metadata_inode));
+ if (ret)
+ iint->evm_status = INTEGRITY_UNKNOWN;
+ }
+
+ return ret;
+}
+
+/**
* evm_revalidate_status - report whether EVM status re-validation is necessary
* @xattr_name: pointer to the affected extended attribute name
*
@@ -752,6 +793,7 @@ bool evm_revalidate_status(const char *xattr_name)
* @xattr_name: pointer to the affected extended attribute name
* @xattr_value: pointer to the new extended attribute value
* @xattr_value_len: pointer to the new extended attribute value length
+ * @flags: flags to pass into filesystem operations
*
* Update the HMAC stored in 'security.evm' to reflect the change.
*
@@ -759,8 +801,11 @@ bool evm_revalidate_status(const char *xattr_name)
* __vfs_setxattr_noperm(). The caller of which has taken the inode's
* i_mutex lock.
*/
-void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
- const void *xattr_value, size_t xattr_value_len)
+static void evm_inode_post_setxattr(struct dentry *dentry,
+ const char *xattr_name,
+ const void *xattr_value,
+ size_t xattr_value_len,
+ int flags)
{
if (!evm_revalidate_status(xattr_name))
return;
@@ -773,13 +818,28 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
if (!(evm_initialized & EVM_INIT_HMAC))
return;
- if (is_unsupported_fs(dentry))
+ if (is_unsupported_hmac_fs(dentry))
return;
evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len);
}
/**
+ * evm_inode_post_set_acl - Update the EVM extended attribute from posix acls
+ * @dentry: pointer to the affected dentry
+ * @acl_name: name of the posix acl
+ * @kacl: pointer to the posix acls
+ *
+ * Update the 'security.evm' xattr with the EVM HMAC re-calculated after setting
+ * posix acls.
+ */
+static void evm_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
+ struct posix_acl *kacl)
+{
+ return evm_inode_post_setxattr(dentry, acl_name, NULL, 0, 0);
+}
+
+/**
* evm_inode_post_removexattr - update 'security.evm' after removing the xattr
* @dentry: pointer to the affected dentry
* @xattr_name: pointer to the affected extended attribute name
@@ -789,7 +849,8 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
* No need to take the i_mutex lock here, as this function is called from
* vfs_removexattr() which takes the i_mutex.
*/
-void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
+static void evm_inode_post_removexattr(struct dentry *dentry,
+ const char *xattr_name)
{
if (!evm_revalidate_status(xattr_name))
return;
@@ -805,6 +866,22 @@ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
evm_update_evmxattr(dentry, xattr_name, NULL, 0);
}
+/**
+ * evm_inode_post_remove_acl - Update the EVM extended attribute from posix acls
+ * @idmap: idmap of the mount
+ * @dentry: pointer to the affected dentry
+ * @acl_name: name of the posix acl
+ *
+ * Update the 'security.evm' xattr with the EVM HMAC re-calculated after
+ * removing posix acls.
+ */
+static inline void evm_inode_post_remove_acl(struct mnt_idmap *idmap,
+ struct dentry *dentry,
+ const char *acl_name)
+{
+ evm_inode_post_removexattr(dentry, acl_name);
+}
+
static int evm_attr_change(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *attr)
{
@@ -828,8 +905,8 @@ static int evm_attr_change(struct mnt_idmap *idmap,
* Permit update of file attributes when files have a valid EVM signature,
* except in the case of them having an immutable portable signature.
*/
-int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
- struct iattr *attr)
+static int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;
enum integrity_status evm_status;
@@ -840,7 +917,7 @@ int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
return 0;
- if (is_unsupported_fs(dentry))
+ if (is_unsupported_hmac_fs(dentry))
return 0;
if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
@@ -870,6 +947,7 @@ int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
/**
* evm_inode_post_setattr - update 'security.evm' after modifying metadata
+ * @idmap: idmap of the idmapped mount
* @dentry: pointer to the affected dentry
* @ia_valid: for the UID and GID status
*
@@ -879,7 +957,8 @@ int evm_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
* This function is called from notify_change(), which expects the caller
* to lock the inode's i_mutex.
*/
-void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
+static void evm_inode_post_setattr(struct mnt_idmap *idmap,
+ struct dentry *dentry, int ia_valid)
{
if (!evm_revalidate_status(NULL))
return;
@@ -889,18 +968,43 @@ void evm_inode_post_setattr(struct dentry *dentry, int ia_valid)
if (!(evm_initialized & EVM_INIT_HMAC))
return;
- if (is_unsupported_fs(dentry))
+ if (is_unsupported_hmac_fs(dentry))
return;
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
evm_update_evmxattr(dentry, NULL, NULL, 0);
}
-int evm_inode_copy_up_xattr(const char *name)
+static int evm_inode_copy_up_xattr(struct dentry *src, const char *name)
{
- if (strcmp(name, XATTR_NAME_EVM) == 0)
- return 1; /* Discard */
- return -EOPNOTSUPP;
+ struct evm_ima_xattr_data *xattr_data = NULL;
+ int rc;
+
+ if (strcmp(name, XATTR_NAME_EVM) != 0)
+ return -EOPNOTSUPP;
+
+ /* first need to know the sig type */
+ rc = vfs_getxattr_alloc(&nop_mnt_idmap, src, XATTR_NAME_EVM,
+ (char **)&xattr_data, 0, GFP_NOFS);
+ if (rc <= 0)
+ return -EPERM;
+
+ if (rc < offsetof(struct evm_ima_xattr_data, type) +
+ sizeof(xattr_data->type))
+ return -EPERM;
+
+ switch (xattr_data->type) {
+ case EVM_XATTR_PORTABLE_DIGSIG:
+ rc = 0; /* allow copy-up */
+ break;
+ case EVM_XATTR_HMAC:
+ case EVM_IMA_XATTR_DIGSIG:
+ default:
+ rc = -ECANCELED; /* discard */
+ }
+
+ kfree(xattr_data);
+ return rc;
}
/*
@@ -960,6 +1064,43 @@ out:
}
EXPORT_SYMBOL_GPL(evm_inode_init_security);
+static int evm_inode_alloc_security(struct inode *inode)
+{
+ struct evm_iint_cache *iint = evm_iint_inode(inode);
+
+ /* Called by security_inode_alloc(), it cannot be NULL. */
+ iint->flags = 0UL;
+ iint->evm_status = INTEGRITY_UNKNOWN;
+
+ return 0;
+}
+
+static void evm_file_release(struct file *file)
+{
+ struct inode *inode = file_inode(file);
+ struct evm_iint_cache *iint = evm_iint_inode(inode);
+ fmode_t mode = file->f_mode;
+
+ if (!S_ISREG(inode->i_mode) || !(mode & FMODE_WRITE))
+ return;
+
+ if (iint && iint->flags & EVM_NEW_FILE &&
+ atomic_read(&inode->i_writecount) == 1)
+ iint->flags &= ~EVM_NEW_FILE;
+}
+
+static void evm_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
+{
+ struct inode *inode = d_backing_inode(dentry);
+ struct evm_iint_cache *iint = evm_iint_inode(inode);
+
+ if (!S_ISREG(inode->i_mode))
+ return;
+
+ if (iint)
+ iint->flags |= EVM_NEW_FILE;
+}
+
#ifdef CONFIG_EVM_LOAD_X509
void __init evm_load_x509(void)
{
@@ -999,4 +1140,45 @@ error:
return error;
}
+static struct security_hook_list evm_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(inode_setattr, evm_inode_setattr),
+ LSM_HOOK_INIT(inode_post_setattr, evm_inode_post_setattr),
+ LSM_HOOK_INIT(inode_copy_up_xattr, evm_inode_copy_up_xattr),
+ LSM_HOOK_INIT(inode_setxattr, evm_inode_setxattr),
+ LSM_HOOK_INIT(inode_post_setxattr, evm_inode_post_setxattr),
+ LSM_HOOK_INIT(inode_set_acl, evm_inode_set_acl),
+ LSM_HOOK_INIT(inode_post_set_acl, evm_inode_post_set_acl),
+ LSM_HOOK_INIT(inode_remove_acl, evm_inode_remove_acl),
+ LSM_HOOK_INIT(inode_post_remove_acl, evm_inode_post_remove_acl),
+ LSM_HOOK_INIT(inode_removexattr, evm_inode_removexattr),
+ LSM_HOOK_INIT(inode_post_removexattr, evm_inode_post_removexattr),
+ LSM_HOOK_INIT(inode_init_security, evm_inode_init_security),
+ LSM_HOOK_INIT(inode_alloc_security, evm_inode_alloc_security),
+ LSM_HOOK_INIT(file_release, evm_file_release),
+ LSM_HOOK_INIT(path_post_mknod, evm_post_path_mknod),
+};
+
+static const struct lsm_id evm_lsmid = {
+ .name = "evm",
+ .id = LSM_ID_EVM,
+};
+
+static int __init init_evm_lsm(void)
+{
+ security_add_hooks(evm_hooks, ARRAY_SIZE(evm_hooks), &evm_lsmid);
+ return 0;
+}
+
+struct lsm_blob_sizes evm_blob_sizes __ro_after_init = {
+ .lbs_inode = sizeof(struct evm_iint_cache),
+ .lbs_xattr_count = 1,
+};
+
+DEFINE_LSM(evm) = {
+ .name = "evm",
+ .init = init_evm_lsm,
+ .order = LSM_ORDER_LAST,
+ .blobs = &evm_blob_sizes,
+};
+
late_initcall(init_evm);
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index d4419a2a1e24..068ac6c2ae1e 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -6,208 +6,15 @@
* Mimi Zohar <zohar@us.ibm.com>
*
* File: integrity_iint.c
- * - implements the integrity hooks: integrity_inode_alloc,
- * integrity_inode_free
- * - cache integrity information associated with an inode
- * using a rbtree tree.
+ * - initialize the integrity directory in securityfs
+ * - load IMA and EVM keys
*/
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/rbtree.h>
-#include <linux/file.h>
-#include <linux/uaccess.h>
#include <linux/security.h>
-#include <linux/lsm_hooks.h>
#include "integrity.h"
-static struct rb_root integrity_iint_tree = RB_ROOT;
-static DEFINE_RWLOCK(integrity_iint_lock);
-static struct kmem_cache *iint_cache __ro_after_init;
-
struct dentry *integrity_dir;
/*
- * __integrity_iint_find - return the iint associated with an inode
- */
-static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
-{
- struct integrity_iint_cache *iint;
- struct rb_node *n = integrity_iint_tree.rb_node;
-
- while (n) {
- iint = rb_entry(n, struct integrity_iint_cache, rb_node);
-
- if (inode < iint->inode)
- n = n->rb_left;
- else if (inode > iint->inode)
- n = n->rb_right;
- else
- return iint;
- }
-
- return NULL;
-}
-
-/*
- * integrity_iint_find - return the iint associated with an inode
- */
-struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
-{
- struct integrity_iint_cache *iint;
-
- if (!IS_IMA(inode))
- return NULL;
-
- read_lock(&integrity_iint_lock);
- iint = __integrity_iint_find(inode);
- read_unlock(&integrity_iint_lock);
-
- return iint;
-}
-
-#define IMA_MAX_NESTING (FILESYSTEM_MAX_STACK_DEPTH+1)
-
-/*
- * It is not clear that IMA should be nested at all, but as long is it measures
- * files both on overlayfs and on underlying fs, we need to annotate the iint
- * mutex to avoid lockdep false positives related to IMA + overlayfs.
- * See ovl_lockdep_annotate_inode_mutex_key() for more details.
- */
-static inline void iint_lockdep_annotate(struct integrity_iint_cache *iint,
- struct inode *inode)
-{
-#ifdef CONFIG_LOCKDEP
- static struct lock_class_key iint_mutex_key[IMA_MAX_NESTING];
-
- int depth = inode->i_sb->s_stack_depth;
-
- if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING))
- depth = 0;
-
- lockdep_set_class(&iint->mutex, &iint_mutex_key[depth]);
-#endif
-}
-
-static void iint_init_always(struct integrity_iint_cache *iint,
- struct inode *inode)
-{
- iint->ima_hash = NULL;
- iint->version = 0;
- iint->flags = 0UL;
- iint->atomic_flags = 0UL;
- iint->ima_file_status = INTEGRITY_UNKNOWN;
- iint->ima_mmap_status = INTEGRITY_UNKNOWN;
- iint->ima_bprm_status = INTEGRITY_UNKNOWN;
- iint->ima_read_status = INTEGRITY_UNKNOWN;
- iint->ima_creds_status = INTEGRITY_UNKNOWN;
- iint->evm_status = INTEGRITY_UNKNOWN;
- iint->measured_pcrs = 0;
- mutex_init(&iint->mutex);
- iint_lockdep_annotate(iint, inode);
-}
-
-static void iint_free(struct integrity_iint_cache *iint)
-{
- kfree(iint->ima_hash);
- mutex_destroy(&iint->mutex);
- kmem_cache_free(iint_cache, iint);
-}
-
-/**
- * integrity_inode_get - find or allocate an iint associated with an inode
- * @inode: pointer to the inode
- * @return: allocated iint
- *
- * Caller must lock i_mutex
- */
-struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
-{
- struct rb_node **p;
- struct rb_node *node, *parent = NULL;
- struct integrity_iint_cache *iint, *test_iint;
-
- iint = integrity_iint_find(inode);
- if (iint)
- return iint;
-
- iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
- if (!iint)
- return NULL;
-
- iint_init_always(iint, inode);
-
- write_lock(&integrity_iint_lock);
-
- p = &integrity_iint_tree.rb_node;
- while (*p) {
- parent = *p;
- test_iint = rb_entry(parent, struct integrity_iint_cache,
- rb_node);
- if (inode < test_iint->inode) {
- p = &(*p)->rb_left;
- } else if (inode > test_iint->inode) {
- p = &(*p)->rb_right;
- } else {
- write_unlock(&integrity_iint_lock);
- kmem_cache_free(iint_cache, iint);
- return test_iint;
- }
- }
-
- iint->inode = inode;
- node = &iint->rb_node;
- inode->i_flags |= S_IMA;
- rb_link_node(node, parent, p);
- rb_insert_color(node, &integrity_iint_tree);
-
- write_unlock(&integrity_iint_lock);
- return iint;
-}
-
-/**
- * integrity_inode_free - called on security_inode_free
- * @inode: pointer to the inode
- *
- * Free the integrity information(iint) associated with an inode.
- */
-void integrity_inode_free(struct inode *inode)
-{
- struct integrity_iint_cache *iint;
-
- if (!IS_IMA(inode))
- return;
-
- write_lock(&integrity_iint_lock);
- iint = __integrity_iint_find(inode);
- rb_erase(&iint->rb_node, &integrity_iint_tree);
- write_unlock(&integrity_iint_lock);
-
- iint_free(iint);
-}
-
-static void iint_init_once(void *foo)
-{
- struct integrity_iint_cache *iint = (struct integrity_iint_cache *) foo;
-
- memset(iint, 0, sizeof(*iint));
-}
-
-static int __init integrity_iintcache_init(void)
-{
- iint_cache =
- kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
- 0, SLAB_PANIC, iint_init_once);
- return 0;
-}
-DEFINE_LSM(integrity) = {
- .name = "integrity",
- .init = integrity_iintcache_init,
- .order = LSM_ORDER_LAST,
-};
-
-
-/*
* integrity_kernel_read - read data from the file
*
* This is a function for reading file content instead of kernel_read().
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index b98bfe9efd0c..475c32615006 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -8,6 +8,7 @@ config IMA
select CRYPTO_HMAC
select CRYPTO_SHA1
select CRYPTO_HASH_INFO
+ select SECURITY_PATH
select TCG_TPM if HAS_IOMEM
select TCG_TIS if TCG_TPM && X86
select TCG_CRB if TCG_TPM && ACPI
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index 2499f2485c04..b376d38b4ee6 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -4,7 +4,7 @@
# Measurement Architecture(IMA).
#
-obj-$(CONFIG_IMA) += ima.o
+obj-$(CONFIG_IMA) += ima.o ima_iint.o
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
ima_policy.o ima_template.o ima_template_lib.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index c29db699c996..a4f284bd846c 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -49,18 +49,26 @@ extern int ima_policy_flag;
/* bitset of digests algorithms allowed in the setxattr hook */
extern atomic_t ima_setxattr_allowed_hash_algorithms;
+/* IMA hash algorithm description */
+struct ima_algo_desc {
+ struct crypto_shash *tfm;
+ enum hash_algo algo;
+};
+
/* set during initialization */
extern int ima_hash_algo __ro_after_init;
extern int ima_sha1_idx __ro_after_init;
extern int ima_hash_algo_idx __ro_after_init;
extern int ima_extra_slots __ro_after_init;
+extern struct ima_algo_desc *ima_algo_array __ro_after_init;
+
extern int ima_appraise;
extern struct tpm_chip *ima_tpm_chip;
extern const char boot_aggregate_name[];
/* IMA event related data */
struct ima_event_data {
- struct integrity_iint_cache *iint;
+ struct ima_iint_cache *iint;
struct file *file;
const unsigned char *filename;
struct evm_ima_xattr_data *xattr_value;
@@ -119,6 +127,108 @@ struct ima_kexec_hdr {
u64 count;
};
+/* IMA iint action cache flags */
+#define IMA_MEASURE 0x00000001
+#define IMA_MEASURED 0x00000002
+#define IMA_APPRAISE 0x00000004
+#define IMA_APPRAISED 0x00000008
+/*#define IMA_COLLECT 0x00000010 do not use this flag */
+#define IMA_COLLECTED 0x00000020
+#define IMA_AUDIT 0x00000040
+#define IMA_AUDITED 0x00000080
+#define IMA_HASH 0x00000100
+#define IMA_HASHED 0x00000200
+
+/* IMA iint policy rule cache flags */
+#define IMA_NONACTION_FLAGS 0xff000000
+#define IMA_DIGSIG_REQUIRED 0x01000000
+#define IMA_PERMIT_DIRECTIO 0x02000000
+#define IMA_NEW_FILE 0x04000000
+#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
+#define IMA_MODSIG_ALLOWED 0x20000000
+#define IMA_CHECK_BLACKLIST 0x40000000
+#define IMA_VERITY_REQUIRED 0x80000000
+
+/* Exclude non-action flags which are not rule-specific. */
+#define IMA_NONACTION_RULE_FLAGS (IMA_NONACTION_FLAGS & ~IMA_NEW_FILE)
+
+#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
+ IMA_HASH | IMA_APPRAISE_SUBMASK)
+#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
+ IMA_HASHED | IMA_COLLECTED | \
+ IMA_APPRAISED_SUBMASK)
+
+/* IMA iint subaction appraise cache flags */
+#define IMA_FILE_APPRAISE 0x00001000
+#define IMA_FILE_APPRAISED 0x00002000
+#define IMA_MMAP_APPRAISE 0x00004000
+#define IMA_MMAP_APPRAISED 0x00008000
+#define IMA_BPRM_APPRAISE 0x00010000
+#define IMA_BPRM_APPRAISED 0x00020000
+#define IMA_READ_APPRAISE 0x00040000
+#define IMA_READ_APPRAISED 0x00080000
+#define IMA_CREDS_APPRAISE 0x00100000
+#define IMA_CREDS_APPRAISED 0x00200000
+#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
+ IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
+ IMA_CREDS_APPRAISE)
+#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
+ IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
+ IMA_CREDS_APPRAISED)
+
+/* IMA iint cache atomic_flags */
+#define IMA_CHANGE_XATTR 0
+#define IMA_UPDATE_XATTR 1
+#define IMA_CHANGE_ATTR 2
+#define IMA_DIGSIG 3
+#define IMA_MUST_MEASURE 4
+
+/* IMA integrity metadata associated with an inode */
+struct ima_iint_cache {
+ struct mutex mutex; /* protects: version, flags, digest */
+ struct integrity_inode_attributes real_inode;
+ unsigned long flags;
+ unsigned long measured_pcrs;
+ unsigned long atomic_flags;
+ enum integrity_status ima_file_status:4;
+ enum integrity_status ima_mmap_status:4;
+ enum integrity_status ima_bprm_status:4;
+ enum integrity_status ima_read_status:4;
+ enum integrity_status ima_creds_status:4;
+ struct ima_digest_data *ima_hash;
+};
+
+extern struct lsm_blob_sizes ima_blob_sizes;
+
+static inline struct ima_iint_cache *
+ima_inode_get_iint(const struct inode *inode)
+{
+ struct ima_iint_cache **iint_sec;
+
+ if (unlikely(!inode->i_security))
+ return NULL;
+
+ iint_sec = inode->i_security + ima_blob_sizes.lbs_inode;
+ return *iint_sec;
+}
+
+static inline void ima_inode_set_iint(const struct inode *inode,
+ struct ima_iint_cache *iint)
+{
+ struct ima_iint_cache **iint_sec;
+
+ if (unlikely(!inode->i_security))
+ return;
+
+ iint_sec = inode->i_security + ima_blob_sizes.lbs_inode;
+ *iint_sec = iint;
+}
+
+struct ima_iint_cache *ima_iint_find(struct inode *inode);
+struct ima_iint_cache *ima_inode_get(struct inode *inode);
+void ima_inode_free_rcu(void *inode_security);
+void __init ima_iintcache_init(void);
+
extern const int read_idmap[];
#ifdef CONFIG_HAVE_IMA_KEXEC
@@ -127,6 +237,12 @@ void ima_load_kexec_buffer(void);
static inline void ima_load_kexec_buffer(void) {}
#endif /* CONFIG_HAVE_IMA_KEXEC */
+#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
+void ima_post_key_create_or_update(struct key *keyring, struct key *key,
+ const void *payload, size_t plen,
+ unsigned long flags, bool create);
+#endif
+
/*
* The default binary_runtime_measurements list format is defined as the
* platform native format. The canonical format is defined as little-endian.
@@ -146,8 +262,8 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data,
struct ima_template_entry *entry);
int ima_calc_boot_aggregate(struct ima_digest_data *hash);
void ima_add_violation(struct file *file, const unsigned char *filename,
- struct integrity_iint_cache *iint,
- const char *op, const char *cause);
+ struct ima_iint_cache *iint, const char *op,
+ const char *cause);
int ima_init_crypto(void);
void ima_putc(struct seq_file *m, void *data, int datalen);
void ima_print_digest(struct seq_file *m, u8 *digest, u32 size);
@@ -165,6 +281,7 @@ unsigned long ima_get_binary_runtime_size(void);
int ima_init_template(void);
void ima_init_template_list(void);
int __init ima_init_digests(void);
+void __init ima_init_reboot_notifier(void);
int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
void *lsm_data);
@@ -256,15 +373,15 @@ static inline void ima_process_queued_keys(void) {}
/* LIM API function definitions */
int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
- const struct cred *cred, u32 secid, int mask,
+ const struct cred *cred, struct lsm_prop *prop, int mask,
enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc,
const char *func_data, unsigned int *allowed_algos);
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
-int ima_collect_measurement(struct integrity_iint_cache *iint,
- struct file *file, void *buf, loff_t size,
- enum hash_algo algo, struct modsig *modsig);
-void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
+ void *buf, loff_t size, enum hash_algo algo,
+ struct modsig *modsig);
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
@@ -274,7 +391,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
const char *eventname, enum ima_hooks func,
int pcr, const char *func_data,
bool buf_hash, u8 *digest, size_t digest_len);
-void ima_audit_measurement(struct integrity_iint_cache *iint,
+void ima_audit_measurement(struct ima_iint_cache *iint,
const unsigned char *filename);
int ima_alloc_init_template(struct ima_event_data *event_data,
struct ima_template_entry **entry,
@@ -287,8 +404,8 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
/* IMA policy related functions */
int ima_match_policy(struct mnt_idmap *idmap, struct inode *inode,
- const struct cred *cred, u32 secid, enum ima_hooks func,
- int mask, int flags, int *pcr,
+ const struct cred *cred, struct lsm_prop *prop,
+ enum ima_hooks func, int mask, int flags, int *pcr,
struct ima_template_desc **template_desc,
const char *func_data, unsigned int *allowed_algos);
void ima_init_policy(void);
@@ -312,32 +429,32 @@ int ima_policy_show(struct seq_file *m, void *v);
#define IMA_APPRAISE_KEXEC 0x40
#ifdef CONFIG_IMA_APPRAISE
-int ima_check_blacklist(struct integrity_iint_cache *iint,
+int ima_check_blacklist(struct ima_iint_cache *iint,
const struct modsig *modsig, int pcr);
-int ima_appraise_measurement(enum ima_hooks func,
- struct integrity_iint_cache *iint,
+int ima_appraise_measurement(enum ima_hooks func, struct ima_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig);
int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode,
int mask, enum ima_hooks func);
-void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
-enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
+void ima_update_xattr(struct ima_iint_cache *iint, struct file *file);
+enum integrity_status ima_get_cache_status(struct ima_iint_cache *iint,
enum ima_hooks func);
enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
int xattr_len);
int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value, int xattr_len);
+void __init init_ima_appraise_lsm(const struct lsm_id *lsmid);
#else
-static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
+static inline int ima_check_blacklist(struct ima_iint_cache *iint,
const struct modsig *modsig, int pcr)
{
return 0;
}
static inline int ima_appraise_measurement(enum ima_hooks func,
- struct integrity_iint_cache *iint,
+ struct ima_iint_cache *iint,
struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
@@ -354,14 +471,13 @@ static inline int ima_must_appraise(struct mnt_idmap *idmap,
return 0;
}
-static inline void ima_update_xattr(struct integrity_iint_cache *iint,
+static inline void ima_update_xattr(struct ima_iint_cache *iint,
struct file *file)
{
}
-static inline enum integrity_status ima_get_cache_status(struct integrity_iint_cache
- *iint,
- enum ima_hooks func)
+static inline enum integrity_status
+ima_get_cache_status(struct ima_iint_cache *iint, enum ima_hooks func)
{
return INTEGRITY_UNKNOWN;
}
@@ -379,6 +495,10 @@ static inline int ima_read_xattr(struct dentry *dentry,
return 0;
}
+static inline void __init init_ima_appraise_lsm(const struct lsm_id *lsmid)
+{
+}
+
#endif /* CONFIG_IMA_APPRAISE */
#ifdef CONFIG_IMA_APPRAISE_MODSIG
@@ -430,7 +550,7 @@ static inline void ima_free_modsig(struct modsig *modsig)
#else
static inline int ima_filter_rule_init(u32 field, u32 op, char *rulestr,
- void **lsmrule)
+ void **lsmrule, gfp_t gfp)
{
return -EINVAL;
}
@@ -439,7 +559,7 @@ static inline void ima_filter_rule_free(void *lsmrule)
{
}
-static inline int ima_filter_rule_match(u32 secid, u32 field, u32 op,
+static inline int ima_filter_rule_match(struct lsm_prop *prop, u32 field, u32 op,
void *lsmrule)
{
return -EINVAL;
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 597ea0c4d72f..c35ea613c9f8 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -131,8 +131,8 @@ int ima_store_template(struct ima_template_entry *entry,
* value is invalidated.
*/
void ima_add_violation(struct file *file, const unsigned char *filename,
- struct integrity_iint_cache *iint,
- const char *op, const char *cause)
+ struct ima_iint_cache *iint, const char *op,
+ const char *cause)
{
struct ima_template_entry *entry;
struct inode *inode = file_inode(file);
@@ -165,7 +165,7 @@ err_out:
* @idmap: idmap of the mount the inode was found from
* @inode: pointer to the inode associated with the object being validated
* @cred: pointer to credentials structure to validate
- * @secid: secid of the task being validated
+ * @prop: properties of the task being validated
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC,
* MAY_APPEND)
* @func: caller identifier
@@ -187,7 +187,7 @@ err_out:
*
*/
int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
- const struct cred *cred, u32 secid, int mask,
+ const struct cred *cred, struct lsm_prop *prop, int mask,
enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc,
const char *func_data, unsigned int *allowed_algos)
@@ -196,12 +196,13 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
flags &= ima_policy_flag;
- return ima_match_policy(idmap, inode, cred, secid, func, mask,
+ return ima_match_policy(idmap, inode, cred, prop, func, mask,
flags, pcr, template_desc, func_data,
allowed_algos);
}
-static bool ima_get_verity_digest(struct integrity_iint_cache *iint,
+static bool ima_get_verity_digest(struct ima_iint_cache *iint,
+ struct inode *inode,
struct ima_max_digest_data *hash)
{
enum hash_algo alg;
@@ -211,7 +212,7 @@ static bool ima_get_verity_digest(struct integrity_iint_cache *iint,
* On failure, 'measure' policy rules will result in a file data
* hash containing 0's.
*/
- digest_len = fsverity_get_digest(iint->inode, hash->digest, NULL, &alg);
+ digest_len = fsverity_get_digest(inode, hash->digest, NULL, &alg);
if (digest_len == 0)
return false;
@@ -237,15 +238,17 @@ static bool ima_get_verity_digest(struct integrity_iint_cache *iint,
*
* Return 0 on success, error code otherwise
*/
-int ima_collect_measurement(struct integrity_iint_cache *iint,
- struct file *file, void *buf, loff_t size,
- enum hash_algo algo, struct modsig *modsig)
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
+ void *buf, loff_t size, enum hash_algo algo,
+ struct modsig *modsig)
{
const char *audit_cause = "failed";
struct inode *inode = file_inode(file);
struct inode *real_inode = d_real_inode(file_dentry(file));
- const char *filename = file->f_path.dentry->d_name.name;
struct ima_max_digest_data hash;
+ struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
+ struct ima_digest_data, hdr);
+ struct name_snapshot filename;
struct kstat stat;
int result = 0;
int length;
@@ -280,14 +283,14 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
memset(&hash.digest, 0, sizeof(hash.digest));
if (iint->flags & IMA_VERITY_REQUIRED) {
- if (!ima_get_verity_digest(iint, &hash)) {
+ if (!ima_get_verity_digest(iint, inode, &hash)) {
audit_cause = "no-verity-digest";
result = -ENODATA;
}
} else if (buf) {
- result = ima_calc_buffer_hash(buf, size, &hash.hdr);
+ result = ima_calc_buffer_hash(buf, size, hash_hdr);
} else {
- result = ima_calc_file_hash(file, &hash.hdr);
+ result = ima_calc_file_hash(file, hash_hdr);
}
if (result && result != -EBADF && result != -EINVAL)
@@ -302,11 +305,11 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
iint->ima_hash = tmpbuf;
memcpy(iint->ima_hash, &hash, length);
- iint->version = i_version;
- if (real_inode != inode) {
- iint->real_ino = real_inode->i_ino;
- iint->real_dev = real_inode->i_sb->s_dev;
- }
+ if (real_inode == inode)
+ iint->real_inode.version = i_version;
+ else
+ integrity_inode_attrs_store(&iint->real_inode, i_version,
+ real_inode);
/* Possibly temporary failure due to type of read (eg. O_DIRECT) */
if (!result)
@@ -316,9 +319,13 @@ out:
if (file->f_flags & O_DIRECT)
audit_cause = "failed(directio)";
+ take_dentry_name_snapshot(&filename, file->f_path.dentry);
+
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
- filename, "collect_data", audit_cause,
- result, 0);
+ filename.name.name, "collect_data",
+ audit_cause, result, 0);
+
+ release_dentry_name_snapshot(&filename);
}
return result;
}
@@ -338,8 +345,8 @@ out:
*
* Must be called with iint->mutex held.
*/
-void ima_store_measurement(struct integrity_iint_cache *iint,
- struct file *file, const unsigned char *filename,
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+ const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
struct ima_template_desc *template_desc)
@@ -382,7 +389,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
ima_free_template_entry(entry);
}
-void ima_audit_measurement(struct integrity_iint_cache *iint,
+void ima_audit_measurement(struct ima_iint_cache *iint,
const unsigned char *filename)
{
struct audit_buffer *ab;
@@ -431,6 +438,7 @@ out:
*/
const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
{
+ struct name_snapshot filename;
char *pathname = NULL;
*pathbuf = __getname();
@@ -444,7 +452,10 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
}
if (!pathname) {
- strscpy(namebuf, path->dentry->d_name.name, NAME_MAX);
+ take_dentry_name_snapshot(&filename, path->dentry);
+ strscpy(namebuf, filename.name.name, NAME_MAX);
+ release_dentry_name_snapshot(&filename);
+
pathname = namebuf;
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 870dde67707b..f435eff4667f 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/file.h>
+#include <linux/binfmts.h>
#include <linux/fs.h>
#include <linux/xattr.h>
#include <linux/magic.h>
@@ -73,19 +74,18 @@ bool is_ima_appraise_enabled(void)
int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode,
int mask, enum ima_hooks func)
{
- u32 secid;
+ struct lsm_prop prop;
if (!ima_appraise)
return 0;
- security_current_getsecid_subj(&secid);
- return ima_match_policy(idmap, inode, current_cred(), secid,
+ security_current_getlsmprop_subj(&prop);
+ return ima_match_policy(idmap, inode, current_cred(), &prop,
func, mask, IMA_APPRAISE | IMA_HASH, NULL,
NULL, NULL, NULL);
}
-static int ima_fix_xattr(struct dentry *dentry,
- struct integrity_iint_cache *iint)
+static int ima_fix_xattr(struct dentry *dentry, struct ima_iint_cache *iint)
{
int rc, offset;
u8 algo = iint->ima_hash->algo;
@@ -106,7 +106,7 @@ static int ima_fix_xattr(struct dentry *dentry,
}
/* Return specific func appraised cached result */
-enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
+enum integrity_status ima_get_cache_status(struct ima_iint_cache *iint,
enum ima_hooks func)
{
switch (func) {
@@ -126,7 +126,7 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
}
}
-static void ima_set_cache_status(struct integrity_iint_cache *iint,
+static void ima_set_cache_status(struct ima_iint_cache *iint,
enum ima_hooks func,
enum integrity_status status)
{
@@ -152,8 +152,7 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,
}
}
-static void ima_cache_flags(struct integrity_iint_cache *iint,
- enum ima_hooks func)
+static void ima_cache_flags(struct ima_iint_cache *iint, enum ima_hooks func)
{
switch (func) {
case MMAP_CHECK:
@@ -276,7 +275,7 @@ static int calc_file_id_hash(enum evm_ima_xattr_type type,
*
* Return 0 on success, error code otherwise.
*/
-static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
+static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
struct evm_ima_xattr_data *xattr_value, int xattr_len,
enum integrity_status *status, const char **cause)
{
@@ -380,7 +379,9 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
}
rc = calc_file_id_hash(IMA_VERITY_DIGSIG, iint->ima_hash->algo,
- iint->ima_hash->digest, &hash.hdr);
+ iint->ima_hash->digest,
+ container_of(&hash.hdr,
+ struct ima_digest_data, hdr));
if (rc) {
*cause = "sigv3-hashing-error";
*status = INTEGRITY_FAIL;
@@ -443,7 +444,7 @@ static int modsig_verify(enum ima_hooks func, const struct modsig *modsig,
*
* Returns -EPERM if the hash is blacklisted.
*/
-int ima_check_blacklist(struct integrity_iint_cache *iint,
+int ima_check_blacklist(struct ima_iint_cache *iint,
const struct modsig *modsig, int pcr)
{
enum hash_algo hash_algo;
@@ -469,6 +470,17 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
return rc;
}
+static bool is_bprm_creds_for_exec(enum ima_hooks func, struct file *file)
+{
+ struct linux_binprm *bprm;
+
+ if (func == BPRM_CHECK) {
+ bprm = container_of(&file, struct linux_binprm, file);
+ return bprm->is_check;
+ }
+ return false;
+}
+
/*
* ima_appraise_measurement - appraise file measurement
*
@@ -477,13 +489,13 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
*
* Return 0 on success, error code otherwise
*/
-int ima_appraise_measurement(enum ima_hooks func,
- struct integrity_iint_cache *iint,
+int ima_appraise_measurement(enum ima_hooks func, struct ima_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig)
{
static const char op[] = "appraise_data";
+ int audit_msgno = AUDIT_INTEGRITY_DATA;
const char *cause = "unknown";
struct dentry *dentry = file_dentry(file);
struct inode *inode = d_backing_inode(dentry);
@@ -495,6 +507,16 @@ int ima_appraise_measurement(enum ima_hooks func,
if (!(inode->i_opflags & IOP_XATTR) && !try_modsig)
return INTEGRITY_UNKNOWN;
+ /*
+ * Unlike any of the other LSM hooks where the kernel enforces file
+ * integrity, enforcing file integrity for the bprm_creds_for_exec()
+ * LSM hook with the AT_EXECVE_CHECK flag is left up to the discretion
+ * of the script interpreter(userspace). Differentiate kernel and
+ * userspace enforced integrity audit messages.
+ */
+ if (is_bprm_creds_for_exec(func, file))
+ audit_msgno = AUDIT_INTEGRITY_USERSPACE;
+
/* If reading the xattr failed and there's no modsig, error out. */
if (rc <= 0 && !try_modsig) {
if (rc && rc != -ENODATA)
@@ -520,7 +542,7 @@ int ima_appraise_measurement(enum ima_hooks func,
}
status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value,
- rc < 0 ? 0 : rc, iint);
+ rc < 0 ? 0 : rc);
switch (status) {
case INTEGRITY_PASS:
case INTEGRITY_PASS_IMMUTABLE:
@@ -570,7 +592,7 @@ out:
(iint->flags & IMA_FAIL_UNVERIFIABLE_SIGS))) {
status = INTEGRITY_FAIL;
cause = "unverifiable-signature";
- integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
+ integrity_audit_msg(audit_msgno, inode, filename,
op, cause, rc, 0);
} else if (status != INTEGRITY_PASS) {
/* Fix mode, but don't replace file signatures. */
@@ -590,7 +612,7 @@ out:
status = INTEGRITY_PASS;
}
- integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
+ integrity_audit_msg(audit_msgno, inode, filename,
op, cause, rc, 0);
} else {
ima_cache_flags(iint, func);
@@ -603,7 +625,7 @@ out:
/*
* ima_update_xattr - update 'security.ima' hash value
*/
-void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
+void ima_update_xattr(struct ima_iint_cache *iint, struct file *file)
{
struct dentry *dentry = file_dentry(file);
int rc = 0;
@@ -629,17 +651,18 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
* ima_inode_post_setattr - reflect file metadata changes
* @idmap: idmap of the mount the inode was found from
* @dentry: pointer to the affected dentry
+ * @ia_valid: for the UID and GID status
*
* Changes to a dentry's metadata might result in needing to appraise.
*
* This function is called from notify_change(), which expects the caller
* to lock the inode's i_mutex.
*/
-void ima_inode_post_setattr(struct mnt_idmap *idmap,
- struct dentry *dentry)
+static void ima_inode_post_setattr(struct mnt_idmap *idmap,
+ struct dentry *dentry, int ia_valid)
{
struct inode *inode = d_backing_inode(dentry);
- struct integrity_iint_cache *iint;
+ struct ima_iint_cache *iint;
int action;
if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
@@ -647,7 +670,7 @@ void ima_inode_post_setattr(struct mnt_idmap *idmap,
return;
action = ima_must_appraise(idmap, inode, MAY_ACCESS, POST_SETATTR);
- iint = integrity_iint_find(inode);
+ iint = ima_iint_find(inode);
if (iint) {
set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
if (!action)
@@ -673,12 +696,12 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
static void ima_reset_appraise_flags(struct inode *inode, int digsig)
{
- struct integrity_iint_cache *iint;
+ struct ima_iint_cache *iint;
if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
return;
- iint = integrity_iint_find(inode);
+ iint = ima_iint_find(inode);
if (!iint)
return;
iint->measured_pcrs = 0;
@@ -749,8 +772,9 @@ static int validate_hash_algo(struct dentry *dentry,
return -EACCES;
}
-int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
- const void *xattr_value, size_t xattr_value_len)
+static int ima_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name, const void *xattr_value,
+ size_t xattr_value_len, int flags)
{
const struct evm_ima_xattr_data *xvalue = xattr_value;
int digsig = 0;
@@ -779,8 +803,8 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
return result;
}
-int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
- const char *acl_name, struct posix_acl *kacl)
+static int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *acl_name, struct posix_acl *kacl)
{
if (evm_revalidate_status(acl_name))
ima_reset_appraise_flags(d_backing_inode(dentry), 0);
@@ -788,7 +812,8 @@ int ima_inode_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
return 0;
}
-int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
+static int ima_inode_removexattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *xattr_name)
{
int result;
@@ -800,3 +825,23 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
}
return result;
}
+
+static int ima_inode_remove_acl(struct mnt_idmap *idmap, struct dentry *dentry,
+ const char *acl_name)
+{
+ return ima_inode_set_acl(idmap, dentry, acl_name, NULL);
+}
+
+static struct security_hook_list ima_appraise_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(inode_post_setattr, ima_inode_post_setattr),
+ LSM_HOOK_INIT(inode_setxattr, ima_inode_setxattr),
+ LSM_HOOK_INIT(inode_set_acl, ima_inode_set_acl),
+ LSM_HOOK_INIT(inode_removexattr, ima_inode_removexattr),
+ LSM_HOOK_INIT(inode_remove_acl, ima_inode_remove_acl),
+};
+
+void __init init_ima_appraise_lsm(const struct lsm_id *lsmid)
+{
+ security_add_hooks(ima_appraise_hooks, ARRAY_SIZE(ima_appraise_hooks),
+ lsmid);
+}
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index f3738b2c8bcd..6f5696d999d0 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -57,11 +57,6 @@ MODULE_PARM_DESC(ahash_bufsize, "Maximum ahash buffer size");
static struct crypto_shash *ima_shash_tfm;
static struct crypto_ahash *ima_ahash_tfm;
-struct ima_algo_desc {
- struct crypto_shash *tfm;
- enum hash_algo algo;
-};
-
int ima_sha1_idx __ro_after_init;
int ima_hash_algo_idx __ro_after_init;
/*
@@ -70,7 +65,7 @@ int ima_hash_algo_idx __ro_after_init;
*/
int ima_extra_slots __ro_after_init;
-static struct ima_algo_desc *ima_algo_array;
+struct ima_algo_desc *ima_algo_array __ro_after_init;
static int __init ima_init_ima_crypto(void)
{
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index cd1683dad3bf..e4a79a9b2d58 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -116,9 +116,31 @@ 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[20]=template digest
+ * char[n]=template digest
* 32bit-le=template name size
* char[n]=template name
* [eventdata length]
@@ -132,7 +154,15 @@ int ima_measurements_show(struct seq_file *m, void *v)
char *template_name;
u32 pcr, namelen, template_data_len; /* temporary fields */
bool is_ima_template = false;
- int i;
+ enum hash_algo algo;
+ int i, algo_idx;
+
+ 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);
/* get entry */
e = qe->entry;
@@ -151,7 +181,7 @@ int ima_measurements_show(struct seq_file *m, void *v)
ima_putc(m, &pcr, sizeof(e->pcr));
/* 2nd: template digest */
- ima_putc(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE);
+ ima_putc(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
/* 3rd: template name size */
namelen = !ima_canonical_fmt ? strlen(template_name) :
@@ -220,7 +250,15 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
struct ima_queue_entry *qe = v;
struct ima_template_entry *e;
char *template_name;
- int i;
+ enum hash_algo algo;
+ int i, algo_idx;
+
+ 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);
/* get entry */
e = qe->entry;
@@ -233,8 +271,8 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
/* 1st: PCR used (config option) */
seq_printf(m, "%2d ", e->pcr);
- /* 2nd: SHA1 template hash */
- ima_print_digest(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE);
+ /* 2nd: template hash */
+ ima_print_digest(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
/* 3th: template name */
seq_printf(m, " %s", template_name);
@@ -379,6 +417,69 @@ 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);
+
+ if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip))
+ securityfs_measurement_list_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;
+
+ 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_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_measurements_ops);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
+ binary_securityfs_measurement_lists[i] = dentry;
+ }
+
+ return 0;
+}
+
/*
* ima_open_policy: sequentialize access to the policy file
*/
@@ -454,6 +555,9 @@ int __init ima_fs_init(void)
{
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);
@@ -465,19 +569,21 @@ int __init ima_fs_init(void)
goto out;
}
+ ret = create_securityfs_measurement_lists();
+ if (ret != 0)
+ goto out;
+
binary_runtime_measurements =
- securityfs_create_file("binary_runtime_measurements",
- S_IRUSR | S_IRGRP, ima_dir, NULL,
- &ima_measurements_ops);
+ 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);
goto out;
}
ascii_runtime_measurements =
- securityfs_create_file("ascii_runtime_measurements",
- S_IRUSR | S_IRGRP, ima_dir, NULL,
- &ima_ascii_measurements_ops);
+ 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);
goto out;
@@ -515,6 +621,9 @@ out:
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/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
new file mode 100644
index 000000000000..00b249101f98
--- /dev/null
+++ b/security/integrity/ima/ima_iint.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2008 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ *
+ * File: ima_iint.c
+ * - implements the IMA hook: ima_inode_free
+ * - cache integrity information in the inode security blob
+ */
+#include <linux/slab.h>
+
+#include "ima.h"
+
+static struct kmem_cache *ima_iint_cache __ro_after_init;
+
+/**
+ * ima_iint_find - Return the iint associated with an inode
+ * @inode: Pointer to the inode
+ *
+ * Return the IMA integrity information (iint) associated with an inode, if the
+ * inode was processed by IMA.
+ *
+ * Return: Found iint or NULL.
+ */
+struct ima_iint_cache *ima_iint_find(struct inode *inode)
+{
+ if (!IS_IMA(inode))
+ return NULL;
+
+ return ima_inode_get_iint(inode);
+}
+
+#define IMA_MAX_NESTING (FILESYSTEM_MAX_STACK_DEPTH + 1)
+
+/*
+ * It is not clear that IMA should be nested at all, but as long is it measures
+ * files both on overlayfs and on underlying fs, we need to annotate the iint
+ * mutex to avoid lockdep false positives related to IMA + overlayfs.
+ * See ovl_lockdep_annotate_inode_mutex_key() for more details.
+ */
+static inline void ima_iint_lockdep_annotate(struct ima_iint_cache *iint,
+ struct inode *inode)
+{
+#ifdef CONFIG_LOCKDEP
+ static struct lock_class_key ima_iint_mutex_key[IMA_MAX_NESTING];
+
+ int depth = inode->i_sb->s_stack_depth;
+
+ if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING))
+ depth = 0;
+
+ lockdep_set_class(&iint->mutex, &ima_iint_mutex_key[depth]);
+#endif
+}
+
+static void ima_iint_init_always(struct ima_iint_cache *iint,
+ struct inode *inode)
+{
+ iint->ima_hash = NULL;
+ iint->real_inode.version = 0;
+ iint->flags = 0UL;
+ iint->atomic_flags = 0UL;
+ iint->ima_file_status = INTEGRITY_UNKNOWN;
+ iint->ima_mmap_status = INTEGRITY_UNKNOWN;
+ iint->ima_bprm_status = INTEGRITY_UNKNOWN;
+ iint->ima_read_status = INTEGRITY_UNKNOWN;
+ iint->ima_creds_status = INTEGRITY_UNKNOWN;
+ iint->measured_pcrs = 0;
+ mutex_init(&iint->mutex);
+ ima_iint_lockdep_annotate(iint, inode);
+}
+
+static void ima_iint_free(struct ima_iint_cache *iint)
+{
+ kfree(iint->ima_hash);
+ mutex_destroy(&iint->mutex);
+ kmem_cache_free(ima_iint_cache, iint);
+}
+
+/**
+ * ima_inode_get - Find or allocate an iint associated with an inode
+ * @inode: Pointer to the inode
+ *
+ * Find an iint associated with an inode, and allocate a new one if not found.
+ * Caller must lock i_mutex.
+ *
+ * Return: An iint on success, NULL on error.
+ */
+struct ima_iint_cache *ima_inode_get(struct inode *inode)
+{
+ struct ima_iint_cache *iint;
+
+ iint = ima_iint_find(inode);
+ if (iint)
+ return iint;
+
+ iint = kmem_cache_alloc(ima_iint_cache, GFP_NOFS);
+ if (!iint)
+ return NULL;
+
+ ima_iint_init_always(iint, inode);
+
+ inode->i_flags |= S_IMA;
+ ima_inode_set_iint(inode, iint);
+
+ return iint;
+}
+
+/**
+ * ima_inode_free_rcu - Called to free an inode via a RCU callback
+ * @inode_security: The inode->i_security pointer
+ *
+ * Free the IMA data associated with an inode.
+ */
+void ima_inode_free_rcu(void *inode_security)
+{
+ struct ima_iint_cache **iint_p = inode_security + ima_blob_sizes.lbs_inode;
+
+ /* *iint_p should be NULL if !IS_IMA(inode) */
+ if (*iint_p)
+ ima_iint_free(*iint_p);
+}
+
+static void ima_iint_init_once(void *foo)
+{
+ struct ima_iint_cache *iint = (struct ima_iint_cache *)foo;
+
+ memset(iint, 0, sizeof(*iint));
+}
+
+void __init ima_iintcache_init(void)
+{
+ ima_iint_cache =
+ kmem_cache_create("ima_iint_cache", sizeof(struct ima_iint_cache),
+ 0, SLAB_PANIC, ima_iint_init_once);
+}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 63979aefc95f..a2f34f2d8ad7 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -44,16 +44,18 @@ static int __init ima_add_boot_aggregate(void)
static const char op[] = "add_boot_aggregate";
const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry;
- struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
+ struct ima_iint_cache tmp_iint, *iint = &tmp_iint;
struct ima_event_data event_data = { .iint = iint,
.filename = boot_aggregate_name };
struct ima_max_digest_data hash;
+ struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
+ struct ima_digest_data, hdr);
int result = -ENOMEM;
int violation = 0;
memset(iint, 0, sizeof(*iint));
memset(&hash, 0, sizeof(hash));
- iint->ima_hash = &hash.hdr;
+ iint->ima_hash = hash_hdr;
iint->ima_hash->algo = ima_hash_algo;
iint->ima_hash->length = hash_digest_size[ima_hash_algo];
@@ -70,7 +72,7 @@ static int __init ima_add_boot_aggregate(void)
* is not found.
*/
if (ima_tpm_chip) {
- result = ima_calc_boot_aggregate(&hash.hdr);
+ result = ima_calc_boot_aggregate(hash_hdr);
if (result < 0) {
audit_cause = "hashing_error";
goto err_out;
@@ -150,6 +152,8 @@ int __init ima_init(void)
ima_init_key_queue();
+ ima_init_reboot_notifier();
+
ima_measure_critical_data("kernel_info", "kernel_version",
UTS_RELEASE, strlen(UTS_RELEASE), false,
NULL, 0);
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index dadc1d138118..9d45f4d26f73 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -30,13 +30,15 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
goto out;
}
+ file.file = NULL;
file.size = segment_size;
file.read_pos = 0;
file.count = sizeof(khdr); /* reserved space */
memset(&khdr, 0, sizeof(khdr));
khdr.version = 1;
- list_for_each_entry_rcu(qe, &ima_measurements, later) {
+ /* This is an append-only list, no need to hold the RCU read lock */
+ list_for_each_entry_rcu(qe, &ima_measurements, later, true) {
if (file.count < file.size) {
khdr.count++;
ima_measurements_show(&file, qe);
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index cc1217ac2c6f..28b8b0db6f9b 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -26,6 +26,7 @@
#include <linux/ima.h>
#include <linux/fs.h>
#include <linux/iversion.h>
+#include <linux/evm.h>
#include "ima.h"
@@ -114,7 +115,7 @@ static int mmap_violation_check(enum ima_hooks func, struct file *file,
*
*/
static void ima_rdwr_violation_check(struct file *file,
- struct integrity_iint_cache *iint,
+ struct ima_iint_cache *iint,
int must_measure,
char **pathbuf,
const char **pathname,
@@ -127,7 +128,7 @@ static void ima_rdwr_violation_check(struct file *file,
if (mode & FMODE_WRITE) {
if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) {
if (!iint)
- iint = integrity_iint_find(inode);
+ iint = ima_iint_find(inode);
/* IMA_MEASURE is set from reader side */
if (iint && test_bit(IMA_MUST_MEASURE,
&iint->atomic_flags))
@@ -153,7 +154,7 @@ static void ima_rdwr_violation_check(struct file *file,
"invalid_pcr", "open_writers");
}
-static void ima_check_last_writer(struct integrity_iint_cache *iint,
+static void ima_check_last_writer(struct ima_iint_cache *iint,
struct inode *inode, struct file *file)
{
fmode_t mode = file->f_mode;
@@ -173,7 +174,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
STATX_CHANGE_COOKIE,
AT_STATX_SYNC_AS_STAT) ||
!(stat.result_mask & STATX_CHANGE_COOKIE) ||
- stat.change_cookie != iint->version) {
+ stat.change_cookie != iint->real_inode.version) {
iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
iint->measured_pcrs = 0;
if (update)
@@ -189,15 +190,15 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
*
* Flag files that changed, based on i_version
*/
-void ima_file_free(struct file *file)
+static void ima_file_free(struct file *file)
{
struct inode *inode = file_inode(file);
- struct integrity_iint_cache *iint;
+ struct ima_iint_cache *iint;
if (!ima_policy_flag || !S_ISREG(inode->i_mode))
return;
- iint = integrity_iint_find(inode);
+ iint = ima_iint_find(inode);
if (!iint)
return;
@@ -205,12 +206,13 @@ void ima_file_free(struct file *file)
}
static int process_measurement(struct file *file, const struct cred *cred,
- u32 secid, char *buf, loff_t size, int mask,
- enum ima_hooks func)
+ struct lsm_prop *prop, char *buf, loff_t size,
+ int mask, enum ima_hooks func)
{
- struct inode *backing_inode, *inode = file_inode(file);
- struct integrity_iint_cache *iint = NULL;
+ struct inode *real_inode, *inode = file_inode(file);
+ struct ima_iint_cache *iint = NULL;
struct ima_template_desc *template_desc = NULL;
+ struct inode *metadata_inode;
char *pathbuf = NULL;
char filename[NAME_MAX];
const char *pathname = NULL;
@@ -230,7 +232,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
* bitmask based on the appraise/audit/measurement policy.
* Included is the appraise submask.
*/
- action = ima_get_action(file_mnt_idmap(file), inode, cred, secid,
+ action = ima_get_action(file_mnt_idmap(file), inode, cred, prop,
mask, func, &pcr, &template_desc, NULL,
&allowed_algos);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK ||
@@ -248,7 +250,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
inode_lock(inode);
if (action) {
- iint = integrity_inode_get(inode);
+ iint = ima_inode_get(inode);
if (!iint)
rc = -ENOMEM;
}
@@ -267,10 +269,13 @@ static int process_measurement(struct file *file, const struct cred *cred,
mutex_lock(&iint->mutex);
if (test_and_clear_bit(IMA_CHANGE_ATTR, &iint->atomic_flags))
- /* reset appraisal flags if ima_inode_post_setattr was called */
+ /*
+ * Reset appraisal flags (action and non-action rule-specific)
+ * if ima_inode_post_setattr was called.
+ */
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
- IMA_NONACTION_FLAGS);
+ IMA_NONACTION_RULE_FLAGS);
/*
* Re-evaulate the file if either the xattr has changed or the
@@ -285,17 +290,28 @@ static int process_measurement(struct file *file, const struct cred *cred,
iint->measured_pcrs = 0;
}
- /* Detect and re-evaluate changes made to the backing file. */
- backing_inode = d_real_inode(file_dentry(file));
- if (backing_inode != inode &&
+ /*
+ * On stacked filesystems, detect and re-evaluate file data and
+ * metadata changes.
+ */
+ real_inode = d_real_inode(file_dentry(file));
+ if (real_inode != inode &&
(action & IMA_DO_MASK) && (iint->flags & IMA_DONE_MASK)) {
- if (!IS_I_VERSION(backing_inode) ||
- backing_inode->i_sb->s_dev != iint->real_dev ||
- backing_inode->i_ino != iint->real_ino ||
- !inode_eq_iversion(backing_inode, iint->version)) {
+ if (!IS_I_VERSION(real_inode) ||
+ integrity_inode_attrs_changed(&iint->real_inode,
+ real_inode)) {
iint->flags &= ~IMA_DONE_MASK;
iint->measured_pcrs = 0;
}
+
+ /*
+ * Reset the EVM status when metadata changed.
+ */
+ metadata_inode = d_inode(d_real(file_dentry(file),
+ D_REAL_METADATA));
+ if (evm_metadata_changed(inode, metadata_inode))
+ iint->flags &= ~(IMA_APPRAISED |
+ IMA_APPRAISED_SUBMASK);
}
/* Determine if already appraised/measured based on bitmask
@@ -427,26 +443,26 @@ out:
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_file_mmap(struct file *file, unsigned long reqprot,
- unsigned long prot, unsigned long flags)
+static int ima_file_mmap(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags)
{
- u32 secid;
+ struct lsm_prop prop;
int ret;
if (!file)
return 0;
- security_current_getsecid_subj(&secid);
+ security_current_getlsmprop_subj(&prop);
if (reqprot & PROT_EXEC) {
- ret = process_measurement(file, current_cred(), secid, NULL,
+ ret = process_measurement(file, current_cred(), &prop, NULL,
0, MAY_EXEC, MMAP_CHECK_REQPROT);
if (ret)
return ret;
}
if (prot & PROT_EXEC)
- return process_measurement(file, current_cred(), secid, NULL,
+ return process_measurement(file, current_cred(), &prop, NULL,
0, MAY_EXEC, MMAP_CHECK);
return 0;
@@ -455,7 +471,8 @@ int ima_file_mmap(struct file *file, unsigned long reqprot,
/**
* ima_file_mprotect - based on policy, limit mprotect change
* @vma: vm_area_struct protection is set to
- * @prot: contains the protection that will be applied by the kernel.
+ * @reqprot: protection requested by the application
+ * @prot: protection that will be applied by the kernel
*
* Files can be mmap'ed read/write and later changed to execute to circumvent
* IMA's mmap appraisal policy rules. Due to locking issues (mmap semaphore
@@ -465,7 +482,8 @@ int ima_file_mmap(struct file *file, unsigned long reqprot,
*
* On mprotect change success, return 0. On failure, return -EACESS.
*/
-int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
+static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot)
{
struct ima_template_desc *template = NULL;
struct file *file;
@@ -473,9 +491,9 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
char *pathbuf = NULL;
const char *pathname = NULL;
struct inode *inode;
+ struct lsm_prop prop;
int result = 0;
int action;
- u32 secid;
int pcr;
/* Is mprotect making an mmap'ed file executable? */
@@ -483,13 +501,13 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
!(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC))
return 0;
- security_current_getsecid_subj(&secid);
+ security_current_getlsmprop_subj(&prop);
inode = file_inode(vma->vm_file);
action = ima_get_action(file_mnt_idmap(vma->vm_file), inode,
- current_cred(), secid, MAY_EXEC, MMAP_CHECK,
+ current_cred(), &prop, MAY_EXEC, MMAP_CHECK,
&pcr, &template, NULL, NULL);
action |= ima_get_action(file_mnt_idmap(vma->vm_file), inode,
- current_cred(), secid, MAY_EXEC,
+ current_cred(), &prop, MAY_EXEC,
MMAP_CHECK_REQPROT, &pcr, &template, NULL,
NULL);
@@ -523,23 +541,51 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_bprm_check(struct linux_binprm *bprm)
+static int ima_bprm_check(struct linux_binprm *bprm)
{
int ret;
- u32 secid;
+ struct lsm_prop prop;
- security_current_getsecid_subj(&secid);
- ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0,
- MAY_EXEC, BPRM_CHECK);
+ security_current_getlsmprop_subj(&prop);
+ ret = process_measurement(bprm->file, current_cred(),
+ &prop, NULL, 0, MAY_EXEC, BPRM_CHECK);
if (ret)
return ret;
- security_cred_getsecid(bprm->cred, &secid);
- return process_measurement(bprm->file, bprm->cred, secid, NULL, 0,
+ security_cred_getlsmprop(bprm->cred, &prop);
+ return process_measurement(bprm->file, bprm->cred, &prop, NULL, 0,
MAY_EXEC, CREDS_CHECK);
}
/**
+ * ima_bprm_creds_for_exec - collect/store/appraise measurement.
+ * @bprm: contains the linux_binprm structure
+ *
+ * Based on the IMA policy and the execveat(2) AT_EXECVE_CHECK flag, measure
+ * and appraise the integrity of a file to be executed by script interpreters.
+ * Unlike any of the other LSM hooks where the kernel enforces file integrity,
+ * enforcing file integrity is left up to the discretion of the script
+ * interpreter (userspace).
+ *
+ * On success return 0. On integrity appraisal error, assuming the file
+ * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
+ */
+static int ima_bprm_creds_for_exec(struct linux_binprm *bprm)
+{
+ /*
+ * As security_bprm_check() is called multiple times, both
+ * the script and the shebang interpreter are measured, appraised,
+ * and audited. Limit usage of this LSM hook to just measuring,
+ * appraising, and auditing the indirect script execution
+ * (e.g. ./sh example.sh).
+ */
+ if (!bprm->is_check)
+ return 0;
+
+ return ima_bprm_check(bprm);
+}
+
+/**
* ima_file_check - based on policy, collect/store measurement.
* @file: pointer to the file to be measured
* @mask: contains MAY_READ, MAY_WRITE, MAY_EXEC or MAY_APPEND
@@ -549,25 +595,24 @@ int ima_bprm_check(struct linux_binprm *bprm)
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_file_check(struct file *file, int mask)
+static int ima_file_check(struct file *file, int mask)
{
- u32 secid;
+ struct lsm_prop prop;
- security_current_getsecid_subj(&secid);
- return process_measurement(file, current_cred(), secid, NULL, 0,
+ security_current_getlsmprop_subj(&prop);
+ return process_measurement(file, current_cred(), &prop, NULL, 0,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
MAY_APPEND), FILE_CHECK);
}
-EXPORT_SYMBOL_GPL(ima_file_check);
static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
size_t buf_size)
{
- struct integrity_iint_cache *iint = NULL, tmp_iint;
+ struct ima_iint_cache *iint = NULL, tmp_iint;
int rc, hash_algo;
if (ima_policy_flag) {
- iint = integrity_iint_find(inode);
+ iint = ima_iint_find(inode);
if (iint)
mutex_lock(&iint->mutex);
}
@@ -577,7 +622,6 @@ static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
mutex_unlock(&iint->mutex);
memset(&tmp_iint, 0, sizeof(tmp_iint));
- tmp_iint.inode = inode;
mutex_init(&tmp_iint.mutex);
rc = ima_collect_measurement(&tmp_iint, file, NULL, 0,
@@ -683,10 +727,11 @@ EXPORT_SYMBOL_GPL(ima_inode_hash);
* Skip calling process_measurement(), but indicate which newly, created
* tmpfiles are in policy.
*/
-void ima_post_create_tmpfile(struct mnt_idmap *idmap,
- struct inode *inode)
+static void ima_post_create_tmpfile(struct mnt_idmap *idmap,
+ struct inode *inode)
+
{
- struct integrity_iint_cache *iint;
+ struct ima_iint_cache *iint;
int must_appraise;
if (!ima_policy_flag || !S_ISREG(inode->i_mode))
@@ -698,7 +743,7 @@ void ima_post_create_tmpfile(struct mnt_idmap *idmap,
return;
/* Nothing to do if we can't allocate memory */
- iint = integrity_inode_get(inode);
+ iint = ima_inode_get(inode);
if (!iint)
return;
@@ -715,10 +760,9 @@ void ima_post_create_tmpfile(struct mnt_idmap *idmap,
* Mark files created via the mknodat syscall as new, so that the
* file data can be written later.
*/
-void ima_post_path_mknod(struct mnt_idmap *idmap,
- struct dentry *dentry)
+static void ima_post_path_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
{
- struct integrity_iint_cache *iint;
+ struct ima_iint_cache *iint;
struct inode *inode = dentry->d_inode;
int must_appraise;
@@ -731,7 +775,7 @@ void ima_post_path_mknod(struct mnt_idmap *idmap,
return;
/* Nothing to do if we can't allocate memory */
- iint = integrity_inode_get(inode);
+ iint = ima_inode_get(inode);
if (!iint)
return;
@@ -751,11 +795,11 @@ void ima_post_path_mknod(struct mnt_idmap *idmap,
*
* For permission return 0, otherwise return -EACCES.
*/
-int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
- bool contents)
+static int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
+ bool contents)
{
enum ima_hooks func;
- u32 secid;
+ struct lsm_prop prop;
/*
* Do devices using pre-allocated memory run the risk of the
@@ -775,9 +819,9 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
/* Read entire file for all partial reads. */
func = read_idmap[read_id] ?: FILE_CHECK;
- security_current_getsecid_subj(&secid);
- return process_measurement(file, current_cred(), secid, NULL,
- 0, MAY_READ, func);
+ security_current_getlsmprop_subj(&prop);
+ return process_measurement(file, current_cred(), &prop, NULL, 0,
+ MAY_READ, func);
}
const int read_idmap[READING_MAX_ID] = {
@@ -801,11 +845,11 @@ const int read_idmap[READING_MAX_ID] = {
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_post_read_file(struct file *file, void *buf, loff_t size,
- enum kernel_read_file_id read_id)
+static int ima_post_read_file(struct file *file, char *buf, loff_t size,
+ enum kernel_read_file_id read_id)
{
enum ima_hooks func;
- u32 secid;
+ struct lsm_prop prop;
/* permit signed certs */
if (!file && read_id == READING_X509_CERTIFICATE)
@@ -818,8 +862,8 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
}
func = read_idmap[read_id] ?: FILE_CHECK;
- security_current_getsecid_subj(&secid);
- return process_measurement(file, current_cred(), secid, buf, size,
+ security_current_getlsmprop_subj(&prop);
+ return process_measurement(file, current_cred(), &prop, buf, size,
MAY_READ, func);
}
@@ -835,7 +879,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
*
* For permission return 0, otherwise return -EACCES.
*/
-int ima_load_data(enum kernel_load_data_id id, bool contents)
+static int ima_load_data(enum kernel_load_data_id id, bool contents)
{
bool ima_enforce, sig_enforce;
@@ -889,9 +933,9 @@ int ima_load_data(enum kernel_load_data_id id, bool contents)
* On success return 0. On integrity appraisal error, assuming the file
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
*/
-int ima_post_load_data(char *buf, loff_t size,
- enum kernel_load_data_id load_id,
- char *description)
+static int ima_post_load_data(char *buf, loff_t size,
+ enum kernel_load_data_id load_id,
+ char *description)
{
if (load_id == LOADING_FIRMWARE) {
if ((ima_appraise & IMA_APPRAISE_FIRMWARE) &&
@@ -902,6 +946,13 @@ int ima_post_load_data(char *buf, loff_t size,
return 0;
}
+ /*
+ * Measure the init_module syscall buffer containing the ELF image.
+ */
+ if (load_id == LOADING_MODULE)
+ ima_measure_critical_data("modules", "init_module",
+ buf, size, true, NULL, 0);
+
return 0;
}
@@ -934,18 +985,20 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
int ret = 0;
const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry = NULL;
- struct integrity_iint_cache iint = {};
+ struct ima_iint_cache iint = {};
struct ima_event_data event_data = {.iint = &iint,
.filename = eventname,
.buf = buf,
.buf_len = size};
struct ima_template_desc *template;
struct ima_max_digest_data hash;
+ struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
+ struct ima_digest_data, hdr);
char digest_hash[IMA_MAX_DIGEST_SIZE];
int digest_hash_len = hash_digest_size[ima_hash_algo];
int violation = 0;
int action = 0;
- u32 secid;
+ struct lsm_prop prop;
if (digest && digest_len < digest_hash_len)
return -EINVAL;
@@ -961,16 +1014,16 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
}
/*
- * Both LSM hooks and auxilary based buffer measurements are
- * based on policy. To avoid code duplication, differentiate
- * between the LSM hooks and auxilary buffer measurements,
+ * Both LSM hooks and auxiliary based buffer measurements are
+ * based on policy. To avoid code duplication, differentiate
+ * between the LSM hooks and auxiliary buffer measurements,
* retrieving the policy rule information only for the LSM hook
* buffer measurements.
*/
if (func) {
- security_current_getsecid_subj(&secid);
+ security_current_getlsmprop_subj(&prop);
action = ima_get_action(idmap, inode, current_cred(),
- secid, 0, func, &pcr, &template,
+ &prop, 0, func, &pcr, &template,
func_data, NULL);
if (!(action & IMA_MEASURE) && !digest)
return -ENOENT;
@@ -979,7 +1032,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
if (!pcr)
pcr = CONFIG_IMA_MEASURE_PCR_IDX;
- iint.ima_hash = &hash.hdr;
+ iint.ima_hash = hash_hdr;
iint.ima_hash->algo = ima_hash_algo;
iint.ima_hash->length = hash_digest_size[ima_hash_algo];
@@ -990,7 +1043,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
}
if (buf_hash) {
- memcpy(digest_hash, hash.hdr.digest, digest_hash_len);
+ memcpy(digest_hash, hash_hdr->digest, digest_hash_len);
ret = ima_calc_buffer_hash(digest_hash, digest_hash_len,
iint.ima_hash);
@@ -1040,19 +1093,16 @@ out:
*/
void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
{
- struct fd f;
-
if (!buf || !size)
return;
- f = fdget(kernel_fd);
- if (!f.file)
+ CLASS(fd, f)(kernel_fd);
+ if (fd_empty(f))
return;
- process_buffer_measurement(file_mnt_idmap(f.file), file_inode(f.file),
+ process_buffer_measurement(file_mnt_idmap(fd_file(f)), file_inode(fd_file(f)),
buf, size, "kexec-cmdline", KEXEC_CMDLINE, 0,
NULL, false, NULL, 0);
- fdput(f);
}
/**
@@ -1089,6 +1139,39 @@ int ima_measure_critical_data(const char *event_label,
}
EXPORT_SYMBOL_GPL(ima_measure_critical_data);
+#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
+
+/**
+ * ima_kernel_module_request - Prevent crypto-pkcs1(rsa,*) requests
+ * @kmod_name: kernel module name
+ *
+ * Avoid a verification loop where verifying the signature of the modprobe
+ * binary requires executing modprobe itself. Since the modprobe iint->mutex
+ * is already held when the signature verification is performed, a deadlock
+ * occurs as soon as modprobe is executed within the critical region, since
+ * the same lock cannot be taken again.
+ *
+ * This happens when public_key_verify_signature(), in case of RSA algorithm,
+ * use alg_name to store internal information in order to construct an
+ * algorithm on the fly, but crypto_larval_lookup() will try to use alg_name
+ * in order to load a kernel module with same name.
+ *
+ * Since we don't have any real "crypto-pkcs1(rsa,*)" kernel modules,
+ * we are safe to fail such module request from crypto_larval_lookup(), and
+ * avoid the verification loop.
+ *
+ * Return: Zero if it is safe to load the kernel module, -EINVAL otherwise.
+ */
+static int ima_kernel_module_request(char *kmod_name)
+{
+ if (strncmp(kmod_name, "crypto-pkcs1(rsa,", 17) == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+#endif /* CONFIG_INTEGRITY_ASYMMETRIC_KEYS */
+
static int __init init_ima(void)
{
int error;
@@ -1120,4 +1203,50 @@ static int __init init_ima(void)
return error;
}
+static struct security_hook_list ima_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(bprm_check_security, ima_bprm_check),
+ LSM_HOOK_INIT(bprm_creds_for_exec, ima_bprm_creds_for_exec),
+ LSM_HOOK_INIT(file_post_open, ima_file_check),
+ LSM_HOOK_INIT(inode_post_create_tmpfile, ima_post_create_tmpfile),
+ LSM_HOOK_INIT(file_release, ima_file_free),
+ LSM_HOOK_INIT(mmap_file, ima_file_mmap),
+ LSM_HOOK_INIT(file_mprotect, ima_file_mprotect),
+ LSM_HOOK_INIT(kernel_load_data, ima_load_data),
+ LSM_HOOK_INIT(kernel_post_load_data, ima_post_load_data),
+ LSM_HOOK_INIT(kernel_read_file, ima_read_file),
+ LSM_HOOK_INIT(kernel_post_read_file, ima_post_read_file),
+ LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod),
+#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
+ LSM_HOOK_INIT(key_post_create_or_update, ima_post_key_create_or_update),
+#endif
+#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
+ LSM_HOOK_INIT(kernel_module_request, ima_kernel_module_request),
+#endif
+ LSM_HOOK_INIT(inode_free_security_rcu, ima_inode_free_rcu),
+};
+
+static const struct lsm_id ima_lsmid = {
+ .name = "ima",
+ .id = LSM_ID_IMA,
+};
+
+static int __init init_ima_lsm(void)
+{
+ ima_iintcache_init();
+ security_add_hooks(ima_hooks, ARRAY_SIZE(ima_hooks), &ima_lsmid);
+ init_ima_appraise_lsm(&ima_lsmid);
+ return 0;
+}
+
+struct lsm_blob_sizes ima_blob_sizes __ro_after_init = {
+ .lbs_inode = sizeof(struct ima_iint_cache *),
+};
+
+DEFINE_LSM(ima) = {
+ .name = "ima",
+ .init = init_ima_lsm,
+ .order = LSM_ORDER_LAST,
+ .blobs = &ima_blob_sizes,
+};
+
late_initcall(init_ima); /* Start IMA after the TPM is available */
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index f69062617754..128fab897930 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -49,7 +49,7 @@
#define DONT_HASH 0x0200
#define INVALID_PCR(a) (((a) < 0) || \
- (a) >= (sizeof_field(struct integrity_iint_cache, measured_pcrs) * 8))
+ (a) >= (sizeof_field(struct ima_iint_cache, measured_pcrs) * 8))
int ima_policy_flag;
static int temp_ima_appraise;
@@ -148,7 +148,8 @@ static struct ima_rule_entry dont_measure_rules[] __ro_after_init = {
{.action = DONT_MEASURE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC},
- {.action = DONT_MEASURE, .fsmagic = TMPFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = TMPFS_MAGIC, .func = FILE_CHECK,
+ .flags = IMA_FSMAGIC | IMA_FUNC},
{.action = DONT_MEASURE, .fsmagic = DEVPTS_SUPER_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC},
@@ -401,7 +402,8 @@ static void ima_free_rule(struct ima_rule_entry *entry)
kfree(entry);
}
-static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
+static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry,
+ gfp_t gfp)
{
struct ima_rule_entry *nentry;
int i;
@@ -410,7 +412,7 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
* Immutable elements are copied over as pointers and data; only
* lsm rules can change
*/
- nentry = kmemdup(entry, sizeof(*nentry), GFP_KERNEL);
+ nentry = kmemdup(entry, sizeof(*nentry), gfp);
if (!nentry)
return NULL;
@@ -425,7 +427,8 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
ima_filter_rule_init(nentry->lsm[i].type, Audit_equal,
nentry->lsm[i].args_p,
- &nentry->lsm[i].rule);
+ &nentry->lsm[i].rule,
+ gfp);
if (!nentry->lsm[i].rule)
pr_warn("rule for LSM \'%s\' is undefined\n",
nentry->lsm[i].args_p);
@@ -438,7 +441,7 @@ static int ima_lsm_update_rule(struct ima_rule_entry *entry)
int i;
struct ima_rule_entry *nentry;
- nentry = ima_lsm_copy_rule(entry);
+ nentry = ima_lsm_copy_rule(entry, GFP_KERNEL);
if (!nentry)
return -ENOMEM;
@@ -555,7 +558,7 @@ static bool ima_match_rule_data(struct ima_rule_entry *rule,
* @idmap: idmap of the mount the inode was found from
* @inode: a pointer to an inode
* @cred: a pointer to a credentials structure for user validation
- * @secid: the secid of the task to be validated
+ * @prop: LSM properties of the task to be validated
* @func: LIM hook identifier
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
* @func_data: func specific data, may be NULL
@@ -565,7 +568,7 @@ static bool ima_match_rule_data(struct ima_rule_entry *rule,
static bool ima_match_rules(struct ima_rule_entry *rule,
struct mnt_idmap *idmap,
struct inode *inode, const struct cred *cred,
- u32 secid, enum ima_hooks func, int mask,
+ struct lsm_prop *prop, enum ima_hooks func, int mask,
const char *func_data)
{
int i;
@@ -633,7 +636,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
return false;
for (i = 0; i < MAX_LSM_RULES; i++) {
int rc = 0;
- u32 osid;
+ struct lsm_prop inode_prop = { };
if (!lsm_rule->lsm[i].rule) {
if (!lsm_rule->lsm[i].args_p)
@@ -647,15 +650,16 @@ retry:
case LSM_OBJ_USER:
case LSM_OBJ_ROLE:
case LSM_OBJ_TYPE:
- security_inode_getsecid(inode, &osid);
- rc = ima_filter_rule_match(osid, lsm_rule->lsm[i].type,
+ security_inode_getlsmprop(inode, &inode_prop);
+ rc = ima_filter_rule_match(&inode_prop,
+ lsm_rule->lsm[i].type,
Audit_equal,
lsm_rule->lsm[i].rule);
break;
case LSM_SUBJ_USER:
case LSM_SUBJ_ROLE:
case LSM_SUBJ_TYPE:
- rc = ima_filter_rule_match(secid, lsm_rule->lsm[i].type,
+ rc = ima_filter_rule_match(prop, lsm_rule->lsm[i].type,
Audit_equal,
lsm_rule->lsm[i].rule);
break;
@@ -664,7 +668,7 @@ retry:
}
if (rc == -ESTALE && !rule_reinitialized) {
- lsm_rule = ima_lsm_copy_rule(rule);
+ lsm_rule = ima_lsm_copy_rule(rule, GFP_ATOMIC);
if (lsm_rule) {
rule_reinitialized = true;
goto retry;
@@ -718,7 +722,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* @inode: pointer to an inode for which the policy decision is being made
* @cred: pointer to a credentials structure for which the policy decision is
* being made
- * @secid: LSM secid of the task to be validated
+ * @prop: LSM properties of the task to be validated
* @func: IMA hook identifier
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
* @flags: IMA actions to consider (e.g. IMA_MEASURE | IMA_APPRAISE)
@@ -735,8 +739,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* than writes so ima_match_policy() is classical RCU candidate.
*/
int ima_match_policy(struct mnt_idmap *idmap, struct inode *inode,
- const struct cred *cred, u32 secid, enum ima_hooks func,
- int mask, int flags, int *pcr,
+ const struct cred *cred, struct lsm_prop *prop,
+ enum ima_hooks func, int mask, int flags, int *pcr,
struct ima_template_desc **template_desc,
const char *func_data, unsigned int *allowed_algos)
{
@@ -754,7 +758,7 @@ int ima_match_policy(struct mnt_idmap *idmap, struct inode *inode,
if (!(entry->action & actmask))
continue;
- if (!ima_match_rules(entry, idmap, inode, cred, secid,
+ if (!ima_match_rules(entry, idmap, inode, cred, prop,
func, mask, func_data))
continue;
@@ -1140,7 +1144,8 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
entry->lsm[lsm_rule].type = audit_type;
result = ima_filter_rule_init(entry->lsm[lsm_rule].type, Audit_equal,
entry->lsm[lsm_rule].args_p,
- &entry->lsm[lsm_rule].rule);
+ &entry->lsm[lsm_rule].rule,
+ GFP_KERNEL);
if (!entry->lsm[lsm_rule].rule) {
pr_warn("rule for LSM \'%s\' is undefined\n",
entry->lsm[lsm_rule].args_p);
@@ -1427,7 +1432,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
int token;
unsigned long lnum;
- if (result < 0)
+ if (result < 0 || *p == '#') /* ignore suffixed comment */
break;
if ((*p == '\0') || (*p == ' ') || (*p == '\t'))
continue;
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 532da87ce519..83d53824aa98 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -16,6 +16,7 @@
*/
#include <linux/rculist.h>
+#include <linux/reboot.h>
#include <linux/slab.h>
#include "ima.h"
@@ -44,6 +45,12 @@ struct ima_h_table ima_htable = {
*/
static DEFINE_MUTEX(ima_extend_list_mutex);
+/*
+ * Used internally by the kernel to suspend measurements.
+ * Protected by ima_extend_list_mutex.
+ */
+static bool ima_measurements_suspended;
+
/* lookup up the digest value in the hash table, and return the entry */
static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
int pcr)
@@ -168,6 +175,18 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
int result = 0, tpmresult = 0;
mutex_lock(&ima_extend_list_mutex);
+
+ /*
+ * Avoid appending to the measurement log when the TPM subsystem has
+ * been shut down while preparing for system reboot.
+ */
+ if (ima_measurements_suspended) {
+ audit_cause = "measurements_suspended";
+ audit_info = 0;
+ result = -ENODEV;
+ goto out;
+ }
+
if (!violation && !IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) {
if (ima_lookup_digest_entry(digest, entry->pcr)) {
audit_cause = "hash_exists";
@@ -211,6 +230,31 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry)
return result;
}
+static void ima_measurements_suspend(void)
+{
+ mutex_lock(&ima_extend_list_mutex);
+ ima_measurements_suspended = true;
+ mutex_unlock(&ima_extend_list_mutex);
+}
+
+static int ima_reboot_notifier(struct notifier_block *nb,
+ unsigned long action,
+ void *data)
+{
+ ima_measurements_suspend();
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ima_reboot_nb = {
+ .notifier_call = ima_reboot_notifier,
+};
+
+void __init ima_init_reboot_notifier(void)
+{
+ register_reboot_notifier(&ima_reboot_nb);
+}
+
int __init ima_init_digests(void)
{
u16 digest_size;
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 6cd0add524cd..0e627eac9c33 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -318,15 +318,21 @@ static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize,
hash_algo_name[hash_algo]);
}
- if (digest)
+ if (digest) {
memcpy(buffer + offset, digest, digestsize);
- else
+ } else {
/*
* If digest is NULL, the event being recorded is a violation.
* Make room for the digest by increasing the offset by the
- * hash algorithm digest size.
+ * hash algorithm digest size. If the hash algorithm is not
+ * specified increase the offset by IMA_DIGEST_SIZE which
+ * fits SHA1 or MD5
*/
- offset += hash_digest_size[hash_algo];
+ if (hash_algo < HASH_ALGO__LAST)
+ offset += hash_digest_size[hash_algo];
+ else
+ offset += IMA_DIGEST_SIZE;
+ }
return ima_write_template_field_data(buffer, offset + digestsize,
fmt, field_data);
@@ -339,6 +345,8 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
struct ima_max_digest_data hash;
+ struct ima_digest_data *hash_hdr = container_of(&hash.hdr,
+ struct ima_digest_data, hdr);
u8 *cur_digest = NULL;
u32 cur_digestsize = 0;
struct inode *inode;
@@ -358,7 +366,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
if ((const char *)event_data->filename == boot_aggregate_name) {
if (ima_tpm_chip) {
hash.hdr.algo = HASH_ALGO_SHA1;
- result = ima_calc_boot_aggregate(&hash.hdr);
+ result = ima_calc_boot_aggregate(hash_hdr);
/* algo can change depending on available PCR banks */
if (!result && hash.hdr.algo != HASH_ALGO_SHA1)
@@ -368,7 +376,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
memset(&hash, 0, sizeof(hash));
}
- cur_digest = hash.hdr.digest;
+ cur_digest = hash_hdr->digest;
cur_digestsize = hash_digest_size[HASH_ALGO_SHA1];
goto out;
}
@@ -379,14 +387,14 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
inode = file_inode(event_data->file);
hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ?
ima_hash_algo : HASH_ALGO_SHA1;
- result = ima_calc_file_hash(event_data->file, &hash.hdr);
+ result = ima_calc_file_hash(event_data->file, hash_hdr);
if (result) {
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
event_data->filename, "collect_data",
"failed", result, 0);
return result;
}
- cur_digest = hash.hdr.digest;
+ cur_digest = hash_hdr->digest;
cur_digestsize = hash.hdr.length;
out:
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
@@ -483,7 +491,10 @@ static int ima_eventname_init_common(struct ima_event_data *event_data,
bool size_limit)
{
const char *cur_filename = NULL;
+ struct name_snapshot filename;
u32 cur_filename_len = 0;
+ bool snapshot = false;
+ int ret;
BUG_ON(event_data->filename == NULL && event_data->file == NULL);
@@ -496,7 +507,10 @@ static int ima_eventname_init_common(struct ima_event_data *event_data,
}
if (event_data->file) {
- cur_filename = event_data->file->f_path.dentry->d_name.name;
+ take_dentry_name_snapshot(&filename,
+ event_data->file->f_path.dentry);
+ snapshot = true;
+ cur_filename = filename.name.name;
cur_filename_len = strlen(cur_filename);
} else
/*
@@ -505,8 +519,13 @@ static int ima_eventname_init_common(struct ima_event_data *event_data,
*/
cur_filename_len = IMA_EVENT_NAME_LEN_MAX;
out:
- return ima_write_template_field_data(cur_filename, cur_filename_len,
- DATA_FMT_STRING, field_data);
+ ret = ima_write_template_field_data(cur_filename, cur_filename_len,
+ DATA_FMT_STRING, field_data);
+
+ if (snapshot)
+ release_dentry_name_snapshot(&filename);
+
+ return ret;
}
/*
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 9561db7cf6b4..c2c2da691123 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -18,60 +18,7 @@
#include <crypto/hash.h>
#include <linux/key.h>
#include <linux/audit.h>
-
-/* iint action cache flags */
-#define IMA_MEASURE 0x00000001
-#define IMA_MEASURED 0x00000002
-#define IMA_APPRAISE 0x00000004
-#define IMA_APPRAISED 0x00000008
-/*#define IMA_COLLECT 0x00000010 do not use this flag */
-#define IMA_COLLECTED 0x00000020
-#define IMA_AUDIT 0x00000040
-#define IMA_AUDITED 0x00000080
-#define IMA_HASH 0x00000100
-#define IMA_HASHED 0x00000200
-
-/* iint policy rule cache flags */
-#define IMA_NONACTION_FLAGS 0xff000000
-#define IMA_DIGSIG_REQUIRED 0x01000000
-#define IMA_PERMIT_DIRECTIO 0x02000000
-#define IMA_NEW_FILE 0x04000000
-#define EVM_IMMUTABLE_DIGSIG 0x08000000
-#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
-#define IMA_MODSIG_ALLOWED 0x20000000
-#define IMA_CHECK_BLACKLIST 0x40000000
-#define IMA_VERITY_REQUIRED 0x80000000
-
-#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
- IMA_HASH | IMA_APPRAISE_SUBMASK)
-#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
- IMA_HASHED | IMA_COLLECTED | \
- IMA_APPRAISED_SUBMASK)
-
-/* iint subaction appraise cache flags */
-#define IMA_FILE_APPRAISE 0x00001000
-#define IMA_FILE_APPRAISED 0x00002000
-#define IMA_MMAP_APPRAISE 0x00004000
-#define IMA_MMAP_APPRAISED 0x00008000
-#define IMA_BPRM_APPRAISE 0x00010000
-#define IMA_BPRM_APPRAISED 0x00020000
-#define IMA_READ_APPRAISE 0x00040000
-#define IMA_READ_APPRAISED 0x00080000
-#define IMA_CREDS_APPRAISE 0x00100000
-#define IMA_CREDS_APPRAISED 0x00200000
-#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
- IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
- IMA_CREDS_APPRAISE)
-#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
- IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
- IMA_CREDS_APPRAISED)
-
-/* iint cache atomic_flags */
-#define IMA_CHANGE_XATTR 0
-#define IMA_UPDATE_XATTR 1
-#define IMA_CHANGE_ATTR 2
-#define IMA_DIGSIG 3
-#define IMA_MUST_MEASURE 4
+#include <linux/lsm_hooks.h>
enum evm_ima_xattr_type {
IMA_XATTR_DIGEST = 0x01,
@@ -84,19 +31,26 @@ enum evm_ima_xattr_type {
};
struct evm_ima_xattr_data {
- u8 type;
+ /* New members must be added within the __struct_group() macro below. */
+ __struct_group(evm_ima_xattr_data_hdr, hdr, __packed,
+ u8 type;
+ );
u8 data[];
} __packed;
+static_assert(offsetof(struct evm_ima_xattr_data, data) == sizeof(struct evm_ima_xattr_data_hdr),
+ "struct member likely outside of __struct_group()");
/* Only used in the EVM HMAC code. */
struct evm_xattr {
- struct evm_ima_xattr_data data;
+ struct evm_ima_xattr_data_hdr data;
u8 digest[SHA1_DIGEST_SIZE];
} __packed;
#define IMA_MAX_DIGEST_SIZE HASH_MAX_DIGESTSIZE
struct ima_digest_data {
+ /* New members must be added within the __struct_group() macro below. */
+ __struct_group(ima_digest_data_hdr, hdr, __packed,
u8 algo;
u8 length;
union {
@@ -110,15 +64,18 @@ struct ima_digest_data {
} ng;
u8 data[2];
} xattr;
+ );
u8 digest[];
} __packed;
+static_assert(offsetof(struct ima_digest_data, digest) == sizeof(struct ima_digest_data_hdr),
+ "struct member likely outside of __struct_group()");
/*
* Instead of wrapping the ima_digest_data struct inside a local structure
* with the maximum hash size, define ima_max_digest_data struct.
*/
struct ima_max_digest_data {
- struct ima_digest_data hdr;
+ struct ima_digest_data_hdr hdr;
u8 digest[HASH_MAX_DIGESTSIZE];
} __packed;
@@ -155,31 +112,6 @@ struct ima_file_id {
__u8 hash[HASH_MAX_DIGESTSIZE];
} __packed;
-/* integrity data associated with an inode */
-struct integrity_iint_cache {
- struct rb_node rb_node; /* rooted in integrity_iint_tree */
- struct mutex mutex; /* protects: version, flags, digest */
- struct inode *inode; /* back pointer to inode in question */
- u64 version; /* track inode changes */
- unsigned long flags;
- unsigned long measured_pcrs;
- unsigned long atomic_flags;
- unsigned long real_ino;
- dev_t real_dev;
- enum integrity_status ima_file_status:4;
- enum integrity_status ima_mmap_status:4;
- enum integrity_status ima_bprm_status:4;
- enum integrity_status ima_read_status:4;
- enum integrity_status ima_creds_status:4;
- enum integrity_status evm_status:4;
- struct ima_digest_data *ima_hash;
-};
-
-/* rbtree tree calls to lookup, insert, delete
- * integrity data associated with an inode.
- */
-struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
-
int integrity_kernel_read(struct file *file, loff_t offset,
void *addr, unsigned long count);
diff --git a/security/ipe/.gitignore b/security/ipe/.gitignore
new file mode 100644
index 000000000000..6e9939be1cb7
--- /dev/null
+++ b/security/ipe/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+boot_policy.c
diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig
new file mode 100644
index 000000000000..3c75bf267da4
--- /dev/null
+++ b/security/ipe/Kconfig
@@ -0,0 +1,116 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Integrity Policy Enforcement (IPE) configuration
+#
+
+menuconfig SECURITY_IPE
+ bool "Integrity Policy Enforcement (IPE)"
+ depends on SECURITY && SECURITYFS && AUDIT && AUDITSYSCALL
+ select PKCS7_MESSAGE_PARSER
+ select SYSTEM_DATA_VERIFICATION
+ select IPE_PROP_DM_VERITY if DM_VERITY
+ select IPE_PROP_DM_VERITY_SIGNATURE if DM_VERITY && DM_VERITY_VERIFY_ROOTHASH_SIG
+ select IPE_PROP_FS_VERITY if FS_VERITY
+ select IPE_PROP_FS_VERITY_BUILTIN_SIG if FS_VERITY && FS_VERITY_BUILTIN_SIGNATURES
+ help
+ This option enables the Integrity Policy Enforcement LSM
+ allowing users to define a policy to enforce a trust-based access
+ control. A key feature of IPE is a customizable policy to allow
+ admins to reconfigure trust requirements on the fly.
+
+ If unsure, answer N.
+
+if SECURITY_IPE
+config IPE_BOOT_POLICY
+ string "Integrity policy to apply on system startup"
+ help
+ This option specifies a filepath to an IPE policy that is compiled
+ into the kernel. This policy will be enforced until a policy update
+ is deployed via the $securityfs/ipe/policies/$policy_name/active
+ interface.
+
+ If unsure, leave blank.
+
+config IPE_POLICY_SIG_SECONDARY_KEYRING
+ bool "IPE policy update verification with secondary keyring"
+ default y
+ depends on SECONDARY_TRUSTED_KEYRING
+ help
+ Also allow the secondary trusted keyring to verify IPE policy
+ updates.
+
+ If unsure, answer Y.
+
+config IPE_POLICY_SIG_PLATFORM_KEYRING
+ bool "IPE policy update verification with platform keyring"
+ default y
+ depends on INTEGRITY_PLATFORM_KEYRING
+ help
+ Also allow the platform keyring to verify IPE policy updates.
+
+ If unsure, answer Y.
+
+menu "IPE Trust Providers"
+
+config IPE_PROP_DM_VERITY
+ bool "Enable support for dm-verity based on root hash"
+ depends on DM_VERITY
+ help
+ This option enables the 'dmverity_roothash' property within IPE
+ policies. The property evaluates to TRUE when a file from a dm-verity
+ volume is evaluated, and the volume's root hash matches the value
+ supplied in the policy.
+
+config IPE_PROP_DM_VERITY_SIGNATURE
+ bool "Enable support for dm-verity based on root hash signature"
+ depends on DM_VERITY && DM_VERITY_VERIFY_ROOTHASH_SIG
+ help
+ This option enables the 'dmverity_signature' property within IPE
+ policies. The property evaluates to TRUE when a file from a dm-verity
+ volume, which has been mounted with a valid signed root hash,
+ is evaluated.
+
+ If unsure, answer Y.
+
+config IPE_PROP_FS_VERITY
+ bool "Enable support for fs-verity based on file digest"
+ depends on FS_VERITY
+ help
+ This option enables the 'fsverity_digest' property within IPE
+ policies. The property evaluates to TRUE when a file is fsverity
+ enabled and its digest matches the supplied digest value in the
+ policy.
+
+ if unsure, answer Y.
+
+config IPE_PROP_FS_VERITY_BUILTIN_SIG
+ bool "Enable support for fs-verity based on builtin signature"
+ depends on FS_VERITY && FS_VERITY_BUILTIN_SIGNATURES
+ help
+ This option enables the 'fsverity_signature' property within IPE
+ policies. The property evaluates to TRUE when a file is fsverity
+ enabled and it has a valid builtin signature whose signing cert
+ is in the .fs-verity keyring.
+
+ if unsure, answer Y.
+
+endmenu
+
+config SECURITY_IPE_KUNIT_TEST
+ bool "Build KUnit tests for IPE" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y
+ default KUNIT_ALL_TESTS
+ help
+ This builds the IPE KUnit tests.
+
+ KUnit tests run during boot and output the results to the debug log
+ in TAP format (https://testanything.org/). Only useful for kernel devs
+ running KUnit test harness and are not for inclusion into a
+ production build.
+
+ For more information on KUnit and unit tests in general please refer
+ to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
+
+endif
diff --git a/security/ipe/Makefile b/security/ipe/Makefile
new file mode 100644
index 000000000000..2ffabfa63fe9
--- /dev/null
+++ b/security/ipe/Makefile
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+#
+# Makefile for building the IPE module as part of the kernel tree.
+#
+
+quiet_cmd_polgen = IPE_POL $(2)
+ cmd_polgen = scripts/ipe/polgen/polgen security/ipe/boot_policy.c $(2)
+
+targets += boot_policy.c
+
+$(obj)/boot_policy.c: scripts/ipe/polgen/polgen $(CONFIG_IPE_BOOT_POLICY) FORCE
+ $(call if_changed,polgen,$(CONFIG_IPE_BOOT_POLICY))
+
+obj-$(CONFIG_SECURITY_IPE) += \
+ boot_policy.o \
+ digest.o \
+ eval.o \
+ hooks.o \
+ fs.o \
+ ipe.o \
+ policy.o \
+ policy_fs.o \
+ policy_parser.o \
+ audit.o \
+
+clean-files := boot_policy.c \
+
+obj-$(CONFIG_SECURITY_IPE_KUNIT_TEST) += \
+ policy_tests.o \
diff --git a/security/ipe/audit.c b/security/ipe/audit.c
new file mode 100644
index 000000000000..f05f0caa4850
--- /dev/null
+++ b/security/ipe/audit.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#include <linux/slab.h>
+#include <linux/audit.h>
+#include <linux/types.h>
+#include <crypto/hash.h>
+
+#include "ipe.h"
+#include "eval.h"
+#include "hooks.h"
+#include "policy.h"
+#include "audit.h"
+#include "digest.h"
+
+#define ACTSTR(x) ((x) == IPE_ACTION_ALLOW ? "ALLOW" : "DENY")
+
+#define IPE_AUDIT_HASH_ALG "sha256"
+
+#define AUDIT_POLICY_LOAD_FMT "policy_name=\"%s\" policy_version=%hu.%hu.%hu "\
+ "policy_digest=" IPE_AUDIT_HASH_ALG ":"
+#define AUDIT_OLD_ACTIVE_POLICY_FMT "old_active_pol_name=\"%s\" "\
+ "old_active_pol_version=%hu.%hu.%hu "\
+ "old_policy_digest=" IPE_AUDIT_HASH_ALG ":"
+#define AUDIT_OLD_ACTIVE_POLICY_NULL_FMT "old_active_pol_name=? "\
+ "old_active_pol_version=? "\
+ "old_policy_digest=?"
+#define AUDIT_NEW_ACTIVE_POLICY_FMT "new_active_pol_name=\"%s\" "\
+ "new_active_pol_version=%hu.%hu.%hu "\
+ "new_policy_digest=" IPE_AUDIT_HASH_ALG ":"
+
+static const char *const audit_op_names[__IPE_OP_MAX + 1] = {
+ "EXECUTE",
+ "FIRMWARE",
+ "KMODULE",
+ "KEXEC_IMAGE",
+ "KEXEC_INITRAMFS",
+ "POLICY",
+ "X509_CERT",
+ "UNKNOWN",
+};
+
+static const char *const audit_hook_names[__IPE_HOOK_MAX] = {
+ "BPRM_CHECK",
+ "MMAP",
+ "MPROTECT",
+ "KERNEL_READ",
+ "KERNEL_LOAD",
+};
+
+static const char *const audit_prop_names[__IPE_PROP_MAX] = {
+ "boot_verified=FALSE",
+ "boot_verified=TRUE",
+ "dmverity_roothash=",
+ "dmverity_signature=FALSE",
+ "dmverity_signature=TRUE",
+ "fsverity_digest=",
+ "fsverity_signature=FALSE",
+ "fsverity_signature=TRUE",
+};
+
+/**
+ * audit_dmv_roothash() - audit the roothash of a dmverity_roothash property.
+ * @ab: Supplies a pointer to the audit_buffer to append to.
+ * @rh: Supplies a pointer to the digest structure.
+ */
+static void audit_dmv_roothash(struct audit_buffer *ab, const void *rh)
+{
+ audit_log_format(ab, "%s", audit_prop_names[IPE_PROP_DMV_ROOTHASH]);
+ ipe_digest_audit(ab, rh);
+}
+
+/**
+ * audit_fsv_digest() - audit the digest of a fsverity_digest property.
+ * @ab: Supplies a pointer to the audit_buffer to append to.
+ * @d: Supplies a pointer to the digest structure.
+ */
+static void audit_fsv_digest(struct audit_buffer *ab, const void *d)
+{
+ audit_log_format(ab, "%s", audit_prop_names[IPE_PROP_FSV_DIGEST]);
+ ipe_digest_audit(ab, d);
+}
+
+/**
+ * audit_rule() - audit an IPE policy rule.
+ * @ab: Supplies a pointer to the audit_buffer to append to.
+ * @r: Supplies a pointer to the ipe_rule to approximate a string form for.
+ */
+static void audit_rule(struct audit_buffer *ab, const struct ipe_rule *r)
+{
+ const struct ipe_prop *ptr;
+
+ audit_log_format(ab, " rule=\"op=%s ", audit_op_names[r->op]);
+
+ list_for_each_entry(ptr, &r->props, next) {
+ switch (ptr->type) {
+ case IPE_PROP_DMV_ROOTHASH:
+ audit_dmv_roothash(ab, ptr->value);
+ break;
+ case IPE_PROP_FSV_DIGEST:
+ audit_fsv_digest(ab, ptr->value);
+ break;
+ default:
+ audit_log_format(ab, "%s", audit_prop_names[ptr->type]);
+ break;
+ }
+
+ audit_log_format(ab, " ");
+ }
+
+ audit_log_format(ab, "action=%s\"", ACTSTR(r->action));
+}
+
+/**
+ * ipe_audit_match() - Audit a rule match in a policy evaluation.
+ * @ctx: Supplies a pointer to the evaluation context that was used in the
+ * evaluation.
+ * @match_type: Supplies the scope of the match: rule, operation default,
+ * global default.
+ * @act: Supplies the IPE's evaluation decision, deny or allow.
+ * @r: Supplies a pointer to the rule that was matched, if possible.
+ */
+void ipe_audit_match(const struct ipe_eval_ctx *const ctx,
+ enum ipe_match match_type,
+ enum ipe_action_type act, const struct ipe_rule *const r)
+{
+ const char *op = audit_op_names[ctx->op];
+ char comm[sizeof(current->comm)];
+ struct audit_buffer *ab;
+ struct inode *inode;
+
+ if (act != IPE_ACTION_DENY && !READ_ONCE(success_audit))
+ return;
+
+ ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
+ AUDIT_IPE_ACCESS);
+ if (!ab)
+ return;
+
+ audit_log_format(ab, "ipe_op=%s ipe_hook=%s enforcing=%d pid=%d comm=",
+ op, audit_hook_names[ctx->hook], READ_ONCE(enforce),
+ task_tgid_nr(current));
+ audit_log_untrustedstring(ab, get_task_comm(comm, current));
+
+ if (ctx->file) {
+ audit_log_d_path(ab, " path=", &ctx->file->f_path);
+ inode = file_inode(ctx->file);
+ if (inode) {
+ audit_log_format(ab, " dev=");
+ audit_log_untrustedstring(ab, inode->i_sb->s_id);
+ audit_log_format(ab, " ino=%lu", inode->i_ino);
+ } else {
+ audit_log_format(ab, " dev=? ino=?");
+ }
+ } else {
+ audit_log_format(ab, " path=? dev=? ino=?");
+ }
+
+ if (match_type == IPE_MATCH_RULE)
+ audit_rule(ab, r);
+ else if (match_type == IPE_MATCH_TABLE)
+ audit_log_format(ab, " rule=\"DEFAULT op=%s action=%s\"", op,
+ ACTSTR(act));
+ else
+ audit_log_format(ab, " rule=\"DEFAULT action=%s\"",
+ ACTSTR(act));
+
+ audit_log_end(ab);
+}
+
+/**
+ * audit_policy() - Audit a policy's name, version and thumbprint to @ab.
+ * @ab: Supplies a pointer to the audit buffer to append to.
+ * @audit_format: Supplies a pointer to the audit format string
+ * @p: Supplies a pointer to the policy to audit.
+ */
+static void audit_policy(struct audit_buffer *ab,
+ const char *audit_format,
+ const struct ipe_policy *const p)
+{
+ SHASH_DESC_ON_STACK(desc, tfm);
+ struct crypto_shash *tfm;
+ u8 *digest = NULL;
+
+ tfm = crypto_alloc_shash(IPE_AUDIT_HASH_ALG, 0, 0);
+ if (IS_ERR(tfm))
+ return;
+
+ desc->tfm = tfm;
+
+ digest = kzalloc(crypto_shash_digestsize(tfm), GFP_KERNEL);
+ if (!digest)
+ goto out;
+
+ if (crypto_shash_init(desc))
+ goto out;
+
+ if (crypto_shash_update(desc, p->pkcs7, p->pkcs7len))
+ goto out;
+
+ if (crypto_shash_final(desc, digest))
+ goto out;
+
+ audit_log_format(ab, audit_format, p->parsed->name,
+ p->parsed->version.major, p->parsed->version.minor,
+ p->parsed->version.rev);
+ audit_log_n_hex(ab, digest, crypto_shash_digestsize(tfm));
+
+out:
+ kfree(digest);
+ crypto_free_shash(tfm);
+}
+
+/**
+ * ipe_audit_policy_activation() - Audit a policy being activated.
+ * @op: Supplies a pointer to the previously activated policy to audit.
+ * @np: Supplies a pointer to the newly activated policy to audit.
+ */
+void ipe_audit_policy_activation(const struct ipe_policy *const op,
+ const struct ipe_policy *const np)
+{
+ struct audit_buffer *ab;
+
+ ab = audit_log_start(audit_context(), GFP_KERNEL,
+ AUDIT_IPE_CONFIG_CHANGE);
+ if (!ab)
+ return;
+
+ if (op) {
+ audit_policy(ab, AUDIT_OLD_ACTIVE_POLICY_FMT, op);
+ audit_log_format(ab, " ");
+ } else {
+ /*
+ * old active policy can be NULL if there is no kernel
+ * built-in policy
+ */
+ audit_log_format(ab, AUDIT_OLD_ACTIVE_POLICY_NULL_FMT);
+ audit_log_format(ab, " ");
+ }
+ audit_policy(ab, AUDIT_NEW_ACTIVE_POLICY_FMT, np);
+ audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=1",
+ from_kuid(&init_user_ns, audit_get_loginuid(current)),
+ audit_get_sessionid(current));
+
+ audit_log_end(ab);
+}
+
+/**
+ * ipe_audit_policy_load() - Audit a policy being loaded into the kernel.
+ * @p: Supplies a pointer to the policy to audit.
+ */
+void ipe_audit_policy_load(const struct ipe_policy *const p)
+{
+ struct audit_buffer *ab;
+
+ ab = audit_log_start(audit_context(), GFP_KERNEL,
+ AUDIT_IPE_POLICY_LOAD);
+ if (!ab)
+ return;
+
+ audit_policy(ab, AUDIT_POLICY_LOAD_FMT, p);
+ audit_log_format(ab, " auid=%u ses=%u lsm=ipe res=1",
+ from_kuid(&init_user_ns, audit_get_loginuid(current)),
+ audit_get_sessionid(current));
+
+ audit_log_end(ab);
+}
+
+/**
+ * ipe_audit_enforce() - Audit a change in IPE's enforcement state.
+ * @new_enforce: The new value enforce to be set.
+ * @old_enforce: The old value currently in enforce.
+ */
+void ipe_audit_enforce(bool new_enforce, bool old_enforce)
+{
+ struct audit_buffer *ab;
+
+ ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS);
+ if (!ab)
+ return;
+
+ audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS,
+ "enforcing=%d old_enforcing=%d auid=%u ses=%u"
+ " enabled=1 old-enabled=1 lsm=ipe res=1",
+ new_enforce, old_enforce,
+ from_kuid(&init_user_ns, audit_get_loginuid(current)),
+ audit_get_sessionid(current));
+
+ audit_log_end(ab);
+}
diff --git a/security/ipe/audit.h b/security/ipe/audit.h
new file mode 100644
index 000000000000..ed2620846a79
--- /dev/null
+++ b/security/ipe/audit.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#ifndef _IPE_AUDIT_H
+#define _IPE_AUDIT_H
+
+#include "policy.h"
+
+void ipe_audit_match(const struct ipe_eval_ctx *const ctx,
+ enum ipe_match match_type,
+ enum ipe_action_type act, const struct ipe_rule *const r);
+void ipe_audit_policy_load(const struct ipe_policy *const p);
+void ipe_audit_policy_activation(const struct ipe_policy *const op,
+ const struct ipe_policy *const np);
+void ipe_audit_enforce(bool new_enforce, bool old_enforce);
+
+#endif /* _IPE_AUDIT_H */
diff --git a/security/ipe/digest.c b/security/ipe/digest.c
new file mode 100644
index 000000000000..493716370570
--- /dev/null
+++ b/security/ipe/digest.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#include "digest.h"
+
+/**
+ * ipe_digest_parse() - parse a digest in IPE's policy.
+ * @valstr: Supplies the string parsed from the policy.
+ *
+ * Digests in IPE are defined in a standard way:
+ * <alg_name>:<hex>
+ *
+ * Use this function to create a property to parse the digest
+ * consistently. The parsed digest will be saved in @value in IPE's
+ * policy.
+ *
+ * Return: The parsed digest_info structure on success. If an error occurs,
+ * the function will return the error value (via ERR_PTR).
+ */
+struct digest_info *ipe_digest_parse(const char *valstr)
+{
+ struct digest_info *info = NULL;
+ char *sep, *raw_digest;
+ size_t raw_digest_len;
+ u8 *digest = NULL;
+ char *alg = NULL;
+ int rc = 0;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ sep = strchr(valstr, ':');
+ if (!sep) {
+ rc = -EBADMSG;
+ goto err;
+ }
+
+ alg = kstrndup(valstr, sep - valstr, GFP_KERNEL);
+ if (!alg) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ raw_digest = sep + 1;
+ raw_digest_len = strlen(raw_digest);
+
+ info->digest_len = (raw_digest_len + 1) / 2;
+ digest = kzalloc(info->digest_len, GFP_KERNEL);
+ if (!digest) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = hex2bin(digest, raw_digest, info->digest_len);
+ if (rc < 0) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ info->alg = alg;
+ info->digest = digest;
+ return info;
+
+err:
+ kfree(alg);
+ kfree(digest);
+ kfree(info);
+ return ERR_PTR(rc);
+}
+
+/**
+ * ipe_digest_eval() - evaluate an IPE digest against another digest.
+ * @expected: Supplies the policy-provided digest value.
+ * @digest: Supplies the digest to compare against the policy digest value.
+ *
+ * Return:
+ * * %true - digests match
+ * * %false - digests do not match
+ */
+bool ipe_digest_eval(const struct digest_info *expected,
+ const struct digest_info *digest)
+{
+ return (expected->digest_len == digest->digest_len) &&
+ (!strcmp(expected->alg, digest->alg)) &&
+ (!memcmp(expected->digest, digest->digest, expected->digest_len));
+}
+
+/**
+ * ipe_digest_free() - free an IPE digest.
+ * @info: Supplies a pointer the policy-provided digest to free.
+ */
+void ipe_digest_free(struct digest_info *info)
+{
+ if (IS_ERR_OR_NULL(info))
+ return;
+
+ kfree(info->alg);
+ kfree(info->digest);
+ kfree(info);
+}
+
+/**
+ * ipe_digest_audit() - audit a digest that was sourced from IPE's policy.
+ * @ab: Supplies the audit_buffer to append the formatted result.
+ * @info: Supplies a pointer to source the audit record from.
+ *
+ * Digests in IPE are audited in this format:
+ * <alg_name>:<hex>
+ */
+void ipe_digest_audit(struct audit_buffer *ab, const struct digest_info *info)
+{
+ audit_log_untrustedstring(ab, info->alg);
+ audit_log_format(ab, ":");
+ audit_log_n_hex(ab, info->digest, info->digest_len);
+}
diff --git a/security/ipe/digest.h b/security/ipe/digest.h
new file mode 100644
index 000000000000..52c9b3844a38
--- /dev/null
+++ b/security/ipe/digest.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#ifndef _IPE_DIGEST_H
+#define _IPE_DIGEST_H
+
+#include <linux/types.h>
+#include <linux/audit.h>
+
+#include "policy.h"
+
+struct digest_info {
+ const char *alg;
+ const u8 *digest;
+ size_t digest_len;
+};
+
+struct digest_info *ipe_digest_parse(const char *valstr);
+void ipe_digest_free(struct digest_info *digest_info);
+void ipe_digest_audit(struct audit_buffer *ab, const struct digest_info *val);
+bool ipe_digest_eval(const struct digest_info *expected,
+ const struct digest_info *digest);
+
+#endif /* _IPE_DIGEST_H */
diff --git a/security/ipe/eval.c b/security/ipe/eval.c
new file mode 100644
index 000000000000..21439c5be336
--- /dev/null
+++ b/security/ipe/eval.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/sched.h>
+#include <linux/rcupdate.h>
+#include <linux/moduleparam.h>
+#include <linux/fsverity.h>
+
+#include "ipe.h"
+#include "eval.h"
+#include "policy.h"
+#include "audit.h"
+#include "digest.h"
+
+struct ipe_policy __rcu *ipe_active_policy;
+bool success_audit;
+bool enforce = true;
+#define INO_BLOCK_DEV(ino) ((ino)->i_sb->s_bdev)
+
+#define FILE_SUPERBLOCK(f) ((f)->f_path.mnt->mnt_sb)
+
+/**
+ * build_ipe_sb_ctx() - Build initramfs field of an ipe evaluation context.
+ * @ctx: Supplies a pointer to the context to be populated.
+ * @file: Supplies the file struct of the file triggered IPE event.
+ */
+static void build_ipe_sb_ctx(struct ipe_eval_ctx *ctx, const struct file *const file)
+{
+ ctx->initramfs = ipe_sb(FILE_SUPERBLOCK(file))->initramfs;
+}
+
+#ifdef CONFIG_IPE_PROP_DM_VERITY
+/**
+ * build_ipe_bdev_ctx() - Build ipe_bdev field of an evaluation context.
+ * @ctx: Supplies a pointer to the context to be populated.
+ * @ino: Supplies the inode struct of the file triggered IPE event.
+ */
+static void build_ipe_bdev_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino)
+{
+ if (INO_BLOCK_DEV(ino))
+ ctx->ipe_bdev = ipe_bdev(INO_BLOCK_DEV(ino));
+}
+#else
+static void build_ipe_bdev_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino)
+{
+}
+#endif /* CONFIG_IPE_PROP_DM_VERITY */
+
+#ifdef CONFIG_IPE_PROP_FS_VERITY
+#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
+static void build_ipe_inode_blob_ctx(struct ipe_eval_ctx *ctx,
+ const struct inode *const ino)
+{
+ ctx->ipe_inode = ipe_inode(ctx->ino);
+}
+#else
+static inline void build_ipe_inode_blob_ctx(struct ipe_eval_ctx *ctx,
+ const struct inode *const ino)
+{
+}
+#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+
+/**
+ * build_ipe_inode_ctx() - Build inode fields of an evaluation context.
+ * @ctx: Supplies a pointer to the context to be populated.
+ * @ino: Supplies the inode struct of the file triggered IPE event.
+ */
+static void build_ipe_inode_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino)
+{
+ ctx->ino = ino;
+ build_ipe_inode_blob_ctx(ctx, ino);
+}
+#else
+static void build_ipe_inode_ctx(struct ipe_eval_ctx *ctx, const struct inode *const ino)
+{
+}
+#endif /* CONFIG_IPE_PROP_FS_VERITY */
+
+/**
+ * ipe_build_eval_ctx() - Build an ipe evaluation context.
+ * @ctx: Supplies a pointer to the context to be populated.
+ * @file: Supplies a pointer to the file to associated with the evaluation.
+ * @op: Supplies the IPE policy operation associated with the evaluation.
+ * @hook: Supplies the LSM hook associated with the evaluation.
+ */
+void ipe_build_eval_ctx(struct ipe_eval_ctx *ctx,
+ const struct file *file,
+ enum ipe_op_type op,
+ enum ipe_hook_type hook)
+{
+ struct inode *ino;
+
+ ctx->file = file;
+ ctx->op = op;
+ ctx->hook = hook;
+
+ if (file) {
+ build_ipe_sb_ctx(ctx, file);
+ ino = d_real_inode(file->f_path.dentry);
+ build_ipe_bdev_ctx(ctx, ino);
+ build_ipe_inode_ctx(ctx, ino);
+ }
+}
+
+/**
+ * evaluate_boot_verified() - Evaluate @ctx for the boot verified property.
+ * @ctx: Supplies a pointer to the context being evaluated.
+ *
+ * Return:
+ * * %true - The current @ctx match the @p
+ * * %false - The current @ctx doesn't match the @p
+ */
+static bool evaluate_boot_verified(const struct ipe_eval_ctx *const ctx)
+{
+ return ctx->initramfs;
+}
+
+#ifdef CONFIG_IPE_PROP_DM_VERITY
+/**
+ * evaluate_dmv_roothash() - Evaluate @ctx against a dmv roothash property.
+ * @ctx: Supplies a pointer to the context being evaluated.
+ * @p: Supplies a pointer to the property being evaluated.
+ *
+ * Return:
+ * * %true - The current @ctx match the @p
+ * * %false - The current @ctx doesn't match the @p
+ */
+static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx,
+ struct ipe_prop *p)
+{
+ return !!ctx->ipe_bdev &&
+ !!ctx->ipe_bdev->root_hash &&
+ ipe_digest_eval(p->value,
+ ctx->ipe_bdev->root_hash);
+}
+#else
+static bool evaluate_dmv_roothash(const struct ipe_eval_ctx *const ctx,
+ struct ipe_prop *p)
+{
+ return false;
+}
+#endif /* CONFIG_IPE_PROP_DM_VERITY */
+
+#ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE
+/**
+ * evaluate_dmv_sig_false() - Evaluate @ctx against a dmv sig false property.
+ * @ctx: Supplies a pointer to the context being evaluated.
+ *
+ * Return:
+ * * %true - The current @ctx match the property
+ * * %false - The current @ctx doesn't match the property
+ */
+static bool evaluate_dmv_sig_false(const struct ipe_eval_ctx *const ctx)
+{
+ return !ctx->ipe_bdev || (!ctx->ipe_bdev->dm_verity_signed);
+}
+
+/**
+ * evaluate_dmv_sig_true() - Evaluate @ctx against a dmv sig true property.
+ * @ctx: Supplies a pointer to the context being evaluated.
+ *
+ * Return:
+ * * %true - The current @ctx match the property
+ * * %false - The current @ctx doesn't match the property
+ */
+static bool evaluate_dmv_sig_true(const struct ipe_eval_ctx *const ctx)
+{
+ return !evaluate_dmv_sig_false(ctx);
+}
+#else
+static bool evaluate_dmv_sig_false(const struct ipe_eval_ctx *const ctx)
+{
+ return false;
+}
+
+static bool evaluate_dmv_sig_true(const struct ipe_eval_ctx *const ctx)
+{
+ return false;
+}
+#endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */
+
+#ifdef CONFIG_IPE_PROP_FS_VERITY
+/**
+ * evaluate_fsv_digest() - Evaluate @ctx against a fsv digest property.
+ * @ctx: Supplies a pointer to the context being evaluated.
+ * @p: Supplies a pointer to the property being evaluated.
+ *
+ * Return:
+ * * %true - The current @ctx match the @p
+ * * %false - The current @ctx doesn't match the @p
+ */
+static bool evaluate_fsv_digest(const struct ipe_eval_ctx *const ctx,
+ struct ipe_prop *p)
+{
+ enum hash_algo alg;
+ u8 digest[FS_VERITY_MAX_DIGEST_SIZE];
+ struct digest_info info;
+
+ if (!ctx->ino)
+ return false;
+ if (!fsverity_get_digest((struct inode *)ctx->ino,
+ digest,
+ NULL,
+ &alg))
+ return false;
+
+ info.alg = hash_algo_name[alg];
+ info.digest = digest;
+ info.digest_len = hash_digest_size[alg];
+
+ return ipe_digest_eval(p->value, &info);
+}
+#else
+static bool evaluate_fsv_digest(const struct ipe_eval_ctx *const ctx,
+ struct ipe_prop *p)
+{
+ return false;
+}
+#endif /* CONFIG_IPE_PROP_FS_VERITY */
+
+#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
+/**
+ * evaluate_fsv_sig_false() - Evaluate @ctx against a fsv sig false property.
+ * @ctx: Supplies a pointer to the context being evaluated.
+ *
+ * Return:
+ * * %true - The current @ctx match the property
+ * * %false - The current @ctx doesn't match the property
+ */
+static bool evaluate_fsv_sig_false(const struct ipe_eval_ctx *const ctx)
+{
+ return !ctx->ino ||
+ !IS_VERITY(ctx->ino) ||
+ !ctx->ipe_inode ||
+ !ctx->ipe_inode->fs_verity_signed;
+}
+
+/**
+ * evaluate_fsv_sig_true() - Evaluate @ctx against a fsv sig true property.
+ * @ctx: Supplies a pointer to the context being evaluated.
+ *
+ * Return:
+ * * %true - The current @ctx match the property
+ * * %false - The current @ctx doesn't match the property
+ */
+static bool evaluate_fsv_sig_true(const struct ipe_eval_ctx *const ctx)
+{
+ return !evaluate_fsv_sig_false(ctx);
+}
+#else
+static bool evaluate_fsv_sig_false(const struct ipe_eval_ctx *const ctx)
+{
+ return false;
+}
+
+static bool evaluate_fsv_sig_true(const struct ipe_eval_ctx *const ctx)
+{
+ return false;
+}
+#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+
+/**
+ * evaluate_property() - Analyze @ctx against a rule property.
+ * @ctx: Supplies a pointer to the context to be evaluated.
+ * @p: Supplies a pointer to the property to be evaluated.
+ *
+ * This function Determines whether the specified @ctx
+ * matches the conditions defined by a rule property @p.
+ *
+ * Return:
+ * * %true - The current @ctx match the @p
+ * * %false - The current @ctx doesn't match the @p
+ */
+static bool evaluate_property(const struct ipe_eval_ctx *const ctx,
+ struct ipe_prop *p)
+{
+ switch (p->type) {
+ case IPE_PROP_BOOT_VERIFIED_FALSE:
+ return !evaluate_boot_verified(ctx);
+ case IPE_PROP_BOOT_VERIFIED_TRUE:
+ return evaluate_boot_verified(ctx);
+ case IPE_PROP_DMV_ROOTHASH:
+ return evaluate_dmv_roothash(ctx, p);
+ case IPE_PROP_DMV_SIG_FALSE:
+ return evaluate_dmv_sig_false(ctx);
+ case IPE_PROP_DMV_SIG_TRUE:
+ return evaluate_dmv_sig_true(ctx);
+ case IPE_PROP_FSV_DIGEST:
+ return evaluate_fsv_digest(ctx, p);
+ case IPE_PROP_FSV_SIG_FALSE:
+ return evaluate_fsv_sig_false(ctx);
+ case IPE_PROP_FSV_SIG_TRUE:
+ return evaluate_fsv_sig_true(ctx);
+ default:
+ return false;
+ }
+}
+
+/**
+ * ipe_evaluate_event() - Analyze @ctx against the current active policy.
+ * @ctx: Supplies a pointer to the context to be evaluated.
+ *
+ * This is the loop where all policy evaluations happen against the IPE policy.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-EACCES - @ctx did not pass evaluation
+ */
+int ipe_evaluate_event(const struct ipe_eval_ctx *const ctx)
+{
+ const struct ipe_op_table *rules = NULL;
+ const struct ipe_rule *rule = NULL;
+ struct ipe_policy *pol = NULL;
+ struct ipe_prop *prop = NULL;
+ enum ipe_action_type action;
+ enum ipe_match match_type;
+ bool match = false;
+ int rc = 0;
+
+ rcu_read_lock();
+
+ pol = rcu_dereference(ipe_active_policy);
+ if (!pol) {
+ rcu_read_unlock();
+ return 0;
+ }
+
+ if (ctx->op == IPE_OP_INVALID) {
+ if (pol->parsed->global_default_action == IPE_ACTION_INVALID) {
+ WARN(1, "no default rule set for unknown op, ALLOW it");
+ action = IPE_ACTION_ALLOW;
+ } else {
+ action = pol->parsed->global_default_action;
+ }
+ match_type = IPE_MATCH_GLOBAL;
+ goto eval;
+ }
+
+ rules = &pol->parsed->rules[ctx->op];
+
+ list_for_each_entry(rule, &rules->rules, next) {
+ match = true;
+
+ list_for_each_entry(prop, &rule->props, next) {
+ match = evaluate_property(ctx, prop);
+ if (!match)
+ break;
+ }
+
+ if (match)
+ break;
+ }
+
+ if (match) {
+ action = rule->action;
+ match_type = IPE_MATCH_RULE;
+ } else if (rules->default_action != IPE_ACTION_INVALID) {
+ action = rules->default_action;
+ match_type = IPE_MATCH_TABLE;
+ } else {
+ action = pol->parsed->global_default_action;
+ match_type = IPE_MATCH_GLOBAL;
+ }
+
+eval:
+ ipe_audit_match(ctx, match_type, action, rule);
+ rcu_read_unlock();
+
+ if (action == IPE_ACTION_DENY)
+ rc = -EACCES;
+
+ if (!READ_ONCE(enforce))
+ rc = 0;
+
+ return rc;
+}
+
+/* Set the right module name */
+#ifdef KBUILD_MODNAME
+#undef KBUILD_MODNAME
+#define KBUILD_MODNAME "ipe"
+#endif
+
+module_param(success_audit, bool, 0400);
+MODULE_PARM_DESC(success_audit, "Start IPE with success auditing enabled");
+module_param(enforce, bool, 0400);
+MODULE_PARM_DESC(enforce, "Start IPE in enforce or permissive mode");
diff --git a/security/ipe/eval.h b/security/ipe/eval.h
new file mode 100644
index 000000000000..fef65a36468c
--- /dev/null
+++ b/security/ipe/eval.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#ifndef _IPE_EVAL_H
+#define _IPE_EVAL_H
+
+#include <linux/file.h>
+#include <linux/types.h>
+
+#include "policy.h"
+#include "hooks.h"
+
+#define IPE_EVAL_CTX_INIT ((struct ipe_eval_ctx){ 0 })
+
+extern struct ipe_policy __rcu *ipe_active_policy;
+extern bool success_audit;
+extern bool enforce;
+
+struct ipe_superblock {
+ bool initramfs;
+};
+
+#ifdef CONFIG_IPE_PROP_DM_VERITY
+struct ipe_bdev {
+#ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE
+ bool dm_verity_signed;
+#endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */
+ struct digest_info *root_hash;
+};
+#endif /* CONFIG_IPE_PROP_DM_VERITY */
+
+#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
+struct ipe_inode {
+ bool fs_verity_signed;
+};
+#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+
+struct ipe_eval_ctx {
+ enum ipe_op_type op;
+ enum ipe_hook_type hook;
+
+ const struct file *file;
+ bool initramfs;
+#ifdef CONFIG_IPE_PROP_DM_VERITY
+ const struct ipe_bdev *ipe_bdev;
+#endif /* CONFIG_IPE_PROP_DM_VERITY */
+#ifdef CONFIG_IPE_PROP_FS_VERITY
+ const struct inode *ino;
+#endif /* CONFIG_IPE_PROP_FS_VERITY */
+#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
+ const struct ipe_inode *ipe_inode;
+#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+};
+
+enum ipe_match {
+ IPE_MATCH_RULE = 0,
+ IPE_MATCH_TABLE,
+ IPE_MATCH_GLOBAL,
+ __IPE_MATCH_MAX
+};
+
+void ipe_build_eval_ctx(struct ipe_eval_ctx *ctx,
+ const struct file *file,
+ enum ipe_op_type op,
+ enum ipe_hook_type hook);
+int ipe_evaluate_event(const struct ipe_eval_ctx *const ctx);
+
+#endif /* _IPE_EVAL_H */
diff --git a/security/ipe/fs.c b/security/ipe/fs.c
new file mode 100644
index 000000000000..5b6d19fb844a
--- /dev/null
+++ b/security/ipe/fs.c
@@ -0,0 +1,247 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#include <linux/dcache.h>
+#include <linux/security.h>
+
+#include "ipe.h"
+#include "fs.h"
+#include "eval.h"
+#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"
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * Return:
+ * * Length of buffer written - Success
+ * * %-EPERM - Insufficient permission
+ */
+static ssize_t setaudit(struct file *f, const char __user *data,
+ size_t len, loff_t *offset)
+{
+ int rc = 0;
+ bool value;
+
+ if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+ return -EPERM;
+
+ rc = kstrtobool_from_user(data, len, &value);
+ if (rc)
+ return rc;
+
+ WRITE_ONCE(success_audit, value);
+
+ return len;
+}
+
+/**
+ * getaudit() - Read handler for the securityfs node, "ipe/success_audit"
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the read syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * Return: Length of buffer written
+ */
+static ssize_t getaudit(struct file *f, char __user *data,
+ size_t len, loff_t *offset)
+{
+ const char *result;
+
+ result = ((READ_ONCE(success_audit)) ? "1" : "0");
+
+ return simple_read_from_buffer(data, len, offset, result, 1);
+}
+
+/**
+ * setenforce() - Write handler for the securityfs node, "ipe/enforce"
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * Return:
+ * * Length of buffer written - Success
+ * * %-EPERM - Insufficient permission
+ */
+static ssize_t setenforce(struct file *f, const char __user *data,
+ size_t len, loff_t *offset)
+{
+ int rc = 0;
+ bool new_value, old_value;
+
+ if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+ return -EPERM;
+
+ old_value = READ_ONCE(enforce);
+ rc = kstrtobool_from_user(data, len, &new_value);
+ if (rc)
+ return rc;
+
+ if (new_value != old_value) {
+ ipe_audit_enforce(new_value, old_value);
+ WRITE_ONCE(enforce, new_value);
+ }
+
+ return len;
+}
+
+/**
+ * getenforce() - Read handler for the securityfs node, "ipe/enforce"
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the read syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * Return: Length of buffer written
+ */
+static ssize_t getenforce(struct file *f, char __user *data,
+ size_t len, loff_t *offset)
+{
+ const char *result;
+
+ result = ((READ_ONCE(enforce)) ? "1" : "0");
+
+ return simple_read_from_buffer(data, len, offset, result, 1);
+}
+
+/**
+ * new_policy() - Write handler for the securityfs node, "ipe/new_policy".
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * Return:
+ * * Length of buffer written - Success
+ * * %-EPERM - Insufficient permission
+ * * %-ENOMEM - Out of memory (OOM)
+ * * %-EBADMSG - Policy is invalid
+ * * %-ERANGE - Policy version number overflow
+ * * %-EINVAL - Policy version parsing error
+ * * %-EEXIST - Same name policy already deployed
+ */
+static ssize_t new_policy(struct file *f, const char __user *data,
+ size_t len, loff_t *offset)
+{
+ struct ipe_policy *p = NULL;
+ char *copy = NULL;
+ int rc = 0;
+
+ if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+ return -EPERM;
+
+ copy = memdup_user_nul(data, len);
+ if (IS_ERR(copy))
+ return PTR_ERR(copy);
+
+ p = ipe_new_policy(NULL, 0, copy, len);
+ if (IS_ERR(p)) {
+ rc = PTR_ERR(p);
+ goto out;
+ }
+
+ rc = ipe_new_policyfs_node(p);
+ if (rc)
+ goto out;
+
+ ipe_audit_policy_load(p);
+
+out:
+ if (rc < 0)
+ ipe_free_policy(p);
+ kfree(copy);
+ return (rc < 0) ? rc : len;
+}
+
+static const struct file_operations np_fops = {
+ .write = new_policy,
+};
+
+static const struct file_operations audit_fops = {
+ .write = setaudit,
+ .read = getaudit,
+};
+
+static const struct file_operations enforce_fops = {
+ .write = setenforce,
+ .read = getenforce,
+};
+
+/**
+ * ipe_init_securityfs() - Initialize IPE's securityfs tree at fsinit.
+ *
+ * Return: %0 on success. If an error occurs, the function will return
+ * the -errno.
+ */
+static int __init ipe_init_securityfs(void)
+{
+ int rc = 0;
+ struct ipe_policy *ap;
+
+ if (!ipe_enabled)
+ return -EOPNOTSUPP;
+
+ root = securityfs_create_dir("ipe", NULL);
+ if (IS_ERR(root)) {
+ rc = PTR_ERR(root);
+ goto err;
+ }
+
+ audit_node = securityfs_create_file("success_audit", 0600, root,
+ NULL, &audit_fops);
+ if (IS_ERR(audit_node)) {
+ rc = PTR_ERR(audit_node);
+ goto err;
+ }
+
+ enforce_node = securityfs_create_file("enforce", 0600, root, NULL,
+ &enforce_fops);
+ if (IS_ERR(enforce_node)) {
+ rc = PTR_ERR(enforce_node);
+ goto err;
+ }
+
+ policy_root = securityfs_create_dir("policies", root);
+ if (IS_ERR(policy_root)) {
+ rc = PTR_ERR(policy_root);
+ goto err;
+ }
+
+ ap = rcu_access_pointer(ipe_active_policy);
+ if (ap) {
+ rc = ipe_new_policyfs_node(ap);
+ if (rc)
+ goto err;
+ }
+
+ np = securityfs_create_file("new_policy", 0200, root, NULL, &np_fops);
+ if (IS_ERR(np)) {
+ rc = PTR_ERR(np);
+ 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;
+}
+
+fs_initcall(ipe_init_securityfs);
diff --git a/security/ipe/fs.h b/security/ipe/fs.h
new file mode 100644
index 000000000000..0141ae8e86ec
--- /dev/null
+++ b/security/ipe/fs.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#ifndef _IPE_FS_H
+#define _IPE_FS_H
+
+#include "policy.h"
+
+extern struct dentry *policy_root __ro_after_init;
+
+int ipe_new_policyfs_node(struct ipe_policy *p);
+void ipe_del_policyfs_node(struct ipe_policy *p);
+
+#endif /* _IPE_FS_H */
diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c
new file mode 100644
index 000000000000..d0323b81cd8f
--- /dev/null
+++ b/security/ipe/hooks.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#include <linux/fs.h>
+#include <linux/fs_struct.h>
+#include <linux/types.h>
+#include <linux/binfmts.h>
+#include <linux/mman.h>
+#include <linux/blk_types.h>
+
+#include "ipe.h"
+#include "hooks.h"
+#include "eval.h"
+#include "digest.h"
+
+/**
+ * ipe_bprm_check_security() - ipe security hook function for bprm check.
+ * @bprm: Supplies a pointer to a linux_binprm structure to source the file
+ * being evaluated.
+ *
+ * This LSM hook is called when a binary is loaded through the exec
+ * family of system calls.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-EACCES - Did not pass IPE policy
+ */
+int ipe_bprm_check_security(struct linux_binprm *bprm)
+{
+ struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
+
+ ipe_build_eval_ctx(&ctx, bprm->file, IPE_OP_EXEC, IPE_HOOK_BPRM_CHECK);
+ return ipe_evaluate_event(&ctx);
+}
+
+/**
+ * ipe_mmap_file() - ipe security hook function for mmap check.
+ * @f: File being mmap'd. Can be NULL in the case of anonymous memory.
+ * @reqprot: The requested protection on the mmap, passed from usermode.
+ * @prot: The effective protection on the mmap, resolved from reqprot and
+ * system configuration.
+ * @flags: Unused.
+ *
+ * This hook is called when a file is loaded through the mmap
+ * family of system calls.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-EACCES - Did not pass IPE policy
+ */
+int ipe_mmap_file(struct file *f, unsigned long reqprot __always_unused,
+ unsigned long prot, unsigned long flags)
+{
+ struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
+
+ if (prot & PROT_EXEC) {
+ ipe_build_eval_ctx(&ctx, f, IPE_OP_EXEC, IPE_HOOK_MMAP);
+ return ipe_evaluate_event(&ctx);
+ }
+
+ return 0;
+}
+
+/**
+ * ipe_file_mprotect() - ipe security hook function for mprotect check.
+ * @vma: Existing virtual memory area created by mmap or similar.
+ * @reqprot: The requested protection on the mmap, passed from usermode.
+ * @prot: The effective protection on the mmap, resolved from reqprot and
+ * system configuration.
+ *
+ * This LSM hook is called when a mmap'd region of memory is changing
+ * its protections via mprotect.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-EACCES - Did not pass IPE policy
+ */
+int ipe_file_mprotect(struct vm_area_struct *vma,
+ unsigned long reqprot __always_unused,
+ unsigned long prot)
+{
+ struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
+
+ /* Already Executable */
+ if (vma->vm_flags & VM_EXEC)
+ return 0;
+
+ if (prot & PROT_EXEC) {
+ ipe_build_eval_ctx(&ctx, vma->vm_file, IPE_OP_EXEC, IPE_HOOK_MPROTECT);
+ return ipe_evaluate_event(&ctx);
+ }
+
+ return 0;
+}
+
+/**
+ * ipe_kernel_read_file() - ipe security hook function for kernel read.
+ * @file: Supplies a pointer to the file structure being read in from disk.
+ * @id: Supplies the enumeration identifying the purpose of the read.
+ * @contents: Unused.
+ *
+ * This LSM hook is called when a file is read from disk in the kernel.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-EACCES - Did not pass IPE policy
+ */
+int ipe_kernel_read_file(struct file *file, enum kernel_read_file_id id,
+ bool contents)
+{
+ struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
+ enum ipe_op_type op;
+
+ switch (id) {
+ case READING_FIRMWARE:
+ op = IPE_OP_FIRMWARE;
+ break;
+ case READING_MODULE:
+ op = IPE_OP_KERNEL_MODULE;
+ break;
+ case READING_KEXEC_INITRAMFS:
+ op = IPE_OP_KEXEC_INITRAMFS;
+ break;
+ case READING_KEXEC_IMAGE:
+ op = IPE_OP_KEXEC_IMAGE;
+ break;
+ case READING_POLICY:
+ op = IPE_OP_POLICY;
+ break;
+ case READING_X509_CERTIFICATE:
+ op = IPE_OP_X509;
+ break;
+ default:
+ op = IPE_OP_INVALID;
+ WARN(1, "no rule setup for kernel_read_file enum %d", id);
+ }
+
+ ipe_build_eval_ctx(&ctx, file, op, IPE_HOOK_KERNEL_READ);
+ return ipe_evaluate_event(&ctx);
+}
+
+/**
+ * ipe_kernel_load_data() - ipe security hook function for kernel load data.
+ * @id: Supplies the enumeration identifying the purpose of the load.
+ * @contents: Unused.
+ *
+ * This LSM hook is called when a data buffer provided by userspace is loading
+ * into the kernel.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-EACCES - Did not pass IPE policy
+ */
+int ipe_kernel_load_data(enum kernel_load_data_id id, bool contents)
+{
+ struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
+ enum ipe_op_type op;
+
+ switch (id) {
+ case LOADING_FIRMWARE:
+ op = IPE_OP_FIRMWARE;
+ break;
+ case LOADING_MODULE:
+ op = IPE_OP_KERNEL_MODULE;
+ break;
+ case LOADING_KEXEC_INITRAMFS:
+ op = IPE_OP_KEXEC_INITRAMFS;
+ break;
+ case LOADING_KEXEC_IMAGE:
+ op = IPE_OP_KEXEC_IMAGE;
+ break;
+ case LOADING_POLICY:
+ op = IPE_OP_POLICY;
+ break;
+ case LOADING_X509_CERTIFICATE:
+ op = IPE_OP_X509;
+ break;
+ default:
+ op = IPE_OP_INVALID;
+ WARN(1, "no rule setup for kernel_load_data enum %d", id);
+ }
+
+ ipe_build_eval_ctx(&ctx, NULL, op, IPE_HOOK_KERNEL_LOAD);
+ return ipe_evaluate_event(&ctx);
+}
+
+/**
+ * ipe_unpack_initramfs() - Mark the current rootfs as initramfs.
+ */
+void ipe_unpack_initramfs(void)
+{
+ ipe_sb(current->fs->root.mnt->mnt_sb)->initramfs = true;
+}
+
+#ifdef CONFIG_IPE_PROP_DM_VERITY
+/**
+ * ipe_bdev_free_security() - Free IPE's LSM blob of block_devices.
+ * @bdev: Supplies a pointer to a block_device that contains the structure
+ * to free.
+ */
+void ipe_bdev_free_security(struct block_device *bdev)
+{
+ struct ipe_bdev *blob = ipe_bdev(bdev);
+
+ ipe_digest_free(blob->root_hash);
+}
+
+#ifdef CONFIG_IPE_PROP_DM_VERITY_SIGNATURE
+static void ipe_set_dmverity_signature(struct ipe_bdev *blob,
+ const void *value,
+ size_t size)
+{
+ blob->dm_verity_signed = size > 0 && value;
+}
+#else
+static inline void ipe_set_dmverity_signature(struct ipe_bdev *blob,
+ const void *value,
+ size_t size)
+{
+}
+#endif /* CONFIG_IPE_PROP_DM_VERITY_SIGNATURE */
+
+/**
+ * ipe_bdev_setintegrity() - Save integrity data from a bdev to IPE's LSM blob.
+ * @bdev: Supplies a pointer to a block_device that contains the LSM blob.
+ * @type: Supplies the integrity type.
+ * @value: Supplies the value to store.
+ * @size: The size of @value.
+ *
+ * This hook is currently used to save dm-verity's root hash or the existence
+ * of a validated signed dm-verity root hash into LSM blob.
+ *
+ * Return: %0 on success. If an error occurs, the function will return the
+ * -errno.
+ */
+int ipe_bdev_setintegrity(struct block_device *bdev, enum lsm_integrity_type type,
+ const void *value, size_t size)
+{
+ const struct dm_verity_digest *digest = NULL;
+ struct ipe_bdev *blob = ipe_bdev(bdev);
+ struct digest_info *info = NULL;
+
+ if (type == LSM_INT_DMVERITY_SIG_VALID) {
+ ipe_set_dmverity_signature(blob, value, size);
+
+ return 0;
+ }
+
+ if (type != LSM_INT_DMVERITY_ROOTHASH)
+ return -EINVAL;
+
+ if (!value) {
+ ipe_digest_free(blob->root_hash);
+ blob->root_hash = NULL;
+
+ return 0;
+ }
+ digest = value;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->digest = kmemdup(digest->digest, digest->digest_len, GFP_KERNEL);
+ if (!info->digest)
+ goto err;
+
+ info->alg = kstrdup(digest->alg, GFP_KERNEL);
+ if (!info->alg)
+ goto err;
+
+ info->digest_len = digest->digest_len;
+
+ ipe_digest_free(blob->root_hash);
+ blob->root_hash = info;
+
+ return 0;
+err:
+ ipe_digest_free(info);
+
+ return -ENOMEM;
+}
+#endif /* CONFIG_IPE_PROP_DM_VERITY */
+
+#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
+/**
+ * ipe_inode_setintegrity() - save integrity data from a inode to IPE's LSM blob.
+ * @inode: The inode to source the security blob from.
+ * @type: Supplies the integrity type.
+ * @value: The value to be stored.
+ * @size: The size of @value.
+ *
+ * This hook is currently used to save the existence of a validated fs-verity
+ * builtin signature into LSM blob.
+ *
+ * Return: %0 on success. If an error occurs, the function will return the
+ * -errno.
+ */
+int ipe_inode_setintegrity(const struct inode *inode,
+ enum lsm_integrity_type type,
+ const void *value, size_t size)
+{
+ struct ipe_inode *inode_sec = ipe_inode(inode);
+
+ if (type == LSM_INT_FSVERITY_BUILTINSIG_VALID) {
+ inode_sec->fs_verity_signed = size > 0 && value;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+#endif /* CONFIG_CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
diff --git a/security/ipe/hooks.h b/security/ipe/hooks.h
new file mode 100644
index 000000000000..38d4a387d039
--- /dev/null
+++ b/security/ipe/hooks.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+#ifndef _IPE_HOOKS_H
+#define _IPE_HOOKS_H
+
+#include <linux/fs.h>
+#include <linux/binfmts.h>
+#include <linux/security.h>
+#include <linux/blk_types.h>
+#include <linux/fsverity.h>
+
+enum ipe_hook_type {
+ IPE_HOOK_BPRM_CHECK = 0,
+ IPE_HOOK_MMAP,
+ IPE_HOOK_MPROTECT,
+ IPE_HOOK_KERNEL_READ,
+ IPE_HOOK_KERNEL_LOAD,
+ __IPE_HOOK_MAX
+};
+
+#define IPE_HOOK_INVALID __IPE_HOOK_MAX
+
+int ipe_bprm_check_security(struct linux_binprm *bprm);
+
+int ipe_mmap_file(struct file *f, unsigned long reqprot, unsigned long prot,
+ unsigned long flags);
+
+int ipe_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot);
+
+int ipe_kernel_read_file(struct file *file, enum kernel_read_file_id id,
+ bool contents);
+
+int ipe_kernel_load_data(enum kernel_load_data_id id, bool contents);
+
+void ipe_unpack_initramfs(void);
+
+#ifdef CONFIG_IPE_PROP_DM_VERITY
+void ipe_bdev_free_security(struct block_device *bdev);
+
+int ipe_bdev_setintegrity(struct block_device *bdev, enum lsm_integrity_type type,
+ const void *value, size_t len);
+#endif /* CONFIG_IPE_PROP_DM_VERITY */
+
+#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
+int ipe_inode_setintegrity(const struct inode *inode, enum lsm_integrity_type type,
+ const void *value, size_t size);
+#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+
+#endif /* _IPE_HOOKS_H */
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
new file mode 100644
index 000000000000..4317134cb0da
--- /dev/null
+++ b/security/ipe/ipe.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+#include <uapi/linux/lsm.h>
+
+#include "ipe.h"
+#include "eval.h"
+#include "hooks.h"
+
+extern const char *const ipe_boot_policy;
+bool ipe_enabled;
+
+static struct lsm_blob_sizes ipe_blobs __ro_after_init = {
+ .lbs_superblock = sizeof(struct ipe_superblock),
+#ifdef CONFIG_IPE_PROP_DM_VERITY
+ .lbs_bdev = sizeof(struct ipe_bdev),
+#endif /* CONFIG_IPE_PROP_DM_VERITY */
+#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
+ .lbs_inode = sizeof(struct ipe_inode),
+#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+};
+
+static const struct lsm_id ipe_lsmid = {
+ .name = "ipe",
+ .id = LSM_ID_IPE,
+};
+
+struct ipe_superblock *ipe_sb(const struct super_block *sb)
+{
+ return sb->s_security + ipe_blobs.lbs_superblock;
+}
+
+#ifdef CONFIG_IPE_PROP_DM_VERITY
+struct ipe_bdev *ipe_bdev(struct block_device *b)
+{
+ return b->bd_security + ipe_blobs.lbs_bdev;
+}
+#endif /* CONFIG_IPE_PROP_DM_VERITY */
+
+#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
+struct ipe_inode *ipe_inode(const struct inode *inode)
+{
+ return inode->i_security + ipe_blobs.lbs_inode;
+}
+#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+
+static struct security_hook_list ipe_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(bprm_check_security, ipe_bprm_check_security),
+ LSM_HOOK_INIT(mmap_file, ipe_mmap_file),
+ LSM_HOOK_INIT(file_mprotect, ipe_file_mprotect),
+ LSM_HOOK_INIT(kernel_read_file, ipe_kernel_read_file),
+ LSM_HOOK_INIT(kernel_load_data, ipe_kernel_load_data),
+ LSM_HOOK_INIT(initramfs_populated, ipe_unpack_initramfs),
+#ifdef CONFIG_IPE_PROP_DM_VERITY
+ LSM_HOOK_INIT(bdev_free_security, ipe_bdev_free_security),
+ LSM_HOOK_INIT(bdev_setintegrity, ipe_bdev_setintegrity),
+#endif /* CONFIG_IPE_PROP_DM_VERITY */
+#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
+ LSM_HOOK_INIT(inode_setintegrity, ipe_inode_setintegrity),
+#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+};
+
+/**
+ * ipe_init() - Entry point of IPE.
+ *
+ * This is called at LSM init, which happens occurs early during kernel
+ * start up. During this phase, IPE registers its hooks and loads the
+ * builtin boot policy.
+ *
+ * Return:
+ * * %0 - OK
+ * * %-ENOMEM - Out of memory (OOM)
+ */
+static int __init ipe_init(void)
+{
+ struct ipe_policy *p = NULL;
+
+ security_add_hooks(ipe_hooks, ARRAY_SIZE(ipe_hooks), &ipe_lsmid);
+ ipe_enabled = true;
+
+ if (ipe_boot_policy) {
+ p = ipe_new_policy(ipe_boot_policy, strlen(ipe_boot_policy),
+ NULL, 0);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+
+ rcu_assign_pointer(ipe_active_policy, p);
+ }
+
+ return 0;
+}
+
+DEFINE_LSM(ipe) = {
+ .name = "ipe",
+ .init = ipe_init,
+ .blobs = &ipe_blobs,
+};
diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h
new file mode 100644
index 000000000000..fb37513812dd
--- /dev/null
+++ b/security/ipe/ipe.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#ifndef _IPE_H
+#define _IPE_H
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+#define pr_fmt(fmt) "ipe: " fmt
+
+#include <linux/lsm_hooks.h>
+struct ipe_superblock *ipe_sb(const struct super_block *sb);
+
+extern bool ipe_enabled;
+
+#ifdef CONFIG_IPE_PROP_DM_VERITY
+struct ipe_bdev *ipe_bdev(struct block_device *b);
+#endif /* CONFIG_IPE_PROP_DM_VERITY */
+#ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
+struct ipe_inode *ipe_inode(const struct inode *inode);
+#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+
+#endif /* _IPE_H */
diff --git a/security/ipe/policy.c b/security/ipe/policy.c
new file mode 100644
index 000000000000..b628f696e32b
--- /dev/null
+++ b/security/ipe/policy.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#include <linux/errno.h>
+#include <linux/verification.h>
+
+#include "ipe.h"
+#include "eval.h"
+#include "fs.h"
+#include "policy.h"
+#include "policy_parser.h"
+#include "audit.h"
+
+/* lock for synchronizing writers across ipe policy */
+DEFINE_MUTEX(ipe_policy_lock);
+
+/**
+ * ver_to_u64() - Convert an internal ipe_policy_version to a u64.
+ * @p: Policy to extract the version from.
+ *
+ * Bits (LSB is index 0):
+ * [48,32] -> Major
+ * [32,16] -> Minor
+ * [16, 0] -> Revision
+ *
+ * Return: u64 version of the embedded version structure.
+ */
+static inline u64 ver_to_u64(const struct ipe_policy *const p)
+{
+ u64 r;
+
+ r = (((u64)p->parsed->version.major) << 32)
+ | (((u64)p->parsed->version.minor) << 16)
+ | ((u64)(p->parsed->version.rev));
+
+ return r;
+}
+
+/**
+ * ipe_free_policy() - Deallocate a given IPE policy.
+ * @p: Supplies the policy to free.
+ *
+ * Safe to call on IS_ERR/NULL.
+ */
+void ipe_free_policy(struct ipe_policy *p)
+{
+ if (IS_ERR_OR_NULL(p))
+ return;
+
+ ipe_del_policyfs_node(p);
+ ipe_free_parsed_policy(p->parsed);
+ /*
+ * p->text is allocated only when p->pkcs7 is not NULL
+ * otherwise it points to the plaintext data inside the pkcs7
+ */
+ if (!p->pkcs7)
+ kfree(p->text);
+ kfree(p->pkcs7);
+ kfree(p);
+}
+
+static int set_pkcs7_data(void *ctx, const void *data, size_t len,
+ size_t asn1hdrlen __always_unused)
+{
+ struct ipe_policy *p = ctx;
+
+ p->text = (const char *)data;
+ p->textlen = len;
+
+ return 0;
+}
+
+/**
+ * ipe_update_policy() - parse a new policy and replace old with it.
+ * @root: Supplies a pointer to the securityfs inode saved the policy.
+ * @text: Supplies a pointer to the plain text policy.
+ * @textlen: Supplies the length of @text.
+ * @pkcs7: Supplies a pointer to a buffer containing a pkcs7 message.
+ * @pkcs7len: Supplies the length of @pkcs7len.
+ *
+ * @text/@textlen is mutually exclusive with @pkcs7/@pkcs7len - see
+ * ipe_new_policy.
+ *
+ * Context: Requires root->i_rwsem to be held.
+ * Return: %0 on success. If an error occurs, the function will return
+ * the -errno.
+ */
+int ipe_update_policy(struct inode *root, const char *text, size_t textlen,
+ const char *pkcs7, size_t pkcs7len)
+{
+ struct ipe_policy *old, *ap, *new = NULL;
+ int rc = 0;
+
+ old = (struct ipe_policy *)root->i_private;
+ if (!old)
+ return -ENOENT;
+
+ new = ipe_new_policy(text, textlen, pkcs7, pkcs7len);
+ if (IS_ERR(new))
+ return PTR_ERR(new);
+
+ if (strcmp(new->parsed->name, old->parsed->name)) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ if (ver_to_u64(old) >= ver_to_u64(new)) {
+ rc = -ESTALE;
+ goto err;
+ }
+
+ root->i_private = new;
+ swap(new->policyfs, old->policyfs);
+ ipe_audit_policy_load(new);
+
+ mutex_lock(&ipe_policy_lock);
+ ap = rcu_dereference_protected(ipe_active_policy,
+ lockdep_is_held(&ipe_policy_lock));
+ if (old == ap) {
+ rcu_assign_pointer(ipe_active_policy, new);
+ mutex_unlock(&ipe_policy_lock);
+ ipe_audit_policy_activation(old, new);
+ } else {
+ mutex_unlock(&ipe_policy_lock);
+ }
+ synchronize_rcu();
+ ipe_free_policy(old);
+
+ return 0;
+err:
+ ipe_free_policy(new);
+ return rc;
+}
+
+/**
+ * ipe_new_policy() - Allocate and parse an ipe_policy structure.
+ *
+ * @text: Supplies a pointer to the plain-text policy to parse.
+ * @textlen: Supplies the length of @text.
+ * @pkcs7: Supplies a pointer to a pkcs7-signed IPE policy.
+ * @pkcs7len: Supplies the length of @pkcs7.
+ *
+ * @text/@textlen Should be NULL/0 if @pkcs7/@pkcs7len is set.
+ *
+ * Return:
+ * * a pointer to the ipe_policy structure - Success
+ * * %-EBADMSG - Policy is invalid
+ * * %-ENOMEM - Out of memory (OOM)
+ * * %-ERANGE - Policy version number overflow
+ * * %-EINVAL - Policy version parsing error
+ */
+struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
+ const char *pkcs7, size_t pkcs7len)
+{
+ struct ipe_policy *new = NULL;
+ int rc = 0;
+
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
+ if (!new)
+ return ERR_PTR(-ENOMEM);
+
+ if (!text) {
+ new->pkcs7len = pkcs7len;
+ new->pkcs7 = kmemdup(pkcs7, pkcs7len, GFP_KERNEL);
+ if (!new->pkcs7) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len,
+#ifdef CONFIG_IPE_POLICY_SIG_SECONDARY_KEYRING
+ VERIFY_USE_SECONDARY_KEYRING,
+#else
+ NULL,
+#endif
+ VERIFYING_UNSPECIFIED_SIGNATURE,
+ set_pkcs7_data, new);
+#ifdef CONFIG_IPE_POLICY_SIG_PLATFORM_KEYRING
+ if (rc == -ENOKEY || rc == -EKEYREJECTED)
+ rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len,
+ VERIFY_USE_PLATFORM_KEYRING,
+ VERIFYING_UNSPECIFIED_SIGNATURE,
+ set_pkcs7_data, new);
+#endif
+ if (rc)
+ goto err;
+ } else {
+ new->textlen = textlen;
+ new->text = kstrdup(text, GFP_KERNEL);
+ if (!new->text) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ }
+
+ rc = ipe_parse_policy(new);
+ if (rc)
+ goto err;
+
+ return new;
+err:
+ ipe_free_policy(new);
+ return ERR_PTR(rc);
+}
+
+/**
+ * ipe_set_active_pol() - Make @p the active policy.
+ * @p: Supplies a pointer to the policy to make active.
+ *
+ * Context: Requires root->i_rwsem, which i_private has the policy, to be held.
+ * Return:
+ * * %0 - Success
+ * * %-EINVAL - New active policy version is invalid
+ */
+int ipe_set_active_pol(const struct ipe_policy *p)
+{
+ struct ipe_policy *ap = NULL;
+
+ mutex_lock(&ipe_policy_lock);
+
+ ap = rcu_dereference_protected(ipe_active_policy,
+ lockdep_is_held(&ipe_policy_lock));
+ if (ap == p) {
+ mutex_unlock(&ipe_policy_lock);
+ return 0;
+ }
+ if (ap && ver_to_u64(ap) > ver_to_u64(p)) {
+ mutex_unlock(&ipe_policy_lock);
+ return -EINVAL;
+ }
+
+ rcu_assign_pointer(ipe_active_policy, p);
+ ipe_audit_policy_activation(ap, p);
+ mutex_unlock(&ipe_policy_lock);
+
+ return 0;
+}
diff --git a/security/ipe/policy.h b/security/ipe/policy.h
new file mode 100644
index 000000000000..5bfbdbddeef8
--- /dev/null
+++ b/security/ipe/policy.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+#ifndef _IPE_POLICY_H
+#define _IPE_POLICY_H
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+
+enum ipe_op_type {
+ IPE_OP_EXEC = 0,
+ IPE_OP_FIRMWARE,
+ IPE_OP_KERNEL_MODULE,
+ IPE_OP_KEXEC_IMAGE,
+ IPE_OP_KEXEC_INITRAMFS,
+ IPE_OP_POLICY,
+ IPE_OP_X509,
+ __IPE_OP_MAX,
+};
+
+#define IPE_OP_INVALID __IPE_OP_MAX
+
+enum ipe_action_type {
+ IPE_ACTION_ALLOW = 0,
+ IPE_ACTION_DENY,
+ __IPE_ACTION_MAX
+};
+
+#define IPE_ACTION_INVALID __IPE_ACTION_MAX
+
+enum ipe_prop_type {
+ IPE_PROP_BOOT_VERIFIED_FALSE,
+ IPE_PROP_BOOT_VERIFIED_TRUE,
+ IPE_PROP_DMV_ROOTHASH,
+ IPE_PROP_DMV_SIG_FALSE,
+ IPE_PROP_DMV_SIG_TRUE,
+ IPE_PROP_FSV_DIGEST,
+ IPE_PROP_FSV_SIG_FALSE,
+ IPE_PROP_FSV_SIG_TRUE,
+ __IPE_PROP_MAX
+};
+
+#define IPE_PROP_INVALID __IPE_PROP_MAX
+
+struct ipe_prop {
+ struct list_head next;
+ enum ipe_prop_type type;
+ void *value;
+};
+
+struct ipe_rule {
+ enum ipe_op_type op;
+ enum ipe_action_type action;
+ struct list_head props;
+ struct list_head next;
+};
+
+struct ipe_op_table {
+ struct list_head rules;
+ enum ipe_action_type default_action;
+};
+
+struct ipe_parsed_policy {
+ const char *name;
+ struct {
+ u16 major;
+ u16 minor;
+ u16 rev;
+ } version;
+
+ enum ipe_action_type global_default_action;
+
+ struct ipe_op_table rules[__IPE_OP_MAX];
+};
+
+struct ipe_policy {
+ const char *pkcs7;
+ size_t pkcs7len;
+
+ const char *text;
+ size_t textlen;
+
+ struct ipe_parsed_policy *parsed;
+
+ struct dentry *policyfs;
+};
+
+struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
+ const char *pkcs7, size_t pkcs7len);
+void ipe_free_policy(struct ipe_policy *pol);
+int ipe_update_policy(struct inode *root, const char *text, size_t textlen,
+ const char *pkcs7, size_t pkcs7len);
+int ipe_set_active_pol(const struct ipe_policy *p);
+extern struct mutex ipe_policy_lock;
+
+#endif /* _IPE_POLICY_H */
diff --git a/security/ipe/policy_fs.c b/security/ipe/policy_fs.c
new file mode 100644
index 000000000000..3bcd8cbd09df
--- /dev/null
+++ b/security/ipe/policy_fs.c
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/types.h>
+#include <linux/dcache.h>
+#include <linux/security.h>
+
+#include "ipe.h"
+#include "policy.h"
+#include "eval.h"
+#include "fs.h"
+
+#define MAX_VERSION_SIZE ARRAY_SIZE("65535.65535.65535")
+
+/**
+ * ipefs_file - defines a file in securityfs.
+ */
+struct ipefs_file {
+ const char *name;
+ umode_t access;
+ const struct file_operations *fops;
+};
+
+/**
+ * read_pkcs7() - Read handler for "ipe/policies/$name/pkcs7".
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * @data will be populated with the pkcs7 blob representing the policy
+ * on success. If the policy is unsigned (like the boot policy), this
+ * will return -ENOENT.
+ *
+ * Return:
+ * * Length of buffer written - Success
+ * * %-ENOENT - Policy initializing/deleted or is unsigned
+ */
+static ssize_t read_pkcs7(struct file *f, char __user *data,
+ size_t len, loff_t *offset)
+{
+ const struct ipe_policy *p = NULL;
+ struct inode *root = NULL;
+ int rc = 0;
+
+ root = d_inode(f->f_path.dentry->d_parent);
+
+ inode_lock_shared(root);
+ p = (struct ipe_policy *)root->i_private;
+ if (!p) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ if (!p->pkcs7) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ rc = simple_read_from_buffer(data, len, offset, p->pkcs7, p->pkcs7len);
+
+out:
+ inode_unlock_shared(root);
+
+ return rc;
+}
+
+/**
+ * read_policy() - Read handler for "ipe/policies/$name/policy".
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * @data will be populated with the plain-text version of the policy
+ * on success.
+ *
+ * Return:
+ * * Length of buffer written - Success
+ * * %-ENOENT - Policy initializing/deleted
+ */
+static ssize_t read_policy(struct file *f, char __user *data,
+ size_t len, loff_t *offset)
+{
+ const struct ipe_policy *p = NULL;
+ struct inode *root = NULL;
+ int rc = 0;
+
+ root = d_inode(f->f_path.dentry->d_parent);
+
+ inode_lock_shared(root);
+ p = (struct ipe_policy *)root->i_private;
+ if (!p) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ rc = simple_read_from_buffer(data, len, offset, p->text, p->textlen);
+
+out:
+ inode_unlock_shared(root);
+
+ return rc;
+}
+
+/**
+ * read_name() - Read handler for "ipe/policies/$name/name".
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * @data will be populated with the policy_name attribute on success.
+ *
+ * Return:
+ * * Length of buffer written - Success
+ * * %-ENOENT - Policy initializing/deleted
+ */
+static ssize_t read_name(struct file *f, char __user *data,
+ size_t len, loff_t *offset)
+{
+ const struct ipe_policy *p = NULL;
+ struct inode *root = NULL;
+ int rc = 0;
+
+ root = d_inode(f->f_path.dentry->d_parent);
+
+ inode_lock_shared(root);
+ p = (struct ipe_policy *)root->i_private;
+ if (!p) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ rc = simple_read_from_buffer(data, len, offset, p->parsed->name,
+ strlen(p->parsed->name));
+
+out:
+ inode_unlock_shared(root);
+
+ return rc;
+}
+
+/**
+ * read_version() - Read handler for "ipe/policies/$name/version".
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * @data will be populated with the version string on success.
+ *
+ * Return:
+ * * Length of buffer written - Success
+ * * %-ENOENT - Policy initializing/deleted
+ */
+static ssize_t read_version(struct file *f, char __user *data,
+ size_t len, loff_t *offset)
+{
+ char buffer[MAX_VERSION_SIZE] = { 0 };
+ const struct ipe_policy *p = NULL;
+ struct inode *root = NULL;
+ size_t strsize = 0;
+ ssize_t rc = 0;
+
+ root = d_inode(f->f_path.dentry->d_parent);
+
+ inode_lock_shared(root);
+ p = (struct ipe_policy *)root->i_private;
+ if (!p) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ strsize = scnprintf(buffer, ARRAY_SIZE(buffer), "%hu.%hu.%hu",
+ p->parsed->version.major, p->parsed->version.minor,
+ p->parsed->version.rev);
+
+ rc = simple_read_from_buffer(data, len, offset, buffer, strsize);
+
+out:
+ inode_unlock_shared(root);
+
+ return rc;
+}
+
+/**
+ * setactive() - Write handler for "ipe/policies/$name/active".
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * Return:
+ * * Length of buffer written - Success
+ * * %-EPERM - Insufficient permission
+ * * %-EINVAL - Invalid input
+ * * %-ENOENT - Policy initializing/deleted
+ */
+static ssize_t setactive(struct file *f, const char __user *data,
+ size_t len, loff_t *offset)
+{
+ const struct ipe_policy *p = NULL;
+ struct inode *root = NULL;
+ bool value = false;
+ int rc = 0;
+
+ if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+ return -EPERM;
+
+ rc = kstrtobool_from_user(data, len, &value);
+ if (rc)
+ return rc;
+
+ if (!value)
+ return -EINVAL;
+
+ root = d_inode(f->f_path.dentry->d_parent);
+ inode_lock(root);
+
+ p = (struct ipe_policy *)root->i_private;
+ if (!p) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ rc = ipe_set_active_pol(p);
+
+out:
+ inode_unlock(root);
+ return (rc < 0) ? rc : len;
+}
+
+/**
+ * getactive() - Read handler for "ipe/policies/$name/active".
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * @data will be populated with the 1 or 0 depending on if the
+ * corresponding policy is active.
+ *
+ * Return:
+ * * Length of buffer written - Success
+ * * %-ENOENT - Policy initializing/deleted
+ */
+static ssize_t getactive(struct file *f, char __user *data,
+ size_t len, loff_t *offset)
+{
+ const struct ipe_policy *p = NULL;
+ struct inode *root = NULL;
+ const char *str;
+ int rc = 0;
+
+ root = d_inode(f->f_path.dentry->d_parent);
+
+ inode_lock_shared(root);
+ p = (struct ipe_policy *)root->i_private;
+ if (!p) {
+ inode_unlock_shared(root);
+ return -ENOENT;
+ }
+ inode_unlock_shared(root);
+
+ str = (p == rcu_access_pointer(ipe_active_policy)) ? "1" : "0";
+ rc = simple_read_from_buffer(data, len, offset, str, 1);
+
+ return rc;
+}
+
+/**
+ * update_policy() - Write handler for "ipe/policies/$name/update".
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * On success this updates the policy represented by $name,
+ * in-place.
+ *
+ * Return: Length of buffer written on success. If an error occurs,
+ * the function will return the -errno.
+ */
+static ssize_t update_policy(struct file *f, const char __user *data,
+ size_t len, loff_t *offset)
+{
+ struct inode *root = NULL;
+ char *copy = NULL;
+ int rc = 0;
+
+ if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+ return -EPERM;
+
+ copy = memdup_user(data, len);
+ if (IS_ERR(copy))
+ return PTR_ERR(copy);
+
+ root = d_inode(f->f_path.dentry->d_parent);
+ inode_lock(root);
+ rc = ipe_update_policy(root, NULL, 0, copy, len);
+ inode_unlock(root);
+
+ kfree(copy);
+ if (rc)
+ return rc;
+
+ return len;
+}
+
+/**
+ * delete_policy() - write handler for "ipe/policies/$name/delete".
+ * @f: Supplies a file structure representing the securityfs node.
+ * @data: Supplies a buffer passed to the write syscall.
+ * @len: Supplies the length of @data.
+ * @offset: unused.
+ *
+ * On success this deletes the policy represented by $name.
+ *
+ * Return:
+ * * Length of buffer written - Success
+ * * %-EPERM - Insufficient permission/deleting active policy
+ * * %-EINVAL - Invalid input
+ * * %-ENOENT - Policy initializing/deleted
+ */
+static ssize_t delete_policy(struct file *f, const char __user *data,
+ size_t len, loff_t *offset)
+{
+ struct ipe_policy *ap = NULL;
+ struct ipe_policy *p = NULL;
+ struct inode *root = NULL;
+ bool value = false;
+ int rc = 0;
+
+ if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+ return -EPERM;
+
+ rc = kstrtobool_from_user(data, len, &value);
+ if (rc)
+ return rc;
+
+ if (!value)
+ return -EINVAL;
+
+ root = d_inode(f->f_path.dentry->d_parent);
+ inode_lock(root);
+ p = (struct ipe_policy *)root->i_private;
+ if (!p) {
+ inode_unlock(root);
+ return -ENOENT;
+ }
+
+ mutex_lock(&ipe_policy_lock);
+ ap = rcu_dereference_protected(ipe_active_policy,
+ lockdep_is_held(&ipe_policy_lock));
+ if (p == ap) {
+ mutex_unlock(&ipe_policy_lock);
+ inode_unlock(root);
+ return -EPERM;
+ }
+ mutex_unlock(&ipe_policy_lock);
+
+ root->i_private = NULL;
+ inode_unlock(root);
+
+ synchronize_rcu();
+ ipe_free_policy(p);
+
+ return len;
+}
+
+static const struct file_operations content_fops = {
+ .read = read_policy,
+};
+
+static const struct file_operations pkcs7_fops = {
+ .read = read_pkcs7,
+};
+
+static const struct file_operations name_fops = {
+ .read = read_name,
+};
+
+static const struct file_operations ver_fops = {
+ .read = read_version,
+};
+
+static const struct file_operations active_fops = {
+ .write = setactive,
+ .read = getactive,
+};
+
+static const struct file_operations update_fops = {
+ .write = update_policy,
+};
+
+static const struct file_operations delete_fops = {
+ .write = delete_policy,
+};
+
+/**
+ * policy_subdir - files under a policy subdirectory
+ */
+static const struct ipefs_file policy_subdir[] = {
+ { "pkcs7", 0444, &pkcs7_fops },
+ { "policy", 0444, &content_fops },
+ { "name", 0444, &name_fops },
+ { "version", 0444, &ver_fops },
+ { "active", 0600, &active_fops },
+ { "update", 0200, &update_fops },
+ { "delete", 0200, &delete_fops },
+};
+
+/**
+ * ipe_del_policyfs_node() - Delete a securityfs entry for @p.
+ * @p: Supplies a pointer to the policy to delete a securityfs entry for.
+ */
+void ipe_del_policyfs_node(struct ipe_policy *p)
+{
+ securityfs_recursive_remove(p->policyfs);
+ p->policyfs = NULL;
+}
+
+/**
+ * ipe_new_policyfs_node() - Create a securityfs entry for @p.
+ * @p: Supplies a pointer to the policy to create a securityfs entry for.
+ *
+ * Return: %0 on success. If an error occurs, the function will return
+ * the -errno.
+ */
+int ipe_new_policyfs_node(struct ipe_policy *p)
+{
+ const struct ipefs_file *f = NULL;
+ struct dentry *policyfs = NULL;
+ struct inode *root = NULL;
+ struct dentry *d = NULL;
+ size_t i = 0;
+ int rc = 0;
+
+ if (p->policyfs)
+ return 0;
+
+ policyfs = securityfs_create_dir(p->parsed->name, policy_root);
+ if (IS_ERR(policyfs))
+ return PTR_ERR(policyfs);
+
+ root = d_inode(policyfs);
+
+ for (i = 0; i < ARRAY_SIZE(policy_subdir); ++i) {
+ f = &policy_subdir[i];
+
+ d = securityfs_create_file(f->name, f->access, policyfs,
+ NULL, f->fops);
+ if (IS_ERR(d)) {
+ rc = PTR_ERR(d);
+ goto err;
+ }
+ }
+
+ inode_lock(root);
+ p->policyfs = policyfs;
+ root->i_private = p;
+ inode_unlock(root);
+
+ return 0;
+err:
+ securityfs_recursive_remove(policyfs);
+ return rc;
+}
diff --git a/security/ipe/policy_parser.c b/security/ipe/policy_parser.c
new file mode 100644
index 000000000000..7f27e39931d6
--- /dev/null
+++ b/security/ipe/policy_parser.c
@@ -0,0 +1,559 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/parser.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+
+#include "policy.h"
+#include "policy_parser.h"
+#include "digest.h"
+
+#define START_COMMENT '#'
+#define IPE_POLICY_DELIM " \t"
+#define IPE_LINE_DELIM "\n\r"
+
+/**
+ * new_parsed_policy() - Allocate and initialize a parsed policy.
+ *
+ * Return:
+ * * a pointer to the ipe_parsed_policy structure - Success
+ * * %-ENOMEM - Out of memory (OOM)
+ */
+static struct ipe_parsed_policy *new_parsed_policy(void)
+{
+ struct ipe_parsed_policy *p = NULL;
+ struct ipe_op_table *t = NULL;
+ size_t i = 0;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ p->global_default_action = IPE_ACTION_INVALID;
+
+ for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
+ t = &p->rules[i];
+
+ t->default_action = IPE_ACTION_INVALID;
+ INIT_LIST_HEAD(&t->rules);
+ }
+
+ return p;
+}
+
+/**
+ * remove_comment() - Truncate all chars following START_COMMENT in a string.
+ *
+ * @line: Supplies a policy line string for preprocessing.
+ */
+static void remove_comment(char *line)
+{
+ line = strchr(line, START_COMMENT);
+
+ if (line)
+ *line = '\0';
+}
+
+/**
+ * remove_trailing_spaces() - Truncate all trailing spaces in a string.
+ *
+ * @line: Supplies a policy line string for preprocessing.
+ *
+ * Return: The length of truncated string.
+ */
+static size_t remove_trailing_spaces(char *line)
+{
+ size_t i = 0;
+
+ i = strlen(line);
+ while (i > 0 && isspace(line[i - 1]))
+ i--;
+
+ line[i] = '\0';
+
+ return i;
+}
+
+/**
+ * parse_version() - Parse policy version.
+ * @ver: Supplies a version string to be parsed.
+ * @p: Supplies the partial parsed policy.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-EBADMSG - Version string is invalid
+ * * %-ERANGE - Version number overflow
+ * * %-EINVAL - Parsing error
+ */
+static int parse_version(char *ver, struct ipe_parsed_policy *p)
+{
+ u16 *const cv[] = { &p->version.major, &p->version.minor, &p->version.rev };
+ size_t sep_count = 0;
+ char *token;
+ int rc = 0;
+
+ while ((token = strsep(&ver, ".")) != NULL) {
+ /* prevent overflow */
+ if (sep_count >= ARRAY_SIZE(cv))
+ return -EBADMSG;
+
+ rc = kstrtou16(token, 10, cv[sep_count]);
+ if (rc)
+ return rc;
+
+ ++sep_count;
+ }
+
+ /* prevent underflow */
+ if (sep_count != ARRAY_SIZE(cv))
+ return -EBADMSG;
+
+ return 0;
+}
+
+enum header_opt {
+ IPE_HEADER_POLICY_NAME = 0,
+ IPE_HEADER_POLICY_VERSION,
+ __IPE_HEADER_MAX
+};
+
+static const match_table_t header_tokens = {
+ {IPE_HEADER_POLICY_NAME, "policy_name=%s"},
+ {IPE_HEADER_POLICY_VERSION, "policy_version=%s"},
+ {__IPE_HEADER_MAX, NULL}
+};
+
+/**
+ * parse_header() - Parse policy header information.
+ * @line: Supplies header line to be parsed.
+ * @p: Supplies the partial parsed policy.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-EBADMSG - Header string is invalid
+ * * %-ENOMEM - Out of memory (OOM)
+ * * %-ERANGE - Version number overflow
+ * * %-EINVAL - Version parsing error
+ */
+static int parse_header(char *line, struct ipe_parsed_policy *p)
+{
+ substring_t args[MAX_OPT_ARGS];
+ char *t, *ver = NULL;
+ size_t idx = 0;
+ int rc = 0;
+
+ while ((t = strsep(&line, IPE_POLICY_DELIM)) != NULL) {
+ int token;
+
+ if (*t == '\0')
+ continue;
+ if (idx >= __IPE_HEADER_MAX) {
+ rc = -EBADMSG;
+ goto out;
+ }
+
+ token = match_token(t, header_tokens, args);
+ if (token != idx) {
+ rc = -EBADMSG;
+ goto out;
+ }
+
+ switch (token) {
+ case IPE_HEADER_POLICY_NAME:
+ p->name = match_strdup(&args[0]);
+ if (!p->name)
+ rc = -ENOMEM;
+ break;
+ case IPE_HEADER_POLICY_VERSION:
+ ver = match_strdup(&args[0]);
+ if (!ver) {
+ rc = -ENOMEM;
+ break;
+ }
+ rc = parse_version(ver, p);
+ break;
+ default:
+ rc = -EBADMSG;
+ }
+ if (rc)
+ goto out;
+ ++idx;
+ }
+
+ if (idx != __IPE_HEADER_MAX)
+ rc = -EBADMSG;
+
+out:
+ kfree(ver);
+ return rc;
+}
+
+/**
+ * token_default() - Determine if the given token is "DEFAULT".
+ * @token: Supplies the token string to be compared.
+ *
+ * Return:
+ * * %false - The token is not "DEFAULT"
+ * * %true - The token is "DEFAULT"
+ */
+static bool token_default(char *token)
+{
+ return !strcmp(token, "DEFAULT");
+}
+
+/**
+ * free_rule() - Free the supplied ipe_rule struct.
+ * @r: Supplies the ipe_rule struct to be freed.
+ *
+ * Free a ipe_rule struct @r. Note @r must be removed from any lists before
+ * calling this function.
+ */
+static void free_rule(struct ipe_rule *r)
+{
+ struct ipe_prop *p, *t;
+
+ if (IS_ERR_OR_NULL(r))
+ return;
+
+ list_for_each_entry_safe(p, t, &r->props, next) {
+ list_del(&p->next);
+ ipe_digest_free(p->value);
+ kfree(p);
+ }
+
+ kfree(r);
+}
+
+static const match_table_t operation_tokens = {
+ {IPE_OP_EXEC, "op=EXECUTE"},
+ {IPE_OP_FIRMWARE, "op=FIRMWARE"},
+ {IPE_OP_KERNEL_MODULE, "op=KMODULE"},
+ {IPE_OP_KEXEC_IMAGE, "op=KEXEC_IMAGE"},
+ {IPE_OP_KEXEC_INITRAMFS, "op=KEXEC_INITRAMFS"},
+ {IPE_OP_POLICY, "op=POLICY"},
+ {IPE_OP_X509, "op=X509_CERT"},
+ {IPE_OP_INVALID, NULL}
+};
+
+/**
+ * parse_operation() - Parse the operation type given a token string.
+ * @t: Supplies the token string to be parsed.
+ *
+ * Return: The parsed operation type.
+ */
+static enum ipe_op_type parse_operation(char *t)
+{
+ substring_t args[MAX_OPT_ARGS];
+
+ return match_token(t, operation_tokens, args);
+}
+
+static const match_table_t action_tokens = {
+ {IPE_ACTION_ALLOW, "action=ALLOW"},
+ {IPE_ACTION_DENY, "action=DENY"},
+ {IPE_ACTION_INVALID, NULL}
+};
+
+/**
+ * parse_action() - Parse the action type given a token string.
+ * @t: Supplies the token string to be parsed.
+ *
+ * Return: The parsed action type.
+ */
+static enum ipe_action_type parse_action(char *t)
+{
+ substring_t args[MAX_OPT_ARGS];
+
+ return match_token(t, action_tokens, args);
+}
+
+static const match_table_t property_tokens = {
+ {IPE_PROP_BOOT_VERIFIED_FALSE, "boot_verified=FALSE"},
+ {IPE_PROP_BOOT_VERIFIED_TRUE, "boot_verified=TRUE"},
+ {IPE_PROP_DMV_ROOTHASH, "dmverity_roothash=%s"},
+ {IPE_PROP_DMV_SIG_FALSE, "dmverity_signature=FALSE"},
+ {IPE_PROP_DMV_SIG_TRUE, "dmverity_signature=TRUE"},
+ {IPE_PROP_FSV_DIGEST, "fsverity_digest=%s"},
+ {IPE_PROP_FSV_SIG_FALSE, "fsverity_signature=FALSE"},
+ {IPE_PROP_FSV_SIG_TRUE, "fsverity_signature=TRUE"},
+ {IPE_PROP_INVALID, NULL}
+};
+
+/**
+ * parse_property() - Parse a rule property given a token string.
+ * @t: Supplies the token string to be parsed.
+ * @r: Supplies the ipe_rule the parsed property will be associated with.
+ *
+ * This function parses and associates a property with an IPE rule based
+ * on a token string.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-ENOMEM - Out of memory (OOM)
+ * * %-EBADMSG - The supplied token cannot be parsed
+ */
+static int parse_property(char *t, struct ipe_rule *r)
+{
+ substring_t args[MAX_OPT_ARGS];
+ struct ipe_prop *p = NULL;
+ int rc = 0;
+ int token;
+ char *dup = NULL;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ token = match_token(t, property_tokens, args);
+
+ switch (token) {
+ case IPE_PROP_DMV_ROOTHASH:
+ case IPE_PROP_FSV_DIGEST:
+ dup = match_strdup(&args[0]);
+ if (!dup) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ p->value = ipe_digest_parse(dup);
+ if (IS_ERR(p->value)) {
+ rc = PTR_ERR(p->value);
+ goto err;
+ }
+ fallthrough;
+ case IPE_PROP_BOOT_VERIFIED_FALSE:
+ case IPE_PROP_BOOT_VERIFIED_TRUE:
+ case IPE_PROP_DMV_SIG_FALSE:
+ case IPE_PROP_DMV_SIG_TRUE:
+ case IPE_PROP_FSV_SIG_FALSE:
+ case IPE_PROP_FSV_SIG_TRUE:
+ p->type = token;
+ break;
+ default:
+ rc = -EBADMSG;
+ break;
+ }
+ if (rc)
+ goto err;
+ list_add_tail(&p->next, &r->props);
+
+out:
+ kfree(dup);
+ return rc;
+err:
+ kfree(p);
+ goto out;
+}
+
+/**
+ * parse_rule() - parse a policy rule line.
+ * @line: Supplies rule line to be parsed.
+ * @p: Supplies the partial parsed policy.
+ *
+ * Return:
+ * * 0 - Success
+ * * %-ENOMEM - Out of memory (OOM)
+ * * %-EBADMSG - Policy syntax error
+ */
+static int parse_rule(char *line, struct ipe_parsed_policy *p)
+{
+ enum ipe_action_type action = IPE_ACTION_INVALID;
+ enum ipe_op_type op = IPE_OP_INVALID;
+ bool is_default_rule = false;
+ struct ipe_rule *r = NULL;
+ bool first_token = true;
+ bool op_parsed = false;
+ int rc = 0;
+ char *t;
+
+ if (IS_ERR_OR_NULL(line))
+ return -EBADMSG;
+
+ r = kzalloc(sizeof(*r), GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&r->next);
+ INIT_LIST_HEAD(&r->props);
+
+ while (t = strsep(&line, IPE_POLICY_DELIM), line) {
+ if (*t == '\0')
+ continue;
+ if (first_token && token_default(t)) {
+ is_default_rule = true;
+ } else {
+ if (!op_parsed) {
+ op = parse_operation(t);
+ if (op == IPE_OP_INVALID)
+ rc = -EBADMSG;
+ else
+ op_parsed = true;
+ } else {
+ rc = parse_property(t, r);
+ }
+ }
+
+ if (rc)
+ goto err;
+ first_token = false;
+ }
+
+ action = parse_action(t);
+ if (action == IPE_ACTION_INVALID) {
+ rc = -EBADMSG;
+ goto err;
+ }
+
+ if (is_default_rule) {
+ if (!list_empty(&r->props)) {
+ rc = -EBADMSG;
+ } else if (op == IPE_OP_INVALID) {
+ if (p->global_default_action != IPE_ACTION_INVALID)
+ rc = -EBADMSG;
+ else
+ p->global_default_action = action;
+ } else {
+ if (p->rules[op].default_action != IPE_ACTION_INVALID)
+ rc = -EBADMSG;
+ else
+ p->rules[op].default_action = action;
+ }
+ } else if (op != IPE_OP_INVALID && action != IPE_ACTION_INVALID) {
+ r->op = op;
+ r->action = action;
+ } else {
+ rc = -EBADMSG;
+ }
+
+ if (rc)
+ goto err;
+ if (!is_default_rule)
+ list_add_tail(&r->next, &p->rules[op].rules);
+ else
+ free_rule(r);
+
+ return rc;
+err:
+ free_rule(r);
+ return rc;
+}
+
+/**
+ * ipe_free_parsed_policy() - free a parsed policy structure.
+ * @p: Supplies the parsed policy.
+ */
+void ipe_free_parsed_policy(struct ipe_parsed_policy *p)
+{
+ struct ipe_rule *pp, *t;
+ size_t i = 0;
+
+ if (IS_ERR_OR_NULL(p))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(p->rules); ++i)
+ list_for_each_entry_safe(pp, t, &p->rules[i].rules, next) {
+ list_del(&pp->next);
+ free_rule(pp);
+ }
+
+ kfree(p->name);
+ kfree(p);
+}
+
+/**
+ * validate_policy() - validate a parsed policy.
+ * @p: Supplies the fully parsed policy.
+ *
+ * Given a policy structure that was just parsed, validate that all
+ * operations have their default rules or a global default rule is set.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-EBADMSG - Policy is invalid
+ */
+static int validate_policy(const struct ipe_parsed_policy *p)
+{
+ size_t i = 0;
+
+ if (p->global_default_action != IPE_ACTION_INVALID)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
+ if (p->rules[i].default_action == IPE_ACTION_INVALID)
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+/**
+ * ipe_parse_policy() - Given a string, parse the string into an IPE policy.
+ * @p: partially filled ipe_policy structure to populate with the result.
+ * it must have text and textlen set.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-EBADMSG - Policy is invalid
+ * * %-ENOMEM - Out of Memory
+ * * %-ERANGE - Policy version number overflow
+ * * %-EINVAL - Policy version parsing error
+ */
+int ipe_parse_policy(struct ipe_policy *p)
+{
+ struct ipe_parsed_policy *pp = NULL;
+ char *policy = NULL, *dup = NULL;
+ bool header_parsed = false;
+ char *line = NULL;
+ size_t len;
+ int rc = 0;
+
+ if (!p->textlen)
+ return -EBADMSG;
+
+ policy = kmemdup_nul(p->text, p->textlen, GFP_KERNEL);
+ if (!policy)
+ return -ENOMEM;
+ dup = policy;
+
+ pp = new_parsed_policy();
+ if (IS_ERR(pp)) {
+ rc = PTR_ERR(pp);
+ goto out;
+ }
+
+ while ((line = strsep(&policy, IPE_LINE_DELIM)) != NULL) {
+ remove_comment(line);
+ len = remove_trailing_spaces(line);
+ if (!len)
+ continue;
+
+ if (!header_parsed) {
+ rc = parse_header(line, pp);
+ if (rc)
+ goto err;
+ header_parsed = true;
+ } else {
+ rc = parse_rule(line, pp);
+ if (rc)
+ goto err;
+ }
+ }
+
+ if (!header_parsed || validate_policy(pp)) {
+ rc = -EBADMSG;
+ goto err;
+ }
+
+ p->parsed = pp;
+
+out:
+ kfree(dup);
+ return rc;
+err:
+ ipe_free_parsed_policy(pp);
+ goto out;
+}
diff --git a/security/ipe/policy_parser.h b/security/ipe/policy_parser.h
new file mode 100644
index 000000000000..62b6209019a2
--- /dev/null
+++ b/security/ipe/policy_parser.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+#ifndef _IPE_POLICY_PARSER_H
+#define _IPE_POLICY_PARSER_H
+
+int ipe_parse_policy(struct ipe_policy *p);
+void ipe_free_parsed_policy(struct ipe_parsed_policy *p);
+
+#endif /* _IPE_POLICY_PARSER_H */
diff --git a/security/ipe/policy_tests.c b/security/ipe/policy_tests.c
new file mode 100644
index 000000000000..5f1654deeb04
--- /dev/null
+++ b/security/ipe/policy_tests.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <kunit/test.h>
+#include "policy.h"
+struct policy_case {
+ const char *const policy;
+ int errno;
+ const char *const desc;
+};
+
+static const struct policy_case policy_cases[] = {
+ {
+ "policy_name=allowall policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW",
+ 0,
+ "basic",
+ },
+ {
+ "policy_name=trailing_comment policy_version=152.0.0 #This is comment\n"
+ "DEFAULT action=ALLOW",
+ 0,
+ "trailing comment",
+ },
+ {
+ "policy_name=allowallnewline policy_version=0.2.0\n"
+ "DEFAULT action=ALLOW\n"
+ "\n",
+ 0,
+ "trailing newline",
+ },
+ {
+ "policy_name=carriagereturnlinefeed policy_version=0.0.1\n"
+ "DEFAULT action=ALLOW\n"
+ "\r\n",
+ 0,
+ "clrf newline",
+ },
+ {
+ "policy_name=whitespace policy_version=0.0.0\n"
+ "DEFAULT\taction=ALLOW\n"
+ " \t DEFAULT \t op=EXECUTE action=DENY\n"
+ "op=EXECUTE boot_verified=TRUE action=ALLOW\n"
+ "# this is a\tcomment\t\t\t\t\n"
+ "DEFAULT \t op=KMODULE\t\t\t action=DENY\r\n"
+ "op=KMODULE boot_verified=TRUE action=ALLOW\n",
+ 0,
+ "various whitespaces and nested default",
+ },
+ {
+ "policy_name=boot_verified policy_version=-1236.0.0\n"
+ "DEFAULT\taction=ALLOW\n",
+ -EINVAL,
+ "negative version",
+ },
+ {
+ "policy_name=$@!*&^%%\\:;{}() policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW",
+ 0,
+ "special characters",
+ },
+ {
+ "policy_name=test policy_version=999999.0.0\n"
+ "DEFAULT action=ALLOW",
+ -ERANGE,
+ "overflow version",
+ },
+ {
+ "policy_name=test policy_version=255.0\n"
+ "DEFAULT action=ALLOW",
+ -EBADMSG,
+ "incomplete version",
+ },
+ {
+ "policy_name=test policy_version=111.0.0.0\n"
+ "DEFAULT action=ALLOW",
+ -EBADMSG,
+ "extra version",
+ },
+ {
+ "",
+ -EBADMSG,
+ "0-length policy",
+ },
+ {
+ "policy_name=test\0policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW",
+ -EBADMSG,
+ "random null in header",
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "\0DEFAULT action=ALLOW",
+ -EBADMSG,
+ "incomplete policy from NULL",
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=DENY\n\0"
+ "op=EXECUTE dmverity_signature=TRUE action=ALLOW\n",
+ 0,
+ "NULL truncates policy",
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "op=EXECUTE dmverity_signature=abc action=ALLOW",
+ -EBADMSG,
+ "invalid property type",
+ },
+ {
+ "DEFAULT action=ALLOW",
+ -EBADMSG,
+ "missing policy header",
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n",
+ -EBADMSG,
+ "missing default definition",
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "dmverity_signature=TRUE op=EXECUTE action=ALLOW",
+ -EBADMSG,
+ "invalid rule ordering"
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "action=ALLOW op=EXECUTE dmverity_signature=TRUE",
+ -EBADMSG,
+ "invalid rule ordering (2)",
+ },
+ {
+ "policy_name=test policy_version=0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "op=EXECUTE dmverity_signature=TRUE action=ALLOW",
+ -EBADMSG,
+ "invalid version",
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "op=UNKNOWN dmverity_signature=TRUE action=ALLOW",
+ -EBADMSG,
+ "unknown operation",
+ },
+ {
+ "policy_name=asdvpolicy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n",
+ -EBADMSG,
+ "missing space after policy name",
+ },
+ {
+ "policy_name=test\xFF\xEF policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "op=EXECUTE dmverity_signature=TRUE action=ALLOW",
+ 0,
+ "expanded ascii",
+ },
+ {
+ "policy_name=test\xFF\xEF policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "op=EXECUTE dmverity_roothash=GOOD_DOG action=ALLOW",
+ -EBADMSG,
+ "invalid property value (2)",
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "policy_name=test policy_version=0.1.0\n"
+ "DEFAULT action=ALLOW",
+ -EBADMSG,
+ "double header"
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "DEFAULT action=ALLOW\n",
+ -EBADMSG,
+ "double default"
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "DEFAULT op=EXECUTE action=DENY\n"
+ "DEFAULT op=EXECUTE action=ALLOW\n",
+ -EBADMSG,
+ "double operation default"
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "DEFAULT op=EXECUTE action=DEN\n",
+ -EBADMSG,
+ "invalid action value"
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "DEFAULT op=EXECUTE action\n",
+ -EBADMSG,
+ "invalid action value (2)"
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "UNKNOWN value=true\n",
+ -EBADMSG,
+ "unrecognized statement"
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "op=EXECUTE dmverity_roothash=1c0d7ee1f8343b7fbe418378e8eb22c061d7dec7 action=DENY\n",
+ -EBADMSG,
+ "old-style digest"
+ },
+ {
+ "policy_name=test policy_version=0.0.0\n"
+ "DEFAULT action=ALLOW\n"
+ "op=EXECUTE fsverity_digest=1c0d7ee1f8343b7fbe418378e8eb22c061d7dec7 action=DENY\n",
+ -EBADMSG,
+ "old-style digest"
+ }
+};
+
+static void pol_to_desc(const struct policy_case *c, char *desc)
+{
+ strscpy(desc, c->desc, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(ipe_policies, policy_cases, pol_to_desc);
+
+/**
+ * ipe_parser_unsigned_test - Test the parser by passing unsigned policies.
+ * @test: Supplies a pointer to a kunit structure.
+ *
+ * This is called by the kunit harness. This test does not check the correctness
+ * of the policy, but ensures that errors are handled correctly.
+ */
+static void ipe_parser_unsigned_test(struct kunit *test)
+{
+ const struct policy_case *p = test->param_value;
+ struct ipe_policy *pol;
+
+ pol = ipe_new_policy(p->policy, strlen(p->policy), NULL, 0);
+
+ if (p->errno) {
+ KUNIT_EXPECT_EQ(test, PTR_ERR(pol), p->errno);
+ return;
+ }
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pol);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, pol->parsed);
+ KUNIT_EXPECT_STREQ(test, pol->text, p->policy);
+ KUNIT_EXPECT_PTR_EQ(test, NULL, pol->pkcs7);
+ KUNIT_EXPECT_EQ(test, 0, pol->pkcs7len);
+
+ ipe_free_policy(pol);
+}
+
+/**
+ * ipe_parser_widestring_test - Ensure parser fail on a wide string policy.
+ * @test: Supplies a pointer to a kunit structure.
+ *
+ * This is called by the kunit harness.
+ */
+static void ipe_parser_widestring_test(struct kunit *test)
+{
+ const unsigned short policy[] = L"policy_name=Test policy_version=0.0.0\n"
+ L"DEFAULT action=ALLOW";
+ struct ipe_policy *pol = NULL;
+
+ pol = ipe_new_policy((const char *)policy, (ARRAY_SIZE(policy) - 1) * 2, NULL, 0);
+ KUNIT_EXPECT_TRUE(test, IS_ERR_OR_NULL(pol));
+
+ ipe_free_policy(pol);
+}
+
+static struct kunit_case ipe_parser_test_cases[] = {
+ KUNIT_CASE_PARAM(ipe_parser_unsigned_test, ipe_policies_gen_params),
+ KUNIT_CASE(ipe_parser_widestring_test),
+ { }
+};
+
+static struct kunit_suite ipe_parser_test_suite = {
+ .name = "ipe-parser",
+ .test_cases = ipe_parser_test_cases,
+};
+
+kunit_test_suite(ipe_parser_test_suite);
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 8af2136069d2..831cb84fd75a 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -1040,4 +1040,5 @@ static void __exit cleanup_encrypted(void)
late_initcall(init_encrypted);
module_exit(cleanup_encrypted);
+MODULE_DESCRIPTION("Encrypted key type");
MODULE_LICENSE("GPL");
diff --git a/security/keys/gc.c b/security/keys/gc.c
index eaddaceda14e..f27223ea4578 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -155,14 +155,6 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
security_key_free(key);
- /* deal with the user's key tracking and quota */
- if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
- spin_lock(&key->user->lock);
- key->user->qnkeys--;
- key->user->qnbytes -= key->quotalen;
- spin_unlock(&key->user->lock);
- }
-
atomic_dec(&key->user->nkeys);
if (state != KEY_IS_UNINSTANTIATED)
atomic_dec(&key->user->nikeys);
@@ -226,8 +218,10 @@ continue_scanning:
key = rb_entry(cursor, struct key, serial_node);
cursor = rb_next(cursor);
- if (refcount_read(&key->usage) == 0)
+ if (test_bit(KEY_FLAG_FINAL_PUT, &key->flags)) {
+ smp_mb(); /* Clobber key->user after FINAL_PUT seen. */
goto found_unreferenced_key;
+ }
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
if (key->type == key_gc_dead_keytype) {
diff --git a/security/keys/key.c b/security/keys/key.c
index 5b10641debd5..7198cd2ac3a3 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -13,7 +13,6 @@
#include <linux/security.h>
#include <linux/workqueue.h>
#include <linux/random.h>
-#include <linux/ima.h>
#include <linux/err.h>
#include "internal.h"
@@ -231,6 +230,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
struct key *key;
size_t desclen, quotalen;
int ret;
+ unsigned long irqflags;
key = ERR_PTR(-EINVAL);
if (!desc || !*desc)
@@ -260,7 +260,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ?
key_quota_root_maxbytes : key_quota_maxbytes;
- spin_lock(&user->lock);
+ spin_lock_irqsave(&user->lock, irqflags);
if (!(flags & KEY_ALLOC_QUOTA_OVERRUN)) {
if (user->qnkeys + 1 > maxkeys ||
user->qnbytes + quotalen > maxbytes ||
@@ -270,7 +270,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
user->qnkeys++;
user->qnbytes += quotalen;
- spin_unlock(&user->lock);
+ spin_unlock_irqrestore(&user->lock, irqflags);
}
/* allocate and initialise the key and its description */
@@ -328,10 +328,10 @@ security_error:
kfree(key->description);
kmem_cache_free(key_jar, key);
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
- spin_lock(&user->lock);
+ spin_lock_irqsave(&user->lock, irqflags);
user->qnkeys--;
user->qnbytes -= quotalen;
- spin_unlock(&user->lock);
+ spin_unlock_irqrestore(&user->lock, irqflags);
}
key_user_put(user);
key = ERR_PTR(ret);
@@ -341,10 +341,10 @@ no_memory_3:
kmem_cache_free(key_jar, key);
no_memory_2:
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
- spin_lock(&user->lock);
+ spin_lock_irqsave(&user->lock, irqflags);
user->qnkeys--;
user->qnbytes -= quotalen;
- spin_unlock(&user->lock);
+ spin_unlock_irqrestore(&user->lock, irqflags);
}
key_user_put(user);
no_memory_1:
@@ -352,7 +352,7 @@ no_memory_1:
goto error;
no_quota:
- spin_unlock(&user->lock);
+ spin_unlock_irqrestore(&user->lock, irqflags);
key_user_put(user);
key = ERR_PTR(-EDQUOT);
goto error;
@@ -381,8 +381,9 @@ int key_payload_reserve(struct key *key, size_t datalen)
if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
unsigned maxbytes = uid_eq(key->user->uid, GLOBAL_ROOT_UID) ?
key_quota_root_maxbytes : key_quota_maxbytes;
+ unsigned long flags;
- spin_lock(&key->user->lock);
+ spin_lock_irqsave(&key->user->lock, flags);
if (delta > 0 &&
(key->user->qnbytes + delta > maxbytes ||
@@ -393,7 +394,7 @@ int key_payload_reserve(struct key *key, size_t datalen)
key->user->qnbytes += delta;
key->quotalen += delta;
}
- spin_unlock(&key->user->lock);
+ spin_unlock_irqrestore(&key->user->lock, flags);
}
/* change the recorded data length if that didn't generate an error */
@@ -464,7 +465,8 @@ static int __key_instantiate_and_link(struct key *key,
if (authkey)
key_invalidate(authkey);
- key_set_expiry(key, prep->expiry);
+ if (prep->expiry != TIME64_MAX)
+ key_set_expiry(key, prep->expiry);
}
}
@@ -646,8 +648,20 @@ void key_put(struct key *key)
if (key) {
key_check(key);
- if (refcount_dec_and_test(&key->usage))
+ if (refcount_dec_and_test(&key->usage)) {
+ unsigned long flags;
+
+ /* deal with the user's key tracking and quota */
+ if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
+ spin_lock_irqsave(&key->user->lock, flags);
+ key->user->qnkeys--;
+ key->user->qnbytes -= key->quotalen;
+ spin_unlock_irqrestore(&key->user->lock, flags);
+ }
+ smp_mb(); /* key->user before FINAL_PUT set. */
+ set_bit(KEY_FLAG_FINAL_PUT, &key->flags);
schedule_work(&key_gc_work);
+ }
}
}
EXPORT_SYMBOL(key_put);
@@ -930,8 +944,8 @@ static key_ref_t __key_create_or_update(key_ref_t keyring_ref,
goto error_link_end;
}
- ima_post_key_create_or_update(keyring, key, payload, plen,
- flags, true);
+ security_key_post_create_or_update(keyring, key, payload, plen, flags,
+ true);
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
@@ -964,9 +978,8 @@ error:
key_ref = __key_update(key_ref, &prep);
if (!IS_ERR(key_ref))
- ima_post_key_create_or_update(keyring, key,
- payload, plen,
- flags, false);
+ security_key_post_create_or_update(keyring, key, payload, plen,
+ flags, false);
goto error_free_prep;
}
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 10ba439968f7..ab927a142f51 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -954,6 +954,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
long ret;
kuid_t uid;
kgid_t gid;
+ unsigned long flags;
uid = make_kuid(current_user_ns(), user);
gid = make_kgid(current_user_ns(), group);
@@ -1010,7 +1011,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ?
key_quota_root_maxbytes : key_quota_maxbytes;
- spin_lock(&newowner->lock);
+ spin_lock_irqsave(&newowner->lock, flags);
if (newowner->qnkeys + 1 > maxkeys ||
newowner->qnbytes + key->quotalen > maxbytes ||
newowner->qnbytes + key->quotalen <
@@ -1019,12 +1020,12 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
newowner->qnkeys++;
newowner->qnbytes += key->quotalen;
- spin_unlock(&newowner->lock);
+ spin_unlock_irqrestore(&newowner->lock, flags);
- spin_lock(&key->user->lock);
+ spin_lock_irqsave(&key->user->lock, flags);
key->user->qnkeys--;
key->user->qnbytes -= key->quotalen;
- spin_unlock(&key->user->lock);
+ spin_unlock_irqrestore(&key->user->lock, flags);
}
atomic_dec(&key->user->nkeys);
@@ -1056,7 +1057,7 @@ error:
return ret;
quota_overrun:
- spin_unlock(&newowner->lock);
+ spin_unlock_irqrestore(&newowner->lock, flags);
zapowner = newowner;
ret = -EDQUOT;
goto error_put;
@@ -1693,7 +1694,7 @@ long keyctl_session_to_parent(void)
goto unlock;
/* cancel an already pending keyring replacement */
- oldwork = task_work_cancel(parent, key_change_session_keyring);
+ oldwork = task_work_cancel_func(parent, key_change_session_keyring);
/* the replacement session keyring is applied just prior to userspace
* restarting */
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 4448758f643a..f331725d5a37 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -772,8 +772,11 @@ ascend_to_node:
for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) {
ptr = READ_ONCE(node->slots[slot]);
- if (assoc_array_ptr_is_meta(ptr) && node->back_pointer)
- goto descend_to_node;
+ if (assoc_array_ptr_is_meta(ptr)) {
+ if (node->back_pointer ||
+ assoc_array_ptr_is_shortcut(ptr))
+ goto descend_to_node;
+ }
if (!keyring_ptr_is_keyring(ptr))
continue;
diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c
index b348e1679d5d..cde08c478f32 100644
--- a/security/keys/sysctl.c
+++ b/security/keys/sysctl.c
@@ -9,7 +9,7 @@
#include <linux/sysctl.h>
#include "internal.h"
-static struct ctl_table key_sysctls[] = {
+static const struct ctl_table key_sysctls[] = {
{
.procname = "maxkeys",
.data = &key_quota_maxkeys,
@@ -66,7 +66,6 @@ static struct ctl_table key_sysctls[] = {
.extra2 = (void *) SYSCTL_INT_MAX,
},
#endif
- { }
};
static int __init init_security_keys_sysctls(void)
diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig
index dbfdd8536468..1fb8aa001995 100644
--- a/security/keys/trusted-keys/Kconfig
+++ b/security/keys/trusted-keys/Kconfig
@@ -1,3 +1,6 @@
+config HAVE_TRUSTED_KEYS
+ bool
+
config TRUSTED_KEYS_TPM
bool "TPM-based trusted keys"
depends on TCG_TPM >= TRUSTED_KEYS
@@ -9,6 +12,7 @@ config TRUSTED_KEYS_TPM
select ASN1_ENCODER
select OID_REGISTRY
select ASN1
+ select HAVE_TRUSTED_KEYS
help
Enable use of the Trusted Platform Module (TPM) as trusted key
backend. Trusted keys are random number symmetric keys,
@@ -20,6 +24,7 @@ config TRUSTED_KEYS_TEE
bool "TEE-based trusted keys"
depends on TEE >= TRUSTED_KEYS
default y
+ select HAVE_TRUSTED_KEYS
help
Enable use of the Trusted Execution Environment (TEE) as trusted
key backend.
@@ -29,10 +34,19 @@ config TRUSTED_KEYS_CAAM
depends on CRYPTO_DEV_FSL_CAAM_JR >= TRUSTED_KEYS
select CRYPTO_DEV_FSL_CAAM_BLOB_GEN
default y
+ select HAVE_TRUSTED_KEYS
help
Enable use of NXP's Cryptographic Accelerator and Assurance Module
(CAAM) as trusted key backend.
-if !TRUSTED_KEYS_TPM && !TRUSTED_KEYS_TEE && !TRUSTED_KEYS_CAAM
-comment "No trust source selected!"
+config TRUSTED_KEYS_DCP
+ bool "DCP-based trusted keys"
+ depends on CRYPTO_DEV_MXS_DCP >= TRUSTED_KEYS
+ default y
+ select HAVE_TRUSTED_KEYS
+ help
+ Enable use of NXP's DCP (Data Co-Processor) as trusted key backend.
+
+if !HAVE_TRUSTED_KEYS
+ comment "No trust source selected!"
endif
diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
index 735aa0bc08ef..f0f3b27f688b 100644
--- a/security/keys/trusted-keys/Makefile
+++ b/security/keys/trusted-keys/Makefile
@@ -14,3 +14,5 @@ trusted-$(CONFIG_TRUSTED_KEYS_TPM) += tpm2key.asn1.o
trusted-$(CONFIG_TRUSTED_KEYS_TEE) += trusted_tee.o
trusted-$(CONFIG_TRUSTED_KEYS_CAAM) += trusted_caam.o
+
+trusted-$(CONFIG_TRUSTED_KEYS_DCP) += trusted_dcp.o
diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
index fee1ab2c734d..e2d9644efde1 100644
--- a/security/keys/trusted-keys/trusted_core.c
+++ b/security/keys/trusted-keys/trusted_core.c
@@ -10,6 +10,7 @@
#include <keys/trusted-type.h>
#include <keys/trusted_tee.h>
#include <keys/trusted_caam.h>
+#include <keys/trusted_dcp.h>
#include <keys/trusted_tpm.h>
#include <linux/capability.h>
#include <linux/err.h>
@@ -30,7 +31,7 @@ MODULE_PARM_DESC(rng, "Select trusted key RNG");
static char *trusted_key_source;
module_param_named(source, trusted_key_source, charp, 0);
-MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee or caam)");
+MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee, caam or dcp)");
static const struct trusted_key_source trusted_key_sources[] = {
#if defined(CONFIG_TRUSTED_KEYS_TPM)
@@ -42,6 +43,9 @@ static const struct trusted_key_source trusted_key_sources[] = {
#if defined(CONFIG_TRUSTED_KEYS_CAAM)
{ "caam", &trusted_key_caam_ops },
#endif
+#if defined(CONFIG_TRUSTED_KEYS_DCP)
+ { "dcp", &dcp_trusted_key_ops },
+#endif
};
DEFINE_STATIC_CALL_NULL(trusted_key_seal, *trusted_key_sources[0].ops->seal);
@@ -391,4 +395,5 @@ static void __exit cleanup_trusted(void)
late_initcall(init_trusted);
module_exit(cleanup_trusted);
+MODULE_DESCRIPTION("Trusted Key type");
MODULE_LICENSE("GPL");
diff --git a/security/keys/trusted-keys/trusted_dcp.c b/security/keys/trusted-keys/trusted_dcp.c
new file mode 100644
index 000000000000..7b6eb655df0c
--- /dev/null
+++ b/security/keys/trusted-keys/trusted_dcp.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 sigma star gmbh
+ */
+
+#include <crypto/aead.h>
+#include <crypto/aes.h>
+#include <crypto/algapi.h>
+#include <crypto/gcm.h>
+#include <crypto/skcipher.h>
+#include <keys/trusted-type.h>
+#include <linux/key-type.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <soc/fsl/dcp.h>
+
+#define DCP_BLOB_VERSION 1
+#define DCP_BLOB_AUTHLEN 16
+
+/**
+ * DOC: dcp blob format
+ *
+ * The Data Co-Processor (DCP) provides hardware-bound AES keys using its
+ * AES encryption engine only. It does not provide direct key sealing/unsealing.
+ * To make DCP hardware encryption keys usable as trust source, we define
+ * our own custom format that uses a hardware-bound key to secure the sealing
+ * key stored in the key blob.
+ *
+ * Whenever a new trusted key using DCP is generated, we generate a random 128-bit
+ * blob encryption key (BEK) and 128-bit nonce. The BEK and nonce are used to
+ * encrypt the trusted key payload using AES-128-GCM.
+ *
+ * The BEK itself is encrypted using the hardware-bound key using the DCP's AES
+ * encryption engine with AES-128-ECB. The encrypted BEK, generated nonce,
+ * BEK-encrypted payload and authentication tag make up the blob format together
+ * with a version number, payload length and authentication tag.
+ */
+
+/**
+ * struct dcp_blob_fmt - DCP BLOB format.
+ *
+ * @fmt_version: Format version, currently being %1.
+ * @blob_key: Random AES 128 key which is used to encrypt @payload,
+ * @blob_key itself is encrypted with OTP or UNIQUE device key in
+ * AES-128-ECB mode by DCP.
+ * @nonce: Random nonce used for @payload encryption.
+ * @payload_len: Length of the plain text @payload.
+ * @payload: The payload itself, encrypted using AES-128-GCM and @blob_key,
+ * GCM auth tag of size DCP_BLOB_AUTHLEN is attached at the end of it.
+ *
+ * The total size of a DCP BLOB is sizeof(struct dcp_blob_fmt) + @payload_len +
+ * DCP_BLOB_AUTHLEN.
+ */
+struct dcp_blob_fmt {
+ __u8 fmt_version;
+ __u8 blob_key[AES_KEYSIZE_128];
+ __u8 nonce[AES_KEYSIZE_128];
+ __le32 payload_len;
+ __u8 payload[];
+} __packed;
+
+static bool use_otp_key;
+module_param_named(dcp_use_otp_key, use_otp_key, bool, 0);
+MODULE_PARM_DESC(dcp_use_otp_key, "Use OTP instead of UNIQUE key for sealing");
+
+static bool skip_zk_test;
+module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0);
+MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed");
+
+static unsigned int calc_blob_len(unsigned int payload_len)
+{
+ return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN;
+}
+
+static int do_dcp_crypto(u8 *in, u8 *out, bool do_encrypt)
+{
+ struct skcipher_request *req = NULL;
+ struct scatterlist src_sg, dst_sg;
+ struct crypto_skcipher *tfm;
+ u8 paes_key[DCP_PAES_KEYSIZE];
+ DECLARE_CRYPTO_WAIT(wait);
+ int res = 0;
+
+ if (use_otp_key)
+ paes_key[0] = DCP_PAES_KEY_OTP;
+ else
+ paes_key[0] = DCP_PAES_KEY_UNIQUE;
+
+ tfm = crypto_alloc_skcipher("ecb-paes-dcp", CRYPTO_ALG_INTERNAL,
+ CRYPTO_ALG_INTERNAL);
+ if (IS_ERR(tfm)) {
+ res = PTR_ERR(tfm);
+ tfm = NULL;
+ goto out;
+ }
+
+ req = skcipher_request_alloc(tfm, GFP_NOFS);
+ if (!req) {
+ res = -ENOMEM;
+ goto out;
+ }
+
+ skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP,
+ crypto_req_done, &wait);
+ res = crypto_skcipher_setkey(tfm, paes_key, sizeof(paes_key));
+ if (res < 0)
+ goto out;
+
+ sg_init_one(&src_sg, in, AES_KEYSIZE_128);
+ sg_init_one(&dst_sg, out, AES_KEYSIZE_128);
+ skcipher_request_set_crypt(req, &src_sg, &dst_sg, AES_KEYSIZE_128,
+ NULL);
+
+ if (do_encrypt)
+ res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
+ else
+ res = crypto_wait_req(crypto_skcipher_decrypt(req), &wait);
+
+out:
+ skcipher_request_free(req);
+ crypto_free_skcipher(tfm);
+
+ return res;
+}
+
+static int do_aead_crypto(u8 *in, u8 *out, size_t len, u8 *key, u8 *nonce,
+ bool do_encrypt)
+{
+ struct aead_request *aead_req = NULL;
+ struct scatterlist src_sg, dst_sg;
+ struct crypto_aead *aead;
+ int ret;
+ DECLARE_CRYPTO_WAIT(wait);
+
+ aead = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(aead)) {
+ ret = PTR_ERR(aead);
+ goto out;
+ }
+
+ ret = crypto_aead_setauthsize(aead, DCP_BLOB_AUTHLEN);
+ if (ret < 0) {
+ pr_err("Can't set crypto auth tag len: %d\n", ret);
+ goto free_aead;
+ }
+
+ aead_req = aead_request_alloc(aead, GFP_KERNEL);
+ if (!aead_req) {
+ ret = -ENOMEM;
+ goto free_aead;
+ }
+
+ sg_init_one(&src_sg, in, len);
+ if (do_encrypt) {
+ /*
+ * If we encrypt our buffer has extra space for the auth tag.
+ */
+ sg_init_one(&dst_sg, out, len + DCP_BLOB_AUTHLEN);
+ } else {
+ sg_init_one(&dst_sg, out, len);
+ }
+
+ aead_request_set_crypt(aead_req, &src_sg, &dst_sg, len, nonce);
+ aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP,
+ crypto_req_done, &wait);
+ aead_request_set_ad(aead_req, 0);
+
+ if (crypto_aead_setkey(aead, key, AES_KEYSIZE_128)) {
+ pr_err("Can't set crypto AEAD key\n");
+ ret = -EINVAL;
+ goto free_req;
+ }
+
+ if (do_encrypt)
+ ret = crypto_wait_req(crypto_aead_encrypt(aead_req), &wait);
+ else
+ ret = crypto_wait_req(crypto_aead_decrypt(aead_req), &wait);
+
+free_req:
+ aead_request_free(aead_req);
+free_aead:
+ crypto_free_aead(aead);
+out:
+ return ret;
+}
+
+static int decrypt_blob_key(u8 *encrypted_key, u8 *plain_key)
+{
+ return do_dcp_crypto(encrypted_key, plain_key, false);
+}
+
+static int encrypt_blob_key(u8 *plain_key, u8 *encrypted_key)
+{
+ return do_dcp_crypto(plain_key, encrypted_key, true);
+}
+
+static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob)
+{
+ struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
+ int blen, ret;
+ u8 *plain_blob_key;
+
+ blen = calc_blob_len(p->key_len);
+ if (blen > MAX_BLOB_SIZE)
+ return -E2BIG;
+
+ plain_blob_key = kmalloc(AES_KEYSIZE_128, GFP_KERNEL);
+ if (!plain_blob_key)
+ return -ENOMEM;
+
+ b->fmt_version = DCP_BLOB_VERSION;
+ get_random_bytes(b->nonce, AES_KEYSIZE_128);
+ get_random_bytes(plain_blob_key, AES_KEYSIZE_128);
+
+ ret = do_aead_crypto(p->key, b->payload, p->key_len, plain_blob_key,
+ b->nonce, true);
+ if (ret) {
+ pr_err("Unable to encrypt blob payload: %i\n", ret);
+ goto out;
+ }
+
+ ret = encrypt_blob_key(plain_blob_key, b->blob_key);
+ if (ret) {
+ pr_err("Unable to encrypt blob key: %i\n", ret);
+ goto out;
+ }
+
+ put_unaligned_le32(p->key_len, &b->payload_len);
+ p->blob_len = blen;
+ ret = 0;
+
+out:
+ memzero_explicit(plain_blob_key, AES_KEYSIZE_128);
+ kfree(plain_blob_key);
+
+ return ret;
+}
+
+static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob)
+{
+ struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
+ int blen, ret;
+ u8 *plain_blob_key = NULL;
+
+ if (b->fmt_version != DCP_BLOB_VERSION) {
+ pr_err("DCP blob has bad version: %i, expected %i\n",
+ b->fmt_version, DCP_BLOB_VERSION);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ p->key_len = le32_to_cpu(b->payload_len);
+ blen = calc_blob_len(p->key_len);
+ if (blen != p->blob_len) {
+ pr_err("DCP blob has bad length: %i != %i\n", blen,
+ p->blob_len);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ plain_blob_key = kmalloc(AES_KEYSIZE_128, GFP_KERNEL);
+ if (!plain_blob_key) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = decrypt_blob_key(b->blob_key, plain_blob_key);
+ if (ret) {
+ pr_err("Unable to decrypt blob key: %i\n", ret);
+ goto out;
+ }
+
+ ret = do_aead_crypto(b->payload, p->key, p->key_len + DCP_BLOB_AUTHLEN,
+ plain_blob_key, b->nonce, false);
+ if (ret) {
+ pr_err("Unwrap of DCP payload failed: %i\n", ret);
+ goto out;
+ }
+
+ ret = 0;
+out:
+ if (plain_blob_key) {
+ memzero_explicit(plain_blob_key, AES_KEYSIZE_128);
+ kfree(plain_blob_key);
+ }
+
+ return ret;
+}
+
+static int test_for_zero_key(void)
+{
+ /*
+ * Encrypting a plaintext of all 0x55 bytes will yield
+ * this ciphertext in case the DCP test key is used.
+ */
+ static const u8 bad[] = {0x9a, 0xda, 0xe0, 0x54, 0xf6, 0x3d, 0xfa, 0xff,
+ 0x5e, 0xa1, 0x8e, 0x45, 0xed, 0xf6, 0xea, 0x6f};
+ void *buf = NULL;
+ int ret = 0;
+
+ if (skip_zk_test)
+ goto out;
+
+ buf = kmalloc(AES_BLOCK_SIZE, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(buf, 0x55, AES_BLOCK_SIZE);
+
+ ret = do_dcp_crypto(buf, buf, true);
+ if (ret)
+ goto out;
+
+ if (memcmp(buf, bad, AES_BLOCK_SIZE) == 0) {
+ pr_warn("Device neither in secure nor trusted mode!\n");
+ ret = -EINVAL;
+ }
+out:
+ kfree(buf);
+ return ret;
+}
+
+static int trusted_dcp_init(void)
+{
+ int ret;
+
+ if (use_otp_key)
+ pr_info("Using DCP OTP key\n");
+
+ ret = test_for_zero_key();
+ if (ret) {
+ pr_warn("Test for zero'ed keys failed: %i\n", ret);
+
+ return -EINVAL;
+ }
+
+ return register_key_type(&key_type_trusted);
+}
+
+static void trusted_dcp_exit(void)
+{
+ unregister_key_type(&key_type_trusted);
+}
+
+struct trusted_key_ops dcp_trusted_key_ops = {
+ .exit = trusted_dcp_exit,
+ .init = trusted_dcp_init,
+ .seal = trusted_dcp_seal,
+ .unseal = trusted_dcp_unseal,
+ .migratable = 0,
+};
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index aa108bea6739..89c9798d1800 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -356,17 +356,28 @@ out:
*/
int trusted_tpm_send(unsigned char *cmd, size_t buflen)
{
+ struct tpm_buf buf;
int rc;
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
+ buf.flags = 0;
+ buf.length = buflen;
+ buf.data = cmd;
dump_tpm_buf(cmd);
- rc = tpm_send(chip, cmd, buflen);
+ rc = tpm_transmit_cmd(chip, &buf, 4, "sending data");
dump_tpm_buf(cmd);
+
if (rc > 0)
- /* Can't return positive return codes values to keyctl */
+ /* TPM error */
rc = -EPERM;
+
+ tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(trusted_tpm_send);
@@ -407,7 +418,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
tpm_buf_append_u32(tb, handle);
tpm_buf_append(tb, ononce, TPM_NONCE_SIZE);
- ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
+ ret = trusted_tpm_send(tb->data, tb->length);
if (ret < 0)
return ret;
@@ -431,7 +442,7 @@ int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
return -ENODEV;
tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OIAP);
- ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
+ ret = trusted_tpm_send(tb->data, tb->length);
if (ret < 0)
return ret;
@@ -543,7 +554,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
tpm_buf_append_u8(tb, cont);
tpm_buf_append(tb, td->pubauth, SHA1_DIGEST_SIZE);
- ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
+ ret = trusted_tpm_send(tb->data, tb->length);
if (ret < 0)
goto out;
@@ -634,7 +645,7 @@ static int tpm_unseal(struct tpm_buf *tb,
tpm_buf_append_u8(tb, cont);
tpm_buf_append(tb, authdata2, SHA1_DIGEST_SIZE);
- ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
+ ret = trusted_tpm_send(tb->data, tb->length);
if (ret < 0) {
pr_info("authhmac failed (%d)\n", ret);
return ret;
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index bc700f85f80b..024be262702f 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -14,7 +14,7 @@
#include <keys/trusted-type.h>
#include <keys/trusted_tpm.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "tpm2key.asn1.h"
@@ -38,6 +38,7 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
u8 *end_work = scratch + SCRATCH_SIZE;
u8 *priv, *pub;
u16 priv_len, pub_len;
+ int ret;
priv_len = get_unaligned_be16(src) + 2;
priv = src;
@@ -57,8 +58,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
unsigned char bool[3], *w = bool;
/* tag 0 is emptyAuth */
w = asn1_encode_boolean(w, w + sizeof(bool), true);
- if (WARN(IS_ERR(w), "BUG: Boolean failed to encode"))
- return PTR_ERR(w);
+ if (WARN(IS_ERR(w), "BUG: Boolean failed to encode")) {
+ ret = PTR_ERR(w);
+ goto err;
+ }
work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
}
@@ -69,8 +72,10 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
* trigger, so if it does there's something nefarious going on
*/
if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
- "BUG: scratch buffer is too small"))
- return -EINVAL;
+ "BUG: scratch buffer is too small")) {
+ ret = -EINVAL;
+ goto err;
+ }
work = asn1_encode_integer(work, end_work, options->keyhandle);
work = asn1_encode_octet_string(work, end_work, pub, pub_len);
@@ -79,10 +84,18 @@ static int tpm2_key_encode(struct trusted_key_payload *payload,
work1 = payload->blob;
work1 = asn1_encode_sequence(work1, work1 + sizeof(payload->blob),
scratch, work - scratch);
- if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed"))
- return PTR_ERR(work1);
+ if (IS_ERR(work1)) {
+ ret = PTR_ERR(work1);
+ pr_err("BUG: ASN.1 encoder failed with %d\n", ret);
+ goto err;
+ }
+ kfree(scratch);
return work1 - payload->blob;
+
+err:
+ kfree(scratch);
+ return ret;
}
struct tpm2_key_context {
@@ -228,8 +241,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
+ off_t offset = TPM_HEADER_SIZE;
+ struct tpm_buf buf, sized;
int blob_len = 0;
- struct tpm_buf buf;
u32 hash;
u32 flags;
int i;
@@ -252,50 +266,58 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
if (rc)
return rc;
+ rc = tpm2_start_auth_session(chip);
+ if (rc)
+ goto out_put;
+
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
if (rc) {
- tpm_put_ops(chip);
- return rc;
+ tpm2_end_auth_session(chip);
+ goto out_put;
}
- tpm_buf_append_u32(&buf, options->keyhandle);
- tpm2_buf_append_auth(&buf, TPM2_RS_PW,
- NULL /* nonce */, 0,
- 0 /* session_attributes */,
- options->keyauth /* hmac */,
- TPM_DIGEST_SIZE);
+ rc = tpm_buf_init_sized(&sized);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ tpm2_end_auth_session(chip);
+ goto out_put;
+ }
+
+ tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
+ options->keyauth, TPM_DIGEST_SIZE);
/* sensitive */
- tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len);
+ tpm_buf_append_u16(&sized, options->blobauth_len);
- tpm_buf_append_u16(&buf, options->blobauth_len);
if (options->blobauth_len)
- tpm_buf_append(&buf, options->blobauth, options->blobauth_len);
+ tpm_buf_append(&sized, options->blobauth, options->blobauth_len);
- tpm_buf_append_u16(&buf, payload->key_len);
- tpm_buf_append(&buf, payload->key, payload->key_len);
+ tpm_buf_append_u16(&sized, payload->key_len);
+ tpm_buf_append(&sized, payload->key, payload->key_len);
+ tpm_buf_append(&buf, sized.data, sized.length);
/* public */
- tpm_buf_append_u16(&buf, 14 + options->policydigest_len);
- tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH);
- tpm_buf_append_u16(&buf, hash);
+ tpm_buf_reset_sized(&sized);
+ tpm_buf_append_u16(&sized, TPM_ALG_KEYEDHASH);
+ tpm_buf_append_u16(&sized, hash);
/* key properties */
flags = 0;
flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
- flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM |
- TPM2_OA_FIXED_PARENT);
- tpm_buf_append_u32(&buf, flags);
+ flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT);
+ tpm_buf_append_u32(&sized, flags);
/* policy */
- tpm_buf_append_u16(&buf, options->policydigest_len);
+ tpm_buf_append_u16(&sized, options->policydigest_len);
if (options->policydigest_len)
- tpm_buf_append(&buf, options->policydigest,
- options->policydigest_len);
+ tpm_buf_append(&sized, options->policydigest, options->policydigest_len);
/* public parameters */
- tpm_buf_append_u16(&buf, TPM_ALG_NULL);
- tpm_buf_append_u16(&buf, 0);
+ tpm_buf_append_u16(&sized, TPM_ALG_NULL);
+ tpm_buf_append_u16(&sized, 0);
+
+ tpm_buf_append(&buf, sized.data, sized.length);
/* outside info */
tpm_buf_append_u16(&buf, 0);
@@ -305,28 +327,30 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
if (buf.flags & TPM_BUF_OVERFLOW) {
rc = -E2BIG;
+ tpm2_end_auth_session(chip);
goto out;
}
+ tpm_buf_fill_hmac_session(chip, &buf);
rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
+ rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (rc)
goto out;
- blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]);
- if (blob_len > MAX_BLOB_SIZE) {
+ blob_len = tpm_buf_read_u32(&buf, &offset);
+ if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_BOUNDARY_ERROR) {
rc = -E2BIG;
goto out;
}
- if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) {
+ if (buf.length - offset < blob_len) {
rc = -EFAULT;
goto out;
}
- blob_len = tpm2_key_encode(payload, options,
- &buf.data[TPM_HEADER_SIZE + 4],
- blob_len);
+ blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
out:
+ tpm_buf_destroy(&sized);
tpm_buf_destroy(&buf);
if (rc > 0) {
@@ -340,6 +364,7 @@ out:
else
payload->blob_len = blob_len;
+out_put:
tpm_put_ops(chip);
return rc;
}
@@ -409,25 +434,31 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
if (blob_len > payload->blob_len)
return -E2BIG;
- rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+ rc = tpm2_start_auth_session(chip);
if (rc)
return rc;
- tpm_buf_append_u32(&buf, options->keyhandle);
- tpm2_buf_append_auth(&buf, TPM2_RS_PW,
- NULL /* nonce */, 0,
- 0 /* session_attributes */,
- options->keyauth /* hmac */,
- TPM_DIGEST_SIZE);
+ rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+ if (rc) {
+ tpm2_end_auth_session(chip);
+ return rc;
+ }
+
+ tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
+ TPM_DIGEST_SIZE);
tpm_buf_append(&buf, blob, blob_len);
if (buf.flags & TPM_BUF_OVERFLOW) {
rc = -E2BIG;
+ tpm2_end_auth_session(chip);
goto out;
}
+ tpm_buf_fill_hmac_session(chip, &buf);
rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
+ rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (!rc)
*blob_handle = be32_to_cpup(
(__be32 *) &buf.data[TPM_HEADER_SIZE]);
@@ -465,20 +496,44 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
u8 *data;
int rc;
- rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
+ rc = tpm2_start_auth_session(chip);
if (rc)
return rc;
- tpm_buf_append_u32(&buf, blob_handle);
- tpm2_buf_append_auth(&buf,
- options->policyhandle ?
- options->policyhandle : TPM2_RS_PW,
- NULL /* nonce */, 0,
- TPM2_SA_CONTINUE_SESSION,
- options->blobauth /* hmac */,
- options->blobauth_len);
+ rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
+ if (rc) {
+ tpm2_end_auth_session(chip);
+ return rc;
+ }
+
+ tpm_buf_append_name(chip, &buf, blob_handle, NULL);
+
+ if (!options->policyhandle) {
+ tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
+ options->blobauth,
+ options->blobauth_len);
+ } else {
+ /*
+ * FIXME: The policy session was generated outside the
+ * kernel so we don't known the nonce and thus can't
+ * calculate a HMAC on it. Therefore, the user can
+ * only really use TPM2_PolicyPassword and we must
+ * send down the plain text password, which could be
+ * intercepted. We can still encrypt the returned
+ * key, but that's small comfort since the interposer
+ * could repeat our actions with the exfiltrated
+ * password.
+ */
+ tpm2_buf_append_auth(&buf, options->policyhandle,
+ NULL /* nonce */, 0, 0,
+ options->blobauth, options->blobauth_len);
+ tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT,
+ NULL, 0);
+ }
+ tpm_buf_fill_hmac_session(chip, &buf);
rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
+ rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (rc > 0)
rc = -EPERM;
diff --git a/security/landlock/.kunitconfig b/security/landlock/.kunitconfig
new file mode 100644
index 000000000000..03e119466604
--- /dev/null
+++ b/security/landlock/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_LANDLOCK=y
+CONFIG_SECURITY_LANDLOCK_KUNIT_TEST=y
diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig
index c4bf0d5eff39..3f1493402052 100644
--- a/security/landlock/Kconfig
+++ b/security/landlock/Kconfig
@@ -20,3 +20,18 @@ config SECURITY_LANDLOCK
If you are unsure how to answer this question, answer N. Otherwise,
you should also prepend "landlock," to the content of CONFIG_LSM to
enable Landlock at boot time.
+
+config SECURITY_LANDLOCK_KUNIT_TEST
+ bool "KUnit tests for Landlock" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y
+ depends on SECURITY_LANDLOCK
+ default KUNIT_ALL_TESTS
+ help
+ Build KUnit tests for Landlock.
+
+ See the KUnit documentation in Documentation/dev-tools/kunit
+
+ Run all KUnit tests for Landlock with:
+ ./tools/testing/kunit/kunit.py run --kunitconfig security/landlock
+
+ If you are unsure how to answer this question, answer N.
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index c2e116f2a299..b4538b7cf7d2 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -1,6 +1,6 @@
obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
landlock-y := setup.o syscalls.o object.o ruleset.o \
- cred.o ptrace.o fs.o
+ cred.o task.o fs.o
landlock-$(CONFIG_INET) += net.o
diff --git a/security/landlock/access.h b/security/landlock/access.h
new file mode 100644
index 000000000000..74fd8f399fbd
--- /dev/null
+++ b/security/landlock/access.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Landlock LSM - Access types and helpers
+ *
+ * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net>
+ * Copyright © 2018-2020 ANSSI
+ * Copyright © 2024-2025 Microsoft Corporation
+ */
+
+#ifndef _SECURITY_LANDLOCK_ACCESS_H
+#define _SECURITY_LANDLOCK_ACCESS_H
+
+#include <linux/bitops.h>
+#include <linux/build_bug.h>
+#include <linux/kernel.h>
+#include <uapi/linux/landlock.h>
+
+#include "limits.h"
+
+/*
+ * All access rights that are denied by default whether they are handled or not
+ * by a ruleset/layer. This must be ORed with all ruleset->access_masks[]
+ * entries when we need to get the absolute handled access masks, see
+ * landlock_upgrade_handled_access_masks().
+ */
+/* clang-format off */
+#define _LANDLOCK_ACCESS_FS_INITIALLY_DENIED ( \
+ LANDLOCK_ACCESS_FS_REFER)
+/* clang-format on */
+
+typedef u16 access_mask_t;
+
+/* Makes sure all filesystem access rights can be stored. */
+static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
+/* Makes sure all network access rights can be stored. */
+static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET);
+/* Makes sure all scoped rights can be stored. */
+static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_SCOPE);
+/* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */
+static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
+
+/* Ruleset access masks. */
+struct access_masks {
+ access_mask_t fs : LANDLOCK_NUM_ACCESS_FS;
+ access_mask_t net : LANDLOCK_NUM_ACCESS_NET;
+ access_mask_t scope : LANDLOCK_NUM_SCOPE;
+};
+
+union access_masks_all {
+ struct access_masks masks;
+ u32 all;
+};
+
+/* Makes sure all fields are covered. */
+static_assert(sizeof(typeof_member(union access_masks_all, masks)) ==
+ sizeof(typeof_member(union access_masks_all, all)));
+
+typedef u16 layer_mask_t;
+
+/* Makes sure all layers can be checked. */
+static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS);
+
+/* Upgrades with all initially denied by default access rights. */
+static inline struct access_masks
+landlock_upgrade_handled_access_masks(struct access_masks access_masks)
+{
+ /*
+ * All access rights that are denied by default whether they are
+ * explicitly handled or not.
+ */
+ if (access_masks.fs)
+ access_masks.fs |= _LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
+
+ return access_masks;
+}
+
+#endif /* _SECURITY_LANDLOCK_ACCESS_H */
diff --git a/security/landlock/common.h b/security/landlock/common.h
index 5dc0fe15707d..0eb1d34c2eae 100644
--- a/security/landlock/common.h
+++ b/security/landlock/common.h
@@ -17,4 +17,6 @@
#define pr_fmt(fmt) LANDLOCK_NAME ": " fmt
+#define BIT_INDEX(bit) HWEIGHT(bit - 1)
+
#endif /* _SECURITY_LANDLOCK_COMMON_H */
diff --git a/security/landlock/cred.c b/security/landlock/cred.c
index 786af18c4a1c..db9fe7d906ba 100644
--- a/security/landlock/cred.c
+++ b/security/landlock/cred.c
@@ -14,8 +14,8 @@
#include "ruleset.h"
#include "setup.h"
-static int hook_cred_prepare(struct cred *const new,
- const struct cred *const old, const gfp_t gfp)
+static void hook_cred_transfer(struct cred *const new,
+ const struct cred *const old)
{
struct landlock_ruleset *const old_dom = landlock_cred(old)->domain;
@@ -23,6 +23,12 @@ static int hook_cred_prepare(struct cred *const new,
landlock_get_ruleset(old_dom);
landlock_cred(new)->domain = old_dom;
}
+}
+
+static int hook_cred_prepare(struct cred *const new,
+ const struct cred *const old, const gfp_t gfp)
+{
+ hook_cred_transfer(new, old);
return 0;
}
@@ -36,6 +42,7 @@ static void hook_cred_free(struct cred *const cred)
static struct security_hook_list landlock_hooks[] __ro_after_init = {
LSM_HOOK_INIT(cred_prepare, hook_cred_prepare),
+ LSM_HOOK_INIT(cred_transfer, hook_cred_transfer),
LSM_HOOK_INIT(cred_free, hook_cred_free),
};
diff --git a/security/landlock/cred.h b/security/landlock/cred.h
index af89ab00e6d1..bf755459838a 100644
--- a/security/landlock/cred.h
+++ b/security/landlock/cred.h
@@ -26,7 +26,7 @@ landlock_cred(const struct cred *cred)
return cred->security + landlock_blob_sizes.lbs_cred;
}
-static inline const struct landlock_ruleset *landlock_get_current_domain(void)
+static inline struct landlock_ruleset *landlock_get_current_domain(void)
{
return landlock_cred(current_cred())->domain;
}
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 0171f7eb6ee1..71b9dc331aae 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -5,14 +5,19 @@
* Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net>
* Copyright © 2018-2020 ANSSI
* Copyright © 2021-2022 Microsoft Corporation
+ * Copyright © 2022 Günther Noack <gnoack3000@gmail.com>
+ * Copyright © 2023-2024 Google LLC
*/
+#include <asm/ioctls.h>
+#include <kunit/test.h>
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/compiler_types.h>
#include <linux/dcache.h>
#include <linux/err.h>
+#include <linux/falloc.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -28,8 +33,10 @@
#include <linux/types.h>
#include <linux/wait_bit.h>
#include <linux/workqueue.h>
+#include <uapi/linux/fiemap.h>
#include <uapi/linux/landlock.h>
+#include "access.h"
#include "common.h"
#include "cred.h"
#include "fs.h"
@@ -83,6 +90,160 @@ static const struct landlock_object_underops landlock_fs_underops = {
.release = release_inode
};
+/* IOCTL helpers */
+
+/**
+ * is_masked_device_ioctl - Determine whether an IOCTL command is always
+ * permitted with Landlock for device files. These commands can not be
+ * restricted on device files by enforcing a Landlock policy.
+ *
+ * @cmd: The IOCTL command that is supposed to be run.
+ *
+ * By default, any IOCTL on a device file requires the
+ * LANDLOCK_ACCESS_FS_IOCTL_DEV right. However, we blanket-permit some
+ * commands, if:
+ *
+ * 1. The command is implemented in fs/ioctl.c's do_vfs_ioctl(),
+ * not in f_ops->unlocked_ioctl() or f_ops->compat_ioctl().
+ *
+ * 2. The command is harmless when invoked on devices.
+ *
+ * We also permit commands that do not make sense for devices, but where the
+ * do_vfs_ioctl() implementation returns a more conventional error code.
+ *
+ * Any new IOCTL commands that are implemented in fs/ioctl.c's do_vfs_ioctl()
+ * should be considered for inclusion here.
+ *
+ * Returns: true if the IOCTL @cmd can not be restricted with Landlock for
+ * device files.
+ */
+static __attribute_const__ bool is_masked_device_ioctl(const unsigned int cmd)
+{
+ switch (cmd) {
+ /*
+ * FIOCLEX, FIONCLEX, FIONBIO and FIOASYNC manipulate the FD's
+ * close-on-exec and the file's buffered-IO and async flags. These
+ * operations are also available through fcntl(2), and are
+ * unconditionally permitted in Landlock.
+ */
+ case FIOCLEX:
+ case FIONCLEX:
+ case FIONBIO:
+ case FIOASYNC:
+ /*
+ * FIOQSIZE queries the size of a regular file, directory, or link.
+ *
+ * We still permit it, because it always returns -ENOTTY for
+ * other file types.
+ */
+ case FIOQSIZE:
+ /*
+ * FIFREEZE and FITHAW freeze and thaw the file system which the
+ * given file belongs to. Requires CAP_SYS_ADMIN.
+ *
+ * These commands operate on the file system's superblock rather
+ * than on the file itself. The same operations can also be
+ * done through any other file or directory on the same file
+ * system, so it is safe to permit these.
+ */
+ case FIFREEZE:
+ case FITHAW:
+ /*
+ * FS_IOC_FIEMAP queries information about the allocation of
+ * blocks within a file.
+ *
+ * This IOCTL command only makes sense for regular files and is
+ * not implemented by devices. It is harmless to permit.
+ */
+ case FS_IOC_FIEMAP:
+ /*
+ * FIGETBSZ queries the file system's block size for a file or
+ * directory.
+ *
+ * This command operates on the file system's superblock rather
+ * than on the file itself. The same operation can also be done
+ * through any other file or directory on the same file system,
+ * so it is safe to permit it.
+ */
+ case FIGETBSZ:
+ /*
+ * FICLONE, FICLONERANGE and FIDEDUPERANGE make files share
+ * their underlying storage ("reflink") between source and
+ * destination FDs, on file systems which support that.
+ *
+ * These IOCTL commands only apply to regular files
+ * and are harmless to permit for device files.
+ */
+ case FICLONE:
+ case FICLONERANGE:
+ case FIDEDUPERANGE:
+ /*
+ * FS_IOC_GETFSUUID and FS_IOC_GETFSSYSFSPATH both operate on
+ * the file system superblock, not on the specific file, so
+ * these operations are available through any other file on the
+ * same file system as well.
+ */
+ case FS_IOC_GETFSUUID:
+ case FS_IOC_GETFSSYSFSPATH:
+ return true;
+
+ /*
+ * FIONREAD, FS_IOC_GETFLAGS, FS_IOC_SETFLAGS, FS_IOC_FSGETXATTR and
+ * FS_IOC_FSSETXATTR are forwarded to device implementations.
+ */
+
+ /*
+ * file_ioctl() commands (FIBMAP, FS_IOC_RESVSP, FS_IOC_RESVSP64,
+ * FS_IOC_UNRESVSP, FS_IOC_UNRESVSP64 and FS_IOC_ZERO_RANGE) are
+ * forwarded to device implementations, so not permitted.
+ */
+
+ /* Other commands are guarded by the access right. */
+ default:
+ return false;
+ }
+}
+
+/*
+ * is_masked_device_ioctl_compat - same as the helper above, but checking the
+ * "compat" IOCTL commands.
+ *
+ * The IOCTL commands with special handling in compat-mode should behave the
+ * same as their non-compat counterparts.
+ */
+static __attribute_const__ bool
+is_masked_device_ioctl_compat(const unsigned int cmd)
+{
+ switch (cmd) {
+ /* FICLONE is permitted, same as in the non-compat variant. */
+ case FICLONE:
+ return true;
+
+#if defined(CONFIG_X86_64)
+ /*
+ * FS_IOC_RESVSP_32, FS_IOC_RESVSP64_32, FS_IOC_UNRESVSP_32,
+ * FS_IOC_UNRESVSP64_32, FS_IOC_ZERO_RANGE_32: not blanket-permitted,
+ * for consistency with their non-compat variants.
+ */
+ case FS_IOC_RESVSP_32:
+ case FS_IOC_RESVSP64_32:
+ case FS_IOC_UNRESVSP_32:
+ case FS_IOC_UNRESVSP64_32:
+ case FS_IOC_ZERO_RANGE_32:
+#endif
+
+ /*
+ * FS_IOC32_GETFLAGS, FS_IOC32_SETFLAGS are forwarded to their device
+ * implementations.
+ */
+ case FS_IOC32_GETFLAGS:
+ case FS_IOC32_SETFLAGS:
+ return false;
+ default:
+ return is_masked_device_ioctl(cmd);
+ }
+}
+
/* Ruleset management */
static struct landlock_object *get_inode_object(struct inode *const inode)
@@ -147,7 +308,8 @@ retry:
LANDLOCK_ACCESS_FS_EXECUTE | \
LANDLOCK_ACCESS_FS_WRITE_FILE | \
LANDLOCK_ACCESS_FS_READ_FILE | \
- LANDLOCK_ACCESS_FS_TRUNCATE)
+ LANDLOCK_ACCESS_FS_TRUNCATE | \
+ LANDLOCK_ACCESS_FS_IOCTL_DEV)
/* clang-format on */
/*
@@ -227,35 +389,14 @@ static bool is_nouser_or_private(const struct dentry *dentry)
unlikely(IS_PRIVATE(d_backing_inode(dentry))));
}
-static access_mask_t
-get_raw_handled_fs_accesses(const struct landlock_ruleset *const domain)
-{
- access_mask_t access_dom = 0;
- size_t layer_level;
-
- for (layer_level = 0; layer_level < domain->num_layers; layer_level++)
- access_dom |=
- landlock_get_raw_fs_access_mask(domain, layer_level);
- return access_dom;
-}
-
-static access_mask_t
-get_handled_fs_accesses(const struct landlock_ruleset *const domain)
-{
- /* Handles all initially denied by default access rights. */
- return get_raw_handled_fs_accesses(domain) |
- LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
-}
+static const struct access_masks any_fs = {
+ .fs = ~0,
+};
static const struct landlock_ruleset *get_current_fs_domain(void)
{
- const struct landlock_ruleset *const dom =
- landlock_get_current_domain();
-
- if (!dom || !get_raw_handled_fs_accesses(dom))
- return NULL;
-
- return dom;
+ return landlock_get_applicable_domain(landlock_get_current_domain(),
+ any_fs);
}
/*
@@ -311,6 +452,125 @@ static bool no_more_access(
return true;
}
+#define NMA_TRUE(...) KUNIT_EXPECT_TRUE(test, no_more_access(__VA_ARGS__))
+#define NMA_FALSE(...) KUNIT_EXPECT_FALSE(test, no_more_access(__VA_ARGS__))
+
+#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
+
+static void test_no_more_access(struct kunit *const test)
+{
+ const layer_mask_t rx0[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_READ_FILE)] = BIT_ULL(0),
+ };
+ const layer_mask_t mx0[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_MAKE_REG)] = BIT_ULL(0),
+ };
+ const layer_mask_t x0[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
+ };
+ const layer_mask_t x1[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(1),
+ };
+ const layer_mask_t x01[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0) |
+ BIT_ULL(1),
+ };
+ const layer_mask_t allows_all[LANDLOCK_NUM_ACCESS_FS] = {};
+
+ /* Checks without restriction. */
+ NMA_TRUE(&x0, &allows_all, false, &allows_all, NULL, false);
+ NMA_TRUE(&allows_all, &x0, false, &allows_all, NULL, false);
+ NMA_FALSE(&x0, &x0, false, &allows_all, NULL, false);
+
+ /*
+ * Checks that we can only refer a file if no more access could be
+ * inherited.
+ */
+ NMA_TRUE(&x0, &x0, false, &rx0, NULL, false);
+ NMA_TRUE(&rx0, &rx0, false, &rx0, NULL, false);
+ NMA_FALSE(&rx0, &rx0, false, &x0, NULL, false);
+ NMA_FALSE(&rx0, &rx0, false, &x1, NULL, false);
+
+ /* Checks allowed referring with different nested domains. */
+ NMA_TRUE(&x0, &x1, false, &x0, NULL, false);
+ NMA_TRUE(&x1, &x0, false, &x0, NULL, false);
+ NMA_TRUE(&x0, &x01, false, &x0, NULL, false);
+ NMA_TRUE(&x0, &x01, false, &rx0, NULL, false);
+ NMA_TRUE(&x01, &x0, false, &x0, NULL, false);
+ NMA_TRUE(&x01, &x0, false, &rx0, NULL, false);
+ NMA_FALSE(&x01, &x01, false, &x0, NULL, false);
+
+ /* Checks that file access rights are also enforced for a directory. */
+ NMA_FALSE(&rx0, &rx0, true, &x0, NULL, false);
+
+ /* Checks that directory access rights don't impact file referring... */
+ NMA_TRUE(&mx0, &mx0, false, &x0, NULL, false);
+ /* ...but only directory referring. */
+ NMA_FALSE(&mx0, &mx0, true, &x0, NULL, false);
+
+ /* Checks directory exchange. */
+ NMA_TRUE(&mx0, &mx0, true, &mx0, &mx0, true);
+ NMA_TRUE(&mx0, &mx0, true, &mx0, &x0, true);
+ NMA_FALSE(&mx0, &mx0, true, &x0, &mx0, true);
+ NMA_FALSE(&mx0, &mx0, true, &x0, &x0, true);
+ NMA_FALSE(&mx0, &mx0, true, &x1, &x1, true);
+
+ /* Checks file exchange with directory access rights... */
+ NMA_TRUE(&mx0, &mx0, false, &mx0, &mx0, false);
+ NMA_TRUE(&mx0, &mx0, false, &mx0, &x0, false);
+ NMA_TRUE(&mx0, &mx0, false, &x0, &mx0, false);
+ NMA_TRUE(&mx0, &mx0, false, &x0, &x0, false);
+ /* ...and with file access rights. */
+ NMA_TRUE(&rx0, &rx0, false, &rx0, &rx0, false);
+ NMA_TRUE(&rx0, &rx0, false, &rx0, &x0, false);
+ NMA_FALSE(&rx0, &rx0, false, &x0, &rx0, false);
+ NMA_FALSE(&rx0, &rx0, false, &x0, &x0, false);
+ NMA_FALSE(&rx0, &rx0, false, &x1, &x1, false);
+
+ /*
+ * Allowing the following requests should not be a security risk
+ * because domain 0 denies execute access, and domain 1 is always
+ * nested with domain 0. However, adding an exception for this case
+ * would mean to check all nested domains to make sure none can get
+ * more privileges (e.g. processes only sandboxed by domain 0).
+ * Moreover, this behavior (i.e. composition of N domains) could then
+ * be inconsistent compared to domain 1's ruleset alone (e.g. it might
+ * be denied to link/rename with domain 1's ruleset, whereas it would
+ * be allowed if nested on top of domain 0). Another drawback would be
+ * to create a cover channel that could enable sandboxed processes to
+ * infer most of the filesystem restrictions from their domain. To
+ * make it simple, efficient, safe, and more consistent, this case is
+ * always denied.
+ */
+ NMA_FALSE(&x1, &x1, false, &x0, NULL, false);
+ NMA_FALSE(&x1, &x1, false, &rx0, NULL, false);
+ NMA_FALSE(&x1, &x1, true, &x0, NULL, false);
+ NMA_FALSE(&x1, &x1, true, &rx0, NULL, false);
+
+ /* Checks the same case of exclusive domains with a file... */
+ NMA_TRUE(&x1, &x1, false, &x01, NULL, false);
+ NMA_FALSE(&x1, &x1, false, &x01, &x0, false);
+ NMA_FALSE(&x1, &x1, false, &x01, &x01, false);
+ NMA_FALSE(&x1, &x1, false, &x0, &x0, false);
+ /* ...and with a directory. */
+ NMA_FALSE(&x1, &x1, false, &x0, &x0, true);
+ NMA_FALSE(&x1, &x1, true, &x0, &x0, false);
+ NMA_FALSE(&x1, &x1, true, &x0, &x0, true);
+}
+
+#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
+
+#undef NMA_TRUE
+#undef NMA_FALSE
+
+static bool is_layer_masks_allowed(
+ layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
+{
+ return !memchr_inv(layer_masks, 0, sizeof(*layer_masks));
+}
+
/*
* Removes @layer_masks accesses that are not requested.
*
@@ -328,9 +588,61 @@ scope_to_request(const access_mask_t access_request,
for_each_clear_bit(access_bit, &access_req, ARRAY_SIZE(*layer_masks))
(*layer_masks)[access_bit] = 0;
- return !memchr_inv(layer_masks, 0, sizeof(*layer_masks));
+
+ return is_layer_masks_allowed(layer_masks);
}
+#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
+
+static void test_scope_to_request_with_exec_none(struct kunit *const test)
+{
+ /* Allows everything. */
+ layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
+
+ /* Checks and scopes with execute. */
+ KUNIT_EXPECT_TRUE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE,
+ &layer_masks));
+ KUNIT_EXPECT_EQ(test, 0,
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]);
+ KUNIT_EXPECT_EQ(test, 0,
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]);
+}
+
+static void test_scope_to_request_with_exec_some(struct kunit *const test)
+{
+ /* Denies execute and write. */
+ layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(1),
+ };
+
+ /* Checks and scopes with execute. */
+ KUNIT_EXPECT_FALSE(test, scope_to_request(LANDLOCK_ACCESS_FS_EXECUTE,
+ &layer_masks));
+ KUNIT_EXPECT_EQ(test, BIT_ULL(0),
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]);
+ KUNIT_EXPECT_EQ(test, 0,
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]);
+}
+
+static void test_scope_to_request_without_access(struct kunit *const test)
+{
+ /* Denies execute and write. */
+ layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)] = BIT_ULL(0),
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(1),
+ };
+
+ /* Checks and scopes without access request. */
+ KUNIT_EXPECT_TRUE(test, scope_to_request(0, &layer_masks));
+ KUNIT_EXPECT_EQ(test, 0,
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_EXECUTE)]);
+ KUNIT_EXPECT_EQ(test, 0,
+ layer_masks[BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)]);
+}
+
+#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
+
/*
* Returns true if there is at least one access right different than
* LANDLOCK_ACCESS_FS_REFER.
@@ -354,6 +666,51 @@ is_eacces(const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS],
return false;
}
+#define IE_TRUE(...) KUNIT_EXPECT_TRUE(test, is_eacces(__VA_ARGS__))
+#define IE_FALSE(...) KUNIT_EXPECT_FALSE(test, is_eacces(__VA_ARGS__))
+
+#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
+
+static void test_is_eacces_with_none(struct kunit *const test)
+{
+ const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
+
+ IE_FALSE(&layer_masks, 0);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
+}
+
+static void test_is_eacces_with_refer(struct kunit *const test)
+{
+ const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_REFER)] = BIT_ULL(0),
+ };
+
+ IE_FALSE(&layer_masks, 0);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
+}
+
+static void test_is_eacces_with_write(struct kunit *const test)
+{
+ const layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {
+ [BIT_INDEX(LANDLOCK_ACCESS_FS_WRITE_FILE)] = BIT_ULL(0),
+ };
+
+ IE_FALSE(&layer_masks, 0);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_REFER);
+ IE_FALSE(&layer_masks, LANDLOCK_ACCESS_FS_EXECUTE);
+
+ IE_TRUE(&layer_masks, LANDLOCK_ACCESS_FS_WRITE_FILE);
+}
+
+#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
+
+#undef IE_TRUE
+#undef IE_FALSE
+
/**
* is_access_to_paths_allowed - Check accesses for requests with a common path
*
@@ -421,16 +778,21 @@ static bool is_access_to_paths_allowed(
if (WARN_ON_ONCE(domain->num_layers < 1 || !layer_masks_parent1))
return false;
+ allowed_parent1 = is_layer_masks_allowed(layer_masks_parent1);
+
if (unlikely(layer_masks_parent2)) {
if (WARN_ON_ONCE(!dentry_child1))
return false;
+
+ allowed_parent2 = is_layer_masks_allowed(layer_masks_parent2);
+
/*
* For a double request, first check for potential privilege
* escalation by looking at domain handled accesses (which are
* a superset of the meaningful requested accesses).
*/
access_masked_parent1 = access_masked_parent2 =
- get_handled_fs_accesses(domain);
+ landlock_union_access_masks(domain).fs;
is_dom_check = true;
} else {
if (WARN_ON_ONCE(dentry_child1 || dentry_child2))
@@ -490,15 +852,6 @@ static bool is_access_to_paths_allowed(
child1_is_directory, layer_masks_parent2,
layer_masks_child2,
child2_is_directory))) {
- allowed_parent1 = scope_to_request(
- access_request_parent1, layer_masks_parent1);
- allowed_parent2 = scope_to_request(
- access_request_parent2, layer_masks_parent2);
-
- /* Stops when all accesses are granted. */
- if (allowed_parent1 && allowed_parent2)
- break;
-
/*
* Now, downgrades the remaining checks from domain
* handled accesses to requested accesses.
@@ -506,15 +859,32 @@ static bool is_access_to_paths_allowed(
is_dom_check = false;
access_masked_parent1 = access_request_parent1;
access_masked_parent2 = access_request_parent2;
+
+ allowed_parent1 =
+ allowed_parent1 ||
+ scope_to_request(access_masked_parent1,
+ layer_masks_parent1);
+ allowed_parent2 =
+ allowed_parent2 ||
+ scope_to_request(access_masked_parent2,
+ layer_masks_parent2);
+
+ /* Stops when all accesses are granted. */
+ if (allowed_parent1 && allowed_parent2)
+ break;
}
rule = find_rule(domain, walker_path.dentry);
- allowed_parent1 = landlock_unmask_layers(
- rule, access_masked_parent1, layer_masks_parent1,
- ARRAY_SIZE(*layer_masks_parent1));
- allowed_parent2 = landlock_unmask_layers(
- rule, access_masked_parent2, layer_masks_parent2,
- ARRAY_SIZE(*layer_masks_parent2));
+ allowed_parent1 = allowed_parent1 ||
+ landlock_unmask_layers(
+ rule, access_masked_parent1,
+ layer_masks_parent1,
+ ARRAY_SIZE(*layer_masks_parent1));
+ allowed_parent2 = allowed_parent2 ||
+ landlock_unmask_layers(
+ rule, access_masked_parent2,
+ layer_masks_parent2,
+ ARRAY_SIZE(*layer_masks_parent2));
/* Stops when a rule from each layer grants access. */
if (allowed_parent1 && allowed_parent2)
@@ -538,8 +908,10 @@ jump_up:
* access to internal filesystems (e.g. nsfs, which is
* reachable through /proc/<pid>/ns/<namespace>).
*/
- allowed_parent1 = allowed_parent2 =
- !!(walker_path.mnt->mnt_flags & MNT_INTERNAL);
+ if (walker_path.mnt->mnt_flags & MNT_INTERNAL) {
+ allowed_parent1 = true;
+ allowed_parent2 = true;
+ }
break;
}
parent_dentry = dget_parent(walker_path.dentry);
@@ -551,39 +923,29 @@ jump_up:
return allowed_parent1 && allowed_parent2;
}
-static int check_access_path(const struct landlock_ruleset *const domain,
- const struct path *const path,
- access_mask_t access_request)
-{
- layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
-
- access_request = landlock_init_layer_masks(
- domain, access_request, &layer_masks, LANDLOCK_KEY_INODE);
- if (is_access_to_paths_allowed(domain, path, access_request,
- &layer_masks, NULL, 0, NULL, NULL))
- return 0;
- return -EACCES;
-}
-
static int current_check_access_path(const struct path *const path,
- const access_mask_t access_request)
+ access_mask_t access_request)
{
const struct landlock_ruleset *const dom = get_current_fs_domain();
+ layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
if (!dom)
return 0;
- return check_access_path(dom, path, access_request);
+
+ access_request = landlock_init_layer_masks(
+ dom, access_request, &layer_masks, LANDLOCK_KEY_INODE);
+ if (is_access_to_paths_allowed(dom, path, access_request, &layer_masks,
+ NULL, 0, NULL, NULL))
+ return 0;
+
+ return -EACCES;
}
-static access_mask_t get_mode_access(const umode_t mode)
+static __attribute_const__ access_mask_t get_mode_access(const umode_t mode)
{
switch (mode & S_IFMT) {
case S_IFLNK:
return LANDLOCK_ACCESS_FS_MAKE_SYM;
- case 0:
- /* A zero mode translates to S_IFREG. */
- case S_IFREG:
- return LANDLOCK_ACCESS_FS_MAKE_REG;
case S_IFDIR:
return LANDLOCK_ACCESS_FS_MAKE_DIR;
case S_IFCHR:
@@ -594,9 +956,12 @@ static access_mask_t get_mode_access(const umode_t mode)
return LANDLOCK_ACCESS_FS_MAKE_FIFO;
case S_IFSOCK:
return LANDLOCK_ACCESS_FS_MAKE_SOCK;
+ case S_IFREG:
+ case 0:
+ /* A zero mode translates to S_IFREG. */
default:
- WARN_ON_ONCE(1);
- return 0;
+ /* Treats weird files as regular files. */
+ return LANDLOCK_ACCESS_FS_MAKE_REG;
}
}
@@ -737,6 +1102,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
bool allow_parent1, allow_parent2;
access_mask_t access_request_parent1, access_request_parent2;
struct path mnt_dir;
+ struct dentry *old_parent;
layer_mask_t layer_masks_parent1[LANDLOCK_NUM_ACCESS_FS] = {},
layer_masks_parent2[LANDLOCK_NUM_ACCESS_FS] = {};
@@ -784,9 +1150,17 @@ static int current_check_refer_path(struct dentry *const old_dentry,
mnt_dir.mnt = new_dir->mnt;
mnt_dir.dentry = new_dir->mnt->mnt_root;
+ /*
+ * old_dentry may be the root of the common mount point and
+ * !IS_ROOT(old_dentry) at the same time (e.g. with open_tree() and
+ * OPEN_TREE_CLONE). We do not need to call dget(old_parent) because
+ * we keep a reference to old_dentry.
+ */
+ old_parent = (old_dentry == mnt_dir.dentry) ? old_dentry :
+ old_dentry->d_parent;
+
/* new_dir->dentry is equal to new_dentry->d_parent */
- allow_parent1 = collect_domain_accesses(dom, mnt_dir.dentry,
- old_dentry->d_parent,
+ allow_parent1 = collect_domain_accesses(dom, mnt_dir.dentry, old_parent,
&layer_masks_parent1);
allow_parent2 = collect_domain_accesses(
dom, mnt_dir.dentry, new_dir->dentry, &layer_masks_parent2);
@@ -825,13 +1199,16 @@ static int current_check_refer_path(struct dentry *const old_dentry,
/* Inode hooks */
-static void hook_inode_free_security(struct inode *const inode)
+static void hook_inode_free_security_rcu(void *inode_security)
{
+ struct landlock_inode_security *inode_sec;
+
/*
* All inodes must already have been untied from their object by
* release_inode() or hook_sb_delete().
*/
- WARN_ON_ONCE(landlock_inode(inode)->object);
+ inode_sec = inode_security + landlock_blob_sizes.lbs_inode;
+ WARN_ON_ONCE(inode_sec->object);
}
/* Super-block hooks */
@@ -1045,11 +1422,7 @@ static int hook_path_mknod(const struct path *const dir,
struct dentry *const dentry, const umode_t mode,
const unsigned int dev)
{
- const struct landlock_ruleset *const dom = get_current_fs_domain();
-
- if (!dom)
- return 0;
- return check_access_path(dom, dir, get_mode_access(mode));
+ return current_check_access_path(dir, get_mode_access(mode));
}
static int hook_path_symlink(const struct path *const dir,
@@ -1119,12 +1492,21 @@ static int hook_file_alloc_security(struct file *const file)
return 0;
}
+static bool is_device(const struct file *const file)
+{
+ const struct inode *inode = file_inode(file);
+
+ return S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode);
+}
+
static int hook_file_open(struct file *const file)
{
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
- access_mask_t open_access_request, full_access_request, allowed_access;
- const access_mask_t optional_access = LANDLOCK_ACCESS_FS_TRUNCATE;
- const struct landlock_ruleset *const dom = get_current_fs_domain();
+ access_mask_t open_access_request, full_access_request, allowed_access,
+ optional_access;
+ const struct landlock_ruleset *const dom =
+ landlock_get_applicable_domain(
+ landlock_cred(file->f_cred)->domain, any_fs);
if (!dom)
return 0;
@@ -1140,6 +1522,10 @@ static int hook_file_open(struct file *const file)
* We look up more access than what we immediately need for open(), so
* that we can later authorize operations on opened files.
*/
+ optional_access = LANDLOCK_ACCESS_FS_TRUNCATE;
+ if (is_device(file))
+ optional_access |= LANDLOCK_ACCESS_FS_IOCTL_DEV;
+
full_access_request = open_access_request | optional_access;
if (is_access_to_paths_allowed(
@@ -1196,8 +1582,77 @@ static int hook_file_truncate(struct file *const file)
return -EACCES;
}
+static int hook_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ access_mask_t allowed_access = landlock_file(file)->allowed_access;
+
+ /*
+ * It is the access rights at the time of opening the file which
+ * determine whether IOCTL can be used on the opened file later.
+ *
+ * The access right is attached to the opened file in hook_file_open().
+ */
+ if (allowed_access & LANDLOCK_ACCESS_FS_IOCTL_DEV)
+ return 0;
+
+ if (!is_device(file))
+ return 0;
+
+ if (is_masked_device_ioctl(cmd))
+ return 0;
+
+ return -EACCES;
+}
+
+static int hook_file_ioctl_compat(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ access_mask_t allowed_access = landlock_file(file)->allowed_access;
+
+ /*
+ * It is the access rights at the time of opening the file which
+ * determine whether IOCTL can be used on the opened file later.
+ *
+ * The access right is attached to the opened file in hook_file_open().
+ */
+ if (allowed_access & LANDLOCK_ACCESS_FS_IOCTL_DEV)
+ return 0;
+
+ if (!is_device(file))
+ return 0;
+
+ if (is_masked_device_ioctl_compat(cmd))
+ return 0;
+
+ return -EACCES;
+}
+
+static void hook_file_set_fowner(struct file *file)
+{
+ struct landlock_ruleset *new_dom, *prev_dom;
+
+ /*
+ * Lock already held by __f_setown(), see commit 26f204380a3c ("fs: Fix
+ * file_set_fowner LSM hook inconsistencies").
+ */
+ lockdep_assert_held(&file_f_owner(file)->lock);
+ new_dom = landlock_get_current_domain();
+ landlock_get_ruleset(new_dom);
+ prev_dom = landlock_file(file)->fown_domain;
+ landlock_file(file)->fown_domain = new_dom;
+
+ /* Called in an RCU read-side critical section. */
+ landlock_put_ruleset_deferred(prev_dom);
+}
+
+static void hook_file_free_security(struct file *file)
+{
+ landlock_put_ruleset_deferred(landlock_file(file)->fown_domain);
+}
+
static struct security_hook_list landlock_hooks[] __ro_after_init = {
- LSM_HOOK_INIT(inode_free_security, hook_inode_free_security),
+ LSM_HOOK_INIT(inode_free_security_rcu, hook_inode_free_security_rcu),
LSM_HOOK_INIT(sb_delete, hook_sb_delete),
LSM_HOOK_INIT(sb_mount, hook_sb_mount),
@@ -1218,6 +1673,10 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
LSM_HOOK_INIT(file_alloc_security, hook_file_alloc_security),
LSM_HOOK_INIT(file_open, hook_file_open),
LSM_HOOK_INIT(file_truncate, hook_file_truncate),
+ LSM_HOOK_INIT(file_ioctl, hook_file_ioctl),
+ LSM_HOOK_INIT(file_ioctl_compat, hook_file_ioctl_compat),
+ LSM_HOOK_INIT(file_set_fowner, hook_file_set_fowner),
+ LSM_HOOK_INIT(file_free_security, hook_file_free_security),
};
__init void landlock_add_fs_hooks(void)
@@ -1225,3 +1684,27 @@ __init void landlock_add_fs_hooks(void)
security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
&landlock_lsmid);
}
+
+#ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
+
+/* clang-format off */
+static struct kunit_case test_cases[] = {
+ KUNIT_CASE(test_no_more_access),
+ KUNIT_CASE(test_scope_to_request_with_exec_none),
+ KUNIT_CASE(test_scope_to_request_with_exec_some),
+ KUNIT_CASE(test_scope_to_request_without_access),
+ KUNIT_CASE(test_is_eacces_with_none),
+ KUNIT_CASE(test_is_eacces_with_refer),
+ KUNIT_CASE(test_is_eacces_with_write),
+ {}
+};
+/* clang-format on */
+
+static struct kunit_suite test_suite = {
+ .name = "landlock_fs",
+ .test_cases = test_cases,
+};
+
+kunit_test_suite(test_suite);
+
+#endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
diff --git a/security/landlock/fs.h b/security/landlock/fs.h
index 488e4813680a..d445f411c26a 100644
--- a/security/landlock/fs.h
+++ b/security/landlock/fs.h
@@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/rcupdate.h>
+#include "access.h"
#include "ruleset.h"
#include "setup.h"
@@ -52,6 +53,13 @@ struct landlock_file_security {
* needed to authorize later operations on the open file.
*/
access_mask_t allowed_access;
+ /**
+ * @fown_domain: Domain of the task that set the PID that may receive a
+ * signal e.g., SIGURG when writing MSG_OOB to the related socket.
+ * This pointer is protected by the related file->f_owner->lock, as for
+ * fown_struct's members: pid, uid, and euid.
+ */
+ struct landlock_ruleset *fown_domain;
};
/**
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 93c9c6f91556..15f7606066c8 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -18,16 +18,17 @@
#define LANDLOCK_MAX_NUM_LAYERS 16
#define LANDLOCK_MAX_NUM_RULES U32_MAX
-#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_TRUNCATE
+#define LANDLOCK_LAST_ACCESS_FS LANDLOCK_ACCESS_FS_IOCTL_DEV
#define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
#define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
-#define LANDLOCK_SHIFT_ACCESS_FS 0
#define LANDLOCK_LAST_ACCESS_NET LANDLOCK_ACCESS_NET_CONNECT_TCP
#define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
#define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET)
-#define LANDLOCK_SHIFT_ACCESS_NET LANDLOCK_NUM_ACCESS_FS
+#define LANDLOCK_LAST_SCOPE LANDLOCK_SCOPE_SIGNAL
+#define LANDLOCK_MASK_SCOPE ((LANDLOCK_LAST_SCOPE << 1) - 1)
+#define LANDLOCK_NUM_SCOPE __const_hweight64(LANDLOCK_MASK_SCOPE)
/* clang-format on */
#endif /* _SECURITY_LANDLOCK_LIMITS_H */
diff --git a/security/landlock/net.c b/security/landlock/net.c
index efa1b644a4af..104b6c01fe50 100644
--- a/security/landlock/net.c
+++ b/security/landlock/net.c
@@ -39,49 +39,31 @@ int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
return err;
}
-static access_mask_t
-get_raw_handled_net_accesses(const struct landlock_ruleset *const domain)
-{
- access_mask_t access_dom = 0;
- size_t layer_level;
-
- for (layer_level = 0; layer_level < domain->num_layers; layer_level++)
- access_dom |= landlock_get_net_access_mask(domain, layer_level);
- return access_dom;
-}
-
-static const struct landlock_ruleset *get_current_net_domain(void)
-{
- const struct landlock_ruleset *const dom =
- landlock_get_current_domain();
-
- if (!dom || !get_raw_handled_net_accesses(dom))
- return NULL;
-
- return dom;
-}
+static const struct access_masks any_net = {
+ .net = ~0,
+};
static int current_check_access_socket(struct socket *const sock,
struct sockaddr *const address,
const int addrlen,
- const access_mask_t access_request)
+ access_mask_t access_request)
{
__be16 port;
layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
const struct landlock_rule *rule;
- access_mask_t handled_access;
struct landlock_id id = {
.type = LANDLOCK_KEY_NET_PORT,
};
- const struct landlock_ruleset *const dom = get_current_net_domain();
+ const struct landlock_ruleset *const dom =
+ landlock_get_applicable_domain(landlock_get_current_domain(),
+ any_net);
if (!dom)
return 0;
if (WARN_ON_ONCE(dom->num_layers < 1))
return -EACCES;
- /* Checks if it's a (potential) TCP socket. */
- if (sock->type != SOCK_STREAM)
+ if (!sk_is_tcp(sock->sk))
return 0;
/* Checks for minimal header length to safely read sa_family. */
@@ -164,9 +146,9 @@ static int current_check_access_socket(struct socket *const sock,
BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
rule = landlock_find_rule(dom, id);
- handled_access = landlock_init_layer_masks(
+ access_request = landlock_init_layer_masks(
dom, access_request, &layer_masks, LANDLOCK_KEY_NET_PORT);
- if (landlock_unmask_layers(rule, handled_access, &layer_masks,
+ if (landlock_unmask_layers(rule, access_request, &layer_masks,
ARRAY_SIZE(layer_masks)))
return 0;
diff --git a/security/landlock/ptrace.c b/security/landlock/ptrace.c
deleted file mode 100644
index 2bfc533d36e4..000000000000
--- a/security/landlock/ptrace.c
+++ /dev/null
@@ -1,120 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Landlock LSM - Ptrace hooks
- *
- * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
- * Copyright © 2019-2020 ANSSI
- */
-
-#include <asm/current.h>
-#include <linux/cred.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/lsm_hooks.h>
-#include <linux/rcupdate.h>
-#include <linux/sched.h>
-
-#include "common.h"
-#include "cred.h"
-#include "ptrace.h"
-#include "ruleset.h"
-#include "setup.h"
-
-/**
- * domain_scope_le - Checks domain ordering for scoped ptrace
- *
- * @parent: Parent domain.
- * @child: Potential child of @parent.
- *
- * Checks if the @parent domain is less or equal to (i.e. an ancestor, which
- * means a subset of) the @child domain.
- */
-static bool domain_scope_le(const struct landlock_ruleset *const parent,
- const struct landlock_ruleset *const child)
-{
- const struct landlock_hierarchy *walker;
-
- if (!parent)
- return true;
- if (!child)
- return false;
- for (walker = child->hierarchy; walker; walker = walker->parent) {
- if (walker == parent->hierarchy)
- /* @parent is in the scoped hierarchy of @child. */
- return true;
- }
- /* There is no relationship between @parent and @child. */
- return false;
-}
-
-static bool task_is_scoped(const struct task_struct *const parent,
- const struct task_struct *const child)
-{
- bool is_scoped;
- const struct landlock_ruleset *dom_parent, *dom_child;
-
- rcu_read_lock();
- dom_parent = landlock_get_task_domain(parent);
- dom_child = landlock_get_task_domain(child);
- is_scoped = domain_scope_le(dom_parent, dom_child);
- rcu_read_unlock();
- return is_scoped;
-}
-
-static int task_ptrace(const struct task_struct *const parent,
- const struct task_struct *const child)
-{
- /* Quick return for non-landlocked tasks. */
- if (!landlocked(parent))
- return 0;
- if (task_is_scoped(parent, child))
- return 0;
- return -EPERM;
-}
-
-/**
- * hook_ptrace_access_check - Determines whether the current process may access
- * another
- *
- * @child: Process to be accessed.
- * @mode: Mode of attachment.
- *
- * If the current task has Landlock rules, then the child must have at least
- * the same rules. Else denied.
- *
- * Determines whether a process may access another, returning 0 if permission
- * granted, -errno if denied.
- */
-static int hook_ptrace_access_check(struct task_struct *const child,
- const unsigned int mode)
-{
- return task_ptrace(current, child);
-}
-
-/**
- * hook_ptrace_traceme - Determines whether another process may trace the
- * current one
- *
- * @parent: Task proposed to be the tracer.
- *
- * If the parent has Landlock rules, then the current task must have the same
- * or more rules. Else denied.
- *
- * Determines whether the nominated task is permitted to trace the current
- * process, returning 0 if permission is granted, -errno if denied.
- */
-static int hook_ptrace_traceme(struct task_struct *const parent)
-{
- return task_ptrace(parent, current);
-}
-
-static struct security_hook_list landlock_hooks[] __ro_after_init = {
- LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check),
- LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme),
-};
-
-__init void landlock_add_ptrace_hooks(void)
-{
- security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
- &landlock_lsmid);
-}
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index e0a5fbf9201a..bff4e40a3093 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -8,11 +8,13 @@
#include <linux/bits.h>
#include <linux/bug.h>
+#include <linux/cleanup.h>
#include <linux/compiler_types.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/lockdep.h>
+#include <linux/mutex.h>
#include <linux/overflow.h>
#include <linux/rbtree.h>
#include <linux/refcount.h>
@@ -20,6 +22,7 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
+#include "access.h"
#include "limits.h"
#include "object.h"
#include "ruleset.h"
@@ -52,12 +55,13 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
struct landlock_ruleset *
landlock_create_ruleset(const access_mask_t fs_access_mask,
- const access_mask_t net_access_mask)
+ const access_mask_t net_access_mask,
+ const access_mask_t scope_mask)
{
struct landlock_ruleset *new_ruleset;
/* Informs about useless ruleset. */
- if (!fs_access_mask && !net_access_mask)
+ if (!fs_access_mask && !net_access_mask && !scope_mask)
return ERR_PTR(-ENOMSG);
new_ruleset = create_ruleset(1);
if (IS_ERR(new_ruleset))
@@ -66,6 +70,8 @@ landlock_create_ruleset(const access_mask_t fs_access_mask,
landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
if (net_access_mask)
landlock_add_net_access_mask(new_ruleset, net_access_mask, 0);
+ if (scope_mask)
+ landlock_add_scope_mask(new_ruleset, scope_mask, 0);
return new_ruleset;
}
@@ -118,7 +124,7 @@ create_rule(const struct landlock_id id,
return ERR_PTR(-ENOMEM);
RB_CLEAR_NODE(&new_rule->node);
if (is_object_pointer(id.type)) {
- /* This should be catched by insert_rule(). */
+ /* This should have been caught by insert_rule(). */
WARN_ON_ONCE(!id.key.object);
landlock_get_object(id.key.object);
}
@@ -169,13 +175,9 @@ static void build_check_ruleset(void)
.num_rules = ~0,
.num_layers = ~0,
};
- typeof(ruleset.access_masks[0]) access_masks = ~0;
BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
- BUILD_BUG_ON(access_masks <
- ((LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS) |
- (LANDLOCK_MASK_ACCESS_NET << LANDLOCK_SHIFT_ACCESS_NET)));
}
/**
@@ -385,7 +387,8 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
err = -EINVAL;
goto out_unlock;
}
- dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
+ dst->access_masks[dst->num_layers - 1] =
+ landlock_upgrade_handled_access_masks(src->access_masks[0]);
/* Merges the @src inode tree. */
err = merge_tree(dst, src, LANDLOCK_KEY_INODE);
@@ -538,7 +541,7 @@ struct landlock_ruleset *
landlock_merge_ruleset(struct landlock_ruleset *const parent,
struct landlock_ruleset *const ruleset)
{
- struct landlock_ruleset *new_dom;
+ struct landlock_ruleset *new_dom __free(landlock_put_ruleset) = NULL;
u32 num_layers;
int err;
@@ -558,29 +561,25 @@ landlock_merge_ruleset(struct landlock_ruleset *const parent,
new_dom = create_ruleset(num_layers);
if (IS_ERR(new_dom))
return new_dom;
+
new_dom->hierarchy =
kzalloc(sizeof(*new_dom->hierarchy), GFP_KERNEL_ACCOUNT);
- if (!new_dom->hierarchy) {
- err = -ENOMEM;
- goto out_put_dom;
- }
+ if (!new_dom->hierarchy)
+ return ERR_PTR(-ENOMEM);
+
refcount_set(&new_dom->hierarchy->usage, 1);
/* ...as a child of @parent... */
err = inherit_ruleset(parent, new_dom);
if (err)
- goto out_put_dom;
+ return ERR_PTR(err);
/* ...and including @ruleset. */
err = merge_ruleset(new_dom, ruleset);
if (err)
- goto out_put_dom;
-
- return new_dom;
+ return ERR_PTR(err);
-out_put_dom:
- landlock_put_ruleset(new_dom);
- return ERR_PTR(err);
+ return no_free_ptr(new_dom);
}
/*
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index c7f1526784fd..52f4f0af6ab0 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -9,45 +9,17 @@
#ifndef _SECURITY_LANDLOCK_RULESET_H
#define _SECURITY_LANDLOCK_RULESET_H
-#include <linux/bitops.h>
-#include <linux/build_bug.h>
+#include <linux/cleanup.h>
+#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/rbtree.h>
#include <linux/refcount.h>
#include <linux/workqueue.h>
-#include <uapi/linux/landlock.h>
+#include "access.h"
#include "limits.h"
#include "object.h"
-/*
- * All access rights that are denied by default whether they are handled or not
- * by a ruleset/layer. This must be ORed with all ruleset->access_masks[]
- * entries when we need to get the absolute handled access masks.
- */
-/* clang-format off */
-#define LANDLOCK_ACCESS_FS_INITIALLY_DENIED ( \
- LANDLOCK_ACCESS_FS_REFER)
-/* clang-format on */
-
-typedef u16 access_mask_t;
-/* Makes sure all filesystem access rights can be stored. */
-static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
-/* Makes sure all network access rights can be stored. */
-static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET);
-/* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */
-static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
-
-/* Ruleset access masks. */
-typedef u32 access_masks_t;
-/* Makes sure all ruleset access rights can be stored. */
-static_assert(BITS_PER_TYPE(access_masks_t) >=
- LANDLOCK_NUM_ACCESS_FS + LANDLOCK_NUM_ACCESS_NET);
-
-typedef u16 layer_mask_t;
-/* Makes sure all layers can be checked. */
-static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS);
-
/**
* struct landlock_layer - Access rights for a given layer
*/
@@ -226,18 +198,22 @@ struct landlock_ruleset {
* layers are set once and never changed for the
* lifetime of the ruleset.
*/
- access_masks_t access_masks[];
+ struct access_masks access_masks[];
};
};
};
struct landlock_ruleset *
landlock_create_ruleset(const access_mask_t access_mask_fs,
- const access_mask_t access_mask_net);
+ const access_mask_t access_mask_net,
+ const access_mask_t scope_mask);
void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
+DEFINE_FREE(landlock_put_ruleset, struct landlock_ruleset *,
+ if (!IS_ERR_OR_NULL(_T)) landlock_put_ruleset(_T))
+
int landlock_insert_rule(struct landlock_ruleset *const ruleset,
const struct landlock_id id,
const access_mask_t access);
@@ -256,6 +232,61 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
refcount_inc(&ruleset->usage);
}
+/**
+ * landlock_union_access_masks - Return all access rights handled in the
+ * domain
+ *
+ * @domain: Landlock ruleset (used as a domain)
+ *
+ * Returns: an access_masks result of the OR of all the domain's access masks.
+ */
+static inline struct access_masks
+landlock_union_access_masks(const struct landlock_ruleset *const domain)
+{
+ union access_masks_all matches = {};
+ size_t layer_level;
+
+ for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
+ union access_masks_all layer = {
+ .masks = domain->access_masks[layer_level],
+ };
+
+ matches.all |= layer.all;
+ }
+
+ return matches.masks;
+}
+
+/**
+ * landlock_get_applicable_domain - Return @domain if it applies to (handles)
+ * at least one of the access rights specified
+ * in @masks
+ *
+ * @domain: Landlock ruleset (used as a domain)
+ * @masks: access masks
+ *
+ * Returns: @domain if any access rights specified in @masks is handled, or
+ * NULL otherwise.
+ */
+static inline const struct landlock_ruleset *
+landlock_get_applicable_domain(const struct landlock_ruleset *const domain,
+ const struct access_masks masks)
+{
+ const union access_masks_all masks_all = {
+ .masks = masks,
+ };
+ union access_masks_all merge = {};
+
+ if (!domain)
+ return NULL;
+
+ merge.masks = landlock_union_access_masks(domain);
+ if (merge.all & masks_all.all)
+ return domain;
+
+ return NULL;
+}
+
static inline void
landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
const access_mask_t fs_access_mask,
@@ -265,8 +296,7 @@ landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
/* Should already be checked in sys_landlock_create_ruleset(). */
WARN_ON_ONCE(fs_access_mask != fs_mask);
- ruleset->access_masks[layer_level] |=
- (fs_mask << LANDLOCK_SHIFT_ACCESS_FS);
+ ruleset->access_masks[layer_level].fs |= fs_mask;
}
static inline void
@@ -278,17 +308,18 @@ landlock_add_net_access_mask(struct landlock_ruleset *const ruleset,
/* Should already be checked in sys_landlock_create_ruleset(). */
WARN_ON_ONCE(net_access_mask != net_mask);
- ruleset->access_masks[layer_level] |=
- (net_mask << LANDLOCK_SHIFT_ACCESS_NET);
+ ruleset->access_masks[layer_level].net |= net_mask;
}
-static inline access_mask_t
-landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset,
- const u16 layer_level)
+static inline void
+landlock_add_scope_mask(struct landlock_ruleset *const ruleset,
+ const access_mask_t scope_mask, const u16 layer_level)
{
- return (ruleset->access_masks[layer_level] >>
- LANDLOCK_SHIFT_ACCESS_FS) &
- LANDLOCK_MASK_ACCESS_FS;
+ access_mask_t mask = scope_mask & LANDLOCK_MASK_SCOPE;
+
+ /* Should already be checked in sys_landlock_create_ruleset(). */
+ WARN_ON_ONCE(scope_mask != mask);
+ ruleset->access_masks[layer_level].scope |= mask;
}
static inline access_mask_t
@@ -296,17 +327,22 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
const u16 layer_level)
{
/* Handles all initially denied by default access rights. */
- return landlock_get_raw_fs_access_mask(ruleset, layer_level) |
- LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
+ return ruleset->access_masks[layer_level].fs |
+ _LANDLOCK_ACCESS_FS_INITIALLY_DENIED;
}
static inline access_mask_t
landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset,
const u16 layer_level)
{
- return (ruleset->access_masks[layer_level] >>
- LANDLOCK_SHIFT_ACCESS_NET) &
- LANDLOCK_MASK_ACCESS_NET;
+ return ruleset->access_masks[layer_level].net;
+}
+
+static inline access_mask_t
+landlock_get_scope_mask(const struct landlock_ruleset *const ruleset,
+ const u16 layer_level)
+{
+ return ruleset->access_masks[layer_level].scope;
}
bool landlock_unmask_layers(const struct landlock_rule *const rule,
diff --git a/security/landlock/setup.c b/security/landlock/setup.c
index f6dd33143b7f..28519a45b11f 100644
--- a/security/landlock/setup.c
+++ b/security/landlock/setup.c
@@ -14,8 +14,8 @@
#include "cred.h"
#include "fs.h"
#include "net.h"
-#include "ptrace.h"
#include "setup.h"
+#include "task.h"
bool landlock_initialized __ro_after_init = false;
@@ -34,7 +34,7 @@ const struct lsm_id landlock_lsmid = {
static int __init landlock_init(void)
{
landlock_add_cred_hooks();
- landlock_add_ptrace_hooks();
+ landlock_add_task_hooks();
landlock_add_fs_hooks();
landlock_add_net_hooks();
landlock_initialized = true;
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 898358f57fa0..a9760d252fc2 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -10,6 +10,7 @@
#include <linux/anon_inodes.h>
#include <linux/build_bug.h>
#include <linux/capability.h>
+#include <linux/cleanup.h>
#include <linux/compiler_types.h>
#include <linux/dcache.h>
#include <linux/err.h>
@@ -33,6 +34,18 @@
#include "ruleset.h"
#include "setup.h"
+static bool is_initialized(void)
+{
+ if (likely(landlock_initialized))
+ return true;
+
+ pr_warn_once(
+ "Disabled but requested by user space. "
+ "You should enable Landlock at boot time: "
+ "https://docs.kernel.org/userspace-api/landlock.html#boot-time-configuration\n");
+ return false;
+}
+
/**
* copy_min_struct_from_user - Safe future-proof argument copying
*
@@ -85,8 +98,9 @@ static void build_check_abi(void)
*/
ruleset_size = sizeof(ruleset_attr.handled_access_fs);
ruleset_size += sizeof(ruleset_attr.handled_access_net);
+ ruleset_size += sizeof(ruleset_attr.scoped);
BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
- BUILD_BUG_ON(sizeof(ruleset_attr) != 16);
+ BUILD_BUG_ON(sizeof(ruleset_attr) != 24);
path_beneath_size = sizeof(path_beneath_attr.allowed_access);
path_beneath_size += sizeof(path_beneath_attr.parent_fd);
@@ -137,7 +151,7 @@ static const struct file_operations ruleset_fops = {
.write = fop_dummy_write,
};
-#define LANDLOCK_ABI_VERSION 4
+#define LANDLOCK_ABI_VERSION 6
/**
* sys_landlock_create_ruleset - Create a new ruleset
@@ -158,8 +172,9 @@ static const struct file_operations ruleset_fops = {
* Possible returned errors are:
*
* - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
- * - %EINVAL: unknown @flags, or unknown access, or too small @size;
- * - %E2BIG or %EFAULT: @attr or @size inconsistencies;
+ * - %EINVAL: unknown @flags, or unknown access, or unknown scope, or too small @size;
+ * - %E2BIG: @attr or @size inconsistencies;
+ * - %EFAULT: @attr or @size inconsistencies;
* - %ENOMSG: empty &landlock_ruleset_attr.handled_access_fs.
*/
SYSCALL_DEFINE3(landlock_create_ruleset,
@@ -173,7 +188,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
/* Build-time checks. */
build_check_abi();
- if (!landlock_initialized)
+ if (!is_initialized())
return -EOPNOTSUPP;
if (flags) {
@@ -201,9 +216,14 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
LANDLOCK_MASK_ACCESS_NET)
return -EINVAL;
+ /* Checks IPC scoping content (and 32-bits cast). */
+ if ((ruleset_attr.scoped | LANDLOCK_MASK_SCOPE) != LANDLOCK_MASK_SCOPE)
+ return -EINVAL;
+
/* Checks arguments and transforms to kernel struct. */
ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
- ruleset_attr.handled_access_net);
+ ruleset_attr.handled_access_net,
+ ruleset_attr.scoped);
if (IS_ERR(ruleset))
return PTR_ERR(ruleset);
@@ -222,31 +242,21 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
static struct landlock_ruleset *get_ruleset_from_fd(const int fd,
const fmode_t mode)
{
- struct fd ruleset_f;
+ CLASS(fd, ruleset_f)(fd);
struct landlock_ruleset *ruleset;
- ruleset_f = fdget(fd);
- if (!ruleset_f.file)
+ if (fd_empty(ruleset_f))
return ERR_PTR(-EBADF);
/* Checks FD type and access right. */
- if (ruleset_f.file->f_op != &ruleset_fops) {
- ruleset = ERR_PTR(-EBADFD);
- goto out_fdput;
- }
- if (!(ruleset_f.file->f_mode & mode)) {
- ruleset = ERR_PTR(-EPERM);
- goto out_fdput;
- }
- ruleset = ruleset_f.file->private_data;
- if (WARN_ON_ONCE(ruleset->num_layers != 1)) {
- ruleset = ERR_PTR(-EINVAL);
- goto out_fdput;
- }
+ if (fd_file(ruleset_f)->f_op != &ruleset_fops)
+ return ERR_PTR(-EBADFD);
+ if (!(fd_file(ruleset_f)->f_mode & mode))
+ return ERR_PTR(-EPERM);
+ ruleset = fd_file(ruleset_f)->private_data;
+ if (WARN_ON_ONCE(ruleset->num_layers != 1))
+ return ERR_PTR(-EINVAL);
landlock_get_ruleset(ruleset);
-
-out_fdput:
- fdput(ruleset_f);
return ruleset;
}
@@ -257,35 +267,28 @@ out_fdput:
*/
static int get_path_from_fd(const s32 fd, struct path *const path)
{
- struct fd f;
- int err = 0;
+ CLASS(fd_raw, f)(fd);
BUILD_BUG_ON(!__same_type(
fd, ((struct landlock_path_beneath_attr *)NULL)->parent_fd));
- /* Handles O_PATH. */
- f = fdget_raw(fd);
- if (!f.file)
+ if (fd_empty(f))
return -EBADF;
/*
* Forbids ruleset FDs, internal filesystems (e.g. nsfs), including
* pseudo filesystems that will never be mountable (e.g. sockfs,
* pipefs).
*/
- if ((f.file->f_op == &ruleset_fops) ||
- (f.file->f_path.mnt->mnt_flags & MNT_INTERNAL) ||
- (f.file->f_path.dentry->d_sb->s_flags & SB_NOUSER) ||
- d_is_negative(f.file->f_path.dentry) ||
- IS_PRIVATE(d_backing_inode(f.file->f_path.dentry))) {
- err = -EBADFD;
- goto out_fdput;
- }
- *path = f.file->f_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;
+
+ *path = fd_file(f)->f_path;
path_get(path);
-
-out_fdput:
- fdput(f);
- return err;
+ return 0;
}
static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
@@ -310,7 +313,7 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
return -ENOMSG;
/* Checks that allowed_access matches the @ruleset constraints. */
- mask = landlock_get_raw_fs_access_mask(ruleset, 0);
+ mask = ruleset->access_masks[0].fs;
if ((path_beneath_attr.allowed_access | mask) != mask)
return -EINVAL;
@@ -366,8 +369,7 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
* with the new rule.
* @rule_type: Identify the structure type pointed to by @rule_attr:
* %LANDLOCK_RULE_PATH_BENEATH or %LANDLOCK_RULE_NET_PORT.
- * @rule_attr: Pointer to a rule (only of type &struct
- * landlock_path_beneath_attr for now).
+ * @rule_attr: Pointer to a rule (matching the @rule_type).
* @flags: Must be 0.
*
* This system call enables to define a new rule and add it to an existing
@@ -378,27 +380,28 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
* - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
* - %EAFNOSUPPORT: @rule_type is %LANDLOCK_RULE_NET_PORT but TCP/IP is not
* supported by the running kernel;
- * - %EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
+ * - %EINVAL: @flags is not 0;
+ * - %EINVAL: The rule accesses are inconsistent (i.e.
* &landlock_path_beneath_attr.allowed_access or
- * &landlock_net_port_attr.allowed_access is not a subset of the
- * ruleset handled accesses), or &landlock_net_port_attr.port is
- * greater than 65535;
- * - %ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
+ * &landlock_net_port_attr.allowed_access is not a subset of the ruleset
+ * handled accesses)
+ * - %EINVAL: &landlock_net_port_attr.port is greater than 65535;
+ * - %ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access is
+ * 0);
* - %EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
* member of @rule_attr is not a file descriptor as expected;
* - %EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
* @rule_attr is not the expected file descriptor type;
* - %EPERM: @ruleset_fd has no write access to the underlying ruleset;
- * - %EFAULT: @rule_attr inconsistency.
+ * - %EFAULT: @rule_attr was not a valid address.
*/
SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
const enum landlock_rule_type, rule_type,
const void __user *const, rule_attr, const __u32, flags)
{
- struct landlock_ruleset *ruleset;
- int err;
+ struct landlock_ruleset *ruleset __free(landlock_put_ruleset) = NULL;
- if (!landlock_initialized)
+ if (!is_initialized())
return -EOPNOTSUPP;
/* No flag for now. */
@@ -412,17 +415,12 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
switch (rule_type) {
case LANDLOCK_RULE_PATH_BENEATH:
- err = add_rule_path_beneath(ruleset, rule_attr);
- break;
+ return add_rule_path_beneath(ruleset, rule_attr);
case LANDLOCK_RULE_NET_PORT:
- err = add_rule_net_port(ruleset, rule_attr);
- break;
+ return add_rule_net_port(ruleset, rule_attr);
default:
- err = -EINVAL;
- break;
+ return -EINVAL;
}
- landlock_put_ruleset(ruleset);
- return err;
}
/* Enforcement */
@@ -453,12 +451,12 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
flags)
{
- struct landlock_ruleset *new_dom, *ruleset;
+ struct landlock_ruleset *new_dom,
+ *ruleset __free(landlock_put_ruleset) = NULL;
struct cred *new_cred;
struct landlock_cred_security *new_llcred;
- int err;
- if (!landlock_initialized)
+ if (!is_initialized())
return -EOPNOTSUPP;
/*
@@ -480,10 +478,9 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
/* Prepares new credentials. */
new_cred = prepare_creds();
- if (!new_cred) {
- err = -ENOMEM;
- goto out_put_ruleset;
- }
+ if (!new_cred)
+ return -ENOMEM;
+
new_llcred = landlock_cred(new_cred);
/*
@@ -492,21 +489,12 @@ SYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32,
*/
new_dom = landlock_merge_ruleset(new_llcred->domain, ruleset);
if (IS_ERR(new_dom)) {
- err = PTR_ERR(new_dom);
- goto out_put_creds;
+ abort_creds(new_cred);
+ return PTR_ERR(new_dom);
}
/* Replaces the old (prepared) domain. */
landlock_put_ruleset(new_llcred->domain);
new_llcred->domain = new_dom;
-
- landlock_put_ruleset(ruleset);
return commit_creds(new_cred);
-
-out_put_creds:
- abort_creds(new_cred);
-
-out_put_ruleset:
- landlock_put_ruleset(ruleset);
- return err;
}
diff --git a/security/landlock/task.c b/security/landlock/task.c
new file mode 100644
index 000000000000..dc7dab78392e
--- /dev/null
+++ b/security/landlock/task.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock LSM - Ptrace hooks
+ *
+ * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
+ * Copyright © 2019-2020 ANSSI
+ */
+
+#include <asm/current.h>
+#include <linux/cred.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/lsm_hooks.h>
+#include <linux/rcupdate.h>
+#include <linux/sched.h>
+#include <net/af_unix.h>
+#include <net/sock.h>
+
+#include "common.h"
+#include "cred.h"
+#include "fs.h"
+#include "ruleset.h"
+#include "setup.h"
+#include "task.h"
+
+/**
+ * domain_scope_le - Checks domain ordering for scoped ptrace
+ *
+ * @parent: Parent domain.
+ * @child: Potential child of @parent.
+ *
+ * Checks if the @parent domain is less or equal to (i.e. an ancestor, which
+ * means a subset of) the @child domain.
+ */
+static bool domain_scope_le(const struct landlock_ruleset *const parent,
+ const struct landlock_ruleset *const child)
+{
+ const struct landlock_hierarchy *walker;
+
+ if (!parent)
+ return true;
+ if (!child)
+ return false;
+ for (walker = child->hierarchy; walker; walker = walker->parent) {
+ if (walker == parent->hierarchy)
+ /* @parent is in the scoped hierarchy of @child. */
+ return true;
+ }
+ /* There is no relationship between @parent and @child. */
+ return false;
+}
+
+static bool task_is_scoped(const struct task_struct *const parent,
+ const struct task_struct *const child)
+{
+ bool is_scoped;
+ const struct landlock_ruleset *dom_parent, *dom_child;
+
+ rcu_read_lock();
+ dom_parent = landlock_get_task_domain(parent);
+ dom_child = landlock_get_task_domain(child);
+ is_scoped = domain_scope_le(dom_parent, dom_child);
+ rcu_read_unlock();
+ return is_scoped;
+}
+
+static int task_ptrace(const struct task_struct *const parent,
+ const struct task_struct *const child)
+{
+ /* Quick return for non-landlocked tasks. */
+ if (!landlocked(parent))
+ return 0;
+ if (task_is_scoped(parent, child))
+ return 0;
+ return -EPERM;
+}
+
+/**
+ * hook_ptrace_access_check - Determines whether the current process may access
+ * another
+ *
+ * @child: Process to be accessed.
+ * @mode: Mode of attachment.
+ *
+ * If the current task has Landlock rules, then the child must have at least
+ * the same rules. Else denied.
+ *
+ * Determines whether a process may access another, returning 0 if permission
+ * granted, -errno if denied.
+ */
+static int hook_ptrace_access_check(struct task_struct *const child,
+ const unsigned int mode)
+{
+ return task_ptrace(current, child);
+}
+
+/**
+ * hook_ptrace_traceme - Determines whether another process may trace the
+ * current one
+ *
+ * @parent: Task proposed to be the tracer.
+ *
+ * If the parent has Landlock rules, then the current task must have the same
+ * or more rules. Else denied.
+ *
+ * Determines whether the nominated task is permitted to trace the current
+ * process, returning 0 if permission is granted, -errno if denied.
+ */
+static int hook_ptrace_traceme(struct task_struct *const parent)
+{
+ return task_ptrace(parent, current);
+}
+
+/**
+ * domain_is_scoped - Checks if the client domain is scoped in the same
+ * domain as the server.
+ *
+ * @client: IPC sender domain.
+ * @server: IPC receiver domain.
+ * @scope: The scope restriction criteria.
+ *
+ * Returns: True if the @client domain is scoped to access the @server,
+ * unless the @server is also scoped in the same domain as @client.
+ */
+static bool domain_is_scoped(const struct landlock_ruleset *const client,
+ const struct landlock_ruleset *const server,
+ access_mask_t scope)
+{
+ int client_layer, server_layer;
+ struct landlock_hierarchy *client_walker, *server_walker;
+
+ /* Quick return if client has no domain */
+ if (WARN_ON_ONCE(!client))
+ return false;
+
+ client_layer = client->num_layers - 1;
+ client_walker = client->hierarchy;
+ /*
+ * client_layer must be a signed integer with greater capacity
+ * than client->num_layers to ensure the following loop stops.
+ */
+ BUILD_BUG_ON(sizeof(client_layer) > sizeof(client->num_layers));
+
+ server_layer = server ? (server->num_layers - 1) : -1;
+ server_walker = server ? server->hierarchy : NULL;
+
+ /*
+ * Walks client's parent domains down to the same hierarchy level
+ * as the server's domain, and checks that none of these client's
+ * parent domains are scoped.
+ */
+ for (; client_layer > server_layer; client_layer--) {
+ if (landlock_get_scope_mask(client, client_layer) & scope)
+ return true;
+
+ client_walker = client_walker->parent;
+ }
+ /*
+ * Walks server's parent domains down to the same hierarchy level as
+ * the client's domain.
+ */
+ for (; server_layer > client_layer; server_layer--)
+ server_walker = server_walker->parent;
+
+ for (; client_layer >= 0; client_layer--) {
+ if (landlock_get_scope_mask(client, client_layer) & scope) {
+ /*
+ * Client and server are at the same level in the
+ * hierarchy. If the client is scoped, the request is
+ * only allowed if this domain is also a server's
+ * ancestor.
+ */
+ return server_walker != client_walker;
+ }
+ client_walker = client_walker->parent;
+ server_walker = server_walker->parent;
+ }
+ return false;
+}
+
+static bool sock_is_scoped(struct sock *const other,
+ const struct landlock_ruleset *const domain)
+{
+ const struct landlock_ruleset *dom_other;
+
+ /* The credentials will not change. */
+ lockdep_assert_held(&unix_sk(other)->lock);
+ dom_other = landlock_cred(other->sk_socket->file->f_cred)->domain;
+ return domain_is_scoped(domain, dom_other,
+ LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
+}
+
+static bool is_abstract_socket(struct sock *const sock)
+{
+ struct unix_address *addr = unix_sk(sock)->addr;
+
+ if (!addr)
+ return false;
+
+ if (addr->len >= offsetof(struct sockaddr_un, sun_path) + 1 &&
+ addr->name->sun_path[0] == '\0')
+ return true;
+
+ return false;
+}
+
+static const struct access_masks unix_scope = {
+ .scope = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET,
+};
+
+static int hook_unix_stream_connect(struct sock *const sock,
+ struct sock *const other,
+ struct sock *const newsk)
+{
+ const struct landlock_ruleset *const dom =
+ landlock_get_applicable_domain(landlock_get_current_domain(),
+ unix_scope);
+
+ /* Quick return for non-landlocked tasks. */
+ if (!dom)
+ return 0;
+
+ if (is_abstract_socket(other) && sock_is_scoped(other, dom))
+ return -EPERM;
+
+ return 0;
+}
+
+static int hook_unix_may_send(struct socket *const sock,
+ struct socket *const other)
+{
+ const struct landlock_ruleset *const dom =
+ landlock_get_applicable_domain(landlock_get_current_domain(),
+ unix_scope);
+
+ if (!dom)
+ return 0;
+
+ /*
+ * Checks if this datagram socket was already allowed to be connected
+ * to other.
+ */
+ if (unix_peer(sock->sk) == other->sk)
+ return 0;
+
+ if (is_abstract_socket(other->sk) && sock_is_scoped(other->sk, dom))
+ return -EPERM;
+
+ return 0;
+}
+
+static const struct access_masks signal_scope = {
+ .scope = LANDLOCK_SCOPE_SIGNAL,
+};
+
+static int hook_task_kill(struct task_struct *const p,
+ struct kernel_siginfo *const info, const int sig,
+ const struct cred *const cred)
+{
+ bool is_scoped;
+ const struct landlock_ruleset *dom;
+
+ if (cred) {
+ /* Dealing with USB IO. */
+ dom = landlock_cred(cred)->domain;
+ } else {
+ dom = landlock_get_current_domain();
+ }
+ dom = landlock_get_applicable_domain(dom, signal_scope);
+
+ /* Quick return for non-landlocked tasks. */
+ if (!dom)
+ return 0;
+
+ rcu_read_lock();
+ is_scoped = domain_is_scoped(dom, landlock_get_task_domain(p),
+ LANDLOCK_SCOPE_SIGNAL);
+ rcu_read_unlock();
+ if (is_scoped)
+ return -EPERM;
+
+ return 0;
+}
+
+static int hook_file_send_sigiotask(struct task_struct *tsk,
+ struct fown_struct *fown, int signum)
+{
+ const struct landlock_ruleset *dom;
+ bool is_scoped = false;
+
+ /* Lock already held by send_sigio() and send_sigurg(). */
+ lockdep_assert_held(&fown->lock);
+ dom = landlock_get_applicable_domain(
+ landlock_file(fown->file)->fown_domain, signal_scope);
+
+ /* Quick return for unowned socket. */
+ if (!dom)
+ return 0;
+
+ rcu_read_lock();
+ is_scoped = domain_is_scoped(dom, landlock_get_task_domain(tsk),
+ LANDLOCK_SCOPE_SIGNAL);
+ rcu_read_unlock();
+ if (is_scoped)
+ return -EPERM;
+
+ return 0;
+}
+
+static struct security_hook_list landlock_hooks[] __ro_after_init = {
+ LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check),
+ LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme),
+
+ LSM_HOOK_INIT(unix_stream_connect, hook_unix_stream_connect),
+ LSM_HOOK_INIT(unix_may_send, hook_unix_may_send),
+
+ LSM_HOOK_INIT(task_kill, hook_task_kill),
+ LSM_HOOK_INIT(file_send_sigiotask, hook_file_send_sigiotask),
+};
+
+__init void landlock_add_task_hooks(void)
+{
+ security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
+ &landlock_lsmid);
+}
diff --git a/security/landlock/ptrace.h b/security/landlock/task.h
index 265b220ae3bf..7c00360219a2 100644
--- a/security/landlock/ptrace.h
+++ b/security/landlock/task.h
@@ -6,9 +6,9 @@
* Copyright © 2019 ANSSI
*/
-#ifndef _SECURITY_LANDLOCK_PTRACE_H
-#define _SECURITY_LANDLOCK_PTRACE_H
+#ifndef _SECURITY_LANDLOCK_TASK_H
+#define _SECURITY_LANDLOCK_TASK_H
-__init void landlock_add_ptrace_hooks(void);
+__init void landlock_add_task_hooks(void);
-#endif /* _SECURITY_LANDLOCK_PTRACE_H */
+#endif /* _SECURITY_LANDLOCK_TASK_H */
diff --git a/security/loadpin/Kconfig b/security/loadpin/Kconfig
index 6724eaba3d36..848f8b4a6019 100644
--- a/security/loadpin/Kconfig
+++ b/security/loadpin/Kconfig
@@ -14,6 +14,9 @@ config SECURITY_LOADPIN
config SECURITY_LOADPIN_ENFORCE
bool "Enforce LoadPin at boot"
depends on SECURITY_LOADPIN
+ # Module compression breaks LoadPin unless modules are decompressed in
+ # the kernel.
+ depends on !MODULES || (MODULE_COMPRESS_NONE || MODULE_DECOMPRESS)
help
If selected, LoadPin will enforce pinning at boot. If not
selected, it can be enabled at boot with the kernel parameter
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index 8e93cda130f1..68252452b66c 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -63,7 +63,6 @@ static struct ctl_table loadpin_sysctl_table[] = {
.extra1 = SYSCTL_ONE,
.extra2 = SYSCTL_ONE,
},
- { }
};
static void set_sysctl(bool is_writable)
@@ -284,7 +283,6 @@ enum loadpin_securityfs_interface_index {
static int read_trusted_verity_root_digests(unsigned int fd)
{
- struct fd f;
void *data;
int rc;
char *p, *d;
@@ -296,8 +294,8 @@ static int read_trusted_verity_root_digests(unsigned int fd)
if (!list_empty(&dm_verity_loadpin_trusted_root_digests))
return -EPERM;
- f = fdget(fd);
- if (!f.file)
+ CLASS(fd, f)(fd);
+ if (fd_empty(f))
return -EINVAL;
data = kzalloc(SZ_4K, GFP_KERNEL);
@@ -306,7 +304,7 @@ static int read_trusted_verity_root_digests(unsigned int fd)
goto err;
}
- rc = kernel_read_file(f.file, 0, (void **)&data, SZ_4K - 1, NULL, READING_POLICY);
+ rc = kernel_read_file(fd_file(f), 0, (void **)&data, SZ_4K - 1, NULL, READING_POLICY);
if (rc < 0)
goto err;
@@ -360,7 +358,6 @@ static int read_trusted_verity_root_digests(unsigned int fd)
}
kfree(data);
- fdput(f);
return 0;
@@ -380,8 +377,6 @@ err:
/* disallow further attempts after reading a corrupt/invalid file */
deny_reading_verity_digests = true;
- fdput(f);
-
return rc;
}
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
index cd84d8ea1dfb..cf83afa1d879 100644
--- a/security/lockdown/lockdown.c
+++ b/security/lockdown/lockdown.c
@@ -76,7 +76,7 @@ static struct security_hook_list lockdown_hooks[] __ro_after_init = {
LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
};
-const struct lsm_id lockdown_lsmid = {
+static const struct lsm_id lockdown_lsmid = {
.name = "lockdown",
.id = LSM_ID_LOCKDOWN,
};
@@ -96,7 +96,7 @@ static int __init lockdown_lsm_init(void)
static ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count,
loff_t *ppos)
{
- char temp[80];
+ char temp[80] = "";
int i, offset = 0;
for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index 849e832719e2..52db886dbba8 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -171,7 +171,7 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,
static inline void print_ipv6_addr(struct audit_buffer *ab,
const struct in6_addr *addr, __be16 port,
- char *name1, char *name2)
+ const char *name1, const char *name2)
{
if (!ipv6_addr_any(addr))
audit_log_format(ab, " %s=%pI6c", name1, addr);
@@ -180,7 +180,7 @@ static inline void print_ipv6_addr(struct audit_buffer *ab,
}
static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
- __be16 port, char *name1, char *name2)
+ __be16 port, const char *name1, const char *name2)
{
if (addr)
audit_log_format(ab, " %s=%pI4", name1, &addr);
@@ -207,7 +207,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2);
audit_log_format(ab, " pid=%d comm=", task_tgid_nr(current));
- audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm)));
+ audit_log_untrustedstring(ab, get_task_comm(comm, current));
switch (a->type) {
case LSM_AUDIT_DATA_NONE:
@@ -299,10 +299,10 @@ static void dump_common_audit_data(struct audit_buffer *ab,
if (tsk) {
pid_t pid = task_tgid_nr(tsk);
if (pid) {
- char comm[sizeof(tsk->comm)];
+ char tskcomm[sizeof(tsk->comm)];
audit_log_format(ab, " opid=%d ocomm=", pid);
audit_log_untrustedstring(ab,
- memcpy(comm, tsk->comm, sizeof(comm)));
+ get_task_comm(tskcomm, tsk));
}
}
break;
@@ -425,6 +425,9 @@ static void dump_common_audit_data(struct audit_buffer *ab,
case LSM_AUDIT_DATA_ANONINODE:
audit_log_format(ab, " anonclass=%s", a->u.anonclass);
break;
+ case LSM_AUDIT_DATA_NLMSGTYPE:
+ audit_log_format(ab, " nl-msgtype=%hu", a->u.nlmsg_type);
+ break;
} /* switch (a->type) */
}
diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c
index 5d391b1f7e69..8440948a690c 100644
--- a/security/lsm_syscalls.c
+++ b/security/lsm_syscalls.c
@@ -53,7 +53,7 @@ u64 lsm_name_to_attr(const char *name)
* value indicating the reason for the error is returned.
*/
SYSCALL_DEFINE4(lsm_set_self_attr, unsigned int, attr, struct lsm_ctx __user *,
- ctx, size_t, size, u32, flags)
+ ctx, u32, size, u32, flags)
{
return security_setselfattr(attr, ctx, size, flags);
}
@@ -75,7 +75,7 @@ SYSCALL_DEFINE4(lsm_set_self_attr, unsigned int, attr, struct lsm_ctx __user *,
* a negative value indicating the error is returned.
*/
SYSCALL_DEFINE4(lsm_get_self_attr, unsigned int, attr, struct lsm_ctx __user *,
- ctx, size_t __user *, size, u32, flags)
+ ctx, u32 __user *, size, u32, flags)
{
return security_getselfattr(attr, ctx, size, flags);
}
@@ -93,11 +93,11 @@ SYSCALL_DEFINE4(lsm_get_self_attr, unsigned int, attr, struct lsm_ctx __user *,
* required size. In all other cases a negative value indicating the
* error is returned.
*/
-SYSCALL_DEFINE3(lsm_list_modules, u64 __user *, ids, size_t __user *, size,
+SYSCALL_DEFINE3(lsm_list_modules, u64 __user *, ids, u32 __user *, size,
u32, flags)
{
- size_t total_size = lsm_active_cnt * sizeof(*ids);
- size_t usize;
+ u32 total_size = lsm_active_cnt * sizeof(*ids);
+ u32 usize;
int i;
if (flags)
diff --git a/security/min_addr.c b/security/min_addr.c
index 88c9a6a21f47..0ce267c041ab 100644
--- a/security/min_addr.c
+++ b/security/min_addr.c
@@ -29,7 +29,7 @@ static void update_mmap_min_addr(void)
* sysctl handler which just sets dac_mmap_min_addr = the new value and then
* calls update_mmap_min_addr() so non MAP_FIXED hints get rounded properly
*/
-int mmap_min_addr_handler(struct ctl_table *table, int write,
+int mmap_min_addr_handler(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
int ret;
diff --git a/security/safesetid/securityfs.c b/security/safesetid/securityfs.c
index 25310468bcdd..8e1ffd70b18a 100644
--- a/security/safesetid/securityfs.c
+++ b/security/safesetid/securityfs.c
@@ -143,6 +143,9 @@ static ssize_t handle_policy_update(struct file *file,
char *buf, *p, *end;
int err;
+ if (len >= KMALLOC_MAX_SIZE)
+ return -EINVAL;
+
pol = kmalloc(sizeof(struct setid_ruleset), GFP_KERNEL);
if (!pol)
return -ENOMEM;
diff --git a/security/security.c b/security/security.c
index 7035ee35a393..143561ebc3e8 100644
--- a/security/security.c
+++ b/security/security.c
@@ -19,39 +19,37 @@
#include <linux/kernel.h>
#include <linux/kernel_read_file.h>
#include <linux/lsm_hooks.h>
-#include <linux/integrity.h>
-#include <linux/ima.h>
-#include <linux/evm.h>
-#include <linux/fsnotify.h>
#include <linux/mman.h>
#include <linux/mount.h>
#include <linux/personality.h>
#include <linux/backing-dev.h>
#include <linux/string.h>
+#include <linux/xattr.h>
#include <linux/msg.h>
#include <linux/overflow.h>
+#include <linux/perf_event.h>
+#include <linux/fs.h>
#include <net/flow.h>
+#include <net/sock.h>
-/* How many LSMs were built into the kernel? */
-#define LSM_COUNT (__end_lsm_info - __start_lsm_info)
+#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
/*
- * How many LSMs are built into the kernel as determined at
- * build time. Used to determine fixed array sizes.
- * The capability module is accounted for by CONFIG_SECURITY
- */
-#define LSM_CONFIG_COUNT ( \
- (IS_ENABLED(CONFIG_SECURITY) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_SECURITY_SELINUX) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_SECURITY_SMACK) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_SECURITY_TOMOYO) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_SECURITY_APPARMOR) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_SECURITY_YAMA) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_SECURITY_LOADPIN) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_SECURITY_SAFESETID) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_SECURITY_LOCKDOWN_LSM) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_BPF_LSM) ? 1 : 0) + \
- (IS_ENABLED(CONFIG_SECURITY_LANDLOCK) ? 1 : 0))
+ * Identifier for the LSM static calls.
+ * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
+ * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
+ */
+#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
+
+/*
+ * Call the macro M for each LSM hook MAX_LSM_COUNT times.
+ */
+#define LSM_LOOP_UNROLL(M, ...) \
+do { \
+ UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
+} while (0)
+
+#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
/*
* These are descriptions of the reasons that can be passed to the
@@ -92,7 +90,6 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
};
-struct security_hook_heads security_hook_heads __ro_after_init;
static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
static struct kmem_cache *lsm_file_cache;
@@ -108,9 +105,58 @@ static __initdata const char *chosen_major_lsm;
static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
/* Ordered list of LSMs to initialize. */
-static __initdata struct lsm_info **ordered_lsms;
+static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
static __initdata struct lsm_info *exclusive;
+#ifdef CONFIG_HAVE_STATIC_CALL
+#define LSM_HOOK_TRAMP(NAME, NUM) \
+ &STATIC_CALL_TRAMP(LSM_STATIC_CALL(NAME, NUM))
+#else
+#define LSM_HOOK_TRAMP(NAME, NUM) NULL
+#endif
+
+/*
+ * Define static calls and static keys for each LSM hook.
+ */
+#define DEFINE_LSM_STATIC_CALL(NUM, NAME, RET, ...) \
+ DEFINE_STATIC_CALL_NULL(LSM_STATIC_CALL(NAME, NUM), \
+ *((RET(*)(__VA_ARGS__))NULL)); \
+ DEFINE_STATIC_KEY_FALSE(SECURITY_HOOK_ACTIVE_KEY(NAME, NUM));
+
+#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+ LSM_DEFINE_UNROLL(DEFINE_LSM_STATIC_CALL, NAME, RET, __VA_ARGS__)
+#include <linux/lsm_hook_defs.h>
+#undef LSM_HOOK
+#undef DEFINE_LSM_STATIC_CALL
+
+/*
+ * Initialise a table of static calls for each LSM hook.
+ * DEFINE_STATIC_CALL_NULL invocation above generates a key (STATIC_CALL_KEY)
+ * and a trampoline (STATIC_CALL_TRAMP) which are used to call
+ * __static_call_update when updating the static call.
+ *
+ * The static calls table is used by early LSMs, some architectures can fault on
+ * unaligned accesses and the fault handling code may not be ready by then.
+ * Thus, the static calls table should be aligned to avoid any unhandled faults
+ * in early init.
+ */
+struct lsm_static_calls_table
+ static_calls_table __ro_after_init __aligned(sizeof(u64)) = {
+#define INIT_LSM_STATIC_CALL(NUM, NAME) \
+ (struct lsm_static_call) { \
+ .key = &STATIC_CALL_KEY(LSM_STATIC_CALL(NAME, NUM)), \
+ .trampoline = LSM_HOOK_TRAMP(NAME, NUM), \
+ .active = &SECURITY_HOOK_ACTIVE_KEY(NAME, NUM), \
+ },
+#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+ .NAME = { \
+ LSM_DEFINE_UNROLL(INIT_LSM_STATIC_CALL, NAME) \
+ },
+#include <linux/lsm_hook_defs.h>
+#undef LSM_HOOK
+#undef INIT_LSM_STATIC_CALL
+ };
+
static __initdata bool debug;
#define init_debug(...) \
do { \
@@ -171,7 +217,7 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
if (exists_ordered_lsm(lsm))
return;
- if (WARN(last_lsm == LSM_COUNT, "%s: out of LSM slots!?\n", from))
+ if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from))
return;
/* Enable this LSM, if it is not already set. */
@@ -218,6 +264,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
+ lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
/*
* The inode blob gets an rcu_head in addition to
* what the modules might need.
@@ -226,11 +273,16 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
blob_sizes.lbs_inode = sizeof(struct rcu_head);
lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
+ lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
+ lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
+ lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
+ lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
lsm_set_blob_size(&needed->lbs_xattr_count,
&blob_sizes.lbs_xattr_count);
+ lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
}
/* Prepare LSM for initialization. */
@@ -268,7 +320,7 @@ static void __init initialize_lsm(struct lsm_info *lsm)
* Current index to use while initializing the lsm id list.
*/
u32 lsm_active_cnt __ro_after_init;
-const struct lsm_id *lsm_idlist[LSM_CONFIG_COUNT];
+const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
/* Populate ordered LSMs list from comma-separated LSM name list. */
static void __init ordered_lsm_parse(const char *order, const char *origin)
@@ -350,6 +402,25 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
kfree(sep);
}
+static void __init lsm_static_call_init(struct security_hook_list *hl)
+{
+ struct lsm_static_call *scall = hl->scalls;
+ int i;
+
+ for (i = 0; i < MAX_LSM_COUNT; i++) {
+ /* Update the first static call that is not used yet */
+ if (!scall->hl) {
+ __static_call_update(scall->key, scall->trampoline,
+ hl->hook.lsm_func_addr);
+ scall->hl = hl;
+ static_branch_enable(scall->active);
+ return;
+ }
+ scall++;
+ }
+ panic("%s - Ran out of static slots.\n", __func__);
+}
+
static void __init lsm_early_cred(struct cred *cred);
static void __init lsm_early_task(struct task_struct *task);
@@ -378,9 +449,6 @@ static void __init ordered_lsm_init(void)
{
struct lsm_info **lsm;
- ordered_lsms = kcalloc(LSM_COUNT + 1, sizeof(*ordered_lsms),
- GFP_KERNEL);
-
if (chosen_lsm_order) {
if (chosen_major_lsm) {
pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
@@ -398,12 +466,20 @@ static void __init ordered_lsm_init(void)
init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
+ init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
+#ifdef CONFIG_KEYS
+ init_debug("key blob size = %d\n", blob_sizes.lbs_key);
+#endif /* CONFIG_KEYS */
init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
+ init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
+ init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
init_debug("task blob size = %d\n", blob_sizes.lbs_task);
+ init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
+ init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);
/*
* Create any kmem_caches needed for blobs
@@ -421,19 +497,12 @@ static void __init ordered_lsm_init(void)
lsm_early_task(current);
for (lsm = ordered_lsms; *lsm; lsm++)
initialize_lsm(*lsm);
-
- kfree(ordered_lsms);
}
int __init early_security_init(void)
{
struct lsm_info *lsm;
-#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
- INIT_HLIST_HEAD(&security_hook_heads.NAME);
-#include "linux/lsm_hook_defs.h"
-#undef LSM_HOOK
-
for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
if (!lsm->enabled)
lsm->enabled = &lsm_enabled_true;
@@ -554,14 +623,14 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
* Look at the previous entry, if there is one, for duplication.
*/
if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
- if (lsm_active_cnt >= LSM_CONFIG_COUNT)
+ if (lsm_active_cnt >= MAX_LSM_COUNT)
panic("%s Too many LSMs registered.\n", __func__);
lsm_idlist[lsm_active_cnt++] = lsmid;
}
for (i = 0; i < count; i++) {
hooks[i].lsmid = lsmid;
- hlist_add_tail_rcu(&hooks[i].list, hooks[i].head);
+ lsm_static_call_init(&hooks[i]);
}
/*
@@ -596,28 +665,43 @@ int unregister_blocking_lsm_notifier(struct notifier_block *nb)
EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
/**
- * lsm_cred_alloc - allocate a composite cred blob
- * @cred: the cred that needs a blob
+ * lsm_blob_alloc - allocate a composite blob
+ * @dest: the destination for the blob
+ * @size: the size of the blob
* @gfp: allocation type
*
- * Allocate the cred blob for all the modules
+ * Allocate a blob for all the modules
*
* Returns 0, or -ENOMEM if memory can't be allocated.
*/
-static int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
+static int lsm_blob_alloc(void **dest, size_t size, gfp_t gfp)
{
- if (blob_sizes.lbs_cred == 0) {
- cred->security = NULL;
+ if (size == 0) {
+ *dest = NULL;
return 0;
}
- cred->security = kzalloc(blob_sizes.lbs_cred, gfp);
- if (cred->security == NULL)
+ *dest = kzalloc(size, gfp);
+ if (*dest == NULL)
return -ENOMEM;
return 0;
}
/**
+ * lsm_cred_alloc - allocate a composite cred blob
+ * @cred: the cred that needs a blob
+ * @gfp: allocation type
+ *
+ * Allocate the cred blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
+{
+ return lsm_blob_alloc(&cred->security, blob_sizes.lbs_cred, gfp);
+}
+
+/**
* lsm_early_cred - during initialization allocate a composite cred blob
* @cred: the cred that needs a blob
*
@@ -655,19 +739,20 @@ static int lsm_file_alloc(struct file *file)
/**
* lsm_inode_alloc - allocate a composite inode blob
* @inode: the inode that needs a blob
+ * @gfp: allocation flags
*
* Allocate the inode blob for all the modules
*
* Returns 0, or -ENOMEM if memory can't be allocated.
*/
-int lsm_inode_alloc(struct inode *inode)
+static int lsm_inode_alloc(struct inode *inode, gfp_t gfp)
{
if (!lsm_inode_cache) {
inode->i_security = NULL;
return 0;
}
- inode->i_security = kmem_cache_zalloc(lsm_inode_cache, GFP_NOFS);
+ inode->i_security = kmem_cache_zalloc(lsm_inode_cache, gfp);
if (inode->i_security == NULL)
return -ENOMEM;
return 0;
@@ -683,15 +768,7 @@ int lsm_inode_alloc(struct inode *inode)
*/
static int lsm_task_alloc(struct task_struct *task)
{
- if (blob_sizes.lbs_task == 0) {
- task->security = NULL;
- return 0;
- }
-
- task->security = kzalloc(blob_sizes.lbs_task, GFP_KERNEL);
- if (task->security == NULL)
- return -ENOMEM;
- return 0;
+ return lsm_blob_alloc(&task->security, blob_sizes.lbs_task, GFP_KERNEL);
}
/**
@@ -704,16 +781,23 @@ static int lsm_task_alloc(struct task_struct *task)
*/
static int lsm_ipc_alloc(struct kern_ipc_perm *kip)
{
- if (blob_sizes.lbs_ipc == 0) {
- kip->security = NULL;
- return 0;
- }
+ return lsm_blob_alloc(&kip->security, blob_sizes.lbs_ipc, GFP_KERNEL);
+}
- kip->security = kzalloc(blob_sizes.lbs_ipc, GFP_KERNEL);
- if (kip->security == NULL)
- return -ENOMEM;
- return 0;
+#ifdef CONFIG_KEYS
+/**
+ * lsm_key_alloc - allocate a composite key blob
+ * @key: the key that needs a blob
+ *
+ * Allocate the key blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_key_alloc(struct key *key)
+{
+ return lsm_blob_alloc(&key->security, blob_sizes.lbs_key, GFP_KERNEL);
}
+#endif /* CONFIG_KEYS */
/**
* lsm_msg_msg_alloc - allocate a composite msg_msg blob
@@ -725,14 +809,29 @@ static int lsm_ipc_alloc(struct kern_ipc_perm *kip)
*/
static int lsm_msg_msg_alloc(struct msg_msg *mp)
{
- if (blob_sizes.lbs_msg_msg == 0) {
- mp->security = NULL;
+ return lsm_blob_alloc(&mp->security, blob_sizes.lbs_msg_msg,
+ GFP_KERNEL);
+}
+
+/**
+ * lsm_bdev_alloc - allocate a composite block_device blob
+ * @bdev: the block_device that needs a blob
+ *
+ * Allocate the block_device blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_bdev_alloc(struct block_device *bdev)
+{
+ if (blob_sizes.lbs_bdev == 0) {
+ bdev->bd_security = NULL;
return 0;
}
- mp->security = kzalloc(blob_sizes.lbs_msg_msg, GFP_KERNEL);
- if (mp->security == NULL)
+ bdev->bd_security = kzalloc(blob_sizes.lbs_bdev, GFP_KERNEL);
+ if (!bdev->bd_security)
return -ENOMEM;
+
return 0;
}
@@ -760,15 +859,8 @@ static void __init lsm_early_task(struct task_struct *task)
*/
static int lsm_superblock_alloc(struct super_block *sb)
{
- if (blob_sizes.lbs_superblock == 0) {
- sb->s_security = NULL;
- return 0;
- }
-
- sb->s_security = kzalloc(blob_sizes.lbs_superblock, GFP_KERNEL);
- if (sb->s_security == NULL)
- return -ENOMEM;
- return 0;
+ return lsm_blob_alloc(&sb->s_security, blob_sizes.lbs_superblock,
+ GFP_KERNEL);
}
/**
@@ -780,12 +872,14 @@ static int lsm_superblock_alloc(struct super_block *sb)
* @id: LSM id
* @flags: LSM defined flags
*
- * Fill all of the fields in a userspace lsm_ctx structure.
+ * Fill all of the fields in a userspace lsm_ctx structure. If @uctx is NULL
+ * simply calculate the required size to output via @utc_len and return
+ * success.
*
* Returns 0 on success, -E2BIG if userspace buffer is not large enough,
* -EFAULT on a copyout error, -ENOMEM if memory can't be allocated.
*/
-int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, size_t *uctx_len,
+int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, u32 *uctx_len,
void *val, size_t val_len,
u64 id, u64 flags)
{
@@ -799,6 +893,10 @@ int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, size_t *uctx_len,
goto out;
}
+ /* no buffer - return success/0 and set @uctx_len to the req size */
+ if (!uctx)
+ goto out;
+
nctx = kzalloc(nctx_len, GFP_KERNEL);
if (nctx == NULL) {
rc = -ENOMEM;
@@ -847,29 +945,43 @@ out:
* call_int_hook:
* This is a hook that returns a value.
*/
+#define __CALL_STATIC_VOID(NUM, HOOK, ...) \
+do { \
+ if (static_branch_unlikely(&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) { \
+ static_call(LSM_STATIC_CALL(HOOK, NUM))(__VA_ARGS__); \
+ } \
+} while (0);
-#define call_void_hook(FUNC, ...) \
- do { \
- struct security_hook_list *P; \
- \
- hlist_for_each_entry(P, &security_hook_heads.FUNC, list) \
- P->hook.FUNC(__VA_ARGS__); \
+#define call_void_hook(HOOK, ...) \
+ do { \
+ LSM_LOOP_UNROLL(__CALL_STATIC_VOID, HOOK, __VA_ARGS__); \
} while (0)
-#define call_int_hook(FUNC, IRC, ...) ({ \
- int RC = IRC; \
- do { \
- struct security_hook_list *P; \
- \
- hlist_for_each_entry(P, &security_hook_heads.FUNC, list) { \
- RC = P->hook.FUNC(__VA_ARGS__); \
- if (RC != 0) \
- break; \
- } \
- } while (0); \
- RC; \
+
+#define __CALL_STATIC_INT(NUM, R, HOOK, LABEL, ...) \
+do { \
+ if (static_branch_unlikely(&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) { \
+ R = static_call(LSM_STATIC_CALL(HOOK, NUM))(__VA_ARGS__); \
+ if (R != LSM_RET_DEFAULT(HOOK)) \
+ goto LABEL; \
+ } \
+} while (0);
+
+#define call_int_hook(HOOK, ...) \
+({ \
+ __label__ OUT; \
+ int RC = LSM_RET_DEFAULT(HOOK); \
+ \
+ LSM_LOOP_UNROLL(__CALL_STATIC_INT, RC, HOOK, OUT, __VA_ARGS__); \
+OUT: \
+ RC; \
})
+#define lsm_for_each_hook(scall, NAME) \
+ for (scall = static_calls_table.NAME; \
+ scall - static_calls_table.NAME < MAX_LSM_COUNT; scall++) \
+ if (static_key_enabled(&scall->active->key))
+
/* Security operations */
/**
@@ -882,7 +994,7 @@ out:
*/
int security_binder_set_context_mgr(const struct cred *mgr)
{
- return call_int_hook(binder_set_context_mgr, 0, mgr);
+ return call_int_hook(binder_set_context_mgr, mgr);
}
/**
@@ -897,7 +1009,7 @@ int security_binder_set_context_mgr(const struct cred *mgr)
int security_binder_transaction(const struct cred *from,
const struct cred *to)
{
- return call_int_hook(binder_transaction, 0, from, to);
+ return call_int_hook(binder_transaction, from, to);
}
/**
@@ -912,7 +1024,7 @@ int security_binder_transaction(const struct cred *from,
int security_binder_transfer_binder(const struct cred *from,
const struct cred *to)
{
- return call_int_hook(binder_transfer_binder, 0, from, to);
+ return call_int_hook(binder_transfer_binder, from, to);
}
/**
@@ -928,7 +1040,7 @@ int security_binder_transfer_binder(const struct cred *from,
int security_binder_transfer_file(const struct cred *from,
const struct cred *to, const struct file *file)
{
- return call_int_hook(binder_transfer_file, 0, from, to, file);
+ return call_int_hook(binder_transfer_file, from, to, file);
}
/**
@@ -947,7 +1059,7 @@ int security_binder_transfer_file(const struct cred *from,
*/
int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
{
- return call_int_hook(ptrace_access_check, 0, child, mode);
+ return call_int_hook(ptrace_access_check, child, mode);
}
/**
@@ -962,7 +1074,7 @@ int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
*/
int security_ptrace_traceme(struct task_struct *parent)
{
- return call_int_hook(ptrace_traceme, 0, parent);
+ return call_int_hook(ptrace_traceme, parent);
}
/**
@@ -984,8 +1096,7 @@ int security_capget(const struct task_struct *target,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
{
- return call_int_hook(capget, 0, target,
- effective, inheritable, permitted);
+ return call_int_hook(capget, target, effective, inheritable, permitted);
}
/**
@@ -1006,8 +1117,8 @@ int security_capset(struct cred *new, const struct cred *old,
const kernel_cap_t *inheritable,
const kernel_cap_t *permitted)
{
- return call_int_hook(capset, 0, new, old,
- effective, inheritable, permitted);
+ return call_int_hook(capset, new, old, effective, inheritable,
+ permitted);
}
/**
@@ -1028,7 +1139,7 @@ int security_capable(const struct cred *cred,
int cap,
unsigned int opts)
{
- return call_int_hook(capable, 0, cred, ns, cap, opts);
+ return call_int_hook(capable, cred, ns, cap, opts);
}
/**
@@ -1044,7 +1155,7 @@ int security_capable(const struct cred *cred,
*/
int security_quotactl(int cmds, int type, int id, const struct super_block *sb)
{
- return call_int_hook(quotactl, 0, cmds, type, id, sb);
+ return call_int_hook(quotactl, cmds, type, id, sb);
}
/**
@@ -1057,7 +1168,7 @@ int security_quotactl(int cmds, int type, int id, const struct super_block *sb)
*/
int security_quota_on(struct dentry *dentry)
{
- return call_int_hook(quota_on, 0, dentry);
+ return call_int_hook(quota_on, dentry);
}
/**
@@ -1072,7 +1183,7 @@ int security_quota_on(struct dentry *dentry)
*/
int security_syslog(int type)
{
- return call_int_hook(syslog, 0, type);
+ return call_int_hook(syslog, type);
}
/**
@@ -1087,7 +1198,7 @@ int security_syslog(int type)
*/
int security_settime64(const struct timespec64 *ts, const struct timezone *tz)
{
- return call_int_hook(settime, 0, ts, tz);
+ return call_int_hook(settime, ts, tz);
}
/**
@@ -1105,20 +1216,19 @@ int security_settime64(const struct timespec64 *ts, const struct timezone *tz)
*/
int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
{
- struct security_hook_list *hp;
+ struct lsm_static_call *scall;
int cap_sys_admin = 1;
int rc;
/*
- * The module will respond with a positive value if
- * it thinks the __vm_enough_memory() call should be
- * made with the cap_sys_admin set. If all of the modules
- * agree that it should be set it will. If any module
- * thinks it should not be set it won't.
+ * The module will respond with 0 if it thinks the __vm_enough_memory()
+ * call should be made with the cap_sys_admin set. If all of the modules
+ * agree that it should be set it will. If any module thinks it should
+ * not be set it won't.
*/
- hlist_for_each_entry(hp, &security_hook_heads.vm_enough_memory, list) {
- rc = hp->hook.vm_enough_memory(mm, pages);
- if (rc <= 0) {
+ lsm_for_each_hook(scall, vm_enough_memory) {
+ rc = scall->hl->hook.vm_enough_memory(mm, pages);
+ if (rc < 0) {
cap_sys_admin = 0;
break;
}
@@ -1138,11 +1248,17 @@ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
* to 1 if AT_SECURE should be set to request libc enable secure mode. @bprm
* contains the linux_binprm structure.
*
+ * If execveat(2) is called with the AT_EXECVE_CHECK flag, bprm->is_check is
+ * set. The result must be the same as without this flag even if the execution
+ * will never really happen and @bprm will always be dropped.
+ *
+ * This hook must not change current->cred, only @bprm->cred.
+ *
* Return: Returns 0 if the hook is successful and permission is granted.
*/
int security_bprm_creds_for_exec(struct linux_binprm *bprm)
{
- return call_int_hook(bprm_creds_for_exec, 0, bprm);
+ return call_int_hook(bprm_creds_for_exec, bprm);
}
/**
@@ -1166,7 +1282,7 @@ int security_bprm_creds_for_exec(struct linux_binprm *bprm)
*/
int security_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
{
- return call_int_hook(bprm_creds_from_file, 0, bprm, file);
+ return call_int_hook(bprm_creds_from_file, bprm, file);
}
/**
@@ -1183,12 +1299,7 @@ int security_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *
*/
int security_bprm_check(struct linux_binprm *bprm)
{
- int ret;
-
- ret = call_int_hook(bprm_check_security, 0, bprm);
- if (ret)
- return ret;
- return ima_bprm_check(bprm);
+ return call_int_hook(bprm_check_security, bprm);
}
/**
@@ -1235,7 +1346,7 @@ void security_bprm_committed_creds(const struct linux_binprm *bprm)
*/
int security_fs_context_submount(struct fs_context *fc, struct super_block *reference)
{
- return call_int_hook(fs_context_submount, 0, fc, reference);
+ return call_int_hook(fs_context_submount, fc, reference);
}
/**
@@ -1251,7 +1362,7 @@ int security_fs_context_submount(struct fs_context *fc, struct super_block *refe
*/
int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
{
- return call_int_hook(fs_context_dup, 0, fc, src_fc);
+ return call_int_hook(fs_context_dup, fc, src_fc);
}
/**
@@ -1269,13 +1380,12 @@ int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
int security_fs_context_parse_param(struct fs_context *fc,
struct fs_parameter *param)
{
- struct security_hook_list *hp;
+ struct lsm_static_call *scall;
int trc;
int rc = -ENOPARAM;
- hlist_for_each_entry(hp, &security_hook_heads.fs_context_parse_param,
- list) {
- trc = hp->hook.fs_context_parse_param(fc, param);
+ lsm_for_each_hook(scall, fs_context_parse_param) {
+ trc = scall->hl->hook.fs_context_parse_param(fc, param);
if (trc == 0)
rc = 0;
else if (trc != -ENOPARAM)
@@ -1300,7 +1410,7 @@ int security_sb_alloc(struct super_block *sb)
if (unlikely(rc))
return rc;
- rc = call_int_hook(sb_alloc_security, 0, sb);
+ rc = call_int_hook(sb_alloc_security, sb);
if (unlikely(rc))
security_sb_free(sb);
return rc;
@@ -1358,7 +1468,7 @@ EXPORT_SYMBOL(security_free_mnt_opts);
*/
int security_sb_eat_lsm_opts(char *options, void **mnt_opts)
{
- return call_int_hook(sb_eat_lsm_opts, 0, options, mnt_opts);
+ return call_int_hook(sb_eat_lsm_opts, options, mnt_opts);
}
EXPORT_SYMBOL(security_sb_eat_lsm_opts);
@@ -1375,7 +1485,7 @@ EXPORT_SYMBOL(security_sb_eat_lsm_opts);
int security_sb_mnt_opts_compat(struct super_block *sb,
void *mnt_opts)
{
- return call_int_hook(sb_mnt_opts_compat, 0, sb, mnt_opts);
+ return call_int_hook(sb_mnt_opts_compat, sb, mnt_opts);
}
EXPORT_SYMBOL(security_sb_mnt_opts_compat);
@@ -1392,7 +1502,7 @@ EXPORT_SYMBOL(security_sb_mnt_opts_compat);
int security_sb_remount(struct super_block *sb,
void *mnt_opts)
{
- return call_int_hook(sb_remount, 0, sb, mnt_opts);
+ return call_int_hook(sb_remount, sb, mnt_opts);
}
EXPORT_SYMBOL(security_sb_remount);
@@ -1406,7 +1516,7 @@ EXPORT_SYMBOL(security_sb_remount);
*/
int security_sb_kern_mount(const struct super_block *sb)
{
- return call_int_hook(sb_kern_mount, 0, sb);
+ return call_int_hook(sb_kern_mount, sb);
}
/**
@@ -1420,7 +1530,7 @@ int security_sb_kern_mount(const struct super_block *sb)
*/
int security_sb_show_options(struct seq_file *m, struct super_block *sb)
{
- return call_int_hook(sb_show_options, 0, m, sb);
+ return call_int_hook(sb_show_options, m, sb);
}
/**
@@ -1434,7 +1544,7 @@ int security_sb_show_options(struct seq_file *m, struct super_block *sb)
*/
int security_sb_statfs(struct dentry *dentry)
{
- return call_int_hook(sb_statfs, 0, dentry);
+ return call_int_hook(sb_statfs, dentry);
}
/**
@@ -1457,7 +1567,7 @@ int security_sb_statfs(struct dentry *dentry)
int security_sb_mount(const char *dev_name, const struct path *path,
const char *type, unsigned long flags, void *data)
{
- return call_int_hook(sb_mount, 0, dev_name, path, type, flags, data);
+ return call_int_hook(sb_mount, dev_name, path, type, flags, data);
}
/**
@@ -1471,7 +1581,7 @@ int security_sb_mount(const char *dev_name, const struct path *path,
*/
int security_sb_umount(struct vfsmount *mnt, int flags)
{
- return call_int_hook(sb_umount, 0, mnt, flags);
+ return call_int_hook(sb_umount, mnt, flags);
}
/**
@@ -1486,7 +1596,7 @@ int security_sb_umount(struct vfsmount *mnt, int flags)
int security_sb_pivotroot(const struct path *old_path,
const struct path *new_path)
{
- return call_int_hook(sb_pivotroot, 0, old_path, new_path);
+ return call_int_hook(sb_pivotroot, old_path, new_path);
}
/**
@@ -1505,9 +1615,16 @@ int security_sb_set_mnt_opts(struct super_block *sb,
unsigned long kern_flags,
unsigned long *set_kern_flags)
{
- return call_int_hook(sb_set_mnt_opts,
- mnt_opts ? -EOPNOTSUPP : 0, sb,
- mnt_opts, kern_flags, set_kern_flags);
+ struct lsm_static_call *scall;
+ int rc = mnt_opts ? -EOPNOTSUPP : LSM_RET_DEFAULT(sb_set_mnt_opts);
+
+ lsm_for_each_hook(scall, sb_set_mnt_opts) {
+ rc = scall->hl->hook.sb_set_mnt_opts(sb, mnt_opts, kern_flags,
+ set_kern_flags);
+ if (rc != LSM_RET_DEFAULT(sb_set_mnt_opts))
+ break;
+ }
+ return rc;
}
EXPORT_SYMBOL(security_sb_set_mnt_opts);
@@ -1527,7 +1644,7 @@ int security_sb_clone_mnt_opts(const struct super_block *oldsb,
unsigned long kern_flags,
unsigned long *set_kern_flags)
{
- return call_int_hook(sb_clone_mnt_opts, 0, oldsb, newsb,
+ return call_int_hook(sb_clone_mnt_opts, oldsb, newsb,
kern_flags, set_kern_flags);
}
EXPORT_SYMBOL(security_sb_clone_mnt_opts);
@@ -1544,7 +1661,7 @@ EXPORT_SYMBOL(security_sb_clone_mnt_opts);
int security_move_mount(const struct path *from_path,
const struct path *to_path)
{
- return call_int_hook(move_mount, 0, from_path, to_path);
+ return call_int_hook(move_mount, from_path, to_path);
}
/**
@@ -1561,12 +1678,13 @@ int security_move_mount(const struct path *from_path,
int security_path_notify(const struct path *path, u64 mask,
unsigned int obj_type)
{
- return call_int_hook(path_notify, 0, path, mask, obj_type);
+ return call_int_hook(path_notify, path, mask, obj_type);
}
/**
* security_inode_alloc() - Allocate an inode LSM blob
* @inode: the inode
+ * @gfp: allocation flags
*
* Allocate and attach a security structure to @inode->i_security. The
* i_security field is initialized to NULL when the inode structure is
@@ -1574,13 +1692,13 @@ int security_path_notify(const struct path *path, u64 mask,
*
* Return: Return 0 if operation was successful.
*/
-int security_inode_alloc(struct inode *inode)
+int security_inode_alloc(struct inode *inode, gfp_t gfp)
{
- int rc = lsm_inode_alloc(inode);
+ int rc = lsm_inode_alloc(inode, gfp);
if (unlikely(rc))
return rc;
- rc = call_int_hook(inode_alloc_security, 0, inode);
+ rc = call_int_hook(inode_alloc_security, inode);
if (unlikely(rc))
security_inode_free(inode);
return rc;
@@ -1588,9 +1706,8 @@ int security_inode_alloc(struct inode *inode)
static void inode_free_by_rcu(struct rcu_head *head)
{
- /*
- * The rcu head is at the start of the inode blob
- */
+ /* The rcu head is at the start of the inode blob */
+ call_void_hook(inode_free_security_rcu, head);
kmem_cache_free(lsm_inode_cache, head);
}
@@ -1598,24 +1715,24 @@ static void inode_free_by_rcu(struct rcu_head *head)
* security_inode_free() - Free an inode's LSM blob
* @inode: the inode
*
- * Deallocate the inode security structure and set @inode->i_security to NULL.
+ * Release any LSM resources associated with @inode, although due to the
+ * inode's RCU protections it is possible that the resources will not be
+ * fully released until after the current RCU grace period has elapsed.
+ *
+ * It is important for LSMs to note that despite being present in a call to
+ * security_inode_free(), @inode may still be referenced in a VFS path walk
+ * and calls to security_inode_permission() may be made during, or after,
+ * a call to security_inode_free(). For this reason the inode->i_security
+ * field is released via a call_rcu() callback and any LSMs which need to
+ * retain inode state for use in security_inode_permission() should only
+ * release that state in the inode_free_security_rcu() LSM hook callback.
*/
void security_inode_free(struct inode *inode)
{
- integrity_inode_free(inode);
call_void_hook(inode_free_security, inode);
- /*
- * The inode may still be referenced in a path walk and
- * a call to security_inode_permission() can be made
- * after inode_free_security() is called. Ideally, the VFS
- * wouldn't do this, but fixing that is a much harder
- * job. For now, simply free the i_security via RCU, and
- * leave the current inode->i_security pointer intact.
- * The inode will be freed after the RCU grace period too.
- */
- if (inode->i_security)
- call_rcu((struct rcu_head *)inode->i_security,
- inode_free_by_rcu);
+ if (!inode->i_security)
+ return;
+ call_rcu((struct rcu_head *)inode->i_security, inode_free_by_rcu);
}
/**
@@ -1624,8 +1741,7 @@ void security_inode_free(struct inode *inode)
* @mode: mode used to determine resource type
* @name: name of the last path component
* @xattr_name: name of the security/LSM xattr
- * @ctx: pointer to the resulting LSM context
- * @ctxlen: length of @ctx
+ * @lsmctx: pointer to the resulting LSM context
*
* Compute a context for a dentry as the inode is not yet available since NFSv4
* has no label backed by an EA anyway. It is important to note that
@@ -1635,23 +1751,11 @@ void security_inode_free(struct inode *inode)
*/
int security_dentry_init_security(struct dentry *dentry, int mode,
const struct qstr *name,
- const char **xattr_name, void **ctx,
- u32 *ctxlen)
+ const char **xattr_name,
+ struct lsm_context *lsmctx)
{
- struct security_hook_list *hp;
- int rc;
-
- /*
- * Only one module will provide a security context.
- */
- hlist_for_each_entry(hp, &security_hook_heads.dentry_init_security,
- list) {
- rc = hp->hook.dentry_init_security(dentry, mode, name,
- xattr_name, ctx, ctxlen);
- if (rc != LSM_RET_DEFAULT(dentry_init_security))
- return rc;
- }
- return LSM_RET_DEFAULT(dentry_init_security);
+ return call_int_hook(dentry_init_security, dentry, mode, name,
+ xattr_name, lsmctx);
}
EXPORT_SYMBOL(security_dentry_init_security);
@@ -1674,7 +1778,7 @@ int security_dentry_create_files_as(struct dentry *dentry, int mode,
struct qstr *name,
const struct cred *old, struct cred *new)
{
- return call_int_hook(dentry_create_files_as, 0, dentry, mode,
+ return call_int_hook(dentry_create_files_as, dentry, mode,
name, old, new);
}
EXPORT_SYMBOL(security_dentry_create_files_as);
@@ -1710,7 +1814,7 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
const struct qstr *qstr,
const initxattrs initxattrs, void *fs_data)
{
- struct security_hook_list *hp;
+ struct lsm_static_call *scall;
struct xattr *new_xattrs = NULL;
int ret = -EOPNOTSUPP, xattr_count = 0;
@@ -1721,16 +1825,15 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
return 0;
if (initxattrs) {
- /* Allocate +1 for EVM and +1 as terminator. */
- new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 2,
+ /* Allocate +1 as terminator. */
+ new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1,
sizeof(*new_xattrs), GFP_NOFS);
if (!new_xattrs)
return -ENOMEM;
}
- hlist_for_each_entry(hp, &security_hook_heads.inode_init_security,
- list) {
- ret = hp->hook.inode_init_security(inode, dir, qstr, new_xattrs,
+ lsm_for_each_hook(scall, inode_init_security) {
+ ret = scall->hl->hook.inode_init_security(inode, dir, qstr, new_xattrs,
&xattr_count);
if (ret && ret != -EOPNOTSUPP)
goto out;
@@ -1746,10 +1849,6 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
if (!xattr_count)
goto out;
- ret = evm_inode_init_security(inode, dir, qstr, new_xattrs,
- &xattr_count);
- if (ret)
- goto out;
ret = initxattrs(inode, new_xattrs, fs_data);
out:
for (; xattr_count > 0; xattr_count--)
@@ -1775,7 +1874,7 @@ int security_inode_init_security_anon(struct inode *inode,
const struct qstr *name,
const struct inode *context_inode)
{
- return call_int_hook(inode_init_security_anon, 0, inode, name,
+ return call_int_hook(inode_init_security_anon, inode, name,
context_inode);
}
@@ -1797,11 +1896,25 @@ int security_path_mknod(const struct path *dir, struct dentry *dentry,
{
if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
return 0;
- return call_int_hook(path_mknod, 0, dir, dentry, mode, dev);
+ return call_int_hook(path_mknod, dir, dentry, mode, dev);
}
EXPORT_SYMBOL(security_path_mknod);
/**
+ * security_path_post_mknod() - Update inode security after reg file creation
+ * @idmap: idmap of the mount
+ * @dentry: new file
+ *
+ * Update inode security field after a regular file has been created.
+ */
+void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
+{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+ call_void_hook(path_post_mknod, idmap, dentry);
+}
+
+/**
* security_path_mkdir() - Check if creating a new directory is allowed
* @dir: parent directory
* @dentry: new directory
@@ -1816,7 +1929,7 @@ int security_path_mkdir(const struct path *dir, struct dentry *dentry,
{
if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
return 0;
- return call_int_hook(path_mkdir, 0, dir, dentry, mode);
+ return call_int_hook(path_mkdir, dir, dentry, mode);
}
EXPORT_SYMBOL(security_path_mkdir);
@@ -1833,7 +1946,7 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
return 0;
- return call_int_hook(path_rmdir, 0, dir, dentry);
+ return call_int_hook(path_rmdir, dir, dentry);
}
/**
@@ -1849,7 +1962,7 @@ int security_path_unlink(const struct path *dir, struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
return 0;
- return call_int_hook(path_unlink, 0, dir, dentry);
+ return call_int_hook(path_unlink, dir, dentry);
}
EXPORT_SYMBOL(security_path_unlink);
@@ -1868,7 +1981,7 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry,
{
if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
return 0;
- return call_int_hook(path_symlink, 0, dir, dentry, old_name);
+ return call_int_hook(path_symlink, dir, dentry, old_name);
}
/**
@@ -1886,7 +1999,7 @@ int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
{
if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
return 0;
- return call_int_hook(path_link, 0, old_dentry, new_dir, new_dentry);
+ return call_int_hook(path_link, old_dentry, new_dir, new_dentry);
}
/**
@@ -1910,7 +2023,7 @@ int security_path_rename(const struct path *old_dir, struct dentry *old_dentry,
IS_PRIVATE(d_backing_inode(new_dentry)))))
return 0;
- return call_int_hook(path_rename, 0, old_dir, old_dentry, new_dir,
+ return call_int_hook(path_rename, old_dir, old_dentry, new_dir,
new_dentry, flags);
}
EXPORT_SYMBOL(security_path_rename);
@@ -1929,7 +2042,7 @@ int security_path_truncate(const struct path *path)
{
if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
return 0;
- return call_int_hook(path_truncate, 0, path);
+ return call_int_hook(path_truncate, path);
}
/**
@@ -1947,7 +2060,7 @@ int security_path_chmod(const struct path *path, umode_t mode)
{
if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
return 0;
- return call_int_hook(path_chmod, 0, path, mode);
+ return call_int_hook(path_chmod, path, mode);
}
/**
@@ -1964,7 +2077,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
{
if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
return 0;
- return call_int_hook(path_chown, 0, path, uid, gid);
+ return call_int_hook(path_chown, path, uid, gid);
}
/**
@@ -1977,7 +2090,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
*/
int security_path_chroot(const struct path *path)
{
- return call_int_hook(path_chroot, 0, path);
+ return call_int_hook(path_chroot, path);
}
#endif /* CONFIG_SECURITY_PATH */
@@ -1996,11 +2109,26 @@ int security_inode_create(struct inode *dir, struct dentry *dentry,
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return call_int_hook(inode_create, 0, dir, dentry, mode);
+ return call_int_hook(inode_create, dir, dentry, mode);
}
EXPORT_SYMBOL_GPL(security_inode_create);
/**
+ * security_inode_post_create_tmpfile() - Update inode security of new tmpfile
+ * @idmap: idmap of the mount
+ * @inode: inode of the new tmpfile
+ *
+ * Update inode security data after a tmpfile has been created.
+ */
+void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
+ struct inode *inode)
+{
+ if (unlikely(IS_PRIVATE(inode)))
+ return;
+ call_void_hook(inode_post_create_tmpfile, idmap, inode);
+}
+
+/**
* security_inode_link() - Check if creating a hard link is allowed
* @old_dentry: existing file
* @dir: new parent directory
@@ -2015,7 +2143,7 @@ int security_inode_link(struct dentry *old_dentry, struct inode *dir,
{
if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
return 0;
- return call_int_hook(inode_link, 0, old_dentry, dir, new_dentry);
+ return call_int_hook(inode_link, old_dentry, dir, new_dentry);
}
/**
@@ -2031,7 +2159,7 @@ int security_inode_unlink(struct inode *dir, struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- return call_int_hook(inode_unlink, 0, dir, dentry);
+ return call_int_hook(inode_unlink, dir, dentry);
}
/**
@@ -2049,7 +2177,7 @@ int security_inode_symlink(struct inode *dir, struct dentry *dentry,
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return call_int_hook(inode_symlink, 0, dir, dentry, old_name);
+ return call_int_hook(inode_symlink, dir, dentry, old_name);
}
/**
@@ -2067,7 +2195,7 @@ int security_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return call_int_hook(inode_mkdir, 0, dir, dentry, mode);
+ return call_int_hook(inode_mkdir, dir, dentry, mode);
}
EXPORT_SYMBOL_GPL(security_inode_mkdir);
@@ -2084,7 +2212,7 @@ int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- return call_int_hook(inode_rmdir, 0, dir, dentry);
+ return call_int_hook(inode_rmdir, dir, dentry);
}
/**
@@ -2106,7 +2234,7 @@ int security_inode_mknod(struct inode *dir, struct dentry *dentry,
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return call_int_hook(inode_mknod, 0, dir, dentry, mode, dev);
+ return call_int_hook(inode_mknod, dir, dentry, mode, dev);
}
/**
@@ -2131,13 +2259,13 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
return 0;
if (flags & RENAME_EXCHANGE) {
- int err = call_int_hook(inode_rename, 0, new_dir, new_dentry,
+ int err = call_int_hook(inode_rename, new_dir, new_dentry,
old_dir, old_dentry);
if (err)
return err;
}
- return call_int_hook(inode_rename, 0, old_dir, old_dentry,
+ return call_int_hook(inode_rename, old_dir, old_dentry,
new_dir, new_dentry);
}
@@ -2153,7 +2281,7 @@ int security_inode_readlink(struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- return call_int_hook(inode_readlink, 0, dentry);
+ return call_int_hook(inode_readlink, dentry);
}
/**
@@ -2172,7 +2300,7 @@ int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
{
if (unlikely(IS_PRIVATE(inode)))
return 0;
- return call_int_hook(inode_follow_link, 0, dentry, inode, rcu);
+ return call_int_hook(inode_follow_link, dentry, inode, rcu);
}
/**
@@ -2193,7 +2321,7 @@ int security_inode_permission(struct inode *inode, int mask)
{
if (unlikely(IS_PRIVATE(inode)))
return 0;
- return call_int_hook(inode_permission, 0, inode, mask);
+ return call_int_hook(inode_permission, inode, mask);
}
/**
@@ -2212,18 +2340,29 @@ int security_inode_permission(struct inode *inode, int mask)
int security_inode_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *attr)
{
- int ret;
-
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- ret = call_int_hook(inode_setattr, 0, dentry, attr);
- if (ret)
- return ret;
- return evm_inode_setattr(idmap, dentry, attr);
+ return call_int_hook(inode_setattr, idmap, dentry, attr);
}
EXPORT_SYMBOL_GPL(security_inode_setattr);
/**
+ * security_inode_post_setattr() - Update the inode after a setattr operation
+ * @idmap: idmap of the mount
+ * @dentry: file
+ * @ia_valid: file attributes set
+ *
+ * Update inode security field after successful setting file attributes.
+ */
+void security_inode_post_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ int ia_valid)
+{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+ call_void_hook(inode_post_setattr, idmap, dentry, ia_valid);
+}
+
+/**
* security_inode_getattr() - Check if getting file attributes is allowed
* @path: file
*
@@ -2235,7 +2374,7 @@ int security_inode_getattr(const struct path *path)
{
if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
return 0;
- return call_int_hook(inode_getattr, 0, path);
+ return call_int_hook(inode_getattr, path);
}
/**
@@ -2247,7 +2386,20 @@ int security_inode_getattr(const struct path *path)
* @size: size of xattr value
* @flags: flags
*
- * Check permission before setting the extended attributes.
+ * This hook performs the desired permission checks before setting the extended
+ * attributes (xattrs) on @dentry. It is important to note that we have some
+ * additional logic before the main LSM implementation calls to detect if we
+ * need to perform an additional capability check at the LSM layer.
+ *
+ * Normally we enforce a capability check prior to executing the various LSM
+ * hook implementations, but if a LSM wants to avoid this capability check,
+ * it can register a 'inode_xattr_skipcap' hook and return a value of 1 for
+ * xattrs that it wants to avoid the capability check, leaving the LSM fully
+ * responsible for enforcing the access control for the specific xattr. If all
+ * of the enabled LSMs refrain from registering a 'inode_xattr_skipcap' hook,
+ * or return a 0 (the default return value), the capability check is still
+ * performed. If no 'inode_xattr_skipcap' hooks are registered the capability
+ * check is performed.
*
* Return: Returns 0 if permission is granted.
*/
@@ -2255,25 +2407,20 @@ int security_inode_setxattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
- int ret;
+ int rc;
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- /*
- * SELinux and Smack integrate the cap call,
- * so assume that all LSMs supplying this call do so.
- */
- ret = call_int_hook(inode_setxattr, 1, idmap, dentry, name, value,
- size, flags);
- if (ret == 1)
- ret = cap_inode_setxattr(dentry, name, value, size, flags);
- if (ret)
- return ret;
- ret = ima_inode_setxattr(dentry, name, value, size);
- if (ret)
- return ret;
- return evm_inode_setxattr(idmap, dentry, name, value, size);
+ /* enforce the capability checks at the lsm layer, if needed */
+ if (!call_int_hook(inode_xattr_skipcap, name)) {
+ rc = cap_inode_setxattr(dentry, name, value, size, flags);
+ if (rc)
+ return rc;
+ }
+
+ return call_int_hook(inode_setxattr, idmap, dentry, name, value, size,
+ flags);
}
/**
@@ -2292,18 +2439,26 @@ int security_inode_set_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name,
struct posix_acl *kacl)
{
- int ret;
-
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- ret = call_int_hook(inode_set_acl, 0, idmap, dentry, acl_name,
- kacl);
- if (ret)
- return ret;
- ret = ima_inode_set_acl(idmap, dentry, acl_name, kacl);
- if (ret)
- return ret;
- return evm_inode_set_acl(idmap, dentry, acl_name, kacl);
+ return call_int_hook(inode_set_acl, idmap, dentry, acl_name, kacl);
+}
+
+/**
+ * security_inode_post_set_acl() - Update inode security from posix acls set
+ * @dentry: file
+ * @acl_name: acl name
+ * @kacl: acl struct
+ *
+ * Update inode security data after successfully setting posix acls on @dentry.
+ * The posix acls in @kacl are identified by @acl_name.
+ */
+void security_inode_post_set_acl(struct dentry *dentry, const char *acl_name,
+ struct posix_acl *kacl)
+{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+ call_void_hook(inode_post_set_acl, dentry, acl_name, kacl);
}
/**
@@ -2322,7 +2477,7 @@ int security_inode_get_acl(struct mnt_idmap *idmap,
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- return call_int_hook(inode_get_acl, 0, idmap, dentry, acl_name);
+ return call_int_hook(inode_get_acl, idmap, dentry, acl_name);
}
/**
@@ -2339,17 +2494,26 @@ int security_inode_get_acl(struct mnt_idmap *idmap,
int security_inode_remove_acl(struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
{
- int ret;
-
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- ret = call_int_hook(inode_remove_acl, 0, idmap, dentry, acl_name);
- if (ret)
- return ret;
- ret = ima_inode_remove_acl(idmap, dentry, acl_name);
- if (ret)
- return ret;
- return evm_inode_remove_acl(idmap, dentry, acl_name);
+ return call_int_hook(inode_remove_acl, idmap, dentry, acl_name);
+}
+
+/**
+ * security_inode_post_remove_acl() - Update inode security after rm posix acls
+ * @idmap: idmap of the mount
+ * @dentry: file
+ * @acl_name: acl name
+ *
+ * Update inode security data after successfully removing posix acls on
+ * @dentry in @idmap. The posix acls are identified by @acl_name.
+ */
+void security_inode_post_remove_acl(struct mnt_idmap *idmap,
+ struct dentry *dentry, const char *acl_name)
+{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+ call_void_hook(inode_post_remove_acl, idmap, dentry, acl_name);
}
/**
@@ -2368,7 +2532,6 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return;
call_void_hook(inode_post_setxattr, dentry, name, value, size, flags);
- evm_inode_post_setxattr(dentry, name, value, size);
}
/**
@@ -2385,7 +2548,7 @@ int security_inode_getxattr(struct dentry *dentry, const char *name)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- return call_int_hook(inode_getxattr, 0, dentry, name);
+ return call_int_hook(inode_getxattr, dentry, name);
}
/**
@@ -2401,7 +2564,7 @@ int security_inode_listxattr(struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- return call_int_hook(inode_listxattr, 0, dentry);
+ return call_int_hook(inode_listxattr, dentry);
}
/**
@@ -2410,31 +2573,53 @@ int security_inode_listxattr(struct dentry *dentry)
* @dentry: file
* @name: xattr name
*
- * Check permission before removing the extended attribute identified by @name
- * for @dentry.
+ * This hook performs the desired permission checks before setting the extended
+ * attributes (xattrs) on @dentry. It is important to note that we have some
+ * additional logic before the main LSM implementation calls to detect if we
+ * need to perform an additional capability check at the LSM layer.
+ *
+ * Normally we enforce a capability check prior to executing the various LSM
+ * hook implementations, but if a LSM wants to avoid this capability check,
+ * it can register a 'inode_xattr_skipcap' hook and return a value of 1 for
+ * xattrs that it wants to avoid the capability check, leaving the LSM fully
+ * responsible for enforcing the access control for the specific xattr. If all
+ * of the enabled LSMs refrain from registering a 'inode_xattr_skipcap' hook,
+ * or return a 0 (the default return value), the capability check is still
+ * performed. If no 'inode_xattr_skipcap' hooks are registered the capability
+ * check is performed.
*
* Return: Returns 0 if permission is granted.
*/
int security_inode_removexattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name)
{
- int ret;
+ int rc;
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- /*
- * SELinux and Smack integrate the cap call,
- * so assume that all LSMs supplying this call do so.
- */
- ret = call_int_hook(inode_removexattr, 1, idmap, dentry, name);
- if (ret == 1)
- ret = cap_inode_removexattr(idmap, dentry, name);
- if (ret)
- return ret;
- ret = ima_inode_removexattr(dentry, name);
- if (ret)
- return ret;
- return evm_inode_removexattr(idmap, dentry, name);
+
+ /* enforce the capability checks at the lsm layer, if needed */
+ if (!call_int_hook(inode_xattr_skipcap, name)) {
+ rc = cap_inode_removexattr(idmap, dentry, name);
+ if (rc)
+ return rc;
+ }
+
+ return call_int_hook(inode_removexattr, idmap, dentry, name);
+}
+
+/**
+ * security_inode_post_removexattr() - Update the inode after a removexattr op
+ * @dentry: file
+ * @name: xattr name
+ *
+ * Update the inode after a successful removexattr operation.
+ */
+void security_inode_post_removexattr(struct dentry *dentry, const char *name)
+{
+ if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ return;
+ call_void_hook(inode_post_removexattr, dentry, name);
}
/**
@@ -2450,7 +2635,7 @@ int security_inode_removexattr(struct mnt_idmap *idmap,
*/
int security_inode_need_killpriv(struct dentry *dentry)
{
- return call_int_hook(inode_need_killpriv, 0, dentry);
+ return call_int_hook(inode_need_killpriv, dentry);
}
/**
@@ -2467,7 +2652,7 @@ int security_inode_need_killpriv(struct dentry *dentry)
int security_inode_killpriv(struct mnt_idmap *idmap,
struct dentry *dentry)
{
- return call_int_hook(inode_killpriv, 0, idmap, dentry);
+ return call_int_hook(inode_killpriv, idmap, dentry);
}
/**
@@ -2490,21 +2675,11 @@ int security_inode_getsecurity(struct mnt_idmap *idmap,
struct inode *inode, const char *name,
void **buffer, bool alloc)
{
- struct security_hook_list *hp;
- int rc;
-
if (unlikely(IS_PRIVATE(inode)))
return LSM_RET_DEFAULT(inode_getsecurity);
- /*
- * Only one module will provide an attribute with a given name.
- */
- hlist_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) {
- rc = hp->hook.inode_getsecurity(idmap, inode, name, buffer,
- alloc);
- if (rc != LSM_RET_DEFAULT(inode_getsecurity))
- return rc;
- }
- return LSM_RET_DEFAULT(inode_getsecurity);
+
+ return call_int_hook(inode_getsecurity, idmap, inode, name, buffer,
+ alloc);
}
/**
@@ -2525,21 +2700,11 @@ int security_inode_getsecurity(struct mnt_idmap *idmap,
int security_inode_setsecurity(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
- struct security_hook_list *hp;
- int rc;
-
if (unlikely(IS_PRIVATE(inode)))
return LSM_RET_DEFAULT(inode_setsecurity);
- /*
- * Only one module will provide an attribute with a given name.
- */
- hlist_for_each_entry(hp, &security_hook_heads.inode_setsecurity, list) {
- rc = hp->hook.inode_setsecurity(inode, name, value, size,
- flags);
- if (rc != LSM_RET_DEFAULT(inode_setsecurity))
- return rc;
- }
- return LSM_RET_DEFAULT(inode_setsecurity);
+
+ return call_int_hook(inode_setsecurity, inode, name, value, size,
+ flags);
}
/**
@@ -2560,21 +2725,20 @@ int security_inode_listsecurity(struct inode *inode,
{
if (unlikely(IS_PRIVATE(inode)))
return 0;
- return call_int_hook(inode_listsecurity, 0, inode, buffer, buffer_size);
+ return call_int_hook(inode_listsecurity, inode, buffer, buffer_size);
}
EXPORT_SYMBOL(security_inode_listsecurity);
/**
- * security_inode_getsecid() - Get an inode's secid
+ * security_inode_getlsmprop() - Get an inode's LSM data
* @inode: inode
- * @secid: secid to return
+ * @prop: lsm specific information to return
*
- * Get the secid associated with the node. In case of failure, @secid will be
- * set to zero.
+ * Get the lsm specific information associated with the node.
*/
-void security_inode_getsecid(struct inode *inode, u32 *secid)
+void security_inode_getlsmprop(struct inode *inode, struct lsm_prop *prop)
{
- call_void_hook(inode_getsecid, inode, secid);
+ call_void_hook(inode_getlsmprop, inode, prop);
}
/**
@@ -2591,44 +2755,56 @@ void security_inode_getsecid(struct inode *inode, u32 *secid)
*/
int security_inode_copy_up(struct dentry *src, struct cred **new)
{
- return call_int_hook(inode_copy_up, 0, src, new);
+ return call_int_hook(inode_copy_up, src, new);
}
EXPORT_SYMBOL(security_inode_copy_up);
/**
* security_inode_copy_up_xattr() - Filter xattrs in an overlayfs copy-up op
+ * @src: union dentry of copy-up file
* @name: xattr name
*
* Filter the xattrs being copied up when a unioned file is copied up from a
* lower layer to the union/overlay layer. The caller is responsible for
* reading and writing the xattrs, this hook is merely a filter.
*
- * Return: Returns 0 to accept the xattr, 1 to discard the xattr, -EOPNOTSUPP
- * if the security module does not know about attribute, or a negative
- * error code to abort the copy up.
+ * Return: Returns 0 to accept the xattr, -ECANCELED to discard the xattr,
+ * -EOPNOTSUPP if the security module does not know about attribute,
+ * or a negative error code to abort the copy up.
*/
-int security_inode_copy_up_xattr(const char *name)
+int security_inode_copy_up_xattr(struct dentry *src, const char *name)
{
- struct security_hook_list *hp;
int rc;
- /*
- * The implementation can return 0 (accept the xattr), 1 (discard the
- * xattr), -EOPNOTSUPP if it does not know anything about the xattr or
- * any other error code in case of an error.
- */
- hlist_for_each_entry(hp,
- &security_hook_heads.inode_copy_up_xattr, list) {
- rc = hp->hook.inode_copy_up_xattr(name);
- if (rc != LSM_RET_DEFAULT(inode_copy_up_xattr))
- return rc;
- }
+ rc = call_int_hook(inode_copy_up_xattr, src, name);
+ if (rc != LSM_RET_DEFAULT(inode_copy_up_xattr))
+ return rc;
- return evm_inode_copy_up_xattr(name);
+ return LSM_RET_DEFAULT(inode_copy_up_xattr);
}
EXPORT_SYMBOL(security_inode_copy_up_xattr);
/**
+ * security_inode_setintegrity() - Set the inode's integrity data
+ * @inode: inode
+ * @type: type of integrity, e.g. hash digest, signature, etc
+ * @value: the integrity value
+ * @size: size of the integrity value
+ *
+ * Register a verified integrity measurement of a inode with LSMs.
+ * LSMs should free the previously saved data if @value is NULL.
+ *
+ * Return: Returns 0 on success, negative values on failure.
+ */
+int security_inode_setintegrity(const struct inode *inode,
+ enum lsm_integrity_type type, const void *value,
+ size_t size)
+{
+ return call_int_hook(inode_setintegrity, inode, type, value, size);
+}
+EXPORT_SYMBOL(security_inode_setintegrity);
+
+/**
* security_kernfs_init_security() - Init LSM context for a kernfs node
* @kn_dir: parent kernfs node
* @kn: the kernfs node to initialize
@@ -2641,7 +2817,7 @@ EXPORT_SYMBOL(security_inode_copy_up_xattr);
int security_kernfs_init_security(struct kernfs_node *kn_dir,
struct kernfs_node *kn)
{
- return call_int_hook(kernfs_init_security, 0, kn_dir, kn);
+ return call_int_hook(kernfs_init_security, kn_dir, kn);
}
/**
@@ -2665,7 +2841,7 @@ int security_kernfs_init_security(struct kernfs_node *kn_dir,
*/
int security_file_permission(struct file *file, int mask)
{
- return call_int_hook(file_permission, 0, file, mask);
+ return call_int_hook(file_permission, file, mask);
}
/**
@@ -2683,13 +2859,24 @@ int security_file_alloc(struct file *file)
if (rc)
return rc;
- rc = call_int_hook(file_alloc_security, 0, file);
+ rc = call_int_hook(file_alloc_security, file);
if (unlikely(rc))
security_file_free(file);
return rc;
}
/**
+ * security_file_release() - Perform actions before releasing the file ref
+ * @file: the file
+ *
+ * Perform actions before releasing the last reference to a file.
+ */
+void security_file_release(struct file *file)
+{
+ call_void_hook(file_release, file);
+}
+
+/**
* security_file_free() - Free a file's LSM blob
* @file: the file
*
@@ -2723,7 +2910,7 @@ void security_file_free(struct file *file)
*/
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- return call_int_hook(file_ioctl, 0, file, cmd, arg);
+ return call_int_hook(file_ioctl, file, cmd, arg);
}
EXPORT_SYMBOL_GPL(security_file_ioctl);
@@ -2741,7 +2928,7 @@ EXPORT_SYMBOL_GPL(security_file_ioctl);
int security_file_ioctl_compat(struct file *file, unsigned int cmd,
unsigned long arg)
{
- return call_int_hook(file_ioctl_compat, 0, file, cmd, arg);
+ return call_int_hook(file_ioctl_compat, file, cmd, arg);
}
EXPORT_SYMBOL_GPL(security_file_ioctl_compat);
@@ -2792,13 +2979,8 @@ static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
int security_mmap_file(struct file *file, unsigned long prot,
unsigned long flags)
{
- unsigned long prot_adj = mmap_prot(file, prot);
- int ret;
-
- ret = call_int_hook(mmap_file, 0, file, prot, prot_adj, flags);
- if (ret)
- return ret;
- return ima_file_mmap(file, prot, prot_adj, flags);
+ return call_int_hook(mmap_file, file, prot, mmap_prot(file, prot),
+ flags);
}
/**
@@ -2811,7 +2993,7 @@ int security_mmap_file(struct file *file, unsigned long prot,
*/
int security_mmap_addr(unsigned long addr)
{
- return call_int_hook(mmap_addr, 0, addr);
+ return call_int_hook(mmap_addr, addr);
}
/**
@@ -2827,12 +3009,7 @@ int security_mmap_addr(unsigned long addr)
int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot)
{
- int ret;
-
- ret = call_int_hook(file_mprotect, 0, vma, reqprot, prot);
- if (ret)
- return ret;
- return ima_file_mprotect(vma, prot);
+ return call_int_hook(file_mprotect, vma, reqprot, prot);
}
/**
@@ -2847,7 +3024,7 @@ int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
*/
int security_file_lock(struct file *file, unsigned int cmd)
{
- return call_int_hook(file_lock, 0, file, cmd);
+ return call_int_hook(file_lock, file, cmd);
}
/**
@@ -2866,7 +3043,7 @@ int security_file_lock(struct file *file, unsigned int cmd)
*/
int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
{
- return call_int_hook(file_fcntl, 0, file, cmd, arg);
+ return call_int_hook(file_fcntl, file, cmd, arg);
}
/**
@@ -2876,6 +3053,8 @@ int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
* Save owner security information (typically from current->security) in
* file->f_security for later use by the send_sigiotask hook.
*
+ * This hook is called with file->f_owner.lock held.
+ *
* Return: Returns 0 on success.
*/
void security_file_set_fowner(struct file *file)
@@ -2900,11 +3079,11 @@ void security_file_set_fowner(struct file *file)
int security_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int sig)
{
- return call_int_hook(file_send_sigiotask, 0, tsk, fown, sig);
+ return call_int_hook(file_send_sigiotask, tsk, fown, sig);
}
/**
- * security_file_receive() - Check is receiving a file via IPC is allowed
+ * security_file_receive() - Check if receiving a file via IPC is allowed
* @file: file being received
*
* This hook allows security modules to control the ability of a process to
@@ -2914,7 +3093,7 @@ int security_file_send_sigiotask(struct task_struct *tsk,
*/
int security_file_receive(struct file *file)
{
- return call_int_hook(file_receive, 0, file);
+ return call_int_hook(file_receive, file);
}
/**
@@ -2924,18 +3103,33 @@ int security_file_receive(struct file *file)
* Save open-time permission checking state for later use upon file_permission,
* and recheck access if anything has changed since inode_permission.
*
+ * We can check if a file is opened for execution (e.g. execve(2) call), either
+ * directly or indirectly (e.g. ELF's ld.so) by checking file->f_flags &
+ * __FMODE_EXEC .
+ *
* Return: Returns 0 if permission is granted.
*/
int security_file_open(struct file *file)
{
- int ret;
-
- ret = call_int_hook(file_open, 0, file);
- if (ret)
- return ret;
+ return call_int_hook(file_open, file);
+}
- return fsnotify_open_perm(file);
+/**
+ * security_file_post_open() - Evaluate a file after it has been opened
+ * @file: the file
+ * @mask: access mask
+ *
+ * Evaluate an opened file and the access mask requested with open(). The hook
+ * is useful for LSMs that require the file content to be available in order to
+ * make decisions.
+ *
+ * Return: Returns 0 if permission is granted.
+ */
+int security_file_post_open(struct file *file, int mask)
+{
+ return call_int_hook(file_post_open, file, mask);
}
+EXPORT_SYMBOL_GPL(security_file_post_open);
/**
* security_file_truncate() - Check if truncating a file is allowed
@@ -2949,7 +3143,7 @@ int security_file_open(struct file *file)
*/
int security_file_truncate(struct file *file)
{
- return call_int_hook(file_truncate, 0, file);
+ return call_int_hook(file_truncate, file);
}
/**
@@ -2967,7 +3161,7 @@ int security_task_alloc(struct task_struct *task, unsigned long clone_flags)
if (rc)
return rc;
- rc = call_int_hook(task_alloc, 0, task, clone_flags);
+ rc = call_int_hook(task_alloc, task, clone_flags);
if (unlikely(rc))
security_task_free(task);
return rc;
@@ -3005,7 +3199,7 @@ int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
if (rc)
return rc;
- rc = call_int_hook(cred_alloc_blank, 0, cred, gfp);
+ rc = call_int_hook(cred_alloc_blank, cred, gfp);
if (unlikely(rc))
security_cred_free(cred);
return rc;
@@ -3049,7 +3243,7 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
if (rc)
return rc;
- rc = call_int_hook(cred_prepare, 0, new, old, gfp);
+ rc = call_int_hook(cred_prepare, new, old, gfp);
if (unlikely(rc))
security_cred_free(new);
return rc;
@@ -3083,6 +3277,21 @@ void security_cred_getsecid(const struct cred *c, u32 *secid)
EXPORT_SYMBOL(security_cred_getsecid);
/**
+ * security_cred_getlsmprop() - Get the LSM data from a set of credentials
+ * @c: credentials
+ * @prop: destination for the LSM data
+ *
+ * Retrieve the security data of the cred structure @c. In case of
+ * failure, @prop will be cleared.
+ */
+void security_cred_getlsmprop(const struct cred *c, struct lsm_prop *prop)
+{
+ lsmprop_init(prop);
+ call_void_hook(cred_getlsmprop, c, prop);
+}
+EXPORT_SYMBOL(security_cred_getlsmprop);
+
+/**
* security_kernel_act_as() - Set the kernel credentials to act as secid
* @new: credentials
* @secid: secid
@@ -3094,7 +3303,7 @@ EXPORT_SYMBOL(security_cred_getsecid);
*/
int security_kernel_act_as(struct cred *new, u32 secid)
{
- return call_int_hook(kernel_act_as, 0, new, secid);
+ return call_int_hook(kernel_act_as, new, secid);
}
/**
@@ -3110,11 +3319,11 @@ int security_kernel_act_as(struct cred *new, u32 secid)
*/
int security_kernel_create_files_as(struct cred *new, struct inode *inode)
{
- return call_int_hook(kernel_create_files_as, 0, new, inode);
+ return call_int_hook(kernel_create_files_as, new, inode);
}
/**
- * security_kernel_module_request() - Check is loading a module is allowed
+ * security_kernel_module_request() - Check if loading a module is allowed
* @kmod_name: module name
*
* Ability to trigger the kernel to automatically upcall to userspace for
@@ -3124,12 +3333,7 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode)
*/
int security_kernel_module_request(char *kmod_name)
{
- int ret;
-
- ret = call_int_hook(kernel_module_request, 0, kmod_name);
- if (ret)
- return ret;
- return integrity_kernel_module_request(kmod_name);
+ return call_int_hook(kernel_module_request, kmod_name);
}
/**
@@ -3145,12 +3349,7 @@ int security_kernel_module_request(char *kmod_name)
int security_kernel_read_file(struct file *file, enum kernel_read_file_id id,
bool contents)
{
- int ret;
-
- ret = call_int_hook(kernel_read_file, 0, file, id, contents);
- if (ret)
- return ret;
- return ima_read_file(file, id, contents);
+ return call_int_hook(kernel_read_file, file, id, contents);
}
EXPORT_SYMBOL_GPL(security_kernel_read_file);
@@ -3170,12 +3369,7 @@ EXPORT_SYMBOL_GPL(security_kernel_read_file);
int security_kernel_post_read_file(struct file *file, char *buf, loff_t size,
enum kernel_read_file_id id)
{
- int ret;
-
- ret = call_int_hook(kernel_post_read_file, 0, file, buf, size, id);
- if (ret)
- return ret;
- return ima_post_read_file(file, buf, size, id);
+ return call_int_hook(kernel_post_read_file, file, buf, size, id);
}
EXPORT_SYMBOL_GPL(security_kernel_post_read_file);
@@ -3190,12 +3384,7 @@ EXPORT_SYMBOL_GPL(security_kernel_post_read_file);
*/
int security_kernel_load_data(enum kernel_load_data_id id, bool contents)
{
- int ret;
-
- ret = call_int_hook(kernel_load_data, 0, id, contents);
- if (ret)
- return ret;
- return ima_load_data(id, contents);
+ return call_int_hook(kernel_load_data, id, contents);
}
EXPORT_SYMBOL_GPL(security_kernel_load_data);
@@ -3217,13 +3406,7 @@ int security_kernel_post_load_data(char *buf, loff_t size,
enum kernel_load_data_id id,
char *description)
{
- int ret;
-
- ret = call_int_hook(kernel_post_load_data, 0, buf, size, id,
- description);
- if (ret)
- return ret;
- return ima_post_load_data(buf, size, id, description);
+ return call_int_hook(kernel_post_load_data, buf, size, id, description);
}
EXPORT_SYMBOL_GPL(security_kernel_post_load_data);
@@ -3244,7 +3427,7 @@ EXPORT_SYMBOL_GPL(security_kernel_post_load_data);
int security_task_fix_setuid(struct cred *new, const struct cred *old,
int flags)
{
- return call_int_hook(task_fix_setuid, 0, new, old, flags);
+ return call_int_hook(task_fix_setuid, new, old, flags);
}
/**
@@ -3264,7 +3447,7 @@ int security_task_fix_setuid(struct cred *new, const struct cred *old,
int security_task_fix_setgid(struct cred *new, const struct cred *old,
int flags)
{
- return call_int_hook(task_fix_setgid, 0, new, old, flags);
+ return call_int_hook(task_fix_setgid, new, old, flags);
}
/**
@@ -3281,7 +3464,7 @@ int security_task_fix_setgid(struct cred *new, const struct cred *old,
*/
int security_task_fix_setgroups(struct cred *new, const struct cred *old)
{
- return call_int_hook(task_fix_setgroups, 0, new, old);
+ return call_int_hook(task_fix_setgroups, new, old);
}
/**
@@ -3296,7 +3479,7 @@ int security_task_fix_setgroups(struct cred *new, const struct cred *old)
*/
int security_task_setpgid(struct task_struct *p, pid_t pgid)
{
- return call_int_hook(task_setpgid, 0, p, pgid);
+ return call_int_hook(task_setpgid, p, pgid);
}
/**
@@ -3310,7 +3493,7 @@ int security_task_setpgid(struct task_struct *p, pid_t pgid)
*/
int security_task_getpgid(struct task_struct *p)
{
- return call_int_hook(task_getpgid, 0, p);
+ return call_int_hook(task_getpgid, p);
}
/**
@@ -3323,37 +3506,37 @@ int security_task_getpgid(struct task_struct *p)
*/
int security_task_getsid(struct task_struct *p)
{
- return call_int_hook(task_getsid, 0, p);
+ return call_int_hook(task_getsid, p);
}
/**
- * security_current_getsecid_subj() - Get the current task's subjective secid
- * @secid: secid value
+ * security_current_getlsmprop_subj() - Current task's subjective LSM data
+ * @prop: lsm specific information
*
* Retrieve the subjective security identifier of the current task and return
- * it in @secid. In case of failure, @secid will be set to zero.
+ * it in @prop.
*/
-void security_current_getsecid_subj(u32 *secid)
+void security_current_getlsmprop_subj(struct lsm_prop *prop)
{
- *secid = 0;
- call_void_hook(current_getsecid_subj, secid);
+ lsmprop_init(prop);
+ call_void_hook(current_getlsmprop_subj, prop);
}
-EXPORT_SYMBOL(security_current_getsecid_subj);
+EXPORT_SYMBOL(security_current_getlsmprop_subj);
/**
- * security_task_getsecid_obj() - Get a task's objective secid
+ * security_task_getlsmprop_obj() - Get a task's objective LSM data
* @p: target task
- * @secid: secid value
+ * @prop: lsm specific information
*
* Retrieve the objective security identifier of the task_struct in @p and
- * return it in @secid. In case of failure, @secid will be set to zero.
+ * return it in @prop.
*/
-void security_task_getsecid_obj(struct task_struct *p, u32 *secid)
+void security_task_getlsmprop_obj(struct task_struct *p, struct lsm_prop *prop)
{
- *secid = 0;
- call_void_hook(task_getsecid_obj, p, secid);
+ lsmprop_init(prop);
+ call_void_hook(task_getlsmprop_obj, p, prop);
}
-EXPORT_SYMBOL(security_task_getsecid_obj);
+EXPORT_SYMBOL(security_task_getlsmprop_obj);
/**
* security_task_setnice() - Check if setting a task's nice value is allowed
@@ -3366,7 +3549,7 @@ EXPORT_SYMBOL(security_task_getsecid_obj);
*/
int security_task_setnice(struct task_struct *p, int nice)
{
- return call_int_hook(task_setnice, 0, p, nice);
+ return call_int_hook(task_setnice, p, nice);
}
/**
@@ -3380,7 +3563,7 @@ int security_task_setnice(struct task_struct *p, int nice)
*/
int security_task_setioprio(struct task_struct *p, int ioprio)
{
- return call_int_hook(task_setioprio, 0, p, ioprio);
+ return call_int_hook(task_setioprio, p, ioprio);
}
/**
@@ -3393,7 +3576,7 @@ int security_task_setioprio(struct task_struct *p, int ioprio)
*/
int security_task_getioprio(struct task_struct *p)
{
- return call_int_hook(task_getioprio, 0, p);
+ return call_int_hook(task_getioprio, p);
}
/**
@@ -3410,7 +3593,7 @@ int security_task_getioprio(struct task_struct *p)
int security_task_prlimit(const struct cred *cred, const struct cred *tcred,
unsigned int flags)
{
- return call_int_hook(task_prlimit, 0, cred, tcred, flags);
+ return call_int_hook(task_prlimit, cred, tcred, flags);
}
/**
@@ -3428,7 +3611,7 @@ int security_task_prlimit(const struct cred *cred, const struct cred *tcred,
int security_task_setrlimit(struct task_struct *p, unsigned int resource,
struct rlimit *new_rlim)
{
- return call_int_hook(task_setrlimit, 0, p, resource, new_rlim);
+ return call_int_hook(task_setrlimit, p, resource, new_rlim);
}
/**
@@ -3442,7 +3625,7 @@ int security_task_setrlimit(struct task_struct *p, unsigned int resource,
*/
int security_task_setscheduler(struct task_struct *p)
{
- return call_int_hook(task_setscheduler, 0, p);
+ return call_int_hook(task_setscheduler, p);
}
/**
@@ -3455,7 +3638,7 @@ int security_task_setscheduler(struct task_struct *p)
*/
int security_task_getscheduler(struct task_struct *p)
{
- return call_int_hook(task_getscheduler, 0, p);
+ return call_int_hook(task_getscheduler, p);
}
/**
@@ -3468,7 +3651,7 @@ int security_task_getscheduler(struct task_struct *p)
*/
int security_task_movememory(struct task_struct *p)
{
- return call_int_hook(task_movememory, 0, p);
+ return call_int_hook(task_movememory, p);
}
/**
@@ -3489,7 +3672,7 @@ int security_task_movememory(struct task_struct *p)
int security_task_kill(struct task_struct *p, struct kernel_siginfo *info,
int sig, const struct cred *cred)
{
- return call_int_hook(task_kill, 0, p, info, sig, cred);
+ return call_int_hook(task_kill, p, info, sig, cred);
}
/**
@@ -3511,10 +3694,10 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
{
int thisrc;
int rc = LSM_RET_DEFAULT(task_prctl);
- struct security_hook_list *hp;
+ struct lsm_static_call *scall;
- hlist_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
- thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
+ lsm_for_each_hook(scall, task_prctl) {
+ thisrc = scall->hl->hook.task_prctl(option, arg2, arg3, arg4, arg5);
if (thisrc != LSM_RET_DEFAULT(task_prctl)) {
rc = thisrc;
if (thisrc != 0)
@@ -3547,7 +3730,7 @@ void security_task_to_inode(struct task_struct *p, struct inode *inode)
*/
int security_create_user_ns(const struct cred *cred)
{
- return call_int_hook(userns_create, 0, cred);
+ return call_int_hook(userns_create, cred);
}
/**
@@ -3561,21 +3744,21 @@ int security_create_user_ns(const struct cred *cred)
*/
int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
{
- return call_int_hook(ipc_permission, 0, ipcp, flag);
+ return call_int_hook(ipc_permission, ipcp, flag);
}
/**
- * security_ipc_getsecid() - Get the sysv ipc object's secid
+ * security_ipc_getlsmprop() - Get the sysv ipc object LSM data
* @ipcp: ipc permission structure
- * @secid: secid pointer
+ * @prop: pointer to lsm information
*
- * Get the secid associated with the ipc object. In case of failure, @secid
- * will be set to zero.
+ * Get the lsm information associated with the ipc object.
*/
-void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
+
+void security_ipc_getlsmprop(struct kern_ipc_perm *ipcp, struct lsm_prop *prop)
{
- *secid = 0;
- call_void_hook(ipc_getsecid, ipcp, secid);
+ lsmprop_init(prop);
+ call_void_hook(ipc_getlsmprop, ipcp, prop);
}
/**
@@ -3593,7 +3776,7 @@ int security_msg_msg_alloc(struct msg_msg *msg)
if (unlikely(rc))
return rc;
- rc = call_int_hook(msg_msg_alloc_security, 0, msg);
+ rc = call_int_hook(msg_msg_alloc_security, msg);
if (unlikely(rc))
security_msg_msg_free(msg);
return rc;
@@ -3627,7 +3810,7 @@ int security_msg_queue_alloc(struct kern_ipc_perm *msq)
if (unlikely(rc))
return rc;
- rc = call_int_hook(msg_queue_alloc_security, 0, msq);
+ rc = call_int_hook(msg_queue_alloc_security, msq);
if (unlikely(rc))
security_msg_queue_free(msq);
return rc;
@@ -3659,7 +3842,7 @@ void security_msg_queue_free(struct kern_ipc_perm *msq)
*/
int security_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg)
{
- return call_int_hook(msg_queue_associate, 0, msq, msqflg);
+ return call_int_hook(msg_queue_associate, msq, msqflg);
}
/**
@@ -3674,7 +3857,7 @@ int security_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg)
*/
int security_msg_queue_msgctl(struct kern_ipc_perm *msq, int cmd)
{
- return call_int_hook(msg_queue_msgctl, 0, msq, cmd);
+ return call_int_hook(msg_queue_msgctl, msq, cmd);
}
/**
@@ -3691,7 +3874,7 @@ int security_msg_queue_msgctl(struct kern_ipc_perm *msq, int cmd)
int security_msg_queue_msgsnd(struct kern_ipc_perm *msq,
struct msg_msg *msg, int msqflg)
{
- return call_int_hook(msg_queue_msgsnd, 0, msq, msg, msqflg);
+ return call_int_hook(msg_queue_msgsnd, msq, msg, msqflg);
}
/**
@@ -3712,7 +3895,7 @@ int security_msg_queue_msgsnd(struct kern_ipc_perm *msq,
int security_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *msg,
struct task_struct *target, long type, int mode)
{
- return call_int_hook(msg_queue_msgrcv, 0, msq, msg, target, type, mode);
+ return call_int_hook(msg_queue_msgrcv, msq, msg, target, type, mode);
}
/**
@@ -3730,7 +3913,7 @@ int security_shm_alloc(struct kern_ipc_perm *shp)
if (unlikely(rc))
return rc;
- rc = call_int_hook(shm_alloc_security, 0, shp);
+ rc = call_int_hook(shm_alloc_security, shp);
if (unlikely(rc))
security_shm_free(shp);
return rc;
@@ -3763,7 +3946,7 @@ void security_shm_free(struct kern_ipc_perm *shp)
*/
int security_shm_associate(struct kern_ipc_perm *shp, int shmflg)
{
- return call_int_hook(shm_associate, 0, shp, shmflg);
+ return call_int_hook(shm_associate, shp, shmflg);
}
/**
@@ -3778,7 +3961,7 @@ int security_shm_associate(struct kern_ipc_perm *shp, int shmflg)
*/
int security_shm_shmctl(struct kern_ipc_perm *shp, int cmd)
{
- return call_int_hook(shm_shmctl, 0, shp, cmd);
+ return call_int_hook(shm_shmctl, shp, cmd);
}
/**
@@ -3796,7 +3979,7 @@ int security_shm_shmctl(struct kern_ipc_perm *shp, int cmd)
int security_shm_shmat(struct kern_ipc_perm *shp,
char __user *shmaddr, int shmflg)
{
- return call_int_hook(shm_shmat, 0, shp, shmaddr, shmflg);
+ return call_int_hook(shm_shmat, shp, shmaddr, shmflg);
}
/**
@@ -3814,7 +3997,7 @@ int security_sem_alloc(struct kern_ipc_perm *sma)
if (unlikely(rc))
return rc;
- rc = call_int_hook(sem_alloc_security, 0, sma);
+ rc = call_int_hook(sem_alloc_security, sma);
if (unlikely(rc))
security_sem_free(sma);
return rc;
@@ -3846,7 +4029,7 @@ void security_sem_free(struct kern_ipc_perm *sma)
*/
int security_sem_associate(struct kern_ipc_perm *sma, int semflg)
{
- return call_int_hook(sem_associate, 0, sma, semflg);
+ return call_int_hook(sem_associate, sma, semflg);
}
/**
@@ -3861,7 +4044,7 @@ int security_sem_associate(struct kern_ipc_perm *sma, int semflg)
*/
int security_sem_semctl(struct kern_ipc_perm *sma, int cmd)
{
- return call_int_hook(sem_semctl, 0, sma, cmd);
+ return call_int_hook(sem_semctl, sma, cmd);
}
/**
@@ -3879,7 +4062,7 @@ int security_sem_semctl(struct kern_ipc_perm *sma, int cmd)
int security_sem_semop(struct kern_ipc_perm *sma, struct sembuf *sops,
unsigned nsops, int alter)
{
- return call_int_hook(sem_semop, 0, sma, sops, nsops, alter);
+ return call_int_hook(sem_semop, sma, sops, nsops, alter);
}
/**
@@ -3918,14 +4101,14 @@ EXPORT_SYMBOL(security_d_instantiate);
* If @size is insufficient to contain the data -E2BIG is returned.
*/
int security_getselfattr(unsigned int attr, struct lsm_ctx __user *uctx,
- size_t __user *size, u32 flags)
+ u32 __user *size, u32 flags)
{
- struct security_hook_list *hp;
+ struct lsm_static_call *scall;
struct lsm_ctx lctx = { .id = LSM_ID_UNDEF, };
u8 __user *base = (u8 __user *)uctx;
- size_t total = 0;
- size_t entrysize;
- size_t left;
+ u32 entrysize;
+ u32 total = 0;
+ u32 left;
bool toobig = false;
bool single = false;
int count = 0;
@@ -3958,17 +4141,15 @@ int security_getselfattr(unsigned int attr, struct lsm_ctx __user *uctx,
* In the usual case gather all the data from the LSMs.
* In the single case only get the data from the LSM specified.
*/
- hlist_for_each_entry(hp, &security_hook_heads.getselfattr, list) {
- if (single && lctx.id != hp->lsmid->id)
+ lsm_for_each_hook(scall, getselfattr) {
+ if (single && lctx.id != scall->hl->lsmid->id)
continue;
entrysize = left;
if (base)
uctx = (struct lsm_ctx __user *)(base + total);
- rc = hp->hook.getselfattr(attr, uctx, &entrysize, flags);
- if (rc == -EOPNOTSUPP) {
- rc = 0;
+ rc = scall->hl->hook.getselfattr(attr, uctx, &entrysize, flags);
+ if (rc == -EOPNOTSUPP)
continue;
- }
if (rc == -E2BIG) {
rc = 0;
left = 0;
@@ -4011,9 +4192,9 @@ int security_getselfattr(unsigned int attr, struct lsm_ctx __user *uctx,
* LSM specific failure.
*/
int security_setselfattr(unsigned int attr, struct lsm_ctx __user *uctx,
- size_t size, u32 flags)
+ u32 size, u32 flags)
{
- struct security_hook_list *hp;
+ struct lsm_static_call *scall;
struct lsm_ctx *lctx;
int rc = LSM_RET_DEFAULT(setselfattr);
u64 required_len;
@@ -4036,9 +4217,9 @@ int security_setselfattr(unsigned int attr, struct lsm_ctx __user *uctx,
goto free_out;
}
- hlist_for_each_entry(hp, &security_hook_heads.setselfattr, list)
- if ((hp->lsmid->id) == lctx->id) {
- rc = hp->hook.setselfattr(attr, lctx, size, flags);
+ lsm_for_each_hook(scall, setselfattr)
+ if ((scall->hl->lsmid->id) == lctx->id) {
+ rc = scall->hl->hook.setselfattr(attr, lctx, size, flags);
break;
}
@@ -4061,12 +4242,12 @@ free_out:
int security_getprocattr(struct task_struct *p, int lsmid, const char *name,
char **value)
{
- struct security_hook_list *hp;
+ struct lsm_static_call *scall;
- hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
- if (lsmid != 0 && lsmid != hp->lsmid->id)
+ lsm_for_each_hook(scall, getprocattr) {
+ if (lsmid != 0 && lsmid != scall->hl->lsmid->id)
continue;
- return hp->hook.getprocattr(p, name, value);
+ return scall->hl->hook.getprocattr(p, name, value);
}
return LSM_RET_DEFAULT(getprocattr);
}
@@ -4085,12 +4266,12 @@ int security_getprocattr(struct task_struct *p, int lsmid, const char *name,
*/
int security_setprocattr(int lsmid, const char *name, void *value, size_t size)
{
- struct security_hook_list *hp;
+ struct lsm_static_call *scall;
- hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
- if (lsmid != 0 && lsmid != hp->lsmid->id)
+ lsm_for_each_hook(scall, setprocattr) {
+ if (lsmid != 0 && lsmid != scall->hl->lsmid->id)
continue;
- return hp->hook.setprocattr(name, value, size);
+ return scall->hl->hook.setprocattr(name, value, size);
}
return LSM_RET_DEFAULT(setprocattr);
}
@@ -4110,11 +4291,11 @@ int security_setprocattr(int lsmid, const char *name, void *value, size_t size)
*/
int security_netlink_send(struct sock *sk, struct sk_buff *skb)
{
- return call_int_hook(netlink_send, 0, sk, skb);
+ return call_int_hook(netlink_send, sk, skb);
}
/**
- * security_ismaclabel() - Check is the named attribute is a MAC label
+ * security_ismaclabel() - Check if the named attribute is a MAC label
* @name: full extended attribute name
*
* Check if the extended attribute specified by @name represents a MAC label.
@@ -4123,43 +4304,47 @@ int security_netlink_send(struct sock *sk, struct sk_buff *skb)
*/
int security_ismaclabel(const char *name)
{
- return call_int_hook(ismaclabel, 0, name);
+ return call_int_hook(ismaclabel, name);
}
EXPORT_SYMBOL(security_ismaclabel);
/**
* security_secid_to_secctx() - Convert a secid to a secctx
* @secid: secid
- * @secdata: secctx
- * @seclen: secctx length
+ * @cp: the LSM context
*
- * Convert secid to security context. If @secdata is NULL the length of the
- * result will be returned in @seclen, but no @secdata will be returned. This
+ * Convert secid to security context. If @cp is NULL the length of the
+ * result will be returned, but no data will be returned. This
* does mean that the length could change between calls to check the length and
- * the next call which actually allocates and returns the @secdata.
+ * the next call which actually allocates and returns the data.
*
- * Return: Return 0 on success, error on failure.
+ * Return: Return length of data on success, error on failure.
*/
-int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+int security_secid_to_secctx(u32 secid, struct lsm_context *cp)
{
- struct security_hook_list *hp;
- int rc;
-
- /*
- * Currently, only one LSM can implement secid_to_secctx (i.e this
- * LSM hook is not "stackable").
- */
- hlist_for_each_entry(hp, &security_hook_heads.secid_to_secctx, list) {
- rc = hp->hook.secid_to_secctx(secid, secdata, seclen);
- if (rc != LSM_RET_DEFAULT(secid_to_secctx))
- return rc;
- }
-
- return LSM_RET_DEFAULT(secid_to_secctx);
+ return call_int_hook(secid_to_secctx, secid, cp);
}
EXPORT_SYMBOL(security_secid_to_secctx);
/**
+ * security_lsmprop_to_secctx() - Convert a lsm_prop to a secctx
+ * @prop: lsm specific information
+ * @cp: the LSM context
+ *
+ * Convert a @prop entry to security context. If @cp is NULL the
+ * length of the result will be returned. This does mean that the
+ * length could change between calls to check the length and the
+ * next call which actually allocates and returns the @cp.
+ *
+ * Return: Return length of data on success, error on failure.
+ */
+int security_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp)
+{
+ return call_int_hook(lsmprop_to_secctx, prop, cp);
+}
+EXPORT_SYMBOL(security_lsmprop_to_secctx);
+
+/**
* security_secctx_to_secid() - Convert a secctx to a secid
* @secdata: secctx
* @seclen: length of secctx
@@ -4172,20 +4357,20 @@ EXPORT_SYMBOL(security_secid_to_secctx);
int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
{
*secid = 0;
- return call_int_hook(secctx_to_secid, 0, secdata, seclen, secid);
+ return call_int_hook(secctx_to_secid, secdata, seclen, secid);
}
EXPORT_SYMBOL(security_secctx_to_secid);
/**
* security_release_secctx() - Free a secctx buffer
- * @secdata: secctx
- * @seclen: length of secctx
+ * @cp: the security context
*
* Release the security context.
*/
-void security_release_secctx(char *secdata, u32 seclen)
+void security_release_secctx(struct lsm_context *cp)
{
- call_void_hook(release_secctx, secdata, seclen);
+ call_void_hook(release_secctx, cp);
+ memset(cp, 0, sizeof(*cp));
}
EXPORT_SYMBOL(security_release_secctx);
@@ -4219,7 +4404,7 @@ EXPORT_SYMBOL(security_inode_invalidate_secctx);
*/
int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
{
- return call_int_hook(inode_notifysecctx, 0, inode, ctx, ctxlen);
+ return call_int_hook(inode_notifysecctx, inode, ctx, ctxlen);
}
EXPORT_SYMBOL(security_inode_notifysecctx);
@@ -4241,36 +4426,24 @@ EXPORT_SYMBOL(security_inode_notifysecctx);
*/
int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
{
- return call_int_hook(inode_setsecctx, 0, dentry, ctx, ctxlen);
+ return call_int_hook(inode_setsecctx, dentry, ctx, ctxlen);
}
EXPORT_SYMBOL(security_inode_setsecctx);
/**
* security_inode_getsecctx() - Get the security label of an inode
* @inode: inode
- * @ctx: secctx
- * @ctxlen: length of secctx
+ * @cp: security context
*
- * On success, returns 0 and fills out @ctx and @ctxlen with the security
- * context for the given @inode.
+ * On success, returns 0 and fills out @cp with the security context
+ * for the given @inode.
*
* Return: Returns 0 on success, error on failure.
*/
-int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+int security_inode_getsecctx(struct inode *inode, struct lsm_context *cp)
{
- struct security_hook_list *hp;
- int rc;
-
- /*
- * Only one module will provide a security context.
- */
- hlist_for_each_entry(hp, &security_hook_heads.inode_getsecctx, list) {
- rc = hp->hook.inode_getsecctx(inode, ctx, ctxlen);
- if (rc != LSM_RET_DEFAULT(inode_getsecctx))
- return rc;
- }
-
- return LSM_RET_DEFAULT(inode_getsecctx);
+ memset(cp, 0, sizeof(*cp));
+ return call_int_hook(inode_getsecctx, inode, cp);
}
EXPORT_SYMBOL(security_inode_getsecctx);
@@ -4289,7 +4462,7 @@ int security_post_notification(const struct cred *w_cred,
const struct cred *cred,
struct watch_notification *n)
{
- return call_int_hook(post_notification, 0, w_cred, cred, n);
+ return call_int_hook(post_notification, w_cred, cred, n);
}
#endif /* CONFIG_WATCH_QUEUE */
@@ -4305,7 +4478,7 @@ int security_post_notification(const struct cred *w_cred,
*/
int security_watch_key(struct key *key)
{
- return call_int_hook(watch_key, 0, key);
+ return call_int_hook(watch_key, key);
}
#endif /* CONFIG_KEY_NOTIFICATIONS */
@@ -4334,7 +4507,7 @@ int security_watch_key(struct key *key)
int security_unix_stream_connect(struct sock *sock, struct sock *other,
struct sock *newsk)
{
- return call_int_hook(unix_stream_connect, 0, sock, other, newsk);
+ return call_int_hook(unix_stream_connect, sock, other, newsk);
}
EXPORT_SYMBOL(security_unix_stream_connect);
@@ -4360,7 +4533,7 @@ EXPORT_SYMBOL(security_unix_stream_connect);
*/
int security_unix_may_send(struct socket *sock, struct socket *other)
{
- return call_int_hook(unix_may_send, 0, sock, other);
+ return call_int_hook(unix_may_send, sock, other);
}
EXPORT_SYMBOL(security_unix_may_send);
@@ -4377,7 +4550,7 @@ EXPORT_SYMBOL(security_unix_may_send);
*/
int security_socket_create(int family, int type, int protocol, int kern)
{
- return call_int_hook(socket_create, 0, family, type, protocol, kern);
+ return call_int_hook(socket_create, family, type, protocol, kern);
}
/**
@@ -4401,7 +4574,7 @@ int security_socket_create(int family, int type, int protocol, int kern)
int security_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern)
{
- return call_int_hook(socket_post_create, 0, sock, family, type,
+ return call_int_hook(socket_post_create, sock, family, type,
protocol, kern);
}
@@ -4417,7 +4590,7 @@ int security_socket_post_create(struct socket *sock, int family,
*/
int security_socket_socketpair(struct socket *socka, struct socket *sockb)
{
- return call_int_hook(socket_socketpair, 0, socka, sockb);
+ return call_int_hook(socket_socketpair, socka, sockb);
}
EXPORT_SYMBOL(security_socket_socketpair);
@@ -4436,7 +4609,7 @@ EXPORT_SYMBOL(security_socket_socketpair);
int security_socket_bind(struct socket *sock,
struct sockaddr *address, int addrlen)
{
- return call_int_hook(socket_bind, 0, sock, address, addrlen);
+ return call_int_hook(socket_bind, sock, address, addrlen);
}
/**
@@ -4453,7 +4626,7 @@ int security_socket_bind(struct socket *sock,
int security_socket_connect(struct socket *sock,
struct sockaddr *address, int addrlen)
{
- return call_int_hook(socket_connect, 0, sock, address, addrlen);
+ return call_int_hook(socket_connect, sock, address, addrlen);
}
/**
@@ -4467,7 +4640,7 @@ int security_socket_connect(struct socket *sock,
*/
int security_socket_listen(struct socket *sock, int backlog)
{
- return call_int_hook(socket_listen, 0, sock, backlog);
+ return call_int_hook(socket_listen, sock, backlog);
}
/**
@@ -4483,11 +4656,11 @@ int security_socket_listen(struct socket *sock, int backlog)
*/
int security_socket_accept(struct socket *sock, struct socket *newsock)
{
- return call_int_hook(socket_accept, 0, sock, newsock);
+ return call_int_hook(socket_accept, sock, newsock);
}
/**
- * security_socket_sendmsg() - Check is sending a message is allowed
+ * security_socket_sendmsg() - Check if sending a message is allowed
* @sock: sending socket
* @msg: message to send
* @size: size of message
@@ -4498,7 +4671,7 @@ int security_socket_accept(struct socket *sock, struct socket *newsock)
*/
int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
{
- return call_int_hook(socket_sendmsg, 0, sock, msg, size);
+ return call_int_hook(socket_sendmsg, sock, msg, size);
}
/**
@@ -4515,7 +4688,7 @@ int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
int size, int flags)
{
- return call_int_hook(socket_recvmsg, 0, sock, msg, size, flags);
+ return call_int_hook(socket_recvmsg, sock, msg, size, flags);
}
/**
@@ -4529,7 +4702,7 @@ int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
*/
int security_socket_getsockname(struct socket *sock)
{
- return call_int_hook(socket_getsockname, 0, sock);
+ return call_int_hook(socket_getsockname, sock);
}
/**
@@ -4542,7 +4715,7 @@ int security_socket_getsockname(struct socket *sock)
*/
int security_socket_getpeername(struct socket *sock)
{
- return call_int_hook(socket_getpeername, 0, sock);
+ return call_int_hook(socket_getpeername, sock);
}
/**
@@ -4558,7 +4731,7 @@ int security_socket_getpeername(struct socket *sock)
*/
int security_socket_getsockopt(struct socket *sock, int level, int optname)
{
- return call_int_hook(socket_getsockopt, 0, sock, level, optname);
+ return call_int_hook(socket_getsockopt, sock, level, optname);
}
/**
@@ -4573,7 +4746,7 @@ int security_socket_getsockopt(struct socket *sock, int level, int optname)
*/
int security_socket_setsockopt(struct socket *sock, int level, int optname)
{
- return call_int_hook(socket_setsockopt, 0, sock, level, optname);
+ return call_int_hook(socket_setsockopt, sock, level, optname);
}
/**
@@ -4588,7 +4761,7 @@ int security_socket_setsockopt(struct socket *sock, int level, int optname)
*/
int security_socket_shutdown(struct socket *sock, int how)
{
- return call_int_hook(socket_shutdown, 0, sock, how);
+ return call_int_hook(socket_shutdown, sock, how);
}
/**
@@ -4605,7 +4778,7 @@ int security_socket_shutdown(struct socket *sock, int how)
*/
int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
- return call_int_hook(socket_sock_rcv_skb, 0, sk, skb);
+ return call_int_hook(socket_sock_rcv_skb, sk, skb);
}
EXPORT_SYMBOL(security_sock_rcv_skb);
@@ -4627,20 +4800,8 @@ EXPORT_SYMBOL(security_sock_rcv_skb);
int security_socket_getpeersec_stream(struct socket *sock, sockptr_t optval,
sockptr_t optlen, unsigned int len)
{
- struct security_hook_list *hp;
- int rc;
-
- /*
- * Only one module will provide a security context.
- */
- hlist_for_each_entry(hp, &security_hook_heads.socket_getpeersec_stream,
- list) {
- rc = hp->hook.socket_getpeersec_stream(sock, optval, optlen,
- len);
- if (rc != LSM_RET_DEFAULT(socket_getpeersec_stream))
- return rc;
- }
- return LSM_RET_DEFAULT(socket_getpeersec_stream);
+ return call_int_hook(socket_getpeersec_stream, sock, optval, optlen,
+ len);
}
/**
@@ -4660,23 +4821,25 @@ int security_socket_getpeersec_stream(struct socket *sock, sockptr_t optval,
int security_socket_getpeersec_dgram(struct socket *sock,
struct sk_buff *skb, u32 *secid)
{
- struct security_hook_list *hp;
- int rc;
-
- /*
- * Only one module will provide a security context.
- */
- hlist_for_each_entry(hp, &security_hook_heads.socket_getpeersec_dgram,
- list) {
- rc = hp->hook.socket_getpeersec_dgram(sock, skb, secid);
- if (rc != LSM_RET_DEFAULT(socket_getpeersec_dgram))
- return rc;
- }
- return LSM_RET_DEFAULT(socket_getpeersec_dgram);
+ return call_int_hook(socket_getpeersec_dgram, sock, skb, secid);
}
EXPORT_SYMBOL(security_socket_getpeersec_dgram);
/**
+ * lsm_sock_alloc - allocate a composite sock blob
+ * @sock: the sock that needs a blob
+ * @gfp: allocation mode
+ *
+ * Allocate the sock blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_sock_alloc(struct sock *sock, gfp_t gfp)
+{
+ return lsm_blob_alloc(&sock->sk_security, blob_sizes.lbs_sock, gfp);
+}
+
+/**
* security_sk_alloc() - Allocate and initialize a sock's LSM blob
* @sk: sock
* @family: protocol family
@@ -4689,7 +4852,14 @@ EXPORT_SYMBOL(security_socket_getpeersec_dgram);
*/
int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
{
- return call_int_hook(sk_alloc_security, 0, sk, family, priority);
+ int rc = lsm_sock_alloc(sk, priority);
+
+ if (unlikely(rc))
+ return rc;
+ rc = call_int_hook(sk_alloc_security, sk, family, priority);
+ if (unlikely(rc))
+ security_sk_free(sk);
+ return rc;
}
/**
@@ -4701,6 +4871,8 @@ int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
void security_sk_free(struct sock *sk)
{
call_void_hook(sk_free_security, sk);
+ kfree(sk->sk_security);
+ sk->sk_security = NULL;
}
/**
@@ -4770,7 +4942,7 @@ EXPORT_SYMBOL(security_sock_graft);
int security_inet_conn_request(const struct sock *sk,
struct sk_buff *skb, struct request_sock *req)
{
- return call_int_hook(inet_conn_request, 0, sk, skb, req);
+ return call_int_hook(inet_conn_request, sk, skb, req);
}
EXPORT_SYMBOL(security_inet_conn_request);
@@ -4811,7 +4983,7 @@ EXPORT_SYMBOL(security_inet_conn_established);
*/
int security_secmark_relabel_packet(u32 secid)
{
- return call_int_hook(secmark_relabel_packet, 0, secid);
+ return call_int_hook(secmark_relabel_packet, secid);
}
EXPORT_SYMBOL(security_secmark_relabel_packet);
@@ -4848,7 +5020,18 @@ EXPORT_SYMBOL(security_secmark_refcount_dec);
*/
int security_tun_dev_alloc_security(void **security)
{
- return call_int_hook(tun_dev_alloc_security, 0, security);
+ int rc;
+
+ rc = lsm_blob_alloc(security, blob_sizes.lbs_tun_dev, GFP_KERNEL);
+ if (rc)
+ return rc;
+
+ rc = call_int_hook(tun_dev_alloc_security, *security);
+ if (rc) {
+ kfree(*security);
+ *security = NULL;
+ }
+ return rc;
}
EXPORT_SYMBOL(security_tun_dev_alloc_security);
@@ -4860,7 +5043,7 @@ EXPORT_SYMBOL(security_tun_dev_alloc_security);
*/
void security_tun_dev_free_security(void *security)
{
- call_void_hook(tun_dev_free_security, security);
+ kfree(security);
}
EXPORT_SYMBOL(security_tun_dev_free_security);
@@ -4873,7 +5056,7 @@ EXPORT_SYMBOL(security_tun_dev_free_security);
*/
int security_tun_dev_create(void)
{
- return call_int_hook(tun_dev_create, 0);
+ return call_int_hook(tun_dev_create);
}
EXPORT_SYMBOL(security_tun_dev_create);
@@ -4887,7 +5070,7 @@ EXPORT_SYMBOL(security_tun_dev_create);
*/
int security_tun_dev_attach_queue(void *security)
{
- return call_int_hook(tun_dev_attach_queue, 0, security);
+ return call_int_hook(tun_dev_attach_queue, security);
}
EXPORT_SYMBOL(security_tun_dev_attach_queue);
@@ -4903,7 +5086,7 @@ EXPORT_SYMBOL(security_tun_dev_attach_queue);
*/
int security_tun_dev_attach(struct sock *sk, void *security)
{
- return call_int_hook(tun_dev_attach, 0, sk, security);
+ return call_int_hook(tun_dev_attach, sk, security);
}
EXPORT_SYMBOL(security_tun_dev_attach);
@@ -4918,7 +5101,7 @@ EXPORT_SYMBOL(security_tun_dev_attach);
*/
int security_tun_dev_open(void *security)
{
- return call_int_hook(tun_dev_open, 0, security);
+ return call_int_hook(tun_dev_open, security);
}
EXPORT_SYMBOL(security_tun_dev_open);
@@ -4934,7 +5117,7 @@ EXPORT_SYMBOL(security_tun_dev_open);
int security_sctp_assoc_request(struct sctp_association *asoc,
struct sk_buff *skb)
{
- return call_int_hook(sctp_assoc_request, 0, asoc, skb);
+ return call_int_hook(sctp_assoc_request, asoc, skb);
}
EXPORT_SYMBOL(security_sctp_assoc_request);
@@ -4955,8 +5138,7 @@ EXPORT_SYMBOL(security_sctp_assoc_request);
int security_sctp_bind_connect(struct sock *sk, int optname,
struct sockaddr *address, int addrlen)
{
- return call_int_hook(sctp_bind_connect, 0, sk, optname,
- address, addrlen);
+ return call_int_hook(sctp_bind_connect, sk, optname, address, addrlen);
}
EXPORT_SYMBOL(security_sctp_bind_connect);
@@ -4990,7 +5172,7 @@ EXPORT_SYMBOL(security_sctp_sk_clone);
int security_sctp_assoc_established(struct sctp_association *asoc,
struct sk_buff *skb)
{
- return call_int_hook(sctp_assoc_established, 0, asoc, skb);
+ return call_int_hook(sctp_assoc_established, asoc, skb);
}
EXPORT_SYMBOL(security_sctp_assoc_established);
@@ -5008,7 +5190,7 @@ EXPORT_SYMBOL(security_sctp_assoc_established);
*/
int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
{
- return call_int_hook(mptcp_add_subflow, 0, sk, ssk);
+ return call_int_hook(mptcp_add_subflow, sk, ssk);
}
#endif /* CONFIG_SECURITY_NETWORK */
@@ -5026,7 +5208,7 @@ int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
*/
int security_ib_pkey_access(void *sec, u64 subnet_prefix, u16 pkey)
{
- return call_int_hook(ib_pkey_access, 0, sec, subnet_prefix, pkey);
+ return call_int_hook(ib_pkey_access, sec, subnet_prefix, pkey);
}
EXPORT_SYMBOL(security_ib_pkey_access);
@@ -5043,8 +5225,7 @@ EXPORT_SYMBOL(security_ib_pkey_access);
int security_ib_endport_manage_subnet(void *sec,
const char *dev_name, u8 port_num)
{
- return call_int_hook(ib_endport_manage_subnet, 0, sec,
- dev_name, port_num);
+ return call_int_hook(ib_endport_manage_subnet, sec, dev_name, port_num);
}
EXPORT_SYMBOL(security_ib_endport_manage_subnet);
@@ -5058,7 +5239,18 @@ EXPORT_SYMBOL(security_ib_endport_manage_subnet);
*/
int security_ib_alloc_security(void **sec)
{
- return call_int_hook(ib_alloc_security, 0, sec);
+ int rc;
+
+ rc = lsm_blob_alloc(sec, blob_sizes.lbs_ib, GFP_KERNEL);
+ if (rc)
+ return rc;
+
+ rc = call_int_hook(ib_alloc_security, *sec);
+ if (rc) {
+ kfree(*sec);
+ *sec = NULL;
+ }
+ return rc;
}
EXPORT_SYMBOL(security_ib_alloc_security);
@@ -5070,7 +5262,7 @@ EXPORT_SYMBOL(security_ib_alloc_security);
*/
void security_ib_free_security(void *sec)
{
- call_void_hook(ib_free_security, sec);
+ kfree(sec);
}
EXPORT_SYMBOL(security_ib_free_security);
#endif /* CONFIG_SECURITY_INFINIBAND */
@@ -5091,7 +5283,7 @@ int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
struct xfrm_user_sec_ctx *sec_ctx,
gfp_t gfp)
{
- return call_int_hook(xfrm_policy_alloc_security, 0, ctxp, sec_ctx, gfp);
+ return call_int_hook(xfrm_policy_alloc_security, ctxp, sec_ctx, gfp);
}
EXPORT_SYMBOL(security_xfrm_policy_alloc);
@@ -5108,7 +5300,7 @@ EXPORT_SYMBOL(security_xfrm_policy_alloc);
int security_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
struct xfrm_sec_ctx **new_ctxp)
{
- return call_int_hook(xfrm_policy_clone_security, 0, old_ctx, new_ctxp);
+ return call_int_hook(xfrm_policy_clone_security, old_ctx, new_ctxp);
}
/**
@@ -5133,7 +5325,7 @@ EXPORT_SYMBOL(security_xfrm_policy_free);
*/
int security_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
{
- return call_int_hook(xfrm_policy_delete_security, 0, ctx);
+ return call_int_hook(xfrm_policy_delete_security, ctx);
}
/**
@@ -5150,7 +5342,7 @@ int security_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
int security_xfrm_state_alloc(struct xfrm_state *x,
struct xfrm_user_sec_ctx *sec_ctx)
{
- return call_int_hook(xfrm_state_alloc, 0, x, sec_ctx);
+ return call_int_hook(xfrm_state_alloc, x, sec_ctx);
}
EXPORT_SYMBOL(security_xfrm_state_alloc);
@@ -5169,7 +5361,7 @@ EXPORT_SYMBOL(security_xfrm_state_alloc);
int security_xfrm_state_alloc_acquire(struct xfrm_state *x,
struct xfrm_sec_ctx *polsec, u32 secid)
{
- return call_int_hook(xfrm_state_alloc_acquire, 0, x, polsec, secid);
+ return call_int_hook(xfrm_state_alloc_acquire, x, polsec, secid);
}
/**
@@ -5182,7 +5374,7 @@ int security_xfrm_state_alloc_acquire(struct xfrm_state *x,
*/
int security_xfrm_state_delete(struct xfrm_state *x)
{
- return call_int_hook(xfrm_state_delete_security, 0, x);
+ return call_int_hook(xfrm_state_delete_security, x);
}
EXPORT_SYMBOL(security_xfrm_state_delete);
@@ -5211,7 +5403,7 @@ void security_xfrm_state_free(struct xfrm_state *x)
*/
int security_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid)
{
- return call_int_hook(xfrm_policy_lookup, 0, ctx, fl_secid);
+ return call_int_hook(xfrm_policy_lookup, ctx, fl_secid);
}
/**
@@ -5228,7 +5420,7 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
struct xfrm_policy *xp,
const struct flowi_common *flic)
{
- struct security_hook_list *hp;
+ struct lsm_static_call *scall;
int rc = LSM_RET_DEFAULT(xfrm_state_pol_flow_match);
/*
@@ -5240,9 +5432,8 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
* For speed optimization, we explicitly break the loop rather than
* using the macro
*/
- hlist_for_each_entry(hp, &security_hook_heads.xfrm_state_pol_flow_match,
- list) {
- rc = hp->hook.xfrm_state_pol_flow_match(x, xp, flic);
+ lsm_for_each_hook(scall, xfrm_state_pol_flow_match) {
+ rc = scall->hl->hook.xfrm_state_pol_flow_match(x, xp, flic);
break;
}
return rc;
@@ -5259,12 +5450,12 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
*/
int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid)
{
- return call_int_hook(xfrm_decode_session, 0, skb, secid, 1);
+ return call_int_hook(xfrm_decode_session, skb, secid, 1);
}
void security_skb_classify_flow(struct sk_buff *skb, struct flowi_common *flic)
{
- int rc = call_int_hook(xfrm_decode_session, 0, skb, &flic->flowic_secid,
+ int rc = call_int_hook(xfrm_decode_session, skb, &flic->flowic_secid,
0);
BUG_ON(rc);
@@ -5287,7 +5478,14 @@ EXPORT_SYMBOL(security_skb_classify_flow);
int security_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
{
- return call_int_hook(key_alloc, 0, key, cred, flags);
+ int rc = lsm_key_alloc(key);
+
+ if (unlikely(rc))
+ return rc;
+ rc = call_int_hook(key_alloc, key, cred, flags);
+ if (unlikely(rc))
+ security_key_free(key);
+ return rc;
}
/**
@@ -5298,7 +5496,8 @@ int security_key_alloc(struct key *key, const struct cred *cred,
*/
void security_key_free(struct key *key)
{
- call_void_hook(key_free, key);
+ kfree(key->security);
+ key->security = NULL;
}
/**
@@ -5314,7 +5513,7 @@ void security_key_free(struct key *key)
int security_key_permission(key_ref_t key_ref, const struct cred *cred,
enum key_need_perm need_perm)
{
- return call_int_hook(key_permission, 0, key_ref, cred, need_perm);
+ return call_int_hook(key_permission, key_ref, cred, need_perm);
}
/**
@@ -5333,7 +5532,26 @@ int security_key_permission(key_ref_t key_ref, const struct cred *cred,
int security_key_getsecurity(struct key *key, char **buffer)
{
*buffer = NULL;
- return call_int_hook(key_getsecurity, 0, key, buffer);
+ return call_int_hook(key_getsecurity, key, buffer);
+}
+
+/**
+ * security_key_post_create_or_update() - Notification of key create or update
+ * @keyring: keyring to which the key is linked to
+ * @key: created or updated key
+ * @payload: data used to instantiate or update the key
+ * @payload_len: length of payload
+ * @flags: key flags
+ * @create: flag indicating whether the key was created or updated
+ *
+ * Notify the caller of a key creation or update.
+ */
+void security_key_post_create_or_update(struct key *keyring, struct key *key,
+ const void *payload, size_t payload_len,
+ unsigned long flags, bool create)
+{
+ call_void_hook(key_post_create_or_update, keyring, key, payload,
+ payload_len, flags, create);
}
#endif /* CONFIG_KEYS */
@@ -5344,15 +5562,17 @@ int security_key_getsecurity(struct key *key, char **buffer)
* @op: rule operator
* @rulestr: rule context
* @lsmrule: receive buffer for audit rule struct
+ * @gfp: GFP flag used for kmalloc
*
* Allocate and initialize an LSM audit rule structure.
*
* Return: Return 0 if @lsmrule has been successfully set, -EINVAL in case of
* an invalid rule.
*/
-int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
+int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule,
+ gfp_t gfp)
{
- return call_int_hook(audit_rule_init, 0, field, op, rulestr, lsmrule);
+ return call_int_hook(audit_rule_init, field, op, rulestr, lsmrule, gfp);
}
/**
@@ -5366,7 +5586,7 @@ int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
*/
int security_audit_rule_known(struct audit_krule *krule)
{
- return call_int_hook(audit_rule_known, 0, krule);
+ return call_int_hook(audit_rule_known, krule);
}
/**
@@ -5383,7 +5603,7 @@ void security_audit_rule_free(void *lsmrule)
/**
* security_audit_rule_match() - Check if a label matches an audit rule
- * @secid: security label
+ * @prop: security label
* @field: LSM audit field
* @op: matching operator
* @lsmrule: audit rule
@@ -5394,9 +5614,10 @@ void security_audit_rule_free(void *lsmrule)
* Return: Returns 1 if secid matches the rule, 0 if it does not, -ERRNO on
* failure.
*/
-int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule)
+int security_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op,
+ void *lsmrule)
{
- return call_int_hook(audit_rule_match, 0, secid, field, op, lsmrule);
+ return call_int_hook(audit_rule_match, prop, field, op, lsmrule);
}
#endif /* CONFIG_AUDIT */
@@ -5415,7 +5636,7 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule)
*/
int security_bpf(int cmd, union bpf_attr *attr, unsigned int size)
{
- return call_int_hook(bpf, 0, cmd, attr, size);
+ return call_int_hook(bpf, cmd, attr, size);
}
/**
@@ -5430,7 +5651,7 @@ int security_bpf(int cmd, union bpf_attr *attr, unsigned int size)
*/
int security_bpf_map(struct bpf_map *map, fmode_t fmode)
{
- return call_int_hook(bpf_map, 0, map, fmode);
+ return call_int_hook(bpf_map, map, fmode);
}
/**
@@ -5444,33 +5665,91 @@ int security_bpf_map(struct bpf_map *map, fmode_t fmode)
*/
int security_bpf_prog(struct bpf_prog *prog)
{
- return call_int_hook(bpf_prog, 0, prog);
+ return call_int_hook(bpf_prog, prog);
}
/**
- * security_bpf_map_alloc() - Allocate a bpf map LSM blob
- * @map: bpf map
+ * security_bpf_map_create() - Check if BPF map creation is allowed
+ * @map: BPF map object
+ * @attr: BPF syscall attributes used to create BPF map
+ * @token: BPF token used to grant user access
+ *
+ * Do a check when the kernel creates a new BPF map. This is also the
+ * point where LSM blob is allocated for LSMs that need them.
+ *
+ * Return: Returns 0 on success, error on failure.
+ */
+int security_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
+ struct bpf_token *token)
+{
+ return call_int_hook(bpf_map_create, map, attr, token);
+}
+
+/**
+ * security_bpf_prog_load() - Check if loading of BPF program is allowed
+ * @prog: BPF program object
+ * @attr: BPF syscall attributes used to create BPF program
+ * @token: BPF token used to grant user access to BPF subsystem
+ *
+ * Perform an access control check when the kernel loads a BPF program and
+ * allocates associated BPF program object. This hook is also responsible for
+ * allocating any required LSM state for the BPF program.
+ *
+ * Return: Returns 0 on success, error on failure.
+ */
+int security_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
+ struct bpf_token *token)
+{
+ return call_int_hook(bpf_prog_load, prog, attr, token);
+}
+
+/**
+ * security_bpf_token_create() - Check if creating of BPF token is allowed
+ * @token: BPF token object
+ * @attr: BPF syscall attributes used to create BPF token
+ * @path: path pointing to BPF FS mount point from which BPF token is created
+ *
+ * Do a check when the kernel instantiates a new BPF token object from BPF FS
+ * instance. This is also the point where LSM blob can be allocated for LSMs.
+ *
+ * Return: Returns 0 on success, error on failure.
+ */
+int security_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
+ const struct path *path)
+{
+ return call_int_hook(bpf_token_create, token, attr, path);
+}
+
+/**
+ * security_bpf_token_cmd() - Check if BPF token is allowed to delegate
+ * requested BPF syscall command
+ * @token: BPF token object
+ * @cmd: BPF syscall command requested to be delegated by BPF token
*
- * Initialize the security field inside bpf map.
+ * Do a check when the kernel decides whether provided BPF token should allow
+ * delegation of requested BPF syscall command.
*
* Return: Returns 0 on success, error on failure.
*/
-int security_bpf_map_alloc(struct bpf_map *map)
+int security_bpf_token_cmd(const struct bpf_token *token, enum bpf_cmd cmd)
{
- return call_int_hook(bpf_map_alloc_security, 0, map);
+ return call_int_hook(bpf_token_cmd, token, cmd);
}
/**
- * security_bpf_prog_alloc() - Allocate a bpf program LSM blob
- * @aux: bpf program aux info struct
+ * security_bpf_token_capable() - Check if BPF token is allowed to delegate
+ * requested BPF-related capability
+ * @token: BPF token object
+ * @cap: capabilities requested to be delegated by BPF token
*
- * Initialize the security field inside bpf program.
+ * Do a check when the kernel decides whether provided BPF token should allow
+ * delegation of requested BPF-related capabilities.
*
* Return: Returns 0 on success, error on failure.
*/
-int security_bpf_prog_alloc(struct bpf_prog_aux *aux)
+int security_bpf_token_capable(const struct bpf_token *token, int cap)
{
- return call_int_hook(bpf_prog_alloc_security, 0, aux);
+ return call_int_hook(bpf_token_capable, token, cap);
}
/**
@@ -5481,18 +5760,29 @@ int security_bpf_prog_alloc(struct bpf_prog_aux *aux)
*/
void security_bpf_map_free(struct bpf_map *map)
{
- call_void_hook(bpf_map_free_security, map);
+ call_void_hook(bpf_map_free, map);
}
/**
- * security_bpf_prog_free() - Free a bpf program's LSM blob
- * @aux: bpf program aux info struct
+ * security_bpf_prog_free() - Free a BPF program's LSM blob
+ * @prog: BPF program struct
*
- * Clean up the security information stored inside bpf prog.
+ * Clean up the security information stored inside BPF program.
*/
-void security_bpf_prog_free(struct bpf_prog_aux *aux)
+void security_bpf_prog_free(struct bpf_prog *prog)
{
- call_void_hook(bpf_prog_free_security, aux);
+ call_void_hook(bpf_prog_free, prog);
+}
+
+/**
+ * security_bpf_token_free() - Free a BPF token's LSM blob
+ * @token: BPF token struct
+ *
+ * Clean up the security information stored inside BPF token.
+ */
+void security_bpf_token_free(struct bpf_token *token)
+{
+ call_void_hook(bpf_token_free, token);
}
#endif /* CONFIG_BPF_SYSCALL */
@@ -5507,10 +5797,89 @@ void security_bpf_prog_free(struct bpf_prog_aux *aux)
*/
int security_locked_down(enum lockdown_reason what)
{
- return call_int_hook(locked_down, 0, what);
+ return call_int_hook(locked_down, what);
}
EXPORT_SYMBOL(security_locked_down);
+/**
+ * security_bdev_alloc() - Allocate a block device LSM blob
+ * @bdev: block device
+ *
+ * Allocate and attach a security structure to @bdev->bd_security. The
+ * security field is initialized to NULL when the bdev structure is
+ * allocated.
+ *
+ * Return: Return 0 if operation was successful.
+ */
+int security_bdev_alloc(struct block_device *bdev)
+{
+ int rc = 0;
+
+ rc = lsm_bdev_alloc(bdev);
+ if (unlikely(rc))
+ return rc;
+
+ rc = call_int_hook(bdev_alloc_security, bdev);
+ if (unlikely(rc))
+ security_bdev_free(bdev);
+
+ return rc;
+}
+EXPORT_SYMBOL(security_bdev_alloc);
+
+/**
+ * security_bdev_free() - Free a block device's LSM blob
+ * @bdev: block device
+ *
+ * Deallocate the bdev security structure and set @bdev->bd_security to NULL.
+ */
+void security_bdev_free(struct block_device *bdev)
+{
+ if (!bdev->bd_security)
+ return;
+
+ call_void_hook(bdev_free_security, bdev);
+
+ kfree(bdev->bd_security);
+ bdev->bd_security = NULL;
+}
+EXPORT_SYMBOL(security_bdev_free);
+
+/**
+ * security_bdev_setintegrity() - Set the device's integrity data
+ * @bdev: block device
+ * @type: type of integrity, e.g. hash digest, signature, etc
+ * @value: the integrity value
+ * @size: size of the integrity value
+ *
+ * Register a verified integrity measurement of a bdev with LSMs.
+ * LSMs should free the previously saved data if @value is NULL.
+ * Please note that the new hook should be invoked every time the security
+ * information is updated to keep these data current. For example, in dm-verity,
+ * if the mapping table is reloaded and configured to use a different dm-verity
+ * target with a new roothash and signing information, the previously stored
+ * data in the LSM blob will become obsolete. It is crucial to re-invoke the
+ * hook to refresh these data and ensure they are up to date. This necessity
+ * arises from the design of device-mapper, where a device-mapper device is
+ * first created, and then targets are subsequently loaded into it. These
+ * targets can be modified multiple times during the device's lifetime.
+ * Therefore, while the LSM blob is allocated during the creation of the block
+ * device, its actual contents are not initialized at this stage and can change
+ * substantially over time. This includes alterations from data that the LSMs
+ * 'trusts' to those they do not, making it essential to handle these changes
+ * correctly. Failure to address this dynamic aspect could potentially allow
+ * for bypassing LSM checks.
+ *
+ * Return: Returns 0 on success, negative values on failure.
+ */
+int security_bdev_setintegrity(struct block_device *bdev,
+ enum lsm_integrity_type type, const void *value,
+ size_t size)
+{
+ return call_int_hook(bdev_setintegrity, bdev, type, value, size);
+}
+EXPORT_SYMBOL(security_bdev_setintegrity);
+
#ifdef CONFIG_PERF_EVENTS
/**
* security_perf_event_open() - Check if a perf event open is allowed
@@ -5523,7 +5892,7 @@ EXPORT_SYMBOL(security_locked_down);
*/
int security_perf_event_open(struct perf_event_attr *attr, int type)
{
- return call_int_hook(perf_event_open, 0, attr, type);
+ return call_int_hook(perf_event_open, attr, type);
}
/**
@@ -5536,7 +5905,19 @@ int security_perf_event_open(struct perf_event_attr *attr, int type)
*/
int security_perf_event_alloc(struct perf_event *event)
{
- return call_int_hook(perf_event_alloc, 0, event);
+ int rc;
+
+ rc = lsm_blob_alloc(&event->security, blob_sizes.lbs_perf_event,
+ GFP_KERNEL);
+ if (rc)
+ return rc;
+
+ rc = call_int_hook(perf_event_alloc, event);
+ if (rc) {
+ kfree(event->security);
+ event->security = NULL;
+ }
+ return rc;
}
/**
@@ -5547,7 +5928,8 @@ int security_perf_event_alloc(struct perf_event *event)
*/
void security_perf_event_free(struct perf_event *event)
{
- call_void_hook(perf_event_free, event);
+ kfree(event->security);
+ event->security = NULL;
}
/**
@@ -5560,7 +5942,7 @@ void security_perf_event_free(struct perf_event *event)
*/
int security_perf_event_read(struct perf_event *event)
{
- return call_int_hook(perf_event_read, 0, event);
+ return call_int_hook(perf_event_read, event);
}
/**
@@ -5573,7 +5955,7 @@ int security_perf_event_read(struct perf_event *event)
*/
int security_perf_event_write(struct perf_event *event)
{
- return call_int_hook(perf_event_write, 0, event);
+ return call_int_hook(perf_event_write, event);
}
#endif /* CONFIG_PERF_EVENTS */
@@ -5589,7 +5971,7 @@ int security_perf_event_write(struct perf_event *event)
*/
int security_uring_override_creds(const struct cred *new)
{
- return call_int_hook(uring_override_creds, 0, new);
+ return call_int_hook(uring_override_creds, new);
}
/**
@@ -5602,7 +5984,7 @@ int security_uring_override_creds(const struct cred *new)
*/
int security_uring_sqpoll(void)
{
- return call_int_hook(uring_sqpoll, 0);
+ return call_int_hook(uring_sqpoll);
}
/**
@@ -5615,6 +5997,16 @@ int security_uring_sqpoll(void)
*/
int security_uring_cmd(struct io_uring_cmd *ioucmd)
{
- return call_int_hook(uring_cmd, 0, ioucmd);
+ return call_int_hook(uring_cmd, ioucmd);
}
#endif /* CONFIG_IO_URING */
+
+/**
+ * security_initramfs_populated() - Notify LSMs that initramfs has been loaded
+ *
+ * Tells the LSMs the initramfs has been unpacked into the rootfs.
+ */
+void security_initramfs_populated(void)
+{
+ call_void_hook(initramfs_populated);
+}
diff --git a/security/selinux/.gitignore b/security/selinux/.gitignore
index 168fae13ca5a..01c0df8ab009 100644
--- a/security/selinux/.gitignore
+++ b/security/selinux/.gitignore
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
av_permissions.h
flask.h
+/genheaders
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index c47519ed8156..66e56e9011df 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -33,10 +33,12 @@ $(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h
quiet_cmd_genhdrs = GEN $(addprefix $(obj)/,$(genhdrs))
cmd_genhdrs = $< $(addprefix $(obj)/,$(genhdrs))
-# see the note above, replace the $targets and 'flask.h' rule with the lines
-# below:
-# targets += $(genhdrs)
-# $(addprefix $(obj)/,$(genhdrs)) &: scripts/selinux/...
-targets += flask.h
-$(obj)/flask.h: scripts/selinux/genheaders/genheaders FORCE
+targets += $(genhdrs)
+
+# see the note above, replace the 'flask.h' rule with the line below:
+# $(addprefix $(obj)/,$(genhdrs)) &: $(obj)/genheaders FORCE
+$(obj)/flask.h: $(obj)/genheaders FORCE
$(call if_changed,genhdrs)
+
+hostprogs := genheaders
+HOST_EXTRACFLAGS += -I$(srctree)/security/selinux/include
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 32eb67fb3e42..1f2680bcc43a 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -134,18 +134,10 @@ static inline u32 avc_hash(u32 ssid, u32 tsid, u16 tclass)
*/
void __init avc_init(void)
{
- avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
- 0, SLAB_PANIC, NULL);
- avc_xperms_cachep = kmem_cache_create("avc_xperms_node",
- sizeof(struct avc_xperms_node),
- 0, SLAB_PANIC, NULL);
- avc_xperms_decision_cachep = kmem_cache_create(
- "avc_xperms_decision_node",
- sizeof(struct avc_xperms_decision_node),
- 0, SLAB_PANIC, NULL);
- avc_xperms_data_cachep = kmem_cache_create("avc_xperms_data",
- sizeof(struct extended_perms_data),
- 0, SLAB_PANIC, NULL);
+ avc_node_cachep = KMEM_CACHE(avc_node, SLAB_PANIC);
+ avc_xperms_cachep = KMEM_CACHE(avc_xperms_node, SLAB_PANIC);
+ avc_xperms_decision_cachep = KMEM_CACHE(avc_xperms_decision_node, SLAB_PANIC);
+ avc_xperms_data_cachep = KMEM_CACHE(extended_perms_data, SLAB_PANIC);
}
int avc_get_hash_stats(char *page)
@@ -182,13 +174,15 @@ int avc_get_hash_stats(char *page)
* using a linked list for extended_perms_decision lookup because the list is
* always small. i.e. less than 5, typically 1
*/
-static struct extended_perms_decision *avc_xperms_decision_lookup(u8 driver,
- struct avc_xperms_node *xp_node)
+static struct extended_perms_decision *
+avc_xperms_decision_lookup(u8 driver, u8 base_perm,
+ struct avc_xperms_node *xp_node)
{
struct avc_xperms_decision_node *xpd_node;
list_for_each_entry(xpd_node, &xp_node->xpd_head, xpd_list) {
- if (xpd_node->xpd.driver == driver)
+ if (xpd_node->xpd.driver == driver &&
+ xpd_node->xpd.base_perm == base_perm)
return &xpd_node->xpd;
}
return NULL;
@@ -213,11 +207,12 @@ avc_xperms_has_perm(struct extended_perms_decision *xpd,
}
static void avc_xperms_allow_perm(struct avc_xperms_node *xp_node,
- u8 driver, u8 perm)
+ u8 driver, u8 base_perm, u8 perm)
{
struct extended_perms_decision *xpd;
security_xperm_set(xp_node->xp.drivers.p, driver);
- xpd = avc_xperms_decision_lookup(driver, xp_node);
+ xp_node->xp.base_perms |= base_perm;
+ xpd = avc_xperms_decision_lookup(driver, base_perm, xp_node);
if (xpd && xpd->allowed)
security_xperm_set(xpd->allowed->p, perm);
}
@@ -253,6 +248,7 @@ static void avc_xperms_free(struct avc_xperms_node *xp_node)
static void avc_copy_xperms_decision(struct extended_perms_decision *dest,
struct extended_perms_decision *src)
{
+ dest->base_perm = src->base_perm;
dest->driver = src->driver;
dest->used = src->used;
if (dest->used & XPERMS_ALLOWED)
@@ -280,6 +276,7 @@ static inline void avc_quick_copy_xperms_decision(u8 perm,
*/
u8 i = perm >> 5;
+ dest->base_perm = src->base_perm;
dest->used = src->used;
if (dest->used & XPERMS_ALLOWED)
dest->allowed->p[i] = src->allowed->p[i];
@@ -330,12 +327,12 @@ static int avc_add_xperms_decision(struct avc_node *node,
{
struct avc_xperms_decision_node *dest_xpd;
- node->ae.xp_node->xp.len++;
dest_xpd = avc_xperms_decision_alloc(src->used);
if (!dest_xpd)
return -ENOMEM;
avc_copy_xperms_decision(&dest_xpd->xpd, src);
list_add(&dest_xpd->xpd_list, &node->ae.xp_node->xpd_head);
+ node->ae.xp_node->xp.len++;
return 0;
}
@@ -365,6 +362,7 @@ static int avc_xperms_populate(struct avc_node *node,
memcpy(dest->xp.drivers.p, src->xp.drivers.p, sizeof(dest->xp.drivers.p));
dest->xp.len = src->xp.len;
+ dest->xp.base_perms = src->xp.base_perms;
/* for each source xpd allocate a destination xpd and copy */
list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) {
@@ -396,7 +394,7 @@ static inline u32 avc_xperms_audit_required(u32 requested,
audited = denied & avd->auditdeny;
if (audited && xpd) {
if (avc_xperms_has_perm(xpd, perm, XPERMS_DONTAUDIT))
- audited &= ~requested;
+ audited = 0;
}
} else if (result) {
audited = denied = requested;
@@ -404,7 +402,7 @@ static inline u32 avc_xperms_audit_required(u32 requested,
audited = requested & avd->auditallow;
if (audited && xpd) {
if (!avc_xperms_has_perm(xpd, perm, XPERMS_AUDITALLOW))
- audited &= ~requested;
+ audited = 0;
}
}
@@ -815,6 +813,7 @@ out:
* @event : Updating event
* @perms : Permission mask bits
* @driver: xperm driver information
+ * @base_perm: the base permission associated with the extended permission
* @xperm: xperm permissions
* @ssid: AVC entry source sid
* @tsid: AVC entry target sid
@@ -828,10 +827,9 @@ out:
* otherwise, this function updates the AVC entry. The original AVC-entry object
* will release later by RCU.
*/
-static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
- u32 tsid, u16 tclass, u32 seqno,
- struct extended_perms_decision *xpd,
- u32 flags)
+static int avc_update_node(u32 event, u32 perms, u8 driver, u8 base_perm,
+ u8 xperm, u32 ssid, u32 tsid, u16 tclass, u32 seqno,
+ struct extended_perms_decision *xpd, u32 flags)
{
u32 hvalue;
int rc = 0;
@@ -888,7 +886,7 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
case AVC_CALLBACK_GRANT:
node->ae.avd.allowed |= perms;
if (node->ae.xp_node && (flags & AVC_EXTENDED_PERMS))
- avc_xperms_allow_perm(node->ae.xp_node, driver, xperm);
+ avc_xperms_allow_perm(node->ae.xp_node, driver, base_perm, xperm);
break;
case AVC_CALLBACK_TRY_REVOKE:
case AVC_CALLBACK_REVOKE:
@@ -907,7 +905,11 @@ static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
node->ae.avd.auditdeny &= ~perms;
break;
case AVC_CALLBACK_ADD_XPERMS:
- avc_add_xperms_decision(node, xpd);
+ rc = avc_add_xperms_decision(node, xpd);
+ if (rc) {
+ avc_node_kill(node);
+ goto out_unlock;
+ }
break;
}
avc_node_replace(node, orig);
@@ -991,10 +993,9 @@ static noinline void avc_compute_av(u32 ssid, u32 tsid, u16 tclass,
avc_insert(ssid, tsid, tclass, avd, xp_node);
}
-static noinline int avc_denied(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- u8 driver, u8 xperm, unsigned int flags,
- struct av_decision *avd)
+static noinline int avc_denied(u32 ssid, u32 tsid, u16 tclass, u32 requested,
+ u8 driver, u8 base_perm, u8 xperm,
+ unsigned int flags, struct av_decision *avd)
{
if (flags & AVC_STRICT)
return -EACCES;
@@ -1003,7 +1004,7 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
!(avd->flags & AVD_FLAGS_PERMISSIVE))
return -EACCES;
- avc_update_node(AVC_CALLBACK_GRANT, requested, driver,
+ avc_update_node(AVC_CALLBACK_GRANT, requested, driver, base_perm,
xperm, ssid, tsid, tclass, avd->seqno, NULL, flags);
return 0;
}
@@ -1016,7 +1017,8 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
* driver field is used to specify which set contains the permission.
*/
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
- u8 driver, u8 xperm, struct common_audit_data *ad)
+ u8 driver, u8 base_perm, u8 xperm,
+ struct common_audit_data *ad)
{
struct avc_node *node;
struct av_decision avd;
@@ -1051,22 +1053,23 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
local_xpd.auditallow = &auditallow;
local_xpd.dontaudit = &dontaudit;
- xpd = avc_xperms_decision_lookup(driver, xp_node);
+ xpd = avc_xperms_decision_lookup(driver, base_perm, xp_node);
if (unlikely(!xpd)) {
/*
* Compute the extended_perms_decision only if the driver
- * is flagged
+ * is flagged and the base permission is known.
*/
- if (!security_xperm_test(xp_node->xp.drivers.p, driver)) {
+ if (!security_xperm_test(xp_node->xp.drivers.p, driver) ||
+ !(xp_node->xp.base_perms & base_perm)) {
avd.allowed &= ~requested;
goto decision;
}
rcu_read_unlock();
- security_compute_xperms_decision(ssid, tsid, tclass,
- driver, &local_xpd);
+ security_compute_xperms_decision(ssid, tsid, tclass, driver,
+ base_perm, &local_xpd);
rcu_read_lock();
- avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested,
- driver, xperm, ssid, tsid, tclass, avd.seqno,
+ avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, driver,
+ base_perm, xperm, ssid, tsid, tclass, avd.seqno,
&local_xpd, 0);
} else {
avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd);
@@ -1079,8 +1082,8 @@ int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
decision:
denied = requested & ~(avd.allowed);
if (unlikely(denied))
- rc = avc_denied(ssid, tsid, tclass, requested,
- driver, xperm, AVC_EXTENDED_PERMS, &avd);
+ rc = avc_denied(ssid, tsid, tclass, requested, driver,
+ base_perm, xperm, AVC_EXTENDED_PERMS, &avd);
rcu_read_unlock();
@@ -1114,7 +1117,7 @@ static noinline int avc_perm_nonode(u32 ssid, u32 tsid, u16 tclass,
avc_compute_av(ssid, tsid, tclass, avd, &xp_node);
denied = requested & ~(avd->allowed);
if (unlikely(denied))
- return avc_denied(ssid, tsid, tclass, requested, 0, 0,
+ return avc_denied(ssid, tsid, tclass, requested, 0, 0, 0,
flags, avd);
return 0;
}
@@ -1162,7 +1165,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
rcu_read_unlock();
if (unlikely(denied))
- return avc_denied(ssid, tsid, tclass, requested, 0, 0,
+ return avc_denied(ssid, tsid, tclass, requested, 0, 0, 0,
flags, avd);
return 0;
}
diff --git a/security/selinux/genheaders.c b/security/selinux/genheaders.c
new file mode 100644
index 000000000000..3834d7eb0af6
--- /dev/null
+++ b/security/selinux/genheaders.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+struct security_class_mapping {
+ const char *name;
+ const char *perms[sizeof(unsigned) * 8 + 1];
+};
+
+#include "classmap.h"
+#include "initial_sid_to_string.h"
+
+const char *progname;
+
+static void usage(void)
+{
+ printf("usage: %s flask.h av_permissions.h\n", progname);
+ exit(1);
+}
+
+static char *stoupperx(const char *s)
+{
+ char *s2 = strdup(s);
+ char *p;
+
+ if (!s2) {
+ fprintf(stderr, "%s: out of memory\n", progname);
+ exit(3);
+ }
+
+ for (p = s2; *p; p++)
+ *p = toupper(*p);
+ return s2;
+}
+
+int main(int argc, char *argv[])
+{
+ int i, j;
+ int isids_len;
+ FILE *fout;
+
+ progname = argv[0];
+
+ if (argc < 3)
+ usage();
+
+ fout = fopen(argv[1], "w");
+ if (!fout) {
+ fprintf(stderr, "Could not open %s for writing: %s\n",
+ argv[1], strerror(errno));
+ exit(2);
+ }
+
+ fprintf(fout, "/* This file is automatically generated. Do not edit. */\n");
+ fprintf(fout, "#ifndef _SELINUX_FLASK_H_\n#define _SELINUX_FLASK_H_\n\n");
+
+ for (i = 0; secclass_map[i].name; i++) {
+ char *name = stoupperx(secclass_map[i].name);
+
+ fprintf(fout, "#define SECCLASS_%-39s %2d\n", name, i+1);
+ free(name);
+ }
+
+ fprintf(fout, "\n");
+
+ isids_len = sizeof(initial_sid_to_string) / sizeof(char *);
+ for (i = 1; i < isids_len; i++) {
+ const char *s = initial_sid_to_string[i];
+ if (s) {
+ char *sidname = stoupperx(s);
+
+ fprintf(fout, "#define SECINITSID_%-39s %2d\n", sidname, i);
+ free(sidname);
+ }
+ }
+ fprintf(fout, "\n#define SECINITSID_NUM %d\n", i-1);
+ fprintf(fout, "\nstatic inline bool security_is_socket_class(u16 kern_tclass)\n");
+ fprintf(fout, "{\n");
+ fprintf(fout, "\tbool sock = false;\n\n");
+ fprintf(fout, "\tswitch (kern_tclass) {\n");
+ for (i = 0; secclass_map[i].name; i++) {
+ static char s[] = "SOCKET";
+ int len, l;
+ char *name = stoupperx(secclass_map[i].name);
+
+ len = strlen(name);
+ l = sizeof(s) - 1;
+ if (len >= l && memcmp(name + len - l, s, l) == 0)
+ fprintf(fout, "\tcase SECCLASS_%s:\n", name);
+ free(name);
+ }
+ fprintf(fout, "\t\tsock = true;\n");
+ fprintf(fout, "\t\tbreak;\n");
+ fprintf(fout, "\tdefault:\n");
+ fprintf(fout, "\t\tbreak;\n");
+ fprintf(fout, "\t}\n\n");
+ fprintf(fout, "\treturn sock;\n");
+ fprintf(fout, "}\n");
+
+ fprintf(fout, "\n#endif\n");
+
+ if (fclose(fout) != 0) {
+ fprintf(stderr, "Could not successfully close %s: %s\n",
+ argv[1], strerror(errno));
+ exit(4);
+ }
+
+ fout = fopen(argv[2], "w");
+ if (!fout) {
+ fprintf(stderr, "Could not open %s for writing: %s\n",
+ argv[2], strerror(errno));
+ exit(5);
+ }
+
+ fprintf(fout, "/* This file is automatically generated. Do not edit. */\n");
+ fprintf(fout, "#ifndef _SELINUX_AV_PERMISSIONS_H_\n#define _SELINUX_AV_PERMISSIONS_H_\n\n");
+
+ for (i = 0; secclass_map[i].name; i++) {
+ const struct security_class_mapping *map = &secclass_map[i];
+ int len;
+ char *name = stoupperx(map->name);
+
+ len = strlen(name);
+ for (j = 0; map->perms[j]; j++) {
+ char *permname;
+
+ if (j >= 32) {
+ fprintf(stderr, "Too many permissions to fit into an access vector at (%s, %s).\n",
+ map->name, map->perms[j]);
+ exit(5);
+ }
+ permname = stoupperx(map->perms[j]);
+ fprintf(fout, "#define %s__%-*s 0x%08xU\n", name,
+ 39-len, permname, 1U<<j);
+ free(permname);
+ }
+ free(name);
+ }
+
+ fprintf(fout, "\n#endif\n");
+
+ if (fclose(fout) != 0) {
+ fprintf(stderr, "Could not successfully close %s: %s\n",
+ argv[2], strerror(errno));
+ exit(6);
+ }
+
+ exit(0);
+}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 338b023a8c3e..7b867dfec88b 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -282,8 +282,13 @@ static int __inode_security_revalidate(struct inode *inode,
might_sleep_if(may_sleep);
+ /*
+ * The check of isec->initialized below is racy but
+ * inode_doinit_with_dentry() will recheck with
+ * isec->lock held.
+ */
if (selinux_initialized() &&
- isec->initialized != LABEL_INITIALIZED) {
+ data_race(isec->initialized != LABEL_INITIALIZED)) {
if (!may_sleep)
return -ECHILD;
@@ -402,7 +407,7 @@ static const struct {
static int match_opt_prefix(char *s, int l, char **arg)
{
- int i;
+ unsigned int i;
for (i = 0; i < ARRAY_SIZE(tokens); i++) {
size_t len = tokens[i].len;
@@ -2202,23 +2207,16 @@ static int selinux_syslog(int type)
}
/*
- * Check that a process has enough memory to allocate a new virtual
- * mapping. 0 means there is enough memory for the allocation to
- * succeed and -ENOMEM implies there is not.
+ * Check permission for allocating a new virtual mapping. Returns
+ * 0 if permission is granted, negative error code if not.
*
* Do not audit the selinux permission check, as this is applied to all
* processes that allocate mappings.
*/
static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
{
- int rc, cap_sys_admin = 0;
-
- rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN,
- CAP_OPT_NOAUDIT, true);
- if (rc == 0)
- cap_sys_admin = 1;
-
- return cap_sys_admin;
+ return cred_has_capability(current_cred(), CAP_SYS_ADMIN,
+ CAP_OPT_NOAUDIT, true);
}
/* binprm security operations */
@@ -2871,8 +2869,8 @@ static void selinux_inode_free_security(struct inode *inode)
static int selinux_dentry_init_security(struct dentry *dentry, int mode,
const struct qstr *name,
- const char **xattr_name, void **ctx,
- u32 *ctxlen)
+ const char **xattr_name,
+ struct lsm_context *cp)
{
u32 newsid;
int rc;
@@ -2887,8 +2885,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
if (xattr_name)
*xattr_name = XATTR_NAME_SELINUX;
- return security_sid_to_context(newsid, (char **)ctx,
- ctxlen);
+ cp->id = LSM_ID_SELINUX;
+ return security_sid_to_context(newsid, &cp->context, &cp->len);
}
static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,
@@ -2920,23 +2918,22 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
struct superblock_security_struct *sbsec;
struct xattr *xattr = lsm_get_xattr_slot(xattrs, xattr_count);
u32 newsid, clen;
+ u16 newsclass;
int rc;
char *context;
sbsec = selinux_superblock(dir->i_sb);
newsid = tsec->create_sid;
-
- rc = selinux_determine_inode_label(tsec, dir, qstr,
- inode_mode_to_security_class(inode->i_mode),
- &newsid);
+ newsclass = inode_mode_to_security_class(inode->i_mode);
+ rc = selinux_determine_inode_label(tsec, dir, qstr, newsclass, &newsid);
if (rc)
return rc;
/* Possibly defer initialization to selinux_complete_init. */
if (sbsec->flags & SE_SBINITIALIZED) {
struct inode_security_struct *isec = selinux_inode(inode);
- isec->sclass = inode_mode_to_security_class(inode->i_mode);
+ isec->sclass = newsclass;
isec->sid = newsid;
isec->initialized = LABEL_INITIALIZED;
}
@@ -2962,7 +2959,7 @@ static int selinux_inode_init_security_anon(struct inode *inode,
const struct qstr *name,
const struct inode *context_inode)
{
- const struct task_security_struct *tsec = selinux_cred(current_cred());
+ u32 sid = current_sid();
struct common_audit_data ad;
struct inode_security_struct *isec;
int rc;
@@ -2991,7 +2988,7 @@ static int selinux_inode_init_security_anon(struct inode *inode,
} else {
isec->sclass = SECCLASS_ANON_INODE;
rc = security_transition_sid(
- tsec->sid, tsec->sid,
+ sid, sid,
isec->sclass, name, &isec->sid);
if (rc)
return rc;
@@ -3006,7 +3003,7 @@ static int selinux_inode_init_security_anon(struct inode *inode,
ad.type = LSM_AUDIT_DATA_ANONINODE;
ad.u.anonclass = name ? (const char *)name->name : "?";
- return avc_has_perm(tsec->sid,
+ return avc_has_perm(sid,
isec->sid,
isec->sclass,
FILE__CREATE,
@@ -3064,14 +3061,12 @@ static int selinux_inode_readlink(struct dentry *dentry)
static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
bool rcu)
{
- const struct cred *cred = current_cred();
struct common_audit_data ad;
struct inode_security_struct *isec;
- u32 sid;
+ u32 sid = current_sid();
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
- sid = cred_sid(cred);
isec = inode_security_rcu(inode, rcu);
if (IS_ERR(isec))
return PTR_ERR(isec);
@@ -3095,12 +3090,11 @@ static noinline int audit_inode_permission(struct inode *inode,
static int selinux_inode_permission(struct inode *inode, int mask)
{
- const struct cred *cred = current_cred();
u32 perms;
bool from_access;
bool no_block = mask & MAY_NOT_BLOCK;
struct inode_security_struct *isec;
- u32 sid;
+ u32 sid = current_sid();
struct av_decision avd;
int rc, rc2;
u32 audited, denied;
@@ -3117,7 +3111,6 @@ static int selinux_inode_permission(struct inode *inode, int mask)
perms = file_mask_to_av(inode->i_mode, mask);
- sid = cred_sid(cred);
isec = inode_security_rcu(inode, no_block);
if (IS_ERR(isec))
return PTR_ERR(isec);
@@ -3136,12 +3129,13 @@ static int selinux_inode_permission(struct inode *inode, int mask)
return rc;
}
-static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *iattr)
{
const struct cred *cred = current_cred();
struct inode *inode = d_backing_inode(dentry);
unsigned int ia_valid = iattr->ia_valid;
- __u32 av = FILE__WRITE;
+ u32 av = FILE__WRITE;
/* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */
if (ia_valid & ATTR_FORCE) {
@@ -3181,6 +3175,23 @@ static bool has_cap_mac_admin(bool audit)
return true;
}
+/**
+ * selinux_inode_xattr_skipcap - Skip the xattr capability checks?
+ * @name: name of the xattr
+ *
+ * Returns 1 to indicate that SELinux "owns" the access control rights to xattrs
+ * named @name; the LSM layer should avoid enforcing any traditional
+ * capability based access controls on this xattr. Returns 0 to indicate that
+ * SELinux does not "own" the access control rights to xattrs named @name and is
+ * deferring to the LSM layer for further access controls, including capability
+ * based controls.
+ */
+static int selinux_inode_xattr_skipcap(const char *name)
+{
+ /* require capability check if not a selinux xattr */
+ return !strcmp(name, XATTR_NAME_SELINUX);
+}
+
static int selinux_inode_setxattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
@@ -3192,15 +3203,9 @@ static int selinux_inode_setxattr(struct mnt_idmap *idmap,
u32 newsid, sid = current_sid();
int rc = 0;
- if (strcmp(name, XATTR_NAME_SELINUX)) {
- rc = cap_inode_setxattr(dentry, name, value, size, flags);
- if (rc)
- return rc;
-
- /* Not an attribute we recognize, so just check the
- ordinary setattr permission. */
+ /* if not a selinux xattr, only check the ordinary setattr perm */
+ if (strcmp(name, XATTR_NAME_SELINUX))
return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
- }
if (!selinux_initialized())
return (inode_owner_or_capable(idmap, inode) ? 0 : -EPERM);
@@ -3349,15 +3354,9 @@ static int selinux_inode_listxattr(struct dentry *dentry)
static int selinux_inode_removexattr(struct mnt_idmap *idmap,
struct dentry *dentry, const char *name)
{
- if (strcmp(name, XATTR_NAME_SELINUX)) {
- int rc = cap_inode_removexattr(idmap, dentry, name);
- if (rc)
- return rc;
-
- /* Not an attribute we recognize, so just check the
- ordinary setattr permission. */
+ /* if not a selinux xattr, only check the ordinary setattr perm */
+ if (strcmp(name, XATTR_NAME_SELINUX))
return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
- }
if (!selinux_initialized())
return 0;
@@ -3405,7 +3404,8 @@ static int selinux_path_notify(const struct path *path, u64 mask,
perm |= FILE__WATCH_WITH_PERM;
/* watches on read-like events need the file:watch_reads permission */
- if (mask & (FS_ACCESS | FS_ACCESS_PERM | FS_CLOSE_NOWRITE))
+ if (mask & (FS_ACCESS | FS_ACCESS_PERM | FS_PRE_ACCESS |
+ FS_CLOSE_NOWRITE))
perm |= FILE__WATCH_READS;
return path_has_perm(current_cred(), path, perm);
@@ -3504,15 +3504,16 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
return len;
}
-static void selinux_inode_getsecid(struct inode *inode, u32 *secid)
+static void selinux_inode_getlsmprop(struct inode *inode, struct lsm_prop *prop)
{
struct inode_security_struct *isec = inode_security_novalidate(inode);
- *secid = isec->sid;
+
+ prop->selinux.secid = isec->sid;
}
static int selinux_inode_copy_up(struct dentry *src, struct cred **new)
{
- u32 sid;
+ struct lsm_prop prop;
struct task_security_struct *tsec;
struct cred *new_creds = *new;
@@ -3524,20 +3525,21 @@ static int selinux_inode_copy_up(struct dentry *src, struct cred **new)
tsec = selinux_cred(new_creds);
/* Get label from overlay inode and set it in create_sid */
- selinux_inode_getsecid(d_inode(src), &sid);
- tsec->create_sid = sid;
+ selinux_inode_getlsmprop(d_inode(src), &prop);
+ tsec->create_sid = prop.selinux.secid;
*new = new_creds;
return 0;
}
-static int selinux_inode_copy_up_xattr(const char *name)
+static int selinux_inode_copy_up_xattr(struct dentry *dentry, const char *name)
{
/* The copy_up hook above sets the initial context on an inode, but we
* don't then want to overwrite it by blindly copying all the lower
- * xattrs up. Instead, we have to filter out SELinux-related xattrs.
+ * xattrs up. Instead, filter out SELinux-related xattrs following
+ * policy load.
*/
- if (strcmp(name, XATTR_NAME_SELINUX) == 0)
- return 1; /* Discard */
+ if (selinux_initialized() && !strcmp(name, XATTR_NAME_SELINUX))
+ return -ECANCELED; /* Discard */
/*
* Any other attribute apart from SELINUX is not claimed, supported
* by selinux.
@@ -3687,8 +3689,8 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
return 0;
isec = inode_security(inode);
- rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
- requested, driver, xperm, &ad);
+ rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, requested,
+ driver, AVC_EXT_IOCTL, xperm, &ad);
out:
return rc;
}
@@ -3850,7 +3852,17 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
if (default_noexec &&
(prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
int rc = 0;
- if (vma_is_initial_heap(vma)) {
+ /*
+ * We don't use the vma_is_initial_heap() helper as it has
+ * a history of problems and is currently broken on systems
+ * where there is no heap, e.g. brk == start_brk. Before
+ * replacing the conditional below with vma_is_initial_heap(),
+ * or something similar, please ensure that the logic is the
+ * same as what we have below or you have tested every possible
+ * corner case you can think to test.
+ */
+ if (vma->vm_start >= vma->vm_mm->start_brk &&
+ vma->vm_end <= vma->vm_mm->brk) {
rc = avc_has_perm(sid, sid, SECCLASS_PROCESS,
PROCESS__EXECHEAP, NULL);
} else if (!vma->vm_file && (vma_is_initial_stack(vma) ||
@@ -3938,7 +3950,7 @@ static int selinux_file_send_sigiotask(struct task_struct *tsk,
struct file_security_struct *fsec;
/* struct fown_struct is never outside the context of a struct file */
- file = container_of(fown, struct file, f_owner);
+ file = fown->file;
fsec = selinux_file(file);
@@ -4024,6 +4036,11 @@ static void selinux_cred_getsecid(const struct cred *c, u32 *secid)
*secid = cred_sid(c);
}
+static void selinux_cred_getlsmprop(const struct cred *c, struct lsm_prop *prop)
+{
+ prop->selinux.secid = cred_sid(c);
+}
+
/*
* set the security data for a kernel service
* - all the creation contexts are set to unlabelled
@@ -4159,14 +4176,15 @@ static int selinux_task_getsid(struct task_struct *p)
PROCESS__GETSESSION, NULL);
}
-static void selinux_current_getsecid_subj(u32 *secid)
+static void selinux_current_getlsmprop_subj(struct lsm_prop *prop)
{
- *secid = current_sid();
+ prop->selinux.secid = current_sid();
}
-static void selinux_task_getsecid_obj(struct task_struct *p, u32 *secid)
+static void selinux_task_getlsmprop_obj(struct task_struct *p,
+ struct lsm_prop *prop)
{
- *secid = task_sid_obj(p);
+ prop->selinux.secid = task_sid_obj(p);
}
static int selinux_task_setnice(struct task_struct *p, int nice)
@@ -4580,14 +4598,10 @@ static int socket_sockcreate_sid(const struct task_security_struct *tsec,
secclass, NULL, socksid);
}
-static int sock_has_perm(struct sock *sk, u32 perms)
+static bool sock_skip_has_perm(u32 sid)
{
- struct sk_security_struct *sksec = sk->sk_security;
- struct common_audit_data ad;
- struct lsm_network_audit net;
-
- if (sksec->sid == SECINITSID_KERNEL)
- return 0;
+ if (sid == SECINITSID_KERNEL)
+ return true;
/*
* Before POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT, sockets that
@@ -4601,7 +4615,19 @@ static int sock_has_perm(struct sock *sk, u32 perms)
* setting.
*/
if (!selinux_policycap_userspace_initial_context() &&
- sksec->sid == SECINITSID_INIT)
+ sid == SECINITSID_INIT)
+ return true;
+ return false;
+}
+
+
+static int sock_has_perm(struct sock *sk, u32 perms)
+{
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct common_audit_data ad;
+ struct lsm_network_audit net;
+
+ if (sock_skip_has_perm(sksec->sid))
return 0;
ad_net_init_from_sk(&ad, &net, sk);
@@ -4650,7 +4676,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
isec->initialized = LABEL_INITIALIZED;
if (sock->sk) {
- sksec = sock->sk->sk_security;
+ sksec = selinux_sock(sock->sk);
sksec->sclass = sclass;
sksec->sid = sid;
/* Allows detection of the first association on this socket */
@@ -4666,8 +4692,8 @@ static int selinux_socket_post_create(struct socket *sock, int family,
static int selinux_socket_socketpair(struct socket *socka,
struct socket *sockb)
{
- struct sk_security_struct *sksec_a = socka->sk->sk_security;
- struct sk_security_struct *sksec_b = sockb->sk->sk_security;
+ struct sk_security_struct *sksec_a = selinux_sock(socka->sk);
+ struct sk_security_struct *sksec_b = selinux_sock(sockb->sk);
sksec_a->peer_sid = sksec_b->sid;
sksec_b->peer_sid = sksec_a->sid;
@@ -4682,7 +4708,7 @@ static int selinux_socket_socketpair(struct socket *socka,
static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
{
struct sock *sk = sock->sk;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
u16 family;
int err;
@@ -4810,7 +4836,7 @@ out:
return err;
err_af:
/* Note that SCTP services expect -EINVAL, others -EAFNOSUPPORT. */
- if (sksec->sclass == SECCLASS_SCTP_SOCKET)
+ if (sk->sk_protocol == IPPROTO_SCTP)
return -EINVAL;
return -EAFNOSUPPORT;
}
@@ -4822,7 +4848,7 @@ static int selinux_socket_connect_helper(struct socket *sock,
struct sockaddr *address, int addrlen)
{
struct sock *sk = sock->sk;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
int err;
err = sock_has_perm(sk, SOCKET__CONNECT);
@@ -5000,9 +5026,9 @@ static int selinux_socket_unix_stream_connect(struct sock *sock,
struct sock *other,
struct sock *newsk)
{
- struct sk_security_struct *sksec_sock = sock->sk_security;
- struct sk_security_struct *sksec_other = other->sk_security;
- struct sk_security_struct *sksec_new = newsk->sk_security;
+ struct sk_security_struct *sksec_sock = selinux_sock(sock);
+ struct sk_security_struct *sksec_other = selinux_sock(other);
+ struct sk_security_struct *sksec_new = selinux_sock(newsk);
struct common_audit_data ad;
struct lsm_network_audit net;
int err;
@@ -5031,8 +5057,8 @@ static int selinux_socket_unix_stream_connect(struct sock *sock,
static int selinux_socket_unix_may_send(struct socket *sock,
struct socket *other)
{
- struct sk_security_struct *ssec = sock->sk->sk_security;
- struct sk_security_struct *osec = other->sk->sk_security;
+ struct sk_security_struct *ssec = selinux_sock(sock->sk);
+ struct sk_security_struct *osec = selinux_sock(other->sk);
struct common_audit_data ad;
struct lsm_network_audit net;
@@ -5069,7 +5095,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
u16 family)
{
int err = 0;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
u32 sk_sid = sksec->sid;
struct common_audit_data ad;
struct lsm_network_audit net;
@@ -5098,7 +5124,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
int err, peerlbl_active, secmark_active;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
u16 family = sk->sk_family;
u32 sk_sid = sksec->sid;
struct common_audit_data ad;
@@ -5166,7 +5192,7 @@ static int selinux_socket_getpeersec_stream(struct socket *sock,
int err = 0;
char *scontext = NULL;
u32 scontext_len;
- struct sk_security_struct *sksec = sock->sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sock->sk);
u32 peer_sid = SECSID_NULL;
if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET ||
@@ -5194,11 +5220,11 @@ out_len:
return err;
}
-static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
+static int selinux_socket_getpeersec_dgram(struct socket *sock,
+ struct sk_buff *skb, u32 *secid)
{
u32 peer_secid = SECSID_NULL;
u16 family;
- struct inode_security_struct *isec;
if (skb && skb->protocol == htons(ETH_P_IP))
family = PF_INET;
@@ -5206,52 +5232,47 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
family = PF_INET6;
else if (sock)
family = sock->sk->sk_family;
- else
- goto out;
+ else {
+ *secid = SECSID_NULL;
+ return -EINVAL;
+ }
if (sock && family == PF_UNIX) {
+ struct inode_security_struct *isec;
isec = inode_security_novalidate(SOCK_INODE(sock));
peer_secid = isec->sid;
} else if (skb)
selinux_skb_peerlbl_sid(skb, family, &peer_secid);
-out:
*secid = peer_secid;
if (peer_secid == SECSID_NULL)
- return -EINVAL;
+ return -ENOPROTOOPT;
return 0;
}
static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
{
- struct sk_security_struct *sksec;
-
- sksec = kzalloc(sizeof(*sksec), priority);
- if (!sksec)
- return -ENOMEM;
+ struct sk_security_struct *sksec = selinux_sock(sk);
sksec->peer_sid = SECINITSID_UNLABELED;
sksec->sid = SECINITSID_UNLABELED;
sksec->sclass = SECCLASS_SOCKET;
selinux_netlbl_sk_security_reset(sksec);
- sk->sk_security = sksec;
return 0;
}
static void selinux_sk_free_security(struct sock *sk)
{
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
- sk->sk_security = NULL;
selinux_netlbl_sk_security_free(sksec);
- kfree(sksec);
}
static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
{
- struct sk_security_struct *sksec = sk->sk_security;
- struct sk_security_struct *newsksec = newsk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct sk_security_struct *newsksec = selinux_sock(newsk);
newsksec->sid = sksec->sid;
newsksec->peer_sid = sksec->peer_sid;
@@ -5265,7 +5286,7 @@ static void selinux_sk_getsecid(const struct sock *sk, u32 *secid)
if (!sk)
*secid = SECINITSID_ANY_SOCKET;
else {
- const struct sk_security_struct *sksec = sk->sk_security;
+ const struct sk_security_struct *sksec = selinux_sock(sk);
*secid = sksec->sid;
}
@@ -5275,7 +5296,7 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent)
{
struct inode_security_struct *isec =
inode_security_novalidate(SOCK_INODE(parent));
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
sk->sk_family == PF_UNIX)
@@ -5292,7 +5313,7 @@ static int selinux_sctp_process_new_assoc(struct sctp_association *asoc,
{
struct sock *sk = asoc->base.sk;
u16 family = sk->sk_family;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
struct common_audit_data ad;
struct lsm_network_audit net;
int err;
@@ -5347,7 +5368,7 @@ static int selinux_sctp_process_new_assoc(struct sctp_association *asoc,
static int selinux_sctp_assoc_request(struct sctp_association *asoc,
struct sk_buff *skb)
{
- struct sk_security_struct *sksec = asoc->base.sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(asoc->base.sk);
u32 conn_sid;
int err;
@@ -5380,7 +5401,7 @@ static int selinux_sctp_assoc_request(struct sctp_association *asoc,
static int selinux_sctp_assoc_established(struct sctp_association *asoc,
struct sk_buff *skb)
{
- struct sk_security_struct *sksec = asoc->base.sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(asoc->base.sk);
if (!selinux_policycap_extsockclass())
return 0;
@@ -5479,8 +5500,8 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname,
static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk,
struct sock *newsk)
{
- struct sk_security_struct *sksec = sk->sk_security;
- struct sk_security_struct *newsksec = newsk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct sk_security_struct *newsksec = selinux_sock(newsk);
/* If policy does not support SECCLASS_SCTP_SOCKET then call
* the non-sctp clone version.
@@ -5496,8 +5517,8 @@ static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk
static int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
{
- struct sk_security_struct *ssksec = ssk->sk_security;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *ssksec = selinux_sock(ssk);
+ struct sk_security_struct *sksec = selinux_sock(sk);
ssksec->sclass = sksec->sclass;
ssksec->sid = sksec->sid;
@@ -5512,7 +5533,7 @@ static int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk)
static int selinux_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
int err;
u16 family = req->rsk_ops->family;
u32 connsid;
@@ -5533,7 +5554,7 @@ static int selinux_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
static void selinux_inet_csk_clone(struct sock *newsk,
const struct request_sock *req)
{
- struct sk_security_struct *newsksec = newsk->sk_security;
+ struct sk_security_struct *newsksec = selinux_sock(newsk);
newsksec->sid = req->secid;
newsksec->peer_sid = req->peer_secid;
@@ -5550,7 +5571,7 @@ static void selinux_inet_csk_clone(struct sock *newsk,
static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
{
u16 family = sk->sk_family;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
/* handle mapped IPv4 packets arriving via IPv6 sockets */
if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
@@ -5561,13 +5582,7 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
static int selinux_secmark_relabel_packet(u32 sid)
{
- const struct task_security_struct *tsec;
- u32 tsid;
-
- tsec = selinux_cred(current_cred());
- tsid = tsec->sid;
-
- return avc_has_perm(tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO,
+ return avc_has_perm(current_sid(), sid, SECCLASS_PACKET, PACKET__RELABELTO,
NULL);
}
@@ -5587,24 +5602,14 @@ static void selinux_req_classify_flow(const struct request_sock *req,
flic->flowic_secid = req->secid;
}
-static int selinux_tun_dev_alloc_security(void **security)
+static int selinux_tun_dev_alloc_security(void *security)
{
- struct tun_security_struct *tunsec;
+ struct tun_security_struct *tunsec = selinux_tun_dev(security);
- tunsec = kzalloc(sizeof(*tunsec), GFP_KERNEL);
- if (!tunsec)
- return -ENOMEM;
tunsec->sid = current_sid();
-
- *security = tunsec;
return 0;
}
-static void selinux_tun_dev_free_security(void *security)
-{
- kfree(security);
-}
-
static int selinux_tun_dev_create(void)
{
u32 sid = current_sid();
@@ -5622,7 +5627,7 @@ static int selinux_tun_dev_create(void)
static int selinux_tun_dev_attach_queue(void *security)
{
- struct tun_security_struct *tunsec = security;
+ struct tun_security_struct *tunsec = selinux_tun_dev(security);
return avc_has_perm(current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET,
TUN_SOCKET__ATTACH_QUEUE, NULL);
@@ -5630,8 +5635,8 @@ static int selinux_tun_dev_attach_queue(void *security)
static int selinux_tun_dev_attach(struct sock *sk, void *security)
{
- struct tun_security_struct *tunsec = security;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct tun_security_struct *tunsec = selinux_tun_dev(security);
+ struct sk_security_struct *sksec = selinux_sock(sk);
/* we don't currently perform any NetLabel based labeling here and it
* isn't clear that we would want to do so anyway; while we could apply
@@ -5648,7 +5653,7 @@ static int selinux_tun_dev_attach(struct sock *sk, void *security)
static int selinux_tun_dev_open(void *security)
{
- struct tun_security_struct *tunsec = security;
+ struct tun_security_struct *tunsec = selinux_tun_dev(security);
u32 sid = current_sid();
int err;
@@ -5734,7 +5739,7 @@ static unsigned int selinux_ip_output(void *priv, struct sk_buff *skb,
/* we do this in the LOCAL_OUT path and not the POST_ROUTING path
* because we want to make sure we apply the necessary labeling
* before IPsec is applied so we can leverage AH protection */
- sk = skb->sk;
+ sk = sk_to_full_sk(skb->sk);
if (sk) {
struct sk_security_struct *sksec;
@@ -5754,7 +5759,7 @@ static unsigned int selinux_ip_output(void *priv, struct sk_buff *skb,
return NF_ACCEPT;
/* standard practice, label using the parent socket */
- sksec = sk->sk_security;
+ sksec = selinux_sock(sk);
sid = sksec->sid;
} else
sid = SECINITSID_KERNEL;
@@ -5777,7 +5782,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
sk = skb_to_full_sk(skb);
if (sk == NULL)
return NF_ACCEPT;
- sksec = sk->sk_security;
+ sksec = selinux_sock(sk);
ad_net_init_from_iif(&ad, &net, state->out->ifindex, state->pf);
if (selinux_parse_skb(skb, &ad, NULL, 0, &proto))
@@ -5866,7 +5871,7 @@ static unsigned int selinux_ip_postroute(void *priv,
u32 skb_sid;
struct sk_security_struct *sksec;
- sksec = sk->sk_security;
+ sksec = selinux_sock(sk);
if (selinux_skb_peerlbl_sid(skb, family, &skb_sid))
return NF_DROP;
/* At this point, if the returned skb peerlbl is SECSID_NULL
@@ -5895,7 +5900,7 @@ static unsigned int selinux_ip_postroute(void *priv,
} else {
/* Locally generated packet, fetch the security label from the
* associated socket. */
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
peer_sid = sksec->sid;
secmark_perm = PACKET__SEND;
}
@@ -5931,6 +5936,26 @@ static unsigned int selinux_ip_postroute(void *priv,
}
#endif /* CONFIG_NETFILTER */
+static int nlmsg_sock_has_extended_perms(struct sock *sk, u32 perms, u16 nlmsg_type)
+{
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct common_audit_data ad;
+ u8 driver;
+ u8 xperm;
+
+ if (sock_skip_has_perm(sksec->sid))
+ return 0;
+
+ ad.type = LSM_AUDIT_DATA_NLMSGTYPE;
+ ad.u.nlmsg_type = nlmsg_type;
+
+ driver = nlmsg_type >> 8;
+ xperm = nlmsg_type & 0xff;
+
+ return avc_has_extended_perms(current_sid(), sksec->sid, sksec->sclass,
+ perms, driver, AVC_EXT_NLMSG, xperm, &ad);
+}
+
static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
{
int rc = 0;
@@ -5938,7 +5963,7 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
unsigned int data_len = skb->len;
unsigned char *data = skb->data;
struct nlmsghdr *nlh;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
u16 sclass = sksec->sclass;
u32 perm;
@@ -5956,7 +5981,12 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
rc = selinux_nlmsg_lookup(sclass, nlh->nlmsg_type, &perm);
if (rc == 0) {
- rc = sock_has_perm(sk, perm);
+ if (selinux_policycap_netlink_xperm()) {
+ rc = nlmsg_sock_has_extended_perms(
+ sk, perm, nlh->nlmsg_type);
+ } else {
+ rc = sock_has_perm(sk, perm);
+ }
if (rc)
return rc;
} else if (rc == -EINVAL) {
@@ -6330,10 +6360,11 @@ static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
return ipc_has_perm(ipcp, av);
}
-static void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
+static void selinux_ipc_getlsmprop(struct kern_ipc_perm *ipcp,
+ struct lsm_prop *prop)
{
struct ipc_security_struct *isec = selinux_ipc(ipcp);
- *secid = isec->sid;
+ prop->selinux.secid = isec->sid;
}
static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
@@ -6345,55 +6376,55 @@ static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
static int selinux_lsm_getattr(unsigned int attr, struct task_struct *p,
char **value)
{
- const struct task_security_struct *__tsec;
- u32 sid;
+ const struct task_security_struct *tsec;
int error;
- unsigned len;
+ u32 sid;
+ u32 len;
rcu_read_lock();
- __tsec = selinux_cred(__task_cred(p));
-
- if (current != p) {
- error = avc_has_perm(current_sid(), __tsec->sid,
+ tsec = selinux_cred(__task_cred(p));
+ if (p != current) {
+ error = avc_has_perm(current_sid(), tsec->sid,
SECCLASS_PROCESS, PROCESS__GETATTR, NULL);
if (error)
- goto bad;
+ goto err_unlock;
}
-
switch (attr) {
case LSM_ATTR_CURRENT:
- sid = __tsec->sid;
+ sid = tsec->sid;
break;
case LSM_ATTR_PREV:
- sid = __tsec->osid;
+ sid = tsec->osid;
break;
case LSM_ATTR_EXEC:
- sid = __tsec->exec_sid;
+ sid = tsec->exec_sid;
break;
case LSM_ATTR_FSCREATE:
- sid = __tsec->create_sid;
+ sid = tsec->create_sid;
break;
case LSM_ATTR_KEYCREATE:
- sid = __tsec->keycreate_sid;
+ sid = tsec->keycreate_sid;
break;
case LSM_ATTR_SOCKCREATE:
- sid = __tsec->sockcreate_sid;
+ sid = tsec->sockcreate_sid;
break;
default:
error = -EOPNOTSUPP;
- goto bad;
+ goto err_unlock;
}
rcu_read_unlock();
- if (!sid)
+ if (sid == SECSID_NULL) {
+ *value = NULL;
return 0;
+ }
error = security_sid_to_context(sid, value, &len);
if (error)
return error;
return len;
-bad:
+err_unlock:
rcu_read_unlock();
return error;
}
@@ -6556,7 +6587,7 @@ abort_change:
* There will only ever be one attribute.
*/
static int selinux_getselfattr(unsigned int attr, struct lsm_ctx __user *ctx,
- size_t *size, u32 flags)
+ u32 *size, u32 flags)
{
int rc;
char *val = NULL;
@@ -6571,7 +6602,7 @@ static int selinux_getselfattr(unsigned int attr, struct lsm_ctx __user *ctx,
}
static int selinux_setselfattr(unsigned int attr, struct lsm_ctx *ctx,
- size_t size, u32 flags)
+ u32 size, u32 flags)
{
int rc;
@@ -6610,10 +6641,28 @@ static int selinux_ismaclabel(const char *name)
return (strcmp(name, XATTR_SELINUX_SUFFIX) == 0);
}
-static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+static int selinux_secid_to_secctx(u32 secid, struct lsm_context *cp)
{
- return security_sid_to_context(secid,
- secdata, seclen);
+ u32 seclen;
+ int ret;
+
+ if (cp) {
+ cp->id = LSM_ID_SELINUX;
+ ret = security_sid_to_context(secid, &cp->context, &cp->len);
+ if (ret < 0)
+ return ret;
+ return cp->len;
+ }
+ ret = security_sid_to_context(secid, NULL, &seclen);
+ if (ret < 0)
+ return ret;
+ return seclen;
+}
+
+static int selinux_lsmprop_to_secctx(struct lsm_prop *prop,
+ struct lsm_context *cp)
+{
+ return selinux_secid_to_secctx(prop->selinux.secid, cp);
}
static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
@@ -6622,9 +6671,13 @@ static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
secid, GFP_KERNEL);
}
-static void selinux_release_secctx(char *secdata, u32 seclen)
+static void selinux_release_secctx(struct lsm_context *cp)
{
- kfree(secdata);
+ if (cp->id == LSM_ID_SELINUX) {
+ kfree(cp->context);
+ cp->context = NULL;
+ cp->id = LSM_ID_UNDEF;
+ }
}
static void selinux_inode_invalidate_secctx(struct inode *inode)
@@ -6652,18 +6705,20 @@ static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen
*/
static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
{
- return __vfs_setxattr_noperm(&nop_mnt_idmap, dentry, XATTR_NAME_SELINUX,
- ctx, ctxlen, 0);
+ return __vfs_setxattr_locked(&nop_mnt_idmap, dentry, XATTR_NAME_SELINUX,
+ ctx, ctxlen, 0, NULL);
}
-static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+static int selinux_inode_getsecctx(struct inode *inode, struct lsm_context *cp)
{
- int len = 0;
+ int len;
len = selinux_inode_getsecurity(&nop_mnt_idmap, inode,
- XATTR_SELINUX_SUFFIX, ctx, true);
+ XATTR_SELINUX_SUFFIX,
+ (void **)&cp->context, true);
if (len < 0)
return len;
- *ctxlen = len;
+ cp->len = len;
+ cp->id = LSM_ID_SELINUX;
return 0;
}
#ifdef CONFIG_KEYS
@@ -6672,11 +6727,7 @@ static int selinux_key_alloc(struct key *k, const struct cred *cred,
unsigned long flags)
{
const struct task_security_struct *tsec;
- struct key_security_struct *ksec;
-
- ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
- if (!ksec)
- return -ENOMEM;
+ struct key_security_struct *ksec = selinux_key(k);
tsec = selinux_cred(cred);
if (tsec->keycreate_sid)
@@ -6684,18 +6735,9 @@ static int selinux_key_alloc(struct key *k, const struct cred *cred,
else
ksec->sid = tsec->sid;
- k->security = ksec;
return 0;
}
-static void selinux_key_free(struct key *k)
-{
- struct key_security_struct *ksec = k->security;
-
- k->security = NULL;
- kfree(ksec);
-}
-
static int selinux_key_permission(key_ref_t key_ref,
const struct cred *cred,
enum key_need_perm need_perm)
@@ -6736,14 +6778,14 @@ static int selinux_key_permission(key_ref_t key_ref,
sid = cred_sid(cred);
key = key_ref_to_ptr(key_ref);
- ksec = key->security;
+ ksec = selinux_key(key);
return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, perm, NULL);
}
static int selinux_key_getsecurity(struct key *key, char **_buffer)
{
- struct key_security_struct *ksec = key->security;
+ struct key_security_struct *ksec = selinux_key(key);
char *context = NULL;
unsigned len;
int rc;
@@ -6759,7 +6801,7 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
#ifdef CONFIG_KEY_NOTIFICATIONS
static int selinux_watch_key(struct key *key)
{
- struct key_security_struct *ksec = key->security;
+ struct key_security_struct *ksec = selinux_key(key);
u32 sid = current_sid();
return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, KEY__VIEW, NULL);
@@ -6813,23 +6855,13 @@ static int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name,
INFINIBAND_ENDPORT__MANAGE_SUBNET, &ad);
}
-static int selinux_ib_alloc_security(void **ib_sec)
+static int selinux_ib_alloc_security(void *ib_sec)
{
- struct ib_security_struct *sec;
+ struct ib_security_struct *sec = selinux_ib(ib_sec);
- sec = kzalloc(sizeof(*sec), GFP_KERNEL);
- if (!sec)
- return -ENOMEM;
sec->sid = current_sid();
-
- *ib_sec = sec;
return 0;
}
-
-static void selinux_ib_free_security(void *ib_sec)
-{
- kfree(ib_sec);
-}
#endif
#ifdef CONFIG_BPF_SYSCALL
@@ -6920,7 +6952,8 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
BPF__PROG_RUN, NULL);
}
-static int selinux_bpf_map_alloc(struct bpf_map *map)
+static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
+ struct bpf_token *token)
{
struct bpf_security_struct *bpfsec;
@@ -6942,7 +6975,31 @@ static void selinux_bpf_map_free(struct bpf_map *map)
kfree(bpfsec);
}
-static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
+static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
+ struct bpf_token *token)
+{
+ struct bpf_security_struct *bpfsec;
+
+ bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
+ if (!bpfsec)
+ return -ENOMEM;
+
+ bpfsec->sid = current_sid();
+ prog->aux->security = bpfsec;
+
+ return 0;
+}
+
+static void selinux_bpf_prog_free(struct bpf_prog *prog)
+{
+ struct bpf_security_struct *bpfsec = prog->aux->security;
+
+ prog->aux->security = NULL;
+ kfree(bpfsec);
+}
+
+static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
+ const struct path *path)
{
struct bpf_security_struct *bpfsec;
@@ -6951,16 +7008,16 @@ static int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux)
return -ENOMEM;
bpfsec->sid = current_sid();
- aux->security = bpfsec;
+ token->security = bpfsec;
return 0;
}
-static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
+static void selinux_bpf_token_free(struct bpf_token *token)
{
- struct bpf_security_struct *bpfsec = aux->security;
+ struct bpf_security_struct *bpfsec = token->security;
- aux->security = NULL;
+ token->security = NULL;
kfree(bpfsec);
}
#endif
@@ -6970,9 +7027,16 @@ struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
.lbs_file = sizeof(struct file_security_struct),
.lbs_inode = sizeof(struct inode_security_struct),
.lbs_ipc = sizeof(struct ipc_security_struct),
+ .lbs_key = sizeof(struct key_security_struct),
.lbs_msg_msg = sizeof(struct msg_security_struct),
+#ifdef CONFIG_PERF_EVENTS
+ .lbs_perf_event = sizeof(struct perf_event_security_struct),
+#endif
+ .lbs_sock = sizeof(struct sk_security_struct),
.lbs_superblock = sizeof(struct superblock_security_struct),
.lbs_xattr_count = SELINUX_INODE_INIT_XATTRS,
+ .lbs_tun_dev = sizeof(struct tun_security_struct),
+ .lbs_ib = sizeof(struct ib_security_struct),
};
#ifdef CONFIG_PERF_EVENTS
@@ -6999,24 +7063,12 @@ static int selinux_perf_event_alloc(struct perf_event *event)
{
struct perf_event_security_struct *perfsec;
- perfsec = kzalloc(sizeof(*perfsec), GFP_KERNEL);
- if (!perfsec)
- return -ENOMEM;
-
+ perfsec = selinux_perf_event(event->security);
perfsec->sid = current_sid();
- event->security = perfsec;
return 0;
}
-static void selinux_perf_event_free(struct perf_event *event)
-{
- struct perf_event_security_struct *perfsec = event->security;
-
- event->security = NULL;
- kfree(perfsec);
-}
-
static int selinux_perf_event_read(struct perf_event *event)
{
struct perf_event_security_struct *perfsec = event->security;
@@ -7157,6 +7209,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_permission, selinux_inode_permission),
LSM_HOOK_INIT(inode_setattr, selinux_inode_setattr),
LSM_HOOK_INIT(inode_getattr, selinux_inode_getattr),
+ LSM_HOOK_INIT(inode_xattr_skipcap, selinux_inode_xattr_skipcap),
LSM_HOOK_INIT(inode_setxattr, selinux_inode_setxattr),
LSM_HOOK_INIT(inode_post_setxattr, selinux_inode_post_setxattr),
LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr),
@@ -7168,7 +7221,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_getsecurity, selinux_inode_getsecurity),
LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity),
LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity),
- LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid),
+ LSM_HOOK_INIT(inode_getlsmprop, selinux_inode_getlsmprop),
LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up),
LSM_HOOK_INIT(inode_copy_up_xattr, selinux_inode_copy_up_xattr),
LSM_HOOK_INIT(path_notify, selinux_path_notify),
@@ -7194,6 +7247,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer),
LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid),
+ LSM_HOOK_INIT(cred_getlsmprop, selinux_cred_getlsmprop),
LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as),
LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as),
LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request),
@@ -7202,8 +7256,8 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid),
LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid),
LSM_HOOK_INIT(task_getsid, selinux_task_getsid),
- LSM_HOOK_INIT(current_getsecid_subj, selinux_current_getsecid_subj),
- LSM_HOOK_INIT(task_getsecid_obj, selinux_task_getsecid_obj),
+ LSM_HOOK_INIT(current_getlsmprop_subj, selinux_current_getlsmprop_subj),
+ LSM_HOOK_INIT(task_getlsmprop_obj, selinux_task_getlsmprop_obj),
LSM_HOOK_INIT(task_setnice, selinux_task_setnice),
LSM_HOOK_INIT(task_setioprio, selinux_task_setioprio),
LSM_HOOK_INIT(task_getioprio, selinux_task_getioprio),
@@ -7217,7 +7271,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(userns_create, selinux_userns_create),
LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission),
- LSM_HOOK_INIT(ipc_getsecid, selinux_ipc_getsecid),
+ LSM_HOOK_INIT(ipc_getlsmprop, selinux_ipc_getlsmprop),
LSM_HOOK_INIT(msg_queue_associate, selinux_msg_queue_associate),
LSM_HOOK_INIT(msg_queue_msgctl, selinux_msg_queue_msgctl),
@@ -7283,7 +7337,6 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(secmark_refcount_inc, selinux_secmark_refcount_inc),
LSM_HOOK_INIT(secmark_refcount_dec, selinux_secmark_refcount_dec),
LSM_HOOK_INIT(req_classify_flow, selinux_req_classify_flow),
- LSM_HOOK_INIT(tun_dev_free_security, selinux_tun_dev_free_security),
LSM_HOOK_INIT(tun_dev_create, selinux_tun_dev_create),
LSM_HOOK_INIT(tun_dev_attach_queue, selinux_tun_dev_attach_queue),
LSM_HOOK_INIT(tun_dev_attach, selinux_tun_dev_attach),
@@ -7292,7 +7345,6 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(ib_pkey_access, selinux_ib_pkey_access),
LSM_HOOK_INIT(ib_endport_manage_subnet,
selinux_ib_endport_manage_subnet),
- LSM_HOOK_INIT(ib_free_security, selinux_ib_free_security),
#endif
#ifdef CONFIG_SECURITY_NETWORK_XFRM
LSM_HOOK_INIT(xfrm_policy_free_security, selinux_xfrm_policy_free),
@@ -7306,7 +7358,6 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
#endif
#ifdef CONFIG_KEYS
- LSM_HOOK_INIT(key_free, selinux_key_free),
LSM_HOOK_INIT(key_permission, selinux_key_permission),
LSM_HOOK_INIT(key_getsecurity, selinux_key_getsecurity),
#ifdef CONFIG_KEY_NOTIFICATIONS
@@ -7324,13 +7375,13 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(bpf, selinux_bpf),
LSM_HOOK_INIT(bpf_map, selinux_bpf_map),
LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog),
- LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free),
- LSM_HOOK_INIT(bpf_prog_free_security, selinux_bpf_prog_free),
+ LSM_HOOK_INIT(bpf_map_free, selinux_bpf_map_free),
+ LSM_HOOK_INIT(bpf_prog_free, selinux_bpf_prog_free),
+ LSM_HOOK_INIT(bpf_token_free, selinux_bpf_token_free),
#endif
#ifdef CONFIG_PERF_EVENTS
LSM_HOOK_INIT(perf_event_open, selinux_perf_event_open),
- LSM_HOOK_INIT(perf_event_free, selinux_perf_event_free),
LSM_HOOK_INIT(perf_event_read, selinux_perf_event_read),
LSM_HOOK_INIT(perf_event_write, selinux_perf_event_write),
#endif
@@ -7363,6 +7414,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_alloc_security, selinux_inode_alloc_security),
LSM_HOOK_INIT(sem_alloc_security, selinux_sem_alloc_security),
LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx),
+ LSM_HOOK_INIT(lsmprop_to_secctx, selinux_lsmprop_to_secctx),
LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx),
LSM_HOOK_INIT(sk_alloc_security, selinux_sk_alloc_security),
LSM_HOOK_INIT(tun_dev_alloc_security, selinux_tun_dev_alloc_security),
@@ -7382,8 +7434,9 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(audit_rule_init, selinux_audit_rule_init),
#endif
#ifdef CONFIG_BPF_SYSCALL
- LSM_HOOK_INIT(bpf_map_alloc_security, selinux_bpf_map_alloc),
- LSM_HOOK_INIT(bpf_prog_alloc_security, selinux_bpf_prog_alloc),
+ LSM_HOOK_INIT(bpf_map_create, selinux_bpf_map_create),
+ LSM_HOOK_INIT(bpf_prog_load, selinux_bpf_prog_load),
+ LSM_HOOK_INIT(bpf_token_create, selinux_bpf_token_create),
#endif
#ifdef CONFIG_PERF_EVENTS
LSM_HOOK_INIT(perf_event_alloc, selinux_perf_event_alloc),
diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h
index 52aca71210b4..d5b0425055e4 100644
--- a/security/selinux/include/audit.h
+++ b/security/selinux/include/audit.h
@@ -16,43 +16,46 @@
#include <linux/types.h>
/**
- * selinux_audit_rule_init - alloc/init an selinux audit rule structure.
- * @field: the field this rule refers to
- * @op: the operator the rule uses
- * @rulestr: the text "target" of the rule
- * @rule: pointer to the new rule structure returned via this
+ * selinux_audit_rule_init - alloc/init an selinux audit rule structure.
+ * @field: the field this rule refers to
+ * @op: the operator the rule uses
+ * @rulestr: the text "target" of the rule
+ * @rule: pointer to the new rule structure returned via this
+ * @gfp: GFP flag used for kmalloc
*
- * Returns 0 if successful, -errno if not. On success, the rule structure
- * will be allocated internally. The caller must free this structure with
- * selinux_audit_rule_free() after use.
+ * Returns 0 if successful, -errno if not. On success, the rule structure
+ * will be allocated internally. The caller must free this structure with
+ * selinux_audit_rule_free() after use.
*/
-int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **rule);
+int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **rule,
+ gfp_t gfp);
/**
- * selinux_audit_rule_free - free an selinux audit rule structure.
- * @rule: pointer to the audit rule to be freed
+ * selinux_audit_rule_free - free an selinux audit rule structure.
+ * @rule: pointer to the audit rule to be freed
*
- * This will free all memory associated with the given rule.
- * If @rule is NULL, no operation is performed.
+ * This will free all memory associated with the given rule.
+ * If @rule is NULL, no operation is performed.
*/
void selinux_audit_rule_free(void *rule);
/**
- * selinux_audit_rule_match - determine if a context ID matches a rule.
- * @sid: the context ID to check
- * @field: the field this rule refers to
- * @op: the operator the rule uses
- * @rule: pointer to the audit rule to check against
+ * selinux_audit_rule_match - determine if a context ID matches a rule.
+ * @prop: includes the context ID to check
+ * @field: the field this rule refers to
+ * @op: the operator the rule uses
+ * @rule: pointer to the audit rule to check against
*
- * Returns 1 if the context id matches the rule, 0 if it does not, and
- * -errno on failure.
+ * Returns 1 if the context id matches the rule, 0 if it does not, and
+ * -errno on failure.
*/
-int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *rule);
+int selinux_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op,
+ void *rule);
/**
- * selinux_audit_rule_known - check to see if rule contains selinux fields.
- * @rule: rule to be checked
- * Returns 1 if there are selinux fields specified in the rule, 0 otherwise.
+ * selinux_audit_rule_known - check to see if rule contains selinux fields.
+ * @rule: rule to be checked
+ * Returns 1 if there are selinux fields specified in the rule, 0 otherwise.
*/
int selinux_audit_rule_known(struct audit_krule *rule);
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index 96a614d47df8..281f40103663 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -136,8 +136,11 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, u16 tclass, u32 requested,
int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, u32 requested,
struct common_audit_data *auditdata);
+#define AVC_EXT_IOCTL (1 << 0) /* Cache entry for an ioctl extended permission */
+#define AVC_EXT_NLMSG (1 << 1) /* Cache entry for an nlmsg extended permission */
int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
- u8 driver, u8 perm, struct common_audit_data *ad);
+ u8 driver, u8 base_perm, u8 perm,
+ struct common_audit_data *ad);
u32 avc_policy_seqno(void);
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 7229c9bf6c27..03e82477dce9 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -1,8 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 */
-#include <linux/capability.h>
-#include <linux/socket.h>
-
#define COMMON_FILE_SOCK_PERMS \
"ioctl", "read", "write", "create", "getattr", "setattr", "lock", \
"relabelfrom", "relabelto", "append", "map"
@@ -36,9 +33,13 @@
"mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", \
"audit_read", "perfmon", "bpf", "checkpoint_restore"
+#ifdef __KERNEL__ /* avoid this check when building host programs */
+#include <linux/capability.h>
+
#if CAP_LAST_CAP > CAP_CHECKPOINT_RESTORE
#error New capability defined, please update COMMON_CAP2_PERMS.
#endif
+#endif
/*
* Note: The name for any socket class should be suffixed by "socket",
@@ -96,17 +97,17 @@ const struct security_class_mapping secclass_map[] = {
{ "shm", { COMMON_IPC_PERMS, "lock", NULL } },
{ "ipc", { COMMON_IPC_PERMS, NULL } },
{ "netlink_route_socket",
- { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", NULL } },
+ { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg", NULL } },
{ "netlink_tcpdiag_socket",
- { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", NULL } },
+ { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg", NULL } },
{ "netlink_nflog_socket", { COMMON_SOCK_PERMS, NULL } },
{ "netlink_xfrm_socket",
- { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", NULL } },
+ { COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg", NULL } },
{ "netlink_selinux_socket", { COMMON_SOCK_PERMS, NULL } },
{ "netlink_iscsi_socket", { COMMON_SOCK_PERMS, NULL } },
{ "netlink_audit_socket",
{ COMMON_SOCK_PERMS, "nlmsg_read", "nlmsg_write", "nlmsg_relay",
- "nlmsg_readpriv", "nlmsg_tty_audit", NULL } },
+ "nlmsg_readpriv", "nlmsg_tty_audit", "nlmsg", NULL } },
{ "netlink_fib_lookup_socket", { COMMON_SOCK_PERMS, NULL } },
{ "netlink_connector_socket", { COMMON_SOCK_PERMS, NULL } },
{ "netlink_netfilter_socket", { COMMON_SOCK_PERMS, NULL } },
@@ -178,9 +179,13 @@ const struct security_class_mapping secclass_map[] = {
{ "anon_inode", { COMMON_FILE_PERMS, NULL } },
{ "io_uring", { "override_creds", "sqpoll", "cmd", NULL } },
{ "user_namespace", { "create", NULL } },
- { NULL }
+ /* last one */ { NULL, {} }
};
+#ifdef __KERNEL__ /* avoid this check when building host programs */
+#include <linux/socket.h>
+
#if PF_MAX > 46
#error New address family defined, please update secclass_map.
#endif
+#endif
diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h
index 5910bb7c2eca..060833e2dba2 100644
--- a/security/selinux/include/conditional.h
+++ b/security/selinux/include/conditional.h
@@ -16,7 +16,7 @@
int security_get_bools(struct selinux_policy *policy, u32 *len, char ***names,
int **values);
-int security_set_bools(u32 len, int *values);
+int security_set_bools(u32 len, const int *values);
int security_get_bool_value(u32 index);
diff --git a/security/selinux/include/initial_sid_to_string.h b/security/selinux/include/initial_sid_to_string.h
index 99b353b2abb4..d7ba60b62491 100644
--- a/security/selinux/include/initial_sid_to_string.h
+++ b/security/selinux/include/initial_sid_to_string.h
@@ -1,6 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
+#ifdef __KERNEL__
#include <linux/stddef.h>
+#else
+#include <stddef.h>
+#endif
static const char *const initial_sid_to_string[] = {
NULL, /* zero placeholder, not used */
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index dea1d6f3ed2d..c88cae81ee4c 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -195,4 +195,32 @@ selinux_superblock(const struct super_block *superblock)
return superblock->s_security + selinux_blob_sizes.lbs_superblock;
}
+#ifdef CONFIG_KEYS
+static inline struct key_security_struct *selinux_key(const struct key *key)
+{
+ return key->security + selinux_blob_sizes.lbs_key;
+}
+#endif /* CONFIG_KEYS */
+
+static inline struct sk_security_struct *selinux_sock(const struct sock *sock)
+{
+ return sock->sk_security + selinux_blob_sizes.lbs_sock;
+}
+
+static inline struct tun_security_struct *selinux_tun_dev(void *security)
+{
+ return security + selinux_blob_sizes.lbs_tun_dev;
+}
+
+static inline struct ib_security_struct *selinux_ib(void *ib_sec)
+{
+ return ib_sec + selinux_blob_sizes.lbs_ib;
+}
+
+static inline struct perf_event_security_struct *
+selinux_perf_event(void *perf_event)
+{
+ return perf_event + selinux_blob_sizes.lbs_perf_event;
+}
+
#endif /* _SELINUX_OBJSEC_H_ */
diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h
index dc3674eb29c1..079679fe7254 100644
--- a/security/selinux/include/policycap.h
+++ b/security/selinux/include/policycap.h
@@ -14,6 +14,7 @@ enum {
POLICYDB_CAP_GENFS_SECLABEL_SYMLINKS,
POLICYDB_CAP_IOCTL_SKIP_CLOEXEC,
POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT,
+ POLICYDB_CAP_NETLINK_XPERM,
__POLICYDB_CAP_MAX
};
#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h
index 2cffcc1ce851..e080827408c4 100644
--- a/security/selinux/include/policycap_names.h
+++ b/security/selinux/include/policycap_names.h
@@ -17,6 +17,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
"genfs_seclabel_symlinks",
"ioctl_skip_cloexec",
"userspace_initial_context",
+ "netlink_xperm",
};
/* clang-format on */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 289bf9233f71..8b4c2aa35839 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -46,10 +46,11 @@
#define POLICYDB_VERSION_INFINIBAND 31
#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 */
/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
-#define POLICYDB_VERSION_MAX POLICYDB_VERSION_COMP_FTRANS
+#define POLICYDB_VERSION_MAX POLICYDB_VERSION_COND_XPERMS
/* Mask for just the mount related flags */
#define SE_MNTMASK 0x0f
@@ -195,6 +196,12 @@ static inline bool selinux_policycap_userspace_initial_context(void)
selinux_state.policycap[POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT]);
}
+static inline bool selinux_policycap_netlink_xperm(void)
+{
+ return READ_ONCE(
+ selinux_state.policycap[POLICYDB_CAP_NETLINK_XPERM]);
+}
+
struct selinux_policy_convert_data;
struct selinux_load_state {
@@ -233,6 +240,7 @@ struct extended_perms_data {
struct extended_perms_decision {
u8 used;
u8 driver;
+ u8 base_perm;
struct extended_perms_data *allowed;
struct extended_perms_data *auditallow;
struct extended_perms_data *dontaudit;
@@ -240,6 +248,7 @@ struct extended_perms_decision {
struct extended_perms {
u16 len; /* length associated decision chain */
+ u8 base_perms; /* which base permissions are covered */
struct extended_perms_data drivers; /* flag drivers that are used */
};
@@ -251,6 +260,7 @@ void security_compute_av(u32 ssid, u32 tsid, u16 tclass,
struct extended_perms *xperms);
void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 tclass, u8 driver,
+ u8 base_perm,
struct extended_perms_decision *xpermd);
void security_compute_av_user(u32 ssid, u32 tsid, u16 tclass,
@@ -283,7 +293,7 @@ int security_context_to_sid_default(const char *scontext, u32 scontext_len,
int security_context_to_sid_force(const char *scontext, u32 scontext_len,
u32 *sid);
-int security_get_user_sids(u32 callsid, char *username, u32 **sids, u32 *nel);
+int security_get_user_sids(u32 fromsid, const char *username, u32 **sids, u32 *nel);
int security_port_sid(u8 protocol, u16 port, u32 *out_sid);
@@ -301,7 +311,7 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
int security_validate_transition_user(u32 oldsid, u32 newsid, u32 tasksid,
u16 tclass);
-int security_bounded_transition(u32 oldsid, u32 newsid);
+int security_bounded_transition(u32 old_sid, u32 new_sid);
int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index 8f182800e412..d51dfe892312 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -17,6 +17,7 @@
#include <linux/gfp.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
+#include <linux/lsm_hooks.h>
#include <net/sock.h>
#include <net/netlabel.h>
#include <net/ip.h>
@@ -62,13 +63,13 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
* Description:
* Generate the NetLabel security attributes for a socket, making full use of
* the socket's attribute cache. Returns a pointer to the security attributes
- * on success, NULL on failure.
+ * on success, or an ERR_PTR on failure.
*
*/
static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
{
int rc;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
struct netlbl_lsm_secattr *secattr;
if (sksec->nlbl_secattr != NULL)
@@ -76,11 +77,12 @@ static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
secattr = netlbl_secattr_alloc(GFP_ATOMIC);
if (secattr == NULL)
- return NULL;
+ return ERR_PTR(-ENOMEM);
+
rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);
if (rc != 0) {
netlbl_secattr_free(secattr);
- return NULL;
+ return ERR_PTR(rc);
}
sksec->nlbl_secattr = secattr;
@@ -100,7 +102,7 @@ static struct netlbl_lsm_secattr *selinux_netlbl_sock_getattr(
const struct sock *sk,
u32 sid)
{
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr;
if (secattr == NULL)
@@ -240,7 +242,7 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
* being labeled by it's parent socket, if it is just exit */
sk = skb_to_full_sk(skb);
if (sk != NULL) {
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
if (sksec->nlbl_state != NLBL_REQSKB)
return 0;
@@ -277,7 +279,7 @@ int selinux_netlbl_sctp_assoc_request(struct sctp_association *asoc,
{
int rc;
struct netlbl_lsm_secattr secattr;
- struct sk_security_struct *sksec = asoc->base.sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(asoc->base.sk);
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
@@ -356,9 +358,9 @@ inet_conn_request_return:
*/
void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
{
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
- if (family == PF_INET)
+ if (family == PF_INET || family == PF_INET6)
sksec->nlbl_state = NLBL_LABELED;
else
sksec->nlbl_state = NLBL_UNSET;
@@ -374,8 +376,8 @@ void selinux_netlbl_inet_csk_clone(struct sock *sk, u16 family)
*/
void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)
{
- struct sk_security_struct *sksec = sk->sk_security;
- struct sk_security_struct *newsksec = newsk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
+ struct sk_security_struct *newsksec = selinux_sock(newsk);
newsksec->nlbl_state = sksec->nlbl_state;
}
@@ -393,16 +395,19 @@ void selinux_netlbl_sctp_sk_clone(struct sock *sk, struct sock *newsk)
int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
{
int rc;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
struct netlbl_lsm_secattr *secattr;
if (family != PF_INET && family != PF_INET6)
return 0;
secattr = selinux_netlbl_sock_genattr(sk);
- if (secattr == NULL)
- return -ENOMEM;
- rc = netlbl_sock_setattr(sk, family, secattr);
+ if (IS_ERR(secattr))
+ return PTR_ERR(secattr);
+ /* On socket creation, replacement of IP options is safe even if
+ * the caller does not hold the socket lock.
+ */
+ rc = netlbl_sock_setattr(sk, family, secattr, true);
switch (rc) {
case 0:
sksec->nlbl_state = NLBL_LABELED;
@@ -507,7 +512,7 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock,
{
int rc = 0;
struct sock *sk = sock->sk;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
struct netlbl_lsm_secattr secattr;
if (selinux_netlbl_option(level, optname) &&
@@ -545,7 +550,7 @@ static int selinux_netlbl_socket_connect_helper(struct sock *sk,
struct sockaddr *addr)
{
int rc;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
struct netlbl_lsm_secattr *secattr;
/* connected sockets are allowed to disconnect when the address family
@@ -558,10 +563,9 @@ static int selinux_netlbl_socket_connect_helper(struct sock *sk,
return rc;
}
secattr = selinux_netlbl_sock_genattr(sk);
- if (secattr == NULL) {
- rc = -ENOMEM;
- return rc;
- }
+ if (IS_ERR(secattr))
+ return PTR_ERR(secattr);
+
rc = netlbl_conn_setattr(sk, addr, secattr);
if (rc == 0)
sksec->nlbl_state = NLBL_CONNLABELED;
@@ -584,7 +588,7 @@ static int selinux_netlbl_socket_connect_helper(struct sock *sk,
int selinux_netlbl_socket_connect_locked(struct sock *sk,
struct sockaddr *addr)
{
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec = selinux_sock(sk);
if (sksec->nlbl_state != NLBL_REQSKB &&
sksec->nlbl_state != NLBL_CONNLABELED)
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 8ff670cf1ee5..3a95986b134f 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -21,142 +21,142 @@
#include "security.h"
struct nlmsg_perm {
- u16 nlmsg_type;
- u32 perm;
+ u16 nlmsg_type;
+ u32 perm;
};
static const struct nlmsg_perm nlmsg_route_perms[] = {
- { RTM_NEWLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETLINK, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_SETLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_NEWADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETADDR, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETROUTE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETRULE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETQDISC, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETACTION, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_GETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_SETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_NEWADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_GETDCB, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_SETDCB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_NEWNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETMDB, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWNSID, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_GETNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_GETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_SETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_NEWCACHEREPORT, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_NEWVLAN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELVLAN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETVLAN, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ },
- { RTM_NEWTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_DELTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
- { RTM_GETTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETLINK, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_SETLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_NEWADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETADDR, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETROUTE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETRULE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETQDISC, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETACTION, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_SETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_NEWADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETADDRLABEL, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETDCB, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_SETDCB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_NEWNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETNETCONF, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELMDB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETMDB, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWNSID, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_SETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_NEWCACHEREPORT, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETCHAIN, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETNEXTHOP, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELLINKPROP, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_NEWVLAN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELVLAN, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETVLAN, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETNEXTHOPBUCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETTUNNEL, NETLINK_ROUTE_SOCKET__NLMSG_READ },
};
static const struct nlmsg_perm nlmsg_tcpdiag_perms[] = {
- { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
- { DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
- { SOCK_DIAG_BY_FAMILY, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
- { SOCK_DESTROY, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE },
+ { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
+ { DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
+ { SOCK_DIAG_BY_FAMILY, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
+ { SOCK_DESTROY, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE },
};
static const struct nlmsg_perm nlmsg_xfrm_perms[] = {
- { XFRM_MSG_NEWSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_DELSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_GETSA, NETLINK_XFRM_SOCKET__NLMSG_READ },
- { XFRM_MSG_NEWPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_DELPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_GETPOLICY, NETLINK_XFRM_SOCKET__NLMSG_READ },
- { XFRM_MSG_ALLOCSPI, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_ACQUIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_EXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_UPDPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_UPDSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_POLEXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_FLUSHSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_FLUSHPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_NEWAE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_GETAE, NETLINK_XFRM_SOCKET__NLMSG_READ },
- { XFRM_MSG_REPORT, NETLINK_XFRM_SOCKET__NLMSG_READ },
- { XFRM_MSG_MIGRATE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_NEWSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
- { XFRM_MSG_GETSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
- { XFRM_MSG_NEWSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_GETSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
- { XFRM_MSG_MAPPING, NETLINK_XFRM_SOCKET__NLMSG_READ },
- { XFRM_MSG_SETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
- { XFRM_MSG_GETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_NEWSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_DELSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETSA, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_NEWPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_DELPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETPOLICY, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_ALLOCSPI, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_ACQUIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_EXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_UPDPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_UPDSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_POLEXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_FLUSHSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_FLUSHPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_NEWAE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETAE, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_REPORT, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_MIGRATE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_NEWSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_GETSADINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_NEWSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETSPDINFO, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_MAPPING, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_SETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETDEFAULT, NETLINK_XFRM_SOCKET__NLMSG_READ },
};
static const struct nlmsg_perm nlmsg_audit_perms[] = {
- { AUDIT_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
- { AUDIT_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
- { AUDIT_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
- { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
- { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
- { AUDIT_LIST_RULES, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
- { AUDIT_ADD_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
- { AUDIT_DEL_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
- { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY },
- { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ },
- { AUDIT_TRIM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
- { AUDIT_MAKE_EQUIV, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
- { AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
- { AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT },
- { AUDIT_GET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_READ },
- { AUDIT_SET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
+ { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_LIST_RULES, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
+ { AUDIT_ADD_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_DEL_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY },
+ { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_TRIM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_MAKE_EQUIV, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_TTY_AUDIT },
+ { AUDIT_GET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_SET_FEATURE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
};
-
-static int nlmsg_perm(u16 nlmsg_type, u32 *perm, const struct nlmsg_perm *tab, size_t tabsize)
+static int nlmsg_perm(u16 nlmsg_type, u32 *perm, const struct nlmsg_perm *tab,
+ size_t tabsize)
{
unsigned int i;
int err = -EINVAL;
- for (i = 0; i < tabsize/sizeof(struct nlmsg_perm); i++)
+ for (i = 0; i < tabsize / sizeof(struct nlmsg_perm); i++)
if (nlmsg_type == tab[i].nlmsg_type) {
*perm = tab[i].perm;
err = 0;
@@ -168,7 +168,12 @@ static int nlmsg_perm(u16 nlmsg_type, u32 *perm, const struct nlmsg_perm *tab, s
int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
{
- int err = 0;
+ /* While it is possible to add a similar permission to other netlink
+ * classes, note that the extended permission value is matched against
+ * the nlmsg_type field. Notably, SECCLASS_NETLINK_GENERIC_SOCKET uses
+ * dynamic values for this field, which means that it cannot be added
+ * as-is.
+ */
switch (sclass) {
case SECCLASS_NETLINK_ROUTE_SOCKET:
@@ -178,42 +183,52 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
* before updating the BUILD_BUG_ON() macro!
*/
BUILD_BUG_ON(RTM_MAX != (RTM_NEWTUNNEL + 3));
- err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
- sizeof(nlmsg_route_perms));
- break;
+ if (selinux_policycap_netlink_xperm()) {
+ *perm = NETLINK_ROUTE_SOCKET__NLMSG;
+ return 0;
+ }
+ return nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
+ sizeof(nlmsg_route_perms));
+ break;
case SECCLASS_NETLINK_TCPDIAG_SOCKET:
- err = nlmsg_perm(nlmsg_type, perm, nlmsg_tcpdiag_perms,
- sizeof(nlmsg_tcpdiag_perms));
+ if (selinux_policycap_netlink_xperm()) {
+ *perm = NETLINK_TCPDIAG_SOCKET__NLMSG;
+ return 0;
+ }
+ return nlmsg_perm(nlmsg_type, perm, nlmsg_tcpdiag_perms,
+ sizeof(nlmsg_tcpdiag_perms));
break;
-
case SECCLASS_NETLINK_XFRM_SOCKET:
/* If the BUILD_BUG_ON() below fails you must update the
* structures at the top of this file with the new mappings
* before updating the BUILD_BUG_ON() macro!
*/
BUILD_BUG_ON(XFRM_MSG_MAX != XFRM_MSG_GETDEFAULT);
- err = nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms,
- sizeof(nlmsg_xfrm_perms));
- break;
+ if (selinux_policycap_netlink_xperm()) {
+ *perm = NETLINK_XFRM_SOCKET__NLMSG;
+ return 0;
+ }
+ return nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms,
+ sizeof(nlmsg_xfrm_perms));
+ break;
case SECCLASS_NETLINK_AUDIT_SOCKET:
- if ((nlmsg_type >= AUDIT_FIRST_USER_MSG &&
- nlmsg_type <= AUDIT_LAST_USER_MSG) ||
- (nlmsg_type >= AUDIT_FIRST_USER_MSG2 &&
- nlmsg_type <= AUDIT_LAST_USER_MSG2)) {
+ if (selinux_policycap_netlink_xperm()) {
+ *perm = NETLINK_AUDIT_SOCKET__NLMSG;
+ return 0;
+ } else if ((nlmsg_type >= AUDIT_FIRST_USER_MSG &&
+ nlmsg_type <= AUDIT_LAST_USER_MSG) ||
+ (nlmsg_type >= AUDIT_FIRST_USER_MSG2 &&
+ nlmsg_type <= AUDIT_LAST_USER_MSG2)) {
*perm = NETLINK_AUDIT_SOCKET__NLMSG_RELAY;
- } else {
- err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms,
- sizeof(nlmsg_audit_perms));
+ return 0;
}
- break;
-
- /* No messaging from userspace, or class unknown/unhandled */
- default:
- err = -ENOENT;
+ return nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms,
+ sizeof(nlmsg_audit_perms));
break;
}
- return err;
+ /* No messaging from userspace, or class unknown/unhandled */
+ return -ENOENT;
}
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 0619a1cbbfbe..47480eb2189b 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -571,11 +571,18 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
+ struct selinux_fs_info *fsi;
struct selinux_load_state load_state;
ssize_t length;
void *data = NULL;
+ /* no partial writes */
+ if (*ppos)
+ return -EINVAL;
+ /* no empty policies */
+ if (!count)
+ return -EINVAL;
+
mutex_lock(&selinux_state.policy_mutex);
length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
@@ -583,26 +590,22 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
if (length)
goto out;
- /* No partial writes. */
- length = -EINVAL;
- if (*ppos != 0)
- goto out;
-
- length = -ENOMEM;
data = vmalloc(count);
- if (!data)
+ if (!data) {
+ length = -ENOMEM;
goto out;
-
- length = -EFAULT;
- if (copy_from_user(data, buf, count) != 0)
+ }
+ if (copy_from_user(data, buf, count) != 0) {
+ length = -EFAULT;
goto out;
+ }
length = security_load_policy(data, count, &load_state);
if (length) {
pr_warn_ratelimited("SELinux: failed to load policy\n");
goto out;
}
-
+ fsi = file_inode(file)->i_sb->s_fs_info;
length = sel_make_policy_nodes(fsi, load_state.policy);
if (length) {
pr_warn_ratelimited("SELinux: failed to initialize selinuxfs\n");
@@ -611,13 +614,12 @@ static ssize_t sel_write_load(struct file *file, const char __user *buf,
}
selinux_policy_commit(&load_state);
-
length = count;
-
audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
"auid=%u ses=%u lsm=selinux res=1",
from_kuid(&init_user_ns, audit_get_loginuid(current)),
audit_get_sessionid(current));
+
out:
mutex_unlock(&selinux_state.policy_mutex);
vfree(data);
@@ -706,7 +708,7 @@ static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
if (new_value) {
char comm[sizeof(current->comm)];
- memcpy(comm, current->comm, sizeof(comm));
+ strscpy(comm, current->comm);
pr_err("SELinux: %s (%d) set checkreqprot to 1. This is no longer supported.\n",
comm, current->pid);
}
@@ -1067,6 +1069,10 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
int rc;
u32 i, len, nsids;
+ 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);
+
length = avc_has_perm(current_sid(), SECINITSID_SECURITY,
SECCLASS_SECURITY, SECURITY__COMPUTE_USER,
NULL);
@@ -1509,7 +1515,7 @@ static const struct file_operations sel_avc_hash_stats_ops = {
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx)
{
- int cpu;
+ loff_t cpu;
for (cpu = *idx; cpu < nr_cpu_ids; ++cpu) {
if (!cpu_possible(cpu))
@@ -1995,7 +2001,7 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
[SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
[SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops,
S_IWUGO},
- /* last one */ {""}
+ /* last one */ {"", NULL, 0}
};
ret = selinux_fs_info_create(sb);
@@ -2123,7 +2129,6 @@ static struct file_system_type sel_fs_type = {
.kill_sb = sel_kill_sb,
};
-static struct vfsmount *selinuxfs_mount __ro_after_init;
struct path selinux_null __ro_after_init;
static int __init init_sel_fs(void)
@@ -2145,20 +2150,29 @@ static int __init init_sel_fs(void)
return err;
}
- selinux_null.mnt = selinuxfs_mount = kern_mount(&sel_fs_type);
- if (IS_ERR(selinuxfs_mount)) {
+ selinux_null.mnt = kern_mount(&sel_fs_type);
+ if (IS_ERR(selinux_null.mnt)) {
pr_err("selinuxfs: could not mount!\n");
- err = PTR_ERR(selinuxfs_mount);
- selinuxfs_mount = NULL;
+ err = PTR_ERR(selinux_null.mnt);
+ selinux_null.mnt = NULL;
+ return err;
}
+
selinux_null.dentry = d_hash_and_lookup(selinux_null.mnt->mnt_root,
&null_name);
if (IS_ERR(selinux_null.dentry)) {
pr_err("selinuxfs: could not lookup null!\n");
err = PTR_ERR(selinux_null.dentry);
selinux_null.dentry = NULL;
+ return err;
}
+ /*
+ * Try to pre-allocate the status page, so the sequence number of the
+ * initial policy load can be stored.
+ */
+ (void) selinux_kernel_status_page();
+
return err;
}
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index 697eb4352439..c2c31521cace 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -1,20 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Implementation of the access vector table type.
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
-/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
- *
- * Added conditional policy language extensions
- *
- * Copyright (C) 2003 Tresys Technology, LLC
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 2.
+/* Updated: Frank Mayer <mayerf@tresys.com> and
+ * Karl MacMillan <kmacmillan@tresys.com>
+ * Added conditional policy language extensions
+ * Copyright (C) 2003 Tresys Technology, LLC
*
* Updated: Yuichi Nakamura <ynakam@hitachisoft.jp>
- * Tuned number of hash slots for avtab to reduce memory usage
+ * Tuned number of hash slots for avtab to reduce memory usage
*/
#include <linux/bitops.h>
@@ -36,19 +33,20 @@ static inline u32 avtab_hash(const struct avtab_key *keyp, u32 mask)
static const u32 c2 = 0x1b873593;
static const u32 r1 = 15;
static const u32 r2 = 13;
- static const u32 m = 5;
- static const u32 n = 0xe6546b64;
+ static const u32 m = 5;
+ static const u32 n = 0xe6546b64;
u32 hash = 0;
-#define mix(input) do { \
- u32 v = input; \
- v *= c1; \
- v = (v << r1) | (v >> (32 - r1)); \
- v *= c2; \
- hash ^= v; \
+#define mix(input) \
+ do { \
+ u32 v = input; \
+ v *= c1; \
+ v = (v << r1) | (v >> (32 - r1)); \
+ v *= c2; \
+ hash ^= v; \
hash = (hash << r2) | (hash >> (32 - r2)); \
- hash = hash * m + n; \
+ hash = hash * m + n; \
} while (0)
mix(keyp->target_class);
@@ -66,9 +64,10 @@ static inline u32 avtab_hash(const struct avtab_key *keyp, u32 mask)
return hash & mask;
}
-static struct avtab_node*
-avtab_insert_node(struct avtab *h, struct avtab_node **dst,
- const struct avtab_key *key, const struct avtab_datum *datum)
+static struct avtab_node *avtab_insert_node(struct avtab *h,
+ struct avtab_node **dst,
+ const struct avtab_key *key,
+ const struct avtab_datum *datum)
{
struct avtab_node *newnode;
struct avtab_extended_perms *xperms;
@@ -99,7 +98,7 @@ avtab_insert_node(struct avtab *h, struct avtab_node **dst,
static int avtab_node_cmp(const struct avtab_key *key1,
const struct avtab_key *key2)
{
- u16 specified = key1->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
+ u16 specified = key1->specified & ~(AVTAB_ENABLED | AVTAB_ENABLED_OLD);
if (key1->source_type == key2->source_type &&
key1->target_type == key2->target_type &&
@@ -129,8 +128,7 @@ static int avtab_insert(struct avtab *h, const struct avtab_key *key,
return -EINVAL;
hvalue = avtab_hash(key, h->mask);
- for (prev = NULL, cur = h->htable[hvalue];
- cur;
+ for (prev = NULL, cur = h->htable[hvalue]; cur;
prev = cur, cur = cur->next) {
cmp = avtab_node_cmp(key, &cur->key);
/* extended perms may not be unique */
@@ -163,8 +161,7 @@ struct avtab_node *avtab_insert_nonunique(struct avtab *h,
if (!h || !h->nslot || h->nel == U32_MAX)
return NULL;
hvalue = avtab_hash(key, h->mask);
- for (prev = NULL, cur = h->htable[hvalue];
- cur;
+ for (prev = NULL, cur = h->htable[hvalue]; cur;
prev = cur, cur = cur->next) {
cmp = avtab_node_cmp(key, &cur->key);
if (cmp <= 0)
@@ -188,8 +185,7 @@ struct avtab_node *avtab_search_node(struct avtab *h,
return NULL;
hvalue = avtab_hash(key, h->mask);
- for (cur = h->htable[hvalue]; cur;
- cur = cur->next) {
+ for (cur = h->htable[hvalue]; cur; cur = cur->next) {
cmp = avtab_node_cmp(key, &cur->key);
if (cmp == 0)
return cur;
@@ -199,8 +195,8 @@ struct avtab_node *avtab_search_node(struct avtab *h,
return NULL;
}
-struct avtab_node*
-avtab_search_node_next(struct avtab_node *node, u16 specified)
+struct avtab_node *avtab_search_node_next(struct avtab_node *node,
+ u16 specified)
{
struct avtab_key tmp_key;
struct avtab_node *cur;
@@ -314,17 +310,19 @@ void avtab_hash_eval(struct avtab *h, const char *tag)
if (chain_len > max_chain_len)
max_chain_len = chain_len;
- chain2_len_sum += (unsigned long long)chain_len * chain_len;
+ chain2_len_sum +=
+ (unsigned long long)chain_len * chain_len;
}
}
pr_debug("SELinux: %s: %d entries and %d/%d buckets used, "
- "longest chain length %d, sum of chain length^2 %llu\n",
- tag, h->nel, slots_used, h->nslot, max_chain_len,
- chain2_len_sum);
+ "longest chain length %d, sum of chain length^2 %llu\n",
+ tag, h->nel, slots_used, h->nslot, max_chain_len,
+ chain2_len_sum);
}
#endif /* CONFIG_SECURITY_SELINUX_DEBUG */
+/* clang-format off */
static const uint16_t spec_order[] = {
AVTAB_ALLOWED,
AVTAB_AUDITDENY,
@@ -336,11 +334,12 @@ static const uint16_t spec_order[] = {
AVTAB_XPERMS_AUDITALLOW,
AVTAB_XPERMS_DONTAUDIT
};
+/* clang-format on */
-int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
+int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *pol,
int (*insertf)(struct avtab *a, const struct avtab_key *k,
const struct avtab_datum *d, void *p),
- void *p)
+ void *p, bool conditional)
{
__le16 buf16[4];
u16 enabled;
@@ -365,9 +364,8 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
if (items2 > ARRAY_SIZE(buf32)) {
pr_err("SELinux: avtab: entry overflow\n");
return -EINVAL;
-
}
- rc = next_entry(buf32, fp, sizeof(u32)*items2);
+ rc = next_entry(buf32, fp, sizeof(u32) * items2);
if (rc) {
pr_err("SELinux: avtab: truncated entry\n");
return rc;
@@ -400,8 +398,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
pr_err("SELinux: avtab: null entry\n");
return -EINVAL;
}
- if ((val & AVTAB_AV) &&
- (val & AVTAB_TYPE)) {
+ if ((val & AVTAB_AV) && (val & AVTAB_TYPE)) {
pr_err("SELinux: avtab: entry has both access vectors and types\n");
return -EINVAL;
}
@@ -428,7 +425,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
return 0;
}
- rc = next_entry(buf16, fp, sizeof(u16)*4);
+ rc = next_entry(buf16, fp, sizeof(u16) * 4);
if (rc) {
pr_err("SELinux: avtab: truncated entry\n");
return rc;
@@ -454,10 +451,18 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
}
if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) &&
- (key.specified & AVTAB_XPERMS)) {
+ (key.specified & AVTAB_XPERMS)) {
pr_err("SELinux: avtab: policy version %u does not "
- "support extended permissions rules and one "
- "was specified\n", vers);
+ "support extended permissions rules and one "
+ "was specified\n",
+ vers);
+ return -EINVAL;
+ } else if ((vers < POLICYDB_VERSION_COND_XPERMS) &&
+ (key.specified & AVTAB_XPERMS) && conditional) {
+ pr_err("SELinux: avtab: policy version %u does not "
+ "support extended permissions rules in conditional "
+ "policies and one was specified\n",
+ vers);
return -EINVAL;
} else if (key.specified & AVTAB_XPERMS) {
memset(&xperms, 0, sizeof(struct avtab_extended_perms));
@@ -471,7 +476,8 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
pr_err("SELinux: avtab: truncated entry\n");
return rc;
}
- rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(xperms.perms.p));
+ rc = next_entry(buf32, fp,
+ sizeof(u32) * ARRAY_SIZE(xperms.perms.p));
if (rc) {
pr_err("SELinux: avtab: truncated entry\n");
return rc;
@@ -501,13 +507,12 @@ static int avtab_insertf(struct avtab *a, const struct avtab_key *k,
return avtab_insert(a, k, d);
}
-int avtab_read(struct avtab *a, void *fp, struct policydb *pol)
+int avtab_read(struct avtab *a, struct policy_file *fp, struct policydb *pol)
{
int rc;
__le32 buf[1];
u32 nel, i;
-
rc = next_entry(buf, fp, sizeof(u32));
if (rc < 0) {
pr_err("SELinux: avtab: truncated table\n");
@@ -525,7 +530,7 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol)
goto bad;
for (i = 0; i < nel; i++) {
- rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL);
+ rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL, false);
if (rc) {
if (rc == -ENOMEM)
pr_err("SELinux: avtab: out of memory\n");
@@ -545,7 +550,7 @@ bad:
goto out;
}
-int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp)
+int avtab_write_item(struct policydb *p, const struct avtab_node *cur, struct policy_file *fp)
{
__le16 buf16[4];
__le32 buf32[ARRAY_SIZE(cur->datum.u.xperms->perms.p)];
@@ -561,7 +566,8 @@ int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp)
return rc;
if (cur->key.specified & AVTAB_XPERMS) {
- rc = put_entry(&cur->datum.u.xperms->specified, sizeof(u8), 1, fp);
+ rc = put_entry(&cur->datum.u.xperms->specified, sizeof(u8), 1,
+ fp);
if (rc)
return rc;
rc = put_entry(&cur->datum.u.xperms->driver, sizeof(u8), 1, fp);
@@ -570,7 +576,7 @@ int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp)
for (i = 0; i < ARRAY_SIZE(cur->datum.u.xperms->perms.p); i++)
buf32[i] = cpu_to_le32(cur->datum.u.xperms->perms.p[i]);
rc = put_entry(buf32, sizeof(u32),
- ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp);
+ ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp);
} else {
buf32[0] = cpu_to_le32(cur->datum.u.data);
rc = put_entry(buf32, sizeof(u32), 1, fp);
@@ -580,7 +586,7 @@ int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp)
return 0;
}
-int avtab_write(struct policydb *p, struct avtab *a, void *fp)
+int avtab_write(struct policydb *p, struct avtab *a, struct policy_file *fp)
{
u32 i;
int rc = 0;
@@ -593,8 +599,7 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp)
return rc;
for (i = 0; i < a->nslot; i++) {
- for (cur = a->htable[i]; cur;
- cur = cur->next) {
+ for (cur = a->htable[i]; cur; cur = cur->next) {
rc = avtab_write_item(p, cur, fp);
if (rc)
return rc;
@@ -606,10 +611,6 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp)
void __init avtab_cache_init(void)
{
- avtab_node_cachep = kmem_cache_create("avtab_node",
- sizeof(struct avtab_node),
- 0, SLAB_PANIC, NULL);
- avtab_xperms_cachep = kmem_cache_create("avtab_extended_perms",
- sizeof(struct avtab_extended_perms),
- 0, SLAB_PANIC, NULL);
+ avtab_node_cachep = KMEM_CACHE(avtab_node, SLAB_PANIC);
+ avtab_xperms_cachep = KMEM_CACHE(avtab_extended_perms, SLAB_PANIC);
}
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index 3c3904bf02b0..850b3453f259 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -9,42 +9,42 @@
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
-/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
- *
- * Added conditional policy language extensions
- *
- * Copyright (C) 2003 Tresys Technology, LLC
+/* Updated: Frank Mayer <mayerf@tresys.com> and
+ * Karl MacMillan <kmacmillan@tresys.com>
+ * Added conditional policy language extensions
+ * Copyright (C) 2003 Tresys Technology, LLC
*
* Updated: Yuichi Nakamura <ynakam@hitachisoft.jp>
- * Tuned number of hash slots for avtab to reduce memory usage
+ * Tuned number of hash slots for avtab to reduce memory usage
*/
+
#ifndef _SS_AVTAB_H_
#define _SS_AVTAB_H_
#include "security.h"
struct avtab_key {
- u16 source_type; /* source type */
- u16 target_type; /* target type */
- u16 target_class; /* target object class */
-#define AVTAB_ALLOWED 0x0001
-#define AVTAB_AUDITALLOW 0x0002
-#define AVTAB_AUDITDENY 0x0004
-#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
-#define AVTAB_TRANSITION 0x0010
-#define AVTAB_MEMBER 0x0020
-#define AVTAB_CHANGE 0x0040
-#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
+ u16 source_type; /* source type */
+ u16 target_type; /* target type */
+ u16 target_class; /* target object class */
+#define AVTAB_ALLOWED 0x0001
+#define AVTAB_AUDITALLOW 0x0002
+#define AVTAB_AUDITDENY 0x0004
+#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
+#define AVTAB_TRANSITION 0x0010
+#define AVTAB_MEMBER 0x0020
+#define AVTAB_CHANGE 0x0040
+#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
/* extended permissions */
#define AVTAB_XPERMS_ALLOWED 0x0100
-#define AVTAB_XPERMS_AUDITALLOW 0x0200
+#define AVTAB_XPERMS_AUDITALLOW 0x0200
#define AVTAB_XPERMS_DONTAUDIT 0x0400
-#define AVTAB_XPERMS (AVTAB_XPERMS_ALLOWED | \
- AVTAB_XPERMS_AUDITALLOW | \
- AVTAB_XPERMS_DONTAUDIT)
-#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
-#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
- u16 specified; /* what field is specified */
+#define AVTAB_XPERMS \
+ (AVTAB_XPERMS_ALLOWED | AVTAB_XPERMS_AUDITALLOW | \
+ AVTAB_XPERMS_DONTAUDIT)
+#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
+#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
+ u16 specified; /* what field is specified */
};
/*
@@ -55,6 +55,7 @@ struct avtab_extended_perms {
/* These are not flags. All 256 values may be used */
#define AVTAB_XPERMS_IOCTLFUNCTION 0x01
#define AVTAB_XPERMS_IOCTLDRIVER 0x02
+#define AVTAB_XPERMS_NLMSG 0x03
/* extension of the avtab_key specified */
u8 specified; /* ioctl, netfilter, ... */
/*
@@ -82,16 +83,19 @@ struct avtab_node {
struct avtab {
struct avtab_node **htable;
- u32 nel; /* number of elements */
- u32 nslot; /* number of hash slots */
- u32 mask; /* mask to compute hash func */
+ u32 nel; /* number of elements */
+ u32 nslot; /* number of hash slots */
+ u32 mask; /* mask to compute hash func */
};
void avtab_init(struct avtab *h);
-int avtab_alloc(struct avtab *, u32);
+int avtab_alloc(struct avtab *h, u32 nrules);
int avtab_alloc_dup(struct avtab *new, const struct avtab *orig);
void avtab_destroy(struct avtab *h);
+#define MAX_AVTAB_HASH_BITS 16
+#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS)
+
#ifdef CONFIG_SECURITY_SELINUX_DEBUG
void avtab_hash_eval(struct avtab *h, const char *tag);
#else
@@ -101,14 +105,16 @@ static inline void avtab_hash_eval(struct avtab *h, const char *tag)
#endif
struct policydb;
-int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
+struct policy_file;
+int avtab_read_item(struct avtab *a, struct policy_file *fp, struct policydb *pol,
int (*insert)(struct avtab *a, const struct avtab_key *k,
const struct avtab_datum *d, void *p),
- void *p);
+ void *p, bool conditional);
-int avtab_read(struct avtab *a, void *fp, struct policydb *pol);
-int avtab_write_item(struct policydb *p, const struct avtab_node *cur, void *fp);
-int avtab_write(struct policydb *p, struct avtab *a, void *fp);
+int avtab_read(struct avtab *a, struct policy_file *fp, struct policydb *pol);
+int avtab_write_item(struct policydb *p, const struct avtab_node *cur,
+ struct policy_file *fp);
+int avtab_write(struct policydb *p, struct avtab *a, struct policy_file *fp);
struct avtab_node *avtab_insert_nonunique(struct avtab *h,
const struct avtab_key *key,
@@ -116,11 +122,7 @@ struct avtab_node *avtab_insert_nonunique(struct avtab *h,
struct avtab_node *avtab_search_node(struct avtab *h,
const struct avtab_key *key);
+struct avtab_node *avtab_search_node_next(struct avtab_node *node,
+ u16 specified);
-struct avtab_node *avtab_search_node_next(struct avtab_node *node, u16 specified);
-
-#define MAX_AVTAB_HASH_BITS 16
-#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS)
-
-#endif /* _SS_AVTAB_H_ */
-
+#endif /* _SS_AVTAB_H_ */
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index 81ff676f209a..1bebfcb9c6a1 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -1,8 +1,7 @@
-// SPDX-License-Identifier: GPL-2.0-only
+/* SPDX-License-Identifier: GPL-2.0-only */
/* Authors: Karl MacMillan <kmacmillan@tresys.com>
* Frank Mayer <mayerf@tresys.com>
- *
- * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
*/
#include <linux/kernel.h>
@@ -166,11 +165,13 @@ void cond_policydb_destroy(struct policydb *p)
int cond_init_bool_indexes(struct policydb *p)
{
kfree(p->bool_val_to_struct);
- p->bool_val_to_struct = kmalloc_array(p->p_bools.nprim,
- sizeof(*p->bool_val_to_struct),
- GFP_KERNEL);
+ p->bool_val_to_struct = kmalloc_array(
+ p->p_bools.nprim, sizeof(*p->bool_val_to_struct), GFP_KERNEL);
if (!p->bool_val_to_struct)
return -ENOMEM;
+
+ avtab_hash_eval(&p->te_cond_avtab, "conditional_rules");
+
return 0;
}
@@ -205,7 +206,7 @@ static int bool_isvalid(struct cond_bool_datum *b)
return 1;
}
-int cond_read_bool(struct policydb *p, struct symtab *s, void *fp)
+int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct cond_bool_datum *booldatum;
@@ -229,17 +230,11 @@ int cond_read_bool(struct policydb *p, struct symtab *s, void *fp)
goto err;
len = le32_to_cpu(buf[2]);
- if (((len == 0) || (len == (u32)-1)))
- goto err;
- rc = -ENOMEM;
- key = kmalloc(len + 1, GFP_KERNEL);
- if (!key)
- goto err;
- rc = next_entry(key, fp, len);
+ rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto err;
- key[len] = '\0';
+
rc = symtab_insert(s, key, booldatum);
if (rc)
goto err;
@@ -287,7 +282,8 @@ static int cond_insertf(struct avtab *a, const struct avtab_key *k,
if (other) {
node_ptr = avtab_search_node(&p->te_cond_avtab, k);
if (node_ptr) {
- if (avtab_search_node_next(node_ptr, k->specified)) {
+ if (avtab_search_node_next(node_ptr,
+ k->specified)) {
pr_err("SELinux: too many conflicting type rules.\n");
return -EINVAL;
}
@@ -321,7 +317,7 @@ static int cond_insertf(struct avtab *a, const struct avtab_key *k,
return 0;
}
-static int cond_read_av_list(struct policydb *p, void *fp,
+static int cond_read_av_list(struct policydb *p, struct policy_file *fp,
struct cond_av_list *list,
struct cond_av_list *other)
{
@@ -347,7 +343,7 @@ static int cond_read_av_list(struct policydb *p, void *fp,
for (i = 0; i < len; i++) {
data.dst = &list->nodes[i];
rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf,
- &data);
+ &data, true);
if (rc) {
kfree(list->nodes);
list->nodes = NULL;
@@ -373,7 +369,7 @@ static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr)
return 1;
}
-static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
+static int cond_read_node(struct policydb *p, struct cond_node *node, struct policy_file *fp)
{
__le32 buf[2];
u32 i, len;
@@ -413,7 +409,7 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
return cond_read_av_list(p, fp, &node->false_list, &node->true_list);
}
-int cond_read_list(struct policydb *p, void *fp)
+int cond_read_list(struct policydb *p, struct policy_file *fp)
{
__le32 buf[1];
u32 i, len;
@@ -451,7 +447,7 @@ int cond_write_bool(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct cond_bool_datum *booldatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[3];
u32 len;
int rc;
@@ -478,8 +474,8 @@ int cond_write_bool(void *vkey, void *datum, void *ptr)
* the conditional. This means that the avtab with the conditional
* rules will not be saved but will be rebuilt on policy load.
*/
-static int cond_write_av_list(struct policydb *p,
- struct cond_av_list *list, struct policy_file *fp)
+static int cond_write_av_list(struct policydb *p, struct cond_av_list *list,
+ struct policy_file *fp)
{
__le32 buf[1];
u32 i;
@@ -500,7 +496,7 @@ static int cond_write_av_list(struct policydb *p,
}
static int cond_write_node(struct policydb *p, struct cond_node *node,
- struct policy_file *fp)
+ struct policy_file *fp)
{
__le32 buf[2];
int rc;
@@ -534,7 +530,7 @@ static int cond_write_node(struct policydb *p, struct cond_node *node,
return 0;
}
-int cond_write_list(struct policydb *p, void *fp)
+int cond_write_list(struct policydb *p, struct policy_file *fp)
{
u32 i;
__le32 buf[1];
@@ -555,7 +551,7 @@ int cond_write_list(struct policydb *p, void *fp)
}
void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
- struct extended_perms_decision *xpermd)
+ struct extended_perms_decision *xpermd)
{
struct avtab_node *node;
@@ -563,7 +559,7 @@ void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
return;
for (node = avtab_search_node(ctab, key); node;
- node = avtab_search_node_next(node, key->specified)) {
+ node = avtab_search_node_next(node, key->specified)) {
if (node->key.specified & AVTAB_ENABLED)
services_compute_xperms_decision(xpermd, node);
}
@@ -572,7 +568,7 @@ void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
* av table, and if so, add them to the result
*/
void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
- struct av_decision *avd, struct extended_perms *xperms)
+ struct av_decision *avd, struct extended_perms *xperms)
{
struct avtab_node *node;
@@ -580,30 +576,30 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
return;
for (node = avtab_search_node(ctab, key); node;
- node = avtab_search_node_next(node, key->specified)) {
- if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) ==
- (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
+ node = avtab_search_node_next(node, key->specified)) {
+ if ((u16)(AVTAB_ALLOWED | AVTAB_ENABLED) ==
+ (node->key.specified & (AVTAB_ALLOWED | AVTAB_ENABLED)))
avd->allowed |= node->datum.u.data;
- if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) ==
- (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
+ if ((u16)(AVTAB_AUDITDENY | AVTAB_ENABLED) ==
+ (node->key.specified & (AVTAB_AUDITDENY | AVTAB_ENABLED)))
/* Since a '0' in an auditdeny mask represents a
* permission we do NOT want to audit (dontaudit), we use
* the '&' operand to ensure that all '0's in the mask
* are retained (much unlike the allow and auditallow cases).
*/
avd->auditdeny &= node->datum.u.data;
- if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
- (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
+ if ((u16)(AVTAB_AUDITALLOW | AVTAB_ENABLED) ==
+ (node->key.specified & (AVTAB_AUDITALLOW | AVTAB_ENABLED)))
avd->auditallow |= node->datum.u.data;
if (xperms && (node->key.specified & AVTAB_ENABLED) &&
- (node->key.specified & AVTAB_XPERMS))
+ (node->key.specified & AVTAB_XPERMS))
services_compute_xperms_drivers(xperms, node);
}
}
static int cond_dup_av_list(struct cond_av_list *new,
- struct cond_av_list *orig,
- struct avtab *avtab)
+ const struct cond_av_list *orig,
+ struct avtab *avtab)
{
u32 i;
@@ -614,9 +610,8 @@ static int cond_dup_av_list(struct cond_av_list *new,
return -ENOMEM;
for (i = 0; i < orig->len; i++) {
- new->nodes[i] = avtab_insert_nonunique(avtab,
- &orig->nodes[i]->key,
- &orig->nodes[i]->datum);
+ new->nodes[i] = avtab_insert_nonunique(
+ avtab, &orig->nodes[i]->key, &orig->nodes[i]->datum);
if (!new->nodes[i])
return -ENOMEM;
new->len++;
@@ -626,7 +621,7 @@ static int cond_dup_av_list(struct cond_av_list *new,
}
static int duplicate_policydb_cond_list(struct policydb *newp,
- struct policydb *origp)
+ const struct policydb *origp)
{
int rc;
u32 i;
@@ -637,19 +632,19 @@ static int duplicate_policydb_cond_list(struct policydb *newp,
newp->cond_list_len = 0;
newp->cond_list = kcalloc(origp->cond_list_len,
- sizeof(*newp->cond_list),
- GFP_KERNEL);
+ sizeof(*newp->cond_list), GFP_KERNEL);
if (!newp->cond_list)
goto error;
for (i = 0; i < origp->cond_list_len; i++) {
struct cond_node *newn = &newp->cond_list[i];
- struct cond_node *orign = &origp->cond_list[i];
+ const struct cond_node *orign = &origp->cond_list[i];
newp->cond_list_len++;
newn->cur_state = orign->cur_state;
- newn->expr.nodes = kmemdup(orign->expr.nodes,
+ newn->expr.nodes =
+ kmemdup(orign->expr.nodes,
orign->expr.len * sizeof(*orign->expr.nodes),
GFP_KERNEL);
if (!newn->expr.nodes)
@@ -658,12 +653,12 @@ static int duplicate_policydb_cond_list(struct policydb *newp,
newn->expr.len = orign->expr.len;
rc = cond_dup_av_list(&newn->true_list, &orign->true_list,
- &newp->te_cond_avtab);
+ &newp->te_cond_avtab);
if (rc)
goto error;
rc = cond_dup_av_list(&newn->false_list, &orign->false_list,
- &newp->te_cond_avtab);
+ &newp->te_cond_avtab);
if (rc)
goto error;
}
@@ -683,7 +678,8 @@ static int cond_bools_destroy(void *key, void *datum, void *args)
return 0;
}
-static int cond_bools_copy(struct hashtab_node *new, struct hashtab_node *orig, void *args)
+static int cond_bools_copy(struct hashtab_node *new,
+ const struct hashtab_node *orig, void *args)
{
struct cond_bool_datum *datum;
@@ -709,7 +705,7 @@ static int cond_bools_index(void *key, void *datum, void *args)
}
static int duplicate_policydb_bools(struct policydb *newdb,
- struct policydb *orig)
+ const struct policydb *orig)
{
struct cond_bool_datum **cond_bool_array;
int rc;
@@ -721,7 +717,7 @@ static int duplicate_policydb_bools(struct policydb *newdb,
return -ENOMEM;
rc = hashtab_duplicate(&newdb->p_bools.table, &orig->p_bools.table,
- cond_bools_copy, cond_bools_destroy, NULL);
+ cond_bools_copy, cond_bools_destroy, NULL);
if (rc) {
kfree(cond_bool_array);
return -ENOMEM;
@@ -742,7 +738,7 @@ void cond_policydb_destroy_dup(struct policydb *p)
cond_policydb_destroy(p);
}
-int cond_policydb_dup(struct policydb *new, struct policydb *orig)
+int cond_policydb_dup(struct policydb *new, const struct policydb *orig)
{
cond_policydb_init(new);
diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h
index 5a7b51278dc6..468e98ad3ea1 100644
--- a/security/selinux/ss/conditional.h
+++ b/security/selinux/ss/conditional.h
@@ -1,8 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Authors: Karl MacMillan <kmacmillan@tresys.com>
* Frank Mayer <mayerf@tresys.com>
- *
- * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
*/
#ifndef _CONDITIONAL_H_
@@ -20,14 +19,14 @@
* in reverse polish notation.
*/
struct cond_expr_node {
-#define COND_BOOL 1 /* plain bool */
-#define COND_NOT 2 /* !bool */
-#define COND_OR 3 /* bool || bool */
-#define COND_AND 4 /* bool && bool */
-#define COND_XOR 5 /* bool ^ bool */
-#define COND_EQ 6 /* bool == bool */
-#define COND_NEQ 7 /* bool != bool */
-#define COND_LAST COND_NEQ
+#define COND_BOOL 1 /* plain bool */
+#define COND_NOT 2 /* !bool */
+#define COND_OR 3 /* bool || bool */
+#define COND_AND 4 /* bool && bool */
+#define COND_XOR 5 /* bool ^ bool */
+#define COND_EQ 6 /* bool == bool */
+#define COND_NEQ 7 /* bool != bool */
+#define COND_LAST COND_NEQ
u32 expr_type;
u32 boolean;
};
@@ -69,17 +68,17 @@ int cond_destroy_bool(void *key, void *datum, void *p);
int cond_index_bool(void *key, void *datum, void *datap);
-int cond_read_bool(struct policydb *p, struct symtab *s, void *fp);
-int cond_read_list(struct policydb *p, void *fp);
+int cond_read_bool(struct policydb *p, struct symtab *s, struct policy_file *fp);
+int cond_read_list(struct policydb *p, struct policy_file *fp);
int cond_write_bool(void *key, void *datum, void *ptr);
-int cond_write_list(struct policydb *p, void *fp);
+int cond_write_list(struct policydb *p, struct policy_file *fp);
void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
- struct av_decision *avd, struct extended_perms *xperms);
+ struct av_decision *avd, struct extended_perms *xperms);
void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
- struct extended_perms_decision *xpermd);
+ struct extended_perms_decision *xpermd);
void evaluate_cond_nodes(struct policydb *p);
void cond_policydb_destroy_dup(struct policydb *p);
-int cond_policydb_dup(struct policydb *new, struct policydb *orig);
+int cond_policydb_dup(struct policydb *new, const struct policydb *orig);
#endif /* _CONDITIONAL_H_ */
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h
index f76eb3128ad5..203033cfad67 100644
--- a/security/selinux/ss/constraint.h
+++ b/security/selinux/ss/constraint.h
@@ -13,6 +13,7 @@
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
+
#ifndef _SS_CONSTRAINT_H_
#define _SS_CONSTRAINT_H_
@@ -21,43 +22,43 @@
#define CEXPR_MAXDEPTH 5
struct constraint_expr {
-#define CEXPR_NOT 1 /* not expr */
-#define CEXPR_AND 2 /* expr and expr */
-#define CEXPR_OR 3 /* expr or expr */
-#define CEXPR_ATTR 4 /* attr op attr */
-#define CEXPR_NAMES 5 /* attr op names */
- u32 expr_type; /* expression type */
-
-#define CEXPR_USER 1 /* user */
-#define CEXPR_ROLE 2 /* role */
-#define CEXPR_TYPE 4 /* type */
-#define CEXPR_TARGET 8 /* target if set, source otherwise */
-#define CEXPR_XTARGET 16 /* special 3rd target for validatetrans rule */
-#define CEXPR_L1L2 32 /* low level 1 vs. low level 2 */
-#define CEXPR_L1H2 64 /* low level 1 vs. high level 2 */
-#define CEXPR_H1L2 128 /* high level 1 vs. low level 2 */
-#define CEXPR_H1H2 256 /* high level 1 vs. high level 2 */
-#define CEXPR_L1H1 512 /* low level 1 vs. high level 1 */
-#define CEXPR_L2H2 1024 /* low level 2 vs. high level 2 */
- u32 attr; /* attribute */
-
-#define CEXPR_EQ 1 /* == or eq */
-#define CEXPR_NEQ 2 /* != */
-#define CEXPR_DOM 3 /* dom */
-#define CEXPR_DOMBY 4 /* domby */
-#define CEXPR_INCOMP 5 /* incomp */
- u32 op; /* operator */
-
- struct ebitmap names; /* names */
+#define CEXPR_NOT 1 /* not expr */
+#define CEXPR_AND 2 /* expr and expr */
+#define CEXPR_OR 3 /* expr or expr */
+#define CEXPR_ATTR 4 /* attr op attr */
+#define CEXPR_NAMES 5 /* attr op names */
+ u32 expr_type; /* expression type */
+
+#define CEXPR_USER 1 /* user */
+#define CEXPR_ROLE 2 /* role */
+#define CEXPR_TYPE 4 /* type */
+#define CEXPR_TARGET 8 /* target if set, source otherwise */
+#define CEXPR_XTARGET 16 /* special 3rd target for validatetrans rule */
+#define CEXPR_L1L2 32 /* low level 1 vs. low level 2 */
+#define CEXPR_L1H2 64 /* low level 1 vs. high level 2 */
+#define CEXPR_H1L2 128 /* high level 1 vs. low level 2 */
+#define CEXPR_H1H2 256 /* high level 1 vs. high level 2 */
+#define CEXPR_L1H1 512 /* low level 1 vs. high level 1 */
+#define CEXPR_L2H2 1024 /* low level 2 vs. high level 2 */
+ u32 attr; /* attribute */
+
+#define CEXPR_EQ 1 /* == or eq */
+#define CEXPR_NEQ 2 /* != */
+#define CEXPR_DOM 3 /* dom */
+#define CEXPR_DOMBY 4 /* domby */
+#define CEXPR_INCOMP 5 /* incomp */
+ u32 op; /* operator */
+
+ struct ebitmap names; /* names */
struct type_set *type_names;
- struct constraint_expr *next; /* next expression */
+ struct constraint_expr *next; /* next expression */
};
struct constraint_node {
- u32 permissions; /* constrained permissions */
- struct constraint_expr *expr; /* constraint on permissions */
- struct constraint_node *next; /* next constraint */
+ u32 permissions; /* constrained permissions */
+ struct constraint_expr *expr; /* constraint on permissions */
+ struct constraint_node *next; /* next constraint */
};
-#endif /* _SS_CONSTRAINT_H_ */
+#endif /* _SS_CONSTRAINT_H_ */
diff --git a/security/selinux/ss/context.c b/security/selinux/ss/context.c
index 38bc0aa524a6..a528b7f76280 100644
--- a/security/selinux/ss/context.c
+++ b/security/selinux/ss/context.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Implementations of the security context functions.
*
@@ -20,7 +20,7 @@ u32 context_compute_hash(const struct context *c)
* context struct with only the len & str set (and vice versa)
* under a given policy. Since context structs from different
* policies should never meet, it is safe to hash valid and
- * invalid contexts differently. The context_cmp() function
+ * invalid contexts differently. The context_equal() function
* already operates under the same assumption.
*/
if (c->len)
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h
index 1f59468c0759..dd3b9b5b588e 100644
--- a/security/selinux/ss/context.h
+++ b/security/selinux/ss/context.h
@@ -13,6 +13,7 @@
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
+
#ifndef _SS_CONTEXT_H_
#define _SS_CONTEXT_H_
@@ -28,9 +29,9 @@ struct context {
u32 user;
u32 role;
u32 type;
- u32 len; /* length of string in bytes */
+ u32 len; /* length of string in bytes */
struct mls_range range;
- char *str; /* string representation if context cannot be mapped. */
+ char *str; /* string representation if context cannot be mapped. */
};
static inline void mls_context_init(struct context *c)
@@ -38,7 +39,8 @@ static inline void mls_context_init(struct context *c)
memset(&c->range, 0, sizeof(c->range));
}
-static inline int mls_context_cpy(struct context *dst, const struct context *src)
+static inline int mls_context_cpy(struct context *dst,
+ const struct context *src)
{
int rc;
@@ -58,7 +60,8 @@ out:
/*
* Sets both levels in the MLS range of 'dst' to the low level of 'src'.
*/
-static inline int mls_context_cpy_low(struct context *dst, const struct context *src)
+static inline int mls_context_cpy_low(struct context *dst,
+ const struct context *src)
{
int rc;
@@ -78,7 +81,8 @@ out:
/*
* Sets both levels in the MLS range of 'dst' to the high level of 'src'.
*/
-static inline int mls_context_cpy_high(struct context *dst, const struct context *src)
+static inline int mls_context_cpy_high(struct context *dst,
+ const struct context *src)
{
int rc;
@@ -95,9 +99,9 @@ out:
return rc;
}
-
static inline int mls_context_glblub(struct context *dst,
- const struct context *c1, const struct context *c2)
+ const struct context *c1,
+ const struct context *c2)
{
struct mls_range *dr = &dst->range;
const struct mls_range *r1 = &c1->range, *r2 = &c2->range;
@@ -114,13 +118,13 @@ static inline int mls_context_glblub(struct context *dst,
/* Take the least of the high */
dr->level[1].sens = min(r1->level[1].sens, r2->level[1].sens);
- rc = ebitmap_and(&dr->level[0].cat,
- &r1->level[0].cat, &r2->level[0].cat);
+ rc = ebitmap_and(&dr->level[0].cat, &r1->level[0].cat,
+ &r2->level[0].cat);
if (rc)
goto out;
- rc = ebitmap_and(&dr->level[1].cat,
- &r1->level[1].cat, &r2->level[1].cat);
+ rc = ebitmap_and(&dr->level[1].cat, &r1->level[1].cat,
+ &r2->level[1].cat);
if (rc)
goto out;
@@ -128,12 +132,13 @@ out:
return rc;
}
-static inline int mls_context_cmp(const struct context *c1, const struct context *c2)
+static inline bool mls_context_equal(const struct context *c1,
+ const struct context *c2)
{
return ((c1->range.level[0].sens == c2->range.level[0].sens) &&
- ebitmap_cmp(&c1->range.level[0].cat, &c2->range.level[0].cat) &&
+ ebitmap_equal(&c1->range.level[0].cat, &c2->range.level[0].cat) &&
(c1->range.level[1].sens == c2->range.level[1].sens) &&
- ebitmap_cmp(&c1->range.level[1].cat, &c2->range.level[1].cat));
+ ebitmap_equal(&c1->range.level[1].cat, &c2->range.level[1].cat));
}
static inline void mls_context_destroy(struct context *c)
@@ -183,19 +188,17 @@ static inline void context_destroy(struct context *c)
mls_context_destroy(c);
}
-static inline int context_cmp(const struct context *c1, const struct context *c2)
+static inline bool context_equal(const struct context *c1,
+ const struct context *c2)
{
if (c1->len && c2->len)
return (c1->len == c2->len && !strcmp(c1->str, c2->str));
if (c1->len || c2->len)
return 0;
- return ((c1->user == c2->user) &&
- (c1->role == c2->role) &&
- (c1->type == c2->type) &&
- mls_context_cmp(c1, c2));
+ return ((c1->user == c2->user) && (c1->role == c2->role) &&
+ (c1->type == c2->type) && mls_context_equal(c1, c2));
}
u32 context_compute_hash(const struct context *c);
-#endif /* _SS_CONTEXT_H_ */
-
+#endif /* _SS_CONTEXT_H_ */
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index 77875ad355f7..43bc19e21960 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* Implementation of the extensible bitmap type.
*
@@ -6,14 +6,11 @@
*/
/*
* Updated: Hewlett-Packard <paul@paul-moore.com>
+ * Added support to import/export the NetLabel category bitmap
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
*
- * Added support to import/export the NetLabel category bitmap
- *
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
- */
-/*
* Updated: KaiGai Kohei <kaigai@ak.jp.nec.com>
- * Applied standard bit operations to improve bitmap scanning.
+ * Applied standard bit operations to improve bitmap scanning.
*/
#include <linux/kernel.h>
@@ -24,30 +21,29 @@
#include "ebitmap.h"
#include "policydb.h"
-#define BITS_PER_U64 (sizeof(u64) * 8)
+#define BITS_PER_U64 ((u32)(sizeof(u64) * 8))
static struct kmem_cache *ebitmap_node_cachep __ro_after_init;
-int ebitmap_cmp(const struct ebitmap *e1, const struct ebitmap *e2)
+bool ebitmap_equal(const struct ebitmap *e1, const struct ebitmap *e2)
{
const struct ebitmap_node *n1, *n2;
if (e1->highbit != e2->highbit)
- return 0;
+ return false;
n1 = e1->node;
n2 = e2->node;
- while (n1 && n2 &&
- (n1->startbit == n2->startbit) &&
+ while (n1 && n2 && (n1->startbit == n2->startbit) &&
!memcmp(n1->maps, n2->maps, EBITMAP_SIZE / 8)) {
n1 = n1->next;
n2 = n2->next;
}
if (n1 || n2)
- return 0;
+ return false;
- return 1;
+ return true;
}
int ebitmap_cpy(struct ebitmap *dst, const struct ebitmap *src)
@@ -79,14 +75,17 @@ int ebitmap_cpy(struct ebitmap *dst, const struct ebitmap *src)
return 0;
}
-int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1, const struct ebitmap *e2)
+int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1,
+ const struct ebitmap *e2)
{
struct ebitmap_node *n;
- int bit, rc;
+ u32 bit;
+ int rc;
ebitmap_init(dst);
- ebitmap_for_each_positive_bit(e1, n, bit) {
+ ebitmap_for_each_positive_bit(e1, n, bit)
+ {
if (ebitmap_get_bit(e2, bit)) {
rc = ebitmap_set_bit(dst, bit, 1);
if (rc < 0)
@@ -96,7 +95,6 @@ int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1, const struct ebit
return 0;
}
-
#ifdef CONFIG_NETLABEL
/**
* ebitmap_netlbl_export - Export an ebitmap into a NetLabel category bitmap
@@ -131,10 +129,8 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap,
for (iter = 0; iter < EBITMAP_UNIT_NUMS; iter++) {
e_map = e_iter->maps[iter];
if (e_map != 0) {
- rc = netlbl_catmap_setlong(catmap,
- offset,
- e_map,
- GFP_ATOMIC);
+ rc = netlbl_catmap_setlong(catmap, offset,
+ e_map, GFP_ATOMIC);
if (rc != 0)
goto netlbl_export_failure;
}
@@ -185,7 +181,8 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap,
if (e_iter == NULL ||
offset >= e_iter->startbit + EBITMAP_SIZE) {
e_prev = e_iter;
- e_iter = kmem_cache_zalloc(ebitmap_node_cachep, GFP_ATOMIC);
+ e_iter = kmem_cache_zalloc(ebitmap_node_cachep,
+ GFP_ATOMIC);
if (e_iter == NULL)
goto netlbl_import_failure;
e_iter->startbit = offset - (offset % EBITMAP_SIZE);
@@ -218,7 +215,8 @@ netlbl_import_failure:
* if last_e2bit is non-zero, the highest set bit in e2 cannot exceed
* last_e2bit.
*/
-int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, u32 last_e2bit)
+int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2,
+ u32 last_e2bit)
{
const struct ebitmap_node *n1, *n2;
int i;
@@ -234,8 +232,8 @@ int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, u32 las
n1 = n1->next;
continue;
}
- for (i = EBITMAP_UNIT_NUMS - 1; (i >= 0) && !n2->maps[i]; )
- i--; /* Skip trailing NULL map entries */
+ for (i = EBITMAP_UNIT_NUMS - 1; (i >= 0) && !n2->maps[i];)
+ i--; /* Skip trailing NULL map entries */
if (last_e2bit && (i >= 0)) {
u32 lastsetbit = n2->startbit + i * EBITMAP_UNIT_SIZE +
__fls(n2->maps[i]);
@@ -259,7 +257,7 @@ int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, u32 las
return 1;
}
-int ebitmap_get_bit(const struct ebitmap *e, unsigned long bit)
+int ebitmap_get_bit(const struct ebitmap *e, u32 bit)
{
const struct ebitmap_node *n;
@@ -276,7 +274,7 @@ int ebitmap_get_bit(const struct ebitmap *e, unsigned long bit)
return 0;
}
-int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
+int ebitmap_set_bit(struct ebitmap *e, u32 bit, int value)
{
struct ebitmap_node *n, *prev, *new;
@@ -287,7 +285,7 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
if (value) {
ebitmap_node_set_bit(n, bit);
} else {
- unsigned int s;
+ u32 s;
ebitmap_node_clr_bit(n, bit);
@@ -302,8 +300,8 @@ int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
* within the bitmap
*/
if (prev)
- e->highbit = prev->startbit
- + EBITMAP_SIZE;
+ e->highbit = prev->startbit +
+ EBITMAP_SIZE;
else
e->highbit = 0;
}
@@ -362,15 +360,15 @@ void ebitmap_destroy(struct ebitmap *e)
e->node = NULL;
}
-int ebitmap_read(struct ebitmap *e, void *fp)
+int ebitmap_read(struct ebitmap *e, struct policy_file *fp)
{
struct ebitmap_node *n = NULL;
- u32 mapunit, count, startbit, index;
+ u32 mapunit, count, startbit, index, i;
__le32 ebitmap_start;
u64 map;
__le64 mapbits;
__le32 buf[3];
- int rc, i;
+ int rc;
ebitmap_init(e);
@@ -384,7 +382,7 @@ int ebitmap_read(struct ebitmap *e, void *fp)
if (mapunit != BITS_PER_U64) {
pr_err("SELinux: ebitmap: map size %u does not "
- "match my size %zd (high bit was %d)\n",
+ "match my size %u (high bit was %u)\n",
mapunit, BITS_PER_U64, e->highbit);
goto bad;
}
@@ -410,13 +408,13 @@ int ebitmap_read(struct ebitmap *e, void *fp)
startbit = le32_to_cpu(ebitmap_start);
if (startbit & (mapunit - 1)) {
- pr_err("SELinux: ebitmap start bit (%d) is "
+ pr_err("SELinux: ebitmap start bit (%u) is "
"not a multiple of the map unit size (%u)\n",
startbit, mapunit);
goto bad;
}
if (startbit > e->highbit - mapunit) {
- pr_err("SELinux: ebitmap start bit (%d) is "
+ pr_err("SELinux: ebitmap start bit (%u) is "
"beyond the end of the bitmap (%u)\n",
startbit, (e->highbit - mapunit));
goto bad;
@@ -424,7 +422,8 @@ int ebitmap_read(struct ebitmap *e, void *fp)
if (!n || startbit >= n->startbit + EBITMAP_SIZE) {
struct ebitmap_node *tmp;
- tmp = kmem_cache_zalloc(ebitmap_node_cachep, GFP_KERNEL);
+ tmp = kmem_cache_zalloc(ebitmap_node_cachep,
+ GFP_KERNEL);
if (!tmp) {
pr_err("SELinux: ebitmap: out of memory\n");
rc = -ENOMEM;
@@ -438,8 +437,8 @@ int ebitmap_read(struct ebitmap *e, void *fp)
e->node = tmp;
n = tmp;
} else if (startbit <= n->startbit) {
- pr_err("SELinux: ebitmap: start bit %d"
- " comes after start bit %d\n",
+ pr_err("SELinux: ebitmap: start bit %u"
+ " comes after start bit %u\n",
startbit, n->startbit);
goto bad;
}
@@ -450,6 +449,10 @@ int ebitmap_read(struct ebitmap *e, void *fp)
goto bad;
}
map = le64_to_cpu(mapbits);
+ if (!map) {
+ pr_err("SELinux: ebitmap: empty map\n");
+ goto bad;
+ }
index = (startbit - n->startbit) / EBITMAP_UNIT_SIZE;
while (map) {
@@ -457,6 +460,13 @@ int ebitmap_read(struct ebitmap *e, void *fp)
map = EBITMAP_SHIFT_UNIT_SIZE(map);
}
}
+
+ if (n && n->startbit + EBITMAP_SIZE != e->highbit) {
+ pr_err("SELinux: ebitmap: high bit %u is not equal to the expected value %zu\n",
+ e->highbit, n->startbit + EBITMAP_SIZE);
+ goto bad;
+ }
+
ok:
rc = 0;
out:
@@ -468,21 +478,23 @@ bad:
goto out;
}
-int ebitmap_write(const struct ebitmap *e, void *fp)
+int ebitmap_write(const struct ebitmap *e, struct policy_file *fp)
{
struct ebitmap_node *n;
- u32 count;
+ u32 bit, count, last_bit, last_startbit;
__le32 buf[3];
u64 map;
- int bit, last_bit, last_startbit, rc;
+ int rc;
buf[0] = cpu_to_le32(BITS_PER_U64);
count = 0;
last_bit = 0;
- last_startbit = -1;
- ebitmap_for_each_positive_bit(e, n, bit) {
- if (rounddown(bit, (int)BITS_PER_U64) > last_startbit) {
+ last_startbit = U32_MAX;
+ ebitmap_for_each_positive_bit(e, n, bit)
+ {
+ if (last_startbit == U32_MAX ||
+ rounddown(bit, BITS_PER_U64) > last_startbit) {
count++;
last_startbit = rounddown(bit, BITS_PER_U64);
}
@@ -496,9 +508,11 @@ int ebitmap_write(const struct ebitmap *e, void *fp)
return rc;
map = 0;
- last_startbit = INT_MIN;
- ebitmap_for_each_positive_bit(e, n, bit) {
- if (rounddown(bit, (int)BITS_PER_U64) > last_startbit) {
+ last_startbit = U32_MAX;
+ ebitmap_for_each_positive_bit(e, n, bit)
+ {
+ if (last_startbit == U32_MAX ||
+ rounddown(bit, BITS_PER_U64) > last_startbit) {
__le64 buf64[1];
/* this is the very first bit */
@@ -558,7 +572,5 @@ u32 ebitmap_hash(const struct ebitmap *e, u32 hash)
void __init ebitmap_cache_init(void)
{
- ebitmap_node_cachep = kmem_cache_create("ebitmap_node",
- sizeof(struct ebitmap_node),
- 0, SLAB_PANIC, NULL);
+ ebitmap_node_cachep = KMEM_CACHE(ebitmap_node, SLAB_PANIC);
}
diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h
index e3c807cfad90..c9569998f287 100644
--- a/security/selinux/ss/ebitmap.h
+++ b/security/selinux/ss/ebitmap.h
@@ -12,23 +12,25 @@
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
+
#ifndef _SS_EBITMAP_H_
#define _SS_EBITMAP_H_
#include <net/netlabel.h>
#ifdef CONFIG_64BIT
-#define EBITMAP_NODE_SIZE 64
+#define EBITMAP_NODE_SIZE 64
#else
-#define EBITMAP_NODE_SIZE 32
+#define EBITMAP_NODE_SIZE 32
#endif
-#define EBITMAP_UNIT_NUMS ((EBITMAP_NODE_SIZE-sizeof(void *)-sizeof(u32))\
- / sizeof(unsigned long))
-#define EBITMAP_UNIT_SIZE BITS_PER_LONG
-#define EBITMAP_SIZE (EBITMAP_UNIT_NUMS * EBITMAP_UNIT_SIZE)
-#define EBITMAP_BIT 1ULL
-#define EBITMAP_SHIFT_UNIT_SIZE(x) \
+#define EBITMAP_UNIT_NUMS \
+ ((EBITMAP_NODE_SIZE - sizeof(void *) - sizeof(u32)) / \
+ sizeof(unsigned long))
+#define EBITMAP_UNIT_SIZE BITS_PER_LONG
+#define EBITMAP_SIZE (EBITMAP_UNIT_NUMS * EBITMAP_UNIT_SIZE)
+#define EBITMAP_BIT 1UL
+#define EBITMAP_SHIFT_UNIT_SIZE(x) \
(((x) >> EBITMAP_UNIT_SIZE / 2) >> EBITMAP_UNIT_SIZE / 2)
struct ebitmap_node {
@@ -38,16 +40,16 @@ struct ebitmap_node {
};
struct ebitmap {
- struct ebitmap_node *node; /* first node in the bitmap */
- u32 highbit; /* highest position in the total bitmap */
+ struct ebitmap_node *node; /* first node in the bitmap */
+ u32 highbit; /* highest position in the total bitmap */
};
#define ebitmap_length(e) ((e)->highbit)
-static inline unsigned int ebitmap_start_positive(const struct ebitmap *e,
- struct ebitmap_node **n)
+static inline u32 ebitmap_start_positive(const struct ebitmap *e,
+ struct ebitmap_node **n)
{
- unsigned int ofs;
+ u32 ofs;
for (*n = e->node; *n; *n = (*n)->next) {
ofs = find_first_bit((*n)->maps, EBITMAP_SIZE);
@@ -62,11 +64,10 @@ static inline void ebitmap_init(struct ebitmap *e)
memset(e, 0, sizeof(*e));
}
-static inline unsigned int ebitmap_next_positive(const struct ebitmap *e,
- struct ebitmap_node **n,
- unsigned int bit)
+static inline u32 ebitmap_next_positive(const struct ebitmap *e,
+ struct ebitmap_node **n, u32 bit)
{
- unsigned int ofs;
+ u32 ofs;
ofs = find_next_bit((*n)->maps, EBITMAP_SIZE, bit - (*n)->startbit + 1);
if (ofs < EBITMAP_SIZE)
@@ -80,16 +81,15 @@ static inline unsigned int ebitmap_next_positive(const struct ebitmap *e,
return ebitmap_length(e);
}
-#define EBITMAP_NODE_INDEX(node, bit) \
+#define EBITMAP_NODE_INDEX(node, bit) \
(((bit) - (node)->startbit) / EBITMAP_UNIT_SIZE)
-#define EBITMAP_NODE_OFFSET(node, bit) \
+#define EBITMAP_NODE_OFFSET(node, bit) \
(((bit) - (node)->startbit) % EBITMAP_UNIT_SIZE)
-static inline int ebitmap_node_get_bit(const struct ebitmap_node *n,
- unsigned int bit)
+static inline int ebitmap_node_get_bit(const struct ebitmap_node *n, u32 bit)
{
- unsigned int index = EBITMAP_NODE_INDEX(n, bit);
- unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit);
+ u32 index = EBITMAP_NODE_INDEX(n, bit);
+ u32 ofs = EBITMAP_NODE_OFFSET(n, bit);
BUG_ON(index >= EBITMAP_UNIT_NUMS);
if ((n->maps[index] & (EBITMAP_BIT << ofs)))
@@ -97,40 +97,41 @@ static inline int ebitmap_node_get_bit(const struct ebitmap_node *n,
return 0;
}
-static inline void ebitmap_node_set_bit(struct ebitmap_node *n,
- unsigned int bit)
+static inline void ebitmap_node_set_bit(struct ebitmap_node *n, u32 bit)
{
- unsigned int index = EBITMAP_NODE_INDEX(n, bit);
- unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit);
+ u32 index = EBITMAP_NODE_INDEX(n, bit);
+ u32 ofs = EBITMAP_NODE_OFFSET(n, bit);
BUG_ON(index >= EBITMAP_UNIT_NUMS);
n->maps[index] |= (EBITMAP_BIT << ofs);
}
-static inline void ebitmap_node_clr_bit(struct ebitmap_node *n,
- unsigned int bit)
+static inline void ebitmap_node_clr_bit(struct ebitmap_node *n, u32 bit)
{
- unsigned int index = EBITMAP_NODE_INDEX(n, bit);
- unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit);
+ u32 index = EBITMAP_NODE_INDEX(n, bit);
+ u32 ofs = EBITMAP_NODE_OFFSET(n, bit);
BUG_ON(index >= EBITMAP_UNIT_NUMS);
n->maps[index] &= ~(EBITMAP_BIT << ofs);
}
-#define ebitmap_for_each_positive_bit(e, n, bit) \
- for ((bit) = ebitmap_start_positive(e, &(n)); \
- (bit) < ebitmap_length(e); \
- (bit) = ebitmap_next_positive(e, &(n), bit)) \
+#define ebitmap_for_each_positive_bit(e, n, bit) \
+ for ((bit) = ebitmap_start_positive(e, &(n)); \
+ (bit) < ebitmap_length(e); \
+ (bit) = ebitmap_next_positive(e, &(n), bit))
-int ebitmap_cmp(const struct ebitmap *e1, const struct ebitmap *e2);
+bool ebitmap_equal(const struct ebitmap *e1, const struct ebitmap *e2);
int ebitmap_cpy(struct ebitmap *dst, const struct ebitmap *src);
-int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1, const struct ebitmap *e2);
-int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2, u32 last_e2bit);
-int ebitmap_get_bit(const struct ebitmap *e, unsigned long bit);
-int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);
+int ebitmap_and(struct ebitmap *dst, const struct ebitmap *e1,
+ const struct ebitmap *e2);
+int ebitmap_contains(const struct ebitmap *e1, const struct ebitmap *e2,
+ u32 last_e2bit);
+int ebitmap_get_bit(const struct ebitmap *e, u32 bit);
+int ebitmap_set_bit(struct ebitmap *e, u32 bit, int value);
void ebitmap_destroy(struct ebitmap *e);
-int ebitmap_read(struct ebitmap *e, void *fp);
-int ebitmap_write(const struct ebitmap *e, void *fp);
+struct policy_file;
+int ebitmap_read(struct ebitmap *e, struct policy_file *fp);
+int ebitmap_write(const struct ebitmap *e, struct policy_file *fp);
u32 ebitmap_hash(const struct ebitmap *e, u32 hash);
#ifdef CONFIG_NETLABEL
@@ -151,4 +152,4 @@ static inline int ebitmap_netlbl_import(struct ebitmap *ebmap,
}
#endif
-#endif /* _SS_EBITMAP_H_ */
+#endif /* _SS_EBITMAP_H_ */
diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c
index c05d8346a94a..383fd2d70878 100644
--- a/security/selinux/ss/hashtab.c
+++ b/security/selinux/ss/hashtab.c
@@ -4,6 +4,7 @@
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
+
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/errno.h>
@@ -47,8 +48,8 @@ int hashtab_init(struct hashtab *h, u32 nel_hint)
return 0;
}
-int __hashtab_insert(struct hashtab *h, struct hashtab_node **dst,
- void *key, void *datum)
+int __hashtab_insert(struct hashtab *h, struct hashtab_node **dst, void *key,
+ void *datum)
{
struct hashtab_node *newnode;
@@ -83,8 +84,7 @@ void hashtab_destroy(struct hashtab *h)
h->htable = NULL;
}
-int hashtab_map(struct hashtab *h,
- int (*apply)(void *k, void *d, void *args),
+int hashtab_map(struct hashtab *h, int (*apply)(void *k, void *d, void *args),
void *args)
{
u32 i;
@@ -136,12 +136,12 @@ void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
}
#endif /* CONFIG_SECURITY_SELINUX_DEBUG */
-int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
- int (*copy)(struct hashtab_node *new,
- struct hashtab_node *orig, void *args),
- int (*destroy)(void *k, void *d, void *args),
- void *args)
+int hashtab_duplicate(struct hashtab *new, const struct hashtab *orig,
+ int (*copy)(struct hashtab_node *new,
+ const struct hashtab_node *orig, void *args),
+ int (*destroy)(void *k, void *d, void *args), void *args)
{
+ const struct hashtab_node *orig_cur;
struct hashtab_node *cur, *tmp, *tail;
u32 i;
int rc;
@@ -156,12 +156,13 @@ int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
for (i = 0; i < orig->size; i++) {
tail = NULL;
- for (cur = orig->htable[i]; cur; cur = cur->next) {
+ for (orig_cur = orig->htable[i]; orig_cur;
+ orig_cur = orig_cur->next) {
tmp = kmem_cache_zalloc(hashtab_node_cachep,
GFP_KERNEL);
if (!tmp)
goto error;
- rc = copy(tmp, cur, args);
+ rc = copy(tmp, orig_cur, args);
if (rc) {
kmem_cache_free(hashtab_node_cachep, tmp);
goto error;
@@ -178,7 +179,7 @@ int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
return 0;
- error:
+error:
for (i = 0; i < new->size; i++) {
for (cur = new->htable[i]; cur; cur = tmp) {
tmp = cur->next;
@@ -193,7 +194,5 @@ int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
void __init hashtab_cache_init(void)
{
- hashtab_node_cachep = kmem_cache_create("hashtab_node",
- sizeof(struct hashtab_node),
- 0, SLAB_PANIC, NULL);
+ hashtab_node_cachep = KMEM_CACHE(hashtab_node, SLAB_PANIC);
}
diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h
index 09b0a3744937..deba82d78c3a 100644
--- a/security/selinux/ss/hashtab.h
+++ b/security/selinux/ss/hashtab.h
@@ -8,6 +8,7 @@
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
+
#ifndef _SS_HASHTAB_H_
#define _SS_HASHTAB_H_
@@ -15,12 +16,11 @@
#include <linux/errno.h>
#include <linux/sched.h>
-#define HASHTAB_MAX_NODES U32_MAX
+#define HASHTAB_MAX_NODES U32_MAX
struct hashtab_key_params {
- u32 (*hash)(const void *key); /* hash function */
- int (*cmp)(const void *key1, const void *key2);
- /* key comparison function */
+ u32 (*hash)(const void *key); /* hash func */
+ int (*cmp)(const void *key1, const void *key2); /* comparison func */
};
struct hashtab_node {
@@ -30,9 +30,9 @@ struct hashtab_node {
};
struct hashtab {
- struct hashtab_node **htable; /* hash table */
- u32 size; /* number of slots in hash table */
- u32 nel; /* number of elements in hash table */
+ struct hashtab_node **htable; /* hash table */
+ u32 size; /* number of slots in hash table */
+ u32 nel; /* number of elements in hash table */
};
struct hashtab_info {
@@ -48,8 +48,8 @@ struct hashtab_info {
*/
int hashtab_init(struct hashtab *h, u32 nel_hint);
-int __hashtab_insert(struct hashtab *h, struct hashtab_node **dst,
- void *key, void *datum);
+int __hashtab_insert(struct hashtab *h, struct hashtab_node **dst, void *key,
+ void *datum);
/*
* Inserts the specified (key, datum) pair into the specified hash table.
@@ -84,8 +84,8 @@ static inline int hashtab_insert(struct hashtab *h, void *key, void *datum,
cur = cur->next;
}
- return __hashtab_insert(h, prev ? &prev->next : &h->htable[hvalue],
- key, datum);
+ return __hashtab_insert(h, prev ? &prev->next : &h->htable[hvalue], key,
+ datum);
}
/*
@@ -133,15 +133,13 @@ void hashtab_destroy(struct hashtab *h);
* iterating through the hash table and will propagate the error
* return to its caller.
*/
-int hashtab_map(struct hashtab *h,
- int (*apply)(void *k, void *d, void *args),
+int hashtab_map(struct hashtab *h, int (*apply)(void *k, void *d, void *args),
void *args);
-int hashtab_duplicate(struct hashtab *new, struct hashtab *orig,
- int (*copy)(struct hashtab_node *new,
- struct hashtab_node *orig, void *args),
- int (*destroy)(void *k, void *d, void *args),
- void *args);
+int hashtab_duplicate(struct hashtab *new, const struct hashtab *orig,
+ int (*copy)(struct hashtab_node *new,
+ const struct hashtab_node *orig, void *args),
+ int (*destroy)(void *k, void *d, void *args), void *args);
#ifdef CONFIG_SECURITY_SELINUX_DEBUG
/* Fill info with some hash table statistics */
@@ -149,7 +147,8 @@ void hashtab_stat(struct hashtab *h, struct hashtab_info *info);
#else
static inline void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
{
+ return;
}
#endif
-#endif /* _SS_HASHTAB_H */
+#endif /* _SS_HASHTAB_H */
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index cd38f5913b63..a6e49269f535 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -4,19 +4,15 @@
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
+
/*
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ * Support for enhanced MLS infrastructure.
+ * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
*
- * Support for enhanced MLS infrastructure.
- *
- * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
- */
-/*
* Updated: Hewlett-Packard <paul@paul-moore.com>
- *
- * Added support to import/export the MLS label from NetLabel
- *
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * Added support to import/export the MLS label from NetLabel
+ * Copyright (C) Hewlett-Packard Development Company, L.P., 2006
*/
#include <linux/kernel.h>
@@ -52,7 +48,8 @@ int mls_compute_context_len(struct policydb *p, struct context *context)
head = -2;
prev = -2;
e = &context->range.level[l].cat;
- ebitmap_for_each_positive_bit(e, node, i) {
+ ebitmap_for_each_positive_bit(e, node, i)
+ {
if (i - prev > 1) {
/* one or more negative bits are skipped */
if (head != prev) {
@@ -86,8 +83,7 @@ int mls_compute_context_len(struct policydb *p, struct context *context)
* the MLS fields of `context' into the string `*scontext'.
* Update `*scontext' to point to the end of the MLS fields.
*/
-void mls_sid_to_context(struct policydb *p,
- struct context *context,
+void mls_sid_to_context(struct policydb *p, struct context *context,
char **scontext)
{
char *scontextp, *nm;
@@ -112,7 +108,8 @@ void mls_sid_to_context(struct policydb *p,
head = -2;
prev = -2;
e = &context->range.level[l].cat;
- ebitmap_for_each_positive_bit(e, node, i) {
+ ebitmap_for_each_positive_bit(e, node, i)
+ {
if (i - prev > 1) {
/* one or more negative bits are skipped */
if (prev != head) {
@@ -174,7 +171,7 @@ int mls_level_isvalid(struct policydb *p, struct mls_level *l)
* levdatum->level->cat and no bit in l->cat is larger than
* p->p_cats.nprim.
*/
- return ebitmap_contains(&levdatum->level->cat, &l->cat,
+ return ebitmap_contains(&levdatum->level.cat, &l->cat,
p->p_cats.nprim);
}
@@ -230,12 +227,8 @@ int mls_context_isvalid(struct policydb *p, struct context *c)
* Policy read-lock must be held for sidtab lookup.
*
*/
-int mls_context_to_sid(struct policydb *pol,
- char oldc,
- char *scontext,
- struct context *context,
- struct sidtab *s,
- u32 def_sid)
+int mls_context_to_sid(struct policydb *pol, char oldc, char *scontext,
+ struct context *context, struct sidtab *s, u32 def_sid)
{
char *sensitivity, *cur_cat, *next_cat, *rngptr;
struct level_datum *levdatum;
@@ -296,7 +289,7 @@ int mls_context_to_sid(struct policydb *pol,
levdatum = symtab_search(&pol->p_levels, sensitivity);
if (!levdatum)
return -EINVAL;
- context->range.level[l].sens = levdatum->level->sens;
+ context->range.level[l].sens = levdatum->level.sens;
/* Extract category set. */
while (next_cat != NULL) {
@@ -333,7 +326,8 @@ int mls_context_to_sid(struct policydb *pol,
return -EINVAL;
for (i = catdatum->value; i < rngdatum->value; i++) {
- rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
+ rc = ebitmap_set_bit(
+ &context->range.level[l].cat, i, 1);
if (rc)
return rc;
}
@@ -371,8 +365,8 @@ int mls_from_string(struct policydb *p, char *str, struct context *context,
if (!tmpstr) {
rc = -ENOMEM;
} else {
- rc = mls_context_to_sid(p, ':', tmpstr, context,
- NULL, SECSID_NULL);
+ rc = mls_context_to_sid(p, ':', tmpstr, context, NULL,
+ SECSID_NULL);
kfree(tmpstr);
}
@@ -382,8 +376,7 @@ int mls_from_string(struct policydb *p, char *str, struct context *context,
/*
* Copies the MLS range `range' into `context'.
*/
-int mls_range_set(struct context *context,
- struct mls_range *range)
+int mls_range_set(struct context *context, struct mls_range *range)
{
int l, rc = 0;
@@ -399,9 +392,8 @@ int mls_range_set(struct context *context,
return rc;
}
-int mls_setup_user_range(struct policydb *p,
- struct context *fromcon, struct user_datum *user,
- struct context *usercon)
+int mls_setup_user_range(struct policydb *p, struct context *fromcon,
+ struct user_datum *user, struct context *usercon)
{
if (p->mls_enabled) {
struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
@@ -444,10 +436,8 @@ int mls_setup_user_range(struct policydb *p,
* policy `oldp' to the values specified in the policy `newp',
* storing the resulting context in `newc'.
*/
-int mls_convert_context(struct policydb *oldp,
- struct policydb *newp,
- struct context *oldc,
- struct context *newc)
+int mls_convert_context(struct policydb *oldp, struct policydb *newp,
+ struct context *oldc, struct context *newc)
{
struct level_datum *levdatum;
struct cat_datum *catdatum;
@@ -466,10 +456,11 @@ int mls_convert_context(struct policydb *oldp,
if (!levdatum)
return -EINVAL;
- newc->range.level[l].sens = levdatum->level->sens;
+ newc->range.level[l].sens = levdatum->level.sens;
- ebitmap_for_each_positive_bit(&oldc->range.level[l].cat,
- node, i) {
+ ebitmap_for_each_positive_bit(&oldc->range.level[l].cat, node,
+ i)
+ {
int rc;
catdatum = symtab_search(&newp->p_cats,
@@ -486,13 +477,9 @@ int mls_convert_context(struct policydb *oldp,
return 0;
}
-int mls_compute_sid(struct policydb *p,
- struct context *scontext,
- struct context *tcontext,
- u16 tclass,
- u32 specified,
- struct context *newcontext,
- bool sock)
+int mls_compute_sid(struct policydb *p, struct context *scontext,
+ struct context *tcontext, u16 tclass, u32 specified,
+ struct context *newcontext, bool sock)
{
struct range_trans rtr;
struct mls_range *r;
@@ -532,8 +519,8 @@ int mls_compute_sid(struct policydb *p,
case DEFAULT_TARGET_LOW_HIGH:
return mls_context_cpy(newcontext, tcontext);
case DEFAULT_GLBLUB:
- return mls_context_glblub(newcontext,
- scontext, tcontext);
+ return mls_context_glblub(newcontext, scontext,
+ tcontext);
}
fallthrough;
@@ -563,8 +550,7 @@ int mls_compute_sid(struct policydb *p,
* NetLabel MLS sensitivity level field.
*
*/
-void mls_export_netlbl_lvl(struct policydb *p,
- struct context *context,
+void mls_export_netlbl_lvl(struct policydb *p, struct context *context,
struct netlbl_lsm_secattr *secattr)
{
if (!p->mls_enabled)
@@ -585,8 +571,7 @@ void mls_export_netlbl_lvl(struct policydb *p,
* NetLabel MLS sensitivity level into the context.
*
*/
-void mls_import_netlbl_lvl(struct policydb *p,
- struct context *context,
+void mls_import_netlbl_lvl(struct policydb *p, struct context *context,
struct netlbl_lsm_secattr *secattr)
{
if (!p->mls_enabled)
@@ -607,8 +592,7 @@ void mls_import_netlbl_lvl(struct policydb *p,
* MLS category field. Returns zero on success, negative values on failure.
*
*/
-int mls_export_netlbl_cat(struct policydb *p,
- struct context *context,
+int mls_export_netlbl_cat(struct policydb *p, struct context *context,
struct netlbl_lsm_secattr *secattr)
{
int rc;
@@ -637,8 +621,7 @@ int mls_export_netlbl_cat(struct policydb *p,
* negative values on failure.
*
*/
-int mls_import_netlbl_cat(struct policydb *p,
- struct context *context,
+int mls_import_netlbl_cat(struct policydb *p, struct context *context,
struct netlbl_lsm_secattr *secattr)
{
int rc;
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 107681dd1824..07980636751f 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -4,19 +4,15 @@
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
+
/*
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ * Support for enhanced MLS infrastructure.
+ * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
*
- * Support for enhanced MLS infrastructure.
- *
- * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
- */
-/*
* Updated: Hewlett-Packard <paul@paul-moore.com>
- *
- * Added support to import/export the MLS label from NetLabel
- *
- * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ * Added support to import/export the MLS label from NetLabel
+ * Copyright (X) Hewlett-Packard Development Company, L.P., 2006
*/
#ifndef _SS_MLS_H_
@@ -35,47 +31,32 @@ int mls_context_isvalid(struct policydb *p, struct context *c);
int mls_range_isvalid(struct policydb *p, struct mls_range *r);
int mls_level_isvalid(struct policydb *p, struct mls_level *l);
-int mls_context_to_sid(struct policydb *p,
- char oldc,
- char *scontext,
- struct context *context,
- struct sidtab *s,
- u32 def_sid);
+int mls_context_to_sid(struct policydb *p, char oldc, char *scontext,
+ struct context *context, struct sidtab *s, u32 def_sid);
int mls_from_string(struct policydb *p, char *str, struct context *context,
gfp_t gfp_mask);
int mls_range_set(struct context *context, struct mls_range *range);
-int mls_convert_context(struct policydb *oldp,
- struct policydb *newp,
- struct context *oldc,
- struct context *newc);
+int mls_convert_context(struct policydb *oldp, struct policydb *newp,
+ struct context *oldc, struct context *newc);
-int mls_compute_sid(struct policydb *p,
- struct context *scontext,
- struct context *tcontext,
- u16 tclass,
- u32 specified,
- struct context *newcontext,
- bool sock);
+int mls_compute_sid(struct policydb *p, struct context *scontext,
+ struct context *tcontext, u16 tclass, u32 specified,
+ struct context *newcontext, bool sock);
-int mls_setup_user_range(struct policydb *p,
- struct context *fromcon, struct user_datum *user,
- struct context *usercon);
+int mls_setup_user_range(struct policydb *p, struct context *fromcon,
+ struct user_datum *user, struct context *usercon);
#ifdef CONFIG_NETLABEL
-void mls_export_netlbl_lvl(struct policydb *p,
- struct context *context,
+void mls_export_netlbl_lvl(struct policydb *p, struct context *context,
struct netlbl_lsm_secattr *secattr);
-void mls_import_netlbl_lvl(struct policydb *p,
- struct context *context,
+void mls_import_netlbl_lvl(struct policydb *p, struct context *context,
struct netlbl_lsm_secattr *secattr);
-int mls_export_netlbl_cat(struct policydb *p,
- struct context *context,
+int mls_export_netlbl_cat(struct policydb *p, struct context *context,
struct netlbl_lsm_secattr *secattr);
-int mls_import_netlbl_cat(struct policydb *p,
- struct context *context,
+int mls_import_netlbl_cat(struct policydb *p, struct context *context,
struct netlbl_lsm_secattr *secattr);
#else
static inline void mls_export_netlbl_lvl(struct policydb *p,
@@ -112,5 +93,4 @@ static inline u32 mls_range_hash(const struct mls_range *r, u32 hash)
return hash;
}
-#endif /* _SS_MLS_H */
-
+#endif /* _SS_MLS_H */
diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h
index f492cf148891..51df2ebd1211 100644
--- a/security/selinux/ss/mls_types.h
+++ b/security/selinux/ss/mls_types.h
@@ -4,12 +4,11 @@
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
+
/*
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
- *
- * Support for enhanced MLS infrastructure.
- *
- * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ * Support for enhanced MLS infrastructure.
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
*/
#ifndef _SS_MLS_TYPES_H_
@@ -19,34 +18,35 @@
#include "ebitmap.h"
struct mls_level {
- u32 sens; /* sensitivity */
- struct ebitmap cat; /* category set */
+ u32 sens; /* sensitivity */
+ struct ebitmap cat; /* category set */
};
struct mls_range {
struct mls_level level[2]; /* low == level[0], high == level[1] */
};
-static inline int mls_level_eq(const struct mls_level *l1, const struct mls_level *l2)
+static inline int mls_level_eq(const struct mls_level *l1,
+ const struct mls_level *l2)
{
- return ((l1->sens == l2->sens) &&
- ebitmap_cmp(&l1->cat, &l2->cat));
+ return ((l1->sens == l2->sens) && ebitmap_equal(&l1->cat, &l2->cat));
}
-static inline int mls_level_dom(const struct mls_level *l1, const struct mls_level *l2)
+static inline int mls_level_dom(const struct mls_level *l1,
+ const struct mls_level *l2)
{
return ((l1->sens >= l2->sens) &&
ebitmap_contains(&l1->cat, &l2->cat, 0));
}
#define mls_level_incomp(l1, l2) \
-(!mls_level_dom((l1), (l2)) && !mls_level_dom((l2), (l1)))
+ (!mls_level_dom((l1), (l2)) && !mls_level_dom((l2), (l1)))
#define mls_level_between(l1, l2, l3) \
-(mls_level_dom((l1), (l2)) && mls_level_dom((l3), (l1)))
+ (mls_level_dom((l1), (l2)) && mls_level_dom((l3), (l1)))
-#define mls_range_contains(r1, r2) \
-(mls_level_dom(&(r2).level[0], &(r1).level[0]) && \
- mls_level_dom(&(r1).level[1], &(r2).level[1]))
+#define mls_range_contains(r1, r2) \
+ (mls_level_dom(&(r2).level[0], &(r1).level[0]) && \
+ mls_level_dom(&(r1).level[1], &(r2).level[1]))
-#endif /* _SS_MLS_TYPES_H_ */
+#endif /* _SS_MLS_TYPES_H_ */
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 3b19ad28c922..9ea971943713 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -7,25 +7,21 @@
/*
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ * Support for enhanced MLS infrastructure.
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
*
- * Support for enhanced MLS infrastructure.
- *
- * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
- *
- * Added conditional policy language extensions
+ * Updated: Frank Mayer <mayerf@tresys.com> and
+ * Karl MacMillan <kmacmillan@tresys.com>
+ * Added conditional policy language extensions
+ * Copyright (C) 2003-2004 Tresys Technology, LLC
*
* Updated: Hewlett-Packard <paul@paul-moore.com>
- *
- * Added support for the policy capability bitmap
+ * Added support for the policy capability bitmap
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
*
* Update: Mellanox Techonologies
- *
- * Added Infiniband support
- *
- * Copyright (C) 2016 Mellanox Techonologies
- * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
- * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
- * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * Added Infiniband support
+ * Copyright (C) 2016 Mellanox Techonologies
*/
#include <linux/kernel.h>
@@ -42,6 +38,7 @@
#include "services.h"
#ifdef CONFIG_SECURITY_SELINUX_DEBUG
+/* clang-format off */
static const char *const symtab_name[SYM_NUM] = {
"common prefixes",
"classes",
@@ -52,6 +49,7 @@ static const char *const symtab_name[SYM_NUM] = {
"levels",
"categories",
};
+/* clang-format off */
#endif
struct policydb_compat_info {
@@ -63,103 +61,109 @@ struct policydb_compat_info {
/* These need to be updated if SYM_NUM or OCON_NUM changes */
static const struct policydb_compat_info policydb_compat[] = {
{
- .version = POLICYDB_VERSION_BASE,
- .sym_num = SYM_NUM - 3,
- .ocon_num = OCON_NUM - 3,
+ .version = POLICYDB_VERSION_BASE,
+ .sym_num = SYM_NUM - 3,
+ .ocon_num = OCON_NUM - 3,
},
{
- .version = POLICYDB_VERSION_BOOL,
- .sym_num = SYM_NUM - 2,
- .ocon_num = OCON_NUM - 3,
+ .version = POLICYDB_VERSION_BOOL,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM - 3,
},
{
- .version = POLICYDB_VERSION_IPV6,
- .sym_num = SYM_NUM - 2,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_IPV6,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_NLCLASS,
- .sym_num = SYM_NUM - 2,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_NLCLASS,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_MLS,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_MLS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_AVTAB,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_AVTAB,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_RANGETRANS,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_RANGETRANS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_POLCAP,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_POLCAP,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_PERMISSIVE,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_PERMISSIVE,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_BOUNDARY,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_BOUNDARY,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_FILENAME_TRANS,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_FILENAME_TRANS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_ROLETRANS,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_ROLETRANS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_DEFAULT_TYPE,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_DEFAULT_TYPE,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_CONSTRAINT_NAMES,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_CONSTRAINT_NAMES,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_XPERMS_IOCTL,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM - 2,
+ .version = POLICYDB_VERSION_XPERMS_IOCTL,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM - 2,
},
{
- .version = POLICYDB_VERSION_INFINIBAND,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_INFINIBAND,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
},
{
- .version = POLICYDB_VERSION_GLBLUB,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_GLBLUB,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
},
{
- .version = POLICYDB_VERSION_COMP_FTRANS,
- .sym_num = SYM_NUM,
- .ocon_num = OCON_NUM,
+ .version = POLICYDB_VERSION_COMP_FTRANS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_COND_XPERMS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
},
};
-static const struct policydb_compat_info *policydb_lookup_compat(unsigned int version)
+static const struct policydb_compat_info *
+policydb_lookup_compat(unsigned int version)
{
unsigned int i;
@@ -297,9 +301,7 @@ static int sens_destroy(void *key, void *datum, void *p)
kfree(key);
if (datum) {
levdatum = datum;
- if (levdatum->level)
- ebitmap_destroy(&levdatum->level->cat);
- kfree(levdatum->level);
+ ebitmap_destroy(&levdatum->level.cat);
}
kfree(datum);
return 0;
@@ -312,7 +314,8 @@ static int cat_destroy(void *key, void *datum, void *p)
return 0;
}
-static int (*const destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = {
+/* clang-format off */
+static int (*const destroy_f[SYM_NUM])(void *key, void *datum, void *datap) = {
common_destroy,
cls_destroy,
role_destroy,
@@ -322,6 +325,7 @@ static int (*const destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = {
sens_destroy,
cat_destroy,
};
+/* clang-format on */
static int filenametr_destroy(void *key, void *datum, void *p)
{
@@ -366,8 +370,8 @@ static void ocontext_destroy(struct ocontext *c, unsigned int i)
context_destroy(&c->context[0]);
context_destroy(&c->context[1]);
- if (i == OCON_ISID || i == OCON_FS ||
- i == OCON_NETIF || i == OCON_FSUSE)
+ if (i == OCON_ISID || i == OCON_FS || i == OCON_NETIF ||
+ i == OCON_FSUSE)
kfree(c->u.name);
kfree(c);
}
@@ -429,7 +433,6 @@ static int filenametr_cmp(const void *k1, const void *k2)
return v;
return strcmp(ft1->name, ft2->name);
-
}
static const struct hashtab_key_params filenametr_key_params = {
@@ -437,8 +440,8 @@ static const struct hashtab_key_params filenametr_key_params = {
.cmp = filenametr_cmp,
};
-struct filename_trans_datum *policydb_filenametr_search(
- struct policydb *p, struct filename_trans_key *key)
+struct filename_trans_datum *
+policydb_filenametr_search(struct policydb *p, struct filename_trans_key *key)
{
return hashtab_search(&p->filename_trans, key, filenametr_key_params);
}
@@ -448,7 +451,7 @@ static u32 rangetr_hash(const void *k)
const struct range_trans *key = k;
return key->source_type + (key->target_type << 3) +
- (key->target_class << 5);
+ (key->target_class << 5);
}
static int rangetr_cmp(const void *k1, const void *k2)
@@ -484,7 +487,8 @@ static u32 role_trans_hash(const void *k)
{
const struct role_trans_key *key = k;
- return jhash_3words(key->role, key->type, (u32)key->tclass << 16 | key->tclass, 0);
+ return jhash_3words(key->role, key->type,
+ (u32)key->tclass << 16 | key->tclass, 0);
}
static int role_trans_cmp(const void *k1, const void *k2)
@@ -576,9 +580,8 @@ static int role_index(void *key, void *datum, void *datap)
role = datum;
p = datap;
- if (!role->value
- || role->value > p->p_roles.nprim
- || role->bounds > p->p_roles.nprim)
+ if (!role->value || role->value > p->p_roles.nprim ||
+ role->bounds > p->p_roles.nprim)
return -EINVAL;
p->sym_val_to_name[SYM_ROLES][role->value - 1] = key;
@@ -595,9 +598,8 @@ static int type_index(void *key, void *datum, void *datap)
p = datap;
if (typdatum->primary) {
- if (!typdatum->value
- || typdatum->value > p->p_types.nprim
- || typdatum->bounds > p->p_types.nprim)
+ if (!typdatum->value || typdatum->value > p->p_types.nprim ||
+ typdatum->bounds > p->p_types.nprim)
return -EINVAL;
p->sym_val_to_name[SYM_TYPES][typdatum->value - 1] = key;
p->type_val_to_struct[typdatum->value - 1] = typdatum;
@@ -613,9 +615,8 @@ static int user_index(void *key, void *datum, void *datap)
usrdatum = datum;
p = datap;
- if (!usrdatum->value
- || usrdatum->value > p->p_users.nprim
- || usrdatum->bounds > p->p_users.nprim)
+ if (!usrdatum->value || usrdatum->value > p->p_users.nprim ||
+ usrdatum->bounds > p->p_users.nprim)
return -EINVAL;
p->sym_val_to_name[SYM_USERS][usrdatum->value - 1] = key;
@@ -632,11 +633,11 @@ static int sens_index(void *key, void *datum, void *datap)
p = datap;
if (!levdatum->isalias) {
- if (!levdatum->level->sens ||
- levdatum->level->sens > p->p_levels.nprim)
+ if (!levdatum->level.sens ||
+ levdatum->level.sens > p->p_levels.nprim)
return -EINVAL;
- p->sym_val_to_name[SYM_LEVELS][levdatum->level->sens - 1] = key;
+ p->sym_val_to_name[SYM_LEVELS][levdatum->level.sens - 1] = key;
}
return 0;
@@ -660,7 +661,8 @@ static int cat_index(void *key, void *datum, void *datap)
return 0;
}
-static int (*const index_f[SYM_NUM]) (void *key, void *datum, void *datap) = {
+/* clang-format off */
+static int (*const index_f[SYM_NUM])(void *key, void *datum, void *datap) = {
common_index,
class_index,
role_index,
@@ -670,16 +672,20 @@ static int (*const index_f[SYM_NUM]) (void *key, void *datum, void *datap) = {
sens_index,
cat_index,
};
+/* clang-format on */
#ifdef CONFIG_SECURITY_SELINUX_DEBUG
-static void hash_eval(struct hashtab *h, const char *hash_name)
+static void hash_eval(struct hashtab *h, const char *hash_name,
+ const char *hash_details)
{
struct hashtab_info info;
hashtab_stat(h, &info);
- pr_debug("SELinux: %s: %d entries and %d/%d buckets used, longest chain length %d, sum of chain length^2 %llu\n",
- hash_name, h->nel, info.slots_used, h->size,
- info.max_chain_len, info.chain2_len_sum);
+ pr_debug(
+ "SELinux: %s%s%s: %d entries and %d/%d buckets used, longest chain length %d, sum of chain length^2 %llu\n",
+ hash_name, hash_details ? "@" : "", hash_details ?: "", h->nel,
+ info.slots_used, h->size, info.max_chain_len,
+ info.chain2_len_sum);
}
static void symtab_hash_eval(struct symtab *s)
@@ -687,11 +693,12 @@ static void symtab_hash_eval(struct symtab *s)
int i;
for (i = 0; i < SYM_NUM; i++)
- hash_eval(&s[i].table, symtab_name[i]);
+ hash_eval(&s[i].table, symtab_name[i], NULL);
}
#else
-static inline void hash_eval(struct hashtab *h, const char *hash_name)
+static inline void hash_eval(struct hashtab *h, const char *hash_name,
+ const char *hash_details)
{
}
static inline void symtab_hash_eval(struct symtab *s)
@@ -710,16 +717,17 @@ static int policydb_index(struct policydb *p)
int i, rc;
if (p->mls_enabled)
- pr_debug("SELinux: %d users, %d roles, %d types, %d bools, %d sens, %d cats\n",
- p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim,
- p->p_bools.nprim, p->p_levels.nprim, p->p_cats.nprim);
+ pr_debug(
+ "SELinux: %d users, %d roles, %d types, %d bools, %d sens, %d cats\n",
+ p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim,
+ p->p_bools.nprim, p->p_levels.nprim, p->p_cats.nprim);
else
pr_debug("SELinux: %d users, %d roles, %d types, %d bools\n",
p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim,
p->p_bools.nprim);
- pr_debug("SELinux: %d classes, %d rules\n",
- p->p_classes.nprim, p->te_avtab.nel);
+ pr_debug("SELinux: %d classes, %d rules\n", p->p_classes.nprim,
+ p->te_avtab.nel);
avtab_hash_eval(&p->te_avtab, "rules");
symtab_hash_eval(p->symtab);
@@ -730,21 +738,18 @@ static int policydb_index(struct policydb *p)
if (!p->class_val_to_struct)
return -ENOMEM;
- p->role_val_to_struct = kcalloc(p->p_roles.nprim,
- sizeof(*p->role_val_to_struct),
- GFP_KERNEL);
+ p->role_val_to_struct = kcalloc(
+ p->p_roles.nprim, sizeof(*p->role_val_to_struct), GFP_KERNEL);
if (!p->role_val_to_struct)
return -ENOMEM;
- p->user_val_to_struct = kcalloc(p->p_users.nprim,
- sizeof(*p->user_val_to_struct),
- GFP_KERNEL);
+ p->user_val_to_struct = kcalloc(
+ p->p_users.nprim, sizeof(*p->user_val_to_struct), GFP_KERNEL);
if (!p->user_val_to_struct)
return -ENOMEM;
- p->type_val_to_struct = kvcalloc(p->p_types.nprim,
- sizeof(*p->type_val_to_struct),
- GFP_KERNEL);
+ p->type_val_to_struct = kvcalloc(
+ p->p_types.nprim, sizeof(*p->type_val_to_struct), GFP_KERNEL);
if (!p->type_val_to_struct)
return -ENOMEM;
@@ -754,8 +759,7 @@ static int policydb_index(struct policydb *p)
for (i = 0; i < SYM_NUM; i++) {
p->sym_val_to_name[i] = kvcalloc(p->symtab[i].nprim,
- sizeof(char *),
- GFP_KERNEL);
+ sizeof(char *), GFP_KERNEL);
if (!p->sym_val_to_name[i])
return -ENOMEM;
@@ -857,8 +861,7 @@ void policydb_destroy(struct policydb *p)
int policydb_load_isids(struct policydb *p, struct sidtab *s)
{
struct ocontext *head, *c;
- bool isid_init_supported = ebitmap_get_bit(&p->policycaps,
- POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT);
+ bool isid_init;
int rc;
rc = sidtab_init(s);
@@ -867,6 +870,9 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
return rc;
}
+ isid_init = ebitmap_get_bit(&p->policycaps,
+ POLICYDB_CAP_USERSPACE_INITIAL_CONTEXT);
+
head = p->ocontexts[OCON_ISID];
for (c = head; c; c = c->next) {
u32 sid = c->sid[0];
@@ -886,7 +892,7 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
* Also ignore SECINITSID_INIT if the policy doesn't declare
* support for it
*/
- if (sid == SECINITSID_INIT && !isid_init_supported)
+ if (sid == SECINITSID_INIT && !isid_init)
continue;
rc = sidtab_set_initial(s, sid, &c->context[0]);
@@ -905,8 +911,9 @@ int policydb_load_isids(struct policydb *p, struct sidtab *s)
* started before policy load would initially get the context
* corresponding to SECINITSID_KERNEL.
*/
- if (sid == SECINITSID_KERNEL && !isid_init_supported) {
- rc = sidtab_set_initial(s, SECINITSID_INIT, &c->context[0]);
+ if (sid == SECINITSID_KERNEL && !isid_init) {
+ rc = sidtab_set_initial(s, SECINITSID_INIT,
+ &c->context[0]);
if (rc) {
pr_err("SELinux: unable to load initial SID %s.\n",
name);
@@ -988,7 +995,7 @@ int policydb_context_isvalid(struct policydb *p, struct context *c)
* Read a MLS range structure from a policydb binary
* representation file.
*/
-static int mls_read_range_helper(struct mls_range *r, void *fp)
+static int mls_read_range_helper(struct mls_range *r, struct policy_file *fp)
{
__le32 buf[2];
u32 items;
@@ -1047,9 +1054,8 @@ out:
* Read and validate a security context structure
* from a policydb binary representation file.
*/
-static int context_read_and_validate(struct context *c,
- struct policydb *p,
- void *fp)
+static int context_read_and_validate(struct context *c, struct policydb *p,
+ struct policy_file *fp)
{
__le32 buf[3];
int rc;
@@ -1087,7 +1093,7 @@ out:
* binary representation file.
*/
-static int str_read(char **strp, gfp_t flags, void *fp, u32 len)
+int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
{
int rc;
char *str;
@@ -1110,7 +1116,7 @@ static int str_read(char **strp, gfp_t flags, void *fp, u32 len)
return 0;
}
-static int perm_read(struct policydb *p, struct symtab *s, void *fp)
+static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct perm_datum *perdatum;
@@ -1143,7 +1149,7 @@ bad:
return rc;
}
-static int common_read(struct policydb *p, struct symtab *s, void *fp)
+static int common_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct common_datum *comdatum;
@@ -1178,6 +1184,8 @@ static int common_read(struct policydb *p, struct symtab *s, void *fp)
goto bad;
}
+ hash_eval(&comdatum->permissions.table, "common_permissions", key);
+
rc = symtab_insert(s, key, comdatum);
if (rc)
goto bad;
@@ -1193,7 +1201,7 @@ static void type_set_init(struct type_set *t)
ebitmap_init(&t->negset);
}
-static int type_set_read(struct type_set *t, void *fp)
+static int type_set_read(struct type_set *t, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -1211,10 +1219,8 @@ static int type_set_read(struct type_set *t, void *fp)
return 0;
}
-
-static int read_cons_helper(struct policydb *p,
- struct constraint_node **nodep,
- u32 ncons, int allowxtarget, void *fp)
+static int read_cons_helper(struct policydb *p, struct constraint_node **nodep,
+ u32 ncons, int allowxtarget, struct policy_file *fp)
{
struct constraint_node *c, *lc;
struct constraint_expr *e, *le;
@@ -1284,8 +1290,9 @@ static int read_cons_helper(struct policydb *p,
return rc;
if (p->policyvers >=
POLICYDB_VERSION_CONSTRAINT_NAMES) {
- e->type_names = kzalloc(sizeof
- (*e->type_names), GFP_KERNEL);
+ e->type_names =
+ kzalloc(sizeof(*e->type_names),
+ GFP_KERNEL);
if (!e->type_names)
return -ENOMEM;
type_set_init(e->type_names);
@@ -1307,7 +1314,7 @@ static int read_cons_helper(struct policydb *p,
return 0;
}
-static int class_read(struct policydb *p, struct symtab *s, void *fp)
+static int class_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct class_datum *cladatum;
@@ -1319,7 +1326,7 @@ static int class_read(struct policydb *p, struct symtab *s, void *fp)
if (!cladatum)
return -ENOMEM;
- rc = next_entry(buf, fp, sizeof(u32)*6);
+ rc = next_entry(buf, fp, sizeof(u32) * 6);
if (rc)
goto bad;
@@ -1345,8 +1352,8 @@ static int class_read(struct policydb *p, struct symtab *s, void *fp)
goto bad;
rc = -EINVAL;
- cladatum->comdatum = symtab_search(&p->p_commons,
- cladatum->comkey);
+ cladatum->comdatum =
+ symtab_search(&p->p_commons, cladatum->comkey);
if (!cladatum->comdatum) {
pr_err("SELinux: unknown common %s\n",
cladatum->comkey);
@@ -1359,6 +1366,8 @@ static int class_read(struct policydb *p, struct symtab *s, void *fp)
goto bad;
}
+ hash_eval(&cladatum->permissions.table, "class_permissions", key);
+
rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
if (rc)
goto bad;
@@ -1369,8 +1378,8 @@ static int class_read(struct policydb *p, struct symtab *s, void *fp)
if (rc)
goto bad;
ncons = le32_to_cpu(buf[0]);
- rc = read_cons_helper(p, &cladatum->validatetrans,
- ncons, 1, fp);
+ rc = read_cons_helper(p, &cladatum->validatetrans, ncons, 1,
+ fp);
if (rc)
goto bad;
}
@@ -1402,7 +1411,7 @@ bad:
return rc;
}
-static int role_read(struct policydb *p, struct symtab *s, void *fp)
+static int role_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct role_datum *role;
@@ -1459,7 +1468,7 @@ bad:
return rc;
}
-static int type_read(struct policydb *p, struct symtab *s, void *fp)
+static int type_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct type_datum *typdatum;
@@ -1507,12 +1516,11 @@ bad:
return rc;
}
-
/*
* Read a MLS level structure from a policydb binary
* representation file.
*/
-static int mls_read_level(struct mls_level *lp, void *fp)
+static int mls_read_level(struct mls_level *lp, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -1534,7 +1542,7 @@ static int mls_read_level(struct mls_level *lp, void *fp)
return 0;
}
-static int user_read(struct policydb *p, struct symtab *s, void *fp)
+static int user_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct user_datum *usrdatum;
@@ -1585,7 +1593,7 @@ bad:
return rc;
}
-static int sens_read(struct policydb *p, struct symtab *s, void *fp)
+static int sens_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct level_datum *levdatum;
@@ -1608,12 +1616,7 @@ static int sens_read(struct policydb *p, struct symtab *s, void *fp)
if (rc)
goto bad;
- rc = -ENOMEM;
- levdatum->level = kmalloc(sizeof(*levdatum->level), GFP_KERNEL);
- if (!levdatum->level)
- goto bad;
-
- rc = mls_read_level(levdatum->level, fp);
+ rc = mls_read_level(&levdatum->level, fp);
if (rc)
goto bad;
@@ -1626,7 +1629,7 @@ bad:
return rc;
}
-static int cat_read(struct policydb *p, struct symtab *s, void *fp)
+static int cat_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
{
char *key = NULL;
struct cat_datum *catdatum;
@@ -1659,8 +1662,9 @@ bad:
return rc;
}
-static int (*const read_f[SYM_NUM]) (struct policydb *p,
- struct symtab *s, void *fp) = {
+/* clang-format off */
+static int (*const read_f[SYM_NUM])(struct policydb *p, struct symtab *s,
+ struct policy_file *fp) = {
common_read,
class_read,
role_read,
@@ -1670,6 +1674,7 @@ static int (*const read_f[SYM_NUM]) (struct policydb *p,
sens_read,
cat_read,
};
+/* clang-format on */
static int user_bounds_sanity_check(void *key, void *datum, void *datap)
{
@@ -1685,12 +1690,13 @@ static int user_bounds_sanity_check(void *key, void *datum, void *datap)
if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
pr_err("SELinux: user %s: "
"too deep or looped boundary\n",
- (char *) key);
+ (char *)key);
return -EINVAL;
}
upper = p->user_val_to_struct[upper->bounds - 1];
- ebitmap_for_each_positive_bit(&user->roles, node, bit) {
+ ebitmap_for_each_positive_bit(&user->roles, node, bit)
+ {
if (ebitmap_get_bit(&upper->roles, bit))
continue;
@@ -1721,12 +1727,13 @@ static int role_bounds_sanity_check(void *key, void *datum, void *datap)
if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
pr_err("SELinux: role %s: "
"too deep or looped bounds\n",
- (char *) key);
+ (char *)key);
return -EINVAL;
}
upper = p->role_val_to_struct[upper->bounds - 1];
- ebitmap_for_each_positive_bit(&role->types, node, bit) {
+ ebitmap_for_each_positive_bit(&role->types, node, bit)
+ {
if (ebitmap_get_bit(&upper->types, bit))
continue;
@@ -1754,7 +1761,7 @@ static int type_bounds_sanity_check(void *key, void *datum, void *datap)
if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
pr_err("SELinux: type %s: "
"too deep or looped boundary\n",
- (char *) key);
+ (char *)key);
return -EINVAL;
}
@@ -1764,7 +1771,7 @@ static int type_bounds_sanity_check(void *key, void *datum, void *datap)
if (upper->attribute) {
pr_err("SELinux: type %s: "
"bounded by attribute %s\n",
- (char *) key,
+ (char *)key,
sym_name(p, SYM_TYPES, upper->value - 1));
return -EINVAL;
}
@@ -1815,7 +1822,7 @@ u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name)
if (!tclass || tclass > p->p_classes.nprim)
return 0;
- cladatum = p->class_val_to_struct[tclass-1];
+ cladatum = p->class_val_to_struct[tclass - 1];
comdatum = cladatum->comdatum;
if (comdatum)
perdatum = symtab_search(&comdatum->permissions, name);
@@ -1824,10 +1831,10 @@ u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name)
if (!perdatum)
return 0;
- return 1U << (perdatum->value-1);
+ return 1U << (perdatum->value - 1);
}
-static int range_read(struct policydb *p, void *fp)
+static int range_read(struct policydb *p, struct policy_file *fp)
{
struct range_trans *rt = NULL;
struct mls_range *r = NULL;
@@ -1896,7 +1903,7 @@ static int range_read(struct policydb *p, void *fp)
rt = NULL;
r = NULL;
}
- hash_eval(&p->range_tr, "rangetr");
+ hash_eval(&p->range_tr, "rangetr", NULL);
rc = 0;
out:
kfree(rt);
@@ -1904,7 +1911,7 @@ out:
return rc;
}
-static int filename_trans_read_helper_compat(struct policydb *p, void *fp)
+static int filename_trans_read_helper_compat(struct policydb *p, struct policy_file *fp)
{
struct filename_trans_key key, *ft = NULL;
struct filename_trans_datum *last, *datum = NULL;
@@ -1941,6 +1948,7 @@ static int filename_trans_read_helper_compat(struct policydb *p, void *fp)
if (unlikely(ebitmap_get_bit(&datum->stypes, stype - 1))) {
/* conflicting/duplicate rules are ignored */
datum = NULL;
+ rc = 0;
goto out;
}
if (likely(datum->otype == otype))
@@ -1988,7 +1996,7 @@ out:
return rc;
}
-static int filename_trans_read_helper(struct policydb *p, void *fp)
+static int filename_trans_read_helper(struct policydb *p, struct policy_file *fp)
{
struct filename_trans_key *ft = NULL;
struct filename_trans_datum **dst, *datum, *first = NULL;
@@ -2077,7 +2085,7 @@ out:
return rc;
}
-static int filename_trans_read(struct policydb *p, void *fp)
+static int filename_trans_read(struct policydb *p, struct policy_file *fp)
{
u32 nel, i;
__le32 buf[1];
@@ -2114,11 +2122,11 @@ static int filename_trans_read(struct policydb *p, void *fp)
return rc;
}
}
- hash_eval(&p->filename_trans, "filenametr");
+ hash_eval(&p->filename_trans, "filenametr", NULL);
return 0;
}
-static int genfs_read(struct policydb *p, void *fp)
+static int genfs_read(struct policydb *p, struct policy_file *fp)
{
int rc;
u32 i, j, nel, nel2, len, len2;
@@ -2192,12 +2200,12 @@ static int genfs_read(struct policydb *p, void *fp)
goto out;
newc->v.sclass = le32_to_cpu(buf[0]);
- rc = context_read_and_validate(&newc->context[0], p, fp);
+ rc = context_read_and_validate(&newc->context[0], p,
+ fp);
if (rc)
goto out;
- for (l = NULL, c = genfs->head; c;
- l = c, c = c->next) {
+ for (l = NULL, c = genfs->head; c; l = c, c = c->next) {
rc = -EINVAL;
if (!strcmp(newc->u.name, c->u.name) &&
(!c->v.sclass || !newc->v.sclass ||
@@ -2231,8 +2239,8 @@ out:
return rc;
}
-static int ocontext_read(struct policydb *p, const struct policydb_compat_info *info,
- void *fp)
+static int ocontext_read(struct policydb *p,
+ const struct policydb_compat_info *info, struct policy_file *fp)
{
int rc;
unsigned int i;
@@ -2267,7 +2275,8 @@ static int ocontext_read(struct policydb *p, const struct policydb_compat_info *
goto out;
c->sid[0] = le32_to_cpu(buf[0]);
- rc = context_read_and_validate(&c->context[0], p, fp);
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
break;
@@ -2286,21 +2295,24 @@ static int ocontext_read(struct policydb *p, const struct policydb_compat_info *
pr_warn("SELinux: void and deprecated fs ocon %s\n",
c->u.name);
- rc = context_read_and_validate(&c->context[0], p, fp);
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
- rc = context_read_and_validate(&c->context[1], p, fp);
+ rc = context_read_and_validate(&c->context[1],
+ p, fp);
if (rc)
goto out;
break;
case OCON_PORT:
- rc = next_entry(buf, fp, sizeof(u32)*3);
+ rc = next_entry(buf, fp, sizeof(u32) * 3);
if (rc)
goto out;
c->u.port.protocol = le32_to_cpu(buf[0]);
c->u.port.low_port = le32_to_cpu(buf[1]);
c->u.port.high_port = le32_to_cpu(buf[2]);
- rc = context_read_and_validate(&c->context[0], p, fp);
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
break;
@@ -2310,12 +2322,13 @@ static int ocontext_read(struct policydb *p, const struct policydb_compat_info *
goto out;
c->u.node.addr = nodebuf[0]; /* network order */
c->u.node.mask = nodebuf[1]; /* network order */
- rc = context_read_and_validate(&c->context[0], p, fp);
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
break;
case OCON_FSUSE:
- rc = next_entry(buf, fp, sizeof(u32)*2);
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto out;
@@ -2332,7 +2345,8 @@ static int ocontext_read(struct policydb *p, const struct policydb_compat_info *
if (rc)
goto out;
- rc = context_read_and_validate(&c->context[0], p, fp);
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
break;
@@ -2345,8 +2359,9 @@ static int ocontext_read(struct policydb *p, const struct policydb_compat_info *
for (k = 0; k < 4; k++)
c->u.node6.addr[k] = nodebuf[k];
for (k = 0; k < 4; k++)
- c->u.node6.mask[k] = nodebuf[k+4];
- rc = context_read_and_validate(&c->context[0], p, fp);
+ c->u.node6.mask[k] = nodebuf[k + 4];
+ rc = context_read_and_validate(&c->context[0],
+ p, fp);
if (rc)
goto out;
break;
@@ -2359,7 +2374,8 @@ static int ocontext_read(struct policydb *p, const struct policydb_compat_info *
goto out;
/* we need to have subnet_prefix in CPU order */
- c->u.ibpkey.subnet_prefix = be64_to_cpu(prefixbuf[0]);
+ c->u.ibpkey.subnet_prefix =
+ be64_to_cpu(prefixbuf[0]);
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
@@ -2373,12 +2389,11 @@ static int ocontext_read(struct policydb *p, const struct policydb_compat_info *
goto out;
}
- c->u.ibpkey.low_pkey = pkey_lo;
+ c->u.ibpkey.low_pkey = pkey_lo;
c->u.ibpkey.high_pkey = pkey_hi;
rc = context_read_and_validate(&c->context[0],
- p,
- fp);
+ p, fp);
if (rc)
goto out;
break;
@@ -2391,7 +2406,8 @@ static int ocontext_read(struct policydb *p, const struct policydb_compat_info *
goto out;
len = le32_to_cpu(buf[0]);
- rc = str_read(&c->u.ibendport.dev_name, GFP_KERNEL, fp, len);
+ rc = str_read(&c->u.ibendport.dev_name,
+ GFP_KERNEL, fp, len);
if (rc)
goto out;
@@ -2404,8 +2420,7 @@ static int ocontext_read(struct policydb *p, const struct policydb_compat_info *
c->u.ibendport.port = port;
rc = context_read_and_validate(&c->context[0],
- p,
- fp);
+ p, fp);
if (rc)
goto out;
break;
@@ -2422,7 +2437,7 @@ out:
* Read the configuration data from a policy database binary
* representation file into a policy database structure.
*/
-int policydb_read(struct policydb *p, void *fp)
+int policydb_read(struct policydb *p, struct policy_file *fp)
{
struct role_allow *ra, *lra;
struct role_trans_key *rtk = NULL;
@@ -2458,26 +2473,22 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
}
- rc = -ENOMEM;
- policydb_str = kmalloc(len + 1, GFP_KERNEL);
- if (!policydb_str) {
- pr_err("SELinux: unable to allocate memory for policydb "
- "string of length %d\n", len);
- goto bad;
- }
-
- rc = next_entry(policydb_str, fp, len);
+ rc = str_read(&policydb_str, GFP_KERNEL, fp, len);
if (rc) {
- pr_err("SELinux: truncated policydb string identifier\n");
- kfree(policydb_str);
+ if (rc == -ENOMEM) {
+ pr_err("SELinux: unable to allocate memory for policydb string of length %d\n",
+ len);
+ } else {
+ pr_err("SELinux: truncated policydb string identifier\n");
+ }
goto bad;
}
rc = -EINVAL;
- policydb_str[len] = '\0';
if (strcmp(policydb_str, POLICYDB_STRING)) {
pr_err("SELinux: policydb string %s does not match "
- "my string %s\n", policydb_str, POLICYDB_STRING);
+ "my string %s\n",
+ policydb_str, POLICYDB_STRING);
kfree(policydb_str);
goto bad;
}
@@ -2486,7 +2497,7 @@ int policydb_read(struct policydb *p, void *fp)
policydb_str = NULL;
/* Read the version and table sizes. */
- rc = next_entry(buf, fp, sizeof(u32)*4);
+ rc = next_entry(buf, fp, sizeof(u32) * 4);
if (rc)
goto bad;
@@ -2496,7 +2507,8 @@ int policydb_read(struct policydb *p, void *fp)
p->policyvers > POLICYDB_VERSION_MAX) {
pr_err("SELinux: policydb version %d does not match "
"my version range %d-%d\n",
- le32_to_cpu(buf[0]), POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX);
+ le32_to_cpu(buf[0]), POLICYDB_VERSION_MIN,
+ POLICYDB_VERSION_MAX);
goto bad;
}
@@ -2506,8 +2518,8 @@ int policydb_read(struct policydb *p, void *fp)
rc = -EINVAL;
if (p->policyvers < POLICYDB_VERSION_MLS) {
pr_err("SELinux: security policydb version %d "
- "(MLS) not backwards compatible\n",
- p->policyvers);
+ "(MLS) not backwards compatible\n",
+ p->policyvers);
goto bad;
}
}
@@ -2530,22 +2542,23 @@ int policydb_read(struct policydb *p, void *fp)
info = policydb_lookup_compat(p->policyvers);
if (!info) {
pr_err("SELinux: unable to find policy compat info "
- "for version %d\n", p->policyvers);
+ "for version %d\n",
+ p->policyvers);
goto bad;
}
rc = -EINVAL;
if (le32_to_cpu(buf[2]) != info->sym_num ||
- le32_to_cpu(buf[3]) != info->ocon_num) {
+ le32_to_cpu(buf[3]) != info->ocon_num) {
pr_err("SELinux: policydb table sizes (%d,%d) do "
- "not match mine (%d,%d)\n", le32_to_cpu(buf[2]),
- le32_to_cpu(buf[3]),
- info->sym_num, info->ocon_num);
+ "not match mine (%d,%d)\n",
+ le32_to_cpu(buf[2]), le32_to_cpu(buf[3]), info->sym_num,
+ info->ocon_num);
goto bad;
}
for (i = 0; i < info->sym_num; i++) {
- rc = next_entry(buf, fp, sizeof(u32)*2);
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto bad;
nprim = le32_to_cpu(buf[0]);
@@ -2606,7 +2619,7 @@ int policydb_read(struct policydb *p, void *fp)
if (!rtd)
goto bad;
- rc = next_entry(buf, fp, sizeof(u32)*3);
+ rc = next_entry(buf, fp, sizeof(u32) * 3);
if (rc)
goto bad;
@@ -2636,6 +2649,8 @@ int policydb_read(struct policydb *p, void *fp)
rtd = NULL;
}
+ hash_eval(&p->role_tr, "roletr", NULL);
+
rc = next_entry(buf, fp, sizeof(u32));
if (rc)
goto bad;
@@ -2650,7 +2665,7 @@ int policydb_read(struct policydb *p, void *fp)
lra->next = ra;
else
p->role_allow = ra;
- rc = next_entry(buf, fp, sizeof(u32)*2);
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto bad;
@@ -2698,9 +2713,8 @@ int policydb_read(struct policydb *p, void *fp)
goto bad;
rc = -ENOMEM;
- p->type_attr_map_array = kvcalloc(p->p_types.nprim,
- sizeof(*p->type_attr_map_array),
- GFP_KERNEL);
+ p->type_attr_map_array = kvcalloc(
+ p->p_types.nprim, sizeof(*p->type_attr_map_array), GFP_KERNEL);
if (!p->type_attr_map_array)
goto bad;
@@ -2740,7 +2754,7 @@ bad:
* Write a MLS level structure to a policydb binary
* representation file.
*/
-static int mls_write_level(struct mls_level *l, void *fp)
+static int mls_write_level(struct mls_level *l, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -2761,7 +2775,7 @@ static int mls_write_level(struct mls_level *l, void *fp)
* Write a MLS range structure to a policydb binary
* representation file.
*/
-static int mls_write_range_helper(struct mls_range *r, void *fp)
+static int mls_write_range_helper(struct mls_range *r, struct policy_file *fp)
{
__le32 buf[3];
size_t items;
@@ -2773,7 +2787,7 @@ static int mls_write_range_helper(struct mls_range *r, void *fp)
items = 2;
else
items = 3;
- buf[0] = cpu_to_le32(items-1);
+ buf[0] = cpu_to_le32(items - 1);
buf[1] = cpu_to_le32(r->level[0].sens);
if (!eq)
buf[2] = cpu_to_le32(r->level[1].sens);
@@ -2801,7 +2815,7 @@ static int sens_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct level_datum *levdatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[2];
size_t len;
int rc;
@@ -2817,7 +2831,7 @@ static int sens_write(void *vkey, void *datum, void *ptr)
if (rc)
return rc;
- rc = mls_write_level(levdatum->level, fp);
+ rc = mls_write_level(&levdatum->level, fp);
if (rc)
return rc;
@@ -2829,7 +2843,7 @@ static int cat_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct cat_datum *catdatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[3];
size_t len;
int rc;
@@ -2854,7 +2868,7 @@ static int role_trans_write_one(void *key, void *datum, void *ptr)
struct role_trans_key *rtk = key;
struct role_trans_datum *rtd = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
struct policydb *p = pd->p;
__le32 buf[3];
int rc;
@@ -2874,7 +2888,7 @@ static int role_trans_write_one(void *key, void *datum, void *ptr)
return 0;
}
-static int role_trans_write(struct policydb *p, void *fp)
+static int role_trans_write(struct policydb *p, struct policy_file *fp)
{
struct policy_data pd = { .p = p, .fp = fp };
__le32 buf[1];
@@ -2888,7 +2902,7 @@ static int role_trans_write(struct policydb *p, void *fp)
return hashtab_map(&p->role_tr, role_trans_write_one, &pd);
}
-static int role_allow_write(struct role_allow *r, void *fp)
+static int role_allow_write(struct role_allow *r, struct policy_file *fp)
{
struct role_allow *ra;
__le32 buf[2];
@@ -2916,8 +2930,7 @@ static int role_allow_write(struct role_allow *r, void *fp)
* Write a security context structure
* to a policydb binary representation file.
*/
-static int context_write(struct policydb *p, struct context *c,
- void *fp)
+static int context_write(struct policydb *p, struct context *c, struct policy_file *fp)
{
int rc;
__le32 buf[3];
@@ -2970,7 +2983,7 @@ static int common_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct common_datum *comdatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[4];
size_t len;
int rc;
@@ -2995,7 +3008,7 @@ static int common_write(void *vkey, void *datum, void *ptr)
return 0;
}
-static int type_set_write(struct type_set *t, void *fp)
+static int type_set_write(struct type_set *t, struct policy_file *fp)
{
int rc;
__le32 buf[1];
@@ -3014,7 +3027,7 @@ static int type_set_write(struct type_set *t, void *fp)
}
static int write_cons_helper(struct policydb *p, struct constraint_node *node,
- void *fp)
+ struct policy_file *fp)
{
struct constraint_node *c;
struct constraint_expr *e;
@@ -3045,7 +3058,7 @@ static int write_cons_helper(struct policydb *p, struct constraint_node *node,
if (rc)
return rc;
if (p->policyvers >=
- POLICYDB_VERSION_CONSTRAINT_NAMES) {
+ POLICYDB_VERSION_CONSTRAINT_NAMES) {
rc = type_set_write(e->type_names, fp);
if (rc)
return rc;
@@ -3065,7 +3078,7 @@ static int class_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct class_datum *cladatum = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
struct policydb *p = pd->p;
struct constraint_node *c;
__le32 buf[6];
@@ -3150,7 +3163,7 @@ static int role_write(void *vkey, void *datum, void *ptr)
char *key = vkey;
struct role_datum *role = datum;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
struct policydb *p = pd->p;
__le32 buf[3];
size_t items, len;
@@ -3190,7 +3203,7 @@ static int type_write(void *vkey, void *datum, void *ptr)
struct type_datum *typdatum = datum;
struct policy_data *pd = ptr;
struct policydb *p = pd->p;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[4];
int rc;
size_t items, len;
@@ -3231,7 +3244,7 @@ static int user_write(void *vkey, void *datum, void *ptr)
struct user_datum *usrdatum = datum;
struct policy_data *pd = ptr;
struct policydb *p = pd->p;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
__le32 buf[3];
size_t items, len;
int rc;
@@ -3266,7 +3279,8 @@ static int user_write(void *vkey, void *datum, void *ptr)
return 0;
}
-static int (*const write_f[SYM_NUM]) (void *key, void *datum, void *datap) = {
+/* clang-format off */
+static int (*const write_f[SYM_NUM])(void *key, void *datum, void *datap) = {
common_write,
class_write,
role_write,
@@ -3276,9 +3290,11 @@ static int (*const write_f[SYM_NUM]) (void *key, void *datum, void *datap) = {
sens_write,
cat_write,
};
+/* clang-format on */
-static int ocontext_write(struct policydb *p, const struct policydb_compat_info *info,
- void *fp)
+static int ocontext_write(struct policydb *p,
+ const struct policydb_compat_info *info,
+ struct policy_file *fp)
{
unsigned int i, j;
int rc;
@@ -3360,9 +3376,13 @@ static int ocontext_write(struct policydb *p, const struct policydb_compat_info
break;
case OCON_NODE6:
for (j = 0; j < 4; j++)
- nodebuf[j] = c->u.node6.addr[j]; /* network order */
+ nodebuf[j] =
+ c->u.node6.addr
+ [j]; /* network order */
for (j = 0; j < 4; j++)
- nodebuf[j + 4] = c->u.node6.mask[j]; /* network order */
+ nodebuf[j + 4] =
+ c->u.node6.mask
+ [j]; /* network order */
rc = put_entry(nodebuf, sizeof(u32), 8, fp);
if (rc)
return rc;
@@ -3372,7 +3392,8 @@ static int ocontext_write(struct policydb *p, const struct policydb_compat_info
break;
case OCON_IBPKEY:
/* subnet_prefix is in CPU order */
- prefixbuf[0] = cpu_to_be64(c->u.ibpkey.subnet_prefix);
+ prefixbuf[0] =
+ cpu_to_be64(c->u.ibpkey.subnet_prefix);
rc = put_entry(prefixbuf, sizeof(u64), 1, fp);
if (rc)
@@ -3395,7 +3416,8 @@ static int ocontext_write(struct policydb *p, const struct policydb_compat_info
rc = put_entry(buf, sizeof(u32), 2, fp);
if (rc)
return rc;
- rc = put_entry(c->u.ibendport.dev_name, 1, len, fp);
+ rc = put_entry(c->u.ibendport.dev_name, 1, len,
+ fp);
if (rc)
return rc;
rc = context_write(p, &c->context[0], fp);
@@ -3408,7 +3430,7 @@ static int ocontext_write(struct policydb *p, const struct policydb_compat_info
return 0;
}
-static int genfs_write(struct policydb *p, void *fp)
+static int genfs_write(struct policydb *p, struct policy_file *fp)
{
struct genfs *genfs;
struct ocontext *c;
@@ -3466,7 +3488,7 @@ static int range_write_helper(void *key, void *data, void *ptr)
struct range_trans *rt = key;
struct mls_range *r = data;
struct policy_data *pd = ptr;
- void *fp = pd->fp;
+ struct policy_file *fp = pd->fp;
struct policydb *p = pd->p;
int rc;
@@ -3488,7 +3510,7 @@ static int range_write_helper(void *key, void *data, void *ptr)
return 0;
}
-static int range_write(struct policydb *p, void *fp)
+static int range_write(struct policydb *p, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -3515,13 +3537,14 @@ static int filename_write_helper_compat(void *key, void *data, void *ptr)
struct filename_trans_key *ft = key;
struct filename_trans_datum *datum = data;
struct ebitmap_node *node;
- void *fp = ptr;
+ struct policy_file *fp = ptr;
__le32 buf[4];
int rc;
u32 bit, len = strlen(ft->name);
do {
- ebitmap_for_each_positive_bit(&datum->stypes, node, bit) {
+ ebitmap_for_each_positive_bit(&datum->stypes, node, bit)
+ {
buf[0] = cpu_to_le32(len);
rc = put_entry(buf, sizeof(u32), 1, fp);
if (rc)
@@ -3551,7 +3574,7 @@ static int filename_write_helper(void *key, void *data, void *ptr)
{
struct filename_trans_key *ft = key;
struct filename_trans_datum *datum;
- void *fp = ptr;
+ struct policy_file *fp = ptr;
__le32 buf[3];
int rc;
u32 ndatum, len = strlen(ft->name);
@@ -3596,7 +3619,7 @@ static int filename_write_helper(void *key, void *data, void *ptr)
return 0;
}
-static int filename_trans_write(struct policydb *p, void *fp)
+static int filename_trans_write(struct policydb *p, struct policy_file *fp)
{
__le32 buf[1];
int rc;
@@ -3628,7 +3651,7 @@ static int filename_trans_write(struct policydb *p, void *fp)
* structure to a policy database binary representation
* file.
*/
-int policydb_write(struct policydb *p, void *fp)
+int policydb_write(struct policydb *p, struct policy_file *fp)
{
unsigned int num_syms;
int rc;
@@ -3645,8 +3668,8 @@ int policydb_write(struct policydb *p, void *fp)
*/
if (p->policyvers < POLICYDB_VERSION_AVTAB) {
pr_err("SELinux: refusing to write policy version %d."
- " Because it is less than version %d\n", p->policyvers,
- POLICYDB_VERSION_AVTAB);
+ " Because it is less than version %d\n",
+ p->policyvers, POLICYDB_VERSION_AVTAB);
return -EINVAL;
}
@@ -3674,7 +3697,8 @@ int policydb_write(struct policydb *p, void *fp)
info = policydb_lookup_compat(p->policyvers);
if (!info) {
pr_err("SELinux: compatibility lookup failed for policy "
- "version %d\n", p->policyvers);
+ "version %d\n",
+ p->policyvers);
return -EINVAL;
}
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index b97cda489753..25650224b6e7 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -8,15 +8,13 @@
/*
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ * Support for enhanced MLS infrastructure.
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
*
- * Support for enhanced MLS infrastructure.
- *
- * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
- *
- * Added conditional policy language extensions
- *
- * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
- * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * Updated: Frank Mayer <mayerf@tresys.com> and
+ * Karl MacMillan <kmacmillan@tresys.com>
+ * Added conditional policy language extensions
+ * Copyright (C) 2003-2004 Tresys Technology, LLC
*/
#ifndef _SS_POLICYDB_H_
@@ -39,104 +37,103 @@
/* Permission attributes */
struct perm_datum {
- u32 value; /* permission bit + 1 */
+ u32 value; /* permission bit + 1 */
};
/* Attributes of a common prefix for access vectors */
struct common_datum {
- u32 value; /* internal common value */
- struct symtab permissions; /* common permissions */
+ u32 value; /* internal common value */
+ struct symtab permissions; /* common permissions */
};
/* Class attributes */
struct class_datum {
- u32 value; /* class value */
- char *comkey; /* common name */
- struct common_datum *comdatum; /* common datum */
- struct symtab permissions; /* class-specific permission symbol table */
- struct constraint_node *constraints; /* constraints on class permissions */
- struct constraint_node *validatetrans; /* special transition rules */
+ u32 value; /* class value */
+ char *comkey; /* common name */
+ struct common_datum *comdatum; /* common datum */
+ struct symtab permissions; /* class-specific permission symbol table */
+ struct constraint_node *constraints; /* constraints on class perms */
+ struct constraint_node *validatetrans; /* special transition rules */
/* Options how a new object user, role, and type should be decided */
-#define DEFAULT_SOURCE 1
-#define DEFAULT_TARGET 2
+#define DEFAULT_SOURCE 1
+#define DEFAULT_TARGET 2
char default_user;
char default_role;
char default_type;
/* Options how a new object range should be decided */
-#define DEFAULT_SOURCE_LOW 1
-#define DEFAULT_SOURCE_HIGH 2
-#define DEFAULT_SOURCE_LOW_HIGH 3
-#define DEFAULT_TARGET_LOW 4
-#define DEFAULT_TARGET_HIGH 5
-#define DEFAULT_TARGET_LOW_HIGH 6
+#define DEFAULT_SOURCE_LOW 1
+#define DEFAULT_SOURCE_HIGH 2
+#define DEFAULT_SOURCE_LOW_HIGH 3
+#define DEFAULT_TARGET_LOW 4
+#define DEFAULT_TARGET_HIGH 5
+#define DEFAULT_TARGET_LOW_HIGH 6
#define DEFAULT_GLBLUB 7
char default_range;
};
/* Role attributes */
struct role_datum {
- u32 value; /* internal role value */
- u32 bounds; /* boundary of role */
- struct ebitmap dominates; /* set of roles dominated by this role */
- struct ebitmap types; /* set of authorized types for role */
+ u32 value; /* internal role value */
+ u32 bounds; /* boundary of role */
+ struct ebitmap dominates; /* set of roles dominated by this role */
+ struct ebitmap types; /* set of authorized types for role */
};
struct role_trans_key {
- u32 role; /* current role */
- u32 type; /* program executable type, or new object type */
- u32 tclass; /* process class, or new object class */
+ u32 role; /* current role */
+ u32 type; /* program executable type, or new object type */
+ u32 tclass; /* process class, or new object class */
};
struct role_trans_datum {
- u32 new_role; /* new role */
+ u32 new_role; /* new role */
};
struct filename_trans_key {
- u32 ttype; /* parent dir context */
- u16 tclass; /* class of new object */
- const char *name; /* last path component */
+ u32 ttype; /* parent dir context */
+ u16 tclass; /* class of new object */
+ const char *name; /* last path component */
};
struct filename_trans_datum {
- struct ebitmap stypes; /* bitmap of source types for this otype */
- u32 otype; /* resulting type of new object */
- struct filename_trans_datum *next; /* record for next otype*/
+ struct ebitmap stypes; /* bitmap of source types for this otype */
+ u32 otype; /* resulting type of new object */
+ struct filename_trans_datum *next; /* record for next otype*/
};
struct role_allow {
- u32 role; /* current role */
- u32 new_role; /* new role */
+ u32 role; /* current role */
+ u32 new_role; /* new role */
struct role_allow *next;
};
/* Type attributes */
struct type_datum {
- u32 value; /* internal type value */
- u32 bounds; /* boundary of type */
- unsigned char primary; /* primary name? */
- unsigned char attribute;/* attribute ?*/
+ u32 value; /* internal type value */
+ u32 bounds; /* boundary of type */
+ unsigned char primary; /* primary name? */
+ unsigned char attribute; /* attribute ?*/
};
/* User attributes */
struct user_datum {
- u32 value; /* internal user value */
- u32 bounds; /* bounds of user */
- struct ebitmap roles; /* set of authorized roles for user */
- struct mls_range range; /* MLS range (min - max) for user */
- struct mls_level dfltlevel; /* default login MLS level for user */
+ u32 value; /* internal user value */
+ u32 bounds; /* bounds of user */
+ struct ebitmap roles; /* set of authorized roles for user */
+ struct mls_range range; /* MLS range (min - max) for user */
+ struct mls_level dfltlevel; /* default login MLS level for user */
};
-
/* Sensitivity attributes */
struct level_datum {
- struct mls_level *level; /* sensitivity and associated categories */
- unsigned char isalias; /* is this sensitivity an alias for another? */
+ struct mls_level level; /* sensitivity and associated categories */
+ unsigned char isalias; /* is this sensitivity an alias for another? */
};
/* Category attributes */
struct cat_datum {
- u32 value; /* internal category bit + 1 */
- unsigned char isalias; /* is this category an alias for another? */
+ u32 value; /* internal category bit + 1 */
+ unsigned char isalias; /* is this category an alias for another? */
};
struct range_trans {
@@ -147,7 +144,7 @@ struct range_trans {
/* Boolean data type */
struct cond_bool_datum {
- __u32 value; /* internal type value */
+ u32 value; /* internal type value */
int state;
};
@@ -173,20 +170,20 @@ struct type_set {
*/
struct ocontext {
union {
- char *name; /* name of initial SID, fs, netif, fstype, path */
+ char *name; /* name of initial SID, fs, netif, fstype, path */
struct {
u8 protocol;
u16 low_port;
u16 high_port;
- } port; /* TCP or UDP port information */
+ } port; /* TCP or UDP port information */
struct {
u32 addr;
u32 mask;
- } node; /* node information */
+ } node; /* node information */
struct {
u32 addr[4];
u32 mask[4];
- } node6; /* IPv6 node information */
+ } node6; /* IPv6 node information */
struct {
u64 subnet_prefix;
u16 low_pkey;
@@ -198,11 +195,11 @@ struct ocontext {
} ibendport;
} u;
union {
- u32 sclass; /* security class for genfs */
- u32 behavior; /* labeling behavior for fs_use */
+ u32 sclass; /* security class for genfs */
+ u32 behavior; /* labeling behavior for fs_use */
} v;
- struct context context[2]; /* security context(s) */
- u32 sid[2]; /* SID(s) */
+ struct context context[2]; /* security context(s) */
+ u32 sid[2]; /* SID(s) */
struct ocontext *next;
};
@@ -221,19 +218,19 @@ struct genfs {
#define SYM_BOOLS 5
#define SYM_LEVELS 6
#define SYM_CATS 7
-#define SYM_NUM 8
+#define SYM_NUM 8
/* object context array indices */
-#define OCON_ISID 0 /* initial SIDs */
-#define OCON_FS 1 /* unlabeled file systems (deprecated) */
-#define OCON_PORT 2 /* TCP and UDP port numbers */
-#define OCON_NETIF 3 /* network interfaces */
-#define OCON_NODE 4 /* nodes */
-#define OCON_FSUSE 5 /* fs_use */
-#define OCON_NODE6 6 /* IPv6 nodes */
-#define OCON_IBPKEY 7 /* Infiniband PKeys */
-#define OCON_IBENDPORT 8 /* Infiniband end ports */
-#define OCON_NUM 9
+#define OCON_ISID 0 /* initial SIDs */
+#define OCON_FS 1 /* unlabeled file systems (deprecated) */
+#define OCON_PORT 2 /* TCP and UDP port numbers */
+#define OCON_NETIF 3 /* network interfaces */
+#define OCON_NODE 4 /* nodes */
+#define OCON_FSUSE 5 /* fs_use */
+#define OCON_NODE6 6 /* IPv6 nodes */
+#define OCON_IBPKEY 7 /* Infiniband PKeys */
+#define OCON_IBENDPORT 8 /* Infiniband end ports */
+#define OCON_NUM 9
/* The policy database */
struct policydb {
@@ -243,15 +240,15 @@ struct policydb {
struct symtab symtab[SYM_NUM];
#define p_commons symtab[SYM_COMMONS]
#define p_classes symtab[SYM_CLASSES]
-#define p_roles symtab[SYM_ROLES]
-#define p_types symtab[SYM_TYPES]
-#define p_users symtab[SYM_USERS]
-#define p_bools symtab[SYM_BOOLS]
-#define p_levels symtab[SYM_LEVELS]
-#define p_cats symtab[SYM_CATS]
+#define p_roles symtab[SYM_ROLES]
+#define p_types symtab[SYM_TYPES]
+#define p_users symtab[SYM_USERS]
+#define p_bools symtab[SYM_BOOLS]
+#define p_levels symtab[SYM_LEVELS]
+#define p_cats symtab[SYM_CATS]
/* symbol names indexed by (value - 1) */
- char **sym_val_to_name[SYM_NUM];
+ char **sym_val_to_name[SYM_NUM];
/* class, role, and user attributes indexed by (value - 1) */
struct class_datum **class_val_to_struct;
@@ -315,44 +312,44 @@ struct policydb {
u32 process_trans_perms;
} __randomize_layout;
+struct policy_file {
+ char *data;
+ size_t len;
+};
+
extern void policydb_destroy(struct policydb *p);
extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
extern int policydb_context_isvalid(struct policydb *p, struct context *c);
extern int policydb_class_isvalid(struct policydb *p, unsigned int class);
extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
-extern int policydb_read(struct policydb *p, void *fp);
-extern int policydb_write(struct policydb *p, void *fp);
+extern int policydb_read(struct policydb *p, struct policy_file *fp);
+extern int policydb_write(struct policydb *p, struct policy_file *fp);
-extern struct filename_trans_datum *policydb_filenametr_search(
- struct policydb *p, struct filename_trans_key *key);
+extern struct filename_trans_datum *
+policydb_filenametr_search(struct policydb *p, struct filename_trans_key *key);
-extern struct mls_range *policydb_rangetr_search(
- struct policydb *p, struct range_trans *key);
+extern struct mls_range *policydb_rangetr_search(struct policydb *p,
+ struct range_trans *key);
-extern struct role_trans_datum *policydb_roletr_search(
- struct policydb *p, struct role_trans_key *key);
+extern struct role_trans_datum *
+policydb_roletr_search(struct policydb *p, struct role_trans_key *key);
-#define POLICYDB_CONFIG_MLS 1
+#define POLICYDB_CONFIG_MLS 1
/* the config flags related to unknown classes/perms are bits 2 and 3 */
-#define REJECT_UNKNOWN 0x00000002
-#define ALLOW_UNKNOWN 0x00000004
+#define REJECT_UNKNOWN 0x00000002
+#define ALLOW_UNKNOWN 0x00000004
-#define OBJECT_R "object_r"
+#define OBJECT_R "object_r"
#define OBJECT_R_VAL 1
-#define POLICYDB_MAGIC SELINUX_MAGIC
+#define POLICYDB_MAGIC SELINUX_MAGIC
#define POLICYDB_STRING "SE Linux"
-struct policy_file {
- char *data;
- size_t len;
-};
-
struct policy_data {
struct policydb *p;
- void *fp;
+ struct policy_file *fp;
};
static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes)
@@ -366,7 +363,8 @@ static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes)
return 0;
}
-static inline int put_entry(const void *buf, size_t bytes, size_t num, struct policy_file *fp)
+static inline int put_entry(const void *buf, size_t bytes, size_t num,
+ struct policy_file *fp)
{
size_t len;
@@ -382,13 +380,15 @@ static inline int put_entry(const void *buf, size_t bytes, size_t num, struct po
return 0;
}
-static inline char *sym_name(struct policydb *p, unsigned int sym_num, unsigned int element_nr)
+static inline char *sym_name(struct policydb *p, unsigned int sym_num,
+ unsigned int element_nr)
{
return p->sym_val_to_name[sym_num][element_nr];
}
+extern int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len);
+
extern u16 string_to_security_class(struct policydb *p, const char *name);
extern u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name);
-#endif /* _SS_POLICYDB_H_ */
-
+#endif /* _SS_POLICYDB_H_ */
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index e88b1b6c4adb..8478842fbf9e 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -582,8 +582,7 @@ static void type_attribute_bounds_av(struct policydb *policydb,
}
/*
- * flag which drivers have permissions
- * only looking for ioctl based extended permissions
+ * Flag which drivers have permissions and which base permissions are covered.
*/
void services_compute_xperms_drivers(
struct extended_perms *xperms,
@@ -591,14 +590,25 @@ void services_compute_xperms_drivers(
{
unsigned int i;
- if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
+ switch (node->datum.u.xperms->specified) {
+ case AVTAB_XPERMS_IOCTLDRIVER:
+ xperms->base_perms |= AVC_EXT_IOCTL;
/* if one or more driver has all permissions allowed */
for (i = 0; i < ARRAY_SIZE(xperms->drivers.p); i++)
xperms->drivers.p[i] |= node->datum.u.xperms->perms.p[i];
- } else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
+ break;
+ case AVTAB_XPERMS_IOCTLFUNCTION:
+ xperms->base_perms |= AVC_EXT_IOCTL;
+ /* if allowing permissions within a driver */
+ security_xperm_set(xperms->drivers.p,
+ node->datum.u.xperms->driver);
+ break;
+ case AVTAB_XPERMS_NLMSG:
+ xperms->base_perms |= AVC_EXT_NLMSG;
/* if allowing permissions within a driver */
security_xperm_set(xperms->drivers.p,
node->datum.u.xperms->driver);
+ break;
}
xperms->len = 1;
@@ -628,13 +638,11 @@ static void context_struct_compute_av(struct policydb *policydb,
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
if (xperms) {
- memset(&xperms->drivers, 0, sizeof(xperms->drivers));
- xperms->len = 0;
+ memset(xperms, 0, sizeof(*xperms));
}
if (unlikely(!tclass || tclass > policydb->p_classes.nprim)) {
- if (printk_ratelimit())
- pr_warn("SELinux: Invalid class %hu\n", tclass);
+ pr_warn_ratelimited("SELinux: Invalid class %u\n", tclass);
return;
}
@@ -943,57 +951,74 @@ static void avd_init(struct selinux_policy *policy, struct av_decision *avd)
avd->flags = 0;
}
+static void update_xperms_extended_data(u8 specified,
+ const struct extended_perms_data *from,
+ struct extended_perms_data *xp_data)
+{
+ unsigned int i;
+
+ switch (specified) {
+ case AVTAB_XPERMS_IOCTLDRIVER:
+ memset(xp_data->p, 0xff, sizeof(xp_data->p));
+ break;
+ case AVTAB_XPERMS_IOCTLFUNCTION:
+ case AVTAB_XPERMS_NLMSG:
+ for (i = 0; i < ARRAY_SIZE(xp_data->p); i++)
+ xp_data->p[i] |= from->p[i];
+ break;
+ }
+
+}
+
void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
struct avtab_node *node)
{
- unsigned int i;
+ u16 specified;
- if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
- if (xpermd->driver != node->datum.u.xperms->driver)
+ switch (node->datum.u.xperms->specified) {
+ case AVTAB_XPERMS_IOCTLFUNCTION:
+ if (xpermd->base_perm != AVC_EXT_IOCTL ||
+ xpermd->driver != node->datum.u.xperms->driver)
return;
- } else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
- if (!security_xperm_test(node->datum.u.xperms->perms.p,
- xpermd->driver))
+ break;
+ case AVTAB_XPERMS_IOCTLDRIVER:
+ if (xpermd->base_perm != AVC_EXT_IOCTL ||
+ !security_xperm_test(node->datum.u.xperms->perms.p,
+ xpermd->driver))
return;
- } else {
- BUG();
+ break;
+ case AVTAB_XPERMS_NLMSG:
+ if (xpermd->base_perm != AVC_EXT_NLMSG ||
+ xpermd->driver != node->datum.u.xperms->driver)
+ return;
+ break;
+ default:
+ pr_warn_once(
+ "SELinux: unknown extended permission (%u) will be ignored\n",
+ node->datum.u.xperms->specified);
+ return;
}
- if (node->key.specified == AVTAB_XPERMS_ALLOWED) {
+ specified = node->key.specified & ~(AVTAB_ENABLED | AVTAB_ENABLED_OLD);
+
+ if (specified == AVTAB_XPERMS_ALLOWED) {
xpermd->used |= XPERMS_ALLOWED;
- if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
- memset(xpermd->allowed->p, 0xff,
- sizeof(xpermd->allowed->p));
- }
- if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
- for (i = 0; i < ARRAY_SIZE(xpermd->allowed->p); i++)
- xpermd->allowed->p[i] |=
- node->datum.u.xperms->perms.p[i];
- }
- } else if (node->key.specified == AVTAB_XPERMS_AUDITALLOW) {
+ update_xperms_extended_data(node->datum.u.xperms->specified,
+ &node->datum.u.xperms->perms,
+ xpermd->allowed);
+ } else if (specified == AVTAB_XPERMS_AUDITALLOW) {
xpermd->used |= XPERMS_AUDITALLOW;
- if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
- memset(xpermd->auditallow->p, 0xff,
- sizeof(xpermd->auditallow->p));
- }
- if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
- for (i = 0; i < ARRAY_SIZE(xpermd->auditallow->p); i++)
- xpermd->auditallow->p[i] |=
- node->datum.u.xperms->perms.p[i];
- }
- } else if (node->key.specified == AVTAB_XPERMS_DONTAUDIT) {
+ update_xperms_extended_data(node->datum.u.xperms->specified,
+ &node->datum.u.xperms->perms,
+ xpermd->auditallow);
+ } else if (specified == AVTAB_XPERMS_DONTAUDIT) {
xpermd->used |= XPERMS_DONTAUDIT;
- if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
- memset(xpermd->dontaudit->p, 0xff,
- sizeof(xpermd->dontaudit->p));
- }
- if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
- for (i = 0; i < ARRAY_SIZE(xpermd->dontaudit->p); i++)
- xpermd->dontaudit->p[i] |=
- node->datum.u.xperms->perms.p[i];
- }
+ update_xperms_extended_data(node->datum.u.xperms->specified,
+ &node->datum.u.xperms->perms,
+ xpermd->dontaudit);
} else {
- BUG();
+ pr_warn_once("SELinux: unknown specified key (%u)\n",
+ node->key.specified);
}
}
@@ -1001,6 +1026,7 @@ void security_compute_xperms_decision(u32 ssid,
u32 tsid,
u16 orig_tclass,
u8 driver,
+ u8 base_perm,
struct extended_perms_decision *xpermd)
{
struct selinux_policy *policy;
@@ -1014,6 +1040,7 @@ void security_compute_xperms_decision(u32 ssid,
struct ebitmap_node *snode, *tnode;
unsigned int i, j;
+ xpermd->base_perm = base_perm;
xpermd->driver = driver;
xpermd->used = 0;
memset(xpermd->allowed->p, 0, sizeof(xpermd->allowed->p));
@@ -1805,22 +1832,9 @@ retry:
newcontext.role = OBJECT_R_VAL;
}
- /* Set the type to default values. */
- if (cladatum && cladatum->default_type == DEFAULT_SOURCE) {
- newcontext.type = scontext->type;
- } else if (cladatum && cladatum->default_type == DEFAULT_TARGET) {
- newcontext.type = tcontext->type;
- } else {
- if ((tclass == policydb->process_class) || sock) {
- /* Use the type of process. */
- newcontext.type = scontext->type;
- } else {
- /* Use the type of the related object. */
- newcontext.type = tcontext->type;
- }
- }
-
- /* Look for a type transition/member/change rule. */
+ /* Set the type.
+ * Look for a type transition/member/change rule.
+ */
avkey.source_type = scontext->type;
avkey.target_type = tcontext->type;
avkey.target_class = tclass;
@@ -1838,9 +1852,24 @@ retry:
}
}
+ /* If a permanent rule is found, use the type from
+ * the type transition/member/change rule. Otherwise,
+ * set the type to its default values.
+ */
if (avnode) {
- /* Use the type from the type transition/member/change rule. */
newcontext.type = avnode->datum.u.data;
+ } else if (cladatum && cladatum->default_type == DEFAULT_SOURCE) {
+ newcontext.type = scontext->type;
+ } else if (cladatum && cladatum->default_type == DEFAULT_TARGET) {
+ newcontext.type = tcontext->type;
+ } else {
+ if ((tclass == policydb->process_class) || sock) {
+ /* Use the type of process. */
+ newcontext.type = scontext->type;
+ } else {
+ /* Use the type of the related object. */
+ newcontext.type = tcontext->type;
+ }
}
/* if we have a objname this is a file trans check so check those rules */
@@ -2586,17 +2615,15 @@ out:
return rc;
}
-static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask)
+static bool match_ipv6_addrmask(const u32 input[4], const u32 addr[4], const u32 mask[4])
{
- int i, fail = 0;
+ int i;
for (i = 0; i < 4; i++)
- if (addr[i] != (input[i] & mask[i])) {
- fail = 1;
- break;
- }
+ if (addr[i] != (input[i] & mask[i]))
+ return false;
- return !fail;
+ return true;
}
/**
@@ -2701,7 +2728,7 @@ out:
*/
int security_get_user_sids(u32 fromsid,
- char *username,
+ const char *username,
u32 **sids,
u32 *nel)
{
@@ -3023,7 +3050,7 @@ err:
}
-int security_set_bools(u32 len, int *values)
+int security_set_bools(u32 len, const int *values)
{
struct selinux_state *state = &selinux_state;
struct selinux_policy *newpolicy, *oldpolicy;
@@ -3322,7 +3349,7 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
__func__, xfrm_sid);
goto out;
}
- rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES);
+ rc = (mls_context_equal(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES);
if (rc)
goto out;
@@ -3508,7 +3535,8 @@ void selinux_audit_rule_free(void *vrule)
}
}
-int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
+int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule,
+ gfp_t gfp)
{
struct selinux_state *state = &selinux_state;
struct selinux_policy *policy;
@@ -3549,7 +3577,7 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
return -EINVAL;
}
- tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL);
+ tmprule = kzalloc(sizeof(struct selinux_audit_rule), gfp);
if (!tmprule)
return -ENOMEM;
context_init(&tmprule->au_ctxt);
@@ -3633,7 +3661,7 @@ int selinux_audit_rule_known(struct audit_krule *rule)
return 0;
}
-int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule)
+int selinux_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op, void *vrule)
{
struct selinux_state *state = &selinux_state;
struct selinux_policy *policy;
@@ -3659,10 +3687,10 @@ int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule)
goto out;
}
- ctxt = sidtab_search(policy->sidtab, sid);
+ ctxt = sidtab_search(policy->sidtab, prop->selinux.secid);
if (unlikely(!ctxt)) {
WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n",
- sid);
+ prop->selinux.secid);
match = -ENOENT;
goto out;
}
diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h
index d24b0a3d198e..93358e7a649c 100644
--- a/security/selinux/ss/services.h
+++ b/security/selinux/ss/services.h
@@ -4,6 +4,7 @@
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
+
#ifndef _SS_SERVICES_H_
#define _SS_SERVICES_H_
@@ -43,4 +44,4 @@ int services_convert_context(struct convert_context_args *args,
struct context *oldc, struct context *newc,
gfp_t gfp_flags);
-#endif /* _SS_SERVICES_H_ */
+#endif /* _SS_SERVICES_H_ */
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
index 732fd8e22a12..59f8c09158ef 100644
--- a/security/selinux/ss/sidtab.c
+++ b/security/selinux/ss/sidtab.c
@@ -7,6 +7,7 @@
*
* Copyright (C) 2018 Red Hat, Inc.
*/
+
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -29,7 +30,7 @@ struct sidtab_str_cache {
};
#define index_to_sid(index) ((index) + SECINITSID_NUM + 1)
-#define sid_to_index(sid) ((sid) - (SECINITSID_NUM + 1))
+#define sid_to_index(sid) ((sid) - (SECINITSID_NUM + 1))
int sidtab_init(struct sidtab *s)
{
@@ -65,7 +66,7 @@ static u32 context_to_sid(struct sidtab *s, struct context *context, u32 hash)
hash_for_each_possible_rcu(s->context_to_sid, entry, list, hash) {
if (entry->hash != hash)
continue;
- if (context_cmp(&entry->context, context)) {
+ if (context_equal(&entry->context, context)) {
sid = entry->sid;
break;
}
@@ -113,12 +114,12 @@ int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
int sidtab_hash_stats(struct sidtab *sidtab, char *page)
{
- int i;
+ unsigned int i;
int chain_len = 0;
int slots_used = 0;
int entries = 0;
int max_chain_len = 0;
- int cur_bucket = 0;
+ unsigned int cur_bucket = 0;
struct sidtab_entry *entry;
rcu_read_lock();
@@ -140,9 +141,11 @@ int sidtab_hash_stats(struct sidtab *sidtab, char *page)
if (chain_len > max_chain_len)
max_chain_len = chain_len;
- return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
- "longest chain: %d\n", entries,
- slots_used, SIDTAB_HASH_BUCKETS, max_chain_len);
+ return scnprintf(page, PAGE_SIZE,
+ "entries: %d\nbuckets used: %d/%d\n"
+ "longest chain: %d\n",
+ entries, slots_used, SIDTAB_HASH_BUCKETS,
+ max_chain_len);
}
static u32 sidtab_level_from_count(u32 count)
@@ -162,15 +165,15 @@ static int sidtab_alloc_roots(struct sidtab *s, u32 level)
u32 l;
if (!s->roots[0].ptr_leaf) {
- s->roots[0].ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
- GFP_ATOMIC);
+ s->roots[0].ptr_leaf =
+ kzalloc(SIDTAB_NODE_ALLOC_SIZE, GFP_ATOMIC);
if (!s->roots[0].ptr_leaf)
return -ENOMEM;
}
for (l = 1; l <= level; ++l)
if (!s->roots[l].ptr_inner) {
- s->roots[l].ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
- GFP_ATOMIC);
+ s->roots[l].ptr_inner =
+ kzalloc(SIDTAB_NODE_ALLOC_SIZE, GFP_ATOMIC);
if (!s->roots[l].ptr_inner)
return -ENOMEM;
s->roots[l].ptr_inner->entries[0] = s->roots[l - 1];
@@ -203,16 +206,16 @@ static struct sidtab_entry *sidtab_do_lookup(struct sidtab *s, u32 index,
if (!entry->ptr_inner) {
if (alloc)
- entry->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
- GFP_ATOMIC);
+ entry->ptr_inner = kzalloc(
+ SIDTAB_NODE_ALLOC_SIZE, GFP_ATOMIC);
if (!entry->ptr_inner)
return NULL;
}
}
if (!entry->ptr_leaf) {
if (alloc)
- entry->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
- GFP_ATOMIC);
+ entry->ptr_leaf =
+ kzalloc(SIDTAB_NODE_ALLOC_SIZE, GFP_ATOMIC);
if (!entry->ptr_leaf)
return NULL;
}
@@ -262,8 +265,7 @@ struct sidtab_entry *sidtab_search_entry_force(struct sidtab *s, u32 sid)
return sidtab_search_core(s, sid, 1);
}
-int sidtab_context_to_sid(struct sidtab *s, struct context *context,
- u32 *sid)
+int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid)
{
unsigned long flags;
u32 count, hash = context_compute_hash(context);
@@ -327,8 +329,8 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,
goto out_unlock;
}
- rc = services_convert_context(convert->args,
- context, &dst_convert->context,
+ rc = services_convert_context(convert->args, context,
+ &dst_convert->context,
GFP_ATOMIC);
if (rc) {
context_destroy(&dst->context);
@@ -338,8 +340,8 @@ int sidtab_context_to_sid(struct sidtab *s, struct context *context,
dst_convert->hash = context_compute_hash(&dst_convert->context);
target->count = count + 1;
- hash_add_rcu(target->context_to_sid,
- &dst_convert->list, dst_convert->hash);
+ hash_add_rcu(target->context_to_sid, &dst_convert->list,
+ dst_convert->hash);
}
if (context->len)
@@ -373,8 +375,8 @@ static void sidtab_convert_hashtable(struct sidtab *s, u32 count)
}
static int sidtab_convert_tree(union sidtab_entry_inner *edst,
- union sidtab_entry_inner *esrc,
- u32 *pos, u32 count, u32 level,
+ union sidtab_entry_inner *esrc, u32 *pos,
+ u32 count, u32 level,
struct sidtab_convert_params *convert)
{
int rc;
@@ -382,8 +384,8 @@ static int sidtab_convert_tree(union sidtab_entry_inner *edst,
if (level != 0) {
if (!edst->ptr_inner) {
- edst->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
- GFP_KERNEL);
+ edst->ptr_inner =
+ kzalloc(SIDTAB_NODE_ALLOC_SIZE, GFP_KERNEL);
if (!edst->ptr_inner)
return -ENOMEM;
}
@@ -399,17 +401,18 @@ static int sidtab_convert_tree(union sidtab_entry_inner *edst,
}
} else {
if (!edst->ptr_leaf) {
- edst->ptr_leaf = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
- GFP_KERNEL);
+ edst->ptr_leaf =
+ kzalloc(SIDTAB_NODE_ALLOC_SIZE, GFP_KERNEL);
if (!edst->ptr_leaf)
return -ENOMEM;
}
i = 0;
while (i < SIDTAB_LEAF_ENTRIES && *pos < count) {
- rc = services_convert_context(convert->args,
- &esrc->ptr_leaf->entries[i].context,
- &edst->ptr_leaf->entries[i].context,
- GFP_KERNEL);
+ rc = services_convert_context(
+ convert->args,
+ &esrc->ptr_leaf->entries[i].context,
+ &edst->ptr_leaf->entries[i].context,
+ GFP_KERNEL);
if (rc)
return rc;
(*pos)++;
@@ -489,13 +492,15 @@ void sidtab_cancel_convert(struct sidtab *s)
spin_unlock_irqrestore(&s->lock, flags);
}
-void sidtab_freeze_begin(struct sidtab *s, unsigned long *flags) __acquires(&s->lock)
+void sidtab_freeze_begin(struct sidtab *s, unsigned long *flags)
+ __acquires(&s->lock)
{
spin_lock_irqsave(&s->lock, *flags);
s->frozen = true;
s->convert = NULL;
}
-void sidtab_freeze_end(struct sidtab *s, unsigned long *flags) __releases(&s->lock)
+void sidtab_freeze_end(struct sidtab *s, unsigned long *flags)
+ __releases(&s->lock)
{
spin_unlock_irqrestore(&s->lock, *flags);
}
@@ -600,8 +605,8 @@ out_unlock:
kfree_rcu(victim, rcu_member);
}
-int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry,
- char **out, u32 *out_len)
+int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry, char **out,
+ u32 *out_len)
{
struct sidtab_str_cache *cache;
int rc = 0;
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
index 22258201cd14..832c85c70d83 100644
--- a/security/selinux/ss/sidtab.h
+++ b/security/selinux/ss/sidtab.h
@@ -8,6 +8,7 @@
*
* Copyright (C) 2018 Red Hat, Inc.
*/
+
#ifndef _SS_SIDTAB_H_
#define _SS_SIDTAB_H_
@@ -29,25 +30,26 @@ struct sidtab_entry {
union sidtab_entry_inner {
struct sidtab_node_inner *ptr_inner;
- struct sidtab_node_leaf *ptr_leaf;
+ struct sidtab_node_leaf *ptr_leaf;
};
/* align node size to page boundary */
#define SIDTAB_NODE_ALLOC_SHIFT PAGE_SHIFT
-#define SIDTAB_NODE_ALLOC_SIZE PAGE_SIZE
+#define SIDTAB_NODE_ALLOC_SIZE PAGE_SIZE
-#define size_to_shift(size) ((size) == 1 ? 1 : (const_ilog2((size) - 1) + 1))
+#define size_to_shift(size) ((size) == 1 ? 1 : (const_ilog2((size)-1) + 1))
-#define SIDTAB_INNER_SHIFT \
- (SIDTAB_NODE_ALLOC_SHIFT - size_to_shift(sizeof(union sidtab_entry_inner)))
+#define SIDTAB_INNER_SHIFT \
+ (SIDTAB_NODE_ALLOC_SHIFT - \
+ size_to_shift(sizeof(union sidtab_entry_inner)))
#define SIDTAB_INNER_ENTRIES ((size_t)1 << SIDTAB_INNER_SHIFT)
#define SIDTAB_LEAF_ENTRIES \
(SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry))
#define SIDTAB_MAX_BITS 32
-#define SIDTAB_MAX U32_MAX
+#define SIDTAB_MAX U32_MAX
/* ensure enough tree levels for SIDTAB_MAX entries */
-#define SIDTAB_MAX_LEVEL \
+#define SIDTAB_MAX_LEVEL \
DIV_ROUND_UP(SIDTAB_MAX_BITS - size_to_shift(SIDTAB_LEAF_ENTRIES), \
SIDTAB_INNER_SHIFT)
@@ -69,7 +71,7 @@ struct sidtab_convert_params {
struct sidtab *target;
};
-#define SIDTAB_HASH_BITS CONFIG_SECURITY_SELINUX_SIDTAB_HASH_BITS
+#define SIDTAB_HASH_BITS CONFIG_SECURITY_SELINUX_SIDTAB_HASH_BITS
#define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS)
struct sidtab {
@@ -125,8 +127,10 @@ int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params);
void sidtab_cancel_convert(struct sidtab *s);
-void sidtab_freeze_begin(struct sidtab *s, unsigned long *flags) __acquires(&s->lock);
-void sidtab_freeze_end(struct sidtab *s, unsigned long *flags) __releases(&s->lock);
+void sidtab_freeze_begin(struct sidtab *s, unsigned long *flags)
+ __acquires(&s->lock);
+void sidtab_freeze_end(struct sidtab *s, unsigned long *flags)
+ __releases(&s->lock);
int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid);
@@ -137,8 +141,8 @@ int sidtab_hash_stats(struct sidtab *sidtab, char *page);
#if CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0
void sidtab_sid2str_put(struct sidtab *s, struct sidtab_entry *entry,
const char *str, u32 str_len);
-int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry,
- char **out, u32 *out_len);
+int sidtab_sid2str_get(struct sidtab *s, struct sidtab_entry *entry, char **out,
+ u32 *out_len);
#else
static inline void sidtab_sid2str_put(struct sidtab *s,
struct sidtab_entry *entry,
@@ -146,13 +150,11 @@ static inline void sidtab_sid2str_put(struct sidtab *s,
{
}
static inline int sidtab_sid2str_get(struct sidtab *s,
- struct sidtab_entry *entry,
- char **out, u32 *out_len)
+ struct sidtab_entry *entry, char **out,
+ u32 *out_len)
{
return -ENOENT;
}
#endif /* CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE > 0 */
-#endif /* _SS_SIDTAB_H_ */
-
-
+#endif /* _SS_SIDTAB_H_ */
diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c
index 43d7f0319ccd..832660fd84a9 100644
--- a/security/selinux/ss/symtab.c
+++ b/security/selinux/ss/symtab.c
@@ -4,6 +4,7 @@
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
+
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
@@ -11,16 +12,17 @@
static unsigned int symhash(const void *key)
{
- const char *p, *keyp;
- unsigned int size;
- unsigned int val;
-
- val = 0;
- keyp = key;
- size = strlen(keyp);
- for (p = keyp; (p - keyp) < size; p++)
- val = (val << 4 | (val >> (8*sizeof(unsigned int)-4))) ^ (*p);
- return val;
+ /*
+ * djb2a
+ * Public domain from cdb v0.75
+ */
+ unsigned int hash = 5381;
+ unsigned char c;
+
+ while ((c = *(const unsigned char *)key++))
+ hash = ((hash << 5) + hash) ^ c;
+
+ return hash;
}
static int symcmp(const void *key1, const void *key2)
diff --git a/security/selinux/ss/symtab.h b/security/selinux/ss/symtab.h
index 0a3b5de79a0f..8e667cdbf38f 100644
--- a/security/selinux/ss/symtab.h
+++ b/security/selinux/ss/symtab.h
@@ -7,14 +7,15 @@
*
* Author : Stephen Smalley, <stephen.smalley.work@gmail.com>
*/
+
#ifndef _SS_SYMTAB_H_
#define _SS_SYMTAB_H_
#include "hashtab.h"
struct symtab {
- struct hashtab table; /* hash table (keyed on a string) */
- u32 nprim; /* number of primary names in table */
+ struct hashtab table; /* hash table (keyed on a string) */
+ u32 nprim; /* number of primary names in table */
};
int symtab_init(struct symtab *s, u32 size);
@@ -22,6 +23,4 @@ int symtab_init(struct symtab *s, u32 size);
int symtab_insert(struct symtab *s, char *name, void *datum);
void *symtab_search(struct symtab *s, const char *name);
-#endif /* _SS_SYMTAB_H_ */
-
-
+#endif /* _SS_SYMTAB_H_ */
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 95fcd2d3433e..90ec4ef1b082 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -76,7 +76,6 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
gfp_t gfp)
{
int rc;
- const struct task_security_struct *tsec = selinux_cred(current_cred());
struct xfrm_sec_ctx *ctx = NULL;
u32 str_len;
@@ -103,7 +102,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp,
if (rc)
goto err;
- rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ rc = avc_has_perm(current_sid(), ctx->ctx_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, NULL);
if (rc)
goto err;
@@ -134,12 +133,10 @@ static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx)
*/
static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx)
{
- const struct task_security_struct *tsec = selinux_cred(current_cred());
-
if (!ctx)
return 0;
- return avc_has_perm(tsec->sid, ctx->ctx_sid,
+ return avc_has_perm(current_sid(), ctx->ctx_sid,
SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT,
NULL);
}
diff --git a/security/smack/smack.h b/security/smack/smack.h
index 041688e5a77a..4608b07607a3 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -280,6 +280,7 @@ int smk_access(struct smack_known *, struct smack_known *,
int smk_tskacc(struct task_smack *, struct smack_known *,
u32, struct smk_audit_info *);
int smk_curacc(struct smack_known *, u32, struct smk_audit_info *);
+int smack_str_from_perm(char *string, int access);
struct smack_known *smack_from_secid(const u32);
char *smk_parse_smack(const char *string, int len);
int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int);
@@ -355,6 +356,18 @@ static inline struct superblock_smack *smack_superblock(
return superblock->s_security + smack_blob_sizes.lbs_superblock;
}
+static inline struct socket_smack *smack_sock(const struct sock *sock)
+{
+ return sock->sk_security + smack_blob_sizes.lbs_sock;
+}
+
+#ifdef CONFIG_KEYS
+static inline struct smack_known **smack_key(const struct key *key)
+{
+ return key->security + smack_blob_sizes.lbs_key;
+}
+#endif /* CONFIG_KEYS */
+
/*
* Is the directory transmuting?
*/
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 585e5e35710b..3727379623e2 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -275,7 +275,6 @@ int smk_curacc(struct smack_known *obj_known,
return smk_tskacc(tsp, obj_known, mode, a);
}
-#ifdef CONFIG_AUDIT
/**
* smack_str_from_perm : helper to transalate an int to a
* readable string
@@ -283,7 +282,7 @@ int smk_curacc(struct smack_known *obj_known,
* @access : the int
*
*/
-static inline void smack_str_from_perm(char *string, int access)
+int smack_str_from_perm(char *string, int access)
{
int i = 0;
@@ -299,8 +298,15 @@ static inline void smack_str_from_perm(char *string, int access)
string[i++] = 't';
if (access & MAY_LOCK)
string[i++] = 'l';
+ if (access & MAY_BRINGUP)
+ string[i++] = 'b';
+ if (i == 0)
+ string[i++] = '-';
string[i] = '\0';
+ return i;
}
+
+#ifdef CONFIG_AUDIT
/**
* smack_log_callback - SMACK specific information
* will be called by generic audit code
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 0fdbf04cc258..239773cdcdcf 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -107,23 +107,7 @@ static char *smk_bu_mess[] = {
static void smk_bu_mode(int mode, char *s)
{
- int i = 0;
-
- if (mode & MAY_READ)
- s[i++] = 'r';
- if (mode & MAY_WRITE)
- s[i++] = 'w';
- if (mode & MAY_EXEC)
- s[i++] = 'x';
- if (mode & MAY_APPEND)
- s[i++] = 'a';
- if (mode & MAY_TRANSMUTE)
- s[i++] = 't';
- if (mode & MAY_LOCK)
- s[i++] = 'l';
- if (i == 0)
- s[i++] = '-';
- s[i] = '\0';
+ smack_str_from_perm(s, mode);
}
#endif
@@ -994,57 +978,62 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
struct xattr *xattrs, int *xattr_count)
{
struct task_smack *tsp = smack_cred(current_cred());
+ struct inode_smack *issp = smack_inode(inode);
struct smack_known *skp = smk_of_task(tsp);
struct smack_known *isp = smk_of_inode(inode);
struct smack_known *dsp = smk_of_inode(dir);
struct xattr *xattr = lsm_get_xattr_slot(xattrs, xattr_count);
int may;
- if (xattr) {
- /*
- * If equal, transmuting already occurred in
- * smack_dentry_create_files_as(). No need to check again.
- */
- if (tsp->smk_task != tsp->smk_transmuted) {
- rcu_read_lock();
- may = smk_access_entry(skp->smk_known, dsp->smk_known,
- &skp->smk_rules);
- rcu_read_unlock();
- }
+ /*
+ * If equal, transmuting already occurred in
+ * smack_dentry_create_files_as(). No need to check again.
+ */
+ if (tsp->smk_task != tsp->smk_transmuted) {
+ rcu_read_lock();
+ may = smk_access_entry(skp->smk_known, dsp->smk_known,
+ &skp->smk_rules);
+ rcu_read_unlock();
+ }
+
+ /*
+ * In addition to having smk_task equal to smk_transmuted,
+ * if the access rule allows transmutation and the directory
+ * requests transmutation then by all means transmute.
+ * Mark the inode as changed.
+ */
+ if ((tsp->smk_task == tsp->smk_transmuted) ||
+ (may > 0 && ((may & MAY_TRANSMUTE) != 0) &&
+ smk_inode_transmutable(dir))) {
+ struct xattr *xattr_transmute;
/*
- * In addition to having smk_task equal to smk_transmuted,
- * if the access rule allows transmutation and the directory
- * requests transmutation then by all means transmute.
- * Mark the inode as changed.
+ * The caller of smack_dentry_create_files_as()
+ * should have overridden the current cred, so the
+ * inode label was already set correctly in
+ * smack_inode_alloc_security().
*/
- if ((tsp->smk_task == tsp->smk_transmuted) ||
- (may > 0 && ((may & MAY_TRANSMUTE) != 0) &&
- smk_inode_transmutable(dir))) {
- struct xattr *xattr_transmute;
+ if (tsp->smk_task != tsp->smk_transmuted)
+ isp = issp->smk_inode = dsp;
+
+ issp->smk_flags |= SMK_INODE_TRANSMUTE;
+ xattr_transmute = lsm_get_xattr_slot(xattrs,
+ xattr_count);
+ if (xattr_transmute) {
+ xattr_transmute->value = kmemdup(TRANS_TRUE,
+ TRANS_TRUE_SIZE,
+ GFP_NOFS);
+ if (!xattr_transmute->value)
+ return -ENOMEM;
- /*
- * The caller of smack_dentry_create_files_as()
- * should have overridden the current cred, so the
- * inode label was already set correctly in
- * smack_inode_alloc_security().
- */
- if (tsp->smk_task != tsp->smk_transmuted)
- isp = dsp;
- xattr_transmute = lsm_get_xattr_slot(xattrs,
- xattr_count);
- if (xattr_transmute) {
- xattr_transmute->value = kmemdup(TRANS_TRUE,
- TRANS_TRUE_SIZE,
- GFP_NOFS);
- if (!xattr_transmute->value)
- return -ENOMEM;
-
- xattr_transmute->value_len = TRANS_TRUE_SIZE;
- xattr_transmute->name = XATTR_SMACK_TRANSMUTE;
- }
+ xattr_transmute->value_len = TRANS_TRUE_SIZE;
+ xattr_transmute->name = XATTR_SMACK_TRANSMUTE;
}
+ }
+ issp->smk_flags |= SMK_INODE_INSTANT;
+
+ if (xattr) {
xattr->value = kstrdup(isp->smk_known, GFP_NOFS);
if (!xattr->value)
return -ENOMEM;
@@ -1233,12 +1222,14 @@ static int smack_inode_permission(struct inode *inode, int mask)
/**
* smack_inode_setattr - Smack check for setting attributes
+ * @idmap: idmap of the mount
* @dentry: the object
* @iattr: for the force flag
*
* Returns 0 if access is permitted, an error code otherwise
*/
-static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+static int smack_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+ struct iattr *iattr)
{
struct smk_audit_info ad;
int rc;
@@ -1276,6 +1267,33 @@ static int smack_inode_getattr(const struct path *path)
}
/**
+ * smack_inode_xattr_skipcap - Skip the xattr capability checks?
+ * @name: name of the xattr
+ *
+ * Returns 1 to indicate that Smack "owns" the access control rights to xattrs
+ * named @name; the LSM layer should avoid enforcing any traditional
+ * capability based access controls on this xattr. Returns 0 to indicate that
+ * Smack does not "own" the access control rights to xattrs named @name and is
+ * deferring to the LSM layer for further access controls, including capability
+ * based controls.
+ */
+static int smack_inode_xattr_skipcap(const char *name)
+{
+ if (strncmp(name, XATTR_SMACK_SUFFIX, strlen(XATTR_SMACK_SUFFIX)))
+ return 0;
+
+ if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKMMAP) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0)
+ return 1;
+
+ return 0;
+}
+
+/**
* smack_inode_setxattr - Smack check for setting xattrs
* @idmap: idmap of the mount
* @dentry: the object
@@ -1314,11 +1332,11 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
check_star = 1;
} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
check_priv = 1;
- if (size != TRANS_TRUE_SIZE ||
+ if (!S_ISDIR(d_backing_inode(dentry)->i_mode) ||
+ size != TRANS_TRUE_SIZE ||
strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
rc = -EINVAL;
- } else
- rc = cap_inode_setxattr(dentry, name, value, size, flags);
+ }
if (check_priv && !smack_privileged(CAP_MAC_ADMIN))
rc = -EPERM;
@@ -1427,8 +1445,7 @@ static int smack_inode_removexattr(struct mnt_idmap *idmap,
strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
if (!smack_privileged(CAP_MAC_ADMIN))
rc = -EPERM;
- } else
- rc = cap_inode_removexattr(idmap, dentry, name);
+ }
if (rc != 0)
return rc;
@@ -1573,7 +1590,7 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap,
if (sock == NULL || sock->sk == NULL)
return -EOPNOTSUPP;
- ssp = sock->sk->sk_security;
+ ssp = smack_sock(sock->sk);
if (strcmp(name, XATTR_SMACK_IPIN) == 0)
isp = ssp->smk_in;
@@ -1616,15 +1633,13 @@ static int smack_inode_listsecurity(struct inode *inode, char *buffer,
}
/**
- * smack_inode_getsecid - Extract inode's security id
+ * smack_inode_getlsmprop - Extract inode's security id
* @inode: inode to extract the info from
- * @secid: where result will be saved
+ * @prop: where result will be saved
*/
-static void smack_inode_getsecid(struct inode *inode, u32 *secid)
+static void smack_inode_getlsmprop(struct inode *inode, struct lsm_prop *prop)
{
- struct smack_known *skp = smk_of_inode(inode);
-
- *secid = skp->smk_secid;
+ prop->smack.skp = smk_of_inode(inode);
}
/*
@@ -1917,7 +1932,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk,
/*
* struct fown_struct is never outside the context of a struct file
*/
- file = container_of(fown, struct file, f_owner);
+ file = fown->file;
/* we don't log here as rc can be overriden */
blob = smack_file(file);
@@ -1961,7 +1976,7 @@ static int smack_file_receive(struct file *file)
if (inode->i_sb->s_magic == SOCKFS_MAGIC) {
sock = SOCKET_I(inode);
- ssp = sock->sk->sk_security;
+ ssp = smack_sock(sock->sk);
tsp = smack_cred(current_cred());
/*
* If the receiving process can't write to the
@@ -2095,12 +2110,7 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old)
struct task_smack *old_tsp = smack_cred(old);
struct task_smack *new_tsp = smack_cred(new);
- new_tsp->smk_task = old_tsp->smk_task;
- new_tsp->smk_forked = old_tsp->smk_task;
- mutex_init(&new_tsp->smk_rules_lock);
- INIT_LIST_HEAD(&new_tsp->smk_rules);
-
- /* cbs copy rule list */
+ init_task_smack(new_tsp, old_tsp->smk_task, old_tsp->smk_task);
}
/**
@@ -2121,6 +2131,21 @@ static void smack_cred_getsecid(const struct cred *cred, u32 *secid)
}
/**
+ * smack_cred_getlsmprop - get the Smack label for a creds structure
+ * @cred: the object creds
+ * @prop: where to put the data
+ *
+ * Sets the Smack part of the ref
+ */
+static void smack_cred_getlsmprop(const struct cred *cred,
+ struct lsm_prop *prop)
+{
+ rcu_read_lock();
+ prop->smack.skp = smk_of_task(smack_cred(cred));
+ rcu_read_unlock();
+}
+
+/**
* smack_kernel_act_as - Set the subjective context in a set of credentials
* @new: points to the set of credentials to be modified.
* @secid: specifies the security ID to be set
@@ -2211,30 +2236,27 @@ static int smack_task_getsid(struct task_struct *p)
}
/**
- * smack_current_getsecid_subj - get the subjective secid of the current task
- * @secid: where to put the result
+ * smack_current_getlsmprop_subj - get the subjective secid of the current task
+ * @prop: where to put the result
*
* Sets the secid to contain a u32 version of the task's subjective smack label.
*/
-static void smack_current_getsecid_subj(u32 *secid)
+static void smack_current_getlsmprop_subj(struct lsm_prop *prop)
{
- struct smack_known *skp = smk_of_current();
-
- *secid = skp->smk_secid;
+ prop->smack.skp = smk_of_current();
}
/**
- * smack_task_getsecid_obj - get the objective secid of the task
+ * smack_task_getlsmprop_obj - get the objective data of the task
* @p: the task
- * @secid: where to put the result
+ * @prop: where to put the result
*
* Sets the secid to contain a u32 version of the task's objective smack label.
*/
-static void smack_task_getsecid_obj(struct task_struct *p, u32 *secid)
+static void smack_task_getlsmprop_obj(struct task_struct *p,
+ struct lsm_prop *prop)
{
- struct smack_known *skp = smk_of_task_struct_obj(p);
-
- *secid = skp->smk_secid;
+ prop->smack.skp = smk_of_task_struct_obj(p);
}
/**
@@ -2381,11 +2403,7 @@ static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
{
struct smack_known *skp = smk_of_current();
- struct socket_smack *ssp;
-
- ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
- if (ssp == NULL)
- return -ENOMEM;
+ struct socket_smack *ssp = smack_sock(sk);
/*
* Sockets created by kernel threads receive web label.
@@ -2399,11 +2417,10 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
}
ssp->smk_packet = NULL;
- sk->sk_security = ssp;
-
return 0;
}
+#ifdef SMACK_IPV6_PORT_LABELING
/**
* smack_sk_free_security - Free a socket blob
* @sk: the socket
@@ -2412,7 +2429,6 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
*/
static void smack_sk_free_security(struct sock *sk)
{
-#ifdef SMACK_IPV6_PORT_LABELING
struct smk_port_label *spp;
if (sk->sk_family == PF_INET6) {
@@ -2425,9 +2441,8 @@ static void smack_sk_free_security(struct sock *sk)
}
rcu_read_unlock();
}
-#endif
- kfree(sk->sk_security);
}
+#endif
/**
* smack_sk_clone_security - Copy security context
@@ -2438,8 +2453,8 @@ static void smack_sk_free_security(struct sock *sk)
*/
static void smack_sk_clone_security(const struct sock *sk, struct sock *newsk)
{
- struct socket_smack *ssp_old = sk->sk_security;
- struct socket_smack *ssp_new = newsk->sk_security;
+ struct socket_smack *ssp_old = smack_sock(sk);
+ struct socket_smack *ssp_new = smack_sock(newsk);
*ssp_new = *ssp_old;
}
@@ -2555,14 +2570,15 @@ static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
*/
static int smack_netlbl_add(struct sock *sk)
{
- struct socket_smack *ssp = sk->sk_security;
+ struct socket_smack *ssp = smack_sock(sk);
struct smack_known *skp = ssp->smk_out;
int rc;
local_bh_disable();
bh_lock_sock_nested(sk);
- rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel);
+ rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel,
+ netlbl_sk_lock_check(sk));
switch (rc) {
case 0:
ssp->smk_state = SMK_NETLBL_LABELED;
@@ -2587,7 +2603,7 @@ static int smack_netlbl_add(struct sock *sk)
*/
static void smack_netlbl_delete(struct sock *sk)
{
- struct socket_smack *ssp = sk->sk_security;
+ struct socket_smack *ssp = smack_sock(sk);
/*
* Take the label off the socket if one is set.
@@ -2619,7 +2635,7 @@ static int smk_ipv4_check(struct sock *sk, struct sockaddr_in *sap)
struct smack_known *skp;
int rc = 0;
struct smack_known *hkp;
- struct socket_smack *ssp = sk->sk_security;
+ struct socket_smack *ssp = smack_sock(sk);
struct smk_audit_info ad;
rcu_read_lock();
@@ -2692,7 +2708,7 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
{
struct sock *sk = sock->sk;
struct sockaddr_in6 *addr6;
- struct socket_smack *ssp = sock->sk->sk_security;
+ struct socket_smack *ssp = smack_sock(sock->sk);
struct smk_port_label *spp;
unsigned short port = 0;
@@ -2780,7 +2796,7 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
int act)
{
struct smk_port_label *spp;
- struct socket_smack *ssp = sk->sk_security;
+ struct socket_smack *ssp = smack_sock(sk);
struct smack_known *skp = NULL;
unsigned short port;
struct smack_known *object;
@@ -2855,6 +2871,15 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
if (value == NULL || size > SMK_LONGLABEL || size == 0)
return -EINVAL;
+ if (strcmp(name, XATTR_SMACK_TRANSMUTE) == 0) {
+ if (!S_ISDIR(inode->i_mode) || size != TRANS_TRUE_SIZE ||
+ strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
+ return -EINVAL;
+
+ nsp->smk_flags |= SMK_INODE_TRANSMUTE;
+ return 0;
+ }
+
skp = smk_import_entry(value, size);
if (IS_ERR(skp))
return PTR_ERR(skp);
@@ -2874,7 +2899,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
if (sock == NULL || sock->sk == NULL)
return -EOPNOTSUPP;
- ssp = sock->sk->sk_security;
+ ssp = smack_sock(sock->sk);
if (strcmp(name, XATTR_SMACK_IPIN) == 0)
ssp->smk_in = skp;
@@ -2922,7 +2947,7 @@ static int smack_socket_post_create(struct socket *sock, int family,
* Sockets created by kernel threads receive web label.
*/
if (unlikely(current->flags & PF_KTHREAD)) {
- ssp = sock->sk->sk_security;
+ ssp = smack_sock(sock->sk);
ssp->smk_in = &smack_known_web;
ssp->smk_out = &smack_known_web;
}
@@ -2947,8 +2972,8 @@ static int smack_socket_post_create(struct socket *sock, int family,
static int smack_socket_socketpair(struct socket *socka,
struct socket *sockb)
{
- struct socket_smack *asp = socka->sk->sk_security;
- struct socket_smack *bsp = sockb->sk->sk_security;
+ struct socket_smack *asp = smack_sock(socka->sk);
+ struct socket_smack *bsp = smack_sock(sockb->sk);
asp->smk_packet = bsp->smk_out;
bsp->smk_packet = asp->smk_out;
@@ -3011,7 +3036,7 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
if (__is_defined(SMACK_IPV6_SECMARK_LABELING))
rsp = smack_ipv6host_label(sip);
if (rsp != NULL) {
- struct socket_smack *ssp = sock->sk->sk_security;
+ struct socket_smack *ssp = smack_sock(sock->sk);
rc = smk_ipv6_check(ssp->smk_out, rsp, sip,
SMK_CONNECTING);
@@ -3404,16 +3429,15 @@ static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag)
}
/**
- * smack_ipc_getsecid - Extract smack security id
+ * smack_ipc_getlsmprop - Extract smack security data
* @ipp: the object permissions
- * @secid: where result will be saved
+ * @prop: where result will be saved
*/
-static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid)
+static void smack_ipc_getlsmprop(struct kern_ipc_perm *ipp, struct lsm_prop *prop)
{
- struct smack_known **blob = smack_ipc(ipp);
- struct smack_known *iskp = *blob;
+ struct smack_known **iskpp = smack_ipc(ipp);
- *secid = iskp->smk_secid;
+ prop->smack.skp = *iskpp;
}
/**
@@ -3641,7 +3665,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
* There will only ever be one attribute.
*/
static int smack_getselfattr(unsigned int attr, struct lsm_ctx __user *ctx,
- size_t *size, u32 flags)
+ u32 *size, u32 flags)
{
int rc;
struct smack_known *skp;
@@ -3762,7 +3786,7 @@ static int do_setattr(u64 attr, void *value, size_t size)
* Returns 0 on success, an error code otherwise.
*/
static int smack_setselfattr(unsigned int attr, struct lsm_ctx *ctx,
- size_t size, u32 flags)
+ u32 size, u32 flags)
{
int rc;
@@ -3806,9 +3830,9 @@ static int smack_unix_stream_connect(struct sock *sock,
{
struct smack_known *skp;
struct smack_known *okp;
- struct socket_smack *ssp = sock->sk_security;
- struct socket_smack *osp = other->sk_security;
- struct socket_smack *nsp = newsk->sk_security;
+ struct socket_smack *ssp = smack_sock(sock);
+ struct socket_smack *osp = smack_sock(other);
+ struct socket_smack *nsp = smack_sock(newsk);
struct smk_audit_info ad;
int rc = 0;
#ifdef CONFIG_AUDIT
@@ -3833,12 +3857,18 @@ static int smack_unix_stream_connect(struct sock *sock,
}
}
- /*
- * Cross reference the peer labels for SO_PEERSEC.
- */
if (rc == 0) {
+ /*
+ * Cross reference the peer labels for SO_PEERSEC.
+ */
nsp->smk_packet = ssp->smk_out;
ssp->smk_packet = osp->smk_out;
+
+ /*
+ * new/child/established socket must inherit listening socket labels
+ */
+ nsp->smk_out = osp->smk_out;
+ nsp->smk_in = osp->smk_in;
}
return rc;
@@ -3854,8 +3884,8 @@ static int smack_unix_stream_connect(struct sock *sock,
*/
static int smack_unix_may_send(struct socket *sock, struct socket *other)
{
- struct socket_smack *ssp = sock->sk->sk_security;
- struct socket_smack *osp = other->sk->sk_security;
+ struct socket_smack *ssp = smack_sock(sock->sk);
+ struct socket_smack *osp = smack_sock(other->sk);
struct smk_audit_info ad;
int rc;
@@ -3892,7 +3922,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name;
#endif
#ifdef SMACK_IPV6_SECMARK_LABELING
- struct socket_smack *ssp = sock->sk->sk_security;
+ struct socket_smack *ssp = smack_sock(sock->sk);
struct smack_known *rsp;
#endif
int rc = 0;
@@ -4104,7 +4134,7 @@ static struct smack_known *smack_from_netlbl(const struct sock *sk, u16 family,
netlbl_secattr_init(&secattr);
if (sk)
- ssp = sk->sk_security;
+ ssp = smack_sock(sk);
if (netlbl_skbuff_getattr(skb, family, &secattr) == 0) {
skp = smack_from_secattr(&secattr, ssp);
@@ -4126,7 +4156,7 @@ static struct smack_known *smack_from_netlbl(const struct sock *sk, u16 family,
*/
static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
- struct socket_smack *ssp = sk->sk_security;
+ struct socket_smack *ssp = smack_sock(sk);
struct smack_known *skp = NULL;
int rc = 0;
struct smk_audit_info ad;
@@ -4230,7 +4260,7 @@ static int smack_socket_getpeersec_stream(struct socket *sock,
u32 slen = 1;
int rc = 0;
- ssp = sock->sk->sk_security;
+ ssp = smack_sock(sock->sk);
if (ssp->smk_packet != NULL) {
rcp = ssp->smk_packet->smk_known;
slen = strlen(rcp) + 1;
@@ -4280,7 +4310,7 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
switch (family) {
case PF_UNIX:
- ssp = sock->sk->sk_security;
+ ssp = smack_sock(sock->sk);
s = ssp->smk_out->smk_secid;
break;
case PF_INET:
@@ -4329,7 +4359,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)
(sk->sk_family != PF_INET && sk->sk_family != PF_INET6))
return;
- ssp = sk->sk_security;
+ ssp = smack_sock(sk);
ssp->smk_in = skp;
ssp->smk_out = skp;
/* cssp->smk_packet is already set in smack_inet_csk_clone() */
@@ -4349,7 +4379,7 @@ static int smack_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
{
u16 family = sk->sk_family;
struct smack_known *skp;
- struct socket_smack *ssp = sk->sk_security;
+ struct socket_smack *ssp = smack_sock(sk);
struct sockaddr_in addr;
struct iphdr *hdr;
struct smack_known *hskp;
@@ -4418,7 +4448,7 @@ static int smack_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
rcu_read_unlock();
if (hskp == NULL)
- rc = netlbl_req_setattr(req, &skp->smk_netlabel);
+ rc = netlbl_req_setattr(req, &ssp->smk_out->smk_netlabel);
else
netlbl_req_delattr(req);
@@ -4435,7 +4465,7 @@ static int smack_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
static void smack_inet_csk_clone(struct sock *sk,
const struct request_sock *req)
{
- struct socket_smack *ssp = sk->sk_security;
+ struct socket_smack *ssp = smack_sock(sk);
struct smack_known *skp;
if (req->peer_secid != 0) {
@@ -4467,24 +4497,14 @@ static void smack_inet_csk_clone(struct sock *sk,
static int smack_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
{
+ struct smack_known **blob = smack_key(key);
struct smack_known *skp = smk_of_task(smack_cred(cred));
- key->security = skp;
+ *blob = skp;
return 0;
}
/**
- * smack_key_free - Clear the key security blob
- * @key: the object
- *
- * Clear the blob pointer
- */
-static void smack_key_free(struct key *key)
-{
- key->security = NULL;
-}
-
-/**
* smack_key_permission - Smack access on a key
* @key_ref: gets to the object
* @cred: the credentials to use
@@ -4497,6 +4517,8 @@ static int smack_key_permission(key_ref_t key_ref,
const struct cred *cred,
enum key_need_perm need_perm)
{
+ struct smack_known **blob;
+ struct smack_known *skp;
struct key *keyp;
struct smk_audit_info ad;
struct smack_known *tkp = smk_of_task(smack_cred(cred));
@@ -4534,7 +4556,9 @@ static int smack_key_permission(key_ref_t key_ref,
* If the key hasn't been initialized give it access so that
* it may do so.
*/
- if (keyp->security == NULL)
+ blob = smack_key(keyp);
+ skp = *blob;
+ if (skp == NULL)
return 0;
/*
* This should not occur
@@ -4550,8 +4574,8 @@ static int smack_key_permission(key_ref_t key_ref,
ad.a.u.key_struct.key = keyp->serial;
ad.a.u.key_struct.key_desc = keyp->description;
#endif
- rc = smk_access(tkp, keyp->security, request, &ad);
- rc = smk_bu_note("key access", tkp, keyp->security, request, rc);
+ rc = smk_access(tkp, skp, request, &ad);
+ rc = smk_bu_note("key access", tkp, skp, request, rc);
return rc;
}
@@ -4566,11 +4590,12 @@ static int smack_key_permission(key_ref_t key_ref,
*/
static int smack_key_getsecurity(struct key *key, char **_buffer)
{
- struct smack_known *skp = key->security;
+ struct smack_known **blob = smack_key(key);
+ struct smack_known *skp = *blob;
size_t length;
char *copy;
- if (key->security == NULL) {
+ if (skp == NULL) {
*_buffer = NULL;
return 0;
}
@@ -4597,16 +4622,9 @@ static int smack_watch_key(struct key *key)
{
struct smk_audit_info ad;
struct smack_known *tkp = smk_of_current();
+ struct smack_known **blob = smack_key(key);
int rc;
- if (key == NULL)
- return -EINVAL;
- /*
- * If the key hasn't been initialized give it access so that
- * it may do so.
- */
- if (key->security == NULL)
- return 0;
/*
* This should not occur
*/
@@ -4621,8 +4639,8 @@ static int smack_watch_key(struct key *key)
ad.a.u.key_struct.key = key->serial;
ad.a.u.key_struct.key_desc = key->description;
#endif
- rc = smk_access(tkp, key->security, MAY_READ, &ad);
- rc = smk_bu_note("key watch", tkp, key->security, MAY_READ, rc);
+ rc = smk_access(tkp, *blob, MAY_READ, &ad);
+ rc = smk_bu_note("key watch", tkp, *blob, MAY_READ, rc);
return rc;
}
#endif /* CONFIG_KEY_NOTIFICATIONS */
@@ -4680,11 +4698,13 @@ static int smack_post_notification(const struct cred *w_cred,
* @op: required testing operator (=, !=, >, <, ...)
* @rulestr: smack label to be audited
* @vrule: pointer to save our own audit rule representation
+ * @gfp: type of the memory for the allocation
*
* Prepare to audit cases where (@field @op @rulestr) is true.
* The label to be audited is created if necessay.
*/
-static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
+static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule,
+ gfp_t gfp)
{
struct smack_known *skp;
char **rule = (char **)vrule;
@@ -4730,7 +4750,7 @@ static int smack_audit_rule_known(struct audit_krule *krule)
/**
* smack_audit_rule_match - Audit given object ?
- * @secid: security id for identifying the object to test
+ * @prop: security id for identifying the object to test
* @field: audit rule flags given from user-space
* @op: required testing operator
* @vrule: smack internal rule presentation
@@ -4738,9 +4758,10 @@ static int smack_audit_rule_known(struct audit_krule *krule)
* The core Audit hook. It's used to take the decision of
* whether to audit or not to audit a given object.
*/
-static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule)
+static int smack_audit_rule_match(struct lsm_prop *prop, u32 field, u32 op,
+ void *vrule)
{
- struct smack_known *skp;
+ struct smack_known *skp = prop->smack.skp;
char *rule = vrule;
if (unlikely(!rule)) {
@@ -4751,8 +4772,6 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule)
if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER)
return 0;
- skp = smack_from_secid(secid);
-
/*
* No need to do string comparisons. If a match occurs,
* both pointers will point to the same smack_known
@@ -4782,23 +4801,48 @@ static int smack_ismaclabel(const char *name)
return (strcmp(name, XATTR_SMACK_SUFFIX) == 0);
}
+/**
+ * smack_to_secctx - fill a lsm_context
+ * @skp: Smack label
+ * @cp: destination
+ *
+ * Fill the passed @cp and return the length of the string
+ */
+static int smack_to_secctx(struct smack_known *skp, struct lsm_context *cp)
+{
+ int len = strlen(skp->smk_known);
+
+ if (cp) {
+ cp->context = skp->smk_known;
+ cp->len = len;
+ cp->id = LSM_ID_SMACK;
+ }
+ return len;
+}
/**
* smack_secid_to_secctx - return the smack label for a secid
* @secid: incoming integer
- * @secdata: destination
- * @seclen: how long it is
+ * @cp: destination
*
* Exists for networking code.
*/
-static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+static int smack_secid_to_secctx(u32 secid, struct lsm_context *cp)
{
- struct smack_known *skp = smack_from_secid(secid);
+ return smack_to_secctx(smack_from_secid(secid), cp);
+}
- if (secdata)
- *secdata = skp->smk_known;
- *seclen = strlen(skp->smk_known);
- return 0;
+/**
+ * smack_lsmprop_to_secctx - return the smack label
+ * @prop: includes incoming Smack data
+ * @cp: destination
+ *
+ * Exists for audit code.
+ */
+static int smack_lsmprop_to_secctx(struct lsm_prop *prop,
+ struct lsm_context *cp)
+{
+ return smack_to_secctx(prop->smack.skp, cp);
}
/**
@@ -4834,16 +4878,17 @@ static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
static int smack_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
{
- return __vfs_setxattr_noperm(&nop_mnt_idmap, dentry, XATTR_NAME_SMACK,
- ctx, ctxlen, 0);
+ return __vfs_setxattr_locked(&nop_mnt_idmap, dentry, XATTR_NAME_SMACK,
+ ctx, ctxlen, 0, NULL);
}
-static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
+static int smack_inode_getsecctx(struct inode *inode, struct lsm_context *cp)
{
struct smack_known *skp = smk_of_inode(inode);
- *ctx = skp->smk_known;
- *ctxlen = strlen(skp->smk_known);
+ cp->context = skp->smk_known;
+ cp->len = strlen(skp->smk_known);
+ cp->id = LSM_ID_SMACK;
return 0;
}
@@ -4873,13 +4918,13 @@ static int smack_inode_copy_up(struct dentry *dentry, struct cred **new)
return 0;
}
-static int smack_inode_copy_up_xattr(const char *name)
+static int smack_inode_copy_up_xattr(struct dentry *src, const char *name)
{
/*
- * Return 1 if this is the smack access Smack attribute.
+ * Return -ECANCELED if this is the smack access Smack attribute.
*/
- if (strcmp(name, XATTR_NAME_SMACK) == 0)
- return 1;
+ if (!strcmp(name, XATTR_NAME_SMACK))
+ return -ECANCELED;
return -EOPNOTSUPP;
}
@@ -5002,7 +5047,9 @@ struct lsm_blob_sizes smack_blob_sizes __ro_after_init = {
.lbs_file = sizeof(struct smack_known *),
.lbs_inode = sizeof(struct inode_smack),
.lbs_ipc = sizeof(struct smack_known *),
+ .lbs_key = sizeof(struct smack_known *),
.lbs_msg_msg = sizeof(struct smack_known *),
+ .lbs_sock = sizeof(struct socket_smack),
.lbs_superblock = sizeof(struct superblock_smack),
.lbs_xattr_count = SMACK_INODE_INIT_XATTRS,
};
@@ -5038,6 +5085,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_permission, smack_inode_permission),
LSM_HOOK_INIT(inode_setattr, smack_inode_setattr),
LSM_HOOK_INIT(inode_getattr, smack_inode_getattr),
+ LSM_HOOK_INIT(inode_xattr_skipcap, smack_inode_xattr_skipcap),
LSM_HOOK_INIT(inode_setxattr, smack_inode_setxattr),
LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr),
LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr),
@@ -5048,7 +5096,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_getsecurity, smack_inode_getsecurity),
LSM_HOOK_INIT(inode_setsecurity, smack_inode_setsecurity),
LSM_HOOK_INIT(inode_listsecurity, smack_inode_listsecurity),
- LSM_HOOK_INIT(inode_getsecid, smack_inode_getsecid),
+ LSM_HOOK_INIT(inode_getlsmprop, smack_inode_getlsmprop),
LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security),
LSM_HOOK_INIT(file_ioctl, smack_file_ioctl),
@@ -5068,13 +5116,14 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
LSM_HOOK_INIT(cred_prepare, smack_cred_prepare),
LSM_HOOK_INIT(cred_transfer, smack_cred_transfer),
LSM_HOOK_INIT(cred_getsecid, smack_cred_getsecid),
+ LSM_HOOK_INIT(cred_getlsmprop, smack_cred_getlsmprop),
LSM_HOOK_INIT(kernel_act_as, smack_kernel_act_as),
LSM_HOOK_INIT(kernel_create_files_as, smack_kernel_create_files_as),
LSM_HOOK_INIT(task_setpgid, smack_task_setpgid),
LSM_HOOK_INIT(task_getpgid, smack_task_getpgid),
LSM_HOOK_INIT(task_getsid, smack_task_getsid),
- LSM_HOOK_INIT(current_getsecid_subj, smack_current_getsecid_subj),
- LSM_HOOK_INIT(task_getsecid_obj, smack_task_getsecid_obj),
+ LSM_HOOK_INIT(current_getlsmprop_subj, smack_current_getlsmprop_subj),
+ LSM_HOOK_INIT(task_getlsmprop_obj, smack_task_getlsmprop_obj),
LSM_HOOK_INIT(task_setnice, smack_task_setnice),
LSM_HOOK_INIT(task_setioprio, smack_task_setioprio),
LSM_HOOK_INIT(task_getioprio, smack_task_getioprio),
@@ -5085,7 +5134,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
LSM_HOOK_INIT(task_to_inode, smack_task_to_inode),
LSM_HOOK_INIT(ipc_permission, smack_ipc_permission),
- LSM_HOOK_INIT(ipc_getsecid, smack_ipc_getsecid),
+ LSM_HOOK_INIT(ipc_getlsmprop, smack_ipc_getlsmprop),
LSM_HOOK_INIT(msg_msg_alloc_security, smack_msg_msg_alloc_security),
@@ -5126,7 +5175,9 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
LSM_HOOK_INIT(socket_getpeersec_stream, smack_socket_getpeersec_stream),
LSM_HOOK_INIT(socket_getpeersec_dgram, smack_socket_getpeersec_dgram),
LSM_HOOK_INIT(sk_alloc_security, smack_sk_alloc_security),
+#ifdef SMACK_IPV6_PORT_LABELING
LSM_HOOK_INIT(sk_free_security, smack_sk_free_security),
+#endif
LSM_HOOK_INIT(sk_clone_security, smack_sk_clone_security),
LSM_HOOK_INIT(sock_graft, smack_sock_graft),
LSM_HOOK_INIT(inet_conn_request, smack_inet_conn_request),
@@ -5135,7 +5186,6 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
/* key management security hooks */
#ifdef CONFIG_KEYS
LSM_HOOK_INIT(key_alloc, smack_key_alloc),
- LSM_HOOK_INIT(key_free, smack_key_free),
LSM_HOOK_INIT(key_permission, smack_key_permission),
LSM_HOOK_INIT(key_getsecurity, smack_key_getsecurity),
#ifdef CONFIG_KEY_NOTIFICATIONS
@@ -5156,6 +5206,7 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
LSM_HOOK_INIT(ismaclabel, smack_ismaclabel),
LSM_HOOK_INIT(secid_to_secctx, smack_secid_to_secctx),
+ LSM_HOOK_INIT(lsmprop_to_secctx, smack_lsmprop_to_secctx),
LSM_HOOK_INIT(secctx_to_secid, smack_secctx_to_secid),
LSM_HOOK_INIT(inode_notifysecctx, smack_inode_notifysecctx),
LSM_HOOK_INIT(inode_setsecctx, smack_inode_setsecctx),
diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c
index b945c1d3a743..8fd747b3653a 100644
--- a/security/smack/smack_netfilter.c
+++ b/security/smack/smack_netfilter.c
@@ -19,15 +19,15 @@
#include "smack.h"
static unsigned int smack_ip_output(void *priv,
- struct sk_buff *skb,
- const struct nf_hook_state *state)
+ struct sk_buff *skb,
+ const struct nf_hook_state *state)
{
struct sock *sk = skb_to_full_sk(skb);
struct socket_smack *ssp;
struct smack_known *skp;
- if (sk && sk->sk_security) {
- ssp = sk->sk_security;
+ if (sk) {
+ ssp = smack_sock(sk);
skp = ssp->smk_out;
skb->secmark = skp->smk_secid;
}
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index e22aad7604e8..357188f764ce 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -182,11 +182,9 @@ static inline void smack_catset_bit(unsigned int cat, char *catsetp)
*/
static void smk_netlabel_audit_set(struct netlbl_audit *nap)
{
- struct smack_known *skp = smk_of_current();
-
nap->loginuid = audit_get_loginuid(current);
nap->sessionid = audit_get_sessionid(current);
- nap->secid = skp->smk_secid;
+ nap->prop.smack.skp = smk_of_current();
}
/*
@@ -564,6 +562,7 @@ static void smk_seq_stop(struct seq_file *s, void *v)
static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
{
+ char acc[SMK_NUM_ACCESS_TYPE + 1];
/*
* Don't show any rules with label names too long for
* interface file (/smack/load or /smack/load2)
@@ -577,28 +576,11 @@ static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
if (srp->smk_access == 0)
return;
- seq_printf(s, "%s %s",
+ smack_str_from_perm(acc, srp->smk_access);
+ seq_printf(s, "%s %s %s\n",
srp->smk_subject->smk_known,
- srp->smk_object->smk_known);
-
- seq_putc(s, ' ');
-
- if (srp->smk_access & MAY_READ)
- seq_putc(s, 'r');
- if (srp->smk_access & MAY_WRITE)
- seq_putc(s, 'w');
- if (srp->smk_access & MAY_EXEC)
- seq_putc(s, 'x');
- if (srp->smk_access & MAY_APPEND)
- seq_putc(s, 'a');
- if (srp->smk_access & MAY_TRANSMUTE)
- seq_putc(s, 't');
- if (srp->smk_access & MAY_LOCK)
- seq_putc(s, 'l');
- if (srp->smk_access & MAY_BRINGUP)
- seq_putc(s, 'b');
-
- seq_putc(s, '\n');
+ srp->smk_object->smk_known,
+ acc);
}
/*
@@ -932,7 +914,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
}
if (rc >= 0) {
old_cat = skp->smk_netlabel.attr.mls.cat;
- skp->smk_netlabel.attr.mls.cat = ncats.attr.mls.cat;
+ rcu_assign_pointer(skp->smk_netlabel.attr.mls.cat, ncats.attr.mls.cat);
skp->smk_netlabel.attr.mls.lvl = ncats.attr.mls.lvl;
synchronize_rcu();
netlbl_catmap_free(old_cat);
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig
index fad75be5f381..1e0dd1a6d0b0 100644
--- a/security/tomoyo/Kconfig
+++ b/security/tomoyo/Kconfig
@@ -10,7 +10,7 @@ config SECURITY_TOMOYO
help
This selects TOMOYO Linux, pathname-based access control.
Required userspace tools and further information may be
- found at <https://tomoyo.osdn.jp/>.
+ found at <https://tomoyo.sourceforge.net/>.
If you are unsure how to answer this question, answer N.
config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile
index 884ff155edc3..55c67b9846a9 100644
--- a/security/tomoyo/Makefile
+++ b/security/tomoyo/Makefile
@@ -11,7 +11,7 @@ quiet_cmd_policy = POLICY $@
printf '\t"";\n';) \
} > $@
-$(obj)/builtin-policy.h: $(wildcard $(obj)/policy/*.conf $(srctree)/$(src)/policy/*.conf.default) FORCE
+$(obj)/builtin-policy.h: $(wildcard $(obj)/policy/*.conf $(src)/policy/*.conf.default) FORCE
$(call if_changed,policy)
ifndef CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index ea3140d510ec..0f78898bce09 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -1981,6 +1981,114 @@ static int tomoyo_truncate(char *str)
}
/**
+ * tomoyo_numscan - sscanf() which stores the length of a decimal integer value.
+ *
+ * @str: String to scan.
+ * @head: Leading string that must start with.
+ * @width: Pointer to "int" for storing length of a decimal integer value after @head.
+ * @tail: Optional character that must match after a decimal integer value.
+ *
+ * Returns whether @str starts with @head and a decimal value follows @head.
+ */
+static bool tomoyo_numscan(const char *str, const char *head, int *width, const char tail)
+{
+ const char *cp;
+ const int n = strlen(head);
+
+ if (!strncmp(str, head, n)) {
+ cp = str + n;
+ while (*cp && *cp >= '0' && *cp <= '9')
+ cp++;
+ if (*cp == tail || !tail) {
+ *width = cp - (str + n);
+ return *width != 0;
+ }
+ }
+ *width = 0;
+ return 0;
+}
+
+/**
+ * tomoyo_patternize_path - Make patterns for file path. Used by learning mode.
+ *
+ * @buffer: Destination buffer.
+ * @len: Size of @buffer.
+ * @entry: Original line.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_patternize_path(char *buffer, const int len, char *entry)
+{
+ int width;
+ char *cp = entry;
+
+ /* Nothing to do if this line is not for "file" related entry. */
+ if (strncmp(entry, "file ", 5))
+ goto flush;
+ /*
+ * Nothing to do if there is no colon in this line, for this rewriting
+ * applies to only filesystems where numeric values in the path are volatile.
+ */
+ cp = strchr(entry + 5, ':');
+ if (!cp) {
+ cp = entry;
+ goto flush;
+ }
+ /* Flush e.g. "file ioctl" part. */
+ while (*cp != ' ')
+ cp--;
+ *cp++ = '\0';
+ tomoyo_addprintf(buffer, len, "%s ", entry);
+ /* e.g. file ioctl pipe:[$INO] $CMD */
+ if (tomoyo_numscan(cp, "pipe:[", &width, ']')) {
+ cp += width + 7;
+ tomoyo_addprintf(buffer, len, "pipe:[\\$]");
+ goto flush;
+ }
+ /* e.g. file ioctl socket:[$INO] $CMD */
+ if (tomoyo_numscan(cp, "socket:[", &width, ']')) {
+ cp += width + 9;
+ tomoyo_addprintf(buffer, len, "socket:[\\$]");
+ goto flush;
+ }
+ if (!strncmp(cp, "proc:/self", 10)) {
+ /* e.g. file read proc:/self/task/$TID/fdinfo/$FD */
+ cp += 10;
+ tomoyo_addprintf(buffer, len, "proc:/self");
+ } else if (tomoyo_numscan(cp, "proc:/", &width, 0)) {
+ /* e.g. file read proc:/$PID/task/$TID/fdinfo/$FD */
+ /*
+ * Don't patternize $PID part if $PID == 1, for several
+ * programs access only files in /proc/1/ directory.
+ */
+ cp += width + 6;
+ if (width == 1 && *(cp - 1) == '1')
+ tomoyo_addprintf(buffer, len, "proc:/1");
+ else
+ tomoyo_addprintf(buffer, len, "proc:/\\$");
+ } else {
+ goto flush;
+ }
+ /* Patternize $TID part if "/task/" follows. */
+ if (tomoyo_numscan(cp, "/task/", &width, 0)) {
+ cp += width + 6;
+ tomoyo_addprintf(buffer, len, "/task/\\$");
+ }
+ /* Patternize $FD part if "/fd/" or "/fdinfo/" follows. */
+ if (tomoyo_numscan(cp, "/fd/", &width, 0)) {
+ cp += width + 4;
+ tomoyo_addprintf(buffer, len, "/fd/\\$");
+ } else if (tomoyo_numscan(cp, "/fdinfo/", &width, 0)) {
+ cp += width + 8;
+ tomoyo_addprintf(buffer, len, "/fdinfo/\\$");
+ }
+flush:
+ /* Flush remaining part if any. */
+ if (*cp)
+ tomoyo_addprintf(buffer, len, "%s", cp);
+}
+
+/**
* tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode.
*
* @domain: Pointer to "struct tomoyo_domain_info".
@@ -2003,7 +2111,8 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
if (!cp)
return;
*cp++ = '\0';
- len = strlen(cp) + 1;
+ /* Reserve some space for potentially using patterns. */
+ len = strlen(cp) + 16;
/* strstr() will return NULL if ordering is wrong. */
if (*cp == 'f') {
argv0 = strstr(header, " argv[]={ \"");
@@ -2020,10 +2129,10 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
if (symlink)
len += tomoyo_truncate(symlink + 1) + 1;
}
- buffer = kmalloc(len, GFP_NOFS);
+ buffer = kmalloc(len, GFP_NOFS | __GFP_ZERO);
if (!buffer)
return;
- snprintf(buffer, len - 1, "%s", cp);
+ tomoyo_patternize_path(buffer, len, cp);
if (realpath)
tomoyo_addprintf(buffer, len, " exec.%s", realpath);
if (argv0)
@@ -2665,7 +2774,7 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
if (head->w.avail >= head->writebuf_size - 1) {
const int len = head->writebuf_size * 2;
- char *cp = kzalloc(len, GFP_NOFS);
+ char *cp = kzalloc(len, GFP_NOFS | __GFP_NOWARN);
if (!cp) {
error = -ENOMEM;
@@ -2787,7 +2896,7 @@ void tomoyo_check_profile(void)
else
continue;
pr_err("Userland tools for TOMOYO 2.6 must be installed and policy must be initialized.\n");
- pr_err("Please see https://tomoyo.osdn.jp/2.6/ for more information.\n");
+ pr_err("Please see https://tomoyo.sourceforge.net/2.6/ for more information.\n");
panic("STOP!");
}
tomoyo_read_unlock(idx);
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index 90b53500a236..5f9ccab26e9a 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -722,11 +722,21 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
ee->bprm = bprm;
ee->r.obj = &ee->obj;
ee->obj.path1 = bprm->file->f_path;
- /* Get symlink's pathname of program. */
- retval = -ENOENT;
+ /*
+ * Get symlink's pathname of program, but fallback to realpath if
+ * symlink's pathname does not exist or symlink's pathname refers
+ * to proc filesystem (e.g. /dev/fd/<num> or /proc/self/fd/<num> ).
+ */
exename.name = tomoyo_realpath_nofollow(original_name);
- if (!exename.name)
- goto out;
+ if (exename.name && !strncmp(exename.name, "proc:/", 6)) {
+ kfree(exename.name);
+ exename.name = NULL;
+ }
+ if (!exename.name) {
+ exename.name = tomoyo_realpath_from_path(&bprm->file->f_path);
+ if (!exename.name)
+ goto out;
+ }
tomoyo_fill_path_info(&exename);
retry:
/* Check 'aggregator' directive. */
@@ -910,7 +920,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
#ifdef CONFIG_MMU
/*
* This is called at execve() time in order to dig around
- * in the argv/environment of the new proceess
+ * in the argv/environment of the new process
* (represented by bprm).
*/
mmap_read_lock(bprm->mm);
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
index a2705798476f..7e69747b2f77 100644
--- a/security/tomoyo/securityfs_if.c
+++ b/security/tomoyo/securityfs_if.c
@@ -229,11 +229,11 @@ static void __init tomoyo_create_entry(const char *name, const umode_t mode,
}
/**
- * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface.
+ * tomoyo_interface_init - Initialize /sys/kernel/security/tomoyo/ interface.
*
* Returns 0.
*/
-static int __init tomoyo_initerface_init(void)
+static int __init tomoyo_interface_init(void)
{
struct tomoyo_domain_info *domain;
struct dentry *tomoyo_dir;
@@ -270,4 +270,4 @@ static int __init tomoyo_initerface_init(void)
return 0;
}
-fs_initcall(tomoyo_initerface_init);
+fs_initcall(tomoyo_interface_init);
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 04a92c3d65d4..d6ebcd9db80a 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -549,10 +549,7 @@ static const struct lsm_id tomoyo_lsmid = {
.id = LSM_ID_TOMOYO,
};
-/*
- * tomoyo_security_ops is a "struct security_operations" which is used for
- * registering TOMOYO.
- */
+/* tomoyo_hooks is used for registering TOMOYO. */
static struct security_hook_list tomoyo_hooks[] __ro_after_init = {
LSM_HOOK_INIT(cred_prepare, tomoyo_cred_prepare),
LSM_HOOK_INIT(bprm_committed_creds, tomoyo_bprm_committed_creds),
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 49dc52b454ef..1971710620c1 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -76,7 +76,6 @@ static void report_access(const char *access, struct task_struct *target,
struct task_struct *agent)
{
struct access_report_info *info;
- char agent_comm[sizeof(agent->comm)];
assert_spin_locked(&target->alloc_lock); /* for target->comm */
@@ -86,8 +85,7 @@ static void report_access(const char *access, struct task_struct *target,
*/
pr_notice_ratelimited(
"ptrace %s of \"%s\"[%d] was attempted by \"%s\"[%d]\n",
- access, target->comm, target->pid,
- get_task_comm(agent_comm, agent), agent->pid);
+ access, target->comm, target->pid, agent->comm, agent->pid);
return;
}
@@ -111,6 +109,7 @@ static void report_access(const char *access, struct task_struct *target,
/**
* yama_relation_cleanup - remove invalid entries from the relation list
+ * @work: unused
*
*/
static void yama_relation_cleanup(struct work_struct *work)
@@ -435,7 +434,7 @@ static struct security_hook_list yama_hooks[] __ro_after_init = {
};
#ifdef CONFIG_SYSCTL
-static int yama_dointvec_minmax(struct ctl_table *table, int write,
+static int yama_dointvec_minmax(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
struct ctl_table table_copy;
@@ -453,7 +452,7 @@ static int yama_dointvec_minmax(struct ctl_table *table, int write,
static int max_scope = YAMA_SCOPE_NO_ATTACH;
-static struct ctl_table yama_sysctl_table[] = {
+static const struct ctl_table yama_sysctl_table[] = {
{
.procname = "ptrace_scope",
.data = &ptrace_scope,
@@ -463,7 +462,6 @@ static struct ctl_table yama_sysctl_table[] = {
.extra1 = SYSCTL_ZERO,
.extra2 = &max_scope,
},
- { }
};
static void __init yama_init_sysctl(void)
{