Update Linux to v5.4.2
Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/security/Kconfig b/security/Kconfig
index d9aa521..2a1a2d3 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -1,10 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Security configuration
#
menu "Security options"
-source security/keys/Kconfig
+source "security/keys/Kconfig"
config SECURITY_DMESG_RESTRICT
bool "Restrict unprivileged access to the kernel syslog"
@@ -40,8 +41,7 @@
bool "Enable the securityfs filesystem"
help
This will build the securityfs filesystem. It is currently used by
- the TPM bios character driver and IMA, an integrity provider. It is
- not used by SELinux or SMACK.
+ various security modules (AppArmor, IMA, SafeSetID, TOMOYO, TPM).
If you are unsure how to answer this question, answer N.
@@ -63,7 +63,7 @@
ensuring that the majority of kernel addresses are not mapped
into userspace.
- See Documentation/x86/pti.txt for more details.
+ See Documentation/x86/pti.rst for more details.
config SECURITY_INFINIBAND
bool "Infiniband Security Hooks"
@@ -121,7 +121,7 @@
See <http://www.intel.com/technology/security/> for more information
about Intel(R) TXT.
See <http://tboot.sourceforge.net> for more information about tboot.
- See Documentation/intel_txt.txt for a description of how to enable
+ See Documentation/x86/intel_txt.rst for a description of how to enable
Intel TXT support in a kernel boot.
If you are unsure as to whether this is required, answer N.
@@ -230,17 +230,19 @@
If you wish for all usermode helper programs to be disabled,
specify an empty string here (i.e. "").
-source security/selinux/Kconfig
-source security/smack/Kconfig
-source security/tomoyo/Kconfig
-source security/apparmor/Kconfig
-source security/loadpin/Kconfig
-source security/yama/Kconfig
+source "security/selinux/Kconfig"
+source "security/smack/Kconfig"
+source "security/tomoyo/Kconfig"
+source "security/apparmor/Kconfig"
+source "security/loadpin/Kconfig"
+source "security/yama/Kconfig"
+source "security/safesetid/Kconfig"
+source "security/lockdown/Kconfig"
-source security/integrity/Kconfig
+source "security/integrity/Kconfig"
choice
- prompt "Default security module"
+ prompt "First legacy 'major LSM' to be initialized"
default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX
default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
@@ -248,8 +250,13 @@
default DEFAULT_SECURITY_DAC
help
- Select the security module that will be used by default if the
- kernel parameter security= is not specified.
+ This choice is there only for converting CONFIG_DEFAULT_SECURITY
+ in old kernel configs to CONFIG_LSM in new kernel configs. Don't
+ change this choice unless you are creating a fresh kernel config,
+ for this choice will be ignored after CONFIG_LSM has been set.
+
+ Selects the legacy "major security module" that will be
+ initialized first. Overridden by non-default CONFIG_LSM.
config DEFAULT_SECURITY_SELINUX
bool "SELinux" if SECURITY_SELINUX=y
@@ -268,13 +275,21 @@
endchoice
-config DEFAULT_SECURITY
- string
- default "selinux" if DEFAULT_SECURITY_SELINUX
- default "smack" if DEFAULT_SECURITY_SMACK
- default "tomoyo" if DEFAULT_SECURITY_TOMOYO
- default "apparmor" if DEFAULT_SECURITY_APPARMOR
- default "" if DEFAULT_SECURITY_DAC
+config LSM
+ string "Ordered list of enabled LSMs"
+ default "lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor" if DEFAULT_SECURITY_SMACK
+ default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo" if DEFAULT_SECURITY_APPARMOR
+ default "lockdown,yama,loadpin,safesetid,integrity,tomoyo" if DEFAULT_SECURITY_TOMOYO
+ default "lockdown,yama,loadpin,safesetid,integrity" if DEFAULT_SECURITY_DAC
+ default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor"
+ help
+ A comma-separated list of LSMs, in initialization order.
+ Any LSMs left off this list will be ignored. This can be
+ controlled at boot with the "lsm=" parameter.
+
+ If unsure, leave this as the default.
+
+source "security/Kconfig.hardening"
endmenu
diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening
new file mode 100644
index 0000000..af4c979
--- /dev/null
+++ b/security/Kconfig.hardening
@@ -0,0 +1,201 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Kernel hardening options"
+
+config GCC_PLUGIN_STRUCTLEAK
+ bool
+ help
+ While the kernel is built with warnings enabled for any missed
+ stack variable initializations, this warning is silenced for
+ anything passed by reference to another function, under the
+ occasionally misguided assumption that the function will do
+ the initialization. As this regularly leads to exploitable
+ flaws, this plugin is available to identify and zero-initialize
+ such variables, depending on the chosen level of coverage.
+
+ This plugin was originally ported from grsecurity/PaX. More
+ information at:
+ * https://grsecurity.net/
+ * https://pax.grsecurity.net/
+
+menu "Memory initialization"
+
+config CC_HAS_AUTO_VAR_INIT
+ def_bool $(cc-option,-ftrivial-auto-var-init=pattern)
+
+choice
+ prompt "Initialize kernel stack variables at function entry"
+ default GCC_PLUGIN_STRUCTLEAK_BYREF_ALL if COMPILE_TEST && GCC_PLUGINS
+ default INIT_STACK_ALL if COMPILE_TEST && CC_HAS_AUTO_VAR_INIT
+ default INIT_STACK_NONE
+ help
+ This option enables initialization of stack variables at
+ function entry time. This has the possibility to have the
+ greatest coverage (since all functions can have their
+ variables initialized), but the performance impact depends
+ on the function calling complexity of a given workload's
+ syscalls.
+
+ This chooses the level of coverage over classes of potentially
+ uninitialized variables. The selected class will be
+ initialized before use in a function.
+
+ config INIT_STACK_NONE
+ bool "no automatic initialization (weakest)"
+ help
+ Disable automatic stack variable initialization.
+ This leaves the kernel vulnerable to the standard
+ classes of uninitialized stack variable exploits
+ and information exposures.
+
+ config GCC_PLUGIN_STRUCTLEAK_USER
+ bool "zero-init structs marked for userspace (weak)"
+ depends on GCC_PLUGINS
+ select GCC_PLUGIN_STRUCTLEAK
+ help
+ Zero-initialize any structures on the stack containing
+ a __user attribute. This can prevent some classes of
+ uninitialized stack variable exploits and information
+ exposures, like CVE-2013-2141:
+ https://git.kernel.org/linus/b9e146d8eb3b9eca
+
+ config GCC_PLUGIN_STRUCTLEAK_BYREF
+ bool "zero-init structs passed by reference (strong)"
+ depends on GCC_PLUGINS
+ depends on !(KASAN && KASAN_STACK=1)
+ select GCC_PLUGIN_STRUCTLEAK
+ help
+ Zero-initialize any structures on the stack that may
+ be passed by reference and had not already been
+ explicitly initialized. This can prevent most classes
+ of uninitialized stack variable exploits and information
+ exposures, like CVE-2017-1000410:
+ https://git.kernel.org/linus/06e7e776ca4d3654
+
+ As a side-effect, this keeps a lot of variables on the
+ stack that can otherwise be optimized out, so combining
+ this with CONFIG_KASAN_STACK can lead to a stack overflow
+ and is disallowed.
+
+ config GCC_PLUGIN_STRUCTLEAK_BYREF_ALL
+ bool "zero-init anything passed by reference (very strong)"
+ depends on GCC_PLUGINS
+ depends on !(KASAN && KASAN_STACK=1)
+ select GCC_PLUGIN_STRUCTLEAK
+ help
+ Zero-initialize any stack variables that may be passed
+ by reference and had not already been explicitly
+ initialized. This is intended to eliminate all classes
+ of uninitialized stack variable exploits and information
+ exposures.
+
+ config INIT_STACK_ALL
+ bool "0xAA-init everything on the stack (strongest)"
+ depends on CC_HAS_AUTO_VAR_INIT
+ help
+ Initializes everything on the stack with a 0xAA
+ pattern. This is intended to eliminate all classes
+ of uninitialized stack variable exploits and information
+ exposures, even variables that were warned to have been
+ left uninitialized.
+
+endchoice
+
+config GCC_PLUGIN_STRUCTLEAK_VERBOSE
+ bool "Report forcefully initialized variables"
+ depends on GCC_PLUGIN_STRUCTLEAK
+ depends on !COMPILE_TEST # too noisy
+ help
+ This option will cause a warning to be printed each time the
+ structleak plugin finds a variable it thinks needs to be
+ initialized. Since not all existing initializers are detected
+ by the plugin, this can produce false positive warnings.
+
+config GCC_PLUGIN_STACKLEAK
+ bool "Poison kernel stack before returning from syscalls"
+ depends on GCC_PLUGINS
+ depends on HAVE_ARCH_STACKLEAK
+ help
+ This option makes the kernel erase the kernel stack before
+ returning from system calls. This has the effect of leaving
+ the stack initialized to the poison value, which both reduces
+ the lifetime of any sensitive stack contents and reduces
+ potential for uninitialized stack variable exploits or information
+ exposures (it does not cover functions reaching the same stack
+ depth as prior functions during the same syscall). This blocks
+ most uninitialized stack variable attacks, with the performance
+ impact being driven by the depth of the stack usage, rather than
+ the function calling complexity.
+
+ The performance impact on a single CPU system kernel compilation
+ sees a 1% slowdown, other systems and workloads may vary and you
+ are advised to test this feature on your expected workload before
+ deploying it.
+
+ This plugin was ported from grsecurity/PaX. More information at:
+ * https://grsecurity.net/
+ * https://pax.grsecurity.net/
+
+config STACKLEAK_TRACK_MIN_SIZE
+ int "Minimum stack frame size of functions tracked by STACKLEAK"
+ default 100
+ range 0 4096
+ depends on GCC_PLUGIN_STACKLEAK
+ help
+ The STACKLEAK gcc plugin instruments the kernel code for tracking
+ the lowest border of the kernel stack (and for some other purposes).
+ It inserts the stackleak_track_stack() call for the functions with
+ a stack frame size greater than or equal to this parameter.
+ If unsure, leave the default value 100.
+
+config STACKLEAK_METRICS
+ bool "Show STACKLEAK metrics in the /proc file system"
+ depends on GCC_PLUGIN_STACKLEAK
+ depends on PROC_FS
+ help
+ If this is set, STACKLEAK metrics for every task are available in
+ the /proc file system. In particular, /proc/<pid>/stack_depth
+ shows the maximum kernel stack consumption for the current and
+ previous syscalls. Although this information is not precise, it
+ can be useful for estimating the STACKLEAK performance impact for
+ your workloads.
+
+config STACKLEAK_RUNTIME_DISABLE
+ bool "Allow runtime disabling of kernel stack erasing"
+ depends on GCC_PLUGIN_STACKLEAK
+ help
+ This option provides 'stack_erasing' sysctl, which can be used in
+ runtime to control kernel stack erasing for kernels built with
+ CONFIG_GCC_PLUGIN_STACKLEAK.
+
+config INIT_ON_ALLOC_DEFAULT_ON
+ bool "Enable heap memory zeroing on allocation by default"
+ help
+ This has the effect of setting "init_on_alloc=1" on the kernel
+ command line. This can be disabled with "init_on_alloc=0".
+ When "init_on_alloc" is enabled, all page allocator and slab
+ allocator memory will be zeroed when allocated, eliminating
+ many kinds of "uninitialized heap memory" flaws, especially
+ heap content exposures. The performance impact varies by
+ workload, but most cases see <1% impact. Some synthetic
+ workloads have measured as high as 7%.
+
+config INIT_ON_FREE_DEFAULT_ON
+ bool "Enable heap memory zeroing on free by default"
+ help
+ This has the effect of setting "init_on_free=1" on the kernel
+ command line. This can be disabled with "init_on_free=0".
+ Similar to "init_on_alloc", when "init_on_free" is enabled,
+ all page allocator and slab allocator memory will be zeroed
+ when freed, eliminating many kinds of "uninitialized heap memory"
+ flaws, especially heap content exposures. The primary difference
+ with "init_on_free" is that data lifetime in memory is reduced,
+ as anything freed is wiped immediately, making live forensics or
+ cold boot memory attacks unable to recover freed memory contents.
+ The performance impact varies by workload, but is more expensive
+ than "init_on_alloc" due to the negative cache effects of
+ touching "cold" memory areas. Most cases see 3-5% impact. Some
+ synthetic workloads have measured as high as 8%.
+
+endmenu
+
+endmenu
diff --git a/security/Makefile b/security/Makefile
index 4d2d378..be1dd9d 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -10,6 +10,8 @@
subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
subdir-$(CONFIG_SECURITY_YAMA) += yama
subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
+subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid
+subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown
# always enable default capabilities
obj-y += commoncap.o
@@ -25,6 +27,8 @@
obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
obj-$(CONFIG_SECURITY_YAMA) += yama/
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
+obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/
+obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
# Object integrity file lists
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
index b6b68a7..d8b1a36 100644
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SECURITY_APPARMOR
bool "AppArmor support"
depends on SECURITY && NET
@@ -14,22 +15,6 @@
If you are unsure how to answer this question, answer N.
-config SECURITY_APPARMOR_BOOTPARAM_VALUE
- int "AppArmor boot parameter default value"
- depends on SECURITY_APPARMOR
- range 0 1
- default 1
- help
- This option sets the default value for the kernel parameter
- 'apparmor', which allows AppArmor to be enabled or disabled
- at boot. If this option is set to 0 (zero), the AppArmor
- kernel parameter will default to 0, disabling AppArmor at
- boot. If this option is set to 1 (one), the AppArmor
- kernel parameter will default to 1, enabling AppArmor at
- boot.
-
- If you are unsure how to answer this question, answer 1.
-
config SECURITY_APPARMOR_HASH
bool "Enable introspection of sha1 hashes for loaded profiles"
depends on SECURITY_APPARMOR
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index e09fe4d..45d13b6 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,17 +6,12 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/ctype.h>
#include <linux/security.h>
#include <linux/vmalloc.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/mount.h>
@@ -23,6 +19,7 @@
#include <linux/capability.h>
#include <linux/rcupdate.h>
#include <linux/fs.h>
+#include <linux/fs_context.h>
#include <linux/poll.h>
#include <uapi/linux/major.h>
#include <uapi/linux/magic.h>
@@ -123,21 +120,20 @@
return 0;
}
-static void aafs_evict_inode(struct inode *inode)
+static void aafs_free_inode(struct inode *inode)
{
- truncate_inode_pages_final(&inode->i_data);
- clear_inode(inode);
if (S_ISLNK(inode->i_mode))
kfree(inode->i_link);
+ free_inode_nonrcu(inode);
}
static const struct super_operations aafs_super_ops = {
.statfs = simple_statfs,
- .evict_inode = aafs_evict_inode,
+ .free_inode = aafs_free_inode,
.show_path = aafs_show_path,
};
-static int fill_super(struct super_block *sb, void *data, int silent)
+static int apparmorfs_fill_super(struct super_block *sb, struct fs_context *fc)
{
static struct tree_descr files[] = { {""} };
int error;
@@ -150,16 +146,25 @@
return 0;
}
-static struct dentry *aafs_mount(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data)
+static int apparmorfs_get_tree(struct fs_context *fc)
{
- return mount_single(fs_type, flags, data, fill_super);
+ return get_tree_single(fc, apparmorfs_fill_super);
+}
+
+static const struct fs_context_operations apparmorfs_context_ops = {
+ .get_tree = apparmorfs_get_tree,
+};
+
+static int apparmorfs_init_fs_context(struct fs_context *fc)
+{
+ fc->ops = &apparmorfs_context_ops;
+ return 0;
}
static struct file_system_type aafs_ops = {
.owner = THIS_MODULE,
.name = AAFS_NAME,
- .mount = aafs_mount,
+ .init_fs_context = apparmorfs_init_fs_context,
.kill_sb = kill_anon_super,
};
@@ -356,6 +361,7 @@
simple_rmdir(dir, dentry);
else
simple_unlink(dir, dentry);
+ d_delete(dentry);
dput(dentry);
}
inode_unlock(dir);
@@ -1742,7 +1748,7 @@
if (error)
return error;
- parent = aa_get_ns(dir->i_private);
+ parent = aa_get_ns(dir->i_private);
/* rmdir calls the generic securityfs functions to remove files
* from the apparmor dir. It is up to the apparmor ns locking
* to avoid races.
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index eeaddfe..5a98661 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/audit.h>
@@ -225,8 +221,7 @@
return 0;
}
-int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
- struct audit_context *actx)
+int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule)
{
struct aa_audit_rule *rule = vrule;
struct aa_label *label;
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c
index 253ef6e..deccea8 100644
--- a/security/apparmor/capability.c
+++ b/security/apparmor/capability.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/capability.h>
@@ -110,13 +106,13 @@
* profile_capable - test if profile allows use of capability @cap
* @profile: profile being enforced (NOT NULL, NOT unconfined)
* @cap: capability to test if allowed
- * @audit: whether an audit record should be generated
+ * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated
* @sa: audit data (MAY BE NULL indicating no auditing)
*
* Returns: 0 if allowed else -EPERM
*/
-static int profile_capable(struct aa_profile *profile, int cap, int audit,
- struct common_audit_data *sa)
+static int profile_capable(struct aa_profile *profile, int cap,
+ unsigned int opts, struct common_audit_data *sa)
{
int error;
@@ -126,7 +122,7 @@
else
error = -EPERM;
- if (audit == SECURITY_CAP_NOAUDIT) {
+ if (opts & CAP_OPT_NOAUDIT) {
if (!COMPLAIN_MODE(profile))
return error;
/* audit the cap request in complain mode but note that it
@@ -142,13 +138,13 @@
* aa_capable - test permission to use capability
* @label: label being tested for capability (NOT NULL)
* @cap: capability to be tested
- * @audit: whether an audit record should be generated
+ * @opts: CAP_OPT_NOAUDIT bit determines whether audit record is generated
*
* Look up capability in profile capability set.
*
* Returns: 0 on success, or else an error code.
*/
-int aa_capable(struct aa_label *label, int cap, int audit)
+int aa_capable(struct aa_label *label, int cap, unsigned int opts)
{
struct aa_profile *profile;
int error = 0;
@@ -156,7 +152,7 @@
sa.u.cap = cap;
error = fn_for_each_confined(label, profile,
- profile_capable(profile, cap, audit, &sa));
+ profile_capable(profile, cap, opts, &sa));
return error;
}
diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c
index 136f2a0..b498ed3 100644
--- a/security/apparmor/crypto.c
+++ b/security/apparmor/crypto.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright 2013 Canonical Ltd.
*
- * 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 of the
- * License.
- *
* Fns to provide a checksum of policy that has been loaded this can be
* compared to userspace policy compiles to check loaded policy is what
* it should be.
@@ -43,7 +39,6 @@
goto fail;
desc->tfm = apparmor_tfm;
- desc->flags = 0;
error = crypto_shash_init(desc);
if (error)
@@ -81,7 +76,6 @@
goto fail;
desc->tfm = apparmor_tfm;
- desc->flags = 0;
error = crypto_shash_init(desc);
if (error)
@@ -112,7 +106,7 @@
if (!apparmor_initialized)
return 0;
- tfm = crypto_alloc_shash("sha1", 0, CRYPTO_ALG_ASYNC);
+ tfm = crypto_alloc_shash("sha1", 0, 0);
if (IS_ERR(tfm)) {
int error = PTR_ERR(tfm);
AA_ERROR("failed to setup profile sha1 hashing: %d\n", error);
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index 08c88de..9e04927 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 2002-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/errno.h>
@@ -572,7 +568,7 @@
stack = NULL;
break;
}
- /* fall through to X_NAME */
+ /* fall through - to X_NAME */
case AA_X_NAME:
if (xindex & AA_X_CHILD)
/* released by caller */
@@ -975,7 +971,7 @@
}
aa_put_label(cred_label(bprm->cred));
/* transfer reference, released when cred is freed */
- cred_label(bprm->cred) = new;
+ set_cred_label(bprm->cred, new);
done:
aa_put_label(label);
@@ -1444,7 +1440,10 @@
new = aa_label_merge(label, target, GFP_KERNEL);
if (IS_ERR_OR_NULL(new)) {
info = "failed to build target label";
- error = PTR_ERR(new);
+ if (!new)
+ error = -ENOMEM;
+ else
+ error = PTR_ERR(new);
new = NULL;
perms.allow = 0;
goto audit;
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index 4285943..4c1b05e 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/tty.h>
@@ -496,7 +492,7 @@
/* update caching of label on file_ctx */
spin_lock(&fctx->lock);
old = rcu_dereference_protected(fctx->label,
- spin_is_locked(&fctx->lock));
+ lockdep_is_held(&fctx->lock));
l = aa_label_merge(old, label, GFP_ATOMIC);
if (l) {
if (l != old) {
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index 73d63b5..6b7e6e1 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __APPARMOR_H
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index bd68911..6e14f6c 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_APPARMORFS_H
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
index b8c8b10..18519a4 100644
--- a/security/apparmor/include/audit.h
+++ b/security/apparmor/include/audit.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_AUDIT_H
@@ -192,7 +188,6 @@
void aa_audit_rule_free(void *vrule);
int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule);
int aa_audit_rule_known(struct audit_krule *rule);
-int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
- struct audit_context *actx);
+int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule);
#endif /* __AA_AUDIT_H */
diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h
index e0304e2..d420e2d 100644
--- a/security/apparmor/include/capability.h
+++ b/security/apparmor/include/capability.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2013 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_CAPABILITY_H
@@ -40,7 +36,7 @@
extern struct aa_sfs_entry aa_sfs_entry_caps[];
-int aa_capable(struct aa_label *label, int cap, int audit);
+int aa_capable(struct aa_label *label, int cap, unsigned int opts);
static inline void aa_free_cap_rules(struct aa_caps *caps)
{
diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
index e287b7d..0b9ae48 100644
--- a/security/apparmor/include/cred.h
+++ b/security/apparmor/include/cred.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_CONTEXT_H
@@ -23,8 +19,22 @@
#include "policy_ns.h"
#include "task.h"
-#define cred_label(X) ((X)->security)
+static inline struct aa_label *cred_label(const struct cred *cred)
+{
+ struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred;
+ AA_BUG(!blob);
+ return *blob;
+}
+
+static inline void set_cred_label(const struct cred *cred,
+ struct aa_label *label)
+{
+ struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred;
+
+ AA_BUG(!blob);
+ *blob = label;
+}
/**
* aa_cred_raw_label - obtain cred's label
@@ -151,6 +161,8 @@
{
struct aa_label *label = aa_current_raw_label();
+ might_sleep();
+
if (label_is_stale(label)) {
label = aa_get_newest_label(label);
if (aa_replace_current_label(label) == 0)
diff --git a/security/apparmor/include/crypto.h b/security/apparmor/include/crypto.h
index c1469f8..636a04e 100644
--- a/security/apparmor/include/crypto.h
+++ b/security/apparmor/include/crypto.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
* This file contains AppArmor policy loading interface function definitions.
*
* Copyright 2013 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __APPARMOR_CRYPTO_H
diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h
index ac9862f..21b875f 100644
--- a/security/apparmor/include/domain.h
+++ b/security/apparmor/include/domain.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/binfmts.h>
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h
index 4c2c8ac..a852be8 100644
--- a/security/apparmor/include/file.h
+++ b/security/apparmor/include/file.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_FILE_H
@@ -32,7 +28,10 @@
AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \
AA_EXEC_MMAP | AA_MAY_LINK)
-#define file_ctx(X) ((struct aa_file_ctx *)(X)->f_security)
+static inline struct aa_file_ctx *file_ctx(struct file *file)
+{
+ return file->f_security + apparmor_blob_sizes.lbs_file;
+}
/* struct aa_file_ctx - the AppArmor context the file was opened in
* @lock: lock to update the ctx
diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h
index 5ffc218..9cafd80 100644
--- a/security/apparmor/include/ipc.h
+++ b/security/apparmor/include/ipc.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_IPC_H
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
index 7ce5fe7..47942c4 100644
--- a/security/apparmor/include/label.h
+++ b/security/apparmor/include/label.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
* This file contains AppArmor label definitions
*
* Copyright 2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_LABEL_H
diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h
index 6505e1a..7d27db7 100644
--- a/security/apparmor/include/lib.h
+++ b/security/apparmor/include/lib.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
* This file contains AppArmor lib definitions
*
* 2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_LIB_H
@@ -16,6 +12,7 @@
#include <linux/slab.h>
#include <linux/fs.h>
+#include <linux/lsm_hooks.h>
#include "match.h"
@@ -55,6 +52,9 @@
size_t *ns_len);
void aa_info_message(const char *str);
+/* Security blob offsets */
+extern struct lsm_blob_sizes apparmor_blob_sizes;
+
/**
* aa_strneq - compare null terminated @str to a non null terminated substring
* @str: a null terminated string
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h
index 958d2b5..6b0af63 100644
--- a/security/apparmor/include/match.h
+++ b/security/apparmor/include/match.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2012 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_MATCH_H
diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h
index 25d6067..a710683 100644
--- a/security/apparmor/include/mount.h
+++ b/security/apparmor/include/mount.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
* This file contains AppArmor file mediation function definitions.
*
* Copyright 2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_MOUNT_H
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
index ec7228e..2431c01 100644
--- a/security/apparmor/include/net.h
+++ b/security/apparmor/include/net.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_NET_H
@@ -83,6 +79,13 @@
__e; \
})
+struct aa_secmark {
+ u8 audit;
+ u8 deny;
+ u32 secid;
+ char *label;
+};
+
extern struct aa_sfs_entry aa_sfs_entry_network[];
void audit_net_cb(struct audit_buffer *ab, void *va);
@@ -103,4 +106,7 @@
int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
struct socket *sock);
+int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
+ u32 secid, struct sock *sk);
+
#endif /* __AA_NET_H */
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h
index b6380c5..35a8295 100644
--- a/security/apparmor/include/path.h
+++ b/security/apparmor/include/path.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_PATH_H
diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h
index b94ec11..13f20c5 100644
--- a/security/apparmor/include/perms.h
+++ b/security/apparmor/include/perms.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
* This file contains AppArmor basic permission sets definitions.
*
* Copyright 2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_PERM_H
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index ab64c6b..b5b4b81 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_POLICY_H
@@ -155,6 +151,9 @@
struct aa_rlimit rlimits;
+ int secmark_count;
+ struct aa_secmark *secmark;
+
struct aa_loaddata *rawdata;
unsigned char *hash;
char *dirname;
@@ -214,7 +213,16 @@
return labels_profile(aa_get_newest_label(&p->label));
}
-#define PROFILE_MEDIATES(P, T) ((P)->policy.start[(unsigned char) (T)])
+static inline unsigned int PROFILE_MEDIATES(struct aa_profile *profile,
+ unsigned char class)
+{
+ if (class <= AA_CLASS_LAST)
+ return profile->policy.start[class];
+ else
+ return aa_dfa_match_len(profile->policy.dfa,
+ profile->policy.start[0], &class, 1);
+}
+
static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile,
u16 AF) {
unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET);
diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h
index 9605f18..3df6f80 100644
--- a/security/apparmor/include/policy_ns.h
+++ b/security/apparmor/include/policy_ns.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_NAMESPACE_H
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h
index 8db4ab7..46aefae 100644
--- a/security/apparmor/include/policy_unpack.h
+++ b/security/apparmor/include/policy_unpack.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __POLICY_INTERFACE_H
diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h
index c8fd99c..3168943 100644
--- a/security/apparmor/include/procattr.h
+++ b/security/apparmor/include/procattr.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_PROCATTR_H
diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h
index 76f1586..961d85d 100644
--- a/security/apparmor/include/resource.h
+++ b/security/apparmor/include/resource.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_RESOURCE_H
diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h
index dee6fa3..48ff1dd 100644
--- a/security/apparmor/include/secid.h
+++ b/security/apparmor/include/secid.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
* This file contains AppArmor security identifier (secid) definitions
*
* Copyright 2009-2018 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_SECID_H
@@ -22,6 +18,9 @@
/* secid value that will not be allocated */
#define AA_SECID_INVALID 0
+/* secid value that matches any other secid */
+#define AA_SECID_WILDCARD 1
+
struct aa_label *aa_secid_to_label(u32 secid);
int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h
index 55edaa1..f13d123 100644
--- a/security/apparmor/include/task.h
+++ b/security/apparmor/include/task.h
@@ -1,20 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AppArmor security module
*
* This file contains AppArmor task related definitions and mediation
*
* Copyright 2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#ifndef __AA_TASK_H
#define __AA_TASK_H
-#define task_ctx(X) ((X)->security)
+static inline struct aa_task_ctx *task_ctx(struct task_struct *task)
+{
+ return task->security + apparmor_blob_sizes.lbs_task;
+}
/*
* struct aa_task_ctx - information for current task label change
@@ -37,17 +36,6 @@
struct aa_label *aa_get_task_label(struct task_struct *task);
/**
- * aa_alloc_task_ctx - allocate a new task_ctx
- * @flags: gfp flags for allocation
- *
- * Returns: allocated buffer or NULL on failure
- */
-static inline struct aa_task_ctx *aa_alloc_task_ctx(gfp_t flags)
-{
- return kzalloc(sizeof(struct aa_task_ctx), flags);
-}
-
-/**
* aa_free_task_ctx - free a task_ctx
* @ctx: task_ctx to free (MAYBE NULL)
*/
@@ -57,8 +45,6 @@
aa_put_label(ctx->nnp);
aa_put_label(ctx->previous);
aa_put_label(ctx->onexec);
-
- kzfree(ctx);
}
}
diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c
index 527ea15..4ecedff 100644
--- a/security/apparmor/ipc.c
+++ b/security/apparmor/ipc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/gfp.h>
@@ -107,7 +103,8 @@
aad(sa)->label = &tracer->label;
aad(sa)->peer = tracee;
aad(sa)->request = 0;
- aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE, 1);
+ aad(sa)->error = aa_capable(&tracer->label, CAP_SYS_PTRACE,
+ CAP_OPT_NONE);
return aa_audit(AUDIT_APPARMOR_AUTO, tracer, sa, audit_ptrace_cb);
}
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
index ba11bdf..59f1cc2 100644
--- a/security/apparmor/label.c
+++ b/security/apparmor/label.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
* This file contains AppArmor label definitions
*
* Copyright 2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/audit.h>
@@ -80,7 +76,7 @@
AA_BUG(!orig);
AA_BUG(!new);
- lockdep_assert_held_exclusive(&labels_set(orig)->lock);
+ lockdep_assert_held_write(&labels_set(orig)->lock);
tmp = rcu_dereference_protected(orig->proxy->label,
&labels_ns(orig)->lock);
@@ -570,7 +566,7 @@
AA_BUG(!ls);
AA_BUG(!label);
- lockdep_assert_held_exclusive(&ls->lock);
+ lockdep_assert_held_write(&ls->lock);
if (new)
__aa_proxy_redirect(label, new);
@@ -607,7 +603,7 @@
AA_BUG(!ls);
AA_BUG(!old);
AA_BUG(!new);
- lockdep_assert_held_exclusive(&ls->lock);
+ lockdep_assert_held_write(&ls->lock);
AA_BUG(new->flags & FLAG_IN_TREE);
if (!label_is_stale(old))
@@ -644,7 +640,7 @@
AA_BUG(!ls);
AA_BUG(!label);
AA_BUG(labels_set(label) != ls);
- lockdep_assert_held_exclusive(&ls->lock);
+ lockdep_assert_held_write(&ls->lock);
AA_BUG(label->flags & FLAG_IN_TREE);
/* Figure out where to put new node */
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index 76491e7..30c246a 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/ctype.h>
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 8b8b706..ec3a928 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/lsm_hooks.h>
@@ -23,7 +19,10 @@
#include <linux/sysctl.h>
#include <linux/audit.h>
#include <linux/user_namespace.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
#include <net/sock.h>
+#include <uapi/linux/mount.h>
#include "include/apparmor.h"
#include "include/apparmorfs.h"
@@ -57,7 +56,7 @@
static void apparmor_cred_free(struct cred *cred)
{
aa_put_label(cred_label(cred));
- cred_label(cred) = NULL;
+ set_cred_label(cred, NULL);
}
/*
@@ -65,7 +64,7 @@
*/
static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
- cred_label(cred) = NULL;
+ set_cred_label(cred, NULL);
return 0;
}
@@ -75,7 +74,7 @@
static int apparmor_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- cred_label(new) = aa_get_newest_label(cred_label(old));
+ set_cred_label(new, aa_get_newest_label(cred_label(old)));
return 0;
}
@@ -84,26 +83,21 @@
*/
static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
{
- cred_label(new) = aa_get_newest_label(cred_label(old));
+ set_cred_label(new, aa_get_newest_label(cred_label(old)));
}
static void apparmor_task_free(struct task_struct *task)
{
aa_free_task_ctx(task_ctx(task));
- task_ctx(task) = NULL;
}
static int apparmor_task_alloc(struct task_struct *task,
unsigned long clone_flags)
{
- struct aa_task_ctx *new = aa_alloc_task_ctx(GFP_KERNEL);
-
- if (!new)
- return -ENOMEM;
+ struct aa_task_ctx *new = task_ctx(task);
aa_dup_task_ctx(new, task_ctx(current));
- task_ctx(task) = new;
return 0;
}
@@ -114,13 +108,13 @@
struct aa_label *tracer, *tracee;
int error;
- tracer = begin_current_label_crit_section();
+ tracer = __begin_current_label_crit_section();
tracee = aa_get_task_label(child);
error = aa_may_ptrace(tracer, tracee,
(mode & PTRACE_MODE_READ) ? AA_PTRACE_READ
: AA_PTRACE_TRACE);
aa_put_label(tracee);
- end_current_label_crit_section(tracer);
+ __end_current_label_crit_section(tracer);
return error;
}
@@ -130,11 +124,11 @@
struct aa_label *tracer, *tracee;
int error;
- tracee = begin_current_label_crit_section();
+ tracee = __begin_current_label_crit_section();
tracer = aa_get_task_label(parent);
error = aa_may_ptrace(tracer, tracee, AA_PTRACE_TRACE);
aa_put_label(tracer);
- end_current_label_crit_section(tracee);
+ __end_current_label_crit_section(tracee);
return error;
}
@@ -174,14 +168,14 @@
}
static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
- int cap, int audit)
+ int cap, unsigned int opts)
{
struct aa_label *label;
int error = 0;
label = aa_get_newest_cred_label(cred);
if (!unconfined(label))
- error = aa_capable(label, cap, audit);
+ error = aa_capable(label, cap, opts);
aa_put_label(label);
return error;
@@ -431,21 +425,21 @@
static int apparmor_file_alloc_security(struct file *file)
{
- int error = 0;
-
- /* freed by apparmor_file_free_security */
+ struct aa_file_ctx *ctx = file_ctx(file);
struct aa_label *label = begin_current_label_crit_section();
- file->f_security = aa_alloc_file_ctx(label, GFP_KERNEL);
- if (!file_ctx(file))
- error = -ENOMEM;
- end_current_label_crit_section(label);
- return error;
+ spin_lock_init(&ctx->lock);
+ rcu_assign_pointer(ctx->label, aa_get_label(label));
+ end_current_label_crit_section(label);
+ return 0;
}
static void apparmor_file_free_security(struct file *file)
{
- aa_free_file_ctx(file_ctx(file));
+ struct aa_file_ctx *ctx = file_ctx(file);
+
+ if (ctx)
+ aa_put_label(rcu_access_pointer(ctx->label));
}
static int common_file_perm(const char *op, struct file *file, u32 mask)
@@ -732,7 +726,7 @@
return error;
}
-static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
+static int apparmor_task_kill(struct task_struct *target, struct kernel_siginfo *info,
int sig, const struct cred *cred)
{
struct aa_label *cl, *tl;
@@ -1020,6 +1014,7 @@
return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock);
}
+#ifdef CONFIG_NETWORK_SECMARK
/**
* apparmor_socket_sock_recv_skb - check perms before associating skb to sk
*
@@ -1030,8 +1025,15 @@
*/
static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
- return 0;
+ struct aa_sk_ctx *ctx = SK_CTX(sk);
+
+ if (!skb->secmark)
+ return 0;
+
+ return apparmor_secmark_check(ctx->label, OP_RECVMSG, AA_MAY_RECEIVE,
+ skb->secmark, sk);
}
+#endif
static struct aa_label *sk_peer_label(struct sock *sk)
@@ -1126,6 +1128,29 @@
ctx->label = aa_get_current_label();
}
+#ifdef CONFIG_NETWORK_SECMARK
+static int apparmor_inet_conn_request(struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+{
+ struct aa_sk_ctx *ctx = SK_CTX(sk);
+
+ if (!skb->secmark)
+ return 0;
+
+ return apparmor_secmark_check(ctx->label, OP_CONNECT, AA_MAY_CONNECT,
+ skb->secmark, sk);
+}
+#endif
+
+/*
+ * The cred blob is a pointer to, not an instance of, an aa_task_ctx.
+ */
+struct lsm_blob_sizes apparmor_blob_sizes __lsm_ro_after_init = {
+ .lbs_cred = sizeof(struct aa_task_ctx *),
+ .lbs_file = sizeof(struct aa_file_ctx),
+ .lbs_task = sizeof(struct aa_task_ctx),
+};
+
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
@@ -1177,12 +1202,17 @@
LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt),
LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt),
LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown),
+#ifdef CONFIG_NETWORK_SECMARK
LSM_HOOK_INIT(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb),
+#endif
LSM_HOOK_INIT(socket_getpeersec_stream,
apparmor_socket_getpeersec_stream),
LSM_HOOK_INIT(socket_getpeersec_dgram,
apparmor_socket_getpeersec_dgram),
LSM_HOOK_INIT(sock_graft, apparmor_sock_graft),
+#ifdef CONFIG_NETWORK_SECMARK
+ LSM_HOOK_INIT(inet_conn_request, apparmor_inet_conn_request),
+#endif
LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank),
LSM_HOOK_INIT(cred_free, apparmor_cred_free),
@@ -1302,9 +1332,16 @@
bool aa_g_paranoid_load = true;
module_param_named(paranoid_load, aa_g_paranoid_load, aabool, S_IRUGO);
+static int param_get_aaintbool(char *buffer, const struct kernel_param *kp);
+static int param_set_aaintbool(const char *val, const struct kernel_param *kp);
+#define param_check_aaintbool param_check_int
+static const struct kernel_param_ops param_ops_aaintbool = {
+ .set = param_set_aaintbool,
+ .get = param_get_aaintbool
+};
/* Boot time disable flag */
-static bool apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE;
-module_param_named(enabled, apparmor_enabled, bool, S_IRUGO);
+static int apparmor_enabled __lsm_ro_after_init = 1;
+module_param_named(enabled, apparmor_enabled, aaintbool, 0444);
static int __init apparmor_enabled_setup(char *str)
{
@@ -1379,6 +1416,46 @@
return param_get_uint(buffer, kp);
}
+/* Can only be set before AppArmor is initialized (i.e. on boot cmdline). */
+static int param_set_aaintbool(const char *val, const struct kernel_param *kp)
+{
+ struct kernel_param kp_local;
+ bool value;
+ int error;
+
+ if (apparmor_initialized)
+ return -EPERM;
+
+ /* Create local copy, with arg pointing to bool type. */
+ value = !!*((int *)kp->arg);
+ memcpy(&kp_local, kp, sizeof(kp_local));
+ kp_local.arg = &value;
+
+ error = param_set_bool(val, &kp_local);
+ if (!error)
+ *((int *)kp->arg) = *((bool *)kp_local.arg);
+ return error;
+}
+
+/*
+ * To avoid changing /sys/module/apparmor/parameters/enabled from Y/N to
+ * 1/0, this converts the "int that is actually bool" back to bool for
+ * display in the /sys filesystem, while keeping it "int" for the LSM
+ * infrastructure.
+ */
+static int param_get_aaintbool(char *buffer, const struct kernel_param *kp)
+{
+ struct kernel_param kp_local;
+ bool value;
+
+ /* Create local copy, with arg pointing to bool type. */
+ value = !!*((int *)kp->arg);
+ memcpy(&kp_local, kp, sizeof(kp_local));
+ kp_local.arg = &value;
+
+ return param_get_bool(buffer, &kp_local);
+}
+
static int param_get_audit(char *buffer, const struct kernel_param *kp)
{
if (!apparmor_enabled)
@@ -1449,14 +1526,8 @@
static int __init set_init_ctx(void)
{
struct cred *cred = (struct cred *)current->real_cred;
- struct aa_task_ctx *ctx;
- ctx = aa_alloc_task_ctx(GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
-
- cred_label(cred) = aa_get_label(ns_unconfined(root_ns));
- task_ctx(current) = ctx;
+ set_cred_label(cred, aa_get_label(ns_unconfined(root_ns)));
return 0;
}
@@ -1538,16 +1609,103 @@
}
#endif /* CONFIG_SYSCTL */
+#if defined(CONFIG_NETFILTER) && defined(CONFIG_NETWORK_SECMARK)
+static unsigned int apparmor_ip_postroute(void *priv,
+ struct sk_buff *skb,
+ const struct nf_hook_state *state)
+{
+ struct aa_sk_ctx *ctx;
+ struct sock *sk;
+
+ if (!skb->secmark)
+ return NF_ACCEPT;
+
+ sk = skb_to_full_sk(skb);
+ if (sk == NULL)
+ return NF_ACCEPT;
+
+ ctx = SK_CTX(sk);
+ if (!apparmor_secmark_check(ctx->label, OP_SENDMSG, AA_MAY_SEND,
+ skb->secmark, sk))
+ return NF_ACCEPT;
+
+ return NF_DROP_ERR(-ECONNREFUSED);
+
+}
+
+static unsigned int apparmor_ipv4_postroute(void *priv,
+ struct sk_buff *skb,
+ const struct nf_hook_state *state)
+{
+ return apparmor_ip_postroute(priv, skb, state);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static unsigned int apparmor_ipv6_postroute(void *priv,
+ struct sk_buff *skb,
+ const struct nf_hook_state *state)
+{
+ return apparmor_ip_postroute(priv, skb, state);
+}
+#endif
+
+static const struct nf_hook_ops apparmor_nf_ops[] = {
+ {
+ .hook = apparmor_ipv4_postroute,
+ .pf = NFPROTO_IPV4,
+ .hooknum = NF_INET_POST_ROUTING,
+ .priority = NF_IP_PRI_SELINUX_FIRST,
+ },
+#if IS_ENABLED(CONFIG_IPV6)
+ {
+ .hook = apparmor_ipv6_postroute,
+ .pf = NFPROTO_IPV6,
+ .hooknum = NF_INET_POST_ROUTING,
+ .priority = NF_IP6_PRI_SELINUX_FIRST,
+ },
+#endif
+};
+
+static int __net_init apparmor_nf_register(struct net *net)
+{
+ int ret;
+
+ ret = nf_register_net_hooks(net, apparmor_nf_ops,
+ ARRAY_SIZE(apparmor_nf_ops));
+ return ret;
+}
+
+static void __net_exit apparmor_nf_unregister(struct net *net)
+{
+ nf_unregister_net_hooks(net, apparmor_nf_ops,
+ ARRAY_SIZE(apparmor_nf_ops));
+}
+
+static struct pernet_operations apparmor_net_ops = {
+ .init = apparmor_nf_register,
+ .exit = apparmor_nf_unregister,
+};
+
+static int __init apparmor_nf_ip_init(void)
+{
+ int err;
+
+ if (!apparmor_enabled)
+ return 0;
+
+ err = register_pernet_subsys(&apparmor_net_ops);
+ if (err)
+ panic("Apparmor: register_pernet_subsys: error %d\n", err);
+
+ return 0;
+}
+__initcall(apparmor_nf_ip_init);
+#endif
+
static int __init apparmor_init(void)
{
int error;
- if (!apparmor_enabled || !security_module_enable("apparmor")) {
- aa_info_message("AppArmor disabled by boot time parameter");
- apparmor_enabled = false;
- return 0;
- }
-
aa_secids_init();
error = aa_setup_dfa_engine();
@@ -1606,4 +1764,10 @@
return error;
}
-security_initcall(apparmor_init);
+DEFINE_LSM(apparmor) = {
+ .name = "apparmor",
+ .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
+ .enabled = &apparmor_enabled,
+ .blobs = &apparmor_blob_sizes,
+ .init = apparmor_init,
+};
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index 55f2ee5..6ccd373 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2012 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/errno.h>
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
index c1da224..17081c8 100644
--- a/security/apparmor/mount.c
+++ b/security/apparmor/mount.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,16 +6,12 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/namei.h>
+#include <uapi/linux/mount.h>
#include "include/apparmor.h"
#include "include/audit.h"
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
index bb24cfa..d8afc39 100644
--- a/security/apparmor/net.c
+++ b/security/apparmor/net.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include "include/apparmor.h"
@@ -18,6 +14,7 @@
#include "include/label.h"
#include "include/net.h"
#include "include/policy.h"
+#include "include/secid.h"
#include "net_names.h"
@@ -146,17 +143,20 @@
static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request,
struct sock *sk)
{
- struct aa_profile *profile;
- DEFINE_AUDIT_SK(sa, op, sk);
+ int error = 0;
AA_BUG(!label);
AA_BUG(!sk);
- if (unconfined(label))
- return 0;
+ if (!unconfined(label)) {
+ struct aa_profile *profile;
+ DEFINE_AUDIT_SK(sa, op, sk);
- return fn_for_each_confined(label, profile,
- aa_profile_af_sk_perm(profile, &sa, request, sk));
+ error = fn_for_each_confined(label, profile,
+ aa_profile_af_sk_perm(profile, &sa, request, sk));
+ }
+
+ return error;
}
int aa_sk_perm(const char *op, u32 request, struct sock *sk)
@@ -185,3 +185,70 @@
return aa_label_sk_perm(label, op, request, sock->sk);
}
+
+#ifdef CONFIG_NETWORK_SECMARK
+static int apparmor_secmark_init(struct aa_secmark *secmark)
+{
+ struct aa_label *label;
+
+ if (secmark->label[0] == '*') {
+ secmark->secid = AA_SECID_WILDCARD;
+ return 0;
+ }
+
+ label = aa_label_strn_parse(&root_ns->unconfined->label,
+ secmark->label, strlen(secmark->label),
+ GFP_ATOMIC, false, false);
+
+ if (IS_ERR(label))
+ return PTR_ERR(label);
+
+ secmark->secid = label->secid;
+
+ return 0;
+}
+
+static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
+ struct common_audit_data *sa, struct sock *sk)
+{
+ int i, ret;
+ struct aa_perms perms = { };
+
+ if (profile->secmark_count == 0)
+ return 0;
+
+ for (i = 0; i < profile->secmark_count; i++) {
+ if (!profile->secmark[i].secid) {
+ ret = apparmor_secmark_init(&profile->secmark[i]);
+ if (ret)
+ return ret;
+ }
+
+ if (profile->secmark[i].secid == secid ||
+ profile->secmark[i].secid == AA_SECID_WILDCARD) {
+ if (profile->secmark[i].deny)
+ perms.deny = ALL_PERMS_MASK;
+ else
+ perms.allow = ALL_PERMS_MASK;
+
+ if (profile->secmark[i].audit)
+ perms.audit = ALL_PERMS_MASK;
+ }
+ }
+
+ aa_apply_modes_to_perms(profile, &perms);
+
+ return aa_check_perms(profile, &perms, request, sa, audit_net_cb);
+}
+
+int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
+ u32 secid, struct sock *sk)
+{
+ struct aa_profile *profile;
+ DEFINE_AUDIT_SK(sa, op, sk);
+
+ return fn_for_each_confined(label, profile,
+ aa_secmark_perm(profile, request, secid,
+ &sa, sk));
+}
+#endif
diff --git a/security/apparmor/path.c b/security/apparmor/path.c
index 9d5de1d..c6da542 100644
--- a/security/apparmor/path.c
+++ b/security/apparmor/path.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/magic.h>
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 1590e2d..ade3330 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -6,12 +7,6 @@
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
*
- * 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 of the
- * License.
- *
- *
* AppArmor policy is based around profiles, which contain the rules a
* task is confined by. Every task in the system has a profile attached
* to it determined either by matching "unconfined" tasks against the
@@ -231,6 +226,9 @@
for (i = 0; i < profile->xattr_count; i++)
kzfree(profile->xattrs[i]);
kzfree(profile->xattrs);
+ for (i = 0; i < profile->secmark_count; i++)
+ kzfree(profile->secmark[i].label);
+ kzfree(profile->secmark);
kzfree(profile->dirname);
aa_put_dfa(profile->xmatch);
aa_put_dfa(profile->policy.dfa);
diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c
index 1a7cec5..d7ef540 100644
--- a/security/apparmor/policy_ns.c
+++ b/security/apparmor/policy_ns.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -6,11 +7,6 @@
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2017 Canonical Ltd.
*
- * 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 of the
- * License.
- *
* AppArmor policy namespaces, allow for different sets of policies
* to be loaded for tasks within the namespace.
*/
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index 21cb384..8cfc949 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -7,11 +8,6 @@
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
*
- * 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 of the
- * License.
- *
* AppArmor uses a serialized binary format for loading policy. To find
* policy format documentation see Documentation/admin-guide/LSM/apparmor.rst
* All policy is validated before it is used.
@@ -223,16 +219,21 @@
static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk)
{
size_t size = 0;
+ void *pos = e->pos;
if (!inbounds(e, sizeof(u16)))
- return 0;
+ goto fail;
size = le16_to_cpu(get_unaligned((__le16 *) e->pos));
e->pos += sizeof(__le16);
if (!inbounds(e, size))
- return 0;
+ goto fail;
*chunk = e->pos;
e->pos += size;
return size;
+
+fail:
+ e->pos = pos;
+ return 0;
}
/* unpack control byte */
@@ -276,7 +277,7 @@
char *tag = NULL;
size_t size = unpack_u16_chunk(e, &tag);
/* if a name is specified it must match. otherwise skip tag */
- if (name && (!size || strcmp(name, tag)))
+ if (name && (!size || tag[size-1] != '\0' || strcmp(name, tag)))
goto fail;
} else if (name) {
/* if a name is specified and there is no name tag fail */
@@ -292,51 +293,86 @@
return 0;
}
+static bool unpack_u8(struct aa_ext *e, u8 *data, const char *name)
+{
+ void *pos = e->pos;
+
+ if (unpack_nameX(e, AA_U8, name)) {
+ if (!inbounds(e, sizeof(u8)))
+ goto fail;
+ if (data)
+ *data = get_unaligned((u8 *)e->pos);
+ e->pos += sizeof(u8);
+ return 1;
+ }
+
+fail:
+ e->pos = pos;
+ return 0;
+}
+
static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name)
{
+ void *pos = e->pos;
+
if (unpack_nameX(e, AA_U32, name)) {
if (!inbounds(e, sizeof(u32)))
- return 0;
+ goto fail;
if (data)
*data = le32_to_cpu(get_unaligned((__le32 *) e->pos));
e->pos += sizeof(u32);
return 1;
}
+
+fail:
+ e->pos = pos;
return 0;
}
static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name)
{
+ void *pos = e->pos;
+
if (unpack_nameX(e, AA_U64, name)) {
if (!inbounds(e, sizeof(u64)))
- return 0;
+ goto fail;
if (data)
*data = le64_to_cpu(get_unaligned((__le64 *) e->pos));
e->pos += sizeof(u64);
return 1;
}
+
+fail:
+ e->pos = pos;
return 0;
}
static size_t unpack_array(struct aa_ext *e, const char *name)
{
+ void *pos = e->pos;
+
if (unpack_nameX(e, AA_ARRAY, name)) {
int size;
if (!inbounds(e, sizeof(u16)))
- return 0;
+ goto fail;
size = (int)le16_to_cpu(get_unaligned((__le16 *) e->pos));
e->pos += sizeof(u16);
return size;
}
+
+fail:
+ e->pos = pos;
return 0;
}
static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name)
{
+ void *pos = e->pos;
+
if (unpack_nameX(e, AA_BLOB, name)) {
u32 size;
if (!inbounds(e, sizeof(u32)))
- return 0;
+ goto fail;
size = le32_to_cpu(get_unaligned((__le32 *) e->pos));
e->pos += sizeof(u32);
if (inbounds(e, (size_t) size)) {
@@ -345,6 +381,9 @@
return size;
}
}
+
+fail:
+ e->pos = pos;
return 0;
}
@@ -361,9 +400,10 @@
if (src_str[size - 1] != 0)
goto fail;
*string = src_str;
+
+ return size;
}
}
- return size;
fail:
e->pos = pos;
@@ -529,6 +569,50 @@
return 0;
}
+static bool unpack_secmark(struct aa_ext *e, struct aa_profile *profile)
+{
+ void *pos = e->pos;
+ int i, size;
+
+ if (unpack_nameX(e, AA_STRUCT, "secmark")) {
+ size = unpack_array(e, NULL);
+
+ profile->secmark = kcalloc(size, sizeof(struct aa_secmark),
+ GFP_KERNEL);
+ if (!profile->secmark)
+ goto fail;
+
+ profile->secmark_count = size;
+
+ for (i = 0; i < size; i++) {
+ if (!unpack_u8(e, &profile->secmark[i].audit, NULL))
+ goto fail;
+ if (!unpack_u8(e, &profile->secmark[i].deny, NULL))
+ goto fail;
+ if (!unpack_strdup(e, &profile->secmark[i].label, NULL))
+ goto fail;
+ }
+ if (!unpack_nameX(e, AA_ARRAYEND, NULL))
+ goto fail;
+ if (!unpack_nameX(e, AA_STRUCTEND, NULL))
+ goto fail;
+ }
+
+ return 1;
+
+fail:
+ if (profile->secmark) {
+ for (i = 0; i < size; i++)
+ kfree(profile->secmark[i].label);
+ kfree(profile->secmark);
+ profile->secmark_count = 0;
+ profile->secmark = NULL;
+ }
+
+ e->pos = pos;
+ return 0;
+}
+
static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
{
void *pos = e->pos;
@@ -727,6 +811,11 @@
goto fail;
}
+ if (!unpack_secmark(e, profile)) {
+ info = "failed to unpack profile secmark rules";
+ goto fail;
+ }
+
if (unpack_nameX(e, AA_STRUCT, "policydb")) {
/* generic policy dfa - optional and may be NULL */
info = "failed to unpack policydb";
diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c
index 80c34ed..c929bf4 100644
--- a/security/apparmor/procattr.c
+++ b/security/apparmor/procattr.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include "include/apparmor.h"
diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c
index 95fd26d..1ae4874 100644
--- a/security/apparmor/resource.c
+++ b/security/apparmor/resource.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 1998-2008 Novell/SUSE
* Copyright 2009-2010 Canonical Ltd.
- *
- * 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 of the
- * License.
*/
#include <linux/audit.h>
@@ -124,7 +120,7 @@
*/
if (label != peer &&
- aa_capable(label, CAP_SYS_RESOURCE, SECURITY_CAP_NOAUDIT) != 0)
+ aa_capable(label, CAP_SYS_RESOURCE, CAP_OPT_NOAUDIT) != 0)
error = fn_for_each(label, profile,
audit_resource(profile, resource,
new_rlim->rlim_max, peer,
diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c
index 4ccec1b..ce545f9 100644
--- a/security/apparmor/secid.c
+++ b/security/apparmor/secid.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,12 +6,6 @@
*
* Copyright 2009-2017 Canonical Ltd.
*
- * 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 of the
- * License.
- *
- *
* AppArmor allocates a unique secid for every label used. If a label
* is replaced it receives the secid of the label it is replacing.
*/
@@ -32,8 +27,7 @@
* secids - do not pin labels with a refcount. They rely on the label
* properly updating/freeing them
*/
-
-#define AA_FIRST_SECID 1
+#define AA_FIRST_SECID 2
static DEFINE_IDR(aa_secids);
static DEFINE_SPINLOCK(secid_lock);
diff --git a/security/apparmor/task.c b/security/apparmor/task.c
index c6b78a1..d17130e 100644
--- a/security/apparmor/task.c
+++ b/security/apparmor/task.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AppArmor security module
*
@@ -5,11 +6,6 @@
*
* Copyright 2017 Canonical Ltd.
*
- * 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 of the
- * License.
- *
* TODO
* If a task uses change_hat it currently does not return to the old
* cred or task context but instead creates a new one. Ideally the task
@@ -81,7 +77,7 @@
*/
aa_get_label(label);
aa_put_label(cred_label(new));
- cred_label(new) = label;
+ set_cred_label(new, label);
commit_creds(new);
return 0;
@@ -138,7 +134,7 @@
return -EACCES;
}
- cred_label(new) = aa_get_newest_label(label);
+ set_cred_label(new, aa_get_newest_label(label));
/* clear exec on switching context */
aa_put_label(ctx->onexec);
ctx->onexec = NULL;
@@ -172,7 +168,7 @@
return -ENOMEM;
aa_put_label(cred_label(new));
- cred_label(new) = aa_get_newest_label(ctx->previous);
+ set_cred_label(new, aa_get_newest_label(ctx->previous));
AA_BUG(!cred_label(new));
/* clear exec && prev information when restoring to previous context */
aa_clear_task_ctx_trans(ctx);
diff --git a/security/commoncap.c b/security/commoncap.c
index 2e489d6..f4ee0ae 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -1,15 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Common capabilities, needed by capability.o.
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
*/
#include <linux/capability.h>
#include <linux/audit.h>
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/lsm_hooks.h>
@@ -58,7 +52,7 @@
* @cred: The credentials to use
* @ns: The user namespace in which we need the capability
* @cap: The capability to check for
- * @audit: Whether to write an audit message or not
+ * @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.
@@ -69,7 +63,7 @@
* kernel's capable() and has_capability() returns 1 for this case.
*/
int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
- int cap, int audit)
+ int cap, unsigned int opts)
{
struct user_namespace *ns = targ_ns;
@@ -223,12 +217,11 @@
*/
static inline int cap_inh_is_capped(void)
{
-
/* they are so limited unless the current task has the CAP_SETPCAP
* capability
*/
if (cap_capable(current_cred(), current_cred()->user_ns,
- CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0)
+ CAP_SETPCAP, CAP_OPT_NONE) == 0)
return 0;
return 1;
}
@@ -644,6 +637,8 @@
cpu_caps->permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
cpu_caps->inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK;
+ cpu_caps->rootid = rootkuid;
+
return 0;
}
@@ -684,9 +679,6 @@
}
rc = bprm_caps_from_vfs_caps(&vcaps, bprm, effective, has_fcap);
- if (rc == -EINVAL)
- printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
- __func__, rc, bprm->filename);
out:
if (rc)
@@ -923,7 +915,7 @@
/* Ignore non-security xattrs */
if (strncmp(name, XATTR_SECURITY_PREFIX,
- sizeof(XATTR_SECURITY_PREFIX) - 1) != 0)
+ XATTR_SECURITY_PREFIX_LEN) != 0)
return 0;
/*
@@ -955,7 +947,7 @@
/* Ignore non-security xattrs */
if (strncmp(name, XATTR_SECURITY_PREFIX,
- sizeof(XATTR_SECURITY_PREFIX) - 1) != 0)
+ XATTR_SECURITY_PREFIX_LEN) != 0)
return 0;
if (strcmp(name, XATTR_NAME_CAPS) == 0) {
@@ -1212,8 +1204,9 @@
|| ((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,
- SECURITY_CAP_AUDIT) != 0) /*[4]*/
+ current_cred()->user_ns,
+ CAP_SETPCAP,
+ CAP_OPT_NONE) != 0) /*[4]*/
/*
* [1] no changing of bits that are locked
* [2] no unlocking of locks
@@ -1308,9 +1301,10 @@
{
int cap_sys_admin = 0;
- if (cap_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN,
- SECURITY_CAP_NOAUDIT) == 0)
+ if (cap_capable(current_cred(), &init_user_ns,
+ CAP_SYS_ADMIN, CAP_OPT_NOAUDIT) == 0)
cap_sys_admin = 1;
+
return cap_sys_admin;
}
@@ -1329,7 +1323,7 @@
if (addr < dac_mmap_min_addr) {
ret = cap_capable(current_cred(), &init_user_ns, CAP_SYS_RAWIO,
- SECURITY_CAP_AUDIT);
+ CAP_OPT_NONE);
/* set PF_SUPERPRIV if it turns out we allow the low mmap */
if (ret == 0)
current->flags |= PF_SUPERPRIV;
@@ -1345,7 +1339,7 @@
#ifdef CONFIG_SECURITY
-struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
+static struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(capable, cap_capable),
LSM_HOOK_INIT(settime, cap_settime),
LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check),
@@ -1366,10 +1360,17 @@
LSM_HOOK_INIT(vm_enough_memory, cap_vm_enough_memory),
};
-void __init capability_add_hooks(void)
+static int __init capability_init(void)
{
security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
"capability");
+ return 0;
}
+DEFINE_LSM(capability) = {
+ .name = "capability",
+ .order = LSM_ORDER_FIRST,
+ .init = capability_init,
+};
+
#endif /* CONFIG_SECURITY */
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index cd97929..725674f 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -509,7 +509,7 @@
* This is one of the three key functions for hierarchy implementation.
* This function is responsible for re-evaluating all the cgroup's active
* exceptions due to a parent's exception change.
- * Refer to Documentation/cgroup-v1/devices.txt for more details.
+ * Refer to Documentation/admin-guide/cgroup-v1/devices.rst for more details.
*/
static void revalidate_active_exceptions(struct dev_cgroup *devcg)
{
@@ -560,7 +560,7 @@
devcg->behavior == DEVCG_DEFAULT_ALLOW) {
rc = dev_exception_add(devcg, ex);
if (rc)
- break;
+ return rc;
} else {
/*
* in the other possible cases:
diff --git a/security/inode.c b/security/inode.c
index 8dd9ca8..6c32693 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -1,20 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* inode.c - securityfs
*
* Copyright (C) 2005 Greg Kroah-Hartman <gregkh@suse.de>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
* Based on fs/debugfs/inode.c which had the following copyright notice:
* Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2004 IBM Inc.
*/
/* #define DEBUG */
-#include <linux/module.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
#include <linux/fs.h>
+#include <linux/fs_context.h>
#include <linux/mount.h>
#include <linux/pagemap.h>
#include <linux/init.h>
@@ -26,20 +25,19 @@
static struct vfsmount *mount;
static int mount_count;
-static void securityfs_evict_inode(struct inode *inode)
+static void securityfs_free_inode(struct inode *inode)
{
- truncate_inode_pages_final(&inode->i_data);
- clear_inode(inode);
if (S_ISLNK(inode->i_mode))
kfree(inode->i_link);
+ free_inode_nonrcu(inode);
}
static const struct super_operations securityfs_super_operations = {
.statfs = simple_statfs,
- .evict_inode = securityfs_evict_inode,
+ .free_inode = securityfs_free_inode,
};
-static int fill_super(struct super_block *sb, void *data, int silent)
+static int securityfs_fill_super(struct super_block *sb, struct fs_context *fc)
{
static const struct tree_descr files[] = {{""}};
int error;
@@ -53,17 +51,25 @@
return 0;
}
-static struct dentry *get_sb(struct file_system_type *fs_type,
- int flags, const char *dev_name,
- void *data)
+static int securityfs_get_tree(struct fs_context *fc)
{
- return mount_single(fs_type, flags, data, fill_super);
+ return get_tree_single(fc, securityfs_fill_super);
+}
+
+static const struct fs_context_operations securityfs_context_ops = {
+ .get_tree = securityfs_get_tree,
+};
+
+static int securityfs_init_fs_context(struct fs_context *fc)
+{
+ fc->ops = &securityfs_context_ops;
+ return 0;
}
static struct file_system_type fs_type = {
.owner = THIS_MODULE,
.name = "securityfs",
- .mount = get_sb,
+ .init_fs_context = securityfs_init_fs_context,
.kill_sb = kill_litter_super,
};
@@ -341,7 +347,4 @@
#endif
return 0;
}
-
core_initcall(securityfs_init);
-MODULE_LICENSE("GPL");
-
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
index da95658..0bae6ad 100644
--- a/security/integrity/Kconfig
+++ b/security/integrity/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
config INTEGRITY
bool "Integrity subsystem"
@@ -17,8 +18,8 @@
config INTEGRITY_SIGNATURE
bool "Digital signature verification using multiple keyrings"
- depends on KEYS
default n
+ select KEYS
select SIGNATURE
help
This option enables digital signature verification support
@@ -51,6 +52,26 @@
.evm keyrings be signed by a key on the system trusted
keyring.
+config INTEGRITY_PLATFORM_KEYRING
+ bool "Provide keyring for platform/firmware trusted keys"
+ depends on INTEGRITY_ASYMMETRIC_KEYS
+ depends on SYSTEM_BLACKLIST_KEYRING
+ help
+ Provide a separate, distinct keyring for platform trusted keys, which
+ the kernel automatically populates during initialization from values
+ provided by the platform for verifying the kexec'ed kerned image
+ and, possibly, the initramfs signature.
+
+config LOAD_UEFI_KEYS
+ depends on INTEGRITY_PLATFORM_KEYRING
+ depends on EFI
+ def_bool y
+
+config LOAD_IPL_KEYS
+ depends on INTEGRITY_PLATFORM_KEYRING
+ depends on S390
+ def_bool y
+
config INTEGRITY_AUDIT
bool "Enables integrity auditing support "
depends on AUDIT
@@ -66,7 +87,7 @@
be enabled by specifying 'integrity_audit=1' on the kernel
command line.
-source security/integrity/ima/Kconfig
-source security/integrity/evm/Kconfig
+source "security/integrity/ima/Kconfig"
+source "security/integrity/evm/Kconfig"
endif # if INTEGRITY
diff --git a/security/integrity/Makefile b/security/integrity/Makefile
index 04d6e46..35e6ca7 100644
--- a/security/integrity/Makefile
+++ b/security/integrity/Makefile
@@ -9,8 +9,10 @@
integrity-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o
integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
+integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o
+integrity-$(CONFIG_LOAD_UEFI_KEYS) += platform_certs/efi_parser.o \
+ platform_certs/load_uefi.o
+integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o
-subdir-$(CONFIG_IMA) += ima
obj-$(CONFIG_IMA) += ima/
-subdir-$(CONFIG_EVM) += evm
obj-$(CONFIG_EVM) += evm/
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 9bb0a7f..ea1aae3 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Intel Corporation
*
* Author:
* Dmitry Kasatkin <dmitry.kasatkin@intel.com>
- *
- * 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 of the License.
- *
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -26,7 +22,7 @@
static struct key *keyring[INTEGRITY_KEYRING_MAX];
-static const char *keyring_name[INTEGRITY_KEYRING_MAX] = {
+static const char * const keyring_name[INTEGRITY_KEYRING_MAX] = {
#ifndef CONFIG_INTEGRITY_TRUSTED_KEYRING
"_evm",
"_ima",
@@ -34,26 +30,19 @@
".evm",
".ima",
#endif
- "_module",
+ ".platform",
};
-#ifdef CONFIG_INTEGRITY_TRUSTED_KEYRING
-static bool init_keyring __initdata = true;
-#else
-static bool init_keyring __initdata;
-#endif
-
#ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
#define restrict_link_to_ima restrict_link_by_builtin_and_secondary_trusted
#else
#define restrict_link_to_ima restrict_link_by_builtin_trusted
#endif
-int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
- const char *digest, int digestlen)
+static struct key *integrity_keyring_from_id(const unsigned int id)
{
- if (id >= INTEGRITY_KEYRING_MAX || siglen < 2)
- return -EINVAL;
+ if (id >= INTEGRITY_KEYRING_MAX)
+ return ERR_PTR(-EINVAL);
if (!keyring[id]) {
keyring[id] =
@@ -62,30 +51,86 @@
int err = PTR_ERR(keyring[id]);
pr_err("no %s keyring: %d\n", keyring_name[id], err);
keyring[id] = NULL;
- return err;
+ return ERR_PTR(err);
}
}
+ return keyring[id];
+}
+
+int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
+ const char *digest, int digestlen)
+{
+ struct key *keyring;
+
+ if (siglen < 2)
+ return -EINVAL;
+
+ keyring = integrity_keyring_from_id(id);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
+
switch (sig[1]) {
case 1:
/* v1 API expect signature without xattr type */
- return digsig_verify(keyring[id], sig + 1, siglen - 1,
- digest, digestlen);
+ return digsig_verify(keyring, sig + 1, siglen - 1, digest,
+ digestlen);
case 2:
- return asymmetric_verify(keyring[id], sig, siglen,
- digest, digestlen);
+ return asymmetric_verify(keyring, sig, siglen, digest,
+ digestlen);
}
return -EOPNOTSUPP;
}
-int __init integrity_init_keyring(const unsigned int id)
+int integrity_modsig_verify(const unsigned int id, const struct modsig *modsig)
+{
+ struct key *keyring;
+
+ keyring = integrity_keyring_from_id(id);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
+
+ return ima_modsig_verify(keyring, modsig);
+}
+
+static int __init __integrity_init_keyring(const unsigned int id,
+ key_perm_t perm,
+ struct key_restriction *restriction)
{
const struct cred *cred = current_cred();
- struct key_restriction *restriction;
int err = 0;
- if (!init_keyring)
+ keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
+ KGIDT_INIT(0), cred, perm,
+ KEY_ALLOC_NOT_IN_QUOTA, restriction, NULL);
+ if (IS_ERR(keyring[id])) {
+ err = PTR_ERR(keyring[id]);
+ pr_info("Can't allocate %s keyring (%d)\n",
+ keyring_name[id], err);
+ keyring[id] = NULL;
+ } else {
+ if (id == INTEGRITY_KEYRING_PLATFORM)
+ set_platform_trusted_keys(keyring[id]);
+ }
+
+ return err;
+}
+
+int __init integrity_init_keyring(const unsigned int id)
+{
+ struct key_restriction *restriction;
+ key_perm_t perm;
+
+ perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW
+ | KEY_USR_READ | KEY_USR_SEARCH;
+
+ if (id == INTEGRITY_KEYRING_PLATFORM) {
+ restriction = NULL;
+ goto out;
+ }
+
+ if (!IS_ENABLED(CONFIG_INTEGRITY_TRUSTED_KEYRING))
return 0;
restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
@@ -93,32 +138,43 @@
return -ENOMEM;
restriction->check = restrict_link_to_ima;
+ perm |= KEY_USR_WRITE;
- keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
- KGIDT_INIT(0), cred,
- ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
- KEY_USR_VIEW | KEY_USR_READ |
- KEY_USR_WRITE | KEY_USR_SEARCH),
- KEY_ALLOC_NOT_IN_QUOTA,
- restriction, NULL);
- if (IS_ERR(keyring[id])) {
- err = PTR_ERR(keyring[id]);
- pr_info("Can't allocate %s keyring (%d)\n",
- keyring_name[id], err);
- keyring[id] = NULL;
+out:
+ return __integrity_init_keyring(id, perm, restriction);
+}
+
+int __init integrity_add_key(const unsigned int id, const void *data,
+ off_t size, key_perm_t perm)
+{
+ key_ref_t key;
+ int rc = 0;
+
+ if (!keyring[id])
+ return -EINVAL;
+
+ key = key_create_or_update(make_key_ref(keyring[id], 1), "asymmetric",
+ NULL, data, size, perm,
+ KEY_ALLOC_NOT_IN_QUOTA);
+ if (IS_ERR(key)) {
+ rc = PTR_ERR(key);
+ pr_err("Problem loading X.509 certificate %d\n", rc);
+ } else {
+ pr_notice("Loaded X.509 cert '%s'\n",
+ key_ref_to_ptr(key)->description);
+ key_ref_put(key);
}
- return err;
+
+ return rc;
+
}
int __init integrity_load_x509(const unsigned int id, const char *path)
{
- key_ref_t key;
void *data;
loff_t size;
int rc;
-
- if (!keyring[id])
- return -EINVAL;
+ key_perm_t perm;
rc = kernel_read_file_from_path(path, &data, &size, 0,
READING_X509_CERTIFICATE);
@@ -127,23 +183,21 @@
return rc;
}
- key = key_create_or_update(make_key_ref(keyring[id], 1),
- "asymmetric",
- NULL,
- data,
- size,
- ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
- KEY_USR_VIEW | KEY_USR_READ),
- KEY_ALLOC_NOT_IN_QUOTA);
- if (IS_ERR(key)) {
- rc = PTR_ERR(key);
- pr_err("Problem loading X.509 certificate (%d): %s\n",
- rc, path);
- } else {
- pr_notice("Loaded X.509 cert '%s': %s\n",
- key_ref_to_ptr(key)->description, path);
- key_ref_put(key);
- }
+ perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ;
+
+ pr_info("Loading X.509 certificate: %s\n", path);
+ rc = integrity_add_key(id, (const void *)data, size, perm);
+
vfree(data);
- return 0;
+ return rc;
+}
+
+int __init integrity_load_cert(const unsigned int id, const char *source,
+ const void *data, size_t len, key_perm_t perm)
+{
+ if (!data)
+ return -EINVAL;
+
+ pr_info("Loading X.509 certificate: %s\n", source);
+ return integrity_add_key(id, data, len, perm);
}
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 6dc0751..55aec16 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Intel Corporation
*
* Author:
* Dmitry Kasatkin <dmitry.kasatkin@intel.com>
- *
- * 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 of the License.
- *
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -39,7 +35,7 @@
key_ref_t kref;
kref = keyring_search(make_key_ref(key, 1),
- &key_type_asymmetric, name);
+ &key_type_asymmetric, name, true);
if (!IS_ERR(kref)) {
pr_err("Key '%s' is in ima_blacklist_keyring\n", name);
return ERR_PTR(-EKEYREJECTED);
@@ -51,7 +47,7 @@
key_ref_t kref;
kref = keyring_search(make_key_ref(keyring, 1),
- &key_type_asymmetric, name);
+ &key_type_asymmetric, name, true);
if (IS_ERR(kref))
key = ERR_CAST(kref);
else
@@ -104,8 +100,16 @@
memset(&pks, 0, sizeof(pks));
- pks.pkey_algo = "rsa";
pks.hash_algo = hash_algo_name[hdr->hash_algo];
+ if (hdr->hash_algo == HASH_ALGO_STREEBOG_256 ||
+ hdr->hash_algo == HASH_ALGO_STREEBOG_512) {
+ /* EC-RDSA and Streebog should go together. */
+ pks.pkey_algo = "ecrdsa";
+ pks.encoding = "raw";
+ } else {
+ pks.pkey_algo = "rsa";
+ pks.encoding = "pkcs1";
+ }
pks.digest = (u8 *)data;
pks.digest_size = datalen;
pks.s = hdr->sig;
diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig
index 6022185..a6e19d2 100644
--- a/security/integrity/evm/Kconfig
+++ b/security/integrity/evm/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config EVM
bool "EVM support"
select KEYS
diff --git a/security/integrity/evm/Makefile b/security/integrity/evm/Makefile
index 7393c41..a56f561 100644
--- a/security/integrity/evm/Makefile
+++ b/security/integrity/evm/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for building the Extended Verification Module(EVM)
#
diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
index c3f437f..f2fef2b 100644
--- a/security/integrity/evm/evm.h
+++ b/security/integrity/evm/evm.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2005-2010 IBM Corporation
*
@@ -5,12 +6,7 @@
* Mimi Zohar <zohar@us.ibm.com>
* Kylene Hall <kjhall@us.ibm.com>
*
- * 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 of the License.
- *
* File: evm.h
- *
*/
#ifndef __INTEGRITY_EVM_H
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c
index 8a3905b..d485f6f 100644
--- a/security/integrity/evm/evm_crypto.c
+++ b/security/integrity/evm/evm_crypto.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005-2010 IBM Corporation
*
@@ -5,17 +6,13 @@
* Mimi Zohar <zohar@us.ibm.com>
* Kylene Hall <kjhall@us.ibm.com>
*
- * 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 of the License.
- *
* File: evm_crypto.c
* Using root's kernel master key (kmk), calculate the HMAC
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/crypto.h>
#include <linux/xattr.h>
#include <linux/evm.h>
@@ -27,7 +24,7 @@
#define EVMKEY "evm-key"
#define MAX_KEY_SIZE 128
static unsigned char evmkey[MAX_KEY_SIZE];
-static int evmkey_len = MAX_KEY_SIZE;
+static const int evmkey_len = MAX_KEY_SIZE;
struct crypto_shash *hmac_tfm;
static struct crypto_shash *evm_tfm[HASH_ALGO__LAST];
@@ -38,7 +35,7 @@
static unsigned long evm_set_key_flags;
-static char * const evm_hmac = "hmac(sha1)";
+static const char evm_hmac[] = "hmac(sha1)";
/**
* evm_set_key() - set EVM HMAC key from the kernel
@@ -89,6 +86,9 @@
tfm = &hmac_tfm;
algo = evm_hmac;
} else {
+ if (hash_algo >= HASH_ALGO__LAST)
+ return ERR_PTR(-EINVAL);
+
tfm = &evm_tfm[hash_algo];
algo = hash_algo_name[hash_algo];
}
@@ -97,8 +97,7 @@
mutex_lock(&mutex);
if (*tfm)
goto out;
- *tfm = crypto_alloc_shash(algo, 0,
- CRYPTO_ALG_ASYNC | CRYPTO_NOLOAD);
+ *tfm = crypto_alloc_shash(algo, 0, CRYPTO_NOLOAD);
if (IS_ERR(*tfm)) {
rc = PTR_ERR(*tfm);
pr_err("Can not allocate %s (reason: %ld)\n", algo, rc);
@@ -125,7 +124,6 @@
return ERR_PTR(-ENOMEM);
desc->tfm = *tfm;
- desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
rc = crypto_shash_init(desc);
if (rc) {
@@ -174,8 +172,7 @@
crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
if ((evm_hmac_attrs & EVM_ATTR_FSUUID) &&
type != EVM_XATTR_PORTABLE_DIGSIG)
- crypto_shash_update(desc, &inode->i_sb->s_uuid.b[0],
- sizeof(inode->i_sb->s_uuid));
+ crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE);
crypto_shash_final(desc, digest);
}
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 7f3f54d..f9a81b1 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005-2010 IBM Corporation
*
@@ -5,10 +6,6 @@
* Mimi Zohar <zohar@us.ibm.com>
* Kylene Hall <kjhall@us.ibm.com>
*
- * 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 of the License.
- *
* File: evm_main.c
* implements evm_inode_setxattr, evm_inode_post_setxattr,
* evm_inode_removexattr, and evm_verifyxattr
@@ -16,7 +13,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/crypto.h>
#include <linux/audit.h>
#include <linux/xattr.h>
@@ -169,7 +166,7 @@
/* check value type */
switch (xattr_data->type) {
case EVM_XATTR_HMAC:
- if (xattr_len != sizeof(struct evm_ima_xattr_data)) {
+ if (xattr_len != sizeof(struct evm_xattr)) {
evm_status = INTEGRITY_FAIL;
goto out;
}
@@ -179,7 +176,7 @@
xattr_value_len, &digest);
if (rc)
break;
- rc = crypto_memneq(xattr_data->digest, digest.digest,
+ rc = crypto_memneq(xattr_data->data, digest.digest,
SHA1_DIGEST_SIZE);
if (rc)
rc = -EINVAL;
@@ -523,7 +520,7 @@
const struct xattr *lsm_xattr,
struct xattr *evm_xattr)
{
- struct evm_ima_xattr_data *xattr_data;
+ struct evm_xattr *xattr_data;
int rc;
if (!evm_key_loaded() || !evm_protected_xattr(lsm_xattr->name))
@@ -533,7 +530,7 @@
if (!xattr_data)
return -ENOMEM;
- xattr_data->type = EVM_XATTR_HMAC;
+ xattr_data->data.type = EVM_XATTR_HMAC;
rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest);
if (rc < 0)
goto out;
@@ -563,7 +560,6 @@
{
int error;
struct list_head *pos, *q;
- struct xattr_list *xattr;
evm_init_config();
@@ -580,11 +576,8 @@
error:
if (error != 0) {
if (!list_empty(&evm_config_xattrnames)) {
- list_for_each_safe(pos, q, &evm_config_xattrnames) {
- xattr = list_entry(pos, struct xattr_list,
- list);
+ list_for_each_safe(pos, q, &evm_config_xattrnames)
list_del(pos);
- }
}
}
@@ -592,6 +585,3 @@
}
late_initcall(init_evm);
-
-MODULE_DESCRIPTION("Extended Verification Module");
-MODULE_LICENSE("GPL");
diff --git a/security/integrity/evm/evm_posix_acl.c b/security/integrity/evm/evm_posix_acl.c
index 46408b9..3727580 100644
--- a/security/integrity/evm/evm_posix_acl.c
+++ b/security/integrity/evm/evm_posix_acl.c
@@ -1,15 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 IBM Corporation
*
* Author:
* Mimi Zohar <zohar@us.ibm.com>
- *
- * 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 of the License.
*/
-#include <linux/module.h>
#include <linux/xattr.h>
#include <linux/evm.h>
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index 77de71b..c11c1f7 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2010 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
*
- * 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 of the License.
- *
* File: evm_secfs.c
* - Used to signal when key is on keyring
* - Get the key and enable EVM
@@ -17,7 +14,7 @@
#include <linux/audit.h>
#include <linux/uaccess.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/mutex.h>
#include "evm.h"
@@ -192,7 +189,8 @@
if (count > XATTR_NAME_MAX)
return -E2BIG;
- ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_EVM_XATTR);
+ ab = audit_log_start(audit_context(), GFP_KERNEL,
+ AUDIT_INTEGRITY_EVM_XATTR);
if (!ab)
return -ENOMEM;
@@ -214,6 +212,9 @@
if (len && xattr->name[len-1] == '\n')
xattr->name[len-1] = '\0';
+ audit_log_format(ab, "xattr=");
+ audit_log_untrustedstring(ab, xattr->name);
+
if (strcmp(xattr->name, ".") == 0) {
evm_xattrs_locked = 1;
newattrs.ia_mode = S_IFREG | 0440;
@@ -222,15 +223,11 @@
inode_lock(inode);
err = simple_setattr(evm_xattrs, &newattrs);
inode_unlock(inode);
- audit_log_format(ab, "locked");
if (!err)
err = count;
goto out;
}
- audit_log_format(ab, "xattr=");
- audit_log_untrustedstring(ab, xattr->name);
-
if (strncmp(xattr->name, XATTR_SECURITY_PREFIX,
XATTR_SECURITY_PREFIX_LEN) != 0) {
err = -EINVAL;
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 5a68100..e12c490 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2008 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
*
- * 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 of the
- * License.
- *
* File: integrity_iint.c
* - implements the integrity hooks: integrity_inode_alloc,
* integrity_inode_free
@@ -16,12 +12,13 @@
* using a rbtree tree.
*/
#include <linux/slab.h>
-#include <linux/module.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;
@@ -174,7 +171,10 @@
0, SLAB_PANIC, init_once);
return 0;
}
-security_initcall(integrity_iintcache_init);
+DEFINE_LSM(integrity) = {
+ .name = "integrity",
+ .init = integrity_iintcache_init,
+};
/*
@@ -196,7 +196,7 @@
return -EBADF;
old_fs = get_fs();
- set_fs(get_ds());
+ set_fs(KERNEL_DS);
ret = __vfs_read(file, buf, count, &offset);
set_fs(old_fs);
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 13b4463..838476d 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
# IBM Integrity Measurement Architecture
#
config IMA
@@ -157,6 +158,15 @@
<http://linux-ima.sourceforge.net>
If unsure, say N.
+config IMA_ARCH_POLICY
+ bool "Enable loading an IMA architecture specific policy"
+ depends on (KEXEC_SIG && IMA) || IMA_APPRAISE \
+ && INTEGRITY_ASYMMETRIC_KEYS
+ default n
+ help
+ This option enables loading an IMA architecture specific policy
+ based on run time secure boot flags.
+
config IMA_APPRAISE_BUILD_POLICY
bool "IMA build time configured policy rules"
depends on IMA_APPRAISE && INTEGRITY_ASYMMETRIC_KEYS
@@ -217,12 +227,25 @@
config IMA_APPRAISE_BOOTPARAM
bool "ima_appraise boot parameter"
- depends on IMA_APPRAISE
+ depends on IMA_APPRAISE && !IMA_ARCH_POLICY
default y
help
This option enables the different "ima_appraise=" modes
(eg. fix, log) from the boot command line.
+config IMA_APPRAISE_MODSIG
+ bool "Support module-style signatures for appraisal"
+ depends on IMA_APPRAISE
+ depends on INTEGRITY_ASYMMETRIC_KEYS
+ select PKCS7_MESSAGE_PARSER
+ select MODULE_SIG_FORMAT
+ default n
+ help
+ Adds support for signatures appended to files. The format of the
+ appended signature is the same used for signed kernel modules.
+ The modsig keyword can be used in the IMA policy to allow a hook
+ to accept such signatures.
+
config IMA_TRUSTED_KEYRING
bool "Require all keys on the .ima keyring be signed (deprecated)"
depends on IMA_APPRAISE && SYSTEM_TRUSTED_KEYRING
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index d921dc4..31d57cd 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -9,5 +9,6 @@
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
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
+ima-$(CONFIG_IMA_APPRAISE_MODSIG) += ima_modsig.o
ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
obj-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 67db9d9..3689081 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
@@ -5,11 +6,6 @@
* Reiner Sailer <sailer@watson.ibm.com>
* Mimi Zohar <zohar@us.ibm.com>
*
- * 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 of the
- * License.
- *
* File: ima.h
* internal Integrity Measurement Architecture (IMA) definitions
*/
@@ -64,7 +60,10 @@
const unsigned char *filename;
struct evm_ima_xattr_data *xattr_value;
int xattr_len;
+ const struct modsig *modsig;
const char *violation;
+ const void *buf;
+ int buf_len;
};
/* IMA template field data definition */
@@ -88,7 +87,7 @@
char *name;
char *fmt;
int num_fields;
- struct ima_template_field **fields;
+ const struct ima_template_field **fields;
};
struct ima_template_entry {
@@ -115,6 +114,8 @@
u64 count;
};
+extern const int read_idmap[];
+
#ifdef CONFIG_HAVE_IMA_KEXEC
void ima_load_kexec_buffer(void);
#else
@@ -146,13 +147,21 @@
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);
+int template_desc_init_fields(const char *template_fmt,
+ const struct ima_template_field ***fields,
+ int *num_fields);
struct ima_template_desc *ima_template_desc_current(void);
+struct ima_template_desc *lookup_template_desc(const char *name);
+bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
int ima_restore_measurement_entry(struct ima_template_entry *entry);
int ima_restore_measurement_list(loff_t bufsize, void *buf);
int ima_measurements_show(struct seq_file *m, void *v);
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);
+int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
+ void *lsm_data);
/*
* used to protect h_table and sha_table
@@ -183,6 +192,7 @@
hook(KEXEC_KERNEL_CHECK) \
hook(KEXEC_INITRAMFS_CHECK) \
hook(POLICY_CHECK) \
+ hook(KEXEC_CMDLINE) \
hook(MAX_CHECK)
#define __ima_hook_enumify(ENUM) ENUM,
@@ -190,21 +200,28 @@
__ima_hooks(__ima_hook_enumify)
};
+extern const char *const func_tokens[];
+
+struct modsig;
+
/* LIM API function definitions */
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
- int mask, enum ima_hooks func, int *pcr);
+ int mask, enum ima_hooks func, int *pcr,
+ struct ima_template_desc **template_desc);
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);
+ enum hash_algo algo, struct modsig *modsig);
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len, int pcr);
+ int xattr_len, const struct modsig *modsig, int pcr,
+ struct ima_template_desc *template_desc);
void ima_audit_measurement(struct integrity_iint_cache *iint,
const unsigned char *filename);
int ima_alloc_init_template(struct ima_event_data *event_data,
- struct ima_template_entry **entry);
+ struct ima_template_entry **entry,
+ struct ima_template_desc *template_desc);
int ima_store_template(struct ima_template_entry *entry, int violation,
struct inode *inode,
const unsigned char *filename, int pcr);
@@ -213,7 +230,8 @@
/* IMA policy related functions */
int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
- enum ima_hooks func, int mask, int flags, int *pcr);
+ enum ima_hooks func, int mask, int flags, int *pcr,
+ struct ima_template_desc **template_desc);
void ima_init_policy(void);
void ima_update_policy(void);
void ima_update_policy_flag(void);
@@ -239,7 +257,7 @@
struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len);
+ int xattr_len, const struct modsig *modsig);
int ima_must_appraise(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,
@@ -255,7 +273,8 @@
struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len)
+ int xattr_len,
+ const struct modsig *modsig)
{
return INTEGRITY_UNKNOWN;
}
@@ -292,6 +311,51 @@
#endif /* CONFIG_IMA_APPRAISE */
+#ifdef CONFIG_IMA_APPRAISE_MODSIG
+bool ima_hook_supports_modsig(enum ima_hooks func);
+int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
+ struct modsig **modsig);
+void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size);
+int ima_get_modsig_digest(const struct modsig *modsig, enum hash_algo *algo,
+ const u8 **digest, u32 *digest_size);
+int ima_get_raw_modsig(const struct modsig *modsig, const void **data,
+ u32 *data_len);
+void ima_free_modsig(struct modsig *modsig);
+#else
+static inline bool ima_hook_supports_modsig(enum ima_hooks func)
+{
+ return false;
+}
+
+static inline int ima_read_modsig(enum ima_hooks func, const void *buf,
+ loff_t buf_len, struct modsig **modsig)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void ima_collect_modsig(struct modsig *modsig, const void *buf,
+ loff_t size)
+{
+}
+
+static inline int ima_get_modsig_digest(const struct modsig *modsig,
+ enum hash_algo *algo, const u8 **digest,
+ u32 *digest_size)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int ima_get_raw_modsig(const struct modsig *modsig,
+ const void **data, u32 *data_len)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void ima_free_modsig(struct modsig *modsig)
+{
+}
+#endif /* CONFIG_IMA_APPRAISE_MODSIG */
+
/* LSM based policy rules require audit */
#ifdef CONFIG_IMA_LSM_RULES
@@ -307,8 +371,7 @@
}
static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
- void *lsmrule,
- struct audit_context *actx)
+ void *lsmrule)
{
return -EINVAL;
}
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index a02c5ac..610759f 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -1,18 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2008 IBM Corporation
*
* Author: Mimi Zohar <zohar@us.ibm.com>
*
- * 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 of the
- * License.
- *
* File: ima_api.c
* Implements must_appraise_or_measure, collect_measurement,
* appraise_measurement, store_measurement and store_template.
*/
-#include <linux/module.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/fs.h>
@@ -39,19 +34,26 @@
* ima_alloc_init_template - create and initialize a new template entry
*/
int ima_alloc_init_template(struct ima_event_data *event_data,
- struct ima_template_entry **entry)
+ struct ima_template_entry **entry,
+ struct ima_template_desc *desc)
{
- struct ima_template_desc *template_desc = ima_template_desc_current();
+ struct ima_template_desc *template_desc;
int i, result = 0;
- *entry = kzalloc(sizeof(**entry) + template_desc->num_fields *
- sizeof(struct ima_field_data), GFP_NOFS);
+ if (desc)
+ template_desc = desc;
+ else
+ template_desc = ima_template_desc_current();
+
+ *entry = kzalloc(struct_size(*entry, template_data,
+ template_desc->num_fields), GFP_NOFS);
if (!*entry)
return -ENOMEM;
(*entry)->template_desc = template_desc;
for (i = 0; i < template_desc->num_fields; i++) {
- struct ima_template_field *field = template_desc->fields[i];
+ const struct ima_template_field *field =
+ template_desc->fields[i];
u32 len;
result = field->field_init(event_data,
@@ -133,15 +135,17 @@
{
struct ima_template_entry *entry;
struct inode *inode = file_inode(file);
- struct ima_event_data event_data = {iint, file, filename, NULL, 0,
- cause};
+ struct ima_event_data event_data = { .iint = iint,
+ .file = file,
+ .filename = filename,
+ .violation = cause };
int violation = 1;
int result;
/* can overflow, only indicator */
atomic_long_inc(&ima_htable.violations);
- result = ima_alloc_init_template(&event_data, &entry);
+ result = ima_alloc_init_template(&event_data, &entry, NULL);
if (result < 0) {
result = -ENOMEM;
goto err_out;
@@ -164,11 +168,13 @@
* MAY_APPEND)
* @func: caller identifier
* @pcr: pointer filled in if matched measure policy sets pcr=
+ * @template_desc: pointer filled in if matched measure policy sets template=
*
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
* func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
+ * | KEXEC_CMDLINE
* mask: contains the permission mask
* fsmagic: hex value
*
@@ -176,13 +182,15 @@
*
*/
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
- int mask, enum ima_hooks func, int *pcr)
+ int mask, enum ima_hooks func, int *pcr,
+ struct ima_template_desc **template_desc)
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
flags &= ima_policy_flag;
- return ima_match_policy(inode, cred, secid, func, mask, flags, pcr);
+ return ima_match_policy(inode, cred, secid, func, mask, flags, pcr,
+ template_desc);
}
/*
@@ -197,7 +205,7 @@
*/
int ima_collect_measurement(struct integrity_iint_cache *iint,
struct file *file, void *buf, loff_t size,
- enum hash_algo algo)
+ enum hash_algo algo, struct modsig *modsig)
{
const char *audit_cause = "failed";
struct inode *inode = file_inode(file);
@@ -211,6 +219,14 @@
char digest[IMA_MAX_DIGEST_SIZE];
} hash;
+ /*
+ * Always collect the modsig, because IMA might have already collected
+ * the file digest without collecting the modsig in a previous
+ * measurement rule.
+ */
+ if (modsig)
+ ima_collect_modsig(modsig, buf, size);
+
if (iint->flags & IMA_COLLECTED)
goto out;
@@ -277,21 +293,32 @@
void ima_store_measurement(struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len, int pcr)
+ int xattr_len, const struct modsig *modsig, int pcr,
+ struct ima_template_desc *template_desc)
{
static const char op[] = "add_template_measure";
static const char audit_cause[] = "ENOMEM";
int result = -ENOMEM;
struct inode *inode = file_inode(file);
struct ima_template_entry *entry;
- struct ima_event_data event_data = {iint, file, filename, xattr_value,
- xattr_len, NULL};
+ struct ima_event_data event_data = { .iint = iint,
+ .file = file,
+ .filename = filename,
+ .xattr_value = xattr_value,
+ .xattr_len = xattr_len,
+ .modsig = modsig };
int violation = 0;
- if (iint->measured_pcrs & (0x1 << pcr))
+ /*
+ * We still need to store the measurement in the case of MODSIG because
+ * we only have its contents to put in the list at the time of
+ * appraisal, but a file measurement from earlier might already exist in
+ * the measurement list.
+ */
+ if (iint->measured_pcrs & (0x1 << pcr) && !modsig)
return;
- result = ima_alloc_init_template(&event_data, &entry);
+ result = ima_alloc_init_template(&event_data, &entry, template_desc);
if (result < 0) {
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
op, audit_cause, result, 0);
@@ -335,7 +362,7 @@
audit_log_untrustedstring(ab, filename);
audit_log_format(ab, " hash=\"%s:%s\"", algo_name, hash);
- audit_log_task_info(ab, current);
+ audit_log_task_info(ab);
audit_log_end(ab);
iint->flags |= IMA_AUDITED;
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index deec180..136ae4e 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -1,14 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 IBM Corporation
*
* Author:
* Mimi Zohar <zohar@us.ibm.com>
- *
- * 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 of the License.
*/
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/xattr.h>
@@ -57,7 +54,7 @@
security_task_getsecid(current, &secid);
return ima_match_policy(inode, current_cred(), secid, func, mask,
- IMA_APPRAISE | IMA_HASH, NULL);
+ IMA_APPRAISE | IMA_HASH, NULL, NULL);
}
static int ima_fix_xattr(struct dentry *dentry,
@@ -114,6 +111,7 @@
break;
case CREDS_CHECK:
iint->ima_creds_status = status;
+ break;
case FILE_CHECK:
case POST_SETATTR:
iint->ima_file_status = status;
@@ -167,7 +165,8 @@
return sig->hash_algo;
break;
case IMA_XATTR_DIGEST_NG:
- ret = xattr_value->digest[0];
+ /* first byte contains algorithm id */
+ ret = xattr_value->data[0];
if (ret < HASH_ALGO__LAST)
return ret;
break;
@@ -175,7 +174,7 @@
/* this is for backward compatibility */
if (xattr_len == 21) {
unsigned int zero = 0;
- if (!memcmp(&xattr_value->digest[16], &zero, 4))
+ if (!memcmp(&xattr_value->data[16], &zero, 4))
return HASH_ALGO_MD5;
else
return HASH_ALGO_SHA1;
@@ -201,6 +200,110 @@
}
/*
+ * xattr_verify - verify xattr digest or signature
+ *
+ * Verify whether the hash or signature matches the file contents.
+ *
+ * Return 0 on success, error code otherwise.
+ */
+static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
+ struct evm_ima_xattr_data *xattr_value, int xattr_len,
+ enum integrity_status *status, const char **cause)
+{
+ int rc = -EINVAL, hash_start = 0;
+
+ switch (xattr_value->type) {
+ case IMA_XATTR_DIGEST_NG:
+ /* first byte contains algorithm id */
+ hash_start = 1;
+ /* fall through */
+ case IMA_XATTR_DIGEST:
+ if (iint->flags & IMA_DIGSIG_REQUIRED) {
+ *cause = "IMA-signature-required";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+ clear_bit(IMA_DIGSIG, &iint->atomic_flags);
+ if (xattr_len - sizeof(xattr_value->type) - hash_start >=
+ iint->ima_hash->length)
+ /*
+ * xattr length may be longer. md5 hash in previous
+ * version occupied 20 bytes in xattr, instead of 16
+ */
+ rc = memcmp(&xattr_value->data[hash_start],
+ iint->ima_hash->digest,
+ iint->ima_hash->length);
+ else
+ rc = -EINVAL;
+ if (rc) {
+ *cause = "invalid-hash";
+ *status = INTEGRITY_FAIL;
+ break;
+ }
+ *status = INTEGRITY_PASS;
+ break;
+ case EVM_IMA_XATTR_DIGSIG:
+ set_bit(IMA_DIGSIG, &iint->atomic_flags);
+ rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
+ (const char *)xattr_value,
+ xattr_len,
+ iint->ima_hash->digest,
+ iint->ima_hash->length);
+ if (rc == -EOPNOTSUPP) {
+ *status = INTEGRITY_UNKNOWN;
+ break;
+ }
+ if (IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING) && rc &&
+ func == KEXEC_KERNEL_CHECK)
+ rc = integrity_digsig_verify(INTEGRITY_KEYRING_PLATFORM,
+ (const char *)xattr_value,
+ xattr_len,
+ iint->ima_hash->digest,
+ iint->ima_hash->length);
+ if (rc) {
+ *cause = "invalid-signature";
+ *status = INTEGRITY_FAIL;
+ } else {
+ *status = INTEGRITY_PASS;
+ }
+ break;
+ default:
+ *status = INTEGRITY_UNKNOWN;
+ *cause = "unknown-ima-data";
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * modsig_verify - verify modsig signature
+ *
+ * Verify whether the signature matches the file contents.
+ *
+ * Return 0 on success, error code otherwise.
+ */
+static int modsig_verify(enum ima_hooks func, const struct modsig *modsig,
+ enum integrity_status *status, const char **cause)
+{
+ int rc;
+
+ rc = integrity_modsig_verify(INTEGRITY_KEYRING_IMA, modsig);
+ if (IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING) && rc &&
+ func == KEXEC_KERNEL_CHECK)
+ rc = integrity_modsig_verify(INTEGRITY_KEYRING_PLATFORM,
+ modsig);
+ if (rc) {
+ *cause = "invalid-signature";
+ *status = INTEGRITY_FAIL;
+ } else {
+ *status = INTEGRITY_PASS;
+ }
+
+ return rc;
+}
+
+/*
* ima_appraise_measurement - appraise file measurement
*
* Call evm_verifyxattr() to verify the integrity of 'security.ima'.
@@ -212,19 +315,22 @@
struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
- int xattr_len)
+ int xattr_len, const struct modsig *modsig)
{
static const char op[] = "appraise_data";
const char *cause = "unknown";
struct dentry *dentry = file_dentry(file);
struct inode *inode = d_backing_inode(dentry);
enum integrity_status status = INTEGRITY_UNKNOWN;
- int rc = xattr_len, hash_start = 0;
+ int rc = xattr_len;
+ bool try_modsig = iint->flags & IMA_MODSIG_ALLOWED && modsig;
- if (!(inode->i_opflags & IOP_XATTR))
+ /* If not appraising a modsig, we need an xattr. */
+ if (!(inode->i_opflags & IOP_XATTR) && !try_modsig)
return INTEGRITY_UNKNOWN;
- if (rc <= 0) {
+ /* If reading the xattr failed and there's no modsig, error out. */
+ if (rc <= 0 && !try_modsig) {
if (rc && rc != -ENODATA)
goto out;
@@ -247,6 +353,10 @@
case INTEGRITY_UNKNOWN:
break;
case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */
+ /* It's fine not to have xattrs when using a modsig. */
+ if (try_modsig)
+ break;
+ /* fall through */
case INTEGRITY_NOLABEL: /* No security.evm xattr. */
cause = "missing-HMAC";
goto out;
@@ -257,55 +367,18 @@
WARN_ONCE(true, "Unexpected integrity status %d\n", status);
}
- switch (xattr_value->type) {
- case IMA_XATTR_DIGEST_NG:
- /* first byte contains algorithm id */
- hash_start = 1;
- /* fall through */
- case IMA_XATTR_DIGEST:
- if (iint->flags & IMA_DIGSIG_REQUIRED) {
- cause = "IMA-signature-required";
- status = INTEGRITY_FAIL;
- break;
- }
- clear_bit(IMA_DIGSIG, &iint->atomic_flags);
- if (xattr_len - sizeof(xattr_value->type) - hash_start >=
- iint->ima_hash->length)
- /* xattr length may be longer. md5 hash in previous
- version occupied 20 bytes in xattr, instead of 16
- */
- rc = memcmp(&xattr_value->digest[hash_start],
- iint->ima_hash->digest,
- iint->ima_hash->length);
- else
- rc = -EINVAL;
- if (rc) {
- cause = "invalid-hash";
- status = INTEGRITY_FAIL;
- break;
- }
- status = INTEGRITY_PASS;
- break;
- case EVM_IMA_XATTR_DIGSIG:
- set_bit(IMA_DIGSIG, &iint->atomic_flags);
- rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
- (const char *)xattr_value, rc,
- iint->ima_hash->digest,
- iint->ima_hash->length);
- if (rc == -EOPNOTSUPP) {
- status = INTEGRITY_UNKNOWN;
- } else if (rc) {
- cause = "invalid-signature";
- status = INTEGRITY_FAIL;
- } else {
- status = INTEGRITY_PASS;
- }
- break;
- default:
- status = INTEGRITY_UNKNOWN;
- cause = "unknown-ima-data";
- break;
- }
+ if (xattr_value)
+ rc = xattr_verify(func, iint, xattr_value, xattr_len, &status,
+ &cause);
+
+ /*
+ * If we have a modsig and either no imasig or the imasig's key isn't
+ * known, then try verifying the modsig.
+ */
+ if (try_modsig &&
+ (!xattr_value || xattr_value->type == IMA_XATTR_DIGEST_NG ||
+ rc == -ENOKEY))
+ rc = modsig_verify(func, modsig, &status, &cause);
out:
/*
@@ -323,7 +396,7 @@
op, cause, rc, 0);
} else if (status != INTEGRITY_PASS) {
/* Fix mode, but don't replace file signatures. */
- if ((ima_appraise & IMA_APPRAISE_FIX) &&
+ if ((ima_appraise & IMA_APPRAISE_FIX) && !try_modsig &&
(!xattr_value ||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
if (!ima_fix_xattr(dentry, iint))
@@ -362,7 +435,7 @@
!(iint->flags & IMA_HASH))
return;
- rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo);
+ rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo, NULL);
if (rc < 0)
return;
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index d9e7728..73044fc 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
@@ -5,10 +6,6 @@
* Mimi Zohar <zohar@us.ibm.com>
* Kylene Hall <kjhall@us.ibm.com>
*
- * 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 of the License.
- *
* File: ima_crypto.c
* Calculates md5/sha1 file hash, template hash, boot-aggreate hash
*/
@@ -271,8 +268,16 @@
rbuf_len = min_t(loff_t, i_size - offset, rbuf_size[active]);
rc = integrity_kernel_read(file, offset, rbuf[active],
rbuf_len);
- if (rc != rbuf_len)
+ if (rc != rbuf_len) {
+ if (rc >= 0)
+ rc = -EINVAL;
+ /*
+ * Forward current rc, do not overwrite with return value
+ * from ahash_wait()
+ */
+ ahash_wait(ahash_rc, &wait);
goto out3;
+ }
if (rbuf[1] && offset) {
/* Using two buffers, and it is not the first
@@ -333,7 +338,6 @@
SHASH_DESC_ON_STACK(shash, tfm);
shash->tfm = tfm;
- shash->flags = 0;
hash->length = crypto_shash_digestsize(tfm);
@@ -469,7 +473,6 @@
int rc, i;
shash->tfm = tfm;
- shash->flags = 0;
hash->length = crypto_shash_digestsize(tfm);
@@ -591,7 +594,6 @@
int rc;
shash->tfm = tfm;
- shash->flags = 0;
hash->length = crypto_shash_digestsize(tfm);
@@ -643,12 +645,12 @@
return calc_buffer_shash(buf, len, hash);
}
-static void __init ima_pcrread(int idx, u8 *pcr)
+static void __init ima_pcrread(u32 idx, struct tpm_digest *d)
{
if (!ima_tpm_chip)
return;
- if (tpm_pcr_read(ima_tpm_chip, idx, pcr) != 0)
+ if (tpm_pcr_read(ima_tpm_chip, idx, d) != 0)
pr_err("Error Communicating to TPM chip\n");
}
@@ -658,12 +660,12 @@
static int __init ima_calc_boot_aggregate_tfm(char *digest,
struct crypto_shash *tfm)
{
- u8 pcr_i[TPM_DIGEST_SIZE];
- int rc, i;
+ struct tpm_digest d = { .alg_id = TPM_ALG_SHA1, .digest = {0} };
+ int rc;
+ u32 i;
SHASH_DESC_ON_STACK(shash, tfm);
shash->tfm = tfm;
- shash->flags = 0;
rc = crypto_shash_init(shash);
if (rc != 0)
@@ -671,9 +673,9 @@
/* cumulative sha1 over tpm registers 0-7 */
for (i = TPM_PCR0; i < TPM_PCR8; i++) {
- ima_pcrread(i, pcr_i);
+ ima_pcrread(i, &d);
/* now accumulate with current aggregate */
- rc = crypto_shash_update(shash, pcr_i, TPM_DIGEST_SIZE);
+ rc = crypto_shash_update(shash, d.digest, TPM_DIGEST_SIZE);
}
if (!rc)
crypto_shash_final(shash, digest);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index cfb8cc3..2000e8d 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
@@ -6,11 +7,6 @@
* Reiner Sailer <sailer@us.ibm.com>
* Mimi Zohar <zohar@us.ibm.com>
*
- * 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 of the
- * License.
- *
* File: ima_fs.c
* implemenents security file system for reporting
* current measurement list and IMA statistics
@@ -20,7 +16,7 @@
#include <linux/fcntl.h>
#include <linux/slab.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/seq_file.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
@@ -179,7 +175,8 @@
/* 6th: template specific data */
for (i = 0; i < e->template_desc->num_fields; i++) {
enum ima_show_type show = IMA_SHOW_BINARY;
- struct ima_template_field *field = e->template_desc->fields[i];
+ const struct ima_template_field *field =
+ e->template_desc->fields[i];
if (is_ima_template && strcmp(field->field_id, "d") == 0)
show = IMA_SHOW_BINARY_NO_FIELD_LEN;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index faac9ec..5d55ade 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
@@ -6,18 +7,13 @@
* Leendert van Doorn <leendert@watson.ibm.com>
* Mimi Zohar <zohar@us.ibm.com>
*
- * 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 of the
- * License.
- *
* File: ima_init.c
* initialization and cleanup functions
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/err.h>
@@ -25,7 +21,7 @@
#include "ima.h"
/* name for boot aggregate entry */
-static const char *boot_aggregate_name = "boot_aggregate";
+static const char boot_aggregate_name[] = "boot_aggregate";
struct tpm_chip *ima_tpm_chip;
/* Add the boot aggregate to the IMA measurement list and extend
@@ -49,8 +45,8 @@
const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry;
struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
- struct ima_event_data event_data = {iint, NULL, boot_aggregate_name,
- NULL, 0, NULL};
+ struct ima_event_data event_data = { .iint = iint,
+ .filename = boot_aggregate_name };
int result = -ENOMEM;
int violation = 0;
struct {
@@ -72,7 +68,7 @@
}
}
- result = ima_alloc_init_template(&event_data, &entry);
+ result = ima_alloc_init_template(&event_data, &entry, NULL);
if (result < 0) {
audit_cause = "alloc_entry";
goto err_out;
@@ -123,8 +119,12 @@
if (rc != 0)
return rc;
+ /* It can be called before ima_init_digests(), it does not use TPM. */
ima_load_kexec_buffer();
+ rc = ima_init_digests();
+ if (rc != 0)
+ return rc;
rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */
if (rc != 0)
return rc;
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 16bd187..9e94eca 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 IBM Corporation
*
* Authors:
* Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
* Mimi Zohar <zohar@linux.vnet.ibm.com>
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -106,7 +102,7 @@
kexec_segment_size = ALIGN(ima_get_binary_runtime_size() +
PAGE_SIZE / 2, PAGE_SIZE);
if ((kexec_segment_size == ULONG_MAX) ||
- ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages / 2)) {
+ ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages() / 2)) {
pr_err("Binary measurement list too large.\n");
return;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 2d31921..60027c6 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -1,4 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
+ * Integrity Measurement Architecture
+ *
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
* Authors:
@@ -7,11 +10,6 @@
* Kylene Hall <kylene@us.ibm.com>
* Mimi Zohar <zohar@us.ibm.com>
*
- * 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 of the
- * License.
- *
* File: ima_main.c
* implements the IMA hooks: ima_bprm_check, ima_file_mmap,
* and ima_file_check.
@@ -41,6 +39,10 @@
int ima_hash_algo = HASH_ALGO_SHA1;
static int hash_setup_done;
+static struct notifier_block ima_lsm_policy_notifier = {
+ .notifier_call = ima_lsm_policy_change,
+};
+
static int __init hash_setup(char *str)
{
struct ima_template_desc *template_desc = ima_template_desc_current();
@@ -70,6 +72,27 @@
}
__setup("ima_hash=", hash_setup);
+/* Prevent mmap'ing a file execute that is already mmap'ed write */
+static int mmap_violation_check(enum ima_hooks func, struct file *file,
+ char **pathbuf, const char **pathname,
+ char *filename)
+{
+ struct inode *inode;
+ int rc = 0;
+
+ if ((func == MMAP_CHECK) && mapping_writably_mapped(file->f_mapping)) {
+ rc = -ETXTBSY;
+ inode = file_inode(file);
+
+ if (!*pathbuf) /* ima_rdwr_violation possibly pre-fetched */
+ *pathname = ima_d_path(&file->f_path, pathbuf,
+ filename);
+ integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, *pathname,
+ "mmap_file", "mmapped_writers", rc, 0);
+ }
+ return rc;
+}
+
/*
* ima_rdwr_violation_check
*
@@ -103,7 +126,7 @@
} else {
if (must_measure)
set_bit(IMA_MUST_MEASURE, &iint->atomic_flags);
- if ((atomic_read(&inode->i_writecount) > 0) && must_measure)
+ if (inode_is_open_for_write(inode) && must_measure)
send_writers = true;
}
@@ -172,13 +195,14 @@
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint = NULL;
- struct ima_template_desc *template_desc;
+ struct ima_template_desc *template_desc = NULL;
char *pathbuf = NULL;
char filename[NAME_MAX];
const char *pathname = NULL;
int rc = 0, action, must_appraise = 0;
int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
struct evm_ima_xattr_data *xattr_value = NULL;
+ struct modsig *modsig = NULL;
int xattr_len = 0;
bool violation_check;
enum hash_algo hash_algo;
@@ -190,7 +214,8 @@
* bitmask based on the appraise/audit/measurement policy.
* Included is the appraise submask.
*/
- action = ima_get_action(inode, cred, secid, mask, func, &pcr);
+ action = ima_get_action(inode, cred, secid, mask, func, &pcr,
+ &template_desc);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
(ima_policy_flag & IMA_MEASURE));
if (!action && !violation_check)
@@ -268,20 +293,37 @@
/* Nothing to do, just return existing appraised status */
if (!action) {
- if (must_appraise)
- rc = ima_get_cache_status(iint, func);
+ if (must_appraise) {
+ rc = mmap_violation_check(func, file, &pathbuf,
+ &pathname, filename);
+ if (!rc)
+ rc = ima_get_cache_status(iint, func);
+ }
goto out_locked;
}
- template_desc = ima_template_desc_current();
if ((action & IMA_APPRAISE_SUBMASK) ||
- strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0)
+ strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) {
/* read 'security.ima' */
xattr_len = ima_read_xattr(file_dentry(file), &xattr_value);
+ /*
+ * Read the appended modsig if allowed by the policy, and allow
+ * an additional measurement list entry, if needed, based on the
+ * template format and whether the file was already measured.
+ */
+ if (iint->flags & IMA_MODSIG_ALLOWED) {
+ rc = ima_read_modsig(func, buf, size, &modsig);
+
+ if (!rc && ima_template_has_modsig(template_desc) &&
+ iint->flags & IMA_MEASURED)
+ action |= IMA_MEASURE;
+ }
+ }
+
hash_algo = ima_get_hash_algo(xattr_value, xattr_len);
- rc = ima_collect_measurement(iint, file, buf, size, hash_algo);
+ rc = ima_collect_measurement(iint, file, buf, size, hash_algo, modsig);
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
goto out_locked;
@@ -290,12 +332,16 @@
if (action & IMA_MEASURE)
ima_store_measurement(iint, file, pathname,
- xattr_value, xattr_len, pcr);
+ xattr_value, xattr_len, modsig, pcr,
+ template_desc);
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
inode_lock(inode);
rc = ima_appraise_measurement(func, iint, file, pathname,
- xattr_value, xattr_len);
+ xattr_value, xattr_len, modsig);
inode_unlock(inode);
+ if (!rc)
+ rc = mmap_violation_check(func, file, &pathbuf,
+ &pathname, filename);
}
if (action & IMA_AUDIT)
ima_audit_measurement(iint, pathname);
@@ -308,6 +354,7 @@
rc = -EACCES;
mutex_unlock(&iint->mutex);
kfree(xattr_value);
+ ima_free_modsig(modsig);
out:
if (pathbuf)
__putname(pathbuf);
@@ -395,6 +442,33 @@
EXPORT_SYMBOL_GPL(ima_file_check);
/**
+ * ima_post_create_tmpfile - mark newly created tmpfile as new
+ * @file : newly created tmpfile
+ *
+ * No measuring, appraising or auditing of newly created tmpfiles is needed.
+ * Skip calling process_measurement(), but indicate which newly, created
+ * tmpfiles are in policy.
+ */
+void ima_post_create_tmpfile(struct inode *inode)
+{
+ struct integrity_iint_cache *iint;
+ int must_appraise;
+
+ must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK);
+ if (!must_appraise)
+ return;
+
+ /* Nothing to do if we can't allocate memory */
+ iint = integrity_inode_get(inode);
+ if (!iint)
+ return;
+
+ /* needed for writing the security xattrs */
+ set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
+ iint->ima_file_status = INTEGRITY_PASS;
+}
+
+/**
* ima_post_path_mknod - mark as a new inode
* @dentry: newly created dentry
*
@@ -411,9 +485,13 @@
if (!must_appraise)
return;
+ /* Nothing to do if we can't allocate memory */
iint = integrity_inode_get(inode);
- if (iint)
- iint->flags |= IMA_NEW_FILE;
+ if (!iint)
+ return;
+
+ /* needed for re-opening empty files */
+ iint->flags |= IMA_NEW_FILE;
}
/**
@@ -440,7 +518,7 @@
return 0;
}
-static int read_idmap[READING_MAX_ID] = {
+const int read_idmap[READING_MAX_ID] = {
[READING_FIRMWARE] = FIRMWARE_CHECK,
[READING_FIRMWARE_PREALLOC_BUFFER] = FIRMWARE_CHECK,
[READING_MODULE] = MODULE_CHECK,
@@ -505,20 +583,26 @@
*/
int ima_load_data(enum kernel_load_data_id id)
{
- bool sig_enforce;
+ bool ima_enforce, sig_enforce;
- if ((ima_appraise & IMA_APPRAISE_ENFORCE) != IMA_APPRAISE_ENFORCE)
- return 0;
+ ima_enforce =
+ (ima_appraise & IMA_APPRAISE_ENFORCE) == IMA_APPRAISE_ENFORCE;
switch (id) {
case LOADING_KEXEC_IMAGE:
- if (ima_appraise & IMA_APPRAISE_KEXEC) {
+ if (IS_ENABLED(CONFIG_KEXEC_SIG)
+ && arch_ima_get_secureboot()) {
+ pr_err("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n");
+ return -EACCES;
+ }
+
+ if (ima_enforce && (ima_appraise & IMA_APPRAISE_KEXEC)) {
pr_err("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n");
return -EACCES; /* INTEGRITY_UNKNOWN */
}
break;
case LOADING_FIRMWARE:
- if (ima_appraise & IMA_APPRAISE_FIRMWARE) {
+ if (ima_enforce && (ima_appraise & IMA_APPRAISE_FIRMWARE)) {
pr_err("Prevent firmware sysfs fallback loading.\n");
return -EACCES; /* INTEGRITY_UNKNOWN */
}
@@ -526,7 +610,8 @@
case LOADING_MODULE:
sig_enforce = is_module_sig_enforced();
- if (!sig_enforce && (ima_appraise & IMA_APPRAISE_MODULES)) {
+ if (ima_enforce && (!sig_enforce
+ && (ima_appraise & IMA_APPRAISE_MODULES))) {
pr_err("impossible to appraise a module without a file descriptor. sig_enforce kernel parameter might help\n");
return -EACCES; /* INTEGRITY_UNKNOWN */
}
@@ -536,6 +621,80 @@
return 0;
}
+/*
+ * process_buffer_measurement - Measure the buffer to ima log.
+ * @buf: pointer to the buffer that needs to be added to the log.
+ * @size: size of buffer(in bytes).
+ * @eventname: event name to be used for the buffer entry.
+ * @cred: a pointer to a credentials structure for user validation.
+ * @secid: the secid of the task to be validated.
+ *
+ * Based on policy, the buffer is measured into the ima log.
+ */
+static void process_buffer_measurement(const void *buf, int size,
+ const char *eventname,
+ const struct cred *cred, u32 secid)
+{
+ int ret = 0;
+ struct ima_template_entry *entry = NULL;
+ struct integrity_iint_cache iint = {};
+ struct ima_event_data event_data = {.iint = &iint,
+ .filename = eventname,
+ .buf = buf,
+ .buf_len = size};
+ struct ima_template_desc *template_desc = NULL;
+ struct {
+ struct ima_digest_data hdr;
+ char digest[IMA_MAX_DIGEST_SIZE];
+ } hash = {};
+ int violation = 0;
+ int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+ int action = 0;
+
+ action = ima_get_action(NULL, cred, secid, 0, KEXEC_CMDLINE, &pcr,
+ &template_desc);
+ if (!(action & IMA_MEASURE))
+ return;
+
+ iint.ima_hash = &hash.hdr;
+ iint.ima_hash->algo = ima_hash_algo;
+ iint.ima_hash->length = hash_digest_size[ima_hash_algo];
+
+ ret = ima_calc_buffer_hash(buf, size, iint.ima_hash);
+ if (ret < 0)
+ goto out;
+
+ ret = ima_alloc_init_template(&event_data, &entry, template_desc);
+ if (ret < 0)
+ goto out;
+
+ ret = ima_store_template(entry, violation, NULL, buf, pcr);
+
+ if (ret < 0)
+ ima_free_template_entry(entry);
+
+out:
+ return;
+}
+
+/**
+ * ima_kexec_cmdline - measure kexec cmdline boot args
+ * @buf: pointer to buffer
+ * @size: size of buffer
+ *
+ * Buffers can only be measured, not appraised.
+ */
+void ima_kexec_cmdline(const void *buf, int size)
+{
+ u32 secid;
+
+ if (buf && size != 0) {
+ security_task_getsecid(current, &secid);
+ process_buffer_measurement(buf, size, "kexec-cmdline",
+ current_cred(), secid);
+ }
+}
+
static int __init init_ima(void)
{
int error;
@@ -553,6 +712,10 @@
error = ima_init();
}
+ error = register_blocking_lsm_notifier(&ima_lsm_policy_notifier);
+ if (error)
+ pr_warn("Couldn't register LSM notifier, error %d\n", error);
+
if (!error)
ima_update_policy_flag();
@@ -560,6 +723,3 @@
}
late_initcall(init_ima); /* Start IMA after the TPM is available */
-
-MODULE_DESCRIPTION("Integrity Measurement Architecture");
-MODULE_LICENSE("GPL");
diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c
new file mode 100644
index 0000000..d106885
--- /dev/null
+++ b/security/integrity/ima/ima_modsig.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * IMA support for appraising module-style appended signatures.
+ *
+ * Copyright (C) 2019 IBM Corporation
+ *
+ * Author:
+ * Thiago Jung Bauermann <bauerman@linux.ibm.com>
+ */
+
+#include <linux/types.h>
+#include <linux/module_signature.h>
+#include <keys/asymmetric-type.h>
+#include <crypto/pkcs7.h>
+
+#include "ima.h"
+
+struct modsig {
+ struct pkcs7_message *pkcs7_msg;
+
+ enum hash_algo hash_algo;
+
+ /* This digest will go in the 'd-modsig' field of the IMA template. */
+ const u8 *digest;
+ u32 digest_size;
+
+ /*
+ * This is what will go to the measurement list if the template requires
+ * storing the signature.
+ */
+ int raw_pkcs7_len;
+ u8 raw_pkcs7[];
+};
+
+/**
+ * ima_hook_supports_modsig - can the policy allow modsig for this hook?
+ *
+ * modsig is only supported by hooks using ima_post_read_file(), because only
+ * they preload the contents of the file in a buffer. FILE_CHECK does that in
+ * some cases, but not when reached from vfs_open(). POLICY_CHECK can support
+ * it, but it's not useful in practice because it's a text file so deny.
+ */
+bool ima_hook_supports_modsig(enum ima_hooks func)
+{
+ switch (func) {
+ case KEXEC_KERNEL_CHECK:
+ case KEXEC_INITRAMFS_CHECK:
+ case MODULE_CHECK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * ima_read_modsig - Read modsig from buf.
+ *
+ * Return: 0 on success, error code otherwise.
+ */
+int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
+ struct modsig **modsig)
+{
+ const size_t marker_len = strlen(MODULE_SIG_STRING);
+ const struct module_signature *sig;
+ struct modsig *hdr;
+ size_t sig_len;
+ const void *p;
+ int rc;
+
+ if (buf_len <= marker_len + sizeof(*sig))
+ return -ENOENT;
+
+ p = buf + buf_len - marker_len;
+ if (memcmp(p, MODULE_SIG_STRING, marker_len))
+ return -ENOENT;
+
+ buf_len -= marker_len;
+ sig = (const struct module_signature *)(p - sizeof(*sig));
+
+ rc = mod_check_sig(sig, buf_len, func_tokens[func]);
+ if (rc)
+ return rc;
+
+ sig_len = be32_to_cpu(sig->sig_len);
+ buf_len -= sig_len + sizeof(*sig);
+
+ /* Allocate sig_len additional bytes to hold the raw PKCS#7 data. */
+ hdr = kzalloc(sizeof(*hdr) + sig_len, GFP_KERNEL);
+ if (!hdr)
+ return -ENOMEM;
+
+ hdr->pkcs7_msg = pkcs7_parse_message(buf + buf_len, sig_len);
+ if (IS_ERR(hdr->pkcs7_msg)) {
+ rc = PTR_ERR(hdr->pkcs7_msg);
+ kfree(hdr);
+ return rc;
+ }
+
+ memcpy(hdr->raw_pkcs7, buf + buf_len, sig_len);
+ hdr->raw_pkcs7_len = sig_len;
+
+ /* We don't know the hash algorithm yet. */
+ hdr->hash_algo = HASH_ALGO__LAST;
+
+ *modsig = hdr;
+
+ return 0;
+}
+
+/**
+ * ima_collect_modsig - Calculate the file hash without the appended signature.
+ *
+ * Since the modsig is part of the file contents, the hash used in its signature
+ * isn't the same one ordinarily calculated by IMA. Therefore PKCS7 code
+ * calculates a separate one for signature verification.
+ */
+void ima_collect_modsig(struct modsig *modsig, const void *buf, loff_t size)
+{
+ int rc;
+
+ /*
+ * Provide the file contents (minus the appended sig) so that the PKCS7
+ * code can calculate the file hash.
+ */
+ size -= modsig->raw_pkcs7_len + strlen(MODULE_SIG_STRING) +
+ sizeof(struct module_signature);
+ rc = pkcs7_supply_detached_data(modsig->pkcs7_msg, buf, size);
+ if (rc)
+ return;
+
+ /* Ask the PKCS7 code to calculate the file hash. */
+ rc = pkcs7_get_digest(modsig->pkcs7_msg, &modsig->digest,
+ &modsig->digest_size, &modsig->hash_algo);
+}
+
+int ima_modsig_verify(struct key *keyring, const struct modsig *modsig)
+{
+ return verify_pkcs7_message_sig(NULL, 0, modsig->pkcs7_msg, keyring,
+ VERIFYING_MODULE_SIGNATURE, NULL, NULL);
+}
+
+int ima_get_modsig_digest(const struct modsig *modsig, enum hash_algo *algo,
+ const u8 **digest, u32 *digest_size)
+{
+ *algo = modsig->hash_algo;
+ *digest = modsig->digest;
+ *digest_size = modsig->digest_size;
+
+ return 0;
+}
+
+int ima_get_raw_modsig(const struct modsig *modsig, const void **data,
+ u32 *data_len)
+{
+ *data = &modsig->raw_pkcs7;
+ *data_len = modsig->raw_pkcs7_len;
+
+ return 0;
+}
+
+void ima_free_modsig(struct modsig *modsig)
+{
+ if (!modsig)
+ return;
+
+ pkcs7_free_message(modsig->pkcs7_msg);
+ kfree(modsig);
+}
diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c
index 073ddc9..36cadad 100644
--- a/security/integrity/ima/ima_mok.c
+++ b/security/integrity/ima/ima_mok.c
@@ -1,14 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2015 Juniper Networks, Inc.
*
* Author:
* Petko Manolov <petko.manolov@konsulko.com>
- *
- * 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 of the
- * License.
- *
*/
#include <linux/export.h>
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 8c94998..5380aca 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1,16 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2008 IBM Corporation
* Author: Mimi Zohar <zohar@us.ibm.com>
*
- * 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 of the License.
- *
* ima_policy.c
* - initialize default measure policy rules
- *
*/
-#include <linux/module.h>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
#include <linux/list.h>
#include <linux/fs.h>
#include <linux/security.h>
@@ -20,6 +19,7 @@
#include <linux/rculist.h>
#include <linux/genhd.h>
#include <linux/seq_file.h>
+#include <linux/ima.h>
#include "ima.h"
@@ -58,6 +58,8 @@
enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB };
+enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY };
+
struct ima_rule_entry {
struct list_head list;
int action;
@@ -77,6 +79,7 @@
int type; /* audit type */
} lsm[MAX_LSM_RULES];
char *fsname;
+ struct ima_template_desc *template;
};
/*
@@ -104,7 +107,8 @@
.flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = CGROUP2_SUPER_MAGIC,
.flags = IMA_FSMAGIC},
- {.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC}
+ {.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = EFIVARFS_MAGIC, .flags = IMA_FSMAGIC}
};
static struct ima_rule_entry original_measurement_rules[] __ro_after_init = {
@@ -147,6 +151,7 @@
{.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = SMACK_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = EFIVARFS_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = CGROUP2_SUPER_MAGIC, .flags = IMA_FSMAGIC},
#ifdef CONFIG_IMA_WRITE_POLICY
@@ -193,6 +198,9 @@
.flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
};
+/* An array of architecture specific rules */
+static struct ima_rule_entry *arch_policy_entry __ro_after_init;
+
static LIST_HEAD(ima_default_rules);
static LIST_HEAD(ima_policy_rules);
static LIST_HEAD(ima_temp_rules);
@@ -241,31 +249,113 @@
}
__setup("ima_appraise_tcb", default_appraise_policy_setup);
+static void ima_lsm_free_rule(struct ima_rule_entry *entry)
+{
+ int i;
+
+ for (i = 0; i < MAX_LSM_RULES; i++) {
+ kfree(entry->lsm[i].rule);
+ kfree(entry->lsm[i].args_p);
+ }
+ kfree(entry);
+}
+
+static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
+{
+ struct ima_rule_entry *nentry;
+ int i, result;
+
+ nentry = kmalloc(sizeof(*nentry), GFP_KERNEL);
+ if (!nentry)
+ return NULL;
+
+ /*
+ * Immutable elements are copied over as pointers and data; only
+ * lsm rules can change
+ */
+ memcpy(nentry, entry, sizeof(*nentry));
+ memset(nentry->lsm, 0, FIELD_SIZEOF(struct ima_rule_entry, lsm));
+
+ for (i = 0; i < MAX_LSM_RULES; i++) {
+ if (!entry->lsm[i].rule)
+ continue;
+
+ nentry->lsm[i].type = entry->lsm[i].type;
+ nentry->lsm[i].args_p = kstrdup(entry->lsm[i].args_p,
+ GFP_KERNEL);
+ if (!nentry->lsm[i].args_p)
+ goto out_err;
+
+ result = security_filter_rule_init(nentry->lsm[i].type,
+ Audit_equal,
+ nentry->lsm[i].args_p,
+ &nentry->lsm[i].rule);
+ if (result == -EINVAL)
+ pr_warn("ima: rule for LSM \'%d\' is undefined\n",
+ entry->lsm[i].type);
+ }
+ return nentry;
+
+out_err:
+ ima_lsm_free_rule(nentry);
+ return NULL;
+}
+
+static int ima_lsm_update_rule(struct ima_rule_entry *entry)
+{
+ struct ima_rule_entry *nentry;
+
+ nentry = ima_lsm_copy_rule(entry);
+ if (!nentry)
+ return -ENOMEM;
+
+ list_replace_rcu(&entry->list, &nentry->list);
+ synchronize_rcu();
+ ima_lsm_free_rule(entry);
+
+ return 0;
+}
+
/*
* The LSM policy can be reloaded, leaving the IMA LSM based rules referring
* to the old, stale LSM policy. Update the IMA LSM based rules to reflect
- * the reloaded LSM policy. We assume the rules still exist; and BUG_ON() if
- * they don't.
+ * the reloaded LSM policy.
*/
static void ima_lsm_update_rules(void)
{
- struct ima_rule_entry *entry;
- int result;
- int i;
+ struct ima_rule_entry *entry, *e;
+ int i, result, needs_update;
- list_for_each_entry(entry, &ima_policy_rules, list) {
+ list_for_each_entry_safe(entry, e, &ima_policy_rules, list) {
+ needs_update = 0;
for (i = 0; i < MAX_LSM_RULES; i++) {
- if (!entry->lsm[i].rule)
- continue;
- result = security_filter_rule_init(entry->lsm[i].type,
- Audit_equal,
- entry->lsm[i].args_p,
- &entry->lsm[i].rule);
- BUG_ON(!entry->lsm[i].rule);
+ if (entry->lsm[i].rule) {
+ needs_update = 1;
+ break;
+ }
+ }
+ if (!needs_update)
+ continue;
+
+ result = ima_lsm_update_rule(entry);
+ if (result) {
+ pr_err("ima: lsm rule update error %d\n",
+ result);
+ return;
}
}
}
+int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
+ void *lsm_data)
+{
+ if (event != LSM_POLICY_CHANGE)
+ return NOTIFY_DONE;
+
+ ima_lsm_update_rules();
+ return NOTIFY_OK;
+}
+
/**
* ima_match_rules - determine whether an inode matches the measure rule.
* @rule: a pointer to a rule
@@ -283,6 +373,11 @@
{
int i;
+ if (func == KEXEC_CMDLINE) {
+ if ((rule->flags & IMA_FUNC) && (rule->func == func))
+ return true;
+ return false;
+ }
if ((rule->flags & IMA_FUNC) &&
(rule->func != func && func != POST_SETATTR))
return false;
@@ -319,11 +414,10 @@
for (i = 0; i < MAX_LSM_RULES; i++) {
int rc = 0;
u32 osid;
- int retried = 0;
if (!rule->lsm[i].rule)
continue;
-retry:
+
switch (i) {
case LSM_OBJ_USER:
case LSM_OBJ_ROLE:
@@ -332,8 +426,7 @@
rc = security_filter_rule_match(osid,
rule->lsm[i].type,
Audit_equal,
- rule->lsm[i].rule,
- NULL);
+ rule->lsm[i].rule);
break;
case LSM_SUBJ_USER:
case LSM_SUBJ_ROLE:
@@ -341,16 +434,10 @@
rc = security_filter_rule_match(secid,
rule->lsm[i].type,
Audit_equal,
- rule->lsm[i].rule,
- NULL);
+ rule->lsm[i].rule);
default:
break;
}
- if ((rc < 0) && (!retried)) {
- retried = 1;
- ima_lsm_update_rules();
- goto retry;
- }
if (!rc)
return false;
}
@@ -391,6 +478,7 @@
* @func: IMA hook identifier
* @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
* @pcr: set the pcr to extend
+ * @template_desc: the template that should be used for this rule
*
* Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
* conditions.
@@ -400,11 +488,15 @@
* than writes so ima_match_policy() is classical RCU candidate.
*/
int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
- enum ima_hooks func, int mask, int flags, int *pcr)
+ enum ima_hooks func, int mask, int flags, int *pcr,
+ struct ima_template_desc **template_desc)
{
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
+ if (template_desc)
+ *template_desc = ima_template_desc_current();
+
rcu_read_lock();
list_for_each_entry_rcu(entry, ima_rules, list) {
@@ -424,6 +516,7 @@
action |= IMA_FAIL_UNVERIFIABLE_SIGS;
}
+
if (entry->action & IMA_DO_MASK)
actmask &= ~(entry->action | entry->action << 1);
else
@@ -432,6 +525,9 @@
if ((pcr) && (entry->flags & IMA_PCR))
*pcr = entry->pcr;
+ if (template_desc && entry->template)
+ *template_desc = entry->template;
+
if (!actmask)
break;
}
@@ -473,6 +569,76 @@
return 0;
}
+static void add_rules(struct ima_rule_entry *entries, int count,
+ enum policy_rule_list policy_rule)
+{
+ int i = 0;
+
+ for (i = 0; i < count; i++) {
+ struct ima_rule_entry *entry;
+
+ if (policy_rule & IMA_DEFAULT_POLICY)
+ list_add_tail(&entries[i].list, &ima_default_rules);
+
+ if (policy_rule & IMA_CUSTOM_POLICY) {
+ entry = kmemdup(&entries[i], sizeof(*entry),
+ GFP_KERNEL);
+ if (!entry)
+ continue;
+
+ list_add_tail(&entry->list, &ima_policy_rules);
+ }
+ if (entries[i].action == APPRAISE) {
+ temp_ima_appraise |= ima_appraise_flag(entries[i].func);
+ if (entries[i].func == POLICY_CHECK)
+ temp_ima_appraise |= IMA_APPRAISE_POLICY;
+ }
+ }
+}
+
+static int ima_parse_rule(char *rule, struct ima_rule_entry *entry);
+
+static int __init ima_init_arch_policy(void)
+{
+ const char * const *arch_rules;
+ const char * const *rules;
+ int arch_entries = 0;
+ int i = 0;
+
+ arch_rules = arch_get_ima_policy();
+ if (!arch_rules)
+ return arch_entries;
+
+ /* Get number of rules */
+ for (rules = arch_rules; *rules != NULL; rules++)
+ arch_entries++;
+
+ arch_policy_entry = kcalloc(arch_entries + 1,
+ sizeof(*arch_policy_entry), GFP_KERNEL);
+ if (!arch_policy_entry)
+ return 0;
+
+ /* Convert each policy string rules to struct ima_rule_entry format */
+ for (rules = arch_rules, i = 0; *rules != NULL; rules++) {
+ char rule[255];
+ int result;
+
+ result = strlcpy(rule, *rules, sizeof(rule));
+
+ INIT_LIST_HEAD(&arch_policy_entry[i].list);
+ result = ima_parse_rule(rule, &arch_policy_entry[i]);
+ if (result) {
+ pr_warn("Skipping unknown architecture policy rule: %s\n",
+ rule);
+ memset(&arch_policy_entry[i], 0,
+ sizeof(*arch_policy_entry));
+ continue;
+ }
+ i++;
+ }
+ return i;
+}
+
/**
* ima_init_policy - initialize the default measure rules.
*
@@ -481,68 +647,68 @@
*/
void __init ima_init_policy(void)
{
- int i, measure_entries, appraise_entries, secure_boot_entries;
+ int build_appraise_entries, arch_entries;
- /* if !ima_policy set entries = 0 so we load NO default rules */
- measure_entries = ima_policy ? ARRAY_SIZE(dont_measure_rules) : 0;
- appraise_entries = ima_use_appraise_tcb ?
- ARRAY_SIZE(default_appraise_rules) : 0;
- secure_boot_entries = ima_use_secure_boot ?
- ARRAY_SIZE(secure_boot_rules) : 0;
-
- for (i = 0; i < measure_entries; i++)
- list_add_tail(&dont_measure_rules[i].list, &ima_default_rules);
+ /* if !ima_policy, we load NO default rules */
+ if (ima_policy)
+ add_rules(dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
+ IMA_DEFAULT_POLICY);
switch (ima_policy) {
case ORIGINAL_TCB:
- for (i = 0; i < ARRAY_SIZE(original_measurement_rules); i++)
- list_add_tail(&original_measurement_rules[i].list,
- &ima_default_rules);
+ add_rules(original_measurement_rules,
+ ARRAY_SIZE(original_measurement_rules),
+ IMA_DEFAULT_POLICY);
break;
case DEFAULT_TCB:
- for (i = 0; i < ARRAY_SIZE(default_measurement_rules); i++)
- list_add_tail(&default_measurement_rules[i].list,
- &ima_default_rules);
+ add_rules(default_measurement_rules,
+ ARRAY_SIZE(default_measurement_rules),
+ IMA_DEFAULT_POLICY);
default:
break;
}
/*
- * Insert the builtin "secure_boot" policy rules requiring file
- * signatures, prior to any other appraise rules.
+ * Based on runtime secure boot flags, insert arch specific measurement
+ * and appraise rules requiring file signatures for both the initial
+ * and custom policies, prior to other appraise rules.
+ * (Highest priority)
*/
- for (i = 0; i < secure_boot_entries; i++) {
- list_add_tail(&secure_boot_rules[i].list, &ima_default_rules);
- temp_ima_appraise |=
- ima_appraise_flag(secure_boot_rules[i].func);
- }
+ arch_entries = ima_init_arch_policy();
+ if (!arch_entries)
+ pr_info("No architecture policies found\n");
+ else
+ add_rules(arch_policy_entry, arch_entries,
+ IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
+
+ /*
+ * Insert the builtin "secure_boot" policy rules requiring file
+ * signatures, prior to other appraise rules.
+ */
+ if (ima_use_secure_boot)
+ add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules),
+ IMA_DEFAULT_POLICY);
/*
* Insert the build time appraise rules requiring file signatures
* for both the initial and custom policies, prior to other appraise
- * rules.
+ * rules. As the secure boot rules includes all of the build time
+ * rules, include either one or the other set of rules, but not both.
*/
- for (i = 0; i < ARRAY_SIZE(build_appraise_rules); i++) {
- struct ima_rule_entry *entry;
-
- if (!secure_boot_entries)
- list_add_tail(&build_appraise_rules[i].list,
- &ima_default_rules);
-
- entry = kmemdup(&build_appraise_rules[i], sizeof(*entry),
- GFP_KERNEL);
- if (entry)
- list_add_tail(&entry->list, &ima_policy_rules);
- build_ima_appraise |=
- ima_appraise_flag(build_appraise_rules[i].func);
+ build_appraise_entries = ARRAY_SIZE(build_appraise_rules);
+ if (build_appraise_entries) {
+ if (ima_use_secure_boot)
+ add_rules(build_appraise_rules, build_appraise_entries,
+ IMA_CUSTOM_POLICY);
+ else
+ add_rules(build_appraise_rules, build_appraise_entries,
+ IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
}
- for (i = 0; i < appraise_entries; i++) {
- list_add_tail(&default_appraise_rules[i].list,
- &ima_default_rules);
- if (default_appraise_rules[i].func == POLICY_CHECK)
- temp_ima_appraise |= IMA_APPRAISE_POLICY;
- }
+ if (ima_use_appraise_tcb)
+ add_rules(default_appraise_rules,
+ ARRAY_SIZE(default_appraise_rules),
+ IMA_DEFAULT_POLICY);
ima_rules = &ima_default_rules;
ima_update_policy_flag();
@@ -576,13 +742,21 @@
if (ima_rules != policy) {
ima_policy_flag = 0;
ima_rules = policy;
+
+ /*
+ * IMA architecture specific policy rules are specified
+ * as strings and converted to an array of ima_entry_rules
+ * on boot. After loading a custom policy, free the
+ * architecture specific rules stored as an array.
+ */
+ kfree(arch_policy_entry);
}
ima_update_policy_flag();
}
+/* Keep the enumeration in sync with the policy_tokens! */
enum {
- Opt_err = -1,
- Opt_measure = 1, Opt_dont_measure,
+ Opt_measure, Opt_dont_measure,
Opt_appraise, Opt_dont_appraise,
Opt_audit, Opt_hash, Opt_dont_hash,
Opt_obj_user, Opt_obj_role, Opt_obj_type,
@@ -592,10 +766,10 @@
Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt,
Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
Opt_appraise_type, Opt_permit_directio,
- Opt_pcr
+ Opt_pcr, Opt_template, Opt_err
};
-static match_table_t policy_tokens = {
+static const match_table_t policy_tokens = {
{Opt_measure, "measure"},
{Opt_dont_measure, "dont_measure"},
{Opt_appraise, "appraise"},
@@ -626,6 +800,7 @@
{Opt_appraise_type, "appraise_type=%s"},
{Opt_permit_directio, "permit_directio"},
{Opt_pcr, "pcr=%s"},
+ {Opt_template, "template=%s"},
{Opt_err, NULL}
};
@@ -673,12 +848,45 @@
ima_log_string_op(ab, key, value, NULL);
}
+/*
+ * Validating the appended signature included in the measurement list requires
+ * the file hash calculated without the appended signature (i.e., the 'd-modsig'
+ * field). Therefore, notify the user if they have the 'modsig' field but not
+ * the 'd-modsig' field in the template.
+ */
+static void check_template_modsig(const struct ima_template_desc *template)
+{
+#define MSG "template with 'modsig' field also needs 'd-modsig' field\n"
+ bool has_modsig, has_dmodsig;
+ static bool checked;
+ int i;
+
+ /* We only need to notify the user once. */
+ if (checked)
+ return;
+
+ has_modsig = has_dmodsig = false;
+ for (i = 0; i < template->num_fields; i++) {
+ if (!strcmp(template->fields[i]->field_id, "modsig"))
+ has_modsig = true;
+ else if (!strcmp(template->fields[i]->field_id, "d-modsig"))
+ has_dmodsig = true;
+ }
+
+ if (has_modsig && !has_dmodsig)
+ pr_notice(MSG);
+
+ checked = true;
+#undef MSG
+}
+
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
{
struct audit_buffer *ab;
char *from;
char *p;
bool uid_token;
+ struct ima_template_desc *template_desc;
int result = 0;
ab = integrity_audit_log_start(audit_context(), GFP_KERNEL,
@@ -786,6 +994,8 @@
entry->func = KEXEC_INITRAMFS_CHECK;
else if (strcmp(args[0].from, "POLICY_CHECK") == 0)
entry->func = POLICY_CHECK;
+ else if (strcmp(args[0].from, "KEXEC_CMDLINE") == 0)
+ entry->func = KEXEC_CMDLINE;
else
result = -EINVAL;
if (!result)
@@ -853,10 +1063,12 @@
case Opt_uid_gt:
case Opt_euid_gt:
entry->uid_op = &uid_gt;
+ /* fall through */
case Opt_uid_lt:
case Opt_euid_lt:
if ((token == Opt_uid_lt) || (token == Opt_euid_lt))
entry->uid_op = &uid_lt;
+ /* fall through */
case Opt_uid_eq:
case Opt_euid_eq:
uid_token = (token == Opt_uid_eq) ||
@@ -885,9 +1097,11 @@
break;
case Opt_fowner_gt:
entry->fowner_op = &uid_gt;
+ /* fall through */
case Opt_fowner_lt:
if (token == Opt_fowner_lt)
entry->fowner_op = &uid_lt;
+ /* fall through */
case Opt_fowner_eq:
ima_log_string_op(ab, "fowner", args[0].from,
entry->fowner_op);
@@ -951,6 +1165,10 @@
ima_log_string(ab, "appraise_type", args[0].from);
if ((strcmp(args[0].from, "imasig")) == 0)
entry->flags |= IMA_DIGSIG_REQUIRED;
+ else if (ima_hook_supports_modsig(entry->func) &&
+ strcmp(args[0].from, "imasig|modsig") == 0)
+ entry->flags |= IMA_DIGSIG_REQUIRED |
+ IMA_MODSIG_ALLOWED;
else
result = -EINVAL;
break;
@@ -971,6 +1189,28 @@
entry->flags |= IMA_PCR;
break;
+ case Opt_template:
+ ima_log_string(ab, "template", args[0].from);
+ if (entry->action != MEASURE) {
+ result = -EINVAL;
+ break;
+ }
+ template_desc = lookup_template_desc(args[0].from);
+ if (!template_desc || entry->template) {
+ result = -EINVAL;
+ break;
+ }
+
+ /*
+ * template_desc_init_fields() does nothing if
+ * the template is already initialised, so
+ * it's safe to do this unconditionally
+ */
+ template_desc_init_fields(template_desc->fmt,
+ &(template_desc->fields),
+ &(template_desc->num_fields));
+ entry->template = template_desc;
+ break;
case Opt_err:
ima_log_string(ab, "UNKNOWN", p);
result = -EINVAL;
@@ -982,6 +1222,12 @@
else if (entry->action == APPRAISE)
temp_ima_appraise |= ima_appraise_flag(entry->func);
+ if (!result && entry->flags & IMA_MODSIG_ALLOWED) {
+ template_desc = entry->template ? entry->template :
+ ima_template_desc_current();
+ check_template_modsig(template_desc);
+ }
+
audit_log_format(ab, "res=%d", !result);
audit_log_end(ab);
return result;
@@ -1053,22 +1299,22 @@
}
}
+#define __ima_hook_stringify(str) (#str),
+
+const char *const func_tokens[] = {
+ __ima_hooks(__ima_hook_stringify)
+};
+
#ifdef CONFIG_IMA_READ_POLICY
enum {
mask_exec = 0, mask_write, mask_read, mask_append
};
static const char *const mask_tokens[] = {
- "MAY_EXEC",
- "MAY_WRITE",
- "MAY_READ",
- "MAY_APPEND"
-};
-
-#define __ima_hook_stringify(str) (#str),
-
-static const char *const func_tokens[] = {
- __ima_hooks(__ima_hook_stringify)
+ "^MAY_EXEC",
+ "^MAY_WRITE",
+ "^MAY_READ",
+ "^MAY_APPEND"
};
void *ima_policy_start(struct seq_file *m, loff_t *pos)
@@ -1103,7 +1349,7 @@
{
}
-#define pt(token) policy_tokens[token + Opt_err].pattern
+#define pt(token) policy_tokens[token].pattern
#define mt(token) mask_tokens[token]
/*
@@ -1122,6 +1368,7 @@
struct ima_rule_entry *entry = v;
int i;
char tbuf[64] = {0,};
+ int offset = 0;
rcu_read_lock();
@@ -1145,15 +1392,17 @@
if (entry->flags & IMA_FUNC)
policy_func_show(m, entry->func);
- if (entry->flags & IMA_MASK) {
+ if ((entry->flags & IMA_MASK) || (entry->flags & IMA_INMASK)) {
+ if (entry->flags & IMA_MASK)
+ offset = 1;
if (entry->mask & MAY_EXEC)
- seq_printf(m, pt(Opt_mask), mt(mask_exec));
+ seq_printf(m, pt(Opt_mask), mt(mask_exec) + offset);
if (entry->mask & MAY_WRITE)
- seq_printf(m, pt(Opt_mask), mt(mask_write));
+ seq_printf(m, pt(Opt_mask), mt(mask_write) + offset);
if (entry->mask & MAY_READ)
- seq_printf(m, pt(Opt_mask), mt(mask_read));
+ seq_printf(m, pt(Opt_mask), mt(mask_read) + offset);
if (entry->mask & MAY_APPEND)
- seq_printf(m, pt(Opt_mask), mt(mask_append));
+ seq_printf(m, pt(Opt_mask), mt(mask_append) + offset);
seq_puts(m, " ");
}
@@ -1243,8 +1492,14 @@
}
}
}
- if (entry->flags & IMA_DIGSIG_REQUIRED)
- seq_puts(m, "appraise_type=imasig ");
+ if (entry->template)
+ seq_printf(m, "template=%s ", entry->template->name);
+ if (entry->flags & IMA_DIGSIG_REQUIRED) {
+ if (entry->flags & IMA_MODSIG_ALLOWED)
+ seq_puts(m, "appraise_type=imasig|modsig ");
+ else
+ seq_puts(m, "appraise_type=imasig ");
+ }
if (entry->flags & IMA_PERMIT_DIRECTIO)
seq_puts(m, "permit_directio ");
rcu_read_unlock();
@@ -1252,3 +1507,53 @@
return 0;
}
#endif /* CONFIG_IMA_READ_POLICY */
+
+#if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
+/*
+ * ima_appraise_signature: whether IMA will appraise a given function using
+ * an IMA digital signature. This is restricted to cases where the kernel
+ * has a set of built-in trusted keys in order to avoid an attacker simply
+ * loading additional keys.
+ */
+bool ima_appraise_signature(enum kernel_read_file_id id)
+{
+ struct ima_rule_entry *entry;
+ bool found = false;
+ enum ima_hooks func;
+
+ if (id >= READING_MAX_ID)
+ return false;
+
+ func = read_idmap[id] ?: FILE_CHECK;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(entry, ima_rules, list) {
+ if (entry->action != APPRAISE)
+ continue;
+
+ /*
+ * A generic entry will match, but otherwise require that it
+ * match the func we're looking for
+ */
+ if (entry->func && entry->func != func)
+ continue;
+
+ /*
+ * We require this to be a digital signature, not a raw IMA
+ * hash.
+ */
+ if (entry->flags & IMA_DIGSIG_REQUIRED)
+ found = true;
+
+ /*
+ * We've found a rule that matches, so break now even if it
+ * didn't require a digital signature - a later rule that does
+ * won't override it, so would be a false positive.
+ */
+ break;
+ }
+
+ rcu_read_unlock();
+ return found;
+}
+#endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index b186819..1ce8b17 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2005,2006,2007,2008 IBM Corporation
*
@@ -6,11 +7,6 @@
* Reiner Sailer <sailer@watson.ibm.com>
* Mimi Zohar <zohar@us.ibm.com>
*
- * 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 of the
- * License.
- *
* File: ima_queue.c
* Implements queues that store template measurements and
* maintains aggregate over the stored measurements
@@ -21,13 +17,15 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
#include <linux/rculist.h>
#include <linux/slab.h>
#include "ima.h"
#define AUDIT_CAUSE_LEN_MAX 32
+/* pre-allocated array of tpm_digest structures to extend a PCR */
+static struct tpm_digest *digests;
+
LIST_HEAD(ima_measurements); /* list of all measurements */
#ifdef CONFIG_IMA_KEXEC
static unsigned long binary_runtime_size;
@@ -141,11 +139,15 @@
static int ima_pcr_extend(const u8 *hash, int pcr)
{
int result = 0;
+ int i;
if (!ima_tpm_chip)
return result;
- result = tpm_pcr_extend(ima_tpm_chip, pcr, hash);
+ for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++)
+ memcpy(digests[i].digest, hash, TPM_DIGEST_SIZE);
+
+ result = tpm_pcr_extend(ima_tpm_chip, pcr, digests);
if (result != 0)
pr_err("Error Communicating to TPM chip, result: %d\n", result);
return result;
@@ -212,3 +214,21 @@
mutex_unlock(&ima_extend_list_mutex);
return result;
}
+
+int __init ima_init_digests(void)
+{
+ int i;
+
+ if (!ima_tpm_chip)
+ return 0;
+
+ digests = kcalloc(ima_tpm_chip->nr_allocated_banks, sizeof(*digests),
+ GFP_NOFS);
+ if (!digests)
+ return -ENOMEM;
+
+ for (i = 0; i < ima_tpm_chip->nr_allocated_banks; i++)
+ digests[i].alg_id = ima_tpm_chip->allocated_banks[i].alg_id;
+
+ return 0;
+}
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 30db39b..6aa6408 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Politecnico di Torino, Italy
* TORSEC group -- http://security.polito.it
*
* Author: Roberto Sassu <roberto.sassu@polito.it>
*
- * 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 of the
- * License.
- *
* File: ima_template.c
* Helpers to manage template descriptors.
*/
@@ -26,13 +22,15 @@
{.name = IMA_TEMPLATE_IMA_NAME, .fmt = IMA_TEMPLATE_IMA_FMT},
{.name = "ima-ng", .fmt = "d-ng|n-ng"},
{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
+ {.name = "ima-buf", .fmt = "d-ng|n-ng|buf"},
+ {.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"},
{.name = "", .fmt = ""}, /* placeholder for a custom format */
};
static LIST_HEAD(defined_templates);
static DEFINE_SPINLOCK(template_list);
-static struct ima_template_field supported_fields[] = {
+static const struct ima_template_field supported_fields[] = {
{.field_id = "d", .field_init = ima_eventdigest_init,
.field_show = ima_show_template_digest},
{.field_id = "n", .field_init = ima_eventname_init,
@@ -43,14 +41,41 @@
.field_show = ima_show_template_string},
{.field_id = "sig", .field_init = ima_eventsig_init,
.field_show = ima_show_template_sig},
+ {.field_id = "buf", .field_init = ima_eventbuf_init,
+ .field_show = ima_show_template_buf},
+ {.field_id = "d-modsig", .field_init = ima_eventdigest_modsig_init,
+ .field_show = ima_show_template_digest_ng},
+ {.field_id = "modsig", .field_init = ima_eventmodsig_init,
+ .field_show = ima_show_template_sig},
};
-#define MAX_TEMPLATE_NAME_LEN 15
+
+/*
+ * Used when restoring measurements carried over from a kexec. 'd' and 'n' don't
+ * need to be accounted for since they shouldn't be defined in the same template
+ * description as 'd-ng' and 'n-ng' respectively.
+ */
+#define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig")
static struct ima_template_desc *ima_template;
-static struct ima_template_desc *lookup_template_desc(const char *name);
-static int template_desc_init_fields(const char *template_fmt,
- struct ima_template_field ***fields,
- int *num_fields);
+
+/**
+ * ima_template_has_modsig - Check whether template has modsig-related fields.
+ * @ima_template: IMA template to check.
+ *
+ * Tells whether the given template has fields referencing a file's appended
+ * signature.
+ */
+bool ima_template_has_modsig(const struct ima_template_desc *ima_template)
+{
+ int i;
+
+ for (i = 0; i < ima_template->num_fields; i++)
+ if (!strcmp(ima_template->fields[i]->field_id, "modsig") ||
+ !strcmp(ima_template->fields[i]->field_id, "d-modsig"))
+ return true;
+
+ return false;
+}
static int __init ima_template_setup(char *str)
{
@@ -108,7 +133,7 @@
}
__setup("ima_template_fmt=", ima_template_fmt_setup);
-static struct ima_template_desc *lookup_template_desc(const char *name)
+struct ima_template_desc *lookup_template_desc(const char *name)
{
struct ima_template_desc *template_desc;
int found = 0;
@@ -125,7 +150,8 @@
return found ? template_desc : NULL;
}
-static struct ima_template_field *lookup_template_field(const char *field_id)
+static const struct ima_template_field *
+lookup_template_field(const char *field_id)
{
int i;
@@ -152,12 +178,12 @@
return j + 1;
}
-static int template_desc_init_fields(const char *template_fmt,
- struct ima_template_field ***fields,
- int *num_fields)
+int template_desc_init_fields(const char *template_fmt,
+ const struct ima_template_field ***fields,
+ int *num_fields)
{
const char *template_fmt_ptr;
- struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX];
+ const struct ima_template_field *found_fields[IMA_TEMPLATE_NUM_FIELDS_MAX];
int template_num_fields;
int i, len;
@@ -280,9 +306,8 @@
int ret = 0;
int i;
- *entry = kzalloc(sizeof(**entry) +
- template_desc->num_fields * sizeof(struct ima_field_data),
- GFP_NOFS);
+ *entry = kzalloc(struct_size(*entry, template_data,
+ template_desc->num_fields), GFP_NOFS);
if (!*entry)
return -ENOMEM;
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 4375200..32ae05d 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Politecnico di Torino, Italy
* TORSEC group -- http://security.polito.it
*
* Author: Roberto Sassu <roberto.sassu@polito.it>
*
- * 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 of the
- * License.
- *
* File: ima_template_lib.c
* Library of supported template fields.
*/
@@ -83,6 +79,7 @@
/* skip ':' and '\0' */
buf_ptr += 2;
buflen -= buf_ptr - field_data->data;
+ /* fall through */
case DATA_FMT_DIGEST:
case DATA_FMT_HEX:
if (!buflen)
@@ -161,6 +158,12 @@
ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
}
+void ima_show_template_buf(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data)
+{
+ ima_show_template_field_data(m, show, DATA_FMT_HEX, field_data);
+}
+
/**
* ima_parse_buf() - Parses lengths and data from an input buffer
* @bufstartp: Buffer start address.
@@ -222,7 +225,8 @@
return 0;
}
-static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo,
+static int ima_eventdigest_init_common(const u8 *digest, u32 digestsize,
+ u8 hash_algo,
struct ima_field_data *field_data)
{
/*
@@ -325,6 +329,41 @@
hash_algo, field_data);
}
+/*
+ * This function writes the digest of the file which is expected to match the
+ * digest contained in the file's appended signature.
+ */
+int ima_eventdigest_modsig_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ enum hash_algo hash_algo;
+ const u8 *cur_digest;
+ u32 cur_digestsize;
+
+ if (!event_data->modsig)
+ return 0;
+
+ if (event_data->violation) {
+ /* Recording a violation. */
+ hash_algo = HASH_ALGO_SHA1;
+ cur_digest = NULL;
+ cur_digestsize = 0;
+ } else {
+ int rc;
+
+ rc = ima_get_modsig_digest(event_data->modsig, &hash_algo,
+ &cur_digest, &cur_digestsize);
+ if (rc)
+ return rc;
+ else if (hash_algo == HASH_ALGO__LAST || cur_digestsize == 0)
+ /* There was some error collecting the digest. */
+ return -EINVAL;
+ }
+
+ return ima_eventdigest_init_common(cur_digest, cur_digestsize,
+ hash_algo, field_data);
+}
+
static int ima_eventname_init_common(struct ima_event_data *event_data,
struct ima_field_data *field_data,
bool size_limit)
@@ -388,3 +427,44 @@
return ima_write_template_field_data(xattr_value, event_data->xattr_len,
DATA_FMT_HEX, field_data);
}
+
+/*
+ * ima_eventbuf_init - include the buffer(kexec-cmldine) as part of the
+ * template data.
+ */
+int ima_eventbuf_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ if ((!event_data->buf) || (event_data->buf_len == 0))
+ return 0;
+
+ return ima_write_template_field_data(event_data->buf,
+ event_data->buf_len, DATA_FMT_HEX,
+ field_data);
+}
+
+/*
+ * ima_eventmodsig_init - include the appended file signature as part of the
+ * template data
+ */
+int ima_eventmodsig_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
+{
+ const void *data;
+ u32 data_len;
+ int rc;
+
+ if (!event_data->modsig)
+ return 0;
+
+ /*
+ * modsig is a runtime structure containing pointers. Get its raw data
+ * instead.
+ */
+ rc = ima_get_raw_modsig(event_data->modsig, &data, &data_len);
+ if (rc)
+ return rc;
+
+ return ima_write_template_field_data(data, data_len, DATA_FMT_HEX,
+ field_data);
+}
diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h
index 6a3d8b8..9a88c79 100644
--- a/security/integrity/ima/ima_template_lib.h
+++ b/security/integrity/ima/ima_template_lib.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2013 Politecnico di Torino, Italy
* TORSEC group -- http://security.polito.it
*
* Author: Roberto Sassu <roberto.sassu@polito.it>
*
- * 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 of the
- * License.
- *
* File: ima_template_lib.h
* Header for the library of supported template fields.
*/
@@ -29,6 +25,8 @@
struct ima_field_data *field_data);
void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
+void ima_show_template_buf(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data);
int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
int maxfields, struct ima_field_data *fields, int *curfields,
unsigned long *len_mask, int enforce_mask, char *bufname);
@@ -38,8 +36,14 @@
struct ima_field_data *field_data);
int ima_eventdigest_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
+int ima_eventdigest_modsig_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
int ima_eventname_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
int ima_eventsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
+int ima_eventbuf_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+int ima_eventmodsig_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index e60473b..d9323d3 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2009-2010 IBM Corporation
*
* Authors:
* Mimi Zohar <zohar@us.ibm.com>
- *
- * 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 of the
- * License.
- *
*/
#include <linux/types.h>
@@ -36,6 +31,7 @@
#define IMA_NEW_FILE 0x04000000
#define EVM_IMMUTABLE_DIGSIG 0x08000000
#define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000
+#define IMA_MODSIG_ALLOWED 0x20000000
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
IMA_HASH | IMA_APPRAISE_SUBMASK)
@@ -79,6 +75,12 @@
struct evm_ima_xattr_data {
u8 type;
+ u8 data[];
+} __packed;
+
+/* Only used in the EVM HMAC code. */
+struct evm_xattr {
+ struct evm_ima_xattr_data data;
u8 digest[SHA1_DIGEST_SIZE];
} __packed;
@@ -141,18 +143,23 @@
#define INTEGRITY_KEYRING_EVM 0
#define INTEGRITY_KEYRING_IMA 1
-#define INTEGRITY_KEYRING_MODULE 2
+#define INTEGRITY_KEYRING_PLATFORM 2
#define INTEGRITY_KEYRING_MAX 3
extern struct dentry *integrity_dir;
+struct modsig;
+
#ifdef CONFIG_INTEGRITY_SIGNATURE
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen);
+int integrity_modsig_verify(unsigned int id, const struct modsig *modsig);
int __init integrity_init_keyring(const unsigned int id);
int __init integrity_load_x509(const unsigned int id, const char *path);
+int __init integrity_load_cert(const unsigned int id, const char *source,
+ const void *data, size_t len, key_perm_t perm);
#else
static inline int integrity_digsig_verify(const unsigned int id,
@@ -162,10 +169,24 @@
return -EOPNOTSUPP;
}
+static inline int integrity_modsig_verify(unsigned int id,
+ const struct modsig *modsig)
+{
+ return -EOPNOTSUPP;
+}
+
static inline int integrity_init_keyring(const unsigned int id)
{
return 0;
}
+
+static inline int __init integrity_load_cert(const unsigned int id,
+ const char *source,
+ const void *data, size_t len,
+ key_perm_t perm)
+{
+ return 0;
+}
#endif /* CONFIG_INTEGRITY_SIGNATURE */
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
@@ -179,6 +200,16 @@
}
#endif
+#ifdef CONFIG_IMA_APPRAISE_MODSIG
+int ima_modsig_verify(struct key *keyring, const struct modsig *modsig);
+#else
+static inline int ima_modsig_verify(struct key *keyring,
+ const struct modsig *modsig)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
#ifdef CONFIG_IMA_LOAD_X509
void __init ima_load_x509(void);
#else
@@ -222,3 +253,13 @@
}
#endif
+
+#ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING
+void __init add_to_platform_keyring(const char *source, const void *data,
+ size_t len);
+#else
+static inline void __init add_to_platform_keyring(const char *source,
+ const void *data, size_t len)
+{
+}
+#endif
diff --git a/security/integrity/integrity_audit.c b/security/integrity/integrity_audit.c
index 82c98f7..5109173 100644
--- a/security/integrity/integrity_audit.c
+++ b/security/integrity/integrity_audit.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2008 IBM Corporation
* Author: Mimi Zohar <zohar@us.ibm.com>
*
- * 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 of the License.
- *
* File: integrity_audit.c
* Audit calls for the integrity subsystem
*/
diff --git a/security/integrity/platform_certs/efi_parser.c b/security/integrity/platform_certs/efi_parser.c
new file mode 100644
index 0000000..18f01f3
--- /dev/null
+++ b/security/integrity/platform_certs/efi_parser.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* EFI signature/key/certificate list parser
+ *
+ * Copyright (C) 2012, 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "EFI: "fmt
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/err.h>
+#include <linux/efi.h>
+
+/**
+ * parse_efi_signature_list - Parse an EFI signature list for certificates
+ * @source: The source of the key
+ * @data: The data blob to parse
+ * @size: The size of the data blob
+ * @get_handler_for_guid: Get the handler func for the sig type (or NULL)
+ *
+ * Parse an EFI signature list looking for elements of interest. A list is
+ * made up of a series of sublists, where all the elements in a sublist are of
+ * the same type, but sublists can be of different types.
+ *
+ * For each sublist encountered, the @get_handler_for_guid function is called
+ * with the type specifier GUID and returns either a pointer to a function to
+ * handle elements of that type or NULL if the type is not of interest.
+ *
+ * If the sublist is of interest, each element is passed to the handler
+ * function in turn.
+ *
+ * Error EBADMSG is returned if the list doesn't parse correctly and 0 is
+ * returned if the list was parsed correctly. No error can be returned from
+ * the @get_handler_for_guid function or the element handler function it
+ * returns.
+ */
+int __init parse_efi_signature_list(
+ const char *source,
+ const void *data, size_t size,
+ efi_element_handler_t (*get_handler_for_guid)(const efi_guid_t *))
+{
+ efi_element_handler_t handler;
+ unsigned int offs = 0;
+
+ pr_devel("-->%s(,%zu)\n", __func__, size);
+
+ while (size > 0) {
+ const efi_signature_data_t *elem;
+ efi_signature_list_t list;
+ size_t lsize, esize, hsize, elsize;
+
+ if (size < sizeof(list))
+ return -EBADMSG;
+
+ memcpy(&list, data, sizeof(list));
+ pr_devel("LIST[%04x] guid=%pUl ls=%x hs=%x ss=%x\n",
+ offs,
+ list.signature_type.b, list.signature_list_size,
+ list.signature_header_size, list.signature_size);
+
+ lsize = list.signature_list_size;
+ hsize = list.signature_header_size;
+ esize = list.signature_size;
+ elsize = lsize - sizeof(list) - hsize;
+
+ if (lsize > size) {
+ pr_devel("<--%s() = -EBADMSG [overrun @%x]\n",
+ __func__, offs);
+ return -EBADMSG;
+ }
+
+ if (lsize < sizeof(list) ||
+ lsize - sizeof(list) < hsize ||
+ esize < sizeof(*elem) ||
+ elsize < esize ||
+ elsize % esize != 0) {
+ pr_devel("- bad size combo @%x\n", offs);
+ return -EBADMSG;
+ }
+
+ handler = get_handler_for_guid(&list.signature_type);
+ if (!handler) {
+ data += lsize;
+ size -= lsize;
+ offs += lsize;
+ continue;
+ }
+
+ data += sizeof(list) + hsize;
+ size -= sizeof(list) + hsize;
+ offs += sizeof(list) + hsize;
+
+ for (; elsize > 0; elsize -= esize) {
+ elem = data;
+
+ pr_devel("ELEM[%04x]\n", offs);
+ handler(source,
+ &elem->signature_data,
+ esize - sizeof(*elem));
+
+ data += esize;
+ size -= esize;
+ offs += esize;
+ }
+ }
+
+ return 0;
+}
diff --git a/security/integrity/platform_certs/load_ipl_s390.c b/security/integrity/platform_certs/load_ipl_s390.c
new file mode 100644
index 0000000..e769dcb
--- /dev/null
+++ b/security/integrity/platform_certs/load_ipl_s390.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <linux/efi.h>
+#include <linux/slab.h>
+#include <keys/asymmetric-type.h>
+#include <keys/system_keyring.h>
+#include <asm/boot_data.h>
+#include "../integrity.h"
+
+/*
+ * Load the certs contained in the IPL report created by the machine loader
+ * into the platform trusted keyring.
+ */
+static int __init load_ipl_certs(void)
+{
+ void *ptr, *end;
+ unsigned int len;
+
+ if (!ipl_cert_list_addr)
+ return 0;
+ /* Copy the certificates to the system keyring */
+ ptr = (void *) ipl_cert_list_addr;
+ end = ptr + ipl_cert_list_size;
+ while ((void *) ptr < end) {
+ len = *(unsigned int *) ptr;
+ ptr += sizeof(unsigned int);
+ add_to_platform_keyring("IPL:db", ptr, len);
+ ptr += len;
+ }
+ return 0;
+}
+late_initcall(load_ipl_certs);
diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c
new file mode 100644
index 0000000..81b19c5
--- /dev/null
+++ b/security/integrity/platform_certs/load_uefi.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <linux/efi.h>
+#include <linux/slab.h>
+#include <keys/asymmetric-type.h>
+#include <keys/system_keyring.h>
+#include "../integrity.h"
+
+static efi_guid_t efi_cert_x509_guid __initdata = EFI_CERT_X509_GUID;
+static efi_guid_t efi_cert_x509_sha256_guid __initdata =
+ EFI_CERT_X509_SHA256_GUID;
+static efi_guid_t efi_cert_sha256_guid __initdata = EFI_CERT_SHA256_GUID;
+
+/*
+ * Look to see if a UEFI variable called MokIgnoreDB exists and return true if
+ * it does.
+ *
+ * This UEFI variable is set by the shim if a user tells the shim to not use
+ * the certs/hashes in the UEFI db variable for verification purposes. If it
+ * is set, we should ignore the db variable also and the true return indicates
+ * this.
+ */
+static __init bool uefi_check_ignore_db(void)
+{
+ efi_status_t status;
+ unsigned int db = 0;
+ unsigned long size = sizeof(db);
+ efi_guid_t guid = EFI_SHIM_LOCK_GUID;
+
+ status = efi.get_variable(L"MokIgnoreDB", &guid, NULL, &size, &db);
+ return status == EFI_SUCCESS;
+}
+
+/*
+ * Get a certificate list blob from the named EFI variable.
+ */
+static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid,
+ unsigned long *size)
+{
+ efi_status_t status;
+ unsigned long lsize = 4;
+ unsigned long tmpdb[4];
+ void *db;
+
+ status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb);
+ if (status != EFI_BUFFER_TOO_SMALL) {
+ pr_err("Couldn't get size: 0x%lx\n", status);
+ return NULL;
+ }
+
+ db = kmalloc(lsize, GFP_KERNEL);
+ if (!db)
+ return NULL;
+
+ status = efi.get_variable(name, guid, NULL, &lsize, db);
+ if (status != EFI_SUCCESS) {
+ kfree(db);
+ pr_err("Error reading db var: 0x%lx\n", status);
+ return NULL;
+ }
+
+ *size = lsize;
+ return db;
+}
+
+/*
+ * Blacklist a hash.
+ */
+static __init void uefi_blacklist_hash(const char *source, const void *data,
+ size_t len, const char *type,
+ size_t type_len)
+{
+ char *hash, *p;
+
+ hash = kmalloc(type_len + len * 2 + 1, GFP_KERNEL);
+ if (!hash)
+ return;
+ p = memcpy(hash, type, type_len);
+ p += type_len;
+ bin2hex(p, data, len);
+ p += len * 2;
+ *p = 0;
+
+ mark_hash_blacklisted(hash);
+ kfree(hash);
+}
+
+/*
+ * Blacklist an X509 TBS hash.
+ */
+static __init void uefi_blacklist_x509_tbs(const char *source,
+ const void *data, size_t len)
+{
+ uefi_blacklist_hash(source, data, len, "tbs:", 4);
+}
+
+/*
+ * Blacklist the hash of an executable.
+ */
+static __init void uefi_blacklist_binary(const char *source,
+ const void *data, size_t len)
+{
+ uefi_blacklist_hash(source, data, len, "bin:", 4);
+}
+
+/*
+ * Return the appropriate handler for particular signature list types found in
+ * the UEFI db and MokListRT tables.
+ */
+static __init efi_element_handler_t get_handler_for_db(const efi_guid_t *
+ sig_type)
+{
+ if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
+ return add_to_platform_keyring;
+ return 0;
+}
+
+/*
+ * Return the appropriate handler for particular signature list types found in
+ * the UEFI dbx and MokListXRT tables.
+ */
+static __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *
+ sig_type)
+{
+ if (efi_guidcmp(*sig_type, efi_cert_x509_sha256_guid) == 0)
+ return uefi_blacklist_x509_tbs;
+ if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0)
+ return uefi_blacklist_binary;
+ return 0;
+}
+
+/*
+ * Load the certs contained in the UEFI databases into the platform trusted
+ * keyring and the UEFI blacklisted X.509 cert SHA256 hashes into the blacklist
+ * keyring.
+ */
+static int __init load_uefi_certs(void)
+{
+ efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
+ efi_guid_t mok_var = EFI_SHIM_LOCK_GUID;
+ void *db = NULL, *dbx = NULL, *mok = NULL;
+ unsigned long dbsize = 0, dbxsize = 0, moksize = 0;
+ int rc = 0;
+
+ if (!efi.get_variable)
+ return false;
+
+ /* Get db, MokListRT, and dbx. They might not exist, so it isn't
+ * an error if we can't get them.
+ */
+ if (!uefi_check_ignore_db()) {
+ db = get_cert_list(L"db", &secure_var, &dbsize);
+ if (!db) {
+ pr_err("MODSIGN: Couldn't get UEFI db list\n");
+ } else {
+ rc = parse_efi_signature_list("UEFI:db",
+ db, dbsize, get_handler_for_db);
+ if (rc)
+ pr_err("Couldn't parse db signatures: %d\n",
+ rc);
+ kfree(db);
+ }
+ }
+
+ mok = get_cert_list(L"MokListRT", &mok_var, &moksize);
+ if (!mok) {
+ pr_info("Couldn't get UEFI MokListRT\n");
+ } else {
+ rc = parse_efi_signature_list("UEFI:MokListRT",
+ mok, moksize, get_handler_for_db);
+ if (rc)
+ pr_err("Couldn't parse MokListRT signatures: %d\n", rc);
+ kfree(mok);
+ }
+
+ dbx = get_cert_list(L"dbx", &secure_var, &dbxsize);
+ if (!dbx) {
+ pr_info("Couldn't get UEFI dbx list\n");
+ } else {
+ rc = parse_efi_signature_list("UEFI:dbx",
+ dbx, dbxsize,
+ get_handler_for_dbx);
+ if (rc)
+ pr_err("Couldn't parse dbx signatures: %d\n", rc);
+ kfree(dbx);
+ }
+
+ return rc;
+}
+late_initcall(load_uefi_certs);
diff --git a/security/integrity/platform_certs/platform_keyring.c b/security/integrity/platform_certs/platform_keyring.c
new file mode 100644
index 0000000..bcafd73
--- /dev/null
+++ b/security/integrity/platform_certs/platform_keyring.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Platform keyring for firmware/platform keys
+ *
+ * Copyright IBM Corporation, 2018
+ * Author(s): Nayna Jain <nayna@linux.ibm.com>
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include "../integrity.h"
+
+/**
+ * add_to_platform_keyring - Add to platform keyring without validation.
+ * @source: Source of key
+ * @data: The blob holding the key
+ * @len: The length of the data blob
+ *
+ * Add a key to the platform keyring without checking its trust chain. This
+ * is available only during kernel initialisation.
+ */
+void __init add_to_platform_keyring(const char *source, const void *data,
+ size_t len)
+{
+ key_perm_t perm;
+ int rc;
+
+ perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW;
+
+ rc = integrity_load_cert(INTEGRITY_KEYRING_PLATFORM, source, data, len,
+ perm);
+ if (rc)
+ pr_info("Error adding keys to platform keyring %s\n", source);
+}
+
+/*
+ * Create the trusted keyrings.
+ */
+static __init int platform_keyring_init(void)
+{
+ int rc;
+
+ rc = integrity_init_keyring(INTEGRITY_KEYRING_PLATFORM);
+ if (rc)
+ return rc;
+
+ pr_notice("Platform Keyring initialized\n");
+ return 0;
+}
+
+/*
+ * Must be initialised before we try and load the keys into the keyring.
+ */
+device_initcall(platform_keyring_init);
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 6462e66..dd31343 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Key management configuration
#
@@ -24,6 +25,24 @@
def_bool y
depends on COMPAT && KEYS
+config KEYS_REQUEST_CACHE
+ bool "Enable temporary caching of the last request_key() result"
+ depends on KEYS
+ help
+ This option causes the result of the last successful request_key()
+ call that didn't upcall to the kernel to be cached temporarily in the
+ task_struct. The cache is cleared by exit and just prior to the
+ resumption of userspace.
+
+ This allows the key used for multiple step processes where each step
+ wants to request a key that is likely the same as the one requested
+ by the last step to save on the searching.
+
+ An example of such a process is a pathwalk through a network
+ filesystem in which each method needs to request an authentication
+ key. Pathwalk will call multiple methods for each dentry traversed
+ (permission, d_revalidate, lookup, getxattr, getacl, ...).
+
config PERSISTENT_KEYRINGS
bool "Enable register of persistent per-UID keyrings"
depends on KEYS
diff --git a/security/keys/Makefile b/security/keys/Makefile
index ef1581b..9cef540 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -22,6 +22,7 @@
obj-$(CONFIG_SYSCTL) += sysctl.o
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
+obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o
#
# Key types
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index 2806e70..001abe5 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Large capacity key type
*
* Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
*/
#define pr_fmt(fmt) "big_key: "fmt
diff --git a/security/keys/compat.c b/security/keys/compat.c
index e87c89c..9bcc404 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* 32-bit compatibility syscall for 64-bit systems
*
* Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * 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; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <linux/syscalls.h>
@@ -141,6 +137,30 @@
return keyctl_restrict_keyring(arg2, compat_ptr(arg3),
compat_ptr(arg4));
+ case KEYCTL_PKEY_QUERY:
+ if (arg3 != 0)
+ return -EINVAL;
+ return keyctl_pkey_query(arg2,
+ compat_ptr(arg4),
+ compat_ptr(arg5));
+
+ case KEYCTL_PKEY_ENCRYPT:
+ case KEYCTL_PKEY_DECRYPT:
+ case KEYCTL_PKEY_SIGN:
+ return keyctl_pkey_e_d_s(option,
+ compat_ptr(arg2), compat_ptr(arg3),
+ compat_ptr(arg4), compat_ptr(arg5));
+
+ case KEYCTL_PKEY_VERIFY:
+ return keyctl_pkey_verify(compat_ptr(arg2), compat_ptr(arg3),
+ compat_ptr(arg4), compat_ptr(arg5));
+
+ case KEYCTL_MOVE:
+ return keyctl_keyring_move(arg2, arg3, arg4, arg5);
+
+ case KEYCTL_CAPABILITIES:
+ return keyctl_capabilities(compat_ptr(arg2), arg3);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/compat_dh.c b/security/keys/compat_dh.c
index aa6b34c..19384e7 100644
--- a/security/keys/compat_dh.c
+++ b/security/keys/compat_dh.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* 32-bit compatibility syscall for 64-bit systems for DH operations
*
* Copyright (C) 2016 Stephan Mueller <smueller@chronox.de>
- *
- * 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; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <linux/uaccess.h>
diff --git a/security/keys/dh.c b/security/keys/dh.c
index 711e89d..c4c629b 100644
--- a/security/keys/dh.c
+++ b/security/keys/dh.c
@@ -1,11 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Crypto operations using stored keys
*
* Copyright (c) 2016, Intel Corporation
- *
- * 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; either version
- * 2 of the License, or (at your option) any later version.
*/
#include <linux/slab.h>
@@ -112,7 +108,6 @@
if (!sdesc)
goto out_free_tfm;
sdesc->shash.tfm = tfm;
- sdesc->shash.flags = 0x0;
*sdesc_ret = sdesc;
diff --git a/security/keys/encrypted-keys/ecryptfs_format.c b/security/keys/encrypted-keys/ecryptfs_format.c
index 6daa3b6..a7339d4 100644
--- a/security/keys/encrypted-keys/ecryptfs_format.c
+++ b/security/keys/encrypted-keys/ecryptfs_format.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ecryptfs_format.c: helper functions for the encrypted key type
*
@@ -9,13 +10,10 @@
* Michael A. Halcrow <mahalcro@us.ibm.com>
* Tyler Hicks <tyhicks@ou.edu>
* Roberto Sassu <roberto.sassu@polito.it>
- *
- * 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 of the License.
*/
-#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/string.h>
#include "ecryptfs_format.h"
u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok)
@@ -77,5 +75,3 @@
return 0;
}
EXPORT_SYMBOL(ecryptfs_fill_auth_tok);
-
-MODULE_LICENSE("GPL");
diff --git a/security/keys/encrypted-keys/ecryptfs_format.h b/security/keys/encrypted-keys/ecryptfs_format.h
index 40294de..939621d 100644
--- a/security/keys/encrypted-keys/ecryptfs_format.h
+++ b/security/keys/encrypted-keys/ecryptfs_format.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ecryptfs_format.h: helper functions for the encrypted key type
*
@@ -9,10 +10,6 @@
* Michael A. Halcrow <mahalcro@us.ibm.com>
* Tyler Hicks <tyhicks@ou.edu>
* Roberto Sassu <roberto.sassu@polito.it>
- *
- * 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 of the License.
*/
#ifndef __KEYS_ECRYPTFS_H
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index d92cbf9..60720f5 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2010 IBM Corporation
* Copyright (C) 2010 Politecnico di Torino, Italy
@@ -7,10 +8,6 @@
* Mimi Zohar <zohar@us.ibm.com>
* Roberto Sassu <roberto.sassu@polito.it>
*
- * 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 of the License.
- *
* See Documentation/security/keys/trusted-encrypted.rst
*/
@@ -45,6 +42,7 @@
static const char blkcipher_alg[] = "cbc(aes)";
static const char key_format_default[] = "default";
static const char key_format_ecryptfs[] = "ecryptfs";
+static const char key_format_enc32[] = "enc32";
static unsigned int ivsize;
static int blksize;
@@ -54,20 +52,22 @@
#define HASH_SIZE SHA256_DIGEST_SIZE
#define MAX_DATA_SIZE 4096
#define MIN_DATA_SIZE 20
+#define KEY_ENC32_PAYLOAD_LEN 32
static struct crypto_shash *hash_tfm;
enum {
- Opt_err = -1, Opt_new, Opt_load, Opt_update
+ Opt_new, Opt_load, Opt_update, Opt_err
};
enum {
- Opt_error = -1, Opt_default, Opt_ecryptfs
+ Opt_default, Opt_ecryptfs, Opt_enc32, Opt_error
};
static const match_table_t key_format_tokens = {
{Opt_default, "default"},
{Opt_ecryptfs, "ecryptfs"},
+ {Opt_enc32, "enc32"},
{Opt_error, NULL}
};
@@ -195,6 +195,7 @@
key_format = match_token(p, key_format_tokens, args);
switch (key_format) {
case Opt_ecryptfs:
+ case Opt_enc32:
case Opt_default:
*format = p;
*master_desc = strsep(&datablob, " \t");
@@ -329,7 +330,6 @@
int err;
desc->tfm = tfm;
- desc->flags = 0;
err = crypto_shash_digest(desc, buf, buflen, digest);
shash_desc_zero(desc);
@@ -342,7 +342,7 @@
struct crypto_shash *tfm;
int err;
- tfm = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC);
+ tfm = crypto_alloc_shash(hmac_alg, 0, 0);
if (IS_ERR(tfm)) {
pr_err("encrypted_key: can't alloc %s transform: %ld\n",
hmac_alg, PTR_ERR(tfm));
@@ -625,15 +625,22 @@
format_len = (!format) ? strlen(key_format_default) : strlen(format);
decrypted_datalen = dlen;
payload_datalen = decrypted_datalen;
- if (format && !strcmp(format, key_format_ecryptfs)) {
- if (dlen != ECRYPTFS_MAX_KEY_BYTES) {
- pr_err("encrypted_key: keylen for the ecryptfs format "
- "must be equal to %d bytes\n",
- ECRYPTFS_MAX_KEY_BYTES);
- return ERR_PTR(-EINVAL);
+ if (format) {
+ if (!strcmp(format, key_format_ecryptfs)) {
+ if (dlen != ECRYPTFS_MAX_KEY_BYTES) {
+ pr_err("encrypted_key: keylen for the ecryptfs format must be equal to %d bytes\n",
+ ECRYPTFS_MAX_KEY_BYTES);
+ return ERR_PTR(-EINVAL);
+ }
+ decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES;
+ payload_datalen = sizeof(struct ecryptfs_auth_tok);
+ } else if (!strcmp(format, key_format_enc32)) {
+ if (decrypted_datalen != KEY_ENC32_PAYLOAD_LEN) {
+ pr_err("encrypted_key: enc32 key payload incorrect length: %d\n",
+ decrypted_datalen);
+ return ERR_PTR(-EINVAL);
+ }
}
- decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES;
- payload_datalen = sizeof(struct ecryptfs_auth_tok);
}
encrypted_datalen = roundup(decrypted_datalen, blksize);
@@ -984,7 +991,7 @@
{
int ret;
- hash_tfm = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC);
+ hash_tfm = crypto_alloc_shash(hash_alg, 0, 0);
if (IS_ERR(hash_tfm)) {
pr_err("encrypted_key: can't allocate %s transform: %ld\n",
hash_alg, PTR_ERR(hash_tfm));
diff --git a/security/keys/encrypted-keys/masterkey_trusted.c b/security/keys/encrypted-keys/masterkey_trusted.c
index cbf0bc1..c68528a 100644
--- a/security/keys/encrypted-keys/masterkey_trusted.c
+++ b/security/keys/encrypted-keys/masterkey_trusted.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2010 IBM Corporation
* Copyright (C) 2010 Politecnico di Torino, Italy
@@ -7,15 +8,10 @@
* Mimi Zohar <zohar@us.ibm.com>
* Roberto Sassu <roberto.sassu@polito.it>
*
- * 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 of the License.
- *
* See Documentation/security/keys/trusted-encrypted.rst
*/
#include <linux/uaccess.h>
-#include <linux/module.h>
#include <linux/err.h>
#include <keys/trusted-type.h>
#include <keys/encrypted-type.h>
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 7207e60..671dd73 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Key garbage collector
*
* Copyright (C) 2009-2011 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
*/
-#include <linux/module.h>
#include <linux/slab.h>
#include <linux/security.h>
#include <keys/keyring-type.h>
@@ -155,7 +150,7 @@
atomic_dec(&key->user->nikeys);
key_user_put(key->user);
-
+ key_put_tag(key->domain_tag);
kfree(key->description);
memzero_explicit(key, sizeof(*key));
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 9f8208d..c039373 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Authentication token and access key management internal defs
*
* Copyright (C) 2003-5, 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * 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; either version
- * 2 of the License, or (at your option) any later version.
*/
#ifndef _INTERNAL_H
@@ -89,10 +85,14 @@
extern struct mutex key_construction_mutex;
extern wait_queue_head_t request_key_conswq;
-
+extern void key_set_index_key(struct keyring_index_key *index_key);
extern struct key_type *key_type_lookup(const char *type);
extern void key_type_put(struct key_type *ktype);
+extern int __key_link_lock(struct key *keyring,
+ const struct keyring_index_key *index_key);
+extern int __key_move_lock(struct key *l_keyring, struct key *u_keyring,
+ const struct keyring_index_key *index_key);
extern int __key_link_begin(struct key *keyring,
const struct keyring_index_key *index_key,
struct assoc_array_edit **_edit);
@@ -123,6 +123,7 @@
#define KEYRING_SEARCH_NO_CHECK_PERM 0x0008 /* Don't check permissions */
#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010 /* Give an error on excessive depth */
#define KEYRING_SEARCH_SKIP_EXPIRED 0x0020 /* Ignore expired keys (intention to replace) */
+#define KEYRING_SEARCH_RECURSE 0x0040 /* Search child keyrings also */
int (*iterator)(const void *object, void *iterator_data);
@@ -135,21 +136,23 @@
extern bool key_default_cmp(const struct key *key,
const struct key_match_data *match_data);
-extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
+extern key_ref_t keyring_search_rcu(key_ref_t keyring_ref,
struct keyring_search_context *ctx);
-extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
-extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);
+extern key_ref_t search_cred_keyrings_rcu(struct keyring_search_context *ctx);
+extern key_ref_t search_process_keyrings_rcu(struct keyring_search_context *ctx);
extern struct key *find_keyring_by_name(const char *name, bool uid_keyring);
-extern int install_user_keyrings(void);
+extern int look_up_user_keyrings(struct key **, struct key **);
+extern struct key *get_user_session_keyring_rcu(const struct cred *);
extern int install_thread_keyring_to_cred(struct cred *);
extern int install_process_keyring_to_cred(struct cred *);
extern int install_session_keyring_to_cred(struct cred *, struct key *);
extern struct key *request_key_and_link(struct key_type *type,
const char *description,
+ struct key_tag *domain_tag,
const void *callout_info,
size_t callout_len,
void *aux,
@@ -158,8 +161,6 @@
extern bool lookup_user_key_possessed(const struct key *key,
const struct key_match_data *match_data);
-extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
- key_perm_t perm);
#define KEY_LOOKUP_CREATE 0x01
#define KEY_LOOKUP_PARTIAL 0x02
#define KEY_LOOKUP_FOR_UNLINK 0x04
@@ -188,20 +189,9 @@
return key_task_permission(key_ref, current_cred(), perm);
}
-/*
- * Authorisation record for request_key().
- */
-struct request_key_auth {
- struct key *target_key;
- struct key *dest_keyring;
- const struct cred *cred;
- void *callout_info;
- size_t callout_len;
- pid_t pid;
-} __randomize_layout;
-
extern struct key_type key_type_request_key_auth;
extern struct key *request_key_auth_new(struct key *target,
+ const char *op,
const void *callout_info,
size_t callout_len,
struct key *dest_keyring);
@@ -216,7 +206,8 @@
return
key->flags & ((1 << KEY_FLAG_DEAD) |
(1 << KEY_FLAG_INVALIDATED)) ||
- (key->expiry > 0 && key->expiry <= limit);
+ (key->expiry > 0 && key->expiry <= limit) ||
+ key->domain_tag->removed;
}
/*
@@ -228,6 +219,7 @@
extern long keyctl_revoke_key(key_serial_t);
extern long keyctl_keyring_clear(key_serial_t);
extern long keyctl_keyring_link(key_serial_t, key_serial_t);
+extern long keyctl_keyring_move(key_serial_t, key_serial_t, key_serial_t, unsigned int);
extern long keyctl_keyring_unlink(key_serial_t, key_serial_t);
extern long keyctl_describe_key(key_serial_t, char __user *, size_t);
extern long keyctl_keyring_search(key_serial_t, const char __user *,
@@ -298,6 +290,47 @@
#endif
#endif
+#ifdef CONFIG_ASYMMETRIC_KEY_TYPE
+extern long keyctl_pkey_query(key_serial_t,
+ const char __user *,
+ struct keyctl_pkey_query __user *);
+
+extern long keyctl_pkey_verify(const struct keyctl_pkey_params __user *,
+ const char __user *,
+ const void __user *, const void __user *);
+
+extern long keyctl_pkey_e_d_s(int,
+ const struct keyctl_pkey_params __user *,
+ const char __user *,
+ const void __user *, void __user *);
+#else
+static inline long keyctl_pkey_query(key_serial_t id,
+ const char __user *_info,
+ struct keyctl_pkey_query __user *_res)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline long keyctl_pkey_verify(const struct keyctl_pkey_params __user *params,
+ const char __user *_info,
+ const void __user *_in,
+ const void __user *_in2)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline long keyctl_pkey_e_d_s(int op,
+ const struct keyctl_pkey_params __user *params,
+ const char __user *_info,
+ const void __user *_in,
+ void __user *_out)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+extern long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen);
+
/*
* Debugging key validation
*/
diff --git a/security/keys/key.c b/security/keys/key.c
index d97c939..764f4c5 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -1,15 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Basic authentication token and access key management
*
* Copyright (C) 2004-2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * 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; either version
- * 2 of the License, or (at your option) any later version.
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/poison.h>
#include <linux/sched.h>
@@ -265,8 +261,8 @@
spin_lock(&user->lock);
if (!(flags & KEY_ALLOC_QUOTA_OVERRUN)) {
- if (user->qnkeys + 1 >= maxkeys ||
- user->qnbytes + quotalen >= maxbytes ||
+ if (user->qnkeys + 1 > maxkeys ||
+ user->qnbytes + quotalen > maxbytes ||
user->qnbytes + quotalen < user->qnbytes)
goto no_quota;
}
@@ -285,11 +281,12 @@
key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);
if (!key->index_key.description)
goto no_memory_3;
+ key->index_key.type = type;
+ key_set_index_key(&key->index_key);
refcount_set(&key->usage, 1);
init_rwsem(&key->sem);
lockdep_set_class(&key->sem, &type->lock_class);
- key->index_key.type = type;
key->user = user;
key->quotalen = quotalen;
key->datalen = type->def_datalen;
@@ -297,6 +294,7 @@
key->gid = gid;
key->perm = perm;
key->restrict_link = restrict_link;
+ key->last_used_at = ktime_get_real_seconds();
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
@@ -315,6 +313,7 @@
goto security_error;
/* publish the key by giving it a serial number */
+ refcount_inc(&key->domain_tag->usage);
atomic_inc(&user->nkeys);
key_alloc_serial(key);
@@ -458,7 +457,7 @@
/* disable the authorisation key */
if (authkey)
- key_revoke(authkey);
+ key_invalidate(authkey);
if (prep->expiry != TIME64_MAX) {
key->expiry = prep->expiry;
@@ -499,7 +498,7 @@
struct key *authkey)
{
struct key_preparsed_payload prep;
- struct assoc_array_edit *edit;
+ struct assoc_array_edit *edit = NULL;
int ret;
memset(&prep, 0, sizeof(prep));
@@ -514,10 +513,14 @@
}
if (keyring) {
- ret = __key_link_begin(keyring, &key->index_key, &edit);
+ ret = __key_link_lock(keyring, &key->index_key);
if (ret < 0)
goto error;
+ ret = __key_link_begin(keyring, &key->index_key, &edit);
+ if (ret < 0)
+ goto error_link_end;
+
if (keyring->restrict_link && keyring->restrict_link->check) {
struct key_restriction *keyres = keyring->restrict_link;
@@ -569,7 +572,7 @@
struct key *keyring,
struct key *authkey)
{
- struct assoc_array_edit *edit;
+ struct assoc_array_edit *edit = NULL;
int ret, awaken, link_ret = 0;
key_check(key);
@@ -582,7 +585,12 @@
if (keyring->restrict_link)
return -EPERM;
- link_ret = __key_link_begin(keyring, &key->index_key, &edit);
+ link_ret = __key_link_lock(keyring, &key->index_key);
+ if (link_ret == 0) {
+ link_ret = __key_link_begin(keyring, &key->index_key, &edit);
+ if (link_ret < 0)
+ __key_link_end(keyring, &key->index_key, edit);
+ }
}
mutex_lock(&key_construction_mutex);
@@ -606,7 +614,7 @@
/* disable the authorisation key */
if (authkey)
- key_revoke(authkey);
+ key_invalidate(authkey);
}
mutex_unlock(&key_construction_mutex);
@@ -809,7 +817,7 @@
.description = description,
};
struct key_preparsed_payload prep;
- struct assoc_array_edit *edit;
+ struct assoc_array_edit *edit = NULL;
const struct cred *cred = current_cred();
struct key *keyring, *key = NULL;
key_ref_t key_ref;
@@ -858,11 +866,18 @@
goto error_free_prep;
}
index_key.desc_len = strlen(index_key.description);
+ key_set_index_key(&index_key);
+
+ ret = __key_link_lock(keyring, &index_key);
+ if (ret < 0) {
+ key_ref = ERR_PTR(ret);
+ goto error_free_prep;
+ }
ret = __key_link_begin(keyring, &index_key, &edit);
if (ret < 0) {
key_ref = ERR_PTR(ret);
- goto error_free_prep;
+ goto error_link_end;
}
if (restrict_link && restrict_link->check) {
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 1ffe60b..9b898c9 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Userspace key control operations
*
* Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * 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; either version
- * 2 of the License, or (at your option) any later version.
*/
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/sched/task.h>
@@ -26,10 +21,25 @@
#include <linux/security.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
+#include <keys/request_key_auth-type.h>
#include "internal.h"
#define KEY_MAX_DESC_SIZE 4096
+static const unsigned char keyrings_capabilities[2] = {
+ [0] = (KEYCTL_CAPS0_CAPABILITIES |
+ (IS_ENABLED(CONFIG_PERSISTENT_KEYRINGS) ? KEYCTL_CAPS0_PERSISTENT_KEYRINGS : 0) |
+ (IS_ENABLED(CONFIG_KEY_DH_OPERATIONS) ? KEYCTL_CAPS0_DIFFIE_HELLMAN : 0) |
+ (IS_ENABLED(CONFIG_ASYMMETRIC_KEY_TYPE) ? KEYCTL_CAPS0_PUBLIC_KEY : 0) |
+ (IS_ENABLED(CONFIG_BIG_KEYS) ? KEYCTL_CAPS0_BIG_KEY : 0) |
+ KEYCTL_CAPS0_INVALIDATE |
+ KEYCTL_CAPS0_RESTRICT_KEYRING |
+ KEYCTL_CAPS0_MOVE
+ ),
+ [1] = (KEYCTL_CAPS1_NS_KEYRING_NAME |
+ KEYCTL_CAPS1_NS_KEY_TAG),
+};
+
static int key_get_type_from_user(char *type,
const char __user *_type,
unsigned len)
@@ -210,7 +220,7 @@
}
/* do the search */
- key = request_key_and_link(ktype, description, callout_info,
+ key = request_key_and_link(ktype, description, NULL, callout_info,
callout_len, NULL, key_ref_to_ptr(dest_ref),
KEY_ALLOC_IN_QUOTA);
if (IS_ERR(key)) {
@@ -573,6 +583,52 @@
}
/*
+ * Move a link to a key from one keyring to another, displacing any matching
+ * key from the destination keyring.
+ *
+ * The key must grant the caller Link permission and both keyrings must grant
+ * the caller Write permission. There must also be a link in the from keyring
+ * to the key. If both keyrings are the same, nothing is done.
+ *
+ * If successful, 0 will be returned.
+ */
+long keyctl_keyring_move(key_serial_t id, key_serial_t from_ringid,
+ key_serial_t to_ringid, unsigned int flags)
+{
+ key_ref_t key_ref, from_ref, to_ref;
+ long ret;
+
+ if (flags & ~KEYCTL_MOVE_EXCL)
+ return -EINVAL;
+
+ key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_LINK);
+ if (IS_ERR(key_ref))
+ return PTR_ERR(key_ref);
+
+ from_ref = lookup_user_key(from_ringid, 0, KEY_NEED_WRITE);
+ if (IS_ERR(from_ref)) {
+ ret = PTR_ERR(from_ref);
+ goto error2;
+ }
+
+ to_ref = lookup_user_key(to_ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
+ if (IS_ERR(to_ref)) {
+ ret = PTR_ERR(to_ref);
+ goto error3;
+ }
+
+ ret = key_move(key_ref_to_ptr(key_ref), key_ref_to_ptr(from_ref),
+ key_ref_to_ptr(to_ref), flags);
+
+ key_ref_put(to_ref);
+error3:
+ key_ref_put(from_ref);
+error2:
+ key_ref_put(key_ref);
+ return ret;
+}
+
+/*
* Return a description of a key to userspace.
*
* The key must grant the caller View permission for this to work.
@@ -704,7 +760,7 @@
}
/* do the search */
- key_ref = keyring_search(keyring_ref, ktype, description);
+ key_ref = keyring_search(keyring_ref, ktype, description, true);
if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref);
@@ -1524,7 +1580,8 @@
ret = -EPERM;
oldwork = NULL;
- parent = me->real_parent;
+ parent = rcu_dereference_protected(me->real_parent,
+ lockdep_is_held(&tasklist_lock));
/* the parent mustn't be init and mustn't be a kernel thread */
if (parent->pid <= 1 || !parent->mm)
@@ -1632,6 +1689,26 @@
}
/*
+ * Get keyrings subsystem capabilities.
+ */
+long keyctl_capabilities(unsigned char __user *_buffer, size_t buflen)
+{
+ size_t size = buflen;
+
+ if (size > 0) {
+ if (size > sizeof(keyrings_capabilities))
+ size = sizeof(keyrings_capabilities);
+ if (copy_to_user(_buffer, keyrings_capabilities, size) != 0)
+ return -EFAULT;
+ if (size < buflen &&
+ clear_user(_buffer + size, buflen - size) != 0)
+ return -EFAULT;
+ }
+
+ return sizeof(keyrings_capabilities);
+}
+
+/*
* The key control system call
*/
SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
@@ -1747,6 +1824,39 @@
(const char __user *) arg3,
(const char __user *) arg4);
+ case KEYCTL_PKEY_QUERY:
+ if (arg3 != 0)
+ return -EINVAL;
+ return keyctl_pkey_query((key_serial_t)arg2,
+ (const char __user *)arg4,
+ (struct keyctl_pkey_query __user *)arg5);
+
+ case KEYCTL_PKEY_ENCRYPT:
+ case KEYCTL_PKEY_DECRYPT:
+ case KEYCTL_PKEY_SIGN:
+ return keyctl_pkey_e_d_s(
+ option,
+ (const struct keyctl_pkey_params __user *)arg2,
+ (const char __user *)arg3,
+ (const void __user *)arg4,
+ (void __user *)arg5);
+
+ case KEYCTL_PKEY_VERIFY:
+ return keyctl_pkey_verify(
+ (const struct keyctl_pkey_params __user *)arg2,
+ (const char __user *)arg3,
+ (const void __user *)arg4,
+ (const void __user *)arg5);
+
+ case KEYCTL_MOVE:
+ return keyctl_keyring_move((key_serial_t)arg2,
+ (key_serial_t)arg3,
+ (key_serial_t)arg4,
+ (unsigned int)arg5);
+
+ case KEYCTL_CAPABILITIES:
+ return keyctl_capabilities((unsigned char __user *)arg2, (size_t)arg3);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/keyctl_pkey.c b/security/keys/keyctl_pkey.c
new file mode 100644
index 0000000..931d8df
--- /dev/null
+++ b/security/keys/keyctl_pkey.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Public-key operation keyctls
+ *
+ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <linux/parser.h>
+#include <linux/uaccess.h>
+#include <keys/user-type.h>
+#include "internal.h"
+
+static void keyctl_pkey_params_free(struct kernel_pkey_params *params)
+{
+ kfree(params->info);
+ key_put(params->key);
+}
+
+enum {
+ Opt_err,
+ Opt_enc, /* "enc=<encoding>" eg. "enc=oaep" */
+ Opt_hash, /* "hash=<digest-name>" eg. "hash=sha1" */
+};
+
+static const match_table_t param_keys = {
+ { Opt_enc, "enc=%s" },
+ { Opt_hash, "hash=%s" },
+ { Opt_err, NULL }
+};
+
+/*
+ * Parse the information string which consists of key=val pairs.
+ */
+static int keyctl_pkey_params_parse(struct kernel_pkey_params *params)
+{
+ unsigned long token_mask = 0;
+ substring_t args[MAX_OPT_ARGS];
+ char *c = params->info, *p, *q;
+ int token;
+
+ while ((p = strsep(&c, " \t"))) {
+ if (*p == '\0' || *p == ' ' || *p == '\t')
+ continue;
+ token = match_token(p, param_keys, args);
+ if (token == Opt_err)
+ return -EINVAL;
+ if (__test_and_set_bit(token, &token_mask))
+ return -EINVAL;
+ q = args[0].from;
+ if (!q[0])
+ return -EINVAL;
+
+ switch (token) {
+ case Opt_enc:
+ params->encoding = q;
+ break;
+
+ case Opt_hash:
+ params->hash_algo = q;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Interpret parameters. Callers must always call the free function
+ * on params, even if an error is returned.
+ */
+static int keyctl_pkey_params_get(key_serial_t id,
+ const char __user *_info,
+ struct kernel_pkey_params *params)
+{
+ key_ref_t key_ref;
+ void *p;
+ int ret;
+
+ memset(params, 0, sizeof(*params));
+ params->encoding = "raw";
+
+ p = strndup_user(_info, PAGE_SIZE);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+ params->info = p;
+
+ ret = keyctl_pkey_params_parse(params);
+ if (ret < 0)
+ return ret;
+
+ key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
+ if (IS_ERR(key_ref))
+ return PTR_ERR(key_ref);
+ params->key = key_ref_to_ptr(key_ref);
+
+ if (!params->key->type->asym_query)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+/*
+ * Get parameters from userspace. Callers must always call the free function
+ * on params, even if an error is returned.
+ */
+static int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_params,
+ const char __user *_info,
+ int op,
+ struct kernel_pkey_params *params)
+{
+ struct keyctl_pkey_params uparams;
+ struct kernel_pkey_query info;
+ int ret;
+
+ memset(params, 0, sizeof(*params));
+ params->encoding = "raw";
+
+ if (copy_from_user(&uparams, _params, sizeof(uparams)) != 0)
+ return -EFAULT;
+
+ ret = keyctl_pkey_params_get(uparams.key_id, _info, params);
+ if (ret < 0)
+ return ret;
+
+ ret = params->key->type->asym_query(params, &info);
+ if (ret < 0)
+ return ret;
+
+ switch (op) {
+ case KEYCTL_PKEY_ENCRYPT:
+ case KEYCTL_PKEY_DECRYPT:
+ if (uparams.in_len > info.max_enc_size ||
+ uparams.out_len > info.max_dec_size)
+ return -EINVAL;
+ break;
+ case KEYCTL_PKEY_SIGN:
+ case KEYCTL_PKEY_VERIFY:
+ if (uparams.in_len > info.max_sig_size ||
+ uparams.out_len > info.max_data_size)
+ return -EINVAL;
+ break;
+ default:
+ BUG();
+ }
+
+ params->in_len = uparams.in_len;
+ params->out_len = uparams.out_len;
+ return 0;
+}
+
+/*
+ * Query information about an asymmetric key.
+ */
+long keyctl_pkey_query(key_serial_t id,
+ const char __user *_info,
+ struct keyctl_pkey_query __user *_res)
+{
+ struct kernel_pkey_params params;
+ struct kernel_pkey_query res;
+ long ret;
+
+ memset(¶ms, 0, sizeof(params));
+
+ ret = keyctl_pkey_params_get(id, _info, ¶ms);
+ if (ret < 0)
+ goto error;
+
+ ret = params.key->type->asym_query(¶ms, &res);
+ if (ret < 0)
+ goto error;
+
+ ret = -EFAULT;
+ if (copy_to_user(_res, &res, sizeof(res)) == 0 &&
+ clear_user(_res->__spare, sizeof(_res->__spare)) == 0)
+ ret = 0;
+
+error:
+ keyctl_pkey_params_free(¶ms);
+ return ret;
+}
+
+/*
+ * Encrypt/decrypt/sign
+ *
+ * Encrypt data, decrypt data or sign data using a public key.
+ *
+ * _info is a string of supplementary information in key=val format. For
+ * instance, it might contain:
+ *
+ * "enc=pkcs1 hash=sha256"
+ *
+ * where enc= specifies the encoding and hash= selects the OID to go in that
+ * particular encoding if required. If enc= isn't supplied, it's assumed that
+ * the caller is supplying raw values.
+ *
+ * If successful, the amount of data written into the output buffer is
+ * returned.
+ */
+long keyctl_pkey_e_d_s(int op,
+ const struct keyctl_pkey_params __user *_params,
+ const char __user *_info,
+ const void __user *_in,
+ void __user *_out)
+{
+ struct kernel_pkey_params params;
+ void *in, *out;
+ long ret;
+
+ ret = keyctl_pkey_params_get_2(_params, _info, op, ¶ms);
+ if (ret < 0)
+ goto error_params;
+
+ ret = -EOPNOTSUPP;
+ if (!params.key->type->asym_eds_op)
+ goto error_params;
+
+ switch (op) {
+ case KEYCTL_PKEY_ENCRYPT:
+ params.op = kernel_pkey_encrypt;
+ break;
+ case KEYCTL_PKEY_DECRYPT:
+ params.op = kernel_pkey_decrypt;
+ break;
+ case KEYCTL_PKEY_SIGN:
+ params.op = kernel_pkey_sign;
+ break;
+ default:
+ BUG();
+ }
+
+ in = memdup_user(_in, params.in_len);
+ if (IS_ERR(in)) {
+ ret = PTR_ERR(in);
+ goto error_params;
+ }
+
+ ret = -ENOMEM;
+ out = kmalloc(params.out_len, GFP_KERNEL);
+ if (!out)
+ goto error_in;
+
+ ret = params.key->type->asym_eds_op(¶ms, in, out);
+ if (ret < 0)
+ goto error_out;
+
+ if (copy_to_user(_out, out, ret) != 0)
+ ret = -EFAULT;
+
+error_out:
+ kfree(out);
+error_in:
+ kfree(in);
+error_params:
+ keyctl_pkey_params_free(¶ms);
+ return ret;
+}
+
+/*
+ * Verify a signature.
+ *
+ * Verify a public key signature using the given key, or if not given, search
+ * for a matching key.
+ *
+ * _info is a string of supplementary information in key=val format. For
+ * instance, it might contain:
+ *
+ * "enc=pkcs1 hash=sha256"
+ *
+ * where enc= specifies the signature blob encoding and hash= selects the OID
+ * to go in that particular encoding. If enc= isn't supplied, it's assumed
+ * that the caller is supplying raw values.
+ *
+ * If successful, 0 is returned.
+ */
+long keyctl_pkey_verify(const struct keyctl_pkey_params __user *_params,
+ const char __user *_info,
+ const void __user *_in,
+ const void __user *_in2)
+{
+ struct kernel_pkey_params params;
+ void *in, *in2;
+ long ret;
+
+ ret = keyctl_pkey_params_get_2(_params, _info, KEYCTL_PKEY_VERIFY,
+ ¶ms);
+ if (ret < 0)
+ goto error_params;
+
+ ret = -EOPNOTSUPP;
+ if (!params.key->type->asym_verify_signature)
+ goto error_params;
+
+ in = memdup_user(_in, params.in_len);
+ if (IS_ERR(in)) {
+ ret = PTR_ERR(in);
+ goto error_params;
+ }
+
+ in2 = memdup_user(_in2, params.in2_len);
+ if (IS_ERR(in2)) {
+ ret = PTR_ERR(in2);
+ goto error_in;
+ }
+
+ params.op = kernel_pkey_verify;
+ ret = params.key->type->asym_verify_signature(¶ms, in, in2);
+
+ kfree(in2);
+error_in:
+ kfree(in);
+error_params:
+ keyctl_pkey_params_free(¶ms);
+ return ret;
+}
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 41bcf57..febf36c 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -1,25 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Keyring handling
*
* Copyright (C) 2004-2005, 2008, 2013 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * 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; either version
- * 2 of the License, or (at your option) any later version.
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/security.h>
#include <linux/seq_file.h>
#include <linux/err.h>
+#include <linux/user_namespace.h>
+#include <linux/nsproxy.h>
#include <keys/keyring-type.h>
#include <keys/user-type.h>
#include <linux/assoc_array_priv.h>
#include <linux/uaccess.h>
+#include <net/net_namespace.h>
#include "internal.h"
/*
@@ -29,11 +28,6 @@
#define KEYRING_SEARCH_MAX_DEPTH 6
/*
- * We keep all named keyrings in a hash to speed looking them up.
- */
-#define KEYRING_NAME_HASH_SIZE (1 << 5)
-
-/*
* We mark pointers we pass to the associative array with bit 1 set if
* they're keyrings and clear otherwise.
*/
@@ -55,17 +49,21 @@
return key;
}
-static struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE];
static DEFINE_RWLOCK(keyring_name_lock);
-static inline unsigned keyring_hash(const char *desc)
+/*
+ * Clean up the bits of user_namespace that belong to us.
+ */
+void key_free_user_ns(struct user_namespace *ns)
{
- unsigned bucket = 0;
+ write_lock(&keyring_name_lock);
+ list_del_init(&ns->keyring_name_list);
+ write_unlock(&keyring_name_lock);
- for (; *desc; desc++)
- bucket += (unsigned char)*desc;
-
- return bucket & (KEYRING_NAME_HASH_SIZE - 1);
+ key_put(ns->user_keyring_register);
+#ifdef CONFIG_PERSISTENT_KEYRINGS
+ key_put(ns->persistent_keyring_register);
+#endif
}
/*
@@ -100,27 +98,21 @@
* Semaphore to serialise link/link calls to prevent two link calls in parallel
* introducing a cycle.
*/
-static DECLARE_RWSEM(keyring_serialise_link_sem);
+static DEFINE_MUTEX(keyring_serialise_link_lock);
/*
* Publish the name of a keyring so that it can be found by name (if it has
- * one).
+ * one and it doesn't begin with a dot).
*/
static void keyring_publish_name(struct key *keyring)
{
- int bucket;
+ struct user_namespace *ns = current_user_ns();
- if (keyring->description) {
- bucket = keyring_hash(keyring->description);
-
+ if (keyring->description &&
+ keyring->description[0] &&
+ keyring->description[0] != '.') {
write_lock(&keyring_name_lock);
-
- if (!keyring_name_hash[bucket].next)
- INIT_LIST_HEAD(&keyring_name_hash[bucket]);
-
- list_add_tail(&keyring->name_link,
- &keyring_name_hash[bucket]);
-
+ list_add_tail(&keyring->name_link, &ns->keyring_name_list);
write_unlock(&keyring_name_lock);
}
}
@@ -168,7 +160,7 @@
/*
* Hash a key type and description.
*/
-static unsigned long hash_key_type_and_desc(const struct keyring_index_key *index_key)
+static void hash_key_type_and_desc(struct keyring_index_key *index_key)
{
const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP;
const unsigned long fan_mask = ASSOC_ARRAY_FAN_MASK;
@@ -179,9 +171,12 @@
int n, desc_len = index_key->desc_len;
type = (unsigned long)index_key->type;
-
acc = mult_64x32_and_fold(type, desc_len + 13);
acc = mult_64x32_and_fold(acc, 9207);
+ piece = (unsigned long)index_key->domain_tag;
+ acc = mult_64x32_and_fold(acc, piece);
+ acc = mult_64x32_and_fold(acc, 9207);
+
for (;;) {
n = desc_len;
if (n <= 0)
@@ -206,65 +201,101 @@
* zero for keyrings and non-zero otherwise.
*/
if (index_key->type != &key_type_keyring && (hash & fan_mask) == 0)
- return hash | (hash >> (ASSOC_ARRAY_KEY_CHUNK_SIZE - level_shift)) | 1;
- if (index_key->type == &key_type_keyring && (hash & fan_mask) != 0)
- return (hash + (hash << level_shift)) & ~fan_mask;
- return hash;
+ hash |= (hash >> (ASSOC_ARRAY_KEY_CHUNK_SIZE - level_shift)) | 1;
+ else if (index_key->type == &key_type_keyring && (hash & fan_mask) != 0)
+ hash = (hash + (hash << level_shift)) & ~fan_mask;
+ index_key->hash = hash;
+}
+
+/*
+ * Finalise an index key to include a part of the description actually in the
+ * index key, to set the domain tag and to calculate the hash.
+ */
+void key_set_index_key(struct keyring_index_key *index_key)
+{
+ static struct key_tag default_domain_tag = { .usage = REFCOUNT_INIT(1), };
+ size_t n = min_t(size_t, index_key->desc_len, sizeof(index_key->desc));
+
+ memcpy(index_key->desc, index_key->description, n);
+
+ if (!index_key->domain_tag) {
+ if (index_key->type->flags & KEY_TYPE_NET_DOMAIN)
+ index_key->domain_tag = current->nsproxy->net_ns->key_domain;
+ else
+ index_key->domain_tag = &default_domain_tag;
+ }
+
+ hash_key_type_and_desc(index_key);
+}
+
+/**
+ * key_put_tag - Release a ref on a tag.
+ * @tag: The tag to release.
+ *
+ * This releases a reference the given tag and returns true if that ref was the
+ * last one.
+ */
+bool key_put_tag(struct key_tag *tag)
+{
+ if (refcount_dec_and_test(&tag->usage)) {
+ kfree_rcu(tag, rcu);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * key_remove_domain - Kill off a key domain and gc its keys
+ * @domain_tag: The domain tag to release.
+ *
+ * This marks a domain tag as being dead and releases a ref on it. If that
+ * wasn't the last reference, the garbage collector is poked to try and delete
+ * all keys that were in the domain.
+ */
+void key_remove_domain(struct key_tag *domain_tag)
+{
+ domain_tag->removed = true;
+ if (!key_put_tag(domain_tag))
+ key_schedule_gc_links();
}
/*
* Build the next index key chunk.
*
- * On 32-bit systems the index key is laid out as:
- *
- * 0 4 5 9...
- * hash desclen typeptr desc[]
- *
- * On 64-bit systems:
- *
- * 0 8 9 17...
- * hash desclen typeptr desc[]
- *
* We return it one word-sized chunk at a time.
*/
static unsigned long keyring_get_key_chunk(const void *data, int level)
{
const struct keyring_index_key *index_key = data;
unsigned long chunk = 0;
- long offset = 0;
+ const u8 *d;
int desc_len = index_key->desc_len, n = sizeof(chunk);
level /= ASSOC_ARRAY_KEY_CHUNK_SIZE;
switch (level) {
case 0:
- return hash_key_type_and_desc(index_key);
+ return index_key->hash;
case 1:
- return ((unsigned long)index_key->type << 8) | desc_len;
+ return index_key->x;
case 2:
- if (desc_len == 0)
- return (u8)((unsigned long)index_key->type >>
- (ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
- n--;
- offset = 1;
+ return (unsigned long)index_key->type;
+ case 3:
+ return (unsigned long)index_key->domain_tag;
default:
- offset += sizeof(chunk) - 1;
- offset += (level - 3) * sizeof(chunk);
- if (offset >= desc_len)
+ level -= 4;
+ if (desc_len <= sizeof(index_key->desc))
return 0;
- desc_len -= offset;
+
+ d = index_key->description + sizeof(index_key->desc);
+ d += level * sizeof(long);
+ desc_len -= sizeof(index_key->desc);
if (desc_len > n)
desc_len = n;
- offset += desc_len;
do {
chunk <<= 8;
- chunk |= ((u8*)index_key->description)[--offset];
+ chunk |= *d++;
} while (--desc_len > 0);
-
- if (level == 2) {
- chunk <<= 8;
- chunk |= (u8)((unsigned long)index_key->type >>
- (ASSOC_ARRAY_KEY_CHUNK_SIZE - 8));
- }
return chunk;
}
}
@@ -281,6 +312,7 @@
const struct key *key = keyring_ptr_to_key(object);
return key->index_key.type == index_key->type &&
+ key->index_key.domain_tag == index_key->domain_tag &&
key->index_key.desc_len == index_key->desc_len &&
memcmp(key->index_key.description, index_key->description,
index_key->desc_len) == 0;
@@ -299,43 +331,38 @@
int level, i;
level = 0;
- seg_a = hash_key_type_and_desc(a);
- seg_b = hash_key_type_and_desc(b);
+ seg_a = a->hash;
+ seg_b = b->hash;
if ((seg_a ^ seg_b) != 0)
goto differ;
+ level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8;
/* The number of bits contributed by the hash is controlled by a
* constant in the assoc_array headers. Everything else thereafter we
* can deal with as being machine word-size dependent.
*/
- level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8;
- seg_a = a->desc_len;
- seg_b = b->desc_len;
+ seg_a = a->x;
+ seg_b = b->x;
if ((seg_a ^ seg_b) != 0)
goto differ;
+ level += sizeof(unsigned long);
/* The next bit may not work on big endian */
- level++;
seg_a = (unsigned long)a->type;
seg_b = (unsigned long)b->type;
if ((seg_a ^ seg_b) != 0)
goto differ;
-
level += sizeof(unsigned long);
- if (a->desc_len == 0)
- goto same;
- i = 0;
- if (((unsigned long)a->description | (unsigned long)b->description) &
- (sizeof(unsigned long) - 1)) {
- do {
- seg_a = *(unsigned long *)(a->description + i);
- seg_b = *(unsigned long *)(b->description + i);
- if ((seg_a ^ seg_b) != 0)
- goto differ_plus_i;
- i += sizeof(unsigned long);
- } while (i < (a->desc_len & (sizeof(unsigned long) - 1)));
- }
+ seg_a = (unsigned long)a->domain_tag;
+ seg_b = (unsigned long)b->domain_tag;
+ if ((seg_a ^ seg_b) != 0)
+ goto differ;
+ level += sizeof(unsigned long);
+
+ i = sizeof(a->desc);
+ if (a->desc_len <= i)
+ goto same;
for (; i < a->desc_len; i++) {
seg_a = *(unsigned char *)(a->description + i);
@@ -519,7 +546,7 @@
* @keyring: The keyring being added to.
* @type: The type of key being added.
* @payload: The payload of the key intended to be added.
- * @data: Additional data for evaluating restriction.
+ * @restriction_key: Keys providing additional data for evaluating restriction.
*
* Reject the addition of any links to a keyring. It can be overridden by
* passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when
@@ -662,7 +689,7 @@
(ctx->flags & STATE_CHECKS) == STATE_CHECKS);
if (ctx->index_key.description)
- ctx->index_key.desc_len = strlen(ctx->index_key.description);
+ key_set_index_key(&ctx->index_key);
/* Check to see if this top-level keyring is what we are looking for
* and whether it is valid or not.
@@ -703,6 +730,9 @@
* Non-keyrings avoid the leftmost branch of the root entirely (root
* slots 1-15).
*/
+ if (!(ctx->flags & KEYRING_SEARCH_RECURSE))
+ goto not_this_keyring;
+
ptr = READ_ONCE(keyring->keys.root);
if (!ptr)
goto not_this_keyring;
@@ -837,7 +867,7 @@
}
/**
- * keyring_search_aux - Search a keyring tree for a key matching some criteria
+ * keyring_search_rcu - Search a keyring tree for a matching key under RCU
* @keyring_ref: A pointer to the keyring with possession indicator.
* @ctx: The keyring search context.
*
@@ -849,7 +879,9 @@
* addition, the LSM gets to forbid keyring searches and key matches.
*
* The search is performed as a breadth-then-depth search up to the prescribed
- * limit (KEYRING_SEARCH_MAX_DEPTH).
+ * limit (KEYRING_SEARCH_MAX_DEPTH). The caller must hold the RCU read lock to
+ * prevent keyrings from being destroyed or rearranged whilst they are being
+ * searched.
*
* Keys are matched to the type provided and are then filtered by the match
* function, which is given the description to use in any way it sees fit. The
@@ -868,7 +900,7 @@
* In the case of a successful return, the possession attribute from
* @keyring_ref is propagated to the returned key reference.
*/
-key_ref_t keyring_search_aux(key_ref_t keyring_ref,
+key_ref_t keyring_search_rcu(key_ref_t keyring_ref,
struct keyring_search_context *ctx)
{
struct key *keyring;
@@ -890,11 +922,9 @@
return ERR_PTR(err);
}
- rcu_read_lock();
ctx->now = ktime_get_real_seconds();
if (search_nested_keyrings(keyring, ctx))
__key_get(key_ref_to_ptr(ctx->result));
- rcu_read_unlock();
return ctx->result;
}
@@ -903,17 +933,20 @@
* @keyring: The root of the keyring tree to be searched.
* @type: The type of keyring we want to find.
* @description: The name of the keyring we want to find.
+ * @recurse: True to search the children of @keyring also
*
- * As keyring_search_aux() above, but using the current task's credentials and
+ * As keyring_search_rcu() above, but using the current task's credentials and
* type's default matching function and preferred search method.
*/
key_ref_t keyring_search(key_ref_t keyring,
struct key_type *type,
- const char *description)
+ const char *description,
+ bool recurse)
{
struct keyring_search_context ctx = {
.index_key.type = type,
.index_key.description = description,
+ .index_key.desc_len = strlen(description),
.cred = current_cred(),
.match_data.cmp = key_default_cmp,
.match_data.raw_data = description,
@@ -923,13 +956,17 @@
key_ref_t key;
int ret;
+ if (recurse)
+ ctx.flags |= KEYRING_SEARCH_RECURSE;
if (type->match_preparse) {
ret = type->match_preparse(&ctx.match_data);
if (ret < 0)
return ERR_PTR(ret);
}
- key = keyring_search_aux(keyring, &ctx);
+ rcu_read_lock();
+ key = keyring_search_rcu(keyring, &ctx);
+ rcu_read_unlock();
if (type->match_free)
type->match_free(&ctx.match_data);
@@ -977,9 +1014,13 @@
/**
* keyring_restrict - Look up and apply a restriction to a keyring
- *
- * @keyring: The keyring to be restricted
+ * @keyring_ref: The keyring to be restricted
+ * @type: The key type that will provide the restriction checker.
* @restriction: The restriction options to apply to the keyring
+ *
+ * Look up a keyring and apply a restriction to it. The restriction is managed
+ * by the specific key type, but can be configured by the options specified in
+ * the restriction string.
*/
int keyring_restrict(key_ref_t keyring_ref, const char *type,
const char *restriction)
@@ -1101,50 +1142,44 @@
*/
struct key *find_keyring_by_name(const char *name, bool uid_keyring)
{
+ struct user_namespace *ns = current_user_ns();
struct key *keyring;
- int bucket;
if (!name)
return ERR_PTR(-EINVAL);
- bucket = keyring_hash(name);
-
read_lock(&keyring_name_lock);
- if (keyring_name_hash[bucket].next) {
- /* search this hash bucket for a keyring with a matching name
- * that's readable and that hasn't been revoked */
- list_for_each_entry(keyring,
- &keyring_name_hash[bucket],
- name_link
- ) {
- if (!kuid_has_mapping(current_user_ns(), keyring->user->uid))
- continue;
+ /* Search this hash bucket for a keyring with a matching name that
+ * grants Search permission and that hasn't been revoked
+ */
+ list_for_each_entry(keyring, &ns->keyring_name_list, name_link) {
+ if (!kuid_has_mapping(ns, keyring->user->uid))
+ continue;
- if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
- continue;
+ if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
+ continue;
- if (strcmp(keyring->description, name) != 0)
- continue;
+ if (strcmp(keyring->description, name) != 0)
+ continue;
- if (uid_keyring) {
- if (!test_bit(KEY_FLAG_UID_KEYRING,
- &keyring->flags))
- continue;
- } else {
- if (key_permission(make_key_ref(keyring, 0),
- KEY_NEED_SEARCH) < 0)
- continue;
- }
-
- /* we've got a match but we might end up racing with
- * key_cleanup() if the keyring is currently 'dead'
- * (ie. it has a zero usage count) */
- if (!refcount_inc_not_zero(&keyring->usage))
+ if (uid_keyring) {
+ if (!test_bit(KEY_FLAG_UID_KEYRING,
+ &keyring->flags))
continue;
- keyring->last_used_at = ktime_get_real_seconds();
- goto out;
+ } else {
+ if (key_permission(make_key_ref(keyring, 0),
+ KEY_NEED_SEARCH) < 0)
+ continue;
}
+
+ /* we've got a match but we might end up racing with
+ * key_cleanup() if the keyring is currently 'dead'
+ * (ie. it has a zero usage count) */
+ if (!refcount_inc_not_zero(&keyring->usage))
+ continue;
+ keyring->last_used_at = ktime_get_real_seconds();
+ goto out;
}
keyring = ERR_PTR(-ENOKEY);
@@ -1187,7 +1222,8 @@
.flags = (KEYRING_SEARCH_NO_STATE_CHECK |
KEYRING_SEARCH_NO_UPDATE_TIME |
KEYRING_SEARCH_NO_CHECK_PERM |
- KEYRING_SEARCH_DETECT_TOO_DEEP),
+ KEYRING_SEARCH_DETECT_TOO_DEEP |
+ KEYRING_SEARCH_RECURSE),
};
rcu_read_lock();
@@ -1197,13 +1233,67 @@
}
/*
+ * Lock keyring for link.
+ */
+int __key_link_lock(struct key *keyring,
+ const struct keyring_index_key *index_key)
+ __acquires(&keyring->sem)
+ __acquires(&keyring_serialise_link_lock)
+{
+ if (keyring->type != &key_type_keyring)
+ return -ENOTDIR;
+
+ down_write(&keyring->sem);
+
+ /* Serialise link/link calls to prevent parallel calls causing a cycle
+ * when linking two keyring in opposite orders.
+ */
+ if (index_key->type == &key_type_keyring)
+ mutex_lock(&keyring_serialise_link_lock);
+
+ return 0;
+}
+
+/*
+ * Lock keyrings for move (link/unlink combination).
+ */
+int __key_move_lock(struct key *l_keyring, struct key *u_keyring,
+ const struct keyring_index_key *index_key)
+ __acquires(&l_keyring->sem)
+ __acquires(&u_keyring->sem)
+ __acquires(&keyring_serialise_link_lock)
+{
+ if (l_keyring->type != &key_type_keyring ||
+ u_keyring->type != &key_type_keyring)
+ return -ENOTDIR;
+
+ /* We have to be very careful here to take the keyring locks in the
+ * right order, lest we open ourselves to deadlocking against another
+ * move operation.
+ */
+ if (l_keyring < u_keyring) {
+ down_write(&l_keyring->sem);
+ down_write_nested(&u_keyring->sem, 1);
+ } else {
+ down_write(&u_keyring->sem);
+ down_write_nested(&l_keyring->sem, 1);
+ }
+
+ /* Serialise link/link calls to prevent parallel calls causing a cycle
+ * when linking two keyring in opposite orders.
+ */
+ if (index_key->type == &key_type_keyring)
+ mutex_lock(&keyring_serialise_link_lock);
+
+ return 0;
+}
+
+/*
* Preallocate memory so that a key can be linked into to a keyring.
*/
int __key_link_begin(struct key *keyring,
const struct keyring_index_key *index_key,
struct assoc_array_edit **_edit)
- __acquires(&keyring->sem)
- __acquires(&keyring_serialise_link_sem)
{
struct assoc_array_edit *edit;
int ret;
@@ -1212,20 +1302,13 @@
keyring->serial, index_key->type->name, index_key->description);
BUG_ON(index_key->desc_len == 0);
+ BUG_ON(*_edit != NULL);
- if (keyring->type != &key_type_keyring)
- return -ENOTDIR;
-
- down_write(&keyring->sem);
+ *_edit = NULL;
ret = -EKEYREVOKED;
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
- goto error_krsem;
-
- /* serialise link/link calls to prevent parallel calls causing a cycle
- * when linking two keyring in opposite orders */
- if (index_key->type == &key_type_keyring)
- down_write(&keyring_serialise_link_sem);
+ goto error;
/* Create an edit script that will insert/replace the key in the
* keyring tree.
@@ -1236,7 +1319,7 @@
NULL);
if (IS_ERR(edit)) {
ret = PTR_ERR(edit);
- goto error_sem;
+ goto error;
}
/* If we're not replacing a link in-place then we're going to need some
@@ -1255,11 +1338,7 @@
error_cancel:
assoc_array_cancel_edit(edit);
-error_sem:
- if (index_key->type == &key_type_keyring)
- up_write(&keyring_serialise_link_sem);
-error_krsem:
- up_write(&keyring->sem);
+error:
kleave(" = %d", ret);
return ret;
}
@@ -1304,14 +1383,11 @@
const struct keyring_index_key *index_key,
struct assoc_array_edit *edit)
__releases(&keyring->sem)
- __releases(&keyring_serialise_link_sem)
+ __releases(&keyring_serialise_link_lock)
{
BUG_ON(index_key->type == NULL);
kenter("%d,%s,", keyring->serial, index_key->type->name);
- if (index_key->type == &key_type_keyring)
- up_write(&keyring_serialise_link_sem);
-
if (edit) {
if (!edit->dead_leaf) {
key_payload_reserve(keyring,
@@ -1320,6 +1396,9 @@
assoc_array_cancel_edit(edit);
}
up_write(&keyring->sem);
+
+ if (index_key->type == &key_type_keyring)
+ mutex_unlock(&keyring_serialise_link_lock);
}
/*
@@ -1355,7 +1434,7 @@
*/
int key_link(struct key *keyring, struct key *key)
{
- struct assoc_array_edit *edit;
+ struct assoc_array_edit *edit = NULL;
int ret;
kenter("{%d,%d}", keyring->serial, refcount_read(&keyring->usage));
@@ -1363,22 +1442,88 @@
key_check(keyring);
key_check(key);
- ret = __key_link_begin(keyring, &key->index_key, &edit);
- if (ret == 0) {
- kdebug("begun {%d,%d}", keyring->serial, refcount_read(&keyring->usage));
- ret = __key_link_check_restriction(keyring, key);
- if (ret == 0)
- ret = __key_link_check_live_key(keyring, key);
- if (ret == 0)
- __key_link(key, &edit);
- __key_link_end(keyring, &key->index_key, edit);
- }
+ ret = __key_link_lock(keyring, &key->index_key);
+ if (ret < 0)
+ goto error;
+ ret = __key_link_begin(keyring, &key->index_key, &edit);
+ if (ret < 0)
+ goto error_end;
+
+ kdebug("begun {%d,%d}", keyring->serial, refcount_read(&keyring->usage));
+ ret = __key_link_check_restriction(keyring, key);
+ if (ret == 0)
+ ret = __key_link_check_live_key(keyring, key);
+ if (ret == 0)
+ __key_link(key, &edit);
+
+error_end:
+ __key_link_end(keyring, &key->index_key, edit);
+error:
kleave(" = %d {%d,%d}", ret, keyring->serial, refcount_read(&keyring->usage));
return ret;
}
EXPORT_SYMBOL(key_link);
+/*
+ * Lock a keyring for unlink.
+ */
+static int __key_unlink_lock(struct key *keyring)
+ __acquires(&keyring->sem)
+{
+ if (keyring->type != &key_type_keyring)
+ return -ENOTDIR;
+
+ down_write(&keyring->sem);
+ return 0;
+}
+
+/*
+ * Begin the process of unlinking a key from a keyring.
+ */
+static int __key_unlink_begin(struct key *keyring, struct key *key,
+ struct assoc_array_edit **_edit)
+{
+ struct assoc_array_edit *edit;
+
+ BUG_ON(*_edit != NULL);
+
+ edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops,
+ &key->index_key);
+ if (IS_ERR(edit))
+ return PTR_ERR(edit);
+
+ if (!edit)
+ return -ENOENT;
+
+ *_edit = edit;
+ return 0;
+}
+
+/*
+ * Apply an unlink change.
+ */
+static void __key_unlink(struct key *keyring, struct key *key,
+ struct assoc_array_edit **_edit)
+{
+ assoc_array_apply_edit(*_edit);
+ *_edit = NULL;
+ key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES);
+}
+
+/*
+ * Finish unlinking a key from to a keyring.
+ */
+static void __key_unlink_end(struct key *keyring,
+ struct key *key,
+ struct assoc_array_edit *edit)
+ __releases(&keyring->sem)
+{
+ if (edit)
+ assoc_array_cancel_edit(edit);
+ up_write(&keyring->sem);
+}
+
/**
* key_unlink - Unlink the first link to a key from a keyring.
* @keyring: The keyring to remove the link from.
@@ -1398,38 +1543,99 @@
*/
int key_unlink(struct key *keyring, struct key *key)
{
- struct assoc_array_edit *edit;
+ struct assoc_array_edit *edit = NULL;
int ret;
key_check(keyring);
key_check(key);
- if (keyring->type != &key_type_keyring)
- return -ENOTDIR;
+ ret = __key_unlink_lock(keyring);
+ if (ret < 0)
+ return ret;
- down_write(&keyring->sem);
-
- edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops,
- &key->index_key);
- if (IS_ERR(edit)) {
- ret = PTR_ERR(edit);
- goto error;
- }
- ret = -ENOENT;
- if (edit == NULL)
- goto error;
-
- assoc_array_apply_edit(edit);
- key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES);
- ret = 0;
-
-error:
- up_write(&keyring->sem);
+ ret = __key_unlink_begin(keyring, key, &edit);
+ if (ret == 0)
+ __key_unlink(keyring, key, &edit);
+ __key_unlink_end(keyring, key, edit);
return ret;
}
EXPORT_SYMBOL(key_unlink);
/**
+ * key_move - Move a key from one keyring to another
+ * @key: The key to move
+ * @from_keyring: The keyring to remove the link from.
+ * @to_keyring: The keyring to make the link in.
+ * @flags: Qualifying flags, such as KEYCTL_MOVE_EXCL.
+ *
+ * Make a link in @to_keyring to a key, such that the keyring holds a reference
+ * on that key and the key can potentially be found by searching that keyring
+ * whilst simultaneously removing a link to the key from @from_keyring.
+ *
+ * This function will write-lock both keyring's semaphores and will consume
+ * some of the user's key data quota to hold the link on @to_keyring.
+ *
+ * Returns 0 if successful, -ENOTDIR if either keyring isn't a keyring,
+ * -EKEYREVOKED if either keyring has been revoked, -ENFILE if the second
+ * keyring is full, -EDQUOT if there is insufficient key data quota remaining
+ * to add another link or -ENOMEM if there's insufficient memory. If
+ * KEYCTL_MOVE_EXCL is set, then -EEXIST will be returned if there's already a
+ * matching key in @to_keyring.
+ *
+ * It is assumed that the caller has checked that it is permitted for a link to
+ * be made (the keyring should have Write permission and the key Link
+ * permission).
+ */
+int key_move(struct key *key,
+ struct key *from_keyring,
+ struct key *to_keyring,
+ unsigned int flags)
+{
+ struct assoc_array_edit *from_edit = NULL, *to_edit = NULL;
+ int ret;
+
+ kenter("%d,%d,%d", key->serial, from_keyring->serial, to_keyring->serial);
+
+ if (from_keyring == to_keyring)
+ return 0;
+
+ key_check(key);
+ key_check(from_keyring);
+ key_check(to_keyring);
+
+ ret = __key_move_lock(from_keyring, to_keyring, &key->index_key);
+ if (ret < 0)
+ goto out;
+ ret = __key_unlink_begin(from_keyring, key, &from_edit);
+ if (ret < 0)
+ goto error;
+ ret = __key_link_begin(to_keyring, &key->index_key, &to_edit);
+ if (ret < 0)
+ goto error;
+
+ ret = -EEXIST;
+ if (to_edit->dead_leaf && (flags & KEYCTL_MOVE_EXCL))
+ goto error;
+
+ ret = __key_link_check_restriction(to_keyring, key);
+ if (ret < 0)
+ goto error;
+ ret = __key_link_check_live_key(to_keyring, key);
+ if (ret < 0)
+ goto error;
+
+ __key_unlink(from_keyring, key, &from_edit);
+ __key_link(key, &to_edit);
+error:
+ __key_link_end(to_keyring, &key->index_key, to_edit);
+ __key_unlink_end(from_keyring, key, from_edit);
+out:
+ kleave(" = %d", ret);
+ return ret;
+}
+EXPORT_SYMBOL(key_move);
+
+/**
* keyring_clear - Clear a keyring
* @keyring: The keyring to clear.
*
diff --git a/security/keys/permission.c b/security/keys/permission.c
index f68dc04..085f907 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -1,15 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Key permission checking
*
* Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * 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; either version
- * 2 of the License, or (at your option) any later version.
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/security.h>
#include "internal.h"
diff --git a/security/keys/persistent.c b/security/keys/persistent.c
index d0cb5b3..97af230 100644
--- a/security/keys/persistent.c
+++ b/security/keys/persistent.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* General persistent per-UID keyrings register
*
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
*/
#include <linux/user_namespace.h>
@@ -84,15 +80,17 @@
long ret;
/* Look in the register if it exists */
+ memset(&index_key, 0, sizeof(index_key));
index_key.type = &key_type_keyring;
index_key.description = buf;
index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid));
+ key_set_index_key(&index_key);
if (ns->persistent_keyring_register) {
reg_ref = make_key_ref(ns->persistent_keyring_register, true);
- down_read(&ns->persistent_keyring_register_sem);
+ down_read(&ns->keyring_sem);
persistent_ref = find_key_to_update(reg_ref, &index_key);
- up_read(&ns->persistent_keyring_register_sem);
+ up_read(&ns->keyring_sem);
if (persistent_ref)
goto found;
@@ -101,9 +99,9 @@
/* It wasn't in the register, so we'll need to create it. We might
* also need to create the register.
*/
- down_write(&ns->persistent_keyring_register_sem);
+ down_write(&ns->keyring_sem);
persistent_ref = key_create_persistent(ns, uid, &index_key);
- up_write(&ns->persistent_keyring_register_sem);
+ up_write(&ns->keyring_sem);
if (!IS_ERR(persistent_ref))
goto found;
diff --git a/security/keys/proc.c b/security/keys/proc.c
index 5af2934..415f3f1 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* procfs files for key database enumeration
*
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * 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; either version
- * 2 of the License, or (at your option) any later version.
*/
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/fs.h>
@@ -166,13 +161,13 @@
int rc;
struct keyring_search_context ctx = {
- .index_key.type = key->type,
- .index_key.description = key->description,
+ .index_key = key->index_key,
.cred = m->file->f_cred,
.match_data.cmp = lookup_user_key_possessed,
.match_data.raw_data = key,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
- .flags = KEYRING_SEARCH_NO_STATE_CHECK,
+ .flags = (KEYRING_SEARCH_NO_STATE_CHECK |
+ KEYRING_SEARCH_RECURSE),
};
key_ref = make_key_ref(key, 0);
@@ -181,7 +176,9 @@
* skip if the key does not indicate the possessor can view it
*/
if (key->perm & KEY_POS_VIEW) {
- skey_ref = search_my_process_keyrings(&ctx);
+ rcu_read_lock();
+ skey_ref = search_cred_keyrings_rcu(&ctx);
+ rcu_read_unlock();
if (!IS_ERR(skey_ref)) {
key_ref_put(skey_ref);
key_ref = make_key_ref(key, 1);
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index d5b25e5..09541de 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Manage a process's keyrings
*
* Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * 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; either version
- * 2 of the License, or (at your option) any later version.
*/
-#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/sched/user.h>
@@ -20,14 +15,13 @@
#include <linux/security.h>
#include <linux/user_namespace.h>
#include <linux/uaccess.h>
+#include <linux/init_task.h>
+#include <keys/request_key_auth-type.h>
#include "internal.h"
/* Session keyring create vs join semaphore */
static DEFINE_MUTEX(key_session_mutex);
-/* User keyring creation semaphore */
-static DEFINE_MUTEX(key_user_keyring_mutex);
-
/* The root user's tracking struct */
struct key_user root_key_user = {
.usage = REFCOUNT_INIT(3),
@@ -39,97 +33,186 @@
};
/*
- * Install the user and user session keyrings for the current process's UID.
+ * Get or create a user register keyring.
*/
-int install_user_keyrings(void)
+static struct key *get_user_register(struct user_namespace *user_ns)
{
- struct user_struct *user;
- const struct cred *cred;
- struct key *uid_keyring, *session_keyring;
+ struct key *reg_keyring = READ_ONCE(user_ns->user_keyring_register);
+
+ if (reg_keyring)
+ return reg_keyring;
+
+ down_write(&user_ns->keyring_sem);
+
+ /* Make sure there's a register keyring. It gets owned by the
+ * user_namespace's owner.
+ */
+ reg_keyring = user_ns->user_keyring_register;
+ if (!reg_keyring) {
+ reg_keyring = keyring_alloc(".user_reg",
+ user_ns->owner, INVALID_GID,
+ &init_cred,
+ KEY_POS_WRITE | KEY_POS_SEARCH |
+ KEY_USR_VIEW | KEY_USR_READ,
+ 0,
+ NULL, NULL);
+ if (!IS_ERR(reg_keyring))
+ smp_store_release(&user_ns->user_keyring_register,
+ reg_keyring);
+ }
+
+ up_write(&user_ns->keyring_sem);
+
+ /* We don't return a ref since the keyring is pinned by the user_ns */
+ return reg_keyring;
+}
+
+/*
+ * Look up the user and user session keyrings for the current process's UID,
+ * creating them if they don't exist.
+ */
+int look_up_user_keyrings(struct key **_user_keyring,
+ struct key **_user_session_keyring)
+{
+ const struct cred *cred = current_cred();
+ struct user_namespace *user_ns = current_user_ns();
+ struct key *reg_keyring, *uid_keyring, *session_keyring;
key_perm_t user_keyring_perm;
+ key_ref_t uid_keyring_r, session_keyring_r;
+ uid_t uid = from_kuid(user_ns, cred->user->uid);
char buf[20];
int ret;
- uid_t uid;
user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL;
- cred = current_cred();
- user = cred->user;
- uid = from_kuid(cred->user_ns, user->uid);
- kenter("%p{%u}", user, uid);
+ kenter("%u", uid);
- if (user->uid_keyring && user->session_keyring) {
- kleave(" = 0 [exist]");
- return 0;
- }
+ reg_keyring = get_user_register(user_ns);
+ if (IS_ERR(reg_keyring))
+ return PTR_ERR(reg_keyring);
- mutex_lock(&key_user_keyring_mutex);
+ down_write(&user_ns->keyring_sem);
ret = 0;
- if (!user->uid_keyring) {
- /* get the UID-specific keyring
- * - there may be one in existence already as it may have been
- * pinned by a session, but the user_struct pointing to it
- * may have been destroyed by setuid */
- sprintf(buf, "_uid.%u", uid);
-
- uid_keyring = find_keyring_by_name(buf, true);
+ /* Get the user keyring. Note that there may be one in existence
+ * already as it may have been pinned by a session, but the user_struct
+ * pointing to it may have been destroyed by setuid.
+ */
+ snprintf(buf, sizeof(buf), "_uid.%u", uid);
+ uid_keyring_r = keyring_search(make_key_ref(reg_keyring, true),
+ &key_type_keyring, buf, false);
+ kdebug("_uid %p", uid_keyring_r);
+ if (uid_keyring_r == ERR_PTR(-EAGAIN)) {
+ uid_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID,
+ cred, user_keyring_perm,
+ KEY_ALLOC_UID_KEYRING |
+ KEY_ALLOC_IN_QUOTA,
+ NULL, reg_keyring);
if (IS_ERR(uid_keyring)) {
- uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
- cred, user_keyring_perm,
- KEY_ALLOC_UID_KEYRING |
- KEY_ALLOC_IN_QUOTA,
- NULL, NULL);
- if (IS_ERR(uid_keyring)) {
- ret = PTR_ERR(uid_keyring);
- goto error;
- }
+ ret = PTR_ERR(uid_keyring);
+ goto error;
}
-
- /* get a default session keyring (which might also exist
- * already) */
- sprintf(buf, "_uid_ses.%u", uid);
-
- session_keyring = find_keyring_by_name(buf, true);
- if (IS_ERR(session_keyring)) {
- session_keyring =
- keyring_alloc(buf, user->uid, INVALID_GID,
- cred, user_keyring_perm,
- KEY_ALLOC_UID_KEYRING |
- KEY_ALLOC_IN_QUOTA,
- NULL, NULL);
- if (IS_ERR(session_keyring)) {
- ret = PTR_ERR(session_keyring);
- goto error_release;
- }
-
- /* we install a link from the user session keyring to
- * the user keyring */
- ret = key_link(session_keyring, uid_keyring);
- if (ret < 0)
- goto error_release_both;
- }
-
- /* install the keyrings */
- user->uid_keyring = uid_keyring;
- user->session_keyring = session_keyring;
+ } else if (IS_ERR(uid_keyring_r)) {
+ ret = PTR_ERR(uid_keyring_r);
+ goto error;
+ } else {
+ uid_keyring = key_ref_to_ptr(uid_keyring_r);
}
- mutex_unlock(&key_user_keyring_mutex);
+ /* Get a default session keyring (which might also exist already) */
+ snprintf(buf, sizeof(buf), "_uid_ses.%u", uid);
+ session_keyring_r = keyring_search(make_key_ref(reg_keyring, true),
+ &key_type_keyring, buf, false);
+ kdebug("_uid_ses %p", session_keyring_r);
+ if (session_keyring_r == ERR_PTR(-EAGAIN)) {
+ session_keyring = keyring_alloc(buf, cred->user->uid, INVALID_GID,
+ cred, user_keyring_perm,
+ KEY_ALLOC_UID_KEYRING |
+ KEY_ALLOC_IN_QUOTA,
+ NULL, NULL);
+ if (IS_ERR(session_keyring)) {
+ ret = PTR_ERR(session_keyring);
+ goto error_release;
+ }
+
+ /* We install a link from the user session keyring to
+ * the user keyring.
+ */
+ ret = key_link(session_keyring, uid_keyring);
+ if (ret < 0)
+ goto error_release_session;
+
+ /* And only then link the user-session keyring to the
+ * register.
+ */
+ ret = key_link(reg_keyring, session_keyring);
+ if (ret < 0)
+ goto error_release_session;
+ } else if (IS_ERR(session_keyring_r)) {
+ ret = PTR_ERR(session_keyring_r);
+ goto error_release;
+ } else {
+ session_keyring = key_ref_to_ptr(session_keyring_r);
+ }
+
+ up_write(&user_ns->keyring_sem);
+
+ if (_user_session_keyring)
+ *_user_session_keyring = session_keyring;
+ else
+ key_put(session_keyring);
+ if (_user_keyring)
+ *_user_keyring = uid_keyring;
+ else
+ key_put(uid_keyring);
kleave(" = 0");
return 0;
-error_release_both:
+error_release_session:
key_put(session_keyring);
error_release:
key_put(uid_keyring);
error:
- mutex_unlock(&key_user_keyring_mutex);
+ up_write(&user_ns->keyring_sem);
kleave(" = %d", ret);
return ret;
}
/*
+ * Get the user session keyring if it exists, but don't create it if it
+ * doesn't.
+ */
+struct key *get_user_session_keyring_rcu(const struct cred *cred)
+{
+ struct key *reg_keyring = READ_ONCE(cred->user_ns->user_keyring_register);
+ key_ref_t session_keyring_r;
+ char buf[20];
+
+ struct keyring_search_context ctx = {
+ .index_key.type = &key_type_keyring,
+ .index_key.description = buf,
+ .cred = cred,
+ .match_data.cmp = key_default_cmp,
+ .match_data.raw_data = buf,
+ .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+ .flags = KEYRING_SEARCH_DO_STATE_CHECK,
+ };
+
+ if (!reg_keyring)
+ return NULL;
+
+ ctx.index_key.desc_len = snprintf(buf, sizeof(buf), "_uid_ses.%u",
+ from_kuid(cred->user_ns,
+ cred->user->uid));
+
+ session_keyring_r = keyring_search_rcu(make_key_ref(reg_keyring, true),
+ &ctx);
+ if (IS_ERR(session_keyring_r))
+ return NULL;
+ return key_ref_to_ptr(session_keyring_r);
+}
+
+/*
* Install a thread keyring to the given credentials struct if it didn't have
* one already. This is allowed to overrun the quota.
*
@@ -227,6 +310,7 @@
* Install the given keyring as the session keyring of the given credentials
* struct, replacing the existing one if any. If the given keyring is NULL,
* then install a new anonymous session keyring.
+ * @cred can not be in use by any task yet.
*
* Return: 0 on success; -errno on failure.
*/
@@ -254,7 +338,7 @@
/* install the keyring */
old = cred->session_keyring;
- rcu_assign_pointer(cred->session_keyring, keyring);
+ cred->session_keyring = keyring;
if (old)
key_put(old);
@@ -290,34 +374,33 @@
/*
* Handle the fsuid changing.
*/
-void key_fsuid_changed(struct task_struct *tsk)
+void key_fsuid_changed(struct cred *new_cred)
{
/* update the ownership of the thread keyring */
- BUG_ON(!tsk->cred);
- if (tsk->cred->thread_keyring) {
- down_write(&tsk->cred->thread_keyring->sem);
- tsk->cred->thread_keyring->uid = tsk->cred->fsuid;
- up_write(&tsk->cred->thread_keyring->sem);
+ if (new_cred->thread_keyring) {
+ down_write(&new_cred->thread_keyring->sem);
+ new_cred->thread_keyring->uid = new_cred->fsuid;
+ up_write(&new_cred->thread_keyring->sem);
}
}
/*
* Handle the fsgid changing.
*/
-void key_fsgid_changed(struct task_struct *tsk)
+void key_fsgid_changed(struct cred *new_cred)
{
/* update the ownership of the thread keyring */
- BUG_ON(!tsk->cred);
- if (tsk->cred->thread_keyring) {
- down_write(&tsk->cred->thread_keyring->sem);
- tsk->cred->thread_keyring->gid = tsk->cred->fsgid;
- up_write(&tsk->cred->thread_keyring->sem);
+ if (new_cred->thread_keyring) {
+ down_write(&new_cred->thread_keyring->sem);
+ new_cred->thread_keyring->gid = new_cred->fsgid;
+ up_write(&new_cred->thread_keyring->sem);
}
}
/*
* Search the process keyrings attached to the supplied cred for the first
- * matching key.
+ * matching key under RCU conditions (the caller must be holding the RCU read
+ * lock).
*
* The search criteria are the type and the match function. The description is
* given to the match function as a parameter, but doesn't otherwise influence
@@ -336,9 +419,11 @@
* In the case of a successful return, the possession attribute is set on the
* returned key reference.
*/
-key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
+key_ref_t search_cred_keyrings_rcu(struct keyring_search_context *ctx)
{
+ struct key *user_session;
key_ref_t key_ref, ret, err;
+ const struct cred *cred = ctx->cred;
/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
* searchable, but we failed to find a key or we found a negative key;
@@ -352,9 +437,9 @@
err = ERR_PTR(-EAGAIN);
/* search the thread keyring first */
- if (ctx->cred->thread_keyring) {
- key_ref = keyring_search_aux(
- make_key_ref(ctx->cred->thread_keyring, 1), ctx);
+ if (cred->thread_keyring) {
+ key_ref = keyring_search_rcu(
+ make_key_ref(cred->thread_keyring, 1), ctx);
if (!IS_ERR(key_ref))
goto found;
@@ -370,9 +455,9 @@
}
/* search the process keyring second */
- if (ctx->cred->process_keyring) {
- key_ref = keyring_search_aux(
- make_key_ref(ctx->cred->process_keyring, 1), ctx);
+ if (cred->process_keyring) {
+ key_ref = keyring_search_rcu(
+ make_key_ref(cred->process_keyring, 1), ctx);
if (!IS_ERR(key_ref))
goto found;
@@ -380,6 +465,7 @@
case -EAGAIN: /* no key */
if (ret)
break;
+ /* fall through */
case -ENOKEY: /* negative key */
ret = key_ref;
break;
@@ -390,12 +476,9 @@
}
/* search the session keyring */
- if (ctx->cred->session_keyring) {
- rcu_read_lock();
- key_ref = keyring_search_aux(
- make_key_ref(rcu_dereference(ctx->cred->session_keyring), 1),
- ctx);
- rcu_read_unlock();
+ if (cred->session_keyring) {
+ key_ref = keyring_search_rcu(
+ make_key_ref(cred->session_keyring, 1), ctx);
if (!IS_ERR(key_ref))
goto found;
@@ -404,6 +487,7 @@
case -EAGAIN: /* no key */
if (ret)
break;
+ /* fall through */
case -ENOKEY: /* negative key */
ret = key_ref;
break;
@@ -413,10 +497,11 @@
}
}
/* or search the user-session keyring */
- else if (ctx->cred->user->session_keyring) {
- key_ref = keyring_search_aux(
- make_key_ref(ctx->cred->user->session_keyring, 1),
- ctx);
+ else if ((user_session = get_user_session_keyring_rcu(cred))) {
+ key_ref = keyring_search_rcu(make_key_ref(user_session, 1),
+ ctx);
+ key_put(user_session);
+
if (!IS_ERR(key_ref))
goto found;
@@ -424,6 +509,7 @@
case -EAGAIN: /* no key */
if (ret)
break;
+ /* fall through */
case -ENOKEY: /* negative key */
ret = key_ref;
break;
@@ -446,16 +532,16 @@
* the keys attached to the assumed authorisation key using its credentials if
* one is available.
*
- * Return same as search_my_process_keyrings().
+ * The caller must be holding the RCU read lock.
+ *
+ * Return same as search_cred_keyrings_rcu().
*/
-key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
+key_ref_t search_process_keyrings_rcu(struct keyring_search_context *ctx)
{
struct request_key_auth *rka;
key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;
- might_sleep();
-
- key_ref = search_my_process_keyrings(ctx);
+ key_ref = search_cred_keyrings_rcu(ctx);
if (!IS_ERR(key_ref))
goto found;
err = key_ref;
@@ -470,24 +556,17 @@
) {
const struct cred *cred = ctx->cred;
- /* defend against the auth key being revoked */
- down_read(&cred->request_key_auth->sem);
-
- if (key_validate(ctx->cred->request_key_auth) == 0) {
+ if (key_validate(cred->request_key_auth) == 0) {
rka = ctx->cred->request_key_auth->payload.data[0];
+ //// was search_process_keyrings() [ie. recursive]
ctx->cred = rka->cred;
- key_ref = search_process_keyrings(ctx);
+ key_ref = search_cred_keyrings_rcu(ctx);
ctx->cred = cred;
- up_read(&cred->request_key_auth->sem);
-
if (!IS_ERR(key_ref))
goto found;
-
ret = key_ref;
- } else {
- up_read(&cred->request_key_auth->sem);
}
}
@@ -502,7 +581,6 @@
found:
return key_ref;
}
-
/*
* See if the key we're looking at is the target key.
*/
@@ -536,10 +614,11 @@
struct keyring_search_context ctx = {
.match_data.cmp = lookup_user_key_possessed,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
- .flags = KEYRING_SEARCH_NO_STATE_CHECK,
+ .flags = (KEYRING_SEARCH_NO_STATE_CHECK |
+ KEYRING_SEARCH_RECURSE),
};
struct request_key_auth *rka;
- struct key *key;
+ struct key *key, *user_session;
key_ref_t key_ref, skey_ref;
int ret;
@@ -588,20 +667,20 @@
if (!ctx.cred->session_keyring) {
/* always install a session keyring upon access if one
* doesn't exist yet */
- ret = install_user_keyrings();
+ ret = look_up_user_keyrings(NULL, &user_session);
if (ret < 0)
goto error;
if (lflags & KEY_LOOKUP_CREATE)
ret = join_session_keyring(NULL);
else
- ret = install_session_keyring(
- ctx.cred->user->session_keyring);
+ ret = install_session_keyring(user_session);
+ key_put(user_session);
if (ret < 0)
goto error;
goto reget_creds;
- } else if (ctx.cred->session_keyring ==
- ctx.cred->user->session_keyring &&
+ } else if (test_bit(KEY_FLAG_UID_KEYRING,
+ &ctx.cred->session_keyring->flags) &&
lflags & KEY_LOOKUP_CREATE) {
ret = join_session_keyring(NULL);
if (ret < 0)
@@ -609,34 +688,22 @@
goto reget_creds;
}
- rcu_read_lock();
- key = rcu_dereference(ctx.cred->session_keyring);
+ key = ctx.cred->session_keyring;
__key_get(key);
- rcu_read_unlock();
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_USER_KEYRING:
- if (!ctx.cred->user->uid_keyring) {
- ret = install_user_keyrings();
- if (ret < 0)
- goto error;
- }
-
- key = ctx.cred->user->uid_keyring;
- __key_get(key);
+ ret = look_up_user_keyrings(&key, NULL);
+ if (ret < 0)
+ goto error;
key_ref = make_key_ref(key, 1);
break;
case KEY_SPEC_USER_SESSION_KEYRING:
- if (!ctx.cred->user->session_keyring) {
- ret = install_user_keyrings();
- if (ret < 0)
- goto error;
- }
-
- key = ctx.cred->user->session_keyring;
- __key_get(key);
+ ret = look_up_user_keyrings(NULL, &key);
+ if (ret < 0)
+ goto error;
key_ref = make_key_ref(key, 1);
break;
@@ -688,12 +755,12 @@
key_ref = make_key_ref(key, 0);
/* check to see if we possess the key */
- ctx.index_key.type = key->type;
- ctx.index_key.description = key->description;
- ctx.index_key.desc_len = strlen(key->description);
+ ctx.index_key = key->index_key;
ctx.match_data.raw_data = key;
kdebug("check possessed");
- skey_ref = search_process_keyrings(&ctx);
+ rcu_read_lock();
+ skey_ref = search_process_keyrings_rcu(&ctx);
+ rcu_read_unlock();
kdebug("possessed=%p", skey_ref);
if (!IS_ERR(skey_ref)) {
@@ -755,6 +822,7 @@
put_cred(ctx.cred);
goto try_again;
}
+EXPORT_SYMBOL(lookup_user_key);
/*
* Join the named keyring as the session keyring if possible else attempt to
@@ -884,7 +952,7 @@
*/
static int __init init_root_keyring(void)
{
- return install_user_keyrings();
+ return look_up_user_keyrings(NULL, NULL);
}
late_initcall(init_root_keyring);
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 114f740..957b9e3 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -1,48 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Request a key from userspace
*
* Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
- * 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; either version
- * 2 of the License, or (at your option) any later version.
- *
* See Documentation/security/keys/request-key.rst
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/sched.h>
#include <linux/kmod.h>
#include <linux/err.h>
#include <linux/keyctl.h>
#include <linux/slab.h>
+#include <net/net_namespace.h>
#include "internal.h"
+#include <keys/request_key_auth-type.h>
#define key_negative_timeout 60 /* default timeout on a negative key's existence */
+static struct key *check_cached_key(struct keyring_search_context *ctx)
+{
+#ifdef CONFIG_KEYS_REQUEST_CACHE
+ struct key *key = current->cached_requested_key;
+
+ if (key &&
+ ctx->match_data.cmp(key, &ctx->match_data) &&
+ !(key->flags & ((1 << KEY_FLAG_INVALIDATED) |
+ (1 << KEY_FLAG_REVOKED))))
+ return key_get(key);
+#endif
+ return NULL;
+}
+
+static void cache_requested_key(struct key *key)
+{
+#ifdef CONFIG_KEYS_REQUEST_CACHE
+ struct task_struct *t = current;
+
+ key_put(t->cached_requested_key);
+ t->cached_requested_key = key_get(key);
+ set_tsk_thread_flag(t, TIF_NOTIFY_RESUME);
+#endif
+}
+
/**
* complete_request_key - Complete the construction of a key.
- * @cons: The key construction record.
+ * @authkey: The authorisation key.
* @error: The success or failute of the construction.
*
* Complete the attempt to construct a key. The key will be negated
* if an error is indicated. The authorisation key will be revoked
* unconditionally.
*/
-void complete_request_key(struct key_construction *cons, int error)
+void complete_request_key(struct key *authkey, int error)
{
- kenter("{%d,%d},%d", cons->key->serial, cons->authkey->serial, error);
+ struct request_key_auth *rka = get_request_key_auth(authkey);
+ struct key *key = rka->target_key;
+
+ kenter("%d{%d},%d", authkey->serial, key->serial, error);
if (error < 0)
- key_negate_and_link(cons->key, key_negative_timeout, NULL,
- cons->authkey);
+ key_negate_and_link(key, key_negative_timeout, NULL, authkey);
else
- key_revoke(cons->authkey);
-
- key_put(cons->key);
- key_put(cons->authkey);
- kfree(cons);
+ key_revoke(authkey);
}
EXPORT_SYMBOL(complete_request_key);
@@ -91,25 +112,23 @@
* Request userspace finish the construction of a key
* - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"
*/
-static int call_sbin_request_key(struct key_construction *cons,
- const char *op,
- void *aux)
+static int call_sbin_request_key(struct key *authkey, void *aux)
{
static char const request_key[] = "/sbin/request-key";
+ struct request_key_auth *rka = get_request_key_auth(authkey);
const struct cred *cred = current_cred();
key_serial_t prkey, sskey;
- struct key *key = cons->key, *authkey = cons->authkey, *keyring,
- *session;
+ struct key *key = rka->target_key, *keyring, *session, *user_session;
char *argv[9], *envp[3], uid_str[12], gid_str[12];
char key_str[12], keyring_str[3][12];
char desc[20];
int ret, i;
- kenter("{%d},{%d},%s", key->serial, authkey->serial, op);
+ kenter("{%d},{%d},%s", key->serial, authkey->serial, rka->op);
- ret = install_user_keyrings();
+ ret = look_up_user_keyrings(NULL, &user_session);
if (ret < 0)
- goto error_alloc;
+ goto error_us;
/* allocate a new session keyring */
sprintf(desc, "_req.%u", key->serial);
@@ -145,12 +164,10 @@
prkey = cred->process_keyring->serial;
sprintf(keyring_str[1], "%d", prkey);
- rcu_read_lock();
- session = rcu_dereference(cred->session_keyring);
+ session = cred->session_keyring;
if (!session)
- session = cred->user->session_keyring;
+ session = user_session;
sskey = session->serial;
- rcu_read_unlock();
sprintf(keyring_str[2], "%d", sskey);
@@ -163,7 +180,7 @@
/* set up the argument list */
i = 0;
argv[i++] = (char *)request_key;
- argv[i++] = (char *) op;
+ argv[i++] = (char *)rka->op;
argv[i++] = key_str;
argv[i++] = uid_str;
argv[i++] = gid_str;
@@ -191,7 +208,9 @@
key_put(keyring);
error_alloc:
- complete_request_key(cons, ret);
+ key_put(user_session);
+error_us:
+ complete_request_key(authkey, ret);
kleave(" = %d", ret);
return ret;
}
@@ -205,42 +224,31 @@
size_t callout_len, void *aux,
struct key *dest_keyring)
{
- struct key_construction *cons;
request_key_actor_t actor;
struct key *authkey;
int ret;
kenter("%d,%p,%zu,%p", key->serial, callout_info, callout_len, aux);
- cons = kmalloc(sizeof(*cons), GFP_KERNEL);
- if (!cons)
- return -ENOMEM;
-
/* allocate an authorisation key */
- authkey = request_key_auth_new(key, callout_info, callout_len,
+ authkey = request_key_auth_new(key, "create", callout_info, callout_len,
dest_keyring);
- if (IS_ERR(authkey)) {
- kfree(cons);
- ret = PTR_ERR(authkey);
- authkey = NULL;
- } else {
- cons->authkey = key_get(authkey);
- cons->key = key_get(key);
+ if (IS_ERR(authkey))
+ return PTR_ERR(authkey);
- /* make the call */
- actor = call_sbin_request_key;
- if (key->type->request_key)
- actor = key->type->request_key;
+ /* Make the call */
+ actor = call_sbin_request_key;
+ if (key->type->request_key)
+ actor = key->type->request_key;
- ret = actor(cons, "create", aux);
+ ret = actor(authkey, aux);
- /* check that the actor called complete_request_key() prior to
- * returning an error */
- WARN_ON(ret < 0 &&
- !test_bit(KEY_FLAG_REVOKED, &authkey->flags));
- key_put(authkey);
- }
+ /* check that the actor called complete_request_key() prior to
+ * returning an error */
+ WARN_ON(ret < 0 &&
+ !test_bit(KEY_FLAG_INVALIDATED, &authkey->flags));
+ key_put(authkey);
kleave(" = %d", ret);
return ret;
}
@@ -275,7 +283,7 @@
if (cred->request_key_auth) {
authkey = cred->request_key_auth;
down_read(&authkey->sem);
- rka = authkey->payload.data[0];
+ rka = get_request_key_auth(authkey);
if (!test_bit(KEY_FLAG_REVOKED,
&authkey->flags))
dest_keyring =
@@ -287,32 +295,36 @@
}
}
+ /* fall through */
case KEY_REQKEY_DEFL_THREAD_KEYRING:
dest_keyring = key_get(cred->thread_keyring);
if (dest_keyring)
break;
+ /* fall through */
case KEY_REQKEY_DEFL_PROCESS_KEYRING:
dest_keyring = key_get(cred->process_keyring);
if (dest_keyring)
break;
+ /* fall through */
case KEY_REQKEY_DEFL_SESSION_KEYRING:
- rcu_read_lock();
- dest_keyring = key_get(
- rcu_dereference(cred->session_keyring));
- rcu_read_unlock();
+ dest_keyring = key_get(cred->session_keyring);
if (dest_keyring)
break;
+ /* fall through */
case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
- dest_keyring =
- key_get(cred->user->session_keyring);
+ ret = look_up_user_keyrings(NULL, &dest_keyring);
+ if (ret < 0)
+ return ret;
break;
case KEY_REQKEY_DEFL_USER_KEYRING:
- dest_keyring = key_get(cred->user->uid_keyring);
+ ret = look_up_user_keyrings(&dest_keyring, NULL);
+ if (ret < 0)
+ return ret;
break;
case KEY_REQKEY_DEFL_GROUP_KEYRING:
@@ -357,7 +369,7 @@
struct key_user *user,
struct key **_key)
{
- struct assoc_array_edit *edit;
+ struct assoc_array_edit *edit = NULL;
struct key *key;
key_perm_t perm;
key_ref_t key_ref;
@@ -386,6 +398,9 @@
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
if (dest_keyring) {
+ ret = __key_link_lock(dest_keyring, &ctx->index_key);
+ if (ret < 0)
+ goto link_lock_failed;
ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit);
if (ret < 0)
goto link_prealloc_failed;
@@ -396,7 +411,9 @@
* waited for locks */
mutex_lock(&key_construction_mutex);
- key_ref = search_process_keyrings(ctx);
+ rcu_read_lock();
+ key_ref = search_process_keyrings_rcu(ctx);
+ rcu_read_unlock();
if (!IS_ERR(key_ref))
goto key_already_present;
@@ -437,6 +454,8 @@
return ret;
link_prealloc_failed:
+ __key_link_end(dest_keyring, &ctx->index_key, edit);
+link_lock_failed:
mutex_unlock(&user->cons_lock);
key_put(key);
kleave(" = %d [prelink]", ret);
@@ -511,16 +530,18 @@
* request_key_and_link - Request a key and cache it in a keyring.
* @type: The type of key we want.
* @description: The searchable description of the key.
+ * @domain_tag: The domain in which the key operates.
* @callout_info: The data to pass to the instantiation upcall (or NULL).
* @callout_len: The length of callout_info.
* @aux: Auxiliary data for the upcall.
* @dest_keyring: Where to cache the key.
* @flags: Flags to key_alloc().
*
- * A key matching the specified criteria is searched for in the process's
- * keyrings and returned with its usage count incremented if found. Otherwise,
- * if callout_info is not NULL, a key will be allocated and some service
- * (probably in userspace) will be asked to instantiate it.
+ * A key matching the specified criteria (type, description, domain_tag) is
+ * searched for in the process's keyrings and returned with its usage count
+ * incremented if found. Otherwise, if callout_info is not NULL, a key will be
+ * allocated and some service (probably in userspace) will be asked to
+ * instantiate it.
*
* If successfully found or created, the key will be linked to the destination
* keyring if one is provided.
@@ -536,6 +557,7 @@
*/
struct key *request_key_and_link(struct key_type *type,
const char *description,
+ struct key_tag *domain_tag,
const void *callout_info,
size_t callout_len,
void *aux,
@@ -544,13 +566,16 @@
{
struct keyring_search_context ctx = {
.index_key.type = type,
+ .index_key.domain_tag = domain_tag,
.index_key.description = description,
+ .index_key.desc_len = strlen(description),
.cred = current_cred(),
.match_data.cmp = key_default_cmp,
.match_data.raw_data = description,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
.flags = (KEYRING_SEARCH_DO_STATE_CHECK |
- KEYRING_SEARCH_SKIP_EXPIRED),
+ KEYRING_SEARCH_SKIP_EXPIRED |
+ KEYRING_SEARCH_RECURSE),
};
struct key *key;
key_ref_t key_ref;
@@ -568,10 +593,26 @@
}
}
+ key = check_cached_key(&ctx);
+ if (key)
+ goto error_free;
+
/* search all the process keyrings for a key */
- key_ref = search_process_keyrings(&ctx);
+ rcu_read_lock();
+ key_ref = search_process_keyrings_rcu(&ctx);
+ rcu_read_unlock();
if (!IS_ERR(key_ref)) {
+ if (dest_keyring) {
+ ret = key_task_permission(key_ref, current_cred(),
+ KEY_NEED_LINK);
+ if (ret < 0) {
+ key_ref_put(key_ref);
+ key = ERR_PTR(ret);
+ goto error_free;
+ }
+ }
+
key = key_ref_to_ptr(key_ref);
if (dest_keyring) {
ret = key_link(dest_keyring, key);
@@ -581,6 +622,9 @@
goto error_free;
}
}
+
+ /* Only cache the key on immediate success */
+ cache_requested_key(key);
} else if (PTR_ERR(key_ref) != -EAGAIN) {
key = ERR_CAST(key_ref);
} else {
@@ -629,9 +673,10 @@
EXPORT_SYMBOL(wait_for_key_construction);
/**
- * request_key - Request a key and wait for construction
+ * request_key_tag - Request a key and wait for construction
* @type: Type of key.
* @description: The searchable description of the key.
+ * @domain_tag: The domain in which the key operates.
* @callout_info: The data to pass to the instantiation upcall (or NULL).
*
* As for request_key_and_link() except that it does not add the returned key
@@ -642,9 +687,10 @@
* Furthermore, it then works as wait_for_key_construction() to wait for the
* completion of keys undergoing construction with a non-interruptible wait.
*/
-struct key *request_key(struct key_type *type,
- const char *description,
- const char *callout_info)
+struct key *request_key_tag(struct key_type *type,
+ const char *description,
+ struct key_tag *domain_tag,
+ const char *callout_info)
{
struct key *key;
size_t callout_len = 0;
@@ -652,7 +698,8 @@
if (callout_info)
callout_len = strlen(callout_info);
- key = request_key_and_link(type, description, callout_info, callout_len,
+ key = request_key_and_link(type, description, domain_tag,
+ callout_info, callout_len,
NULL, NULL, KEY_ALLOC_IN_QUOTA);
if (!IS_ERR(key)) {
ret = wait_for_key_construction(key, false);
@@ -663,12 +710,13 @@
}
return key;
}
-EXPORT_SYMBOL(request_key);
+EXPORT_SYMBOL(request_key_tag);
/**
* request_key_with_auxdata - Request a key with auxiliary data for the upcaller
* @type: The type of key we want.
* @description: The searchable description of the key.
+ * @domain_tag: The domain in which the key operates.
* @callout_info: The data to pass to the instantiation upcall (or NULL).
* @callout_len: The length of callout_info.
* @aux: Auxiliary data for the upcall.
@@ -681,6 +729,7 @@
*/
struct key *request_key_with_auxdata(struct key_type *type,
const char *description,
+ struct key_tag *domain_tag,
const void *callout_info,
size_t callout_len,
void *aux)
@@ -688,7 +737,8 @@
struct key *key;
int ret;
- key = request_key_and_link(type, description, callout_info, callout_len,
+ key = request_key_and_link(type, description, domain_tag,
+ callout_info, callout_len,
aux, NULL, KEY_ALLOC_IN_QUOTA);
if (!IS_ERR(key)) {
ret = wait_for_key_construction(key, false);
@@ -701,52 +751,55 @@
}
EXPORT_SYMBOL(request_key_with_auxdata);
-/*
- * request_key_async - Request a key (allow async construction)
- * @type: Type of key.
- * @description: The searchable description of the key.
- * @callout_info: The data to pass to the instantiation upcall (or NULL).
- * @callout_len: The length of callout_info.
+/**
+ * request_key_rcu - Request key from RCU-read-locked context
+ * @type: The type of key we want.
+ * @description: The name of the key we want.
+ * @domain_tag: The domain in which the key operates.
*
- * As for request_key_and_link() except that it does not add the returned key
- * to a keyring if found, new keys are always allocated in the user's quota and
- * no auxiliary data can be passed.
+ * Request a key from a context that we may not sleep in (such as RCU-mode
+ * pathwalk). Keys under construction are ignored.
*
- * The caller should call wait_for_key_construction() to wait for the
- * completion of the returned key if it is still undergoing construction.
+ * Return a pointer to the found key if successful, -ENOKEY if we couldn't find
+ * a key or some other error if the key found was unsuitable or inaccessible.
*/
-struct key *request_key_async(struct key_type *type,
- const char *description,
- const void *callout_info,
- size_t callout_len)
+struct key *request_key_rcu(struct key_type *type,
+ const char *description,
+ struct key_tag *domain_tag)
{
- return request_key_and_link(type, description, callout_info,
- callout_len, NULL, NULL,
- KEY_ALLOC_IN_QUOTA);
-}
-EXPORT_SYMBOL(request_key_async);
+ struct keyring_search_context ctx = {
+ .index_key.type = type,
+ .index_key.domain_tag = domain_tag,
+ .index_key.description = description,
+ .index_key.desc_len = strlen(description),
+ .cred = current_cred(),
+ .match_data.cmp = key_default_cmp,
+ .match_data.raw_data = description,
+ .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+ .flags = (KEYRING_SEARCH_DO_STATE_CHECK |
+ KEYRING_SEARCH_SKIP_EXPIRED),
+ };
+ struct key *key;
+ key_ref_t key_ref;
-/*
- * request a key with auxiliary data for the upcaller (allow async construction)
- * @type: Type of key.
- * @description: The searchable description of the key.
- * @callout_info: The data to pass to the instantiation upcall (or NULL).
- * @callout_len: The length of callout_info.
- * @aux: Auxiliary data for the upcall.
- *
- * As for request_key_and_link() except that it does not add the returned key
- * to a keyring if found and new keys are always allocated in the user's quota.
- *
- * The caller should call wait_for_key_construction() to wait for the
- * completion of the returned key if it is still undergoing construction.
- */
-struct key *request_key_async_with_auxdata(struct key_type *type,
- const char *description,
- const void *callout_info,
- size_t callout_len,
- void *aux)
-{
- return request_key_and_link(type, description, callout_info,
- callout_len, aux, NULL, KEY_ALLOC_IN_QUOTA);
+ kenter("%s,%s", type->name, description);
+
+ key = check_cached_key(&ctx);
+ if (key)
+ return key;
+
+ /* search all the process keyrings for a key */
+ key_ref = search_process_keyrings_rcu(&ctx);
+ if (IS_ERR(key_ref)) {
+ key = ERR_CAST(key_ref);
+ if (PTR_ERR(key_ref) == -EAGAIN)
+ key = ERR_PTR(-ENOKEY);
+ } else {
+ key = key_ref_to_ptr(key_ref);
+ cache_requested_key(key);
+ }
+
+ kleave(" = %p", key);
+ return key;
}
-EXPORT_SYMBOL(request_key_async_with_auxdata);
+EXPORT_SYMBOL(request_key_rcu);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 424e1d9..ecba39c 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -1,24 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Request key authorisation token key definition.
*
* Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
- * 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; either version
- * 2 of the License, or (at your option) any later version.
- *
* See Documentation/security/keys/request-key.rst
*/
-#include <linux/module.h>
#include <linux/sched.h>
#include <linux/err.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include "internal.h"
-#include <keys/user-type.h>
+#include <keys/request_key_auth-type.h>
static int request_key_auth_preparse(struct key_preparsed_payload *);
static void request_key_auth_free_preparse(struct key_preparsed_payload *);
@@ -59,7 +54,7 @@
static int request_key_auth_instantiate(struct key *key,
struct key_preparsed_payload *prep)
{
- key->payload.data[0] = (struct request_key_auth *)prep->data;
+ rcu_assign_keypointer(key, (struct request_key_auth *)prep->data);
return 0;
}
@@ -69,7 +64,10 @@
static void request_key_auth_describe(const struct key *key,
struct seq_file *m)
{
- struct request_key_auth *rka = key->payload.data[0];
+ struct request_key_auth *rka = dereference_key_rcu(key);
+
+ if (!rka)
+ return;
seq_puts(m, "key:");
seq_puts(m, key->description);
@@ -84,10 +82,13 @@
static long request_key_auth_read(const struct key *key,
char __user *buffer, size_t buflen)
{
- struct request_key_auth *rka = key->payload.data[0];
+ struct request_key_auth *rka = dereference_key_locked(key);
size_t datalen;
long ret;
+ if (!rka)
+ return -EKEYREVOKED;
+
datalen = rka->callout_len;
ret = datalen;
@@ -103,23 +104,6 @@
return ret;
}
-/*
- * Handle revocation of an authorisation token key.
- *
- * Called with the key sem write-locked.
- */
-static void request_key_auth_revoke(struct key *key)
-{
- struct request_key_auth *rka = key->payload.data[0];
-
- kenter("{%d}", key->serial);
-
- if (rka->cred) {
- put_cred(rka->cred);
- rka->cred = NULL;
- }
-}
-
static void free_request_key_auth(struct request_key_auth *rka)
{
if (!rka)
@@ -133,26 +117,54 @@
}
/*
+ * Dispose of the request_key_auth record under RCU conditions
+ */
+static void request_key_auth_rcu_disposal(struct rcu_head *rcu)
+{
+ struct request_key_auth *rka =
+ container_of(rcu, struct request_key_auth, rcu);
+
+ free_request_key_auth(rka);
+}
+
+/*
+ * Handle revocation of an authorisation token key.
+ *
+ * Called with the key sem write-locked.
+ */
+static void request_key_auth_revoke(struct key *key)
+{
+ struct request_key_auth *rka = dereference_key_locked(key);
+
+ kenter("{%d}", key->serial);
+ rcu_assign_keypointer(key, NULL);
+ call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
+}
+
+/*
* Destroy an instantiation authorisation token key.
*/
static void request_key_auth_destroy(struct key *key)
{
- struct request_key_auth *rka = key->payload.data[0];
+ struct request_key_auth *rka = rcu_access_pointer(key->payload.rcu_data0);
kenter("{%d}", key->serial);
-
- free_request_key_auth(rka);
+ if (rka) {
+ rcu_assign_keypointer(key, NULL);
+ call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
+ }
}
/*
* Create an authorisation token for /sbin/request-key or whoever to gain
* access to the caller's security data.
*/
-struct key *request_key_auth_new(struct key *target, const void *callout_info,
- size_t callout_len, struct key *dest_keyring)
+struct key *request_key_auth_new(struct key *target, const char *op,
+ const void *callout_info, size_t callout_len,
+ struct key *dest_keyring)
{
struct request_key_auth *rka, *irka;
- const struct cred *cred = current->cred;
+ const struct cred *cred = current_cred();
struct key *authkey = NULL;
char desc[20];
int ret = -ENOMEM;
@@ -167,6 +179,7 @@
if (!rka->callout_info)
goto error_free_rka;
rka->callout_len = callout_len;
+ strlcpy(rka->op, op, sizeof(rka->op));
/* see if the calling process is already servicing the key request of
* another process */
@@ -203,7 +216,7 @@
authkey = key_alloc(&key_type_request_key_auth, desc,
cred->fsuid, cred->fsgid, cred,
- KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
+ KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_POS_LINK |
KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(authkey)) {
ret = PTR_ERR(authkey);
@@ -241,14 +254,17 @@
.match_data.cmp = key_default_cmp,
.match_data.raw_data = description,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
- .flags = KEYRING_SEARCH_DO_STATE_CHECK,
+ .flags = (KEYRING_SEARCH_DO_STATE_CHECK |
+ KEYRING_SEARCH_RECURSE),
};
struct key *authkey;
key_ref_t authkey_ref;
- sprintf(description, "%x", target_id);
+ ctx.index_key.desc_len = sprintf(description, "%x", target_id);
- authkey_ref = search_process_keyrings(&ctx);
+ rcu_read_lock();
+ authkey_ref = search_process_keyrings_rcu(&ctx);
+ rcu_read_unlock();
if (IS_ERR(authkey_ref)) {
authkey = ERR_CAST(authkey_ref);
diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c
index b68faa1..b46b651 100644
--- a/security/keys/sysctl.c
+++ b/security/keys/sysctl.c
@@ -1,20 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* Key management controls
*
* Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
*/
#include <linux/key.h>
#include <linux/sysctl.h>
#include "internal.h"
-static const int zero, one = 1, max = INT_MAX;
-
struct ctl_table key_sysctls[] = {
{
.procname = "maxkeys",
@@ -22,8 +16,8 @@
.maxlen = sizeof(unsigned),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = (void *) &one,
- .extra2 = (void *) &max,
+ .extra1 = (void *) SYSCTL_ONE,
+ .extra2 = (void *) SYSCTL_INT_MAX,
},
{
.procname = "maxbytes",
@@ -31,8 +25,8 @@
.maxlen = sizeof(unsigned),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = (void *) &one,
- .extra2 = (void *) &max,
+ .extra1 = (void *) SYSCTL_ONE,
+ .extra2 = (void *) SYSCTL_INT_MAX,
},
{
.procname = "root_maxkeys",
@@ -40,8 +34,8 @@
.maxlen = sizeof(unsigned),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = (void *) &one,
- .extra2 = (void *) &max,
+ .extra1 = (void *) SYSCTL_ONE,
+ .extra2 = (void *) SYSCTL_INT_MAX,
},
{
.procname = "root_maxbytes",
@@ -49,8 +43,8 @@
.maxlen = sizeof(unsigned),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = (void *) &one,
- .extra2 = (void *) &max,
+ .extra1 = (void *) SYSCTL_ONE,
+ .extra2 = (void *) SYSCTL_INT_MAX,
},
{
.procname = "gc_delay",
@@ -58,8 +52,8 @@
.maxlen = sizeof(unsigned),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = (void *) &zero,
- .extra2 = (void *) &max,
+ .extra1 = (void *) SYSCTL_ZERO,
+ .extra2 = (void *) SYSCTL_INT_MAX,
},
#ifdef CONFIG_PERSISTENT_KEYRINGS
{
@@ -68,8 +62,8 @@
.maxlen = sizeof(unsigned),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = (void *) &zero,
- .extra2 = (void *) &max,
+ .extra1 = (void *) SYSCTL_ZERO,
+ .extra2 = (void *) SYSCTL_INT_MAX,
},
#endif
{ }
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index b69d3b1..1fbd778 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2010 IBM Corporation
*
* Author:
* David Safford <safford@us.ibm.com>
*
- * 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 of the License.
- *
* See Documentation/security/keys/trusted-encrypted.rst
*/
@@ -30,10 +27,12 @@
#include <linux/tpm.h>
#include <linux/tpm_command.h>
-#include "trusted.h"
+#include <keys/trusted.h>
static const char hmac_alg[] = "hmac(sha1)";
static const char hash_alg[] = "sha1";
+static struct tpm_chip *chip;
+static struct tpm_digest *digests;
struct sdesc {
struct shash_desc shash;
@@ -53,7 +52,6 @@
if (!sdesc)
return ERR_PTR(-ENOMEM);
sdesc->shash.tfm = alg;
- sdesc->shash.flags = 0x0;
return sdesc;
}
@@ -121,9 +119,9 @@
/*
* calculate authorization info fields to send to TPM
*/
-static int TSS_authhmac(unsigned char *digest, const unsigned char *key,
+int TSS_authhmac(unsigned char *digest, const unsigned char *key,
unsigned int keylen, unsigned char *h1,
- unsigned char *h2, unsigned char h3, ...)
+ unsigned char *h2, unsigned int h3, ...)
{
unsigned char paramdigest[SHA1_DIGEST_SIZE];
struct sdesc *sdesc;
@@ -133,13 +131,16 @@
int ret;
va_list argp;
+ if (!chip)
+ return -ENODEV;
+
sdesc = init_sdesc(hashalg);
if (IS_ERR(sdesc)) {
pr_info("trusted_key: can't alloc %s\n", hash_alg);
return PTR_ERR(sdesc);
}
- c = h3;
+ c = !!h3;
ret = crypto_shash_init(&sdesc->shash);
if (ret < 0)
goto out;
@@ -168,11 +169,12 @@
kzfree(sdesc);
return ret;
}
+EXPORT_SYMBOL_GPL(TSS_authhmac);
/*
* verify the AUTH1_COMMAND (Seal) result from TPM
*/
-static int TSS_checkhmac1(unsigned char *buffer,
+int TSS_checkhmac1(unsigned char *buffer,
const uint32_t command,
const unsigned char *ononce,
const unsigned char *key,
@@ -193,6 +195,9 @@
va_list argp;
int ret;
+ if (!chip)
+ return -ENODEV;
+
bufsize = LOAD32(buffer, TPM_SIZE_OFFSET);
tag = LOAD16(buffer, 0);
ordinal = command;
@@ -249,6 +254,7 @@
kzfree(sdesc);
return ret;
}
+EXPORT_SYMBOL_GPL(TSS_checkhmac1);
/*
* verify the AUTH2_COMMAND (unseal) result from TPM
@@ -355,18 +361,22 @@
* For key specific tpm requests, we will generate and send our
* own TPM command packets using the drivers send function.
*/
-static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
+int trusted_tpm_send(unsigned char *cmd, size_t buflen)
{
int rc;
+ if (!chip)
+ return -ENODEV;
+
dump_tpm_buf(cmd);
- rc = tpm_send(NULL, cmd, buflen);
+ rc = tpm_send(chip, cmd, buflen);
dump_tpm_buf(cmd);
if (rc > 0)
/* Can't return positive return codes values to keyctl */
rc = -EPERM;
return rc;
}
+EXPORT_SYMBOL_GPL(trusted_tpm_send);
/*
* Lock a trusted key, by extending a selected PCR.
@@ -376,15 +386,10 @@
*/
static int pcrlock(const int pcrnum)
{
- unsigned char hash[SHA1_DIGEST_SIZE];
- int ret;
-
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- ret = tpm_get_random(NULL, hash, SHA1_DIGEST_SIZE);
- if (ret != SHA1_DIGEST_SIZE)
- return ret;
- return tpm_pcr_extend(NULL, pcrnum, hash) ? -EINVAL : 0;
+
+ return tpm_pcr_extend(chip, pcrnum, digests) ? -EINVAL : 0;
}
/*
@@ -397,7 +402,7 @@
unsigned char ononce[TPM_NONCE_SIZE];
int ret;
- ret = tpm_get_random(NULL, ononce, TPM_NONCE_SIZE);
+ ret = tpm_get_random(chip, ononce, TPM_NONCE_SIZE);
if (ret != TPM_NONCE_SIZE)
return ret;
@@ -425,10 +430,13 @@
/*
* Create an object independent authorisation protocol (oiap) session
*/
-static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
+int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
{
int ret;
+ if (!chip)
+ return -ENODEV;
+
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_COMMAND);
store32(tb, TPM_OIAP_SIZE);
@@ -442,6 +450,7 @@
TPM_NONCE_SIZE);
return 0;
}
+EXPORT_SYMBOL_GPL(oiap);
struct tpm_digests {
unsigned char encauth[SHA1_DIGEST_SIZE];
@@ -492,7 +501,7 @@
if (ret < 0)
goto out;
- ret = tpm_get_random(NULL, td->nonceodd, TPM_NONCE_SIZE);
+ ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE);
if (ret != TPM_NONCE_SIZE)
goto out;
ordinal = htonl(TPM_ORD_SEAL);
@@ -602,7 +611,7 @@
ordinal = htonl(TPM_ORD_UNSEAL);
keyhndl = htonl(SRKHANDLE);
- ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE);
+ ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE);
if (ret != TPM_NONCE_SIZE) {
pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
return ret;
@@ -707,7 +716,7 @@
}
enum {
- Opt_err = -1,
+ Opt_err,
Opt_new, Opt_load, Opt_update,
Opt_keyhandle, Opt_keyauth, Opt_blobauth,
Opt_pcrinfo, Opt_pcrlock, Opt_migratable,
@@ -747,7 +756,7 @@
int i;
int tpm2;
- tpm2 = tpm_is_tpm2(NULL);
+ tpm2 = tpm_is_tpm2(chip);
if (tpm2 < 0)
return tpm2;
@@ -916,7 +925,7 @@
struct trusted_key_options *options;
int tpm2;
- tpm2 = tpm_is_tpm2(NULL);
+ tpm2 = tpm_is_tpm2(chip);
if (tpm2 < 0)
return NULL;
@@ -966,7 +975,7 @@
size_t key_len;
int tpm2;
- tpm2 = tpm_is_tpm2(NULL);
+ tpm2 = tpm_is_tpm2(chip);
if (tpm2 < 0)
return tpm2;
@@ -1007,7 +1016,7 @@
switch (key_cmd) {
case Opt_load:
if (tpm2)
- ret = tpm_unseal_trusted(NULL, payload, options);
+ ret = tpm_unseal_trusted(chip, payload, options);
else
ret = key_unseal(payload, options);
dump_payload(payload);
@@ -1017,13 +1026,13 @@
break;
case Opt_new:
key_len = payload->key_len;
- ret = tpm_get_random(NULL, payload->key, key_len);
+ ret = tpm_get_random(chip, payload->key, key_len);
if (ret != key_len) {
pr_info("trusted_key: key_create failed (%d)\n", ret);
goto out;
}
if (tpm2)
- ret = tpm_seal_trusted(NULL, payload, options);
+ ret = tpm_seal_trusted(chip, payload, options);
else
ret = key_seal(payload, options);
if (ret < 0)
@@ -1195,14 +1204,14 @@
{
int ret;
- hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC);
+ hmacalg = crypto_alloc_shash(hmac_alg, 0, 0);
if (IS_ERR(hmacalg)) {
pr_info("trusted_key: could not allocate crypto %s\n",
hmac_alg);
return PTR_ERR(hmacalg);
}
- hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC);
+ hashalg = crypto_alloc_shash(hash_alg, 0, 0);
if (IS_ERR(hashalg)) {
pr_info("trusted_key: could not allocate crypto %s\n",
hash_alg);
@@ -1217,23 +1226,59 @@
return ret;
}
+static int __init init_digests(void)
+{
+ int i;
+
+ digests = kcalloc(chip->nr_allocated_banks, sizeof(*digests),
+ GFP_KERNEL);
+ if (!digests)
+ return -ENOMEM;
+
+ for (i = 0; i < chip->nr_allocated_banks; i++)
+ digests[i].alg_id = chip->allocated_banks[i].alg_id;
+
+ return 0;
+}
+
static int __init init_trusted(void)
{
int ret;
+ /* encrypted_keys.ko depends on successful load of this module even if
+ * TPM is not used.
+ */
+ chip = tpm_default_chip();
+ if (!chip)
+ return 0;
+
+ ret = init_digests();
+ if (ret < 0)
+ goto err_put;
ret = trusted_shash_alloc();
if (ret < 0)
- return ret;
+ goto err_free;
ret = register_key_type(&key_type_trusted);
if (ret < 0)
- trusted_shash_release();
+ goto err_release;
+ return 0;
+err_release:
+ trusted_shash_release();
+err_free:
+ kfree(digests);
+err_put:
+ put_device(&chip->dev);
return ret;
}
static void __exit cleanup_trusted(void)
{
- trusted_shash_release();
- unregister_key_type(&key_type_trusted);
+ if (chip) {
+ put_device(&chip->dev);
+ kfree(digests);
+ trusted_shash_release();
+ unregister_key_type(&key_type_trusted);
+ }
}
late_initcall(init_trusted);
diff --git a/security/keys/trusted.h b/security/keys/trusted.h
deleted file mode 100644
index 8d5fe9e..0000000
--- a/security/keys/trusted.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __TRUSTED_KEY_H
-#define __TRUSTED_KEY_H
-
-/* implementation specific TPM constants */
-#define MAX_BUF_SIZE 512
-#define TPM_GETRANDOM_SIZE 14
-#define TPM_OSAP_SIZE 36
-#define TPM_OIAP_SIZE 10
-#define TPM_SEAL_SIZE 87
-#define TPM_UNSEAL_SIZE 104
-#define TPM_SIZE_OFFSET 2
-#define TPM_RETURN_OFFSET 6
-#define TPM_DATA_OFFSET 10
-
-#define LOAD32(buffer, offset) (ntohl(*(uint32_t *)&buffer[offset]))
-#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset])
-#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset]))
-
-struct tpm_buf {
- int len;
- unsigned char data[MAX_BUF_SIZE];
-};
-
-#define INIT_BUF(tb) (tb->len = 0)
-
-struct osapsess {
- uint32_t handle;
- unsigned char secret[SHA1_DIGEST_SIZE];
- unsigned char enonce[TPM_NONCE_SIZE];
-};
-
-/* discrete values, but have to store in uint16_t for TPM use */
-enum {
- SEAL_keytype = 1,
- SRK_keytype = 4
-};
-
-#define TPM_DEBUG 0
-
-#if TPM_DEBUG
-static inline void dump_options(struct trusted_key_options *o)
-{
- pr_info("trusted_key: sealing key type %d\n", o->keytype);
- pr_info("trusted_key: sealing key handle %0X\n", o->keyhandle);
- pr_info("trusted_key: pcrlock %d\n", o->pcrlock);
- pr_info("trusted_key: pcrinfo %d\n", o->pcrinfo_len);
- print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE,
- 16, 1, o->pcrinfo, o->pcrinfo_len, 0);
-}
-
-static inline void dump_payload(struct trusted_key_payload *p)
-{
- pr_info("trusted_key: key_len %d\n", p->key_len);
- print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
- 16, 1, p->key, p->key_len, 0);
- pr_info("trusted_key: bloblen %d\n", p->blob_len);
- print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
- 16, 1, p->blob, p->blob_len, 0);
- pr_info("trusted_key: migratable %d\n", p->migratable);
-}
-
-static inline void dump_sess(struct osapsess *s)
-{
- print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE,
- 16, 1, &s->handle, 4, 0);
- pr_info("trusted-key: secret:\n");
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
- 16, 1, &s->secret, SHA1_DIGEST_SIZE, 0);
- pr_info("trusted-key: enonce:\n");
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
- 16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0);
-}
-
-static inline void dump_tpm_buf(unsigned char *buf)
-{
- int len;
-
- pr_info("\ntrusted-key: tpm buffer\n");
- len = LOAD32(buf, TPM_SIZE_OFFSET);
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
-}
-#else
-static inline void dump_options(struct trusted_key_options *o)
-{
-}
-
-static inline void dump_payload(struct trusted_key_payload *p)
-{
-}
-
-static inline void dump_sess(struct osapsess *s)
-{
-}
-
-static inline void dump_tpm_buf(unsigned char *buf)
-{
-}
-#endif
-
-static inline void store8(struct tpm_buf *buf, const unsigned char value)
-{
- buf->data[buf->len++] = value;
-}
-
-static inline void store16(struct tpm_buf *buf, const uint16_t value)
-{
- *(uint16_t *) & buf->data[buf->len] = htons(value);
- buf->len += sizeof value;
-}
-
-static inline void store32(struct tpm_buf *buf, const uint32_t value)
-{
- *(uint32_t *) & buf->data[buf->len] = htonl(value);
- buf->len += sizeof value;
-}
-
-static inline void storebytes(struct tpm_buf *buf, const unsigned char *in,
- const int len)
-{
- memcpy(buf->data + buf->len, in, len);
- buf->len += len;
-}
-#endif
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 9f558be..6f12de4 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -1,15 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/* user_defined.c: user defined key type
*
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
- *
- * 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; either version
- * 2 of the License, or (at your option) any later version.
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
diff --git a/security/loadpin/Kconfig b/security/loadpin/Kconfig
index dd01aa9..91be65d 100644
--- a/security/loadpin/Kconfig
+++ b/security/loadpin/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SECURITY_LOADPIN
bool "Pin load of kernel files (modules, fw, etc) to one filesystem"
depends on SECURITY && BLOCK
@@ -10,10 +11,10 @@
have a root filesystem backed by a read-only device such as
dm-verity or a CDROM.
-config SECURITY_LOADPIN_ENABLED
+config SECURITY_LOADPIN_ENFORCE
bool "Enforce LoadPin at boot"
depends on SECURITY_LOADPIN
help
If selected, LoadPin will enforce pinning at boot. If not
selected, it can be enabled at boot with the kernel parameter
- "loadpin.enabled=1".
+ "loadpin.enforce=1".
diff --git a/security/loadpin/Makefile b/security/loadpin/Makefile
index c2d77f8..0ead1c3 100644
--- a/security/loadpin/Makefile
+++ b/security/loadpin/Makefile
@@ -1 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SECURITY_LOADPIN) += loadpin.o
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index 0716af2..ee5cb94 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Module and Firmware Pinning Security Module
*
* Copyright 2011-2016 Google Inc.
*
* Author: Kees Cook <keescook@chromium.org>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#define pr_fmt(fmt) "LoadPin: " fmt
@@ -44,13 +36,13 @@
kfree(pathname);
}
-static int enabled = IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENABLED);
+static int enforce = IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENFORCE);
+static char *exclude_read_files[READING_MAX_ID];
+static int ignore_read_file_id[READING_MAX_ID] __ro_after_init;
static struct super_block *pinned_root;
static DEFINE_SPINLOCK(pinned_root_spinlock);
#ifdef CONFIG_SYSCTL
-static int zero;
-static int one = 1;
static struct ctl_path loadpin_sysctl_path[] = {
{ .procname = "kernel", },
@@ -60,13 +52,13 @@
static struct ctl_table loadpin_sysctl_table[] = {
{
- .procname = "enabled",
- .data = &enabled,
+ .procname = "enforce",
+ .data = &enforce,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = &zero,
- .extra2 = &one,
+ .extra1 = SYSCTL_ZERO,
+ .extra2 = SYSCTL_ONE,
},
{ }
};
@@ -84,8 +76,11 @@
* device, allow sysctl to change modes for testing.
*/
if (mnt_sb->s_bdev) {
+ char bdev[BDEVNAME_SIZE];
+
ro = bdev_read_only(mnt_sb->s_bdev);
- pr_info("dev(%u,%u): %s\n",
+ bdevname(mnt_sb->s_bdev, bdev);
+ pr_info("%s (%u:%u): %s\n", bdev,
MAJOR(mnt_sb->s_bdev->bd_dev),
MINOR(mnt_sb->s_bdev->bd_dev),
ro ? "read-only" : "writable");
@@ -97,7 +92,7 @@
loadpin_sysctl_table))
pr_notice("sysctl registration failed!\n");
else
- pr_info("load pinning can be disabled.\n");
+ pr_info("enforcement can be disabled.\n");
} else
pr_info("load pinning engaged.\n");
}
@@ -126,9 +121,16 @@
struct super_block *load_root;
const char *origin = kernel_read_file_id_str(id);
+ /* If the file id is excluded, ignore the pinning. */
+ if ((unsigned int)id < ARRAY_SIZE(ignore_read_file_id) &&
+ ignore_read_file_id[id]) {
+ report_load(origin, file, "pinning-excluded");
+ return 0;
+ }
+
/* This handles the older init_module API that has a NULL file. */
if (!file) {
- if (!enabled) {
+ if (!enforce) {
report_load(origin, NULL, "old-api-pinning-ignored");
return 0;
}
@@ -151,7 +153,7 @@
* Unlock now since it's only pinned_root we care about.
* In the worst case, we will (correctly) report pinning
* failures before we have announced that pinning is
- * enabled. This would be purely cosmetic.
+ * enforcing. This would be purely cosmetic.
*/
spin_unlock(&pinned_root_spinlock);
check_pinning_enforcement(pinned_root);
@@ -161,7 +163,7 @@
}
if (IS_ERR_OR_NULL(pinned_root) || load_root != pinned_root) {
- if (unlikely(!enabled)) {
+ if (unlikely(!enforce)) {
report_load(origin, file, "pinning-ignored");
return 0;
}
@@ -184,12 +186,58 @@
LSM_HOOK_INIT(kernel_load_data, loadpin_load_data),
};
-void __init loadpin_add_hooks(void)
+static void __init parse_exclude(void)
{
- pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
- security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
+ int i, j;
+ char *cur;
+
+ /*
+ * Make sure all the arrays stay within expected sizes. This
+ * is slightly weird because kernel_read_file_str[] includes
+ * READING_MAX_ID, which isn't actually meaningful here.
+ */
+ BUILD_BUG_ON(ARRAY_SIZE(exclude_read_files) !=
+ ARRAY_SIZE(ignore_read_file_id));
+ BUILD_BUG_ON(ARRAY_SIZE(kernel_read_file_str) <
+ ARRAY_SIZE(ignore_read_file_id));
+
+ for (i = 0; i < ARRAY_SIZE(exclude_read_files); i++) {
+ cur = exclude_read_files[i];
+ if (!cur)
+ break;
+ if (*cur == '\0')
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(ignore_read_file_id); j++) {
+ if (strcmp(cur, kernel_read_file_str[j]) == 0) {
+ pr_info("excluding: %s\n",
+ kernel_read_file_str[j]);
+ ignore_read_file_id[j] = 1;
+ /*
+ * Can not break, because one read_file_str
+ * may map to more than on read_file_id.
+ */
+ }
+ }
+ }
}
+static int __init loadpin_init(void)
+{
+ pr_info("ready to pin (currently %senforcing)\n",
+ enforce ? "" : "not ");
+ parse_exclude();
+ security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
+ return 0;
+}
+
+DEFINE_LSM(loadpin) = {
+ .name = "loadpin",
+ .init = loadpin_init,
+};
+
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
-module_param(enabled, int, 0);
-MODULE_PARM_DESC(enabled, "Pin module/firmware loading (default: true)");
+module_param(enforce, int, 0);
+MODULE_PARM_DESC(enforce, "Enforce module/firmware pinning");
+module_param_array_named(exclude, exclude_read_files, charp, NULL, 0);
+MODULE_PARM_DESC(exclude, "Exclude pinning specific read file types");
diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig
new file mode 100644
index 0000000..e84ddf4
--- /dev/null
+++ b/security/lockdown/Kconfig
@@ -0,0 +1,47 @@
+config SECURITY_LOCKDOWN_LSM
+ bool "Basic module for enforcing kernel lockdown"
+ depends on SECURITY
+ select MODULE_SIG if MODULES
+ help
+ Build support for an LSM that enforces a coarse kernel lockdown
+ behaviour.
+
+config SECURITY_LOCKDOWN_LSM_EARLY
+ bool "Enable lockdown LSM early in init"
+ depends on SECURITY_LOCKDOWN_LSM
+ help
+ Enable the lockdown LSM early in boot. This is necessary in order
+ to ensure that lockdown enforcement can be carried out on kernel
+ boot parameters that are otherwise parsed before the security
+ subsystem is fully initialised. If enabled, lockdown will
+ unconditionally be called before any other LSMs.
+
+choice
+ prompt "Kernel default lockdown mode"
+ default LOCK_DOWN_KERNEL_FORCE_NONE
+ depends on SECURITY_LOCKDOWN_LSM
+ help
+ The kernel can be configured to default to differing levels of
+ lockdown.
+
+config LOCK_DOWN_KERNEL_FORCE_NONE
+ bool "None"
+ help
+ No lockdown functionality is enabled by default. Lockdown may be
+ enabled via the kernel commandline or /sys/kernel/security/lockdown.
+
+config LOCK_DOWN_KERNEL_FORCE_INTEGRITY
+ bool "Integrity"
+ help
+ The kernel runs in integrity mode by default. Features that allow
+ the kernel to be modified at runtime are disabled.
+
+config LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY
+ bool "Confidentiality"
+ help
+ The kernel runs in confidentiality mode by default. Features that
+ allow the kernel to be modified at runtime or that permit userland
+ code to read confidential material held inside the kernel are
+ disabled.
+
+endchoice
diff --git a/security/lockdown/Makefile b/security/lockdown/Makefile
new file mode 100644
index 0000000..e3634b9
--- /dev/null
+++ b/security/lockdown/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown.o
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
new file mode 100644
index 0000000..40b7905
--- /dev/null
+++ b/security/lockdown/lockdown.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Lock down the kernel
+ *
+ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/security.h>
+#include <linux/export.h>
+#include <linux/lsm_hooks.h>
+
+static enum lockdown_reason kernel_locked_down;
+
+static const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
+ [LOCKDOWN_NONE] = "none",
+ [LOCKDOWN_MODULE_SIGNATURE] = "unsigned module loading",
+ [LOCKDOWN_DEV_MEM] = "/dev/mem,kmem,port",
+ [LOCKDOWN_EFI_TEST] = "/dev/efi_test access",
+ [LOCKDOWN_KEXEC] = "kexec of unsigned images",
+ [LOCKDOWN_HIBERNATION] = "hibernation",
+ [LOCKDOWN_PCI_ACCESS] = "direct PCI access",
+ [LOCKDOWN_IOPORT] = "raw io port access",
+ [LOCKDOWN_MSR] = "raw MSR access",
+ [LOCKDOWN_ACPI_TABLES] = "modifying ACPI tables",
+ [LOCKDOWN_PCMCIA_CIS] = "direct PCMCIA CIS storage",
+ [LOCKDOWN_TIOCSSERIAL] = "reconfiguration of serial port IO",
+ [LOCKDOWN_MODULE_PARAMETERS] = "unsafe module parameters",
+ [LOCKDOWN_MMIOTRACE] = "unsafe mmio",
+ [LOCKDOWN_DEBUGFS] = "debugfs access",
+ [LOCKDOWN_INTEGRITY_MAX] = "integrity",
+ [LOCKDOWN_KCORE] = "/proc/kcore access",
+ [LOCKDOWN_KPROBES] = "use of kprobes",
+ [LOCKDOWN_BPF_READ] = "use of bpf to read kernel RAM",
+ [LOCKDOWN_PERF] = "unsafe use of perf",
+ [LOCKDOWN_TRACEFS] = "use of tracefs",
+ [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
+};
+
+static const enum lockdown_reason lockdown_levels[] = {LOCKDOWN_NONE,
+ LOCKDOWN_INTEGRITY_MAX,
+ LOCKDOWN_CONFIDENTIALITY_MAX};
+
+/*
+ * Put the kernel into lock-down mode.
+ */
+static int lock_kernel_down(const char *where, enum lockdown_reason level)
+{
+ if (kernel_locked_down >= level)
+ return -EPERM;
+
+ kernel_locked_down = level;
+ pr_notice("Kernel is locked down from %s; see man kernel_lockdown.7\n",
+ where);
+ return 0;
+}
+
+static int __init lockdown_param(char *level)
+{
+ if (!level)
+ return -EINVAL;
+
+ if (strcmp(level, "integrity") == 0)
+ lock_kernel_down("command line", LOCKDOWN_INTEGRITY_MAX);
+ else if (strcmp(level, "confidentiality") == 0)
+ lock_kernel_down("command line", LOCKDOWN_CONFIDENTIALITY_MAX);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+early_param("lockdown", lockdown_param);
+
+/**
+ * lockdown_is_locked_down - Find out if the kernel is locked down
+ * @what: Tag to use in notice generated if lockdown is in effect
+ */
+static int lockdown_is_locked_down(enum lockdown_reason what)
+{
+ if (WARN(what >= LOCKDOWN_CONFIDENTIALITY_MAX,
+ "Invalid lockdown reason"))
+ return -EPERM;
+
+ if (kernel_locked_down >= what) {
+ if (lockdown_reasons[what])
+ pr_notice("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
+ current->comm, lockdown_reasons[what]);
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static struct security_hook_list lockdown_hooks[] __lsm_ro_after_init = {
+ LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
+};
+
+static int __init lockdown_lsm_init(void)
+{
+#if defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY)
+ lock_kernel_down("Kernel configuration", LOCKDOWN_INTEGRITY_MAX);
+#elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY)
+ lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY_MAX);
+#endif
+ security_add_hooks(lockdown_hooks, ARRAY_SIZE(lockdown_hooks),
+ "lockdown");
+ return 0;
+}
+
+static ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ char temp[80];
+ int i, offset = 0;
+
+ for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
+ enum lockdown_reason level = lockdown_levels[i];
+
+ if (lockdown_reasons[level]) {
+ const char *label = lockdown_reasons[level];
+
+ if (kernel_locked_down == level)
+ offset += sprintf(temp+offset, "[%s] ", label);
+ else
+ offset += sprintf(temp+offset, "%s ", label);
+ }
+ }
+
+ /* Convert the last space to a newline if needed. */
+ if (offset > 0)
+ temp[offset-1] = '\n';
+
+ return simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+}
+
+static ssize_t lockdown_write(struct file *file, const char __user *buf,
+ size_t n, loff_t *ppos)
+{
+ char *state;
+ int i, len, err = -EINVAL;
+
+ state = memdup_user_nul(buf, n);
+ if (IS_ERR(state))
+ return PTR_ERR(state);
+
+ len = strlen(state);
+ if (len && state[len-1] == '\n') {
+ state[len-1] = '\0';
+ len--;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
+ enum lockdown_reason level = lockdown_levels[i];
+ const char *label = lockdown_reasons[level];
+
+ if (label && !strcmp(state, label))
+ err = lock_kernel_down("securityfs", level);
+ }
+
+ kfree(state);
+ return err ? err : n;
+}
+
+static const struct file_operations lockdown_ops = {
+ .read = lockdown_read,
+ .write = lockdown_write,
+};
+
+static int __init lockdown_secfs_init(void)
+{
+ struct dentry *dentry;
+
+ dentry = securityfs_create_file("lockdown", 0600, NULL, NULL,
+ &lockdown_ops);
+ return PTR_ERR_OR_ZERO(dentry);
+}
+
+core_initcall(lockdown_secfs_init);
+
+#ifdef CONFIG_SECURITY_LOCKDOWN_LSM_EARLY
+DEFINE_EARLY_LSM(lockdown) = {
+#else
+DEFINE_LSM(lockdown) = {
+#endif
+ .name = "lockdown",
+ .init = lockdown_lsm_init,
+};
diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index f840010..e408743 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* common LSM auditing functions
*
@@ -5,10 +6,6 @@
* Stephen Smalley, <sds@tycho.nsa.gov>
* James Morris <jmorris@redhat.com>
* Author : Etienne Basset, <etienne.basset@ensta.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#include <linux/types.h>
@@ -321,6 +318,7 @@
if (a->u.net->sk) {
struct sock *sk = a->u.net->sk;
struct unix_sock *u;
+ struct unix_address *addr;
int len = 0;
char *p = NULL;
@@ -351,14 +349,15 @@
#endif
case AF_UNIX:
u = unix_sk(sk);
+ addr = smp_load_acquire(&u->addr);
+ if (!addr)
+ break;
if (u->path.dentry) {
audit_log_d_path(ab, " path=", &u->path);
break;
}
- if (!u->addr)
- break;
- len = u->addr->len-sizeof(short);
- p = &u->addr->name->sun_path[0];
+ len = addr->len-sizeof(short);
+ p = &addr->name->sun_path[0];
audit_log_format(ab, " path=");
if (*p)
audit_log_untrustedstring(ab, p);
diff --git a/security/safesetid/Kconfig b/security/safesetid/Kconfig
new file mode 100644
index 0000000..18b5fb9
--- /dev/null
+++ b/security/safesetid/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SECURITY_SAFESETID
+ bool "Gate setid transitions to limit CAP_SET{U/G}ID capabilities"
+ depends on SECURITY
+ select SECURITYFS
+ default n
+ help
+ SafeSetID is an LSM module that gates the setid family of syscalls to
+ restrict UID/GID transitions from a given UID/GID to only those
+ approved by a system-wide whitelist. These restrictions also prohibit
+ the given UIDs/GIDs from obtaining auxiliary privileges associated
+ with CAP_SET{U/G}ID, such as allowing a user to set up user namespace
+ UID mappings.
+
+ If you are unsure how to answer this question, answer N.
diff --git a/security/safesetid/Makefile b/security/safesetid/Makefile
new file mode 100644
index 0000000..6b06603
--- /dev/null
+++ b/security/safesetid/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the safesetid LSM.
+#
+
+obj-$(CONFIG_SECURITY_SAFESETID) := safesetid.o
+safesetid-y := lsm.o securityfs.o
diff --git a/security/safesetid/lsm.c b/security/safesetid/lsm.c
new file mode 100644
index 0000000..7760019
--- /dev/null
+++ b/security/safesetid/lsm.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SafeSetID Linux Security Module
+ *
+ * Author: Micah Morton <mortonm@chromium.org>
+ *
+ * Copyright (C) 2018 The Chromium OS Authors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt) "SafeSetID: " fmt
+
+#include <linux/lsm_hooks.h>
+#include <linux/module.h>
+#include <linux/ptrace.h>
+#include <linux/sched/task_stack.h>
+#include <linux/security.h>
+#include "lsm.h"
+
+/* Flag indicating whether initialization completed */
+int safesetid_initialized;
+
+struct setuid_ruleset __rcu *safesetid_setuid_rules;
+
+/* Compute a decision for a transition from @src to @dst under @policy. */
+enum sid_policy_type _setuid_policy_lookup(struct setuid_ruleset *policy,
+ kuid_t src, kuid_t dst)
+{
+ struct setuid_rule *rule;
+ enum sid_policy_type result = SIDPOL_DEFAULT;
+
+ hash_for_each_possible(policy->rules, rule, next, __kuid_val(src)) {
+ if (!uid_eq(rule->src_uid, src))
+ continue;
+ if (uid_eq(rule->dst_uid, dst))
+ return SIDPOL_ALLOWED;
+ result = SIDPOL_CONSTRAINED;
+ }
+ return result;
+}
+
+/*
+ * Compute a decision for a transition from @src to @dst under the active
+ * policy.
+ */
+static enum sid_policy_type setuid_policy_lookup(kuid_t src, kuid_t dst)
+{
+ enum sid_policy_type result = SIDPOL_DEFAULT;
+ struct setuid_ruleset *pol;
+
+ rcu_read_lock();
+ pol = rcu_dereference(safesetid_setuid_rules);
+ if (pol)
+ result = _setuid_policy_lookup(pol, src, dst);
+ rcu_read_unlock();
+ return result;
+}
+
+static int safesetid_security_capable(const struct cred *cred,
+ struct user_namespace *ns,
+ int cap,
+ unsigned int opts)
+{
+ /* We're only interested in CAP_SETUID. */
+ if (cap != CAP_SETUID)
+ return 0;
+
+ /*
+ * If CAP_SETUID is currently used for a set*uid() syscall, we want to
+ * let it go through here; the real security check happens later, in the
+ * task_fix_setuid hook.
+ */
+ if ((opts & CAP_OPT_INSETID) != 0)
+ return 0;
+
+ /*
+ * If no policy applies to this task, allow the use of CAP_SETUID for
+ * other purposes.
+ */
+ if (setuid_policy_lookup(cred->uid, INVALID_UID) == SIDPOL_DEFAULT)
+ return 0;
+
+ /*
+ * Reject use of CAP_SETUID for functionality other than calling
+ * set*uid() (e.g. setting up userns uid mappings).
+ */
+ pr_warn("Operation requires CAP_SETUID, which is not available to UID %u for operations besides approved set*uid transitions\n",
+ __kuid_val(cred->uid));
+ return -EPERM;
+}
+
+/*
+ * Check whether a caller with old credentials @old is allowed to switch to
+ * credentials that contain @new_uid.
+ */
+static bool uid_permitted_for_cred(const struct cred *old, kuid_t new_uid)
+{
+ bool permitted;
+
+ /* If our old creds already had this UID in it, it's fine. */
+ if (uid_eq(new_uid, old->uid) || uid_eq(new_uid, old->euid) ||
+ uid_eq(new_uid, old->suid))
+ return true;
+
+ /*
+ * Transitions to new UIDs require a check against the policy of the old
+ * RUID.
+ */
+ permitted =
+ setuid_policy_lookup(old->uid, new_uid) != SIDPOL_CONSTRAINED;
+ if (!permitted) {
+ pr_warn("UID transition ((%d,%d,%d) -> %d) blocked\n",
+ __kuid_val(old->uid), __kuid_val(old->euid),
+ __kuid_val(old->suid), __kuid_val(new_uid));
+ }
+ return permitted;
+}
+
+/*
+ * Check whether there is either an exception for user under old cred struct to
+ * set*uid to user under new cred struct, or the UID transition is allowed (by
+ * Linux set*uid rules) even without CAP_SETUID.
+ */
+static int safesetid_task_fix_setuid(struct cred *new,
+ const struct cred *old,
+ int flags)
+{
+
+ /* Do nothing if there are no setuid restrictions for our old RUID. */
+ if (setuid_policy_lookup(old->uid, INVALID_UID) == SIDPOL_DEFAULT)
+ return 0;
+
+ if (uid_permitted_for_cred(old, new->uid) &&
+ uid_permitted_for_cred(old, new->euid) &&
+ uid_permitted_for_cred(old, new->suid) &&
+ uid_permitted_for_cred(old, new->fsuid))
+ return 0;
+
+ /*
+ * Kill this process to avoid potential security vulnerabilities
+ * that could arise from a missing whitelist entry preventing a
+ * privileged process from dropping to a lesser-privileged one.
+ */
+ force_sig(SIGKILL);
+ return -EACCES;
+}
+
+static struct security_hook_list safesetid_security_hooks[] = {
+ LSM_HOOK_INIT(task_fix_setuid, safesetid_task_fix_setuid),
+ LSM_HOOK_INIT(capable, safesetid_security_capable)
+};
+
+static int __init safesetid_security_init(void)
+{
+ security_add_hooks(safesetid_security_hooks,
+ ARRAY_SIZE(safesetid_security_hooks), "safesetid");
+
+ /* Report that SafeSetID successfully initialized */
+ safesetid_initialized = 1;
+
+ return 0;
+}
+
+DEFINE_LSM(safesetid_security_init) = {
+ .init = safesetid_security_init,
+ .name = "safesetid",
+};
diff --git a/security/safesetid/lsm.h b/security/safesetid/lsm.h
new file mode 100644
index 0000000..db6d16e
--- /dev/null
+++ b/security/safesetid/lsm.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SafeSetID Linux Security Module
+ *
+ * Author: Micah Morton <mortonm@chromium.org>
+ *
+ * Copyright (C) 2018 The Chromium OS Authors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+#ifndef _SAFESETID_H
+#define _SAFESETID_H
+
+#include <linux/types.h>
+#include <linux/uidgid.h>
+#include <linux/hashtable.h>
+
+/* Flag indicating whether initialization completed */
+extern int safesetid_initialized;
+
+enum sid_policy_type {
+ SIDPOL_DEFAULT, /* source ID is unaffected by policy */
+ SIDPOL_CONSTRAINED, /* source ID is affected by policy */
+ SIDPOL_ALLOWED /* target ID explicitly allowed */
+};
+
+/*
+ * Hash table entry to store safesetid policy signifying that 'src_uid'
+ * can setuid to 'dst_uid'.
+ */
+struct setuid_rule {
+ struct hlist_node next;
+ kuid_t src_uid;
+ kuid_t dst_uid;
+};
+
+#define SETID_HASH_BITS 8 /* 256 buckets in hash table */
+
+struct setuid_ruleset {
+ DECLARE_HASHTABLE(rules, SETID_HASH_BITS);
+ char *policy_str;
+ struct rcu_head rcu;
+};
+
+enum sid_policy_type _setuid_policy_lookup(struct setuid_ruleset *policy,
+ kuid_t src, kuid_t dst);
+
+extern struct setuid_ruleset __rcu *safesetid_setuid_rules;
+
+#endif /* _SAFESETID_H */
diff --git a/security/safesetid/securityfs.c b/security/safesetid/securityfs.c
new file mode 100644
index 0000000..74a13d4
--- /dev/null
+++ b/security/safesetid/securityfs.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SafeSetID Linux Security Module
+ *
+ * Author: Micah Morton <mortonm@chromium.org>
+ *
+ * Copyright (C) 2018 The Chromium OS Authors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt) "SafeSetID: " fmt
+
+#include <linux/security.h>
+#include <linux/cred.h>
+
+#include "lsm.h"
+
+static DEFINE_MUTEX(policy_update_lock);
+
+/*
+ * In the case the input buffer contains one or more invalid UIDs, the kuid_t
+ * variables pointed to by @parent and @child will get updated but this
+ * function will return an error.
+ * Contents of @buf may be modified.
+ */
+static int parse_policy_line(struct file *file, char *buf,
+ struct setuid_rule *rule)
+{
+ char *child_str;
+ int ret;
+ u32 parsed_parent, parsed_child;
+
+ /* Format of |buf| string should be <UID>:<UID>. */
+ child_str = strchr(buf, ':');
+ if (child_str == NULL)
+ return -EINVAL;
+ *child_str = '\0';
+ child_str++;
+
+ ret = kstrtou32(buf, 0, &parsed_parent);
+ if (ret)
+ return ret;
+
+ ret = kstrtou32(child_str, 0, &parsed_child);
+ if (ret)
+ return ret;
+
+ rule->src_uid = make_kuid(file->f_cred->user_ns, parsed_parent);
+ rule->dst_uid = make_kuid(file->f_cred->user_ns, parsed_child);
+ if (!uid_valid(rule->src_uid) || !uid_valid(rule->dst_uid))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void __release_ruleset(struct rcu_head *rcu)
+{
+ struct setuid_ruleset *pol =
+ container_of(rcu, struct setuid_ruleset, rcu);
+ int bucket;
+ struct setuid_rule *rule;
+ struct hlist_node *tmp;
+
+ hash_for_each_safe(pol->rules, bucket, tmp, rule, next)
+ kfree(rule);
+ kfree(pol->policy_str);
+ kfree(pol);
+}
+
+static void release_ruleset(struct setuid_ruleset *pol)
+{
+ call_rcu(&pol->rcu, __release_ruleset);
+}
+
+static void insert_rule(struct setuid_ruleset *pol, struct setuid_rule *rule)
+{
+ hash_add(pol->rules, &rule->next, __kuid_val(rule->src_uid));
+}
+
+static int verify_ruleset(struct setuid_ruleset *pol)
+{
+ int bucket;
+ struct setuid_rule *rule, *nrule;
+ int res = 0;
+
+ hash_for_each(pol->rules, bucket, rule, next) {
+ if (_setuid_policy_lookup(pol, rule->dst_uid, INVALID_UID) ==
+ SIDPOL_DEFAULT) {
+ pr_warn("insecure policy detected: uid %d is constrained but transitively unconstrained through uid %d\n",
+ __kuid_val(rule->src_uid),
+ __kuid_val(rule->dst_uid));
+ res = -EINVAL;
+
+ /* fix it up */
+ nrule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL);
+ if (!nrule)
+ return -ENOMEM;
+ nrule->src_uid = rule->dst_uid;
+ nrule->dst_uid = rule->dst_uid;
+ insert_rule(pol, nrule);
+ }
+ }
+ return res;
+}
+
+static ssize_t handle_policy_update(struct file *file,
+ const char __user *ubuf, size_t len)
+{
+ struct setuid_ruleset *pol;
+ char *buf, *p, *end;
+ int err;
+
+ pol = kmalloc(sizeof(struct setuid_ruleset), GFP_KERNEL);
+ if (!pol)
+ return -ENOMEM;
+ pol->policy_str = NULL;
+ hash_init(pol->rules);
+
+ p = buf = memdup_user_nul(ubuf, len);
+ if (IS_ERR(buf)) {
+ err = PTR_ERR(buf);
+ goto out_free_pol;
+ }
+ pol->policy_str = kstrdup(buf, GFP_KERNEL);
+ if (pol->policy_str == NULL) {
+ err = -ENOMEM;
+ goto out_free_buf;
+ }
+
+ /* policy lines, including the last one, end with \n */
+ while (*p != '\0') {
+ struct setuid_rule *rule;
+
+ end = strchr(p, '\n');
+ if (end == NULL) {
+ err = -EINVAL;
+ goto out_free_buf;
+ }
+ *end = '\0';
+
+ rule = kmalloc(sizeof(struct setuid_rule), GFP_KERNEL);
+ if (!rule) {
+ err = -ENOMEM;
+ goto out_free_buf;
+ }
+
+ err = parse_policy_line(file, p, rule);
+ if (err)
+ goto out_free_rule;
+
+ if (_setuid_policy_lookup(pol, rule->src_uid, rule->dst_uid) ==
+ SIDPOL_ALLOWED) {
+ pr_warn("bad policy: duplicate entry\n");
+ err = -EEXIST;
+ goto out_free_rule;
+ }
+
+ insert_rule(pol, rule);
+ p = end + 1;
+ continue;
+
+out_free_rule:
+ kfree(rule);
+ goto out_free_buf;
+ }
+
+ err = verify_ruleset(pol);
+ /* bogus policy falls through after fixing it up */
+ if (err && err != -EINVAL)
+ goto out_free_buf;
+
+ /*
+ * Everything looks good, apply the policy and release the old one.
+ * What we really want here is an xchg() wrapper for RCU, but since that
+ * doesn't currently exist, just use a spinlock for now.
+ */
+ mutex_lock(&policy_update_lock);
+ rcu_swap_protected(safesetid_setuid_rules, pol,
+ lockdep_is_held(&policy_update_lock));
+ mutex_unlock(&policy_update_lock);
+ err = len;
+
+out_free_buf:
+ kfree(buf);
+out_free_pol:
+ if (pol)
+ release_ruleset(pol);
+ return err;
+}
+
+static ssize_t safesetid_file_write(struct file *file,
+ const char __user *buf,
+ size_t len,
+ loff_t *ppos)
+{
+ if (!file_ns_capable(file, &init_user_ns, CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (*ppos != 0)
+ return -EINVAL;
+
+ return handle_policy_update(file, buf, len);
+}
+
+static ssize_t safesetid_file_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ ssize_t res = 0;
+ struct setuid_ruleset *pol;
+ const char *kbuf;
+
+ mutex_lock(&policy_update_lock);
+ pol = rcu_dereference_protected(safesetid_setuid_rules,
+ lockdep_is_held(&policy_update_lock));
+ if (pol) {
+ kbuf = pol->policy_str;
+ res = simple_read_from_buffer(buf, len, ppos,
+ kbuf, strlen(kbuf));
+ }
+ mutex_unlock(&policy_update_lock);
+ return res;
+}
+
+static const struct file_operations safesetid_file_fops = {
+ .read = safesetid_file_read,
+ .write = safesetid_file_write,
+};
+
+static int __init safesetid_init_securityfs(void)
+{
+ int ret;
+ struct dentry *policy_dir;
+ struct dentry *policy_file;
+
+ if (!safesetid_initialized)
+ return 0;
+
+ policy_dir = securityfs_create_dir("safesetid", NULL);
+ if (IS_ERR(policy_dir)) {
+ ret = PTR_ERR(policy_dir);
+ goto error;
+ }
+
+ policy_file = securityfs_create_file("whitelist_policy", 0600,
+ policy_dir, NULL, &safesetid_file_fops);
+ if (IS_ERR(policy_file)) {
+ ret = PTR_ERR(policy_file);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ securityfs_remove(policy_dir);
+ return ret;
+}
+fs_initcall(safesetid_init_securityfs);
diff --git a/security/security.c b/security/security.c
index 736e78d..1bc000f 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Security plug functions
*
@@ -5,17 +6,14 @@
* Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
* Copyright (C) 2016 Mellanox Technologies
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
*/
+#define pr_fmt(fmt) "LSM: " fmt
+
#include <linux/bpf.h>
#include <linux/capability.h>
#include <linux/dcache.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/lsm_hooks.h>
@@ -28,40 +26,326 @@
#include <linux/personality.h>
#include <linux/backing-dev.h>
#include <linux/string.h>
+#include <linux/msg.h>
#include <net/flow.h>
-#include <trace/events/initcall.h>
-
#define MAX_LSM_EVM_XATTR 2
-/* Maximum number of letters for an LSM name string */
-#define SECURITY_NAME_MAX 10
+/* How many LSMs were built into the kernel? */
+#define LSM_COUNT (__end_lsm_info - __start_lsm_info)
+#define EARLY_LSM_COUNT (__end_early_lsm_info - __start_early_lsm_info)
struct security_hook_heads security_hook_heads __lsm_ro_after_init;
-static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
+static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
+
+static struct kmem_cache *lsm_file_cache;
+static struct kmem_cache *lsm_inode_cache;
char *lsm_names;
+static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init;
+
/* Boot-time LSM user choice */
-static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
- CONFIG_DEFAULT_SECURITY;
+static __initdata const char *chosen_lsm_order;
+static __initdata const char *chosen_major_lsm;
-static void __init do_security_initcalls(void)
+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 *exclusive;
+
+static __initdata bool debug;
+#define init_debug(...) \
+ do { \
+ if (debug) \
+ pr_info(__VA_ARGS__); \
+ } while (0)
+
+static bool __init is_enabled(struct lsm_info *lsm)
{
- int ret;
- initcall_t call;
- initcall_entry_t *ce;
+ if (!lsm->enabled)
+ return false;
- ce = __security_initcall_start;
- trace_initcall_level("security");
- while (ce < __security_initcall_end) {
- call = initcall_from_entry(ce);
- trace_initcall_start(call);
- ret = call();
- trace_initcall_finish(call, ret);
- ce++;
+ return *lsm->enabled;
+}
+
+/* Mark an LSM's enabled flag. */
+static int lsm_enabled_true __initdata = 1;
+static int lsm_enabled_false __initdata = 0;
+static void __init set_enabled(struct lsm_info *lsm, bool enabled)
+{
+ /*
+ * When an LSM hasn't configured an enable variable, we can use
+ * a hard-coded location for storing the default enabled state.
+ */
+ if (!lsm->enabled) {
+ if (enabled)
+ lsm->enabled = &lsm_enabled_true;
+ else
+ lsm->enabled = &lsm_enabled_false;
+ } else if (lsm->enabled == &lsm_enabled_true) {
+ if (!enabled)
+ lsm->enabled = &lsm_enabled_false;
+ } else if (lsm->enabled == &lsm_enabled_false) {
+ if (enabled)
+ lsm->enabled = &lsm_enabled_true;
+ } else {
+ *lsm->enabled = enabled;
}
}
+/* Is an LSM already listed in the ordered LSMs list? */
+static bool __init exists_ordered_lsm(struct lsm_info *lsm)
+{
+ struct lsm_info **check;
+
+ for (check = ordered_lsms; *check; check++)
+ if (*check == lsm)
+ return true;
+
+ return false;
+}
+
+/* Append an LSM to the list of ordered LSMs to initialize. */
+static int last_lsm __initdata;
+static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
+{
+ /* Ignore duplicate selections. */
+ if (exists_ordered_lsm(lsm))
+ return;
+
+ if (WARN(last_lsm == LSM_COUNT, "%s: out of LSM slots!?\n", from))
+ return;
+
+ /* Enable this LSM, if it is not already set. */
+ if (!lsm->enabled)
+ lsm->enabled = &lsm_enabled_true;
+ ordered_lsms[last_lsm++] = lsm;
+
+ init_debug("%s ordering: %s (%sabled)\n", from, lsm->name,
+ is_enabled(lsm) ? "en" : "dis");
+}
+
+/* Is an LSM allowed to be initialized? */
+static bool __init lsm_allowed(struct lsm_info *lsm)
+{
+ /* Skip if the LSM is disabled. */
+ if (!is_enabled(lsm))
+ return false;
+
+ /* Not allowed if another exclusive LSM already initialized. */
+ if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
+ init_debug("exclusive disabled: %s\n", lsm->name);
+ return false;
+ }
+
+ return true;
+}
+
+static void __init lsm_set_blob_size(int *need, int *lbs)
+{
+ int offset;
+
+ if (*need > 0) {
+ offset = *lbs;
+ *lbs += *need;
+ *need = offset;
+ }
+}
+
+static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
+{
+ if (!needed)
+ return;
+
+ lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
+ lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
+ /*
+ * The inode blob gets an rcu_head in addition to
+ * what the modules might need.
+ */
+ if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
+ 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_msg_msg, &blob_sizes.lbs_msg_msg);
+ lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
+}
+
+/* Prepare LSM for initialization. */
+static void __init prepare_lsm(struct lsm_info *lsm)
+{
+ int enabled = lsm_allowed(lsm);
+
+ /* Record enablement (to handle any following exclusive LSMs). */
+ set_enabled(lsm, enabled);
+
+ /* If enabled, do pre-initialization work. */
+ if (enabled) {
+ if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
+ exclusive = lsm;
+ init_debug("exclusive chosen: %s\n", lsm->name);
+ }
+
+ lsm_set_blob_sizes(lsm->blobs);
+ }
+}
+
+/* Initialize a given LSM, if it is enabled. */
+static void __init initialize_lsm(struct lsm_info *lsm)
+{
+ if (is_enabled(lsm)) {
+ int ret;
+
+ init_debug("initializing %s\n", lsm->name);
+ ret = lsm->init();
+ WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
+ }
+}
+
+/* Populate ordered LSMs list from comma-separated LSM name list. */
+static void __init ordered_lsm_parse(const char *order, const char *origin)
+{
+ struct lsm_info *lsm;
+ char *sep, *name, *next;
+
+ /* LSM_ORDER_FIRST is always first. */
+ for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+ if (lsm->order == LSM_ORDER_FIRST)
+ append_ordered_lsm(lsm, "first");
+ }
+
+ /* Process "security=", if given. */
+ if (chosen_major_lsm) {
+ struct lsm_info *major;
+
+ /*
+ * To match the original "security=" behavior, this
+ * explicitly does NOT fallback to another Legacy Major
+ * if the selected one was separately disabled: disable
+ * all non-matching Legacy Major LSMs.
+ */
+ for (major = __start_lsm_info; major < __end_lsm_info;
+ major++) {
+ if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
+ strcmp(major->name, chosen_major_lsm) != 0) {
+ set_enabled(major, false);
+ init_debug("security=%s disabled: %s\n",
+ chosen_major_lsm, major->name);
+ }
+ }
+ }
+
+ sep = kstrdup(order, GFP_KERNEL);
+ next = sep;
+ /* Walk the list, looking for matching LSMs. */
+ while ((name = strsep(&next, ",")) != NULL) {
+ bool found = false;
+
+ for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+ if (lsm->order == LSM_ORDER_MUTABLE &&
+ strcmp(lsm->name, name) == 0) {
+ append_ordered_lsm(lsm, origin);
+ found = true;
+ }
+ }
+
+ if (!found)
+ init_debug("%s ignored: %s\n", origin, name);
+ }
+
+ /* Process "security=", if given. */
+ if (chosen_major_lsm) {
+ for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+ if (exists_ordered_lsm(lsm))
+ continue;
+ if (strcmp(lsm->name, chosen_major_lsm) == 0)
+ append_ordered_lsm(lsm, "security=");
+ }
+ }
+
+ /* Disable all LSMs not in the ordered list. */
+ for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
+ if (exists_ordered_lsm(lsm))
+ continue;
+ set_enabled(lsm, false);
+ init_debug("%s disabled: %s\n", origin, lsm->name);
+ }
+
+ kfree(sep);
+}
+
+static void __init lsm_early_cred(struct cred *cred);
+static void __init lsm_early_task(struct task_struct *task);
+
+static int lsm_append(const char *new, char **result);
+
+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_info("security= is ignored because it is superseded by lsm=\n");
+ chosen_major_lsm = NULL;
+ }
+ ordered_lsm_parse(chosen_lsm_order, "cmdline");
+ } else
+ ordered_lsm_parse(builtin_lsm_order, "builtin");
+
+ for (lsm = ordered_lsms; *lsm; lsm++)
+ prepare_lsm(*lsm);
+
+ init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
+ init_debug("file blob size = %d\n", blob_sizes.lbs_file);
+ init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
+ init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
+ init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
+ init_debug("task blob size = %d\n", blob_sizes.lbs_task);
+
+ /*
+ * Create any kmem_caches needed for blobs
+ */
+ if (blob_sizes.lbs_file)
+ lsm_file_cache = kmem_cache_create("lsm_file_cache",
+ blob_sizes.lbs_file, 0,
+ SLAB_PANIC, NULL);
+ if (blob_sizes.lbs_inode)
+ lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
+ blob_sizes.lbs_inode, 0,
+ SLAB_PANIC, NULL);
+
+ lsm_early_cred((struct cred *) current->cred);
+ lsm_early_task(current);
+ for (lsm = ordered_lsms; *lsm; lsm++)
+ initialize_lsm(*lsm);
+
+ kfree(ordered_lsms);
+}
+
+int __init early_security_init(void)
+{
+ int i;
+ struct hlist_head *list = (struct hlist_head *) &security_hook_heads;
+ struct lsm_info *lsm;
+
+ for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct hlist_head);
+ i++)
+ INIT_HLIST_HEAD(&list[i]);
+
+ for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
+ if (!lsm->enabled)
+ lsm->enabled = &lsm_enabled_true;
+ prepare_lsm(lsm);
+ initialize_lsm(lsm);
+ }
+
+ return 0;
+}
+
/**
* security_init - initializes the security framework
*
@@ -69,36 +353,48 @@
*/
int __init security_init(void)
{
- int i;
- struct hlist_head *list = (struct hlist_head *) &security_hook_heads;
+ struct lsm_info *lsm;
- for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct hlist_head);
- i++)
- INIT_HLIST_HEAD(&list[i]);
- pr_info("Security Framework initialized\n");
+ pr_info("Security Framework initializing\n");
/*
- * Load minor LSMs, with the capability module always first.
+ * Append the names of the early LSM modules now that kmalloc() is
+ * available
*/
- capability_add_hooks();
- yama_add_hooks();
- loadpin_add_hooks();
+ for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
+ if (lsm->enabled)
+ lsm_append(lsm->name, &lsm_names);
+ }
- /*
- * Load all the remaining security modules.
- */
- do_security_initcalls();
+ /* Load LSMs in specified order. */
+ ordered_lsm_init();
return 0;
}
/* Save user chosen LSM */
-static int __init choose_lsm(char *str)
+static int __init choose_major_lsm(char *str)
{
- strncpy(chosen_lsm, str, SECURITY_NAME_MAX);
+ chosen_major_lsm = str;
return 1;
}
-__setup("security=", choose_lsm);
+__setup("security=", choose_major_lsm);
+
+/* Explicitly choose LSM initialization order. */
+static int __init choose_lsm_order(char *str)
+{
+ chosen_lsm_order = str;
+ return 1;
+}
+__setup("lsm=", choose_lsm_order);
+
+/* Enable LSM order debugging. */
+static int __init enable_debug(char *str)
+{
+ debug = true;
+ return 1;
+}
+__setup("lsm.debug", enable_debug);
static bool match_last_lsm(const char *list, const char *lsm)
{
@@ -115,7 +411,7 @@
return !strcmp(last, lsm);
}
-static int lsm_append(char *new, char **result)
+static int lsm_append(const char *new, char **result)
{
char *cp;
@@ -137,29 +433,6 @@
}
/**
- * security_module_enable - Load given security module on boot ?
- * @module: the name of the module
- *
- * Each LSM must pass this method before registering its own operations
- * to avoid security registration races. This method may also be used
- * to check if your LSM is currently loaded during kernel initialization.
- *
- * Returns:
- *
- * true if:
- *
- * - The passed LSM is the one chosen by user at boot time,
- * - or the passed LSM is configured as the default and the user did not
- * choose an alternate LSM at boot time.
- *
- * Otherwise, return false.
- */
-int __init security_module_enable(const char *module)
-{
- return !strcmp(module, chosen_lsm);
-}
-
-/**
* security_add_hooks - Add a modules hooks to the hook lists.
* @hooks: the hooks to add
* @count: the number of hooks to add
@@ -176,27 +449,192 @@
hooks[i].lsm = lsm;
hlist_add_tail_rcu(&hooks[i].list, hooks[i].head);
}
- if (lsm_append(lsm, &lsm_names) < 0)
- panic("%s - Cannot get early memory.\n", __func__);
+
+ /*
+ * Don't try to append during early_security_init(), we'll come back
+ * and fix this up afterwards.
+ */
+ if (slab_is_available()) {
+ if (lsm_append(lsm, &lsm_names) < 0)
+ panic("%s - Cannot get early memory.\n", __func__);
+ }
}
-int call_lsm_notifier(enum lsm_event event, void *data)
+int call_blocking_lsm_notifier(enum lsm_event event, void *data)
{
- return atomic_notifier_call_chain(&lsm_notifier_chain, event, data);
+ return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
+ event, data);
}
-EXPORT_SYMBOL(call_lsm_notifier);
+EXPORT_SYMBOL(call_blocking_lsm_notifier);
-int register_lsm_notifier(struct notifier_block *nb)
+int register_blocking_lsm_notifier(struct notifier_block *nb)
{
- return atomic_notifier_chain_register(&lsm_notifier_chain, nb);
+ return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
+ nb);
}
-EXPORT_SYMBOL(register_lsm_notifier);
+EXPORT_SYMBOL(register_blocking_lsm_notifier);
-int unregister_lsm_notifier(struct notifier_block *nb)
+int unregister_blocking_lsm_notifier(struct notifier_block *nb)
{
- return atomic_notifier_chain_unregister(&lsm_notifier_chain, nb);
+ return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
+ nb);
}
-EXPORT_SYMBOL(unregister_lsm_notifier);
+EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
+
+/**
+ * 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)
+{
+ if (blob_sizes.lbs_cred == 0) {
+ cred->security = NULL;
+ return 0;
+ }
+
+ cred->security = kzalloc(blob_sizes.lbs_cred, gfp);
+ if (cred->security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * lsm_early_cred - during initialization allocate a composite cred blob
+ * @cred: the cred that needs a blob
+ *
+ * Allocate the cred blob for all the modules
+ */
+static void __init lsm_early_cred(struct cred *cred)
+{
+ int rc = lsm_cred_alloc(cred, GFP_KERNEL);
+
+ if (rc)
+ panic("%s: Early cred alloc failed.\n", __func__);
+}
+
+/**
+ * lsm_file_alloc - allocate a composite file blob
+ * @file: the file that needs a blob
+ *
+ * Allocate the file blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_file_alloc(struct file *file)
+{
+ if (!lsm_file_cache) {
+ file->f_security = NULL;
+ return 0;
+ }
+
+ file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
+ if (file->f_security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * lsm_inode_alloc - allocate a composite inode blob
+ * @inode: the inode that needs a blob
+ *
+ * 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)
+{
+ if (!lsm_inode_cache) {
+ inode->i_security = NULL;
+ return 0;
+ }
+
+ inode->i_security = kmem_cache_zalloc(lsm_inode_cache, GFP_NOFS);
+ if (inode->i_security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * lsm_task_alloc - allocate a composite task blob
+ * @task: the task that needs a blob
+ *
+ * Allocate the task blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+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;
+}
+
+/**
+ * lsm_ipc_alloc - allocate a composite ipc blob
+ * @kip: the ipc that needs a blob
+ *
+ * Allocate the ipc blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_ipc_alloc(struct kern_ipc_perm *kip)
+{
+ if (blob_sizes.lbs_ipc == 0) {
+ kip->security = NULL;
+ return 0;
+ }
+
+ kip->security = kzalloc(blob_sizes.lbs_ipc, GFP_KERNEL);
+ if (kip->security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * lsm_msg_msg_alloc - allocate a composite msg_msg blob
+ * @mp: the msg_msg that needs a blob
+ *
+ * Allocate the ipc blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_msg_msg_alloc(struct msg_msg *mp)
+{
+ if (blob_sizes.lbs_msg_msg == 0) {
+ mp->security = NULL;
+ return 0;
+ }
+
+ mp->security = kzalloc(blob_sizes.lbs_msg_msg, GFP_KERNEL);
+ if (mp->security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * lsm_early_task - during initialization allocate a composite task blob
+ * @task: the task that needs a blob
+ *
+ * Allocate the task blob for all the modules
+ */
+static void __init lsm_early_task(struct task_struct *task)
+{
+ int rc = lsm_task_alloc(task);
+
+ if (rc)
+ panic("%s: Early task alloc failed.\n", __func__);
+}
/*
* Hook list operation macros.
@@ -283,16 +721,12 @@
effective, inheritable, permitted);
}
-int security_capable(const struct cred *cred, struct user_namespace *ns,
- int cap)
+int security_capable(const struct cred *cred,
+ struct user_namespace *ns,
+ int cap,
+ unsigned int opts)
{
- return call_int_hook(capable, 0, cred, ns, cap, SECURITY_CAP_AUDIT);
-}
-
-int security_capable_noaudit(const struct cred *cred, struct user_namespace *ns,
- int cap)
-{
- return call_int_hook(capable, 0, cred, ns, cap, SECURITY_CAP_NOAUDIT);
+ return call_int_hook(capable, 0, cred, ns, cap, opts);
}
int security_quotactl(int cmds, int type, int id, struct super_block *sb)
@@ -363,6 +797,16 @@
call_void_hook(bprm_committed_creds, bprm);
}
+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);
+}
+
+int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+ return call_int_hook(fs_context_parse_param, -ENOPARAM, fc, param);
+}
+
int security_sb_alloc(struct super_block *sb)
{
return call_int_hook(sb_alloc_security, 0, sb);
@@ -373,20 +817,31 @@
call_void_hook(sb_free_security, sb);
}
-int security_sb_copy_data(char *orig, char *copy)
+void security_free_mnt_opts(void **mnt_opts)
{
- return call_int_hook(sb_copy_data, 0, orig, copy);
+ if (!*mnt_opts)
+ return;
+ call_void_hook(sb_free_mnt_opts, *mnt_opts);
+ *mnt_opts = NULL;
}
-EXPORT_SYMBOL(security_sb_copy_data);
+EXPORT_SYMBOL(security_free_mnt_opts);
-int security_sb_remount(struct super_block *sb, void *data)
+int security_sb_eat_lsm_opts(char *options, void **mnt_opts)
{
- return call_int_hook(sb_remount, 0, sb, data);
+ return call_int_hook(sb_eat_lsm_opts, 0, options, mnt_opts);
}
+EXPORT_SYMBOL(security_sb_eat_lsm_opts);
-int security_sb_kern_mount(struct super_block *sb, int flags, void *data)
+int security_sb_remount(struct super_block *sb,
+ void *mnt_opts)
{
- return call_int_hook(sb_kern_mount, 0, sb, flags, data);
+ return call_int_hook(sb_remount, 0, sb, mnt_opts);
+}
+EXPORT_SYMBOL(security_sb_remount);
+
+int security_sb_kern_mount(struct super_block *sb)
+{
+ return call_int_hook(sb_kern_mount, 0, sb);
}
int security_sb_show_options(struct seq_file *m, struct super_block *sb)
@@ -416,13 +871,13 @@
}
int security_sb_set_mnt_opts(struct super_block *sb,
- struct security_mnt_opts *opts,
+ void *mnt_opts,
unsigned long kern_flags,
unsigned long *set_kern_flags)
{
return call_int_hook(sb_set_mnt_opts,
- opts->num_mnt_opts ? -EOPNOTSUPP : 0, sb,
- opts, kern_flags, set_kern_flags);
+ mnt_opts ? -EOPNOTSUPP : 0, sb,
+ mnt_opts, kern_flags, set_kern_flags);
}
EXPORT_SYMBOL(security_sb_set_mnt_opts);
@@ -436,22 +891,61 @@
}
EXPORT_SYMBOL(security_sb_clone_mnt_opts);
-int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
+int security_add_mnt_opt(const char *option, const char *val, int len,
+ void **mnt_opts)
{
- return call_int_hook(sb_parse_opts_str, 0, options, opts);
+ return call_int_hook(sb_add_mnt_opt, -EINVAL,
+ option, val, len, mnt_opts);
}
-EXPORT_SYMBOL(security_sb_parse_opts_str);
+EXPORT_SYMBOL(security_add_mnt_opt);
+
+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);
+}
+
+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);
+}
int security_inode_alloc(struct inode *inode)
{
- inode->i_security = NULL;
- return call_int_hook(inode_alloc_security, 0, inode);
+ int rc = lsm_inode_alloc(inode);
+
+ if (unlikely(rc))
+ return rc;
+ rc = call_int_hook(inode_alloc_security, 0, inode);
+ if (unlikely(rc))
+ security_inode_free(inode);
+ return rc;
+}
+
+static void inode_free_by_rcu(struct rcu_head *head)
+{
+ /*
+ * The rcu head is at the start of the inode blob
+ */
+ kmem_cache_free(lsm_inode_cache, head);
}
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);
}
int security_dentry_init_security(struct dentry *dentry, int mode,
@@ -868,6 +1362,12 @@
}
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);
+}
+
int security_file_permission(struct file *file, int mask)
{
int ret;
@@ -881,12 +1381,27 @@
int security_file_alloc(struct file *file)
{
- return call_int_hook(file_alloc_security, 0, file);
+ int rc = lsm_file_alloc(file);
+
+ if (rc)
+ return rc;
+ rc = call_int_hook(file_alloc_security, 0, file);
+ if (unlikely(rc))
+ security_file_free(file);
+ return rc;
}
void security_file_free(struct file *file)
{
+ void *blob;
+
call_void_hook(file_free_security, file);
+
+ blob = file->f_security;
+ if (blob) {
+ file->f_security = NULL;
+ kmem_cache_free(lsm_file_cache, blob);
+ }
}
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -988,27 +1503,63 @@
int security_task_alloc(struct task_struct *task, unsigned long clone_flags)
{
- return call_int_hook(task_alloc, 0, task, clone_flags);
+ int rc = lsm_task_alloc(task);
+
+ if (rc)
+ return rc;
+ rc = call_int_hook(task_alloc, 0, task, clone_flags);
+ if (unlikely(rc))
+ security_task_free(task);
+ return rc;
}
void security_task_free(struct task_struct *task)
{
call_void_hook(task_free, task);
+
+ kfree(task->security);
+ task->security = NULL;
}
int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
- return call_int_hook(cred_alloc_blank, 0, cred, gfp);
+ int rc = lsm_cred_alloc(cred, gfp);
+
+ if (rc)
+ return rc;
+
+ rc = call_int_hook(cred_alloc_blank, 0, cred, gfp);
+ if (unlikely(rc))
+ security_cred_free(cred);
+ return rc;
}
void security_cred_free(struct cred *cred)
{
+ /*
+ * There is a failure case in prepare_creds() that
+ * may result in a call here with ->security being NULL.
+ */
+ if (unlikely(cred->security == NULL))
+ return;
+
call_void_hook(cred_free, cred);
+
+ kfree(cred->security);
+ cred->security = NULL;
}
int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
{
- return call_int_hook(cred_prepare, 0, new, old, gfp);
+ int rc = lsm_cred_alloc(new, gfp);
+
+ if (rc)
+ return rc;
+
+ rc = call_int_hook(cred_prepare, 0, new, old, gfp);
+ if (unlikely(rc))
+ security_cred_free(new);
+ return rc;
}
void security_transfer_creds(struct cred *new, const struct cred *old)
@@ -1147,7 +1698,7 @@
return call_int_hook(task_movememory, 0, p);
}
-int security_task_kill(struct task_struct *p, struct siginfo *info,
+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);
@@ -1189,22 +1740,40 @@
int security_msg_msg_alloc(struct msg_msg *msg)
{
- return call_int_hook(msg_msg_alloc_security, 0, msg);
+ int rc = lsm_msg_msg_alloc(msg);
+
+ if (unlikely(rc))
+ return rc;
+ rc = call_int_hook(msg_msg_alloc_security, 0, msg);
+ if (unlikely(rc))
+ security_msg_msg_free(msg);
+ return rc;
}
void security_msg_msg_free(struct msg_msg *msg)
{
call_void_hook(msg_msg_free_security, msg);
+ kfree(msg->security);
+ msg->security = NULL;
}
int security_msg_queue_alloc(struct kern_ipc_perm *msq)
{
- return call_int_hook(msg_queue_alloc_security, 0, msq);
+ int rc = lsm_ipc_alloc(msq);
+
+ if (unlikely(rc))
+ return rc;
+ rc = call_int_hook(msg_queue_alloc_security, 0, msq);
+ if (unlikely(rc))
+ security_msg_queue_free(msq);
+ return rc;
}
void security_msg_queue_free(struct kern_ipc_perm *msq)
{
call_void_hook(msg_queue_free_security, msq);
+ kfree(msq->security);
+ msq->security = NULL;
}
int security_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg)
@@ -1231,12 +1800,21 @@
int security_shm_alloc(struct kern_ipc_perm *shp)
{
- return call_int_hook(shm_alloc_security, 0, shp);
+ int rc = lsm_ipc_alloc(shp);
+
+ if (unlikely(rc))
+ return rc;
+ rc = call_int_hook(shm_alloc_security, 0, shp);
+ if (unlikely(rc))
+ security_shm_free(shp);
+ return rc;
}
void security_shm_free(struct kern_ipc_perm *shp)
{
call_void_hook(shm_free_security, shp);
+ kfree(shp->security);
+ shp->security = NULL;
}
int security_shm_associate(struct kern_ipc_perm *shp, int shmflg)
@@ -1256,12 +1834,21 @@
int security_sem_alloc(struct kern_ipc_perm *sma)
{
- return call_int_hook(sem_alloc_security, 0, sma);
+ int rc = lsm_ipc_alloc(sma);
+
+ if (unlikely(rc))
+ return rc;
+ rc = call_int_hook(sem_alloc_security, 0, sma);
+ if (unlikely(rc))
+ security_sem_free(sma);
+ return rc;
}
void security_sem_free(struct kern_ipc_perm *sma)
{
call_void_hook(sem_free_security, sma);
+ kfree(sma->security);
+ sma->security = NULL;
}
int security_sem_associate(struct kern_ipc_perm *sma, int semflg)
@@ -1288,14 +1875,30 @@
}
EXPORT_SYMBOL(security_d_instantiate);
-int security_getprocattr(struct task_struct *p, char *name, char **value)
+int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
+ char **value)
{
- return call_int_hook(getprocattr, -EINVAL, p, name, value);
+ struct security_hook_list *hp;
+
+ hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
+ if (lsm != NULL && strcmp(lsm, hp->lsm))
+ continue;
+ return hp->hook.getprocattr(p, name, value);
+ }
+ return -EINVAL;
}
-int security_setprocattr(const char *name, void *value, size_t size)
+int security_setprocattr(const char *lsm, const char *name, void *value,
+ size_t size)
{
- return call_int_hook(setprocattr, -EINVAL, name, value, size);
+ struct security_hook_list *hp;
+
+ hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
+ if (lsm != NULL && strcmp(lsm, hp->lsm))
+ continue;
+ return hp->hook.setprocattr(name, value, size);
+ }
+ return -EINVAL;
}
int security_netlink_send(struct sock *sk, struct sk_buff *skb)
@@ -1759,11 +2362,9 @@
call_void_hook(audit_rule_free, lsmrule);
}
-int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
- struct audit_context *actx)
+int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule)
{
- return call_int_hook(audit_rule_match, 0, secid, field, op, lsmrule,
- actx);
+ return call_int_hook(audit_rule_match, 0, secid, field, op, lsmrule);
}
#endif /* CONFIG_AUDIT */
@@ -1797,3 +2398,9 @@
call_void_hook(bpf_prog_free_security, aux);
}
#endif /* CONFIG_BPF_SYSCALL */
+
+int security_locked_down(enum lockdown_reason what)
+{
+ return call_int_hook(locked_down, 0, what);
+}
+EXPORT_SYMBOL(security_locked_down);
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
index 8af7a69..5711689 100644
--- a/security/selinux/Kconfig
+++ b/security/selinux/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SECURITY_SELINUX
bool "NSA SELinux Support"
depends on SECURITY_NETWORK && AUDIT && NET && INET
@@ -22,21 +23,6 @@
If you are unsure how to answer this question, answer N.
-config SECURITY_SELINUX_BOOTPARAM_VALUE
- int "NSA SELinux boot parameter default value"
- depends on SECURITY_SELINUX_BOOTPARAM
- range 0 1
- default 1
- help
- This option sets the default value for the kernel parameter
- 'selinux', which allows SELinux to be disabled at boot. If this
- option is set to 0 (zero), the SELinux kernel parameter will
- default to 0, disabling SELinux at bootup. If this option is
- set to 1 (one), the SELinux kernel parameter will default to 1,
- enabling SELinux at bootup.
-
- If you are unsure how to answer this question, answer 1.
-
config SECURITY_SELINUX_DISABLE
bool "NSA SELinux runtime disable"
depends on SECURITY_SELINUX
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index c7161f8..ccf9504 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -6,7 +6,7 @@
obj-$(CONFIG_SECURITY_SELINUX) := selinux.o
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
- netnode.o netport.o ibpkey.o exports.o \
+ netnode.o netport.o ibpkey.o \
ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \
ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/status.o
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 635e5c1..ecd3829 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Implementation of the kernel access vector cache (AVC).
*
@@ -8,10 +9,6 @@
* Replaced the avc_lock spinlock by RCU.
*
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/stddef.h>
@@ -130,75 +127,6 @@
}
/**
- * avc_dump_av - Display an access vector in human-readable form.
- * @tclass: target security class
- * @av: access vector
- */
-static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
-{
- const char **perms;
- int i, perm;
-
- if (av == 0) {
- audit_log_format(ab, " null");
- return;
- }
-
- BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map));
- perms = secclass_map[tclass-1].perms;
-
- audit_log_format(ab, " {");
- i = 0;
- perm = 1;
- while (i < (sizeof(av) * 8)) {
- if ((perm & av) && perms[i]) {
- audit_log_format(ab, " %s", perms[i]);
- av &= ~perm;
- }
- i++;
- perm <<= 1;
- }
-
- if (av)
- audit_log_format(ab, " 0x%x", av);
-
- audit_log_format(ab, " }");
-}
-
-/**
- * avc_dump_query - Display a SID pair and a class in human-readable form.
- * @ssid: source security identifier
- * @tsid: target security identifier
- * @tclass: target security class
- */
-static void avc_dump_query(struct audit_buffer *ab, struct selinux_state *state,
- u32 ssid, u32 tsid, u16 tclass)
-{
- int rc;
- char *scontext;
- u32 scontext_len;
-
- rc = security_sid_to_context(state, ssid, &scontext, &scontext_len);
- if (rc)
- audit_log_format(ab, "ssid=%d", ssid);
- else {
- audit_log_format(ab, "scontext=%s", scontext);
- kfree(scontext);
- }
-
- rc = security_sid_to_context(state, tsid, &scontext, &scontext_len);
- if (rc)
- audit_log_format(ab, " tsid=%d", tsid);
- else {
- audit_log_format(ab, " tcontext=%s", scontext);
- kfree(scontext);
- }
-
- BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map));
- audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name);
-}
-
-/**
* avc_init - Initialize the AVC.
*
* Initialize the access vector cache.
@@ -735,11 +663,36 @@
static void avc_audit_pre_callback(struct audit_buffer *ab, void *a)
{
struct common_audit_data *ad = a;
- audit_log_format(ab, "avc: %s ",
- ad->selinux_audit_data->denied ? "denied" : "granted");
- avc_dump_av(ab, ad->selinux_audit_data->tclass,
- ad->selinux_audit_data->audited);
- audit_log_format(ab, " for ");
+ struct selinux_audit_data *sad = ad->selinux_audit_data;
+ u32 av = sad->audited;
+ const char **perms;
+ int i, perm;
+
+ audit_log_format(ab, "avc: %s ", sad->denied ? "denied" : "granted");
+
+ if (av == 0) {
+ audit_log_format(ab, " null");
+ return;
+ }
+
+ perms = secclass_map[sad->tclass-1].perms;
+
+ audit_log_format(ab, " {");
+ i = 0;
+ perm = 1;
+ while (i < (sizeof(av) * 8)) {
+ if ((perm & av) && perms[i]) {
+ audit_log_format(ab, " %s", perms[i]);
+ av &= ~perm;
+ }
+ i++;
+ perm <<= 1;
+ }
+
+ if (av)
+ audit_log_format(ab, " 0x%x", av);
+
+ audit_log_format(ab, " } for ");
}
/**
@@ -751,14 +704,53 @@
static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
{
struct common_audit_data *ad = a;
- audit_log_format(ab, " ");
- avc_dump_query(ab, ad->selinux_audit_data->state,
- ad->selinux_audit_data->ssid,
- ad->selinux_audit_data->tsid,
- ad->selinux_audit_data->tclass);
- if (ad->selinux_audit_data->denied) {
- audit_log_format(ab, " permissive=%u",
- ad->selinux_audit_data->result ? 0 : 1);
+ struct selinux_audit_data *sad = ad->selinux_audit_data;
+ char *scontext;
+ u32 scontext_len;
+ int rc;
+
+ rc = security_sid_to_context(sad->state, sad->ssid, &scontext,
+ &scontext_len);
+ if (rc)
+ audit_log_format(ab, " ssid=%d", sad->ssid);
+ else {
+ audit_log_format(ab, " scontext=%s", scontext);
+ kfree(scontext);
+ }
+
+ rc = security_sid_to_context(sad->state, sad->tsid, &scontext,
+ &scontext_len);
+ if (rc)
+ audit_log_format(ab, " tsid=%d", sad->tsid);
+ else {
+ audit_log_format(ab, " tcontext=%s", scontext);
+ kfree(scontext);
+ }
+
+ audit_log_format(ab, " tclass=%s", secclass_map[sad->tclass-1].name);
+
+ if (sad->denied)
+ audit_log_format(ab, " permissive=%u", sad->result ? 0 : 1);
+
+ /* in case of invalid context report also the actual context string */
+ rc = security_sid_to_context_inval(sad->state, sad->ssid, &scontext,
+ &scontext_len);
+ if (!rc && scontext) {
+ if (scontext_len && scontext[scontext_len - 1] == '\0')
+ scontext_len--;
+ audit_log_format(ab, " srawcon=");
+ audit_log_n_untrustedstring(ab, scontext, scontext_len);
+ kfree(scontext);
+ }
+
+ rc = security_sid_to_context_inval(sad->state, sad->tsid, &scontext,
+ &scontext_len);
+ if (!rc && scontext) {
+ if (scontext_len && scontext[scontext_len - 1] == '\0')
+ scontext_len--;
+ audit_log_format(ab, " trawcon=");
+ audit_log_n_untrustedstring(ab, scontext, scontext_len);
+ kfree(scontext);
}
}
@@ -772,6 +764,9 @@
struct common_audit_data stack_data;
struct selinux_audit_data sad;
+ if (WARN_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map)))
+ return -EINVAL;
+
if (!a) {
a = &stack_data;
a->type = LSM_AUDIT_DATA_NONE;
@@ -838,6 +833,7 @@
* @ssid,@tsid,@tclass : identifier of an AVC entry
* @seqno : sequence number when decision was made
* @xpd: extended_perms_decision to be added to the node
+ * @flags: the AVC_* flags, e.g. AVC_NONBLOCKING, AVC_EXTENDED_PERMS, or 0.
*
* if a valid AVC entry doesn't exist,this function returns -ENOENT.
* if kmalloc() called internal returns NULL, this function returns -ENOMEM.
@@ -856,6 +852,22 @@
struct hlist_head *head;
spinlock_t *lock;
+ /*
+ * If we are in a non-blocking code path, e.g. VFS RCU walk,
+ * then we must not add permissions to a cache entry
+ * because we cannot safely audit the denial. Otherwise,
+ * during the subsequent blocking retry (e.g. VFS ref walk), we
+ * will find the permissions already granted in the cache entry
+ * and won't audit anything at all, leading to silent denials in
+ * permissive mode that only appear when in enforcing mode.
+ *
+ * See the corresponding handling in slow_avc_audit(), and the
+ * logic in selinux_inode_permission for the MAY_NOT_BLOCK flag,
+ * which is transliterated into AVC_NONBLOCKING.
+ */
+ if (flags & AVC_NONBLOCKING)
+ return 0;
+
node = avc_alloc_node(avc);
if (!node) {
rc = -ENOMEM;
@@ -1050,7 +1062,8 @@
int rc = 0, rc2;
xp_node = &local_xp_node;
- BUG_ON(!requested);
+ if (WARN_ON(!requested))
+ return -EACCES;
rcu_read_lock();
@@ -1115,7 +1128,7 @@
* @tsid: target security identifier
* @tclass: target security class
* @requested: requested permissions, interpreted based on @tclass
- * @flags: AVC_STRICT or 0
+ * @flags: AVC_STRICT, AVC_NONBLOCKING, or 0
* @avd: access vector decisions
*
* Check the AVC to determine whether the @requested permissions are granted
@@ -1140,7 +1153,8 @@
int rc = 0;
u32 denied;
- BUG_ON(!requested);
+ if (WARN_ON(!requested))
+ return -EACCES;
rcu_read_lock();
@@ -1191,24 +1205,6 @@
return rc;
}
-int avc_has_perm_flags(struct selinux_state *state,
- u32 ssid, u32 tsid, u16 tclass, u32 requested,
- struct common_audit_data *auditdata,
- int flags)
-{
- struct av_decision avd;
- int rc, rc2;
-
- rc = avc_has_perm_noaudit(state, ssid, tsid, tclass, requested, 0,
- &avd);
-
- rc2 = avc_audit(state, ssid, tsid, tclass, requested, &avd, rc,
- auditdata, flags);
- if (rc2)
- return rc2;
- return rc;
-}
-
u32 avc_policy_seqno(struct selinux_state *state)
{
return state->avc->avc_cache.latest_notif;
diff --git a/security/selinux/exports.c b/security/selinux/exports.c
deleted file mode 100644
index e75dd94..0000000
--- a/security/selinux/exports.c
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SELinux services exported to the rest of the kernel.
- *
- * Author: James Morris <jmorris@redhat.com>
- *
- * Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com>
- * Copyright (C) 2006 Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
- * Copyright (C) 2006 IBM Corporation, Timothy R. Chavez <tinytim@us.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- */
-#include <linux/module.h>
-#include <linux/selinux.h>
-
-#include "security.h"
-
-bool selinux_is_enabled(void)
-{
- return selinux_enabled;
-}
-EXPORT_SYMBOL_GPL(selinux_is_enabled);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index fe251c6..9625b99 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* NSA Security-Enhanced Linux (SELinux) security module
*
@@ -18,10 +19,6 @@
* Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
* Yuichi Nakamura <ynakam@hitachisoft.jp>
* Copyright (C) 2016 Mellanox Technologies
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#include <linux/init.h>
@@ -48,6 +45,8 @@
#include <linux/fdtable.h>
#include <linux/namei.h>
#include <linux/mount.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/tty.h>
@@ -79,7 +78,6 @@
#include <linux/personality.h>
#include <linux/audit.h>
#include <linux/string.h>
-#include <linux/selinux.h>
#include <linux/mutex.h>
#include <linux/posix-timers.h>
#include <linux/syslog.h>
@@ -88,6 +86,11 @@
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/bpf.h>
+#include <linux/kernfs.h>
+#include <linux/stringhash.h> /* for hashlen_string() */
+#include <uapi/linux/mount.h>
+#include <linux/fsnotify.h>
+#include <linux/fanotify.h>
#include "avc.h"
#include "objsec.h"
@@ -120,9 +123,8 @@
#define selinux_enforcing_boot 1
#endif
+int selinux_enabled __lsm_ro_after_init = 1;
#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
-int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
-
static int __init selinux_enabled_setup(char *str)
{
unsigned long enabled;
@@ -131,8 +133,6 @@
return 1;
}
__setup("selinux=", selinux_enabled_setup);
-#else
-int selinux_enabled = 1;
#endif
static unsigned int selinux_checkreqprot_boot =
@@ -148,9 +148,6 @@
}
__setup("checkreqprot=", checkreqprot_setup);
-static struct kmem_cache *sel_inode_cache;
-static struct kmem_cache *file_security_cache;
-
/**
* selinux_secmark_enabled - Check to see if SECMARK is currently enabled
*
@@ -199,7 +196,7 @@
{
if (event == AVC_CALLBACK_RESET) {
sel_ib_pkey_flush();
- call_lsm_notifier(LSM_POLICY_CHANGE, NULL);
+ call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL);
}
return 0;
@@ -213,12 +210,8 @@
struct cred *cred = (struct cred *) current->real_cred;
struct task_security_struct *tsec;
- tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
- if (!tsec)
- panic("SELinux: Failed to initialize initial task.\n");
-
+ tsec = selinux_cred(cred);
tsec->osid = tsec->sid = SECINITSID_KERNEL;
- cred->security = tsec;
}
/*
@@ -228,7 +221,7 @@
{
const struct task_security_struct *tsec;
- tsec = cred->security;
+ tsec = selinux_cred(cred);
return tsec->sid;
}
@@ -249,13 +242,9 @@
static int inode_alloc_security(struct inode *inode)
{
- struct inode_security_struct *isec;
+ struct inode_security_struct *isec = selinux_inode(inode);
u32 sid = current_sid();
- isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);
- if (!isec)
- return -ENOMEM;
-
spin_lock_init(&isec->lock);
INIT_LIST_HEAD(&isec->list);
isec->inode = inode;
@@ -263,7 +252,6 @@
isec->sclass = SECCLASS_FILE;
isec->task_sid = sid;
isec->initialized = LABEL_INVALID;
- inode->i_security = isec;
return 0;
}
@@ -280,7 +268,7 @@
struct dentry *dentry,
bool may_sleep)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
might_sleep_if(may_sleep);
@@ -301,7 +289,7 @@
static struct inode_security_struct *inode_security_novalidate(struct inode *inode)
{
- return inode->i_security;
+ return selinux_inode(inode);
}
static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu)
@@ -311,7 +299,7 @@
error = __inode_security_revalidate(inode, NULL, !rcu);
if (error)
return ERR_PTR(error);
- return inode->i_security;
+ return selinux_inode(inode);
}
/*
@@ -320,14 +308,14 @@
static struct inode_security_struct *inode_security(struct inode *inode)
{
__inode_security_revalidate(inode, NULL, true);
- return inode->i_security;
+ return selinux_inode(inode);
}
static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
- return inode->i_security;
+ return selinux_inode(inode);
}
/*
@@ -338,22 +326,17 @@
struct inode *inode = d_backing_inode(dentry);
__inode_security_revalidate(inode, dentry, true);
- return inode->i_security;
-}
-
-static void inode_free_rcu(struct rcu_head *head)
-{
- struct inode_security_struct *isec;
-
- isec = container_of(head, struct inode_security_struct, rcu);
- kmem_cache_free(sel_inode_cache, isec);
+ return selinux_inode(inode);
}
static void inode_free_security(struct inode *inode)
{
- struct inode_security_struct *isec = inode->i_security;
- struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
+ struct superblock_security_struct *sbsec;
+ if (!isec)
+ return;
+ sbsec = inode->i_sb->s_security;
/*
* As not all inode security structures are in a list, we check for
* empty list outside of the lock to make sure that we won't waste
@@ -369,42 +352,19 @@
list_del_init(&isec->list);
spin_unlock(&sbsec->isec_lock);
}
-
- /*
- * The inode may still be referenced in a path walk and
- * a call to selinux_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.
- */
- call_rcu(&isec->rcu, inode_free_rcu);
}
static int file_alloc_security(struct file *file)
{
- struct file_security_struct *fsec;
+ struct file_security_struct *fsec = selinux_file(file);
u32 sid = current_sid();
- fsec = kmem_cache_zalloc(file_security_cache, GFP_KERNEL);
- if (!fsec)
- return -ENOMEM;
-
fsec->sid = sid;
fsec->fown_sid = sid;
- file->f_security = fsec;
return 0;
}
-static void file_free_security(struct file *file)
-{
- struct file_security_struct *fsec = file->f_security;
- file->f_security = NULL;
- kmem_cache_free(file_security_cache, fsec);
-}
-
static int superblock_alloc_security(struct super_block *sb)
{
struct superblock_security_struct *sbsec;
@@ -432,6 +392,20 @@
kfree(sbsec);
}
+struct selinux_mnt_opts {
+ const char *fscontext, *context, *rootcontext, *defcontext;
+};
+
+static void selinux_free_mnt_opts(void *mnt_opts)
+{
+ struct selinux_mnt_opts *opts = mnt_opts;
+ kfree(opts->fscontext);
+ kfree(opts->context);
+ kfree(opts->rootcontext);
+ kfree(opts->defcontext);
+ kfree(opts);
+}
+
static inline int inode_doinit(struct inode *inode)
{
return inode_doinit_with_dentry(inode, NULL);
@@ -439,24 +413,46 @@
enum {
Opt_error = -1,
- Opt_context = 1,
+ Opt_context = 0,
+ Opt_defcontext = 1,
Opt_fscontext = 2,
- Opt_defcontext = 3,
- Opt_rootcontext = 4,
- Opt_labelsupport = 5,
- Opt_nextmntopt = 6,
+ Opt_rootcontext = 3,
+ Opt_seclabel = 4,
};
-#define NUM_SEL_MNT_OPTS (Opt_nextmntopt - 1)
-
-static const match_table_t tokens = {
- {Opt_context, CONTEXT_STR "%s"},
- {Opt_fscontext, FSCONTEXT_STR "%s"},
- {Opt_defcontext, DEFCONTEXT_STR "%s"},
- {Opt_rootcontext, ROOTCONTEXT_STR "%s"},
- {Opt_labelsupport, LABELSUPP_STR},
- {Opt_error, NULL},
+#define A(s, has_arg) {#s, sizeof(#s) - 1, Opt_##s, has_arg}
+static struct {
+ const char *name;
+ int len;
+ int opt;
+ bool has_arg;
+} tokens[] = {
+ A(context, true),
+ A(fscontext, true),
+ A(defcontext, true),
+ A(rootcontext, true),
+ A(seclabel, false),
};
+#undef A
+
+static int match_opt_prefix(char *s, int l, char **arg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tokens); i++) {
+ size_t len = tokens[i].len;
+ if (len > l || memcmp(s, tokens[i].name, len))
+ continue;
+ if (tokens[i].has_arg) {
+ if (len == l || s[len] != '=')
+ continue;
+ *arg = s + len + 1;
+ } else if (len != l)
+ continue;
+ return tokens[i].opt;
+ }
+ return Opt_error;
+}
#define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n"
@@ -464,7 +460,7 @@
struct superblock_security_struct *sbsec,
const struct cred *cred)
{
- const struct task_security_struct *tsec = cred->security;
+ const struct task_security_struct *tsec = selinux_cred(cred);
int rc;
rc = avc_has_perm(&selinux_state,
@@ -483,7 +479,7 @@
struct superblock_security_struct *sbsec,
const struct cred *cred)
{
- const struct task_security_struct *tsec = cred->security;
+ const struct task_security_struct *tsec = selinux_cred(cred);
int rc;
rc = avc_has_perm(&selinux_state,
tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
@@ -497,16 +493,10 @@
return rc;
}
-static int selinux_is_sblabel_mnt(struct super_block *sb)
+static int selinux_is_genfs_special_handling(struct super_block *sb)
{
- struct superblock_security_struct *sbsec = sb->s_security;
-
- return sbsec->behavior == SECURITY_FS_USE_XATTR ||
- sbsec->behavior == SECURITY_FS_USE_TRANS ||
- sbsec->behavior == SECURITY_FS_USE_TASK ||
- sbsec->behavior == SECURITY_FS_USE_NATIVE ||
- /* Special handling. Genfs but also in-core setxattr handler */
- !strcmp(sb->s_type->name, "sysfs") ||
+ /* Special handling. Genfs but also in-core setxattr handler */
+ return !strcmp(sb->s_type->name, "sysfs") ||
!strcmp(sb->s_type->name, "pstore") ||
!strcmp(sb->s_type->name, "debugfs") ||
!strcmp(sb->s_type->name, "tracefs") ||
@@ -516,6 +506,34 @@
!strcmp(sb->s_type->name, "cgroup2")));
}
+static int selinux_is_sblabel_mnt(struct super_block *sb)
+{
+ struct superblock_security_struct *sbsec = sb->s_security;
+
+ /*
+ * IMPORTANT: Double-check logic in this function when adding a new
+ * SECURITY_FS_USE_* definition!
+ */
+ BUILD_BUG_ON(SECURITY_FS_USE_MAX != 7);
+
+ switch (sbsec->behavior) {
+ case SECURITY_FS_USE_XATTR:
+ case SECURITY_FS_USE_TRANS:
+ case SECURITY_FS_USE_TASK:
+ case SECURITY_FS_USE_NATIVE:
+ return 1;
+
+ case SECURITY_FS_USE_GENFS:
+ return selinux_is_genfs_special_handling(sb);
+
+ /* Never allow relabeling on context mounts */
+ case SECURITY_FS_USE_MNTPOINT:
+ case SECURITY_FS_USE_NONE:
+ default:
+ return 0;
+ }
+}
+
static int sb_finish_set_opts(struct super_block *sb)
{
struct superblock_security_struct *sbsec = sb->s_security;
@@ -570,10 +588,9 @@
during get_sb by a pseudo filesystem that directly
populates itself. */
spin_lock(&sbsec->isec_lock);
-next_inode:
- if (!list_empty(&sbsec->isec_head)) {
+ while (!list_empty(&sbsec->isec_head)) {
struct inode_security_struct *isec =
- list_entry(sbsec->isec_head.next,
+ list_first_entry(&sbsec->isec_head,
struct inode_security_struct, list);
struct inode *inode = isec->inode;
list_del_init(&isec->list);
@@ -585,112 +602,12 @@
iput(inode);
}
spin_lock(&sbsec->isec_lock);
- goto next_inode;
}
spin_unlock(&sbsec->isec_lock);
out:
return rc;
}
-/*
- * This function should allow an FS to ask what it's mount security
- * options were so it can use those later for submounts, displaying
- * mount options, or whatever.
- */
-static int selinux_get_mnt_opts(const struct super_block *sb,
- struct security_mnt_opts *opts)
-{
- int rc = 0, i;
- struct superblock_security_struct *sbsec = sb->s_security;
- char *context = NULL;
- u32 len;
- char tmp;
-
- security_init_mnt_opts(opts);
-
- if (!(sbsec->flags & SE_SBINITIALIZED))
- return -EINVAL;
-
- if (!selinux_state.initialized)
- return -EINVAL;
-
- /* make sure we always check enough bits to cover the mask */
- BUILD_BUG_ON(SE_MNTMASK >= (1 << NUM_SEL_MNT_OPTS));
-
- tmp = sbsec->flags & SE_MNTMASK;
- /* count the number of mount options for this sb */
- for (i = 0; i < NUM_SEL_MNT_OPTS; i++) {
- if (tmp & 0x01)
- opts->num_mnt_opts++;
- tmp >>= 1;
- }
- /* Check if the Label support flag is set */
- if (sbsec->flags & SBLABEL_MNT)
- opts->num_mnt_opts++;
-
- opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
- if (!opts->mnt_opts) {
- rc = -ENOMEM;
- goto out_free;
- }
-
- opts->mnt_opts_flags = kcalloc(opts->num_mnt_opts, sizeof(int), GFP_ATOMIC);
- if (!opts->mnt_opts_flags) {
- rc = -ENOMEM;
- goto out_free;
- }
-
- i = 0;
- if (sbsec->flags & FSCONTEXT_MNT) {
- rc = security_sid_to_context(&selinux_state, sbsec->sid,
- &context, &len);
- if (rc)
- goto out_free;
- opts->mnt_opts[i] = context;
- opts->mnt_opts_flags[i++] = FSCONTEXT_MNT;
- }
- if (sbsec->flags & CONTEXT_MNT) {
- rc = security_sid_to_context(&selinux_state,
- sbsec->mntpoint_sid,
- &context, &len);
- if (rc)
- goto out_free;
- opts->mnt_opts[i] = context;
- opts->mnt_opts_flags[i++] = CONTEXT_MNT;
- }
- if (sbsec->flags & DEFCONTEXT_MNT) {
- rc = security_sid_to_context(&selinux_state, sbsec->def_sid,
- &context, &len);
- if (rc)
- goto out_free;
- opts->mnt_opts[i] = context;
- opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT;
- }
- if (sbsec->flags & ROOTCONTEXT_MNT) {
- struct dentry *root = sbsec->sb->s_root;
- struct inode_security_struct *isec = backing_inode_security(root);
-
- rc = security_sid_to_context(&selinux_state, isec->sid,
- &context, &len);
- if (rc)
- goto out_free;
- opts->mnt_opts[i] = context;
- opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
- }
- if (sbsec->flags & SBLABEL_MNT) {
- opts->mnt_opts[i] = NULL;
- opts->mnt_opts_flags[i++] = SBLABEL_MNT;
- }
-
- BUG_ON(i != opts->num_mnt_opts);
-
- return 0;
-
-out_free:
- security_free_mnt_opts(opts);
- return rc;
-}
-
static int bad_option(struct superblock_security_struct *sbsec, char flag,
u32 old_sid, u32 new_sid)
{
@@ -711,31 +628,39 @@
return 0;
}
+static int parse_sid(struct super_block *sb, const char *s, u32 *sid)
+{
+ int rc = security_context_str_to_sid(&selinux_state, s,
+ sid, GFP_KERNEL);
+ if (rc)
+ pr_warn("SELinux: security_context_str_to_sid"
+ "(%s) failed for (dev %s, type %s) errno=%d\n",
+ s, sb->s_id, sb->s_type->name, rc);
+ return rc;
+}
+
/*
* Allow filesystems with binary mount data to explicitly set mount point
* labeling information.
*/
static int selinux_set_mnt_opts(struct super_block *sb,
- struct security_mnt_opts *opts,
+ void *mnt_opts,
unsigned long kern_flags,
unsigned long *set_kern_flags)
{
const struct cred *cred = current_cred();
- int rc = 0, i;
struct superblock_security_struct *sbsec = sb->s_security;
- const char *name = sb->s_type->name;
struct dentry *root = sbsec->sb->s_root;
+ struct selinux_mnt_opts *opts = mnt_opts;
struct inode_security_struct *root_isec;
u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
u32 defcontext_sid = 0;
- char **mount_options = opts->mnt_opts;
- int *flags = opts->mnt_opts_flags;
- int num_opts = opts->num_mnt_opts;
+ int rc = 0;
mutex_lock(&sbsec->lock);
if (!selinux_state.initialized) {
- if (!num_opts) {
+ if (!opts) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
server is ready to handle calls. */
@@ -765,7 +690,7 @@
* will be used for both mounts)
*/
if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
- && (num_opts == 0))
+ && !opts)
goto out;
root_isec = backing_inode_security_novalidate(root);
@@ -775,68 +700,48 @@
* also check if someone is trying to mount the same sb more
* than once with different security options.
*/
- for (i = 0; i < num_opts; i++) {
- u32 sid;
-
- if (flags[i] == SBLABEL_MNT)
- continue;
- rc = security_context_str_to_sid(&selinux_state,
- mount_options[i], &sid,
- GFP_KERNEL);
- if (rc) {
- pr_warn("SELinux: security_context_str_to_sid"
- "(%s) failed for (dev %s, type %s) errno=%d\n",
- mount_options[i], sb->s_id, name, rc);
- goto out;
- }
- switch (flags[i]) {
- case FSCONTEXT_MNT:
- fscontext_sid = sid;
-
+ if (opts) {
+ if (opts->fscontext) {
+ rc = parse_sid(sb, opts->fscontext, &fscontext_sid);
+ if (rc)
+ goto out;
if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid,
fscontext_sid))
goto out_double_mount;
-
sbsec->flags |= FSCONTEXT_MNT;
- break;
- case CONTEXT_MNT:
- context_sid = sid;
-
+ }
+ if (opts->context) {
+ rc = parse_sid(sb, opts->context, &context_sid);
+ if (rc)
+ goto out;
if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
context_sid))
goto out_double_mount;
-
sbsec->flags |= CONTEXT_MNT;
- break;
- case ROOTCONTEXT_MNT:
- rootcontext_sid = sid;
-
+ }
+ if (opts->rootcontext) {
+ rc = parse_sid(sb, opts->rootcontext, &rootcontext_sid);
+ if (rc)
+ goto out;
if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
rootcontext_sid))
goto out_double_mount;
-
sbsec->flags |= ROOTCONTEXT_MNT;
-
- break;
- case DEFCONTEXT_MNT:
- defcontext_sid = sid;
-
+ }
+ if (opts->defcontext) {
+ rc = parse_sid(sb, opts->defcontext, &defcontext_sid);
+ if (rc)
+ goto out;
if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
defcontext_sid))
goto out_double_mount;
-
sbsec->flags |= DEFCONTEXT_MNT;
-
- break;
- default:
- rc = -EINVAL;
- goto out;
}
}
if (sbsec->flags & SE_SBINITIALIZED) {
/* previously mounted with options, but not on this attempt? */
- if ((sbsec->flags & SE_MNTMASK) && !num_opts)
+ if ((sbsec->flags & SE_MNTMASK) && !opts)
goto out_double_mount;
rc = 0;
goto out;
@@ -847,11 +752,13 @@
if (!strcmp(sb->s_type->name, "debugfs") ||
!strcmp(sb->s_type->name, "tracefs") ||
- !strcmp(sb->s_type->name, "sysfs") ||
- !strcmp(sb->s_type->name, "pstore") ||
+ !strcmp(sb->s_type->name, "pstore"))
+ sbsec->flags |= SE_SBGENFS;
+
+ if (!strcmp(sb->s_type->name, "sysfs") ||
!strcmp(sb->s_type->name, "cgroup") ||
!strcmp(sb->s_type->name, "cgroup2"))
- sbsec->flags |= SE_SBGENFS;
+ sbsec->flags |= SE_SBGENFS | SE_SBGENFS_XATTR;
if (!sbsec->behavior) {
/*
@@ -969,7 +876,8 @@
out_double_mount:
rc = -EINVAL;
pr_warn("SELinux: mount invalid. Same superblock, different "
- "security settings for (dev %s, type %s)\n", sb->s_id, name);
+ "security settings for (dev %s, type %s)\n", sb->s_id,
+ sb->s_type->name);
goto out;
}
@@ -1034,8 +942,11 @@
BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
/* if fs is reusing a sb, make sure that the contexts match */
- if (newsbsec->flags & SE_SBINITIALIZED)
+ if (newsbsec->flags & SE_SBINITIALIZED) {
+ if ((kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context)
+ *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
return selinux_cmp_sb_context(oldsb, newsb);
+ }
mutex_lock(&newsbsec->lock);
@@ -1081,218 +992,155 @@
return rc;
}
-static int selinux_parse_opts_str(char *options,
- struct security_mnt_opts *opts)
+static int selinux_add_opt(int token, const char *s, void **mnt_opts)
{
- char *p;
- char *context = NULL, *defcontext = NULL;
- char *fscontext = NULL, *rootcontext = NULL;
- int rc, num_mnt_opts = 0;
+ struct selinux_mnt_opts *opts = *mnt_opts;
- opts->num_mnt_opts = 0;
+ if (token == Opt_seclabel) /* eaten and completely ignored */
+ return 0;
- /* Standard string-based options. */
- while ((p = strsep(&options, "|")) != NULL) {
- int token;
- substring_t args[MAX_OPT_ARGS];
+ if (!opts) {
+ opts = kzalloc(sizeof(struct selinux_mnt_opts), GFP_KERNEL);
+ if (!opts)
+ return -ENOMEM;
+ *mnt_opts = opts;
+ }
+ if (!s)
+ return -ENOMEM;
+ switch (token) {
+ case Opt_context:
+ if (opts->context || opts->defcontext)
+ goto Einval;
+ opts->context = s;
+ break;
+ case Opt_fscontext:
+ if (opts->fscontext)
+ goto Einval;
+ opts->fscontext = s;
+ break;
+ case Opt_rootcontext:
+ if (opts->rootcontext)
+ goto Einval;
+ opts->rootcontext = s;
+ break;
+ case Opt_defcontext:
+ if (opts->context || opts->defcontext)
+ goto Einval;
+ opts->defcontext = s;
+ break;
+ }
+ return 0;
+Einval:
+ pr_warn(SEL_MOUNT_FAIL_MSG);
+ return -EINVAL;
+}
- if (!*p)
- continue;
+static int selinux_add_mnt_opt(const char *option, const char *val, int len,
+ void **mnt_opts)
+{
+ int token = Opt_error;
+ int rc, i;
- token = match_token(p, tokens, args);
-
- switch (token) {
- case Opt_context:
- if (context || defcontext) {
- rc = -EINVAL;
- pr_warn(SEL_MOUNT_FAIL_MSG);
- goto out_err;
- }
- context = match_strdup(&args[0]);
- if (!context) {
- rc = -ENOMEM;
- goto out_err;
- }
+ for (i = 0; i < ARRAY_SIZE(tokens); i++) {
+ if (strcmp(option, tokens[i].name) == 0) {
+ token = tokens[i].opt;
break;
-
- case Opt_fscontext:
- if (fscontext) {
- rc = -EINVAL;
- pr_warn(SEL_MOUNT_FAIL_MSG);
- goto out_err;
- }
- fscontext = match_strdup(&args[0]);
- if (!fscontext) {
- rc = -ENOMEM;
- goto out_err;
- }
- break;
-
- case Opt_rootcontext:
- if (rootcontext) {
- rc = -EINVAL;
- pr_warn(SEL_MOUNT_FAIL_MSG);
- goto out_err;
- }
- rootcontext = match_strdup(&args[0]);
- if (!rootcontext) {
- rc = -ENOMEM;
- goto out_err;
- }
- break;
-
- case Opt_defcontext:
- if (context || defcontext) {
- rc = -EINVAL;
- pr_warn(SEL_MOUNT_FAIL_MSG);
- goto out_err;
- }
- defcontext = match_strdup(&args[0]);
- if (!defcontext) {
- rc = -ENOMEM;
- goto out_err;
- }
- break;
- case Opt_labelsupport:
- break;
- default:
- rc = -EINVAL;
- pr_warn("SELinux: unknown mount option\n");
- goto out_err;
-
}
}
- rc = -ENOMEM;
- opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_KERNEL);
- if (!opts->mnt_opts)
- goto out_err;
+ if (token == Opt_error)
+ return -EINVAL;
- opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int),
- GFP_KERNEL);
- if (!opts->mnt_opts_flags)
- goto out_err;
+ if (token != Opt_seclabel) {
+ val = kmemdup_nul(val, len, GFP_KERNEL);
+ if (!val) {
+ rc = -ENOMEM;
+ goto free_opt;
+ }
+ }
+ rc = selinux_add_opt(token, val, mnt_opts);
+ if (unlikely(rc)) {
+ kfree(val);
+ goto free_opt;
+ }
+ return rc;
- if (fscontext) {
- opts->mnt_opts[num_mnt_opts] = fscontext;
- opts->mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
+free_opt:
+ if (*mnt_opts) {
+ selinux_free_mnt_opts(*mnt_opts);
+ *mnt_opts = NULL;
}
- if (context) {
- opts->mnt_opts[num_mnt_opts] = context;
- opts->mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
- }
- if (rootcontext) {
- opts->mnt_opts[num_mnt_opts] = rootcontext;
- opts->mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
- }
- if (defcontext) {
- opts->mnt_opts[num_mnt_opts] = defcontext;
- opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
- }
+ return rc;
+}
- opts->num_mnt_opts = num_mnt_opts;
- return 0;
+static int show_sid(struct seq_file *m, u32 sid)
+{
+ char *context = NULL;
+ u32 len;
+ int rc;
-out_err:
- security_free_mnt_opts(opts);
+ rc = security_sid_to_context(&selinux_state, sid,
+ &context, &len);
+ if (!rc) {
+ bool has_comma = context && strchr(context, ',');
+
+ seq_putc(m, '=');
+ if (has_comma)
+ seq_putc(m, '\"');
+ seq_escape(m, context, "\"\n\\");
+ if (has_comma)
+ seq_putc(m, '\"');
+ }
kfree(context);
- kfree(defcontext);
- kfree(fscontext);
- kfree(rootcontext);
return rc;
}
-/*
- * string mount options parsing and call set the sbsec
- */
-static int superblock_doinit(struct super_block *sb, void *data)
-{
- int rc = 0;
- char *options = data;
- struct security_mnt_opts opts;
-
- security_init_mnt_opts(&opts);
-
- if (!data)
- goto out;
-
- BUG_ON(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA);
-
- rc = selinux_parse_opts_str(options, &opts);
- if (rc)
- goto out_err;
-
-out:
- rc = selinux_set_mnt_opts(sb, &opts, 0, NULL);
-
-out_err:
- security_free_mnt_opts(&opts);
- return rc;
-}
-
-static void selinux_write_opts(struct seq_file *m,
- struct security_mnt_opts *opts)
-{
- int i;
- char *prefix;
-
- for (i = 0; i < opts->num_mnt_opts; i++) {
- char *has_comma;
-
- if (opts->mnt_opts[i])
- has_comma = strchr(opts->mnt_opts[i], ',');
- else
- has_comma = NULL;
-
- switch (opts->mnt_opts_flags[i]) {
- case CONTEXT_MNT:
- prefix = CONTEXT_STR;
- break;
- case FSCONTEXT_MNT:
- prefix = FSCONTEXT_STR;
- break;
- case ROOTCONTEXT_MNT:
- prefix = ROOTCONTEXT_STR;
- break;
- case DEFCONTEXT_MNT:
- prefix = DEFCONTEXT_STR;
- break;
- case SBLABEL_MNT:
- seq_putc(m, ',');
- seq_puts(m, LABELSUPP_STR);
- continue;
- default:
- BUG();
- return;
- };
- /* we need a comma before each option */
- seq_putc(m, ',');
- seq_puts(m, prefix);
- if (has_comma)
- seq_putc(m, '\"');
- seq_escape(m, opts->mnt_opts[i], "\"\n\\");
- if (has_comma)
- seq_putc(m, '\"');
- }
-}
static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
{
- struct security_mnt_opts opts;
+ struct superblock_security_struct *sbsec = sb->s_security;
int rc;
- rc = selinux_get_mnt_opts(sb, &opts);
- if (rc) {
- /* before policy load we may get EINVAL, don't show anything */
- if (rc == -EINVAL)
- rc = 0;
- return rc;
+ if (!(sbsec->flags & SE_SBINITIALIZED))
+ return 0;
+
+ if (!selinux_state.initialized)
+ return 0;
+
+ if (sbsec->flags & FSCONTEXT_MNT) {
+ seq_putc(m, ',');
+ seq_puts(m, FSCONTEXT_STR);
+ rc = show_sid(m, sbsec->sid);
+ if (rc)
+ return rc;
}
-
- selinux_write_opts(m, &opts);
-
- security_free_mnt_opts(&opts);
-
- return rc;
+ if (sbsec->flags & CONTEXT_MNT) {
+ seq_putc(m, ',');
+ seq_puts(m, CONTEXT_STR);
+ rc = show_sid(m, sbsec->mntpoint_sid);
+ if (rc)
+ return rc;
+ }
+ if (sbsec->flags & DEFCONTEXT_MNT) {
+ seq_putc(m, ',');
+ seq_puts(m, DEFCONTEXT_STR);
+ rc = show_sid(m, sbsec->def_sid);
+ if (rc)
+ return rc;
+ }
+ if (sbsec->flags & ROOTCONTEXT_MNT) {
+ struct dentry *root = sbsec->sb->s_root;
+ struct inode_security_struct *isec = backing_inode_security(root);
+ seq_putc(m, ',');
+ seq_puts(m, ROOTCONTEXT_STR);
+ rc = show_sid(m, isec->sid);
+ if (rc)
+ return rc;
+ }
+ if (sbsec->flags & SBLABEL_MNT) {
+ seq_putc(m, ',');
+ seq_puts(m, SECLABEL_STR);
+ }
+ return 0;
}
static inline u16 inode_mode_to_security_class(umode_t mode)
@@ -1518,17 +1366,75 @@
return rc;
}
+static int inode_doinit_use_xattr(struct inode *inode, struct dentry *dentry,
+ u32 def_sid, u32 *sid)
+{
+#define INITCONTEXTLEN 255
+ char *context;
+ unsigned int len;
+ int rc;
+
+ len = INITCONTEXTLEN;
+ context = kmalloc(len + 1, GFP_NOFS);
+ if (!context)
+ return -ENOMEM;
+
+ context[len] = '\0';
+ rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len);
+ if (rc == -ERANGE) {
+ kfree(context);
+
+ /* Need a larger buffer. Query for the right size. */
+ rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0);
+ if (rc < 0)
+ return rc;
+
+ len = rc;
+ context = kmalloc(len + 1, GFP_NOFS);
+ if (!context)
+ return -ENOMEM;
+
+ context[len] = '\0';
+ rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX,
+ context, len);
+ }
+ if (rc < 0) {
+ kfree(context);
+ if (rc != -ENODATA) {
+ pr_warn("SELinux: %s: getxattr returned %d for dev=%s ino=%ld\n",
+ __func__, -rc, inode->i_sb->s_id, inode->i_ino);
+ return rc;
+ }
+ *sid = def_sid;
+ return 0;
+ }
+
+ rc = security_context_to_sid_default(&selinux_state, context, rc, sid,
+ def_sid, GFP_NOFS);
+ if (rc) {
+ char *dev = inode->i_sb->s_id;
+ unsigned long ino = inode->i_ino;
+
+ if (rc == -EINVAL) {
+ pr_notice_ratelimited("SELinux: inode=%lu on dev=%s was found to have an invalid context=%s. This indicates you may need to relabel the inode or the filesystem in question.\n",
+ ino, dev, context);
+ } else {
+ pr_warn("SELinux: %s: context_to_sid(%s) returned %d for dev=%s ino=%ld\n",
+ __func__, context, -rc, dev, ino);
+ }
+ }
+ kfree(context);
+ return 0;
+}
+
/* The inode's security attributes must be initialized before first use. */
static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry)
{
struct superblock_security_struct *sbsec = NULL;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
u32 task_sid, sid = 0;
u16 sclass;
struct dentry *dentry;
-#define INITCONTEXTLEN 255
- char *context = NULL;
- unsigned len = 0;
int rc = 0;
if (isec->initialized == LABEL_INITIALIZED)
@@ -1596,72 +1502,11 @@
goto out;
}
- len = INITCONTEXTLEN;
- context = kmalloc(len+1, GFP_NOFS);
- if (!context) {
- rc = -ENOMEM;
- dput(dentry);
- goto out;
- }
- context[len] = '\0';
- rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len);
- if (rc == -ERANGE) {
- kfree(context);
-
- /* Need a larger buffer. Query for the right size. */
- rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0);
- if (rc < 0) {
- dput(dentry);
- goto out;
- }
- len = rc;
- context = kmalloc(len+1, GFP_NOFS);
- if (!context) {
- rc = -ENOMEM;
- dput(dentry);
- goto out;
- }
- context[len] = '\0';
- rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len);
- }
+ rc = inode_doinit_use_xattr(inode, dentry, sbsec->def_sid,
+ &sid);
dput(dentry);
- if (rc < 0) {
- if (rc != -ENODATA) {
- pr_warn("SELinux: %s: getxattr returned "
- "%d for dev=%s ino=%ld\n", __func__,
- -rc, inode->i_sb->s_id, inode->i_ino);
- kfree(context);
- goto out;
- }
- /* Map ENODATA to the default file SID */
- sid = sbsec->def_sid;
- rc = 0;
- } else {
- rc = security_context_to_sid_default(&selinux_state,
- context, rc, &sid,
- sbsec->def_sid,
- GFP_NOFS);
- if (rc) {
- char *dev = inode->i_sb->s_id;
- unsigned long ino = inode->i_ino;
-
- if (rc == -EINVAL) {
- if (printk_ratelimit())
- pr_notice("SELinux: inode=%lu on dev=%s was found to have an invalid "
- "context=%s. This indicates you may need to relabel the inode or the "
- "filesystem in question.\n", ino, dev, context);
- } else {
- pr_warn("SELinux: %s: context_to_sid(%s) "
- "returned %d for dev=%s ino=%ld\n",
- __func__, context, -rc, dev, ino);
- }
- kfree(context);
- /* Leave with the unlabeled SID */
- rc = 0;
- break;
- }
- }
- kfree(context);
+ if (rc)
+ goto out;
break;
case SECURITY_FS_USE_TASK:
sid = task_sid;
@@ -1712,9 +1557,21 @@
goto out;
rc = selinux_genfs_get_sid(dentry, sclass,
sbsec->flags, &sid);
- dput(dentry);
- if (rc)
+ if (rc) {
+ dput(dentry);
goto out;
+ }
+
+ if ((sbsec->flags & SE_SBGENFS_XATTR) &&
+ (inode->i_opflags & IOP_XATTR)) {
+ rc = inode_doinit_use_xattr(inode, dentry,
+ sid, &sid);
+ if (rc) {
+ dput(dentry);
+ goto out;
+ }
+ }
+ dput(dentry);
}
break;
}
@@ -1769,7 +1626,7 @@
/* Check whether a task is allowed to use a capability. */
static int cred_has_capability(const struct cred *cred,
- int cap, int audit, bool initns)
+ int cap, unsigned int opts, bool initns)
{
struct common_audit_data ad;
struct av_decision avd;
@@ -1796,7 +1653,7 @@
rc = avc_has_perm_noaudit(&selinux_state,
sid, sid, sclass, av, 0, &avd);
- if (audit == SECURITY_CAP_AUDIT) {
+ if (!(opts & CAP_OPT_NOAUDIT)) {
int rc2 = avc_audit(&selinux_state,
sid, sid, sclass, av, &avd, rc, &ad, 0);
if (rc2)
@@ -1822,7 +1679,7 @@
return 0;
sid = cred_sid(cred);
- isec = inode->i_security;
+ isec = selinux_inode(inode);
return avc_has_perm(&selinux_state,
sid, isec->sid, isec->sclass, perms, adp);
@@ -1888,7 +1745,7 @@
struct file *file,
u32 av)
{
- struct file_security_struct *fsec = file->f_security;
+ struct file_security_struct *fsec = selinux_file(file);
struct inode *inode = file_inode(file);
struct common_audit_data ad;
u32 sid = cred_sid(cred);
@@ -1954,7 +1811,7 @@
struct dentry *dentry,
u16 tclass)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
struct inode_security_struct *dsec;
struct superblock_security_struct *sbsec;
u32 sid, newsid;
@@ -1976,7 +1833,7 @@
if (rc)
return rc;
- rc = selinux_determine_inode_label(current_security(), dir,
+ rc = selinux_determine_inode_label(selinux_cred(current_cred()), dir,
&dentry->d_name, tclass, &newsid);
if (rc)
return rc;
@@ -2232,7 +2089,7 @@
struct file *file)
{
u32 sid = task_sid(to);
- struct file_security_struct *fsec = file->f_security;
+ struct file_security_struct *fsec = selinux_file(file);
struct dentry *dentry = file->f_path.dentry;
struct inode_security_struct *isec;
struct common_audit_data ad;
@@ -2316,9 +2173,9 @@
*/
static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
- int cap, int audit)
+ int cap, unsigned int opts)
{
- return cred_has_capability(cred, cap, audit, ns == &init_user_ns);
+ return cred_has_capability(cred, cap, opts, ns == &init_user_ns);
}
static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
@@ -2392,7 +2249,7 @@
int rc, cap_sys_admin = 0;
rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN,
- SECURITY_CAP_NOAUDIT, true);
+ CAP_OPT_NOAUDIT, true);
if (rc == 0)
cap_sys_admin = 1;
@@ -2483,8 +2340,8 @@
if (bprm->called_set_creds)
return 0;
- old_tsec = current_security();
- new_tsec = bprm->cred->security;
+ old_tsec = selinux_cred(current_cred());
+ new_tsec = selinux_cred(bprm->cred);
isec = inode_security(inode);
/* Default to the current task SID. */
@@ -2648,7 +2505,7 @@
struct rlimit *rlim, *initrlim;
int rc, i;
- new_tsec = bprm->cred->security;
+ new_tsec = selinux_cred(bprm->cred);
if (new_tsec->sid == new_tsec->osid)
return;
@@ -2691,7 +2548,7 @@
*/
static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
struct itimerval itimer;
u32 osid, sid;
int rc, i;
@@ -2747,195 +2604,137 @@
superblock_free_security(sb);
}
-static inline int match_prefix(char *prefix, int plen, char *option, int olen)
+static inline int opt_len(const char *s)
{
- if (plen > olen)
- return 0;
+ bool open_quote = false;
+ int len;
+ char c;
- return !memcmp(prefix, option, plen);
-}
-
-static inline int selinux_option(char *option, int len)
-{
- return (match_prefix(CONTEXT_STR, sizeof(CONTEXT_STR)-1, option, len) ||
- match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) ||
- match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) ||
- match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len) ||
- match_prefix(LABELSUPP_STR, sizeof(LABELSUPP_STR)-1, option, len));
-}
-
-static inline void take_option(char **to, char *from, int *first, int len)
-{
- if (!*first) {
- **to = ',';
- *to += 1;
- } else
- *first = 0;
- memcpy(*to, from, len);
- *to += len;
-}
-
-static inline void take_selinux_option(char **to, char *from, int *first,
- int len)
-{
- int current_size = 0;
-
- if (!*first) {
- **to = '|';
- *to += 1;
- } else
- *first = 0;
-
- while (current_size < len) {
- if (*from != '"') {
- **to = *from;
- *to += 1;
- }
- from += 1;
- current_size += 1;
- }
-}
-
-static int selinux_sb_copy_data(char *orig, char *copy)
-{
- int fnosec, fsec, rc = 0;
- char *in_save, *in_curr, *in_end;
- char *sec_curr, *nosec_save, *nosec;
- int open_quote = 0;
-
- in_curr = orig;
- sec_curr = copy;
-
- nosec = (char *)get_zeroed_page(GFP_KERNEL);
- if (!nosec) {
- rc = -ENOMEM;
- goto out;
- }
-
- nosec_save = nosec;
- fnosec = fsec = 1;
- in_save = in_end = orig;
-
- do {
- if (*in_end == '"')
+ for (len = 0; (c = s[len]) != '\0'; len++) {
+ if (c == '"')
open_quote = !open_quote;
- if ((*in_end == ',' && open_quote == 0) ||
- *in_end == '\0') {
- int len = in_end - in_curr;
+ if (c == ',' && !open_quote)
+ break;
+ }
+ return len;
+}
- if (selinux_option(in_curr, len))
- take_selinux_option(&sec_curr, in_curr, &fsec, len);
- else
- take_option(&nosec, in_curr, &fnosec, len);
+static int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts)
+{
+ char *from = options;
+ char *to = options;
+ bool first = true;
+ int rc;
- in_curr = in_end + 1;
+ while (1) {
+ int len = opt_len(from);
+ int token;
+ char *arg = NULL;
+
+ token = match_opt_prefix(from, len, &arg);
+
+ if (token != Opt_error) {
+ char *p, *q;
+
+ /* strip quotes */
+ if (arg) {
+ for (p = q = arg; p < from + len; p++) {
+ char c = *p;
+ if (c != '"')
+ *q++ = c;
+ }
+ arg = kmemdup_nul(arg, q - arg, GFP_KERNEL);
+ if (!arg) {
+ rc = -ENOMEM;
+ goto free_opt;
+ }
+ }
+ rc = selinux_add_opt(token, arg, mnt_opts);
+ if (unlikely(rc)) {
+ kfree(arg);
+ goto free_opt;
+ }
+ } else {
+ if (!first) { // copy with preceding comma
+ from--;
+ len++;
+ }
+ if (to != from)
+ memmove(to, from, len);
+ to += len;
+ first = false;
}
- } while (*in_end++);
+ if (!from[len])
+ break;
+ from += len + 1;
+ }
+ *to = '\0';
+ return 0;
- strcpy(in_save, nosec_save);
- free_page((unsigned long)nosec_save);
-out:
+free_opt:
+ if (*mnt_opts) {
+ selinux_free_mnt_opts(*mnt_opts);
+ *mnt_opts = NULL;
+ }
return rc;
}
-static int selinux_sb_remount(struct super_block *sb, void *data)
+static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
{
- int rc, i, *flags;
- struct security_mnt_opts opts;
- char *secdata, **mount_options;
+ struct selinux_mnt_opts *opts = mnt_opts;
struct superblock_security_struct *sbsec = sb->s_security;
+ u32 sid;
+ int rc;
if (!(sbsec->flags & SE_SBINITIALIZED))
return 0;
- if (!data)
+ if (!opts)
return 0;
- if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
- return 0;
-
- security_init_mnt_opts(&opts);
- secdata = alloc_secdata();
- if (!secdata)
- return -ENOMEM;
- rc = selinux_sb_copy_data(data, secdata);
- if (rc)
- goto out_free_secdata;
-
- rc = selinux_parse_opts_str(secdata, &opts);
- if (rc)
- goto out_free_secdata;
-
- mount_options = opts.mnt_opts;
- flags = opts.mnt_opts_flags;
-
- for (i = 0; i < opts.num_mnt_opts; i++) {
- u32 sid;
-
- if (flags[i] == SBLABEL_MNT)
- continue;
- rc = security_context_str_to_sid(&selinux_state,
- mount_options[i], &sid,
- GFP_KERNEL);
- if (rc) {
- pr_warn("SELinux: security_context_str_to_sid"
- "(%s) failed for (dev %s, type %s) errno=%d\n",
- mount_options[i], sb->s_id, sb->s_type->name, rc);
- goto out_free_opts;
- }
- rc = -EINVAL;
- switch (flags[i]) {
- case FSCONTEXT_MNT:
- if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
- goto out_bad_option;
- break;
- case CONTEXT_MNT:
- if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
- goto out_bad_option;
- break;
- case ROOTCONTEXT_MNT: {
- struct inode_security_struct *root_isec;
- root_isec = backing_inode_security(sb->s_root);
-
- if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
- goto out_bad_option;
- break;
- }
- case DEFCONTEXT_MNT:
- if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
- goto out_bad_option;
- break;
- default:
- goto out_free_opts;
- }
+ if (opts->fscontext) {
+ rc = parse_sid(sb, opts->fscontext, &sid);
+ if (rc)
+ return rc;
+ if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
+ goto out_bad_option;
}
+ if (opts->context) {
+ rc = parse_sid(sb, opts->context, &sid);
+ if (rc)
+ return rc;
+ if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
+ goto out_bad_option;
+ }
+ if (opts->rootcontext) {
+ struct inode_security_struct *root_isec;
+ root_isec = backing_inode_security(sb->s_root);
+ rc = parse_sid(sb, opts->rootcontext, &sid);
+ if (rc)
+ return rc;
+ if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
+ goto out_bad_option;
+ }
+ if (opts->defcontext) {
+ rc = parse_sid(sb, opts->defcontext, &sid);
+ if (rc)
+ return rc;
+ if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
+ goto out_bad_option;
+ }
+ return 0;
- rc = 0;
-out_free_opts:
- security_free_mnt_opts(&opts);
-out_free_secdata:
- free_secdata(secdata);
- return rc;
out_bad_option:
pr_warn("SELinux: unable to change security options "
"during remount (dev %s, type=%s)\n", sb->s_id,
sb->s_type->name);
- goto out_free_opts;
+ return -EINVAL;
}
-static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
+static int selinux_sb_kern_mount(struct super_block *sb)
{
const struct cred *cred = current_cred();
struct common_audit_data ad;
- int rc;
-
- rc = superblock_doinit(sb, data);
- if (rc)
- return rc;
-
- /* Allow all mounts performed by the kernel */
- if (flags & MS_KERNMOUNT)
- return 0;
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = sb->s_root;
@@ -2975,6 +2774,76 @@
FILESYSTEM__UNMOUNT, NULL);
}
+static int selinux_fs_context_dup(struct fs_context *fc,
+ struct fs_context *src_fc)
+{
+ const struct selinux_mnt_opts *src = src_fc->security;
+ struct selinux_mnt_opts *opts;
+
+ if (!src)
+ return 0;
+
+ fc->security = kzalloc(sizeof(struct selinux_mnt_opts), GFP_KERNEL);
+ if (!fc->security)
+ return -ENOMEM;
+
+ opts = fc->security;
+
+ if (src->fscontext) {
+ opts->fscontext = kstrdup(src->fscontext, GFP_KERNEL);
+ if (!opts->fscontext)
+ return -ENOMEM;
+ }
+ if (src->context) {
+ opts->context = kstrdup(src->context, GFP_KERNEL);
+ if (!opts->context)
+ return -ENOMEM;
+ }
+ if (src->rootcontext) {
+ opts->rootcontext = kstrdup(src->rootcontext, GFP_KERNEL);
+ if (!opts->rootcontext)
+ return -ENOMEM;
+ }
+ if (src->defcontext) {
+ opts->defcontext = kstrdup(src->defcontext, GFP_KERNEL);
+ if (!opts->defcontext)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static const struct fs_parameter_spec selinux_param_specs[] = {
+ fsparam_string(CONTEXT_STR, Opt_context),
+ fsparam_string(DEFCONTEXT_STR, Opt_defcontext),
+ fsparam_string(FSCONTEXT_STR, Opt_fscontext),
+ fsparam_string(ROOTCONTEXT_STR, Opt_rootcontext),
+ fsparam_flag (SECLABEL_STR, Opt_seclabel),
+ {}
+};
+
+static const struct fs_parameter_description selinux_fs_parameters = {
+ .name = "SELinux",
+ .specs = selinux_param_specs,
+};
+
+static int selinux_fs_context_parse_param(struct fs_context *fc,
+ struct fs_parameter *param)
+{
+ struct fs_parse_result result;
+ int opt, rc;
+
+ opt = fs_parse(fc, &selinux_fs_parameters, param, &result);
+ if (opt < 0)
+ return opt;
+
+ rc = selinux_add_opt(opt, param->string, &fc->security);
+ if (!rc) {
+ param->string = NULL;
+ rc = 1;
+ }
+ return rc;
+}
+
/* inode security operations */
static int selinux_inode_alloc_security(struct inode *inode)
@@ -2994,7 +2863,7 @@
u32 newsid;
int rc;
- rc = selinux_determine_inode_label(current_security(),
+ rc = selinux_determine_inode_label(selinux_cred(current_cred()),
d_inode(dentry->d_parent), name,
inode_mode_to_security_class(mode),
&newsid);
@@ -3014,14 +2883,14 @@
int rc;
struct task_security_struct *tsec;
- rc = selinux_determine_inode_label(old->security,
+ rc = selinux_determine_inode_label(selinux_cred(old),
d_inode(dentry->d_parent), name,
inode_mode_to_security_class(mode),
&newsid);
if (rc)
return rc;
- tsec = new->security;
+ tsec = selinux_cred(new);
tsec->create_sid = newsid;
return 0;
}
@@ -3031,7 +2900,7 @@
const char **name,
void **value, size_t *len)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
struct superblock_security_struct *sbsec;
u32 newsid, clen;
int rc;
@@ -3041,7 +2910,7 @@
newsid = tsec->create_sid;
- rc = selinux_determine_inode_label(current_security(),
+ rc = selinux_determine_inode_label(selinux_cred(current_cred()),
dir, qstr,
inode_mode_to_security_class(inode->i_mode),
&newsid);
@@ -3050,7 +2919,7 @@
/* Possibly defer initialization to selinux_complete_init. */
if (sbsec->flags & SE_SBINITIALIZED) {
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
isec->initialized = LABEL_INITIALIZED;
@@ -3139,9 +3008,8 @@
if (IS_ERR(isec))
return PTR_ERR(isec);
- return avc_has_perm_flags(&selinux_state,
- sid, isec->sid, isec->sclass, FILE__READ, &ad,
- rcu ? MAY_NOT_BLOCK : 0);
+ return avc_has_perm(&selinux_state,
+ sid, isec->sid, isec->sclass, FILE__READ, &ad);
}
static noinline int audit_inode_permission(struct inode *inode,
@@ -3150,7 +3018,7 @@
unsigned flags)
{
struct common_audit_data ad;
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
int rc;
ad.type = LSM_AUDIT_DATA_INODE;
@@ -3196,7 +3064,9 @@
return PTR_ERR(isec);
rc = avc_has_perm_noaudit(&selinux_state,
- sid, isec->sid, isec->sclass, perms, 0, &avd);
+ sid, isec->sid, isec->sclass, perms,
+ (flags & MAY_NOT_BLOCK) ? AVC_NONBLOCKING : 0,
+ &avd);
audited = avc_audit_required(perms, &avd, rc,
from_access ? FILE__AUDIT_ACCESS : 0,
&denied);
@@ -3245,11 +3115,11 @@
static bool has_cap_mac_admin(bool audit)
{
const struct cred *cred = current_cred();
- int cap_audit = audit ? SECURITY_CAP_AUDIT : SECURITY_CAP_NOAUDIT;
+ unsigned int opts = audit ? CAP_OPT_NONE : CAP_OPT_NOAUDIT;
- if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, cap_audit))
+ if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, opts))
return false;
- if (cred_has_capability(cred, CAP_MAC_ADMIN, cap_audit, true))
+ if (cred_has_capability(cred, CAP_MAC_ADMIN, opts, true))
return false;
return true;
}
@@ -3407,6 +3277,50 @@
return -EACCES;
}
+static int selinux_path_notify(const struct path *path, u64 mask,
+ unsigned int obj_type)
+{
+ int ret;
+ u32 perm;
+
+ struct common_audit_data ad;
+
+ ad.type = LSM_AUDIT_DATA_PATH;
+ ad.u.path = *path;
+
+ /*
+ * Set permission needed based on the type of mark being set.
+ * Performs an additional check for sb watches.
+ */
+ switch (obj_type) {
+ case FSNOTIFY_OBJ_TYPE_VFSMOUNT:
+ perm = FILE__WATCH_MOUNT;
+ break;
+ case FSNOTIFY_OBJ_TYPE_SB:
+ perm = FILE__WATCH_SB;
+ ret = superblock_has_perm(current_cred(), path->dentry->d_sb,
+ FILESYSTEM__WATCH, &ad);
+ if (ret)
+ return ret;
+ break;
+ case FSNOTIFY_OBJ_TYPE_INODE:
+ perm = FILE__WATCH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* blocking watches require the file:watch_with_perm permission */
+ if (mask & (ALL_FSNOTIFY_PERM_EVENTS))
+ 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))
+ perm |= FILE__WATCH_READS;
+
+ return path_has_perm(current_cred(), path, perm);
+}
+
/*
* Copy the inode security context value to the user.
*
@@ -3455,12 +3369,16 @@
const void *value, size_t size, int flags)
{
struct inode_security_struct *isec = inode_security_novalidate(inode);
+ struct superblock_security_struct *sbsec = inode->i_sb->s_security;
u32 newsid;
int rc;
if (strcmp(name, XATTR_SELINUX_SUFFIX))
return -EOPNOTSUPP;
+ if (!(sbsec->flags & SBLABEL_MNT))
+ return -EOPNOTSUPP;
+
if (!value || !size)
return -EACCES;
@@ -3503,7 +3421,7 @@
return -ENOMEM;
}
- tsec = new_creds->security;
+ 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;
@@ -3526,6 +3444,67 @@
return -EOPNOTSUPP;
}
+/* kernfs node operations */
+
+static int selinux_kernfs_init_security(struct kernfs_node *kn_dir,
+ struct kernfs_node *kn)
+{
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
+ u32 parent_sid, newsid, clen;
+ int rc;
+ char *context;
+
+ rc = kernfs_xattr_get(kn_dir, XATTR_NAME_SELINUX, NULL, 0);
+ if (rc == -ENODATA)
+ return 0;
+ else if (rc < 0)
+ return rc;
+
+ clen = (u32)rc;
+ context = kmalloc(clen, GFP_KERNEL);
+ if (!context)
+ return -ENOMEM;
+
+ rc = kernfs_xattr_get(kn_dir, XATTR_NAME_SELINUX, context, clen);
+ if (rc < 0) {
+ kfree(context);
+ return rc;
+ }
+
+ rc = security_context_to_sid(&selinux_state, context, clen, &parent_sid,
+ GFP_KERNEL);
+ kfree(context);
+ if (rc)
+ return rc;
+
+ if (tsec->create_sid) {
+ newsid = tsec->create_sid;
+ } else {
+ u16 secclass = inode_mode_to_security_class(kn->mode);
+ struct qstr q;
+
+ q.name = kn->name;
+ q.hash_len = hashlen_string(kn_dir, kn->name);
+
+ rc = security_transition_sid(&selinux_state, tsec->sid,
+ parent_sid, secclass, &q,
+ &newsid);
+ if (rc)
+ return rc;
+ }
+
+ rc = security_sid_to_context_force(&selinux_state, newsid,
+ &context, &clen);
+ if (rc)
+ return rc;
+
+ rc = kernfs_xattr_set(kn, XATTR_NAME_SELINUX, context, clen,
+ XATTR_CREATE);
+ kfree(context);
+ return rc;
+}
+
+
/* file security operations */
static int selinux_revalidate_file_permission(struct file *file, int mask)
@@ -3544,7 +3523,7 @@
static int selinux_file_permission(struct file *file, int mask)
{
struct inode *inode = file_inode(file);
- struct file_security_struct *fsec = file->f_security;
+ struct file_security_struct *fsec = selinux_file(file);
struct inode_security_struct *isec;
u32 sid = current_sid();
@@ -3566,11 +3545,6 @@
return file_alloc_security(file);
}
-static void selinux_file_free_security(struct file *file)
-{
- file_free_security(file);
-}
-
/*
* Check whether a task has the ioctl permission and cmd
* operation to an inode.
@@ -3579,7 +3553,7 @@
u32 requested, u16 cmd)
{
struct common_audit_data ad;
- struct file_security_struct *fsec = file->f_security;
+ struct file_security_struct *fsec = selinux_file(file);
struct inode *inode = file_inode(file);
struct inode_security_struct *isec;
struct lsm_ioctlop_audit ioctl;
@@ -3649,7 +3623,7 @@
case KDSKBENT:
case KDSKBSENT:
error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG,
- SECURITY_CAP_AUDIT, true);
+ CAP_OPT_NONE, true);
break;
/* default case assumes that the command will go
@@ -3831,7 +3805,7 @@
{
struct file_security_struct *fsec;
- fsec = file->f_security;
+ fsec = selinux_file(file);
fsec->fown_sid = current_sid();
}
@@ -3846,7 +3820,7 @@
/* struct fown_struct is never outside the context of a struct file */
file = container_of(fown, struct file, f_owner);
- fsec = file->f_security;
+ fsec = selinux_file(file);
if (!signum)
perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */
@@ -3870,7 +3844,7 @@
struct file_security_struct *fsec;
struct inode_security_struct *isec;
- fsec = file->f_security;
+ fsec = selinux_file(file);
isec = inode_security(file_inode(file));
/*
* Save inode label and policy sequence number
@@ -3904,52 +3878,15 @@
}
/*
- * allocate the SELinux part of blank credentials
- */
-static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp)
-{
- struct task_security_struct *tsec;
-
- tsec = kzalloc(sizeof(struct task_security_struct), gfp);
- if (!tsec)
- return -ENOMEM;
-
- cred->security = tsec;
- return 0;
-}
-
-/*
- * detach and free the LSM part of a set of credentials
- */
-static void selinux_cred_free(struct cred *cred)
-{
- struct task_security_struct *tsec = cred->security;
-
- /*
- * cred->security == NULL if security_cred_alloc_blank() or
- * security_prepare_creds() returned an error.
- */
- BUG_ON(cred->security && (unsigned long) cred->security < PAGE_SIZE);
- cred->security = (void *) 0x7UL;
- kfree(tsec);
-}
-
-/*
* prepare a new set of credentials for modification
*/
static int selinux_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- const struct task_security_struct *old_tsec;
- struct task_security_struct *tsec;
+ const struct task_security_struct *old_tsec = selinux_cred(old);
+ struct task_security_struct *tsec = selinux_cred(new);
- old_tsec = old->security;
-
- tsec = kmemdup(old_tsec, sizeof(struct task_security_struct), gfp);
- if (!tsec)
- return -ENOMEM;
-
- new->security = tsec;
+ *tsec = *old_tsec;
return 0;
}
@@ -3958,8 +3895,8 @@
*/
static void selinux_cred_transfer(struct cred *new, const struct cred *old)
{
- const struct task_security_struct *old_tsec = old->security;
- struct task_security_struct *tsec = new->security;
+ const struct task_security_struct *old_tsec = selinux_cred(old);
+ struct task_security_struct *tsec = selinux_cred(new);
*tsec = *old_tsec;
}
@@ -3975,7 +3912,7 @@
*/
static int selinux_kernel_act_as(struct cred *new, u32 secid)
{
- struct task_security_struct *tsec = new->security;
+ struct task_security_struct *tsec = selinux_cred(new);
u32 sid = current_sid();
int ret;
@@ -4000,7 +3937,7 @@
static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
{
struct inode_security_struct *isec = inode_security(inode);
- struct task_security_struct *tsec = new->security;
+ struct task_security_struct *tsec = selinux_cred(new);
u32 sid = current_sid();
int ret;
@@ -4046,7 +3983,7 @@
ad.type = LSM_AUDIT_DATA_FILE;
ad.u.file = file;
- fsec = file->f_security;
+ fsec = selinux_file(file);
if (sid != fsec->sid) {
rc = avc_has_perm(&selinux_state,
sid, fsec->sid, SECCLASS_FD, FD__USE, &ad);
@@ -4191,7 +4128,7 @@
PROCESS__SETSCHED, NULL);
}
-static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
+static int selinux_task_kill(struct task_struct *p, struct kernel_siginfo *info,
int sig, const struct cred *cred)
{
u32 secid;
@@ -4212,7 +4149,7 @@
static void selinux_task_to_inode(struct task_struct *p,
struct inode *inode)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
u32 sid = task_sid(p);
spin_lock(&isec->lock);
@@ -4549,7 +4486,7 @@
static int selinux_socket_create(int family, int type,
int protocol, int kern)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
u32 newsid;
u16 secclass;
int rc;
@@ -4569,7 +4506,7 @@
static int selinux_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock));
struct sk_security_struct *sksec;
u16 sclass = socket_type_to_security_class(family, type, protocol);
@@ -4635,7 +4572,7 @@
struct lsm_network_audit net = {0,};
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
- u16 family_sa = address->sa_family;
+ u16 family_sa;
unsigned short snum;
u32 sid, node_perm;
@@ -4645,6 +4582,9 @@
* need to check address->sa_family as it is possible to have
* sk->sk_family = PF_INET6 with addr->sa_family = AF_INET.
*/
+ if (addrlen < offsetofend(struct sockaddr, sa_family))
+ return -EINVAL;
+ family_sa = address->sa_family;
switch (family_sa) {
case AF_UNSPEC:
case AF_INET:
@@ -4745,7 +4685,7 @@
}
/* This supports connect(2) and SCTP connect services such as sctp_connectx(3)
- * and sctp_sendmsg(3) as described in Documentation/security/LSM-sctp.rst
+ * and sctp_sendmsg(3) as described in Documentation/security/SCTP.rst
*/
static int selinux_socket_connect_helper(struct socket *sock,
struct sockaddr *address, int addrlen)
@@ -4757,6 +4697,14 @@
err = sock_has_perm(sk, SOCKET__CONNECT);
if (err)
return err;
+ if (addrlen < offsetofend(struct sockaddr, sa_family))
+ return -EINVAL;
+
+ /* connect(AF_UNSPEC) has special handling, as it is a documented
+ * way to disconnect the socket
+ */
+ if (address->sa_family == AF_UNSPEC)
+ return 0;
/*
* If a TCP, DCCP or SCTP socket, check name_connect permission
@@ -5334,6 +5282,9 @@
return -EINVAL;
}
+ if (walk_size + len > addrlen)
+ return -EINVAL;
+
err = -EINVAL;
switch (optname) {
/* Bind checks */
@@ -5450,7 +5401,7 @@
const struct task_security_struct *__tsec;
u32 tsid;
- __tsec = current_security();
+ __tsec = selinux_cred(current_cred());
tsid = __tsec->sid;
return avc_has_perm(&selinux_state,
@@ -5925,51 +5876,22 @@
return selinux_nlmsg_perm(sk, skb);
}
-static int ipc_alloc_security(struct kern_ipc_perm *perm,
- u16 sclass)
+static void ipc_init_security(struct ipc_security_struct *isec, u16 sclass)
{
- struct ipc_security_struct *isec;
-
- isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
- if (!isec)
- return -ENOMEM;
-
isec->sclass = sclass;
isec->sid = current_sid();
- perm->security = isec;
-
- return 0;
-}
-
-static void ipc_free_security(struct kern_ipc_perm *perm)
-{
- struct ipc_security_struct *isec = perm->security;
- perm->security = NULL;
- kfree(isec);
}
static int msg_msg_alloc_security(struct msg_msg *msg)
{
struct msg_security_struct *msec;
- msec = kzalloc(sizeof(struct msg_security_struct), GFP_KERNEL);
- if (!msec)
- return -ENOMEM;
-
+ msec = selinux_msg_msg(msg);
msec->sid = SECINITSID_UNLABELED;
- msg->security = msec;
return 0;
}
-static void msg_msg_free_security(struct msg_msg *msg)
-{
- struct msg_security_struct *msec = msg->security;
-
- msg->security = NULL;
- kfree(msec);
-}
-
static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
u32 perms)
{
@@ -5977,7 +5899,7 @@
struct common_audit_data ad;
u32 sid = current_sid();
- isec = ipc_perms->security;
+ isec = selinux_ipc(ipc_perms);
ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = ipc_perms->key;
@@ -5991,11 +5913,6 @@
return msg_msg_alloc_security(msg);
}
-static void selinux_msg_msg_free_security(struct msg_msg *msg)
-{
- msg_msg_free_security(msg);
-}
-
/* message queue security operations */
static int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq)
{
@@ -6004,11 +5921,8 @@
u32 sid = current_sid();
int rc;
- rc = ipc_alloc_security(msq, SECCLASS_MSGQ);
- if (rc)
- return rc;
-
- isec = msq->security;
+ isec = selinux_ipc(msq);
+ ipc_init_security(isec, SECCLASS_MSGQ);
ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = msq->key;
@@ -6016,16 +5930,7 @@
rc = avc_has_perm(&selinux_state,
sid, isec->sid, SECCLASS_MSGQ,
MSGQ__CREATE, &ad);
- if (rc) {
- ipc_free_security(msq);
- return rc;
- }
- return 0;
-}
-
-static void selinux_msg_queue_free_security(struct kern_ipc_perm *msq)
-{
- ipc_free_security(msq);
+ return rc;
}
static int selinux_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg)
@@ -6034,7 +5939,7 @@
struct common_audit_data ad;
u32 sid = current_sid();
- isec = msq->security;
+ isec = selinux_ipc(msq);
ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = msq->key;
@@ -6083,8 +5988,8 @@
u32 sid = current_sid();
int rc;
- isec = msq->security;
- msec = msg->security;
+ isec = selinux_ipc(msq);
+ msec = selinux_msg_msg(msg);
/*
* First time through, need to assign label to the message
@@ -6131,8 +6036,8 @@
u32 sid = task_sid(target);
int rc;
- isec = msq->security;
- msec = msg->security;
+ isec = selinux_ipc(msq);
+ msec = selinux_msg_msg(msg);
ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = msq->key;
@@ -6155,11 +6060,8 @@
u32 sid = current_sid();
int rc;
- rc = ipc_alloc_security(shp, SECCLASS_SHM);
- if (rc)
- return rc;
-
- isec = shp->security;
+ isec = selinux_ipc(shp);
+ ipc_init_security(isec, SECCLASS_SHM);
ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = shp->key;
@@ -6167,16 +6069,7 @@
rc = avc_has_perm(&selinux_state,
sid, isec->sid, SECCLASS_SHM,
SHM__CREATE, &ad);
- if (rc) {
- ipc_free_security(shp);
- return rc;
- }
- return 0;
-}
-
-static void selinux_shm_free_security(struct kern_ipc_perm *shp)
-{
- ipc_free_security(shp);
+ return rc;
}
static int selinux_shm_associate(struct kern_ipc_perm *shp, int shmflg)
@@ -6185,7 +6078,7 @@
struct common_audit_data ad;
u32 sid = current_sid();
- isec = shp->security;
+ isec = selinux_ipc(shp);
ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = shp->key;
@@ -6252,11 +6145,8 @@
u32 sid = current_sid();
int rc;
- rc = ipc_alloc_security(sma, SECCLASS_SEM);
- if (rc)
- return rc;
-
- isec = sma->security;
+ isec = selinux_ipc(sma);
+ ipc_init_security(isec, SECCLASS_SEM);
ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = sma->key;
@@ -6264,16 +6154,7 @@
rc = avc_has_perm(&selinux_state,
sid, isec->sid, SECCLASS_SEM,
SEM__CREATE, &ad);
- if (rc) {
- ipc_free_security(sma);
- return rc;
- }
- return 0;
-}
-
-static void selinux_sem_free_security(struct kern_ipc_perm *sma)
-{
- ipc_free_security(sma);
+ return rc;
}
static int selinux_sem_associate(struct kern_ipc_perm *sma, int semflg)
@@ -6282,7 +6163,7 @@
struct common_audit_data ad;
u32 sid = current_sid();
- isec = sma->security;
+ isec = selinux_ipc(sma);
ad.type = LSM_AUDIT_DATA_IPC;
ad.u.ipc_id = sma->key;
@@ -6368,7 +6249,7 @@
static void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
{
- struct ipc_security_struct *isec = ipcp->security;
+ struct ipc_security_struct *isec = selinux_ipc(ipcp);
*secid = isec->sid;
}
@@ -6387,7 +6268,7 @@
unsigned len;
rcu_read_lock();
- __tsec = __task_cred(p)->security;
+ __tsec = selinux_cred(__task_cred(p));
if (current != p) {
error = avc_has_perm(&selinux_state,
@@ -6510,17 +6391,18 @@
operation. See selinux_bprm_set_creds for the execve
checks and may_create for the file creation checks. The
operation will then fail if the context is not permitted. */
- tsec = new->security;
+ tsec = selinux_cred(new);
if (!strcmp(name, "exec")) {
tsec->exec_sid = sid;
} else if (!strcmp(name, "fscreate")) {
tsec->create_sid = sid;
} else if (!strcmp(name, "keycreate")) {
- error = avc_has_perm(&selinux_state,
- mysid, sid, SECCLASS_KEY, KEY__CREATE,
- NULL);
- if (error)
- goto abort_change;
+ if (sid) {
+ error = avc_has_perm(&selinux_state, mysid, sid,
+ SECCLASS_KEY, KEY__CREATE, NULL);
+ if (error)
+ goto abort_change;
+ }
tsec->keycreate_sid = sid;
} else if (!strcmp(name, "sockcreate")) {
tsec->sockcreate_sid = sid;
@@ -6594,7 +6476,7 @@
static void selinux_inode_invalidate_secctx(struct inode *inode)
{
- struct inode_security_struct *isec = inode->i_security;
+ struct inode_security_struct *isec = selinux_inode(inode);
spin_lock(&isec->lock);
isec->initialized = LABEL_INVALID;
@@ -6606,7 +6488,10 @@
*/
static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
{
- return selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX, ctx, ctxlen, 0);
+ int rc = selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX,
+ ctx, ctxlen, 0);
+ /* Do not return error when suppressing label (SBLABEL_MNT not set). */
+ return rc == -EOPNOTSUPP ? 0 : rc;
}
/*
@@ -6639,7 +6524,7 @@
if (!ksec)
return -ENOMEM;
- tsec = cred->security;
+ tsec = selinux_cred(cred);
if (tsec->keycreate_sid)
ksec->sid = tsec->keycreate_sid;
else
@@ -6902,6 +6787,14 @@
}
#endif
+struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = {
+ .lbs_cred = sizeof(struct task_security_struct),
+ .lbs_file = sizeof(struct file_security_struct),
+ .lbs_inode = sizeof(struct inode_security_struct),
+ .lbs_ipc = sizeof(struct ipc_security_struct),
+ .lbs_msg_msg = sizeof(struct msg_security_struct),
+};
+
static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
@@ -6924,9 +6817,13 @@
LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds),
LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),
+ LSM_HOOK_INIT(fs_context_dup, selinux_fs_context_dup),
+ LSM_HOOK_INIT(fs_context_parse_param, selinux_fs_context_parse_param),
+
LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security),
LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
- LSM_HOOK_INIT(sb_copy_data, selinux_sb_copy_data),
+ LSM_HOOK_INIT(sb_eat_lsm_opts, selinux_sb_eat_lsm_opts),
+ LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts),
LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options),
@@ -6935,7 +6832,7 @@
LSM_HOOK_INIT(sb_umount, selinux_umount),
LSM_HOOK_INIT(sb_set_mnt_opts, selinux_set_mnt_opts),
LSM_HOOK_INIT(sb_clone_mnt_opts, selinux_sb_clone_mnt_opts),
- LSM_HOOK_INIT(sb_parse_opts_str, selinux_parse_opts_str),
+ LSM_HOOK_INIT(sb_add_mnt_opt, selinux_add_mnt_opt),
LSM_HOOK_INIT(dentry_init_security, selinux_dentry_init_security),
LSM_HOOK_INIT(dentry_create_files_as, selinux_dentry_create_files_as),
@@ -6967,10 +6864,12 @@
LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid),
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),
+
+ LSM_HOOK_INIT(kernfs_init_security, selinux_kernfs_init_security),
LSM_HOOK_INIT(file_permission, selinux_file_permission),
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
- LSM_HOOK_INIT(file_free_security, selinux_file_free_security),
LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
@@ -6984,8 +6883,6 @@
LSM_HOOK_INIT(file_open, selinux_file_open),
LSM_HOOK_INIT(task_alloc, selinux_task_alloc),
- LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank),
- LSM_HOOK_INIT(cred_free, selinux_cred_free),
LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer),
LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid),
@@ -7013,24 +6910,20 @@
LSM_HOOK_INIT(ipc_getsecid, selinux_ipc_getsecid),
LSM_HOOK_INIT(msg_msg_alloc_security, selinux_msg_msg_alloc_security),
- LSM_HOOK_INIT(msg_msg_free_security, selinux_msg_msg_free_security),
LSM_HOOK_INIT(msg_queue_alloc_security,
selinux_msg_queue_alloc_security),
- LSM_HOOK_INIT(msg_queue_free_security, selinux_msg_queue_free_security),
LSM_HOOK_INIT(msg_queue_associate, selinux_msg_queue_associate),
LSM_HOOK_INIT(msg_queue_msgctl, selinux_msg_queue_msgctl),
LSM_HOOK_INIT(msg_queue_msgsnd, selinux_msg_queue_msgsnd),
LSM_HOOK_INIT(msg_queue_msgrcv, selinux_msg_queue_msgrcv),
LSM_HOOK_INIT(shm_alloc_security, selinux_shm_alloc_security),
- LSM_HOOK_INIT(shm_free_security, selinux_shm_free_security),
LSM_HOOK_INIT(shm_associate, selinux_shm_associate),
LSM_HOOK_INIT(shm_shmctl, selinux_shm_shmctl),
LSM_HOOK_INIT(shm_shmat, selinux_shm_shmat),
LSM_HOOK_INIT(sem_alloc_security, selinux_sem_alloc_security),
- LSM_HOOK_INIT(sem_free_security, selinux_sem_free_security),
LSM_HOOK_INIT(sem_associate, selinux_sem_associate),
LSM_HOOK_INIT(sem_semctl, selinux_sem_semctl),
LSM_HOOK_INIT(sem_semop, selinux_sem_semop),
@@ -7141,16 +7034,6 @@
static __init int selinux_init(void)
{
- if (!security_module_enable("selinux")) {
- selinux_enabled = 0;
- return 0;
- }
-
- if (!selinux_enabled) {
- pr_info("SELinux: Disabled at boot.\n");
- return 0;
- }
-
pr_info("SELinux: Initializing.\n");
memset(&selinux_state, 0, sizeof(selinux_state));
@@ -7164,12 +7047,6 @@
default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC);
- sel_inode_cache = kmem_cache_create("selinux_inode_security",
- sizeof(struct inode_security_struct),
- 0, SLAB_PANIC, NULL);
- file_security_cache = kmem_cache_create("selinux_file_security",
- sizeof(struct file_security_struct),
- 0, SLAB_PANIC, NULL);
avc_init();
avtab_cache_init();
@@ -7191,12 +7068,14 @@
else
pr_debug("SELinux: Starting in permissive mode\n");
+ fs_validate_description(&selinux_fs_parameters);
+
return 0;
}
static void delayed_superblock_init(struct super_block *sb, void *unused)
{
- superblock_doinit(sb, NULL);
+ selinux_set_mnt_opts(sb, NULL, 0, NULL);
}
void selinux_complete_init(void)
@@ -7210,7 +7089,13 @@
/* SELinux requires early initialization in order to label
all processes and objects when they are created. */
-security_initcall(selinux_init);
+DEFINE_LSM(selinux) = {
+ .name = "selinux",
+ .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
+ .enabled = &selinux_enabled,
+ .blobs = &selinux_blob_sizes,
+ .init = selinux_init,
+};
#if defined(CONFIG_NETFILTER)
diff --git a/security/selinux/ibpkey.c b/security/selinux/ibpkey.c
index 0a4b89d..de92365 100644
--- a/security/selinux/ibpkey.c
+++ b/security/selinux/ibpkey.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Pkey table
*
@@ -11,21 +12,10 @@
* Paul Moore <paul@paul-moore.com>
* (see security/selinux/netif.c and security/selinux/netport.c for more
* information)
- *
*/
/*
* (c) Mellanox Technologies, 2016
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <linux/types.h>
diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h
index 1bdf973..073a3d3 100644
--- a/security/selinux/include/audit.h
+++ b/security/selinux/include/audit.h
@@ -1,18 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* SELinux support for the Audit LSM hooks
*
- * Most of below header was moved from include/linux/selinux.h which
- * is released under below copyrights:
- *
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2006 Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
* Copyright (C) 2006 IBM Corporation, Timothy R. Chavez <tinytim@us.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#ifndef _SELINUX_AUDIT_H
@@ -46,13 +40,11 @@
* @field: the field this rule refers to
* @op: the operater the rule uses
* @rule: pointer to the audit rule to check against
- * @actx: the audit context (can be NULL) associated with the check
*
* 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,
- struct audit_context *actx);
+int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *rule);
/**
* selinux_audit_rule_known - check to see if rule contains selinux fields.
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
index ef899bc..7be0e1e 100644
--- a/security/selinux/include/avc.h
+++ b/security/selinux/include/avc.h
@@ -142,6 +142,7 @@
#define AVC_STRICT 1 /* Ignore permissive mode. */
#define AVC_EXTENDED_PERMS 2 /* update extended permissions */
+#define AVC_NONBLOCKING 4 /* non blocking */
int avc_has_perm_noaudit(struct selinux_state *state,
u32 ssid, u32 tsid,
u16 tclass, u32 requested,
@@ -152,11 +153,6 @@
u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct common_audit_data *auditdata);
-int avc_has_perm_flags(struct selinux_state *state,
- u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct common_audit_data *auditdata,
- int flags);
int avc_has_extended_perms(struct selinux_state *state,
u32 ssid, u32 tsid, u16 tclass, u32 requested,
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index bd5fe0d..32e9b03 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -1,12 +1,14 @@
/* 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"
#define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \
"rename", "execute", "quotaon", "mounton", "audit_access", \
- "open", "execmod"
+ "open", "execmod", "watch", "watch_mount", "watch_sb", \
+ "watch_with_perm", "watch_reads"
#define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \
"listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \
@@ -59,7 +61,7 @@
{ "filesystem",
{ "mount", "remount", "unmount", "getattr",
"relabelfrom", "relabelto", "associate", "quotamod",
- "quotaget", NULL } },
+ "quotaget", "watch", NULL } },
{ "file",
{ COMMON_FILE_PERMS,
"execute_no_trans", "entrypoint", NULL } },
diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h
index 0e30eca..0ab316f 100644
--- a/security/selinux/include/conditional.h
+++ b/security/selinux/include/conditional.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Interface to booleans in the security server. This is exported
* for the selinuxfs.
@@ -5,9 +6,6 @@
* Author: Karl MacMillan <kmacmillan@tresys.com>
*
* Copyright (C) 2003 - 2004 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.
*/
#ifndef _SELINUX_CONDITIONAL_H_
diff --git a/security/selinux/include/ibpkey.h b/security/selinux/include/ibpkey.h
index b17a19e..a2ebe39 100644
--- a/security/selinux/include/ibpkey.h
+++ b/security/selinux/include/ibpkey.h
@@ -1,24 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* pkey table
*
* SELinux must keep a mapping of pkeys to labels/SIDs. This
* mapping is maintained as part of the normal policy but a fast cache is
* needed to reduce the lookup overhead.
- *
*/
/*
* (c) Mellanox Technologies, 2016
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#ifndef _SELINUX_IB_PKEY_H
diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h
index c721454..85ec30d 100644
--- a/security/selinux/include/netif.h
+++ b/security/selinux/include/netif.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Network interface table.
*
@@ -9,10 +10,6 @@
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
* Paul Moore <paul@paul-moore.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#ifndef _SELINUX_NETIF_H_
#define _SELINUX_NETIF_H_
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
index 8671de0..d30d8d7 100644
--- a/security/selinux/include/netlabel.h
+++ b/security/selinux/include/netlabel.h
@@ -1,26 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* SELinux interface to the NetLabel subsystem
*
* Author: Paul Moore <paul@paul-moore.com>
- *
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2006
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
- * the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
*/
#ifndef _SELINUX_NETLABEL_H_
diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h
index 937668d..e3f784a 100644
--- a/security/selinux/include/netnode.h
+++ b/security/selinux/include/netnode.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Network node table
*
@@ -7,21 +8,10 @@
* a per-packet basis.
*
* Author: Paul Moore <paul@paul-moore.com>
- *
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2007
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#ifndef _SELINUX_NETNODE_H
diff --git a/security/selinux/include/netport.h b/security/selinux/include/netport.h
index d1ce896..31bc16e 100644
--- a/security/selinux/include/netport.h
+++ b/security/selinux/include/netport.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Network port table
*
@@ -6,21 +7,10 @@
* needed to reduce the lookup overhead.
*
* Author: Paul Moore <paul@paul-moore.com>
- *
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2008
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#ifndef _SELINUX_NETPORT_H
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index cc5e26b..586b7ab 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* NSA Security-Enhanced Linux (SELinux) security module
*
@@ -11,10 +12,6 @@
* Copyright (C) 2001,2002 Networks Associates Technology, Inc.
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2016 Mellanox Technologies
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#ifndef _SELINUX_OBJSEC_H_
#define _SELINUX_OBJSEC_H_
@@ -25,6 +22,8 @@
#include <linux/binfmts.h>
#include <linux/in.h>
#include <linux/spinlock.h>
+#include <linux/lsm_hooks.h>
+#include <linux/msg.h>
#include <net/net_namespace.h>
#include "flask.h"
#include "avc.h"
@@ -38,16 +37,6 @@
u32 sockcreate_sid; /* fscreate SID */
};
-/*
- * get the subjective security ID of the current task
- */
-static inline u32 current_sid(void)
-{
- const struct task_security_struct *tsec = current_security();
-
- return tsec->sid;
-}
-
enum label_initialized {
LABEL_INVALID, /* invalid or not initialized */
LABEL_INITIALIZED, /* initialized */
@@ -56,10 +45,7 @@
struct inode_security_struct {
struct inode *inode; /* back pointer to inode object */
- union {
- struct list_head list; /* list of inode_security_struct */
- struct rcu_head rcu; /* for freeing the inode_security_struct */
- };
+ struct list_head list; /* list of inode_security_struct */
u32 task_sid; /* SID of creating task */
u32 sid; /* SID of this object */
u16 sclass; /* security class of this object */
@@ -158,4 +144,45 @@
u32 sid; /*SID of bpf obj creater*/
};
+extern struct lsm_blob_sizes selinux_blob_sizes;
+static inline struct task_security_struct *selinux_cred(const struct cred *cred)
+{
+ return cred->security + selinux_blob_sizes.lbs_cred;
+}
+
+static inline struct file_security_struct *selinux_file(const struct file *file)
+{
+ return file->f_security + selinux_blob_sizes.lbs_file;
+}
+
+static inline struct inode_security_struct *selinux_inode(
+ const struct inode *inode)
+{
+ if (unlikely(!inode->i_security))
+ return NULL;
+ return inode->i_security + selinux_blob_sizes.lbs_inode;
+}
+
+static inline struct msg_security_struct *selinux_msg_msg(
+ const struct msg_msg *msg_msg)
+{
+ return msg_msg->security + selinux_blob_sizes.lbs_msg_msg;
+}
+
+static inline struct ipc_security_struct *selinux_ipc(
+ const struct kern_ipc_perm *ipc)
+{
+ return ipc->security + selinux_blob_sizes.lbs_ipc;
+}
+
+/*
+ * get the subjective security ID of the current task
+ */
+static inline u32 current_sid(void)
+{
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
+
+ return tsec->sid;
+}
+
#endif /* _SELINUX_OBJSEC_H_ */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 23e762d..1111212 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -58,12 +58,13 @@
#define SE_SBINITIALIZED 0x0100
#define SE_SBPROC 0x0200
#define SE_SBGENFS 0x0400
+#define SE_SBGENFS_XATTR 0x0800
-#define CONTEXT_STR "context="
-#define FSCONTEXT_STR "fscontext="
-#define ROOTCONTEXT_STR "rootcontext="
-#define DEFCONTEXT_STR "defcontext="
-#define LABELSUPP_STR "seclabel"
+#define CONTEXT_STR "context"
+#define FSCONTEXT_STR "fscontext"
+#define ROOTCONTEXT_STR "rootcontext"
+#define DEFCONTEXT_STR "defcontext"
+#define SECLABEL_STR "seclabel"
struct netlbl_lsm_secattr;
@@ -81,7 +82,7 @@
};
#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
-extern char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX];
+extern const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX];
/*
* type_datum properties
@@ -255,6 +256,9 @@
int security_sid_to_context_force(struct selinux_state *state,
u32 sid, char **scontext, u32 *scontext_len);
+int security_sid_to_context_inval(struct selinux_state *state,
+ u32 sid, char **scontext, u32 *scontext_len);
+
int security_context_to_sid(struct selinux_state *state,
const char *scontext, u32 scontext_len,
u32 *out_sid, gfp_t gfp);
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
index 8c738c1..e40fecd 100644
--- a/security/selinux/netif.c
+++ b/security/selinux/netif.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Network interface table.
*
@@ -9,10 +10,6 @@
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
* Paul Moore <paul@paul-moore.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/types.h>
@@ -135,9 +132,9 @@
*/
static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid)
{
- int ret;
+ int ret = 0;
struct sel_netif *netif;
- struct sel_netif *new = NULL;
+ struct sel_netif *new;
struct net_device *dev;
/* NOTE: we always use init's network namespace since we don't
@@ -154,32 +151,27 @@
netif = sel_netif_find(ns, ifindex);
if (netif != NULL) {
*sid = netif->nsec.sid;
- ret = 0;
goto out;
}
+
+ ret = security_netif_sid(&selinux_state, dev->name, sid);
+ if (ret != 0)
+ goto out;
new = kzalloc(sizeof(*new), GFP_ATOMIC);
- if (new == NULL) {
- ret = -ENOMEM;
- goto out;
+ if (new) {
+ new->nsec.ns = ns;
+ new->nsec.ifindex = ifindex;
+ new->nsec.sid = *sid;
+ if (sel_netif_insert(new))
+ kfree(new);
}
- ret = security_netif_sid(&selinux_state, dev->name, &new->nsec.sid);
- if (ret != 0)
- goto out;
- new->nsec.ns = ns;
- new->nsec.ifindex = ifindex;
- ret = sel_netif_insert(new);
- if (ret != 0)
- goto out;
- *sid = new->nsec.sid;
out:
spin_unlock_bh(&sel_netif_lock);
dev_put(dev);
- if (unlikely(ret)) {
+ if (unlikely(ret))
pr_warn("SELinux: failure in %s(), unable to determine network interface label (%d)\n",
__func__, ifindex);
- kfree(new);
- }
return ret;
}
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index 186e727..abaab76 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* SELinux NetLabel Support
*
@@ -5,25 +6,10 @@
* subsystem.
*
* Author: Paul Moore <paul@paul-moore.com>
- *
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008
- *
- * 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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
- * the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
*/
#include <linux/spinlock.h>
@@ -288,11 +274,8 @@
int rc;
struct netlbl_lsm_secattr secattr;
struct sk_security_struct *sksec = ep->base.sk->sk_security;
- struct sockaddr *addr;
struct sockaddr_in addr4;
-#if IS_ENABLED(CONFIG_IPV6)
struct sockaddr_in6 addr6;
-#endif
if (ep->base.sk->sk_family != PF_INET &&
ep->base.sk->sk_family != PF_INET6)
@@ -310,16 +293,15 @@
if (ip_hdr(skb)->version == 4) {
addr4.sin_family = AF_INET;
addr4.sin_addr.s_addr = ip_hdr(skb)->saddr;
- addr = (struct sockaddr *)&addr4;
-#if IS_ENABLED(CONFIG_IPV6)
- } else {
+ rc = netlbl_conn_setattr(ep->base.sk, (void *)&addr4, &secattr);
+ } else if (IS_ENABLED(CONFIG_IPV6) && ip_hdr(skb)->version == 6) {
addr6.sin6_family = AF_INET6;
addr6.sin6_addr = ipv6_hdr(skb)->saddr;
- addr = (struct sockaddr *)&addr6;
-#endif
+ rc = netlbl_conn_setattr(ep->base.sk, (void *)&addr6, &secattr);
+ } else {
+ rc = -EAFNOSUPPORT;
}
- rc = netlbl_conn_setattr(ep->base.sk, addr, &secattr);
if (rc == 0)
sksec->nlbl_state = NLBL_LABELED;
diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c
index 8a8a725..621e2e9 100644
--- a/security/selinux/netlink.c
+++ b/security/selinux/netlink.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Netlink event notifications for SELinux.
*
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/types.h>
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index afa0d43..9ab84ef 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Network node table
*
@@ -11,21 +12,10 @@
* This code is heavily based on the "netif" concept originally developed by
* James Morris <jmorris@redhat.com>
* (see security/selinux/netif.c for more information)
- *
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2007
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <linux/types.h>
@@ -199,9 +189,9 @@
*/
static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
{
- int ret = -ENOMEM;
+ int ret;
struct sel_netnode *node;
- struct sel_netnode *new = NULL;
+ struct sel_netnode *new;
spin_lock_bh(&sel_netnode_lock);
node = sel_netnode_find(addr, family);
@@ -210,38 +200,36 @@
spin_unlock_bh(&sel_netnode_lock);
return 0;
}
+
new = kzalloc(sizeof(*new), GFP_ATOMIC);
- if (new == NULL)
- goto out;
switch (family) {
case PF_INET:
ret = security_node_sid(&selinux_state, PF_INET,
addr, sizeof(struct in_addr), sid);
- new->nsec.addr.ipv4 = *(__be32 *)addr;
+ if (new)
+ new->nsec.addr.ipv4 = *(__be32 *)addr;
break;
case PF_INET6:
ret = security_node_sid(&selinux_state, PF_INET6,
addr, sizeof(struct in6_addr), sid);
- new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
+ if (new)
+ new->nsec.addr.ipv6 = *(struct in6_addr *)addr;
break;
default:
BUG();
ret = -EINVAL;
}
- if (ret != 0)
- goto out;
+ if (ret == 0 && new) {
+ new->nsec.family = family;
+ new->nsec.sid = *sid;
+ sel_netnode_insert(new);
+ } else
+ kfree(new);
- new->nsec.family = family;
- new->nsec.sid = *sid;
- sel_netnode_insert(new);
-
-out:
spin_unlock_bh(&sel_netnode_lock);
- if (unlikely(ret)) {
+ if (unlikely(ret))
pr_warn("SELinux: failure in %s(), unable to determine network node label\n",
__func__);
- kfree(new);
- }
return ret;
}
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index 7a141ca..3f8b2c0 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Network port table
*
@@ -10,21 +11,10 @@
* This code is heavily based on the "netif" concept originally developed by
* James Morris <jmorris@redhat.com>
* (see security/selinux/netif.c for more information)
- *
*/
/*
* (c) Copyright Hewlett-Packard Development Company, L.P., 2008
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <linux/types.h>
@@ -147,9 +137,9 @@
*/
static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
{
- int ret = -ENOMEM;
+ int ret;
struct sel_netport *port;
- struct sel_netport *new = NULL;
+ struct sel_netport *new;
spin_lock_bh(&sel_netport_lock);
port = sel_netport_find(protocol, pnum);
@@ -158,25 +148,23 @@
spin_unlock_bh(&sel_netport_lock);
return 0;
}
- new = kzalloc(sizeof(*new), GFP_ATOMIC);
- if (new == NULL)
- goto out;
+
ret = security_port_sid(&selinux_state, protocol, pnum, sid);
if (ret != 0)
goto out;
-
- new->psec.port = pnum;
- new->psec.protocol = protocol;
- new->psec.sid = *sid;
- sel_netport_insert(new);
+ new = kzalloc(sizeof(*new), GFP_ATOMIC);
+ if (new) {
+ new->psec.port = pnum;
+ new->psec.protocol = protocol;
+ new->psec.sid = *sid;
+ sel_netport_insert(new);
+ }
out:
spin_unlock_bh(&sel_netport_lock);
- if (unlikely(ret)) {
+ if (unlikely(ret))
pr_warn("SELinux: failure in %s(), unable to determine network port label\n",
__func__);
- kfree(new);
- }
return ret;
}
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 9cec812..58345ba 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Netlink message type permission tables, for user generated messages.
*
* Author: James Morris <jmorris@redhat.com>
*
* Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/kernel.h>
@@ -83,6 +80,9 @@
{ 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 },
};
static const struct nlmsg_perm nlmsg_tcpdiag_perms[] =
@@ -166,7 +166,7 @@
* structures at the top of this file with the new mappings
* before updating the BUILD_BUG_ON() macro!
*/
- BUILD_BUG_ON(RTM_MAX != (RTM_NEWCHAIN + 3));
+ BUILD_BUG_ON(RTM_MAX != (RTM_NEWNEXTHOP + 3));
err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
sizeof(nlmsg_route_perms));
break;
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index f3a5a13..e6c7643 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Updated: Karl MacMillan <kmacmillan@tresys.com>
*
* Added conditional policy language extensions
@@ -9,9 +10,6 @@
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2003 - 2004 Tresys Technology, LLC
* Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
- * 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.
*/
#include <linux/kernel.h>
@@ -19,6 +17,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
+#include <linux/fs_context.h>
#include <linux/mount.h>
#include <linux/mutex.h>
#include <linux/init.h>
@@ -180,7 +179,7 @@
selnl_notify_setenforce(new_value);
selinux_status_update_setenforce(state, new_value);
if (!new_value)
- call_lsm_notifier(LSM_POLICY_CHANGE, NULL);
+ call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL);
}
length = count;
out:
@@ -1378,7 +1377,7 @@
goto out;
}
- isec = (struct inode_security_struct *)inode->i_security;
+ isec = selinux_inode(inode);
ret = security_genfs_sid(fsi->state, "selinuxfs", page,
SECCLASS_FILE, &sid);
if (ret) {
@@ -1893,7 +1892,7 @@
#define NULL_FILE_NAME "null"
-static int sel_fill_super(struct super_block *sb, void *data, int silent)
+static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct selinux_fs_info *fsi;
int ret;
@@ -1953,7 +1952,7 @@
}
inode->i_ino = ++fsi->last_ino;
- isec = (struct inode_security_struct *)inode->i_security;
+ isec = selinux_inode(inode);
isec->sid = SECINITSID_DEVNULL;
isec->sclass = SECCLASS_CHR_FILE;
isec->initialized = LABEL_INITIALIZED;
@@ -2009,10 +2008,19 @@
return ret;
}
-static struct dentry *sel_mount(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data)
+static int sel_get_tree(struct fs_context *fc)
{
- return mount_single(fs_type, flags, data, sel_fill_super);
+ return get_tree_single(fc, sel_fill_super);
+}
+
+static const struct fs_context_operations sel_context_ops = {
+ .get_tree = sel_get_tree,
+};
+
+static int sel_init_fs_context(struct fs_context *fc)
+{
+ fc->ops = &sel_context_ops;
+ return 0;
}
static void sel_kill_sb(struct super_block *sb)
@@ -2023,7 +2031,7 @@
static struct file_system_type sel_fs_type = {
.name = "selinuxfs",
- .mount = sel_mount,
+ .init_fs_context = sel_init_fs_context,
.kill_sb = sel_kill_sb,
};
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index c0417cf..8c58007 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -93,12 +93,10 @@
newnode->next = prev->next;
prev->next = newnode;
} else {
- newnode->next = flex_array_get_ptr(h->htable, hvalue);
- if (flex_array_put_ptr(h->htable, hvalue, newnode,
- GFP_KERNEL|__GFP_ZERO)) {
- kmem_cache_free(avtab_node_cachep, newnode);
- return NULL;
- }
+ struct avtab_node **n = &h->htable[hvalue];
+
+ newnode->next = *n;
+ *n = newnode;
}
h->nel++;
@@ -111,11 +109,11 @@
struct avtab_node *prev, *cur, *newnode;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
- if (!h || !h->htable)
+ if (!h)
return -EINVAL;
hvalue = avtab_hash(key, h->mask);
- for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue);
+ for (prev = NULL, cur = h->htable[hvalue];
cur;
prev = cur, cur = cur->next) {
if (key->source_type == cur->key.source_type &&
@@ -156,10 +154,10 @@
struct avtab_node *prev, *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
- if (!h || !h->htable)
+ if (!h)
return NULL;
hvalue = avtab_hash(key, h->mask);
- for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue);
+ for (prev = NULL, cur = h->htable[hvalue];
cur;
prev = cur, cur = cur->next) {
if (key->source_type == cur->key.source_type &&
@@ -186,11 +184,11 @@
struct avtab_node *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
- if (!h || !h->htable)
+ if (!h)
return NULL;
hvalue = avtab_hash(key, h->mask);
- for (cur = flex_array_get_ptr(h->htable, hvalue); cur;
+ for (cur = h->htable[hvalue]; cur;
cur = cur->next) {
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
@@ -222,11 +220,11 @@
struct avtab_node *cur;
u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
- if (!h || !h->htable)
+ if (!h)
return NULL;
hvalue = avtab_hash(key, h->mask);
- for (cur = flex_array_get_ptr(h->htable, hvalue); cur;
+ for (cur = h->htable[hvalue]; cur;
cur = cur->next) {
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
@@ -281,11 +279,11 @@
int i;
struct avtab_node *cur, *temp;
- if (!h || !h->htable)
+ if (!h)
return;
for (i = 0; i < h->nslot; i++) {
- cur = flex_array_get_ptr(h->htable, i);
+ cur = h->htable[i];
while (cur) {
temp = cur;
cur = cur->next;
@@ -295,7 +293,7 @@
kmem_cache_free(avtab_node_cachep, temp);
}
}
- flex_array_free(h->htable);
+ kvfree(h->htable);
h->htable = NULL;
h->nslot = 0;
h->mask = 0;
@@ -303,6 +301,7 @@
int avtab_init(struct avtab *h)
{
+ kvfree(h->htable);
h->htable = NULL;
h->nel = 0;
return 0;
@@ -329,8 +328,7 @@
nslot = MAX_AVTAB_HASH_BUCKETS;
mask = nslot - 1;
- h->htable = flex_array_alloc(sizeof(struct avtab_node *), nslot,
- GFP_KERNEL | __GFP_ZERO);
+ h->htable = kvcalloc(nslot, sizeof(void *), GFP_KERNEL);
if (!h->htable)
return -ENOMEM;
@@ -353,7 +351,7 @@
max_chain_len = 0;
chain2_len_sum = 0;
for (i = 0; i < h->nslot; i++) {
- cur = flex_array_get_ptr(h->htable, i);
+ cur = h->htable[i];
if (cur) {
slots_used++;
chain_len = 0;
@@ -646,7 +644,7 @@
return rc;
for (i = 0; i < a->nslot; i++) {
- for (cur = flex_array_get_ptr(a->htable, i); cur;
+ for (cur = a->htable[i]; cur;
cur = cur->next) {
rc = avtab_write_item(p, cur, fp);
if (rc)
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index 0d652fa..837e938 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* An access vector table (avtab) is a hash table
* of access vectors and transition types indexed
@@ -13,9 +14,6 @@
* 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: Yuichi Nakamura <ynakam@hitachisoft.jp>
* Tuned number of hash slots for avtab to reduce memory usage
@@ -24,7 +22,6 @@
#define _SS_AVTAB_H_
#include "security.h"
-#include <linux/flex_array.h>
struct avtab_key {
u16 source_type; /* source type */
@@ -84,11 +81,10 @@
};
struct avtab {
- struct flex_array *htable;
+ struct avtab_node **htable;
u32 nel; /* number of elements */
u32 nslot; /* number of hash slots */
u32 mask; /* mask to compute hash func */
-
};
int avtab_init(struct avtab *);
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
index f49e522..70c378e 100644
--- a/security/selinux/ss/conditional.c
+++ b/security/selinux/ss/conditional.c
@@ -1,10 +1,8 @@
+// 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
- * 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.
*/
#include <linux/kernel.h>
@@ -195,7 +193,6 @@
{
struct policydb *p;
struct cond_bool_datum *booldatum;
- struct flex_array *fa;
booldatum = datum;
p = datap;
@@ -203,10 +200,7 @@
if (!booldatum->value || booldatum->value > p->p_bools.nprim)
return -EINVAL;
- fa = p->sym_val_to_name[SYM_BOOLS];
- if (flex_array_put_ptr(fa, booldatum->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
+ p->sym_val_to_name[SYM_BOOLS][booldatum->value - 1] = key;
p->bool_val_to_struct[booldatum->value - 1] = booldatum;
return 0;
diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h
index ddb43e7..ec846e4 100644
--- a/security/selinux/ss/conditional.h
+++ b/security/selinux/ss/conditional.h
@@ -1,10 +1,8 @@
+/* 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
- * 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.
*/
#ifndef _CONDITIONAL_H_
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index 8f624f8..09929fc 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -347,7 +347,9 @@
{
struct ebitmap_node *n = NULL;
u32 mapunit, count, startbit, index;
+ __le32 ebitmap_start;
u64 map;
+ __le64 mapbits;
__le32 buf[3];
int rc, i;
@@ -381,12 +383,12 @@
goto bad;
for (i = 0; i < count; i++) {
- rc = next_entry(&startbit, fp, sizeof(u32));
+ rc = next_entry(&ebitmap_start, fp, sizeof(u32));
if (rc < 0) {
pr_err("SELinux: ebitmap: truncated map\n");
goto bad;
}
- startbit = le32_to_cpu(startbit);
+ startbit = le32_to_cpu(ebitmap_start);
if (startbit & (mapunit - 1)) {
pr_err("SELinux: ebitmap start bit (%d) is "
@@ -423,12 +425,12 @@
goto bad;
}
- rc = next_entry(&map, fp, sizeof(u64));
+ rc = next_entry(&mapbits, fp, sizeof(u64));
if (rc < 0) {
pr_err("SELinux: ebitmap: truncated map\n");
goto bad;
}
- map = le64_to_cpu(map);
+ map = le64_to_cpu(mapbits);
index = (startbit - n->startbit) / EBITMAP_UNIT_SIZE;
while (map) {
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 39475fb..5e05f5b 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -218,9 +218,7 @@
/*
* Set the MLS fields in the security context structure
* `context' based on the string representation in
- * the string `*scontext'. Update `*scontext' to
- * point to the end of the string representation of
- * the MLS fields.
+ * the string `scontext'.
*
* This function modifies the string in place, inserting
* NULL characters to terminate the MLS fields.
@@ -235,21 +233,24 @@
*/
int mls_context_to_sid(struct policydb *pol,
char oldc,
- char **scontext,
+ char *scontext,
struct context *context,
struct sidtab *s,
u32 def_sid)
{
-
- char delim;
- char *scontextp, *p, *rngptr;
+ char *sensitivity, *cur_cat, *next_cat, *rngptr;
struct level_datum *levdatum;
struct cat_datum *catdatum, *rngdatum;
- int l, rc = -EINVAL;
+ int l, rc, i;
+ char *rangep[2];
if (!pol->mls_enabled) {
- if (def_sid != SECSID_NULL && oldc)
- *scontext += strlen(*scontext) + 1;
+ /*
+ * With no MLS, only return -EINVAL if there is a MLS field
+ * and it did not come from an xattr.
+ */
+ if (oldc && def_sid == SECSID_NULL)
+ return -EINVAL;
return 0;
}
@@ -261,113 +262,94 @@
struct context *defcon;
if (def_sid == SECSID_NULL)
- goto out;
+ return -EINVAL;
defcon = sidtab_search(s, def_sid);
if (!defcon)
- goto out;
+ return -EINVAL;
- rc = mls_context_cpy(context, defcon);
- goto out;
+ return mls_context_cpy(context, defcon);
}
- /* Extract low sensitivity. */
- scontextp = p = *scontext;
- while (*p && *p != ':' && *p != '-')
- p++;
+ /*
+ * If we're dealing with a range, figure out where the two parts
+ * of the range begin.
+ */
+ rangep[0] = scontext;
+ rangep[1] = strchr(scontext, '-');
+ if (rangep[1]) {
+ rangep[1][0] = '\0';
+ rangep[1]++;
+ }
- delim = *p;
- if (delim != '\0')
- *p++ = '\0';
-
+ /* For each part of the range: */
for (l = 0; l < 2; l++) {
- levdatum = hashtab_search(pol->p_levels.table, scontextp);
- if (!levdatum) {
- rc = -EINVAL;
- goto out;
- }
+ /* Split sensitivity and category set. */
+ sensitivity = rangep[l];
+ if (sensitivity == NULL)
+ break;
+ next_cat = strchr(sensitivity, ':');
+ if (next_cat)
+ *(next_cat++) = '\0';
+ /* Parse sensitivity. */
+ levdatum = hashtab_search(pol->p_levels.table, sensitivity);
+ if (!levdatum)
+ return -EINVAL;
context->range.level[l].sens = levdatum->level->sens;
- if (delim == ':') {
- /* Extract category set. */
- while (1) {
- scontextp = p;
- while (*p && *p != ',' && *p != '-')
- p++;
- delim = *p;
- if (delim != '\0')
- *p++ = '\0';
+ /* Extract category set. */
+ while (next_cat != NULL) {
+ cur_cat = next_cat;
+ next_cat = strchr(next_cat, ',');
+ if (next_cat != NULL)
+ *(next_cat++) = '\0';
- /* Separate into range if exists */
- rngptr = strchr(scontextp, '.');
- if (rngptr != NULL) {
- /* Remove '.' */
- *rngptr++ = '\0';
- }
+ /* Separate into range if exists */
+ rngptr = strchr(cur_cat, '.');
+ if (rngptr != NULL) {
+ /* Remove '.' */
+ *rngptr++ = '\0';
+ }
- catdatum = hashtab_search(pol->p_cats.table,
- scontextp);
- if (!catdatum) {
- rc = -EINVAL;
- goto out;
- }
+ catdatum = hashtab_search(pol->p_cats.table, cur_cat);
+ if (!catdatum)
+ return -EINVAL;
- rc = ebitmap_set_bit(&context->range.level[l].cat,
- catdatum->value - 1, 1);
+ rc = ebitmap_set_bit(&context->range.level[l].cat,
+ catdatum->value - 1, 1);
+ if (rc)
+ return rc;
+
+ /* If range, set all categories in range */
+ if (rngptr == NULL)
+ continue;
+
+ rngdatum = hashtab_search(pol->p_cats.table, rngptr);
+ if (!rngdatum)
+ return -EINVAL;
+
+ if (catdatum->value >= rngdatum->value)
+ return -EINVAL;
+
+ for (i = catdatum->value; i < rngdatum->value; i++) {
+ rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
if (rc)
- goto out;
-
- /* If range, set all categories in range */
- if (rngptr) {
- int i;
-
- rngdatum = hashtab_search(pol->p_cats.table, rngptr);
- if (!rngdatum) {
- rc = -EINVAL;
- goto out;
- }
-
- if (catdatum->value >= rngdatum->value) {
- rc = -EINVAL;
- goto out;
- }
-
- for (i = catdatum->value; i < rngdatum->value; i++) {
- rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
- if (rc)
- goto out;
- }
- }
-
- if (delim != ',')
- break;
+ return rc;
}
}
- if (delim == '-') {
- /* Extract high sensitivity. */
- scontextp = p;
- while (*p && *p != ':')
- p++;
-
- delim = *p;
- if (delim != '\0')
- *p++ = '\0';
- } else
- break;
}
- if (l == 0) {
+ /* If we didn't see a '-', the range start is also the range end. */
+ if (rangep[1] == NULL) {
context->range.level[1].sens = context->range.level[0].sens;
rc = ebitmap_cpy(&context->range.level[1].cat,
&context->range.level[0].cat);
if (rc)
- goto out;
+ return rc;
}
- *scontext = ++p;
- rc = 0;
-out:
- return rc;
+
+ return 0;
}
/*
@@ -379,21 +361,19 @@
int mls_from_string(struct policydb *p, char *str, struct context *context,
gfp_t gfp_mask)
{
- char *tmpstr, *freestr;
+ char *tmpstr;
int rc;
if (!p->mls_enabled)
return -EINVAL;
- /* we need freestr because mls_context_to_sid will change
- the value of tmpstr */
- tmpstr = freestr = kstrdup(str, gfp_mask);
+ tmpstr = kstrdup(str, gfp_mask);
if (!tmpstr) {
rc = -ENOMEM;
} else {
- rc = mls_context_to_sid(p, ':', &tmpstr, context,
+ rc = mls_context_to_sid(p, ':', tmpstr, context,
NULL, SECSID_NULL);
- kfree(freestr);
+ kfree(tmpstr);
}
return rc;
@@ -460,16 +440,17 @@
/*
* Convert the MLS fields in the security context
- * structure `c' from the values specified in the
- * policy `oldp' to the values specified in the policy `newp'.
+ * structure `oldc' from the values specified in the
+ * 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 *c)
+ struct context *oldc,
+ struct context *newc)
{
struct level_datum *levdatum;
struct cat_datum *catdatum;
- struct ebitmap bitmap;
struct ebitmap_node *node;
int l, i;
@@ -479,28 +460,25 @@
for (l = 0; l < 2; l++) {
levdatum = hashtab_search(newp->p_levels.table,
sym_name(oldp, SYM_LEVELS,
- c->range.level[l].sens - 1));
+ oldc->range.level[l].sens - 1));
if (!levdatum)
return -EINVAL;
- c->range.level[l].sens = levdatum->level->sens;
+ newc->range.level[l].sens = levdatum->level->sens;
- ebitmap_init(&bitmap);
- ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) {
+ ebitmap_for_each_positive_bit(&oldc->range.level[l].cat,
+ node, i) {
int rc;
catdatum = hashtab_search(newp->p_cats.table,
sym_name(oldp, SYM_CATS, i));
if (!catdatum)
return -EINVAL;
- rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
+ rc = ebitmap_set_bit(&newc->range.level[l].cat,
+ catdatum->value - 1, 1);
if (rc)
return rc;
-
- cond_resched();
}
- ebitmap_destroy(&c->range.level[l].cat);
- c->range.level[l].cat = bitmap;
}
return 0;
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 9a3ff7a..7954b1e 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -34,7 +34,7 @@
int mls_context_to_sid(struct policydb *p,
char oldc,
- char **scontext,
+ char *scontext,
struct context *context,
struct sidtab *s,
u32 def_sid);
@@ -46,7 +46,8 @@
int mls_convert_context(struct policydb *oldp,
struct policydb *newp,
- struct context *context);
+ struct context *oldc,
+ struct context *newc);
int mls_compute_sid(struct policydb *p,
struct context *scontext,
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index f4eadd3..1260f5f 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Implementation of the policy database.
*
@@ -25,9 +26,6 @@
* Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004 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.
*/
#include <linux/kernel.h>
@@ -36,7 +34,6 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/audit.h>
-#include <linux/flex_array.h>
#include "security.h"
#include "policydb.h"
@@ -180,433 +177,6 @@
}
/*
- * Initialize the role table.
- */
-static int roles_init(struct policydb *p)
-{
- char *key = NULL;
- int rc;
- struct role_datum *role;
-
- role = kzalloc(sizeof(*role), GFP_KERNEL);
- if (!role)
- return -ENOMEM;
-
- rc = -EINVAL;
- role->value = ++p->p_roles.nprim;
- if (role->value != OBJECT_R_VAL)
- goto out;
-
- rc = -ENOMEM;
- key = kstrdup(OBJECT_R, GFP_KERNEL);
- if (!key)
- goto out;
-
- rc = hashtab_insert(p->p_roles.table, key, role);
- if (rc)
- goto out;
-
- return 0;
-out:
- kfree(key);
- kfree(role);
- return rc;
-}
-
-static u32 filenametr_hash(struct hashtab *h, const void *k)
-{
- const struct filename_trans *ft = k;
- unsigned long hash;
- unsigned int byte_num;
- unsigned char focus;
-
- hash = ft->stype ^ ft->ttype ^ ft->tclass;
-
- byte_num = 0;
- while ((focus = ft->name[byte_num++]))
- hash = partial_name_hash(focus, hash);
- return hash & (h->size - 1);
-}
-
-static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2)
-{
- const struct filename_trans *ft1 = k1;
- const struct filename_trans *ft2 = k2;
- int v;
-
- v = ft1->stype - ft2->stype;
- if (v)
- return v;
-
- v = ft1->ttype - ft2->ttype;
- if (v)
- return v;
-
- v = ft1->tclass - ft2->tclass;
- if (v)
- return v;
-
- return strcmp(ft1->name, ft2->name);
-
-}
-
-static u32 rangetr_hash(struct hashtab *h, const void *k)
-{
- const struct range_trans *key = k;
- return (key->source_type + (key->target_type << 3) +
- (key->target_class << 5)) & (h->size - 1);
-}
-
-static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
-{
- const struct range_trans *key1 = k1, *key2 = k2;
- int v;
-
- v = key1->source_type - key2->source_type;
- if (v)
- return v;
-
- v = key1->target_type - key2->target_type;
- if (v)
- return v;
-
- v = key1->target_class - key2->target_class;
-
- return v;
-}
-
-/*
- * Initialize a policy database structure.
- */
-static int policydb_init(struct policydb *p)
-{
- int i, rc;
-
- memset(p, 0, sizeof(*p));
-
- for (i = 0; i < SYM_NUM; i++) {
- rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
- if (rc)
- goto out;
- }
-
- rc = avtab_init(&p->te_avtab);
- if (rc)
- goto out;
-
- rc = roles_init(p);
- if (rc)
- goto out;
-
- rc = cond_policydb_init(p);
- if (rc)
- goto out;
-
- p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp, (1 << 10));
- if (!p->filename_trans) {
- rc = -ENOMEM;
- goto out;
- }
-
- p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256);
- if (!p->range_tr) {
- rc = -ENOMEM;
- goto out;
- }
-
- ebitmap_init(&p->filename_trans_ttypes);
- ebitmap_init(&p->policycaps);
- ebitmap_init(&p->permissive_map);
-
- return 0;
-out:
- hashtab_destroy(p->filename_trans);
- hashtab_destroy(p->range_tr);
- for (i = 0; i < SYM_NUM; i++)
- hashtab_destroy(p->symtab[i].table);
- return rc;
-}
-
-/*
- * The following *_index functions are used to
- * define the val_to_name and val_to_struct arrays
- * in a policy database structure. The val_to_name
- * arrays are used when converting security context
- * structures into string representations. The
- * val_to_struct arrays are used when the attributes
- * of a class, role, or user are needed.
- */
-
-static int common_index(void *key, void *datum, void *datap)
-{
- struct policydb *p;
- struct common_datum *comdatum;
- struct flex_array *fa;
-
- comdatum = datum;
- p = datap;
- if (!comdatum->value || comdatum->value > p->p_commons.nprim)
- return -EINVAL;
-
- fa = p->sym_val_to_name[SYM_COMMONS];
- if (flex_array_put_ptr(fa, comdatum->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
- return 0;
-}
-
-static int class_index(void *key, void *datum, void *datap)
-{
- struct policydb *p;
- struct class_datum *cladatum;
- struct flex_array *fa;
-
- cladatum = datum;
- p = datap;
- if (!cladatum->value || cladatum->value > p->p_classes.nprim)
- return -EINVAL;
- fa = p->sym_val_to_name[SYM_CLASSES];
- if (flex_array_put_ptr(fa, cladatum->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
- p->class_val_to_struct[cladatum->value - 1] = cladatum;
- return 0;
-}
-
-static int role_index(void *key, void *datum, void *datap)
-{
- struct policydb *p;
- struct role_datum *role;
- struct flex_array *fa;
-
- role = datum;
- p = datap;
- if (!role->value
- || role->value > p->p_roles.nprim
- || role->bounds > p->p_roles.nprim)
- return -EINVAL;
-
- fa = p->sym_val_to_name[SYM_ROLES];
- if (flex_array_put_ptr(fa, role->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
- p->role_val_to_struct[role->value - 1] = role;
- return 0;
-}
-
-static int type_index(void *key, void *datum, void *datap)
-{
- struct policydb *p;
- struct type_datum *typdatum;
- struct flex_array *fa;
-
- typdatum = datum;
- p = datap;
-
- if (typdatum->primary) {
- if (!typdatum->value
- || typdatum->value > p->p_types.nprim
- || typdatum->bounds > p->p_types.nprim)
- return -EINVAL;
- fa = p->sym_val_to_name[SYM_TYPES];
- if (flex_array_put_ptr(fa, typdatum->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
-
- fa = p->type_val_to_struct_array;
- if (flex_array_put_ptr(fa, typdatum->value - 1, typdatum,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
- }
-
- return 0;
-}
-
-static int user_index(void *key, void *datum, void *datap)
-{
- struct policydb *p;
- struct user_datum *usrdatum;
- struct flex_array *fa;
-
- usrdatum = datum;
- p = datap;
- if (!usrdatum->value
- || usrdatum->value > p->p_users.nprim
- || usrdatum->bounds > p->p_users.nprim)
- return -EINVAL;
-
- fa = p->sym_val_to_name[SYM_USERS];
- if (flex_array_put_ptr(fa, usrdatum->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
- p->user_val_to_struct[usrdatum->value - 1] = usrdatum;
- return 0;
-}
-
-static int sens_index(void *key, void *datum, void *datap)
-{
- struct policydb *p;
- struct level_datum *levdatum;
- struct flex_array *fa;
-
- levdatum = datum;
- p = datap;
-
- if (!levdatum->isalias) {
- if (!levdatum->level->sens ||
- levdatum->level->sens > p->p_levels.nprim)
- return -EINVAL;
- fa = p->sym_val_to_name[SYM_LEVELS];
- if (flex_array_put_ptr(fa, levdatum->level->sens - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
- }
-
- return 0;
-}
-
-static int cat_index(void *key, void *datum, void *datap)
-{
- struct policydb *p;
- struct cat_datum *catdatum;
- struct flex_array *fa;
-
- catdatum = datum;
- p = datap;
-
- if (!catdatum->isalias) {
- if (!catdatum->value || catdatum->value > p->p_cats.nprim)
- return -EINVAL;
- fa = p->sym_val_to_name[SYM_CATS];
- if (flex_array_put_ptr(fa, catdatum->value - 1, key,
- GFP_KERNEL | __GFP_ZERO))
- BUG();
- }
-
- return 0;
-}
-
-static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) =
-{
- common_index,
- class_index,
- role_index,
- type_index,
- user_index,
- cond_index_bool,
- sens_index,
- cat_index,
-};
-
-#ifdef DEBUG_HASHES
-static void hash_eval(struct hashtab *h, const char *hash_name)
-{
- struct hashtab_info info;
-
- hashtab_stat(h, &info);
- pr_debug("SELinux: %s: %d entries and %d/%d buckets used, "
- "longest chain length %d\n", hash_name, h->nel,
- info.slots_used, h->size, info.max_chain_len);
-}
-
-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]);
-}
-
-#else
-static inline void hash_eval(struct hashtab *h, char *hash_name)
-{
-}
-#endif
-
-/*
- * Define the other val_to_name and val_to_struct arrays
- * in a policy database structure.
- *
- * Caller must clean up on failure.
- */
-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);
- 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);
-
-#ifdef DEBUG_HASHES
- avtab_hash_eval(&p->te_avtab, "rules");
- symtab_hash_eval(p->symtab);
-#endif
-
- p->class_val_to_struct = kcalloc(p->p_classes.nprim,
- sizeof(*p->class_val_to_struct),
- GFP_KERNEL);
- 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);
- 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);
- if (!p->user_val_to_struct)
- return -ENOMEM;
-
- /* Yes, I want the sizeof the pointer, not the structure */
- p->type_val_to_struct_array = flex_array_alloc(sizeof(struct type_datum *),
- p->p_types.nprim,
- GFP_KERNEL | __GFP_ZERO);
- if (!p->type_val_to_struct_array)
- return -ENOMEM;
-
- rc = flex_array_prealloc(p->type_val_to_struct_array, 0,
- p->p_types.nprim, GFP_KERNEL | __GFP_ZERO);
- if (rc)
- goto out;
-
- rc = cond_init_bool_indexes(p);
- if (rc)
- goto out;
-
- for (i = 0; i < SYM_NUM; i++) {
- p->sym_val_to_name[i] = flex_array_alloc(sizeof(char *),
- p->symtab[i].nprim,
- GFP_KERNEL | __GFP_ZERO);
- if (!p->sym_val_to_name[i])
- return -ENOMEM;
-
- rc = flex_array_prealloc(p->sym_val_to_name[i],
- 0, p->symtab[i].nprim,
- GFP_KERNEL | __GFP_ZERO);
- if (rc)
- goto out;
-
- rc = hashtab_map(p->symtab[i].table, index_f[i], p);
- if (rc)
- goto out;
- }
- rc = 0;
-out:
- return rc;
-}
-
-/*
* The following *_destroy functions are used to
* free any memory allocated for each kind of
* symbol data in the policy database.
@@ -732,7 +302,8 @@
kfree(key);
if (datum) {
levdatum = datum;
- ebitmap_destroy(&levdatum->level->cat);
+ if (levdatum->level)
+ ebitmap_destroy(&levdatum->level->cat);
kfree(levdatum->level);
}
kfree(datum);
@@ -761,6 +332,7 @@
static int filenametr_destroy(void *key, void *datum, void *p)
{
struct filename_trans *ft = key;
+
kfree(ft->name);
kfree(key);
kfree(datum);
@@ -771,6 +343,7 @@
static int range_tr_destroy(void *key, void *datum, void *p)
{
struct mls_range *rt = datum;
+
kfree(key);
ebitmap_destroy(&rt->level[0].cat);
ebitmap_destroy(&rt->level[1].cat);
@@ -793,6 +366,397 @@
}
/*
+ * Initialize the role table.
+ */
+static int roles_init(struct policydb *p)
+{
+ char *key = NULL;
+ int rc;
+ struct role_datum *role;
+
+ role = kzalloc(sizeof(*role), GFP_KERNEL);
+ if (!role)
+ return -ENOMEM;
+
+ rc = -EINVAL;
+ role->value = ++p->p_roles.nprim;
+ if (role->value != OBJECT_R_VAL)
+ goto out;
+
+ rc = -ENOMEM;
+ key = kstrdup(OBJECT_R, GFP_KERNEL);
+ if (!key)
+ goto out;
+
+ rc = hashtab_insert(p->p_roles.table, key, role);
+ if (rc)
+ goto out;
+
+ return 0;
+out:
+ kfree(key);
+ kfree(role);
+ return rc;
+}
+
+static u32 filenametr_hash(struct hashtab *h, const void *k)
+{
+ const struct filename_trans *ft = k;
+ unsigned long hash;
+ unsigned int byte_num;
+ unsigned char focus;
+
+ hash = ft->stype ^ ft->ttype ^ ft->tclass;
+
+ byte_num = 0;
+ while ((focus = ft->name[byte_num++]))
+ hash = partial_name_hash(focus, hash);
+ return hash & (h->size - 1);
+}
+
+static int filenametr_cmp(struct hashtab *h, const void *k1, const void *k2)
+{
+ const struct filename_trans *ft1 = k1;
+ const struct filename_trans *ft2 = k2;
+ int v;
+
+ v = ft1->stype - ft2->stype;
+ if (v)
+ return v;
+
+ v = ft1->ttype - ft2->ttype;
+ if (v)
+ return v;
+
+ v = ft1->tclass - ft2->tclass;
+ if (v)
+ return v;
+
+ return strcmp(ft1->name, ft2->name);
+
+}
+
+static u32 rangetr_hash(struct hashtab *h, const void *k)
+{
+ const struct range_trans *key = k;
+
+ return (key->source_type + (key->target_type << 3) +
+ (key->target_class << 5)) & (h->size - 1);
+}
+
+static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
+{
+ const struct range_trans *key1 = k1, *key2 = k2;
+ int v;
+
+ v = key1->source_type - key2->source_type;
+ if (v)
+ return v;
+
+ v = key1->target_type - key2->target_type;
+ if (v)
+ return v;
+
+ v = key1->target_class - key2->target_class;
+
+ return v;
+}
+
+/*
+ * Initialize a policy database structure.
+ */
+static int policydb_init(struct policydb *p)
+{
+ int i, rc;
+
+ memset(p, 0, sizeof(*p));
+
+ for (i = 0; i < SYM_NUM; i++) {
+ rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
+ if (rc)
+ goto out;
+ }
+
+ rc = avtab_init(&p->te_avtab);
+ if (rc)
+ goto out;
+
+ rc = roles_init(p);
+ if (rc)
+ goto out;
+
+ rc = cond_policydb_init(p);
+ if (rc)
+ goto out;
+
+ p->filename_trans = hashtab_create(filenametr_hash, filenametr_cmp,
+ (1 << 10));
+ if (!p->filename_trans) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256);
+ if (!p->range_tr) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ ebitmap_init(&p->filename_trans_ttypes);
+ ebitmap_init(&p->policycaps);
+ ebitmap_init(&p->permissive_map);
+
+ return 0;
+out:
+ hashtab_destroy(p->filename_trans);
+ hashtab_destroy(p->range_tr);
+ for (i = 0; i < SYM_NUM; i++) {
+ hashtab_map(p->symtab[i].table, destroy_f[i], NULL);
+ hashtab_destroy(p->symtab[i].table);
+ }
+ return rc;
+}
+
+/*
+ * The following *_index functions are used to
+ * define the val_to_name and val_to_struct arrays
+ * in a policy database structure. The val_to_name
+ * arrays are used when converting security context
+ * structures into string representations. The
+ * val_to_struct arrays are used when the attributes
+ * of a class, role, or user are needed.
+ */
+
+static int common_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct common_datum *comdatum;
+
+ comdatum = datum;
+ p = datap;
+ if (!comdatum->value || comdatum->value > p->p_commons.nprim)
+ return -EINVAL;
+
+ p->sym_val_to_name[SYM_COMMONS][comdatum->value - 1] = key;
+
+ return 0;
+}
+
+static int class_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct class_datum *cladatum;
+
+ cladatum = datum;
+ p = datap;
+ if (!cladatum->value || cladatum->value > p->p_classes.nprim)
+ return -EINVAL;
+
+ p->sym_val_to_name[SYM_CLASSES][cladatum->value - 1] = key;
+ p->class_val_to_struct[cladatum->value - 1] = cladatum;
+ return 0;
+}
+
+static int role_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct role_datum *role;
+
+ role = datum;
+ p = datap;
+ 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;
+ p->role_val_to_struct[role->value - 1] = role;
+ return 0;
+}
+
+static int type_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct type_datum *typdatum;
+
+ typdatum = datum;
+ p = datap;
+
+ if (typdatum->primary) {
+ 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;
+ }
+
+ return 0;
+}
+
+static int user_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct user_datum *usrdatum;
+
+ usrdatum = datum;
+ p = datap;
+ 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;
+ p->user_val_to_struct[usrdatum->value - 1] = usrdatum;
+ return 0;
+}
+
+static int sens_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct level_datum *levdatum;
+
+ levdatum = datum;
+ p = datap;
+
+ if (!levdatum->isalias) {
+ 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;
+ }
+
+ return 0;
+}
+
+static int cat_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct cat_datum *catdatum;
+
+ catdatum = datum;
+ p = datap;
+
+ if (!catdatum->isalias) {
+ if (!catdatum->value || catdatum->value > p->p_cats.nprim)
+ return -EINVAL;
+
+ p->sym_val_to_name[SYM_CATS][catdatum->value - 1] = key;
+ }
+
+ return 0;
+}
+
+static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) =
+{
+ common_index,
+ class_index,
+ role_index,
+ type_index,
+ user_index,
+ cond_index_bool,
+ sens_index,
+ cat_index,
+};
+
+#ifdef DEBUG_HASHES
+static void hash_eval(struct hashtab *h, const char *hash_name)
+{
+ struct hashtab_info info;
+
+ hashtab_stat(h, &info);
+ pr_debug("SELinux: %s: %d entries and %d/%d buckets used, longest chain length %d\n",
+ hash_name, h->nel, info.slots_used, h->size,
+ info.max_chain_len);
+}
+
+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]);
+}
+
+#else
+static inline void hash_eval(struct hashtab *h, char *hash_name)
+{
+}
+#endif
+
+/*
+ * Define the other val_to_name and val_to_struct arrays
+ * in a policy database structure.
+ *
+ * Caller must clean up on failure.
+ */
+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);
+ 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);
+
+#ifdef DEBUG_HASHES
+ avtab_hash_eval(&p->te_avtab, "rules");
+ symtab_hash_eval(p->symtab);
+#endif
+
+ p->class_val_to_struct = kcalloc(p->p_classes.nprim,
+ sizeof(*p->class_val_to_struct),
+ GFP_KERNEL);
+ 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);
+ 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);
+ 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);
+ if (!p->type_val_to_struct)
+ return -ENOMEM;
+
+ rc = cond_init_bool_indexes(p);
+ if (rc)
+ goto out;
+
+ for (i = 0; i < SYM_NUM; i++) {
+ p->sym_val_to_name[i] = kvcalloc(p->symtab[i].nprim,
+ sizeof(char *),
+ GFP_KERNEL);
+ if (!p->sym_val_to_name[i])
+ return -ENOMEM;
+
+ rc = hashtab_map(p->symtab[i].table, index_f[i], p);
+ if (rc)
+ goto out;
+ }
+ rc = 0;
+out:
+ return rc;
+}
+
+/*
* Free any memory allocated by a policy database structure.
*/
void policydb_destroy(struct policydb *p)
@@ -809,16 +773,13 @@
hashtab_destroy(p->symtab[i].table);
}
- for (i = 0; i < SYM_NUM; i++) {
- if (p->sym_val_to_name[i])
- flex_array_free(p->sym_val_to_name[i]);
- }
+ for (i = 0; i < SYM_NUM; i++)
+ kvfree(p->sym_val_to_name[i]);
kfree(p->class_val_to_struct);
kfree(p->role_val_to_struct);
kfree(p->user_val_to_struct);
- if (p->type_val_to_struct_array)
- flex_array_free(p->type_val_to_struct_array);
+ kvfree(p->type_val_to_struct);
avtab_destroy(&p->te_avtab);
@@ -872,15 +833,9 @@
hashtab_destroy(p->range_tr);
if (p->type_attr_map_array) {
- for (i = 0; i < p->p_types.nprim; i++) {
- struct ebitmap *e;
-
- e = flex_array_get(p->type_attr_map_array, i);
- if (!e)
- continue;
- ebitmap_destroy(e);
- }
- flex_array_free(p->type_attr_map_array);
+ for (i = 0; i < p->p_types.nprim; i++)
+ ebitmap_destroy(&p->type_attr_map_array[i]);
+ kvfree(p->type_attr_map_array);
}
ebitmap_destroy(&p->filename_trans_ttypes);
@@ -909,13 +864,21 @@
if (!c->context[0].user) {
pr_err("SELinux: SID %s was never defined.\n",
c->u.name);
+ sidtab_destroy(s);
+ goto out;
+ }
+ if (c->sid[0] == SECSID_NULL || c->sid[0] > SECINITSID_NUM) {
+ pr_err("SELinux: Initial SID %s out of range.\n",
+ c->u.name);
+ sidtab_destroy(s);
goto out;
}
- rc = sidtab_insert(s, c->sid[0], &c->context[0]);
+ rc = sidtab_set_initial(s, c->sid[0], &c->context[0]);
if (rc) {
pr_err("SELinux: unable to load initial SID %s.\n",
c->u.name);
+ sidtab_destroy(s);
goto out;
}
}
@@ -1761,8 +1724,7 @@
return -EINVAL;
}
- upper = flex_array_get_ptr(p->type_val_to_struct_array,
- upper->bounds - 1);
+ upper = p->type_val_to_struct[upper->bounds - 1];
BUG_ON(!upper);
if (upper->attribute) {
@@ -2108,6 +2070,7 @@
{
int i, j, rc;
u32 nel, len;
+ __be64 prefixbuf[1];
__le32 buf[3];
struct ocontext *l, *c;
u32 nodebuf[8];
@@ -2217,21 +2180,30 @@
goto out;
break;
}
- case OCON_IBPKEY:
- rc = next_entry(nodebuf, fp, sizeof(u32) * 4);
+ case OCON_IBPKEY: {
+ u32 pkey_lo, pkey_hi;
+
+ rc = next_entry(prefixbuf, fp, sizeof(u64));
if (rc)
goto out;
- c->u.ibpkey.subnet_prefix = be64_to_cpu(*((__be64 *)nodebuf));
+ /* we need to have subnet_prefix in CPU order */
+ c->u.ibpkey.subnet_prefix = be64_to_cpu(prefixbuf[0]);
- if (nodebuf[2] > 0xffff ||
- nodebuf[3] > 0xffff) {
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
+ if (rc)
+ goto out;
+
+ pkey_lo = le32_to_cpu(buf[0]);
+ pkey_hi = le32_to_cpu(buf[1]);
+
+ if (pkey_lo > U16_MAX || pkey_hi > U16_MAX) {
rc = -EINVAL;
goto out;
}
- c->u.ibpkey.low_pkey = le32_to_cpu(nodebuf[2]);
- c->u.ibpkey.high_pkey = le32_to_cpu(nodebuf[3]);
+ c->u.ibpkey.low_pkey = pkey_lo;
+ c->u.ibpkey.high_pkey = pkey_hi;
rc = context_read_and_validate(&c->context[0],
p,
@@ -2239,7 +2211,10 @@
if (rc)
goto out;
break;
- case OCON_IBENDPORT:
+ }
+ case OCON_IBENDPORT: {
+ u32 port;
+
rc = next_entry(buf, fp, sizeof(u32) * 2);
if (rc)
goto out;
@@ -2249,12 +2224,13 @@
if (rc)
goto out;
- if (buf[1] > 0xff || buf[1] == 0) {
+ port = le32_to_cpu(buf[1]);
+ if (port > U8_MAX || port == 0) {
rc = -EINVAL;
goto out;
}
- c->u.ibendport.port = le32_to_cpu(buf[1]);
+ c->u.ibendport.port = port;
rc = context_read_and_validate(&c->context[0],
p,
@@ -2262,7 +2238,8 @@
if (rc)
goto out;
break;
- }
+ } /* end case */
+ } /* end switch */
}
}
rc = 0;
@@ -2519,24 +2496,19 @@
if (rc)
goto bad;
- rc = -ENOMEM;
- p->type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap),
- p->p_types.nprim,
- GFP_KERNEL | __GFP_ZERO);
+ 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;
- /* preallocate so we don't have to worry about the put ever failing */
- rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim,
- GFP_KERNEL | __GFP_ZERO);
- if (rc)
- goto bad;
+ /* just in case ebitmap_init() becomes more than just a memset(0): */
+ for (i = 0; i < p->p_types.nprim; i++)
+ ebitmap_init(&p->type_attr_map_array[i]);
for (i = 0; i < p->p_types.nprim; i++) {
- struct ebitmap *e = flex_array_get(p->type_attr_map_array, i);
+ struct ebitmap *e = &p->type_attr_map_array[i];
- BUG_ON(!e);
- ebitmap_init(e);
if (p->policyvers >= POLICYDB_VERSION_AVTAB) {
rc = ebitmap_read(e, fp);
if (rc)
@@ -3105,6 +3077,7 @@
{
unsigned int i, j, rc;
size_t nel, len;
+ __be64 prefixbuf[1];
__le32 buf[3];
u32 nodebuf[8];
struct ocontext *c;
@@ -3192,12 +3165,17 @@
return rc;
break;
case OCON_IBPKEY:
- *((__be64 *)nodebuf) = cpu_to_be64(c->u.ibpkey.subnet_prefix);
+ /* subnet_prefix is in CPU order */
+ prefixbuf[0] = cpu_to_be64(c->u.ibpkey.subnet_prefix);
- nodebuf[2] = cpu_to_le32(c->u.ibpkey.low_pkey);
- nodebuf[3] = cpu_to_le32(c->u.ibpkey.high_pkey);
+ rc = put_entry(prefixbuf, sizeof(u64), 1, fp);
+ if (rc)
+ return rc;
- rc = put_entry(nodebuf, sizeof(u32), 4, fp);
+ buf[0] = cpu_to_le32(c->u.ibpkey.low_pkey);
+ buf[1] = cpu_to_le32(c->u.ibpkey.high_pkey);
+
+ rc = put_entry(buf, sizeof(u32), 2, fp);
if (rc)
return rc;
rc = context_write(p, &c->context[0], fp);
@@ -3524,9 +3502,8 @@
return rc;
for (i = 0; i < p->p_types.nprim; i++) {
- struct ebitmap *e = flex_array_get(p->type_attr_map_array, i);
+ struct ebitmap *e = &p->type_attr_map_array[i];
- BUG_ON(!e);
rc = ebitmap_write(e, fp);
if (rc)
return rc;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 215f8f3..162d0e7 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* A policy database (policydb) specifies the
* configuration data for the security policy.
@@ -16,16 +17,11 @@
*
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004 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.
*/
#ifndef _SS_POLICYDB_H_
#define _SS_POLICYDB_H_
-#include <linux/flex_array.h>
-
#include "symtab.h"
#include "avtab.h"
#include "sidtab.h"
@@ -251,13 +247,13 @@
#define p_cats symtab[SYM_CATS]
/* symbol names indexed by (value - 1) */
- struct flex_array *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;
struct role_datum **role_val_to_struct;
struct user_datum **user_val_to_struct;
- struct flex_array *type_val_to_struct_array;
+ struct type_datum **type_val_to_struct;
/* type enforcement access vectors and transitions */
struct avtab te_avtab;
@@ -294,7 +290,7 @@
struct hashtab *range_tr;
/* type -> attribute reverse mapping */
- struct flex_array *type_attr_map_array;
+ struct ebitmap *type_attr_map_array;
struct ebitmap policycaps;
@@ -369,9 +365,7 @@
static inline char *sym_name(struct policydb *p, unsigned int sym_num, unsigned int element_nr)
{
- struct flex_array *fa = p->sym_val_to_name[sym_num];
-
- return flex_array_get_ptr(fa, element_nr);
+ return p->sym_val_to_name[sym_num][element_nr];
}
extern u16 string_to_security_class(struct policydb *p, const char *name);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index f3def29..a5813c7 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Implementation of the security services.
*
@@ -35,9 +36,6 @@
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC
* Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
- * 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.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -49,8 +47,6 @@
#include <linux/sched.h>
#include <linux/audit.h>
#include <linux/mutex.h>
-#include <linux/selinux.h>
-#include <linux/flex_array.h>
#include <linux/vmalloc.h>
#include <net/netlabel.h>
@@ -71,7 +67,7 @@
#include "audit.h"
/* Policy capability names */
-char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
+const char *selinux_policycap_names[__POLICYDB_CAPABILITY_MAX] = {
"network_peer_controls",
"open_perms",
"extended_socket_class",
@@ -546,15 +542,13 @@
struct type_datum *target;
u32 masked = 0;
- source = flex_array_get_ptr(policydb->type_val_to_struct_array,
- scontext->type - 1);
+ source = policydb->type_val_to_struct[scontext->type - 1];
BUG_ON(!source);
if (!source->bounds)
return;
- target = flex_array_get_ptr(policydb->type_val_to_struct_array,
- tcontext->type - 1);
+ target = policydb->type_val_to_struct[tcontext->type - 1];
BUG_ON(!target);
memset(&lo_avd, 0, sizeof(lo_avd));
@@ -654,12 +648,8 @@
*/
avkey.target_class = tclass;
avkey.specified = AVTAB_AV | AVTAB_XPERMS;
- sattr = flex_array_get(policydb->type_attr_map_array,
- scontext->type - 1);
- BUG_ON(!sattr);
- tattr = flex_array_get(policydb->type_attr_map_array,
- tcontext->type - 1);
- BUG_ON(!tattr);
+ sattr = &policydb->type_attr_map_array[scontext->type - 1];
+ tattr = &policydb->type_attr_map_array[tcontext->type - 1];
ebitmap_for_each_positive_bit(sattr, snode, i) {
ebitmap_for_each_positive_bit(tattr, tnode, j) {
avkey.source_type = i + 1;
@@ -776,7 +766,7 @@
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
if (!user)
tclass = unmap_class(&state->ss->map, orig_tclass);
@@ -876,7 +866,7 @@
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
rc = -EINVAL;
old_context = sidtab_search(sidtab, old_sid);
@@ -901,8 +891,7 @@
index = new_context->type;
while (true) {
- type = flex_array_get_ptr(policydb->type_val_to_struct_array,
- index - 1);
+ type = policydb->type_val_to_struct[index - 1];
BUG_ON(!type);
/* not bounded anymore */
@@ -1034,7 +1023,7 @@
goto allow;
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
@@ -1065,12 +1054,8 @@
avkey.target_class = tclass;
avkey.specified = AVTAB_XPERMS;
- sattr = flex_array_get(policydb->type_attr_map_array,
- scontext->type - 1);
- BUG_ON(!sattr);
- tattr = flex_array_get(policydb->type_attr_map_array,
- tcontext->type - 1);
- BUG_ON(!tattr);
+ sattr = &policydb->type_attr_map_array[scontext->type - 1];
+ tattr = &policydb->type_attr_map_array[tcontext->type - 1];
ebitmap_for_each_positive_bit(sattr, snode, i) {
ebitmap_for_each_positive_bit(tattr, tnode, j) {
avkey.source_type = i + 1;
@@ -1123,7 +1108,7 @@
goto allow;
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
@@ -1177,7 +1162,7 @@
goto allow;
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
@@ -1281,7 +1266,8 @@
static int security_sid_to_context_core(struct selinux_state *state,
u32 sid, char **scontext,
- u32 *scontext_len, int force)
+ u32 *scontext_len, int force,
+ int only_invalid)
{
struct policydb *policydb;
struct sidtab *sidtab;
@@ -1315,7 +1301,7 @@
}
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
if (force)
context = sidtab_search_force(sidtab, sid);
else
@@ -1326,8 +1312,11 @@
rc = -EINVAL;
goto out_unlock;
}
- rc = context_struct_to_string(policydb, context, scontext,
- scontext_len);
+ if (only_invalid && !context->len)
+ rc = 0;
+ else
+ rc = context_struct_to_string(policydb, context, scontext,
+ scontext_len);
out_unlock:
read_unlock(&state->ss->policy_rwlock);
out:
@@ -1349,14 +1338,34 @@
u32 sid, char **scontext, u32 *scontext_len)
{
return security_sid_to_context_core(state, sid, scontext,
- scontext_len, 0);
+ scontext_len, 0, 0);
}
int security_sid_to_context_force(struct selinux_state *state, u32 sid,
char **scontext, u32 *scontext_len)
{
return security_sid_to_context_core(state, sid, scontext,
- scontext_len, 1);
+ scontext_len, 1, 0);
+}
+
+/**
+ * security_sid_to_context_inval - Obtain a context for a given SID if it
+ * is invalid.
+ * @sid: security identifier, SID
+ * @scontext: security context
+ * @scontext_len: length in bytes
+ *
+ * Write the string representation of the context associated with @sid
+ * into a dynamically allocated string of the correct size, but only if the
+ * context is invalid in the current policy. Set @scontext to point to
+ * this string (or NULL if the context is valid) and set @scontext_len to
+ * the length of the string (or 0 if the context is valid).
+ */
+int security_sid_to_context_inval(struct selinux_state *state, u32 sid,
+ char **scontext, u32 *scontext_len)
+{
+ return security_sid_to_context_core(state, sid, scontext,
+ scontext_len, 1, 1);
}
/*
@@ -1365,7 +1374,6 @@
static int string_to_context_struct(struct policydb *pol,
struct sidtab *sidtabp,
char *scontext,
- u32 scontext_len,
struct context *ctx,
u32 def_sid)
{
@@ -1426,15 +1434,12 @@
ctx->type = typdatum->value;
- rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid);
+ rc = mls_context_to_sid(pol, oldc, p, ctx, sidtabp, def_sid);
if (rc)
goto out;
- rc = -EINVAL;
- if ((p - scontext) < scontext_len)
- goto out;
-
/* Check the validity of the new context. */
+ rc = -EINVAL;
if (!policydb_context_isvalid(pol, ctx))
goto out;
rc = 0;
@@ -1487,9 +1492,9 @@
}
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
rc = string_to_context_struct(policydb, sidtab, scontext2,
- scontext_len, &context, def_sid);
+ &context, def_sid);
if (rc == -EINVAL && force) {
context.str = str;
context.len = strlen(str) + 1;
@@ -1577,6 +1582,7 @@
struct policydb *policydb = &state->ss->policydb;
char *s = NULL, *t = NULL, *n = NULL;
u32 slen, tlen, nlen;
+ struct audit_buffer *ab;
if (context_struct_to_string(policydb, scontext, &s, &slen))
goto out;
@@ -1584,12 +1590,14 @@
goto out;
if (context_struct_to_string(policydb, newcontext, &n, &nlen))
goto out;
- audit_log(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR,
- "op=security_compute_sid invalid_context=%s"
- " scontext=%s"
- " tcontext=%s"
- " tclass=%s",
- n, s, t, sym_name(policydb, SYM_CLASSES, tclass-1));
+ ab = audit_log_start(audit_context(), GFP_ATOMIC, AUDIT_SELINUX_ERR);
+ audit_log_format(ab,
+ "op=security_compute_sid invalid_context=");
+ /* no need to record the NUL with untrusted strings */
+ audit_log_n_untrustedstring(ab, n, nlen - 1);
+ audit_log_format(ab, " scontext=%s tcontext=%s tclass=%s",
+ s, t, sym_name(policydb, SYM_CLASSES, tclass-1));
+ audit_log_end(ab);
out:
kfree(s);
kfree(t);
@@ -1672,7 +1680,7 @@
}
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
scontext = sidtab_search(sidtab, ssid);
if (!scontext) {
@@ -1884,19 +1892,6 @@
out_sid, false);
}
-/* Clone the SID into the new SID table. */
-static int clone_sid(u32 sid,
- struct context *context,
- void *arg)
-{
- struct sidtab *s = arg;
-
- if (sid > SECINITSID_NUM)
- return sidtab_insert(s, sid, context);
- else
- return 0;
-}
-
static inline int convert_context_handle_invalid_context(
struct selinux_state *state,
struct context *context)
@@ -1924,101 +1919,91 @@
/*
* Convert the values in the security context
- * structure `c' from the values specified
+ * structure `oldc' from the values specified
* in the policy `p->oldp' to the values specified
- * in the policy `p->newp'. Verify that the
- * context is valid under the new policy.
+ * in the policy `p->newp', storing the new context
+ * in `newc'. Verify that the context is valid
+ * under the new policy.
*/
-static int convert_context(u32 key,
- struct context *c,
- void *p)
+static int convert_context(struct context *oldc, struct context *newc, void *p)
{
struct convert_context_args *args;
- struct context oldc;
struct ocontext *oc;
- struct mls_range *range;
struct role_datum *role;
struct type_datum *typdatum;
struct user_datum *usrdatum;
char *s;
u32 len;
- int rc = 0;
-
- if (key <= SECINITSID_NUM)
- goto out;
+ int rc;
args = p;
- if (c->str) {
- struct context ctx;
-
- rc = -ENOMEM;
- s = kstrdup(c->str, GFP_KERNEL);
+ if (oldc->str) {
+ s = kstrdup(oldc->str, GFP_KERNEL);
if (!s)
- goto out;
+ return -ENOMEM;
rc = string_to_context_struct(args->newp, NULL, s,
- c->len, &ctx, SECSID_NULL);
+ newc, SECSID_NULL);
+ if (rc == -EINVAL) {
+ /*
+ * Retain string representation for later mapping.
+ *
+ * IMPORTANT: We need to copy the contents of oldc->str
+ * back into s again because string_to_context_struct()
+ * may have garbled it.
+ */
+ memcpy(s, oldc->str, oldc->len);
+ context_init(newc);
+ newc->str = s;
+ newc->len = oldc->len;
+ return 0;
+ }
kfree(s);
- if (!rc) {
- pr_info("SELinux: Context %s became valid (mapped).\n",
- c->str);
- /* Replace string with mapped representation. */
- kfree(c->str);
- memcpy(c, &ctx, sizeof(*c));
- goto out;
- } else if (rc == -EINVAL) {
- /* Retain string representation for later mapping. */
- rc = 0;
- goto out;
- } else {
+ if (rc) {
/* Other error condition, e.g. ENOMEM. */
pr_err("SELinux: Unable to map context %s, rc = %d.\n",
- c->str, -rc);
- goto out;
+ oldc->str, -rc);
+ return rc;
}
+ pr_info("SELinux: Context %s became valid (mapped).\n",
+ oldc->str);
+ return 0;
}
- rc = context_cpy(&oldc, c);
- if (rc)
- goto out;
+ context_init(newc);
/* Convert the user. */
rc = -EINVAL;
usrdatum = hashtab_search(args->newp->p_users.table,
- sym_name(args->oldp, SYM_USERS, c->user - 1));
+ sym_name(args->oldp,
+ SYM_USERS, oldc->user - 1));
if (!usrdatum)
goto bad;
- c->user = usrdatum->value;
+ newc->user = usrdatum->value;
/* Convert the role. */
rc = -EINVAL;
role = hashtab_search(args->newp->p_roles.table,
- sym_name(args->oldp, SYM_ROLES, c->role - 1));
+ sym_name(args->oldp, SYM_ROLES, oldc->role - 1));
if (!role)
goto bad;
- c->role = role->value;
+ newc->role = role->value;
/* Convert the type. */
rc = -EINVAL;
typdatum = hashtab_search(args->newp->p_types.table,
- sym_name(args->oldp, SYM_TYPES, c->type - 1));
+ sym_name(args->oldp,
+ SYM_TYPES, oldc->type - 1));
if (!typdatum)
goto bad;
- c->type = typdatum->value;
+ newc->type = typdatum->value;
/* Convert the MLS fields if dealing with MLS policies */
if (args->oldp->mls_enabled && args->newp->mls_enabled) {
- rc = mls_convert_context(args->oldp, args->newp, c);
+ rc = mls_convert_context(args->oldp, args->newp, oldc, newc);
if (rc)
goto bad;
- } else if (args->oldp->mls_enabled && !args->newp->mls_enabled) {
- /*
- * Switching between MLS and non-MLS policy:
- * free any storage used by the MLS fields in the
- * context for all existing entries in the sidtab.
- */
- mls_context_destroy(c);
} else if (!args->oldp->mls_enabled && args->newp->mls_enabled) {
/*
* Switching between non-MLS and MLS policy:
@@ -2036,38 +2021,30 @@
" the initial SIDs list\n");
goto bad;
}
- range = &oc->context[0].range;
- rc = mls_range_set(c, range);
+ rc = mls_range_set(newc, &oc->context[0].range);
if (rc)
goto bad;
}
/* Check the validity of the new context. */
- if (!policydb_context_isvalid(args->newp, c)) {
- rc = convert_context_handle_invalid_context(args->state,
- &oldc);
+ if (!policydb_context_isvalid(args->newp, newc)) {
+ rc = convert_context_handle_invalid_context(args->state, oldc);
if (rc)
goto bad;
}
- context_destroy(&oldc);
-
- rc = 0;
-out:
- return rc;
+ return 0;
bad:
/* Map old representation to string and save it. */
- rc = context_struct_to_string(args->oldp, &oldc, &s, &len);
+ rc = context_struct_to_string(args->oldp, oldc, &s, &len);
if (rc)
return rc;
- context_destroy(&oldc);
- context_destroy(c);
- c->str = s;
- c->len = len;
+ context_destroy(newc);
+ newc->str = s;
+ newc->len = len;
pr_info("SELinux: Context %s became invalid (unmapped).\n",
- c->str);
- rc = 0;
- goto out;
+ newc->str);
+ return 0;
}
static void security_load_policycaps(struct selinux_state *state)
@@ -2107,11 +2084,11 @@
int security_load_policy(struct selinux_state *state, void *data, size_t len)
{
struct policydb *policydb;
- struct sidtab *sidtab;
+ struct sidtab *oldsidtab, *newsidtab;
struct policydb *oldpolicydb, *newpolicydb;
- struct sidtab oldsidtab, newsidtab;
struct selinux_mapping *oldmapping;
struct selinux_map newmap;
+ struct sidtab_convert_params convert_params;
struct convert_context_args args;
u32 seqno;
int rc = 0;
@@ -2125,27 +2102,37 @@
newpolicydb = oldpolicydb + 1;
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+
+ newsidtab = kmalloc(sizeof(*newsidtab), GFP_KERNEL);
+ if (!newsidtab) {
+ rc = -ENOMEM;
+ goto out;
+ }
if (!state->initialized) {
rc = policydb_read(policydb, fp);
- if (rc)
+ if (rc) {
+ kfree(newsidtab);
goto out;
+ }
policydb->len = len;
rc = selinux_set_mapping(policydb, secclass_map,
&state->ss->map);
if (rc) {
+ kfree(newsidtab);
policydb_destroy(policydb);
goto out;
}
- rc = policydb_load_isids(policydb, sidtab);
+ rc = policydb_load_isids(policydb, newsidtab);
if (rc) {
+ kfree(newsidtab);
policydb_destroy(policydb);
goto out;
}
+ state->ss->sidtab = newsidtab;
security_load_policycaps(state);
state->initialized = 1;
seqno = ++state->ss->latest_granting;
@@ -2158,13 +2145,11 @@
goto out;
}
-#if 0
- sidtab_hash_eval(sidtab, "sids");
-#endif
-
rc = policydb_read(newpolicydb, fp);
- if (rc)
+ if (rc) {
+ kfree(newsidtab);
goto out;
+ }
newpolicydb->len = len;
/* If switching between different policy types, log MLS status */
@@ -2173,10 +2158,11 @@
else if (!policydb->mls_enabled && newpolicydb->mls_enabled)
pr_info("SELinux: Enabling MLS support...\n");
- rc = policydb_load_isids(newpolicydb, &newsidtab);
+ rc = policydb_load_isids(newpolicydb, newsidtab);
if (rc) {
pr_err("SELinux: unable to load the initial SIDs\n");
policydb_destroy(newpolicydb);
+ kfree(newsidtab);
goto out;
}
@@ -2190,12 +2176,7 @@
goto err;
}
- /* Clone the SID table. */
- sidtab_shutdown(sidtab);
-
- rc = sidtab_map(sidtab, clone_sid, &newsidtab);
- if (rc)
- goto err;
+ oldsidtab = state->ss->sidtab;
/*
* Convert the internal representations of contexts
@@ -2204,7 +2185,12 @@
args.state = state;
args.oldp = policydb;
args.newp = newpolicydb;
- rc = sidtab_map(&newsidtab, convert_context, &args);
+
+ convert_params.func = convert_context;
+ convert_params.args = &args;
+ convert_params.target = newsidtab;
+
+ rc = sidtab_convert(oldsidtab, &convert_params);
if (rc) {
pr_err("SELinux: unable to convert the internal"
" representation of contexts in the new SID"
@@ -2214,12 +2200,11 @@
/* Save the old policydb and SID table to free later. */
memcpy(oldpolicydb, policydb, sizeof(*policydb));
- sidtab_set(&oldsidtab, sidtab);
/* Install the new policydb and SID table. */
write_lock_irq(&state->ss->policy_rwlock);
memcpy(policydb, newpolicydb, sizeof(*policydb));
- sidtab_set(sidtab, &newsidtab);
+ state->ss->sidtab = newsidtab;
security_load_policycaps(state);
oldmapping = state->ss->map.mapping;
state->ss->map.mapping = newmap.mapping;
@@ -2229,7 +2214,8 @@
/* Free the old policydb and SID table. */
policydb_destroy(oldpolicydb);
- sidtab_destroy(&oldsidtab);
+ sidtab_destroy(oldsidtab);
+ kfree(oldsidtab);
kfree(oldmapping);
avc_ss_reset(state->avc, seqno);
@@ -2243,7 +2229,8 @@
err:
kfree(newmap.mapping);
- sidtab_destroy(&newsidtab);
+ sidtab_destroy(newsidtab);
+ kfree(newsidtab);
policydb_destroy(newpolicydb);
out:
@@ -2280,7 +2267,7 @@
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
c = policydb->ocontexts[OCON_PORT];
while (c) {
@@ -2326,7 +2313,7 @@
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
c = policydb->ocontexts[OCON_IBPKEY];
while (c) {
@@ -2372,7 +2359,7 @@
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
c = policydb->ocontexts[OCON_IBENDPORT];
while (c) {
@@ -2418,7 +2405,7 @@
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
c = policydb->ocontexts[OCON_NETIF];
while (c) {
@@ -2483,7 +2470,7 @@
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
switch (domain) {
case AF_INET: {
@@ -2583,7 +2570,7 @@
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
context_init(&usercon);
@@ -2685,7 +2672,7 @@
u32 *sid)
{
struct policydb *policydb = &state->ss->policydb;
- struct sidtab *sidtab = &state->ss->sidtab;
+ struct sidtab *sidtab = state->ss->sidtab;
int len;
u16 sclass;
struct genfs *genfs;
@@ -2771,7 +2758,7 @@
read_lock(&state->ss->policy_rwlock);
policydb = &state->ss->policydb;
- sidtab = &state->ss->sidtab;
+ sidtab = state->ss->sidtab;
c = policydb->ocontexts[OCON_FSUSE];
while (c) {
@@ -2977,7 +2964,7 @@
u32 sid, u32 mls_sid, u32 *new_sid)
{
struct policydb *policydb = &state->ss->policydb;
- struct sidtab *sidtab = &state->ss->sidtab;
+ struct sidtab *sidtab = state->ss->sidtab;
struct context *context1;
struct context *context2;
struct context newcon;
@@ -3024,10 +3011,16 @@
if (rc) {
if (!context_struct_to_string(policydb, &newcon, &s,
&len)) {
- audit_log(audit_context(),
- GFP_ATOMIC, AUDIT_SELINUX_ERR,
- "op=security_sid_mls_copy "
- "invalid_context=%s", s);
+ struct audit_buffer *ab;
+
+ ab = audit_log_start(audit_context(),
+ GFP_ATOMIC,
+ AUDIT_SELINUX_ERR);
+ audit_log_format(ab,
+ "op=security_sid_mls_copy invalid_context=");
+ /* don't record NUL with untrusted strings */
+ audit_log_n_untrustedstring(ab, s, len - 1);
+ audit_log_end(ab);
kfree(s);
}
goto out_unlock;
@@ -3068,7 +3061,7 @@
u32 *peer_sid)
{
struct policydb *policydb = &state->ss->policydb;
- struct sidtab *sidtab = &state->ss->sidtab;
+ struct sidtab *sidtab = state->ss->sidtab;
int rc;
struct context *nlbl_ctx;
struct context *xfrm_ctx;
@@ -3408,8 +3401,7 @@
return 0;
}
-int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
- struct audit_context *actx)
+int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule)
{
struct selinux_state *state = &selinux_state;
struct context *ctxt;
@@ -3429,7 +3421,7 @@
goto out;
}
- ctxt = sidtab_search(&state->ss->sidtab, sid);
+ ctxt = sidtab_search(state->ss->sidtab, sid);
if (unlikely(!ctxt)) {
WARN_ONCE(1, "selinux_audit_rule_match: unrecognized SID %d\n",
sid);
@@ -3592,7 +3584,7 @@
u32 *sid)
{
struct policydb *policydb = &state->ss->policydb;
- struct sidtab *sidtab = &state->ss->sidtab;
+ struct sidtab *sidtab = state->ss->sidtab;
int rc;
struct context *ctx;
struct context ctx_new;
@@ -3670,7 +3662,7 @@
read_lock(&state->ss->policy_rwlock);
rc = -ENOENT;
- ctx = sidtab_search(&state->ss->sidtab, sid);
+ ctx = sidtab_search(state->ss->sidtab, sid);
if (ctx == NULL)
goto out;
diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h
index 24c7bdc..9a36de8 100644
--- a/security/selinux/ss/services.h
+++ b/security/selinux/ss/services.h
@@ -24,7 +24,7 @@
};
struct selinux_ss {
- struct sidtab sidtab;
+ struct sidtab *sidtab;
struct policydb policydb;
rwlock_t policy_rwlock;
u32 latest_granting;
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
index fd75a12..7d49994 100644
--- a/security/selinux/ss/sidtab.c
+++ b/security/selinux/ss/sidtab.c
@@ -2,108 +2,162 @@
/*
* Implementation of the SID table type.
*
- * Author : Stephen Smalley, <sds@tycho.nsa.gov>
+ * Original author: Stephen Smalley, <sds@tycho.nsa.gov>
+ * Author: Ondrej Mosnacek, <omosnacek@gmail.com>
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
*/
+#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/sched.h>
#include <linux/spinlock.h>
-#include <linux/errno.h>
+#include <asm/barrier.h>
#include "flask.h"
#include "security.h"
#include "sidtab.h"
-#define SIDTAB_HASH(sid) \
-(sid & SIDTAB_HASH_MASK)
-
int sidtab_init(struct sidtab *s)
{
- int i;
+ u32 i;
- s->htable = kmalloc_array(SIDTAB_SIZE, sizeof(*s->htable), GFP_ATOMIC);
- if (!s->htable)
- return -ENOMEM;
- for (i = 0; i < SIDTAB_SIZE; i++)
- s->htable[i] = NULL;
- s->nel = 0;
- s->next_sid = 1;
- s->shutdown = 0;
+ memset(s->roots, 0, sizeof(s->roots));
+
+ /* max count is SIDTAB_MAX so valid index is always < SIDTAB_MAX */
+ for (i = 0; i < SIDTAB_RCACHE_SIZE; i++)
+ s->rcache[i] = SIDTAB_MAX;
+
+ for (i = 0; i < SECINITSID_NUM; i++)
+ s->isids[i].set = 0;
+
+ s->count = 0;
+ s->convert = NULL;
+
spin_lock_init(&s->lock);
return 0;
}
-int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
+int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context)
{
- int hvalue;
- struct sidtab_node *prev, *cur, *newnode;
+ struct sidtab_isid_entry *entry;
+ int rc;
- if (!s)
- return -ENOMEM;
+ if (sid == 0 || sid > SECINITSID_NUM)
+ return -EINVAL;
- hvalue = SIDTAB_HASH(sid);
- prev = NULL;
- cur = s->htable[hvalue];
- while (cur && sid > cur->sid) {
- prev = cur;
- cur = cur->next;
- }
+ entry = &s->isids[sid - 1];
- if (cur && sid == cur->sid)
- return -EEXIST;
+ rc = context_cpy(&entry->context, context);
+ if (rc)
+ return rc;
- newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
- if (!newnode)
- return -ENOMEM;
-
- newnode->sid = sid;
- if (context_cpy(&newnode->context, context)) {
- kfree(newnode);
- return -ENOMEM;
- }
-
- if (prev) {
- newnode->next = prev->next;
- wmb();
- prev->next = newnode;
- } else {
- newnode->next = s->htable[hvalue];
- wmb();
- s->htable[hvalue] = newnode;
- }
-
- s->nel++;
- if (sid >= s->next_sid)
- s->next_sid = sid + 1;
+ entry->set = 1;
return 0;
}
+static u32 sidtab_level_from_count(u32 count)
+{
+ u32 capacity = SIDTAB_LEAF_ENTRIES;
+ u32 level = 0;
+
+ while (count > capacity) {
+ capacity <<= SIDTAB_INNER_SHIFT;
+ ++level;
+ }
+ return level;
+}
+
+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);
+ 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);
+ if (!s->roots[l].ptr_inner)
+ return -ENOMEM;
+ s->roots[l].ptr_inner->entries[0] = s->roots[l - 1];
+ }
+ return 0;
+}
+
+static struct context *sidtab_do_lookup(struct sidtab *s, u32 index, int alloc)
+{
+ union sidtab_entry_inner *entry;
+ u32 level, capacity_shift, leaf_index = index / SIDTAB_LEAF_ENTRIES;
+
+ /* find the level of the subtree we need */
+ level = sidtab_level_from_count(index + 1);
+ capacity_shift = level * SIDTAB_INNER_SHIFT;
+
+ /* allocate roots if needed */
+ if (alloc && sidtab_alloc_roots(s, level) != 0)
+ return NULL;
+
+ /* lookup inside the subtree */
+ entry = &s->roots[level];
+ while (level != 0) {
+ capacity_shift -= SIDTAB_INNER_SHIFT;
+ --level;
+
+ entry = &entry->ptr_inner->entries[leaf_index >> capacity_shift];
+ leaf_index &= ((u32)1 << capacity_shift) - 1;
+
+ if (!entry->ptr_inner) {
+ if (alloc)
+ 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);
+ if (!entry->ptr_leaf)
+ return NULL;
+ }
+ return &entry->ptr_leaf->entries[index % SIDTAB_LEAF_ENTRIES].context;
+}
+
+static struct context *sidtab_lookup(struct sidtab *s, u32 index)
+{
+ /* read entries only after reading count */
+ u32 count = smp_load_acquire(&s->count);
+
+ if (index >= count)
+ return NULL;
+
+ return sidtab_do_lookup(s, index, 0);
+}
+
+static struct context *sidtab_lookup_initial(struct sidtab *s, u32 sid)
+{
+ return s->isids[sid - 1].set ? &s->isids[sid - 1].context : NULL;
+}
+
static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
{
- int hvalue;
- struct sidtab_node *cur;
+ struct context *context;
- if (!s)
- return NULL;
-
- hvalue = SIDTAB_HASH(sid);
- cur = s->htable[hvalue];
- while (cur && sid > cur->sid)
- cur = cur->next;
-
- if (force && cur && sid == cur->sid && cur->context.len)
- return &cur->context;
-
- if (!cur || sid != cur->sid || cur->context.len) {
- /* Remap invalid SIDs to the unlabeled SID. */
- sid = SECINITSID_UNLABELED;
- hvalue = SIDTAB_HASH(sid);
- cur = s->htable[hvalue];
- while (cur && sid > cur->sid)
- cur = cur->next;
- if (!cur || sid != cur->sid)
- return NULL;
+ if (sid != 0) {
+ if (sid > SECINITSID_NUM)
+ context = sidtab_lookup(s, sid - (SECINITSID_NUM + 1));
+ else
+ context = sidtab_lookup_initial(s, sid);
+ if (context && (!context->len || force))
+ return context;
}
- return &cur->context;
+ return sidtab_lookup_initial(s, SECINITSID_UNLABELED);
}
struct context *sidtab_search(struct sidtab *s, u32 sid)
@@ -116,191 +170,325 @@
return sidtab_search_core(s, sid, 1);
}
-int sidtab_map(struct sidtab *s,
- int (*apply) (u32 sid,
- struct context *context,
- void *args),
- void *args)
+static int sidtab_find_context(union sidtab_entry_inner entry,
+ u32 *pos, u32 count, u32 level,
+ struct context *context, u32 *index)
{
- int i, rc = 0;
- struct sidtab_node *cur;
+ int rc;
+ u32 i;
- if (!s)
- goto out;
+ if (level != 0) {
+ struct sidtab_node_inner *node = entry.ptr_inner;
- for (i = 0; i < SIDTAB_SIZE; i++) {
- cur = s->htable[i];
- while (cur) {
- rc = apply(cur->sid, &cur->context, args);
- if (rc)
- goto out;
- cur = cur->next;
+ i = 0;
+ while (i < SIDTAB_INNER_ENTRIES && *pos < count) {
+ rc = sidtab_find_context(node->entries[i],
+ pos, count, level - 1,
+ context, index);
+ if (rc == 0)
+ return 0;
+ i++;
+ }
+ } else {
+ struct sidtab_node_leaf *node = entry.ptr_leaf;
+
+ i = 0;
+ while (i < SIDTAB_LEAF_ENTRIES && *pos < count) {
+ if (context_cmp(&node->entries[i].context, context)) {
+ *index = *pos;
+ return 0;
+ }
+ (*pos)++;
+ i++;
}
}
-out:
+ return -ENOENT;
+}
+
+static void sidtab_rcache_update(struct sidtab *s, u32 index, u32 pos)
+{
+ while (pos > 0) {
+ WRITE_ONCE(s->rcache[pos], READ_ONCE(s->rcache[pos - 1]));
+ --pos;
+ }
+ WRITE_ONCE(s->rcache[0], index);
+}
+
+static void sidtab_rcache_push(struct sidtab *s, u32 index)
+{
+ sidtab_rcache_update(s, index, SIDTAB_RCACHE_SIZE - 1);
+}
+
+static int sidtab_rcache_search(struct sidtab *s, struct context *context,
+ u32 *index)
+{
+ u32 i;
+
+ for (i = 0; i < SIDTAB_RCACHE_SIZE; i++) {
+ u32 v = READ_ONCE(s->rcache[i]);
+
+ if (v >= SIDTAB_MAX)
+ continue;
+
+ if (context_cmp(sidtab_do_lookup(s, v, 0), context)) {
+ sidtab_rcache_update(s, v, i);
+ *index = v;
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+static int sidtab_reverse_lookup(struct sidtab *s, struct context *context,
+ u32 *index)
+{
+ unsigned long flags;
+ u32 count, count_locked, level, pos;
+ struct sidtab_convert_params *convert;
+ struct context *dst, *dst_convert;
+ int rc;
+
+ rc = sidtab_rcache_search(s, context, index);
+ if (rc == 0)
+ return 0;
+
+ /* read entries only after reading count */
+ count = smp_load_acquire(&s->count);
+ level = sidtab_level_from_count(count);
+
+ pos = 0;
+ rc = sidtab_find_context(s->roots[level], &pos, count, level,
+ context, index);
+ if (rc == 0) {
+ sidtab_rcache_push(s, *index);
+ return 0;
+ }
+
+ /* lock-free search failed: lock, re-search, and insert if not found */
+ spin_lock_irqsave(&s->lock, flags);
+
+ convert = s->convert;
+ count_locked = s->count;
+ level = sidtab_level_from_count(count_locked);
+
+ /* if count has changed before we acquired the lock, then catch up */
+ while (count < count_locked) {
+ if (context_cmp(sidtab_do_lookup(s, count, 0), context)) {
+ sidtab_rcache_push(s, count);
+ *index = count;
+ rc = 0;
+ goto out_unlock;
+ }
+ ++count;
+ }
+
+ /* bail out if we already reached max entries */
+ rc = -EOVERFLOW;
+ if (count >= SIDTAB_MAX)
+ goto out_unlock;
+
+ /* insert context into new entry */
+ rc = -ENOMEM;
+ dst = sidtab_do_lookup(s, count, 1);
+ if (!dst)
+ goto out_unlock;
+
+ rc = context_cpy(dst, context);
+ if (rc)
+ goto out_unlock;
+
+ /*
+ * if we are building a new sidtab, we need to convert the context
+ * and insert it there as well
+ */
+ if (convert) {
+ rc = -ENOMEM;
+ dst_convert = sidtab_do_lookup(convert->target, count, 1);
+ if (!dst_convert) {
+ context_destroy(dst);
+ goto out_unlock;
+ }
+
+ rc = convert->func(context, dst_convert, convert->args);
+ if (rc) {
+ context_destroy(dst);
+ goto out_unlock;
+ }
+
+ /* at this point we know the insert won't fail */
+ convert->target->count = count + 1;
+ }
+
+ if (context->len)
+ pr_info("SELinux: Context %s is not valid (left unmapped).\n",
+ context->str);
+
+ sidtab_rcache_push(s, count);
+ *index = count;
+
+ /* write entries before writing new count */
+ smp_store_release(&s->count, count + 1);
+
+ rc = 0;
+out_unlock:
+ spin_unlock_irqrestore(&s->lock, flags);
return rc;
}
-static void sidtab_update_cache(struct sidtab *s, struct sidtab_node *n, int loc)
+int sidtab_context_to_sid(struct sidtab *s, struct context *context, u32 *sid)
{
- BUG_ON(loc >= SIDTAB_CACHE_LEN);
+ int rc;
+ u32 i;
- while (loc > 0) {
- s->cache[loc] = s->cache[loc - 1];
- loc--;
- }
- s->cache[0] = n;
-}
+ for (i = 0; i < SECINITSID_NUM; i++) {
+ struct sidtab_isid_entry *entry = &s->isids[i];
-static inline u32 sidtab_search_context(struct sidtab *s,
- struct context *context)
-{
- int i;
- struct sidtab_node *cur;
-
- for (i = 0; i < SIDTAB_SIZE; i++) {
- cur = s->htable[i];
- while (cur) {
- if (context_cmp(&cur->context, context)) {
- sidtab_update_cache(s, cur, SIDTAB_CACHE_LEN - 1);
- return cur->sid;
- }
- cur = cur->next;
- }
- }
- return 0;
-}
-
-static inline u32 sidtab_search_cache(struct sidtab *s, struct context *context)
-{
- int i;
- struct sidtab_node *node;
-
- for (i = 0; i < SIDTAB_CACHE_LEN; i++) {
- node = s->cache[i];
- if (unlikely(!node))
+ if (entry->set && context_cmp(context, &entry->context)) {
+ *sid = i + 1;
return 0;
- if (context_cmp(&node->context, context)) {
- sidtab_update_cache(s, node, i);
- return node->sid;
}
}
+
+ rc = sidtab_reverse_lookup(s, context, sid);
+ if (rc)
+ return rc;
+ *sid += SECINITSID_NUM + 1;
+ return 0;
+}
+
+static int sidtab_convert_tree(union sidtab_entry_inner *edst,
+ union sidtab_entry_inner *esrc,
+ u32 *pos, u32 count, u32 level,
+ struct sidtab_convert_params *convert)
+{
+ int rc;
+ u32 i;
+
+ if (level != 0) {
+ if (!edst->ptr_inner) {
+ edst->ptr_inner = kzalloc(SIDTAB_NODE_ALLOC_SIZE,
+ GFP_KERNEL);
+ if (!edst->ptr_inner)
+ return -ENOMEM;
+ }
+ i = 0;
+ while (i < SIDTAB_INNER_ENTRIES && *pos < count) {
+ rc = sidtab_convert_tree(&edst->ptr_inner->entries[i],
+ &esrc->ptr_inner->entries[i],
+ pos, count, level - 1,
+ convert);
+ if (rc)
+ return rc;
+ i++;
+ }
+ } else {
+ if (!edst->ptr_leaf) {
+ 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 = convert->func(&esrc->ptr_leaf->entries[i].context,
+ &edst->ptr_leaf->entries[i].context,
+ convert->args);
+ if (rc)
+ return rc;
+ (*pos)++;
+ i++;
+ }
+ cond_resched();
+ }
return 0;
}
-int sidtab_context_to_sid(struct sidtab *s,
- struct context *context,
- u32 *out_sid)
+int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params)
{
- u32 sid;
- int ret = 0;
unsigned long flags;
+ u32 count, level, pos;
+ int rc;
- *out_sid = SECSID_NULL;
+ spin_lock_irqsave(&s->lock, flags);
- sid = sidtab_search_cache(s, context);
- if (!sid)
- sid = sidtab_search_context(s, context);
- if (!sid) {
+ /* concurrent policy loads are not allowed */
+ if (s->convert) {
+ spin_unlock_irqrestore(&s->lock, flags);
+ return -EBUSY;
+ }
+
+ count = s->count;
+ level = sidtab_level_from_count(count);
+
+ /* allocate last leaf in the new sidtab (to avoid race with
+ * live convert)
+ */
+ rc = sidtab_do_lookup(params->target, count - 1, 1) ? 0 : -ENOMEM;
+ if (rc) {
+ spin_unlock_irqrestore(&s->lock, flags);
+ return rc;
+ }
+
+ /* set count in case no new entries are added during conversion */
+ params->target->count = count;
+
+ /* enable live convert of new entries */
+ s->convert = params;
+
+ /* we can safely do the rest of the conversion outside the lock */
+ spin_unlock_irqrestore(&s->lock, flags);
+
+ pr_info("SELinux: Converting %u SID table entries...\n", count);
+
+ /* convert all entries not covered by live convert */
+ pos = 0;
+ rc = sidtab_convert_tree(¶ms->target->roots[level],
+ &s->roots[level], &pos, count, level, params);
+ if (rc) {
+ /* we need to keep the old table - disable live convert */
spin_lock_irqsave(&s->lock, flags);
- /* Rescan now that we hold the lock. */
- sid = sidtab_search_context(s, context);
- if (sid)
- goto unlock_out;
- /* No SID exists for the context. Allocate a new one. */
- if (s->next_sid == UINT_MAX || s->shutdown) {
- ret = -ENOMEM;
- goto unlock_out;
- }
- sid = s->next_sid++;
- if (context->len)
- pr_info("SELinux: Context %s is not valid (left unmapped).\n",
- context->str);
- ret = sidtab_insert(s, sid, context);
- if (ret)
- s->next_sid--;
-unlock_out:
+ s->convert = NULL;
spin_unlock_irqrestore(&s->lock, flags);
}
-
- if (ret)
- return ret;
-
- *out_sid = sid;
- return 0;
+ return rc;
}
-void sidtab_hash_eval(struct sidtab *h, char *tag)
+static void sidtab_destroy_tree(union sidtab_entry_inner entry, u32 level)
{
- int i, chain_len, slots_used, max_chain_len;
- struct sidtab_node *cur;
+ u32 i;
- slots_used = 0;
- max_chain_len = 0;
- for (i = 0; i < SIDTAB_SIZE; i++) {
- cur = h->htable[i];
- if (cur) {
- slots_used++;
- chain_len = 0;
- while (cur) {
- chain_len++;
- cur = cur->next;
- }
+ if (level != 0) {
+ struct sidtab_node_inner *node = entry.ptr_inner;
- if (chain_len > max_chain_len)
- max_chain_len = chain_len;
- }
+ if (!node)
+ return;
+
+ for (i = 0; i < SIDTAB_INNER_ENTRIES; i++)
+ sidtab_destroy_tree(node->entries[i], level - 1);
+ kfree(node);
+ } else {
+ struct sidtab_node_leaf *node = entry.ptr_leaf;
+
+ if (!node)
+ return;
+
+ for (i = 0; i < SIDTAB_LEAF_ENTRIES; i++)
+ context_destroy(&node->entries[i].context);
+ kfree(node);
}
-
- pr_debug("%s: %d entries and %d/%d buckets used, longest "
- "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE,
- max_chain_len);
}
void sidtab_destroy(struct sidtab *s)
{
- int i;
- struct sidtab_node *cur, *temp;
+ u32 i, level;
- if (!s)
- return;
+ for (i = 0; i < SECINITSID_NUM; i++)
+ if (s->isids[i].set)
+ context_destroy(&s->isids[i].context);
- for (i = 0; i < SIDTAB_SIZE; i++) {
- cur = s->htable[i];
- while (cur) {
- temp = cur;
- cur = cur->next;
- context_destroy(&temp->context);
- kfree(temp);
- }
- s->htable[i] = NULL;
- }
- kfree(s->htable);
- s->htable = NULL;
- s->nel = 0;
- s->next_sid = 1;
-}
+ level = SIDTAB_MAX_LEVEL;
+ while (level && !s->roots[level].ptr_inner)
+ --level;
-void sidtab_set(struct sidtab *dst, struct sidtab *src)
-{
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&src->lock, flags);
- dst->htable = src->htable;
- dst->nel = src->nel;
- dst->next_sid = src->next_sid;
- dst->shutdown = 0;
- for (i = 0; i < SIDTAB_CACHE_LEN; i++)
- dst->cache[i] = NULL;
- spin_unlock_irqrestore(&src->lock, flags);
-}
-
-void sidtab_shutdown(struct sidtab *s)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&s->lock, flags);
- s->shutdown = 1;
- spin_unlock_irqrestore(&s->lock, flags);
+ sidtab_destroy_tree(s->roots[level], level);
}
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
index a1a1d26..1f47631 100644
--- a/security/selinux/ss/sidtab.h
+++ b/security/selinux/ss/sidtab.h
@@ -1,56 +1,105 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * A security identifier table (sidtab) is a hash table
+ * A security identifier table (sidtab) is a lookup table
* of security context structures indexed by SID value.
*
- * Author : Stephen Smalley, <sds@tycho.nsa.gov>
+ * Original author: Stephen Smalley, <sds@tycho.nsa.gov>
+ * Author: Ondrej Mosnacek, <omosnacek@gmail.com>
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
*/
#ifndef _SS_SIDTAB_H_
#define _SS_SIDTAB_H_
+#include <linux/spinlock_types.h>
+#include <linux/log2.h>
+
#include "context.h"
-struct sidtab_node {
- u32 sid; /* security identifier */
- struct context context; /* security context structure */
- struct sidtab_node *next;
+struct sidtab_entry_leaf {
+ struct context context;
};
-#define SIDTAB_HASH_BITS 7
-#define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS)
-#define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1)
+struct sidtab_node_inner;
+struct sidtab_node_leaf;
-#define SIDTAB_SIZE SIDTAB_HASH_BUCKETS
+union sidtab_entry_inner {
+ struct sidtab_node_inner *ptr_inner;
+ 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 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_ENTRIES ((size_t)1 << SIDTAB_INNER_SHIFT)
+#define SIDTAB_LEAF_ENTRIES \
+ (SIDTAB_NODE_ALLOC_SIZE / sizeof(struct sidtab_entry_leaf))
+
+#define SIDTAB_MAX_BITS 32
+#define SIDTAB_MAX U32_MAX
+/* ensure enough tree levels for SIDTAB_MAX entries */
+#define SIDTAB_MAX_LEVEL \
+ DIV_ROUND_UP(SIDTAB_MAX_BITS - size_to_shift(SIDTAB_LEAF_ENTRIES), \
+ SIDTAB_INNER_SHIFT)
+
+struct sidtab_node_leaf {
+ struct sidtab_entry_leaf entries[SIDTAB_LEAF_ENTRIES];
+};
+
+struct sidtab_node_inner {
+ union sidtab_entry_inner entries[SIDTAB_INNER_ENTRIES];
+};
+
+struct sidtab_isid_entry {
+ int set;
+ struct context context;
+};
+
+struct sidtab_convert_params {
+ int (*func)(struct context *oldc, struct context *newc, void *args);
+ void *args;
+ struct sidtab *target;
+};
+
+#define SIDTAB_RCACHE_SIZE 3
struct sidtab {
- struct sidtab_node **htable;
- unsigned int nel; /* number of elements */
- unsigned int next_sid; /* next SID to allocate */
- unsigned char shutdown;
-#define SIDTAB_CACHE_LEN 3
- struct sidtab_node *cache[SIDTAB_CACHE_LEN];
+ /*
+ * lock-free read access only for as many items as a prior read of
+ * 'count'
+ */
+ union sidtab_entry_inner roots[SIDTAB_MAX_LEVEL + 1];
+ /*
+ * access atomically via {READ|WRITE}_ONCE(); only increment under
+ * spinlock
+ */
+ u32 count;
+ /* access only under spinlock */
+ struct sidtab_convert_params *convert;
spinlock_t lock;
+
+ /* reverse lookup cache - access atomically via {READ|WRITE}_ONCE() */
+ u32 rcache[SIDTAB_RCACHE_SIZE];
+
+ /* index == SID - 1 (no entry for SECSID_NULL) */
+ struct sidtab_isid_entry isids[SECINITSID_NUM];
};
int sidtab_init(struct sidtab *s);
-int sidtab_insert(struct sidtab *s, u32 sid, struct context *context);
+int sidtab_set_initial(struct sidtab *s, u32 sid, struct context *context);
struct context *sidtab_search(struct sidtab *s, u32 sid);
struct context *sidtab_search_force(struct sidtab *s, u32 sid);
-int sidtab_map(struct sidtab *s,
- int (*apply) (u32 sid,
- struct context *context,
- void *args),
- void *args);
+int sidtab_convert(struct sidtab *s, struct sidtab_convert_params *params);
-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);
-void sidtab_hash_eval(struct sidtab *h, char *tag);
void sidtab_destroy(struct sidtab *s);
-void sidtab_set(struct sidtab *dst, struct sidtab *src);
-void sidtab_shutdown(struct sidtab *s);
#endif /* _SS_SIDTAB_H_ */
diff --git a/security/selinux/ss/status.c b/security/selinux/ss/status.c
index a121de4..3c554a4 100644
--- a/security/selinux/ss/status.c
+++ b/security/selinux/ss/status.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* mmap based event notifications for SELinux
*
* Author: KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* Copyright (C) 2010 NEC corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/gfp.h>
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index 91dc378..7314196 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* NSA Security-Enhanced Linux (SELinux) security module
*
@@ -12,10 +13,6 @@
*
* Copyright (C) 2005 International Business Machines Corporation
* Copyright (C) 2006 Trusted Computer Solutions, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
/*
@@ -79,7 +76,7 @@
gfp_t gfp)
{
int rc;
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
struct xfrm_sec_ctx *ctx = NULL;
u32 str_len;
@@ -138,7 +135,7 @@
*/
static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx)
{
- const struct task_security_struct *tsec = current_security();
+ const struct task_security_struct *tsec = selinux_cred(current_cred());
if (!ctx)
return 0;
@@ -230,7 +227,7 @@
u32 *sid, int ckall)
{
u32 sid_session = SECSID_NULL;
- struct sec_path *sp = skb->sp;
+ struct sec_path *sp = skb_sec_path(skb);
if (sp) {
int i;
@@ -408,7 +405,7 @@
struct common_audit_data *ad)
{
int i;
- struct sec_path *sp = skb->sp;
+ struct sec_path *sp = skb_sec_path(skb);
u32 peer_sid = SECINITSID_UNLABELED;
if (sp) {
diff --git a/security/smack/Kconfig b/security/smack/Kconfig
index 923b120..5a8dfad 100644
--- a/security/smack/Kconfig
+++ b/security/smack/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SECURITY_SMACK
bool "Simplified Mandatory Access Control Kernel Support"
depends on NET
diff --git a/security/smack/Makefile b/security/smack/Makefile
index ee2ebd5..6dbf6e2 100644
--- a/security/smack/Makefile
+++ b/security/smack/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the SMACK LSM
#
diff --git a/security/smack/smack.h b/security/smack/smack.h
index f7db791..62529f3 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -1,13 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
*
- * 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.
- *
* Author:
* Casey Schaufler <casey@schaufler-ca.com>
- *
*/
#ifndef _SECURITY_SMACK_H
@@ -24,6 +20,7 @@
#include <linux/list.h>
#include <linux/rculist.h>
#include <linux/lsm_audit.h>
+#include <linux/msg.h>
/*
* Use IPv6 port labeling if IPv6 is enabled and secmarks
@@ -195,22 +192,13 @@
enum {
Opt_error = -1,
- Opt_fsdefault = 1,
- Opt_fsfloor = 2,
- Opt_fshat = 3,
- Opt_fsroot = 4,
- Opt_fstransmute = 5,
+ Opt_fsdefault = 0,
+ Opt_fsfloor = 1,
+ Opt_fshat = 2,
+ Opt_fsroot = 3,
+ Opt_fstransmute = 4,
};
-/*
- * Mount options
- */
-#define SMK_FSDEFAULT "smackfsdef="
-#define SMK_FSFLOOR "smackfsfloor="
-#define SMK_FSHAT "smackfshat="
-#define SMK_FSROOT "smackfsroot="
-#define SMK_FSTRANS "smackfstransmute="
-
#define SMACK_DELETE_OPTION "-DELETE"
#define SMACK_CIPSO_OPTION "-CIPSO"
@@ -336,6 +324,7 @@
extern struct smack_known *smack_unconfined;
#endif
extern int smack_ptrace_rule;
+extern struct lsm_blob_sizes smack_blob_sizes;
extern struct smack_known smack_known_floor;
extern struct smack_known smack_known_hat;
@@ -355,13 +344,40 @@
#define SMACK_HASH_SLOTS 16
extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
+extern struct kmem_cache *smack_rule_cache;
+
+static inline struct task_smack *smack_cred(const struct cred *cred)
+{
+ return cred->security + smack_blob_sizes.lbs_cred;
+}
+
+static inline struct smack_known **smack_file(const struct file *file)
+{
+ return (struct smack_known **)(file->f_security +
+ smack_blob_sizes.lbs_file);
+}
+
+static inline struct inode_smack *smack_inode(const struct inode *inode)
+{
+ return inode->i_security + smack_blob_sizes.lbs_inode;
+}
+
+static inline struct smack_known **smack_msg_msg(const struct msg_msg *msg)
+{
+ return msg->security + smack_blob_sizes.lbs_msg_msg;
+}
+
+static inline struct smack_known **smack_ipc(const struct kern_ipc_perm *ipc)
+{
+ return ipc->security + smack_blob_sizes.lbs_ipc;
+}
/*
* Is the directory transmuting?
*/
static inline int smk_inode_transmutable(const struct inode *isp)
{
- struct inode_smack *sip = isp->i_security;
+ struct inode_smack *sip = smack_inode(isp);
return (sip->smk_flags & SMK_INODE_TRANSMUTE) != 0;
}
@@ -370,7 +386,7 @@
*/
static inline struct smack_known *smk_of_inode(const struct inode *isp)
{
- struct inode_smack *sip = isp->i_security;
+ struct inode_smack *sip = smack_inode(isp);
return sip->smk_inode;
}
@@ -382,13 +398,19 @@
return tsp->smk_task;
}
-static inline struct smack_known *smk_of_task_struct(const struct task_struct *t)
+static inline struct smack_known *smk_of_task_struct(
+ const struct task_struct *t)
{
struct smack_known *skp;
+ const struct cred *cred;
rcu_read_lock();
- skp = smk_of_task(__task_cred(t)->security);
+
+ cred = __task_cred(t);
+ skp = smk_of_task(smack_cred(cred));
+
rcu_read_unlock();
+
return skp;
}
@@ -405,7 +427,7 @@
*/
static inline struct smack_known *smk_of_current(void)
{
- return smk_of_task(current_security());
+ return smk_of_task(smack_cred(current_cred()));
}
/*
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 9a4c0ad..38ac3da 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
*
- * 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.
- *
* Author:
* Casey Schaufler <casey@schaufler-ca.com>
- *
*/
#include <linux/types.h>
@@ -275,7 +271,7 @@
int smk_curacc(struct smack_known *obj_known,
u32 mode, struct smk_audit_info *a)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_tskacc(tsp, obj_known, mode, a);
}
@@ -469,7 +465,7 @@
if (i == 0 || i >= SMK_LONGLABEL)
return ERR_PTR(-EINVAL);
- smack = kzalloc(i + 1, GFP_KERNEL);
+ smack = kzalloc(i + 1, GFP_NOFS);
if (smack == NULL)
return ERR_PTR(-ENOMEM);
@@ -504,7 +500,7 @@
if ((m & *cp) == 0)
continue;
rc = netlbl_catmap_setbit(&sap->attr.mls.cat,
- cat, GFP_KERNEL);
+ cat, GFP_NOFS);
if (rc < 0) {
netlbl_catmap_free(sap->attr.mls.cat);
return rc;
@@ -540,7 +536,7 @@
if (skp != NULL)
goto freeout;
- skp = kzalloc(sizeof(*skp), GFP_KERNEL);
+ skp = kzalloc(sizeof(*skp), GFP_NOFS);
if (skp == NULL) {
skp = ERR_PTR(-ENOMEM);
goto freeout;
@@ -635,12 +631,12 @@
*/
bool smack_privileged_cred(int cap, const struct cred *cred)
{
- struct task_smack *tsp = cred->security;
+ struct task_smack *tsp = smack_cred(cred);
struct smack_known *skp = tsp->smk_task;
struct smack_known_list_elem *sklep;
int rc;
- rc = cap_capable(cred, &init_user_ns, cap, SECURITY_CAP_AUDIT);
+ rc = cap_capable(cred, &init_user_ns, cap, CAP_OPT_NONE);
if (rc)
return false;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 70d3066..abeb09c 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Simplified MAC Kernel (smack) security module
*
@@ -12,10 +13,6 @@
* Paul Moore <paul@paul-moore.com>
* Copyright (C) 2010 Nokia Corporation
* Copyright (C) 2011 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#include <linux/xattr.h>
@@ -43,6 +40,8 @@
#include <linux/shm.h>
#include <linux/binfmts.h>
#include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
#include "smack.h"
#define TRANS_TRUE "TRUE"
@@ -57,16 +56,35 @@
static LIST_HEAD(smk_ipv6_port_list);
#endif
static struct kmem_cache *smack_inode_cache;
+struct kmem_cache *smack_rule_cache;
int smack_enabled;
-static const match_table_t smk_mount_tokens = {
- {Opt_fsdefault, SMK_FSDEFAULT "%s"},
- {Opt_fsfloor, SMK_FSFLOOR "%s"},
- {Opt_fshat, SMK_FSHAT "%s"},
- {Opt_fsroot, SMK_FSROOT "%s"},
- {Opt_fstransmute, SMK_FSTRANS "%s"},
- {Opt_error, NULL},
+#define A(s) {"smack"#s, sizeof("smack"#s) - 1, Opt_##s}
+static struct {
+ const char *name;
+ int len;
+ int opt;
+} smk_mount_opts[] = {
+ {"smackfsdef", sizeof("smackfsdef") - 1, Opt_fsdefault},
+ A(fsdefault), A(fsfloor), A(fshat), A(fsroot), A(fstransmute)
};
+#undef A
+
+static int match_opt_prefix(char *s, int l, char **arg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(smk_mount_opts); i++) {
+ size_t len = smk_mount_opts[i].len;
+ if (len > l || memcmp(s, smk_mount_opts[i].name, len))
+ continue;
+ if (len == l || s[len] != '=')
+ continue;
+ *arg = s + len + 1;
+ return smk_mount_opts[i].opt;
+ }
+ return Opt_error;
+}
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static char *smk_bu_mess[] = {
@@ -122,7 +140,7 @@
static int smk_bu_current(char *note, struct smack_known *oskp,
int mode, int rc)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
char acc[SMK_NUM_ACCESS_TYPE + 1];
if (rc <= 0)
@@ -143,7 +161,7 @@
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static int smk_bu_task(struct task_struct *otp, int mode, int rc)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
struct smack_known *smk_task = smk_of_task_struct(otp);
char acc[SMK_NUM_ACCESS_TYPE + 1];
@@ -165,8 +183,8 @@
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static int smk_bu_inode(struct inode *inode, int mode, int rc)
{
- struct task_smack *tsp = current_security();
- struct inode_smack *isp = inode->i_security;
+ struct task_smack *tsp = smack_cred(current_cred());
+ struct inode_smack *isp = smack_inode(inode);
char acc[SMK_NUM_ACCESS_TYPE + 1];
if (isp->smk_flags & SMK_INODE_IMPURE)
@@ -195,10 +213,10 @@
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static int smk_bu_file(struct file *file, int mode, int rc)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
struct smack_known *sskp = tsp->smk_task;
struct inode *inode = file_inode(file);
- struct inode_smack *isp = inode->i_security;
+ struct inode_smack *isp = smack_inode(inode);
char acc[SMK_NUM_ACCESS_TYPE + 1];
if (isp->smk_flags & SMK_INODE_IMPURE)
@@ -225,10 +243,10 @@
static int smk_bu_credfile(const struct cred *cred, struct file *file,
int mode, int rc)
{
- struct task_smack *tsp = cred->security;
+ struct task_smack *tsp = smack_cred(cred);
struct smack_known *sskp = tsp->smk_task;
struct inode *inode = file_inode(file);
- struct inode_smack *isp = inode->i_security;
+ struct inode_smack *isp = smack_inode(inode);
char acc[SMK_NUM_ACCESS_TYPE + 1];
if (isp->smk_flags & SMK_INODE_IMPURE)
@@ -270,7 +288,7 @@
if (!(ip->i_opflags & IOP_XATTR))
return ERR_PTR(-EOPNOTSUPP);
- buffer = kzalloc(SMK_LONGLABEL, GFP_KERNEL);
+ buffer = kzalloc(SMK_LONGLABEL, GFP_NOFS);
if (buffer == NULL)
return ERR_PTR(-ENOMEM);
@@ -288,50 +306,35 @@
}
/**
- * new_inode_smack - allocate an inode security blob
+ * init_inode_smack - initialize an inode security blob
+ * @inode: inode to extract the info from
* @skp: a pointer to the Smack label entry to use in the blob
*
- * Returns the new blob or NULL if there's no memory available
*/
-static struct inode_smack *new_inode_smack(struct smack_known *skp)
+static void init_inode_smack(struct inode *inode, struct smack_known *skp)
{
- struct inode_smack *isp;
-
- isp = kmem_cache_zalloc(smack_inode_cache, GFP_NOFS);
- if (isp == NULL)
- return NULL;
+ struct inode_smack *isp = smack_inode(inode);
isp->smk_inode = skp;
isp->smk_flags = 0;
mutex_init(&isp->smk_lock);
-
- return isp;
}
/**
- * new_task_smack - allocate a task security blob
+ * init_task_smack - initialize a task security blob
+ * @tsp: blob to initialize
* @task: a pointer to the Smack label for the running task
* @forked: a pointer to the Smack label for the forked task
- * @gfp: type of the memory for the allocation
*
- * Returns the new blob or NULL if there's no memory available
*/
-static struct task_smack *new_task_smack(struct smack_known *task,
- struct smack_known *forked, gfp_t gfp)
+static void init_task_smack(struct task_smack *tsp, struct smack_known *task,
+ struct smack_known *forked)
{
- struct task_smack *tsp;
-
- tsp = kzalloc(sizeof(struct task_smack), gfp);
- if (tsp == NULL)
- return NULL;
-
tsp->smk_task = task;
tsp->smk_forked = forked;
INIT_LIST_HEAD(&tsp->smk_rules);
INIT_LIST_HEAD(&tsp->smk_relabel);
mutex_init(&tsp->smk_rules_lock);
-
- return tsp;
}
/**
@@ -350,7 +353,7 @@
int rc = 0;
list_for_each_entry_rcu(orp, ohead, list) {
- nrp = kzalloc(sizeof(struct smack_rule), gfp);
+ nrp = kmem_cache_zalloc(smack_rule_cache, gfp);
if (nrp == NULL) {
rc = -ENOMEM;
break;
@@ -431,7 +434,7 @@
rcu_read_lock();
tracercred = __task_cred(tracer);
- tsp = tracercred->security;
+ tsp = smack_cred(tracercred);
tracer_known = smk_of_task(tsp);
if ((mode & PTRACE_MODE_ATTACH) &&
@@ -498,7 +501,7 @@
int rc;
struct smack_known *skp;
- skp = smk_of_task(current_security());
+ skp = smk_of_task(smack_cred(current_cred()));
rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__);
return rc;
@@ -506,7 +509,7 @@
/**
* smack_syslog - Smack approval on syslog
- * @type: message type
+ * @typefrom_file: unused
*
* Returns 0 on success, error code otherwise.
*/
@@ -524,7 +527,6 @@
return rc;
}
-
/*
* Superblock Hooks.
*/
@@ -567,181 +569,203 @@
sb->s_security = NULL;
}
-/**
- * smack_sb_copy_data - copy mount options data for processing
- * @orig: where to start
- * @smackopts: mount options string
- *
- * Returns 0 on success or -ENOMEM on error.
- *
- * Copy the Smack specific mount options out of the mount
- * options list.
- */
-static int smack_sb_copy_data(char *orig, char *smackopts)
+struct smack_mnt_opts {
+ const char *fsdefault, *fsfloor, *fshat, *fsroot, *fstransmute;
+};
+
+static void smack_free_mnt_opts(void *mnt_opts)
{
- char *cp, *commap, *otheropts, *dp;
-
- otheropts = (char *)get_zeroed_page(GFP_KERNEL);
- if (otheropts == NULL)
- return -ENOMEM;
-
- for (cp = orig, commap = orig; commap != NULL; cp = commap + 1) {
- if (strstr(cp, SMK_FSDEFAULT) == cp)
- dp = smackopts;
- else if (strstr(cp, SMK_FSFLOOR) == cp)
- dp = smackopts;
- else if (strstr(cp, SMK_FSHAT) == cp)
- dp = smackopts;
- else if (strstr(cp, SMK_FSROOT) == cp)
- dp = smackopts;
- else if (strstr(cp, SMK_FSTRANS) == cp)
- dp = smackopts;
- else
- dp = otheropts;
-
- commap = strchr(cp, ',');
- if (commap != NULL)
- *commap = '\0';
-
- if (*dp != '\0')
- strcat(dp, ",");
- strcat(dp, cp);
- }
-
- strcpy(orig, otheropts);
- free_page((unsigned long)otheropts);
-
- return 0;
+ struct smack_mnt_opts *opts = mnt_opts;
+ kfree(opts->fsdefault);
+ kfree(opts->fsfloor);
+ kfree(opts->fshat);
+ kfree(opts->fsroot);
+ kfree(opts->fstransmute);
+ kfree(opts);
}
-/**
- * smack_parse_opts_str - parse Smack specific mount options
- * @options: mount options string
- * @opts: where to store converted mount opts
- *
- * Returns 0 on success or -ENOMEM on error.
- *
- * converts Smack specific mount options to generic security option format
- */
-static int smack_parse_opts_str(char *options,
- struct security_mnt_opts *opts)
+static int smack_add_opt(int token, const char *s, void **mnt_opts)
{
- char *p;
- char *fsdefault = NULL;
- char *fsfloor = NULL;
- char *fshat = NULL;
- char *fsroot = NULL;
- char *fstransmute = NULL;
- int rc = -ENOMEM;
- int num_mnt_opts = 0;
- int token;
+ struct smack_mnt_opts *opts = *mnt_opts;
- opts->num_mnt_opts = 0;
-
- if (!options)
- return 0;
-
- while ((p = strsep(&options, ",")) != NULL) {
- substring_t args[MAX_OPT_ARGS];
-
- if (!*p)
- continue;
-
- token = match_token(p, smk_mount_tokens, args);
-
- switch (token) {
- case Opt_fsdefault:
- if (fsdefault)
- goto out_opt_err;
- fsdefault = match_strdup(&args[0]);
- if (!fsdefault)
- goto out_err;
- break;
- case Opt_fsfloor:
- if (fsfloor)
- goto out_opt_err;
- fsfloor = match_strdup(&args[0]);
- if (!fsfloor)
- goto out_err;
- break;
- case Opt_fshat:
- if (fshat)
- goto out_opt_err;
- fshat = match_strdup(&args[0]);
- if (!fshat)
- goto out_err;
- break;
- case Opt_fsroot:
- if (fsroot)
- goto out_opt_err;
- fsroot = match_strdup(&args[0]);
- if (!fsroot)
- goto out_err;
- break;
- case Opt_fstransmute:
- if (fstransmute)
- goto out_opt_err;
- fstransmute = match_strdup(&args[0]);
- if (!fstransmute)
- goto out_err;
- break;
- default:
- rc = -EINVAL;
- pr_warn("Smack: unknown mount option\n");
- goto out_err;
- }
+ if (!opts) {
+ opts = kzalloc(sizeof(struct smack_mnt_opts), GFP_KERNEL);
+ if (!opts)
+ return -ENOMEM;
+ *mnt_opts = opts;
}
+ if (!s)
+ return -ENOMEM;
- opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_KERNEL);
- if (!opts->mnt_opts)
- goto out_err;
-
- opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
- GFP_KERNEL);
- if (!opts->mnt_opts_flags)
- goto out_err;
-
- if (fsdefault) {
- opts->mnt_opts[num_mnt_opts] = fsdefault;
- opts->mnt_opts_flags[num_mnt_opts++] = FSDEFAULT_MNT;
+ switch (token) {
+ case Opt_fsdefault:
+ if (opts->fsdefault)
+ goto out_opt_err;
+ opts->fsdefault = s;
+ break;
+ case Opt_fsfloor:
+ if (opts->fsfloor)
+ goto out_opt_err;
+ opts->fsfloor = s;
+ break;
+ case Opt_fshat:
+ if (opts->fshat)
+ goto out_opt_err;
+ opts->fshat = s;
+ break;
+ case Opt_fsroot:
+ if (opts->fsroot)
+ goto out_opt_err;
+ opts->fsroot = s;
+ break;
+ case Opt_fstransmute:
+ if (opts->fstransmute)
+ goto out_opt_err;
+ opts->fstransmute = s;
+ break;
}
- if (fsfloor) {
- opts->mnt_opts[num_mnt_opts] = fsfloor;
- opts->mnt_opts_flags[num_mnt_opts++] = FSFLOOR_MNT;
- }
- if (fshat) {
- opts->mnt_opts[num_mnt_opts] = fshat;
- opts->mnt_opts_flags[num_mnt_opts++] = FSHAT_MNT;
- }
- if (fsroot) {
- opts->mnt_opts[num_mnt_opts] = fsroot;
- opts->mnt_opts_flags[num_mnt_opts++] = FSROOT_MNT;
- }
- if (fstransmute) {
- opts->mnt_opts[num_mnt_opts] = fstransmute;
- opts->mnt_opts_flags[num_mnt_opts++] = FSTRANS_MNT;
- }
-
- opts->num_mnt_opts = num_mnt_opts;
return 0;
out_opt_err:
- rc = -EINVAL;
pr_warn("Smack: duplicate mount options\n");
+ return -EINVAL;
+}
-out_err:
- kfree(fsdefault);
- kfree(fsfloor);
- kfree(fshat);
- kfree(fsroot);
- kfree(fstransmute);
+/**
+ * smack_fs_context_dup - Duplicate the security data on fs_context duplication
+ * @fc: The new filesystem context.
+ * @src_fc: The source filesystem context being duplicated.
+ *
+ * Returns 0 on success or -ENOMEM on error.
+ */
+static int smack_fs_context_dup(struct fs_context *fc,
+ struct fs_context *src_fc)
+{
+ struct smack_mnt_opts *dst, *src = src_fc->security;
+
+ if (!src)
+ return 0;
+
+ fc->security = kzalloc(sizeof(struct smack_mnt_opts), GFP_KERNEL);
+ if (!fc->security)
+ return -ENOMEM;
+ dst = fc->security;
+
+ if (src->fsdefault) {
+ dst->fsdefault = kstrdup(src->fsdefault, GFP_KERNEL);
+ if (!dst->fsdefault)
+ return -ENOMEM;
+ }
+ if (src->fsfloor) {
+ dst->fsfloor = kstrdup(src->fsfloor, GFP_KERNEL);
+ if (!dst->fsfloor)
+ return -ENOMEM;
+ }
+ if (src->fshat) {
+ dst->fshat = kstrdup(src->fshat, GFP_KERNEL);
+ if (!dst->fshat)
+ return -ENOMEM;
+ }
+ if (src->fsroot) {
+ dst->fsroot = kstrdup(src->fsroot, GFP_KERNEL);
+ if (!dst->fsroot)
+ return -ENOMEM;
+ }
+ if (src->fstransmute) {
+ dst->fstransmute = kstrdup(src->fstransmute, GFP_KERNEL);
+ if (!dst->fstransmute)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static const struct fs_parameter_spec smack_param_specs[] = {
+ fsparam_string("smackfsdef", Opt_fsdefault),
+ fsparam_string("smackfsdefault", Opt_fsdefault),
+ fsparam_string("smackfsfloor", Opt_fsfloor),
+ fsparam_string("smackfshat", Opt_fshat),
+ fsparam_string("smackfsroot", Opt_fsroot),
+ fsparam_string("smackfstransmute", Opt_fstransmute),
+ {}
+};
+
+static const struct fs_parameter_description smack_fs_parameters = {
+ .name = "smack",
+ .specs = smack_param_specs,
+};
+
+/**
+ * smack_fs_context_parse_param - Parse a single mount parameter
+ * @fc: The new filesystem context being constructed.
+ * @param: The parameter.
+ *
+ * Returns 0 on success, -ENOPARAM to pass the parameter on or anything else on
+ * error.
+ */
+static int smack_fs_context_parse_param(struct fs_context *fc,
+ struct fs_parameter *param)
+{
+ struct fs_parse_result result;
+ int opt, rc;
+
+ opt = fs_parse(fc, &smack_fs_parameters, param, &result);
+ if (opt < 0)
+ return opt;
+
+ rc = smack_add_opt(opt, param->string, &fc->security);
+ if (!rc)
+ param->string = NULL;
return rc;
}
+static int smack_sb_eat_lsm_opts(char *options, void **mnt_opts)
+{
+ char *from = options, *to = options;
+ bool first = true;
+
+ while (1) {
+ char *next = strchr(from, ',');
+ int token, len, rc;
+ char *arg = NULL;
+
+ if (next)
+ len = next - from;
+ else
+ len = strlen(from);
+
+ token = match_opt_prefix(from, len, &arg);
+ if (token != Opt_error) {
+ arg = kmemdup_nul(arg, from + len - arg, GFP_KERNEL);
+ rc = smack_add_opt(token, arg, mnt_opts);
+ if (unlikely(rc)) {
+ kfree(arg);
+ if (*mnt_opts)
+ smack_free_mnt_opts(*mnt_opts);
+ *mnt_opts = NULL;
+ return rc;
+ }
+ } else {
+ if (!first) { // copy with preceding comma
+ from--;
+ len++;
+ }
+ if (to != from)
+ memmove(to, from, len);
+ to += len;
+ first = false;
+ }
+ if (!from[len])
+ break;
+ from += len + 1;
+ }
+ *to = '\0';
+ return 0;
+}
+
/**
* smack_set_mnt_opts - set Smack specific mount options
* @sb: the file system superblock
- * @opts: Smack mount options
+ * @mnt_opts: Smack mount options
* @kern_flags: mount option from kernel space or user space
* @set_kern_flags: where to store converted mount opts
*
@@ -751,7 +775,7 @@
* labels.
*/
static int smack_set_mnt_opts(struct super_block *sb,
- struct security_mnt_opts *opts,
+ void *mnt_opts,
unsigned long kern_flags,
unsigned long *set_kern_flags)
{
@@ -760,18 +784,24 @@
struct superblock_smack *sp = sb->s_security;
struct inode_smack *isp;
struct smack_known *skp;
- int i;
- int num_opts = opts->num_mnt_opts;
- int transmute = 0;
+ struct smack_mnt_opts *opts = mnt_opts;
+ bool transmute = false;
if (sp->smk_flags & SMK_SB_INITIALIZED)
return 0;
+ if (inode->i_security == NULL) {
+ int rc = lsm_inode_alloc(inode);
+
+ if (rc)
+ return rc;
+ }
+
if (!smack_privileged(CAP_MAC_ADMIN)) {
/*
* Unprivileged mounts don't get to specify Smack values.
*/
- if (num_opts)
+ if (opts)
return -EPERM;
/*
* Unprivileged mounts get root and default from the caller.
@@ -787,101 +817,61 @@
if (sb->s_user_ns != &init_user_ns &&
sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC &&
sb->s_magic != RAMFS_MAGIC) {
- transmute = 1;
+ transmute = true;
sp->smk_flags |= SMK_SB_UNTRUSTED;
}
}
sp->smk_flags |= SMK_SB_INITIALIZED;
- for (i = 0; i < num_opts; i++) {
- switch (opts->mnt_opts_flags[i]) {
- case FSDEFAULT_MNT:
- skp = smk_import_entry(opts->mnt_opts[i], 0);
+ if (opts) {
+ if (opts->fsdefault) {
+ skp = smk_import_entry(opts->fsdefault, 0);
if (IS_ERR(skp))
return PTR_ERR(skp);
sp->smk_default = skp;
- break;
- case FSFLOOR_MNT:
- skp = smk_import_entry(opts->mnt_opts[i], 0);
+ }
+ if (opts->fsfloor) {
+ skp = smk_import_entry(opts->fsfloor, 0);
if (IS_ERR(skp))
return PTR_ERR(skp);
sp->smk_floor = skp;
- break;
- case FSHAT_MNT:
- skp = smk_import_entry(opts->mnt_opts[i], 0);
+ }
+ if (opts->fshat) {
+ skp = smk_import_entry(opts->fshat, 0);
if (IS_ERR(skp))
return PTR_ERR(skp);
sp->smk_hat = skp;
- break;
- case FSROOT_MNT:
- skp = smk_import_entry(opts->mnt_opts[i], 0);
+ }
+ if (opts->fsroot) {
+ skp = smk_import_entry(opts->fsroot, 0);
if (IS_ERR(skp))
return PTR_ERR(skp);
sp->smk_root = skp;
- break;
- case FSTRANS_MNT:
- skp = smk_import_entry(opts->mnt_opts[i], 0);
+ }
+ if (opts->fstransmute) {
+ skp = smk_import_entry(opts->fstransmute, 0);
if (IS_ERR(skp))
return PTR_ERR(skp);
sp->smk_root = skp;
- transmute = 1;
- break;
- default:
- break;
+ transmute = true;
}
}
/*
* Initialize the root inode.
*/
- isp = inode->i_security;
- if (isp == NULL) {
- isp = new_inode_smack(sp->smk_root);
- if (isp == NULL)
- return -ENOMEM;
- inode->i_security = isp;
- } else
- isp->smk_inode = sp->smk_root;
+ init_inode_smack(inode, sp->smk_root);
- if (transmute)
+ if (transmute) {
+ isp = smack_inode(inode);
isp->smk_flags |= SMK_INODE_TRANSMUTE;
+ }
return 0;
}
/**
- * smack_sb_kern_mount - Smack specific mount processing
- * @sb: the file system superblock
- * @flags: the mount flags
- * @data: the smack mount options
- *
- * Returns 0 on success, an error code on failure
- */
-static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
-{
- int rc = 0;
- char *options = data;
- struct security_mnt_opts opts;
-
- security_init_mnt_opts(&opts);
-
- if (!options)
- goto out;
-
- rc = smack_parse_opts_str(options, &opts);
- if (rc)
- goto out_err;
-
-out:
- rc = smack_set_mnt_opts(sb, &opts, 0, NULL);
-
-out_err:
- security_free_mnt_opts(&opts);
- return rc;
-}
-
-/**
* smack_sb_statfs - Smack check on statfs
* @dentry: identifies the file system in question
*
@@ -915,7 +905,7 @@
static int smack_bprm_set_creds(struct linux_binprm *bprm)
{
struct inode *inode = file_inode(bprm->file);
- struct task_smack *bsp = bprm->cred->security;
+ struct task_smack *bsp = smack_cred(bprm->cred);
struct inode_smack *isp;
struct superblock_smack *sbsp;
int rc;
@@ -923,7 +913,7 @@
if (bprm->called_set_creds)
return 0;
- isp = inode->i_security;
+ isp = smack_inode(inode);
if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
return 0;
@@ -947,7 +937,8 @@
if (rc != 0)
return rc;
- } else if (bprm->unsafe)
+ }
+ if (bprm->unsafe & ~LSM_UNSAFE_PTRACE)
return -EPERM;
bsp->smk_task = isp->smk_task;
@@ -968,55 +959,17 @@
* smack_inode_alloc_security - allocate an inode blob
* @inode: the inode in need of a blob
*
- * Returns 0 if it gets a blob, -ENOMEM otherwise
+ * Returns 0
*/
static int smack_inode_alloc_security(struct inode *inode)
{
struct smack_known *skp = smk_of_current();
- inode->i_security = new_inode_smack(skp);
- if (inode->i_security == NULL)
- return -ENOMEM;
+ init_inode_smack(inode, skp);
return 0;
}
/**
- * smack_inode_free_rcu - Free inode_smack blob from cache
- * @head: the rcu_head for getting inode_smack pointer
- *
- * Call back function called from call_rcu() to free
- * the i_security blob pointer in inode
- */
-static void smack_inode_free_rcu(struct rcu_head *head)
-{
- struct inode_smack *issp;
-
- issp = container_of(head, struct inode_smack, smk_rcu);
- kmem_cache_free(smack_inode_cache, issp);
-}
-
-/**
- * smack_inode_free_security - free an inode blob using call_rcu()
- * @inode: the inode with a blob
- *
- * Clears the blob pointer in inode using RCU
- */
-static void smack_inode_free_security(struct inode *inode)
-{
- struct inode_smack *issp = inode->i_security;
-
- /*
- * The inode may still be referenced in a path walk and
- * a call to smack_inode_permission() can be made
- * after smack_inode_free_security() is called.
- * To avoid race condition 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.
- */
- call_rcu(&issp->smk_rcu, smack_inode_free_rcu);
-}
-
-/**
* smack_inode_init_security - copy out the smack from an inode
* @inode: the newly created inode
* @dir: containing directory object
@@ -1031,7 +984,7 @@
const struct qstr *qstr, const char **name,
void **value, size_t *len)
{
- struct inode_smack *issp = inode->i_security;
+ struct inode_smack *issp = smack_inode(inode);
struct smack_known *skp = smk_of_current();
struct smack_known *isp = smk_of_inode(inode);
struct smack_known *dsp = smk_of_inode(dir);
@@ -1212,7 +1165,7 @@
*
* This is the important Smack hook.
*
- * Returns 0 if access is permitted, -EACCES otherwise
+ * Returns 0 if access is permitted, an error code otherwise
*/
static int smack_inode_permission(struct inode *inode, int mask)
{
@@ -1270,8 +1223,7 @@
/**
* smack_inode_getattr - Smack check for getting attributes
- * @mnt: vfsmount of the object
- * @dentry: the object
+ * @path: path to extract the info from
*
* Returns 0 if access is permitted, an error code otherwise
*/
@@ -1369,7 +1321,7 @@
const void *value, size_t size, int flags)
{
struct smack_known *skp;
- struct inode_smack *isp = d_backing_inode(dentry)->i_security;
+ struct inode_smack *isp = smack_inode(d_backing_inode(dentry));
if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
isp->smk_flags |= SMK_INODE_TRANSMUTE;
@@ -1450,7 +1402,7 @@
if (rc != 0)
return rc;
- isp = d_backing_inode(dentry)->i_security;
+ isp = smack_inode(d_backing_inode(dentry));
/*
* Don't do anything special for these.
* XATTR_NAME_SMACKIPIN
@@ -1582,25 +1534,13 @@
*/
static int smack_file_alloc_security(struct file *file)
{
- struct smack_known *skp = smk_of_current();
+ struct smack_known **blob = smack_file(file);
- file->f_security = skp;
+ *blob = smk_of_current();
return 0;
}
/**
- * smack_file_free_security - clear a file security blob
- * @file: the object
- *
- * The security blob for a file is a pointer to the master
- * label list, so no memory is freed.
- */
-static void smack_file_free_security(struct file *file)
-{
- file->f_security = NULL;
-}
-
-/**
* smack_file_ioctl - Smack check on ioctls
* @file: the object
* @cmd: what to do
@@ -1737,7 +1677,7 @@
if (unlikely(IS_PRIVATE(file_inode(file))))
return 0;
- isp = file_inode(file)->i_security;
+ isp = smack_inode(file_inode(file));
if (isp->smk_mmap == NULL)
return 0;
sbsp = file_inode(file)->i_sb->s_security;
@@ -1746,7 +1686,7 @@
return -EACCES;
mkp = isp->smk_mmap;
- tsp = current_security();
+ tsp = smack_cred(current_cred());
skp = smk_of_current();
rc = 0;
@@ -1824,7 +1764,9 @@
*/
static void smack_file_set_fowner(struct file *file)
{
- file->f_security = smk_of_current();
+ struct smack_known **blob = smack_file(file);
+
+ *blob = smk_of_current();
}
/**
@@ -1841,8 +1783,9 @@
static int smack_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int signum)
{
+ struct smack_known **blob;
struct smack_known *skp;
- struct smack_known *tkp = smk_of_task(tsk->cred->security);
+ struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred));
const struct cred *tcred;
struct file *file;
int rc;
@@ -1854,7 +1797,8 @@
file = container_of(fown, struct file, f_owner);
/* we don't log here as rc can be overriden */
- skp = file->f_security;
+ blob = smack_file(file);
+ skp = *blob;
rc = smk_access(skp, tkp, MAY_DELIVER, NULL);
rc = smk_bu_note("sigiotask", skp, tkp, MAY_DELIVER, rc);
@@ -1895,7 +1839,7 @@
if (inode->i_sb->s_magic == SOCKFS_MAGIC) {
sock = SOCKET_I(inode);
ssp = sock->sk->sk_security;
- tsp = current_security();
+ tsp = smack_cred(current_cred());
/*
* If the receiving process can't write to the
* passed socket or if the passed socket can't
@@ -1926,18 +1870,17 @@
/**
* smack_file_open - Smack dentry open processing
* @file: the object
- * @cred: task credential
*
* Set the security blob in the file structure.
* Allow the open only if the task has read access. There are
* many read operations (e.g. fstat) that you can do with an
* fd even if you have the file open write-only.
*
- * Returns 0
+ * Returns 0 if current has access, error code otherwise
*/
static int smack_file_open(struct file *file)
{
- struct task_smack *tsp = file->f_cred->security;
+ struct task_smack *tsp = smack_cred(file->f_cred);
struct inode *inode = file_inode(file);
struct smk_audit_info ad;
int rc;
@@ -1956,7 +1899,7 @@
/**
* smack_cred_alloc_blank - "allocate" blank task-level security credentials
- * @new: the new credentials
+ * @cred: the new credentials
* @gfp: the atomicity of any memory allocations
*
* Prepare a blank set of credentials for modification. This must allocate all
@@ -1965,14 +1908,7 @@
*/
static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
- struct task_smack *tsp;
-
- tsp = new_task_smack(NULL, NULL, gfp);
- if (tsp == NULL)
- return -ENOMEM;
-
- cred->security = tsp;
-
+ init_task_smack(smack_cred(cred), NULL, NULL);
return 0;
}
@@ -1984,23 +1920,18 @@
*/
static void smack_cred_free(struct cred *cred)
{
- struct task_smack *tsp = cred->security;
+ struct task_smack *tsp = smack_cred(cred);
struct smack_rule *rp;
struct list_head *l;
struct list_head *n;
- if (tsp == NULL)
- return;
- cred->security = NULL;
-
smk_destroy_label_list(&tsp->smk_relabel);
list_for_each_safe(l, n, &tsp->smk_rules) {
rp = list_entry(l, struct smack_rule, list);
list_del(&rp->list);
- kfree(rp);
+ kmem_cache_free(smack_rule_cache, rp);
}
- kfree(tsp);
}
/**
@@ -2014,15 +1945,11 @@
static int smack_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- struct task_smack *old_tsp = old->security;
- struct task_smack *new_tsp;
+ struct task_smack *old_tsp = smack_cred(old);
+ struct task_smack *new_tsp = smack_cred(new);
int rc;
- new_tsp = new_task_smack(old_tsp->smk_task, old_tsp->smk_task, gfp);
- if (new_tsp == NULL)
- return -ENOMEM;
-
- new->security = new_tsp;
+ init_task_smack(new_tsp, old_tsp->smk_task, old_tsp->smk_task);
rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp);
if (rc != 0)
@@ -2030,10 +1957,7 @@
rc = smk_copy_relabel(&new_tsp->smk_relabel, &old_tsp->smk_relabel,
gfp);
- if (rc != 0)
- return rc;
-
- return 0;
+ return rc;
}
/**
@@ -2045,31 +1969,30 @@
*/
static void smack_cred_transfer(struct cred *new, const struct cred *old)
{
- struct task_smack *old_tsp = old->security;
- struct task_smack *new_tsp = new->security;
+ 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 */
}
/**
* smack_cred_getsecid - get the secid corresponding to a creds structure
- * @c: the object creds
+ * @cred: the object creds
* @secid: where to put the result
*
* Sets the secid to contain a u32 version of the smack label.
*/
-static void smack_cred_getsecid(const struct cred *c, u32 *secid)
+static void smack_cred_getsecid(const struct cred *cred, u32 *secid)
{
struct smack_known *skp;
rcu_read_lock();
- skp = smk_of_task(c->security);
+ skp = smk_of_task(smack_cred(cred));
*secid = skp->smk_secid;
rcu_read_unlock();
}
@@ -2083,7 +2006,7 @@
*/
static int smack_kernel_act_as(struct cred *new, u32 secid)
{
- struct task_smack *new_tsp = new->security;
+ struct task_smack *new_tsp = smack_cred(new);
new_tsp->smk_task = smack_from_secid(secid);
return 0;
@@ -2100,8 +2023,8 @@
static int smack_kernel_create_files_as(struct cred *new,
struct inode *inode)
{
- struct inode_smack *isp = inode->i_security;
- struct task_smack *tsp = new->security;
+ struct inode_smack *isp = smack_inode(inode);
+ struct task_smack *tsp = smack_cred(new);
tsp->smk_forked = isp->smk_inode;
tsp->smk_task = tsp->smk_forked;
@@ -2216,8 +2139,6 @@
/**
* smack_task_setscheduler - Smack check on setting scheduler
* @p: the task object
- * @policy: unused
- * @lp: unused
*
* Return 0 if read access is permitted
*/
@@ -2258,7 +2179,7 @@
* Return 0 if write access is permitted
*
*/
-static int smack_task_kill(struct task_struct *p, struct siginfo *info,
+static int smack_task_kill(struct task_struct *p, struct kernel_siginfo *info,
int sig, const struct cred *cred)
{
struct smk_audit_info ad;
@@ -2285,7 +2206,7 @@
* specific behavior. This is not clean. For one thing
* we can't take privilege into account.
*/
- skp = smk_of_task(cred->security);
+ skp = smk_of_task(smack_cred(cred));
rc = smk_access(skp, tkp, MAY_DELIVER, &ad);
rc = smk_bu_note("USB signal", skp, tkp, MAY_DELIVER, rc);
return rc;
@@ -2300,7 +2221,7 @@
*/
static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
{
- struct inode_smack *isp = inode->i_security;
+ struct inode_smack *isp = smack_inode(inode);
struct smack_known *skp = smk_of_task_struct(p);
isp->smk_inode = skp;
@@ -2687,8 +2608,9 @@
/**
* smk_ipv6_port_check - check Smack port access
- * @sock: socket
+ * @sk: socket
* @address: address
+ * @act: the action being taken
*
* Create or update the port list entry
*/
@@ -2763,7 +2685,7 @@
const void *value, size_t size, int flags)
{
struct smack_known *skp;
- struct inode_smack *nsp = inode->i_security;
+ struct inode_smack *nsp = smack_inode(inode);
struct socket_smack *ssp;
struct socket *sock;
int rc = 0;
@@ -2858,7 +2780,7 @@
*
* Cross reference the peer labels for SO_PEERSEC
*
- * Returns 0 on success, and error code otherwise
+ * Returns 0
*/
static int smack_socket_socketpair(struct socket *socka,
struct socket *sockb)
@@ -2881,13 +2803,17 @@
*
* Records the label bound to a port.
*
- * Returns 0
+ * Returns 0 on success, and error code otherwise
*/
static int smack_socket_bind(struct socket *sock, struct sockaddr *address,
int addrlen)
{
- if (sock->sk != NULL && sock->sk->sk_family == PF_INET6)
+ if (sock->sk != NULL && sock->sk->sk_family == PF_INET6) {
+ if (addrlen < SIN6_LEN_RFC2133 ||
+ address->sa_family != AF_INET6)
+ return -EINVAL;
smk_ipv6_port_label(sock, address);
+ }
return 0;
}
#endif /* SMACK_IPV6_PORT_LABELING */
@@ -2923,12 +2849,13 @@
switch (sock->sk->sk_family) {
case PF_INET:
- if (addrlen < sizeof(struct sockaddr_in))
+ if (addrlen < sizeof(struct sockaddr_in) ||
+ sap->sa_family != AF_INET)
return -EINVAL;
rc = smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap);
break;
case PF_INET6:
- if (addrlen < sizeof(struct sockaddr_in6))
+ if (addrlen < SIN6_LEN_RFC2133 || sap->sa_family != AF_INET6)
return -EINVAL;
#ifdef SMACK_IPV6_SECMARK_LABELING
rsp = smack_ipv6host_label(sip);
@@ -2972,24 +2899,13 @@
*/
static int smack_msg_msg_alloc_security(struct msg_msg *msg)
{
- struct smack_known *skp = smk_of_current();
+ struct smack_known **blob = smack_msg_msg(msg);
- msg->security = skp;
+ *blob = smk_of_current();
return 0;
}
/**
- * smack_msg_msg_free_security - Clear the security blob for msg_msg
- * @msg: the object
- *
- * Clears the blob pointer
- */
-static void smack_msg_msg_free_security(struct msg_msg *msg)
-{
- msg->security = NULL;
-}
-
-/**
* smack_of_ipc - the smack pointer for the ipc
* @isp: the object
*
@@ -2997,7 +2913,9 @@
*/
static struct smack_known *smack_of_ipc(struct kern_ipc_perm *isp)
{
- return (struct smack_known *)isp->security;
+ struct smack_known **blob = smack_ipc(isp);
+
+ return *blob;
}
/**
@@ -3008,24 +2926,13 @@
*/
static int smack_ipc_alloc_security(struct kern_ipc_perm *isp)
{
- struct smack_known *skp = smk_of_current();
+ struct smack_known **blob = smack_ipc(isp);
- isp->security = skp;
+ *blob = smk_of_current();
return 0;
}
/**
- * smack_ipc_free_security - Clear the security blob for ipc
- * @isp: the object
- *
- * Clears the blob pointer
- */
-static void smack_ipc_free_security(struct kern_ipc_perm *isp)
-{
- isp->security = NULL;
-}
-
-/**
* smk_curacc_shm : check if current has access on shm
* @isp : the object
* @access : access requested
@@ -3105,13 +3012,13 @@
*
* Returns 0 if current has the requested access, error code otherwise
*/
-static int smack_shm_shmat(struct kern_ipc_perm *ipc, char __user *shmaddr,
+static int smack_shm_shmat(struct kern_ipc_perm *isp, char __user *shmaddr,
int shmflg)
{
int may;
may = smack_flags_to_may(shmflg);
- return smk_curacc_shm(ipc, may);
+ return smk_curacc_shm(isp, may);
}
/**
@@ -3322,7 +3229,8 @@
*/
static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag)
{
- struct smack_known *iskp = ipp->security;
+ struct smack_known **blob = smack_ipc(ipp);
+ struct smack_known *iskp = *blob;
int may = smack_flags_to_may(flag);
struct smk_audit_info ad;
int rc;
@@ -3343,7 +3251,8 @@
*/
static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid)
{
- struct smack_known *iskp = ipp->security;
+ struct smack_known **blob = smack_ipc(ipp);
+ struct smack_known *iskp = *blob;
*secid = iskp->smk_secid;
}
@@ -3371,7 +3280,7 @@
if (inode == NULL)
return;
- isp = inode->i_security;
+ isp = smack_inode(inode);
mutex_lock(&isp->smk_lock);
/*
@@ -3474,13 +3383,12 @@
*/
final = &smack_known_star;
/*
- * No break.
- *
* If a smack value has been set we want to use it,
* but since tmpfs isn't giving us the opportunity
* to set mount options simulate setting the
* superblock default.
*/
+ /* Fall through */
default:
/*
* This isn't an understood special case.
@@ -3612,7 +3520,7 @@
*/
static int smack_setprocattr(const char *name, void *value, size_t size)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
struct cred *new;
struct smack_known *skp;
struct smack_known_list_elem *sklep;
@@ -3653,7 +3561,7 @@
if (new == NULL)
return -ENOMEM;
- tsp = new->security;
+ tsp = smack_cred(new);
tsp->smk_task = skp;
/*
* process can change its label only once
@@ -3777,9 +3685,16 @@
switch (sock->sk->sk_family) {
case AF_INET:
+ if (msg->msg_namelen < sizeof(struct sockaddr_in) ||
+ sip->sin_family != AF_INET)
+ return -EINVAL;
rc = smack_netlabel_send(sock->sk, sip);
break;
+#if IS_ENABLED(CONFIG_IPV6)
case AF_INET6:
+ if (msg->msg_namelen < SIN6_LEN_RFC2133 ||
+ sap->sin6_family != AF_INET6)
+ return -EINVAL;
#ifdef SMACK_IPV6_SECMARK_LABELING
rsp = smack_ipv6host_label(sap);
if (rsp != NULL)
@@ -3789,6 +3704,7 @@
#ifdef SMACK_IPV6_PORT_LABELING
rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING);
#endif
+#endif /* IS_ENABLED(CONFIG_IPV6) */
break;
}
return rc;
@@ -4001,10 +3917,14 @@
#ifdef SMACK_IPV6_SECMARK_LABELING
if (skb && skb->secmark != 0)
skp = smack_from_secid(skb->secmark);
+ else if (smk_ipv6_localhost(&sadd))
+ break;
else
skp = smack_ipv6host_label(&sadd);
if (skp == NULL)
skp = smack_net_ambient;
+ if (skb == NULL)
+ break;
#ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
ad.a.u.net->family = family;
@@ -4298,7 +4218,7 @@
static int smack_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
{
- struct smack_known *skp = smk_of_task(cred->security);
+ struct smack_known *skp = smk_of_task(smack_cred(cred));
key->security = skp;
return 0;
@@ -4329,10 +4249,16 @@
{
struct key *keyp;
struct smk_audit_info ad;
- struct smack_known *tkp = smk_of_task(cred->security);
+ struct smack_known *tkp = smk_of_task(smack_cred(cred));
int request = 0;
int rc;
+ /*
+ * Validate requested permissions
+ */
+ if (perm & ~KEY_NEED_ALL)
+ return -EINVAL;
+
keyp = key_ref_to_ptr(key_ref);
if (keyp == NULL)
return -EINVAL;
@@ -4356,10 +4282,10 @@
ad.a.u.key_struct.key = keyp->serial;
ad.a.u.key_struct.key_desc = keyp->description;
#endif
- if (perm & KEY_NEED_READ)
- request = MAY_READ;
+ if (perm & (KEY_NEED_READ | KEY_NEED_SEARCH | KEY_NEED_VIEW))
+ request |= MAY_READ;
if (perm & (KEY_NEED_WRITE | KEY_NEED_LINK | KEY_NEED_SETATTR))
- request = MAY_WRITE;
+ request |= MAY_WRITE;
rc = smk_access(tkp, keyp->security, request, &ad);
rc = smk_bu_note("key access", tkp, keyp->security, request, rc);
return rc;
@@ -4471,13 +4397,11 @@
* @field: audit rule flags given from user-space
* @op: required testing operator
* @vrule: smack internal rule presentation
- * @actx: audit context associated with the check
*
* 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,
- struct audit_context *actx)
+static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule)
{
struct smack_known *skp;
char *rule = vrule;
@@ -4598,12 +4522,12 @@
return -ENOMEM;
}
- tsp = new_creds->security;
+ tsp = smack_cred(new_creds);
/*
* Get label from overlay inode and set it in create_sid
*/
- isp = d_inode(dentry->d_parent)->i_security;
+ isp = smack_inode(d_inode(dentry->d_parent));
skp = isp->smk_inode;
tsp->smk_task = skp;
*new = new_creds;
@@ -4626,8 +4550,8 @@
const struct cred *old,
struct cred *new)
{
- struct task_smack *otsp = old->security;
- struct task_smack *ntsp = new->security;
+ struct task_smack *otsp = smack_cred(old);
+ struct task_smack *ntsp = smack_cred(new);
struct inode_smack *isp;
int may;
@@ -4640,7 +4564,7 @@
/*
* the attribute of the containing directory
*/
- isp = d_inode(dentry->d_parent)->i_security;
+ isp = smack_inode(d_inode(dentry->d_parent));
if (isp->smk_flags & SMK_INODE_TRANSMUTE) {
rcu_read_lock();
@@ -4660,23 +4584,32 @@
return 0;
}
+struct lsm_blob_sizes smack_blob_sizes __lsm_ro_after_init = {
+ .lbs_cred = sizeof(struct task_smack),
+ .lbs_file = sizeof(struct smack_known *),
+ .lbs_inode = sizeof(struct inode_smack),
+ .lbs_ipc = sizeof(struct smack_known *),
+ .lbs_msg_msg = sizeof(struct smack_known *),
+};
+
static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
LSM_HOOK_INIT(syslog, smack_syslog),
+ LSM_HOOK_INIT(fs_context_dup, smack_fs_context_dup),
+ LSM_HOOK_INIT(fs_context_parse_param, smack_fs_context_parse_param),
+
LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
- LSM_HOOK_INIT(sb_copy_data, smack_sb_copy_data),
- LSM_HOOK_INIT(sb_kern_mount, smack_sb_kern_mount),
+ LSM_HOOK_INIT(sb_free_mnt_opts, smack_free_mnt_opts),
+ LSM_HOOK_INIT(sb_eat_lsm_opts, smack_sb_eat_lsm_opts),
LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),
- LSM_HOOK_INIT(sb_parse_opts_str, smack_parse_opts_str),
LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security),
- LSM_HOOK_INIT(inode_free_security, smack_inode_free_security),
LSM_HOOK_INIT(inode_init_security, smack_inode_init_security),
LSM_HOOK_INIT(inode_link, smack_inode_link),
LSM_HOOK_INIT(inode_unlink, smack_inode_unlink),
@@ -4695,7 +4628,6 @@
LSM_HOOK_INIT(inode_getsecid, smack_inode_getsecid),
LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security),
- LSM_HOOK_INIT(file_free_security, smack_file_free_security),
LSM_HOOK_INIT(file_ioctl, smack_file_ioctl),
LSM_HOOK_INIT(file_lock, smack_file_lock),
LSM_HOOK_INIT(file_fcntl, smack_file_fcntl),
@@ -4731,23 +4663,19 @@
LSM_HOOK_INIT(ipc_getsecid, smack_ipc_getsecid),
LSM_HOOK_INIT(msg_msg_alloc_security, smack_msg_msg_alloc_security),
- LSM_HOOK_INIT(msg_msg_free_security, smack_msg_msg_free_security),
LSM_HOOK_INIT(msg_queue_alloc_security, smack_ipc_alloc_security),
- LSM_HOOK_INIT(msg_queue_free_security, smack_ipc_free_security),
LSM_HOOK_INIT(msg_queue_associate, smack_msg_queue_associate),
LSM_HOOK_INIT(msg_queue_msgctl, smack_msg_queue_msgctl),
LSM_HOOK_INIT(msg_queue_msgsnd, smack_msg_queue_msgsnd),
LSM_HOOK_INIT(msg_queue_msgrcv, smack_msg_queue_msgrcv),
LSM_HOOK_INIT(shm_alloc_security, smack_ipc_alloc_security),
- LSM_HOOK_INIT(shm_free_security, smack_ipc_free_security),
LSM_HOOK_INIT(shm_associate, smack_shm_associate),
LSM_HOOK_INIT(shm_shmctl, smack_shm_shmctl),
LSM_HOOK_INIT(shm_shmat, smack_shm_shmat),
LSM_HOOK_INIT(sem_alloc_security, smack_ipc_alloc_security),
- LSM_HOOK_INIT(sem_free_security, smack_ipc_free_security),
LSM_HOOK_INIT(sem_associate, smack_sem_associate),
LSM_HOOK_INIT(sem_semctl, smack_sem_semctl),
LSM_HOOK_INIT(sem_semop, smack_sem_semop),
@@ -4834,27 +4762,33 @@
/**
* smack_init - initialize the smack system
*
- * Returns 0
+ * Returns 0 on success, -ENOMEM is there's no memory
*/
static __init int smack_init(void)
{
- struct cred *cred;
+ struct cred *cred = (struct cred *) current->cred;
struct task_smack *tsp;
- if (!security_module_enable("smack"))
- return 0;
-
smack_inode_cache = KMEM_CACHE(inode_smack, 0);
if (!smack_inode_cache)
return -ENOMEM;
- tsp = new_task_smack(&smack_known_floor, &smack_known_floor,
- GFP_KERNEL);
- if (tsp == NULL) {
+ smack_rule_cache = KMEM_CACHE(smack_rule, 0);
+ if (!smack_rule_cache) {
kmem_cache_destroy(smack_inode_cache);
return -ENOMEM;
}
+ /*
+ * Set the security state for the initial task.
+ */
+ tsp = smack_cred(cred);
+ init_task_smack(tsp, &smack_known_floor, &smack_known_floor);
+
+ /*
+ * Register with LSM
+ */
+ security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
smack_enabled = 1;
pr_info("Smack: Initializing.\n");
@@ -4868,20 +4802,9 @@
pr_info("Smack: IPv6 Netfilter enabled.\n");
#endif
- /*
- * Set the security state for the initial task.
- */
- cred = (struct cred *) current->cred;
- cred->security = tsp;
-
/* initialize the smack_known_list */
init_smack_known_list();
- /*
- * Register with LSM
- */
- security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
-
return 0;
}
@@ -4889,4 +4812,9 @@
* Smack requires early initialization in order to label
* all processes and objects when they are created.
*/
-security_initcall(smack_init);
+DEFINE_LSM(smack) = {
+ .name = "smack",
+ .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
+ .blobs = &smack_blob_sizes,
+ .init = smack_init,
+};
diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c
index e36d178..fc7399b 100644
--- a/security/smack/smack_netfilter.c
+++ b/security/smack/smack_netfilter.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Simplified MAC Kernel (smack) security module
*
@@ -8,10 +9,6 @@
*
* Copyright (C) 2014 Casey Schaufler <casey@schaufler-ca.com>
* Copyright (C) 2014 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
*/
#include <linux/netfilter_ipv4.h>
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index f6482e5..e3e05c0 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
*
- * 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.
- *
* Authors:
* Casey Schaufler <casey@schaufler-ca.com>
* Ahmed S. Darwish <darwish.07@gmail.com>
@@ -13,7 +10,6 @@
*
* Karl MacMillan <kmacmillan@tresys.com>
* James Morris <jmorris@redhat.com>
- *
*/
#include <linux/kernel.h>
@@ -27,6 +23,7 @@
#include <linux/ctype.h>
#include <linux/audit.h>
#include <linux/magic.h>
+#include <linux/fs_context.h>
#include "smack.h"
#define BEBITS (sizeof(__be32) * 8)
@@ -67,7 +64,6 @@
/*
* List locks
*/
-static DEFINE_MUTEX(smack_master_list_lock);
static DEFINE_MUTEX(smack_cipso_lock);
static DEFINE_MUTEX(smack_ambient_lock);
static DEFINE_MUTEX(smk_net4addr_lock);
@@ -134,15 +130,7 @@
/*
* Rule lists are maintained for each label.
- * This master list is just for reading /smack/load and /smack/load2.
*/
-struct smack_master_list {
- struct list_head list;
- struct smack_rule *smk_rule;
-};
-
-static LIST_HEAD(smack_rule_list);
-
struct smack_parsed_rule {
struct smack_known *smk_subject;
struct smack_known *smk_object;
@@ -211,7 +199,6 @@
* @srp: the rule to add or replace
* @rule_list: the list of rules
* @rule_lock: the rule list lock
- * @global: if non-zero, indicates a global rule
*
* Looks through the current subject/object/access list for
* the subject/object pair and replaces the access that was
@@ -223,10 +210,9 @@
*/
static int smk_set_access(struct smack_parsed_rule *srp,
struct list_head *rule_list,
- struct mutex *rule_lock, int global)
+ struct mutex *rule_lock)
{
struct smack_rule *sp;
- struct smack_master_list *smlp;
int found = 0;
int rc = 0;
@@ -247,7 +233,7 @@
}
if (found == 0) {
- sp = kzalloc(sizeof(*sp), GFP_KERNEL);
+ sp = kmem_cache_zalloc(smack_rule_cache, GFP_KERNEL);
if (sp == NULL) {
rc = -ENOMEM;
goto out;
@@ -258,22 +244,6 @@
sp->smk_access = srp->smk_access1 & ~srp->smk_access2;
list_add_rcu(&sp->list, rule_list);
- /*
- * If this is a global as opposed to self and a new rule
- * it needs to get added for reporting.
- */
- if (global) {
- mutex_unlock(rule_lock);
- smlp = kzalloc(sizeof(*smlp), GFP_KERNEL);
- if (smlp != NULL) {
- smlp->smk_rule = sp;
- mutex_lock(&smack_master_list_lock);
- list_add_rcu(&smlp->list, &smack_rule_list);
- mutex_unlock(&smack_master_list_lock);
- } else
- rc = -ENOMEM;
- return rc;
- }
}
out:
@@ -540,9 +510,9 @@
if (rule_list == NULL)
rc = smk_set_access(&rule, &rule.smk_subject->smk_rules,
- &rule.smk_subject->smk_rules_lock, 1);
+ &rule.smk_subject->smk_rules_lock);
else
- rc = smk_set_access(&rule, rule_list, rule_lock, 0);
+ rc = smk_set_access(&rule, rule_list, rule_lock);
if (rc)
goto out;
@@ -636,21 +606,23 @@
static void *load2_seq_start(struct seq_file *s, loff_t *pos)
{
- return smk_seq_start(s, pos, &smack_rule_list);
+ return smk_seq_start(s, pos, &smack_known_list);
}
static void *load2_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- return smk_seq_next(s, v, pos, &smack_rule_list);
+ return smk_seq_next(s, v, pos, &smack_known_list);
}
static int load_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
- struct smack_master_list *smlp =
- list_entry_rcu(list, struct smack_master_list, list);
+ struct smack_rule *srp;
+ struct smack_known *skp =
+ list_entry_rcu(list, struct smack_known, list);
- smk_rule_show(s, smlp->smk_rule, SMK_LABELLEN);
+ list_for_each_entry_rcu(srp, &skp->smk_rules, list)
+ smk_rule_show(s, srp, SMK_LABELLEN);
return 0;
}
@@ -2208,14 +2180,14 @@
static void *load_self_seq_start(struct seq_file *s, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_start(s, pos, &tsp->smk_rules);
}
static void *load_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_next(s, v, pos, &tsp->smk_rules);
}
@@ -2262,7 +2234,7 @@
static ssize_t smk_write_load_self(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules,
&tsp->smk_rules_lock, SMK_FIXED24_FMT);
@@ -2352,10 +2324,12 @@
static int load2_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
- struct smack_master_list *smlp =
- list_entry_rcu(list, struct smack_master_list, list);
+ struct smack_rule *srp;
+ struct smack_known *skp =
+ list_entry_rcu(list, struct smack_known, list);
- smk_rule_show(s, smlp->smk_rule, SMK_LONGLABEL);
+ list_for_each_entry_rcu(srp, &skp->smk_rules, list)
+ smk_rule_show(s, srp, SMK_LONGLABEL);
return 0;
}
@@ -2414,14 +2388,14 @@
static void *load_self2_seq_start(struct seq_file *s, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_start(s, pos, &tsp->smk_rules);
}
static void *load_self2_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_next(s, v, pos, &tsp->smk_rules);
}
@@ -2467,7 +2441,7 @@
static ssize_t smk_write_load_self2(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules,
&tsp->smk_rules_lock, SMK_LONG_FMT);
@@ -2681,14 +2655,14 @@
static void *relabel_self_seq_start(struct seq_file *s, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_start(s, pos, &tsp->smk_relabel);
}
static void *relabel_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
return smk_seq_next(s, v, pos, &tsp->smk_relabel);
}
@@ -2736,7 +2710,7 @@
static ssize_t smk_write_relabel_self(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct task_smack *tsp = current_security();
+ struct task_smack *tsp = smack_cred(current_cred());
char *data;
int rc;
LIST_HEAD(list_tmp);
@@ -2843,17 +2817,15 @@
/**
* smk_fill_super - fill the smackfs superblock
* @sb: the empty superblock
- * @data: unused
- * @silent: unused
+ * @fc: unused
*
* Fill in the well known entries for the smack filesystem
*
* Returns 0 on success, an error code on failure
*/
-static int smk_fill_super(struct super_block *sb, void *data, int silent)
+static int smk_fill_super(struct super_block *sb, struct fs_context *fc)
{
int rc;
- struct inode *root_inode;
static const struct tree_descr smack_files[] = {
[SMK_LOAD] = {
@@ -2917,31 +2889,39 @@
return rc;
}
- root_inode = d_inode(sb->s_root);
-
return 0;
}
/**
- * smk_mount - get the smackfs superblock
- * @fs_type: passed along without comment
- * @flags: passed along without comment
- * @dev_name: passed along without comment
- * @data: passed along without comment
+ * smk_get_tree - get the smackfs superblock
+ * @fc: The mount context, including any options
*
* Just passes everything along.
*
* Returns what the lower level code does.
*/
-static struct dentry *smk_mount(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data)
+static int smk_get_tree(struct fs_context *fc)
{
- return mount_single(fs_type, flags, data, smk_fill_super);
+ return get_tree_single(fc, smk_fill_super);
+}
+
+static const struct fs_context_operations smk_context_ops = {
+ .get_tree = smk_get_tree,
+};
+
+/**
+ * smk_init_fs_context - Initialise a filesystem context for smackfs
+ * @fc: The blank mount context
+ */
+static int smk_init_fs_context(struct fs_context *fc)
+{
+ fc->ops = &smk_context_ops;
+ return 0;
}
static struct file_system_type smk_fs_type = {
.name = "smackfs",
- .mount = smk_mount,
+ .init_fs_context = smk_init_fs_context,
.kill_sb = kill_litter_super,
};
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig
index 404dce6..9221ea5 100644
--- a/security/tomoyo/Kconfig
+++ b/security/tomoyo/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SECURITY_TOMOYO
bool "TOMOYO Linux Support"
depends on SECURITY
@@ -74,3 +75,13 @@
You can override this setting via TOMOYO_trigger= kernel command line
option. For example, if you pass init=/bin/systemd option, you may
want to also pass TOMOYO_trigger=/bin/systemd option.
+
+config SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING
+ bool "Use insecure built-in settings for fuzzing tests."
+ default n
+ depends on SECURITY_TOMOYO
+ select SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+ help
+ Enabling this option forces minimal built-in policy and disables
+ domain/program checks for run-time policy modifications. Please enable
+ this option only if this kernel is built for doing fuzzing tests.
diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c
index 479b03a..3c96e84 100644
--- a/security/tomoyo/audit.c
+++ b/security/tomoyo/audit.c
@@ -32,6 +32,7 @@
int argv_count = bprm->argc;
int envp_count = bprm->envc;
bool truncated = false;
+
if (!buffer)
return NULL;
len = snprintf(buffer, tomoyo_buffer_len - 1, "argv[]={ ");
@@ -49,6 +50,7 @@
while (offset < PAGE_SIZE) {
const char *kaddr = dump->data;
const unsigned char c = kaddr[offset++];
+
if (cp == last_start)
*cp++ = '"';
if (cp >= buffer + tomoyo_buffer_len - 32) {
@@ -154,19 +156,18 @@
char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
int pos;
u8 i;
+
if (!buffer)
return NULL;
tomoyo_convert_time(ktime_get_real_seconds(), &stamp);
pos = snprintf(buffer, tomoyo_buffer_len - 1,
- "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s "
- "granted=%s (global-pid=%u) task={ pid=%u ppid=%u "
- "uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u "
- "fsuid=%u fsgid=%u }", stamp.year, stamp.month,
- stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile,
- tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid,
- tomoyo_sys_getpid(), tomoyo_sys_getppid(),
+ "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s granted=%s (global-pid=%u) task={ pid=%u ppid=%u uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }",
+ stamp.year, stamp.month, stamp.day, stamp.hour,
+ stamp.min, stamp.sec, r->profile, tomoyo_mode[r->mode],
+ tomoyo_yesno(r->granted), gpid, tomoyo_sys_getpid(),
+ tomoyo_sys_getppid(),
from_kuid(&init_user_ns, current_uid()),
from_kgid(&init_user_ns, current_gid()),
from_kuid(&init_user_ns, current_euid()),
@@ -185,6 +186,7 @@
struct tomoyo_mini_stat *stat;
unsigned int dev;
umode_t mode;
+
if (!obj->stat_valid[i])
continue;
stat = &obj->stat[i];
@@ -193,8 +195,8 @@
if (i & 1) {
pos += snprintf(buffer + pos,
tomoyo_buffer_len - 1 - pos,
- " path%u.parent={ uid=%u gid=%u "
- "ino=%lu perm=0%o }", (i >> 1) + 1,
+ " path%u.parent={ uid=%u gid=%u ino=%lu perm=0%o }",
+ (i >> 1) + 1,
from_kuid(&init_user_ns, stat->uid),
from_kgid(&init_user_ns, stat->gid),
(unsigned long)stat->ino,
@@ -202,8 +204,8 @@
continue;
}
pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
- " path%u={ uid=%u gid=%u ino=%lu major=%u"
- " minor=%u perm=0%o type=%s", (i >> 1) + 1,
+ " path%u={ uid=%u gid=%u ino=%lu major=%u minor=%u perm=0%o type=%s",
+ (i >> 1) + 1,
from_kuid(&init_user_ns, stat->uid),
from_kgid(&init_user_ns, stat->gid),
(unsigned long)stat->ino,
@@ -249,6 +251,7 @@
const char *symlink = NULL;
int pos;
const char *domainname = r->domain->domainname->name;
+
header = tomoyo_print_header(r);
if (!header)
return NULL;
@@ -256,6 +259,7 @@
len += strlen(domainname) + strlen(header) + 10;
if (r->ee) {
struct file *file = r->ee->bprm->file;
+
realpath = tomoyo_realpath_from_path(&file->f_path);
bprm_info = tomoyo_print_bprm(r->ee->bprm, &r->ee->dump);
if (!realpath || !bprm_info)
@@ -275,6 +279,7 @@
pos = snprintf(buf, len, "%s", header);
if (realpath) {
struct linux_binprm *bprm = r->ee->bprm;
+
pos += snprintf(buf + pos, len - pos,
" exec={ realpath=\"%s\" argc=%d envc=%d %s }",
realpath, bprm->argc, bprm->envc, bprm_info);
@@ -328,6 +333,7 @@
const u8 category = tomoyo_index2category[index] +
TOMOYO_MAX_MAC_INDEX;
struct tomoyo_profile *p;
+
if (!tomoyo_policy_loaded)
return false;
p = tomoyo_profile(ns, profile);
@@ -362,6 +368,7 @@
char *buf;
struct tomoyo_log *entry;
bool quota_exceeded = false;
+
if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type,
r->matched_acl, r->granted))
goto out;
@@ -413,6 +420,7 @@
{
va_list args;
int len;
+
va_start(args, fmt);
len = vsnprintf((char *) &len, 1, fmt, args) + 1;
va_end(args);
@@ -431,6 +439,7 @@
void tomoyo_read_log(struct tomoyo_io_buffer *head)
{
struct tomoyo_log *ptr = NULL;
+
if (head->r.w_pos)
return;
kfree(head->read_buf);
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index 03923a1..dd3d594 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -197,6 +197,7 @@
{
va_list args;
const int pos = strlen(buffer);
+
va_start(args, fmt);
vsnprintf(buffer + pos, len - pos - 1, fmt, args);
va_end(args);
@@ -214,6 +215,7 @@
while (head->r.w_pos) {
const char *w = head->r.w[0];
size_t len = strlen(w);
+
if (len) {
if (len > head->read_user_buf_avail)
len = head->read_user_buf_avail;
@@ -279,6 +281,7 @@
size_t len;
size_t pos = head->r.avail;
int size = head->readbuf_size - pos;
+
if (size <= 0)
return;
va_start(args, fmt);
@@ -344,13 +347,14 @@
void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
{
unsigned int idx;
+
for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++)
INIT_LIST_HEAD(&ns->acl_group[idx]);
for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
INIT_LIST_HEAD(&ns->group_list[idx]);
for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
INIT_LIST_HEAD(&ns->policy_list[idx]);
- ns->profile_version = 20110903;
+ ns->profile_version = 20150505;
tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
}
@@ -433,6 +437,7 @@
u8 min_type = ptr->value_type[0];
const u8 max_type = ptr->value_type[1];
char buffer[128];
+
buffer[0] = '\0';
for (i = 0; i < 2; i++) {
switch (min_type) {
@@ -487,6 +492,7 @@
{
struct tomoyo_profile *ptr;
struct tomoyo_profile *entry;
+
if (profile >= TOMOYO_MAX_PROFILES)
return NULL;
ptr = ns->profile_ptr[profile];
@@ -530,6 +536,7 @@
{
static struct tomoyo_profile tomoyo_null_profile;
struct tomoyo_profile *ptr = ns->profile_ptr[profile];
+
if (!ptr)
ptr = &tomoyo_null_profile;
return ptr;
@@ -546,6 +553,7 @@
static s8 tomoyo_find_yesno(const char *string, const char *find)
{
const char *cp = strstr(string, find);
+
if (cp) {
cp += strlen(find);
if (!strncmp(cp, "=yes", 4))
@@ -569,6 +577,7 @@
const char *find)
{
const char *cp = strstr(string, find);
+
if (cp)
sscanf(cp + strlen(find), "=%u", i);
}
@@ -587,6 +596,7 @@
{
u8 i;
u8 config;
+
if (!strcmp(name, "CONFIG")) {
i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX;
config = profile->default_config;
@@ -595,10 +605,12 @@
for (i = 0; i < TOMOYO_MAX_MAC_INDEX
+ TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
int len = 0;
+
if (i < TOMOYO_MAX_MAC_INDEX) {
const u8 c = tomoyo_index2category[i];
const char *category =
tomoyo_category_keywords[c];
+
len = strlen(category);
if (strncmp(name, category, len) ||
name[len++] != ':' || name[len++] != ':')
@@ -618,6 +630,7 @@
config = TOMOYO_CONFIG_USE_DEFAULT;
} else {
u8 mode;
+
for (mode = 0; mode < 4; mode++)
if (strstr(value, tomoyo_mode[mode]))
/*
@@ -664,6 +677,7 @@
unsigned int i;
char *cp;
struct tomoyo_profile *profile;
+
if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version)
== 1)
return 0;
@@ -683,6 +697,7 @@
const struct tomoyo_path_info *new_comment
= tomoyo_get_name(cp);
const struct tomoyo_path_info *old_comment;
+
if (!new_comment)
return -ENOMEM;
spin_lock(&lock);
@@ -732,6 +747,7 @@
struct tomoyo_policy_namespace *ns =
container_of(head->r.ns, typeof(*ns), namespace_list);
const struct tomoyo_profile *profile;
+
if (head->r.eof)
return;
next:
@@ -760,6 +776,7 @@
u8 i;
const struct tomoyo_path_info *comment =
profile->comment;
+
tomoyo_print_namespace(head);
tomoyo_io_printf(head, "%u-COMMENT=", index);
tomoyo_set_string(head, comment ? comment->name : "");
@@ -788,6 +805,7 @@
+ TOMOYO_MAX_MAC_CATEGORY_INDEX; head->r.bit++) {
const u8 i = head->r.bit;
const u8 config = profile->config[i];
+
if (config == TOMOYO_CONFIG_USE_DEFAULT)
continue;
tomoyo_print_namespace(head);
@@ -847,10 +865,10 @@
struct tomoyo_acl_param param = {
/* .ns = &tomoyo_kernel_namespace, */
.is_delete = is_delete,
- .list = &tomoyo_kernel_namespace.
- policy_list[TOMOYO_ID_MANAGER],
+ .list = &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER],
};
int error = is_delete ? -ENOENT : -ENOMEM;
+
if (!tomoyo_correct_domain(manager) &&
!tomoyo_correct_word(manager))
return -EINVAL;
@@ -894,10 +912,10 @@
{
if (head->r.eof)
return;
- list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace.
- policy_list[TOMOYO_ID_MANAGER]) {
+ list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER]) {
struct tomoyo_manager *ptr =
list_entry(head->r.acl, typeof(*ptr), head.list);
+
if (ptr->head.is_deleted)
continue;
if (!tomoyo_flush(head))
@@ -922,7 +940,7 @@
const char *exe;
const struct task_struct *task = current;
const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname;
- bool found = false;
+ bool found = IS_ENABLED(CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING);
if (!tomoyo_policy_loaded)
return true;
@@ -933,8 +951,7 @@
exe = tomoyo_get_exe();
if (!exe)
return false;
- list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
- policy_list[TOMOYO_ID_MANAGER], head.list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER], head.list) {
if (!ptr->head.is_deleted &&
(!tomoyo_pathcmp(domainname, ptr->manager) ||
!strcmp(exe, ptr->manager->name))) {
@@ -945,9 +962,10 @@
if (!found) { /* Reduce error messages. */
static pid_t last_pid;
const pid_t pid = current->pid;
+
if (last_pid != pid) {
- printk(KERN_WARNING "%s ( %s ) is not permitted to "
- "update policies.\n", domainname->name, exe);
+ pr_warn("%s ( %s ) is not permitted to update policies.\n",
+ domainname->name, exe);
last_pid = pid;
}
}
@@ -974,19 +992,21 @@
unsigned int pid;
struct tomoyo_domain_info *domain = NULL;
bool global_pid = false;
+
if (strncmp(data, "select ", 7))
return false;
data += 7;
if (sscanf(data, "pid=%u", &pid) == 1 ||
(global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
struct task_struct *p;
+
rcu_read_lock();
if (global_pid)
p = find_task_by_pid_ns(pid, &init_pid_ns);
else
p = find_task_by_vpid(pid);
if (p)
- domain = tomoyo_real_domain(p);
+ domain = tomoyo_task(p)->domain_info;
rcu_read_unlock();
} else if (!strncmp(data, "domain=", 7)) {
if (tomoyo_domain_def(data + 7))
@@ -1020,10 +1040,11 @@
* Returns true if @a == @b, false otherwise.
*/
static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a,
- const struct tomoyo_acl_info *b)
+ const struct tomoyo_acl_info *b)
{
const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head);
+
return p1->domainname == p2->domainname;
}
@@ -1039,11 +1060,13 @@
static int tomoyo_write_task(struct tomoyo_acl_param *param)
{
int error = -EINVAL;
+
if (tomoyo_str_starts(¶m->data, "manual_domain_transition ")) {
struct tomoyo_task_acl e = {
.head.type = TOMOYO_TYPE_MANUAL_TASK_ACL,
.domainname = tomoyo_get_domainname(param),
};
+
if (e.domainname)
error = tomoyo_update_domain(&e.head, sizeof(e), param,
tomoyo_same_task_acl,
@@ -1110,7 +1133,7 @@
};
static const struct {
const char *keyword;
- int (*write) (struct tomoyo_acl_param *);
+ int (*write)(struct tomoyo_acl_param *param);
} tomoyo_callback[5] = {
{ "file ", tomoyo_write_file },
{ "network inet ", tomoyo_write_inet_network },
@@ -1151,9 +1174,11 @@
struct tomoyo_domain_info *domain = head->w.domain;
const bool is_delete = head->w.is_delete;
bool is_select = !is_delete && tomoyo_str_starts(&data, "select ");
- unsigned int profile;
+ unsigned int idx;
+
if (*data == '<') {
int ret = 0;
+
domain = NULL;
if (is_delete)
ret = tomoyo_delete_domain(data);
@@ -1167,23 +1192,27 @@
if (!domain)
return -EINVAL;
ns = domain->ns;
- if (sscanf(data, "use_profile %u", &profile) == 1
- && profile < TOMOYO_MAX_PROFILES) {
- if (!tomoyo_policy_loaded || ns->profile_ptr[profile])
- domain->profile = (u8) profile;
+ if (sscanf(data, "use_profile %u", &idx) == 1
+ && idx < TOMOYO_MAX_PROFILES) {
+ if (!tomoyo_policy_loaded || ns->profile_ptr[idx])
+ if (!is_delete)
+ domain->profile = (u8) idx;
return 0;
}
- if (sscanf(data, "use_group %u\n", &profile) == 1
- && profile < TOMOYO_MAX_ACL_GROUPS) {
+ if (sscanf(data, "use_group %u\n", &idx) == 1
+ && idx < TOMOYO_MAX_ACL_GROUPS) {
if (!is_delete)
- domain->group = (u8) profile;
+ set_bit(idx, domain->group);
+ else
+ clear_bit(idx, domain->group);
return 0;
}
- for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) {
- const char *cp = tomoyo_dif[profile];
+ for (idx = 0; idx < TOMOYO_MAX_DOMAIN_INFO_FLAGS; idx++) {
+ const char *cp = tomoyo_dif[idx];
+
if (strncmp(data, cp, strlen(cp) - 1))
continue;
- domain->flags[profile] = !is_delete;
+ domain->flags[idx] = !is_delete;
return 0;
}
return tomoyo_write_domain2(ns, &domain->acl_info_list, data,
@@ -1225,9 +1254,11 @@
const struct tomoyo_envp *envp =
(typeof(envp)) (argv + cond->argc);
u16 skip;
+
for (skip = 0; skip < head->r.cond_index; skip++) {
const u8 left = condp->left;
const u8 right = condp->right;
+
condp++;
switch (left) {
case TOMOYO_ARGV_ENTRY:
@@ -1253,6 +1284,7 @@
const u8 match = condp->equals;
const u8 left = condp->left;
const u8 right = condp->right;
+
if (!tomoyo_flush(head))
return false;
condp++;
@@ -1262,8 +1294,7 @@
case TOMOYO_ARGV_ENTRY:
tomoyo_io_printf(head,
"exec.argv[%lu]%s=\"",
- argv->index, argv->
- is_not ? "!" : "");
+ argv->index, argv->is_not ? "!" : "");
tomoyo_set_string(head,
argv->value->name);
tomoyo_set_string(head, "\"");
@@ -1274,12 +1305,10 @@
"exec.envp[\"");
tomoyo_set_string(head,
envp->name->name);
- tomoyo_io_printf(head, "\"]%s=", envp->
- is_not ? "!" : "");
+ tomoyo_io_printf(head, "\"]%s=", envp->is_not ? "!" : "");
if (envp->value) {
tomoyo_set_string(head, "\"");
- tomoyo_set_string(head, envp->
- value->name);
+ tomoyo_set_string(head, envp->value->name);
tomoyo_set_string(head, "\"");
} else {
tomoyo_set_string(head,
@@ -1375,6 +1404,7 @@
struct tomoyo_path_acl *ptr =
container_of(acl, typeof(*ptr), head);
const u16 perm = ptr->perm;
+
for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
if (!(perm & (1 << bit)))
continue;
@@ -1395,6 +1425,7 @@
} else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) {
struct tomoyo_task_acl *ptr =
container_of(acl, typeof(*ptr), head);
+
tomoyo_set_group(head, "task ");
tomoyo_set_string(head, "manual_domain_transition ");
tomoyo_set_string(head, ptr->domainname->name);
@@ -1404,6 +1435,7 @@
struct tomoyo_path2_acl *ptr =
container_of(acl, typeof(*ptr), head);
const u8 perm = ptr->perm;
+
for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
if (!(perm & (1 << bit)))
continue;
@@ -1424,6 +1456,7 @@
struct tomoyo_path_number_acl *ptr =
container_of(acl, typeof(*ptr), head);
const u8 perm = ptr->perm;
+
for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) {
if (!(perm & (1 << bit)))
continue;
@@ -1444,6 +1477,7 @@
struct tomoyo_mkdev_acl *ptr =
container_of(acl, typeof(*ptr), head);
const u8 perm = ptr->perm;
+
for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) {
if (!(perm & (1 << bit)))
continue;
@@ -1490,6 +1524,7 @@
->name);
} else {
char buf[128];
+
tomoyo_print_ip(buf, sizeof(buf), &ptr->address);
tomoyo_io_printf(head, "%s", buf);
}
@@ -1519,6 +1554,7 @@
} else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
struct tomoyo_mount_acl *ptr =
container_of(acl, typeof(*ptr), head);
+
tomoyo_set_group(head, "file mount");
tomoyo_print_name_union(head, &ptr->dev_name);
tomoyo_print_name_union(head, &ptr->dir_name);
@@ -1562,6 +1598,7 @@
list_for_each_cookie(head->r.acl, list) {
struct tomoyo_acl_info *ptr =
list_entry(head->r.acl, typeof(*ptr), list);
+
if (!tomoyo_print_entry(head, ptr))
return false;
}
@@ -1583,8 +1620,9 @@
list_for_each_cookie(head->r.domain, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain =
list_entry(head->r.domain, typeof(*domain), list);
+ u8 i;
+
switch (head->r.step) {
- u8 i;
case 0:
if (domain->is_deleted &&
!head->r.print_this_domain_only)
@@ -1594,22 +1632,33 @@
tomoyo_set_lf(head);
tomoyo_io_printf(head, "use_profile %u\n",
domain->profile);
- tomoyo_io_printf(head, "use_group %u\n",
- domain->group);
for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++)
if (domain->flags[i])
tomoyo_set_string(head, tomoyo_dif[i]);
+ head->r.index = 0;
+ head->r.step++;
+ /* fall through */
+ case 1:
+ while (head->r.index < TOMOYO_MAX_ACL_GROUPS) {
+ i = head->r.index++;
+ if (!test_bit(i, domain->group))
+ continue;
+ tomoyo_io_printf(head, "use_group %u\n", i);
+ if (!tomoyo_flush(head))
+ return;
+ }
+ head->r.index = 0;
head->r.step++;
tomoyo_set_lf(head);
/* fall through */
- case 1:
+ case 2:
if (!tomoyo_read_domain2(head, &domain->acl_info_list))
return;
head->r.step++;
if (!tomoyo_set_lf(head))
return;
/* fall through */
- case 2:
+ case 3:
head->r.step = 0;
if (head->r.print_this_domain_only)
goto done;
@@ -1660,14 +1709,15 @@
head->r.eof = true;
if (tomoyo_str_starts(&buf, "global-pid "))
global_pid = true;
- pid = (unsigned int) simple_strtoul(buf, NULL, 10);
+ if (kstrtouint(buf, 10, &pid))
+ return;
rcu_read_lock();
if (global_pid)
p = find_task_by_pid_ns(pid, &init_pid_ns);
else
p = find_task_by_vpid(pid);
if (p)
- domain = tomoyo_real_domain(p);
+ domain = tomoyo_task(p)->domain_info;
rcu_read_unlock();
if (!domain)
return;
@@ -1710,6 +1760,7 @@
.data = head->write_buf,
};
u8 i;
+
if (tomoyo_str_starts(¶m.data, "aggregator "))
return tomoyo_write_aggregator(¶m);
for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++)
@@ -1721,6 +1772,7 @@
if (tomoyo_str_starts(¶m.data, "acl_group ")) {
unsigned int group;
char *data;
+
group = simple_strtoul(param.data, &data, 10);
if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ')
return tomoyo_write_domain2
@@ -1745,12 +1797,15 @@
struct tomoyo_policy_namespace *ns =
container_of(head->r.ns, typeof(*ns), namespace_list);
struct list_head *list = &ns->group_list[idx];
+
list_for_each_cookie(head->r.group, list) {
struct tomoyo_group *group =
list_entry(head->r.group, typeof(*group), head.list);
+
list_for_each_cookie(head->r.acl, &group->member_list) {
struct tomoyo_acl_head *ptr =
list_entry(head->r.acl, typeof(*ptr), list);
+
if (ptr->is_deleted)
continue;
if (!tomoyo_flush(head))
@@ -1770,10 +1825,10 @@
head)->number);
} else if (idx == TOMOYO_ADDRESS_GROUP) {
char buffer[128];
-
struct tomoyo_address_group *member =
container_of(ptr, typeof(*member),
head);
+
tomoyo_print_ip(buffer, sizeof(buffer),
&member->address);
tomoyo_io_printf(head, " %s", buffer);
@@ -1801,6 +1856,7 @@
struct tomoyo_policy_namespace *ns =
container_of(head->r.ns, typeof(*ns), namespace_list);
struct list_head *list = &ns->policy_list[idx];
+
list_for_each_cookie(head->r.acl, list) {
struct tomoyo_acl_head *acl =
container_of(head->r.acl, typeof(*acl), list);
@@ -1813,6 +1869,7 @@
{
struct tomoyo_transition_control *ptr =
container_of(acl, typeof(*ptr), head);
+
tomoyo_print_namespace(head);
tomoyo_set_string(head, tomoyo_transition_type
[ptr->type]);
@@ -1828,6 +1885,7 @@
{
struct tomoyo_aggregator *ptr =
container_of(acl, typeof(*ptr), head);
+
tomoyo_print_namespace(head);
tomoyo_set_string(head, "aggregator ");
tomoyo_set_string(head,
@@ -1857,6 +1915,7 @@
{
struct tomoyo_policy_namespace *ns =
container_of(head->r.ns, typeof(*ns), namespace_list);
+
if (head->r.eof)
return;
while (head->r.step < TOMOYO_MAX_POLICY &&
@@ -1920,6 +1979,7 @@
static int tomoyo_truncate(char *str)
{
char *start = str;
+
while (*(unsigned char *) str > (unsigned char) ' ')
str++;
*str = '\0';
@@ -1942,6 +2002,7 @@
char *symlink = NULL;
char *cp = strchr(header, '\n');
int len;
+
if (!cp)
return;
cp = strchr(cp + 1, '\n');
@@ -2001,6 +2062,7 @@
static unsigned int tomoyo_serial;
struct tomoyo_query entry = { };
bool quota_exceeded = false;
+
va_start(args, fmt);
len = vsnprintf((char *) &len, 1, fmt, args) + 1;
va_end(args);
@@ -2062,8 +2124,7 @@
(tomoyo_answer_wait, entry.answer ||
!atomic_read(&tomoyo_query_observers), HZ))
break;
- else
- entry.timer++;
+ entry.timer++;
}
spin_lock(&tomoyo_query_list_lock);
list_del(&entry.list);
@@ -2099,6 +2160,7 @@
{
struct tomoyo_query *ptr;
struct tomoyo_domain_info *domain = NULL;
+
spin_lock(&tomoyo_query_list_lock);
list_for_each_entry(ptr, &tomoyo_query_list, list) {
if (ptr->serial != serial)
@@ -2141,15 +2203,15 @@
unsigned int pos = 0;
size_t len = 0;
char *buf;
+
if (head->r.w_pos)
return;
- if (head->read_buf) {
- kfree(head->read_buf);
- head->read_buf = NULL;
- }
+ kfree(head->read_buf);
+ head->read_buf = NULL;
spin_lock(&tomoyo_query_list_lock);
list_for_each(tmp, &tomoyo_query_list) {
struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
+
if (pos++ != head->r.query_index)
continue;
len = ptr->query_len;
@@ -2167,6 +2229,7 @@
spin_lock(&tomoyo_query_list_lock);
list_for_each(tmp, &tomoyo_query_list) {
struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
+
if (pos++ != head->r.query_index)
continue;
/*
@@ -2201,9 +2264,11 @@
struct list_head *tmp;
unsigned int serial;
unsigned int answer;
+
spin_lock(&tomoyo_query_list_lock);
list_for_each(tmp, &tomoyo_query_list) {
struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
+
ptr->timer = 0;
}
spin_unlock(&tomoyo_query_list_lock);
@@ -2212,6 +2277,7 @@
spin_lock(&tomoyo_query_list_lock);
list_for_each(tmp, &tomoyo_query_list) {
struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list);
+
if (ptr->serial != serial)
continue;
ptr->answer = answer;
@@ -2234,7 +2300,7 @@
static void tomoyo_read_version(struct tomoyo_io_buffer *head)
{
if (!head->r.eof) {
- tomoyo_io_printf(head, "2.5.0");
+ tomoyo_io_printf(head, "2.6.0");
head->r.eof = true;
}
}
@@ -2286,6 +2352,7 @@
{
u8 i;
unsigned int total = 0;
+
if (head->r.eof)
return;
for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) {
@@ -2294,9 +2361,9 @@
tomoyo_stat_updated[i]);
if (tomoyo_stat_modified[i]) {
struct tomoyo_time stamp;
+
tomoyo_convert_time(tomoyo_stat_modified[i], &stamp);
- tomoyo_io_printf(head, " (Last: %04u/%02u/%02u "
- "%02u:%02u:%02u)",
+ tomoyo_io_printf(head, " (Last: %04u/%02u/%02u %02u:%02u:%02u)",
stamp.year, stamp.month, stamp.day,
stamp.hour, stamp.min, stamp.sec);
}
@@ -2304,6 +2371,7 @@
}
for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) {
unsigned int used = tomoyo_memory_used[i];
+
total += used;
tomoyo_io_printf(head, "Memory used by %-22s %10u",
tomoyo_memory_headers[i], used);
@@ -2328,6 +2396,7 @@
{
char *data = head->write_buf;
u8 i;
+
if (tomoyo_str_starts(&data, "Memory used by "))
for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++)
if (tomoyo_str_starts(&data, tomoyo_memory_headers[i]))
@@ -2456,6 +2525,7 @@
__poll_t tomoyo_poll_control(struct file *file, poll_table *wait)
{
struct tomoyo_io_buffer *head = file->private_data;
+
if (head->poll)
return head->poll(file, wait) | EPOLLOUT | EPOLLWRNORM;
return EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM;
@@ -2471,6 +2541,7 @@
static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head)
{
struct list_head *ns;
+
if (head->type != TOMOYO_EXCEPTIONPOLICY &&
head->type != TOMOYO_PROFILE)
return;
@@ -2516,7 +2587,7 @@
int idx;
if (!head->read)
- return -ENOSYS;
+ return -EINVAL;
if (mutex_lock_interruptible(&head->io_sem))
return -EINTR;
head->read_user_buf = buffer;
@@ -2556,6 +2627,7 @@
head->type == TOMOYO_PROFILE) {
if (*line == '<') {
char *cp = strchr(line, ' ');
+
if (cp) {
*cp++ = '\0';
head->w.ns = tomoyo_assign_namespace(line);
@@ -2588,9 +2660,10 @@
size_t avail_len = buffer_len;
char *cp0 = head->write_buf;
int idx;
+
if (!head->write)
- return -ENOSYS;
- if (!access_ok(VERIFY_READ, buffer, buffer_len))
+ return -EINVAL;
+ if (!access_ok(buffer, buffer_len))
return -EFAULT;
if (mutex_lock_interruptible(&head->io_sem))
return -EINTR;
@@ -2599,9 +2672,11 @@
/* Read a line and dispatch it to the policy handler. */
while (avail_len > 0) {
char c;
+
if (head->w.avail >= head->writebuf_size - 1) {
const int len = head->writebuf_size * 2;
char *cp = kzalloc(len, GFP_NOFS);
+
if (!cp) {
error = -ENOMEM;
break;
@@ -2700,30 +2775,32 @@
{
struct tomoyo_domain_info *domain;
const int idx = tomoyo_read_lock();
+
tomoyo_policy_loaded = true;
- printk(KERN_INFO "TOMOYO: 2.5.0\n");
+ pr_info("TOMOYO: 2.6.0\n");
list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
const u8 profile = domain->profile;
- const struct tomoyo_policy_namespace *ns = domain->ns;
- if (ns->profile_version != 20110903)
- printk(KERN_ERR
- "Profile version %u is not supported.\n",
+ struct tomoyo_policy_namespace *ns = domain->ns;
+
+ if (ns->profile_version == 20110903) {
+ pr_info_once("Converting profile version from %u to %u.\n",
+ 20110903, 20150505);
+ ns->profile_version = 20150505;
+ }
+ if (ns->profile_version != 20150505)
+ pr_err("Profile version %u is not supported.\n",
ns->profile_version);
else if (!ns->profile_ptr[profile])
- printk(KERN_ERR
- "Profile %u (used by '%s') is not defined.\n",
+ pr_err("Profile %u (used by '%s') is not defined.\n",
profile, domain->domainname->name);
else
continue;
- printk(KERN_ERR
- "Userland tools for TOMOYO 2.5 must be installed and "
- "policy must be initialized.\n");
- printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ "
- "for more information.\n");
+ 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");
panic("STOP!");
}
tomoyo_read_unlock(idx);
- printk(KERN_INFO "Mandatory Access Control activated.\n");
+ pr_info("Mandatory Access Control activated.\n");
}
/**
@@ -2733,6 +2810,16 @@
*/
void __init tomoyo_load_builtin_policy(void)
{
+#ifdef CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING
+ static char tomoyo_builtin_profile[] __initdata =
+ "PROFILE_VERSION=20150505\n"
+ "0-CONFIG={ mode=learning grant_log=no reject_log=yes }\n";
+ static char tomoyo_builtin_exception_policy[] __initdata =
+ "aggregator proc:/self/exe /proc/self/exe\n";
+ static char tomoyo_builtin_domain_policy[] __initdata = "";
+ static char tomoyo_builtin_manager[] __initdata = "";
+ static char tomoyo_builtin_stat[] __initdata = "";
+#else
/*
* This include file is manually created and contains built-in policy
* named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy",
@@ -2740,11 +2827,14 @@
* "tomoyo_builtin_stat" in the form of "static char [] __initdata".
*/
#include "builtin-policy.h"
+#endif
u8 i;
const int idx = tomoyo_read_lock();
+
for (i = 0; i < 5; i++) {
struct tomoyo_io_buffer head = { };
char *start = "";
+
switch (i) {
case 0:
start = tomoyo_builtin_profile;
@@ -2774,6 +2864,7 @@
}
while (1) {
char *end = strchr(start, '\n');
+
if (!end)
break;
*end = '\0';
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 539bcdd..050473d 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -10,6 +10,8 @@
#ifndef _SECURITY_TOMOYO_COMMON_H
#define _SECURITY_TOMOYO_COMMON_H
+#define pr_fmt(fmt) fmt
+
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/mm.h>
@@ -29,6 +31,7 @@
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/un.h>
+#include <linux/lsm_hooks.h>
#include <net/sock.h>
#include <net/af_unix.h>
#include <net/ip.h>
@@ -681,11 +684,12 @@
const struct tomoyo_path_info *domainname;
/* Namespace for this domain. Never NULL. */
struct tomoyo_policy_namespace *ns;
+ /* Group numbers to use. */
+ unsigned long group[TOMOYO_MAX_ACL_GROUPS / BITS_PER_LONG];
u8 profile; /* Profile number to use. */
- u8 group; /* Group number to use. */
bool is_deleted; /* Delete flag. */
bool flags[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
- atomic_t users; /* Number of referring credentials. */
+ atomic_t users; /* Number of referring tasks. */
};
/*
@@ -787,9 +791,9 @@
* interfaces.
*/
struct tomoyo_io_buffer {
- void (*read) (struct tomoyo_io_buffer *);
- int (*write) (struct tomoyo_io_buffer *);
- __poll_t (*poll) (struct file *file, poll_table *wait);
+ void (*read)(struct tomoyo_io_buffer *head);
+ int (*write)(struct tomoyo_io_buffer *head);
+ __poll_t (*poll)(struct file *file, poll_table *wait);
/* Exclusive lock for this structure. */
struct mutex io_sem;
char __user *read_user_buf;
@@ -906,12 +910,18 @@
struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS];
/* List for connecting to tomoyo_namespace_list list. */
struct list_head namespace_list;
- /* Profile version. Currently only 20110903 is defined. */
+ /* Profile version. Currently only 20150505 is defined. */
unsigned int profile_version;
/* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
const char *name;
};
+/* Structure for "struct task_struct"->security. */
+struct tomoyo_task {
+ struct tomoyo_domain_info *domain_info;
+ struct tomoyo_domain_info *old_domain_info;
+};
+
/********** Function prototypes. **********/
bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address,
@@ -1020,6 +1030,7 @@
struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param);
struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
const bool transit);
+struct tomoyo_domain_info *tomoyo_domain(void);
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
const u8 idx);
@@ -1034,8 +1045,8 @@
void __init tomoyo_load_builtin_policy(void);
void __init tomoyo_mm_init(void);
void tomoyo_check_acl(struct tomoyo_request_info *r,
- bool (*check_entry) (struct tomoyo_request_info *,
- const struct tomoyo_acl_info *));
+ bool (*check_entry)(struct tomoyo_request_info *,
+ const struct tomoyo_acl_info *));
void tomoyo_check_profile(void);
void tomoyo_convert_time(time64_t time, struct tomoyo_time *stamp);
void tomoyo_del_condition(struct list_head *element);
@@ -1062,6 +1073,7 @@
/********** External variable definitions. **********/
extern bool tomoyo_policy_loaded;
+extern int tomoyo_enabled;
extern const char * const tomoyo_condition_keyword
[TOMOYO_MAX_CONDITION_KEYWORD];
extern const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS];
@@ -1085,6 +1097,7 @@
extern struct tomoyo_policy_namespace tomoyo_kernel_namespace;
extern unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
extern unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
+extern struct lsm_blob_sizes tomoyo_blob_sizes;
/********** Inlined functions. **********/
@@ -1121,6 +1134,7 @@
static inline pid_t tomoyo_sys_getppid(void)
{
pid_t pid;
+
rcu_read_lock();
pid = task_tgid_vnr(rcu_dereference(current->real_parent));
rcu_read_unlock();
@@ -1197,26 +1211,15 @@
}
/**
- * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
+ * tomoyo_task - Get "struct tomoyo_task" for specified thread.
*
- * Returns pointer to "struct tomoyo_domain_info" for current thread.
+ * @task - Pointer to "struct task_struct".
+ *
+ * Returns pointer to "struct tomoyo_task" for specified thread.
*/
-static inline struct tomoyo_domain_info *tomoyo_domain(void)
+static inline struct tomoyo_task *tomoyo_task(struct task_struct *task)
{
- return current_cred()->security;
-}
-
-/**
- * tomoyo_real_domain - Get "struct tomoyo_domain_info" for specified thread.
- *
- * @task: Pointer to "struct task_struct".
- *
- * Returns pointer to "struct tomoyo_security" for specified thread.
- */
-static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
- *task)
-{
- return task_cred_xxx(task, security);
+ return task->security + tomoyo_blob_sizes.lbs_task;
}
/**
diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c
index 8d0e1b9..8f6d57c 100644
--- a/security/tomoyo/condition.c
+++ b/security/tomoyo/condition.c
@@ -28,9 +28,11 @@
{
int i;
struct tomoyo_path_info arg;
+
arg.name = arg_ptr;
for (i = 0; i < argc; argv++, checked++, i++) {
bool result;
+
if (index != argv->index)
continue;
*checked = 1;
@@ -62,12 +64,14 @@
int i;
struct tomoyo_path_info name;
struct tomoyo_path_info value;
+
name.name = env_name;
tomoyo_fill_path_info(&name);
value.name = env_value;
tomoyo_fill_path_info(&value);
for (i = 0; i < envc; envp++, checked++, i++) {
bool result;
+
if (!tomoyo_path_matches_pattern(&name, envp->name))
continue;
*checked = 1;
@@ -113,6 +117,7 @@
bool result = true;
u8 local_checked[32];
u8 *checked;
+
if (argc + envc <= sizeof(local_checked)) {
checked = local_checked;
memset(local_checked, 0, sizeof(local_checked));
@@ -131,6 +136,7 @@
/* Read. */
const char *kaddr = dump->data;
const unsigned char c = kaddr[offset++];
+
if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
if (c == '\\') {
arg_ptr[arg_len++] = '\\';
@@ -160,6 +166,7 @@
argv_count--;
} else if (envp_count) {
char *cp = strchr(arg_ptr, '=');
+
if (cp) {
*cp = '\0';
if (!tomoyo_envp(arg_ptr, cp + 1,
@@ -182,6 +189,7 @@
out:
if (result) {
int i;
+
/* Check not-yet-checked entries. */
for (i = 0; i < argc; i++) {
if (checked[i])
@@ -229,6 +237,7 @@
{
bool result;
struct tomoyo_path_info exe;
+
if (!file)
return false;
exe.name = tomoyo_realpath_from_path(&file->f_path);
@@ -250,6 +259,7 @@
static const struct tomoyo_path_info *tomoyo_get_dqword(char *start)
{
char *cp = start + strlen(start) - 1;
+
if (cp == start || *start++ != '"' || *cp != '"')
return NULL;
*cp = '\0';
@@ -270,6 +280,7 @@
struct tomoyo_name_union *ptr)
{
char *filename = param->data;
+
if (*filename == '@')
return tomoyo_parse_name_union(param, ptr);
ptr->filename = tomoyo_get_dqword(filename);
@@ -310,6 +321,7 @@
const struct tomoyo_path_info *name;
const struct tomoyo_path_info *value;
char *cp = left + strlen(left) - 1;
+
if (*cp-- != ']' || *cp != '"')
goto out;
*cp = '\0';
@@ -364,6 +376,7 @@
static u8 tomoyo_condition_type(const char *word)
{
u8 i;
+
for (i = 0; i < TOMOYO_MAX_CONDITION_KEYWORD; i++) {
if (!strcmp(word, tomoyo_condition_keyword[i]))
break;
@@ -395,6 +408,7 @@
{
struct tomoyo_condition *ptr;
bool found = false;
+
if (mutex_lock_interruptible(&tomoyo_policy_lock)) {
dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__);
ptr = NULL;
@@ -442,12 +456,14 @@
{
char * const pos = param->data;
bool flag;
+
if (*pos == '<') {
e->transit = tomoyo_get_domainname(param);
goto done;
}
{
char *cp = strchr(pos, ' ');
+
if (cp)
*cp = '\0';
flag = tomoyo_correct_path(pos) || !strcmp(pos, "keep") ||
@@ -489,6 +505,7 @@
tomoyo_get_transit_preference(param, &e);
char * const end_of_string = start_of_string + strlen(start_of_string);
char *pos;
+
rerun:
pos = start_of_string;
while (1) {
@@ -498,6 +515,7 @@
char *cp;
char *right_word;
bool is_not;
+
if (!*left_word)
break;
/*
@@ -622,8 +640,8 @@
}
store_value:
if (!condp) {
- dprintk(KERN_WARNING "%u: dry_run left=%u right=%u "
- "match=%u\n", __LINE__, left, right, !is_not);
+ dprintk(KERN_WARNING "%u: dry_run left=%u right=%u match=%u\n",
+ __LINE__, left, right, !is_not);
continue;
}
condp->left = left;
@@ -660,6 +678,7 @@
envp = (struct tomoyo_envp *) (argv + e.argc);
{
bool flag = false;
+
for (pos = start_of_string; pos < end_of_string; pos++) {
if (*pos)
continue;
@@ -698,6 +717,7 @@
for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) {
struct inode *inode;
+
switch (i) {
case TOMOYO_PATH1:
dentry = obj->path1.dentry;
@@ -718,6 +738,7 @@
inode = d_backing_inode(dentry);
if (inode) {
struct tomoyo_mini_stat *stat = &obj->stat[i];
+
stat->uid = inode->i_uid;
stat->gid = inode->i_gid;
stat->ino = inode->i_ino;
@@ -726,8 +747,7 @@
stat->rdev = inode->i_rdev;
obj->stat_valid[i] = true;
}
- if (i & 1) /* i == TOMOYO_PATH1_PARENT ||
- i == TOMOYO_PATH2_PARENT */
+ if (i & 1) /* TOMOYO_PATH1_PARENT or TOMOYO_PATH2_PARENT */
dput(dentry);
}
}
@@ -758,6 +778,7 @@
u16 argc;
u16 envc;
struct linux_binprm *bprm = NULL;
+
if (!cond)
return true;
condc = cond->condc;
@@ -780,6 +801,7 @@
const u8 right = condp->right;
bool is_bitop[2] = { false, false };
u8 j;
+
condp++;
/* Check argv[] and envp[] later. */
if (left == TOMOYO_ARGV_ENTRY || left == TOMOYO_ENVP_ENTRY)
@@ -787,10 +809,11 @@
/* Check string expressions. */
if (right == TOMOYO_NAME_UNION) {
const struct tomoyo_name_union *ptr = names_p++;
+ struct tomoyo_path_info *symlink;
+ struct tomoyo_execve *ee;
+ struct file *file;
+
switch (left) {
- struct tomoyo_path_info *symlink;
- struct tomoyo_execve *ee;
- struct file *file;
case TOMOYO_SYMLINK_TARGET:
symlink = obj ? obj->symlink_target : NULL;
if (!symlink ||
@@ -812,6 +835,7 @@
for (j = 0; j < 2; j++) {
const u8 index = j ? right : left;
unsigned long value = 0;
+
switch (index) {
case TOMOYO_TASK_UID:
value = from_kuid(&init_user_ns, current_uid());
@@ -874,31 +898,31 @@
value = S_ISVTX;
break;
case TOMOYO_MODE_OWNER_READ:
- value = S_IRUSR;
+ value = 0400;
break;
case TOMOYO_MODE_OWNER_WRITE:
- value = S_IWUSR;
+ value = 0200;
break;
case TOMOYO_MODE_OWNER_EXECUTE:
- value = S_IXUSR;
+ value = 0100;
break;
case TOMOYO_MODE_GROUP_READ:
- value = S_IRGRP;
+ value = 0040;
break;
case TOMOYO_MODE_GROUP_WRITE:
- value = S_IWGRP;
+ value = 0020;
break;
case TOMOYO_MODE_GROUP_EXECUTE:
- value = S_IXGRP;
+ value = 0010;
break;
case TOMOYO_MODE_OTHERS_READ:
- value = S_IROTH;
+ value = 0004;
break;
case TOMOYO_MODE_OTHERS_WRITE:
- value = S_IWOTH;
+ value = 0002;
break;
case TOMOYO_MODE_OTHERS_EXECUTE:
- value = S_IXOTH;
+ value = 0001;
break;
case TOMOYO_EXEC_ARGC:
if (!bprm)
@@ -923,6 +947,7 @@
{
u8 stat_index;
struct tomoyo_mini_stat *stat;
+
switch (index) {
case TOMOYO_PATH1_UID:
case TOMOYO_PATH1_GID:
@@ -1036,12 +1061,14 @@
if (left == TOMOYO_NUMBER_UNION) {
/* Fetch values now. */
const struct tomoyo_number_union *ptr = numbers_p++;
+
min_v[0] = ptr->values[0];
max_v[0] = ptr->values[1];
}
if (right == TOMOYO_NUMBER_UNION) {
/* Fetch values now. */
const struct tomoyo_number_union *ptr = numbers_p++;
+
if (ptr->group) {
if (tomoyo_number_matches_group(min_v[0],
max_v[0],
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index f6758da..8526a0a 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -30,10 +30,10 @@
*/
int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
struct tomoyo_acl_param *param,
- bool (*check_duplicate) (const struct tomoyo_acl_head
- *,
- const struct tomoyo_acl_head
- *))
+ bool (*check_duplicate)(const struct tomoyo_acl_head
+ *,
+ const struct tomoyo_acl_head
+ *))
{
int error = param->is_delete ? -ENOENT : -ENOMEM;
struct tomoyo_acl_head *entry;
@@ -90,13 +90,13 @@
*/
int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
struct tomoyo_acl_param *param,
- bool (*check_duplicate) (const struct tomoyo_acl_info
- *,
- const struct tomoyo_acl_info
- *),
- bool (*merge_duplicate) (struct tomoyo_acl_info *,
- struct tomoyo_acl_info *,
- const bool))
+ bool (*check_duplicate)(const struct tomoyo_acl_info
+ *,
+ const struct tomoyo_acl_info
+ *),
+ bool (*merge_duplicate)(struct tomoyo_acl_info *,
+ struct tomoyo_acl_info *,
+ const bool))
{
const bool is_delete = param->is_delete;
int error = is_delete ? -ENOENT : -ENOMEM;
@@ -157,13 +157,13 @@
* Caller holds tomoyo_read_lock().
*/
void tomoyo_check_acl(struct tomoyo_request_info *r,
- bool (*check_entry) (struct tomoyo_request_info *,
- const struct tomoyo_acl_info *))
+ bool (*check_entry)(struct tomoyo_request_info *,
+ const struct tomoyo_acl_info *))
{
const struct tomoyo_domain_info *domain = r->domain;
struct tomoyo_acl_info *ptr;
- bool retried = false;
const struct list_head *list = &domain->acl_info_list;
+ u16 i = 0;
retry:
list_for_each_entry_rcu(ptr, list, list) {
@@ -177,9 +177,10 @@
r->granted = true;
return;
}
- if (!retried) {
- retried = true;
- list = &domain->ns->acl_group[domain->group];
+ for (; i < TOMOYO_MAX_ACL_GROUPS; i++) {
+ if (!test_bit(i, domain->group))
+ continue;
+ list = &domain->ns->acl_group[i++];
goto retry;
}
r->granted = false;
@@ -198,6 +199,7 @@
static const char *tomoyo_last_word(const char *name)
{
const char *cp = strrchr(name, ' ');
+
if (cp)
return cp + 1;
return name;
@@ -220,6 +222,7 @@
const struct tomoyo_transition_control *p2 = container_of(b,
typeof(*p2),
head);
+
return p1->type == p2->type && p1->is_last_name == p2->is_last_name
&& p1->domainname == p2->domainname
&& p1->program == p2->program;
@@ -240,6 +243,7 @@
int error = param->is_delete ? -ENOENT : -ENOMEM;
char *program = param->data;
char *domainname = strstr(program, " from ");
+
if (domainname) {
*domainname = '\0';
domainname += 6;
@@ -293,6 +297,7 @@
const enum tomoyo_transition_type type)
{
const struct tomoyo_transition_control *ptr;
+
list_for_each_entry_rcu(ptr, list, head.list) {
if (ptr->head.is_deleted || ptr->type != type)
continue;
@@ -338,9 +343,11 @@
{
const char *last_name = tomoyo_last_word(domainname->name);
enum tomoyo_transition_type type = TOMOYO_TRANSITION_CONTROL_NO_RESET;
+
while (type < TOMOYO_MAX_TRANSITION_TYPE) {
const struct list_head * const list =
&ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL];
+
if (!tomoyo_scan_transition(list, domainname, program,
last_name, type)) {
type++;
@@ -375,6 +382,7 @@
head);
const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2),
head);
+
return p1->original_name == p2->original_name &&
p1->aggregated_name == p2->aggregated_name;
}
@@ -394,6 +402,7 @@
int error = param->is_delete ? -ENOENT : -ENOMEM;
const char *original_name = tomoyo_read_token(param);
const char *aggregated_name = tomoyo_read_token(param);
+
if (!tomoyo_correct_word(original_name) ||
!tomoyo_correct_path(aggregated_name))
return -EINVAL;
@@ -426,6 +435,7 @@
(const char *name, const unsigned int len)
{
struct tomoyo_policy_namespace *ns;
+
list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
if (strncmp(name, ns->name, len) ||
(name[len] && name[len] != ' '))
@@ -451,6 +461,7 @@
struct tomoyo_policy_namespace *entry;
const char *cp = domainname;
unsigned int len = 0;
+
while (*cp && *cp++ != ' ')
len++;
ptr = tomoyo_find_namespace(domainname, len);
@@ -466,6 +477,7 @@
ptr = tomoyo_find_namespace(domainname, len);
if (!ptr && tomoyo_memory_ok(entry)) {
char *name = (char *) (entry + 1);
+
ptr = entry;
memmove(name, domainname, len);
name[len] = '\0';
@@ -490,6 +502,7 @@
{
const char *namespace = tomoyo_current_namespace()->name;
const int len = strlen(namespace);
+
return strncmp(domainname, namespace, len) ||
(domainname[len] && domainname[len] != ' ');
}
@@ -510,6 +523,7 @@
struct tomoyo_domain_info e = { };
struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname);
bool created = false;
+
if (entry) {
if (transit) {
/*
@@ -546,8 +560,9 @@
*/
if (transit) {
const struct tomoyo_domain_info *domain = tomoyo_domain();
+
e.profile = domain->profile;
- e.group = domain->group;
+ memcpy(e.group, domain->group, sizeof(e.group));
}
e.domainname = tomoyo_get_name(domainname);
if (!e.domainname)
@@ -569,12 +584,17 @@
if (entry && transit) {
if (created) {
struct tomoyo_request_info r;
+ int i;
+
tomoyo_init_request_info(&r, entry,
TOMOYO_MAC_FILE_EXECUTE);
r.granted = false;
tomoyo_write_log(&r, "use_profile %u\n",
entry->profile);
- tomoyo_write_log(&r, "use_group %u\n", entry->group);
+ for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
+ if (test_bit(i, entry->group))
+ tomoyo_write_log(&r, "use_group %u\n",
+ i);
tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
}
}
@@ -712,6 +732,7 @@
struct tomoyo_aggregator *ptr;
struct list_head *list =
&old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR];
+
/* Check 'aggregator' directive. */
candidate = &exename;
list_for_each_entry_rcu(ptr, list, head.list) {
@@ -747,6 +768,7 @@
*/
if (ee->transition) {
const char *domainname = ee->transition->name;
+
reject_on_transition_failure = true;
if (!strcmp(domainname, "keep"))
goto force_keep_domain;
@@ -758,6 +780,7 @@
goto force_initialize_domain;
if (!strcmp(domainname, "parent")) {
char *cp;
+
strncpy(ee->tmp, old_domain->domainname->name,
TOMOYO_EXEC_TMPSIZE - 1);
cp = strrchr(ee->tmp, ' ');
@@ -822,8 +845,7 @@
if (domain)
retval = 0;
else if (reject_on_transition_failure) {
- printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n",
- ee->tmp);
+ pr_warn("ERROR: Domain '%s' not ready.\n", ee->tmp);
retval = -ENOMEM;
} else if (ee->r.mode == TOMOYO_CONFIG_ENFORCING)
retval = -ENOMEM;
@@ -834,16 +856,20 @@
ee->r.granted = false;
tomoyo_write_log(&ee->r, "%s", tomoyo_dif
[TOMOYO_DIF_TRANSITION_FAILED]);
- printk(KERN_WARNING
- "ERROR: Domain '%s' not defined.\n", ee->tmp);
+ pr_warn("ERROR: Domain '%s' not defined.\n", ee->tmp);
}
}
out:
if (!domain)
domain = old_domain;
/* Update reference count on "struct tomoyo_domain_info". */
- atomic_inc(&domain->users);
- bprm->cred->security = domain;
+ {
+ struct tomoyo_task *s = tomoyo_task(current);
+
+ s->old_domain_info = s->domain_info;
+ s->domain_info = domain;
+ atomic_inc(&domain->users);
+ }
kfree(exename.name);
if (!retval) {
ee->r.domain = domain;
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
index 2a374b4..86f7d1b 100644
--- a/security/tomoyo/file.c
+++ b/security/tomoyo/file.c
@@ -214,6 +214,7 @@
const u8 type = r->param.path_number.operation;
u8 radix;
char buffer[64];
+
switch (type) {
case TOMOYO_TYPE_CREATE:
case TOMOYO_TYPE_MKDIR:
@@ -253,6 +254,7 @@
{
const struct tomoyo_path_acl *acl = container_of(ptr, typeof(*acl),
head);
+
if (acl->perm & (1 << r->param.path.operation)) {
r->param.path.matched_path =
tomoyo_compare_name_union(r->param.path.filename,
@@ -275,6 +277,7 @@
{
const struct tomoyo_path_number_acl *acl =
container_of(ptr, typeof(*acl), head);
+
return (acl->perm & (1 << r->param.path_number.operation)) &&
tomoyo_compare_number_union(r->param.path_number.number,
&acl->number) &&
@@ -295,6 +298,7 @@
{
const struct tomoyo_path2_acl *acl =
container_of(ptr, typeof(*acl), head);
+
return (acl->perm & (1 << r->param.path2.operation)) &&
tomoyo_compare_name_union(r->param.path2.filename1, &acl->name1)
&& tomoyo_compare_name_union(r->param.path2.filename2,
@@ -314,6 +318,7 @@
{
const struct tomoyo_mkdev_acl *acl =
container_of(ptr, typeof(*acl), head);
+
return (acl->perm & (1 << r->param.mkdev.operation)) &&
tomoyo_compare_number_union(r->param.mkdev.mode,
&acl->mode) &&
@@ -338,6 +343,7 @@
{
const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head);
+
return tomoyo_same_name_union(&p1->name, &p2->name);
}
@@ -358,6 +364,7 @@
->perm;
u16 perm = *a_perm;
const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm;
+
if (is_delete)
perm &= ~b_perm;
else
@@ -384,6 +391,7 @@
.perm = perm
};
int error;
+
if (!tomoyo_parse_name_union(param, &e.name))
error = -EINVAL;
else
@@ -407,6 +415,7 @@
{
const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), head);
+
return tomoyo_same_name_union(&p1->name, &p2->name) &&
tomoyo_same_number_union(&p1->mode, &p2->mode) &&
tomoyo_same_number_union(&p1->major, &p2->major) &&
@@ -431,6 +440,7 @@
u8 perm = *a_perm;
const u8 b_perm = container_of(b, struct tomoyo_mkdev_acl, head)
->perm;
+
if (is_delete)
perm &= ~b_perm;
else
@@ -457,6 +467,7 @@
.perm = perm
};
int error;
+
if (!tomoyo_parse_name_union(param, &e.name) ||
!tomoyo_parse_number_union(param, &e.mode) ||
!tomoyo_parse_number_union(param, &e.major) ||
@@ -486,6 +497,7 @@
{
const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head);
+
return tomoyo_same_name_union(&p1->name1, &p2->name1) &&
tomoyo_same_name_union(&p1->name2, &p2->name2);
}
@@ -507,6 +519,7 @@
->perm;
u8 perm = *a_perm;
const u8 b_perm = container_of(b, struct tomoyo_path2_acl, head)->perm;
+
if (is_delete)
perm &= ~b_perm;
else
@@ -533,6 +546,7 @@
.perm = perm
};
int error;
+
if (!tomoyo_parse_name_union(param, &e.name1) ||
!tomoyo_parse_name_union(param, &e.name2))
error = -EINVAL;
@@ -621,6 +635,7 @@
head);
const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2),
head);
+
return tomoyo_same_name_union(&p1->name, &p2->name) &&
tomoyo_same_number_union(&p1->number, &p2->number);
}
@@ -643,6 +658,7 @@
u8 perm = *a_perm;
const u8 b_perm = container_of(b, struct tomoyo_path_number_acl, head)
->perm;
+
if (is_delete)
perm &= ~b_perm;
else
@@ -667,6 +683,7 @@
.perm = perm
};
int error;
+
if (!tomoyo_parse_name_union(param, &e.name) ||
!tomoyo_parse_number_union(param, &e.number))
error = -EINVAL;
@@ -947,6 +964,7 @@
{
const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head);
+
return tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) &&
tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) &&
tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) &&
@@ -966,6 +984,7 @@
{
struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL };
int error;
+
if (!tomoyo_parse_name_union(param, &e.dev_name) ||
!tomoyo_parse_name_union(param, &e.dir_name) ||
!tomoyo_parse_name_union(param, &e.fs_type) ||
@@ -995,6 +1014,7 @@
u16 perm = 0;
u8 type;
const char *operation = tomoyo_read_token(param);
+
for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++)
if (tomoyo_permstr(operation, tomoyo_path_keyword[type]))
perm |= 1 << type;
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c
index e22bea8..9537832 100644
--- a/security/tomoyo/gc.c
+++ b/security/tomoyo/gc.c
@@ -77,11 +77,13 @@
spin_lock(&tomoyo_io_buffer_list_lock);
list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
int i;
+
head->users++;
spin_unlock(&tomoyo_io_buffer_list_lock);
mutex_lock(&head->io_sem);
for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) {
const char *w = head->r.w[i];
+
if (w < string || w > string + size)
continue;
in_use = true;
@@ -108,6 +110,7 @@
{
struct tomoyo_transition_control *ptr =
container_of(element, typeof(*ptr), head.list);
+
tomoyo_put_name(ptr->domainname);
tomoyo_put_name(ptr->program);
}
@@ -123,6 +126,7 @@
{
struct tomoyo_aggregator *ptr =
container_of(element, typeof(*ptr), head.list);
+
tomoyo_put_name(ptr->original_name);
tomoyo_put_name(ptr->aggregated_name);
}
@@ -138,6 +142,7 @@
{
struct tomoyo_manager *ptr =
container_of(element, typeof(*ptr), head.list);
+
tomoyo_put_name(ptr->manager);
}
@@ -152,6 +157,7 @@
{
struct tomoyo_acl_info *acl =
container_of(element, typeof(*acl), list);
+
tomoyo_put_condition(acl->cond);
switch (acl->type) {
case TOMOYO_TYPE_PATH_ACL:
@@ -226,6 +232,7 @@
{
struct tomoyo_task_acl *entry =
container_of(acl, typeof(*entry), head);
+
tomoyo_put_name(entry->domainname);
}
break;
@@ -247,6 +254,7 @@
container_of(element, typeof(*domain), list);
struct tomoyo_acl_info *acl;
struct tomoyo_acl_info *tmp;
+
/*
* Since this domain is referenced from neither
* "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete
@@ -286,6 +294,7 @@
= (const struct tomoyo_argv *) (names_p + names_count);
const struct tomoyo_envp *envp
= (const struct tomoyo_envp *) (argv + argc);
+
for (i = 0; i < numbers_count; i++)
tomoyo_put_number_union(numbers_p++);
for (i = 0; i < names_count; i++)
@@ -321,6 +330,7 @@
{
struct tomoyo_path_group *member =
container_of(element, typeof(*member), head.list);
+
tomoyo_put_name(member->member_name);
}
@@ -335,6 +345,7 @@
{
struct tomoyo_group *group =
container_of(element, typeof(*group), head.list);
+
tomoyo_put_name(group->group_name);
}
@@ -476,6 +487,7 @@
{
struct tomoyo_acl_head *member;
struct tomoyo_acl_head *tmp;
+
list_for_each_entry_safe(member, tmp, member_list, list) {
if (!member->is_deleted)
continue;
@@ -495,6 +507,7 @@
{
struct tomoyo_acl_info *acl;
struct tomoyo_acl_info *tmp;
+
list_for_each_entry_safe(acl, tmp, list, list) {
if (!acl->is_deleted)
continue;
@@ -513,10 +526,12 @@
int i;
enum tomoyo_policy_id id;
struct tomoyo_policy_namespace *ns;
+
mutex_lock(&tomoyo_policy_lock);
{
struct tomoyo_domain_info *domain;
struct tomoyo_domain_info *tmp;
+
list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list,
list) {
tomoyo_collect_acl(&domain->acl_info_list);
@@ -534,6 +549,7 @@
{
struct tomoyo_shared_acl_head *ptr;
struct tomoyo_shared_acl_head *tmp;
+
list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list,
list) {
if (atomic_read(&ptr->users) > 0)
@@ -547,6 +563,7 @@
struct list_head *list = &ns->group_list[i];
struct tomoyo_group *group;
struct tomoyo_group *tmp;
+
switch (i) {
case 0:
id = TOMOYO_ID_PATH_GROUP;
@@ -574,6 +591,7 @@
struct list_head *list = &tomoyo_name_list[i];
struct tomoyo_shared_acl_head *ptr;
struct tomoyo_shared_acl_head *tmp;
+
list_for_each_entry_safe(ptr, tmp, list, list) {
if (atomic_read(&ptr->users) > 0)
continue;
@@ -595,6 +613,7 @@
{
/* Garbage collector thread is exclusive. */
static DEFINE_MUTEX(tomoyo_gc_mutex);
+
if (!mutex_trylock(&tomoyo_gc_mutex))
goto out;
tomoyo_collect_entry();
diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c
index 21b0cc3..a37c7dc 100644
--- a/security/tomoyo/group.c
+++ b/security/tomoyo/group.c
@@ -75,11 +75,13 @@
{
struct tomoyo_group *group = tomoyo_get_group(param, type);
int error = -EINVAL;
+
if (!group)
return -ENOMEM;
param->list = &group->member_list;
if (type == TOMOYO_PATH_GROUP) {
struct tomoyo_path_group e = { };
+
e.member_name = tomoyo_get_name(tomoyo_read_token(param));
if (!e.member_name) {
error = -ENOMEM;
@@ -90,6 +92,7 @@
tomoyo_put_name(e.member_name);
} else if (type == TOMOYO_NUMBER_GROUP) {
struct tomoyo_number_group e = { };
+
if (param->data[0] == '@' ||
!tomoyo_parse_number_union(param, &e.number))
goto out;
@@ -129,6 +132,7 @@
const struct tomoyo_group *group)
{
struct tomoyo_path_group *member;
+
list_for_each_entry_rcu(member, &group->member_list, head.list) {
if (member->head.is_deleted)
continue;
@@ -156,6 +160,7 @@
{
struct tomoyo_number_group *member;
bool matched = false;
+
list_for_each_entry_rcu(member, &group->member_list, head.list) {
if (member->head.is_deleted)
continue;
diff --git a/security/tomoyo/load_policy.c b/security/tomoyo/load_policy.c
index 81b9516..3445ae6 100644
--- a/security/tomoyo/load_policy.c
+++ b/security/tomoyo/load_policy.c
@@ -37,11 +37,12 @@
static bool tomoyo_policy_loader_exists(void)
{
struct path path;
+
if (!tomoyo_loader)
tomoyo_loader = CONFIG_SECURITY_TOMOYO_POLICY_LOADER;
if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) {
- printk(KERN_INFO "Not activating Mandatory Access Control "
- "as %s does not exist.\n", tomoyo_loader);
+ pr_info("Not activating Mandatory Access Control as %s does not exist.\n",
+ tomoyo_loader);
return false;
}
path_put(&path);
@@ -96,8 +97,7 @@
if (!tomoyo_policy_loader_exists())
return;
done = true;
- printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
- tomoyo_loader);
+ pr_info("Calling %s to load policy. Please wait.\n", tomoyo_loader);
argv[0] = (char *) tomoyo_loader;
argv[1] = NULL;
envp[0] = "HOME=/";
diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c
index 12477e0..2e7fcfa 100644
--- a/security/tomoyo/memory.c
+++ b/security/tomoyo/memory.c
@@ -19,9 +19,9 @@
/* Reduce error messages. */
static pid_t tomoyo_last_pid;
const pid_t pid = current->pid;
+
if (tomoyo_last_pid != pid) {
- printk(KERN_WARNING "ERROR: Out of memory at %s.\n",
- function);
+ pr_warn("ERROR: Out of memory at %s.\n", function);
tomoyo_last_pid = pid;
}
if (!tomoyo_policy_loaded)
@@ -48,6 +48,7 @@
{
if (ptr) {
const size_t s = ksize(ptr);
+
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s;
if (!tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] ||
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <=
@@ -73,6 +74,7 @@
void *tomoyo_commit_ok(void *data, const unsigned int size)
{
void *ptr = kzalloc(size, GFP_NOFS);
+
if (tomoyo_memory_ok(ptr)) {
memmove(ptr, data, size);
memset(data, 0, size);
@@ -98,6 +100,7 @@
struct list_head *list;
const char *group_name = tomoyo_read_token(param);
bool found = false;
+
if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP)
return NULL;
e.group_name = tomoyo_get_name(group_name);
@@ -116,6 +119,7 @@
}
if (!found) {
struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e));
+
if (entry) {
INIT_LIST_HEAD(&entry->member_list);
atomic_set(&entry->head.users, 1);
@@ -191,6 +195,7 @@
void __init tomoyo_mm_init(void)
{
int idx;
+
for (idx = 0; idx < TOMOYO_MAX_HASH; idx++)
INIT_LIST_HEAD(&tomoyo_name_list[idx]);
tomoyo_kernel_namespace.name = "<kernel>";
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
index 807fd91..2755971 100644
--- a/security/tomoyo/mount.c
+++ b/security/tomoyo/mount.c
@@ -6,6 +6,7 @@
*/
#include <linux/slab.h>
+#include <uapi/linux/mount.h>
#include "common.h"
/* String table for special mount operations. */
@@ -48,6 +49,7 @@
{
const struct tomoyo_mount_acl *acl =
container_of(ptr, typeof(*acl), head);
+
return tomoyo_compare_number_union(r->param.mount.flags,
&acl->flags) &&
tomoyo_compare_name_union(r->param.mount.type,
@@ -88,6 +90,7 @@
struct tomoyo_path_info rdir;
int need_dev = 0;
int error = -ENOMEM;
+
r->obj = &obj;
/* Get fstype. */
diff --git a/security/tomoyo/network.c b/security/tomoyo/network.c
index 9094f4b..f9ff121 100644
--- a/security/tomoyo/network.c
+++ b/security/tomoyo/network.c
@@ -505,6 +505,8 @@
{
struct tomoyo_inet_addr_info *i = &address->inet;
+ if (addr_len < offsetofend(struct sockaddr, sa_family))
+ return 0;
switch (addr->sa_family) {
case AF_INET6:
if (addr_len < SIN6_LEN_RFC2133)
@@ -594,6 +596,8 @@
{
struct tomoyo_unix_addr_info *u = &address->unix0;
+ if (addr_len < offsetofend(struct sockaddr, sa_family))
+ return 0;
if (addr->sa_family != AF_UNIX)
return 0;
u->addr = ((struct sockaddr_un *) addr)->sun_path;
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
index 6ff8c21..e783244 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -94,11 +94,13 @@
const int buflen)
{
char *pos = ERR_PTR(-ENOMEM);
+
if (buflen >= 256) {
/* go to whatever namespace root we are under */
pos = d_absolute_path(path, buffer, buflen - 1);
if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
struct inode *inode = d_backing_inode(path->dentry);
+
if (inode && S_ISDIR(inode->i_mode)) {
buffer[buflen - 2] = '/';
buffer[buflen - 1] = '\0';
@@ -123,10 +125,12 @@
const int buflen)
{
char *pos = ERR_PTR(-ENOMEM);
+
if (buflen >= 256) {
pos = dentry_path_raw(dentry, buffer, buflen - 1);
if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
struct inode *inode = d_backing_inode(dentry);
+
if (inode && S_ISDIR(inode->i_mode)) {
buffer[buflen - 2] = '/';
buffer[buflen - 1] = '\0';
@@ -150,12 +154,14 @@
{
struct super_block *sb = dentry->d_sb;
char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen);
+
if (IS_ERR(pos))
return pos;
/* Convert from $PID to self if $PID is current thread. */
if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {
char *ep;
const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);
+
if (*ep == '/' && pid && pid ==
task_tgid_nr_ns(current, sb->s_fs_info)) {
pos = ep - 5;
@@ -170,6 +176,7 @@
goto prepend_filesystem_name;
{
struct inode *inode = d_backing_inode(sb->s_root);
+
/*
* Use filesystem name if filesystem does not support rename()
* operation.
@@ -182,6 +189,7 @@
char name[64];
int name_len;
const dev_t dev = sb->s_dev;
+
name[sizeof(name) - 1] = '\0';
snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev),
MINOR(dev));
@@ -197,6 +205,7 @@
{
const char *name = sb->s_type->name;
const int name_len = strlen(name);
+
pos -= name_len + 1;
if (pos < buffer)
goto out;
@@ -223,10 +232,10 @@
struct inode *inode = d_backing_inode(path->dentry);
struct socket *sock = inode ? SOCKET_I(inode) : NULL;
struct sock *sk = sock ? sock->sk : NULL;
+
if (sk) {
- snprintf(buffer, buflen, "socket:[family=%u:type=%u:"
- "protocol=%u]", sk->sk_family, sk->sk_type,
- sk->sk_protocol);
+ snprintf(buffer, buflen, "socket:[family=%u:type=%u:protocol=%u]",
+ sk->sk_family, sk->sk_type, sk->sk_protocol);
} else {
snprintf(buffer, buflen, "socket:[unknown]");
}
@@ -255,12 +264,14 @@
unsigned int buf_len = PAGE_SIZE / 2;
struct dentry *dentry = path->dentry;
struct super_block *sb;
+
if (!dentry)
return NULL;
sb = dentry->d_sb;
while (1) {
char *pos;
struct inode *inode;
+
buf_len <<= 1;
kfree(buf);
buf = kmalloc(buf_len, GFP_NOFS);
@@ -284,7 +295,8 @@
* or dentry without vfsmount.
*/
if (!path->mnt ||
- (!inode->i_op->rename))
+ (!inode->i_op->rename &&
+ !(sb->s_type->fs_flags & FS_REQUIRES_DEV)))
pos = tomoyo_get_local_path(path->dentry, buf,
buf_len - 1);
/* Get absolute name for the rest. */
@@ -323,6 +335,7 @@
if (pathname && kern_path(pathname, 0, &path) == 0) {
char *buf = tomoyo_realpath_from_path(&path);
+
path_put(&path);
return buf;
}
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
index 1d3d7e7..546281c 100644
--- a/security/tomoyo/securityfs_if.c
+++ b/security/tomoyo/securityfs_if.c
@@ -21,6 +21,7 @@
{
const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl),
head);
+
return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname);
}
@@ -42,6 +43,7 @@
{
char *data;
int error;
+
if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10)
return -ENOMEM;
data = memdup_user_nul(buf, count);
@@ -52,6 +54,7 @@
const int idx = tomoyo_read_lock();
struct tomoyo_path_info name;
struct tomoyo_request_info r;
+
name.name = data;
tomoyo_fill_path_info(&name);
/* Check "task manual_domain_transition" permission. */
@@ -67,18 +70,14 @@
if (!new_domain) {
error = -ENOENT;
} else {
- struct cred *cred = prepare_creds();
- if (!cred) {
- error = -ENOMEM;
- } else {
- struct tomoyo_domain_info *old_domain =
- cred->security;
- cred->security = new_domain;
- atomic_inc(&new_domain->users);
- atomic_dec(&old_domain->users);
- commit_creds(cred);
- error = 0;
- }
+ struct tomoyo_task *s = tomoyo_task(current);
+ struct tomoyo_domain_info *old_domain =
+ s->domain_info;
+
+ s->domain_info = new_domain;
+ atomic_inc(&new_domain->users);
+ atomic_dec(&old_domain->users);
+ error = 0;
}
}
tomoyo_read_unlock(idx);
@@ -104,6 +103,7 @@
const char *domain = tomoyo_domain()->domainname->name;
loff_t len = strlen(domain);
loff_t pos = *ppos;
+
if (pos >= len || !count)
return 0;
len -= pos;
@@ -234,10 +234,14 @@
*/
static int __init tomoyo_initerface_init(void)
{
+ struct tomoyo_domain_info *domain;
struct dentry *tomoyo_dir;
+ if (!tomoyo_enabled)
+ return 0;
+ domain = tomoyo_domain();
/* Don't create securityfs entries unless registered. */
- if (current_cred()->security != &tomoyo_kernel_domain)
+ if (domain != &tomoyo_kernel_domain)
return 0;
tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 9f932e2..716c92e 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -9,17 +9,19 @@
#include "common.h"
/**
- * tomoyo_cred_alloc_blank - Target for security_cred_alloc_blank().
+ * tomoyo_domain - Get "struct tomoyo_domain_info" for current thread.
*
- * @new: Pointer to "struct cred".
- * @gfp: Memory allocation flags.
- *
- * Returns 0.
+ * Returns pointer to "struct tomoyo_domain_info" for current thread.
*/
-static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
+struct tomoyo_domain_info *tomoyo_domain(void)
{
- new->security = NULL;
- return 0;
+ struct tomoyo_task *s = tomoyo_task(current);
+
+ if (s->old_domain_info && !current->in_execve) {
+ atomic_dec(&s->old_domain_info->users);
+ s->old_domain_info = NULL;
+ }
+ return s->domain_info;
}
/**
@@ -34,42 +36,38 @@
static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- struct tomoyo_domain_info *domain = old->security;
- new->security = domain;
- if (domain)
- atomic_inc(&domain->users);
+ /* Restore old_domain_info saved by previous execve() request. */
+ struct tomoyo_task *s = tomoyo_task(current);
+
+ if (s->old_domain_info && !current->in_execve) {
+ atomic_dec(&s->domain_info->users);
+ s->domain_info = s->old_domain_info;
+ s->old_domain_info = NULL;
+ }
return 0;
}
/**
- * tomoyo_cred_transfer - Target for security_transfer_creds().
+ * tomoyo_bprm_committed_creds - Target for security_bprm_committed_creds().
*
- * @new: Pointer to "struct cred".
- * @old: Pointer to "struct cred".
+ * @bprm: Pointer to "struct linux_binprm".
*/
-static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
+static void tomoyo_bprm_committed_creds(struct linux_binprm *bprm)
{
- tomoyo_cred_prepare(new, old, 0);
+ /* Clear old_domain_info saved by execve() request. */
+ struct tomoyo_task *s = tomoyo_task(current);
+
+ atomic_dec(&s->old_domain_info->users);
+ s->old_domain_info = NULL;
}
-/**
- * tomoyo_cred_free - Target for security_cred_free().
- *
- * @cred: Pointer to "struct cred".
- */
-static void tomoyo_cred_free(struct cred *cred)
-{
- struct tomoyo_domain_info *domain = cred->security;
- if (domain)
- atomic_dec(&domain->users);
-}
-
+#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
/**
* tomoyo_bprm_set_creds - Target for security_bprm_set_creds().
*
* @bprm: Pointer to "struct linux_binprm".
*
- * Returns 0 on success, negative value otherwise.
+ * Returns 0.
*/
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
{
@@ -79,29 +77,15 @@
*/
if (bprm->called_set_creds)
return 0;
-#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
/*
* Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
* for the first time.
*/
if (!tomoyo_policy_loaded)
tomoyo_load_policy(bprm->filename);
-#endif
- /*
- * Release reference to "struct tomoyo_domain_info" stored inside
- * "bprm->cred->security". New reference to "struct tomoyo_domain_info"
- * stored inside "bprm->cred->security" will be acquired later inside
- * tomoyo_find_next_domain().
- */
- atomic_dec(&((struct tomoyo_domain_info *)
- bprm->cred->security)->users);
- /*
- * Tell tomoyo_bprm_check_security() is called for the first time of an
- * execve operation.
- */
- bprm->cred->security = NULL;
return 0;
}
+#endif
/**
* tomoyo_bprm_check_security - Target for security_bprm_check().
@@ -112,23 +96,24 @@
*/
static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
{
- struct tomoyo_domain_info *domain = bprm->cred->security;
+ struct tomoyo_task *s = tomoyo_task(current);
/*
* Execute permission is checked against pathname passed to do_execve()
* using current domain.
*/
- if (!domain) {
+ if (!s->old_domain_info) {
const int idx = tomoyo_read_lock();
const int err = tomoyo_find_next_domain(bprm);
+
tomoyo_read_unlock(idx);
return err;
}
/*
* Read permission is checked against interpreters using next domain.
*/
- return tomoyo_check_open_permission(domain, &bprm->file->f_path,
- O_RDONLY);
+ return tomoyo_check_open_permission(s->domain_info,
+ &bprm->file->f_path, O_RDONLY);
}
/**
@@ -167,6 +152,7 @@
static int tomoyo_path_unlink(const struct path *parent, struct dentry *dentry)
{
struct path path = { .mnt = parent->mnt, .dentry = dentry };
+
return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);
}
@@ -183,6 +169,7 @@
umode_t mode)
{
struct path path = { .mnt = parent->mnt, .dentry = dentry };
+
return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path,
mode & S_IALLUGO);
}
@@ -198,6 +185,7 @@
static int tomoyo_path_rmdir(const struct path *parent, struct dentry *dentry)
{
struct path path = { .mnt = parent->mnt, .dentry = dentry };
+
return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);
}
@@ -214,6 +202,7 @@
const char *old_name)
{
struct path path = { .mnt = parent->mnt, .dentry = dentry };
+
return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);
}
@@ -271,6 +260,7 @@
{
struct path path1 = { .mnt = new_dir->mnt, .dentry = old_dentry };
struct path path2 = { .mnt = new_dir->mnt, .dentry = new_dentry };
+
return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
}
@@ -291,6 +281,7 @@
{
struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry };
struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry };
+
return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
}
@@ -322,11 +313,11 @@
*/
static int tomoyo_file_open(struct file *f)
{
- int flags = f->f_flags;
/* Don't check read permission here if called from do_execve(). */
if (current->in_execve)
return 0;
- return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
+ return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path,
+ f->f_flags);
}
/**
@@ -370,6 +361,7 @@
static int tomoyo_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
{
int error = 0;
+
if (uid_valid(uid))
error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path,
from_kuid(&init_user_ns, uid));
@@ -419,6 +411,7 @@
static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
{
struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
+
return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path, NULL);
}
@@ -493,16 +486,61 @@
return tomoyo_socket_sendmsg_permission(sock, msg, size);
}
+struct lsm_blob_sizes tomoyo_blob_sizes __lsm_ro_after_init = {
+ .lbs_task = sizeof(struct tomoyo_task),
+};
+
+/**
+ * tomoyo_task_alloc - Target for security_task_alloc().
+ *
+ * @task: Pointer to "struct task_struct".
+ * @flags: clone() flags.
+ *
+ * Returns 0.
+ */
+static int tomoyo_task_alloc(struct task_struct *task,
+ unsigned long clone_flags)
+{
+ struct tomoyo_task *old = tomoyo_task(current);
+ struct tomoyo_task *new = tomoyo_task(task);
+
+ new->domain_info = old->domain_info;
+ atomic_inc(&new->domain_info->users);
+ new->old_domain_info = NULL;
+ return 0;
+}
+
+/**
+ * tomoyo_task_free - Target for security_task_free().
+ *
+ * @task: Pointer to "struct task_struct".
+ */
+static void tomoyo_task_free(struct task_struct *task)
+{
+ struct tomoyo_task *s = tomoyo_task(task);
+
+ if (s->domain_info) {
+ atomic_dec(&s->domain_info->users);
+ s->domain_info = NULL;
+ }
+ if (s->old_domain_info) {
+ atomic_dec(&s->old_domain_info->users);
+ s->old_domain_info = NULL;
+ }
+}
+
/*
* tomoyo_security_ops is a "struct security_operations" which is used for
* registering TOMOYO.
*/
static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
- LSM_HOOK_INIT(cred_alloc_blank, tomoyo_cred_alloc_blank),
LSM_HOOK_INIT(cred_prepare, tomoyo_cred_prepare),
- LSM_HOOK_INIT(cred_transfer, tomoyo_cred_transfer),
- LSM_HOOK_INIT(cred_free, tomoyo_cred_free),
+ LSM_HOOK_INIT(bprm_committed_creds, tomoyo_bprm_committed_creds),
+ LSM_HOOK_INIT(task_alloc, tomoyo_task_alloc),
+ LSM_HOOK_INIT(task_free, tomoyo_task_free),
+#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
LSM_HOOK_INIT(bprm_set_creds, tomoyo_bprm_set_creds),
+#endif
LSM_HOOK_INIT(bprm_check_security, tomoyo_bprm_check_security),
LSM_HOOK_INIT(file_fcntl, tomoyo_file_fcntl),
LSM_HOOK_INIT(file_open, tomoyo_file_open),
@@ -531,6 +569,8 @@
/* Lock for GC. */
DEFINE_SRCU(tomoyo_ss);
+int tomoyo_enabled __lsm_ro_after_init = 1;
+
/**
* tomoyo_init - Register TOMOYO Linux as a LSM module.
*
@@ -538,16 +578,23 @@
*/
static int __init tomoyo_init(void)
{
- struct cred *cred = (struct cred *) current_cred();
+ struct tomoyo_task *s = tomoyo_task(current);
- if (!security_module_enable("tomoyo"))
- return 0;
/* register ourselves with the security framework */
security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
- printk(KERN_INFO "TOMOYO Linux initialized\n");
- cred->security = &tomoyo_kernel_domain;
+ pr_info("TOMOYO Linux initialized\n");
+ s->domain_info = &tomoyo_kernel_domain;
+ atomic_inc(&tomoyo_kernel_domain.users);
+ s->old_domain_info = NULL;
tomoyo_mm_init();
+
return 0;
}
-security_initcall(tomoyo_init);
+DEFINE_LSM(tomoyo) = {
+ .name = "tomoyo",
+ .enabled = &tomoyo_enabled,
+ .flags = LSM_FLAG_LEGACY_MAJOR,
+ .blobs = &tomoyo_blob_sizes,
+ .init = tomoyo_init,
+};
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c
index d3d9d9f..52752e1 100644
--- a/security/tomoyo/util.c
+++ b/security/tomoyo/util.c
@@ -91,6 +91,7 @@
void tomoyo_convert_time(time64_t time64, struct tomoyo_time *stamp)
{
struct tm tm;
+
time64_to_tm(time64, 0, &tm);
stamp->sec = tm.tm_sec;
stamp->min = tm.tm_min;
@@ -106,13 +107,14 @@
* @string: String representation for permissions in foo/bar/buz format.
* @keyword: Keyword to find from @string/
*
- * Returns ture if @keyword was found in @string, false otherwise.
+ * Returns true if @keyword was found in @string, false otherwise.
*
* This function assumes that strncmp(w1, w2, strlen(w1)) != 0 if w1 != w2.
*/
bool tomoyo_permstr(const char *string, const char *keyword)
{
const char *cp = strstr(string, keyword);
+
if (cp)
return cp == string || *(cp - 1) == '/';
return false;
@@ -132,6 +134,7 @@
{
char *pos = param->data;
char *del = strchr(pos, ' ');
+
if (del)
*del++ = '\0';
else
@@ -152,6 +155,7 @@
{
char *start = param->data;
char *pos = start;
+
while (*pos) {
if (*pos++ != ' ' || *pos++ == '/')
continue;
@@ -181,8 +185,10 @@
const char *cp = *str;
char *ep;
int base = 10;
+
if (*cp == '0') {
char c = *(cp + 1);
+
if (c == 'x' || c == 'X') {
base = 16;
cp += 2;
@@ -240,6 +246,7 @@
struct tomoyo_name_union *ptr)
{
char *filename;
+
if (param->data[0] == '@') {
param->data++;
ptr->group = tomoyo_get_group(param, TOMOYO_PATH_GROUP);
@@ -266,6 +273,7 @@
char *data;
u8 type;
unsigned long v;
+
memset(ptr, 0, sizeof(*ptr));
if (param->data[0] == '@') {
param->data++;
@@ -429,6 +437,7 @@
unsigned char c;
unsigned char d;
unsigned char e;
+
if (!len)
goto out;
while (len--) {
@@ -533,6 +542,7 @@
return true;
while (1) {
const unsigned char *cp = strchr(domainname, ' ');
+
if (!cp)
break;
if (*domainname != '/' ||
@@ -554,6 +564,7 @@
{
const unsigned char *cp;
int len;
+
if (*buffer != '<')
return false;
cp = strchr(buffer, ' ');
@@ -668,6 +679,9 @@
{
while (filename < filename_end && pattern < pattern_end) {
char c;
+ int i;
+ int j;
+
if (*pattern != '\\') {
if (*filename++ != *pattern++)
return false;
@@ -676,8 +690,6 @@
c = *filename;
pattern++;
switch (*pattern) {
- int i;
- int j;
case '?':
if (c == '/') {
return false;
@@ -985,6 +997,7 @@
struct tomoyo_domain_info *domain, const u8 index)
{
u8 profile;
+
memset(r, 0, sizeof(*r));
if (!domain)
domain = tomoyo_domain();
@@ -1018,6 +1031,7 @@
list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
u16 perm;
u8 i;
+
if (ptr->is_deleted)
continue;
switch (ptr->type) {
@@ -1062,9 +1076,10 @@
domain->flags[TOMOYO_DIF_QUOTA_WARNED] = true;
/* r->granted = false; */
tomoyo_write_log(r, "%s", tomoyo_dif[TOMOYO_DIF_QUOTA_WARNED]);
- printk(KERN_WARNING "WARNING: "
- "Domain '%s' has too many ACLs to hold. "
- "Stopped learning mode.\n", domain->domainname->name);
+#ifndef CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING
+ pr_warn("WARNING: Domain '%s' has too many ACLs to hold. Stopped learning mode.\n",
+ domain->domainname->name);
+#endif
}
return false;
}
diff --git a/security/yama/Kconfig b/security/yama/Kconfig
index 96b2740..a810304 100644
--- a/security/yama/Kconfig
+++ b/security/yama/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config SECURITY_YAMA
bool "Yama support"
depends on SECURITY
diff --git a/security/yama/Makefile b/security/yama/Makefile
index 8b5e065..0fa5d0f 100644
--- a/security/yama/Makefile
+++ b/security/yama/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SECURITY_YAMA) := yama.o
yama-y := yama_lsm.o
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index ffda91a..94dc346 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Yama Linux Security Module
*
@@ -5,11 +6,6 @@
*
* Copyright (C) 2010 Canonical, Ltd.
* Copyright (C) 2011 The Chromium OS Authors.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
*/
#include <linux/lsm_hooks.h>
@@ -206,7 +202,7 @@
* yama_task_free - check for task_pid to remove from exception list
* @task: task being removed
*/
-void yama_task_free(struct task_struct *task)
+static void yama_task_free(struct task_struct *task)
{
yama_ptracer_del(task, task);
}
@@ -222,7 +218,7 @@
* Return 0 on success, -ve on error. -ENOSYS is returned when Yama
* does not handle the given option.
*/
-int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
+static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
int rc = -ENOSYS;
@@ -368,7 +364,9 @@
break;
case YAMA_SCOPE_RELATIONAL:
rcu_read_lock();
- if (!task_is_descendant(current, child) &&
+ if (!pid_alive(child))
+ rc = -EPERM;
+ if (!rc && !task_is_descendant(current, child) &&
!ptracer_exception_found(current, child) &&
!ns_capable(__task_cred(child)->user_ns, CAP_SYS_PTRACE))
rc = -EPERM;
@@ -399,7 +397,7 @@
*
* Returns 0 if following the ptrace is allowed, -ve on error.
*/
-int yama_ptrace_traceme(struct task_struct *parent)
+static int yama_ptrace_traceme(struct task_struct *parent)
{
int rc = 0;
@@ -447,10 +445,9 @@
return proc_dointvec_minmax(&table_copy, write, buffer, lenp, ppos);
}
-static int zero;
static int max_scope = YAMA_SCOPE_NO_ATTACH;
-struct ctl_path yama_sysctl_path[] = {
+static struct ctl_path yama_sysctl_path[] = {
{ .procname = "kernel", },
{ .procname = "yama", },
{ }
@@ -463,7 +460,7 @@
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = yama_dointvec_minmax,
- .extra1 = &zero,
+ .extra1 = SYSCTL_ZERO,
.extra2 = &max_scope,
},
{ }
@@ -477,9 +474,15 @@
static inline void yama_init_sysctl(void) { }
#endif /* CONFIG_SYSCTL */
-void __init yama_add_hooks(void)
+static int __init yama_init(void)
{
pr_info("Yama: becoming mindful.\n");
security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama");
yama_init_sysctl();
+ return 0;
}
+
+DEFINE_LSM(yama) = {
+ .name = "yama",
+ .init = yama_init,
+};