aboutsummaryrefslogtreecommitdiff
path: root/security/selinux
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux')
-rw-r--r--security/selinux/Makefile2
-rw-r--r--security/selinux/hooks.c23
-rw-r--r--security/selinux/ima.c44
-rw-r--r--security/selinux/include/ima.h24
-rw-r--r--security/selinux/include/security.h3
-rw-r--r--security/selinux/ss/services.c64
6 files changed, 141 insertions, 19 deletions
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index 4d8e0e8adf0b..776162444882 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -16,6 +16,8 @@ selinux-$(CONFIG_NETLABEL) += netlabel.o
selinux-$(CONFIG_SECURITY_INFINIBAND) += ibpkey.o
+selinux-$(CONFIG_IMA) += ima.o
+
ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
$(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index af2994adf9dd..ddd097790d47 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3203,7 +3203,8 @@ static bool has_cap_mac_admin(bool audit)
return true;
}
-static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
+static int selinux_inode_setxattr(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct inode *inode = d_backing_inode(dentry);
@@ -3224,13 +3225,13 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
}
if (!selinux_initialized(&selinux_state))
- return (inode_owner_or_capable(inode) ? 0 : -EPERM);
+ return (inode_owner_or_capable(mnt_userns, inode) ? 0 : -EPERM);
sbsec = inode->i_sb->s_security;
if (!(sbsec->flags & SBLABEL_MNT))
return -EOPNOTSUPP;
- if (!inode_owner_or_capable(inode))
+ if (!inode_owner_or_capable(mnt_userns, inode))
return -EPERM;
ad.type = LSM_AUDIT_DATA_DENTRY;
@@ -3351,10 +3352,11 @@ static int selinux_inode_listxattr(struct dentry *dentry)
return dentry_has_perm(cred, dentry, FILE__GETATTR);
}
-static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
+static int selinux_inode_removexattr(struct user_namespace *mnt_userns,
+ struct dentry *dentry, const char *name)
{
if (strcmp(name, XATTR_NAME_SELINUX)) {
- int rc = cap_inode_removexattr(dentry, name);
+ int rc = cap_inode_removexattr(mnt_userns, dentry, name);
if (rc)
return rc;
@@ -3420,7 +3422,9 @@ static int selinux_path_notify(const struct path *path, u64 mask,
*
* Permission check is handled by selinux_inode_getxattr hook.
*/
-static int selinux_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc)
+static int selinux_inode_getsecurity(struct user_namespace *mnt_userns,
+ struct inode *inode, const char *name,
+ void **buffer, bool alloc)
{
u32 size;
int error;
@@ -6614,14 +6618,15 @@ static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen
*/
static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
{
- return __vfs_setxattr_noperm(dentry, XATTR_NAME_SELINUX, ctx, ctxlen, 0);
+ return __vfs_setxattr_noperm(&init_user_ns, dentry, XATTR_NAME_SELINUX,
+ ctx, ctxlen, 0);
}
static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
{
int len = 0;
- len = selinux_inode_getsecurity(inode, XATTR_SELINUX_SUFFIX,
- ctx, true);
+ len = selinux_inode_getsecurity(&init_user_ns, inode,
+ XATTR_SELINUX_SUFFIX, ctx, true);
if (len < 0)
return len;
*ctxlen = len;
diff --git a/security/selinux/ima.c b/security/selinux/ima.c
new file mode 100644
index 000000000000..03715893ff97
--- /dev/null
+++ b/security/selinux/ima.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Microsoft Corporation
+ *
+ * Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com)
+ *
+ * Measure critical data structures maintainted by SELinux
+ * using IMA subsystem.
+ */
+#include <linux/vmalloc.h>
+#include <linux/ima.h>
+#include "security.h"
+#include "ima.h"
+
+/*
+ * selinux_ima_measure_state - Measure hash of the SELinux policy
+ *
+ * @state: selinux state struct
+ *
+ * NOTE: This function must be called with policy_mutex held.
+ */
+void selinux_ima_measure_state(struct selinux_state *state)
+{
+ void *policy = NULL;
+ size_t policy_len;
+ int rc = 0;
+
+ /*
+ * Measure SELinux policy only after initialization is completed.
+ */
+ if (!selinux_initialized(state))
+ return;
+
+ rc = security_read_state_kernel(state, &policy, &policy_len);
+ if (rc) {
+ pr_err("SELinux: %s: failed to read policy %d.\n", __func__, rc);
+ return;
+ }
+
+ ima_measure_critical_data("selinux", "selinux-policy-hash",
+ policy, policy_len, true);
+
+ vfree(policy);
+}
diff --git a/security/selinux/include/ima.h b/security/selinux/include/ima.h
new file mode 100644
index 000000000000..d69c36611423
--- /dev/null
+++ b/security/selinux/include/ima.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2021 Microsoft Corporation
+ *
+ * Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com)
+ *
+ * Measure critical data structures maintainted by SELinux
+ * using IMA subsystem.
+ */
+
+#ifndef _SELINUX_IMA_H_
+#define _SELINUX_IMA_H_
+
+#include "security.h"
+
+#ifdef CONFIG_IMA
+extern void selinux_ima_measure_state(struct selinux_state *selinux_state);
+#else
+static inline void selinux_ima_measure_state(struct selinux_state *selinux_state)
+{
+}
+#endif
+
+#endif /* _SELINUX_IMA_H_ */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 25db66e0ac51..7650de048570 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -236,7 +236,8 @@ void selinux_policy_cancel(struct selinux_state *state,
struct selinux_load_state *load_state);
int security_read_policy(struct selinux_state *state,
void **data, size_t *len);
-
+int security_read_state_kernel(struct selinux_state *state,
+ void **data, size_t *len);
int security_policycap_supported(struct selinux_state *state,
unsigned int req_cap);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 6f095c0dc658..301633145040 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -65,6 +65,7 @@
#include "ebitmap.h"
#include "audit.h"
#include "policycap_names.h"
+#include "ima.h"
struct convert_context_args {
struct selinux_state *state;
@@ -2201,6 +2202,7 @@ static void selinux_notify_policy_change(struct selinux_state *state,
selinux_status_update_policyload(state, seqno);
selinux_netlbl_cache_invalidate();
selinux_xfrm_notify_policyload();
+ selinux_ima_measure_state(state);
}
void selinux_policy_commit(struct selinux_state *state,
@@ -3968,7 +3970,32 @@ out:
#endif /* CONFIG_NETLABEL */
/**
+ * __security_read_policy - read the policy.
+ * @policy: SELinux policy
+ * @data: binary policy data
+ * @len: length of data in bytes
+ *
+ */
+static int __security_read_policy(struct selinux_policy *policy,
+ void *data, size_t *len)
+{
+ int rc;
+ struct policy_file fp;
+
+ fp.data = data;
+ fp.len = *len;
+
+ rc = policydb_write(&policy->policydb, &fp);
+ if (rc)
+ return rc;
+
+ *len = (unsigned long)fp.data - (unsigned long)data;
+ return 0;
+}
+
+/**
* security_read_policy - read the policy.
+ * @state: selinux_state
* @data: binary policy data
* @len: length of data in bytes
*
@@ -3977,8 +4004,6 @@ int security_read_policy(struct selinux_state *state,
void **data, size_t *len)
{
struct selinux_policy *policy;
- int rc;
- struct policy_file fp;
policy = rcu_dereference_protected(
state->policy, lockdep_is_held(&state->policy_mutex));
@@ -3990,14 +4015,35 @@ int security_read_policy(struct selinux_state *state,
if (!*data)
return -ENOMEM;
- fp.data = *data;
- fp.len = *len;
+ return __security_read_policy(policy, *data, len);
+}
- rc = policydb_write(&policy->policydb, &fp);
- if (rc)
- return rc;
+/**
+ * security_read_state_kernel - read the policy.
+ * @state: selinux_state
+ * @data: binary policy data
+ * @len: length of data in bytes
+ *
+ * Allocates kernel memory for reading SELinux policy.
+ * This function is for internal use only and should not
+ * be used for returning data to user space.
+ *
+ * This function must be called with policy_mutex held.
+ */
+int security_read_state_kernel(struct selinux_state *state,
+ void **data, size_t *len)
+{
+ struct selinux_policy *policy;
- *len = (unsigned long)fp.data - (unsigned long)*data;
- return 0;
+ policy = rcu_dereference_protected(
+ state->policy, lockdep_is_held(&state->policy_mutex));
+ if (!policy)
+ return -EINVAL;
+
+ *len = policy->policydb.len;
+ *data = vmalloc(*len);
+ if (!*data)
+ return -ENOMEM;
+ return __security_read_policy(policy, *data, len);
}