summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Makefile2
-rw-r--r--security/apparmor/apparmorfs.c4
-rw-r--r--security/apparmor/crypto.c3
-rw-r--r--security/apparmor/include/apparmorfs.h2
-rw-r--r--security/apparmor/include/crypto.h1
-rw-r--r--security/apparmor/lsm.c11
-rw-r--r--security/bpf/hooks.c2
-rw-r--r--security/commoncap.c2
-rw-r--r--security/device_cgroup.c56
-rw-r--r--security/inode.c46
-rw-r--r--security/integrity/evm/evm_main.c5
-rw-r--r--security/integrity/evm/evm_secfs.c11
-rw-r--r--security/integrity/iint.c14
-rw-r--r--security/integrity/ima/ima_fs.c11
-rw-r--r--security/integrity/ima/ima_main.c68
-rw-r--r--security/integrity/ima/ima_policy.c62
-rw-r--r--security/integrity/integrity.h2
-rw-r--r--security/ipe/audit.c1
-rw-r--r--security/ipe/fs.c4
-rw-r--r--security/ipe/hooks.c30
-rw-r--r--security/ipe/hooks.h3
-rw-r--r--security/ipe/ipe.c4
-rw-r--r--security/ipe/ipe.h2
-rw-r--r--security/keys/big_key.c2
-rw-r--r--security/keys/encrypted-keys/ecryptfs_format.c3
-rw-r--r--security/keys/encrypted-keys/encrypted.c4
-rw-r--r--security/keys/trusted-keys/trusted_caam.c108
-rw-r--r--security/keys/trusted-keys/trusted_core.c4
-rw-r--r--security/keys/trusted-keys/trusted_tpm2.c29
-rw-r--r--security/keys/user_defined.c2
-rw-r--r--security/landlock/setup.c2
-rw-r--r--security/loadpin/loadpin.c13
-rw-r--r--security/lockdown/lockdown.c5
-rw-r--r--security/lsm.h58
-rw-r--r--security/lsm_init.c564
-rw-r--r--security/lsm_notifier.c31
-rw-r--r--security/lsm_syscalls.c2
-rw-r--r--security/min_addr.c5
-rw-r--r--security/safesetid/lsm.c3
-rw-r--r--security/safesetid/lsm.h2
-rw-r--r--security/safesetid/securityfs.c3
-rw-r--r--security/security.c623
-rw-r--r--security/selinux/Kconfig11
-rw-r--r--security/selinux/Makefile2
-rw-r--r--security/selinux/avc.c9
-rw-r--r--security/selinux/hooks.c42
-rw-r--r--security/selinux/ibpkey.c5
-rw-r--r--security/selinux/include/audit.h9
-rw-r--r--security/selinux/include/classmap.h2
-rw-r--r--security/selinux/include/hash.h47
-rw-r--r--security/selinux/include/initcalls.h19
-rw-r--r--security/selinux/include/policycap.h1
-rw-r--r--security/selinux/include/policycap_names.h1
-rw-r--r--security/selinux/include/security.h5
-rw-r--r--security/selinux/initcalls.c52
-rw-r--r--security/selinux/netif.c5
-rw-r--r--security/selinux/netlink.c5
-rw-r--r--security/selinux/netnode.c5
-rw-r--r--security/selinux/netport.c5
-rw-r--r--security/selinux/selinuxfs.c5
-rw-r--r--security/selinux/ss/avtab.c39
-rw-r--r--security/selinux/ss/services.c26
-rw-r--r--security/smack/smack.h17
-rw-r--r--security/smack/smack_access.c96
-rw-r--r--security/smack/smack_lsm.c290
-rw-r--r--security/smack/smack_netfilter.c4
-rw-r--r--security/smack/smackfs.c4
-rw-r--r--security/tomoyo/common.h2
-rw-r--r--security/tomoyo/securityfs_if.c4
-rw-r--r--security/tomoyo/tomoyo.c3
-rw-r--r--security/yama/yama_lsm.c2
71 files changed, 1597 insertions, 929 deletions
diff --git a/security/Makefile b/security/Makefile
index 22ff4c8bd8ce..4601230ba442 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) += lsm_syscalls.o
obj-$(CONFIG_MMU) += min_addr.o
# Object file lists
-obj-$(CONFIG_SECURITY) += security.o
+obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o lsm_init.o
obj-$(CONFIG_SECURITYFS) += inode.o
obj-$(CONFIG_SECURITY_SELINUX) += selinux/
obj-$(CONFIG_SECURITY_SMACK) += smack/
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 9d08d103f142..e4114aa1e04f 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -2649,7 +2649,7 @@ static const struct inode_operations policy_link_iops = {
*
* Returns: error on failure
*/
-static int __init aa_create_aafs(void)
+int __init aa_create_aafs(void)
{
struct dentry *dent;
int error;
@@ -2728,5 +2728,3 @@ error:
AA_ERROR("Error creating AppArmor securityfs\n");
return error;
}
-
-fs_initcall(aa_create_aafs);
diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c
index 227d47c14907..d8a7bde94d79 100644
--- a/security/apparmor/crypto.c
+++ b/security/apparmor/crypto.c
@@ -53,10 +53,9 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
return 0;
}
-static int __init init_profile_hash(void)
+int __init init_profile_hash(void)
{
if (apparmor_initialized)
aa_info_message("AppArmor sha256 policy hashing enabled");
return 0;
}
-late_initcall(init_profile_hash);
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index 1e94904f68d9..dd580594dfb7 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -104,6 +104,8 @@ enum aafs_prof_type {
#define prof_dir(X) ((X)->dents[AAFS_PROF_DIR])
#define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS])
+int aa_create_aafs(void);
+
void __aa_bump_ns_revision(struct aa_ns *ns);
void __aafs_profile_rmdir(struct aa_profile *profile);
void __aafs_profile_migrate_dents(struct aa_profile *old,
diff --git a/security/apparmor/include/crypto.h b/security/apparmor/include/crypto.h
index 636a04e20d91..f3ffd388cc58 100644
--- a/security/apparmor/include/crypto.h
+++ b/security/apparmor/include/crypto.h
@@ -13,6 +13,7 @@
#include "policy.h"
#ifdef CONFIG_SECURITY_APPARMOR_HASH
+int init_profile_hash(void);
unsigned int aa_hash_size(void);
char *aa_calc_hash(void *data, size_t len);
int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start,
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index b3f7a3258a2c..a87cd60ed206 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -32,6 +32,7 @@
#include "include/audit.h"
#include "include/capability.h"
#include "include/cred.h"
+#include "include/crypto.h"
#include "include/file.h"
#include "include/ipc.h"
#include "include/net.h"
@@ -2426,7 +2427,6 @@ static int __init apparmor_nf_ip_init(void)
return 0;
}
-__initcall(apparmor_nf_ip_init);
#endif
static char nulldfa_src[] __aligned(8) = {
@@ -2555,9 +2555,16 @@ alloc_out:
}
DEFINE_LSM(apparmor) = {
- .name = "apparmor",
+ .id = &apparmor_lsmid,
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
.enabled = &apparmor_enabled,
.blobs = &apparmor_blob_sizes,
.init = apparmor_init,
+ .initcall_fs = aa_create_aafs,
+#if defined(CONFIG_NETFILTER) && defined(CONFIG_NETWORK_SECMARK)
+ .initcall_device = apparmor_nf_ip_init,
+#endif
+#ifdef CONFIG_SECURITY_APPARMOR_HASH
+ .initcall_late = init_profile_hash,
+#endif
};
diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
index db759025abe1..40efde233f3a 100644
--- a/security/bpf/hooks.c
+++ b/security/bpf/hooks.c
@@ -33,7 +33,7 @@ struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = {
};
DEFINE_LSM(bpf) = {
- .name = "bpf",
+ .id = &bpf_lsmid,
.init = bpf_lsm_init,
.blobs = &bpf_lsm_blob_sizes
};
diff --git a/security/commoncap.c b/security/commoncap.c
index 6bd4adeb4795..b50479bd0286 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -1505,7 +1505,7 @@ static int __init capability_init(void)
}
DEFINE_LSM(capability) = {
- .name = "capability",
+ .id = &capability_lsmid,
.order = LSM_ORDER_FIRST,
.init = capability_init,
};
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index dc4df7475081..7fec575d32d6 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -244,45 +244,40 @@ static void devcgroup_css_free(struct cgroup_subsys_state *css)
#define DEVCG_DENY 2
#define DEVCG_LIST 3
-#define MAJMINLEN 13
-#define ACCLEN 4
-
-static void set_access(char *acc, short access)
+static void seq_putaccess(struct seq_file *m, short access)
{
- int idx = 0;
- memset(acc, 0, ACCLEN);
if (access & DEVCG_ACC_READ)
- acc[idx++] = 'r';
+ seq_putc(m, 'r');
if (access & DEVCG_ACC_WRITE)
- acc[idx++] = 'w';
+ seq_putc(m, 'w');
if (access & DEVCG_ACC_MKNOD)
- acc[idx++] = 'm';
+ seq_putc(m, 'm');
}
-static char type_to_char(short type)
+static void seq_puttype(struct seq_file *m, short type)
{
if (type == DEVCG_DEV_ALL)
- return 'a';
- if (type == DEVCG_DEV_CHAR)
- return 'c';
- if (type == DEVCG_DEV_BLOCK)
- return 'b';
- return 'X';
+ seq_putc(m, 'a');
+ else if (type == DEVCG_DEV_CHAR)
+ seq_putc(m, 'c');
+ else if (type == DEVCG_DEV_BLOCK)
+ seq_putc(m, 'b');
+ else
+ seq_putc(m, 'X');
}
-static void set_majmin(char *str, unsigned m)
+static void seq_putversion(struct seq_file *m, unsigned int version)
{
- if (m == ~0)
- strcpy(str, "*");
+ if (version == ~0)
+ seq_putc(m, '*');
else
- sprintf(str, "%u", m);
+ seq_printf(m, "%u", version);
}
static int devcgroup_seq_show(struct seq_file *m, void *v)
{
struct dev_cgroup *devcgroup = css_to_devcgroup(seq_css(m));
struct dev_exception_item *ex;
- char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
rcu_read_lock();
/*
@@ -292,18 +287,17 @@ static int devcgroup_seq_show(struct seq_file *m, void *v)
* This way, the file remains as a "whitelist of devices"
*/
if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
- set_access(acc, DEVCG_ACC_MASK);
- set_majmin(maj, ~0);
- set_majmin(min, ~0);
- seq_printf(m, "%c %s:%s %s\n", type_to_char(DEVCG_DEV_ALL),
- maj, min, acc);
+ seq_puts(m, "a *:* rwm\n");
} else {
list_for_each_entry_rcu(ex, &devcgroup->exceptions, list) {
- set_access(acc, ex->access);
- set_majmin(maj, ex->major);
- set_majmin(min, ex->minor);
- seq_printf(m, "%c %s:%s %s\n", type_to_char(ex->type),
- maj, min, acc);
+ seq_puttype(m, ex->type);
+ seq_putc(m, ' ');
+ seq_putversion(m, ex->major);
+ seq_putc(m, ':');
+ seq_putversion(m, ex->minor);
+ seq_putc(m, ' ');
+ seq_putaccess(m, ex->access);
+ seq_putc(m, '\n');
}
}
rcu_read_unlock();
diff --git a/security/inode.c b/security/inode.c
index 43382ef8896e..ab8d6a2acadb 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -22,6 +22,8 @@
#include <linux/lsm_hooks.h>
#include <linux/magic.h>
+#include "lsm.h"
+
static struct vfsmount *mount;
static int mount_count;
@@ -315,12 +317,49 @@ void securityfs_remove(struct dentry *dentry)
EXPORT_SYMBOL_GPL(securityfs_remove);
#ifdef CONFIG_SECURITY
+#include <linux/spinlock.h>
+
static struct dentry *lsm_dentry;
+
static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count,
loff_t *ppos)
{
- return simple_read_from_buffer(buf, count, ppos, lsm_names,
- strlen(lsm_names));
+ int i;
+ static char *str;
+ static size_t len;
+ static DEFINE_SPINLOCK(lock);
+
+ /* NOTE: we never free or modify the string once it is set */
+
+ if (unlikely(!str || !len)) {
+ char *str_tmp;
+ size_t len_tmp = 0;
+
+ for (i = 0; i < lsm_active_cnt; i++)
+ /* the '+ 1' accounts for either a comma or a NUL */
+ len_tmp += strlen(lsm_idlist[i]->name) + 1;
+
+ str_tmp = kmalloc(len_tmp, GFP_KERNEL);
+ if (!str_tmp)
+ return -ENOMEM;
+ str_tmp[0] = '\0';
+
+ for (i = 0; i < lsm_active_cnt; i++) {
+ if (i > 0)
+ strcat(str_tmp, ",");
+ strcat(str_tmp, lsm_idlist[i]->name);
+ }
+
+ spin_lock(&lock);
+ if (!str) {
+ str = str_tmp;
+ len = len_tmp - 1;
+ } else
+ kfree(str_tmp);
+ spin_unlock(&lock);
+ }
+
+ return simple_read_from_buffer(buf, count, ppos, str, len);
}
static const struct file_operations lsm_ops = {
@@ -329,7 +368,7 @@ static const struct file_operations lsm_ops = {
};
#endif
-static int __init securityfs_init(void)
+int __init securityfs_init(void)
{
int retval;
@@ -348,4 +387,3 @@ static int __init securityfs_init(void)
#endif
return 0;
}
-core_initcall(securityfs_init);
diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c
index 0add782e73ba..73d500a375cb 100644
--- a/security/integrity/evm/evm_main.c
+++ b/security/integrity/evm/evm_main.c
@@ -1175,10 +1175,9 @@ struct lsm_blob_sizes evm_blob_sizes __ro_after_init = {
};
DEFINE_LSM(evm) = {
- .name = "evm",
+ .id = &evm_lsmid,
.init = init_evm_lsm,
.order = LSM_ORDER_LAST,
.blobs = &evm_blob_sizes,
+ .initcall_late = init_evm,
};
-
-late_initcall(init_evm);
diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c
index b0d2aad27850..c26724690cec 100644
--- a/security/integrity/evm/evm_secfs.c
+++ b/security/integrity/evm/evm_secfs.c
@@ -302,10 +302,16 @@ int __init evm_init_secfs(void)
int error = 0;
struct dentry *dentry;
- evm_dir = securityfs_create_dir("evm", integrity_dir);
- if (IS_ERR(evm_dir))
+ error = integrity_fs_init();
+ if (error < 0)
return -EFAULT;
+ evm_dir = securityfs_create_dir("evm", integrity_dir);
+ if (IS_ERR(evm_dir)) {
+ error = -EFAULT;
+ goto out;
+ }
+
dentry = securityfs_create_file("evm", 0660,
evm_dir, NULL, &evm_key_ops);
if (IS_ERR(dentry)) {
@@ -329,5 +335,6 @@ int __init evm_init_secfs(void)
out:
securityfs_remove(evm_symlink);
securityfs_remove(evm_dir);
+ integrity_fs_fini();
return error;
}
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 068ac6c2ae1e..8ec1a3436a71 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -42,8 +42,11 @@ void __init integrity_load_keys(void)
evm_load_x509();
}
-static int __init integrity_fs_init(void)
+int __init integrity_fs_init(void)
{
+ if (integrity_dir)
+ return 0;
+
integrity_dir = securityfs_create_dir("integrity", NULL);
if (IS_ERR(integrity_dir)) {
int ret = PTR_ERR(integrity_dir);
@@ -58,4 +61,11 @@ static int __init integrity_fs_init(void)
return 0;
}
-late_initcall(integrity_fs_init)
+void __init integrity_fs_fini(void)
+{
+ if (!integrity_dir || !simple_empty(integrity_dir))
+ return;
+
+ securityfs_remove(integrity_dir);
+ integrity_dir = NULL;
+}
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 87045b09f120..012a58959ff0 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -499,9 +499,15 @@ int __init ima_fs_init(void)
struct dentry *dentry;
int ret;
+ ret = integrity_fs_init();
+ if (ret < 0)
+ return ret;
+
ima_dir = securityfs_create_dir("ima", integrity_dir);
- if (IS_ERR(ima_dir))
- return PTR_ERR(ima_dir);
+ if (IS_ERR(ima_dir)) {
+ ret = PTR_ERR(ima_dir);
+ goto out;
+ }
ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
NULL);
@@ -555,6 +561,7 @@ int __init ima_fs_init(void)
out:
securityfs_remove(ima_symlink);
securityfs_remove(ima_dir);
+ integrity_fs_fini();
return ret;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index cdd225f65a62..5770cf691912 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -235,7 +235,8 @@ static void ima_file_free(struct file *file)
static int process_measurement(struct file *file, const struct cred *cred,
struct lsm_prop *prop, char *buf, loff_t size,
- int mask, enum ima_hooks func)
+ int mask, enum ima_hooks func,
+ enum kernel_read_file_id read_id)
{
struct inode *real_inode, *inode = file_inode(file);
struct ima_iint_cache *iint = NULL;
@@ -406,6 +407,12 @@ static int process_measurement(struct file *file, const struct cred *cred,
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
goto out_locked;
+ /* Defer measuring/appraising kernel modules to READING_MODULE */
+ if (read_id == READING_MODULE_COMPRESSED) {
+ must_appraise = 0;
+ goto out_locked;
+ }
+
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
pathname = ima_d_path(&file->f_path, &pathbuf, filename);
@@ -486,14 +493,14 @@ static int ima_file_mmap(struct file *file, unsigned long reqprot,
if (reqprot & PROT_EXEC) {
ret = process_measurement(file, current_cred(), &prop, NULL,
- 0, MAY_EXEC, MMAP_CHECK_REQPROT);
+ 0, MAY_EXEC, MMAP_CHECK_REQPROT, 0);
if (ret)
return ret;
}
if (prot & PROT_EXEC)
return process_measurement(file, current_cred(), &prop, NULL,
- 0, MAY_EXEC, MMAP_CHECK);
+ 0, MAY_EXEC, MMAP_CHECK, 0);
return 0;
}
@@ -573,18 +580,41 @@ static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
*/
static int ima_bprm_check(struct linux_binprm *bprm)
{
- int ret;
struct lsm_prop prop;
security_current_getlsmprop_subj(&prop);
- ret = process_measurement(bprm->file, current_cred(),
- &prop, NULL, 0, MAY_EXEC, BPRM_CHECK);
- if (ret)
- return ret;
-
- security_cred_getlsmprop(bprm->cred, &prop);
- return process_measurement(bprm->file, bprm->cred, &prop, NULL, 0,
- MAY_EXEC, CREDS_CHECK);
+ return process_measurement(bprm->file, current_cred(),
+ &prop, NULL, 0, MAY_EXEC, BPRM_CHECK, 0);
+}
+
+/**
+ * ima_creds_check - based on policy, collect/store measurement.
+ * @bprm: contains the linux_binprm structure
+ * @file: contains the file descriptor of the binary being executed
+ *
+ * The OS protects against an executable file, already open for write,
+ * from being executed in deny_write_access() and an executable file,
+ * already open for execute, from being modified in get_write_access().
+ * So we can be certain that what we verify and measure here is actually
+ * what is being executed.
+ *
+ * The difference from ima_bprm_check() is that ima_creds_check() is invoked
+ * only after determining the final binary to be executed without interpreter,
+ * and not when searching for intermediate binaries. The reason is that since
+ * commit 56305aa9b6fab ("exec: Compute file based creds only once"), the
+ * credentials to be applied to the process are calculated only at that stage
+ * (bprm_creds_from_file security hook instead of bprm_check_security).
+ *
+ * On success return 0. On integrity appraisal error, assuming the file
+ * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
+ */
+static int ima_creds_check(struct linux_binprm *bprm, const struct file *file)
+{
+ struct lsm_prop prop;
+
+ security_current_getlsmprop_subj(&prop);
+ return process_measurement((struct file *)file, bprm->cred, &prop, NULL,
+ 0, MAY_EXEC, CREDS_CHECK, 0);
}
/**
@@ -632,7 +662,7 @@ static int ima_file_check(struct file *file, int mask)
security_current_getlsmprop_subj(&prop);
return process_measurement(file, current_cred(), &prop, NULL, 0,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
- MAY_APPEND), FILE_CHECK);
+ MAY_APPEND), FILE_CHECK, 0);
}
static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
@@ -851,12 +881,13 @@ static int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
func = read_idmap[read_id] ?: FILE_CHECK;
security_current_getlsmprop_subj(&prop);
return process_measurement(file, current_cred(), &prop, NULL, 0,
- MAY_READ, func);
+ MAY_READ, func, 0);
}
const int read_idmap[READING_MAX_ID] = {
[READING_FIRMWARE] = FIRMWARE_CHECK,
[READING_MODULE] = MODULE_CHECK,
+ [READING_MODULE_COMPRESSED] = MODULE_CHECK,
[READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK,
[READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK,
[READING_POLICY] = POLICY_CHECK
@@ -894,7 +925,7 @@ static int ima_post_read_file(struct file *file, char *buf, loff_t size,
func = read_idmap[read_id] ?: FILE_CHECK;
security_current_getlsmprop_subj(&prop);
return process_measurement(file, current_cred(), &prop, buf, size,
- MAY_READ, func);
+ MAY_READ, func, read_id);
}
/**
@@ -1242,6 +1273,7 @@ static int __init init_ima(void)
static struct security_hook_list ima_hooks[] __ro_after_init = {
LSM_HOOK_INIT(bprm_check_security, ima_bprm_check),
LSM_HOOK_INIT(bprm_creds_for_exec, ima_bprm_creds_for_exec),
+ LSM_HOOK_INIT(bprm_creds_from_file, ima_creds_check),
LSM_HOOK_INIT(file_post_open, ima_file_check),
LSM_HOOK_INIT(inode_post_create_tmpfile, ima_post_create_tmpfile),
LSM_HOOK_INIT(file_release, ima_file_free),
@@ -1279,10 +1311,10 @@ struct lsm_blob_sizes ima_blob_sizes __ro_after_init = {
};
DEFINE_LSM(ima) = {
- .name = "ima",
+ .id = &ima_lsmid,
.init = init_ima_lsm,
.order = LSM_ORDER_LAST,
.blobs = &ima_blob_sizes,
+ /* Start IMA after the TPM is available */
+ .initcall_late = init_ima,
};
-
-late_initcall(init_ima); /* Start IMA after the TPM is available */
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 128fab897930..8fbd8755f5bc 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -38,6 +38,7 @@
#define IMA_GID 0x2000
#define IMA_EGID 0x4000
#define IMA_FGROUP 0x8000
+#define IMA_FS_SUBTYPE 0x10000
#define UNKNOWN 0
#define MEASURE 0x0001 /* same as IMA_MEASURE */
@@ -45,6 +46,7 @@
#define APPRAISE 0x0004 /* same as IMA_APPRAISE */
#define DONT_APPRAISE 0x0008
#define AUDIT 0x0040
+#define DONT_AUDIT 0x0080
#define HASH 0x0100
#define DONT_HASH 0x0200
@@ -119,6 +121,7 @@ struct ima_rule_entry {
int type; /* audit type */
} lsm[MAX_LSM_RULES];
char *fsname;
+ char *fs_subtype;
struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */
struct ima_rule_opt_list *label; /* Measure data grouped under this label */
struct ima_template_desc *template;
@@ -241,7 +244,8 @@ static struct ima_rule_entry build_appraise_rules[] __ro_after_init = {
static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
{.action = APPRAISE, .func = MODULE_CHECK,
- .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
+ .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED |
+ IMA_CHECK_BLACKLIST},
{.action = APPRAISE, .func = FIRMWARE_CHECK,
.flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
{.action = APPRAISE, .func = KEXEC_KERNEL_CHECK,
@@ -397,6 +401,7 @@ static void ima_free_rule(struct ima_rule_entry *entry)
* the defined_templates list and cannot be freed here
*/
kfree(entry->fsname);
+ kfree(entry->fs_subtype);
ima_free_rule_opt_list(entry->keyrings);
ima_lsm_free_rule(entry);
kfree(entry);
@@ -601,6 +606,12 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
if ((rule->flags & IMA_FSNAME)
&& strcmp(rule->fsname, inode->i_sb->s_type->name))
return false;
+ if (rule->flags & IMA_FS_SUBTYPE) {
+ if (!inode->i_sb->s_subtype)
+ return false;
+ if (strcmp(rule->fs_subtype, inode->i_sb->s_subtype))
+ return false;
+ }
if ((rule->flags & IMA_FSUUID) &&
!uuid_equal(&rule->fsuuid, &inode->i_sb->s_uuid))
return false;
@@ -674,7 +685,7 @@ retry:
goto retry;
}
}
- if (!rc) {
+ if (rc <= 0) {
result = false;
goto out;
}
@@ -1064,10 +1075,10 @@ void ima_update_policy(void)
enum policy_opt {
Opt_measure, Opt_dont_measure,
Opt_appraise, Opt_dont_appraise,
- Opt_audit, Opt_hash, Opt_dont_hash,
+ Opt_audit, Opt_dont_audit, Opt_hash, Opt_dont_hash,
Opt_obj_user, Opt_obj_role, Opt_obj_type,
Opt_subj_user, Opt_subj_role, Opt_subj_type,
- Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, Opt_fsuuid,
+ Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, Opt_fs_subtype, Opt_fsuuid,
Opt_uid_eq, Opt_euid_eq, Opt_gid_eq, Opt_egid_eq,
Opt_fowner_eq, Opt_fgroup_eq,
Opt_uid_gt, Opt_euid_gt, Opt_gid_gt, Opt_egid_gt,
@@ -1086,6 +1097,7 @@ static const match_table_t policy_tokens = {
{Opt_appraise, "appraise"},
{Opt_dont_appraise, "dont_appraise"},
{Opt_audit, "audit"},
+ {Opt_dont_audit, "dont_audit"},
{Opt_hash, "hash"},
{Opt_dont_hash, "dont_hash"},
{Opt_obj_user, "obj_user=%s"},
@@ -1098,6 +1110,7 @@ static const match_table_t policy_tokens = {
{Opt_mask, "mask=%s"},
{Opt_fsmagic, "fsmagic=%s"},
{Opt_fsname, "fsname=%s"},
+ {Opt_fs_subtype, "fs_subtype=%s"},
{Opt_fsuuid, "fsuuid=%s"},
{Opt_uid_eq, "uid=%s"},
{Opt_euid_eq, "euid=%s"},
@@ -1282,7 +1295,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC |
IMA_UID | IMA_FOWNER | IMA_FSUUID |
IMA_INMASK | IMA_EUID | IMA_PCR |
- IMA_FSNAME | IMA_GID | IMA_EGID |
+ IMA_FSNAME | IMA_FS_SUBTYPE |
+ IMA_GID | IMA_EGID |
IMA_FGROUP | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS |
IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED))
@@ -1295,7 +1309,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC |
IMA_UID | IMA_FOWNER | IMA_FSUUID |
IMA_INMASK | IMA_EUID | IMA_PCR |
- IMA_FSNAME | IMA_GID | IMA_EGID |
+ IMA_FSNAME | IMA_FS_SUBTYPE |
+ IMA_GID | IMA_EGID |
IMA_FGROUP | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED |
IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS))
@@ -1308,7 +1323,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->flags & ~(IMA_FUNC | IMA_FSMAGIC | IMA_UID |
IMA_FOWNER | IMA_FSUUID | IMA_EUID |
- IMA_PCR | IMA_FSNAME | IMA_GID | IMA_EGID |
+ IMA_PCR | IMA_FSNAME | IMA_FS_SUBTYPE |
+ IMA_GID | IMA_EGID |
IMA_FGROUP))
return false;
@@ -1478,6 +1494,14 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->action = AUDIT;
break;
+ case Opt_dont_audit:
+ ima_log_string(ab, "action", "dont_audit");
+
+ if (entry->action != UNKNOWN)
+ result = -EINVAL;
+
+ entry->action = DONT_AUDIT;
+ break;
case Opt_hash:
ima_log_string(ab, "action", "hash");
@@ -1587,6 +1611,22 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
result = 0;
entry->flags |= IMA_FSNAME;
break;
+ case Opt_fs_subtype:
+ ima_log_string(ab, "fs_subtype", args[0].from);
+
+ if (entry->fs_subtype) {
+ result = -EINVAL;
+ break;
+ }
+
+ entry->fs_subtype = kstrdup(args[0].from, GFP_KERNEL);
+ if (!entry->fs_subtype) {
+ result = -ENOMEM;
+ break;
+ }
+ result = 0;
+ entry->flags |= IMA_FS_SUBTYPE;
+ break;
case Opt_keyrings:
ima_log_string(ab, "keyrings", args[0].from);
@@ -2097,6 +2137,8 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, pt(Opt_dont_appraise));
if (entry->action & AUDIT)
seq_puts(m, pt(Opt_audit));
+ if (entry->action & DONT_AUDIT)
+ seq_puts(m, pt(Opt_dont_audit));
if (entry->action & HASH)
seq_puts(m, pt(Opt_hash));
if (entry->action & DONT_HASH)
@@ -2133,6 +2175,12 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, " ");
}
+ if (entry->flags & IMA_FS_SUBTYPE) {
+ snprintf(tbuf, sizeof(tbuf), "%s", entry->fs_subtype);
+ seq_printf(m, pt(Opt_fs_subtype), tbuf);
+ seq_puts(m, " ");
+ }
+
if (entry->flags & IMA_KEYRINGS) {
seq_puts(m, "keyrings=");
ima_show_rule_opt_list(m, entry->keyrings);
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index c2c2da691123..7b388b66cf80 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -114,6 +114,8 @@ struct ima_file_id {
int integrity_kernel_read(struct file *file, loff_t offset,
void *addr, unsigned long count);
+int __init integrity_fs_init(void);
+void __init integrity_fs_fini(void);
#define INTEGRITY_KEYRING_EVM 0
#define INTEGRITY_KEYRING_IMA 1
diff --git a/security/ipe/audit.c b/security/ipe/audit.c
index de5fed62592e..3f0deeb54912 100644
--- a/security/ipe/audit.c
+++ b/security/ipe/audit.c
@@ -46,6 +46,7 @@ static const char *const audit_op_names[__IPE_OP_MAX + 1] = {
static const char *const audit_hook_names[__IPE_HOOK_MAX] = {
"BPRM_CHECK",
+ "BPRM_CREDS_FOR_EXEC",
"MMAP",
"MPROTECT",
"KERNEL_READ",
diff --git a/security/ipe/fs.c b/security/ipe/fs.c
index 0bb9468b8026..076c111c85c8 100644
--- a/security/ipe/fs.c
+++ b/security/ipe/fs.c
@@ -193,7 +193,7 @@ static const struct file_operations enforce_fops = {
* Return: %0 on success. If an error occurs, the function will return
* the -errno.
*/
-static int __init ipe_init_securityfs(void)
+int __init ipe_init_securityfs(void)
{
int rc = 0;
struct ipe_policy *ap;
@@ -244,5 +244,3 @@ err:
securityfs_remove(root);
return rc;
}
-
-fs_initcall(ipe_init_securityfs);
diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c
index d0323b81cd8f..603abdc9ce3b 100644
--- a/security/ipe/hooks.c
+++ b/security/ipe/hooks.c
@@ -36,6 +36,33 @@ int ipe_bprm_check_security(struct linux_binprm *bprm)
}
/**
+ * ipe_bprm_creds_for_exec() - ipe security hook function for bprm creds check.
+ * @bprm: Supplies a pointer to a linux_binprm structure to source the file
+ * being evaluated.
+ *
+ * This LSM hook is called when userspace signals the kernel to check a file
+ * for execution through the execveat syscall with the AT_EXECVE_CHECK flag.
+ * The hook triggers IPE policy evaluation on the script file and returns
+ * the policy decision to userspace. The userspace program receives the
+ * return code and can decide whether to proceed with script execution.
+ *
+ * Return:
+ * * %0 - Success
+ * * %-EACCES - Did not pass IPE policy
+ */
+int ipe_bprm_creds_for_exec(struct linux_binprm *bprm)
+{
+ struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
+
+ if (!bprm->is_check)
+ return 0;
+
+ ipe_build_eval_ctx(&ctx, bprm->file, IPE_OP_EXEC,
+ IPE_HOOK_BPRM_CREDS_FOR_EXEC);
+ return ipe_evaluate_event(&ctx);
+}
+
+/**
* ipe_mmap_file() - ipe security hook function for mmap check.
* @f: File being mmap'd. Can be NULL in the case of anonymous memory.
* @reqprot: The requested protection on the mmap, passed from usermode.
@@ -118,6 +145,7 @@ int ipe_kernel_read_file(struct file *file, enum kernel_read_file_id id,
op = IPE_OP_FIRMWARE;
break;
case READING_MODULE:
+ case READING_MODULE_COMPRESSED:
op = IPE_OP_KERNEL_MODULE;
break;
case READING_KEXEC_INITRAMFS:
@@ -311,4 +339,4 @@ int ipe_inode_setintegrity(const struct inode *inode,
return -EINVAL;
}
-#endif /* CONFIG_CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
diff --git a/security/ipe/hooks.h b/security/ipe/hooks.h
index 38d4a387d039..07db37332740 100644
--- a/security/ipe/hooks.h
+++ b/security/ipe/hooks.h
@@ -13,6 +13,7 @@
enum ipe_hook_type {
IPE_HOOK_BPRM_CHECK = 0,
+ IPE_HOOK_BPRM_CREDS_FOR_EXEC,
IPE_HOOK_MMAP,
IPE_HOOK_MPROTECT,
IPE_HOOK_KERNEL_READ,
@@ -24,6 +25,8 @@ enum ipe_hook_type {
int ipe_bprm_check_security(struct linux_binprm *bprm);
+int ipe_bprm_creds_for_exec(struct linux_binprm *bprm);
+
int ipe_mmap_file(struct file *f, unsigned long reqprot, unsigned long prot,
unsigned long flags);
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 4317134cb0da..495bb765de1b 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -47,6 +47,7 @@ struct ipe_inode *ipe_inode(const struct inode *inode)
static struct security_hook_list ipe_hooks[] __ro_after_init = {
LSM_HOOK_INIT(bprm_check_security, ipe_bprm_check_security),
+ LSM_HOOK_INIT(bprm_creds_for_exec, ipe_bprm_creds_for_exec),
LSM_HOOK_INIT(mmap_file, ipe_mmap_file),
LSM_HOOK_INIT(file_mprotect, ipe_file_mprotect),
LSM_HOOK_INIT(kernel_read_file, ipe_kernel_read_file),
@@ -92,7 +93,8 @@ static int __init ipe_init(void)
}
DEFINE_LSM(ipe) = {
- .name = "ipe",
+ .id = &ipe_lsmid,
.init = ipe_init,
.blobs = &ipe_blobs,
+ .initcall_fs = ipe_init_securityfs,
};
diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h
index fb37513812dd..25cfdb8f0c20 100644
--- a/security/ipe/ipe.h
+++ b/security/ipe/ipe.h
@@ -23,4 +23,6 @@ struct ipe_bdev *ipe_bdev(struct block_device *b);
struct ipe_inode *ipe_inode(const struct inode *inode);
#endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
+int ipe_init_securityfs(void);
+
#endif /* _IPE_H */
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index c3367622c683..d46862ab90d6 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -66,7 +66,7 @@ int big_key_preparse(struct key_preparsed_payload *prep)
BUILD_BUG_ON(sizeof(*payload) != sizeof(prep->payload.data));
- if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
+ if (datalen == 0 || datalen > 1024 * 1024 || !prep->data)
return -EINVAL;
/* Set an arbitrary quota */
diff --git a/security/keys/encrypted-keys/ecryptfs_format.c b/security/keys/encrypted-keys/ecryptfs_format.c
index 8fdd76105ce3..2fc6f3a66135 100644
--- a/security/keys/encrypted-keys/ecryptfs_format.c
+++ b/security/keys/encrypted-keys/ecryptfs_format.c
@@ -54,8 +54,7 @@ int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok,
auth_tok->version = (((uint16_t)(major << 8) & 0xFF00)
| ((uint16_t)minor & 0x00FF));
auth_tok->token_type = ECRYPTFS_PASSWORD;
- strncpy((char *)auth_tok->token.password.signature, key_desc,
- ECRYPTFS_PASSWORD_SIG_SIZE);
+ strscpy_pad(auth_tok->token.password.signature, key_desc);
auth_tok->token.password.session_key_encryption_key_bytes =
ECRYPTFS_MAX_KEY_BYTES;
/*
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 513c09e2b01c..596e7a30bd3c 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -795,7 +795,7 @@ static int encrypted_instantiate(struct key *key,
size_t datalen = prep->datalen;
int ret;
- if (datalen <= 0 || datalen > 32767 || !prep->data)
+ if (datalen == 0 || datalen > 32767 || !prep->data)
return -EINVAL;
datablob = kmalloc(datalen + 1, GFP_KERNEL);
@@ -856,7 +856,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
if (key_is_negative(key))
return -ENOKEY;
- if (datalen <= 0 || datalen > 32767 || !prep->data)
+ if (datalen == 0 || datalen > 32767 || !prep->data)
return -EINVAL;
buf = kmalloc(datalen + 1, GFP_KERNEL);
diff --git a/security/keys/trusted-keys/trusted_caam.c b/security/keys/trusted-keys/trusted_caam.c
index e3415c520c0a..601943ce0d60 100644
--- a/security/keys/trusted-keys/trusted_caam.c
+++ b/security/keys/trusted-keys/trusted_caam.c
@@ -1,12 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de>
+ * Copyright 2025 NXP
*/
#include <keys/trusted_caam.h>
#include <keys/trusted-type.h>
#include <linux/build_bug.h>
#include <linux/key-type.h>
+#include <linux/parser.h>
#include <soc/fsl/caam-blob.h>
static struct caam_blob_priv *blobifier;
@@ -16,6 +18,77 @@ static struct caam_blob_priv *blobifier;
static_assert(MAX_KEY_SIZE + CAAM_BLOB_OVERHEAD <= CAAM_BLOB_MAX_LEN);
static_assert(MAX_BLOB_SIZE <= CAAM_BLOB_MAX_LEN);
+enum {
+ opt_err,
+ opt_key_enc_algo,
+};
+
+static const match_table_t key_tokens = {
+ {opt_key_enc_algo, "key_enc_algo=%s"},
+ {opt_err, NULL}
+};
+
+#ifdef CAAM_DEBUG
+static inline void dump_options(const struct caam_pkey_info *pkey_info)
+{
+ pr_info("key encryption algo %d\n", pkey_info->key_enc_algo);
+}
+#else
+static inline void dump_options(const struct caam_pkey_info *pkey_info)
+{
+}
+#endif
+
+static int get_pkey_options(char *c,
+ struct caam_pkey_info *pkey_info)
+{
+ substring_t args[MAX_OPT_ARGS];
+ unsigned long token_mask = 0;
+ u16 key_enc_algo;
+ char *p = c;
+ int token;
+ int res;
+
+ if (!c)
+ return 0;
+
+ while ((p = strsep(&c, " \t"))) {
+ if (*p == '\0' || *p == ' ' || *p == '\t')
+ continue;
+ token = match_token(p, key_tokens, args);
+ if (test_and_set_bit(token, &token_mask))
+ return -EINVAL;
+
+ switch (token) {
+ case opt_key_enc_algo:
+ res = kstrtou16(args[0].from, 16, &key_enc_algo);
+ if (res < 0)
+ return -EINVAL;
+ pkey_info->key_enc_algo = key_enc_algo;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static bool is_key_pkey(char **datablob)
+{
+ char *c = NULL;
+
+ do {
+ /* Second argument onwards,
+ * determine if tied to HW
+ */
+ c = strsep(datablob, " \t");
+ if (c && (strcmp(c, "pk") == 0))
+ return true;
+ } while (c);
+
+ return false;
+}
+
static int trusted_caam_seal(struct trusted_key_payload *p, char *datablob)
{
int ret;
@@ -25,11 +98,30 @@ static int trusted_caam_seal(struct trusted_key_payload *p, char *datablob)
.key_mod = KEYMOD, .key_mod_len = sizeof(KEYMOD) - 1,
};
+ /*
+ * If it is to be treated as protected key,
+ * read next arguments too.
+ */
+ if (is_key_pkey(&datablob)) {
+ info.pkey_info.plain_key_sz = p->key_len;
+ info.pkey_info.is_pkey = 1;
+ ret = get_pkey_options(datablob, &info.pkey_info);
+ if (ret < 0)
+ return 0;
+ dump_options(&info.pkey_info);
+ }
+
ret = caam_encap_blob(blobifier, &info);
if (ret)
return ret;
p->blob_len = info.output_len;
+ if (info.pkey_info.is_pkey) {
+ p->key_len = p->blob_len + sizeof(struct caam_pkey_info);
+ memcpy(p->key, &info.pkey_info, sizeof(struct caam_pkey_info));
+ memcpy(p->key + sizeof(struct caam_pkey_info), p->blob, p->blob_len);
+ }
+
return 0;
}
@@ -42,11 +134,27 @@ static int trusted_caam_unseal(struct trusted_key_payload *p, char *datablob)
.key_mod = KEYMOD, .key_mod_len = sizeof(KEYMOD) - 1,
};
+ if (is_key_pkey(&datablob)) {
+ info.pkey_info.plain_key_sz = p->blob_len - CAAM_BLOB_OVERHEAD;
+ info.pkey_info.is_pkey = 1;
+ ret = get_pkey_options(datablob, &info.pkey_info);
+ if (ret < 0)
+ return 0;
+ dump_options(&info.pkey_info);
+
+ p->key_len = p->blob_len + sizeof(struct caam_pkey_info);
+ memcpy(p->key, &info.pkey_info, sizeof(struct caam_pkey_info));
+ memcpy(p->key + sizeof(struct caam_pkey_info), p->blob, p->blob_len);
+
+ return 0;
+ }
+
ret = caam_decap_blob(blobifier, &info);
if (ret)
return ret;
p->key_len = info.output_len;
+
return 0;
}
diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
index e2d9644efde1..b1680ee53f86 100644
--- a/security/keys/trusted-keys/trusted_core.c
+++ b/security/keys/trusted-keys/trusted_core.c
@@ -157,7 +157,7 @@ static int trusted_instantiate(struct key *key,
int key_cmd;
size_t key_len;
- if (datalen <= 0 || datalen > 32767 || !prep->data)
+ if (datalen == 0 || datalen > 32767 || !prep->data)
return -EINVAL;
orig_datablob = datablob = kmalloc(datalen + 1, GFP_KERNEL);
@@ -240,7 +240,7 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
p = key->payload.data[0];
if (!p->migratable)
return -EPERM;
- if (datalen <= 0 || datalen > 32767 || !prep->data)
+ if (datalen == 0 || datalen > 32767 || !prep->data)
return -EINVAL;
orig_datablob = datablob = kmalloc(datalen + 1, GFP_KERNEL);
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 024be262702f..91656e44b326 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -18,14 +18,6 @@
#include "tpm2key.asn1.h"
-static struct tpm2_hash tpm2_hash_map[] = {
- {HASH_ALGO_SHA1, TPM_ALG_SHA1},
- {HASH_ALGO_SHA256, TPM_ALG_SHA256},
- {HASH_ALGO_SHA384, TPM_ALG_SHA384},
- {HASH_ALGO_SHA512, TPM_ALG_SHA512},
- {HASH_ALGO_SM3_256, TPM_ALG_SM3_256},
-};
-
static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };
static int tpm2_key_encode(struct trusted_key_payload *payload,
@@ -244,20 +236,13 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
off_t offset = TPM_HEADER_SIZE;
struct tpm_buf buf, sized;
int blob_len = 0;
- u32 hash;
+ int hash;
u32 flags;
- int i;
int rc;
- for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) {
- if (options->hash == tpm2_hash_map[i].crypto_id) {
- hash = tpm2_hash_map[i].tpm_id;
- break;
- }
- }
-
- if (i == ARRAY_SIZE(tpm2_hash_map))
- return -EINVAL;
+ hash = tpm2_find_hash_alg(options->hash);
+ if (hash < 0)
+ return hash;
if (!options->keyhandle)
return -EINVAL;
@@ -387,6 +372,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
struct trusted_key_options *options,
u32 *blob_handle)
{
+ u8 *blob_ref __free(kfree) = NULL;
struct tpm_buf buf;
unsigned int private_len;
unsigned int public_len;
@@ -400,6 +386,9 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
/* old form */
blob = payload->blob;
payload->old_format = 1;
+ } else {
+ /* Bind for cleanup: */
+ blob_ref = blob;
}
/* new format carries keyhandle but old format doesn't */
@@ -464,8 +453,6 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
(__be32 *) &buf.data[TPM_HEADER_SIZE]);
out:
- if (blob != payload->blob)
- kfree(blob);
tpm_buf_destroy(&buf);
if (rc > 0)
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 749e2a4dcb13..686d56e4cc85 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -61,7 +61,7 @@ int user_preparse(struct key_preparsed_payload *prep)
struct user_key_payload *upayload;
size_t datalen = prep->datalen;
- if (datalen <= 0 || datalen > 32767 || !prep->data)
+ if (datalen == 0 || datalen > 32767 || !prep->data)
return -EINVAL;
upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
diff --git a/security/landlock/setup.c b/security/landlock/setup.c
index bd53c7a56ab9..47dac1736f10 100644
--- a/security/landlock/setup.c
+++ b/security/landlock/setup.c
@@ -75,7 +75,7 @@ static int __init landlock_init(void)
}
DEFINE_LSM(LANDLOCK_NAME) = {
- .name = LANDLOCK_NAME,
+ .id = &landlock_lsmid,
.init = landlock_init,
.blobs = &landlock_blob_sizes,
};
diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c
index 68252452b66c..273ffbd6defe 100644
--- a/security/loadpin/loadpin.c
+++ b/security/loadpin/loadpin.c
@@ -270,11 +270,6 @@ static int __init loadpin_init(void)
return 0;
}
-DEFINE_LSM(loadpin) = {
- .name = "loadpin",
- .init = loadpin_init,
-};
-
#ifdef CONFIG_SECURITY_LOADPIN_VERITY
enum loadpin_securityfs_interface_index {
@@ -434,9 +429,15 @@ static int __init init_loadpin_securityfs(void)
return 0;
}
-fs_initcall(init_loadpin_securityfs);
+#endif /* CONFIG_SECURITY_LOADPIN_VERITY */
+DEFINE_LSM(loadpin) = {
+ .id = &loadpin_lsmid,
+ .init = loadpin_init,
+#ifdef CONFIG_SECURITY_LOADPIN_VERITY
+ .initcall_fs = init_loadpin_securityfs,
#endif /* CONFIG_SECURITY_LOADPIN_VERITY */
+};
/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
module_param(enforce, int, 0);
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
index cf83afa1d879..8d46886d2cca 100644
--- a/security/lockdown/lockdown.c
+++ b/security/lockdown/lockdown.c
@@ -161,13 +161,12 @@ static int __init lockdown_secfs_init(void)
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",
+ .id = &lockdown_lsmid,
.init = lockdown_lsm_init,
+ .initcall_core = lockdown_secfs_init,
};
diff --git a/security/lsm.h b/security/lsm.h
new file mode 100644
index 000000000000..81aadbc61685
--- /dev/null
+++ b/security/lsm.h
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LSM functions
+ */
+
+#ifndef _LSM_H_
+#define _LSM_H_
+
+#include <linux/printk.h>
+#include <linux/lsm_hooks.h>
+#include <linux/lsm_count.h>
+
+/* LSM debugging */
+extern bool lsm_debug;
+#define lsm_pr(...) pr_info(__VA_ARGS__)
+#define lsm_pr_cont(...) pr_cont(__VA_ARGS__)
+#define lsm_pr_dbg(...) \
+ do { \
+ if (lsm_debug) \
+ pr_info(__VA_ARGS__); \
+ } while (0)
+
+/* List of configured LSMs */
+extern unsigned int lsm_active_cnt;
+extern const struct lsm_id *lsm_idlist[];
+
+/* LSM blob configuration */
+extern struct lsm_blob_sizes blob_sizes;
+
+/* LSM blob caches */
+extern struct kmem_cache *lsm_file_cache;
+extern struct kmem_cache *lsm_inode_cache;
+
+/* LSM blob allocators */
+int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
+int lsm_task_alloc(struct task_struct *task);
+
+/* LSM framework initializers */
+
+#ifdef CONFIG_MMU
+int min_addr_init(void);
+#else
+static inline int min_addr_init(void)
+{
+ return 0;
+}
+#endif /* CONFIG_MMU */
+
+#ifdef CONFIG_SECURITYFS
+int securityfs_init(void);
+#else
+static inline int securityfs_init(void)
+{
+ return 0;
+}
+#endif /* CONFIG_SECURITYFS */
+
+#endif /* _LSM_H_ */
diff --git a/security/lsm_init.c b/security/lsm_init.c
new file mode 100644
index 000000000000..05bd52e6b1f2
--- /dev/null
+++ b/security/lsm_init.c
@@ -0,0 +1,564 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LSM initialization functions
+ */
+
+#define pr_fmt(fmt) "LSM: " fmt
+
+#include <linux/init.h>
+#include <linux/lsm_hooks.h>
+
+#include "lsm.h"
+
+/* LSM enabled constants. */
+static __initdata int lsm_enabled_true = 1;
+static __initdata int lsm_enabled_false = 0;
+
+/* Pointers to LSM sections defined in include/asm-generic/vmlinux.lds.h */
+extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
+extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
+
+/* Number of "early" LSMs */
+static __initdata unsigned int lsm_count_early;
+
+/* Build and boot-time LSM ordering. */
+static __initconst const char *const lsm_order_builtin = CONFIG_LSM;
+static __initdata const char *lsm_order_cmdline;
+static __initdata const char *lsm_order_legacy;
+
+/* Ordered list of LSMs to initialize. */
+static __initdata struct lsm_info *lsm_order[MAX_LSM_COUNT + 1];
+static __initdata struct lsm_info *lsm_exclusive;
+
+#define lsm_order_for_each(iter) \
+ for ((iter) = lsm_order; *(iter); (iter)++)
+#define lsm_for_each_raw(iter) \
+ for ((iter) = __start_lsm_info; \
+ (iter) < __end_lsm_info; (iter)++)
+#define lsm_early_for_each_raw(iter) \
+ for ((iter) = __start_early_lsm_info; \
+ (iter) < __end_early_lsm_info; (iter)++)
+
+#define lsm_initcall(level) \
+ ({ \
+ int _r, _rc = 0; \
+ struct lsm_info **_lp, *_l; \
+ lsm_order_for_each(_lp) { \
+ _l = *_lp; \
+ if (!_l->initcall_##level) \
+ continue; \
+ lsm_pr_dbg("running %s %s initcall", \
+ _l->id->name, #level); \
+ _r = _l->initcall_##level(); \
+ if (_r) { \
+ pr_warn("failed LSM %s %s initcall with errno %d\n", \
+ _l->id->name, #level, _r); \
+ if (!_rc) \
+ _rc = _r; \
+ } \
+ } \
+ _rc; \
+ })
+
+/**
+ * lsm_choose_security - Legacy "major" LSM selection
+ * @str: kernel command line parameter
+ */
+static int __init lsm_choose_security(char *str)
+{
+ lsm_order_legacy = str;
+ return 1;
+}
+__setup("security=", lsm_choose_security);
+
+/**
+ * lsm_choose_lsm - Modern LSM selection
+ * @str: kernel command line parameter
+ */
+static int __init lsm_choose_lsm(char *str)
+{
+ lsm_order_cmdline = str;
+ return 1;
+}
+__setup("lsm=", lsm_choose_lsm);
+
+/**
+ * lsm_debug_enable - Enable LSM framework debugging
+ * @str: kernel command line parameter
+ *
+ * Currently we only provide debug info during LSM initialization, but we may
+ * want to expand this in the future.
+ */
+static int __init lsm_debug_enable(char *str)
+{
+ lsm_debug = true;
+ return 1;
+}
+__setup("lsm.debug", lsm_debug_enable);
+
+/**
+ * lsm_enabled_set - Mark a LSM as enabled
+ * @lsm: LSM definition
+ * @enabled: enabled flag
+ */
+static void __init lsm_enabled_set(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 ||
+ lsm->enabled == &lsm_enabled_true ||
+ lsm->enabled == &lsm_enabled_false) {
+ lsm->enabled = enabled ? &lsm_enabled_true : &lsm_enabled_false;
+ } else {
+ *lsm->enabled = enabled;
+ }
+}
+
+/**
+ * lsm_is_enabled - Determine if a LSM is enabled
+ * @lsm: LSM definition
+ */
+static inline bool lsm_is_enabled(struct lsm_info *lsm)
+{
+ return (lsm->enabled ? *lsm->enabled : false);
+}
+
+/**
+ * lsm_order_exists - Determine if a LSM exists in the ordered list
+ * @lsm: LSM definition
+ */
+static bool __init lsm_order_exists(struct lsm_info *lsm)
+{
+ struct lsm_info **check;
+
+ lsm_order_for_each(check) {
+ if (*check == lsm)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * lsm_order_append - Append a LSM to the ordered list
+ * @lsm: LSM definition
+ * @src: source of the addition
+ *
+ * Append @lsm to the enabled LSM array after ensuring that it hasn't been
+ * explicitly disabled, is a duplicate entry, or would run afoul of the
+ * LSM_FLAG_EXCLUSIVE logic.
+ */
+static void __init lsm_order_append(struct lsm_info *lsm, const char *src)
+{
+ /* Ignore duplicate selections. */
+ if (lsm_order_exists(lsm))
+ return;
+
+ /* Skip explicitly disabled LSMs. */
+ if (lsm->enabled && !lsm_is_enabled(lsm)) {
+ lsm_pr_dbg("skip previously disabled LSM %s:%s\n",
+ src, lsm->id->name);
+ return;
+ }
+
+ if (lsm_active_cnt == MAX_LSM_COUNT) {
+ pr_warn("exceeded maximum LSM count on %s:%s\n",
+ src, lsm->id->name);
+ lsm_enabled_set(lsm, false);
+ return;
+ }
+
+ if (lsm->flags & LSM_FLAG_EXCLUSIVE) {
+ if (lsm_exclusive) {
+ lsm_pr_dbg("skip exclusive LSM conflict %s:%s\n",
+ src, lsm->id->name);
+ lsm_enabled_set(lsm, false);
+ return;
+ } else {
+ lsm_pr_dbg("select exclusive LSM %s:%s\n",
+ src, lsm->id->name);
+ lsm_exclusive = lsm;
+ }
+ }
+
+ lsm_enabled_set(lsm, true);
+ lsm_order[lsm_active_cnt] = lsm;
+ lsm_idlist[lsm_active_cnt++] = lsm->id;
+
+ lsm_pr_dbg("enabling LSM %s:%s\n", src, lsm->id->name);
+}
+
+/**
+ * lsm_order_parse - Parse the comma delimited LSM list
+ * @list: LSM list
+ * @src: source of the list
+ */
+static void __init lsm_order_parse(const char *list, const char *src)
+{
+ struct lsm_info *lsm;
+ char *sep, *name, *next;
+
+ /* Handle any Legacy LSM exclusions if one was specified. */
+ if (lsm_order_legacy) {
+ /*
+ * 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.
+ */
+ lsm_for_each_raw(lsm) {
+ if ((lsm->flags & LSM_FLAG_LEGACY_MAJOR) &&
+ strcmp(lsm->id->name, lsm_order_legacy)) {
+ lsm_enabled_set(lsm, false);
+ lsm_pr_dbg("skip legacy LSM conflict %s:%s\n",
+ src, lsm->id->name);
+ }
+ }
+ }
+
+ /* LSM_ORDER_FIRST */
+ lsm_for_each_raw(lsm) {
+ if (lsm->order == LSM_ORDER_FIRST)
+ lsm_order_append(lsm, "first");
+ }
+
+ /* Normal or "mutable" LSMs */
+ sep = kstrdup(list, GFP_KERNEL);
+ next = sep;
+ /* Walk the list, looking for matching LSMs. */
+ while ((name = strsep(&next, ",")) != NULL) {
+ lsm_for_each_raw(lsm) {
+ if (!strcmp(lsm->id->name, name) &&
+ lsm->order == LSM_ORDER_MUTABLE)
+ lsm_order_append(lsm, src);
+ }
+ }
+ kfree(sep);
+
+ /* Legacy LSM if specified. */
+ if (lsm_order_legacy) {
+ lsm_for_each_raw(lsm) {
+ if (!strcmp(lsm->id->name, lsm_order_legacy))
+ lsm_order_append(lsm, src);
+ }
+ }
+
+ /* LSM_ORDER_LAST */
+ lsm_for_each_raw(lsm) {
+ if (lsm->order == LSM_ORDER_LAST)
+ lsm_order_append(lsm, "last");
+ }
+
+ /* Disable all LSMs not previously enabled. */
+ lsm_for_each_raw(lsm) {
+ if (lsm_order_exists(lsm))
+ continue;
+ lsm_enabled_set(lsm, false);
+ lsm_pr_dbg("skip disabled LSM %s:%s\n", src, lsm->id->name);
+ }
+}
+
+/**
+ * lsm_blob_size_update - Update the LSM blob size and offset information
+ * @sz_req: the requested additional blob size
+ * @sz_cur: the existing blob size
+ */
+static void __init lsm_blob_size_update(unsigned int *sz_req,
+ unsigned int *sz_cur)
+{
+ unsigned int offset;
+
+ if (*sz_req == 0)
+ return;
+
+ offset = ALIGN(*sz_cur, sizeof(void *));
+ *sz_cur = offset + *sz_req;
+ *sz_req = offset;
+}
+
+/**
+ * lsm_prepare - Prepare the LSM framework for a new LSM
+ * @lsm: LSM definition
+ */
+static void __init lsm_prepare(struct lsm_info *lsm)
+{
+ struct lsm_blob_sizes *blobs = lsm->blobs;
+
+ if (!blobs)
+ return;
+
+ /* Register the LSM blob sizes. */
+ blobs = lsm->blobs;
+ lsm_blob_size_update(&blobs->lbs_cred, &blob_sizes.lbs_cred);
+ lsm_blob_size_update(&blobs->lbs_file, &blob_sizes.lbs_file);
+ lsm_blob_size_update(&blobs->lbs_ib, &blob_sizes.lbs_ib);
+ /* inode blob gets an rcu_head in addition to LSM blobs. */
+ if (blobs->lbs_inode && blob_sizes.lbs_inode == 0)
+ blob_sizes.lbs_inode = sizeof(struct rcu_head);
+ lsm_blob_size_update(&blobs->lbs_inode, &blob_sizes.lbs_inode);
+ lsm_blob_size_update(&blobs->lbs_ipc, &blob_sizes.lbs_ipc);
+ lsm_blob_size_update(&blobs->lbs_key, &blob_sizes.lbs_key);
+ lsm_blob_size_update(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
+ lsm_blob_size_update(&blobs->lbs_perf_event,
+ &blob_sizes.lbs_perf_event);
+ lsm_blob_size_update(&blobs->lbs_sock, &blob_sizes.lbs_sock);
+ lsm_blob_size_update(&blobs->lbs_superblock,
+ &blob_sizes.lbs_superblock);
+ lsm_blob_size_update(&blobs->lbs_task, &blob_sizes.lbs_task);
+ lsm_blob_size_update(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
+ lsm_blob_size_update(&blobs->lbs_xattr_count,
+ &blob_sizes.lbs_xattr_count);
+ lsm_blob_size_update(&blobs->lbs_bdev, &blob_sizes.lbs_bdev);
+ lsm_blob_size_update(&blobs->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
+ lsm_blob_size_update(&blobs->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
+ lsm_blob_size_update(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
+}
+
+/**
+ * lsm_init_single - Initialize a given LSM
+ * @lsm: LSM definition
+ */
+static void __init lsm_init_single(struct lsm_info *lsm)
+{
+ int ret;
+
+ if (!lsm_is_enabled(lsm))
+ return;
+
+ lsm_pr_dbg("initializing %s\n", lsm->id->name);
+ ret = lsm->init();
+ WARN(ret, "%s failed to initialize: %d\n", lsm->id->name, ret);
+}
+
+/**
+ * lsm_static_call_init - Initialize a LSM's static calls
+ * @hl: LSM hook list
+ */
+static int __init lsm_static_call_init(struct security_hook_list *hl)
+{
+ struct lsm_static_call *scall = hl->scalls;
+ int i;
+
+ for (i = 0; i < MAX_LSM_COUNT; i++) {
+ /* Update the first static call that is not used yet */
+ if (!scall->hl) {
+ __static_call_update(scall->key, scall->trampoline,
+ hl->hook.lsm_func_addr);
+ scall->hl = hl;
+ static_branch_enable(scall->active);
+ return 0;
+ }
+ scall++;
+ }
+
+ return -ENOSPC;
+}
+
+/**
+ * security_add_hooks - Add a LSM's hooks to the LSM framework's hook lists
+ * @hooks: LSM hooks to add
+ * @count: number of hooks to add
+ * @lsmid: identification information for the LSM
+ *
+ * Each LSM has to register its hooks with the LSM framework.
+ */
+void __init security_add_hooks(struct security_hook_list *hooks, int count,
+ const struct lsm_id *lsmid)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ hooks[i].lsmid = lsmid;
+ if (lsm_static_call_init(&hooks[i]))
+ panic("exhausted LSM callback slots with LSM %s\n",
+ lsmid->name);
+ }
+}
+
+/**
+ * early_security_init - Initialize the early LSMs
+ */
+int __init early_security_init(void)
+{
+ struct lsm_info *lsm;
+
+ /* NOTE: lsm_pr_dbg() doesn't work here as lsm_debug is not yet set */
+
+ lsm_early_for_each_raw(lsm) {
+ lsm_enabled_set(lsm, true);
+ lsm_order_append(lsm, "early");
+ lsm_prepare(lsm);
+ lsm_init_single(lsm);
+ lsm_count_early++;
+ }
+
+ return 0;
+}
+
+/**
+ * security_init - Initializes the LSM framework
+ *
+ * This should be called early in the kernel initialization sequence.
+ */
+int __init security_init(void)
+{
+ unsigned int cnt;
+ struct lsm_info **lsm;
+
+ if (lsm_debug) {
+ struct lsm_info *i;
+
+ cnt = 0;
+ lsm_pr("available LSMs: ");
+ lsm_early_for_each_raw(i)
+ lsm_pr_cont("%s%s(E)", (cnt++ ? "," : ""), i->id->name);
+ lsm_for_each_raw(i)
+ lsm_pr_cont("%s%s", (cnt++ ? "," : ""), i->id->name);
+ lsm_pr_cont("\n");
+
+ lsm_pr("built-in LSM config: %s\n", lsm_order_builtin);
+
+ lsm_pr("legacy LSM parameter: %s\n", lsm_order_legacy);
+ lsm_pr("boot LSM parameter: %s\n", lsm_order_cmdline);
+
+ /* see the note about lsm_pr_dbg() in early_security_init() */
+ lsm_early_for_each_raw(i)
+ lsm_pr("enabled LSM early:%s\n", i->id->name);
+ }
+
+ if (lsm_order_cmdline) {
+ if (lsm_order_legacy)
+ lsm_order_legacy = NULL;
+ lsm_order_parse(lsm_order_cmdline, "cmdline");
+ } else
+ lsm_order_parse(lsm_order_builtin, "builtin");
+
+ lsm_order_for_each(lsm)
+ lsm_prepare(*lsm);
+
+ if (lsm_debug) {
+ lsm_pr("blob(cred) size %d\n", blob_sizes.lbs_cred);
+ lsm_pr("blob(file) size %d\n", blob_sizes.lbs_file);
+ lsm_pr("blob(ib) size %d\n", blob_sizes.lbs_ib);
+ lsm_pr("blob(inode) size %d\n", blob_sizes.lbs_inode);
+ lsm_pr("blob(ipc) size %d\n", blob_sizes.lbs_ipc);
+ lsm_pr("blob(key) size %d\n", blob_sizes.lbs_key);
+ lsm_pr("blob(msg_msg)_size %d\n", blob_sizes.lbs_msg_msg);
+ lsm_pr("blob(sock) size %d\n", blob_sizes.lbs_sock);
+ lsm_pr("blob(superblock) size %d\n", blob_sizes.lbs_superblock);
+ lsm_pr("blob(perf_event) size %d\n", blob_sizes.lbs_perf_event);
+ lsm_pr("blob(task) size %d\n", blob_sizes.lbs_task);
+ lsm_pr("blob(tun_dev) size %d\n", blob_sizes.lbs_tun_dev);
+ lsm_pr("blob(xattr) count %d\n", blob_sizes.lbs_xattr_count);
+ lsm_pr("blob(bdev) size %d\n", blob_sizes.lbs_bdev);
+ lsm_pr("blob(bpf_map) size %d\n", blob_sizes.lbs_bpf_map);
+ lsm_pr("blob(bpf_prog) size %d\n", blob_sizes.lbs_bpf_prog);
+ lsm_pr("blob(bpf_token) size %d\n", blob_sizes.lbs_bpf_token);
+ }
+
+ 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);
+
+ if (lsm_cred_alloc((struct cred *)unrcu_pointer(current->cred),
+ GFP_KERNEL))
+ panic("early LSM cred alloc failed\n");
+ if (lsm_task_alloc(current))
+ panic("early LSM task alloc failed\n");
+
+ cnt = 0;
+ lsm_order_for_each(lsm) {
+ /* skip the "early" LSMs as they have already been setup */
+ if (cnt++ < lsm_count_early)
+ continue;
+ lsm_init_single(*lsm);
+ }
+
+ return 0;
+}
+
+/**
+ * security_initcall_pure - Run the LSM pure initcalls
+ */
+static int __init security_initcall_pure(void)
+{
+ int rc_adr, rc_lsm;
+
+ rc_adr = min_addr_init();
+ rc_lsm = lsm_initcall(pure);
+
+ return (rc_adr ? rc_adr : rc_lsm);
+}
+pure_initcall(security_initcall_pure);
+
+/**
+ * security_initcall_early - Run the LSM early initcalls
+ */
+static int __init security_initcall_early(void)
+{
+ return lsm_initcall(early);
+}
+early_initcall(security_initcall_early);
+
+/**
+ * security_initcall_core - Run the LSM core initcalls
+ */
+static int __init security_initcall_core(void)
+{
+ int rc_sfs, rc_lsm;
+
+ rc_sfs = securityfs_init();
+ rc_lsm = lsm_initcall(core);
+
+ return (rc_sfs ? rc_sfs : rc_lsm);
+}
+core_initcall(security_initcall_core);
+
+/**
+ * security_initcall_subsys - Run the LSM subsys initcalls
+ */
+static int __init security_initcall_subsys(void)
+{
+ return lsm_initcall(subsys);
+}
+subsys_initcall(security_initcall_subsys);
+
+/**
+ * security_initcall_fs - Run the LSM fs initcalls
+ */
+static int __init security_initcall_fs(void)
+{
+ return lsm_initcall(fs);
+}
+fs_initcall(security_initcall_fs);
+
+/**
+ * security_initcall_device - Run the LSM device initcalls
+ */
+static int __init security_initcall_device(void)
+{
+ return lsm_initcall(device);
+}
+device_initcall(security_initcall_device);
+
+/**
+ * security_initcall_late - Run the LSM late initcalls
+ */
+static int __init security_initcall_late(void)
+{
+ int rc;
+
+ rc = lsm_initcall(late);
+ lsm_pr_dbg("all enabled LSMs fully activated\n");
+ call_blocking_lsm_notifier(LSM_STARTED_ALL, NULL);
+
+ return rc;
+}
+late_initcall(security_initcall_late);
diff --git a/security/lsm_notifier.c b/security/lsm_notifier.c
new file mode 100644
index 000000000000..c92fad5d57d4
--- /dev/null
+++ b/security/lsm_notifier.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LSM notifier functions
+ *
+ */
+
+#include <linux/notifier.h>
+#include <linux/security.h>
+
+static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
+
+int call_blocking_lsm_notifier(enum lsm_event event, void *data)
+{
+ return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
+ event, data);
+}
+EXPORT_SYMBOL(call_blocking_lsm_notifier);
+
+int register_blocking_lsm_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
+ nb);
+}
+EXPORT_SYMBOL(register_blocking_lsm_notifier);
+
+int unregister_blocking_lsm_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
+ nb);
+}
+EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c
index 8440948a690c..5648b1f0ce9c 100644
--- a/security/lsm_syscalls.c
+++ b/security/lsm_syscalls.c
@@ -17,6 +17,8 @@
#include <linux/lsm_hooks.h>
#include <uapi/linux/lsm.h>
+#include "lsm.h"
+
/**
* lsm_name_to_attr - map an LSM attribute name to its ID
* @name: name of the attribute
diff --git a/security/min_addr.c b/security/min_addr.c
index c55bb84b8632..0fde5ec9abc8 100644
--- a/security/min_addr.c
+++ b/security/min_addr.c
@@ -5,6 +5,8 @@
#include <linux/sysctl.h>
#include <linux/minmax.h>
+#include "lsm.h"
+
/* amount of vm to protect from userspace access by both DAC and the LSM*/
unsigned long mmap_min_addr;
/* amount of vm to protect from userspace using CAP_SYS_RAWIO (DAC) */
@@ -52,11 +54,10 @@ static const struct ctl_table min_addr_sysctl_table[] = {
},
};
-static int __init init_mmap_min_addr(void)
+int __init min_addr_init(void)
{
register_sysctl_init("vm", min_addr_sysctl_table);
update_mmap_min_addr();
return 0;
}
-pure_initcall(init_mmap_min_addr);
diff --git a/security/safesetid/lsm.c b/security/safesetid/lsm.c
index 1ba564f097f5..d5fb949050dd 100644
--- a/security/safesetid/lsm.c
+++ b/security/safesetid/lsm.c
@@ -287,6 +287,7 @@ static int __init safesetid_security_init(void)
}
DEFINE_LSM(safesetid_security_init) = {
+ .id = &safesetid_lsmid,
.init = safesetid_security_init,
- .name = "safesetid",
+ .initcall_fs = safesetid_init_securityfs,
};
diff --git a/security/safesetid/lsm.h b/security/safesetid/lsm.h
index d346f4849cea..bf5172e2c3f7 100644
--- a/security/safesetid/lsm.h
+++ b/security/safesetid/lsm.h
@@ -70,4 +70,6 @@ enum sid_policy_type _setid_policy_lookup(struct setid_ruleset *policy,
extern struct setid_ruleset __rcu *safesetid_setuid_rules;
extern struct setid_ruleset __rcu *safesetid_setgid_rules;
+int safesetid_init_securityfs(void);
+
#endif /* _SAFESETID_H */
diff --git a/security/safesetid/securityfs.c b/security/safesetid/securityfs.c
index 8e1ffd70b18a..ece259f75b0d 100644
--- a/security/safesetid/securityfs.c
+++ b/security/safesetid/securityfs.c
@@ -308,7 +308,7 @@ static const struct file_operations safesetid_gid_file_fops = {
.write = safesetid_gid_file_write,
};
-static int __init safesetid_init_securityfs(void)
+int __init safesetid_init_securityfs(void)
{
int ret;
struct dentry *policy_dir;
@@ -345,4 +345,3 @@ error:
securityfs_remove(policy_dir);
return ret;
}
-fs_initcall(safesetid_init_securityfs);
diff --git a/security/security.c b/security/security.c
index 4d3c03a4524c..31a688650601 100644
--- a/security/security.c
+++ b/security/security.c
@@ -32,24 +32,7 @@
#include <net/flow.h>
#include <net/sock.h>
-#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
-
-/*
- * Identifier for the LSM static calls.
- * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
- * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
- */
-#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
-
-/*
- * Call the macro M for each LSM hook MAX_LSM_COUNT times.
- */
-#define LSM_LOOP_UNROLL(M, ...) \
-do { \
- UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
-} while (0)
-
-#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
+#include "lsm.h"
/*
* These are descriptions of the reasons that can be passed to the
@@ -90,23 +73,34 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
[LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
};
-static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
+bool lsm_debug __ro_after_init;
-static struct kmem_cache *lsm_file_cache;
-static struct kmem_cache *lsm_inode_cache;
+unsigned int lsm_active_cnt __ro_after_init;
+const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
-char *lsm_names;
-static struct lsm_blob_sizes blob_sizes __ro_after_init;
+struct lsm_blob_sizes blob_sizes;
-/* Boot-time LSM user choice */
-static __initdata const char *chosen_lsm_order;
-static __initdata const char *chosen_major_lsm;
+struct kmem_cache *lsm_file_cache;
+struct kmem_cache *lsm_inode_cache;
-static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
+#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
-/* Ordered list of LSMs to initialize. */
-static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
-static __initdata struct lsm_info *exclusive;
+/*
+ * Identifier for the LSM static calls.
+ * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
+ * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
+ */
+#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
+
+/*
+ * Call the macro M for each LSM hook MAX_LSM_COUNT times.
+ */
+#define LSM_LOOP_UNROLL(M, ...) \
+do { \
+ UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
+} while (0)
+
+#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
#ifdef CONFIG_HAVE_STATIC_CALL
#define LSM_HOOK_TRAMP(NAME, NUM) \
@@ -157,518 +151,26 @@ struct lsm_static_calls_table
#undef INIT_LSM_STATIC_CALL
};
-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)
-{
- if (!lsm->enabled)
- return false;
-
- 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 == MAX_LSM_COUNT, "%s: out of LSM static calls!?\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 ordered: %s (%s)\n", from, lsm->name,
- is_enabled(lsm) ? "enabled" : "disabled");
-}
-
-/* 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)
- return;
-
- offset = ALIGN(*lbs, sizeof(void *));
- *lbs = offset + *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);
- lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
- /*
- * The inode blob gets an rcu_head in addition to
- * what the modules might need.
- */
- 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_key, &blob_sizes.lbs_key);
- lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
- lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
- lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
- lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
- lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
- lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
- lsm_set_blob_size(&needed->lbs_xattr_count,
- &blob_sizes.lbs_xattr_count);
- lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
- lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
- lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
- lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
-}
-
-/* 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);
- }
-}
-
-/*
- * Current index to use while initializing the lsm id list.
- */
-u32 lsm_active_cnt __ro_after_init;
-const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
-
-/* Populate ordered LSMs list from comma-separated LSM name list. */
-static void __init ordered_lsm_parse(const char *order, const char *origin)
-{
- 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 (only one legacy major LSM)\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 (strcmp(lsm->name, name) == 0) {
- if (lsm->order == LSM_ORDER_MUTABLE)
- append_ordered_lsm(lsm, origin);
- found = true;
- }
- }
-
- if (!found)
- init_debug("%s ignored: %s (not built into kernel)\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=");
- }
- }
-
- /* LSM_ORDER_LAST is always last. */
- for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
- if (lsm->order == LSM_ORDER_LAST)
- append_ordered_lsm(lsm, " last");
- }
-
- /* 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 skipped: %s (not in requested order)\n",
- origin, lsm->name);
- }
-
- kfree(sep);
-}
-
-static void __init lsm_static_call_init(struct security_hook_list *hl)
-{
- struct lsm_static_call *scall = hl->scalls;
- int i;
-
- for (i = 0; i < MAX_LSM_COUNT; i++) {
- /* Update the first static call that is not used yet */
- if (!scall->hl) {
- __static_call_update(scall->key, scall->trampoline,
- hl->hook.lsm_func_addr);
- scall->hl = hl;
- static_branch_enable(scall->active);
- return;
- }
- scall++;
- }
- panic("%s - Ran out of static slots.\n", __func__);
-}
-
-static void __init lsm_early_cred(struct cred *cred);
-static void __init lsm_early_task(struct task_struct *task);
-
-static int lsm_append(const char *new, char **result);
-
-static void __init report_lsm_order(void)
-{
- struct lsm_info **lsm, *early;
- int first = 0;
-
- pr_info("initializing lsm=");
-
- /* Report each enabled LSM name, comma separated. */
- for (early = __start_early_lsm_info;
- early < __end_early_lsm_info; early++)
- if (is_enabled(early))
- pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
- for (lsm = ordered_lsms; *lsm; lsm++)
- if (is_enabled(*lsm))
- pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
-
- pr_cont("\n");
-}
-
-static void __init ordered_lsm_init(void)
-{
- struct lsm_info **lsm;
-
- if (chosen_lsm_order) {
- if (chosen_major_lsm) {
- pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
- chosen_major_lsm, chosen_lsm_order);
- 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);
-
- report_lsm_order();
-
- init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
- init_debug("file blob size = %d\n", blob_sizes.lbs_file);
- init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
- init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
- init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
-#ifdef CONFIG_KEYS
- init_debug("key blob size = %d\n", blob_sizes.lbs_key);
-#endif /* CONFIG_KEYS */
- init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
- init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
- init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
- init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
- init_debug("task blob size = %d\n", blob_sizes.lbs_task);
- init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
- init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
- init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);
- init_debug("bpf map blob size = %d\n", blob_sizes.lbs_bpf_map);
- init_debug("bpf prog blob size = %d\n", blob_sizes.lbs_bpf_prog);
- init_debug("bpf token blob size = %d\n", blob_sizes.lbs_bpf_token);
-
- /*
- * 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);
-}
-
-int __init early_security_init(void)
-{
- struct lsm_info *lsm;
-
- 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
+ * lsm_file_alloc - allocate a composite file blob
+ * @file: the file that needs a blob
*
- * This should be called early in the kernel initialization sequence.
- */
-int __init security_init(void)
-{
- struct lsm_info *lsm;
-
- init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*");
- init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order);
- init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*");
-
- /*
- * Append the names of the early LSM modules now that kmalloc() is
- * available
- */
- for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
- init_debug(" early started: %s (%s)\n", lsm->name,
- is_enabled(lsm) ? "enabled" : "disabled");
- if (lsm->enabled)
- lsm_append(lsm->name, &lsm_names);
- }
-
- /* Load LSMs in specified order. */
- ordered_lsm_init();
-
- return 0;
-}
-
-/* Save user chosen LSM */
-static int __init choose_major_lsm(char *str)
-{
- chosen_major_lsm = str;
- return 1;
-}
-__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)
-{
- const char *last;
-
- if (WARN_ON(!list || !lsm))
- return false;
- last = strrchr(list, ',');
- if (last)
- /* Pass the comma, strcmp() will check for '\0' */
- last++;
- else
- last = list;
- return !strcmp(last, lsm);
-}
-
-static int lsm_append(const char *new, char **result)
-{
- char *cp;
-
- if (*result == NULL) {
- *result = kstrdup(new, GFP_KERNEL);
- if (*result == NULL)
- return -ENOMEM;
- } else {
- /* Check if it is the last registered name */
- if (match_last_lsm(*result, new))
- return 0;
- cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
- if (cp == NULL)
- return -ENOMEM;
- kfree(*result);
- *result = cp;
- }
- return 0;
-}
-
-/**
- * security_add_hooks - Add a modules hooks to the hook lists.
- * @hooks: the hooks to add
- * @count: the number of hooks to add
- * @lsmid: the identification information for the security module
+ * Allocate the file blob for all the modules
*
- * Each LSM has to register its hooks with the infrastructure.
+ * Returns 0, or -ENOMEM if memory can't be allocated.
*/
-void __init security_add_hooks(struct security_hook_list *hooks, int count,
- const struct lsm_id *lsmid)
+static int lsm_file_alloc(struct file *file)
{
- int i;
-
- /*
- * A security module may call security_add_hooks() more
- * than once during initialization, and LSM initialization
- * is serialized. Landlock is one such case.
- * Look at the previous entry, if there is one, for duplication.
- */
- if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
- if (lsm_active_cnt >= MAX_LSM_COUNT)
- panic("%s Too many LSMs registered.\n", __func__);
- lsm_idlist[lsm_active_cnt++] = lsmid;
- }
-
- for (i = 0; i < count; i++) {
- hooks[i].lsmid = lsmid;
- lsm_static_call_init(&hooks[i]);
- }
-
- /*
- * 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(lsmid->name, &lsm_names) < 0)
- panic("%s - Cannot get early memory.\n", __func__);
+ if (!lsm_file_cache) {
+ file->f_security = NULL;
+ return 0;
}
-}
-
-int call_blocking_lsm_notifier(enum lsm_event event, void *data)
-{
- return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
- event, data);
-}
-EXPORT_SYMBOL(call_blocking_lsm_notifier);
-
-int register_blocking_lsm_notifier(struct notifier_block *nb)
-{
- return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
- nb);
-}
-EXPORT_SYMBOL(register_blocking_lsm_notifier);
-int unregister_blocking_lsm_notifier(struct notifier_block *nb)
-{
- return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
- nb);
+ file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
+ if (file->f_security == NULL)
+ return -ENOMEM;
+ return 0;
}
-EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
/**
* lsm_blob_alloc - allocate a composite blob
@@ -702,47 +204,12 @@ static int lsm_blob_alloc(void **dest, size_t size, gfp_t gfp)
*
* Returns 0, or -ENOMEM if memory can't be allocated.
*/
-static int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
+int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
{
return lsm_blob_alloc(&cred->security, blob_sizes.lbs_cred, gfp);
}
/**
- * lsm_early_cred - during initialization allocate a composite cred blob
- * @cred: the cred that needs a blob
- *
- * 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
* @gfp: allocation flags
@@ -772,7 +239,7 @@ static int lsm_inode_alloc(struct inode *inode, gfp_t gfp)
*
* Returns 0, or -ENOMEM if memory can't be allocated.
*/
-static int lsm_task_alloc(struct task_struct *task)
+int lsm_task_alloc(struct task_struct *task)
{
return lsm_blob_alloc(&task->security, blob_sizes.lbs_task, GFP_KERNEL);
}
@@ -875,20 +342,6 @@ static int lsm_bpf_token_alloc(struct bpf_token *token)
#endif /* CONFIG_BPF_SYSCALL */
/**
- * 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__);
-}
-
-/**
* lsm_superblock_alloc - allocate a composite superblock blob
* @sb: the superblock that needs a blob
*
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
index 61abc1e094a8..5588c4d573f6 100644
--- a/security/selinux/Kconfig
+++ b/security/selinux/Kconfig
@@ -69,6 +69,17 @@ config SECURITY_SELINUX_SID2STR_CACHE_SIZE
If unsure, keep the default value.
+config SECURITY_SELINUX_AVC_HASH_BITS
+ int "SELinux avc hashtable size"
+ depends on SECURITY_SELINUX
+ range 9 14
+ default 9
+ help
+ This option sets the number of buckets used in the AVC hash table
+ to 2^SECURITY_SELINUX_AVC_HASH_BITS. A higher value helps maintain
+ shorter chain lengths especially when expanding AVC nodes via
+ /sys/fs/selinux/avc/cache_threshold.
+
config SECURITY_SELINUX_DEBUG
bool "SELinux kernel debugging support"
depends on SECURITY_SELINUX
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index 66e56e9011df..72d3baf7900c 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -15,7 +15,7 @@ ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
ccflags-$(CONFIG_SECURITY_SELINUX_DEBUG) += -DDEBUG
selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \
- netnode.o netport.o status.o \
+ netnode.o netport.o status.o initcalls.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/context.o
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 430b0e23ee00..8f77b9a732e1 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -30,13 +30,14 @@
#include "avc.h"
#include "avc_ss.h"
#include "classmap.h"
+#include "hash.h"
#define CREATE_TRACE_POINTS
#include <trace/events/avc.h>
-#define AVC_CACHE_SLOTS 512
-#define AVC_DEF_CACHE_THRESHOLD 512
-#define AVC_CACHE_RECLAIM 16
+#define AVC_CACHE_SLOTS (1 << CONFIG_SECURITY_SELINUX_AVC_HASH_BITS)
+#define AVC_DEF_CACHE_THRESHOLD AVC_CACHE_SLOTS
+#define AVC_CACHE_RECLAIM 16
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
#define avc_cache_stats_incr(field) this_cpu_inc(avc_cache_stats.field)
@@ -124,7 +125,7 @@ static struct kmem_cache *avc_xperms_cachep __ro_after_init;
static inline u32 avc_hash(u32 ssid, u32 tsid, u16 tclass)
{
- return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1);
+ return av_hash(ssid, tsid, (u32)tclass, (u32)(AVC_CACHE_SLOTS - 1));
}
/**
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e713291db873..d053ce562370 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -93,7 +93,9 @@
#include <linux/fanotify.h>
#include <linux/io_uring/cmd.h>
#include <uapi/linux/lsm.h>
+#include <linux/memfd.h>
+#include "initcalls.h"
#include "avc.h"
#include "objsec.h"
#include "netif.h"
@@ -2319,6 +2321,10 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
new_crsec = selinux_cred(bprm->cred);
isec = inode_security(inode);
+ if (WARN_ON(isec->sclass != SECCLASS_FILE &&
+ isec->sclass != SECCLASS_MEMFD_FILE))
+ return -EACCES;
+
/* Default to the current task SID. */
new_crsec->sid = old_crsec->sid;
new_crsec->osid = old_crsec->sid;
@@ -2371,8 +2377,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
ad.u.file = bprm->file;
if (new_crsec->sid == old_crsec->sid) {
- rc = avc_has_perm(old_crsec->sid, isec->sid,
- SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
+ rc = avc_has_perm(old_crsec->sid, isec->sid, isec->sclass,
+ FILE__EXECUTE_NO_TRANS, &ad);
if (rc)
return rc;
} else {
@@ -2382,8 +2388,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
if (rc)
return rc;
- rc = avc_has_perm(new_crsec->sid, isec->sid,
- SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
+ rc = avc_has_perm(new_crsec->sid, isec->sid, isec->sclass,
+ FILE__ENTRYPOINT, &ad);
if (rc)
return rc;
@@ -2978,10 +2984,18 @@ static int selinux_inode_init_security_anon(struct inode *inode,
struct common_audit_data ad;
struct inode_security_struct *isec;
int rc;
+ bool is_memfd = false;
if (unlikely(!selinux_initialized()))
return 0;
+ if (name != NULL && name->name != NULL &&
+ !strcmp(name->name, MEMFD_ANON_NAME)) {
+ if (!selinux_policycap_memfd_class())
+ return 0;
+ is_memfd = true;
+ }
+
isec = selinux_inode(inode);
/*
@@ -3001,7 +3015,10 @@ static int selinux_inode_init_security_anon(struct inode *inode,
isec->sclass = context_isec->sclass;
isec->sid = context_isec->sid;
} else {
- isec->sclass = SECCLASS_ANON_INODE;
+ if (is_memfd)
+ isec->sclass = SECCLASS_MEMFD_FILE;
+ else
+ isec->sclass = SECCLASS_ANON_INODE;
rc = security_transition_sid(
sid, sid,
isec->sclass, name, &isec->sid);
@@ -4279,7 +4296,7 @@ static int selinux_kernel_read_file(struct file *file,
{
int rc = 0;
- BUILD_BUG_ON_MSG(READING_MAX_ID > 7,
+ BUILD_BUG_ON_MSG(READING_MAX_ID > 8,
"New kernel_read_file_id introduced; update SELinux!");
switch (id) {
@@ -4287,6 +4304,7 @@ static int selinux_kernel_read_file(struct file *file,
rc = selinux_kernel_load_from_file(file, SYSTEM__FIRMWARE_LOAD);
break;
case READING_MODULE:
+ case READING_MODULE_COMPRESSED:
rc = selinux_kernel_load_from_file(file, SYSTEM__MODULE_LOAD);
break;
case READING_KEXEC_IMAGE:
@@ -4315,7 +4333,7 @@ static int selinux_kernel_load_data(enum kernel_load_data_id id, bool contents)
{
int rc = 0;
- BUILD_BUG_ON_MSG(LOADING_MAX_ID > 7,
+ BUILD_BUG_ON_MSG(LOADING_MAX_ID > 8,
"New kernel_load_data_id introduced; update SELinux!");
switch (id) {
@@ -7617,6 +7635,10 @@ static __init int selinux_init(void)
if (avc_add_callback(selinux_lsm_notifier_avc_callback, AVC_CALLBACK_RESET))
panic("SELinux: Unable to register AVC LSM notifier callback\n");
+ if (avc_add_callback(selinux_audit_rule_avc_callback,
+ AVC_CALLBACK_RESET))
+ panic("SELinux: Unable to register AVC audit callback\n");
+
if (selinux_enforcing_boot)
pr_debug("SELinux: Starting in enforcing mode\n");
else
@@ -7644,11 +7666,12 @@ void selinux_complete_init(void)
/* SELinux requires early initialization in order to label
all processes and objects when they are created. */
DEFINE_LSM(selinux) = {
- .name = "selinux",
+ .id = &selinux_lsmid,
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
.enabled = &selinux_enabled_boot,
.blobs = &selinux_blob_sizes,
.init = selinux_init,
+ .initcall_device = selinux_initcall,
};
#if defined(CONFIG_NETFILTER)
@@ -7710,7 +7733,7 @@ static struct pernet_operations selinux_net_ops = {
.exit = selinux_nf_unregister,
};
-static int __init selinux_nf_ip_init(void)
+int __init selinux_nf_ip_init(void)
{
int err;
@@ -7725,5 +7748,4 @@ static int __init selinux_nf_ip_init(void)
return 0;
}
-__initcall(selinux_nf_ip_init);
#endif /* CONFIG_NETFILTER */
diff --git a/security/selinux/ibpkey.c b/security/selinux/ibpkey.c
index 470481cfe0e8..ea1d9b2c7d2b 100644
--- a/security/selinux/ibpkey.c
+++ b/security/selinux/ibpkey.c
@@ -23,6 +23,7 @@
#include <linux/list.h>
#include <linux/spinlock.h>
+#include "initcalls.h"
#include "ibpkey.h"
#include "objsec.h"
@@ -218,7 +219,7 @@ void sel_ib_pkey_flush(void)
spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
}
-static __init int sel_ib_pkey_init(void)
+int __init sel_ib_pkey_init(void)
{
int iter;
@@ -232,5 +233,3 @@ static __init int sel_ib_pkey_init(void)
return 0;
}
-
-subsys_initcall(sel_ib_pkey_init);
diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h
index d5b0425055e4..85a531ac737b 100644
--- a/security/selinux/include/audit.h
+++ b/security/selinux/include/audit.h
@@ -16,6 +16,15 @@
#include <linux/types.h>
/**
+ * selinux_audit_rule_avc_callback - update the audit LSM rules on AVC events.
+ * @event: the AVC event
+ *
+ * Update any audit LSM rules based on the AVC event specified in @event.
+ * Returns 0 on success, negative values otherwise.
+ */
+int selinux_audit_rule_avc_callback(u32 event);
+
+/**
* selinux_audit_rule_init - alloc/init an selinux audit rule structure.
* @field: the field this rule refers to
* @op: the operator the rule uses
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 5665aa5e7853..3ec85142771f 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -179,6 +179,8 @@ const struct security_class_mapping secclass_map[] = {
{ "anon_inode", { COMMON_FILE_PERMS, NULL } },
{ "io_uring", { "override_creds", "sqpoll", "cmd", "allowed", NULL } },
{ "user_namespace", { "create", NULL } },
+ { "memfd_file",
+ { COMMON_FILE_PERMS, "execute_no_trans", "entrypoint", NULL } },
/* last one */ { NULL, {} }
};
diff --git a/security/selinux/include/hash.h b/security/selinux/include/hash.h
new file mode 100644
index 000000000000..18956dbef8ff
--- /dev/null
+++ b/security/selinux/include/hash.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _SELINUX_HASH_H_
+#define _SELINUX_HASH_H_
+
+/*
+ * Based on MurmurHash3, written by Austin Appleby and placed in the
+ * public domain.
+ */
+static inline u32 av_hash(u32 key1, u32 key2, u32 key3, u32 mask)
+{
+ static const u32 c1 = 0xcc9e2d51;
+ static const u32 c2 = 0x1b873593;
+ static const u32 r1 = 15;
+ static const u32 r2 = 13;
+ static const u32 m = 5;
+ static const u32 n = 0xe6546b64;
+
+ u32 hash = 0;
+
+#define mix(input) \
+ do { \
+ u32 v = input; \
+ v *= c1; \
+ v = (v << r1) | (v >> (32 - r1)); \
+ v *= c2; \
+ hash ^= v; \
+ hash = (hash << r2) | (hash >> (32 - r2)); \
+ hash = hash * m + n; \
+ } while (0)
+
+ mix(key1);
+ mix(key2);
+ mix(key3);
+
+#undef mix
+
+ hash ^= hash >> 16;
+ hash *= 0x85ebca6b;
+ hash ^= hash >> 13;
+ hash *= 0xc2b2ae35;
+ hash ^= hash >> 16;
+
+ return hash & mask;
+}
+
+#endif /* _SELINUX_HASH_H_ */
diff --git a/security/selinux/include/initcalls.h b/security/selinux/include/initcalls.h
new file mode 100644
index 000000000000..6674cf489473
--- /dev/null
+++ b/security/selinux/include/initcalls.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SELinux initcalls
+ */
+
+#ifndef _SELINUX_INITCALLS_H
+#define _SELINUX_INITCALLS_H
+
+int init_sel_fs(void);
+int sel_netport_init(void);
+int sel_netnode_init(void);
+int sel_netif_init(void);
+int sel_netlink_init(void);
+int sel_ib_pkey_init(void);
+int selinux_nf_ip_init(void);
+
+int selinux_initcall(void);
+
+#endif
diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h
index 135a969f873c..231d02227e59 100644
--- a/security/selinux/include/policycap.h
+++ b/security/selinux/include/policycap.h
@@ -18,6 +18,7 @@ enum {
POLICYDB_CAP_NETIF_WILDCARD,
POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
POLICYDB_CAP_FUNCTIONFS_SECLABEL,
+ POLICYDB_CAP_MEMFD_CLASS,
__POLICYDB_CAP_MAX
};
#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h
index ff8882887651..454dab37bda3 100644
--- a/security/selinux/include/policycap_names.h
+++ b/security/selinux/include/policycap_names.h
@@ -21,6 +21,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
"netif_wildcard",
"genfs_seclabel_wildcard",
"functionfs_seclabel",
+ "memfd_class",
};
/* clang-format on */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 0f954a40d3fc..5d1dad8058b1 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -209,6 +209,11 @@ static inline bool selinux_policycap_functionfs_seclabel(void)
selinux_state.policycap[POLICYDB_CAP_FUNCTIONFS_SECLABEL]);
}
+static inline bool selinux_policycap_memfd_class(void)
+{
+ return READ_ONCE(selinux_state.policycap[POLICYDB_CAP_MEMFD_CLASS]);
+}
+
struct selinux_policy_convert_data;
struct selinux_load_state {
diff --git a/security/selinux/initcalls.c b/security/selinux/initcalls.c
new file mode 100644
index 000000000000..f6716a1d38c1
--- /dev/null
+++ b/security/selinux/initcalls.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SELinux initcalls
+ */
+
+#include <linux/init.h>
+
+#include "initcalls.h"
+
+/**
+ * selinux_initcall - Perform the SELinux initcalls
+ *
+ * Used as a device initcall in the SELinux LSM definition.
+ */
+int __init selinux_initcall(void)
+{
+ int rc = 0, rc_tmp = 0;
+
+ rc_tmp = init_sel_fs();
+ if (!rc && rc_tmp)
+ rc = rc_tmp;
+
+ rc_tmp = sel_netport_init();
+ if (!rc && rc_tmp)
+ rc = rc_tmp;
+
+ rc_tmp = sel_netnode_init();
+ if (!rc && rc_tmp)
+ rc = rc_tmp;
+
+ rc_tmp = sel_netif_init();
+ if (!rc && rc_tmp)
+ rc = rc_tmp;
+
+ rc_tmp = sel_netlink_init();
+ if (!rc && rc_tmp)
+ rc = rc_tmp;
+
+#if defined(CONFIG_SECURITY_INFINIBAND)
+ rc_tmp = sel_ib_pkey_init();
+ if (!rc && rc_tmp)
+ rc = rc_tmp;
+#endif
+
+#if defined(CONFIG_NETFILTER)
+ rc_tmp = selinux_nf_ip_init();
+ if (!rc && rc_tmp)
+ rc = rc_tmp;
+#endif
+
+ return rc;
+}
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
index 78afbecdbe57..e24b2cba28ea 100644
--- a/security/selinux/netif.c
+++ b/security/selinux/netif.c
@@ -22,6 +22,7 @@
#include <linux/rcupdate.h>
#include <net/net_namespace.h>
+#include "initcalls.h"
#include "security.h"
#include "objsec.h"
#include "netif.h"
@@ -265,7 +266,7 @@ static struct notifier_block sel_netif_netdev_notifier = {
.notifier_call = sel_netif_netdev_notifier_handler,
};
-static __init int sel_netif_init(void)
+int __init sel_netif_init(void)
{
int i;
@@ -280,5 +281,3 @@ static __init int sel_netif_init(void)
return 0;
}
-__initcall(sel_netif_init);
-
diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c
index 1760aee712fd..eb40e4603475 100644
--- a/security/selinux/netlink.c
+++ b/security/selinux/netlink.c
@@ -17,6 +17,7 @@
#include <net/net_namespace.h>
#include <net/netlink.h>
+#include "initcalls.h"
#include "security.h"
static struct sock *selnl __ro_after_init;
@@ -105,7 +106,7 @@ void selnl_notify_policyload(u32 seqno)
selnl_notify(SELNL_MSG_POLICYLOAD, &seqno);
}
-static int __init selnl_init(void)
+int __init sel_netlink_init(void)
{
struct netlink_kernel_cfg cfg = {
.groups = SELNLGRP_MAX,
@@ -117,5 +118,3 @@ static int __init selnl_init(void)
panic("SELinux: Cannot create netlink socket.");
return 0;
}
-
-__initcall(selnl_init);
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index 5d0ed08d46e5..9b3da5ce8d39 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -30,6 +30,7 @@
#include <net/ip.h>
#include <net/ipv6.h>
+#include "initcalls.h"
#include "netnode.h"
#include "objsec.h"
@@ -290,7 +291,7 @@ void sel_netnode_flush(void)
spin_unlock_bh(&sel_netnode_lock);
}
-static __init int sel_netnode_init(void)
+int __init sel_netnode_init(void)
{
int iter;
@@ -304,5 +305,3 @@ static __init int sel_netnode_init(void)
return 0;
}
-
-__initcall(sel_netnode_init);
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index 6fd7da4b3576..9e62f7285e81 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -29,6 +29,7 @@
#include <net/ip.h>
#include <net/ipv6.h>
+#include "initcalls.h"
#include "netport.h"
#include "objsec.h"
@@ -218,7 +219,7 @@ void sel_netport_flush(void)
spin_unlock_bh(&sel_netport_lock);
}
-static __init int sel_netport_init(void)
+int __init sel_netport_init(void)
{
int iter;
@@ -232,5 +233,3 @@ static __init int sel_netport_init(void)
return 0;
}
-
-__initcall(sel_netport_init);
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 404e08bf60ba..d98fbc4d068f 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -35,6 +35,7 @@
/* selinuxfs pseudo filesystem for exporting the security policy API.
Based on the proc code and the fs/nfsd/nfsctl.c code. */
+#include "initcalls.h"
#include "flask.h"
#include "avc.h"
#include "avc_ss.h"
@@ -2133,7 +2134,7 @@ static struct file_system_type sel_fs_type = {
struct path selinux_null __ro_after_init;
-static int __init init_sel_fs(void)
+int __init init_sel_fs(void)
{
struct qstr null_name = QSTR_INIT(NULL_FILE_NAME,
sizeof(NULL_FILE_NAME)-1);
@@ -2177,5 +2178,3 @@ static int __init init_sel_fs(void)
return err;
}
-
-__initcall(init_sel_fs);
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
index c2c31521cace..d12ca337e649 100644
--- a/security/selinux/ss/avtab.c
+++ b/security/selinux/ss/avtab.c
@@ -20,48 +20,15 @@
#include <linux/errno.h>
#include "avtab.h"
#include "policydb.h"
+#include "hash.h"
static struct kmem_cache *avtab_node_cachep __ro_after_init;
static struct kmem_cache *avtab_xperms_cachep __ro_after_init;
-/* Based on MurmurHash3, written by Austin Appleby and placed in the
- * public domain.
- */
static inline u32 avtab_hash(const struct avtab_key *keyp, u32 mask)
{
- static const u32 c1 = 0xcc9e2d51;
- static const u32 c2 = 0x1b873593;
- static const u32 r1 = 15;
- static const u32 r2 = 13;
- static const u32 m = 5;
- static const u32 n = 0xe6546b64;
-
- u32 hash = 0;
-
-#define mix(input) \
- do { \
- u32 v = input; \
- v *= c1; \
- v = (v << r1) | (v >> (32 - r1)); \
- v *= c2; \
- hash ^= v; \
- hash = (hash << r2) | (hash >> (32 - r2)); \
- hash = hash * m + n; \
- } while (0)
-
- mix(keyp->target_class);
- mix(keyp->target_type);
- mix(keyp->source_type);
-
-#undef mix
-
- hash ^= hash >> 16;
- hash *= 0x85ebca6b;
- hash ^= hash >> 13;
- hash *= 0xc2b2ae35;
- hash ^= hash >> 16;
-
- return hash & mask;
+ return av_hash((u32)keyp->target_class, (u32)keyp->target_type,
+ (u32)keyp->source_type, mask);
}
static struct avtab_node *avtab_insert_node(struct avtab *h,
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 713130bd43c4..13fc712d5923 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -3570,6 +3570,13 @@ struct selinux_audit_rule {
struct context au_ctxt;
};
+int selinux_audit_rule_avc_callback(u32 event)
+{
+ if (event == AVC_CALLBACK_RESET)
+ return audit_update_lsm_rules();
+ return 0;
+}
+
void selinux_audit_rule_free(void *vrule)
{
struct selinux_audit_rule *rule = vrule;
@@ -3820,25 +3827,6 @@ out:
return match;
}
-static int aurule_avc_callback(u32 event)
-{
- if (event == AVC_CALLBACK_RESET)
- return audit_update_lsm_rules();
- return 0;
-}
-
-static int __init aurule_init(void)
-{
- int err;
-
- err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET);
- if (err)
- panic("avc_add_callback() failed, error %d\n", err);
-
- return err;
-}
-__initcall(aurule_init);
-
#ifdef CONFIG_NETLABEL
/**
* security_netlbl_cache_add - Add an entry to the NetLabel cache
diff --git a/security/smack/smack.h b/security/smack/smack.h
index bf6a6ed3946c..9b9eb262fe33 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -276,6 +276,20 @@ struct smk_audit_info {
};
/*
+ * Initialization
+ */
+#if defined(CONFIG_SECURITY_SMACK_NETFILTER)
+int smack_nf_ip_init(void);
+#else
+static inline int smack_nf_ip_init(void)
+{
+ return 0;
+}
+#endif
+int init_smk_fs(void);
+int smack_initcall(void);
+
+/*
* These functions are in smack_access.c
*/
int smk_access_entry(char *, char *, struct list_head *);
@@ -286,9 +300,12 @@ int smk_tskacc(struct task_smack *, struct smack_known *,
int smk_curacc(struct smack_known *, u32, struct smk_audit_info *);
int smack_str_from_perm(char *string, int access);
struct smack_known *smack_from_secid(const u32);
+int smk_parse_label_len(const char *string, int len);
char *smk_parse_smack(const char *string, int len);
int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int);
struct smack_known *smk_import_entry(const char *, int);
+struct smack_known *smk_import_valid_label(const char *label, int label_len,
+ gfp_t gfp);
void smk_insert_entry(struct smack_known *skp);
struct smack_known *smk_find_entry(const char *);
bool smack_privileged(int cap);
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 2e4a0cb22782..fc507dcc7ea5 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -443,19 +443,19 @@ struct smack_known *smk_find_entry(const char *string)
}
/**
- * smk_parse_smack - parse smack label from a text string
- * @string: a text string that might contain a Smack label
- * @len: the maximum size, or zero if it is NULL terminated.
+ * smk_parse_label_len - calculate the length of the starting segment
+ * in the string that constitutes a valid smack label
+ * @string: a text string that might contain a Smack label at the beginning
+ * @len: the maximum size to look into, may be zero if string is null-terminated
*
- * Returns a pointer to the clean label or an error code.
+ * Returns the length of the segment (0 < L < SMK_LONGLABEL) or an error code.
*/
-char *smk_parse_smack(const char *string, int len)
+int smk_parse_label_len(const char *string, int len)
{
- char *smack;
int i;
- if (len <= 0)
- len = strlen(string) + 1;
+ if (len <= 0 || len > SMK_LONGLABEL)
+ len = SMK_LONGLABEL;
/*
* Reserve a leading '-' as an indicator that
@@ -463,7 +463,7 @@ char *smk_parse_smack(const char *string, int len)
* including /smack/cipso and /smack/cipso2
*/
if (string[0] == '-')
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
for (i = 0; i < len; i++)
if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' ||
@@ -471,6 +471,25 @@ char *smk_parse_smack(const char *string, int len)
break;
if (i == 0 || i >= SMK_LONGLABEL)
+ return -EINVAL;
+
+ return i;
+}
+
+/**
+ * smk_parse_smack - copy the starting segment in the string
+ * that constitutes a valid smack label
+ * @string: a text string that might contain a Smack label at the beginning
+ * @len: the maximum size to look into, may be zero if string is null-terminated
+ *
+ * Returns a pointer to the copy of the label or an error code.
+ */
+char *smk_parse_smack(const char *string, int len)
+{
+ char *smack;
+ int i = smk_parse_label_len(string, len);
+
+ if (i < 0)
return ERR_PTR(-EINVAL);
smack = kstrndup(string, i, GFP_NOFS);
@@ -554,31 +573,26 @@ int smack_populate_secattr(struct smack_known *skp)
}
/**
- * smk_import_entry - import a label, return the list entry
- * @string: a text string that might be a Smack label
- * @len: the maximum size, or zero if it is NULL terminated.
+ * smk_import_valid_allocated_label - import a label, return the list entry
+ * @smack: a text string that is a valid Smack label and may be kfree()ed.
+ * It is consumed: either becomes a part of the entry or kfree'ed.
+ * @gfp: Allocation type
*
- * Returns a pointer to the entry in the label list that
- * matches the passed string, adding it if necessary,
- * or an error code.
+ * Returns: see description of smk_import_entry()
*/
-struct smack_known *smk_import_entry(const char *string, int len)
+static struct smack_known *
+smk_import_allocated_label(char *smack, gfp_t gfp)
{
struct smack_known *skp;
- char *smack;
int rc;
- smack = smk_parse_smack(string, len);
- if (IS_ERR(smack))
- return ERR_CAST(smack);
-
mutex_lock(&smack_known_lock);
skp = smk_find_entry(smack);
if (skp != NULL)
goto freeout;
- skp = kzalloc(sizeof(*skp), GFP_NOFS);
+ skp = kzalloc(sizeof(*skp), gfp);
if (skp == NULL) {
skp = ERR_PTR(-ENOMEM);
goto freeout;
@@ -609,6 +623,44 @@ unlockout:
}
/**
+ * smk_import_entry - import a label, return the list entry
+ * @string: a text string that might contain a Smack label at the beginning
+ * @len: the maximum size to look into, may be zero if string is null-terminated
+ *
+ * Returns a pointer to the entry in the label list that
+ * matches the passed string, adding it if necessary,
+ * or an error code.
+ */
+struct smack_known *smk_import_entry(const char *string, int len)
+{
+ char *smack = smk_parse_smack(string, len);
+
+ if (IS_ERR(smack))
+ return ERR_CAST(smack);
+
+ return smk_import_allocated_label(smack, GFP_NOFS);
+}
+
+/**
+ * smk_import_valid_label - import a label, return the list entry
+ * @label: a text string that is a valid Smack label, not null-terminated
+ * @label_len: the length of the text string in the @label
+ * @gfp: the GFP mask used for allocating memory for the @label text string copy
+ *
+ * Return: see description of smk_import_entry()
+ */
+struct smack_known *
+smk_import_valid_label(const char *label, int label_len, gfp_t gfp)
+{
+ char *smack = kstrndup(label, label_len, gfp);
+
+ if (!smack)
+ return ERR_PTR(-ENOMEM);
+
+ return smk_import_allocated_label(smack, gfp);
+}
+
+/**
* smack_from_secid - find the Smack label associated with a secid
* @secid: an integer that might be associated with a Smack label
*
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index af986587841d..a0bd4919a9d9 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -963,6 +963,42 @@ static int smack_inode_alloc_security(struct inode *inode)
}
/**
+ * smk_rule_transmutes - does access rule for (subject,object) contain 't'?
+ * @subject: a pointer to the subject's Smack label entry
+ * @object: a pointer to the object's Smack label entry
+ */
+static bool
+smk_rule_transmutes(struct smack_known *subject,
+ const struct smack_known *object)
+{
+ int may;
+
+ rcu_read_lock();
+ may = smk_access_entry(subject->smk_known, object->smk_known,
+ &subject->smk_rules);
+ rcu_read_unlock();
+ return (may > 0) && (may & MAY_TRANSMUTE);
+}
+
+static int
+xattr_dupval(struct xattr *xattrs, int *xattr_count,
+ const char *name, const void *value, unsigned int vallen)
+{
+ struct xattr * const xattr = lsm_get_xattr_slot(xattrs, xattr_count);
+
+ if (!xattr)
+ return 0;
+
+ xattr->value = kmemdup(value, vallen, GFP_NOFS);
+ if (!xattr->value)
+ return -ENOMEM;
+
+ xattr->value_len = vallen;
+ xattr->name = name;
+ return 0;
+}
+
+/**
* smack_inode_init_security - copy out the smack from an inode
* @inode: the newly created inode
* @dir: containing directory object
@@ -977,23 +1013,30 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
struct xattr *xattrs, int *xattr_count)
{
struct task_smack *tsp = smack_cred(current_cred());
- struct inode_smack *issp = smack_inode(inode);
- struct smack_known *skp = smk_of_task(tsp);
- struct smack_known *isp = smk_of_inode(inode);
+ struct inode_smack * const issp = smack_inode(inode);
struct smack_known *dsp = smk_of_inode(dir);
- struct xattr *xattr = lsm_get_xattr_slot(xattrs, xattr_count);
- int may;
+ int rc = 0;
+ int transflag = 0;
+ bool trans_cred;
+ bool trans_rule;
/*
+ * UNIX domain sockets use lower level socket data. Let
+ * UDS inode have fixed * label to keep smack_inode_permission() calm
+ * when called from unix_find_bsd()
+ */
+ if (S_ISSOCK(inode->i_mode)) {
+ /* forced label, no need to save to xattrs */
+ issp->smk_inode = &smack_known_star;
+ goto instant_inode;
+ }
+ /*
* If equal, transmuting already occurred in
* smack_dentry_create_files_as(). No need to check again.
*/
- if (tsp->smk_task != tsp->smk_transmuted) {
- rcu_read_lock();
- may = smk_access_entry(skp->smk_known, dsp->smk_known,
- &skp->smk_rules);
- rcu_read_unlock();
- }
+ trans_cred = (tsp->smk_task == tsp->smk_transmuted);
+ if (!trans_cred)
+ trans_rule = smk_rule_transmutes(smk_of_task(tsp), dsp);
/*
* In addition to having smk_task equal to smk_transmuted,
@@ -1001,47 +1044,38 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir,
* requests transmutation then by all means transmute.
* Mark the inode as changed.
*/
- if ((tsp->smk_task == tsp->smk_transmuted) ||
- (may > 0 && ((may & MAY_TRANSMUTE) != 0) &&
- smk_inode_transmutable(dir))) {
- struct xattr *xattr_transmute;
-
+ if (trans_cred || (trans_rule && smk_inode_transmutable(dir))) {
/*
* The caller of smack_dentry_create_files_as()
* should have overridden the current cred, so the
* inode label was already set correctly in
* smack_inode_alloc_security().
*/
- if (tsp->smk_task != tsp->smk_transmuted)
- isp = issp->smk_inode = dsp;
-
- issp->smk_flags |= SMK_INODE_TRANSMUTE;
- xattr_transmute = lsm_get_xattr_slot(xattrs,
- xattr_count);
- if (xattr_transmute) {
- xattr_transmute->value = kmemdup(TRANS_TRUE,
- TRANS_TRUE_SIZE,
- GFP_NOFS);
- if (!xattr_transmute->value)
- return -ENOMEM;
+ if (!trans_cred)
+ issp->smk_inode = dsp;
- xattr_transmute->value_len = TRANS_TRUE_SIZE;
- xattr_transmute->name = XATTR_SMACK_TRANSMUTE;
+ if (S_ISDIR(inode->i_mode)) {
+ transflag = SMK_INODE_TRANSMUTE;
+
+ if (xattr_dupval(xattrs, xattr_count,
+ XATTR_SMACK_TRANSMUTE,
+ TRANS_TRUE,
+ TRANS_TRUE_SIZE
+ ))
+ rc = -ENOMEM;
}
}
- issp->smk_flags |= SMK_INODE_INSTANT;
-
- if (xattr) {
- xattr->value = kstrdup(isp->smk_known, GFP_NOFS);
- if (!xattr->value)
- return -ENOMEM;
-
- xattr->value_len = strlen(isp->smk_known);
- xattr->name = XATTR_SMACK_SUFFIX;
- }
-
- return 0;
+ if (rc == 0)
+ if (xattr_dupval(xattrs, xattr_count,
+ XATTR_SMACK_SUFFIX,
+ issp->smk_inode->smk_known,
+ strlen(issp->smk_inode->smk_known)
+ ))
+ rc = -ENOMEM;
+instant_inode:
+ issp->smk_flags |= (SMK_INODE_INSTANT | transflag);
+ return rc;
}
/**
@@ -1315,13 +1349,23 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
int check_import = 0;
int check_star = 0;
int rc = 0;
+ umode_t const i_mode = d_backing_inode(dentry)->i_mode;
/*
* Check label validity here so import won't fail in post_setxattr
*/
- if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
- strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
- strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
+ if (strcmp(name, XATTR_NAME_SMACK) == 0) {
+ /*
+ * UDS inode has fixed label
+ */
+ if (S_ISSOCK(i_mode)) {
+ rc = -EINVAL;
+ } else {
+ check_priv = 1;
+ check_import = 1;
+ }
+ } else if (strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
check_priv = 1;
check_import = 1;
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0 ||
@@ -1331,7 +1375,7 @@ static int smack_inode_setxattr(struct mnt_idmap *idmap,
check_star = 1;
} else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
check_priv = 1;
- if (!S_ISDIR(d_backing_inode(dentry)->i_mode) ||
+ if (!S_ISDIR(i_mode) ||
size != TRANS_TRUE_SIZE ||
strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0)
rc = -EINVAL;
@@ -1462,12 +1506,15 @@ static int smack_inode_removexattr(struct mnt_idmap *idmap,
* Don't do anything special for these.
* XATTR_NAME_SMACKIPIN
* XATTR_NAME_SMACKIPOUT
+ * XATTR_NAME_SMACK if S_ISSOCK (UDS inode has fixed label)
*/
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
- struct super_block *sbp = dentry->d_sb;
- struct superblock_smack *sbsp = smack_superblock(sbp);
+ if (!S_ISSOCK(d_backing_inode(dentry)->i_mode)) {
+ struct super_block *sbp = dentry->d_sb;
+ struct superblock_smack *sbsp = smack_superblock(sbp);
- isp->smk_inode = sbsp->smk_default;
+ isp->smk_inode = sbsp->smk_default;
+ }
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0)
isp->smk_task = NULL;
else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0)
@@ -3585,7 +3632,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
*/
/*
- * UNIX domain sockets use lower level socket data.
+ * UDS inode has fixed label (*)
*/
if (S_ISSOCK(inode->i_mode)) {
final = &smack_known_star;
@@ -3663,7 +3710,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
* @attr: which attribute to fetch
* @ctx: buffer to receive the result
* @size: available size in, actual size out
- * @flags: unused
+ * @flags: reserved, currently zero
*
* Fill the passed user space @ctx with the details of the requested
* attribute.
@@ -3724,47 +3771,55 @@ static int smack_getprocattr(struct task_struct *p, const char *name, char **val
* Sets the Smack value of the task. Only setting self
* is permitted and only with privilege
*
- * Returns the length of the smack label or an error code
+ * Returns zero on success or an error code
*/
-static int do_setattr(u64 attr, void *value, size_t size)
+static int do_setattr(unsigned int attr, void *value, size_t size)
{
struct task_smack *tsp = smack_cred(current_cred());
struct cred *new;
struct smack_known *skp;
- struct smack_known_list_elem *sklep;
- int rc;
-
- if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel))
- return -EPERM;
+ int label_len;
+ /*
+ * let unprivileged user validate input, check permissions later
+ */
if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
return -EINVAL;
- if (attr != LSM_ATTR_CURRENT)
- return -EOPNOTSUPP;
-
- skp = smk_import_entry(value, size);
- if (IS_ERR(skp))
- return PTR_ERR(skp);
+ label_len = smk_parse_label_len(value, size);
+ if (label_len < 0 || label_len != size)
+ return -EINVAL;
/*
* No process is ever allowed the web ("@") label
* and the star ("*") label.
*/
- if (skp == &smack_known_web || skp == &smack_known_star)
- return -EINVAL;
+ if (label_len == 1 /* '@', '*' */) {
+ const char c = *(const char *)value;
+
+ if (c == *smack_known_web.smk_known ||
+ c == *smack_known_star.smk_known)
+ return -EPERM;
+ }
if (!smack_privileged(CAP_MAC_ADMIN)) {
- rc = -EPERM;
- list_for_each_entry(sklep, &tsp->smk_relabel, list)
- if (sklep->smk_label == skp) {
- rc = 0;
- break;
- }
- if (rc)
- return rc;
+ const struct smack_known_list_elem *sklep;
+ list_for_each_entry(sklep, &tsp->smk_relabel, list) {
+ const char *cp = sklep->smk_label->smk_known;
+
+ if (strlen(cp) == label_len &&
+ strncmp(cp, value, label_len) == 0)
+ goto in_relabel;
+ }
+ return -EPERM;
+in_relabel:
+ ;
}
+ skp = smk_import_valid_label(value, label_len, GFP_KERNEL);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+
new = prepare_creds();
if (new == NULL)
return -ENOMEM;
@@ -3777,7 +3832,7 @@ static int do_setattr(u64 attr, void *value, size_t size)
smk_destroy_label_list(&tsp->smk_relabel);
commit_creds(new);
- return size;
+ return 0;
}
/**
@@ -3785,7 +3840,7 @@ static int do_setattr(u64 attr, void *value, size_t size)
* @attr: which attribute to set
* @ctx: buffer containing the data
* @size: size of @ctx
- * @flags: unused
+ * @flags: reserved, must be zero
*
* Fill the passed user space @ctx with the details of the requested
* attribute.
@@ -3795,12 +3850,26 @@ static int do_setattr(u64 attr, void *value, size_t size)
static int smack_setselfattr(unsigned int attr, struct lsm_ctx *ctx,
u32 size, u32 flags)
{
- int rc;
+ if (attr != LSM_ATTR_CURRENT)
+ return -EOPNOTSUPP;
- rc = do_setattr(attr, ctx->ctx, ctx->ctx_len);
- if (rc > 0)
- return 0;
- return rc;
+ if (ctx->flags)
+ return -EINVAL;
+ /*
+ * string must have \0 terminator, included in ctx->ctx
+ * (see description of struct lsm_ctx)
+ */
+ if (ctx->ctx_len == 0)
+ return -EINVAL;
+
+ if (ctx->ctx[ctx->ctx_len - 1] != '\0')
+ return -EINVAL;
+ /*
+ * other do_setattr() caller, smack_setprocattr(),
+ * does not count \0 into size, so
+ * decreasing length by 1 to accommodate the divergence.
+ */
+ return do_setattr(attr, ctx->ctx, ctx->ctx_len - 1);
}
/**
@@ -3812,15 +3881,39 @@ static int smack_setselfattr(unsigned int attr, struct lsm_ctx *ctx,
* Sets the Smack value of the task. Only setting self
* is permitted and only with privilege
*
- * Returns the length of the smack label or an error code
+ * Returns the size of the input value or an error code
*/
static int smack_setprocattr(const char *name, void *value, size_t size)
{
- int attr = lsm_name_to_attr(name);
+ size_t realsize = size;
+ unsigned int attr = lsm_name_to_attr(name);
- if (attr != LSM_ATTR_UNDEF)
- return do_setattr(attr, value, size);
- return -EINVAL;
+ switch (attr) {
+ case LSM_ATTR_UNDEF: return -EINVAL;
+ default: return -EOPNOTSUPP;
+ case LSM_ATTR_CURRENT:
+ ;
+ }
+
+ /*
+ * The value for the "current" attribute is the label
+ * followed by one of the 4 trailers: none, \0, \n, \n\0
+ *
+ * I.e. following inputs are accepted as 3-characters long label "foo":
+ *
+ * "foo" (3 characters)
+ * "foo\0" (4 characters)
+ * "foo\n" (4 characters)
+ * "foo\n\0" (5 characters)
+ */
+
+ if (realsize && (((const char *)value)[realsize - 1] == '\0'))
+ --realsize;
+
+ if (realsize && (((const char *)value)[realsize - 1] == '\n'))
+ --realsize;
+
+ return do_setattr(attr, value, realsize) ? : size;
}
/**
@@ -4850,6 +4943,11 @@ static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
{
+ /*
+ * UDS inode has fixed label. Ignore nfs label.
+ */
+ if (S_ISSOCK(inode->i_mode))
+ return 0;
return smack_inode_setsecurity(inode, XATTR_SMACK_SUFFIX, ctx,
ctxlen, 0);
}
@@ -4915,7 +5013,6 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
struct task_smack *otsp = smack_cred(old);
struct task_smack *ntsp = smack_cred(new);
struct inode_smack *isp;
- int may;
/*
* Use the process credential unless all of
@@ -4929,18 +5026,12 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
isp = smack_inode(d_inode(dentry->d_parent));
if (isp->smk_flags & SMK_INODE_TRANSMUTE) {
- rcu_read_lock();
- may = smk_access_entry(otsp->smk_task->smk_known,
- isp->smk_inode->smk_known,
- &otsp->smk_task->smk_rules);
- rcu_read_unlock();
-
/*
* If the directory is transmuting and the rule
* providing access is transmuting use the containing
* directory label instead of the process label.
*/
- if (may > 0 && (may & MAY_TRANSMUTE)) {
+ if (smk_rule_transmutes(otsp->smk_task, isp->smk_inode)) {
ntsp->smk_task = isp->smk_inode;
ntsp->smk_transmuted = ntsp->smk_task;
}
@@ -5275,13 +5366,22 @@ static __init int smack_init(void)
return 0;
}
+int __init smack_initcall(void)
+{
+ int rc_fs = init_smk_fs();
+ int rc_nf = smack_nf_ip_init();
+
+ return rc_fs ? rc_fs : rc_nf;
+}
+
/*
* Smack requires early initialization in order to label
* all processes and objects when they are created.
*/
DEFINE_LSM(smack) = {
- .name = "smack",
+ .id = &smack_lsmid,
.flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE,
.blobs = &smack_blob_sizes,
.init = smack_init,
+ .initcall_device = smack_initcall,
};
diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c
index 8fd747b3653a..17ba578b1308 100644
--- a/security/smack/smack_netfilter.c
+++ b/security/smack/smack_netfilter.c
@@ -68,7 +68,7 @@ static struct pernet_operations smack_net_ops = {
.exit = smack_nf_unregister,
};
-static int __init smack_nf_ip_init(void)
+int __init smack_nf_ip_init(void)
{
if (smack_enabled == 0)
return 0;
@@ -76,5 +76,3 @@ static int __init smack_nf_ip_init(void)
printk(KERN_DEBUG "Smack: Registering netfilter hooks\n");
return register_pernet_subsys(&smack_net_ops);
}
-
-__initcall(smack_nf_ip_init);
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index b1e5e62f5cbd..405ace6db109 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -2978,7 +2978,7 @@ static struct vfsmount *smackfs_mount;
* Returns true if we were not chosen on boot or if
* we were chosen and filesystem registration succeeded.
*/
-static int __init init_smk_fs(void)
+int __init init_smk_fs(void)
{
int err;
int rc;
@@ -3021,5 +3021,3 @@ static int __init init_smk_fs(void)
return err;
}
-
-__initcall(init_smk_fs);
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 0e8e2e959aef..3b2a97d10a5d 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -924,6 +924,8 @@ struct tomoyo_task {
/********** Function prototypes. **********/
+int tomoyo_interface_init(void);
+
bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address,
const struct tomoyo_group *group);
bool tomoyo_compare_number_union(const unsigned long value,
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c
index 7e69747b2f77..33933645f5b9 100644
--- a/security/tomoyo/securityfs_if.c
+++ b/security/tomoyo/securityfs_if.c
@@ -233,7 +233,7 @@ static void __init tomoyo_create_entry(const char *name, const umode_t mode,
*
* Returns 0.
*/
-static int __init tomoyo_interface_init(void)
+int __init tomoyo_interface_init(void)
{
struct tomoyo_domain_info *domain;
struct dentry *tomoyo_dir;
@@ -269,5 +269,3 @@ static int __init tomoyo_interface_init(void)
tomoyo_load_builtin_policy();
return 0;
}
-
-fs_initcall(tomoyo_interface_init);
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 48fc59d38ab2..c66e02ed8ee3 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -612,9 +612,10 @@ static int __init tomoyo_init(void)
}
DEFINE_LSM(tomoyo) = {
- .name = "tomoyo",
+ .id = &tomoyo_lsmid,
.enabled = &tomoyo_enabled,
.flags = LSM_FLAG_LEGACY_MAJOR,
.blobs = &tomoyo_blob_sizes,
.init = tomoyo_init,
+ .initcall_fs = tomoyo_interface_init,
};
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 3d064dd4e03f..38b21ee0c560 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -476,6 +476,6 @@ static int __init yama_init(void)
}
DEFINE_LSM(yama) = {
- .name = "yama",
+ .id = &yama_lsmid,
.init = yama_init,
};