From: José Bollo <jo...@nonadev.net>

The ptags module allows to attach tags to processes.
Tags are accessed through the new files /proc/PID/attr/ptags
and /proc/PID/tasks/TID/attr/ptags (named below "ptag file").

See Documentation/security/ptags.txt for details

Signed-off-by: José Bollo <jo...@nonadev.net>
---
 Documentation/security/ptags.txt |  428 +++++++
 fs/proc/base.c                   |    3 +
 include/linux/cred.h             |    3 +
 include/linux/user_namespace.h   |   23 +
 kernel/cred.c                    |    6 +
 kernel/user_namespace.c          |    3 +
 security/Kconfig                 |    1 +
 security/Makefile                |    2 +
 security/apparmor/lsm.c          |   14 +-
 security/ptags/Kconfig           |   16 +
 security/ptags/Makefile          |    6 +
 security/ptags/ptags-lsm.c       |  213 ++++
 security/ptags/ptags.c           | 2377 ++++++++++++++++++++++++++++++++++++++
 security/selinux/hooks.c         |    8 +
 security/smack/smack_lsm.c       |   14 +-
 15 files changed, 3113 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/security/ptags.txt
 create mode 100644 security/ptags/Kconfig
 create mode 100644 security/ptags/Makefile
 create mode 100644 security/ptags/ptags-lsm.c
 create mode 100644 security/ptags/ptags.c

