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;
/*
* 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.
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);
*/
static int lsm_task_alloc(struct task_struct *task)
{
+ int *display;
+
if (blob_sizes.lbs_task == 0) {
task->security = NULL;
return 0;
task->security = kzalloc(blob_sizes.lbs_task, GFP_KERNEL);
if (task->security == NULL)
return -ENOMEM;
+
+ /*
+ * 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;
}
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)
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);
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);
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;
}
void security_release_secctx(char *secdata, u32 seclen)
{
- call_void_hook(release_secctx, secdata, seclen);
+ struct security_hook_list *hp;
+ 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);
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,