--- v30
+++ v20
@@ -1,317 +1,473 @@
-Change the security_cred_getsecid() interface to fill in a
-lsmblob instead of a u32 secid. The associated data elements
-in the audit sub-system are changed from a secid to a lsmblob
-to accommodate multiple possible LSM audit users.
+Create a new entry "display" in the procfs attr directory for
+controlling which LSM security information is displayed for a
+process. A process can only read or write its own display value.
+
+The name of an active LSM that supplies hooks for
+human readable data may be written to "display" to set the
+value. The name of the LSM currently in use can be read from
+"display". At this point there can only be one LSM capable
+of display active. A helper function lsm_task_display() is
+provided to get the display slot for a task_struct.
+
+Setting the "display" requires that all security modules using
+setprocattr hooks allow the action. Each security module is
+responsible for defining its policy.
+
+AppArmor hook provided by John Johansen <john.johansen@canonical.com>
+SELinux hook provided by Stephen Smalley <sds@tycho.nsa.gov>
Reviewed-by: Kees Cook <keescook@chromium.org>
-Reviewed-by: John Johansen <john.johansen@canonical.com>
-Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
+Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
Acked-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
-Cc: linux-integrity@vger.kernel.org
-Cc: linux-audit@redhat.com
---
- drivers/android/binder.c | 12 +----------
- include/linux/security.h | 2 +-
- kernel/audit.c | 25 +++++++----------------
- kernel/audit.h | 3 ++-
- kernel/auditsc.c | 33 +++++++++++--------------------
- security/integrity/ima/ima_main.c | 8 ++++----
- security/security.c | 12 ++++++++---
- 7 files changed, 36 insertions(+), 59 deletions(-)
-
-diff --git a/drivers/android/binder.c b/drivers/android/binder.c
-index 780c7e265f3a..2be77ae9ca52 100644
---- a/drivers/android/binder.c
-+++ b/drivers/android/binder.c
-@@ -2720,18 +2720,8 @@ static void binder_transaction(struct binder_proc *proc,
- if (target_node && target_node->txn_security_ctx) {
- struct lsmblob blob;
- size_t added_size;
-- u32 secid;
-
-- security_cred_getsecid(proc->cred, &secid);
-- /*
-- * Later in this patch set security_cred_getsecid() will
-- * provide a lsmblob instead of a secid. lsmblob_init
-- * is used to ensure that all the secids in the lsmblob
-- * get the value returned from security_cred_getsecid(),
-- * which means that the one expected by
-- * security_secid_to_secctx() will be set.
-- */
-- lsmblob_init(&blob, secid);
-+ security_cred_getsecid(proc->cred, &blob);
- ret = security_secid_to_secctx(&blob, &secctx, &secctx_sz);
- if (ret) {
- return_error = BR_FAILED_REPLY;
-diff --git a/include/linux/security.h b/include/linux/security.h
-index f324acc2c029..5271b2f4dfca 100644
---- a/include/linux/security.h
-+++ b/include/linux/security.h
-@@ -484,7 +484,7 @@ int security_cred_alloc_blank(struct cred *cred, gfp_t gfp);
- void security_cred_free(struct cred *cred);
- int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
- void security_transfer_creds(struct cred *new, const struct cred *old);
--void security_cred_getsecid(const struct cred *c, u32 *secid);
-+void security_cred_getsecid(const struct cred *c, struct lsmblob *blob);
- int security_kernel_act_as(struct cred *new, struct lsmblob *blob);
- int security_kernel_create_files_as(struct cred *new, struct inode *inode);
- int security_kernel_module_request(char *kmod_name);
-diff --git a/kernel/audit.c b/kernel/audit.c
-index d92c7b894183..8ec64e6e8bc0 100644
---- a/kernel/audit.c
-+++ b/kernel/audit.c
-@@ -125,7 +125,7 @@ static u32 audit_backlog_wait_time = AUDIT_BACKLOG_WAIT_TIME;
- /* The identity of the user shutting down the audit system. */
- static kuid_t audit_sig_uid = INVALID_UID;
- static pid_t audit_sig_pid = -1;
--static u32 audit_sig_sid;
-+struct lsmblob audit_sig_lsm;
-
- /* Records can be lost in several ways:
- 0) [suppressed in audit_alloc]
-@@ -1441,29 +1441,21 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
- }
- case AUDIT_SIGNAL_INFO:
- len = 0;
-- if (audit_sig_sid) {
-- struct lsmblob blob;
--
-- /*
-- * lsmblob_init sets all values in the lsmblob
-- * to audit_sig_sid. This is temporary until
-- * audit_sig_sid is converted to a lsmblob, which
-- * happens later in this patch set.
-- */
-- lsmblob_init(&blob, audit_sig_sid);
-- err = security_secid_to_secctx(&blob, &ctx, &len);
-+ if (lsmblob_is_set(&audit_sig_lsm)) {
-+ err = security_secid_to_secctx(&audit_sig_lsm, &ctx,
-+ &len);
- if (err)
- return err;
- }
- sig_data = kmalloc(sizeof(*sig_data) + len, GFP_KERNEL);
- if (!sig_data) {
-- if (audit_sig_sid)
-+ if (lsmblob_is_set(&audit_sig_lsm))
- security_release_secctx(ctx, len);
- return -ENOMEM;
- }
- sig_data->uid = from_kuid(&init_user_ns, audit_sig_uid);
- sig_data->pid = audit_sig_pid;
-- if (audit_sig_sid) {
-+ if (lsmblob_is_set(&audit_sig_lsm)) {
- memcpy(sig_data->ctx, ctx, len);
- security_release_secctx(ctx, len);
- }
-@@ -2352,7 +2344,6 @@ int audit_set_loginuid(kuid_t loginuid)
- int audit_signal_info(int sig, struct task_struct *t)
- {
- kuid_t uid = current_uid(), auid;
-- struct lsmblob blob;
-
- if (auditd_test_task(t) &&
- (sig == SIGTERM || sig == SIGHUP ||
-@@ -2363,9 +2354,7 @@ int audit_signal_info(int sig, struct task_struct *t)
- audit_sig_uid = auid;
- else
- audit_sig_uid = uid;
-- security_task_getsecid_subj(current, &blob);
-- /* scaffolding until audit_sig_sid is converted */
-- audit_sig_sid = blob.secid[0];
-+ security_task_getsecid_subj(current, &audit_sig_lsm);
- }
-
- return audit_signal_info_syscall(t);
-diff --git a/kernel/audit.h b/kernel/audit.h
-index c4498090a5bd..527d4c4acb12 100644
---- a/kernel/audit.h
-+++ b/kernel/audit.h
-@@ -12,6 +12,7 @@
- #include <linux/fs.h>
- #include <linux/audit.h>
- #include <linux/skbuff.h>
-+#include <linux/security.h>
- #include <uapi/linux/mqueue.h>
- #include <linux/tty.h>
- #include <uapi/linux/openat2.h> // struct open_how
-@@ -143,7 +144,7 @@ struct audit_context {
- kuid_t target_auid;
- kuid_t target_uid;
- unsigned int target_sessionid;
-- u32 target_sid;
-+ struct lsmblob target_lsm;
- char target_comm[TASK_COMM_LEN];
-
- struct audit_tree_refs *trees, *first_trees;
-diff --git a/kernel/auditsc.c b/kernel/auditsc.c
-index 3123c97d13b3..7d256fb2ec03 100644
---- a/kernel/auditsc.c
-+++ b/kernel/auditsc.c
-@@ -99,7 +99,7 @@ struct audit_aux_data_pids {
- kuid_t target_auid[AUDIT_AUX_PIDS];
- kuid_t target_uid[AUDIT_AUX_PIDS];
- unsigned int target_sessionid[AUDIT_AUX_PIDS];
-- u32 target_sid[AUDIT_AUX_PIDS];
-+ struct lsmblob target_lsm[AUDIT_AUX_PIDS];
- char target_comm[AUDIT_AUX_PIDS][TASK_COMM_LEN];
- int pid_count;
- };
-@@ -1009,7 +1009,7 @@ static void audit_reset_context(struct audit_context *ctx)
- ctx->target_pid = 0;
- ctx->target_auid = ctx->target_uid = KUIDT_INIT(0);
- ctx->target_sessionid = 0;
-- ctx->target_sid = 0;
-+ lsmblob_init(&ctx->target_lsm, 0);
- ctx->target_comm[0] = '\0';
- unroll_tree_refs(ctx, NULL, 0);
- WARN_ON(!list_empty(&ctx->killed_trees));
-@@ -1107,14 +1107,14 @@ static inline void audit_free_context(struct audit_context *context)
- }
-
- static int audit_log_pid_context(struct audit_context *context, pid_t pid,
-- kuid_t auid, kuid_t uid, unsigned int sessionid,
-- u32 sid, char *comm)
-+ kuid_t auid, kuid_t uid,
-+ unsigned int sessionid,
-+ struct lsmblob *blob, char *comm)
- {
- struct audit_buffer *ab;
- char *ctx = NULL;
- u32 len;
- int rc = 0;
-- struct lsmblob blob;
-
- ab = audit_log_start(context, GFP_KERNEL, AUDIT_OBJ_PID);
- if (!ab)
-@@ -1123,9 +1123,8 @@ static int audit_log_pid_context(struct audit_context *context, pid_t pid,
- audit_log_format(ab, "opid=%d oauid=%d ouid=%d oses=%d", pid,
- from_kuid(&init_user_ns, auid),
- from_kuid(&init_user_ns, uid), sessionid);
-- if (sid) {
-- lsmblob_init(&blob, sid);
-- if (security_secid_to_secctx(&blob, &ctx, &len)) {
-+ if (lsmblob_is_set(blob)) {
-+ if (security_secid_to_secctx(blob, &ctx, &len)) {
- audit_log_format(ab, " obj=(none)");
- rc = 1;
- } else {
-@@ -1753,7 +1752,7 @@ static void audit_log_exit(void)
- axs->target_auid[i],
- axs->target_uid[i],
- axs->target_sessionid[i],
-- axs->target_sid[i],
-+ &axs->target_lsm[i],
- axs->target_comm[i]))
- call_panic = 1;
- }
-@@ -1762,7 +1761,7 @@ static void audit_log_exit(void)
- audit_log_pid_context(context, context->target_pid,
- context->target_auid, context->target_uid,
- context->target_sessionid,
-- context->target_sid, context->target_comm))
-+ &context->target_lsm, context->target_comm))
- call_panic = 1;
-
- if (context->pwd.dentry && context->pwd.mnt) {
-@@ -2698,15 +2697,12 @@ int __audit_sockaddr(int len, void *a)
- void __audit_ptrace(struct task_struct *t)
- {
- struct audit_context *context = audit_context();
-- struct lsmblob blob;
-
- context->target_pid = task_tgid_nr(t);
- context->target_auid = audit_get_loginuid(t);
- context->target_uid = task_uid(t);
- context->target_sessionid = audit_get_sessionid(t);
-- security_task_getsecid_obj(t, &blob);
-- /* scaffolding - until target_sid is converted */
-- context->target_sid = blob.secid[0];
-+ security_task_getsecid_obj(t, &context->target_lsm);
- memcpy(context->target_comm, t->comm, TASK_COMM_LEN);
- }
-
-@@ -2722,7 +2718,6 @@ int audit_signal_info_syscall(struct task_struct *t)
- struct audit_aux_data_pids *axp;
- struct audit_context *ctx = audit_context();
- kuid_t t_uid = task_uid(t);
-- struct lsmblob blob;
-
- if (!audit_signals || audit_dummy_context())
- return 0;
-@@ -2734,9 +2729,7 @@ int audit_signal_info_syscall(struct task_struct *t)
- ctx->target_auid = audit_get_loginuid(t);
- ctx->target_uid = t_uid;
- ctx->target_sessionid = audit_get_sessionid(t);
-- security_task_getsecid_obj(t, &blob);
-- /* scaffolding until target_sid is converted */
-- ctx->target_sid = blob.secid[0];
-+ security_task_getsecid_obj(t, &ctx->target_lsm);
- memcpy(ctx->target_comm, t->comm, TASK_COMM_LEN);
- return 0;
- }
-@@ -2757,9 +2750,7 @@ int audit_signal_info_syscall(struct task_struct *t)
- axp->target_auid[axp->pid_count] = audit_get_loginuid(t);
- axp->target_uid[axp->pid_count] = t_uid;
- axp->target_sessionid[axp->pid_count] = audit_get_sessionid(t);
-- security_task_getsecid_obj(t, &blob);
-- /* scaffolding until target_sid is converted */
-- axp->target_sid[axp->pid_count] = blob.secid[0];
-+ security_task_getsecid_obj(t, &axp->target_lsm[axp->pid_count]);
- memcpy(axp->target_comm[axp->pid_count], t->comm, TASK_COMM_LEN);
- axp->pid_count++;
-
-diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
-index c327f93d3962..1a4f7b00253b 100644
---- a/security/integrity/ima/ima_main.c
-+++ b/security/integrity/ima/ima_main.c
-@@ -486,7 +486,6 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
- int ima_bprm_check(struct linux_binprm *bprm)
- {
- int ret;
-- u32 secid;
- struct lsmblob blob;
-
- security_task_getsecid_subj(current, &blob);
-@@ -496,9 +495,10 @@ int ima_bprm_check(struct linux_binprm *bprm)
- if (ret)
- return ret;
-
-- security_cred_getsecid(bprm->cred, &secid);
-- return process_measurement(bprm->file, bprm->cred, secid, NULL, 0,
-- MAY_EXEC, CREDS_CHECK);
-+ security_cred_getsecid(bprm->cred, &blob);
-+ /* scaffolding until process_measurement changes */
-+ return process_measurement(bprm->file, bprm->cred, blob.secid[0],
-+ NULL, 0, MAY_EXEC, CREDS_CHECK);
- }
-
- /**
+ fs/proc/base.c | 1 +
+ include/linux/lsm_hooks.h | 17 +++
+ security/apparmor/include/apparmor.h | 3 +-
+ security/apparmor/lsm.c | 32 +++++
+ security/security.c | 167 ++++++++++++++++++++++++---
+ security/selinux/hooks.c | 11 ++
+ security/selinux/include/classmap.h | 2 +-
+ security/smack/smack_lsm.c | 7 ++
+ 8 files changed, 221 insertions(+), 19 deletions(-)
+
+diff --git a/fs/proc/base.c b/fs/proc/base.c
+index 617db4e0faa0..2edb51d4c725 100644
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -2803,6 +2803,7 @@ static const struct pid_entry attr_dir_stuff[] = {
+ ATTR(NULL, "fscreate", 0666),
+ ATTR(NULL, "keycreate", 0666),
+ ATTR(NULL, "sockcreate", 0666),
++ ATTR(NULL, "display", 0666),
+ #ifdef CONFIG_SECURITY_SMACK
+ DIR("smack", 0555,
+ proc_smack_attr_dir_inode_ops, proc_smack_attr_dir_ops),
+diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
+index 983008c6fede..e559df1df169 100644
+--- a/include/linux/lsm_hooks.h
++++ b/include/linux/lsm_hooks.h
+@@ -1647,4 +1647,21 @@ static inline void security_delete_hooks(struct security_hook_list *hooks,
+
+ extern int lsm_inode_alloc(struct inode *inode);
+
++/**
++ * lsm_task_display - the "display" LSM for this task
++ * @task: The task to report on
++ *
++ * Returns the task's display LSM slot.
++ */
++static inline int lsm_task_display(struct task_struct *task)
++{
++#ifdef CONFIG_SECURITY
++ int *display = task->security;
++
++ if (display)
++ return *display;
++#endif
++ return LSMBLOB_INVALID;
++}
++
+ #endif /* ! __LINUX_LSM_HOOKS_H */
+diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
+index 1fbabdb565a8..b1622fcb4394 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 432915c1d427..31a6f11890f1 100644
+--- a/security/apparmor/lsm.c
++++ b/security/apparmor/lsm.c
+@@ -612,6 +612,25 @@ static int apparmor_getprocattr(struct task_struct *task, char *name,
+ return error;
+ }
+
++
++static int profile_display_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_setprocattr(const char *name, void *value,
+ size_t size)
+ {
+@@ -623,6 +642,19 @@ static int apparmor_setprocattr(const char *name, void *value,
+ if (size == 0)
+ return -EINVAL;
+
++ /* LSM infrastructure does actual setting of display if allowed */
++ if (!strcmp(name, "display")) {
++ struct aa_profile *profile;
++ struct aa_label *label;
++
++ aad(&sa)->info = "set display lsm";
++ label = begin_current_label_crit_section();
++ error = fn_for_each_confined(label, profile,
++ profile_display_lsm(profile, &sa));
++ end_current_label_crit_section(label);
++ return error;
++ }
++
+ /* AppArmor requires that the buffer must be null terminated atm */
+ if (args[size - 1] != '\0') {
+ /* null terminate */
diff --git a/security/security.c b/security/security.c
-index 54b5dbe63265..f7af2444fcc3 100644
+index c3bac45bbb79..e1c9f87db64b 100644
--- a/security/security.c
+++ b/security/security.c
-@@ -1800,10 +1800,16 @@ void security_transfer_creds(struct cred *new, const struct cred *old)
- call_void_hook(cred_transfer, new, old);
- }
-
--void security_cred_getsecid(const struct cred *c, u32 *secid)
-+void security_cred_getsecid(const struct cred *c, struct lsmblob *blob)
- {
-- *secid = 0;
-- call_void_hook(cred_getsecid, c, secid);
+@@ -75,7 +75,14 @@ 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 "display" slot used for
++ * chosing which module presents contexts.
++ */
++static struct lsm_blob_sizes blob_sizes __lsm_ro_after_init = {
++ .lbs_task = sizeof(int),
++};
+
+ /* Boot-time LSM user choice */
+ static __initdata const char *chosen_lsm_order;
+@@ -470,8 +477,10 @@ static int lsm_append(const char *new, char **result)
+
+ /*
+ * Current index to use while initializing the lsmblob secid list.
++ * Pointers to the LSM id structures for local use.
+ */
+ static int lsm_slot __lsm_ro_after_init;
++static struct lsm_id *lsm_slotlist[LSMBLOB_ENTRIES];
+
+ /**
+ * security_add_hooks - Add a modules hooks to the hook lists.
+@@ -491,6 +500,7 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
+ if (lsmid->slot == LSMBLOB_NEEDED) {
+ if (lsm_slot >= LSMBLOB_ENTRIES)
+ panic("%s Too many LSMs registered.\n", __func__);
++ lsm_slotlist[lsm_slot] = lsmid;
+ lsmid->slot = lsm_slot++;
+ init_debug("%s assigned lsmblob slot %d\n", lsmid->lsm,
+ lsmid->slot);
+@@ -620,6 +630,8 @@ int lsm_inode_alloc(struct inode *inode)
+ */
+ static int lsm_task_alloc(struct task_struct *task)
+ {
++ int *display;
++
+ if (blob_sizes.lbs_task == 0) {
+ task->security = NULL;
+ return 0;
+@@ -628,6 +640,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 "display" LSM slot number.
++ * Start with it set to the invalid slot number, indicating that the
++ * default first registered LSM be displayed.
++ */
++ display = task->security;
++ *display = LSMBLOB_INVALID;
++
+ return 0;
+ }
+
+@@ -1628,14 +1649,26 @@ int security_file_open(struct file *file)
+
+ int security_task_alloc(struct task_struct *task, unsigned long clone_flags)
+ {
++ int *odisplay = current->security;
++ int *ndisplay;
+ 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 (odisplay) {
++ ndisplay = task->security;
++ if (ndisplay)
++ *ndisplay = *odisplay;
++ }
++
++ return 0;
+ }
+
+ void security_task_free(struct task_struct *task)
+@@ -2038,23 +2071,110 @@ int security_getprocattr(struct task_struct *p, const char *lsm, char *name,
+ char **value)
+ {
+ struct security_hook_list *hp;
++ int display = lsm_task_display(current);
++ int slot = 0;
++
++ if (!strcmp(name, "display")) {
++ /*
++ * lsm_slot will be 0 if there are no displaying modules.
++ */
++ if (lsm_slot == 0)
++ return -EINVAL;
++
++ /*
++ * Only allow getting the current process' display.
++ * There are too few reasons to get another process'
++ * display and too many LSM policy issues.
++ */
++ if (current != p)
++ return -EINVAL;
++
++ display = lsm_task_display(p);
++ if (display != LSMBLOB_INVALID)
++ slot = display;
++ *value = kstrdup(lsm_slotlist[slot]->lsm, GFP_KERNEL);
++ if (*value)
++ return strlen(*value);
++ return -ENOMEM;
++ }
+
+ hlist_for_each_entry(hp, &security_hook_heads.getprocattr, list) {
+ if (lsm != NULL && strcmp(lsm, hp->lsmid->lsm))
+ continue;
++ if (lsm == NULL && display != LSMBLOB_INVALID &&
++ display != hp->lsmid->slot)
++ continue;
+ return hp->hook.getprocattr(p, name, value);
+ }
+ return LSM_RET_DEFAULT(getprocattr);
+ }
+
++/**
++ * security_setprocattr - Set process attributes via /proc
++ * @lsm: name of module involved, or NULL
++ * @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(const char *lsm, const char *name, void *value,
+ size_t size)
+ {
+ struct security_hook_list *hp;
++ char *termed;
++ char *copy;
++ int *display = current->security;
++ int rc = -EINVAL;
++ int slot = 0;
++
++ if (!strcmp(name, "display")) {
++ /*
++ * Change the "display" value only if all the security
++ * modules that support setting a procattr allow it.
++ * It is assumed that all such security modules will be
++ * cooperative.
++ */
++ if (size == 0)
++ return -EINVAL;
++
++ hlist_for_each_entry(hp, &security_hook_heads.setprocattr,
++ list) {
++ rc = hp->hook.setprocattr(name, value, size);
++ if (rc < 0)
++ return rc;
++ }
++
++ rc = -EINVAL;
++
++ copy = kmemdup_nul(value, size, GFP_KERNEL);
++ if (copy == NULL)
++ return -ENOMEM;
++
++ termed = strsep(©, " \n");
++
++ for (slot = 0; slot < lsm_slot; slot++)
++ if (!strcmp(termed, lsm_slotlist[slot]->lsm)) {
++ *display = lsm_slotlist[slot]->slot;
++ rc = size;
++ break;
++ }
++
++ kfree(termed);
++ return rc;
++ }
+
+ hlist_for_each_entry(hp, &security_hook_heads.setprocattr, list) {
+ if (lsm != NULL && strcmp(lsm, hp->lsmid->lsm))
+ continue;
++ if (lsm == NULL && *display != LSMBLOB_INVALID &&
++ *display != hp->lsmid->slot)
++ continue;
+ return hp->hook.setprocattr(name, value, size);
+ }
+ return LSM_RET_DEFAULT(setprocattr);
+@@ -2074,15 +2194,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 display = lsm_task_display(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 (display == LSMBLOB_INVALID || display == hp->lsmid->slot)
++ return hp->hook.secid_to_secctx(
++ blob->secid[hp->lsmid->slot],
++ secdata, seclen);
+ }
+
+ return LSM_RET_DEFAULT(secid_to_secctx);
+@@ -2093,16 +2213,15 @@ int security_secctx_to_secid(const char *secdata, u32 seclen,
+ struct lsmblob *blob)
+ {
+ struct security_hook_list *hp;
+- int rc;
++ int display = lsm_task_display(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 (display == LSMBLOB_INVALID || display == hp->lsmid->slot)
++ return hp->hook.secctx_to_secid(secdata, seclen,
++ &blob->secid[hp->lsmid->slot]);
+ }
+ return 0;
+ }
+@@ -2110,7 +2229,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;
-+
-+ lsmblob_init(blob, 0);
-+ hlist_for_each_entry(hp, &security_hook_heads.cred_getsecid, list) {
-+ if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
-+ continue;
-+ hp->hook.cred_getsecid(c, &blob->secid[hp->lsmid->slot]);
-+ }
- }
- EXPORT_SYMBOL(security_cred_getsecid);
++ int display = lsm_task_display(current);
++
++ hlist_for_each_entry(hp, &security_hook_heads.release_secctx, list)
++ if (display == LSMBLOB_INVALID || display == hp->lsmid->slot) {
++ hp->hook.release_secctx(secdata, seclen);
++ return;
++ }
+ }
+ EXPORT_SYMBOL(security_release_secctx);
+
+@@ -2251,8 +2377,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 display = lsm_task_display(current);
++ struct security_hook_list *hp;
++
++ hlist_for_each_entry(hp, &security_hook_heads.socket_getpeersec_stream,
++ list)
++ if (display == LSMBLOB_INVALID || display == 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 1172b3f30f99..f73dec68ee32 100644
+--- a/security/selinux/hooks.c
++++ b/security/selinux/hooks.c
+@@ -6335,6 +6335,17 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
+ /*
+ * Basic control over ability to set these attributes at all.
+ */
++
++ /*
++ * For setting display, we only perform a permission check;
++ * the actual update to the display value is handled by the
++ * LSM framework.
++ */
++ if (!strcmp(name, "display"))
++ return avc_has_perm(&selinux_state,
++ mysid, mysid, SECCLASS_PROCESS2,
++ PROCESS2__SETDISPLAY, NULL);
++
+ if (!strcmp(name, "exec"))
+ error = avc_has_perm(&selinux_state,
+ mysid, mysid, SECCLASS_PROCESS,
+diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
+index 40cebde62856..1858aa47b32a 100644
+--- a/security/selinux/include/classmap.h
++++ b/security/selinux/include/classmap.h
+@@ -53,7 +53,7 @@ struct security_class_mapping secclass_map[] = {
+ "execmem", "execstack", "execheap", "setkeycreate",
+ "setsockcreate", "getrlimit", NULL } },
+ { "process2",
+- { "nnp_transition", "nosuid_transition", NULL } },
++ { "nnp_transition", "nosuid_transition", "setdisplay", NULL } },
+ { "system",
+ { "ipc_info", "syslog_read", "syslog_mod",
+ "syslog_console", "module_request", "module_load", NULL } },
+diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
+index d4655dec2d70..8b708cca921a 100644
+--- a/security/smack/smack_lsm.c
++++ b/security/smack/smack_lsm.c
+@@ -3494,6 +3494,13 @@ static int smack_setprocattr(const char *name, void *value, size_t size)
+ struct smack_known_list_elem *sklep;
+ int rc;
+
++ /*
++ * Allow the /proc/.../attr/current and SO_PEERSEC "display"
++ * to be reset at will.
++ */
++ if (strcmp(name, "display") == 0)
++ return 0;
++
+ if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel))
+ return -EPERM;
--
-2.31.1
-
+2.24.1
+