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

From: Tetsuo Handa
Date: Fri Oct 21 2016 - 08:51:09 EST


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@xxxxxxxxxxxxxxxxxxx>
---
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