Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1505842 > unrolled thread

[PATCH 0/8] CaitSith LSM module

Started byTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
First post2016-10-21 14:50 +0200
Last post2016-10-25 13:30 +0200
Articles 12 — 3 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH 0/8] CaitSith LSM module Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2016-10-21 14:50 +0200
    [PATCH 7/8] CaitSith: Add garbage collector functions. Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2016-10-21 15:00 +0200
    [PATCH 2/8] CaitSith: Add pathname calculation functions. Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2016-10-21 15:00 +0200
    [PATCH 5/8] CaitSith: Add LSM adapter functions. Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2016-10-21 15:00 +0200
    [PATCH 6/8] CaitSith: Add policy loader functions. Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2016-10-21 15:00 +0200
    [PATCH 4/8] CaitSith: Add permission check functions. Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2016-10-21 15:00 +0200
    [PATCH 8/8] CaitSith: Add Kconfig and Makefile Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2016-10-21 15:00 +0200
    [PATCH 1/8] CaitSith: Add header file. Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2016-10-21 15:00 +0200
    Re: [PATCH 0/8] CaitSith LSM module James Morris <jmorris@namei.org> - 2016-10-24 06:50 +0200
      Re: [PATCH 0/8] CaitSith LSM module John Johansen <john.johansen@canonical.com> - 2016-10-24 16:40 +0200
    Re: [PATCH 0/8] CaitSith LSM module John Johansen <john.johansen@canonical.com> - 2016-10-24 20:20 +0200
      Re: [PATCH 0/8] CaitSith LSM module Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> - 2016-10-25 13:30 +0200

#1505842 — [PATCH 0/8] CaitSith LSM module

FromTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date2016-10-21 14:50 +0200
Subject[PATCH 0/8] CaitSith LSM module
Message-ID<suHqN-5zP-7@gated-at.bofh.it>
CaitSith (acronym for "Characteristic action inspection tool. See if
this helps.") is an LSM based access control implementation which uses
action check list (acl) as policy syntax.

Syntax of an acl block is shown below.

  acl "Action" "Whether to check Action or not"
    "Decision1" "Whether to use Decision1 or not"
    "Decision2" "Whether to use Decision2 or not"
    "Decision3" "Whether to use Decision3 or not"

  Specify Action as a key, and enumerate conditions as needed
  using variable=value or variable!=value expression.

  Split into two phases: "whether to check or not" and "whether
  to grant/deny or not".

  "Action" is name of action (e.g. "execve").

  "Decision" is either "allow" or "deny".

  You can define multiple acl blocks as needed. All acl blocks for
  each "Action" will be evaluated. The access request will be granted
  when none of deny line in all evaluated acl blocks matches.

  An unsigned integer value is prefixed to each line of rules for
  controlling priority of evaluation.

Examples shown below will help you understanding.

  Example 1: /usr/sbin/httpd is allowed to execute only
             /var/www/cgi-bin/counter.cgi and /usr/bin/suexec .

    1000 acl execute task.exe="/usr/sbin/httpd"
      100 allow path="/var/www/cgi-bin/counter.cgi"
      150 allow path="/usr/sbin/suexec"
      200 deny

  Example 2: /usr/sbin/httpd is not allowed to execute /bin/sh and
             /bin/bash .

    2000 acl execute task.exe="/usr/sbin/httpd"
      100 deny path="/bin/sh"
      150 deny path="/bin/bash"
      200 allow

  Example 3: /usr/sbin/suexec is allowed to be executed by only
             /usr/sbin/httpd .

    2000 acl execute path="/usr/sbin/suexec"
      100 allow task.exe="/usr/sbin/httpd"
      200 deny

CaitSith tries to remove many limitations which existing security
enhanced Linux (I mean any LSM based access control implementation) has.

  (1) CaitSith can use both string / numeric arguments (like TOMOYO and
      AppArmor) and security labels (like SELinux and Smack). There is no
      reason that access control implementation must not use both.

  (2) CaitSith can specify rules from the point of view of both subjects
      (a.k.a. capability list) and objects (a.k.a. access control list).
      There is no reason that access control implementation must use
      capability list which entails identification of all subjects.

      While security people are tempted to protect as much as possible,
      users generally have insufficient resource for protecting all. If
      users have sufficient resource, they will be already using existing
      implementations.

      I found users who want to restrict only a few objects without
      being bothered by managing all subjects in their systems. This is
      impossible for TOMOYO because TOMOYO entails managing all subjects
      in their systems. In CaitSith, this limitation is solved by writing
      rules using action as a key.

  (3) CaitSith can represent complicated string / numeric arguments
      compared to TOMOYO, for any condition is represented as zero or
      more repetition of variable=value or variable!=value expression.

      In TOMOYO, the policy syntax requires positional mandatory parameters
      (e.g. pathname when opening a pathname, pathname and DAC permission
      mode when creating a pathname) based on type and number of arguments
      for that action. But it turned out that using positional parameters
      undesirably limits ability to specify complicated conditions. For
      example, when specific pattern should be excluded from some pattern
      (e.g. "*" but "*.tmp"), administrator has to use \- operator (e.g.
      "\*\-\*.tmp") for that purpose. It makes conditions difficult to
      understand. \- operator needs to be used with cautions that unwanted
      patterns are not included by error. It made complicated conditions
      very hard to understand.
      In CaitSith, this limitation is solved by writing rules like
      path="\*" path!="\*.tmp" instead of path="\*\-\*.tmp" .

  (4) CaitSith can specify rules using both whitelisting and blacklisting.

      As far as I know, whoever involved in security enhanced Linux in
      Japan lost their jobs. In other words, security enhanced Linux made
      no business sense in Japan. I think that existing implementations
      are asking for too much skill/knowledge compared to users can afford.

      CaitSith's syntax acts as whitelisting if an unconditional deny line
      comes before an unconditional allow line comes, acts as blacklisting
      if an unconditional allow line comes before an unconditional deny
      line comes.

CaitSith stayed four years and a half listening for whether it suits
user's needs. In the last year or so, questions regarding how to use
TOMOYO are getting to come. However, it turned out that CaitSith seems
to fit better than TOMOYO for what many of the questioners want to
achieve. You can see slides shown below for full explanation of
the how and why.

  http://I-love.SAKURA.ne.jp/tomoyo/CaitSith-en.pdf (English)
  http://I-love.SAKURA.ne.jp/tomoyo/CaitSith-ja.pdf (Japanese)

In order to minimize the burden of reviewing, this patchset implements
only functionality of checking program execution requests (i.e. execve()
system call) using pathnames. I'm planning to add other functionalities
after this version got included into mainline. You can find how future
versions of CaitSith will look like at http://caitsith.osdn.jp/ .

Tetsuo Handa (8):
  CaitSith: Add header file.
  CaitSith: Add pathname calculation functions.
  CaitSith: Add policy I/O functions.
  CaitSith: Add permission check functions.
  CaitSith: Add LSM adapter functions.
  CaitSith: Add policy loader functions.
  CaitSith: Add garbage collector functions.
  CaitSith: Add Kconfig and Makefile

 security/Kconfig                |    6 +
 security/Makefile               |    2 +
 security/caitsith/Kconfig       |   48 ++
 security/caitsith/Makefile      |   15 +
 security/caitsith/caitsith.h    |  347 +++++++++
 security/caitsith/gc.c          |  373 ++++++++++
 security/caitsith/load_policy.c |  106 +++
 security/caitsith/lsm.c         |   60 ++
 security/caitsith/permission.c  |  691 +++++++++++++++++
 security/caitsith/policy_io.c   | 1553 +++++++++++++++++++++++++++++++++++++++
 security/caitsith/realpath.c    |  227 ++++++
 11 files changed, 3428 insertions(+)
 create mode 100644 security/caitsith/Kconfig
 create mode 100644 security/caitsith/Makefile
 create mode 100644 security/caitsith/caitsith.h
 create mode 100644 security/caitsith/gc.c
 create mode 100644 security/caitsith/load_policy.c
 create mode 100644 security/caitsith/lsm.c
 create mode 100644 security/caitsith/permission.c
 create mode 100644 security/caitsith/policy_io.c
 create mode 100644 security/caitsith/realpath.c

-- 
1.8.3.1

[toc] | [next] | [standalone]


#1505870 — [PATCH 7/8] CaitSith: Add garbage collector functions.

FromTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date2016-10-21 15:00 +0200
Subject[PATCH 7/8] CaitSith: Add garbage collector functions.
Message-ID<suHAu-5DG-23@gated-at.bofh.it>
In reply to#1505842
This file corresponds to security/tomoyo/gc.c . Since CaitSith uses
a common variable length data structure, garbage collector became
simpler than TOMOYO.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/caitsith/gc.c | 373 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 373 insertions(+)
 create mode 100644 security/caitsith/gc.c

diff --git a/security/caitsith/gc.c b/security/caitsith/gc.c
new file mode 100644
index 0000000..d55f02d
--- /dev/null
+++ b/security/caitsith/gc.c
@@ -0,0 +1,373 @@
+/*
+ * security/caitsith/gc.c
+ *
+ * Copyright (C) 2005-2012  NTT DATA CORPORATION
+ */
+
+#include "caitsith.h"
+
+/* The list for "struct cs_io_buffer". */
+static LIST_HEAD(cs_io_buffer_list);
+/* Lock for protecting cs_io_buffer_list. */
+static DEFINE_SPINLOCK(cs_io_buffer_list_lock);
+
+static bool cs_name_used_by_io_buffer(const char *string, const size_t size);
+static bool cs_struct_used_by_io_buffer(const struct list_head *element);
+static int cs_gc_thread(void *unused);
+static void cs_collect_acl(struct list_head *list);
+static void cs_collect_entry(void);
+static void cs_try_to_gc(const enum cs_policy_id type,
+			 struct list_head *element);
+
+/*
+ * Lock for syscall users.
+ *
+ * This lock is held for only protecting single SRCU section.
+ */
+struct srcu_struct cs_ss;
+
+/**
+ * cs_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/caitsith/ users or not.
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns true if @element is used by /sys/kernel/security/caitsith/ users,
+ * false otherwise.
+ */
+static bool cs_struct_used_by_io_buffer(const struct list_head *element)
+{
+	struct cs_io_buffer *head;
+	bool in_use = false;
+
+	spin_lock(&cs_io_buffer_list_lock);
+	list_for_each_entry(head, &cs_io_buffer_list, list) {
+		head->users++;
+		spin_unlock(&cs_io_buffer_list_lock);
+		mutex_lock(&head->io_sem);
+		if (head->r.acl == element || head->r.subacl == element ||
+		    &head->w.acl->list == element)
+			in_use = true;
+		mutex_unlock(&head->io_sem);
+		spin_lock(&cs_io_buffer_list_lock);
+		head->users--;
+		if (in_use)
+			break;
+	}
+	spin_unlock(&cs_io_buffer_list_lock);
+	return in_use;
+}
+
+/**
+ * cs_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/caitsith/ users or not.
+ *
+ * @string: String to check.
+ * @size:   Memory allocated for @string .
+ *
+ * Returns true if @string is used by /sys/kernel/security/caitsith/ users,
+ * false otherwise.
+ */
+static bool cs_name_used_by_io_buffer(const char *string, const size_t size)
+{
+	struct cs_io_buffer *head;
+	bool in_use = false;
+
+	spin_lock(&cs_io_buffer_list_lock);
+	list_for_each_entry(head, &cs_io_buffer_list, list) {
+		int i;
+
+		head->users++;
+		spin_unlock(&cs_io_buffer_list_lock);
+		mutex_lock(&head->io_sem);
+		for (i = 0; i < CS_MAX_IO_READ_QUEUE; i++) {
+			const char *w = head->r.w[i];
+
+			if (w < string || w > string + size)
+				continue;
+			in_use = true;
+			break;
+		}
+		mutex_unlock(&head->io_sem);
+		spin_lock(&cs_io_buffer_list_lock);
+		head->users--;
+		if (in_use)
+			break;
+	}
+	spin_unlock(&cs_io_buffer_list_lock);
+	return in_use;
+}
+
+/**
+ * cs_acl_info_has_sub_acl - Clear "struct cs_acl_info"->acl_info.
+ *
+ * @list: Pointer to "struct list_head".
+ *
+ * Returns true if @list is not empty, false otherwise.
+ */
+static bool cs_acl_info_has_sub_acl(struct list_head *list)
+{
+	struct cs_acl_info *acl;
+	struct cs_acl_info *tmp;
+
+	if (list_empty(list))
+		return false;
+	mutex_lock(&cs_policy_lock);
+	list_for_each_entry_safe(acl, tmp, list, list) {
+		cs_try_to_gc(CS_ID_ACL, &acl->list);
+	}
+	mutex_unlock(&cs_policy_lock);
+	return !list_empty(list);
+}
+
+/**
+ * cs_del_acl - Delete members in "struct cs_acl_info".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
+static inline void cs_del_acl(struct list_head *element)
+{
+	struct cs_acl_info *acl = container_of(element, typeof(*acl), list);
+
+	cs_put_condition(acl->cond);
+}
+
+/**
+ * cs_del_condition - Delete members in "struct cs_condition".
+ *
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
+void cs_del_condition(struct list_head *element)
+{
+	struct cs_condition *cond = container_of(element, typeof(*cond),
+						 head.list);
+	const union cs_condition_element *condp = (typeof(condp)) (cond + 1);
+
+	while ((void *) condp < (void *) ((u8 *) cond) + cond->size) {
+		const enum cs_conditions_index right = condp->right;
+
+		condp++;
+		if (right == CS_IMM_NAME_ENTRY) {
+			if (condp->path != &cs_null_name)
+				cs_put_name(condp->path);
+			condp++;
+		}
+	}
+}
+
+/**
+ * cs_try_to_gc - Try to kfree() an entry.
+ *
+ * @type:    One of values in "enum cs_policy_id".
+ * @element: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ *
+ * Caller holds cs_policy_lock mutex.
+ */
+static void cs_try_to_gc(const enum cs_policy_id type,
+			 struct list_head *element)
+{
+	/*
+	 * __list_del_entry() guarantees that the list element became no longer
+	 * reachable from the list which the element was originally on (e.g.
+	 * cs_domain_list). Also, synchronize_srcu() guarantees that the list
+	 * element became no longer referenced by syscall users.
+	 */
+	__list_del_entry(element);
+	mutex_unlock(&cs_policy_lock);
+	synchronize_srcu(&cs_ss);
+	/*
+	 * However, there are two users which may still be using the list
+	 * element. We need to defer until both users forget this element.
+	 *
+	 * Don't kfree() until "struct cs_io_buffer"->r.{acl,subacl} and
+	 * "struct cs_io_buffer"->w.acl forget this element.
+	 */
+	if (cs_struct_used_by_io_buffer(element))
+		goto reinject;
+	switch (type) {
+	case CS_ID_CONDITION:
+		cs_del_condition(element);
+		break;
+	case CS_ID_NAME:
+		/*
+		 * Don't kfree() until all "struct cs_io_buffer"->r.w[] forget
+		 * this element.
+		 */
+		if (cs_name_used_by_io_buffer
+		    (container_of(element, typeof(struct cs_name),
+				  head.list)->entry.name,
+		     container_of(element, typeof(struct cs_name),
+				  head.list)->size))
+			goto reinject;
+		break;
+	case CS_ID_ACL:
+		/*
+		 * Don't kfree() until "struct cs_acl_info"->acl_info_list
+		 * becomes empty.
+		 */
+		if (cs_acl_info_has_sub_acl
+		    (&container_of(element, typeof(struct cs_acl_info),
+				   list)->acl_info_list))
+			goto reinject;
+		cs_del_acl(element);
+		break;
+	default:
+		break;
+	}
+	mutex_lock(&cs_policy_lock);
+	cs_memory_used[CS_MEMORY_POLICY] -= ksize(element);
+	kfree(element);
+	return;
+reinject:
+	/*
+	 * We can safely reinject this element here bacause
+	 * (1) Appending list elements and removing list elements are protected
+	 *     by cs_policy_lock mutex.
+	 * (2) Only this function removes list elements and this function is
+	 *     exclusively executed by cs_gc_mutex mutex.
+	 * are true.
+	 */
+	mutex_lock(&cs_policy_lock);
+	list_add_rcu(element, element->prev);
+}
+
+/**
+ * cs_collect_acl - Delete elements in "struct cs_acl_info".
+ *
+ * @list: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ *
+ * Caller holds cs_policy_lock mutex.
+ */
+static void cs_collect_acl(struct list_head *list)
+{
+	struct cs_acl_info *acl;
+	struct cs_acl_info *tmp;
+
+	list_for_each_entry_safe(acl, tmp, list, list) {
+		if (!acl->is_deleted)
+			continue;
+		cs_try_to_gc(CS_ID_ACL, &acl->list);
+	}
+}
+
+/**
+ * cs_collect_entry - Try to kfree() deleted elements.
+ *
+ * Returns nothing.
+ */
+static void cs_collect_entry(void)
+{
+	int i;
+
+	mutex_lock(&cs_policy_lock);
+	for (i = 0; i < CS_MAX_MAC_INDEX; i++) {
+		struct cs_acl_info *ptr;
+		struct cs_acl_info *tmp;
+		struct list_head * const list = &cs_acl_list[i];
+
+		list_for_each_entry_safe(ptr, tmp, list, list) {
+			cs_collect_acl(&ptr->acl_info_list);
+			if (!ptr->is_deleted)
+				continue;
+			ptr->is_deleted = CS_GC_IN_PROGRESS;
+			cs_try_to_gc(CS_ID_ACL, &ptr->list);
+		}
+	}
+	{
+		struct cs_shared_acl_head *ptr;
+		struct cs_shared_acl_head *tmp;
+
+		list_for_each_entry_safe(ptr, tmp, &cs_condition_list, list) {
+			if (atomic_read(&ptr->users) > 0)
+				continue;
+			atomic_set(&ptr->users, CS_GC_IN_PROGRESS);
+			cs_try_to_gc(CS_ID_CONDITION, &ptr->list);
+		}
+	}
+	for (i = 0; i < CS_MAX_HASH; i++) {
+		struct list_head *list = &cs_name_list[i];
+		struct cs_shared_acl_head *ptr;
+		struct cs_shared_acl_head *tmp;
+
+		list_for_each_entry_safe(ptr, tmp, list, list) {
+			if (atomic_read(&ptr->users) > 0)
+				continue;
+			atomic_set(&ptr->users, CS_GC_IN_PROGRESS);
+			cs_try_to_gc(CS_ID_NAME, &ptr->list);
+		}
+	}
+	mutex_unlock(&cs_policy_lock);
+}
+
+/**
+ * cs_gc_thread - Garbage collector thread function.
+ *
+ * @unused: Unused.
+ *
+ * Returns 0.
+ */
+static int cs_gc_thread(void *unused)
+{
+	/* Garbage collector thread is exclusive. */
+	static DEFINE_MUTEX(cs_gc_mutex);
+
+	if (!mutex_trylock(&cs_gc_mutex))
+		goto out;
+	cs_collect_entry();
+	{
+		struct cs_io_buffer *head;
+		struct cs_io_buffer *tmp;
+
+		spin_lock(&cs_io_buffer_list_lock);
+		list_for_each_entry_safe(head, tmp, &cs_io_buffer_list,
+					 list) {
+			if (head->users)
+				continue;
+			list_del(&head->list);
+			kfree(head->read_buf);
+			kfree(head->write_buf);
+			kfree(head);
+		}
+		spin_unlock(&cs_io_buffer_list_lock);
+	}
+	mutex_unlock(&cs_gc_mutex);
+out:
+	/* This acts as do_exit(0). */
+	return 0;
+}
+
+/**
+ * cs_notify_gc - Register/unregister /sys/kernel/security/caitsith/ users.
+ *
+ * @head:        Pointer to "struct cs_io_buffer".
+ * @is_register: True if register, false if unregister.
+ *
+ * Returns nothing.
+ */
+void cs_notify_gc(struct cs_io_buffer *head, const bool is_register)
+{
+	bool is_write = false;
+
+	spin_lock(&cs_io_buffer_list_lock);
+	if (is_register) {
+		head->users = 1;
+		list_add(&head->list, &cs_io_buffer_list);
+	} else {
+		is_write = head->write_buf != NULL;
+		if (!--head->users) {
+			list_del(&head->list);
+			kfree(head->read_buf);
+			kfree(head->write_buf);
+			kfree(head);
+		}
+	}
+	spin_unlock(&cs_io_buffer_list_lock);
+	if (is_write)
+		kthread_run(cs_gc_thread, NULL, "CaitSith's GC");
+}
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1505873 — [PATCH 2/8] CaitSith: Add pathname calculation functions.

FromTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date2016-10-21 15:00 +0200
Subject[PATCH 2/8] CaitSith: Add pathname calculation functions.
Message-ID<suHAu-5DG-27@gated-at.bofh.it>
In reply to#1505842
This is a simplified version of security/tomoyo/realpath.c , except
two changes listed below.

  A "\" character (0x5C) in TOMOYO is represented as "\\" while
  it in CaitSith is represented as "\134". This allows us to tell
  whether a "\" character at any position is a literal "\" character
  without scanning from the beginning of the string.

  A "/" character (0x2F) is appended in TOMOYO in order to represent
  a directory while it is not appended in CaitSith. In CaitSith,
  path.type variable which will be implemented after this version is
  accepted will allow us to tell whether a pathname represents a
  directory without appending a "/" character.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/caitsith/realpath.c | 227 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 227 insertions(+)
 create mode 100644 security/caitsith/realpath.c

diff --git a/security/caitsith/realpath.c b/security/caitsith/realpath.c
new file mode 100644
index 0000000..f62d843
--- /dev/null
+++ b/security/caitsith/realpath.c
@@ -0,0 +1,227 @@
+/*
+ * security/caitsith/realpath.c
+ *
+ * Copyright (C) 2005-2012  NTT DATA CORPORATION
+ */
+
+#include "caitsith.h"
+
+/**
+ * cs_realpath - Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns the realpath of the given @path on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+char *cs_realpath(const struct path *path)
+{
+	char *buf = NULL;
+	char *name = NULL;
+	unsigned int buf_len = PAGE_SIZE / 2;
+	struct dentry *dentry = path->dentry;
+	struct super_block *sb;
+
+	if (!dentry)
+		return NULL;
+	sb = dentry->d_sb;
+	while (1) {
+		char *pos;
+
+		buf_len <<= 1;
+		kfree(buf);
+		buf = kmalloc(buf_len, GFP_NOFS);
+		if (!buf)
+			break;
+		/* To make sure that pos is '\0' terminated. */
+		buf[buf_len - 1] = '\0';
+		/* For "pipe:[\$]". */
+		if (dentry->d_op && dentry->d_op->d_dname)
+			pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
+		else
+			pos = d_absolute_path(path, buf, buf_len);
+		if (IS_ERR(pos))
+			continue;
+		name = cs_encode(pos);
+		break;
+	}
+	kfree(buf);
+	if (!name)
+		cs_warn_oom(__func__);
+	return name;
+}
+
+/**
+ * cs_encode2 - Encode binary string to ascii string.
+ *
+ * @str:     String in binary format. Maybe NULL.
+ * @str_len: Size of @str in byte.
+ *
+ * Returns pointer to @str in ascii format on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+static char *cs_encode2(const char *str, int str_len)
+{
+	int i;
+	int len;
+	const char *p = str;
+	char *cp;
+	char *cp0;
+
+	if (!p)
+		return NULL;
+	len = str_len;
+	for (i = 0; i < str_len; i++) {
+		const unsigned char c = p[i];
+
+		if (!(c > ' ' && c < 127 && c != '\\'))
+			len += 3;
+	}
+	len++;
+	cp = kzalloc(len, GFP_NOFS);
+	if (!cp)
+		return NULL;
+	cp0 = cp;
+	p = str;
+	for (i = 0; i < str_len; i++) {
+		const unsigned char c = p[i];
+
+		if (c > ' ' && c < 127 && c != '\\') {
+			*cp++ = c;
+		} else {
+			*cp++ = '\\';
+			*cp++ = (c >> 6) + '0';
+			*cp++ = ((c >> 3) & 7) + '0';
+			*cp++ = (c & 7) + '0';
+		}
+	}
+	return cp0;
+}
+
+/**
+ * cs_encode - Encode binary string to ascii string.
+ *
+ * @str: String in binary format. Maybe NULL.
+ *
+ * Returns pointer to @str in ascii format on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so caller must kfree() if this function
+ * didn't return NULL.
+ */
+char *cs_encode(const char *str)
+{
+	return str ? cs_encode2(str, strlen(str)) : NULL;
+}
+
+/**
+ * cs_const_part_length - Evaluate the initial length without a pattern in a token.
+ *
+ * @filename: The string to evaluate. Maybe NULL.
+ *
+ * Returns the initial length without a pattern in @filename.
+ */
+static int cs_const_part_length(const char *filename)
+{
+	char c;
+	int len = 0;
+
+	if (!filename)
+		return 0;
+	while (1) {
+		c = *filename++;
+		if (!c)
+			break;
+		if (c != '\\') {
+			len++;
+			continue;
+		}
+		c = *filename++;
+		switch (c) {
+		case '0':   /* "\ooo" */
+		case '1':
+		case '2':
+		case '3':
+			c = *filename++;
+			if (c < '0' || c > '7')
+				break;
+			c = *filename++;
+			if (c < '0' || c > '7')
+				break;
+			len += 4;
+			continue;
+		}
+		break;
+	}
+	return len;
+}
+
+/**
+ * cs_fill_path_info - Fill in "struct cs_path_info" members.
+ *
+ * @ptr: Pointer to "struct cs_path_info" to fill in.
+ *
+ * Returns nothing.
+ *
+ * The caller sets "struct cs_path_info"->name.
+ */
+void cs_fill_path_info(struct cs_path_info *ptr)
+{
+	const char *name = ptr->name;
+	const int len = strlen(name);
+
+	ptr->total_len = len;
+	ptr->const_len = cs_const_part_length(name);
+	ptr->hash = full_name_hash(NULL, name, len);
+}
+
+/**
+ * cs_get_exe - Get cs_realpath() of current process.
+ *
+ * Returns the cs_realpath() of current process on success, NULL otherwise.
+ *
+ * This function uses kzalloc(), so the caller must kfree()
+ * if this function didn't return NULL.
+ */
+char *cs_get_exe(void)
+{
+	struct mm_struct *mm = current->mm;
+	char *cp;
+	struct file *exe_file;
+
+	if (current->flags & PF_KTHREAD)
+		return kstrdup("<kernel>", GFP_NOFS);
+	if (!mm)
+		goto task_has_no_mm;
+	exe_file = get_mm_exe_file(mm);
+	if (!exe_file)
+		goto task_has_no_mm;
+	cp = cs_realpath(&exe_file->f_path);
+	fput(exe_file);
+	return cp;
+task_has_no_mm:
+	return kstrdup("<unknown>", GFP_NOFS);
+}
+
+/**
+ * cs_get_exename - Get cs_realpath() of current process.
+ *
+ * @buf: Pointer to "struct cs_path_info".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * This function uses kzalloc(), so the caller must kfree()
+ * if this function returned true.
+ */
+bool cs_get_exename(struct cs_path_info *buf)
+{
+	buf->name = cs_get_exe();
+	if (buf->name) {
+		cs_fill_path_info(buf);
+		return true;
+	}
+	return false;
+}
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1505874 — [PATCH 5/8] CaitSith: Add LSM adapter functions.

FromTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date2016-10-21 15:00 +0200
Subject[PATCH 5/8] CaitSith: Add LSM adapter functions.
Message-ID<suHAv-5DG-39@gated-at.bofh.it>
In reply to#1505842
This version implements only execve() related LSM hooks.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/caitsith/lsm.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 security/caitsith/lsm.c

diff --git a/security/caitsith/lsm.c b/security/caitsith/lsm.c
new file mode 100644
index 0000000..675cee8
--- /dev/null
+++ b/security/caitsith/lsm.c
@@ -0,0 +1,60 @@
+/*
+ * security/caitsith/lsm.c
+ *
+ * Copyright (C) 2010-2013  Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+ */
+
+#include <linux/lsm_hooks.h>
+#include "caitsith.h"
+
+/**
+ * caitsith_bprm_set_creds - Target for security_bprm_set_creds().
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int caitsith_bprm_set_creds(struct linux_binprm *bprm)
+{
+	/*
+	 * Do only if this function is called for the first time of an execve
+	 * operation.
+	 */
+	if (bprm->cred_prepared)
+		return 0;
+#ifndef CONFIG_SECURITY_CAITSITH_OMIT_USERSPACE_LOADER
+	/*
+	 * Load policy if /sbin/caitsith-init exists and /sbin/init is requested
+	 * for the first time.
+	 */
+	if (!cs_policy_loaded)
+		cs_load_policy(bprm->filename);
+#endif
+	return cs_start_execve(bprm);
+}
+
+/*
+ * caitsith_security_ops is a "struct security_operations" which is used for
+ * registering CaitSith.
+ */
+static struct security_hook_list caitsith_hooks[] = {
+	LSM_HOOK_INIT(bprm_set_creds, caitsith_bprm_set_creds),
+};
+
+/**
+ * caitsith_init - Register CaitSith as a LSM module.
+ *
+ * Returns 0.
+ */
+static int __init caitsith_init(void)
+{
+	if (!security_module_enable("caitsith"))
+		return 0;
+	/* register ourselves with the security framework */
+	security_add_hooks(caitsith_hooks, ARRAY_SIZE(caitsith_hooks));
+	printk(KERN_INFO "CaitSith initialized\n");
+	cs_init_module();
+	return 0;
+}
+
+security_initcall(caitsith_init);
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1505884 — [PATCH 6/8] CaitSith: Add policy loader functions.

FromTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date2016-10-21 15:00 +0200
Subject[PATCH 6/8] CaitSith: Add policy loader functions.
Message-ID<suHAv-5DG-55@gated-at.bofh.it>
In reply to#1505842
This file allows userspace tools to load policy configuration
unless CONFIG_SECURITY_CAITSITH_OMIT_USERSPACE_LOADER is defined.

If CONFIG_SECURITY_CAITSITH_OMIT_USERSPACE_LOADER is defined,
only built-in policy configuration which is generated as
security/caitsith/builtin-policy.h at compilation time from
security/caitsith/policy/policy.conf will be loaded.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/caitsith/load_policy.c | 106 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 106 insertions(+)
 create mode 100644 security/caitsith/load_policy.c

diff --git a/security/caitsith/load_policy.c b/security/caitsith/load_policy.c
new file mode 100644
index 0000000..e9b9706
--- /dev/null
+++ b/security/caitsith/load_policy.c
@@ -0,0 +1,106 @@
+/*
+ * security/caitsith/load_policy.c
+ *
+ * Copyright (C) 2005-2012  NTT DATA CORPORATION
+ */
+
+#include "caitsith.h"
+
+#ifndef CONFIG_SECURITY_CAITSITH_OMIT_USERSPACE_LOADER
+
+/* Path to the policy loader. */
+static const char *cs_loader;
+
+/**
+ * cs_loader_setup - Set policy loader.
+ *
+ * @str: Program to use as a policy loader (e.g. /sbin/caitsith-init ).
+ *
+ * Returns 0.
+ */
+static int __init cs_loader_setup(char *str)
+{
+	cs_loader = str;
+	return 0;
+}
+
+__setup("CS_loader=", cs_loader_setup);
+
+/**
+ * cs_policy_loader_exists - Check whether /sbin/caitsith-init exists.
+ *
+ * Returns true if /sbin/caitsith-init exists, false otherwise.
+ */
+static bool cs_policy_loader_exists(void)
+{
+	struct path path;
+
+	if (!cs_loader)
+		cs_loader = CONFIG_SECURITY_CAITSITH_POLICY_LOADER;
+	if (kern_path(cs_loader, LOOKUP_FOLLOW, &path) == 0) {
+		path_put(&path);
+		return true;
+	}
+	printk(KERN_INFO "Not activating CaitSith as %s does not exist.\n",
+	       cs_loader);
+	return false;
+}
+
+/* Path to the trigger. */
+static const char *cs_trigger;
+
+/**
+ * cs_trigger_setup - Set trigger for activation.
+ *
+ * @str: Program to use as an activation trigger (e.g. /sbin/init ).
+ *
+ * Returns 0.
+ */
+static int __init cs_trigger_setup(char *str)
+{
+	cs_trigger = str;
+	return 0;
+}
+
+__setup("CS_trigger=", cs_trigger_setup);
+
+/**
+ * cs_load_policy - Run external policy loader to load policy.
+ *
+ * @filename: The program about to start.
+ *
+ * Returns nothing.
+ *
+ * This function checks whether @filename is /sbin/init, and if so
+ * invoke /sbin/caitsith-init and wait for the termination of
+ * /sbin/caitsith-init and then continues invocation of /sbin/init.
+ * /sbin/caitsith-init reads policy files in /etc/caitsith/ directory and
+ * writes to /sys/kernel/security/caitsith/ interfaces.
+ */
+void cs_load_policy(const char *filename)
+{
+	static _Bool done;
+	char *argv[2];
+	char *envp[3];
+
+	if (done)
+		return;
+	if (!cs_trigger)
+		cs_trigger = CONFIG_SECURITY_CAITSITH_ACTIVATION_TRIGGER;
+	if (strcmp(filename, cs_trigger))
+		return;
+	if (!cs_policy_loader_exists())
+		return;
+	done = 1;
+	printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
+	       cs_loader);
+	argv[0] = (char *) cs_loader;
+	argv[1] = NULL;
+	envp[0] = "HOME=/";
+	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+	envp[2] = NULL;
+	call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
+	cs_check_profile();
+}
+
+#endif
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1505893 — [PATCH 4/8] CaitSith: Add permission check functions.

FromTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date2016-10-21 15:00 +0200
Subject[PATCH 4/8] CaitSith: Add permission check functions.
Message-ID<suHAv-5DG-69@gated-at.bofh.it>
In reply to#1505842
This file corresponds to security/tomoyo/condition.c and
security/tomoyo/file.c .

Compared to TOMOYO, a new wildcard /\(dir\)/ has been introduced for
helping converting from (e.g.) "/tmp/\{\*\}/" to "/tmp/\(\*\)/\*", for
directory's pathname (except the root directory itself) no longer ends
with / character which previously matched /\{\*\}/ wildcard.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/caitsith/permission.c | 691 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 691 insertions(+)
 create mode 100644 security/caitsith/permission.c

diff --git a/security/caitsith/permission.c b/security/caitsith/permission.c
new file mode 100644
index 0000000..9b33bea
--- /dev/null
+++ b/security/caitsith/permission.c
@@ -0,0 +1,691 @@
+/*
+ * security/caitsith/permission.c
+ *
+ * Copyright (C) 2005-2012  NTT DATA CORPORATION
+ */
+
+#include "caitsith.h"
+
+/* Type of condition argument. */
+enum cs_arg_type {
+	CS_ARG_TYPE_NONE,
+	CS_ARG_TYPE_NAME,
+} __packed;
+
+/* Structure for holding single condition component. */
+struct cs_cond_arg {
+	enum cs_arg_type type;
+	const struct cs_path_info *name;
+};
+
+static bool cs_alphabet_char(const char c);
+static bool cs_byte_range(const char *str);
+static bool cs_check_entry(struct cs_request_info *r,
+			   const struct cs_acl_info *ptr);
+static bool cs_condition(struct cs_request_info *r,
+			 const struct cs_condition *cond);
+static bool cs_file_matches_pattern(const char *filename,
+				    const char *filename_end,
+				    const char *pattern,
+				    const char *pattern_end);
+static bool cs_file_matches_pattern2(const char *filename,
+				     const char *filename_end,
+				     const char *pattern,
+				     const char *pattern_end);
+static bool cs_path_matches_pattern(const struct cs_path_info *filename,
+				    const struct cs_path_info *pattern);
+static bool cs_path_matches_pattern2(const char *f, const char *p);
+static int cs_execute_path(struct linux_binprm *bprm, struct path *path);
+static int cs_execute(struct cs_request_info *r);
+static void cs_clear_request_info(struct cs_request_info *r);
+
+/* The list for ACL policy. */
+struct list_head cs_acl_list[CS_MAX_MAC_INDEX];
+
+/* NULL value. */
+struct cs_path_info cs_null_name;
+
+/**
+ * cs_check_entry - Do permission check.
+ *
+ * @r:   Pointer to "struct cs_request_info".
+ * @ptr: Pointer to "struct cs_acl_info".
+ *
+ * Returns true on match, false otherwise.
+ *
+ * Caller holds cs_read_lock().
+ */
+static bool cs_check_entry(struct cs_request_info *r,
+			   const struct cs_acl_info *ptr)
+{
+	return !ptr->is_deleted && cs_condition(r, ptr->cond);
+}
+
+/**
+ * cs_check_acl_list - Do permission check.
+ *
+ * @r: Pointer to "struct cs_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds cs_read_lock().
+ */
+static int cs_check_acl_list(struct cs_request_info *r)
+{
+	struct cs_acl_info *ptr;
+	int error = 0;
+	struct list_head * const list = &cs_acl_list[r->type];
+
+	r->matched_acl = NULL;
+	list_for_each_entry_rcu(ptr, list, list) {
+		struct cs_acl_info *ptr2;
+
+		if (!cs_check_entry(r, ptr)) {
+			if (unlikely(r->failed_by_oom))
+				goto oom;
+			continue;
+		}
+		r->matched_acl = ptr;
+		r->result = CS_MATCHING_UNMATCHED;
+		list_for_each_entry_rcu(ptr2, &ptr->acl_info_list, list) {
+			if (!cs_check_entry(r, ptr2)) {
+				if (unlikely(r->failed_by_oom))
+					goto oom;
+				continue;
+			}
+			r->result = ptr2->is_deny ?
+				CS_MATCHING_DENIED : CS_MATCHING_ALLOWED;
+			break;
+		}
+		error = cs_audit_log(r);
+		/* Ignore out of memory during audit. */
+		r->failed_by_oom = false;
+		if (error)
+			break;
+	}
+	return error;
+oom:
+	/*
+	 * If conditions could not be checked due to out of memory,
+	 * reject the request with -ENOMEM, for we don't know whether
+	 * there was a possibility of matching "deny" lines or not.
+	 */
+	{
+		static unsigned long cs_last_oom;
+		unsigned long oom = get_seconds();
+
+		if (oom != cs_last_oom) {
+			cs_last_oom = oom;
+			printk(KERN_INFO "CaitSith: Rejecting access request due to out of memory.\n");
+		}
+	}
+	return -ENOMEM;
+}
+
+/**
+ * cs_check_acl - Do permission check.
+ *
+ * @r:     Pointer to "struct cs_request_info".
+ * @clear: True to cleanup @r before return, false otherwise.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int cs_check_acl(struct cs_request_info *r, const bool clear)
+{
+	int error;
+	const int idx = cs_read_lock();
+
+	error = cs_check_acl_list(r);
+	cs_read_unlock(idx);
+	if (clear)
+		cs_clear_request_info(r);
+	return error;
+}
+
+/**
+ * cs_execute - Check permission for "execute".
+ *
+ * @r: Pointer to "struct cs_request_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds cs_read_lock().
+ */
+static int cs_execute(struct cs_request_info *r)
+{
+	int retval;
+
+	/* Get symlink's dentry/vfsmount. */
+	retval = cs_execute_path(r->bprm, &r->obj.path[1]);
+	if (retval < 0)
+		return retval;
+	cs_populate_patharg(r, false);
+	if (!r->param.s[1])
+		return -ENOMEM;
+
+	/* Check execute permission. */
+	r->type = CS_MAC_EXECUTE;
+	return cs_check_acl(r, false);
+}
+
+/**
+ * cs_start_execve - Prepare for execve() operation.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int cs_start_execve(struct linux_binprm *bprm)
+{
+	int retval;
+	struct cs_request_info r = { };
+	int idx;
+
+	r.tmp = kzalloc(CS_EXEC_TMPSIZE, GFP_NOFS);
+	if (!r.tmp)
+		return -ENOMEM;
+	idx = cs_read_lock();
+	r.bprm = bprm;
+	r.obj.path[0] = bprm->file->f_path;
+	retval = cs_execute(&r);
+	cs_clear_request_info(&r);
+	/* Drop refcount obtained by cs_execute_path(). */
+	if (r.obj.path[1].dentry) {
+		path_put(&r.obj.path[1]);
+		r.obj.path[1].dentry = NULL;
+	}
+	cs_read_unlock(idx);
+	kfree(r.tmp);
+	return retval;
+}
+
+/**
+ * cs_execute_path - Get dentry/vfsmount of a program.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_execute_path(struct linux_binprm *bprm, struct path *path)
+{
+	/*
+	 * Follow symlinks if the requested pathname is on procfs, for
+	 * /proc/\$/exe is meaningless.
+	 */
+	const unsigned int follow =
+		(bprm->file->f_path.dentry->d_sb->s_magic == PROC_SUPER_MAGIC)
+		? LOOKUP_FOLLOW : 0;
+
+	if (kern_path(bprm->filename, follow, path))
+		return -ENOENT;
+	return 0;
+}
+
+/**
+ * cs_manager - Check whether the current process is a policy manager.
+ *
+ * Returns true if the current process is permitted to modify policy
+ * via /sys/kernel/security/caitsith/ interface.
+ *
+ * Caller holds cs_read_lock().
+ */
+bool cs_manager(void)
+{
+	if (!cs_policy_loaded)
+		return true;
+	{
+		struct cs_request_info r = { };
+
+		r.type = CS_MAC_MODIFY_POLICY;
+		if (cs_check_acl(&r, true) == 0)
+			return true;
+	}
+	{ /* Reduce error messages. */
+		static pid_t cs_last_pid;
+		const pid_t pid = current->pid;
+
+		if (cs_last_pid != pid) {
+			const char *exe = cs_get_exe();
+
+			printk(KERN_WARNING "'%s' (pid=%u) is not permitted to update policies.\n",
+			       exe, pid);
+			cs_last_pid = pid;
+			kfree(exe);
+		}
+	}
+	return false;
+}
+
+/**
+ * cs_populate_patharg - Calculate pathname for permission check and audit logs.
+ *
+ * @r:     Pointer to "struct cs_request_info".
+ * @first: True for first pathname, false for second pathname.
+ *
+ * Returns nothing.
+ */
+void cs_populate_patharg(struct cs_request_info *r, const bool first)
+{
+	struct cs_path_info *buf = &r->obj.pathname[!first];
+	struct path *path = &r->obj.path[!first];
+
+	if (!buf->name && path->dentry) {
+		buf->name = cs_realpath(path);
+		/* Set OOM flag if failed. */
+		if (!buf->name) {
+			r->failed_by_oom = true;
+			return;
+		}
+		cs_fill_path_info(buf);
+	}
+	if (!r->param.s[!first] && buf->name)
+		r->param.s[!first] = buf;
+}
+
+/**
+ * cs_cond2arg - Assign values to condition variables.
+ *
+ * @arg:   Pointer to "struct cs_cond_arg".
+ * @cmd:   One of values in "enum cs_conditions_index".
+ * @condp: Pointer to "union cs_condition_element *".
+ * @r:     Pointer to "struct cs_request_info".
+ *
+ * Returns true on success, false othwerwise.
+ *
+ * This function should not fail. But it can fail if (for example) out of
+ * memory has occurred while calculating cs_populate_patharg() or
+ * cs_get_exename().
+ */
+static bool cs_cond2arg(struct cs_cond_arg *arg,
+			const enum cs_conditions_index cmd,
+			const union cs_condition_element **condp,
+			struct cs_request_info *r)
+{
+	switch (cmd) {
+	case CS_COND_SARG0:
+		if (!r->param.s[0])
+			cs_populate_patharg(r, true);
+		arg->name = r->param.s[0];
+		break;
+	case CS_COND_SARG1:
+		if (!r->param.s[1])
+			cs_populate_patharg(r, false);
+		arg->name = r->param.s[1];
+		break;
+	case CS_IMM_NAME_ENTRY:
+		arg->name = (*condp)->path;
+		(*condp)++;
+		break;
+	case CS_SELF_EXE:
+		if (!r->exename.name) {
+			cs_get_exename(&r->exename);
+			/* Set OOM flag if failed. */
+			if (!r->exename.name)
+				r->failed_by_oom = true;
+		}
+		arg->name = &r->exename;
+		break;
+	default:
+		arg->name = NULL;
+		break;
+	}
+	if (!arg->name)
+		return false;
+	arg->type = CS_ARG_TYPE_NAME;
+	return true;
+}
+
+/**
+ * cs_condition - Check condition part.
+ *
+ * @r:    Pointer to "struct cs_request_info".
+ * @cond: Pointer to "struct cs_condition". Maybe NULL.
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds cs_read_lock().
+ */
+static bool cs_condition(struct cs_request_info *r,
+			 const struct cs_condition *cond)
+{
+	const union cs_condition_element *condp;
+
+	if (!cond)
+		return true;
+	condp = (typeof(condp)) (cond + 1);
+	while ((void *) condp < (void *) ((u8 *) cond) + cond->size) {
+		struct cs_cond_arg left;
+		struct cs_cond_arg right;
+		const enum cs_conditions_index left_op = condp->left;
+		const enum cs_conditions_index right_op = condp->right;
+		const bool match = !condp->is_not;
+
+		condp++;
+		if (!cs_cond2arg(&left, left_op, &condp, r) ||
+		    !cs_cond2arg(&right, right_op, &condp, r))
+			/*
+			 * Something wrong (e.g. out of memory or invalid
+			 * argument) occurred. We can't check permission.
+			 */
+			return false;
+		if (left.type == CS_ARG_TYPE_NAME) {
+			if (right.type != CS_ARG_TYPE_NAME)
+				return false;
+			if (cs_path_matches_pattern
+			    (left.name, right.name) == match)
+				continue;
+		}
+		return false;
+	}
+	return true;
+}
+
+/**
+ * cs_byte_range - Check whether the string is a \ooo style octal value.
+ *
+ * @str: Pointer to the string.
+ *
+ * Returns true if @str is a \ooo style octal value, false otherwise.
+ */
+static bool cs_byte_range(const char *str)
+{
+	return *str >= '0' && *str++ <= '3' &&
+		*str >= '0' && *str++ <= '7' &&
+		*str >= '0' && *str <= '7';
+}
+
+/**
+ * cs_alphabet_char - Check whether the character is an alphabet.
+ *
+ * @c: The character to check.
+ *
+ * Returns true if @c is an alphabet character, false otherwise.
+ */
+static bool cs_alphabet_char(const char c)
+{
+	return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+/**
+ * cs_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern.
+ *
+ * @filename:     The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern:      The start of pattern to compare.
+ * @pattern_end:  The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool cs_file_matches_pattern2(const char *filename,
+				     const char *filename_end,
+				     const char *pattern,
+				     const char *pattern_end)
+{
+	while (filename < filename_end && pattern < pattern_end) {
+		char c;
+
+		if (*pattern != '\\') {
+			if (*filename++ != *pattern++)
+				return false;
+			continue;
+		}
+		c = *filename;
+		pattern++;
+		switch (*pattern) {
+			int i;
+			int j;
+		case '?':
+			if (c == '/') {
+				return false;
+			} else if (c == '\\') {
+				if (cs_byte_range(filename + 1))
+					filename += 3;
+				else
+					return false;
+			}
+			break;
+		case '+':
+			if (!isdigit(c))
+				return false;
+			break;
+		case 'x':
+			if (!isxdigit(c))
+				return false;
+			break;
+		case 'a':
+			if (!cs_alphabet_char(c))
+				return false;
+			break;
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+			if (c == '\\' && cs_byte_range(filename + 1)
+			    && !strncmp(filename + 1, pattern, 3)) {
+				filename += 3;
+				pattern += 2;
+				break;
+			}
+			return false; /* Not matched. */
+		case '*':
+		case '@':
+			for (i = 0; i <= filename_end - filename; i++) {
+				if (cs_file_matches_pattern2(filename + i,
+							     filename_end,
+							     pattern + 1,
+							     pattern_end))
+					return true;
+				c = filename[i];
+				if (c == '.' && *pattern == '@')
+					break;
+				if (c != '\\')
+					continue;
+				if (cs_byte_range(filename + i + 1))
+					i += 3;
+				else
+					break; /* Bad pattern. */
+			}
+			return false; /* Not matched. */
+		default:
+			j = 0;
+			c = *pattern;
+			if (c == '$') {
+				while (isdigit(filename[j]))
+					j++;
+			} else if (c == 'X') {
+				while (isxdigit(filename[j]))
+					j++;
+			} else if (c == 'A') {
+				while (cs_alphabet_char(filename[j]))
+					j++;
+			}
+			for (i = 1; i <= j; i++) {
+				if (cs_file_matches_pattern2(filename + i,
+							     filename_end,
+							     pattern + 1,
+							     pattern_end))
+					return true;
+			}
+			return false; /* Not matched or bad pattern. */
+		}
+		filename++;
+		pattern++;
+	}
+	/* Ignore trailing "\*" and "\@" in @pattern. */
+	while (*pattern == '\\' &&
+	       (*(pattern + 1) == '*' || *(pattern + 1) == '@'))
+		pattern += 2;
+	return filename == filename_end && pattern == pattern_end;
+}
+
+/**
+ * cs_file_matches_pattern - Pattern matching without '/' character.
+ *
+ * @filename:     The start of string to check.
+ * @filename_end: The end of string to check.
+ * @pattern:      The start of pattern to compare.
+ * @pattern_end:  The end of pattern to compare.
+ *
+ * Returns true if @filename matches @pattern, false otherwise.
+ */
+static bool cs_file_matches_pattern(const char *filename,
+				    const char *filename_end,
+				    const char *pattern,
+				    const char *pattern_end)
+{
+	const char *pattern_start = pattern;
+	bool first = true;
+	bool result;
+
+	while (pattern < pattern_end - 1) {
+		/* Split at "\-" pattern. */
+		if (*pattern++ != '\\' || *pattern++ != '-')
+			continue;
+		result = cs_file_matches_pattern2(filename, filename_end,
+						  pattern_start, pattern - 2);
+		if (first)
+			result = !result;
+		if (result)
+			return false;
+		first = false;
+		pattern_start = pattern;
+	}
+	result = cs_file_matches_pattern2(filename, filename_end,
+					  pattern_start, pattern_end);
+	return first ? result : !result;
+}
+
+/**
+ * cs_path_matches_pattern2 - Do pathname pattern matching.
+ *
+ * @f: The start of string to check.
+ * @p: The start of pattern to compare.
+ *
+ * Returns true if @f matches @p, false otherwise.
+ */
+static bool cs_path_matches_pattern2(const char *f, const char *p)
+{
+	const char *f_delimiter;
+	const char *p_delimiter;
+
+	while (*f && *p) {
+		f_delimiter = strchr(f + 1, '/');
+		if (!f_delimiter)
+			f_delimiter = f + strlen(f);
+		p_delimiter = strchr(p + 1, '/');
+		if (!p_delimiter)
+			p_delimiter = p + strlen(p);
+		if (*p == '/' && *(p + 1) == '\\') {
+			if (*(p + 2) == '(') {
+				/* Check zero repetition. */
+				if (cs_path_matches_pattern2(f, p_delimiter))
+					return true;
+				/* Check one or more repetition. */
+				goto repetition;
+			}
+			if (*(p + 2) == '{')
+				goto repetition;
+		}
+		if ((*f == '/' || *p == '/') && *f++ != *p++)
+			return false;
+		if (!cs_file_matches_pattern(f, f_delimiter, p, p_delimiter))
+			return false;
+		f = f_delimiter;
+		p = p_delimiter;
+	}
+	/* Ignore trailing "\*" and "\@" in @pattern. */
+	while (*p == '\\' && (*(p + 1) == '*' || *(p + 1) == '@'))
+		p += 2;
+	return !*f && !*p;
+repetition:
+	do {
+		/* Compare current component with pattern. */
+		if (!cs_file_matches_pattern(f + 1, f_delimiter, p + 3,
+					     p_delimiter - 2))
+			break;
+		/* Proceed to next component. */
+		f = f_delimiter;
+		if (!*f)
+			break;
+		/* Continue comparison. */
+		if (cs_path_matches_pattern2(f, p_delimiter))
+			return true;
+		f_delimiter = strchr(f + 1, '/');
+	} while (f_delimiter);
+	return false; /* Not matched. */
+}
+
+/**
+ * cs_path_matches_pattern - Check whether the given filename matches the given pattern.
+ *
+ * @filename: The filename to check.
+ * @pattern:  The pattern to compare.
+ *
+ * Returns true if matches, false otherwise.
+ *
+ * The following patterns are available.
+ *   \ooo   Octal representation of a byte.
+ *   \*     Zero or more repetitions of characters other than '/'.
+ *   \@     Zero or more repetitions of characters other than '/' or '.'.
+ *   \?     1 byte character other than '/'.
+ *   \$     One or more repetitions of decimal digits.
+ *   \+     1 decimal digit.
+ *   \X     One or more repetitions of hexadecimal digits.
+ *   \x     1 hexadecimal digit.
+ *   \A     One or more repetitions of alphabet characters.
+ *   \a     1 alphabet character.
+ *
+ *   \-     Subtraction operator.
+ *
+ *   /\{dir\}/   '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/
+ *               /dir/dir/dir/ ).
+ *
+ *   /\(dir\)/   '/' + 'Zero or more repetitions of dir/' (e.g. / /dir/
+ *               /dir/dir/ ).
+ */
+static bool cs_path_matches_pattern(const struct cs_path_info *filename,
+				    const struct cs_path_info *pattern)
+{
+	const char *f = filename->name;
+	const char *p = pattern->name;
+	const int len = pattern->const_len;
+
+	/* If @pattern doesn't contain pattern, I can use strcmp(). */
+	if (len == pattern->total_len)
+		return !cs_pathcmp(filename, pattern);
+	/* Compare the initial length without patterns. */
+	if (len) {
+		if (strncmp(f, p, len))
+			return false;
+		f += len - 1;
+		p += len - 1;
+	}
+	return cs_path_matches_pattern2(f, p);
+}
+
+/**
+ * cs_clear_request_info - Release memory allocated during permission check.
+ *
+ * @r: Pointer to "struct cs_request_info".
+ *
+ * Returns nothing.
+ */
+static void cs_clear_request_info(struct cs_request_info *r)
+{
+	u8 i;
+
+	/*
+	 * r->obj.pathname[0] (which is referenced by r->obj.s[0]) and
+	 * r->obj.pathname[1] (which is referenced by r->obj.s[1]) may contain
+	 * pathnames allocated using cs_populate_patharg().
+	 * Their callers do not allocate memory until pathnames becomes needed
+	 * for checking condition.
+	 */
+	for (i = 0; i < 2; i++) {
+		kfree(r->obj.pathname[i].name);
+		r->obj.pathname[i].name = NULL;
+	}
+	kfree(r->exename.name);
+	r->exename.name = NULL;
+}
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1505896 — [PATCH 8/8] CaitSith: Add Kconfig and Makefile

FromTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date2016-10-21 15:00 +0200
Subject[PATCH 8/8] CaitSith: Add Kconfig and Makefile
Message-ID<suHAw-5DG-85@gated-at.bofh.it>
In reply to#1505842
CaitSith uses LSM hooks and securityfs support. For now, CaitSith is not
using security blobs (i.e. "void *security" field in data structures)
so that you can enable CaitSith using Multiple Concurrent LSMs.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/Kconfig           |  6 ++++++
 security/Makefile          |  2 ++
 security/caitsith/Kconfig  | 48 ++++++++++++++++++++++++++++++++++++++++++++++
 security/caitsith/Makefile | 15 +++++++++++++++
 4 files changed, 71 insertions(+)
 create mode 100644 security/caitsith/Kconfig
 create mode 100644 security/caitsith/Makefile

diff --git a/security/Kconfig b/security/Kconfig
index 176758c..ab5b634 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -124,6 +124,7 @@ source security/tomoyo/Kconfig
 source security/apparmor/Kconfig
 source security/loadpin/Kconfig
 source security/yama/Kconfig
+source security/caitsith/Kconfig
 
 source security/integrity/Kconfig
 
@@ -133,6 +134,7 @@ choice
 	default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
 	default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
 	default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
+	default DEFAULT_SECURITY_CAITSITH if SECURITY_CAITSITH
 	default DEFAULT_SECURITY_DAC
 
 	help
@@ -151,6 +153,9 @@ choice
 	config DEFAULT_SECURITY_APPARMOR
 		bool "AppArmor" if SECURITY_APPARMOR=y
 
+	config DEFAULT_SECURITY_CAITSITH
+		bool "CaitSith" if SECURITY_CAITSITH=y
+
 	config DEFAULT_SECURITY_DAC
 		bool "Unix Discretionary Access Controls"
 
@@ -162,6 +167,7 @@ config DEFAULT_SECURITY
 	default "smack" if DEFAULT_SECURITY_SMACK
 	default "tomoyo" if DEFAULT_SECURITY_TOMOYO
 	default "apparmor" if DEFAULT_SECURITY_APPARMOR
+	default "caitsith" if DEFAULT_SECURITY_CAITSITH
 	default "" if DEFAULT_SECURITY_DAC
 
 endmenu
diff --git a/security/Makefile b/security/Makefile
index f2d71cd..3745af0 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_CAITSITH)	+= caitsith
 
 # 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_CAITSITH)		+= caitsith/
 
 # Object integrity file lists
 subdir-$(CONFIG_INTEGRITY)		+= integrity
diff --git a/security/caitsith/Kconfig b/security/caitsith/Kconfig
new file mode 100644
index 0000000..005cdb1
--- /dev/null
+++ b/security/caitsith/Kconfig
@@ -0,0 +1,48 @@
+config SECURITY_CAITSITH
+	bool "CaitSith Support"
+	depends on SECURITY
+	select SECURITYFS
+	select SRCU
+	default n
+	help
+	  This selects CaitSith, check list based access control.
+	  Required userspace tools and further information may be
+	  found at <https://caitsith.osdn.jp/>.
+	  If you are unsure how to answer this question, answer N.
+
+config SECURITY_CAITSITH_OMIT_USERSPACE_LOADER
+	bool "Activate without calling userspace policy loader."
+	default n
+	depends on SECURITY_CAITSITH
+	---help---
+	  Say Y here if you want to activate access control as soon as built-in
+	  policy was loaded. This option will be useful for systems where
+	  operations which can lead to the hijacking of the boot sequence are
+	  needed before loading the policy. For example, you can activate
+	  immediately after loading the fixed part of policy which will allow
+	  only operations needed for mounting a partition which contains the
+	  variant part of policy and verifying (e.g. running GPG check) and
+	  loading the variant part of policy. Since you can start using
+	  enforcing mode from the beginning, you can reduce the possibility of
+	  hijacking the boot sequence.
+
+config SECURITY_CAITSITH_POLICY_LOADER
+	string "Location of userspace policy loader"
+	default "/sbin/caitsith-init"
+	depends on SECURITY_CAITSITH
+	depends on !SECURITY_CAITSITH_OMIT_USERSPACE_LOADER
+	---help---
+	  This is the default pathname of policy loader which is called before
+	  activation. You can override this setting via CS_loader= kernel
+	  command line option.
+
+config SECURITY_CAITSITH_ACTIVATION_TRIGGER
+	string "Trigger for calling userspace policy loader"
+	default "/sbin/init"
+	depends on SECURITY_CAITSITH
+	depends on !SECURITY_CAITSITH_OMIT_USERSPACE_LOADER
+	---help---
+	  This is the default pathname of activation trigger. You can override
+	  this setting via CS_trigger= kernel command line option.
+	  For example, if you pass init=/usr/lib/systemd/systemd option, you
+	  may want to also pass CS_trigger=/usr/lib/systemd/systemd option.
diff --git a/security/caitsith/Makefile b/security/caitsith/Makefile
new file mode 100644
index 0000000..1f0b83f
--- /dev/null
+++ b/security/caitsith/Makefile
@@ -0,0 +1,15 @@
+obj-y += permission.o gc.o policy_io.o realpath.o lsm.o load_policy.o
+
+$(obj)/policy/policy.conf:
+	@mkdir -p $(obj)/policy/
+	@echo Creating an empty policy/policy.conf
+	@touch $@
+
+$(obj)/builtin-policy.h: $(obj)/policy/policy.conf
+	@echo Generating built-in policy for CaitSith.
+	@echo "static char cs_builtin_policy[] __initdata =" > $@.tmp
+	@sed -e 's/\\/\\134/g' -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/policy.conf >> $@.tmp
+	@echo "\"\";" >> $@.tmp
+	@mv $@.tmp $@
+
+$(obj)/policy_io.o: $(obj)/builtin-policy.h
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1505898 — [PATCH 1/8] CaitSith: Add header file.

FromTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date2016-10-21 15:00 +0200
Subject[PATCH 1/8] CaitSith: Add header file.
Message-ID<suHAw-5DG-77@gated-at.bofh.it>
In reply to#1505842
This file defines data structures, prototypes and inlined functions
used by CaitSith.

In TOMOYO, the policy syntax requires positional mandatory parameters
(e.g. pathname when opening a pathname, pathname and DAC permission mode
when creating a pathname), and is using a dedicated data structure for
type and number of arguments (e.g. "struct tomoyo_path_acl" for operations
which pass one pathname, "struct tomoyo_path_number_acl" for operations
which pass one pathname and one numeric argument). But it turned out that
using a dedicated structure undesirably limits ability to specify
complicated conditions. For example, when specific pattern should be
excluded from some pattern (e.g. "*" but "*.tmp"), administrator has to
use \- operator (e.g. "\*\-\*.tmp") for that purpose. It makes conditions
difficult to understand. Also, since TOMOYO uses only whitelisting
approach, \- operator needs to be used with cautions that unwanted
patterns are not included by error. It made complicated conditions very
hard to understand. Such problems can be avoided if policy syntax (and
underlying data structures) supports referencing same argument for
multiple times using variable=value or variable!=value expression.
Therefore, there is no positional mandatory parameter in CaitSith, and
CaitSith is using a common variable length data structure
("struct cs_acl_info" has a reference to "struct cs_condition" if
there are parameters, and array of "union cs_condition_element" follows
"struct cs_condition" for holding all parameters).

In TOMOYO, a domain ("struct tomoyo_domain_info") is used as a key for
identifying state of "struct task_struct" (via "struct cred"). As a
result, management of the domains is mandatory when using TOMOYO. But it
turned out that management of the domains became a huge burden for users
who is trying to use TOMOYO for usages which TOMOYO did not expect.
Therefore, management of the domains became optional in CaitSith (though
this version does not implement domains). In CaitSith, policy is a
collection of action check lists (list of "struct cs_acl_info" with
optional "struct cs_condition") which uses type of action as a key.

By combination with two changes explained above, the policy syntax in
CaitSith allows both blacklisting approach and whitelisting approach,
as explained below. Since this version implements minimal functionality,
only few conditions are supported by acl (action check list).

  acl $action $conditions_to_check_this_acl_block
     $decision $conditions_to_use_this_decision

