diff --git a/include/linux/security.h b/include/linux/security.h
index 9f80b685542d..8d3b875cd04a 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -257,6 +257,23 @@ static inline u32 lsmblob_first(const struct lsmblob *blob)
#endif
}
+/**
+ * lsm_task_ilsm - the "interface_lsm" for this task
+ * @task: The task to report on
+ *
+ * Returns the task's interface LSM slot.
+ */
+static inline int lsm_task_ilsm(struct task_struct *task)
+{
+#ifdef CONFIG_SECURITY
+ int *ilsm = task->security;
+
+ if (ilsm)
+ return *ilsm;
+#endif
+ return LSMBLOB_INVALID;
+}
+
/* These functions are in security/commoncap.c */
extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
int cap, unsigned int opts);diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index a5e06dcbba13..531c6e8d2d18 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -281,6 +281,10 @@ struct prctl_mm_map {
# define PR_SME_VL_LEN_MASK 0xffff
# define PR_SME_VL_INHERIT (1 << 17) /* inherit across exec */
+/* Set/get the active security module for /proc/self/attr */
+#define PR_LSM_ATTR_SET 65 /* Set the display LSM ID */
+#define PR_LSM_ATTR_GET 66 /* Get the display LSM ID */
+
#define PR_SET_VMA 0x53564d41
# define PR_SET_VMA_ANON_NAME 0
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index 9c3fc36a0702..c9e08102c96f 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -28,8 +28,9 @@
#define AA_CLASS_SIGNAL 10
#define AA_CLASS_NET 14
#define AA_CLASS_LABEL 16
+#define AA_CLASS_DISPLAY_LSM 17
-#define AA_CLASS_LAST AA_CLASS_LABEL
+#define AA_CLASS_LAST AA_CLASS_DISPLAY_LSM
/* Control parameters settable through module/boot flags */
extern enum audit_mode aa_g_audit;
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 25423a3aa572..f47134da6723 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -15,6 +15,7 @@
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/ptrace.h>
+#include <linux/prctl.h>
#include <linux/ctype.h>
#include <linux/sysctl.h>
#include <linux/audit.h>
@@ -642,6 +643,46 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
return error;
}
+
+static int profile_interface_lsm(struct aa_profile *profile,
+ struct common_audit_data *sa)
+{
+ struct aa_perms perms = { };
+ unsigned int state;
+
+ state = PROFILE_MEDIATES(profile, AA_CLASS_DISPLAY_LSM);
+ if (state) {
+ aa_compute_perms(profile->policy.dfa, state, &perms);
+ aa_apply_modes_to_perms(profile, &perms);
+ aad(sa)->label = &profile->label;
+
+ return aa_check_perms(profile, &perms, AA_MAY_WRITE, sa, NULL);
+ }
+
+ return 0;
+}
+
+static int apparmor_task_prctl(int option, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4,
+ unsigned long arg5)
+{
+ struct aa_profile *profile;
+ struct aa_label *label;
+ int error;
+ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SETPROCATTR);
+
+ if (option != PR_LSM_ATTR_SET)
+ return -ENOSYS;
+
+ /* LSM infrastructure does actual setting of interface_lsm if allowed */
+ aad(&sa)->info = "set interface lsm";
+ label = begin_current_label_crit_section();
+ error = fn_for_each_confined(label, profile,
+ profile_interface_lsm(profile, &sa));
+ end_current_label_crit_section(label);
+ return error;
+}
+
static int apparmor_setprocattr(const char *name, void *value,
size_t size)
{@@ -1196,6 +1237,7 @@ static struct lsm_id apparmor_lsmid __lsm_ro_after_init = {
static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
+ LSM_HOOK_INIT(task_prctl, apparmor_task_prctl),
LSM_HOOK_INIT(capget, apparmor_capget),
LSM_HOOK_INIT(capable, apparmor_capable),
diff --git a/security/security.c b/security/security.c
index 80133d6e982c..43d2431dbda0 100644
--- a/security/security.c
+++ b/security/security.c
@@ -28,6 +28,7 @@
#include <linux/backing-dev.h>
#include <linux/string.h>
#include <linux/msg.h>
+#include <linux/prctl.h>
#include <uapi/linux/lsm.h>
#include <net/flow.h>
#include <net/sock.h>
@@ -81,7 +82,16 @@ static struct kmem_cache *lsm_file_cache;
static struct kmem_cache *lsm_inode_cache;
char *lsm_names;
-static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init;
+
+/*
+ * The task blob includes the "interface_lsm" slot used for
+ * chosing which module presents contexts.
+ * Using a long to avoid potential alignment issues with
+ * module assigned task blobs.
+ */
+static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init = {
+ .lbs_task = sizeof(long),
+};
/* Boot-time LSM user choice */
static __initdata const char *chosen_lsm_order;@@ -691,6 +701,8 @@ int lsm_inode_alloc(struct inode *inode)
*/
static int lsm_task_alloc(struct task_struct *task)
{
+ int *ilsm;
+
if (blob_sizes.lbs_task == 0) {
task->security = NULL;
return 0;@@ -699,6 +711,15 @@ static int lsm_task_alloc(struct task_struct *task)
task->security = kzalloc(blob_sizes.lbs_task, GFP_KERNEL);
if (task->security == NULL)
return -ENOMEM;
+
+ /*
+ * The start of the task blob contains the "interface" LSM slot number.
+ * Start with it set to the invalid slot number, indicating that the
+ * default first registered LSM be displayed.
+ */
+ ilsm = task->security;
+ *ilsm = LSMBLOB_INVALID;
+
return 0;
}
@@ -1765,14 +1786,26 @@ int security_file_open(struct file *file)
int security_task_alloc(struct task_struct *task, unsigned long clone_flags)
{
+ int *oilsm = current->security;
+ int *nilsm;
int rc = lsm_task_alloc(task);
- if (rc)
+ if (unlikely(rc))
return rc;
+
rc = call_int_hook(task_alloc, 0, task, clone_flags);
- if (unlikely(rc))
+ if (unlikely(rc)) {
security_task_free(task);
- return rc;
+ return rc;
+ }
+
+ if (oilsm) {
+ nilsm = task->security;
+ if (nilsm)
+ *nilsm = *oilsm;
+ }
+
+ return 0;
}
void security_task_free(struct task_struct *task)@@ -2031,10 +2064,15 @@ int security_task_kill(struct task_struct *p, struct kernel_siginfo *info,
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
+ int *ilsm = current->security;
int thisrc;
+ int slot;
int rc = LSM_RET_DEFAULT(task_prctl);
struct security_hook_list *hp;
+ if (lsm_slot == 0)
+ return -EINVAL;
+
hlist_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
if (thisrc != LSM_RET_DEFAULT(task_prctl)) {@@ -2043,6 +2081,25 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
break;
}
}
+
+ switch (option) {
+ case PR_LSM_ATTR_SET:
+ if (rc && rc != LSM_RET_DEFAULT(task_prctl))
+ return rc;
+ for (slot = 0; slot < lsm_slot; slot++)
+ if (lsm_slotlist[slot]->id == arg2) {
+ *ilsm = lsm_slotlist[slot]->slot;
+ return 0;
+ }
+ return -EINVAL;
+ case PR_LSM_ATTR_GET:
+ if (rc && rc != LSM_RET_DEFAULT(task_prctl))
+ return rc;
+ if (*ilsm != LSMBLOB_INVALID)
+ return lsm_slotlist[*ilsm]->id;
+ return lsm_slotlist[0]->id;
+ }
+
return rc;
}
@@ -2209,22 +2266,45 @@ int security_getprocattr(struct task_struct *p, int lsmid, char *name,
char **value)
{
struct security_hook_list *hp;
+ int ilsm = lsm_task_ilsm(current);
hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
if (lsmid != LSM_ID_INVALID && lsmid != hp->lsmid->id)
continue;
+ if (lsmid == LSM_ID_INVALID && ilsm != LSMBLOB_INVALID &&
+ ilsm != hp->lsmid->slot)
+ continue;
+
return hp->hook.getprocattr(p, name, value);
}
return LSM_RET_DEFAULT(getprocattr);
}
+/**
+ * security_setprocattr - Set process attributes via /proc
+ * @lsmid: LSM ID of module involved
+ * @name: name of the attribute
+ * @value: value to set the attribute to
+ * @size: size of the value
+ *
+ * Set the process attribute for the specified security module
+ * to the specified value. Note that this can only be used to set
+ * the process attributes for the current, or "self" process.
+ * The /proc code has already done this check.
+ *
+ * Returns 0 on success, an appropriate code otherwise.
+ */
int security_setprocattr(int lsmid, const char *name, void *value, size_t size)
{
struct security_hook_list *hp;
+ int ilsm = lsm_task_ilsm(current);
hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
if (lsmid != LSM_ID_INVALID && lsmid != hp->lsmid->id)
continue;
+ if (lsmid == LSM_ID_INVALID && ilsm != LSMBLOB_INVALID &&
+ ilsm != hp->lsmid->slot)
+ continue;
return hp->hook.setprocattr(name, value, size);
}
return LSM_RET_DEFAULT(setprocattr);@@ -2244,15 +2324,15 @@ EXPORT_SYMBOL(security_ismaclabel);
int security_secid_to_secctx(struct lsmblob *blob, char **secdata, u32 *seclen)
{
struct security_hook_list *hp;
- int rc;
+ int ilsm = lsm_task_ilsm(current);
hlist_for_each_entry(hp, &security_hook_heads.secid_to_secctx, list) {
if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
continue;
- rc = hp->hook.secid_to_secctx(blob->secid[hp->lsmid->slot],
- secdata, seclen);
- if (rc != LSM_RET_DEFAULT(secid_to_secctx))
- return rc;
+ if (ilsm == LSMBLOB_INVALID || ilsm == hp->lsmid->slot)
+ return hp->hook.secid_to_secctx(
+ blob->secid[hp->lsmid->slot],
+ secdata, seclen);
}
return LSM_RET_DEFAULT(secid_to_secctx);@@ -2263,16 +2343,15 @@ int security_secctx_to_secid(const char *secdata, u32 seclen,
struct lsmblob *blob)
{
struct security_hook_list *hp;
- int rc;
+ int ilsm = lsm_task_ilsm(current);
lsmblob_init(blob, 0);
hlist_for_each_entry(hp, &security_hook_heads.secctx_to_secid, list) {
if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
continue;
- rc = hp->hook.secctx_to_secid(secdata, seclen,
- &blob->secid[hp->lsmid->slot]);
- if (rc != 0)
- return rc;
+ if (ilsm == LSMBLOB_INVALID || ilsm == hp->lsmid->slot)
+ return hp->hook.secctx_to_secid(secdata, seclen,
+ &blob->secid[hp->lsmid->slot]);
}
return 0;
}@@ -2280,7 +2359,14 @@ EXPORT_SYMBOL(security_secctx_to_secid);
void security_release_secctx(char *secdata, u32 seclen)
{
- call_void_hook(release_secctx, secdata, seclen);
+ struct security_hook_list *hp;
+ int ilsm = lsm_task_ilsm(current);
+
+ hlist_for_each_entry(hp, &security_hook_heads.release_secctx, list)
+ if (ilsm == LSMBLOB_INVALID || ilsm == hp->lsmid->slot) {
+ hp->hook.release_secctx(secdata, seclen);
+ return;
+ }
}
EXPORT_SYMBOL(security_release_secctx);
@@ -2421,8 +2507,15 @@ EXPORT_SYMBOL(security_sock_rcv_skb);
int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
int __user *optlen, unsigned len)
{
- return call_int_hook(socket_getpeersec_stream, -ENOPROTOOPT, sock,
- optval, optlen, len);
+ int ilsm = lsm_task_ilsm(current);
+ struct security_hook_list *hp;
+
+ hlist_for_each_entry(hp, &security_hook_heads.socket_getpeersec_stream,
+ list)
+ if (ilsm == LSMBLOB_INVALID || ilsm == hp->lsmid->slot)
+ return hp->hook.socket_getpeersec_stream(sock, optval,
+ optlen, len);
+ return -ENOPROTOOPT;
}
int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb,diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index cf89ccaa018d..aad795306bd2 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -92,6 +92,7 @@
#include <linux/fsnotify.h>
#include <linux/fanotify.h>
#include <linux/io_uring.h>
+#include <linux/prctl.h>
#include <uapi/linux/lsm.h>
#include "avc.h"
@@ -6373,6 +6374,24 @@ static int selinux_getprocattr(struct task_struct *p,
return error;
}
+static int selinux_task_prctl(int option, unsigned long arg2,
+ unsigned long arg3, unsigned long arg4,
+ unsigned long arg5)
+{
+ u32 mysid = current_sid();
+
+ if (option != PR_LSM_ATTR_SET)
+ return -ENOSYS;
+
+ /*
+ * For setting interface_lsm, we only perform a permission check;
+ * the actual update to the interface_lsm value is handled by the
+ * LSM framework.
+ */
+ return avc_has_perm(&selinux_state, mysid, mysid, SECCLASS_PROCESS2,
+ PROCESS2__SETINTERFACE_LSM, NULL);
+}
+
static int selinux_setprocattr(const char *name, void *value, size_t size)
{
struct task_security_struct *tsec;@@ -6384,6 +6403,7 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
/*
* Basic control over ability to set these attributes at all.
*/
+
if (!strcmp(name, "exec"))
error = avc_has_perm(&selinux_state,
mysid, mysid, SECCLASS_PROCESS,
@@ -7040,6 +7060,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(ptrace_access_check, selinux_ptrace_access_check),
LSM_HOOK_INIT(ptrace_traceme, selinux_ptrace_traceme),
+ LSM_HOOK_INIT(task_prctl, selinux_task_prctl),
LSM_HOOK_INIT(capget, selinux_capget),
LSM_HOOK_INIT(capset, selinux_capset),
LSM_HOOK_INIT(capable, selinux_capable),diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 1c2f41ff4e55..6b8569347bc0 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -53,7 +53,8 @@ const struct security_class_mapping secclass_map[] = {
"execmem", "execstack", "execheap", "setkeycreate",
"setsockcreate", "getrlimit", NULL } },
{ "process2",
- { "nnp_transition", "nosuid_transition", NULL } },
+ { "nnp_transition", "nosuid_transition", "setinterface_lsm",
+ NULL } },
{ "system",
{ "ipc_info", "syslog_read", "syslog_mod",
"syslog_console", "module_request", "module_load", NULL } },--
2.37.3