[RFC,v1,1/1] LSM ptags: Add tagging of processes
diff mbox

Message ID 1475145649-24293-2-git-send-email-jobol@nonadev.net
State New
Headers show

Commit Message

José Bollo Sept. 29, 2016, 10:40 a.m. UTC
From: José Bollo <jobol@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 <jobol@nonadev.net>
---
 Documentation/security/ptags.txt |  428 +++++++++++
 fs/proc/base.c                   |    3 +
 include/linux/cred.h             |    3 +
 security/Kconfig                 |    1 +
 security/Makefile                |    2 +
 security/apparmor/lsm.c          |   14 +-
 security/ptags/Kconfig           |    8 +
 security/ptags/Makefile          |    6 +
 security/ptags/lsm-ptags.c       |  204 ++++++
 security/ptags/ptags.c           | 1448 ++++++++++++++++++++++++++++++++++++++
 security/selinux/hooks.c         |    8 +
 security/smack/smack_lsm.c       |   14 +-
 12 files changed, 2135 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/lsm-ptags.c
 create mode 100644 security/ptags/ptags.c

Patch
diff mbox

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 ac0df4d..d2dde95 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2497,6 +2497,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 257db64..fbb29eb 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -148,6 +148,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/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..c548562
--- /dev/null
+++ b/security/ptags/Kconfig
@@ -0,0 +1,8 @@ 
+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
+
diff --git a/security/ptags/Makefile b/security/ptags/Makefile
new file mode 100644
index 0000000..da26539
--- /dev/null
+++ b/security/ptags/Makefile
@@ -0,0 +1,6 @@ 
+#
+# Makefile for the Tag module
+#
+
+obj-$(CONFIG_SECURITY_PTAGS) := lsm-ptags.o
+
diff --git a/security/ptags/lsm-ptags.c b/security/ptags/lsm-ptags.c
new file mode 100644
index 0000000..87fe6d7
--- /dev/null
+++ b/security/ptags/lsm-ptags.c
@@ -0,0 +1,204 @@ 
+/*
+ * Copyright (C) 2016 José Bollo <jobol@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 <jobol@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>
+
+#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)
+		    || ns_capable(task_cred_xxx(task, user_ns), CAP_MAC_ADMIN))
+			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..dfd4f6b
--- /dev/null
+++ b/security/ptags/ptags.c
@@ -0,0 +1,1448 @@ 
+/*
+ * Copyright (C) 2016 José Bollo <jobol@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 <jobol@nonadev.net>
+ */
+
+/*
+ * Definition of characters at the beginning of lines
+ */
+#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) */
+
+/*
+ * 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))
+
+/*
+ * ptags slice
+ */
+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 {
+	unsigned refcount;	/* reference count of the string */
+	unsigned length;	/* length of the string (without terminal zero) */
+	char value[1];		/* the zero terminated string */
+};
+
+/*
+ * entries record ptags, their value and kept flag
+ */
+struct entry {
+	struct item *name;	/* the item for the name */
+	uintptr_t data;		/* item for the value with used lower bits */
+	/* bit 0: kept flag */
+	/* bit 1: removed flag */
+};
+
+/*
+ * ptags data attached to tasks
+ */
+struct ptags {
+	struct mutex lock;	/* mutex access */
+	struct entry *entries;	/* array of entries */
+	unsigned count;		/* count of entries */
+	unsigned capacity;	/* allocated count of entries in entries */
+};
+
+/*
+ * mutex for accessing item's
+ */
+static DEFINE_MUTEX(itemlock);
+
+/*******************************************************************
+ * 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 a string of data that is used either
+ * for storing the tags or their values.
+ * This structure is reference counted.
+ ******************************************************************/
+
+/*
+ * item_create - Creates an item for the 'data' of 'length'
+ *
+ * @data: the data copied as value of the item
+ * @length: length in bytes of the data
+ *
+ * Returns the create item or NULL on error.
+ */
+static struct item *item_create(const char *data, unsigned length)
+{
+	struct item *item;
+
+	item = kmalloc(length + sizeof *item, GFP_KERNEL);
+	if (item) {
+		item->refcount = 1;
+		item->length = length;
+		memcpy(item->value, data, 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 or NULL on error
+ */
+static struct item *item_addref(struct item *item)
+{
+	unsigned n;
+	mutex_lock(&itemlock);
+	n = item->refcount + 1;
+	if (n != 0)
+		item->refcount = n;
+	else
+		item = NULL;
+	mutex_unlock(&itemlock);
+	return item;
+}
+
+/*
+ * item_addref - Adds a reference to 'item' and returns it
+ *
+ * @item: the item to use (can be NULL)
+ *
+ * Returns the item or NULL on error
+ */
+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 void item_unref(struct item *item)
+{
+	unsigned n;
+
+	mutex_lock(&itemlock);
+	n = item->refcount - 1;
+	item->refcount = n;
+	mutex_unlock(&itemlock);
+	if (n <= 0)
+		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: 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;
+}
+
+/*
+ * entry_value - Gets the value of 'entry'
+ *
+ * @entry: the entry whose value is to get
+ *
+ * Returns the value that can be NULL of 'entry'
+ */
+static inline struct item *entry_value(struct entry entry)
+{
+	return (struct item *)(entry.data & ~(uintptr_t) 3);
+}
+
+/*
+ * entry_set_value - Sets the value of 'entry' to 'value'
+ *
+ * @entry: the entry whose value is to set
+ * @value: the value to set (can be NULL)
+ */
+static inline void entry_set_value(struct entry *entry, struct item *value)
+{
+	item_unref_safe(entry_value(*entry));
+	entry->data &= (uintptr_t) 3;
+	entry->data |= (uintptr_t) value;
+}
+
+/*
+ * entry_is_removed - Is the 'entry' removed?
+ *
+ * @entry: the entry to test
+ *
+ * Returns 1 if entry is removed or 0 else
+ */
+static inline int entry_is_removed(struct entry entry)
+{
+	return (int)(entry.data & (uintptr_t) 2);
+}
+
+/*
+ * entry_set_removed - Sets the 'entry' as removed
+ *
+ * @entry: the entry to set
+ */
+static inline void entry_set_removed(struct entry *entry)
+{
+	entry->data |= (uintptr_t) 2;
+}
+
+/*
+ * entry_is_kept - Is the 'entry' to be kept?
+ *
+ * @entry: the entry to test
+ *
+ * Returns 1 if entry is kept or 0 else
+ */
+static inline int entry_is_kept(struct entry entry)
+{
+	return (int)(entry.data & (uintptr_t) 1);
+}
+
+/*
+ * entry_set_kept - Sets the kept flag of the 'entry'
+ *
+ * @entry: the entry to set
+ */
+static inline void entry_set_kept(struct entry *entry)
+{
+	entry->data |= (uintptr_t) 1;
+}
+
+/*
+ * entry_clear_kept - Clears the kept flag of the 'entry'
+ *
+ * @entry: the entry to clear
+ */
+static inline void entry_clear_kept(struct entry *entry)
+{
+	entry->data &= ~(uintptr_t) 1;
+}
+
+/*
+ * entry_make - Makes a new entry
+ *
+ * @name: name of the entry
+ * @kept: should be kept?
+ * @value: value of the entry
+ *
+ * Returns the entry ionitialized with the given values
+ */
+static inline struct entry entry_make(struct item *name, int kept,
+				      struct item *value)
+{
+	struct entry result;
+	result.name = name;
+	result.data = (uintptr_t) (kept != 0) | (uintptr_t) value;
+	return result;
+}
+
+/*
+ * entry_clone - Clones the 'entry'
+ *
+ * @entry: the entry to clone
+ *
+ * Returns an entry with same data than 'entry' but whose reference
+ * counts are incremented.
+ */
+static inline struct entry entry_clone(struct entry entry)
+{
+	return entry_make(item_addref(entry_name(entry)), entry_is_kept(entry),
+			  item_addref_safe(entry_value(entry)));
+}
+
+/*
+ * 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)
+{
+	item_unref_safe(entry_value(entry));
+	item_unref(entry_name(entry));
+}
+
+/*
+ * entry_print_length - Computes the count of bytes needed to print 'entry'
+ *
+ * @entry: the entry to print
+ */
+static unsigned entry_print_length(struct entry entry)
+{
+	struct item *value = entry_value(entry);
+	return (unsigned)entry_is_kept(entry)
+	    + entry_name(entry)->length + (value ? 2 + value->length : 1);
+}
+
+/*
+ * entry_print - Prints the 'entry' in 'head'
+ *
+ * @entry: the entry to print
+ * @head: the head of the buffer where to print the entry
+ *
+ * Returns the buffer offsetted by the printed length
+ */
+static char *entry_print(struct entry entry, char *head)
+{
+	struct item *item;
+
+	if (entry_is_kept(entry))
+		*head++ = KEEP_CHAR;
+
+	item = entry_name(entry);
+	memcpy(head, item->value, item->length);
+	head += item->length;
+
+	item = entry_value(entry);
+	if (item) {
+		*head++ = ASSIGN_CHAR;
+		memcpy(head, item->value, item->length);
+		head += item->length;
+	}
+	*head++ = '\n';
+	return head;
+}
+
+/*******************************************************************
+ * 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;
+
+	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:
+	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_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);
+	}
+}
+
+/*
+ * ptags_lock2 - 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)
+{
+	mutex_unlock(&ptags1->lock);
+	mutex_unlock(&ptags2->lock);
+}
+
+/*
+ * 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;
+
+	count = ptags->count;
+	entries = ptags->entries;
+	ptags->entries = NULL;
+	ptags->count = 0;
+	ptags->capacity = 0;
+	while (count)
+		entry_erase(entries[--count]);
+	kfree(entries);
+}
+
+/*
+ * ptags_erase_safe - Erases the content of 'ptags' if not NULL
+ *
+ * @ptags: the ptags whose content is to erase (can be NULL)
+ */
+static inline void ptags_erase_safe(struct ptags *ptags)
+{
+	if (ptags)
+		ptags_erase(ptags);
+}
+
+/**
+ * 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, e;
+
+	mutex_lock(&ptags->lock);
+
+	entries = ptags->entries;
+	count = ptags->count;
+	for (i = j = 0; i < count; i++) {
+		e = entries[i];
+		if (!entry_is_kept(e))
+			entry_erase(e);
+		else
+			entries[j++] = e;
+	}
+	ptags->count = j;
+	mutex_unlock(&ptags->lock);
+}
+
+/**
+ * ptags_copy_locked - 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_locked(struct ptags *dst, struct ptags *src)
+{
+	unsigned i, count;
+	struct entry *to, *from;
+
+	/* creates the copy */
+	from = src->entries;
+	count = src->count;
+	to = kmalloc(count * sizeof *to, GFP_KERNEL);
+	if (!to)
+		return -ENOMEM;
+	for (i = 0; i < count; i++)
+		to[i] = entry_clone(from[i]);
+
+	/* assign the copy */
+	ptags_erase(dst);
+	dst->entries = to;
+	dst->count = count;
+	dst->capacity = count;
+
+	return 0;
+}
+
+/**
+ * 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_locked(dst, src);
+	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_erase(dst);
+	dst->entries = src->entries;
+	dst->count = src->count;
+	dst->capacity = src->capacity;
+	src->entries = NULL;
+	src->count = 0;
+	src->capacity = 0;
+	ptags_unlock2(dst, src);
+}
+
+/*
+ * 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->entries = NULL;
+		ptags->count = 0;
+		ptags->capacity = 0;
+	}
+	return ptags;
+}
+
+/*
+ * ptags_free - Frees ptags
+ *
+ * @ptags: the ptags to free
+ */
+static void ptags_free(struct ptags *ptags)
+{
+	ptags_erase_safe(ptags);
+	kfree(ptags);
+}
+
+/*******************************************************************
+ * 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
+ *
+ * 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)
+{
+	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++) {
+
+		/* 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
+ *
+ * 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)
+{
+	/* current ptags == NULL means "super capable" */
+	if (!cptags)
+		return 1;
+
+	/* check if the action is forbidden */
+	if (!check_action(cptags, tag, length, action, alen))
+		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);
+}
+
+/*
+ * 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
+ *
+ * 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 item *name = entry_name(*entry);
+	return check_tag(cptags, mptags, name->value, name->length, action,
+			 alen);
+}
+
+/*******************************************************************
+ * 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
+ *
+ * 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)
+{
+	int qkept, glob;
+	unsigned count;
+	struct slice slice;
+	struct entry *entries;
+
+	/* 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)
+		if (!qkept || entry_is_kept(entries[slice.lower++]))
+			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
+ *
+ * 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 slice slice;
+	unsigned taglen, idxval, vallen;
+	struct item *value;
+
+	/* 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))
+		return -EPERM;
+
+	/* search the entry */
+	slice = entries_search(mptags->entries, mptags->count, line, taglen, 0);
+	if (slice.lower == slice.upper)
+		return -ENOENT;
+
+	/* instanciate the value */
+	idxval = taglen + 1;
+	if (length <= idxval)
+		value = NULL;
+	else {
+		/* check validity of value */
+		vallen = length - idxval;
+		if (!is_valid_value(line + idxval, vallen))
+			return -EINVAL;
+		/* create the value */
+		value = item_create(line + idxval, vallen);
+		if (!value)
+			return -ENOMEM;
+	}
+
+	/* replace the previous value */
+	entry_set_value(&mptags->entries[slice.lower], value);
+	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
+ *
+ * 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 slice slice;
+	int subkept, glob;
+	unsigned i, j, count;
+	struct entry *entries, *entry, e;
+
+	/* 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))
+				entry_clear_kept(entry);
+			else if (!glob)
+				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))
+				entry_set_removed(entry);
+			else if (!glob)
+				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;
+		}
+	}
+	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
+ *
+ * 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 slice slice;
+	int addkept, glob;
+	unsigned i, n, count;
+	struct entry *entries, *entry;
+	struct item *name;
+
+	/* is for adding @s? */
+	addkept = length > 0 && line[0] == KEEP_CHAR;
+	if (addkept) {
+		line++;
+		length--;
+	}
+
+	/* is a global sub? */
+	glob = length > 1 && line[length - 2] == SEPAR_CHAR
+	    && line[length - 1] == GLOB_CHAR;
+	if (glob) {
+		/* global */
+		if (!addkept)
+			return -EINVAL;
+		length -= 1;
+		if (!is_valid_prefix(line, length))
+			return -EINVAL;
+	} else {
+		/* not global */
+		if (!is_valid_tag(line, length))
+			return -EINVAL;
+	}
+
+	/* search entry slice */
+	entries = mptags->entries;
+	count = mptags->count;
+	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))
+				entry_set_kept(entry);
+		}
+	} else if (slice.lower != slice.upper) {
+		/* add kept if needed */
+		entry = &entries[slice.lower];
+		if (addkept && !entry_is_kept(*entry)) {
+			if (!check_entry
+			    (cptags, mptags, entry, add_string,
+			     add_string_length))
+				return -EPERM;
+			entry_set_kept(entry);
+		}
+	} else {
+		/* adds a new entry */
+		if (count == MAXCOUNT)
+			return -ECANCELED;
+		if (!check_tag
+		    (cptags, mptags, line, length, add_string,
+		     add_string_length))
+			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, addkept, NULL);
+	}
+	return 0;
+}
+
+/**
+ * ptags_write_locked - 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_locked(struct ptags *cptags, struct ptags *mptags,
+			      const char *buffer, unsigned size)
+{
+	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] != '\n';
+		     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 '\n' */
+		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);
+
+		/* lines starting with SUB_CHAR */
+		else if (buffer[start] == SUB_CHAR)
+			err = ptags_sub(cptags, mptags,
+					buffer + start + 1, len - 1);
+
+		/* lines starting with SET_CHAR */
+		else if (buffer[start] == SET_CHAR)
+			err = ptags_set(cptags, mptags,
+					buffer + start + 1, len - 1);
+
+		/* lines starting with QUERY_CHAR */
+		else if (buffer[start] == QUERY_CHAR)
+			err = ptags_query(mptags, buffer + start + 1, len - 1);
+
+		/* 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_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;
+
+	/* crop the length */
+	length = (size > (size_t) INT_MAX) ? INT_MAX : (unsigned)size;
+
+	/* lock the ptagss */
+	if (!cptags || cptags == mptags) {
+		mutex_lock(&mptags->lock);
+		result = ptags_write_locked(cptags, mptags, buffer, length);
+		mutex_unlock(&mptags->lock);
+	} else {
+		ptags_lock2(cptags, mptags);
+		result = ptags_write_locked(cptags, mptags, buffer, length);
+		ptags_unlock2(cptags, mptags);
+	}
+	return result;
+}
+
+/**
+ * ptags_read_locked - Implement the reading of the tags
+ *
+ * @ptags: tags structure of the readen task
+ * @result: a pointer for storing the read result
+ *
+ * Returns the count of byte read or the negative code -ENOMEM
+ * if an allocation failed.
+ */
+static int ptags_read_locked(struct ptags *ptags, char **result)
+{
+	unsigned idx, count;
+	size_t size;
+	struct entry *entries;
+	char *buffer;
+
+	count = ptags->count;
+	entries = ptags->entries;
+	size = 0;
+	for (idx = 0; idx < count; idx++)
+		size += entry_print_length(entries[idx]);
+
+	if (size > INT_MAX)
+		return -E2BIG;
+	buffer = kmalloc(size, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	*result = buffer;
+	for (idx = 0; idx < count; idx++)
+		buffer = entry_print(entries[idx], buffer);
+
+	return (int)size;
+}
+
+/**
+ * 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;
+
+	mutex_lock(&ptags->lock);
+	result = ptags_read_locked(ptags, data);
+	mutex_unlock(&ptags->lock);
+	return result;
+}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 13185a6..9235d4e 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5732,6 +5732,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)
@@ -5779,6 +5783,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 87a9741..2fd7f26 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3603,13 +3603,18 @@  unlockandout:
  */
 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;