$action is either "execute" (for checking execve() system call) or
"modify_policy" (for updating CaitSith's on-memory policy configuration).

$decision is either "allow" (for granting this acl block) or "deny"
(for rejecting this access request).

$conditions_to_check_this_acl_block and $conditions_to_use_this_decision
are optional which contain conditions to apply current acl block or
decision. Available variables are

  task.exe ... The pathname of current thread (i.e. /proc/self/exe in
               CaitSith's pathname representation rule).

  exec ... Requested program's pathname in CaitSith's pathname
           representation rule, but maybe a symbolic link.
           Applicable to only $action == "execve" case.

  path ... Requested program's pathname in CaitSith's pathname
           representation rule.
           Applicable to only $action == "execve" case.

Some examples are shown below. An unsigned integer value is prefixed to
each line of rules for controlling priority of evaluation. A line with
smaller priority value is evaluated before a line with larger priority
value.

  /usr/sbin/httpd is allowed to execute only /var/www/cgi-bin/counter.cgi
  and /usr/bin/suexec using whitelist.

    1000 acl execute task.exe="/usr/sbin/httpd"
      100 allow path="/var/www/cgi-bin/counter.cgi"
      150 allow path="/usr/sbin/suexec"
      200 deny

  /usr/sbin/httpd is not allowed to execute /bin/sh and /bin/bash using
  blacklist.

    2000 acl execute task.exe="/usr/sbin/httpd"
      100 deny path="/bin/sh"
      150 deny path="/bin/bash"
      200 allow

  /usr/sbin/suexec is allowed to be executed by only /usr/sbin/httpd
  using inversed point of view. (This is not possible with TOMOYO.)

    2000 acl execute path="/usr/sbin/suexec"
      100 allow task.exe="/usr/sbin/httpd"
      200 deny

For each $action, all acl blocks with that $action will be checked.
Access request will be denied as soon as one of
$conditions_to_use_this_decision in any "deny" lines in all acl blocks
with that $action and $conditions_to_check_this_acl_block was evaluated
as "true" turned out to be "true" (or out of memory problem occurred
while calculating the value of pathname represented by variables).
Otherwise, access request will be granted.

Note that in order to minimize the burden of reviewing, this version
implements only functionality of checking program execution requests
using pathnames. More $actions and variables will be added in future
version. There is no limitation that CaitSith cannot use security
labels like SELinux or Smack does.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/caitsith/caitsith.h | 347 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 347 insertions(+)
 create mode 100644 security/caitsith/caitsith.h

diff --git a/security/caitsith/caitsith.h b/security/caitsith/caitsith.h
new file mode 100644
index 0000000..4292052
--- /dev/null
+++ b/security/caitsith/caitsith.h
@@ -0,0 +1,347 @@
+/*
+ * security/caitsith/caitsith.h
+ *
+ * Copyright (C) 2005-2012  NTT DATA CORPORATION
+ */
+
+#ifndef _SECURITY_CAITSITH_INTERNAL_H
+#define _SECURITY_CAITSITH_INTERNAL_H
+
+#include <linux/security.h>
+#include <linux/binfmts.h>
+#include <linux/namei.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/file.h>
+#include <linux/ctype.h> /* isdigit()/isxdigit() */
+#include <linux/kmod.h>
+
+/* Enumeration definition for internal use. */
+
+/* Index numbers for "struct cs_condition". */
+enum cs_conditions_index {
+	CS_INVALID_CONDITION,
+	CS_SELF_EXE,
+	CS_COND_SARG0,
+	CS_COND_SARG1,
+	CS_IMM_NAME_ENTRY,
+} __packed;
+
+/* Index numbers for functionality. */
+enum cs_mac_index {
+	CS_MAC_EXECUTE,
+	CS_MAC_MODIFY_POLICY,
+	CS_MAX_MAC_INDEX,
+} __packed;
+
+/* Index numbers for statistic information. */
+enum cs_memory_stat_type {
+	CS_MEMORY_POLICY,
+	CS_MAX_MEMORY_STAT
+} __packed;
+
+enum cs_matching_result {
+	CS_MATCHING_UNMATCHED,
+	CS_MATCHING_ALLOWED,
+	CS_MATCHING_DENIED,
+	CS_MAX_MATCHING
+} __packed;
+
+/* Index numbers for entry type. */
+enum cs_policy_id {
+	CS_ID_CONDITION,
+	CS_ID_NAME,
+	CS_ID_ACL,
+	CS_MAX_POLICY
+} __packed;
+
+/* Index numbers for statistic information. */
+enum cs_policy_stat_type {
+	CS_STAT_POLICY_UPDATES,
+	CS_STAT_REQUEST_DENIED,
+	CS_MAX_POLICY_STAT
+} __packed;
+
+/* Index numbers for /sys/kernel/security/caitsith/ interfaces. */
+enum cs_securityfs_interface_index {
+	CS_POLICY,
+	CS_VERSION,
+} __packed;
+
+/* Constants definition for internal use. */
+
+/*
+ * CaitSith uses this hash only when appending a string into the string table.
+ * Frequency of appending strings is very low. So we don't need large (e.g.
+ * 64k) hash size. 256 will be sufficient.
+ */
+#define CS_HASH_BITS 8
+#define CS_MAX_HASH (1u << CS_HASH_BITS)
+
+/* Size of temporary buffer for execve() operation. */
+#define CS_EXEC_TMPSIZE     4096
+
+/* Garbage collector is trying to kfree() this element. */
+#define CS_GC_IN_PROGRESS -1
+
+/* Size of read buffer for /sys/kernel/security/caitsith/ interface. */
+#define CS_MAX_IO_READ_QUEUE 64
+
+/* Structure definition for internal use. */
+
+/* Common header for holding ACL entries. */
+struct cs_acl_head {
+	struct list_head list;
+	s8 is_deleted; /* true or false or CS_GC_IN_PROGRESS */
+} __packed;
+
+/* Common header for shared entries. */
+struct cs_shared_acl_head {
+	struct list_head list;
+	atomic_t users;
+} __packed;
+
+/* Common header for individual entries. */
+struct cs_acl_info {
+	struct list_head list;
+	struct list_head acl_info_list;
+	struct cs_condition *cond; /* Maybe NULL. */
+	bool is_deleted;
+	bool is_deny;
+	u16 priority;
+};
+
+/* Structure for entries which follows "struct cs_condition". */
+union cs_condition_element {
+	struct {
+		enum cs_conditions_index left;
+		enum cs_conditions_index right;
+		bool is_not;
+	};
+	const struct cs_path_info *path;
+};
+
+/* Structure for optional arguments. */
+struct cs_condition {
+	struct cs_shared_acl_head head;
+	u32 size; /* Memory size allocated for this entry. */
+	/* union cs_condition_element condition[]; */
+};
+
+/* Structure for holding a token. */
+struct cs_path_info {
+	const char *name;
+	u32 hash;          /* = full_name_hash(name, strlen(name)) */
+	u32 total_len;     /* = strlen(name)                       */
+	u32 const_len;     /* = cs_const_part_length(name)        */
+};
+
+/* Structure for request info. */
+struct cs_request_info {
+	/* For holding parameters. */
+	struct cs_request_param {
+		const struct cs_path_info *s[2];
+	} param;
+	/* For holding pathnames and attributes. */
+	struct {
+		/* Pointer to file objects. */
+		struct path path[2];
+		/*
+		 * Name of @path[0] and @path[1].
+		 * Cleared by cs_clear_request_info().
+		 */
+		struct cs_path_info pathname[2];
+	} obj;
+	struct {
+		struct linux_binprm *bprm;
+		/* For temporary use. Size is CS_EXEC_TMPSIZE bytes. */
+		char *tmp;
+	};
+	/*
+	 * Name of current thread's executable.
+	 * Cleared by cs_clear_request_info().
+	 */
+	struct cs_path_info exename;
+	/*
+	 * Matching "struct cs_acl_info" is copied. Used for caitsith-queryd.
+	 * Valid until cs_read_unlock().
+	 */
+	struct cs_acl_info *matched_acl;
+	/*
+	 * For holding operation index used for this request.
+	 * One of values in "enum cs_mac_index".
+	 */
+	enum cs_mac_index type;
+	/* For holding matching result. */
+	enum cs_matching_result result;
+	/*
+	 * Set to true if condition could not be checked due to out of memory.
+	 * This flag is used for returning out of memory flag back to
+	 * cs_check_acl_list(). Thus, this flag will not be set if out of
+	 * memory occurred before cs_check_acl_list() is called.
+	 */
+	bool failed_by_oom;
+};
+
+/* Structure for holding string data. */
+struct cs_name {
+	struct cs_shared_acl_head head;
+	int size; /* Memory size allocated for this entry. */
+	struct cs_path_info entry;
+};
+
+/*
+ * Structure for reading/writing policy via /sys/kernel/security/caitsith/
+ * interfaces.
+ */
+struct cs_io_buffer {
+	/* Exclusive lock for this structure.   */
+	struct mutex io_sem;
+	char __user *read_user_buf;
+	size_t read_user_buf_avail;
+	struct {
+		struct list_head *acl;
+		struct list_head *subacl;
+		const union cs_condition_element *cond;
+		size_t avail;
+		unsigned int step;
+		u16 index;
+		u8 cond_step;
+		u8 w_pos;
+		enum cs_mac_index acl_index;
+		bool eof;
+		bool version_done;
+		bool stat_done;
+		const char *w[CS_MAX_IO_READ_QUEUE];
+	} r;
+	struct {
+		char *data;
+		struct cs_acl_info *acl;
+		size_t avail;
+		enum cs_mac_index acl_index;
+		bool is_delete;
+		bool is_deny;
+		u16 priority;
+	} w;
+	/* Buffer for reading.                  */
+	char *read_buf;
+	/* Size of read buffer.                 */
+	size_t readbuf_size;
+	/* Buffer for writing.                  */
+	char *write_buf;
+	/* Size of write buffer.                */
+	size_t writebuf_size;
+	/* Type of interface. */
+	enum cs_securityfs_interface_index type;
+	/* Users counter protected by cs_io_buffer_list_lock. */
+	u8 users;
+	/* List for telling GC not to kfree() elements. */
+	struct list_head list;
+};
+
+/* Structure for representing YYYY/MM/DD hh/mm/ss. */
+struct cs_time {
+	u16 year;
+	u8 month;
+	u8 day;
+	u8 hour;
+	u8 min;
+	u8 sec;
+};
+
+/* Prototype definition for internal use. */
+
+void __init cs_init_module(void);
+void cs_load_policy(const char *filename);
+void cs_check_profile(void);
+bool cs_get_exename(struct cs_path_info *buf);
+bool cs_manager(void);
+char *cs_encode(const char *str);
+char *cs_realpath(const struct path *path);
+char *cs_get_exe(void);
+int cs_audit_log(struct cs_request_info *r);
+int cs_check_acl(struct cs_request_info *r, const bool clear);
+void cs_del_condition(struct list_head *element);
+void cs_fill_path_info(struct cs_path_info *ptr);
+void cs_notify_gc(struct cs_io_buffer *head, const bool is_register);
+void cs_populate_patharg(struct cs_request_info *r, const bool first);
+void cs_warn_oom(const char *function);
+int cs_start_execve(struct linux_binprm *bprm);
+
+/* Variable definition for internal use. */
+
+extern bool cs_policy_loaded;
+extern struct cs_path_info cs_null_name;
+extern struct list_head cs_acl_list[CS_MAX_MAC_INDEX];
+extern struct list_head cs_condition_list;
+extern struct list_head cs_name_list[CS_MAX_HASH];
+extern struct mutex cs_policy_lock;
+extern struct srcu_struct cs_ss;
+extern unsigned int cs_memory_used[CS_MAX_MEMORY_STAT];
+
+/* Inlined functions for internal use. */
+
+/**
+ * cs_pathcmp - strcmp() for "struct cs_path_info" structure.
+ *
+ * @a: Pointer to "struct cs_path_info".
+ * @b: Pointer to "struct cs_path_info".
+ *
+ * Returns true if @a != @b, false otherwise.
+ */
+static inline bool cs_pathcmp(const struct cs_path_info *a,
+			      const struct cs_path_info *b)
+{
+	return a->hash != b->hash || strcmp(a->name, b->name);
+}
+
+/**
+ * cs_read_lock - Take lock for protecting policy.
+ *
+ * Returns index number for cs_read_unlock().
+ */
+static inline int cs_read_lock(void)
+{
+	return srcu_read_lock(&cs_ss);
+}
+
+/**
+ * cs_read_unlock - Release lock for protecting policy.
+ *
+ * @idx: Index number returned by cs_read_lock().
+ *
+ * Returns nothing.
+ */
+static inline void cs_read_unlock(const int idx)
+{
+	srcu_read_unlock(&cs_ss, idx);
+}
+
+/**
+ * cs_put_condition - Drop reference on "struct cs_condition".
+ *
+ * @cond: Pointer to "struct cs_condition". Maybe NULL.
+ *
+ * Returns nothing.
+ */
+static inline void cs_put_condition(struct cs_condition *cond)
+{
+	if (cond)
+		atomic_dec(&cond->head.users);
+}
+
+/**
+ * cs_put_name - Drop reference on "struct cs_name".
+ *
+ * @name: Pointer to "struct cs_path_info". Maybe NULL.
+ *
+ * Returns nothing.
+ */
+static inline void cs_put_name(const struct cs_path_info *name)
+{
+	if (name)
+		atomic_dec(&container_of(name, struct cs_name, entry)->
+			   head.users);
+}
+
+#endif
-- 
1.8.3.1

[toc] | [prev] | [next] | [standalone]


#1506893

FromJames Morris <jmorris@namei.org>
Date2016-10-24 06:50 +0200
Message-ID<svFmW-2IU-35@gated-at.bofh.it>
In reply to#1505842
On Fri, 21 Oct 2016, Tetsuo Handa wrote:

>   (1) CaitSith can use both string / numeric arguments (like TOMOYO and
>       AppArmor) and security labels (like SELinux and Smack). There is no
>       reason that access control implementation must not use both.
> 

I believe that AppArmor will be gaining more support for security labels.

JJ: is that correct?


-- 
James Morris
<jmorris@namei.org>

[toc] | [prev] | [next] | [standalone]


#1507249

FromJohn Johansen <john.johansen@canonical.com>
Date2016-10-24 16:40 +0200
Message-ID<svOzT-lR-1@gated-at.bofh.it>
In reply to#1506893
On 10/23/2016 09:44 PM, James Morris wrote:
> On Fri, 21 Oct 2016, Tetsuo Handa wrote:
> 
>>   (1) CaitSith can use both string / numeric arguments (like TOMOYO and
>>       AppArmor) and security labels (like SELinux and Smack). There is no
>>       reason that access control implementation must not use both.
>>
> 
> I believe that AppArmor will be gaining more support for security labels.
> 
> JJ: is that correct?
> 
yes, it will

[toc] | [prev] | [next] | [standalone]


#1507550

FromJohn Johansen <john.johansen@canonical.com>
Date2016-10-24 20:20 +0200
Message-ID<svS0N-2QU-19@gated-at.bofh.it>
In reply to#1505842
On 10/21/2016 05:49 AM, Tetsuo Handa wrote:
> CaitSith (acronym for "Characteristic action inspection tool. See if
> this helps.") is an LSM based access control implementation which uses
> action check list (acl) as policy syntax.
> 

<< snip >>

> CaitSith tries to remove many limitations which existing security
> enhanced Linux (I mean any LSM based access control implementation) has.
> 
>   (1) CaitSith can use both string / numeric arguments (like TOMOYO and
>       AppArmor) and security labels (like SELinux and Smack). There is no
>       reason that access control implementation must not use both.
> 
In fact a full DTE implementation requires using both.

>   (2) CaitSith can specify rules from the point of view of both subjects
>       (a.k.a. capability list) and objects (a.k.a. access control list).
>       There is no reason that access control implementation must use
>       capability list which entails identification of all subjects.
> 
>       While security people are tempted to protect as much as possible,
>       users generally have insufficient resource for protecting all. If
>       users have sufficient resource, they will be already using existing
>       implementations.
> 
>       I found users who want to restrict only a few objects without
>       being bothered by managing all subjects in their systems. This is
>       impossible for TOMOYO because TOMOYO entails managing all subjects
>       in their systems. In CaitSith, this limitation is solved by writing
>       rules using action as a key.
> 
>   (3) CaitSith can represent complicated string / numeric arguments
>       compared to TOMOYO, for any condition is represented as zero or
>       more repetition of variable=value or variable!=value expression.
> 
>       In TOMOYO, the policy syntax requires positional mandatory parameters
>       (e.g. pathname when opening a pathname, pathname and DAC permission
>       mode when creating a pathname) based on type and number of arguments
>       for that action. But it turned out that using positional parameters
>       undesirably limits ability to specify complicated conditions. For
>       example, when specific pattern should be excluded from some pattern
>       (e.g. "*" but "*.tmp"), administrator has to use \- operator (e.g.
>       "\*\-\*.tmp") for that purpose. It makes conditions difficult to
>       understand. \- operator needs to be used with cautions that unwanted
>       patterns are not included by error. It made complicated conditions
>       very hard to understand.
>       In CaitSith, this limitation is solved by writing rules like
>       path="\*" path!="\*.tmp" instead of path="\*\-\*.tmp" .
> 
>   (4) CaitSith can specify rules using both whitelisting and blacklisting.
> 
>       As far as I know, whoever involved in security enhanced Linux in
>       Japan lost their jobs. In other words, security enhanced Linux made
>       no business sense in Japan. I think that existing implementations
>       are asking for too much skill/knowledge compared to users can afford.
> 
>       CaitSith's syntax acts as whitelisting if an unconditional deny line
>       comes before an unconditional allow line comes, acts as blacklisting
>       if an unconditional allow line comes before an unconditional deny
>       line comes.
> 
> CaitSith stayed four years and a half listening for whether it suits
> user's needs. In the last year or so, questions regarding how to use
> TOMOYO are getting to come. However, it turned out that CaitSith seems
> to fit better than TOMOYO for what many of the questioners want to
> achieve. You can see slides shown below for full explanation of
> the how and why.
> 
>   http://I-love.SAKURA.ne.jp/tomoyo/CaitSith-en.pdf (English)
>   http://I-love.SAKURA.ne.jp/tomoyo/CaitSith-ja.pdf (Japanese)
> 

The majority of your points for CaitSith are comparisons to TOMOYO your other
LSM. In the long run do you see CaitSith replacing Tomoyo, as it seems to be
the LSM you are now focused on?

> In order to minimize the burden of reviewing, this patchset implements
> only functionality of checking program execution requests (i.e. execve()
> system call) using pathnames. I'm planning to add other functionalities
> after this version got included into mainline. You can find how future
> versions of CaitSith will look like at http://caitsith.osdn.jp/ .
> 
Thanks I've started working my way through this, but it is going to take
me a while.

> Tetsuo Handa (8):
>   CaitSith: Add header file.
>   CaitSith: Add pathname calculation functions.
>   CaitSith: Add policy I/O functions.
>   CaitSith: Add permission check functions.
>   CaitSith: Add LSM adapter functions.
>   CaitSith: Add policy loader functions.
>   CaitSith: Add garbage collector functions.
>   CaitSith: Add Kconfig and Makefile
> 
>  security/Kconfig                |    6 +
>  security/Makefile               |    2 +
>  security/caitsith/Kconfig       |   48 ++
>  security/caitsith/Makefile      |   15 +
>  security/caitsith/caitsith.h    |  347 +++++++++
>  security/caitsith/gc.c          |  373 ++++++++++
>  security/caitsith/load_policy.c |  106 +++
>  security/caitsith/lsm.c         |   60 ++
>  security/caitsith/permission.c  |  691 +++++++++++++++++
>  security/caitsith/policy_io.c   | 1553 +++++++++++++++++++++++++++++++++++++++
>  security/caitsith/realpath.c    |  227 ++++++
>  11 files changed, 3428 insertions(+)
>  create mode 100644 security/caitsith/Kconfig
>  create mode 100644 security/caitsith/Makefile
>  create mode 100644 security/caitsith/caitsith.h
>  create mode 100644 security/caitsith/gc.c
>  create mode 100644 security/caitsith/load_policy.c
>  create mode 100644 security/caitsith/lsm.c
>  create mode 100644 security/caitsith/permission.c
>  create mode 100644 security/caitsith/policy_io.c
>  create mode 100644 security/caitsith/realpath.c
> 

[toc] | [prev] | [next] | [standalone]


#1508218

FromTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date2016-10-25 13:30 +0200
Message-ID<sw85z-4Vb-11@gated-at.bofh.it>
In reply to#1507550
John Johansen wrote:
> On 10/21/2016 05:49 AM, Tetsuo Handa wrote:
> > CaitSith (acronym for "Characteristic action inspection tool. See if
> > this helps.") is an LSM based access control implementation which uses
> > action check list (acl) as policy syntax.
> > 
> 
> << snip >>
> 
> > CaitSith tries to remove many limitations which existing security
> > enhanced Linux (I mean any LSM based access control implementation) has.
> > 
> >   (1) CaitSith can use both string / numeric arguments (like TOMOYO and
> >       AppArmor) and security labels (like SELinux and Smack). There is no
> >       reason that access control implementation must not use both.
> > 
> In fact a full DTE implementation requires using both.

I see.

> 
> >   (2) CaitSith can specify rules from the point of view of both subjects
> >       (a.k.a. capability list) and objects (a.k.a. access control list).
> >       There is no reason that access control implementation must use
> >       capability list which entails identification of all subjects.
> > 
> >       While security people are tempted to protect as much as possible,
> >       users generally have insufficient resource for protecting all. If
> >       users have sufficient resource, they will be already using existing
> >       implementations.
> > 
> >       I found users who want to restrict only a few objects without
> >       being bothered by managing all subjects in their systems. This is
> >       impossible for TOMOYO because TOMOYO entails managing all subjects
> >       in their systems. In CaitSith, this limitation is solved by writing
> >       rules using action as a key.
> > 
> >   (3) CaitSith can represent complicated string / numeric arguments
> >       compared to TOMOYO, for any condition is represented as zero or
> >       more repetition of variable=value or variable!=value expression.
> > 
> >       In TOMOYO, the policy syntax requires positional mandatory parameters
> >       (e.g. pathname when opening a pathname, pathname and DAC permission
> >       mode when creating a pathname) based on type and number of arguments
> >       for that action. But it turned out that using positional parameters
> >       undesirably limits ability to specify complicated conditions. For
> >       example, when specific pattern should be excluded from some pattern
> >       (e.g. "*" but "*.tmp"), administrator has to use \- operator (e.g.
> >       "\*\-\*.tmp") for that purpose. It makes conditions difficult to
> >       understand. \- operator needs to be used with cautions that unwanted
> >       patterns are not included by error. It made complicated conditions
> >       very hard to understand.
> >       In CaitSith, this limitation is solved by writing rules like
> >       path="\*" path!="\*.tmp" instead of path="\*\-\*.tmp" .
> > 
> >   (4) CaitSith can specify rules using both whitelisting and blacklisting.
> > 
> >       As far as I know, whoever involved in security enhanced Linux in
> >       Japan lost their jobs. In other words, security enhanced Linux made
> >       no business sense in Japan. I think that existing implementations
> >       are asking for too much skill/knowledge compared to users can afford.
> > 
> >       CaitSith's syntax acts as whitelisting if an unconditional deny line
> >       comes before an unconditional allow line comes, acts as blacklisting
> >       if an unconditional allow line comes before an unconditional deny
> >       line comes.
> > 
> > CaitSith stayed four years and a half listening for whether it suits
> > user's needs. In the last year or so, questions regarding how to use
> > TOMOYO are getting to come. However, it turned out that CaitSith seems
> > to fit better than TOMOYO for what many of the questioners want to
> > achieve. You can see slides shown below for full explanation of
> > the how and why.
> > 
> >   http://I-love.SAKURA.ne.jp/tomoyo/CaitSith-en.pdf (English)
> >   http://I-love.SAKURA.ne.jp/tomoyo/CaitSith-ja.pdf (Japanese)
> > 
> 
> The majority of your points for CaitSith are comparisons to TOMOYO your other
> LSM. In the long run do you see CaitSith replacing Tomoyo, as it seems to be
> the LSM you are now focused on?

I can't predict that. There are users who migrated to CaitSith, but there are
also users who continue using TOMOYO. Users will choose from any implementation
based on what is most suitable for their goals. I'm trying to rescue users
who are trying to use TOMOYO for usages which TOMOYO did not expect.

> 
> > In order to minimize the burden of reviewing, this patchset implements
> > only functionality of checking program execution requests (i.e. execve()
> > system call) using pathnames. I'm planning to add other functionalities
> > after this version got included into mainline. You can find how future
> > versions of CaitSith will look like at http://caitsith.osdn.jp/ .
> > 
> Thanks I've started working my way through this, but it is going to take
> me a while.
> 

Thank you for your time.

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web