The Smack-Tags subsystems allows to attach tags to processes.
Tags are accessed through the new files /proc/PID/attr/tags
and /proc/PID/tasks/TID/attr/tags below named "tag file".

Reading a tag file returns all the tags attached to the process (or
thread). The tags are listed one per line, each followed by exactly one
line-feed character ('\n').

Writing it allows under condition to change the tags of a process.
When writting the file, it acts like a protocol accepting lines.
The accepted lines are:
 - an empty: "\n", only a line-feed;
 - remove all tags: "-\n", a minus followed by a line-feed;
 - remove one tag: "-TAGNAME\n", a minus followed by the name of the tag
                                 and a line-feed;
 - remove one tag: "+TAGNAME\n", a plus followed by the name of the tag
                                 and a line-feed.

Names of tags can have any byte in the ranges [33...126] and
[128...255]. The minimum length of a tag's name is 1 and the maximum
length is 1000 bytes.

It exist 7 special tags. Their names are:
 - smack-tags:add
 - smack-tags:sub
 - smack-tags:keep
 - smack-tags:add-all
 - smack-tags:sub-all
 - smack-tags:keep-all
 - smack-tags:set-others

Normally a process can not remove tags from itself. A process can
remove a tag from itself only if one or more of the following conditions
is true:
 - the process has not the tag (it is not an error)
 - the process has the tag "smack-tags:sub" and the tag to remove
   is not a special tag
 - the process has the tag "smack-tags:sub-all"
 - the process has the capability CAP_MAC_ADMIN

Normally a process can not add tags to itself. A process can add a tag
to itself only if one or more of the following conditions is true:
 - the process already has the tag (it is not an error)
 - the process has the tag "smack-tags:add" and the tag to add
   is not a special tag
 - the process has the tag "smack-tags:add-all"
 - the process has the capability CAP_MAC_ADMIN

Even if a process has the right to add tags, it can be canceled
(ECANCELED) if the count of tag reached is too high. The current limit
is 1000. This limit is global to the kernel, not by process.

Normally a process can not add or remove tags of other processes. But it
can do that if it has the tag "smack-tags:set-others".

Tags are copied to the children processes during 'clone'.

Normally, all tags are discarded during 'exec'. But this is changed by
the following tags: "smack-tags:keep-all" and "smack-tags:keep".

The rule is that during 'exec':
 - processes having the tag "smack-tags:keep-all" keep all there tags;
 - otherwise, processes having "smack-tags:keep" keep the tags that are
   not specials;
 - otherwise, processes lose all their tags.

Because changes only occur through tag files accesses, the notifications
might be available to any possible observer.

Signed-off-by: José Bollo <jo...@nonadev.net>
---
 fs/proc/base.c              |   3 +
 security/smack/Kconfig      |   8 +
 security/smack/Makefile     |   1 +
 security/smack/smack.h      |   7 +
 security/smack/smack_lsm.c  |  39 +++
 security/smack/smack_tags.c | 641
++++++++++++++++++++++++++++++++++++++++++++
 security/smack/smack_tags.h |  40 +++
 7 files changed, 739 insertions(+)
 create mode 100644 security/smack/smack_tags.c
 create mode 100644 security/smack/smack_tags.h

diff --git a/fs/proc/base.c b/fs/proc/base.c
index b25eee4..d6186a1 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2406,6 +2406,9 @@ static const struct pid_entry attr_dir_stuff[] = {
        REG("fscreate",   S_IRUGO|S_IWUGO, proc_pid_attr_operations),
        REG("keycreate",  S_IRUGO|S_IWUGO, proc_pid_attr_operations),
        REG("sockcreate", S_IRUGO|S_IWUGO, proc_pid_attr_operations),
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+       REG("tags",       S_IRUGO|S_IWUGO, proc_pid_attr_operations),
+#endif
 };
 
 static int proc_attr_dir_readdir(struct file *file, struct dir_context
*ctx)
diff --git a/security/smack/Kconfig b/security/smack/Kconfig
index 271adae..438012fd 100644
--- a/security/smack/Kconfig
+++ b/security/smack/Kconfig
@@ -40,3 +40,11 @@ config SECURITY_SMACK_NETFILTER
          This enables security marking of network packets using
          Smack labels.
          If you are unsure how to answer this question, answer N.
+
+config SECURITY_SMACK_TAGS
+       bool "Handling smack tags"
+       depends on SECURITY_SMACK
+       default n
+       help
+         To be done
+
diff --git a/security/smack/Makefile b/security/smack/Makefile
index ee2ebd5..f2bd836 100644
--- a/security/smack/Makefile
+++ b/security/smack/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_SECURITY_SMACK) := smack.o
 
 smack-y := smack_lsm.o smack_access.o smackfs.o
 smack-$(CONFIG_SECURITY_SMACK_NETFILTER) += smack_netfilter.o
+smack-$(CONFIG_SECURITY_SMACK_TAGS) += smack_tags.o
diff --git a/security/smack/smack.h b/security/smack/smack.h
index fff0c61..09cbac8 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -110,11 +110,18 @@ struct inode_smack {
        int                     smk_flags;      /* smack inode flags */
 };
 
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+struct smack_tagset;
+#endif
 struct task_smack {
        struct smack_known      *smk_task;      /* label for access control */
        struct smack_known      *smk_forked;    /* label when forked */
        struct list_head        smk_rules;      /* per task access rules */
        struct mutex            smk_rules_lock; /* lock for the rules */
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+       struct smack_tagset     *smk_tagset;    /* set of tags */
+       unsigned int            smk_tagmask;    /* mask of the task */
+#endif
 };
 
 #define        SMK_INODE_INSTANT       0x01    /* inode is instantiated */
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index c2d66ca..3751318 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -44,6 +44,10 @@
 #include <linux/parser.h>
 #include "smack.h"
 
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+#include "smack_tags.h"
+#endif
+
 #define TRANS_TRUE     "TRUE"
 #define TRANS_TRUE_SIZE        4
 
@@ -925,6 +929,10 @@ static void smack_bprm_committing_creds(struct
linux_binprm *bprm)
 
        if (bsp->smk_task != bsp->smk_forked)
                current->pdeath_signal = 0;
+
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+       smack_tags_task_committing(bsp);
+#endif
 }
 
 /**
@@ -1900,6 +1908,9 @@ static int smack_cred_alloc_blank(struct cred
*cred, gfp_t gfp)
        if (tsp == NULL)
                return -ENOMEM;
 
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+       smack_tags_task_alloc_blank(tsp, gfp);
+#endif
        cred->security = tsp;
 
        return 0;
@@ -1927,6 +1938,9 @@ static void smack_cred_free(struct cred *cred)
                list_del(&rp->list);
                kfree(rp);
        }
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+       smack_tags_task_free(tsp);
+#endif
        kfree(tsp);
 }
 
@@ -1953,6 +1967,11 @@ static int smack_cred_prepare(struct cred *new,
const struct cred *old,
        if (rc != 0)
                return rc;
 
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+       rc = smack_tags_task_prepare(new_tsp, old_tsp, gfp);
+       if (rc != 0)
+               return rc;
+#endif
        new->security = new_tsp;
        return 0;
 }
@@ -1973,6 +1992,9 @@ static void smack_cred_transfer(struct cred *new,
const struct cred *old)
        new_tsp->smk_forked = old_tsp->smk_task;
        mutex_init(&new_tsp->smk_rules_lock);
        INIT_LIST_HEAD(&new_tsp->smk_rules);
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+       smack_tags_task_transfer(new_tsp, old_tsp);
+#endif
 
 
        /* cbs copy rule list */
@@ -3525,6 +3547,11 @@ static int smack_getprocattr(struct task_struct
*p, char *name, char **value)
        char *cp;
        int slen;
 
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+       if (strcmp(name, "tags") == 0)
+               return smack_tags_read(p, value);
+#endif
+
        if (strcmp(name, "current") != 0)
                return -EINVAL;
 
@@ -3556,6 +3583,11 @@ static int smack_setprocattr(struct task_struct
*p, char *name,
        struct cred *new;
        struct smack_known *skp;
 
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+       if (strcmp(name, "tags") == 0)
+               return smack_tags_write(p, value, size);
+#endif
+
        /*
         * Changing another process' Smack value is too dangerous
         * and supports no sane use case.
@@ -4734,6 +4766,9 @@ static __init int smack_init(void)
 #ifdef SMACK_IPV6_SECMARK_LABELING
        pr_info("Smack:  IPv6 Netfilter enabled.\n");
 #endif
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+       pr_info("Smack:  Tags enabled.\n");
+#endif
 
        /*
         * Set the security state for the initial task.
@@ -4744,6 +4779,10 @@ static __init int smack_init(void)
        /* initialize the smack_known_list */
        init_smack_known_list();
 
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+       smack_tags_init();
+#endif
+
        /*
         * Register with LSM
         */
diff --git a/security/smack/smack_tags.c b/security/smack/smack_tags.c
new file mode 100644
index 0000000..9f1f1df
--- /dev/null
+++ b/security/smack/smack_tags.c
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2015 José Bollo <jo...@nonadev.net>
+ *
+ *      This program is free software; you can redistribute it and/or
modify
+ *      it under the terms of the GNU General Public License as
published by
+ *      the Free Software Foundation, version 2.
+ *
+ * Author:
+ *      José Bollo <jo...@nonadev.net>
+ */
+
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include "smack.h"
+#include "smack_tags.h"
+
+/* maximum count of tags allowed */
+#define MAX_TAG_COUNT          1000
+
+/* maximum lengh for a single tag name */
+#define MAX_TAG_LENGTH         1000
+
+/*
+ * predefined tags: their names, their masking value
+ */
+
+/* prefix of predefined tags */
+#define SMACKTAG_PREFIX                "smack-tags:"
+
+#define SMACKTAG_ADD           SMACKTAG_PREFIX"add"
+#define SMACKTAG_SUB           SMACKTAG_PREFIX"sub"
+#define SMACKTAG_KEEP          SMACKTAG_PREFIX"keep"
+#define SMACKTAG_ADD_ALL       SMACKTAG_PREFIX"add-all"
+#define SMACKTAG_SUB_ALL       SMACKTAG_PREFIX"sub-all"
+#define SMACKTAG_KEEP_ALL      SMACKTAG_PREFIX"keep-all"
+#define SMACKTAG_SET_OTHERS    SMACKTAG_PREFIX"set-others"
+
+#define FLAG_ADD               1
+#define FLAG_SUB               2
+#define FLAG_KEEP              4
+#define FLAG_ADD_ALL           8
+#define FLAG_SUB_ALL           16
+#define FLAG_KEEP_ALL          32
+#define FLAG_SET_OTHERS                64
+
+
+/*
+ * Definition of the tags
+ */
+struct smack_tag {
+       struct smack_tag *next; /* next tag in hashed list */
+       unsigned int mask;      /* mask of the tag */
+       unsigned int length;    /* length in byte of the tag's name */
+       unsigned int hash;      /* hash code of the name */
+};
+
+/* macro to access the tag's name */
+#define TAGNAME(tag)     ((char*)((&((tag)->hash))+1))
+
+
+/*
+ * The hash table of tags
+ */
+#define TAGHASHSIZE      64    /* size of the hash table */
+
+/* the size of the tag's hash table must be a power of 2 */
+#if (TAGHASHSIZE & (TAGHASHSIZE - 1)) != 0
+#error "TAGHASHSIZE MUST be a power of 2"
+#endif
+
+/* global mutex to access the tag's hash table */
+static DEFINE_MUTEX(smack_tags_htable_lock);
+
+/* the tag's hash table */
+static struct smack_tag *htable_of_tags[TAGHASHSIZE];
+
+/* macro to get/set the head of the hashed list */
+#define TAGHEAD(hash)    htable_of_tags[(hash)&((TAGHASHSIZE)-1)]
+
+/* count of tags created */
+static unsigned int count_of_tags;
+
+/*
+ * The tagsets
+ */
+struct smack_tagset {  /* a tag set is a simple linked list */
+       const struct smack_tag *tag;    /* the tag */
+       struct smack_tagset *next;      /* next item of the list */
+};
+
+/* global mutex to access the list of free tag set links */
+static DEFINE_MUTEX(smack_tags_tagset_lock);
+
+/* the list of free tagset links */
+static struct smack_tagset *free_sets;
+
+/*
+ * smack_tags_valid_name - Checks if a tag's name is valid.
+ *
+ * @name: string for the name of the tag
+ * @length: length in bytes of the tag's name
+ *
+ * Returns 1 if the name is valid or otherwise returns 0
+ */
+static int smack_tags_valid_name(const char *name, unsigned int length)
+{
+       unsigned int index;
+
+       /* check the length */
+       if (!length || length > MAX_TAG_LENGTH)
+               return 0;
+
+       /* check the characters */
+       for (index = 0 ; index < length ; index++)
+               if (unlikely(32 >= (unsigned)name[index]
+                                               || name[index] == '\x7f'))
+                       return 0;
+
+       /* valid if here */
+       return 1;
+}
+
+/*
+ * smack_tags_get - Get an existing or new tag
+ *
+ * @name: string for the name of the tag
+ * @length: length in bytes of the tag's name
+ * @create: boolean indicator that allows creation if true
+ * @flags: flags for kernel allocations
+ *
+ * Returns the tag found.
+ *
+ * If the tag is not found and 'create' == 0, returns NULL.
+ * If the tag is not found and 'create' != 0, tries to create
+ * and return the new tag. Returns NULL on creation failure.
+ * The creation can fail either if the maximum count of tags
+ * was created or if the allocation of memory failed.
+ */
+static struct smack_tag *smack_tags_get(const char *name,
+                                       unsigned int length,
+                                       int create,
+                                       gfp_t flags)
+{
+       struct smack_tag *tag;
+       unsigned int hash;
+       size_t size;
+
+       /* compute the hash code */
+       hash = full_name_hash(name, length);
+
+       /* search the tag */
+       mutex_lock(&smack_tags_htable_lock);
+       tag = TAGHEAD(hash);
+       while (tag) {
+               if (tag->hash == hash && tag->length == tag->length
+                  && 0 == memcmp(name, TAGNAME(tag), length))
+                       goto found;
+               tag = tag->next;
+       }
+
+       /* not found */
+       if (unlikely(create != 0) && likely(count_of_tags < MAX_TAG_COUNT)) {
+
+               /* compute the size */
+               size = length + (size_t)TAGNAME((struct smack_tag*)0);
+
+               /* allocation */
+               tag = kmalloc(size, flags);
+               if (likely(tag)) {
+                       /* initialisation */
+                       tag->mask = 0;
+                       tag->length = length;
+                       tag->hash = hash;
+                       memcpy(TAGNAME(tag), name, length);
+                       /* insert ahead */
+                       tag->next = TAGHEAD(hash);
+                       TAGHEAD(hash) = tag;
+                       count_of_tags++;
+               }
+       }
+
+found:
+       mutex_unlock(&smack_tags_htable_lock);
+       return tag;
+}
+
+/*
+ * smack_tags_make_predefined - Creates a predefined tag
+ *
+ * @name: string for the name of the tag
+ * @mask: the mask to set to the tag
+ */
+static void smack_tags_make_predefined(const char *name, unsigned int
mask)
+{
+       struct smack_tag *tag;
+
+       tag = smack_tags_get(name, (unsigned int)strlen(name), 1, GFP_KERNEL);
+       if (likely(tag))
+               tag->mask = mask;
+}
+
+/*
+ * smack_tags_set_clone - Clones a tag set
+ *
+ * @set: the tag set to clone
+ * @result: a not-null pointer to where store the cloned tag set
+ * @flags: flags for kernel allocations
+ *
+ * Returns 1 if the tag set is cloned or 0 otherwise if an allocation
+ * of memory failed.
+ */
+static int smack_tags_set_clone(struct smack_tagset *set,
+                                       struct smack_tagset **result,
+                                       gfp_t flags)
+{
+       struct smack_tagset **prev;
+       struct smack_tagset *head;
+       struct smack_tagset *next;
+       struct smack_tagset *iter;
+
+       iter = set;
+
+       /* first loop: use free allocated links */
+       mutex_lock(&smack_tags_tagset_lock);
+       head = free_sets;
+       prev = &head;
+       next = free_sets;
+       while (iter && next) {
+               next->tag = iter->tag;
+               iter = iter->next;
+               prev = &next->next;
+               next = next->next;
+       }
+       free_sets = next;
+       mutex_unlock(&smack_tags_tagset_lock);
+
+       /* second loop: allocate links */ 
+       while (iter) {
+               next = kmalloc(sizeof(*next), flags);
+               if (!next) {
+                       mutex_lock(&smack_tags_tagset_lock);
+                       *prev = free_sets;
+                       free_sets = head;
+                       mutex_unlock(&smack_tags_tagset_lock);
+                       return 0;
+               }
+               *prev = next;
+               next->tag = iter->tag;
+               iter = iter->next;
+               prev = &next->next;
+       }
+       *prev = NULL;
+       *result = head;
+       return 1;
+}
+
+/*
+ * smack_tags_set_free - Frees the tag set links
+ *
+ * @head: head of the tag set to free
+ */
+static void smack_tags_set_free(struct smack_tagset *head)
+{
+       struct smack_tagset *tail;
+
+       if (head) {
+               for (tail = head ; tail->next ; tail = tail->next);
+               mutex_lock(&smack_tags_tagset_lock);
+               tail->next = free_sets;
+               free_sets = head;
+               mutex_unlock(&smack_tags_tagset_lock);
+       }
+}
+
+/*
+ * smack_tags_sub - Removes one tag
+ *
+ * @tsp: smack structure of the target task
+ * @name: string for the name of the tag
+ * @length: length in bytes of the tag's name
+ * @privileged: boolean telling if privileged
+ *
+ * Returns 0 in case of success or -EINVAL if the name is invalid
+ */
+static int smack_tags_sub(struct task_smack *tsp, const char *name,
+                                       unsigned int length, int privileged)
+{
+       const struct smack_tag *tag;
+       struct smack_tagset **previous;
+       struct smack_tagset *iter;
+
+       /* checks validity of the tag's name */
+       if (!smack_tags_valid_name(name, length))
+               return -EINVAL;
+
+       /* search the tag, never create it and silently ignore unkown tags */
+       tag = smack_tags_get(name, length, 0, GFP_KERNEL);
+       if (tag) {
+               /* search the tag in 'tsp', silently ignore tags not owned */
+               previous = &tsp->smk_tagset;
+               iter = tsp->smk_tagset;
+               while (iter && iter->tag != tag) {
+                       previous = &iter->next;
+                       iter = iter->next;
+               }
+               if (iter) {
+                       /* found in 'tsp', check right to remove */
+                       if (!privileged && !(tsp->smk_tagmask & FLAG_SUB_ALL))
+                               if (tag->mask || !(tsp->smk_tagmask & FLAG_SUB))
+                                       return -EPERM;
+
+                       /* remove it */
+                       tsp->smk_tagmask &= ~tag->mask;
+                       *previous = iter->next;
+
+                       /* free the link */
+                       mutex_lock(&smack_tags_tagset_lock);
+                       iter->next = free_sets;
+                       free_sets = iter;
+                       mutex_unlock(&smack_tags_tagset_lock);
+               }
+       }
+       return 0;
+}
+
+
+/*
+ * smack_tags_add - Adds one tag
+ *
+ * @tsp: smack structure of the target task
+ * @name: string for the name of the tag
+ * @length: length in bytes of the tag's name
+ * @privileged: boolean telling if privileged
+ *
+ * Returns 0 in case of success or one of the following error code if
failed:
+ *   o -EINVAL if the name is invalid
+ *   o -EPERM if the addition is forbidden
+ *   o -ENOMEM if the an allocation failed
+ *   o -ECANCELED if the maximum count of tag is reached
+ */
+static int smack_tags_add(struct task_smack *tsp, const char *name,
+                                       unsigned int length, int privileged)
+{
+       const struct smack_tag *tag;
+       struct smack_tagset **previous;
+       struct smack_tagset *iter;
+       int create;
+
+       /* checks validity of the tag's name */
+       if (!smack_tags_valid_name(name, length))
+               return -EINVAL;
+
+       /* get the tag, avoid creation not needed */
+       create = privileged || (tsp->smk_tagmask & FLAG_ADD);
+       tag = smack_tags_get(name, length, create, GFP_KERNEL);
+       if (!tag)
+               return !create ? -EPERM :
+                       count_of_tags < MAX_TAG_COUNT ? -ENOMEM : -ECANCELED;
+
+       /* search the tag in 'tsp', silently ignore tags already owned */
+       previous = &tsp->smk_tagset;
+       iter = tsp->smk_tagset;
+       while (iter && iter->tag != tag) {
+               previous = &iter->next;
+               iter = iter->next;
+       }
+       if (!iter) {
+               /* not found, check right to add */
+               if (!privileged && !(tsp->smk_tagmask & FLAG_ADD_ALL))
+                       if (tag->mask || !(tsp->smk_tagmask & FLAG_ADD))
+                               return -EPERM;
+
+               /* allocates the link*/
+               mutex_lock(&smack_tags_tagset_lock);
+               iter = free_sets;
+               if (iter) {
+                       free_sets = iter->next;
+                       mutex_unlock(&smack_tags_tagset_lock);
+               } else {
+                       mutex_unlock(&smack_tags_tagset_lock);
+                       iter = kmalloc(sizeof(*iter), GFP_KERNEL);
+                       if (!iter)
+                               return -ENOMEM;
+               }
+
+               /* adds the tag */
+               iter->tag = tag;
+               iter->next = NULL;
+               *previous = iter;
+               tsp->smk_tagmask |= tag->mask;
+       }
+       return 0;
+}
+
+/*
+ * smack_tags_write - Implement the writing of the tags
+ *
+ * @task: the task read
+ * @value: a pointer to the written data
+ * @size: the size of the written data
+ *
+ * Returns the positive count of byte written. It can be less than the
+ * count given by size if an error appears after. This count indicates
+ * the count of data treated without errors.
+ * Returns one of the negative error code below if the data begins on
error:
+ *   o -EINVAL if the name or the syntax is invalid
+ *   o -EPERM if the addition is forbidden
+ *   o -ENOMEM if the an allocation failed
+ *   o -ECANCELED if the maximum count of tag is reached
+ */
+int smack_tags_write(struct task_struct *task, char *value, size_t
size)
+{
+       unsigned int start, stop, len;
+       int err, privileged;
+       struct task_smack *tsp;
+
+       /* retrieves the smack structure of the current task */
+       tsp = (struct task_smack *)current_cred_xxx(security);
+
+       /* check if writting itself or has the tag "set-others" */
+       if (task != current && !(tsp->smk_tagmask & FLAG_SET_OTHERS))
+               return -EPERM; /* can't write others */
+
+       /* retrieves the smack structure of the task */
+       tsp = (struct task_smack *)task_cred_xxx(task, security);
+
+       /* compute if privileged */
+       privileged = smack_privileged(CAP_MAC_ADMIN);
+
+       /* begin the parsing */
+       start = 0;
+       while (start < size) {
+               /* scan a line of 'len' */
+               for (stop = start; stop < size && value[stop] != '\n'; stop++);
+               len = stop - start;
+
+               /* ignore empty lines */
+               if (!len)
+                       err = 0;
+
+               /* lines not terminated with '\n' */
+               else if (stop == size)
+                       err = -EINVAL;
+
+               /* line starting with '+' */
+               else if (value[start] == '+')
+                       err = smack_tags_add(tsp, value + start + 1, 
+                                                       len - 1, privileged);
+
+               /* lines not starting with '+' or '-' */
+               else if (value[start] != '-')
+                       err = -EINVAL;
+
+               /* lines having removing one tag */
+               else if (len > 1)
+                       err = smack_tags_sub(tsp, value + start + 1,
+                                                       len - 1, privileged);
+
+               /* lines removing all tags */
+               else {
+                       smack_tags_set_free(tsp->smk_tagset);
+                       tsp->smk_tagset = NULL;
+                       tsp->smk_tagmask = 0;
+                       err = 0;
+               }
+
+               /* treat the error case if any */
+               if (err)
+                       return start ? start : err;
+
+               /* parse next line */
+               start = stop + 1;
+       }
+       return start;
+}
+
+/*
+ * smack_tags_read - Implement the reading of the tags
+ *
+ * @task: the task read
+ * @value: a pointer for storing the read result
+ *
+ * Returns the count of byte read or the negative code -ENOMEM
+ * if an allocation failed.
+ */
+int smack_tags_read(struct task_struct *task, char **value)
+{
+       struct task_smack *tsp; 
+       int length, len;
+       struct smack_tagset *iter;
+       char *buffer;
+
+       /* retrieves the smack structure of the target task */
+       tsp = (struct task_smack *)task_cred_xxx(task, security);
+
+       /* compute the read length */
+       length = 0;
+       iter = tsp->smk_tagset;
+       while (iter) {
+               len = (int)(iter->tag->length);
+               length += 1 + len;
+               iter = iter->next;
+       }
+
+       /* allocates storage for the result */
+       buffer = kmalloc(length, GFP_KERNEL);
+       if (buffer == NULL)
+               return -ENOMEM;
+
+       /* store the result */
+       length = 0;
+       iter = tsp->smk_tagset;
+       while (iter) {
+               len = (int)(iter->tag->length);
+               memcpy(buffer + length, TAGNAME(iter->tag), len);
+               length += len;
+               buffer[length++] = '\n';
+               iter = iter->next;
+       }
+       *value = buffer;
+       return length;
+}
+
+/*
+ * smack_tags_task_free - Frees tag set on security_free_creds
+ *
+ * @tsp: the smack structure to process
+ */
+void smack_tags_task_free(struct task_smack *tsp)
+{
+       smack_tags_set_free(tsp->smk_tagset);
+}
+
+/*
+ * smack_tags_task_prepare - Prepares tag set on security_prepare_creds
+ *
+ * @new_tsp: the smack structure to prepare
+ * @old_tsp: the smack structure of reference
+ * @gfp: flags for kernel allocations
+ *
+ * Returns 0 on success or -ENOMEM on memory allocation failure.
+ */
+int smack_tags_task_prepare(struct task_smack *new_tsp,
+                           const struct task_smack *old_tsp, gfp_t gfp)
+{
+       struct smack_tagset *tagset;
+
+       if (!smack_tags_set_clone(old_tsp->smk_tagset, &tagset, gfp))
+               return -ENOMEM;
+       new_tsp->smk_tagset = tagset;
+       new_tsp->smk_tagmask = old_tsp->smk_tagmask;
+       return 0;
+}
+
+/*
+ * smack_tags_task_committing - Prunes tag set on
security_bprm_committing_creds
+ *
+ * @tsp: the smack structure to process
+ */
+void smack_tags_task_committing(struct task_smack *tsp)
+{
+       struct smack_tagset *iter;
+       struct smack_tagset *head;
+       struct smack_tagset **kept;
+       struct smack_tagset **thrown;
+
+       /* check if the tag set must be pruned of the predefined tags */
+       iter = tsp->smk_tagset;
+       if (!(tsp->smk_tagmask & FLAG_KEEP_ALL) && iter) {
+               if (tsp->smk_tagmask & FLAG_KEEP) {
+                       /* split in what is kept and what is thrown */
+                       thrown = &head;
+                       kept = &tsp->smk_tagset;
+                       while (iter) {
+                               if (iter->tag->mask) {
+                                       *thrown = iter;
+                                       thrown = &iter->next;
+                               } else {
+                                       *kept = iter;
+                                       kept = &iter->next;
+                               }
+                               iter = iter->next;
+                       }
+                       /* finalize the kept tag set */
+                       *kept = NULL;
+               } else {
+                       /* remove all */
+                       head = iter;
+                       while (iter->next)
+                               iter = iter->next;
+                       thrown = &iter->next;
+                       tsp->smk_tagset = NULL;
+               }
+               tsp->smk_tagmask = 0;
+               /* free the thrown links */
+               mutex_lock(&smack_tags_tagset_lock);
+               *thrown = free_sets;
+               free_sets = head;
+               mutex_unlock(&smack_tags_tagset_lock);
+       }
+}
+
+/*
+ * smack_tags_task_transfer - Transfers tag set on
security_transfer_creds
+ *
+ * @new_tsp: the target smack structure
+ * @old_tsp: the origin smack structure
+ */
+void smack_tags_task_transfer(struct task_smack *new_tsp,
+                             struct task_smack *old_tsp)
+{
+       new_tsp->smk_tagset = old_tsp->smk_tagset;
+       new_tsp->smk_tagmask = old_tsp->smk_tagmask;
+       old_tsp->smk_tagset = NULL;
+       old_tsp->smk_tagmask = 0;
+}
+
+
+/*
+ * smack_tags_init - Initialize the smack's submodule for tags
+ */
+void smack_tags_init()
+{
+       /* create the predefined tags */
+       smack_tags_make_predefined(SMACKTAG_ADD, FLAG_ADD);
+       smack_tags_make_predefined(SMACKTAG_SUB, FLAG_SUB);
+       smack_tags_make_predefined(SMACKTAG_KEEP, FLAG_KEEP);
+       smack_tags_make_predefined(SMACKTAG_ADD_ALL, FLAG_ADD_ALL);
+       smack_tags_make_predefined(SMACKTAG_SUB_ALL, FLAG_SUB_ALL);
+       smack_tags_make_predefined(SMACKTAG_KEEP_ALL, FLAG_KEEP_ALL);
+       smack_tags_make_predefined(SMACKTAG_SET_OTHERS, FLAG_SET_OTHERS);
+}
+
+#endif
+
+
diff --git a/security/smack/smack_tags.h b/security/smack/smack_tags.h
new file mode 100644
index 0000000..6f87940
--- /dev/null
+++ b/security/smack/smack_tags.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 José Bollo <jo...@nonadev.net>
+ *
+ *      This program is free software; you can redistribute it and/or
modify
+ *      it under the terms of the GNU General Public License as
published by
+ *      the Free Software Foundation, version 2.
+ *
+ * Author:
+ *      José Bollo <jo...@nonadev.net>
+ *
+ */
+
+#ifdef CONFIG_SECURITY_SMACK_TAGS
+
+#ifndef _SECURITY_SMACK_TAGS_H
+#define _SECURITY_SMACK_TAGS_H
+
+
+void smack_tags_init( void );
+
+void smack_tags_task_free(struct task_smack *);
+
+#define smack_tags_task_alloc_blank(new_tsp,gfp)
+
+int smack_tags_task_prepare(struct task_smack *,
+                           const struct task_smack *, gfp_t);
+
+void smack_tags_task_transfer(struct task_smack *,
+                             struct task_smack *);
+
+void smack_tags_task_committing(struct task_smack *);
+
+int smack_tags_read(struct task_struct *p, char **);
+
+int smack_tags_write(struct task_struct *p, char *, size_t);
+
+#endif
+
+#endif
+
-- 
2.1.4



--
To unsubscribe from this list: send the line "unsubscribe 
linux-security-module" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to