From patchwork Fri Oct 21 12:49:03 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tetsuo Handa X-Patchwork-Id: 9388919 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1C144607F0 for ; Fri, 21 Oct 2016 12:53:08 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0BDA329195 for ; Fri, 21 Oct 2016 12:53:08 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 007D52A177; Fri, 21 Oct 2016 12:53:07 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 18C042A155 for ; Fri, 21 Oct 2016 12:53:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932937AbcJUMu4 (ORCPT ); Fri, 21 Oct 2016 08:50:56 -0400 Received: from www262.sakura.ne.jp ([202.181.97.72]:39005 "EHLO www262.sakura.ne.jp" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933531AbcJUMtg (ORCPT ); Fri, 21 Oct 2016 08:49:36 -0400 Received: from fsav301.sakura.ne.jp (fsav301.sakura.ne.jp [153.120.85.132]) by www262.sakura.ne.jp (8.14.5/8.14.5) with ESMTP id u9LCnTGv029085; Fri, 21 Oct 2016 21:49:29 +0900 (JST) (envelope-from penguin-kernel@I-love.SAKURA.ne.jp) Received: from www262.sakura.ne.jp (202.181.97.72) by fsav301.sakura.ne.jp (F-Secure/fsigk_smtp/530/fsav301.sakura.ne.jp); Fri, 21 Oct 2016 21:49:29 +0900 (JST) X-Virus-Status: clean(F-Secure/fsigk_smtp/530/fsav301.sakura.ne.jp) Received: from ccsecurity.localdomain (softbank126227147111.bbtec.net [126.227.147.111]) (authenticated bits=0) by www262.sakura.ne.jp (8.14.5/8.14.5) with ESMTP id u9LCnMFl029053 (version=TLSv1/SSLv3 cipher=DHE-RSA-CAMELLIA256-SHA bits=256 verify=NO); Fri, 21 Oct 2016 21:49:29 +0900 (JST) (envelope-from penguin-kernel@I-love.SAKURA.ne.jp) From: Tetsuo Handa To: linux-security-module@vger.kernel.org Cc: linux-kernel@vger.kernel.org, Tetsuo Handa Subject: [PATCH 1/8] CaitSith: Add header file. Date: Fri, 21 Oct 2016 21:49:03 +0900 Message-Id: <1477054150-4772-2-git-send-email-penguin-kernel@I-love.SAKURA.ne.jp> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1477054150-4772-1-git-send-email-penguin-kernel@I-love.SAKURA.ne.jp> References: <1477054150-4772-1-git-send-email-penguin-kernel@I-love.SAKURA.ne.jp> Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 +#include +#include +#include +#include +#include +#include /* isdigit()/isxdigit() */ +#include + +/* 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