diff --git a/Documentation/security/ptags.txt b/Documentation/security/ptags.txt
new file mode 100644
index 0000000..ef3c131
--- /dev/null
+++ b/Documentation/security/ptags.txt
@@ -0,0 +1,428 @@
+The PTAGS security module
+=========================
+
+
+Overview
+--------
+
+The PTAGS module allows to attach tags to processes. Tags are accessed
+through the files /proc/<PID>/attr/ptags and /proc/<PID>/tasks/<TID>/attr/ptags
+(named below "ptags file").
+
+The module PTAGS is part of the security of linux for 2 reasons:
+
+ 1. it is built on top of the Linux Security Module (LSM) infrastructure
+    as it exists since v4.1
+
+ 2. it is a foundation for building permissions, privileges and cookies based
+    security in user land.
+
+The tags are public by nature. Accesses to the ptags file are not restricted
+except by standards DAC and MAC for files: any process having read access to
+/proc/<PID>/attr/ptags or to /proc/<PID>/tasks/<TID>/attr/ptags can read the
+tags of the task (and their value if any).
+
+Writing on tags files is also possible and subject to DAC and MAC rules.
+
+Writing a ptags file allows (under conditions) to change the tags of a
+process. When writting the file, it acts like a protocol accepting
+one or more lines. This protocol allows to add new tags, to remove existing
+tags, to attach values to tags, to tell wether the tag is kept or not on
+execution of the system call "execve".
+
+The accepted lines are:
+
+ - empty line: "\n"
+
+    Only a new-line
+
+ - comment: "#...anything...\n"
+
+     A sharp followed by anything and a new-line
+
+ - action:
+
+     Action lines begins with one of the following characters:
+
+      o + (plus) adds a tag and/or set keep flags
+      o - (minus) removes tags and/or keep flags
+      o ! (bang) assign a value to a tag
+      o ? (question mark) query existence of tags and/or keep flags
+
+
+Example of using ptags
+----------------------
+
+Usages of ptags are multiple. One of the main use is setting privileges,
+permissions or cookies to processes in the user land.
+
+For this example, a server S will be queried to give permissions to a process.
+The permissions given are handled by a simple tag: one permission, one tag.
+Because the server S doesn't want to interfere with other permission managers,
+it prefixes its permissions with the prefix "S:".
+
+To be able to add and remove permissions aof prefix "S:" to other processes,
+the server process S must have the 3 special tags below:
+
+ - ptags:S:add
+ - ptags:S:others
+ - ptags:S:sub
+
+This means that the process can add and sub tags prefixed by S: for itself and
+for other processes.
+
+Having this tags, the server can now add tags prefixed by S: to other
+processes by writing the ptags file /proc/<PID>/attr/ptags as if the following
+command was issued:
+
+ $ echo "+S:PERMISSION-NAME" > /proc/<PID>/attr/ptags
+
+This will add the tags S:PERMISSION-NAME to the process of <PID>.
+In that case, the "keep flag" is not set.
+
+The fact that this means that the permission S:PERMISSION-NAME is given
+to the process is only a convention between processes of the user land.
+In the user land, checking if a process has or not the tag S:PERMISSION-NAME
+is done by reading the ptags file /proc/<PID>/attr/ptags as if the following
+command was issued:
+
+ $ grep -q '^S:PERMISSION-NAME$' /proc/<PID>/attr/ptags
+
+The following command use ptags protocol and has the same behaviour:
+
+ $ echo "?S:PERMISSION-NAME" > /proc/<PID>/attr/ptags
+
+The role of the ptags module is to ensure that the given tags are attached
+to the process and will die with it.
+
+The module ptags also allows the tags to be automatically copied to cloned
+processes (forks and threads) and manages how tags are kept or not when
+the system call "execve" is invoked.
+
+In the above example, the tag S:PERMISSION-NAME is removed when execve is
+called because the keep flag is not set.
+
+To set the keep flag, an @ (at sign) must prefix the tag name in the + (add)
+command, as if the following command was issued:
+
+ $ echo "+@S:PERMISSION-NAME" > /proc/<PID>/attr/ptags
+
+It is possible to attach a value to a tag. Thos achieve it, the process must
+have the special tag "ptags:S:set". This is done by writing the ptags file
+as with the command:
+
+ $ echo "!S:PERMISSION-NAME=VALUE" > /proc/<PID>/attr/ptags
+
+
+Structure of tags
+-----------------
+
+Tags are structured in fields separated by : (colon). For example, the tag
+"scope:subscope:item" is made of 3 fields: "scope", "subscope" and "item".
+
+Tags whose first field is "ptags" are specials: they are used to give
+permissions to modify tags.
+
+Note that fields can be empty: the tag ":x::a" is a valid tag.
+
+The structure of the tags allows to define prefix and pattern, the pattern
+"scope:subscope:*" matches any tag that has the prefix "scope:subscope:".
+Matching ":*" is allowed and matches any tag beginning with :.
+
+
+Tag Validity (TV) Rules
+-----------------------
+
+The following rules detail what are valid tags:
+
+(TV.1)
+
+   Tags are valid utf-8 strings with a maximum length of 4000 bytes (the count
+   of char is lower or equal to 4000 because utf8 encoding).
+
+(TV.2)
+
+   Tags must contain neither control characters (code lower than 32),
+   nor '=' (equal), nor '*' (star), nor DEL (ASCII code 127). (note that tags
+   can contain spaces but can't contain tab)
+
+(TV.3)
+
+   Tags must neither begin with @ (at sign) nor end with : (colon).
+
+(TV.4)
+
+   Tags starting with the string "ptags:" (6 characters) must end with
+   one of the following strings: ":add", ":sub", ":set", ":others". For
+   example, "ptags:add" (9 characters) and "ptags:myself:sub" (16 characters)
+   are valid tags but "ptags:myadd" (11 characters) is not valid.
+
+
+Value Validity (VV) rules
+-------------------------
+
+(VV.1)
+
+   Values are valid utf-8 strings with a maximum length of 32700 bytes (the
+   count of char is lower or equal to 32700 because utf8 encoding).
+
+(VV.2)
+
+   Values must contain neither control characters (code lower than 32),
+   nor DEL (ASCII code 127). (note that values can contain spaces but can't
+   contain tab)
+
+
+Pattern Validity (PV) rules
+---------------------------
+
+(PV.1)
+
+   Any valid tag (see TV) is a valid pattern matching only that tag.
+   This is an exact pattern.
+
+(PV.2)
+
+   Any valid tag (see TV) followed by :* (the two characters colon then star)
+   is a valid pattern matching any tag prefixed by the string before * (star).
+   This is a global pattern.
+
+Example: the pattern "S:*" will match "S:A:B.c" and "S:X" but will not match
+neither "S" nor "X".
+
+For actions matching global patterns, the action is performed where allowed and
+silentely not performed where not allowed.
+
+Action of reading the ptags file
+--------------------------------
+
+Any process allowed to read the ptags file of an other process (depending on
+DAC and MAC) has access to the tag set.
+
+Reading the ptags file returns valid utf8 lines, one tag per line. Each LINE
+has the following format:
+
+ LINE = KEEP-FLAG? TAG-NAME VALUE? LF
+ KEEP-FLAG = '@'
+ TAG-NAME = see TV rules
+ VALUE = '=' see VV rules
+
+The tags are listed in lexicographic order as below:
+
+a-tag
+@b-tag
+c-tag=value-of-c-tag
+
+The keep flag (@ at sign at start of the line) is ignored when sorting tags.
+On the above example, the b-tag is kept across "execve" while other tags are
+dropped. The value of c-tag is available for any reader after the equal sign.
+
+
+Action of adding for one tag
+----------------------------
+
+The action of adding one tag of name TAGNAME is done by writing either
+"+TAGNAME\n" or "+@TAGNAME\n" to the ptags file.
+
+The table below shows the effect of writing either +TAGNAME or +@TAGNAME
+when adding is allowed.
+
+      BEFORE   : +TAGNAME  : +@TAGNAME
+     ----------+-----------+---------------
+               : TAGNAME   : @TAGNAME
+      TAGNAME  : TAGNAME   : @TAGNAME
+      @TAGNAME : @TAGNAME  : @TAGNAME
+
+The table below shows the effect of writing either +TAGNAME or +@TAGNAME
+when adding is NOT allowed.
+
+      BEFORE   : +TAGNAME  : +@TAGNAME
+     ----------+-----------+---------------
+               :           :
+      TAGNAME  : TAGNAME   :
+      @TAGNAME : @TAGNAME  : @TAGNAME
+
+
+Action of adding the keep flag for global patterns
+--------------------------------------------------
+
+The action of adding the keep flag to tags matching the global pattern GLOB:*
+is done by writing "+@GLOB:*\n" to the ptags file.
+
+See the third columns of the tables above for effect of the command on tags
+matching the pattern.
+
+
+Action of removing for one tag
+------------------------------
+
+The action of removing the tag of name TAGNAME is done by writing
+"-TAGNAME\n" to the ptags file.
+
+The action of removing the keep flag tag of name TAGNAME is done by writing
+"-@TAGNAME\n" to the ptags file.
+
+The table below shows the effect of writing either -TAGNAME or -@TAGNAME
+when removing is allowed.
+
+      BEFORE   : -TAGNAME  : -@TAGNAME
+     ----------+-----------+---------------
+               :           :
+      TAGNAME  :           : TAGNAME
+      @TAGNAME :           : TAGNAME
+
+The table below shows the effect of writing either -TAGNAME or -@TAGNAME
+when removing is NOT allowed.
+
+      BEFORE   : -TAGNAME  : -@TAGNAME
+     ----------+-----------+---------------
+               :           :
+      TAGNAME  : TAGNAME   : TAGNAME
+      @TAGNAME : @TAGNAME  : @TAGNAME
+
+
+Action of removing for global patterns
+--------------------------------------
+
+The action of removing tags matching the global pattern GLOB:* is done by
+writing "-GLOB:*\n" to the ptags file.
+
+The action of removing the keep flag of tags matching the global pattern
+GLOB:* is done by writing "-@GLOB:*\n" to the ptags file.
+
+See the tables above for effect of the command on tags matching the pattern.
+
+Action of removing all
+----------------------
+
+The action of removing all tags is done by writing "-\n" to the ptags file.
+
+The action of removing the keep flag of all tags is done by writing "-@\n" to
+the ptags file.
+
+See the tables above for effect of the command on tags matching the pattern.
+
+
+Action of setting the value of one tag
+--------------------------------------
+
+The action of attaching VALUE to the tag of name TAGNAME is done by writing
+"!TAGNAME=VALUE\n" to the ptags file.
+
+The value can be removed (or set to nothing)  by writing "!TAGNAME=\n" or
+"!TAGNAMEn" to the ptags file.
+
+
+Querying existence of tags or keep flags of tags
+------------------------------------------------
+
+Writing "?PATTERN\n" returns an error if no tags matching pattern exist.
+
+Writing "?@PATTERN\n" returns an error if no tags matching pattern exist or if
+no tags matching pattern has the keep flag.
+
+
+Allowed actions
+---------------
+
+Any process can query any other processes (? command).
+
+Normally a process can not remove tags or keep flags from itself. A process can
+remove a tag or a keep flag from itself only if one or more of the following
+conditions is true:
+
+ - the process has not the tag or the keep flag (it is not an error)
+
+ - the process has the tag "ptags:sub" and the tag to remove
+   or whose keep flag is to remove is not a special tag
+
+ - the process has a tag "ptags:PREFIX:sub" where PREFIX is any valid prefix
+   and the tag to remove or whose keep flag is to remove has the prefix
+   "PREFIX:" (in particular, the tag "ptags:ptags:sub" allows to remove any
+   special tag)
+
+ - the process is a kernel's thread
+
+ - the process has the capability CAP_MAC_ADMIN according to user namespaces
+
+Normally a process can not add tags or keep flags to itself. A process can add
+a tag or keep flags to itself only if one or more of the following conditions
+is true:
+
+ - the process already has the tag or the keep flag (it is not an error)
+
+ - the process has the tag "ptags:add" and the tag to add or whose keep flags
+   is to set is not a special tag
+
+ - the process has a tag "ptags:PREFIX:add" where PREFIX is any valid prefix
+   and the tag to add or whose keep flag is to set has the prefix "PREFIX:"
+   (in particular, the tag "ptags:ptags:add" allows to add any special tag)
+
+ - the process is a kernel's thread
+
+ - the process has the capability CAP_MAC_ADMIN according to user namespaces
+
+Normally a process can not set values to its tags. A process can set values
+to its tags only if one or more of the following conditions is true:
+
+ - the process has the tag "ptags:set" and the tag to set is not a special tag
+
+ - the process has a tag "ptags:PREFIX:set"  where PREFIX is any valid prefix
+   and the tag to set to set has the prefix "PREFIX:"
+
+ - the process is a kernel's thread
+
+ - the process has the capability CAP_MAC_ADMIN according to user namespaces
+
+Normally a process can not modify tags of other processes. A process can modify
+the tag set of an other process only if one or more of the following conditions
+is true:
+
+ - the process is a kernel's thread
+
+ - the process has the capability CAP_MAC_ADMIN according to user namespaces
+
+ - the following two conditions are both true:
+
+   o the process could modify the tags if it was for itself
+
+   o one of the following condition is true:
+
+     # the process has the tag "ptags:others" and tags to modify are not
+       special tags
+
+     # the process has the tag "ptags:PREFIX:others"  where PREFIX is any valid
+       prefix and tags to modify have the prefix "PREFIX:" (in particular, the
+       tag "ptags:ptags:others" allows to modify special tags of other
+       processes)
+
+
+Writing to ptags files
+----------------------
+
+When writing lines to ptags file, using "write" call, the result is the count
+of bytes written without error. If the first line raise an error, an error is
+returned through errno. The returned errors are:
+
+   error       action    reason
+   --------------------------------------------------------------
+   EINVAL      +-!?      invalid value
+   EPERM       +-!       not allowed
+   ENOMEM      +!        out of memory
+   ENOENT      -?        no tag found
+   ECANCELED   +         maximum count of tags reached
+
+
+Watching ptags files
+--------------------
+
+Because tags are modified only through accesses to the ptags file, it is
+possible to monitor the inotify(7) interface to track possible changes.
+
+
+User land library
+-----------------
+
+A library for accessing and managing process tags is available for
+download at https://gitlab.com/jobol/ptags
+
diff --git a/fs/proc/base.c b/fs/proc/base.c
index ca651ac..94eb88e 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2532,6 +2532,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_PTAGS
+       REG("ptags",      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/include/linux/cred.h b/include/linux/cred.h
index f0e70a1..34777e4 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -139,6 +139,9 @@ struct cred {
 #endif
 #ifdef CONFIG_SECURITY
        void            *security;      /* subjective LSM security */
+#ifdef CONFIG_SECURITY_PTAGS
+       void            *ptags;         /* secured process tagging */
+#endif
 #endif
        struct user_struct *user;       /* real user ID subscription */
        struct user_namespace *user_ns; /* user_ns the caps and keyrings are 
relative to. */
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index eb209d4..8ae8c60 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -40,6 +40,9 @@ struct user_namespace {
        struct uid_gid_map      gid_map;
        struct uid_gid_map      projid_map;
        atomic_t                count;
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       atomic_t                weak_count;
+#endif
        struct user_namespace   *parent;
        int                     level;
        kuid_t                  owner;
@@ -95,6 +98,26 @@ static inline void put_user_ns(struct user_namespace *ns)
                __put_user_ns(ns);
 }
 
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+static inline struct user_namespace *get_weak_user_ns(struct user_namespace 
*ns)
+{
+       if (ns && atomic_inc_return(&ns->weak_count) == 1)
+               get_user_ns(ns);
+       return ns;
+}
+
+static inline void put_weak_user_ns(struct user_namespace *ns)
+{
+       if (ns && atomic_dec_and_test(&ns->weak_count))
+               put_user_ns(ns);
+}
+
+static inline int is_weak_user_ns_still_alive(struct user_namespace *ns)
+{
+       return ns && atomic_read(&ns->count) > 1;
+}
+#endif
+
 struct seq_operations;
 extern const struct seq_operations proc_uid_seq_operations;
 extern const struct seq_operations proc_gid_seq_operations;
diff --git a/kernel/cred.c b/kernel/cred.c
index 5f264fb..303f766 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -273,6 +273,9 @@ struct cred *prepare_creds(void)
 #ifdef CONFIG_SECURITY
        new->security = NULL;
 #endif
+#ifdef CONFIG_SECURITY_PTAGS
+       new->ptags = NULL;
+#endif
 
        if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
                goto error;
@@ -627,6 +630,9 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
 #ifdef CONFIG_SECURITY
        new->security = NULL;
 #endif
+#ifdef CONFIG_SECURITY_PTAGS
+       new->ptags = NULL;
+#endif
        if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
                goto error;
 
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 86b7854..66a14b6 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -114,6 +114,9 @@ int create_user_ns(struct cred *new)
        ns->ns.ops = &userns_operations;
 
        atomic_set(&ns->count, 1);
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       atomic_set(&ns->weak_count, 0);
+#endif
        /* Leave the new->user_ns reference with the new user namespace. */
        ns->parent = parent_ns;
        ns->level = parent_ns->level + 1;
diff --git a/security/Kconfig b/security/Kconfig
index 118f454..68c1e10 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -164,6 +164,7 @@ source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 source security/loadpin/Kconfig
 source security/yama/Kconfig
+source security/ptags/Kconfig
 
 source security/integrity/Kconfig
 
diff --git a/security/Makefile b/security/Makefile
index f2d71cd..85e7cd4 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -9,6 +9,7 @@ subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
 subdir-$(CONFIG_SECURITY_APPARMOR)     += apparmor
 subdir-$(CONFIG_SECURITY_YAMA)         += yama
 subdir-$(CONFIG_SECURITY_LOADPIN)      += loadpin
+subdir-$(CONFIG_SECURITY_PTAGS)                += ptags
 
 # always enable default capabilities
 obj-y                                  += commoncap.o
@@ -25,6 +26,7 @@ obj-$(CONFIG_SECURITY_APPARMOR)               += apparmor/
 obj-$(CONFIG_SECURITY_YAMA)            += yama/
 obj-$(CONFIG_SECURITY_LOADPIN)         += loadpin/
 obj-$(CONFIG_CGROUP_DEVICE)            += device_cgroup.o
+obj-$(CONFIG_SECURITY_PTAGS)           += ptags/
 
 # Object integrity file lists
 subdir-$(CONFIG_INTEGRITY)             += integrity
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 41b8cb1..8478b25 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -473,10 +473,16 @@ static int apparmor_getprocattr(struct task_struct *task, 
char *name,
 {
        int error = -ENOENT;
        /* released below */
-       const struct cred *cred = get_task_cred(task);
-       struct aa_task_cxt *cxt = cred_cxt(cred);
+       const struct cred *cred;
+       struct aa_task_cxt *cxt;
        struct aa_profile *profile = NULL;
 
+#ifdef CONFIG_SECURITY_PTAGS
+       if (strcmp(name, "ptags") == 0)
+               return 0;
+#endif
+       cred = get_task_cred(task);
+       cxt = cred_cxt(cred);
        if (strcmp(name, "current") == 0)
                profile = aa_get_newest_profile(cxt->profile);
        else if (strcmp(name, "prev") == 0  && cxt->previous)
@@ -504,6 +510,10 @@ static int apparmor_setprocattr(struct task_struct *task, 
char *name,
        size_t arg_size;
        int error;
 
+#ifdef CONFIG_SECURITY_PTAGS
+       if (strcmp(name, "ptags") == 0)
+               return 0;
+#endif
        if (size == 0)
                return -EINVAL;
        /* task can only write its own attributes */
diff --git a/security/ptags/Kconfig b/security/ptags/Kconfig
new file mode 100644
index 0000000..ab383d11
--- /dev/null
+++ b/security/ptags/Kconfig
@@ -0,0 +1,16 @@
+config SECURITY_PTAGS
+       bool "Secure process tagging support (PTAGS)"
+       depends on SECURITY
+       default n
+       help
+         PTags module allows to tag the processes through
+         the special files /proc/<pid>/attr/ptags.
+
+config SECURITY_PTAGS_WITH_USER_NS
+       bool "PTAGS with user namespace support (EXPERIMENTAL)"
+       depends on SECURITY_PTAGS && USER_NS
+       default n
+       help
+         Activating this option allows PTAGS module to
+         manage tags by user namespaces.
+
diff --git a/security/ptags/Makefile b/security/ptags/Makefile
new file mode 100644
index 0000000..f15098f
--- /dev/null
+++ b/security/ptags/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Tag module
+#
+
+obj-$(CONFIG_SECURITY_PTAGS) := ptags-lsm.o
+
diff --git a/security/ptags/ptags-lsm.c b/security/ptags/ptags-lsm.c
new file mode 100644
index 0000000..77bd19e
--- /dev/null
+++ b/security/ptags/ptags-lsm.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2016 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>
+ */
+
+#include <linux/types.h>
+
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/binfmts.h>
+#include <linux/lsm_hooks.h>
+#include <linux/printk.h>
+
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+#include <linux/user_namespace.h>
+#endif
+
+#include "ptags.c"
+
+#define set_ptags_of_cred(cred,value)  ((cred)->ptags = value)
+#define ptags_of_cred(cred)            ((struct ptags*)((cred)->ptags))
+#define ptags_of_bprm(bprm)            ptags_of_cred((bprm)->cred)
+#define ptags_of_task(task)            ((struct ptags*) \
+                                               task_cred_xxx(task, ptags))
+#define ptags_of_current()             ((struct ptags*) \
+                                               current_cred_xxx(ptags))
+
+/**
+ * ptags_is_ptags_file - Is 'name' of ptags entry?
+ *
+ * @name:      the name to test
+ *
+ * Returns 1 when 'name' is the ptags entry name
+ * or, otherwise, returns 0.
+ */
+static int inline ptags_is_ptags_file(const char *name)
+{
+       return !strcmp(name, "ptags");
+}
+
+/**
+ * ptags_bprm_committing_creds - Prepare to install the new credentials
+ * from bprm.
+ *
+ * @bprm: binprm for exec
+ */
+static void ptags_bprm_committing_creds(struct linux_binprm *bprm)
+{
+       ptags_prune(ptags_of_bprm(bprm));
+}
+
+/**
+ * ptags_cred_alloc_blank - "allocate" blank task-level security credentials
+ * @new: the new credentials
+ * @gfp: the atomicity of any memory allocations
+ *
+ * Prepare a blank set of credentials for modification.  This must allocate all
+ * the memory the LSM module might require such that cred_transfer() can
+ * complete without error.
+ */
+static int ptags_cred_alloc_blank(struct cred *cred, gfp_t gfp)
+{
+       struct ptags *root;
+
+       root = ptags_create();
+       set_ptags_of_cred(cred, root);
+       return root ? 0 : -ENOMEM;
+}
+
+/**
+ * ptags_cred_free - "free" task-level security credentials
+ * @cred: the credentials in question
+ *
+ */
+static void ptags_cred_free(struct cred *cred)
+{
+       struct ptags *root;
+
+       root = ptags_of_cred(cred);
+       set_ptags_of_cred(cred, NULL);
+       ptags_free(root);
+}
+
+/**
+ * ptags_cred_prepare - prepare new set of credentials for modification
+ * @new: the new credentials
+ * @old: the original credentials
+ * @gfp: the atomicity of any memory allocations
+ *
+ * Prepare a new set of credentials for modification.
+ */
+static int ptags_cred_prepare(struct cred *new, const struct cred *old,
+                             gfp_t gfp)
+{
+       int rc;
+
+       rc = ptags_cred_alloc_blank(new, gfp);
+       if (rc == 0)
+               rc = ptags_copy(ptags_of_cred(new), ptags_of_cred(old));
+       return rc;
+}
+
+/**
+ * ptags_cred_transfer - Transfer the old credentials to the new credentials
+ * @new: the new credentials
+ * @old: the original credentials
+ *
+ * Fill in a set of blank credentials from another set of credentials.
+ */
+static void ptags_cred_transfer(struct cred *new, const struct cred *old)
+{
+       ptags_move(ptags_of_cred(new), ptags_of_cred(old));
+}
+
+/**
+ * ptags_getprocattr - reads the file 'name' of the task 'task'
+ * @task: the object task
+ * @name: the name of the attribute in /proc/.../attr
+ * @value: where to put the result
+ *
+ * Reads the ptags
+ *
+ * Returns the length read
+ */
+static int ptags_getprocattr(struct task_struct *task, char *name, char 
**value)
+{
+       if (ptags_is_ptags_file(name))
+               return ptags_read(ptags_of_task(task), value);
+       return 0;
+}
+
+/**
+ * ptags_setprocattr - write the file 'name' of the task 'task
+ *
+ * @task: the object task
+ * @name: the name of the attribute in /proc/.../attr
+ * @value: the value to set
+ * @size: the size of the value
+ *
+ * Sets ptags
+ *
+ * Returns the length writen data
+ */
+static int ptags_setprocattr(struct task_struct *task, char *name,
+                            void *value, size_t size)
+{
+       struct ptags *croot;
+       if (ptags_is_ptags_file(name)) {
+               if ((current->flags & PF_KTHREAD) ||
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+                   has_ns_capability(task, task_cred_xxx(task, user_ns), 
CAP_MAC_ADMIN)
+#else
+                   has_capability(task, CAP_MAC_ADMIN)
+#endif
+                   )
+                       croot = NULL;
+               else
+                       croot = ptags_of_current();
+               return ptags_write(croot, ptags_of_task(task), value, size);
+       }
+       return 0;
+}
+
+/*
+ * List of hooks
+ */
+static struct security_hook_list ptags_hooks[] = {
+
+       LSM_HOOK_INIT(bprm_committing_creds, ptags_bprm_committing_creds),
+
+       LSM_HOOK_INIT(cred_alloc_blank, ptags_cred_alloc_blank),
+       LSM_HOOK_INIT(cred_free, ptags_cred_free),
+       LSM_HOOK_INIT(cred_prepare, ptags_cred_prepare),
+       LSM_HOOK_INIT(cred_transfer, ptags_cred_transfer),
+
+       LSM_HOOK_INIT(getprocattr, ptags_getprocattr),
+       LSM_HOOK_INIT(setprocattr, ptags_setprocattr),
+};
+
+/**
+ * ptags_init - initialize the tags system
+ *
+ * Returns 0
+ */
+static __init int ptags_init(void)
+{
+       int rc;
+
+       pr_info("PTags:  Initialising.\n");
+
+       /* Set the tags for the initial task. */
+       rc = ptags_cred_alloc_blank((struct cred *)current->cred, GFP_KERNEL);
+       if (rc != 0)
+               return -ENOMEM;
+
+       /* Register with LSM */
+       security_add_hooks(ptags_hooks, ARRAY_SIZE(ptags_hooks));
+
+       return 0;
+}
+
+/*
+ * Smack requires early initialization in order to label
+ * all processes and objects when they are created.
+ */
+security_initcall(ptags_init);
diff --git a/security/ptags/ptags.c b/security/ptags/ptags.c
new file mode 100644
index 0000000..75b4523
--- /dev/null
+++ b/security/ptags/ptags.c
@@ -0,0 +1,2377 @@
+/*
+ * Copyright (C) 2016 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_PTAGS_WITH_USER_NS
+#  define IF_NS(x)    x
+#  define IF_NO_NS(x)
+#else
+#  define IF_NS(x)
+#  define IF_NO_NS(x) x
+#endif
+
+/*
+ * Definition of characters
+ */
+#define ADD_CHAR       '+'     /* add tag or keep flag */
+#define SUB_CHAR       '-'     /* sub tag or keep flag */
+#define SET_CHAR       '!'     /* set a value to a tag */
+#define COMMENT_CHAR   '#'     /* comment */
+#define QUERY_CHAR     '?'     /* query */
+
+#define KEEP_CHAR      '@'     /* keep char */
+#define ASSIGN_CHAR    '='     /* key/value separator */
+#define SEPAR_CHAR     ':'     /* field delimitors */
+#define GLOB_CHAR      '*'     /* global pattern (at end) */
+
+#define EOL_CHAR       '\n'    /* End Of Line */
+
+/*
+ * Maximum count of ptags
+ */
+#define MAXCOUNT       4000
+
+/*
+ * Maximum length of a tag
+ */
+#define MAXTAGLEN      4000
+
+/*
+ * Maximum length of a value
+ */
+#define MAXVALUELEN    32700
+
+/*
+ * Increment size
+ */
+#define CAPACITYINCR   100
+
+/*
+ * static strings
+ */
+static const char prefix_string[] = "ptags:";
+static const char add_string[] = "add";
+static const char sub_string[] = "sub";
+static const char set_string[] = "set";
+static const char others_string[] = "others";
+
+/*
+ * length of static strings
+ */
+#define prefix_string_length   ((int)((sizeof prefix_string) - 1))
+#define add_string_length      ((int)((sizeof add_string) - 1))
+#define sub_string_length      ((int)((sizeof sub_string) - 1))
+#define set_string_length      ((int)((sizeof set_string) - 1))
+#define others_string_length   ((int)((sizeof others_string) - 1))
+
+/*
+ * slice of entry index
+ */
+struct slice {
+       unsigned lower;         /* lower index of the slice */
+       unsigned upper;         /* upper index of the slice plus one */
+};
+
+/*
+ * items are reference counted strings
+ */
+struct item {
+       atomic_t refcount;      /* reference count of the string */
+       unsigned length;        /* length of the string (without terminal zero) 
*/
+       char value[1];          /* the zero terminated string */
+};
+
+/*
+ * value records the value and the state of tags
+ */
+struct value {
+       uintptr_t data;
+       /* pointer to the value with used lower bits */
+       /* bit 0: kept flag */
+       /* bit 1: removed flag */
+};
+
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+/*
+ * Specific data for user namespace
+ */
+#define HINT_COUNT     3       /* cache count for hints */
+#define HINT_NONE      -1      /* not a parent */
+#define NSCAPACITYINCR 10      /* capacity increment for nsrefs */
+
+/*
+ * hints record how namespaces are linked
+ */
+struct nshint {
+       struct user_namespace *target;  /* target user namespace */
+       int hint;                       /* the hint: level of ancestry */
+};
+/*
+ * weak reference to user namespace with cache
+ */
+struct nsref {
+       struct user_namespace *userns;  /* the user namespace */
+       struct nshint hints[HINT_COUNT]; /* cache of hints */
+};
+
+/*
+ * value for multi user namespace
+ */
+struct nsval {
+       struct nsval *next;     /* next nsval if any */
+       struct nsref *nsref;    /* namespace reference */
+       struct value value;     /* the value */
+};
+#endif
+
+/*
+ * entries record ptags, their value and kept flag
+ */
+struct entry {
+       struct item *name;      /* the item for the name */
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       struct nsval *first;    /* first value of the entry */
+#else
+       struct value value;     /* the value of the entry */
+#endif
+};
+
+/*
+ * ptags internal data
+ */
+struct _ptags {
+       struct entry *entries;  /* array of entries */
+       unsigned count;         /* count of entries */
+       unsigned capacity;      /* allocated count of entries */
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       struct nsref *nsrefs;           /* references to user namespaces */
+       unsigned nsrefs_count;          /* count of user namespaces */
+       unsigned nsrefs_capacity;       /* allocated count of user namespaces */
+       int wantgc;                     /* is garbage collection expected ? */
+#endif
+};
+
+/*
+ * ptags data attached to tasks
+ */
+struct ptags {
+       struct mutex lock;      /* mutex access */
+       struct _ptags data;     /* internal data */
+};
+
+/*
+ * user namespace proxy
+ */
+struct uns {
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       struct user_namespace *userns;  /* the user namespace */
+#endif
+};
+
+/*******************************************************************
+ * section: validity
+ ******************************************************************/
+
+/**
+ * is_valid_utf8 - Is buffer a valid utf8 string?
+ *
+ * @buffer: the start of the string
+ * @length: length in bytes of the buffer
+ *
+ * Return 1 when valid or else returns 0
+ */
+static int is_valid_utf8(const char *buffer, unsigned length)
+{
+       unsigned x;
+       while (length) {
+               /* check the first character */
+               x = (unsigned)(unsigned char)*buffer++;
+               if (x <= 127)
+                       x = 1;
+               else if ((x & 0xc0) == 0x80)
+                       return 0;
+               else if ((x & 0xe0) == 0xc0) {
+                       if ((x & 0xfe) == 0xc0)
+                               return 0;
+                       x = 2;
+               } else if ((x & 0xf0) == 0xe0)
+                       x = 3;
+               else if ((x & 0xf8) == 0xf0)
+                       x = 4;
+               else if ((x & 0xfc) == 0xf8)
+                       x = 5;
+               else if ((x & 0xfe) == 0xfc)
+                       x = 6;
+               else
+                       return 0;
+
+               /* check the length */
+               if (length < x)
+                       return 0;
+               length -= x;
+
+               /* check the remaining characters */
+               while (--x) {
+                       if ((*buffer++ & '\xc0') != '\x80')
+                               return 0;
+               }
+       }
+       return 1;
+}
+
+/**
+ * is_valid_base - Is buffer a valid base for tag or prefix?
+ *
+ * @buffer: string for the tag or prefix name
+ * @length: length in bytes of the buffer
+ *
+ * Return 1 when valid or else returns 0
+ */
+static int is_valid_base(const char *buffer, unsigned length)
+{
+       unsigned i;
+       char c;
+
+       /* should not start with KEEP_CHAR */
+       if (length <= 0 || length > MAXTAGLEN || buffer[0] == KEEP_CHAR)
+               return 0;
+
+       /* should not contain ASSIGN_CHAR or GLOB_CHAR */
+       for (i = 0; i < length; i++) {
+               c = buffer[i];
+               if (c < ' ' || c == '\x7f' || c == ASSIGN_CHAR
+                   || c == GLOB_CHAR)
+                       return 0;
+       }
+       return 1;
+}
+
+/**
+ * is_valid_tag - Is buffer a valid tag?
+ *
+ * @buffer: string for the tag name
+ * @length: length in bytes of the buffer
+ *
+ * Return 1 when valid or else returns 0
+ */
+static int is_valid_tag(const char *buffer, unsigned length)
+{
+       unsigned i;
+
+       /* exclude bad tags */
+       if (!is_valid_base(buffer, length)
+           || buffer[length - 1] == SEPAR_CHAR)
+               return 0;
+
+       /* accept common tags */
+       if (length <= prefix_string_length
+           || memcmp(buffer, prefix_string, prefix_string_length))
+               return 1;
+
+       /*
+        * The tag is "ptags:...."
+        * Get the last part
+        */
+       i = length;
+       while (buffer[i - 1] != SEPAR_CHAR)
+               i = i - 1;
+       length -= i;
+       buffer += i;
+
+       /*
+        * Check the last part
+        */
+       return (length == add_string_length
+               && !memcmp(add_string, buffer, length))
+           || (length == sub_string_length
+               && !memcmp(sub_string, buffer, length))
+           || (length == set_string_length
+               && !memcmp(set_string, buffer, length))
+           || (length == others_string_length
+               && !memcmp(others_string, buffer, length));
+}
+
+/**
+ * is_valid_prefix - Is buffer a valid prefix?
+ *
+ * @buffer: string for the tag name
+ * @length: length in bytes of the buffer
+ *
+ * Return 1 when valid or else returns 0
+ */
+static inline int is_valid_prefix(const char *buffer, unsigned length)
+{
+       return is_valid_base(buffer, length)
+           && buffer[length - 1] == SEPAR_CHAR;
+}
+
+/**
+ * is_valid_value - Is buffer a valid value?
+ *
+ * @buffer: string for the tag name
+ * @length: length in bytes of the buffer
+ *
+ * Return 1 when valid or else returns 0
+ */
+static int is_valid_value(const char *buffer, unsigned length)
+{
+       unsigned i;
+       char c;
+       if (length > MAXVALUELEN)
+               return 0;
+       for (i = 0; i < length; i++) {
+               c = buffer[i];
+               if (c < ' ' || c == '\x7f')
+                       return 0;
+       }
+       return 1;
+}
+
+/*******************************************************************
+ * section: item
+ *
+ * The structure item handles one string that is used either
+ * for storing the tags or their values.
+ * This structure is reference counted.
+ ******************************************************************/
+
+/*
+ * item_create - Creates an item for the 'value' of 'length'
+ *
+ * @value: the value copied as value of the item
+ * @length: length in bytes of the value
+ *
+ * Returns the create item or NULL on error.
+ */
+static struct item *item_create(const char *value, unsigned length)
+{
+       struct item *item;
+
+       item = kmalloc(length + sizeof *item, GFP_KERNEL);
+       if (item) {
+               atomic_set(&item->refcount, 1);
+               item->length = length;
+               memcpy(item->value, value, length);
+               item->value[length] = 0;
+       }
+       return item;
+}
+
+/*
+ * item_addref - Adds a reference to 'item' and returns it
+ *
+ * @item: the item to use (must not be NULL)
+ *
+ * Returns the item
+ */
+static inline struct item *item_addref(struct item *item)
+{
+       atomic_inc(&item->refcount);
+       return item;
+}
+
+/*
+ * item_addref_safe - Adds a reference to 'item' and returns it
+ *
+ * @item: the item to use (can be NULL)
+ *
+ * Returns the item
+ */
+static inline struct item *item_addref_safe(struct item *item)
+{
+       return item ? item_addref(item) : item;
+}
+
+/*
+ * item_unref - Removes a reference to 'item'
+ *
+ * @item: the item whose reference count is to be decremented
+ *
+ * 'item' must not be NULL. It is destroyed when no more used.
+ */
+static inline void item_unref(struct item *item)
+{
+       if (atomic_dec_and_test(&item->refcount))
+               kfree(item);
+}
+
+/*
+ * item_unref - Removes a reference to 'item'
+ *
+ * @item: the item whose reference count is to be decremented
+ *
+ * 'item' can be NULL. It is destroyed when no more used.
+ */
+static inline void item_unref_safe(struct item *item)
+{
+       if (item)
+               item_unref(item);
+}
+
+/*
+ * item_has_prefix - Has 'item' the 'prefix' of 'length'?
+ *
+ * @item: the item to test
+ * @prefix: the prefix to search
+ * @length: the length in byte of the prefix
+ *
+ * Returns 1 if 'item' has the 'prefix' or else returns 0
+ */
+static inline int item_has_prefix(const struct item *item, const char *prefix,
+                                 unsigned length)
+{
+       return item->length >= length && !memcmp(item->value, prefix, length);
+}
+
+/*******************************************************************
+ * section: value
+ *
+ * A value records the following data:
+ *  - value: an item being the value attached to the tag (can be NULL)
+ *  - kept: a boolean flag indicating if the tag is kept accross execve
+ *  - removed: a boolean flag indicating if the value was removed
+ *
+ * Note: for limiting memory usage, boolean flags are taken in the
+ * lower bits of the pointer to the value
+ ******************************************************************/
+
+/*
+ * value_get - Gets the value of 'value'
+ *
+ * @value: the value whose value is to get
+ *
+ * Returns the value that can be NULL of 'value'
+ */
+static inline struct item *value_get(struct value value)
+{
+       return (struct item *)(value.data & ~(uintptr_t) 3);
+}
+
+/*
+ * value_set - Sets the value of 'value' to 'value'
+ *
+ * @value: the value whose value is to set
+ * @item: the item to set (can be NULL)
+ */
+static inline void value_set(struct value *value, struct item *item)
+{
+       item_unref_safe(value_get(*value));
+       value->data = (uintptr_t) item | (value->data & (uintptr_t) 1);
+}
+
+/*
+ * value_is_removed - Is the 'value' removed?
+ *
+ * @value: the value to test
+ *
+ * Returns 1 if value is removed or 0 else
+ */
+static inline int value_is_removed(struct value value)
+{
+       return (int)(value.data & (uintptr_t) 2);
+}
+
+/*
+ * value_set_removed - Sets the 'value' as removed
+ *
+ * @value: the value to set
+ */
+static inline void value_set_removed(struct value *value)
+{
+       item_unref_safe(value_get(*value));
+       value->data = (value->data & (uintptr_t) 1) | (uintptr_t) 2;
+}
+
+/*
+ * value_is_kept - Is the 'value' to be kept?
+ *
+ * @value: the value to test
+ *
+ * Returns 1 if value is kept or 0 else
+ */
+static inline int value_is_kept(struct value value)
+{
+       return (int)(value.data & (uintptr_t) 1);
+}
+
+/*
+ * value_set_kept - Sets the kept flag of the 'value'
+ *
+ * @value: the value to set
+ */
+static inline void value_set_kept(struct value *value)
+{
+       value->data |= (uintptr_t) 1;
+}
+
+/*
+ * value_clear_kept - Clears the kept flag of the 'value'
+ *
+ * @value: the value to clear
+ */
+static inline void value_clear_kept(struct value *value)
+{
+       value->data &= ~(uintptr_t) 1;
+}
+
+/*
+ * value_make - Makes a new value
+ *
+ * Returns a just existing value
+ */
+static inline struct value value_make(void)
+{
+       struct value result;
+       result.data = 0;
+       return result;
+}
+
+/*
+ * value_clone - Clones the 'value'
+ *
+ * @value: the value to clone
+ *
+ * Returns an value with same data than 'value' but whose reference
+ * counts are incremented.
+ */
+static inline struct value value_clone(struct value value)
+{
+       struct value result;
+       result.data = value.data;
+       item_addref_safe(value_get(result));
+       return result;
+}
+
+/*
+ * value_erase - Erases the content of 'value'
+ *
+ * @value: the value to erase
+ *
+ * The name and value of the value are dereferenced
+ */
+static inline void value_erase(struct value value)
+{
+       item_unref_safe(value_get(value));
+}
+
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+/*******************************************************************
+ * section: nsref
+ ******************************************************************/
+
+/*
+ * nsref_init - Initialise 'nsref' to reference 'userns'
+ *
+ * @nsref: the nsref to initialize
+ * @userns: the user namespace to reference
+ */
+static void nsref_init(struct nsref *nsref, struct user_namespace *userns)
+{
+       struct nshint *hcur, *hend;
+
+       /* get a weak reference to the user namespace */
+       nsref->userns = get_weak_user_ns(userns);
+
+       /* clears hint cache */
+       hcur = nsref->hints;
+       hend = &hcur[HINT_COUNT];
+       while (hcur != hend) {
+               hcur->target = NULL;
+               hcur++;
+       }
+}
+
+/*
+ * nsref_erase_hints - Erase hints data of 'nsref'
+ *
+ * @nsref: the reference to erase.
+ */
+static void nsref_erase_hints(struct nsref *nsref)
+{
+       struct nshint *hcur, *hend;
+
+       /* unref hints */
+       hcur = nsref->hints;
+       hend = &hcur[HINT_COUNT];
+       while (hcur != hend && hcur->target != NULL) {
+               put_weak_user_ns(hcur->target);
+               hcur++;
+       }
+}
+
+/*
+ * nsref_erase - Erase data of 'nsref'
+ *
+ * @nsref: the reference to erase.
+ */
+static inline void nsref_erase(struct nsref *nsref)
+{
+       nsref_erase_hints(nsref);
+       put_weak_user_ns(nsref->userns);
+}
+
+/*
+ * nsref_remove_ghost_hints - Removes hint to ghost namespaces
+ *
+ * @nsref: the reference to clean
+ */
+static void nsref_remove_ghost_hints(struct nsref *nsref)
+{
+       struct user_namespace *userns;
+       struct nshint *hcur, *hend, *hto;
+
+       hcur = nsref->hints;
+       hto = hcur;
+       hend = &hcur[HINT_COUNT];
+       while (hcur != hend && (userns = hcur->target) != NULL) {
+               if (!is_weak_user_ns_still_alive(userns))
+                       put_weak_user_ns(userns);
+               else {
+                       if (hto != hcur)
+                               *hto = *hcur;
+                       hto++;
+               }
+               hcur++;
+       }
+       while (hto != hcur)
+               hto++->target = NULL;
+}
+
+/*
+ * nsref_userns_hint - Get the hint for 'userns'
+ *
+ * The hint is the level of ancestry of 'userns' within context
+ * of 'nsref'. If 'nsref' references 'userns', the result if 0.
+ * If 'userns' is a parent of the namespace referenced by 'nsref'
+ * it returns a positive integer being the level of ancestry.
+ * Otherwise a negative value is returned (HINT_NONE).
+ *
+ * @nsref: the reference to the usernamespace
+ * @userns: the usernamespace queried
+ *
+ * Returns the hint.
+ */
+static int nsref_userns_hint(struct nsref *nsref, struct user_namespace 
*userns)
+{
+       struct nshint h0, h1, *hcur, *hend;
+       struct user_namespace *it, *ref;
+
+       /* 0 if equal to reference */
+       ref = nsref->userns;
+       if (userns == ref)
+               return 0;
+
+       /* search cached hint and reorder lru */
+       hcur = nsref->hints;
+       hend = &hcur[HINT_COUNT];
+       h0 = *hcur;
+       if (h0.target == userns)
+               return h0.hint; /* no reorder needed */
+       while (h0.target) {
+               if (++hcur == hend) {
+                       put_weak_user_ns(h0.target);
+                       break;
+               }
+               h1 = *hcur;
+               *hcur = h0;
+               if (h1.target == userns) {
+                       nsref->hints[0] = h1;
+                       return h1.hint;
+               }
+               if (!h1.target)
+                       break;
+               if (++hcur == hend) {
+                       put_weak_user_ns(h1.target);
+                       break;
+               }
+               h0 = *hcur;
+               *hcur = h1;
+               if (h0.target == userns) {
+                       nsref->hints[0] = h0;
+                       return h0.hint;
+               }
+       }
+
+       /* compute the hint */
+       h0.target = get_weak_user_ns(userns);
+       h0.hint = 1;
+       it = userns->parent;
+       for (;;) {
+               if (!it) {
+                       h0.hint = HINT_NONE;
+                       break;
+               }
+               if (it == ref)
+                       break;
+               h0.hint++;
+               it = it->parent;
+       }
+
+       /* record and end */
+       nsref->hints[0] = h0;
+       return h0.hint;
+}
+#endif
+/*******************************************************************
+ * section: entry
+ *
+ * An entry records the following data:
+ *  - name: an item being the tag name
+ *  - value: an item being the value attached to the tag (can be NULL)
+ *  - kept: a boolean flag indicating if the tag is kept accross execve
+ *  - removed: a boolean flag indicating if the entry was removed
+ *
+ * Note: for limiting memory usage, bollean flags are taken in the
+ * lower bits of the pointer to the value
+ ******************************************************************/
+
+/*
+ * entry_name - Gets the name of 'entry'
+ *
+ * @entry: the entry whose name is to get
+ *
+ * Returns the name of 'entry'
+ */
+static inline struct item *entry_name(struct entry entry)
+{
+       return entry.name;
+}
+
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+/*
+ * entry_nsval - Returns the existing readable nsval of 'entry' for 'uns'
+ *
+ * @entry: the entry to search in
+ * @uns: the user namespace proxy
+ *
+ * Returns the nsval of entry having the best hint or NULL if none exist.
+ */
+static struct nsval *entry_nsval(struct entry *entry, struct uns uns)
+{
+       struct nsval *val, *r;
+       int h, rh;
+
+       val = entry->first;
+       while(val) {
+               h = nsref_userns_hint(val->nsref, uns.userns);
+               if (!h)
+                       return val;
+               if (h > 0) {
+                       rh = h;
+                       r = val->next;
+                       while(r) {
+                               h = nsref_userns_hint(r->nsref, uns.userns);
+                               if (!h)
+                                       return r;
+                               if (h > 0 && h < rh) {
+                                       rh = h;
+                                       val = r;
+                               }
+                               r = r->next;
+                       }
+                       return val;
+               }
+               val = val->next;
+       }
+       return NULL;
+}
+#endif
+
+/*
+ * entry_read - Get the read value of 'entry' for 'uns'
+ *
+ * @entry: the entry to search in
+ * @uns: the user namespace proxy
+ *
+ * Returns the address of the value to read or NULL if the entry doean't
+ * exist or is removed.
+ */
+static inline struct value *entry_read(struct entry *entry, struct uns uns)
+{
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       struct nsval *r;
+
+       r = entry_nsval(entry, uns);
+       return r && !value_is_removed(r->value) ? &r->value : NULL;
+#else
+       return value_is_removed(entry->value) ? NULL : &entry->value;
+#endif
+}
+
+/*
+ * entry_make - Makes a new entry
+ *
+ * @name: name of the entry
+ *
+ * Returns the entry initialized with the given values
+ */
+static inline struct entry entry_make(struct item *name)
+{
+       struct entry result;
+       result.name = name;
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       result.first = NULL;
+#else
+       result.value = value_make();
+#endif
+       return result;
+}
+
+/*
+ * entry_erase - Erases the content of 'entry'
+ *
+ * @entry: the entry to erase
+ *
+ * The name and value of the entry are dereferenced
+ */
+static inline void entry_erase(struct entry entry)
+{
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       struct nsval *val;
+
+       while((val = entry.first) != NULL) {
+               entry.first = val->next;
+               value_erase(val->value);
+               kfree(val);
+       }
+#else
+       value_erase(entry.value);
+#endif
+       item_unref(entry_name(entry));
+}
+
+/*
+ * entry_is_removed - Is the entry to be removed?
+ *
+ * @entry: the entry to test
+ *
+ * Returns 1 if the entry can be removed.
+ */
+static inline int entry_is_removed(struct entry entry)
+{
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       struct nsval *val;
+
+       /* note: an other politic, here, would be to return 0 always */
+       val = entry.first;
+       while(val) {
+               if (!value_is_removed(val->value))
+                       return 0;
+               val = val->next;
+       }
+       return 1;
+#else
+       return value_is_removed(entry.value);
+#endif
+}
+
+/*
+ * entry_prune - Removes the values that are not to be kept
+ *
+ * @entry: the entry to prune
+ *
+ * Return 1 if the entry is to be kept or 0 if it is removed
+ * and erased (erase is not to be called)
+ */
+static IF_NO_NS(inline) int entry_prune(struct entry *entry)
+{
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       struct nsval *val, **prv;
+       int remove;
+
+       /* effective prune */
+       remove = 1;
+       prv = &entry->first;
+       val = entry->first;
+       while(val != NULL) {
+               if (value_is_kept(val->value)) {
+                       if (!value_is_removed(val->value))
+                               remove = 0;
+                       prv = &val->next;
+                       val = val->next;
+               } else {
+                       *prv = val->next;
+                       value_erase(val->value);
+                       kfree(val);
+                       val = *prv;
+               }
+       }
+       /* test if empty */
+       prv = &entry->first;
+       val = *prv;
+       if (val && !remove)
+               return 1;
+       /* clean up */
+       while(val != NULL) {
+               *prv = val->next;
+               value_erase(val->value);
+               kfree(val);
+               val = *prv;
+       }       
+#else
+       if (value_is_kept(entry->value))
+               return 1;
+       value_erase(entry->value);
+#endif
+       item_unref(entry_name(*entry));
+       return 0;
+}
+
+/*******************************************************************
+ * section: entries
+ ******************************************************************/
+
+/*
+ * entries_search - Searchs the 'name' of 'length' in the entries
+ *
+ * @entries: array of entries to be searched
+ * @count: count of entries in 'entries'
+ * @name: name to search
+ * @length: length in bytes of the searched name
+ * @glob: boolean indicating if search is global (on prefix)
+ *
+ * Returns the slice found. The entries found are at indexes from result.lower
+ * to result.upper-1. When nothing is found, the insert index is returned
+ * in result.lower and result.upper.
+ *
+ * This function asserts that entries is ordered.
+ */
+static struct slice entries_search(struct entry *entries, unsigned count,
+                                  const char *name, unsigned length, int glob)
+{
+       int cmp;
+       unsigned idx;
+       struct slice r;
+       struct item *item;
+
+       /* dichotomic search */
+       r.lower = 0;
+       r.upper = count;
+       while (r.lower != r.upper) {
+               idx = (r.lower + r.upper) >> 1;
+               item = entry_name(entries[idx]);
+               if (length > item->length) {
+                       cmp = memcmp(item->value, name, item->length);
+                       if (cmp <= 0)
+                               r.lower = idx + 1;
+                       else
+                               r.upper = idx;
+               } else {
+                       cmp = memcmp(item->value, name, length);
+                       if (!cmp && (glob || length == item->length)) {
+                               r.lower = idx;
+                               r.upper = idx + 1;
+                               if (glob)
+                                       goto extend;
+                               goto end;
+                       }
+                       if (cmp < 0)
+                               r.lower = idx + 1;
+                       else
+                               r.upper = idx;
+               }
+       }
+       goto end;
+ extend:
+       /* extend selection (if glob) */
+       while (r.lower > 0
+              && item_has_prefix(entry_name(entries[r.lower - 1]), name,
+                                 length))
+               r.lower--;
+       while (r.upper < count
+              && item_has_prefix(entry_name(entries[r.upper]), name, length))
+               r.upper++;
+ end:
+       return r;
+}
+
+/*******************************************************************
+ * section: _ptags
+ ******************************************************************/
+
+/*
+ * _ptags_query_gc - Request a garbage collection for ptags
+ *
+ * @ptags: the ptags to be cleaned
+ */
+static inline void _ptags_query_gc(struct _ptags *ptags)
+{
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       ptags->wantgc = 1;
+#endif
+}
+
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+/*
+ * _ptags_erase_tagged_nsref - remove tagged namespace references
+ *
+ * @ptags: ptags to be cleaned
+ */
+static void _ptags_erase_tagged_nsref(struct _ptags *ptags)
+{
+       struct nsref *nscur, *nsend;
+       struct entry *entcur, *entend, *entto;
+       struct nsval *curval, **preval;
+
+       /* clean ghost references references */
+       nscur = ptags->nsrefs;
+       nsend = &nscur[ptags->nsrefs_count];
+       while (nscur != nsend) {
+               if (nscur->userns) {
+                       /* still alive, skip */
+                       nscur++;
+               } else {
+                       /* removes the current nsref */
+                       nsref_erase_hints(nscur);
+                       if (--nsend != nscur)
+                               *nscur = *nsend;
+
+                       /* update entries */
+                       entcur = ptags->entries;
+                       entto = entcur;
+                       entend = &entcur[ptags->count];
+                       while (entcur != entend) {
+                               preval = &entcur->first;
+                               curval = entcur->first;
+                               while (curval) {
+                                       if (curval->nsref == nscur) {
+                                               /*
+                                                * note: it is asserted that 
parent
+                                                * namespaces die after 
children.
+                                                */
+                                               *preval = curval->next;
+                                               value_erase(curval->value);
+                                               kfree(curval);
+                                               curval = *preval;
+                                       } else {
+                                               if (curval->nsref == nsend)
+                                                       curval->nsref = nscur;
+                                               preval = &curval->next;
+                                               curval = curval->next;
+                                       }
+                               }
+                               if (entry_is_removed(*entcur))
+                                       entry_erase(*entcur);
+                               else {
+                                       if (entto != entcur)
+                                               *entto = *entcur;
+                                       entto++;
+                               }
+                               entcur++;
+                       }
+                       ptags->count = (unsigned)(entto - ptags->entries);
+               }
+       }
+       /* clean ghost hints */
+       nscur = ptags->nsrefs;
+       ptags->nsrefs_count = (unsigned)(nsend - nscur);
+       while (nscur != nsend) {
+               nsref_remove_ghost_hints(nscur);
+               nscur++;
+       }
+}
+
+/*
+ * _ptags_collect_garbage - remove unused namespace references
+ *
+ * @ptags: ptags to be cleaned
+ */
+static void _ptags_collect_garbage(struct _ptags *ptags)
+{
+       struct user_namespace *userns;
+       struct nsref *nscur, *nsend;
+       struct entry *entcur, *entend;
+       struct nsval *val;
+       int changed;
+
+return;
+       changed = 0;
+
+       /* scan entries entries */
+       entcur = ptags->entries;
+       entend = &entcur[ptags->count];
+       while (entcur != entend) {
+               val = entcur->first;
+               while (val) {
+                       nscur = val->nsref;
+                       userns = nscur->userns;
+                       nscur->userns = (void*)((uintptr_t) userns | 
(uintptr_t)1);
+                       val = val->next;
+               }
+               entcur++;
+       }
+
+       /* detect leaks */
+       nscur = ptags->nsrefs;
+       nsend = &nscur[ptags->nsrefs_count];
+       while (nscur != nsend) {
+               userns = nscur->userns;
+               if (((uintptr_t)1) & ((uintptr_t)userns))
+                       nscur->userns = (void*)((uintptr_t) userns & 
~(uintptr_t)1);
+               else {
+                       nscur->userns = NULL;
+                       put_weak_user_ns(userns);
+                       changed = 1;
+               }
+               nscur++;
+       }
+       if (changed)
+               _ptags_erase_tagged_nsref(ptags);
+
+       ptags->wantgc = 0;
+}
+
+/*
+ * _ptags_clean_nsrefs - Removes dependencies to ghost namespaces of 'ptags'
+ *
+ * @ptags: ptags to be cleaned
+ */
+static void _ptags_clean_nsrefs(struct _ptags *ptags)
+{
+       struct user_namespace *userns;
+       struct nsref *nscur, *nsend;
+       int changed;
+
+       for(;;) {
+               changed = 0;
+
+               /* clean ghost references references */
+               nscur = ptags->nsrefs;
+               nsend = &nscur[ptags->nsrefs_count];
+               while (nscur != nsend) {
+                       userns = nscur->userns;
+                       if (!is_weak_user_ns_still_alive(userns)) {
+                               nscur->userns = NULL;
+                               put_weak_user_ns(userns);
+                               changed = 1;
+                       }
+                       nscur++;
+               }
+               if (!changed)
+                       break;
+               _ptags_erase_tagged_nsref(ptags);
+       }
+}
+
+/*
+ * _ptags_nsref - Get/create within 'ptags' the namespace reference for 'uns'
+ *
+ * @ptags: where is the reference
+ * @uns: the user namespace proxy
+ *
+ * Return the reference to the user namespace proxied by 'uns' within 'ptags'
+ */
+static struct nsref *_ptags_nsref(struct _ptags *ptags, struct uns uns)
+{
+       struct nsref *nsorg, *nsref, *nsend;
+       struct entry *ecur, *eend;
+       struct nsval *nsval;
+       unsigned count;
+
+       /* search existing */
+       nsorg = ptags->nsrefs;
+       nsref = nsorg;
+       count = ptags->nsrefs_count;
+       nsend = &nsorg[count];
+       while (nsref != nsend) {
+               if (nsref->userns == uns.userns)
+                       return nsref; /* found */
+               nsref++;
+       }
+
+       /* create one */
+       if (count == ptags->nsrefs_capacity) {
+               /* increase the size */
+               nsref = krealloc(nsorg, (count + NSCAPACITYINCR) * sizeof 
*nsref, GFP_KERNEL);
+               if (nsref == NULL)
+                       return NULL;
+
+               /* rellocation if needed */
+               if (nsref != nsorg) {
+                       ecur = ptags->entries;
+                       eend = &ecur[ptags->count];
+                       while (ecur != eend) {
+                               nsval = ecur++->first;
+                               while(nsval) {
+                                       nsval->nsref = &nsref[nsval->nsref - 
nsorg];
+                                       nsval = nsval->next;
+                               }
+                       }
+               }
+
+               /* records data */
+               ptags->nsrefs = nsref;
+               ptags->nsrefs_capacity = count + NSCAPACITYINCR;
+               nsref += count;
+       }
+       ptags->nsrefs_count = count + 1;
+       nsref_init(nsref, uns.userns);
+       return nsref;
+}
+#endif
+
+/*
+ * _ptags_entry_write - Gets the write value of existing 'entry' for 'uns' 
within 'ptags'
+ *
+ * @ptags: the ptags where the value is to be written
+ * @entry: the entry where the value will be written
+ * @uns: proxy to user namespace
+ *
+ * Returns the pointer to the value to write or NULL on memory depletion.
+ */
+static IF_NO_NS(inline) int _ptags_entry_write(struct _ptags *ptags, struct 
entry *entry, struct uns uns, struct value **to)
+{
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       struct nsval *rval, *val;
+       struct nsref *nsref;
+
+       rval = entry_nsval(entry, uns);
+       if (!rval || value_is_removed(rval->value))
+               return 0;
+
+       nsref = _ptags_nsref(ptags, uns);
+       if (nsref == NULL)
+               return -1;
+
+       if (rval && rval->nsref == nsref)
+               *to = &rval->value;
+       else {
+               val = kmalloc(sizeof *val, GFP_KERNEL);
+               if (!val)
+                       return -1;
+
+               val->value = rval ? value_clone(rval->value) : value_make();
+               val->nsref = nsref;
+               val->next = entry->first;
+               entry->first = val;
+               *to = &val->value;
+       }
+#else
+       *to = &entry->value;
+#endif
+       return 1;
+}
+
+/*
+ * _ptags_entry_create - Creates the write value of 'entry' for 'uns' within 
'ptags'
+ *
+ * @ptags: the ptags where the value is to be written
+ * @entry: the entry where the value will be written
+ * @uns: proxy to user namespace
+ *
+ * Returns the pointer to the value to write or NULL on memory depletion.
+ */
+static IF_NO_NS(inline) struct value *_ptags_entry_create(struct _ptags 
*ptags, struct entry *entry, struct uns uns)
+{
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       struct nsval *rval, *val;
+       struct nsref *nsref;
+
+       /* get the namespace reference for uns */
+       nsref = _ptags_nsref(ptags, uns);
+       if (nsref == NULL)
+               return NULL;
+
+       /* search the read nsval */
+       rval = entry_nsval(entry, uns);
+       if (rval) {
+               if (rval->nsref == nsref) {
+                       if (value_is_removed(rval->value))
+                               rval->value = value_make();
+                       return &rval->value;
+               }
+               if (value_is_removed(rval->value))
+                       rval = NULL;
+       }
+
+       /* creates the nsval */
+       val = kmalloc(sizeof *val, GFP_KERNEL);
+       if (!val)
+               return NULL;
+
+       /* init it */
+       val->value = rval ? value_clone(rval->value) : value_make();
+       val->nsref = nsref;
+       val->next = entry->first;
+       entry->first = val;
+       return &val->value;
+#else
+       return &entry->value;
+#endif
+}
+
+/*
+ * _ptags_erase - Erases the content of 'ptags'
+ *
+ * @ptags: the ptags whose content is to erase (must not be NULL)
+ */
+static void _ptags_erase(struct _ptags *ptags)
+{
+       struct entry *entries;
+       unsigned count;
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       struct nsref *nsrefs;
+#endif
+
+       count = ptags->count;
+       entries = ptags->entries;
+       while (count)
+               entry_erase(entries[--count]);
+       kfree(entries);
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       count = ptags->nsrefs_count;
+       nsrefs = ptags->nsrefs;
+       while (count)
+               nsref_erase(&nsrefs[--count]);
+       kfree(nsrefs);
+#endif
+}
+
+/**
+ * _ptags_prune - Prunes from 'ptags' the entries not kept
+ *
+ * @ptags: the ptags to be puned
+ */
+static void _ptags_prune(struct _ptags *ptags)
+{
+       unsigned i, j, count;
+       struct entry *entries;
+
+       entries = ptags->entries;
+       count = ptags->count;
+       for (i = j = 0; i < count; i++) {
+               if (entry_prune(&entries[i]))
+                       entries[j++] = entries[i];
+       }
+       ptags->count = j;
+}
+
+/**
+ * _ptags_copy - Copies entries from locked 'src' to locked 'dst'
+ *
+ * @dst: locked destination ptags
+ * @src: locked source ptags
+ *
+ * Returns 0 on success or -ENOMEM on memory allocation failure.
+ */
+static int _ptags_copy(struct _ptags *dst, struct _ptags *src)
+{
+       struct _ptags tmp;
+       unsigned i;
+       struct entry *from, e, x;
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       struct nsref *fromrefs, *nscur, *nsend;
+       struct nshint *hcur, *hend;
+       struct nsval *val, **toval, *itval;
+#endif
+
+       /* allocates the entries */
+       tmp.count = src->count;
+       tmp.entries = kmalloc(tmp.count * sizeof *tmp.entries, GFP_KERNEL);
+       if (!tmp.entries)
+               return -ENOMEM;
+       tmp.capacity = tmp.count;
+
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       /* allocates the namespace references */
+       tmp.nsrefs_count = src->nsrefs_count;
+       tmp.nsrefs = kmalloc(tmp.nsrefs_count * sizeof *tmp.nsrefs, GFP_KERNEL);
+       if (!tmp.nsrefs) {
+               kfree(tmp.entries);
+               return -ENOMEM;
+       }
+       tmp.nsrefs_capacity = tmp.nsrefs_count;
+
+       /* copy the namespace references */
+       fromrefs = src->nsrefs;
+       memcpy(tmp.nsrefs, fromrefs, tmp.nsrefs_count * sizeof *tmp.nsrefs);
+       nscur = tmp.nsrefs;
+       nsend = &nscur[tmp.nsrefs_count];
+       while (nscur != nsend) {
+               get_weak_user_ns(nscur->userns);
+               hcur = nscur++->hints;
+               hend = &hcur[HINT_COUNT];
+               while(hcur != hend && hcur->target)
+                       get_weak_user_ns(hcur++->target);
+       }
+#endif
+       /* copy the entries */
+       from = src->entries;
+       for (i = 0; i < tmp.count; i++) {
+               x = from[i];
+               e.name = item_addref(entry_name(x));
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+               toval = &e.first;
+               itval = x.first;
+               while (itval) {
+                       val = kmalloc(sizeof *val, GFP_KERNEL);
+                       *toval = val;
+                       if (val == NULL) {
+                               /* out of memory: cleanup */
+                               entry_erase(e);
+                               tmp.count = i;
+                               _ptags_erase(&tmp);
+                               return -ENOMEM;
+                       }
+                       val->nsref = &tmp.nsrefs[itval->nsref - fromrefs];
+                       val->value = value_clone(itval->value);
+                       toval = &val->next;
+                       itval = itval->next;
+               }
+               *toval = NULL;
+#else
+               e.value = value_clone(x.value);
+#endif
+               tmp.entries[i] = e;
+       }
+
+       /* assign the copy */
+       _ptags_erase(dst);
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       tmp.wantgc = 0;
+#endif
+       *dst = tmp;
+
+       return 0;
+}
+
+/*
+ * _ptags_init - Creates and initializes the ptags structure
+ *
+ * Returns the init ptags.
+ */
+static inline void _ptags_init(struct _ptags *ptags)
+{
+       ptags->entries = NULL;
+       ptags->count = 0;
+       ptags->capacity = 0;
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       ptags->nsrefs = NULL;
+       ptags->nsrefs_count = 0;
+       ptags->nsrefs_capacity = 0;
+       ptags->wantgc = 0;
+#endif
+}
+
+/**
+ * _ptags_move - Transfers entries from 'src' to 'dst'
+ *
+ * @dst: destination ptags
+ * @src: source ptags
+ */
+static inline void _ptags_move(struct _ptags *dst, struct _ptags *src)
+{
+       _ptags_erase(dst);
+       *dst = *src;
+       _ptags_init(src);
+}
+
+/*******************************************************************
+ * section: uns
+ ******************************************************************/
+/*
+ * uns_get - Get the current user namespace proxy
+ */
+static inline struct uns uns_get(void)
+{
+       struct uns uns;
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       uns.userns = get_user_ns(current_user_ns());
+#endif
+       return uns;
+}
+
+/*
+ * uns_put - release the proxy
+ *
+ * @uns: the proxy to release
+ */
+static inline void uns_put(struct uns uns)
+{
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       put_user_ns(uns.userns);
+#endif
+}
+
+/*******************************************************************
+ * section: ptags check
+ *
+ * This checks the validity of requested actions
+ ******************************************************************/
+
+/*
+ * check_action - Checks if the action 'astr' of length 'alen' is
+ *                authorized for the tag 'tstr' of length 'tlen'
+ *
+ * @ptags: the ptags controling the action (ptags of current)
+ * @tstr: the tags string to modify
+ * @tlen: the length of the tag
+ * @astr: the action sting to check
+ * @alen: the length of the action
+ * @uns: user namespace proxy
+ *
+ * Returns 1 if the action is authorized or 0 if not
+ */
+static int check_action(struct _ptags *ptags, const char *tstr, unsigned tlen,
+                       const char *astr, unsigned alen, struct uns uns)
+{
+       unsigned i, ilen;
+       struct slice slice;
+       struct entry *entries;
+       struct item *item;
+       char *istr;
+
+       /* Searchs the entries "ptags:...." */
+       entries = ptags->entries;
+       slice = entries_search(entries, ptags->count, prefix_string,
+                              prefix_string_length, 1);
+
+       /* Loop on found entries */
+       for (i = slice.lower; i < slice.upper; i++) {
+
+               /* must be available for uns */
+               if (!entry_read(&entries[i], uns))
+                       continue;
+
+               /* get the unprefixed entry in istr and ilen */
+               item = entry_name(entries[i]);
+               ilen = item->length - prefix_string_length;
+               istr = item->value + prefix_string_length;
+
+               if (ilen == alen) {
+                       /*
+                        * case of ptags:action
+                        *
+                        * Accept when action is the searched action 'astr'
+                        * and when tstr hasn't prefix "ptags:"
+                        */
+                       if (!memcmp(&istr[ilen - alen], astr, alen) &&
+                           (tlen < prefix_string_length ||
+                            memcmp(tstr, prefix_string, prefix_string_length)))
+                               return 1;
+               } else if (ilen > alen) {
+                       /*
+                        * case of ptags:prefix:action
+                        *
+                        * Accept when action is the searched action 'astr'
+                        * and either 'tstr' has prefix "prefix:"
+                        * or 'tstr' == prefix
+                        */
+                       ilen = ilen - alen - 1;
+                       if (istr[ilen] == SEPAR_CHAR
+                           && !memcmp(&istr[ilen + 1], astr, alen)) {
+                               /* searched action found */
+                               if (tlen > ilen) {
+                                       if (!memcmp(istr, tstr, ilen + 1))
+                                               return 1;
+                               } else if (tlen == ilen) {
+                                       if (!memcmp(istr, tstr, tlen))
+                                               return 1;
+                               }
+                       }
+               }
+       }
+
+       /* not authorized */
+       return 0;
+}
+
+/*
+ * check_tag - Checks if the action 'action' of length 'alen' is
+ *             authorized for the 'tag' of length 'length'
+ *
+ * @cptags: the ptags controling the action (ptags of current)
+ * @mptags: the ptags modified
+ * @tag: the tag name to modify
+ * @length: the length of the tag
+ * @action: the action sting to check
+ * @alen: the length of the action
+ * @uns: user namespace proxy
+ *
+ * Returns 1 if the action is authorized or 0 if not
+ */
+static inline int check_tag(struct _ptags *cptags, struct _ptags *mptags,
+                           const char *tag, unsigned length,
+                           const char *action, unsigned alen, struct uns uns)
+{
+       /* current ptags == NULL means "super capable" */
+       if (!cptags)
+               return 1;
+
+       /* check if the action is forbidden */
+       if (!check_action(cptags, tag, length, action, alen, uns))
+               return 0;
+
+       /* the action is authorized if current ptags == modified ptags */
+       if (cptags == mptags)
+               return 1;
+
+       /* not the same process/thread, check "others" authorisation */
+       return check_action(cptags, tag, length, others_string,
+                           others_string_length, uns);
+}
+
+/*
+ * check_tag - Checks if the action 'action' of length 'alen' is
+ *             authorized for the 'entry'
+ *
+ * @cptags: the ptags controling the action (ptags of current)
+ * @mptags: the ptags modified
+ * @entry: the entry to modify
+ * @action: the action sting to check
+ * @alen: the length of the action
+ * @uns: user namespace proxy
+ *
+ * Returns 1 if the action is authorized or 0 if not
+ */
+static inline int check_entry(struct _ptags *cptags, struct _ptags *mptags,
+                             struct entry *entry, const char *action,
+                             unsigned alen, struct uns uns)
+{
+       struct item *name = entry_name(*entry);
+       return check_tag(cptags, mptags, name->value, name->length, action,
+                        alen, uns);
+}
+
+/*******************************************************************
+ * section: ptags operations
+ ******************************************************************/
+
+/**
+ * _ptags_query - Queries existing of tags
+ *
+ * @ptags: queried ptags
+ * @name: string for the name of the tag
+ * @length: length in bytes of the tag's name
+ * @uns: user namespace proxy
+ *
+ * Returns 0 in case of success tag present or -ENOENT if the tag is not found
+ * or invalid.
+ */
+static int _ptags_query(struct _ptags *ptags, const char *line, unsigned 
length, struct uns uns)
+{
+       int qkept, glob;
+       unsigned count;
+       struct slice slice;
+       struct entry *entries;
+       struct value *value;
+
+       /* is querying only @s? */
+       qkept = length > 0 && line[0] == KEEP_CHAR;
+       if (qkept) {
+               line++;
+               length--;
+       }
+
+       entries = ptags->entries;
+       count = ptags->count;
+
+       /* check length */
+       if (length == 0) {
+               /*
+                * global query
+                */
+               glob = 1;
+               slice.lower = 0;
+               slice.upper = count;
+       } else {
+               /* is a global query? */
+               glob = length > 1 && line[length - 2] == SEPAR_CHAR
+                   && line[length - 1] == GLOB_CHAR;
+               if (glob) {
+                       /* global */
+                       length -= 1;
+                       if (!is_valid_prefix(line, length))
+                               return -EINVAL;
+               } else {
+                       /* not global */
+                       if (!is_valid_tag(line, length))
+                               return -EINVAL;
+               }
+               /* search entry slice */
+               slice = entries_search(entries, count, line, length, glob);
+       }
+
+       /* iterate over found entries */
+       while (slice.lower != slice.upper) {
+               value = entry_read(&entries[slice.lower++], uns);
+               if (value && (!qkept || value_is_kept(*value)))
+                       return 0;
+       }
+       return -ENOENT;
+}
+
+/**
+ * _ptags_set - set the value of one tag
+ *
+ * @cptags: controling ptags
+ * @mptags: modified ptags
+ * @line: string for the line of setting the tag
+ * @length: length in bytes of the tag's line
+ * @uns: user namespace proxy
+ *
+ * Returns 0 in case of success tag present or
+ *  o -ENOENT if the tag is not found
+ *  o -EINVAL if the syntax is invalid
+ *  o -EPERM if the operation is forbidden
+ *  o -ENOMEM if not enough memory
+ */
+static int _ptags_set(struct _ptags *cptags, struct _ptags *mptags,
+                    const char *line, unsigned length, struct uns uns)
+{
+       struct slice slice;
+       unsigned taglen, idxval, vallen;
+       struct entry *entry;
+       struct item *item;
+       struct value *value;
+       int rc;
+
+       /* compute the tag length */
+       taglen = 0;
+       while (taglen < length && line[taglen] != ASSIGN_CHAR)
+               taglen++;
+       if (!is_valid_tag(line, taglen))
+               return -EINVAL;
+
+       /* search the permission */
+       if (!check_tag
+           (cptags, mptags, line, taglen, set_string, set_string_length, uns))
+               return -EPERM;
+
+       /* search the entry */
+       slice = entries_search(mptags->entries, mptags->count, line, taglen, 0);
+       if (slice.lower == slice.upper)
+               return -ENOENT;
+
+       /* instanciate the item */
+       idxval = taglen + 1;
+       if (length <= idxval)
+               item = NULL;
+       else {
+               /* check validity of item */
+               vallen = length - idxval;
+               if (!is_valid_value(line + idxval, vallen))
+                       return -EINVAL;
+               /* create the item */
+               item = item_create(line + idxval, vallen);
+               if (!item)
+                       return -ENOMEM;
+       }
+
+       /* create a value for writing */
+       entry = &mptags->entries[slice.lower];
+       rc = _ptags_entry_write(mptags, entry, uns, &value);
+       if (rc <= 0) {
+               item_unref_safe(item);
+               /* test the case of deleted entry */
+               return rc ? -ENOMEM : -ENOENT;
+       }
+
+       /* replace the previous value */
+       value_set(value, item);
+       return 0;
+}
+
+/**
+ * _ptags_sub - Removes one or more tags
+ *
+ * @cptags: controling ptags
+ * @mptags: modified ptags
+ * @line: string for the line of the tag
+ * @length: length in bytes of the tag's line
+ * @uns: user namespace proxy
+ *
+ * Returns 0 in case of success or
+ *  o -EINVAL if the syntax is invalid
+ *  o -EPERM if the operation is forbiden
+ */
+static int _ptags_sub(struct _ptags *cptags, struct _ptags *mptags,
+                    const char *line, unsigned length, struct uns uns)
+{
+       struct slice slice;
+       int subkept, glob, rc;
+       unsigned i, j, count;
+       struct entry *entries, *entry, e;
+       struct value *value;
+
+       /* is for removing @s? */
+       subkept = length > 0 && line[0] == KEEP_CHAR;
+       if (subkept) {
+               line++;
+               length--;
+       }
+
+       /* init */
+       entries = mptags->entries;
+       count = mptags->count;
+
+       /* check length */
+       if (length == 0) {
+               /*
+                *  global selection of all
+                */
+               glob = 1;
+               slice.lower = 0;
+               slice.upper = count;
+       } else {
+               /* is a global sub? */
+               glob = length > 1 && line[length - 2] == SEPAR_CHAR
+                   && line[length - 1] == GLOB_CHAR;
+               if (glob) {
+                       /* global */
+                       length -= 1;
+                       if (!is_valid_prefix(line, length))
+                               return -EINVAL;
+               } else {
+                       /* not global */
+                       if (!is_valid_tag(line, length))
+                               return -EINVAL;
+               }
+               /* search entry slice */
+               slice = entries_search(entries, count, line, length, glob);
+       }
+
+       /* check action */
+       if (subkept) {
+               /* remove kept flags */
+               for (i = slice.lower; i < slice.upper; i++) {
+                       entry = &entries[i];
+                       if (check_entry (cptags, mptags, entry, sub_string, 
sub_string_length, uns)) {
+                               /* authorized */
+                               rc = _ptags_entry_write(mptags, entry, uns, 
&value);
+                               if (rc < 0)
+                                       return -ENOMEM;
+                               if (rc)
+                                       value_clear_kept(value);
+                       } else if (!glob) {
+                               /* not authorized and not global */
+                               value = entry_read(entry, uns);
+                               if (value && value_is_kept(*value))
+                                       return -EPERM;
+                       }
+               }
+       } else {
+               /* mark entries to remove */
+               for (i = slice.lower; i < slice.upper; i++) {
+                       entry = &entries[i];
+                       if (check_entry (cptags, mptags, entry, sub_string, 
sub_string_length, uns)) {
+                               /* authorized */
+                               rc = _ptags_entry_write(mptags, entry, uns, 
&value);
+                               if (rc < 0)
+                                       return -ENOMEM;
+                               if (rc)
+                                       value_set_removed(value);
+                       } else if (!glob) {
+                               /* not authorized and not global */
+                               value = entry_read(entry, uns);
+                               if (value)
+                                       return -EPERM;
+                       }
+               }
+               /* remove entries */
+               for (i = j = slice.lower; i < slice.upper; i++) {
+                       e = entries[i];
+                       if (entry_is_removed(e))
+                               entry_erase(e);
+                       else {
+                               if (i != j)
+                                       entries[j] = e;
+                               j++;
+                       }
+               }
+               if (i != j) {
+                       if (i != count)
+                               memmove(&entries[j], &entries[i], (count - i) * 
sizeof *entry);
+                       mptags->count -= i - j;
+                       _ptags_query_gc(mptags);
+               }
+       }
+       return 0;
+}
+
+/**
+ * _ptags_add - Adds one tag
+ *
+ * @cptags: controling ptags
+ * @mptags: modified ptags
+ * @line: string for the line of the tag
+ * @length: length in bytes of the tag's line
+ * @uns: user namespace proxy
+ *
+ * Returns 0 in case of success or one of the following error code if failed:
+ *   o -EINVAL if the line 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 _ptags_add(struct _ptags *cptags, struct _ptags *mptags,
+                    const char *line, unsigned length, struct uns uns)
+{
+       struct slice slice;
+       int addkept, glob, rc;
+       unsigned i, n, count;
+       struct entry *entries, *entry;
+       struct item *name;
+       struct value *value;
+
+       /* is for adding @s? */
+       addkept = length > 0 && line[0] == KEEP_CHAR;
+       if (addkept) {
+               line++;
+               length--;
+       }
+
+       entries = mptags->entries;
+       count = mptags->count;
+
+       /* check length */
+       if (length == 0) {
+               /*
+                *  global selection of all
+                */
+               glob = 1;
+               slice.lower = 0;
+               slice.upper = count;
+       } else {
+               /* is a global sub? */
+               glob = length > 1 && line[length - 2] == SEPAR_CHAR
+                   && line[length - 1] == GLOB_CHAR;
+               if (glob) {
+                       length -= 1;
+                       if (!is_valid_prefix(line, length))
+                               return -EINVAL;
+               } else {
+                       /* not global */
+                       if (!is_valid_tag(line, length))
+                               return -EINVAL;
+               }
+       }
+       if (glob && !addkept)
+               return -EINVAL;
+
+       /* search entry slice */
+       slice = entries_search(entries, count, line, length, glob);
+
+       /* check action */
+       if (glob) {
+               /* globally add kept flags to existing */
+               for (i = slice.lower; i < slice.upper; i++) {
+                       entry = &entries[i];
+                       if (check_entry (cptags, mptags, entry, add_string, 
add_string_length, uns)) {
+                               /* authorized */
+                               rc = _ptags_entry_write(mptags, entry, uns, 
&value);
+                               if (rc < 0)
+                                       return -ENOMEM;
+                               if (rc)
+                                       value_set_kept(value);
+                       } 
+               }
+       } else if (slice.lower != slice.upper) {
+               /* add kept if needed */
+               entry = &entries[slice.lower];
+               if (check_entry (cptags, mptags, entry, add_string, 
add_string_length, uns)) {
+                       /* authorized */
+                       value = _ptags_entry_create(mptags, entry, uns);
+                       if (!value)
+                               return -ENOMEM;
+                       if (addkept)
+                               value_set_kept(value);
+               } else {
+                       /* not authorized */
+                       value = entry_read(entry, uns);
+                       if (!value || (addkept && !value_is_kept(*value)))
+                               return -EPERM;
+               }
+       } else {
+               /* adds a new entry */
+               if (count == MAXCOUNT)
+                       return -ECANCELED;
+               if (!check_tag (cptags, mptags, line, length, add_string, 
add_string_length, uns))
+                       return -EPERM;
+               if (count == mptags->capacity) {
+                       n = mptags->capacity + CAPACITYINCR;
+                       entries = krealloc(entries, n * sizeof *entries, 
GFP_KERNEL);
+                       if (!entries)
+                               return -ENOMEM;
+                       mptags->entries = entries;
+                       mptags->capacity = n;
+               }
+               name = item_create(line, length);
+               if (!name)
+                       return -ENOMEM;
+               entry = &entries[slice.lower];
+               mptags->count = count + 1;
+               n = count - slice.lower;
+               if (n)
+                       memmove(&entry[1], entry, n * sizeof *entry);
+               *entry = entry_make(name);
+               value = _ptags_entry_create(mptags, entry, uns);
+               if (!value)
+                       return -ENOMEM;
+               if (addkept)
+                       value_set_kept(value);
+       }
+       return 0;
+}
+
+/**
+ * _ptags_write - Implement the writing of the tags
+ *
+ * @cptags: controling ptags
+ * @mptags: modified ptags
+ * @buffer: a pointer to the written data
+ * @size: the size of the written data
+ * @uns: user namespace proxy
+ *
+ * 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 -ENOENT if the query failed
+ *   o -ECANCELED if the maximum count of tag is reached
+ */
+static int _ptags_write(struct _ptags *cptags, struct _ptags *mptags,
+                             const char *buffer, unsigned size, struct uns uns)
+{
+       unsigned start, stop, len;
+       int err;
+
+       /* begin the parsing */
+       start = 0;
+       while (start < size) {
+               /* scan a line of 'len' */
+               for (stop = start; stop < size && buffer[stop] != EOL_CHAR;
+                    stop++) ;
+               len = stop - start;
+
+               /* ignore empty lines */
+               if (len == 0)
+                       err = 0;
+
+               /* check utf8 validity of the line */
+               else if (!is_valid_utf8(buffer + start, len))
+                       err = -EINVAL;
+
+               /* lines not terminated with EOL_CHAR */
+               else if (stop == size)
+                       err = -EINVAL;
+
+               /* skip comments (starting with COMMENT_CHAR) */
+               else if (buffer[start] == COMMENT_CHAR)
+                       err = 0;
+
+               /* line starting with ADD_CHAR */
+               else if (buffer[start] == ADD_CHAR)
+                       err = _ptags_add(cptags, mptags,
+                                       buffer + start + 1, len - 1, uns);
+
+               /* lines starting with SUB_CHAR */
+               else if (buffer[start] == SUB_CHAR)
+                       err = _ptags_sub(cptags, mptags,
+                                       buffer + start + 1, len - 1, uns);
+
+               /* lines starting with SET_CHAR */
+               else if (buffer[start] == SET_CHAR)
+                       err = _ptags_set(cptags, mptags,
+                                       buffer + start + 1, len - 1, uns);
+
+               /* lines starting with QUERY_CHAR */
+               else if (buffer[start] == QUERY_CHAR)
+                       err = _ptags_query(mptags, buffer + start + 1, len - 1, 
uns);
+
+               /* other lines */
+               else
+                       err = -EINVAL;
+
+               /* treat the error case if any */
+               if (err != 0) {
+                       return start ? (int)start : err;
+               }
+
+               /* parse next line */
+               start = stop + 1;
+       }
+       return (int)start;
+}
+
+/**
+ * _ptags_read - Implement the reading of the tags
+ *
+ * @ptags: tags structure of the readen task
+ * @result: a pointer for storing the read result
+ * @uns: user namespace proxy
+ *
+ * Returns the count of byte read or the negative code -ENOMEM
+ * if an allocation failed.
+ */
+static int _ptags_read(struct _ptags *ptags, char **result, struct uns uns)
+{
+       unsigned idx, count;
+       size_t size;
+       struct entry *entries, *entry;
+       char *buffer;
+       struct value *value;
+       struct item *item;
+
+       /* init loops */
+       count = ptags->count;
+       entries = ptags->entries;
+
+       /* compute printed size */
+       size = 0;
+       for (idx = 0; idx < count; idx++) {
+               entry = &entries[idx];
+               value = entry_read(entry, uns);
+               if (value) {
+                       item = value_get(*value);
+                       size += entry_name(*entry)->length
+                               + (unsigned)value_is_kept(*value)
+                               + (item ? 2 + item->length : 1);
+               }
+       }
+
+       if (size > INT_MAX)
+               return -E2BIG;
+       buffer = kmalloc(size, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+
+       /* print in the buffer */
+       *result = buffer;
+       for (idx = 0; idx < count; idx++) {
+               entry = &entries[idx];
+               value = entry_read(entry, uns);
+               if (value) {
+                       if (value_is_kept(*value))
+                               *buffer++ = KEEP_CHAR;
+                       item = entry_name(*entry);
+                       memcpy(buffer, item->value, item->length);
+                       buffer += item->length;
+                       item = value_get(*value);
+                       if (item) {
+                               *buffer++ = ASSIGN_CHAR;
+                               memcpy(buffer, item->value, item->length);
+                               buffer += item->length;
+                       }
+                       *buffer++ = EOL_CHAR;
+               }
+       }
+
+       return (int)size;
+}
+
+/*******************************************************************
+ * section: ptags
+ ******************************************************************/
+
+/*
+ * ptags_lock - Locks one ptags
+ *
+ * @ptags: the ptags to lock
+ */
+static inline void ptags_lock(struct ptags *ptags)
+{
+       mutex_lock(&ptags->lock);
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       _ptags_clean_nsrefs(&ptags->data);
+#endif
+}
+
+/*
+ * ptags_unlock - Unlocks one ptags
+ *
+ * @ptags: the ptags to unlock
+ */
+static inline void ptags_unlock(struct ptags *ptags)
+{
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       if (ptags->data.wantgc)
+               _ptags_collect_garbage(&ptags->data);
+#endif
+       mutex_unlock(&ptags->lock);
+}
+
+/*
+ * ptags_lock2 - Locks 2 ptags avoiding deadlocks
+ *
+ * @ptags1: one of the ptags to lock
+ * @ptags2: the other of the ptags to lock
+ */
+static inline void ptags_lock2(struct ptags *ptags1, struct ptags *ptags2)
+{
+       if ((uintptr_t) ptags1 < (uintptr_t) ptags2) {
+               mutex_lock(&ptags1->lock);
+               mutex_lock(&ptags2->lock);
+       } else {
+               mutex_lock(&ptags2->lock);
+               mutex_lock(&ptags1->lock);
+       }
+#ifdef CONFIG_SECURITY_PTAGS_WITH_USER_NS
+       _ptags_clean_nsrefs(&ptags1->data);
+       _ptags_clean_nsrefs(&ptags2->data);
+#endif
+}
+
+/*
+ * ptags_unlock2 - Unlocks 2 ptags locked together
+ *
+ * @ptags1: one of the ptags to unlock
+ * @ptags2: the other of the ptags to unlock
+ */
+static inline void ptags_unlock2(struct ptags *ptags1, struct ptags *ptags2)
+{
+       ptags_unlock(ptags1);
+       ptags_unlock(ptags2);
+}
+
+/**
+ * ptags_write - Implement the writing of the tags
+ *
+ * @cptags: controling ptags
+ * @mptags: modified ptags
+ * @buffer: 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 -ENOENT if the query failed
+ *   o -ECANCELED if the maximum count of tag is reached
+ */
+static int ptags_write(struct ptags *cptags, struct ptags *mptags,
+                      const char *buffer, size_t size)
+{
+       int result;
+       unsigned length;
+       struct uns uns;
+
+       /* crop the length */
+       length = (size > (size_t) INT_MAX) ? INT_MAX : (unsigned)size;
+
+       /* lock the ptags */
+       uns = uns_get();
+       if (!cptags || cptags == mptags) {
+               ptags_lock(mptags);
+               result = _ptags_write(cptags ? &cptags->data : NULL, 
&mptags->data, buffer, length, uns);
+               ptags_unlock(mptags);
+       } else {
+               ptags_lock2(cptags, mptags);
+               result = _ptags_write(&cptags->data, &mptags->data, buffer, 
length, uns);
+               ptags_unlock2(cptags, mptags);
+       }
+       uns_put(uns);
+
+       return result;
+}
+
+/**
+ * ptags_read - Implement the reading of the tags
+ *
+ * @ptags: tags structure of the readen task
+ * @data: a pointer for storing the read data
+ *
+ * Returns the count of byte read or the negative code -ENOMEM
+ * if an allocation failed.
+ */
+static int ptags_read(struct ptags *ptags, char **data)
+{
+       int result;
+       struct uns uns;
+
+       uns = uns_get();
+       ptags_lock(ptags);
+       result = _ptags_read(&ptags->data, data, uns);
+       ptags_unlock(ptags);
+       uns_put(uns);
+
+       return result;
+}
+
+/*
+ * ptags_free - Frees ptags
+ *
+ * @ptags: the ptags to free
+ */
+static void ptags_free(struct ptags *ptags)
+{
+       if (ptags) {
+               _ptags_erase(&ptags->data);
+               kfree(ptags);
+       }
+}
+
+/**
+ * ptags_copy - Copies entries from 'src' to 'dst'
+ *
+ * @dst: destination ptags
+ * @src: source ptags
+ *
+ * Returns 0 on success or -ENOMEM on memory allocation failure.
+ */
+static int ptags_copy(struct ptags *dst, struct ptags *src)
+{
+       int rc;
+
+       ptags_lock2(dst, src);
+       rc = _ptags_copy(&dst->data, &src->data);
+       ptags_unlock2(dst, src);
+       return rc;
+}
+
+/**
+ * ptags_move - Transfers entries from 'src' to 'dst'
+ *
+ * @dst: destination ptags
+ * @src: source ptags
+ */
+static void ptags_move(struct ptags *dst, struct ptags *src)
+{
+       ptags_lock2(dst, src);
+       _ptags_move(&dst->data, &src->data);
+       ptags_unlock2(dst, src);
+}
+
+/**
+ * ptags_prune - Prunes from 'ptags' the entries not kept
+ *
+ * @ptags: the ptags to be puned
+ */
+static void ptags_prune(struct ptags *ptags)
+{
+       ptags_lock(ptags);
+       _ptags_prune(&ptags->data);
+       ptags_unlock(ptags);
+}
+
+/*
+ * ptags_create - Creates and initializes the ptags structure
+ *
+ * Returns the created ptags.
+ */
+static struct ptags *ptags_create(void)
+{
+       struct ptags *ptags;
+
+       ptags = kmalloc(sizeof *ptags, GFP_KERNEL);
+       if (ptags) {
+               mutex_init(&ptags->lock);
+               _ptags_init(&ptags->data);
+       }
+       return ptags;
+}
+
+
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 09fd610..fe486d3 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5784,6 +5784,10 @@ static int selinux_getprocattr(struct task_struct *p,
        int error;
        unsigned len;
 
+#ifdef CONFIG_SECURITY_PTAGS
+       if (strcmp(name, "ptags") == 0)
+               return 0;
+#endif
        if (current != p) {
                error = current_has_perm(p, PROCESS__GETATTR);
                if (error)
@@ -5831,6 +5835,10 @@ static int selinux_setprocattr(struct task_struct *p,
        int error;
        char *str = value;
 
+#ifdef CONFIG_SECURITY_PTAGS
+       if (strcmp(name, "ptags") == 0)
+               return 0;
+#endif
        if (current != p) {
                /* SELinux only allows a process to change its own
                   security attributes. */
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 1cb0602..28678e9 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3603,13 +3603,18 @@ static void smack_d_instantiate(struct dentry 
*opt_dentry, struct inode *inode)
  */
 static int smack_getprocattr(struct task_struct *p, char *name, char **value)
 {
-       struct smack_known *skp = smk_of_task_struct(p);
+       struct smack_known *skp;
        char *cp;
        int slen;
 
+#ifdef CONFIG_SECURITY_PTAGS
+       if (strcmp(name, "ptags") == 0)
+               return 0;
+#endif
        if (strcmp(name, "current") != 0)
                return -EINVAL;
 
+       skp = smk_of_task_struct(p);
        cp = kstrdup(skp->smk_known, GFP_KERNEL);
        if (cp == NULL)
                return -ENOMEM;
@@ -3634,12 +3639,16 @@ static int smack_getprocattr(struct task_struct *p, 
char *name, char **value)
 static int smack_setprocattr(struct task_struct *p, char *name,
                             void *value, size_t size)
 {
-       struct task_smack *tsp = current_security();
+       struct task_smack *tsp;
        struct cred *new;
        struct smack_known *skp;
        struct smack_known_list_elem *sklep;
        int rc;
 
+#ifdef CONFIG_SECURITY_PTAGS
+       if (strcmp(name, "ptags") == 0)
+               return 0;
+#endif
        /*
         * Changing another process' Smack value is too dangerous
         * and supports no sane use case.
@@ -3647,6 +3656,7 @@ static int smack_setprocattr(struct task_struct *p, char 
*name,
        if (p != current)
                return -EPERM;
 
+       tsp = current_security();
        if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel))
                return -EPERM;
 
-- 
2.7.4


Reply via email to