From: Kentaro Takeda <takedakn@nttdata.co.jp>
To: akpm@linux-foundation.org
Cc: linux-kernel@vger.kernel.org,
linux-security-module@vger.kernel.org,
Kentaro Takeda <takedakn@nttdata.co.jp>,
Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Subject: [TOMOYO #6 retry 17/21] Capability access control functions.
Date: Wed, 09 Jan 2008 09:53:37 +0900 [thread overview]
Message-ID: <20080109005426.344192310@nttdata.co.jp> (raw)
In-Reply-To: 20080109005320.323184643@nttdata.co.jp
TOMOYO Linux checks permission for non-POSIX capability
so that the number of capabilities won't be limited to 32 or 64.
TOMOYO Linux uses per-a-domain capability, an approach that associate
capabilities with each domain, and assign a domain for each process.
The advantages of this approach are that
(1) minimum capabilities required for a process is directly given
compared to setting/clearing/inheriting bit fields of a process's
capability value.
(2) it can support as many types of capabilities as you want
without consuming much memory because you don't need to allocate
memory for per-a-task basis.
Although current implementation can handle up to 65536 types of capabilities.
we don't have enough hooks for checking/distinguishing
"What operation is requested.".
Therefore, some of capabilities defined in this module doesn't work.
We want to insert a new hook like operation_capable(syscall_number)
so that we can have enough hooks for checking/distinguishing requests.
May be system call auditing mechanism helps distinguish operations that are
all-combined into capable(CAP_SYS_ADMIN) operation. But to use it,
we think we have to enable system call auditing for all processes,
which may cause performance and log flooding problem?
Each permission can be automatically accumulated into
the policy of each domain using 'learning mode'.
Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
security/tomoyo/capability.c | 320 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 320 insertions(+)
--- /dev/null
+++ linux-2.6-mm/security/tomoyo/capability.c
@@ -0,0 +1,320 @@
+/*
+ * security/tomoyo/capability.c
+ *
+ * Capability access control functions for TOMOYO Linux.
+ */
+
+#include "tomoyo.h"
+#include "realpath.h"
+
+static struct {
+ const char *keyword;
+ unsigned int current_value;
+ const char *capability_name;
+} capability_control_array[TMY_MAX_CAPABILITY_INDEX] = {
+ [TMY_INET_STREAM_SOCKET_CREATE] = /* OK */
+ { "inet_tcp_create", 0, "socket(PF_INET, SOCK_STREAM)" },
+ [TMY_INET_STREAM_SOCKET_LISTEN] = /* OK */
+ { "inet_tcp_listen", 0, "listen(PF_INET, SOCK_STREAM)" },
+ [TMY_INET_STREAM_SOCKET_CONNECT] = /* OK */
+ { "inet_tcp_connect", 0, "connect(PF_INET, SOCK_STREAM)" },
+ [TMY_USE_INET_DGRAM_SOCKET] = /* OK */
+ { "use_inet_udp", 0, "socket(PF_INET, SOCK_DGRAM)" },
+ [TMY_USE_INET_RAW_SOCKET] = /* OK */
+ { "use_inet_ip", 0, "socket(PF_INET, SOCK_RAW)" },
+ [TMY_USE_ROUTE_SOCKET] = /* OK */
+ { "use_route", 0, "socket(PF_ROUTE)" },
+ [TMY_USE_PACKET_SOCKET] = /* OK */
+ { "use_packet", 0, "socket(PF_PACKET)" },
+ [TMY_SYS_MOUNT] = /* OK */
+ { "SYS_MOUNT", 0, "sys_mount()" },
+ [TMY_SYS_UMOUNT] = /* OK */
+ { "SYS_UMOUNT", 0, "sys_umount()" },
+ [TMY_SYS_REBOOT] = /* Too many hooks. */
+ { "SYS_REBOOT", 0, "sys_reboot()" },
+ [TMY_SYS_CHROOT] = /* OK */
+ { "SYS_CHROOT", 0, "sys_chroot()" },
+ [TMY_SYS_KILL] = /* No appropriate hook. */
+ { "SYS_KILL", 0, "sys_kill()" },
+ [TMY_SYS_VHANGUP] = /* Too many hooks. */
+ { "SYS_VHANGUP", 0, "sys_vhangup()" },
+ [TMY_SYS_SETTIME] = /* Too many hooks. */
+ { "SYS_TIME", 0, "sys_settimeofday()" },
+ [TMY_SYS_NICE] = /* No appropriate hook. */
+ { "SYS_NICE", 0, "sys_nice()" },
+ [TMY_SYS_SETHOSTNAME] = /* No appropriate hook. */
+ { "SYS_SETHOSTNAME", 0, "sys_sethostname()" },
+ [TMY_USE_KERNEL_MODULE] = /* Too many hooks. */
+ { "use_kernel_module", 0, "kernel_module" },
+ [TMY_CREATE_FIFO] = /* OK */
+ { "create_fifo", 0, "mknod(FIFO)" },
+ [TMY_CREATE_BLOCK_DEV] = /* OK */
+ { "create_block_dev", 0, "mknod(BDEV)" },
+ [TMY_CREATE_CHAR_DEV] = /* OK */
+ { "create_char_dev", 0, "mknod(CDEV)" },
+ [TMY_CREATE_UNIX_SOCKET] = /* OK */
+ { "create_unix_socket", 0, "mknod(SOCKET)" },
+ [TMY_SYS_LINK] = /* OK */
+ { "SYS_LINK", 0, "sys_link()" },
+ [TMY_SYS_SYMLINK] = /* OK */
+ { "SYS_SYMLINK", 0, "sys_symlink()" },
+ [TMY_SYS_RENAME] = /* OK */
+ { "SYS_RENAME", 0, "sys_rename()" },
+ [TMY_SYS_UNLINK] = /* OK */
+ { "SYS_UNLINK", 0, "sys_unlink()" },
+ [TMY_SYS_CHMOD] = /* OK */
+ { "SYS_CHMOD", 0, "sys_chmod()" },
+ [TMY_SYS_CHOWN] = /* OK */
+ { "SYS_CHOWN", 0, "sys_chown()" },
+ [TMY_SYS_IOCTL] = /* Too many hooks. */
+ { "SYS_IOCTL", 0, "sys_ioctl()" },
+ [TMY_SYS_KEXEC_LOAD] = /* No appropriate hook. */
+ { "SYS_KEXEC_LOAD", 0, "sys_kexec_load()" },
+ [TMY_SYS_PIVOT_ROOT] = /* OK */
+ { "SYS_PIVOT_ROOT", 0, "sys_pivot_root()" },
+};
+
+struct profile {
+ unsigned char value[TMY_MAX_CAPABILITY_INDEX];
+};
+
+static struct profile *profile_ptr[TMY_MAX_PROFILES];
+
+/************************* UTILITY FUNCTIONS *************************/
+
+const char *tmy_capability2keyword(const unsigned int capability)
+{
+ return capability < TMY_MAX_CAPABILITY_INDEX ?
+ capability_control_array[capability].keyword : NULL;
+}
+
+static const char *tmy_capability2name(const unsigned int capability)
+{
+ return capability < TMY_MAX_CAPABILITY_INDEX ?
+ capability_control_array[capability].capability_name : NULL;
+}
+
+/* Check whether the given capability control is enabled. */
+static unsigned int tmy_capability_flags(const unsigned int index)
+{
+ const u8 profile = TMY_SECURITY->domain->profile;
+ /* All operations might sleep. See tmy_supervisor(). */
+ might_sleep();
+ if (in_interrupt())
+ return 0;
+ return sbin_init_started && index < TMY_MAX_CAPABILITY_INDEX
+#if TMY_MAX_PROFILES != 256
+ && profile < TMY_MAX_PROFILES
+#endif
+ && profile_ptr[profile] ?
+ profile_ptr[profile]->value[index] : 0;
+}
+
+static struct profile *tmy_new_capability_profile(const unsigned int profile)
+{
+ static DEFINE_MUTEX(mutex);
+ struct profile *ptr;
+ int i;
+ if (profile >= TMY_MAX_PROFILES)
+ return NULL;
+ mutex_lock(&mutex);
+ ptr = profile_ptr[profile];
+ if (ptr)
+ goto ok;
+ ptr = tmy_alloc_element(sizeof(*ptr));
+ if (!ptr)
+ goto ok;
+ for (i = 0; i < TMY_MAX_CAPABILITY_INDEX; i++)
+ ptr->value[i] = capability_control_array[i].current_value;
+ mb(); /* Avoid out-of-order execution. */
+ profile_ptr[profile] = ptr;
+ok: ;
+ mutex_unlock(&mutex);
+ return ptr;
+}
+
+int tmy_set_capability_profile(const char *data, unsigned int value,
+ const unsigned int profile)
+{
+ int i;
+ struct profile *ptr;
+ ptr = tmy_new_capability_profile(profile);
+ if (!ptr)
+ return -EINVAL;
+ for (i = 0; i < TMY_MAX_CAPABILITY_INDEX; i++) {
+ if (strcmp(data, capability_control_array[i].keyword))
+ continue;
+ if (value > 3)
+ value = 3;
+ ptr->value[i] = value;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+int tmy_read_capability_profile(struct io_buffer *head)
+{
+ int step;
+ for (step = head->read_step;
+ step < TMY_MAX_PROFILES * TMY_MAX_CAPABILITY_INDEX; step++) {
+ const int i = step / TMY_MAX_CAPABILITY_INDEX;
+ const int j = step % TMY_MAX_CAPABILITY_INDEX;
+ const struct profile *profile = profile_ptr[i];
+ head->read_step = step;
+ if (!profile)
+ continue;
+ if (tmy_io_printf(head, "%u-" TMY_MAC_FOR_CAPABILITY "%s=%u\n",
+ i, capability_control_array[j].keyword,
+ profile->value[j]))
+ break;
+ }
+ return step < TMY_MAX_PROFILES * TMY_MAX_CAPABILITY_INDEX ? -ENOMEM : 0;
+}
+
+/************************* AUDIT FUNCTIONS *************************/
+
+static int tmy_audit_capability_log(const unsigned int capability,
+ const bool is_granted,
+ const u8 profile, const unsigned int mode)
+{
+ char *buf;
+ int len = 64 ;
+
+ if (is_granted) {
+ if (!tmy_audit_grant())
+ return 0;
+ } else {
+ if (!tmy_audit_reject())
+ return 0;
+ }
+
+ buf = tmy_init_audit_log(&len, profile, mode);
+
+ if (!buf)
+ return -ENOMEM;
+
+ tmy_sncatprintf(buf, len - 1, TMY_ALLOW_CAPABILITY "%s\n",
+ tmy_capability2keyword(capability));
+
+ return tmy_write_audit_log(buf, is_granted);
+}
+
+/************************* CAPABILITY ACL HANDLER *************************/
+
+static int tmy_add_capability_acl(const unsigned int capability,
+ struct domain_info *domain,
+ const struct condition_list *condition,
+ const bool is_delete)
+{
+ struct acl_info *ptr;
+ struct capability_acl *acl;
+ int error = -ENOMEM;
+ const u16 hash = capability;
+ if (!domain)
+ return -EINVAL;
+ mutex_lock(&domain_acl_lock);
+ if (is_delete)
+ goto remove;
+
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ acl = container_of(ptr, struct capability_acl, head);
+ if (ptr->type == TMY_TYPE_CAPABILITY_ACL
+ && acl->capability == hash
+ && ptr->cond == condition) {
+ ptr->is_deleted = 0;
+ /* Found. Nothing to do. */
+ error = 0;
+ tmy_update_counter(TMY_UPDATE_DOMAINPOLICY);
+ goto ok;
+ }
+ }
+ /* Not found. Append it to the tail. */
+ acl = tmy_alloc_element(sizeof(*acl));
+ if (!acl)
+ goto ok;
+ acl->head.type = TMY_TYPE_CAPABILITY_ACL;
+ acl->head.cond = condition;
+ acl->capability = hash;
+ error = tmy_add_acl(domain, &acl->head);
+ goto ok;
+remove: ;
+ error = -ENOENT;
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ acl = container_of(ptr, struct capability_acl, head);
+ if (ptr->type != TMY_TYPE_CAPABILITY_ACL || ptr->is_deleted ||
+ acl->capability != hash || ptr->cond != condition)
+ continue;
+ error = tmy_del_acl(ptr);
+ break;
+ }
+ok: ;
+ mutex_unlock(&domain_acl_lock);
+ return error;
+}
+
+/**
+ * tmy_capable - check permission for capability.
+ * @capability: capability index.
+ *
+ * Returns zero if permission granted.
+ * Returns nonzero if permission denied.
+ */
+int tmy_capable(const unsigned int capability)
+{
+ struct domain_info * const domain = TMY_SECURITY->domain;
+ struct acl_info *ptr;
+ const u8 profile = domain->profile;
+ const unsigned int mode = tmy_capability_flags(capability);
+ const bool is_enforce = (mode == 3);
+ const u16 hash = capability;
+ if (!mode)
+ return 0;
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ struct capability_acl *acl;
+ acl = container_of(ptr, struct capability_acl, head);
+ if (ptr->type != TMY_TYPE_CAPABILITY_ACL || ptr->is_deleted
+ || acl->capability != hash
+ || tmy_check_condition(ptr->cond, NULL))
+ continue;
+ tmy_audit_capability_log(capability, 1, profile, mode);
+ return 0;
+ }
+ if (tmy_flags(TMY_VERBOSE))
+ tmy_audit("TOMOYO-%s: %s denied for %s\n",
+ tmy_getmsg(is_enforce),
+ tmy_capability2name(capability),
+ tmy_lastname(domain));
+ tmy_audit_capability_log(capability, 0, profile, mode);
+ if (is_enforce)
+ return tmy_supervisor("%s\n" TMY_ALLOW_CAPABILITY "%s\n",
+ domain->domainname->name,
+ tmy_capability2keyword(capability));
+ if (mode == 1 && tmy_quota())
+ tmy_add_capability_acl(capability, domain, NULL, 0);
+ return 0;
+}
+
+/**
+ * tmy_add_capability_policy - add or delete capability policy.
+ * @data: a line to parse.
+ * @domain: pointer to "struct domain_info".
+ * @cond: pointer to "struct condition_list". May be NULL.
+ * @is_delete: is this delete request?
+ *
+ * Returns zero on success.
+ * Returns nonzero on failure.
+ */
+int tmy_add_capability_policy(char *data, struct domain_info *domain,
+ const struct condition_list *cond,
+ const bool is_delete)
+{
+ unsigned int capability;
+ for (capability = 0; capability < TMY_MAX_CAPABILITY_INDEX;
+ capability++) {
+ if (strcmp(data, capability_control_array[capability].keyword))
+ continue;
+ return tmy_add_capability_acl(capability, domain,
+ cond, is_delete);
+ }
+ return -EINVAL;
+}
--
next prev parent reply other threads:[~2008-01-09 1:01 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-01-09 0:53 [TOMOYO #6 retry 00/21] TOMOYO Linux - MAC based on process invocation history Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 01/21] TOMOYO Linux documentation Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 02/21] Add struct vfsmount to struct task_struct Kentaro Takeda
2008-01-15 21:16 ` Serge E. Hallyn
2008-01-16 0:22 ` Kentaro Takeda
2008-01-16 14:39 ` Serge E. Hallyn
2008-01-17 4:55 ` Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 03/21] Add wrapper functions for VFS helper functions Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 04/21] Replace VFS with wrapper functions Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 05/21] Add packet filtering based on processs security context Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 06/21] Data structures and prototype defitions Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 07/21] Memory and pathname management functions Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 08/21] Utility functions and policy manipulation interface Kentaro Takeda
2008-01-09 4:25 ` James Morris
2008-01-09 4:29 ` James Morris
2008-01-12 2:06 ` [TOMOYO #6 retry 08/21] Utility functions and policy manipulationinterface Tetsuo Handa
2008-01-12 3:06 ` James Morris
2008-01-12 4:45 ` Greg KH
2008-01-12 7:34 ` [TOMOYO #6 retry 08/21] Utility functions and policymanipulationinterface Tetsuo Handa
2008-01-09 4:31 ` [TOMOYO #6 retry 08/21] Utility functions and policy manipulation interface Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 09/21] Domain transition functions Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 10/21] Auditing interface Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 11/21] File access control functions Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 12/21] argv0 check functions Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 13/21] environment variable name " Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 14/21] Network access control functions Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 15/21] Namespace manipulation " Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 16/21] Signal " Kentaro Takeda
2008-01-09 0:53 ` Kentaro Takeda [this message]
2008-01-09 0:53 ` [TOMOYO #6 retry 18/21] LSM adapter functions Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 19/21] Conditional permission support Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 20/21] Kconfig and Makefile Kentaro Takeda
2008-01-09 0:53 ` [TOMOYO #6 retry 21/21] Add signal hooks at sleepable location Kentaro Takeda
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20080109005426.344192310@nttdata.co.jp \
--to=takedakn@nttdata.co.jp \
--cc=akpm@linux-foundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=penguin-kernel@I-love.SAKURA.ne.jp \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox