aboutsummaryrefslogtreecommitdiffstats
path: root/security/tomoyo/common.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/tomoyo/common.c')
-rw-r--r--security/tomoyo/common.c2176
1 files changed, 1459 insertions, 717 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index a0d09e5..c47d3ce 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -1,9 +1,7 @@
/*
* security/tomoyo/common.c
*
- * Common functions for TOMOYO.
- *
- * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ * Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include <linux/uaccess.h>
@@ -11,54 +9,163 @@
#include <linux/security.h>
#include "common.h"
-static struct tomoyo_profile tomoyo_default_profile = {
- .learning = &tomoyo_default_profile.preference,
- .permissive = &tomoyo_default_profile.preference,
- .enforcing = &tomoyo_default_profile.preference,
- .preference.enforcing_verbose = true,
- .preference.learning_max_entry = 2048,
- .preference.learning_verbose = false,
- .preference.permissive_verbose = true
+/* String table for operation mode. */
+const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = {
+ [TOMOYO_CONFIG_DISABLED] = "disabled",
+ [TOMOYO_CONFIG_LEARNING] = "learning",
+ [TOMOYO_CONFIG_PERMISSIVE] = "permissive",
+ [TOMOYO_CONFIG_ENFORCING] = "enforcing"
};
-/* Profile version. Currently only 20090903 is defined. */
-static unsigned int tomoyo_profile_version;
+/* String table for /sys/kernel/security/tomoyo/profile */
+const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+ + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+ /* CONFIG::file group */
+ [TOMOYO_MAC_FILE_EXECUTE] = "execute",
+ [TOMOYO_MAC_FILE_OPEN] = "open",
+ [TOMOYO_MAC_FILE_CREATE] = "create",
+ [TOMOYO_MAC_FILE_UNLINK] = "unlink",
+ [TOMOYO_MAC_FILE_GETATTR] = "getattr",
+ [TOMOYO_MAC_FILE_MKDIR] = "mkdir",
+ [TOMOYO_MAC_FILE_RMDIR] = "rmdir",
+ [TOMOYO_MAC_FILE_MKFIFO] = "mkfifo",
+ [TOMOYO_MAC_FILE_MKSOCK] = "mksock",
+ [TOMOYO_MAC_FILE_TRUNCATE] = "truncate",
+ [TOMOYO_MAC_FILE_SYMLINK] = "symlink",
+ [TOMOYO_MAC_FILE_MKBLOCK] = "mkblock",
+ [TOMOYO_MAC_FILE_MKCHAR] = "mkchar",
+ [TOMOYO_MAC_FILE_LINK] = "link",
+ [TOMOYO_MAC_FILE_RENAME] = "rename",
+ [TOMOYO_MAC_FILE_CHMOD] = "chmod",
+ [TOMOYO_MAC_FILE_CHOWN] = "chown",
+ [TOMOYO_MAC_FILE_CHGRP] = "chgrp",
+ [TOMOYO_MAC_FILE_IOCTL] = "ioctl",
+ [TOMOYO_MAC_FILE_CHROOT] = "chroot",
+ [TOMOYO_MAC_FILE_MOUNT] = "mount",
+ [TOMOYO_MAC_FILE_UMOUNT] = "unmount",
+ [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root",
+ /* CONFIG::network group */
+ [TOMOYO_MAC_NETWORK_INET_STREAM_BIND] = "inet_stream_bind",
+ [TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN] = "inet_stream_listen",
+ [TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT] = "inet_stream_connect",
+ [TOMOYO_MAC_NETWORK_INET_DGRAM_BIND] = "inet_dgram_bind",
+ [TOMOYO_MAC_NETWORK_INET_DGRAM_SEND] = "inet_dgram_send",
+ [TOMOYO_MAC_NETWORK_INET_RAW_BIND] = "inet_raw_bind",
+ [TOMOYO_MAC_NETWORK_INET_RAW_SEND] = "inet_raw_send",
+ [TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND] = "unix_stream_bind",
+ [TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN] = "unix_stream_listen",
+ [TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT] = "unix_stream_connect",
+ [TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND] = "unix_dgram_bind",
+ [TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND] = "unix_dgram_send",
+ [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind",
+ [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen",
+ [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect",
+ /* CONFIG::misc group */
+ [TOMOYO_MAC_ENVIRON] = "env",
+ /* CONFIG group */
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_NETWORK] = "network",
+ [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc",
+};
-/* Profile table. Memory is allocated as needed. */
-static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
+/* String table for conditions. */
+const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = {
+ [TOMOYO_TASK_UID] = "task.uid",
+ [TOMOYO_TASK_EUID] = "task.euid",
+ [TOMOYO_TASK_SUID] = "task.suid",
+ [TOMOYO_TASK_FSUID] = "task.fsuid",
+ [TOMOYO_TASK_GID] = "task.gid",
+ [TOMOYO_TASK_EGID] = "task.egid",
+ [TOMOYO_TASK_SGID] = "task.sgid",
+ [TOMOYO_TASK_FSGID] = "task.fsgid",
+ [TOMOYO_TASK_PID] = "task.pid",
+ [TOMOYO_TASK_PPID] = "task.ppid",
+ [TOMOYO_EXEC_ARGC] = "exec.argc",
+ [TOMOYO_EXEC_ENVC] = "exec.envc",
+ [TOMOYO_TYPE_IS_SOCKET] = "socket",
+ [TOMOYO_TYPE_IS_SYMLINK] = "symlink",
+ [TOMOYO_TYPE_IS_FILE] = "file",
+ [TOMOYO_TYPE_IS_BLOCK_DEV] = "block",
+ [TOMOYO_TYPE_IS_DIRECTORY] = "directory",
+ [TOMOYO_TYPE_IS_CHAR_DEV] = "char",
+ [TOMOYO_TYPE_IS_FIFO] = "fifo",
+ [TOMOYO_MODE_SETUID] = "setuid",
+ [TOMOYO_MODE_SETGID] = "setgid",
+ [TOMOYO_MODE_STICKY] = "sticky",
+ [TOMOYO_MODE_OWNER_READ] = "owner_read",
+ [TOMOYO_MODE_OWNER_WRITE] = "owner_write",
+ [TOMOYO_MODE_OWNER_EXECUTE] = "owner_execute",
+ [TOMOYO_MODE_GROUP_READ] = "group_read",
+ [TOMOYO_MODE_GROUP_WRITE] = "group_write",
+ [TOMOYO_MODE_GROUP_EXECUTE] = "group_execute",
+ [TOMOYO_MODE_OTHERS_READ] = "others_read",
+ [TOMOYO_MODE_OTHERS_WRITE] = "others_write",
+ [TOMOYO_MODE_OTHERS_EXECUTE] = "others_execute",
+ [TOMOYO_EXEC_REALPATH] = "exec.realpath",
+ [TOMOYO_SYMLINK_TARGET] = "symlink.target",
+ [TOMOYO_PATH1_UID] = "path1.uid",
+ [TOMOYO_PATH1_GID] = "path1.gid",
+ [TOMOYO_PATH1_INO] = "path1.ino",
+ [TOMOYO_PATH1_MAJOR] = "path1.major",
+ [TOMOYO_PATH1_MINOR] = "path1.minor",
+ [TOMOYO_PATH1_PERM] = "path1.perm",
+ [TOMOYO_PATH1_TYPE] = "path1.type",
+ [TOMOYO_PATH1_DEV_MAJOR] = "path1.dev_major",
+ [TOMOYO_PATH1_DEV_MINOR] = "path1.dev_minor",
+ [TOMOYO_PATH2_UID] = "path2.uid",
+ [TOMOYO_PATH2_GID] = "path2.gid",
+ [TOMOYO_PATH2_INO] = "path2.ino",
+ [TOMOYO_PATH2_MAJOR] = "path2.major",
+ [TOMOYO_PATH2_MINOR] = "path2.minor",
+ [TOMOYO_PATH2_PERM] = "path2.perm",
+ [TOMOYO_PATH2_TYPE] = "path2.type",
+ [TOMOYO_PATH2_DEV_MAJOR] = "path2.dev_major",
+ [TOMOYO_PATH2_DEV_MINOR] = "path2.dev_minor",
+ [TOMOYO_PATH1_PARENT_UID] = "path1.parent.uid",
+ [TOMOYO_PATH1_PARENT_GID] = "path1.parent.gid",
+ [TOMOYO_PATH1_PARENT_INO] = "path1.parent.ino",
+ [TOMOYO_PATH1_PARENT_PERM] = "path1.parent.perm",
+ [TOMOYO_PATH2_PARENT_UID] = "path2.parent.uid",
+ [TOMOYO_PATH2_PARENT_GID] = "path2.parent.gid",
+ [TOMOYO_PATH2_PARENT_INO] = "path2.parent.ino",
+ [TOMOYO_PATH2_PARENT_PERM] = "path2.parent.perm",
+};
-/* String table for functionality that takes 4 modes. */
-static const char *tomoyo_mode[4] = {
- "disabled", "learning", "permissive", "enforcing"
+/* String table for PREFERENCE keyword. */
+static const char * const tomoyo_pref_keywords[TOMOYO_MAX_PREF] = {
+ [TOMOYO_PREF_MAX_AUDIT_LOG] = "max_audit_log",
+ [TOMOYO_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry",
};
-/* String table for /sys/kernel/security/tomoyo/profile */
-static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
- + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
- [TOMOYO_MAC_FILE_EXECUTE] = "file::execute",
- [TOMOYO_MAC_FILE_OPEN] = "file::open",
- [TOMOYO_MAC_FILE_CREATE] = "file::create",
- [TOMOYO_MAC_FILE_UNLINK] = "file::unlink",
- [TOMOYO_MAC_FILE_MKDIR] = "file::mkdir",
- [TOMOYO_MAC_FILE_RMDIR] = "file::rmdir",
- [TOMOYO_MAC_FILE_MKFIFO] = "file::mkfifo",
- [TOMOYO_MAC_FILE_MKSOCK] = "file::mksock",
- [TOMOYO_MAC_FILE_TRUNCATE] = "file::truncate",
- [TOMOYO_MAC_FILE_SYMLINK] = "file::symlink",
- [TOMOYO_MAC_FILE_REWRITE] = "file::rewrite",
- [TOMOYO_MAC_FILE_MKBLOCK] = "file::mkblock",
- [TOMOYO_MAC_FILE_MKCHAR] = "file::mkchar",
- [TOMOYO_MAC_FILE_LINK] = "file::link",
- [TOMOYO_MAC_FILE_RENAME] = "file::rename",
- [TOMOYO_MAC_FILE_CHMOD] = "file::chmod",
- [TOMOYO_MAC_FILE_CHOWN] = "file::chown",
- [TOMOYO_MAC_FILE_CHGRP] = "file::chgrp",
- [TOMOYO_MAC_FILE_IOCTL] = "file::ioctl",
- [TOMOYO_MAC_FILE_CHROOT] = "file::chroot",
- [TOMOYO_MAC_FILE_MOUNT] = "file::mount",
- [TOMOYO_MAC_FILE_UMOUNT] = "file::umount",
- [TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root",
- [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
+/* String table for path operation. */
+const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
+ [TOMOYO_TYPE_EXECUTE] = "execute",
+ [TOMOYO_TYPE_READ] = "read",
+ [TOMOYO_TYPE_WRITE] = "write",
+ [TOMOYO_TYPE_APPEND] = "append",
+ [TOMOYO_TYPE_UNLINK] = "unlink",
+ [TOMOYO_TYPE_GETATTR] = "getattr",
+ [TOMOYO_TYPE_RMDIR] = "rmdir",
+ [TOMOYO_TYPE_TRUNCATE] = "truncate",
+ [TOMOYO_TYPE_SYMLINK] = "symlink",
+ [TOMOYO_TYPE_CHROOT] = "chroot",
+ [TOMOYO_TYPE_UMOUNT] = "unmount",
+};
+
+/* String table for socket's operation. */
+const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION] = {
+ [TOMOYO_NETWORK_BIND] = "bind",
+ [TOMOYO_NETWORK_LISTEN] = "listen",
+ [TOMOYO_NETWORK_CONNECT] = "connect",
+ [TOMOYO_NETWORK_SEND] = "send",
+};
+
+/* String table for categories. */
+static const char * const tomoyo_category_keywords
+[TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+ [TOMOYO_MAC_CATEGORY_FILE] = "file",
+ [TOMOYO_MAC_CATEGORY_NETWORK] = "network",
+ [TOMOYO_MAC_CATEGORY_MISC] = "misc",
};
/* Permit policy management by non-root user? */
@@ -71,11 +178,20 @@ static bool tomoyo_manage_by_non_root;
*
* @value: Bool value.
*/
-static const char *tomoyo_yesno(const unsigned int value)
+const char *tomoyo_yesno(const unsigned int value)
{
return value ? "yes" : "no";
}
+/**
+ * tomoyo_addprintf - strncat()-like-snprintf().
+ *
+ * @buffer: Buffer to write to. Must be '\0'-terminated.
+ * @len: Size of @buffer.
+ * @fmt: The printf()'s format string, followed by parameters.
+ *
+ * Returns nothing.
+ */
static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...)
{
va_list args;
@@ -96,7 +212,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)
{
while (head->r.w_pos) {
const char *w = head->r.w[0];
- int len = strlen(w);
+ size_t len = strlen(w);
if (len) {
if (len > head->read_user_buf_avail)
len = head->read_user_buf_avail;
@@ -111,7 +227,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)
head->r.w[0] = w;
if (*w)
return false;
- /* Add '\0' for query. */
+ /* Add '\0' for audit logs and query. */
if (head->poll) {
if (!head->read_user_buf_avail ||
copy_to_user(head->read_user_buf, "", 1))
@@ -146,17 +262,21 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string)
WARN_ON(1);
}
+static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt,
+ ...) __printf(2, 3);
+
/**
* tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure.
*
* @head: Pointer to "struct tomoyo_io_buffer".
* @fmt: The printf()'s format string, followed by parameters.
*/
-void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
+static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt,
+ ...)
{
va_list args;
- int len;
- int pos = head->r.avail;
+ size_t len;
+ size_t pos = head->r.avail;
int size = head->readbuf_size - pos;
if (size <= 0)
return;
@@ -171,11 +291,25 @@ void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
tomoyo_set_string(head, head->read_buf + pos);
}
+/**
+ * tomoyo_set_space - Put a space to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
static void tomoyo_set_space(struct tomoyo_io_buffer *head)
{
tomoyo_set_string(head, " ");
}
+/**
+ * tomoyo_set_lf - Put a line feed to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)
{
tomoyo_set_string(head, "\n");
@@ -183,6 +317,62 @@ static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)
}
/**
+ * tomoyo_set_slash - Put a shash to "struct tomoyo_io_buffer" structure.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_set_slash(struct tomoyo_io_buffer *head)
+{
+ tomoyo_set_string(head, "/");
+}
+
+/* List of namespaces. */
+LIST_HEAD(tomoyo_namespace_list);
+/* True if namespace other than tomoyo_kernel_namespace is defined. */
+static bool tomoyo_namespace_enabled;
+
+/**
+ * tomoyo_init_policy_namespace - Initialize namespace.
+ *
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
+ *
+ * Returns nothing.
+ */
+void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
+{
+ unsigned int idx;
+ for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++)
+ INIT_LIST_HEAD(&ns->acl_group[idx]);
+ for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++)
+ INIT_LIST_HEAD(&ns->group_list[idx]);
+ for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
+ INIT_LIST_HEAD(&ns->policy_list[idx]);
+ ns->profile_version = 20110903;
+ tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
+ list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
+}
+
+/**
+ * tomoyo_print_namespace - Print namespace header.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_namespace(struct tomoyo_io_buffer *head)
+{
+ if (!tomoyo_namespace_enabled)
+ return;
+ tomoyo_set_string(head,
+ container_of(head->r.ns,
+ struct tomoyo_policy_namespace,
+ namespace_list)->name);
+ tomoyo_set_space(head);
+}
+
+/**
* tomoyo_print_name_union - Print a tomoyo_name_union.
*
* @head: Pointer to "struct tomoyo_io_buffer".
@@ -192,7 +382,7 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head,
const struct tomoyo_name_union *ptr)
{
tomoyo_set_space(head);
- if (ptr->is_group) {
+ if (ptr->group) {
tomoyo_set_string(head, "@");
tomoyo_set_string(head, ptr->group->group_name->name);
} else {
@@ -201,24 +391,46 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head,
}
/**
- * tomoyo_print_number_union - Print a tomoyo_number_union.
+ * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote.
*
- * @head: Pointer to "struct tomoyo_io_buffer".
- * @ptr: Pointer to "struct tomoyo_number_union".
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns nothing.
*/
-static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
- const struct tomoyo_number_union *ptr)
+static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head,
+ const struct tomoyo_name_union *ptr)
{
- tomoyo_set_space(head);
- if (ptr->is_group) {
+ if (ptr->group) {
+ tomoyo_set_string(head, "@");
+ tomoyo_set_string(head, ptr->group->group_name->name);
+ } else {
+ tomoyo_set_string(head, "\"");
+ tomoyo_set_string(head, ptr->filename->name);
+ tomoyo_set_string(head, "\"");
+ }
+}
+
+/**
+ * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_number_union_nospace
+(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr)
+{
+ if (ptr->group) {
tomoyo_set_string(head, "@");
tomoyo_set_string(head, ptr->group->group_name->name);
} else {
int i;
unsigned long min = ptr->values[0];
const unsigned long max = ptr->values[1];
- u8 min_type = ptr->min_type;
- const u8 max_type = ptr->max_type;
+ u8 min_type = ptr->value_type[0];
+ const u8 max_type = ptr->value_type[1];
char buffer[128];
buffer[0] = '\0';
for (i = 0; i < 2; i++) {
@@ -232,8 +444,8 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
"0%lo", min);
break;
default:
- tomoyo_addprintf(buffer, sizeof(buffer),
- "%lu", min);
+ tomoyo_addprintf(buffer, sizeof(buffer), "%lu",
+ min);
break;
}
if (min == max && min_type == max_type)
@@ -247,35 +459,55 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
}
/**
+ * tomoyo_print_number_union - Print a tomoyo_number_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_number_union".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,
+ const struct tomoyo_number_union *ptr)
+{
+ tomoyo_set_space(head);
+ tomoyo_print_number_union_nospace(head, ptr);
+}
+
+/**
* tomoyo_assign_profile - Create a new profile.
*
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
* @profile: Profile number to create.
*
* Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
*/
-static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile)
+static struct tomoyo_profile *tomoyo_assign_profile
+(struct tomoyo_policy_namespace *ns, const unsigned int profile)
{
struct tomoyo_profile *ptr;
struct tomoyo_profile *entry;
if (profile >= TOMOYO_MAX_PROFILES)
return NULL;
- ptr = tomoyo_profile_ptr[profile];
+ ptr = ns->profile_ptr[profile];
if (ptr)
return ptr;
entry = kzalloc(sizeof(*entry), GFP_NOFS);
if (mutex_lock_interruptible(&tomoyo_policy_lock))
goto out;
- ptr = tomoyo_profile_ptr[profile];
+ ptr = ns->profile_ptr[profile];
if (!ptr && tomoyo_memory_ok(entry)) {
ptr = entry;
- ptr->learning = &tomoyo_default_profile.preference;
- ptr->permissive = &tomoyo_default_profile.preference;
- ptr->enforcing = &tomoyo_default_profile.preference;
- ptr->default_config = TOMOYO_CONFIG_DISABLED;
+ ptr->default_config = TOMOYO_CONFIG_DISABLED |
+ TOMOYO_CONFIG_WANT_GRANT_LOG |
+ TOMOYO_CONFIG_WANT_REJECT_LOG;
memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
sizeof(ptr->config));
+ ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] =
+ CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG;
+ ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] =
+ CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY;
mb(); /* Avoid out-of-order execution. */
- tomoyo_profile_ptr[profile] = ptr;
+ ns->profile_ptr[profile] = ptr;
entry = NULL;
}
mutex_unlock(&tomoyo_policy_lock);
@@ -287,19 +519,29 @@ static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile)
/**
* tomoyo_profile - Find a profile.
*
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
* @profile: Profile number to find.
*
* Returns pointer to "struct tomoyo_profile".
*/
-struct tomoyo_profile *tomoyo_profile(const u8 profile)
+struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns,
+ const u8 profile)
{
- struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile];
- if (!tomoyo_policy_loaded)
- return &tomoyo_default_profile;
- BUG_ON(!ptr);
+ static struct tomoyo_profile tomoyo_null_profile;
+ struct tomoyo_profile *ptr = ns->profile_ptr[profile];
+ if (!ptr)
+ ptr = &tomoyo_null_profile;
return ptr;
}
+/**
+ * tomoyo_find_yesno - Find values for specified keyword.
+ *
+ * @string: String to check.
+ * @find: Name of keyword.
+ *
+ * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise.
+ */
static s8 tomoyo_find_yesno(const char *string, const char *find)
{
const char *cp = strstr(string, find);
@@ -313,18 +555,15 @@ static s8 tomoyo_find_yesno(const char *string, const char *find)
return -1;
}
-static void tomoyo_set_bool(bool *b, const char *string, const char *find)
-{
- switch (tomoyo_find_yesno(string, find)) {
- case 1:
- *b = true;
- break;
- case 0:
- *b = false;
- break;
- }
-}
-
+/**
+ * tomoyo_set_uint - Set value for specified preference.
+ *
+ * @i: Pointer to "unsigned int".
+ * @string: String to check.
+ * @find: Name of keyword.
+ *
+ * Returns nothing.
+ */
static void tomoyo_set_uint(unsigned int *i, const char *string,
const char *find)
{
@@ -333,51 +572,16 @@ static void tomoyo_set_uint(unsigned int *i, const char *string,
sscanf(cp + strlen(find), "=%u", i);
}
-static void tomoyo_set_pref(const char *name, const char *value,
- const bool use_default,
- struct tomoyo_profile *profile)
-{
- struct tomoyo_preference **pref;
- bool *verbose;
- if (!strcmp(name, "enforcing")) {
- if (use_default) {
- pref = &profile->enforcing;
- goto set_default;
- }
- profile->enforcing = &profile->preference;
- verbose = &profile->preference.enforcing_verbose;
- goto set_verbose;
- }
- if (!strcmp(name, "permissive")) {
- if (use_default) {
- pref = &profile->permissive;
- goto set_default;
- }
- profile->permissive = &profile->preference;
- verbose = &profile->preference.permissive_verbose;
- goto set_verbose;
- }
- if (!strcmp(name, "learning")) {
- if (use_default) {
- pref = &profile->learning;
- goto set_default;
- }
- profile->learning = &profile->preference;
- tomoyo_set_uint(&profile->preference.learning_max_entry, value,
- "max_entry");
- verbose = &profile->preference.learning_verbose;
- goto set_verbose;
- }
- return;
- set_default:
- *pref = &tomoyo_default_profile.preference;
- return;
- set_verbose:
- tomoyo_set_bool(verbose, value, "verbose");
-}
-
+/**
+ * tomoyo_set_mode - Set mode for specified profile.
+ *
+ * @name: Name of functionality.
+ * @value: Mode for @name.
+ * @profile: Pointer to "struct tomoyo_profile".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
static int tomoyo_set_mode(char *name, const char *value,
- const bool use_default,
struct tomoyo_profile *profile)
{
u8 i;
@@ -389,7 +593,17 @@ static int tomoyo_set_mode(char *name, const char *value,
config = 0;
for (i = 0; i < TOMOYO_MAX_MAC_INDEX
+ TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
- if (strcmp(name, tomoyo_mac_keywords[i]))
+ int len = 0;
+ if (i < TOMOYO_MAX_MAC_INDEX) {
+ const u8 c = tomoyo_index2category[i];
+ const char *category =
+ tomoyo_category_keywords[c];
+ len = strlen(category);
+ if (strncmp(name, category, len) ||
+ name[len++] != ':' || name[len++] != ':')
+ continue;
+ }
+ if (strcmp(name + len, tomoyo_mac_keywords[i]))
continue;
config = profile->config[i];
break;
@@ -399,7 +613,7 @@ static int tomoyo_set_mode(char *name, const char *value,
} else {
return -EINVAL;
}
- if (use_default) {
+ if (strstr(value, "use_default")) {
config = TOMOYO_CONFIG_USE_DEFAULT;
} else {
u8 mode;
@@ -410,6 +624,24 @@ static int tomoyo_set_mode(char *name, const char *value,
* 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'.
*/
config = (config & ~7) | mode;
+ if (config != TOMOYO_CONFIG_USE_DEFAULT) {
+ switch (tomoyo_find_yesno(value, "grant_log")) {
+ case 1:
+ config |= TOMOYO_CONFIG_WANT_GRANT_LOG;
+ break;
+ case 0:
+ config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG;
+ break;
+ }
+ switch (tomoyo_find_yesno(value, "reject_log")) {
+ case 1:
+ config |= TOMOYO_CONFIG_WANT_REJECT_LOG;
+ break;
+ case 0:
+ config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG;
+ break;
+ }
+ }
}
if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
profile->config[i] = config;
@@ -429,34 +661,22 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
{
char *data = head->write_buf;
unsigned int i;
- bool use_default = false;
char *cp;
struct tomoyo_profile *profile;
- if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1)
+ if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version)
+ == 1)
return 0;
i = simple_strtoul(data, &cp, 10);
- if (data == cp) {
- profile = &tomoyo_default_profile;
- } else {
- if (*cp != '-')
- return -EINVAL;
- data = cp + 1;
- profile = tomoyo_assign_profile(i);
- if (!profile)
- return -EINVAL;
- }
+ if (*cp != '-')
+ return -EINVAL;
+ data = cp + 1;
+ profile = tomoyo_assign_profile(head->w.ns, i);
+ if (!profile)
+ return -EINVAL;
cp = strchr(data, '=');
if (!cp)
return -EINVAL;
*cp++ = '\0';
- if (profile != &tomoyo_default_profile)
- use_default = strstr(cp, "use_default") != NULL;
- if (tomoyo_str_starts(&data, "PREFERENCE::")) {
- tomoyo_set_pref(data, cp, use_default, profile);
- return 0;
- }
- if (profile == &tomoyo_default_profile)
- return -EINVAL;
if (!strcmp(data, "COMMENT")) {
static DEFINE_SPINLOCK(lock);
const struct tomoyo_path_info *new_comment
@@ -471,94 +691,91 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
tomoyo_put_name(old_comment);
return 0;
}
- return tomoyo_set_mode(data, cp, use_default, profile);
-}
-
-static void tomoyo_print_preference(struct tomoyo_io_buffer *head,
- const int idx)
-{
- struct tomoyo_preference *pref = &tomoyo_default_profile.preference;
- const struct tomoyo_profile *profile = idx >= 0 ?
- tomoyo_profile_ptr[idx] : NULL;
- char buffer[16] = "";
- if (profile) {
- buffer[sizeof(buffer) - 1] = '\0';
- snprintf(buffer, sizeof(buffer) - 1, "%u-", idx);
- }
- if (profile) {
- pref = profile->learning;
- if (pref == &tomoyo_default_profile.preference)
- goto skip1;
- }
- tomoyo_io_printf(head, "%sPREFERENCE::%s={ "
- "verbose=%s max_entry=%u }\n",
- buffer, "learning",
- tomoyo_yesno(pref->learning_verbose),
- pref->learning_max_entry);
- skip1:
- if (profile) {
- pref = profile->permissive;
- if (pref == &tomoyo_default_profile.preference)
- goto skip2;
- }
- tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n",
- buffer, "permissive",
- tomoyo_yesno(pref->permissive_verbose));
- skip2:
- if (profile) {
- pref = profile->enforcing;
- if (pref == &tomoyo_default_profile.preference)
- return;
+ if (!strcmp(data, "PREFERENCE")) {
+ for (i = 0; i < TOMOYO_MAX_PREF; i++)
+ tomoyo_set_uint(&profile->pref[i], cp,
+ tomoyo_pref_keywords[i]);
+ return 0;
}
- tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n",
- buffer, "enforcing",
- tomoyo_yesno(pref->enforcing_verbose));
+ return tomoyo_set_mode(data, cp, profile);
}
+/**
+ * tomoyo_print_config - Print mode for specified functionality.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @config: Mode for that functionality.
+ *
+ * Returns nothing.
+ *
+ * Caller prints functionality's name.
+ */
static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config)
{
- tomoyo_io_printf(head, "={ mode=%s }\n", tomoyo_mode[config & 3]);
+ tomoyo_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n",
+ tomoyo_mode[config & 3],
+ tomoyo_yesno(config & TOMOYO_CONFIG_WANT_GRANT_LOG),
+ tomoyo_yesno(config & TOMOYO_CONFIG_WANT_REJECT_LOG));
}
/**
* tomoyo_read_profile - Read profile table.
*
* @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
*/
static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
{
u8 index;
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
const struct tomoyo_profile *profile;
+ if (head->r.eof)
+ return;
next:
index = head->r.index;
- profile = tomoyo_profile_ptr[index];
+ profile = ns->profile_ptr[index];
switch (head->r.step) {
case 0:
- tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903");
- tomoyo_print_preference(head, -1);
+ tomoyo_print_namespace(head);
+ tomoyo_io_printf(head, "PROFILE_VERSION=%u\n",
+ ns->profile_version);
head->r.step++;
break;
case 1:
for ( ; head->r.index < TOMOYO_MAX_PROFILES;
head->r.index++)
- if (tomoyo_profile_ptr[head->r.index])
+ if (ns->profile_ptr[head->r.index])
break;
- if (head->r.index == TOMOYO_MAX_PROFILES)
+ if (head->r.index == TOMOYO_MAX_PROFILES) {
+ head->r.eof = true;
return;
+ }
head->r.step++;
break;
case 2:
{
+ u8 i;
const struct tomoyo_path_info *comment =
profile->comment;
+ tomoyo_print_namespace(head);
tomoyo_io_printf(head, "%u-COMMENT=", index);
tomoyo_set_string(head, comment ? comment->name : "");
tomoyo_set_lf(head);
+ tomoyo_print_namespace(head);
+ tomoyo_io_printf(head, "%u-PREFERENCE={ ", index);
+ for (i = 0; i < TOMOYO_MAX_PREF; i++)
+ tomoyo_io_printf(head, "%s=%u ",
+ tomoyo_pref_keywords[i],
+ profile->pref[i]);
+ tomoyo_set_string(head, "}\n");
head->r.step++;
}
break;
case 3:
{
+ tomoyo_print_namespace(head);
tomoyo_io_printf(head, "%u-%s", index, "CONFIG");
tomoyo_print_config(head, profile->default_config);
head->r.bit = 0;
@@ -572,15 +789,22 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
const u8 config = profile->config[i];
if (config == TOMOYO_CONFIG_USE_DEFAULT)
continue;
- tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::",
- tomoyo_mac_keywords[i]);
+ tomoyo_print_namespace(head);
+ if (i < TOMOYO_MAX_MAC_INDEX)
+ tomoyo_io_printf(head, "%u-CONFIG::%s::%s",
+ index,
+ tomoyo_category_keywords
+ [tomoyo_index2category[i]],
+ tomoyo_mac_keywords[i]);
+ else
+ tomoyo_io_printf(head, "%u-CONFIG::%s", index,
+ tomoyo_mac_keywords[i]);
tomoyo_print_config(head, config);
head->r.bit++;
break;
}
if (head->r.bit == TOMOYO_MAX_MAC_INDEX
+ TOMOYO_MAX_MAC_CATEGORY_INDEX) {
- tomoyo_print_preference(head, index);
head->r.index++;
head->r.step = 1;
}
@@ -590,6 +814,14 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)
goto next;
}
+/**
+ * tomoyo_same_manager - Check for duplicated "struct tomoyo_manager" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_head".
+ * @b: Pointer to "struct tomoyo_acl_head".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
static bool tomoyo_same_manager(const struct tomoyo_acl_head *a,
const struct tomoyo_acl_head *b)
{
@@ -611,8 +843,13 @@ static int tomoyo_update_manager_entry(const char *manager,
const bool is_delete)
{
struct tomoyo_manager e = { };
- int error;
-
+ struct tomoyo_acl_param param = {
+ /* .ns = &tomoyo_kernel_namespace, */
+ .is_delete = is_delete,
+ .list = &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER],
+ };
+ int error = is_delete ? -ENOENT : -ENOMEM;
if (tomoyo_domain_def(manager)) {
if (!tomoyo_correct_domain(manager))
return -EINVAL;
@@ -622,12 +859,11 @@ static int tomoyo_update_manager_entry(const char *manager,
return -EINVAL;
}
e.manager = tomoyo_get_name(manager);
- if (!e.manager)
- return -ENOMEM;
- error = tomoyo_update_policy(&e.head, sizeof(e), is_delete,
- &tomoyo_policy_list[TOMOYO_ID_MANAGER],
- tomoyo_same_manager);
- tomoyo_put_name(e.manager);
+ if (e.manager) {
+ error = tomoyo_update_policy(&e.head, sizeof(e), &param,
+ tomoyo_same_manager);
+ tomoyo_put_name(e.manager);
+ }
return error;
}
@@ -643,13 +879,12 @@ static int tomoyo_update_manager_entry(const char *manager,
static int tomoyo_write_manager(struct tomoyo_io_buffer *head)
{
char *data = head->write_buf;
- bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
if (!strcmp(data, "manage_by_non_root")) {
- tomoyo_manage_by_non_root = !is_delete;
+ tomoyo_manage_by_non_root = !head->w.is_delete;
return 0;
}
- return tomoyo_update_manager_entry(data, is_delete);
+ return tomoyo_update_manager_entry(data, head->w.is_delete);
}
/**
@@ -663,8 +898,8 @@ static void tomoyo_read_manager(struct tomoyo_io_buffer *head)
{
if (head->r.eof)
return;
- list_for_each_cookie(head->r.acl,
- &tomoyo_policy_list[TOMOYO_ID_MANAGER]) {
+ list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER]) {
struct tomoyo_manager *ptr =
list_entry(head->r.acl, typeof(*ptr), head.list);
if (ptr->head.is_deleted)
@@ -697,8 +932,8 @@ static bool tomoyo_manager(void)
return true;
if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
return false;
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER],
- head.list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER], head.list) {
if (!ptr->head.is_deleted && ptr->is_domain
&& !tomoyo_pathcmp(domainname, ptr->manager)) {
found = true;
@@ -710,8 +945,8 @@ static bool tomoyo_manager(void)
exe = tomoyo_get_exe();
if (!exe)
return false;
- list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER],
- head.list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.
+ policy_list[TOMOYO_ID_MANAGER], head.list) {
if (!ptr->head.is_deleted && !ptr->is_domain
&& !strcmp(exe, ptr->manager->name)) {
found = true;
@@ -731,8 +966,11 @@ static bool tomoyo_manager(void)
return found;
}
+static struct tomoyo_domain_info *tomoyo_find_domain_by_qid
+(unsigned int serial);
+
/**
- * tomoyo_select_one - Parse select command.
+ * tomoyo_select_domain - Parse select command.
*
* @head: Pointer to "struct tomoyo_io_buffer".
* @data: String to parse.
@@ -741,35 +979,34 @@ static bool tomoyo_manager(void)
*
* Caller holds tomoyo_read_lock().
*/
-static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data)
+static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
+ const char *data)
{
unsigned int pid;
struct tomoyo_domain_info *domain = NULL;
bool global_pid = false;
-
- if (!strcmp(data, "allow_execute")) {
- head->r.print_execute_only = true;
- return true;
- }
+ if (strncmp(data, "select ", 7))
+ return false;
+ data += 7;
if (sscanf(data, "pid=%u", &pid) == 1 ||
(global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
struct task_struct *p;
rcu_read_lock();
- read_lock(&tasklist_lock);
if (global_pid)
p = find_task_by_pid_ns(pid, &init_pid_ns);
else
p = find_task_by_vpid(pid);
if (p)
domain = tomoyo_real_domain(p);
- read_unlock(&tasklist_lock);
rcu_read_unlock();
} else if (!strncmp(data, "domain=", 7)) {
if (tomoyo_domain_def(data + 7))
domain = tomoyo_find_domain(data + 7);
+ } else if (sscanf(data, "Q=%u", &pid) == 1) {
+ domain = tomoyo_find_domain_by_qid(pid);
} else
return false;
- head->write_var1 = domain;
+ head->w.domain = domain;
/* Accessing read_buf is safe because head->io_sem is held. */
if (!head->read_buf)
return true; /* Do nothing if open(O_WRONLY). */
@@ -786,6 +1023,48 @@ static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data)
}
/**
+ * tomoyo_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry.
+ *
+ * @a: Pointer to "struct tomoyo_acl_info".
+ * @b: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true if @a == @b, false otherwise.
+ */
+static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a,
+ const struct tomoyo_acl_info *b)
+{
+ const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head);
+ const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head);
+ return p1->domainname == p2->domainname;
+}
+
+/**
+ * tomoyo_write_task - Update task related list.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_write_task(struct tomoyo_acl_param *param)
+{
+ int error = -EINVAL;
+ if (tomoyo_str_starts(&param->data, "manual_domain_transition ")) {
+ struct tomoyo_task_acl e = {
+ .head.type = TOMOYO_TYPE_MANUAL_TASK_ACL,
+ .domainname = tomoyo_get_domainname(param),
+ };
+ if (e.domainname)
+ error = tomoyo_update_domain(&e.head, sizeof(e), param,
+ tomoyo_same_task_acl,
+ NULL);
+ tomoyo_put_name(e.domainname);
+ }
+ return error;
+}
+
+/**
* tomoyo_delete_domain - Delete a domain.
*
* @domainname: The name of domain.
@@ -821,20 +1100,52 @@ static int tomoyo_delete_domain(char *domainname)
/**
* tomoyo_write_domain2 - Write domain policy.
*
- * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ns: Pointer to "struct tomoyo_policy_namespace".
+ * @list: Pointer to "struct list_head".
+ * @data: Policy to be interpreted.
+ * @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain,
+static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
+ struct list_head *list, char *data,
const bool is_delete)
{
- if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT))
- return tomoyo_write_mount(data, domain, is_delete);
- return tomoyo_write_file(data, domain, is_delete);
+ struct tomoyo_acl_param param = {
+ .ns = ns,
+ .list = list,
+ .data = data,
+ .is_delete = is_delete,
+ };
+ static const struct {
+ const char *keyword;
+ int (*write) (struct tomoyo_acl_param *);
+ } tomoyo_callback[5] = {
+ { "file ", tomoyo_write_file },
+ { "network inet ", tomoyo_write_inet_network },
+ { "network unix ", tomoyo_write_unix_network },
+ { "misc ", tomoyo_write_misc },
+ { "task ", tomoyo_write_task },
+ };
+ u8 i;
+
+ for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) {
+ if (!tomoyo_str_starts(&param.data,
+ tomoyo_callback[i].keyword))
+ continue;
+ return tomoyo_callback[i].write(&param);
+ }
+ return -EINVAL;
}
+/* String table for domain flags. */
+const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS] = {
+ [TOMOYO_DIF_QUOTA_WARNED] = "quota_exceeded\n",
+ [TOMOYO_DIF_TRANSITION_FAILED] = "transition_failed\n",
+};
+
/**
* tomoyo_write_domain - Write domain policy.
*
@@ -847,69 +1158,206 @@ static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain,
static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
{
char *data = head->write_buf;
- struct tomoyo_domain_info *domain = head->write_var1;
- bool is_delete = false;
- bool is_select = false;
+ struct tomoyo_policy_namespace *ns;
+ struct tomoyo_domain_info *domain = head->w.domain;
+ const bool is_delete = head->w.is_delete;
+ bool is_select = !is_delete && tomoyo_str_starts(&data, "select ");
unsigned int profile;
-
- if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE))
- is_delete = true;
- else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT))
- is_select = true;
- if (is_select && tomoyo_select_one(head, data))
- return 0;
- /* Don't allow updating policies by non manager programs. */
- if (!tomoyo_manager())
- return -EPERM;
- if (tomoyo_domain_def(data)) {
+ if (*data == '<') {
domain = NULL;
if (is_delete)
tomoyo_delete_domain(data);
else if (is_select)
domain = tomoyo_find_domain(data);
else
- domain = tomoyo_assign_domain(data, 0);
- head->write_var1 = domain;
+ domain = tomoyo_assign_domain(data, false);
+ head->w.domain = domain;
return 0;
}
if (!domain)
return -EINVAL;
-
- if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1
+ ns = domain->ns;
+ if (sscanf(data, "use_profile %u", &profile) == 1
&& profile < TOMOYO_MAX_PROFILES) {
- if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)
+ if (!tomoyo_policy_loaded || ns->profile_ptr[profile])
domain->profile = (u8) profile;
return 0;
}
- if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
- domain->ignore_global_allow_read = !is_delete;
- return 0;
- }
- if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) {
- domain->quota_warned = !is_delete;
+ if (sscanf(data, "use_group %u\n", &profile) == 1
+ && profile < TOMOYO_MAX_ACL_GROUPS) {
+ if (!is_delete)
+ domain->group = (u8) profile;
return 0;
}
- if (!strcmp(data, TOMOYO_KEYWORD_TRANSITION_FAILED)) {
- domain->transition_failed = !is_delete;
+ for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) {
+ const char *cp = tomoyo_dif[profile];
+ if (strncmp(data, cp, strlen(cp) - 1))
+ continue;
+ domain->flags[profile] = !is_delete;
return 0;
}
- return tomoyo_write_domain2(data, domain, is_delete);
+ return tomoyo_write_domain2(ns, &domain->acl_info_list, data,
+ is_delete);
}
/**
- * tomoyo_fns - Find next set bit.
+ * tomoyo_print_condition - Print condition part.
*
- * @perm: 8 bits value.
- * @bit: First bit to find.
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @cond: Pointer to "struct tomoyo_condition".
*
- * Returns next on-bit on success, 8 otherwise.
+ * Returns true on success, false otherwise.
*/
-static u8 tomoyo_fns(const u8 perm, u8 bit)
+static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
+ const struct tomoyo_condition *cond)
{
- for ( ; bit < 8; bit++)
- if (perm & (1 << bit))
+ switch (head->r.cond_step) {
+ case 0:
+ head->r.cond_index = 0;
+ head->r.cond_step++;
+ if (cond->transit) {
+ tomoyo_set_space(head);
+ tomoyo_set_string(head, cond->transit->name);
+ }
+ /* fall through */
+ case 1:
+ {
+ const u16 condc = cond->condc;
+ const struct tomoyo_condition_element *condp =
+ (typeof(condp)) (cond + 1);
+ const struct tomoyo_number_union *numbers_p =
+ (typeof(numbers_p)) (condp + condc);
+ const struct tomoyo_name_union *names_p =
+ (typeof(names_p))
+ (numbers_p + cond->numbers_count);
+ const struct tomoyo_argv *argv =
+ (typeof(argv)) (names_p + cond->names_count);
+ const struct tomoyo_envp *envp =
+ (typeof(envp)) (argv + cond->argc);
+ u16 skip;
+ for (skip = 0; skip < head->r.cond_index; skip++) {
+ const u8 left = condp->left;
+ const u8 right = condp->right;
+ condp++;
+ switch (left) {
+ case TOMOYO_ARGV_ENTRY:
+ argv++;
+ continue;
+ case TOMOYO_ENVP_ENTRY:
+ envp++;
+ continue;
+ case TOMOYO_NUMBER_UNION:
+ numbers_p++;
+ break;
+ }
+ switch (right) {
+ case TOMOYO_NAME_UNION:
+ names_p++;
+ break;
+ case TOMOYO_NUMBER_UNION:
+ numbers_p++;
+ break;
+ }
+ }
+ while (head->r.cond_index < condc) {
+ const u8 match = condp->equals;
+ const u8 left = condp->left;
+ const u8 right = condp->right;
+ if (!tomoyo_flush(head))
+ return false;
+ condp++;
+ head->r.cond_index++;
+ tomoyo_set_space(head);
+ switch (left) {
+ case TOMOYO_ARGV_ENTRY:
+ tomoyo_io_printf(head,
+ "exec.argv[%lu]%s=\"",
+ argv->index, argv->
+ is_not ? "!" : "");
+ tomoyo_set_string(head,
+ argv->value->name);
+ tomoyo_set_string(head, "\"");
+ argv++;
+ continue;
+ case TOMOYO_ENVP_ENTRY:
+ tomoyo_set_string(head,
+ "exec.envp[\"");
+ tomoyo_set_string(head,
+ envp->name->name);
+ tomoyo_io_printf(head, "\"]%s=", envp->
+ is_not ? "!" : "");
+ if (envp->value) {
+ tomoyo_set_string(head, "\"");
+ tomoyo_set_string(head, envp->
+ value->name);
+ tomoyo_set_string(head, "\"");
+ } else {
+ tomoyo_set_string(head,
+ "NULL");
+ }
+ envp++;
+ continue;
+ case TOMOYO_NUMBER_UNION:
+ tomoyo_print_number_union_nospace
+ (head, numbers_p++);
+ break;
+ default:
+ tomoyo_set_string(head,
+ tomoyo_condition_keyword[left]);
+ break;
+ }
+ tomoyo_set_string(head, match ? "=" : "!=");
+ switch (right) {
+ case TOMOYO_NAME_UNION:
+ tomoyo_print_name_union_quoted
+ (head, names_p++);
+ break;
+ case TOMOYO_NUMBER_UNION:
+ tomoyo_print_number_union_nospace
+ (head, numbers_p++);
+ break;
+ default:
+ tomoyo_set_string(head,
+ tomoyo_condition_keyword[right]);
+ break;
+ }
+ }
+ }
+ head->r.cond_step++;
+ /* fall through */
+ case 2:
+ if (!tomoyo_flush(head))
break;
- return bit;
+ head->r.cond_step++;
+ /* fall through */
+ case 3:
+ if (cond->grant_log != TOMOYO_GRANTLOG_AUTO)
+ tomoyo_io_printf(head, " grant_log=%s",
+ tomoyo_yesno(cond->grant_log ==
+ TOMOYO_GRANTLOG_YES));
+ tomoyo_set_lf(head);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * tomoyo_set_group - Print "acl_group " header keyword and category name.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @category: Category name.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_set_group(struct tomoyo_io_buffer *head,
+ const char *category)
+{
+ if (head->type == TOMOYO_EXCEPTIONPOLICY) {
+ tomoyo_print_namespace(head);
+ tomoyo_io_printf(head, "acl_group %u ",
+ head->r.acl_group_index);
+ }
+ tomoyo_set_string(head, category);
}
/**
@@ -924,99 +1372,204 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
struct tomoyo_acl_info *acl)
{
const u8 acl_type = acl->type;
+ bool first = true;
u8 bit;
+ if (head->r.print_cond_part)
+ goto print_cond_part;
if (acl->is_deleted)
return true;
- next:
- bit = head->r.bit;
if (!tomoyo_flush(head))
return false;
else if (acl_type == TOMOYO_TYPE_PATH_ACL) {
struct tomoyo_path_acl *ptr =
container_of(acl, typeof(*ptr), head);
const u16 perm = ptr->perm;
- for ( ; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
+ for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
if (!(perm & (1 << bit)))
continue;
- if (head->r.print_execute_only &&
+ if (head->r.print_transition_related_only &&
bit != TOMOYO_TYPE_EXECUTE)
continue;
- /* Print "read/write" instead of "read" and "write". */
- if ((bit == TOMOYO_TYPE_READ ||
- bit == TOMOYO_TYPE_WRITE)
- && (perm & (1 << TOMOYO_TYPE_READ_WRITE)))
- continue;
- break;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_path_keyword[bit]);
}
- if (bit >= TOMOYO_MAX_PATH_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s", tomoyo_path_keyword[bit]);
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name);
- } else if (head->r.print_execute_only) {
+ } else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) {
+ struct tomoyo_task_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ tomoyo_set_group(head, "task ");
+ tomoyo_set_string(head, "manual_domain_transition ");
+ tomoyo_set_string(head, ptr->domainname->name);
+ } else if (head->r.print_transition_related_only) {
return true;
} else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
struct tomoyo_path2_acl *ptr =
container_of(acl, typeof(*ptr), head);
- bit = tomoyo_fns(ptr->perm, bit);
- if (bit >= TOMOYO_MAX_PATH2_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s", tomoyo_path2_keyword[bit]);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_mac_keywords
+ [tomoyo_pp2mac[bit]]);
+ }
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name1);
tomoyo_print_name_union(head, &ptr->name2);
} else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) {
struct tomoyo_path_number_acl *ptr =
container_of(acl, typeof(*ptr), head);
- bit = tomoyo_fns(ptr->perm, bit);
- if (bit >= TOMOYO_MAX_PATH_NUMBER_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s",
- tomoyo_path_number_keyword[bit]);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_mac_keywords
+ [tomoyo_pn2mac[bit]]);
+ }
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name);
tomoyo_print_number_union(head, &ptr->number);
} else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) {
struct tomoyo_mkdev_acl *ptr =
container_of(acl, typeof(*ptr), head);
- bit = tomoyo_fns(ptr->perm, bit);
- if (bit >= TOMOYO_MAX_MKDEV_OPERATION)
- goto done;
- tomoyo_io_printf(head, "allow_%s", tomoyo_mkdev_keyword[bit]);
+ const u8 perm = ptr->perm;
+ for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ tomoyo_set_group(head, "file ");
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_mac_keywords
+ [tomoyo_pnnn2mac[bit]]);
+ }
+ if (first)
+ return true;
tomoyo_print_name_union(head, &ptr->name);
tomoyo_print_number_union(head, &ptr->mode);
tomoyo_print_number_union(head, &ptr->major);
tomoyo_print_number_union(head, &ptr->minor);
+ } else if (acl_type == TOMOYO_TYPE_INET_ACL) {
+ struct tomoyo_inet_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ const u8 perm = ptr->perm;
+
+ for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ tomoyo_set_group(head, "network inet ");
+ tomoyo_set_string(head, tomoyo_proto_keyword
+ [ptr->protocol]);
+ tomoyo_set_space(head);
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
+ }
+ if (first)
+ return true;
+ tomoyo_set_space(head);
+ if (ptr->address.group) {
+ tomoyo_set_string(head, "@");
+ tomoyo_set_string(head, ptr->address.group->group_name
+ ->name);
+ } else {
+ char buf[128];
+ tomoyo_print_ip(buf, sizeof(buf), &ptr->address);
+ tomoyo_io_printf(head, "%s", buf);
+ }
+ tomoyo_print_number_union(head, &ptr->port);
+ } else if (acl_type == TOMOYO_TYPE_UNIX_ACL) {
+ struct tomoyo_unix_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+ const u8 perm = ptr->perm;
+
+ for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
+ if (!(perm & (1 << bit)))
+ continue;
+ if (first) {
+ tomoyo_set_group(head, "network unix ");
+ tomoyo_set_string(head, tomoyo_proto_keyword
+ [ptr->protocol]);
+ tomoyo_set_space(head);
+ first = false;
+ } else {
+ tomoyo_set_slash(head);
+ }
+ tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
+ }
+ if (first)
+ return true;
+ tomoyo_print_name_union(head, &ptr->name);
} else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
struct tomoyo_mount_acl *ptr =
container_of(acl, typeof(*ptr), head);
- tomoyo_io_printf(head, "allow_mount");
+ tomoyo_set_group(head, "file mount");
tomoyo_print_name_union(head, &ptr->dev_name);
tomoyo_print_name_union(head, &ptr->dir_name);
tomoyo_print_name_union(head, &ptr->fs_type);
tomoyo_print_number_union(head, &ptr->flags);
+ } else if (acl_type == TOMOYO_TYPE_ENV_ACL) {
+ struct tomoyo_env_acl *ptr =
+ container_of(acl, typeof(*ptr), head);
+
+ tomoyo_set_group(head, "misc env ");
+ tomoyo_set_string(head, ptr->env->name);
+ }
+ if (acl->cond) {
+ head->r.print_cond_part = true;
+ head->r.cond_step = 0;
+ if (!tomoyo_flush(head))
+ return false;
+print_cond_part:
+ if (!tomoyo_print_condition(head, acl->cond))
+ return false;
+ head->r.print_cond_part = false;
+ } else {
+ tomoyo_set_lf(head);
}
- head->r.bit = bit + 1;
- tomoyo_io_printf(head, "\n");
- if (acl_type != TOMOYO_TYPE_MOUNT_ACL)
- goto next;
- done:
- head->r.bit = 0;
return true;
}
/**
* tomoyo_read_domain2 - Read domain policy.
*
- * @head: Pointer to "struct tomoyo_io_buffer".
- * @domain: Pointer to "struct tomoyo_domain_info".
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @list: Pointer to "struct list_head".
*
* Caller holds tomoyo_read_lock().
*
* Returns true on success, false otherwise.
*/
static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head,
- struct tomoyo_domain_info *domain)
+ struct list_head *list)
{
- list_for_each_cookie(head->r.acl, &domain->acl_info_list) {
+ list_for_each_cookie(head->r.acl, list) {
struct tomoyo_acl_info *ptr =
list_entry(head->r.acl, typeof(*ptr), list);
if (!tomoyo_print_entry(head, ptr))
@@ -1041,6 +1594,7 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
struct tomoyo_domain_info *domain =
list_entry(head->r.domain, typeof(*domain), list);
switch (head->r.step) {
+ u8 i;
case 0:
if (domain->is_deleted &&
!head->r.print_this_domain_only)
@@ -1048,22 +1602,18 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
/* Print domainname and flags. */
tomoyo_set_string(head, domain->domainname->name);
tomoyo_set_lf(head);
- tomoyo_io_printf(head,
- TOMOYO_KEYWORD_USE_PROFILE "%u\n",
+ tomoyo_io_printf(head, "use_profile %u\n",
domain->profile);
- if (domain->quota_warned)
- tomoyo_set_string(head, "quota_exceeded\n");
- if (domain->transition_failed)
- tomoyo_set_string(head, "transition_failed\n");
- if (domain->ignore_global_allow_read)
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ
- "\n");
+ tomoyo_io_printf(head, "use_group %u\n",
+ domain->group);
+ for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++)
+ if (domain->flags[i])
+ tomoyo_set_string(head, tomoyo_dif[i]);
head->r.step++;
tomoyo_set_lf(head);
/* fall through */
case 1:
- if (!tomoyo_read_domain2(head, domain))
+ if (!tomoyo_read_domain2(head, &domain->acl_info_list))
return;
head->r.step++;
if (!tomoyo_set_lf(head))
@@ -1080,73 +1630,6 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
}
/**
- * tomoyo_write_domain_profile - Assign profile for specified domain.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns 0 on success, -EINVAL otherwise.
- *
- * This is equivalent to doing
- *
- * ( echo "select " $domainname; echo "use_profile " $profile ) |
- * /usr/sbin/tomoyo-loadpolicy -d
- *
- * Caller holds tomoyo_read_lock().
- */
-static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
-{
- char *data = head->write_buf;
- char *cp = strchr(data, ' ');
- struct tomoyo_domain_info *domain;
- unsigned long profile;
-
- if (!cp)
- return -EINVAL;
- *cp = '\0';
- domain = tomoyo_find_domain(cp + 1);
- if (strict_strtoul(data, 10, &profile))
- return -EINVAL;
- if (domain && profile < TOMOYO_MAX_PROFILES
- && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded))
- domain->profile = (u8) profile;
- return 0;
-}
-
-/**
- * tomoyo_read_domain_profile - Read only domainname and profile.
- *
- * @head: Pointer to "struct tomoyo_io_buffer".
- *
- * Returns list of profile number and domainname pairs.
- *
- * This is equivalent to doing
- *
- * grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy |
- * awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
- * domainname = $0; } else if ( $1 == "use_profile" ) {
- * print $2 " " domainname; domainname = ""; } } ; '
- *
- * Caller holds tomoyo_read_lock().
- */
-static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
-{
- if (head->r.eof)
- return;
- list_for_each_cookie(head->r.domain, &tomoyo_domain_list) {
- struct tomoyo_domain_info *domain =
- list_entry(head->r.domain, typeof(*domain), list);
- if (domain->is_deleted)
- continue;
- if (!tomoyo_flush(head))
- return;
- tomoyo_io_printf(head, "%u ", domain->profile);
- tomoyo_set_string(head, domain->domainname->name);
- tomoyo_set_lf(head);
- }
- head->r.eof = true;
-}
-
-/**
* tomoyo_write_pid: Specify PID to obtain domainname.
*
* @head: Pointer to "struct tomoyo_io_buffer".
@@ -1189,14 +1672,12 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head)
global_pid = true;
pid = (unsigned int) simple_strtoul(buf, NULL, 10);
rcu_read_lock();
- read_lock(&tasklist_lock);
if (global_pid)
p = find_task_by_pid_ns(pid, &init_pid_ns);
else
p = find_task_by_vpid(pid);
if (p)
domain = tomoyo_real_domain(p);
- read_unlock(&tasklist_lock);
rcu_read_unlock();
if (!domain)
return;
@@ -1204,18 +1685,21 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head)
tomoyo_set_string(head, domain->domainname->name);
}
+/* String table for domain transition control keywords. */
static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = {
- [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE]
- = TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN,
- [TOMOYO_TRANSITION_CONTROL_INITIALIZE]
- = TOMOYO_KEYWORD_INITIALIZE_DOMAIN,
- [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = TOMOYO_KEYWORD_NO_KEEP_DOMAIN,
- [TOMOYO_TRANSITION_CONTROL_KEEP] = TOMOYO_KEYWORD_KEEP_DOMAIN
+ [TOMOYO_TRANSITION_CONTROL_NO_RESET] = "no_reset_domain ",
+ [TOMOYO_TRANSITION_CONTROL_RESET] = "reset_domain ",
+ [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ",
+ [TOMOYO_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ",
+ [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ",
+ [TOMOYO_TRANSITION_CONTROL_KEEP] = "keep_domain ",
};
+/* String table for grouping keywords. */
static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
- [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP,
- [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP
+ [TOMOYO_PATH_GROUP] = "path_group ",
+ [TOMOYO_NUMBER_GROUP] = "number_group ",
+ [TOMOYO_ADDRESS_GROUP] = "address_group ",
};
/**
@@ -1229,34 +1713,35 @@ static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
*/
static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
{
- char *data = head->write_buf;
- bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);
- u8 i;
- static const struct {
- const char *keyword;
- int (*write) (char *, const bool);
- } tomoyo_callback[4] = {
- { TOMOYO_KEYWORD_AGGREGATOR, tomoyo_write_aggregator },
- { TOMOYO_KEYWORD_FILE_PATTERN, tomoyo_write_pattern },
- { TOMOYO_KEYWORD_DENY_REWRITE, tomoyo_write_no_rewrite },
- { TOMOYO_KEYWORD_ALLOW_READ, tomoyo_write_globally_readable },
+ const bool is_delete = head->w.is_delete;
+ struct tomoyo_acl_param param = {
+ .ns = head->w.ns,
+ .is_delete = is_delete,
+ .data = head->write_buf,
};
-
+ u8 i;
+ if (tomoyo_str_starts(&param.data, "aggregator "))
+ return tomoyo_write_aggregator(&param);
for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++)
- if (tomoyo_str_starts(&data, tomoyo_transition_type[i]))
- return tomoyo_write_transition_control(data, is_delete,
- i);
- for (i = 0; i < 4; i++)
- if (tomoyo_str_starts(&data, tomoyo_callback[i].keyword))
- return tomoyo_callback[i].write(data, is_delete);
+ if (tomoyo_str_starts(&param.data, tomoyo_transition_type[i]))
+ return tomoyo_write_transition_control(&param, i);
for (i = 0; i < TOMOYO_MAX_GROUP; i++)
- if (tomoyo_str_starts(&data, tomoyo_group_name[i]))
- return tomoyo_write_group(data, is_delete, i);
+ if (tomoyo_str_starts(&param.data, tomoyo_group_name[i]))
+ return tomoyo_write_group(&param, i);
+ if (tomoyo_str_starts(&param.data, "acl_group ")) {
+ unsigned int group;
+ char *data;
+ group = simple_strtoul(param.data, &data, 10);
+ if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ')
+ return tomoyo_write_domain2
+ (head->w.ns, &head->w.ns->acl_group[group],
+ data, is_delete);
+ }
return -EINVAL;
}
/**
- * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
+ * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list.
*
* @head: Pointer to "struct tomoyo_io_buffer".
* @idx: Index number.
@@ -1267,9 +1752,12 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
*/
static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
{
- list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) {
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
+ struct list_head *list = &ns->group_list[idx];
+ list_for_each_cookie(head->r.group, list) {
struct tomoyo_group *group =
- list_entry(head->r.group, typeof(*group), list);
+ list_entry(head->r.group, typeof(*group), head.list);
list_for_each_cookie(head->r.acl, &group->member_list) {
struct tomoyo_acl_head *ptr =
list_entry(head->r.acl, typeof(*ptr), list);
@@ -1277,6 +1765,7 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
continue;
if (!tomoyo_flush(head))
return false;
+ tomoyo_print_namespace(head);
tomoyo_set_string(head, tomoyo_group_name[idx]);
tomoyo_set_string(head, group->group_name->name);
if (idx == TOMOYO_PATH_GROUP) {
@@ -1289,6 +1778,15 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
(ptr,
struct tomoyo_number_group,
head)->number);
+ } else if (idx == TOMOYO_ADDRESS_GROUP) {
+ char buffer[128];
+
+ struct tomoyo_address_group *member =
+ container_of(ptr, typeof(*member),
+ head);
+ tomoyo_print_ip(buffer, sizeof(buffer),
+ &member->address);
+ tomoyo_io_printf(head, " %s", buffer);
}
tomoyo_set_lf(head);
}
@@ -1310,7 +1808,10 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
*/
static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
{
- list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) {
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
+ struct list_head *list = &ns->policy_list[idx];
+ list_for_each_cookie(head->r.acl, list) {
struct tomoyo_acl_head *acl =
container_of(head->r.acl, typeof(*acl), list);
if (acl->is_deleted)
@@ -1322,35 +1823,23 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
{
struct tomoyo_transition_control *ptr =
container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- tomoyo_transition_type
+ tomoyo_print_namespace(head);
+ tomoyo_set_string(head, tomoyo_transition_type
[ptr->type]);
- if (ptr->program)
- tomoyo_set_string(head,
- ptr->program->name);
- if (ptr->program && ptr->domainname)
- tomoyo_set_string(head, " from ");
- if (ptr->domainname)
- tomoyo_set_string(head,
- ptr->domainname->
- name);
- }
- break;
- case TOMOYO_ID_GLOBALLY_READABLE:
- {
- struct tomoyo_readable_file *ptr =
- container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_ALLOW_READ);
- tomoyo_set_string(head, ptr->filename->name);
+ tomoyo_set_string(head, ptr->program ?
+ ptr->program->name : "any");
+ tomoyo_set_string(head, " from ");
+ tomoyo_set_string(head, ptr->domainname ?
+ ptr->domainname->name :
+ "any");
}
break;
case TOMOYO_ID_AGGREGATOR:
{
struct tomoyo_aggregator *ptr =
container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_AGGREGATOR);
+ tomoyo_print_namespace(head);
+ tomoyo_set_string(head, "aggregator ");
tomoyo_set_string(head,
ptr->original_name->name);
tomoyo_set_space(head);
@@ -1358,24 +1847,6 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
ptr->aggregated_name->name);
}
break;
- case TOMOYO_ID_PATTERN:
- {
- struct tomoyo_no_pattern *ptr =
- container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_FILE_PATTERN);
- tomoyo_set_string(head, ptr->pattern->name);
- }
- break;
- case TOMOYO_ID_NO_REWRITE:
- {
- struct tomoyo_no_rewrite *ptr =
- container_of(acl, typeof(*ptr), head);
- tomoyo_set_string(head,
- TOMOYO_KEYWORD_DENY_REWRITE);
- tomoyo_set_string(head, ptr->pattern->name);
- }
- break;
default:
continue;
}
@@ -1394,6 +1865,8 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)
*/
static void tomoyo_read_exception(struct tomoyo_io_buffer *head)
{
+ struct tomoyo_policy_namespace *ns =
+ container_of(head->r.ns, typeof(*ns), namespace_list);
if (head->r.eof)
return;
while (head->r.step < TOMOYO_MAX_POLICY &&
@@ -1406,95 +1879,41 @@ static void tomoyo_read_exception(struct tomoyo_io_buffer *head)
head->r.step++;
if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP)
return;
+ while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP
+ + TOMOYO_MAX_ACL_GROUPS) {
+ head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY
+ - TOMOYO_MAX_GROUP;
+ if (!tomoyo_read_domain2(head, &ns->acl_group
+ [head->r.acl_group_index]))
+ return;
+ head->r.step++;
+ }
head->r.eof = true;
}
-/**
- * tomoyo_print_header - Get header line of audit log.
- *
- * @r: Pointer to "struct tomoyo_request_info".
- *
- * Returns string representation.
- *
- * This function uses kmalloc(), so caller must kfree() if this function
- * didn't return NULL.
- */
-static char *tomoyo_print_header(struct tomoyo_request_info *r)
-{
- struct timeval tv;
- const pid_t gpid = task_pid_nr(current);
- static const int tomoyo_buffer_len = 4096;
- char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
- pid_t ppid;
- if (!buffer)
- return NULL;
- do_gettimeofday(&tv);
- rcu_read_lock();
- ppid = task_tgid_vnr(current->real_parent);
- rcu_read_unlock();
- snprintf(buffer, tomoyo_buffer_len - 1,
- "#timestamp=%lu profile=%u mode=%s (global-pid=%u)"
- " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u"
- " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }",
- tv.tv_sec, r->profile, tomoyo_mode[r->mode], gpid,
- task_tgid_vnr(current), ppid,
- current_uid(), current_gid(), current_euid(),
- current_egid(), current_suid(), current_sgid(),
- current_fsuid(), current_fsgid());
- return buffer;
-}
-
-/**
- * tomoyo_init_audit_log - Allocate buffer for audit logs.
- *
- * @len: Required size.
- * @r: Pointer to "struct tomoyo_request_info".
- *
- * Returns pointer to allocated memory.
- *
- * The @len is updated to add the header lines' size on success.
- *
- * This function uses kzalloc(), so caller must kfree() if this function
- * didn't return NULL.
- */
-static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r)
-{
- char *buf = NULL;
- const char *header;
- const char *domainname;
- if (!r->domain)
- r->domain = tomoyo_domain();
- domainname = r->domain->domainname->name;
- header = tomoyo_print_header(r);
- if (!header)
- return NULL;
- *len += strlen(domainname) + strlen(header) + 10;
- buf = kzalloc(*len, GFP_NOFS);
- if (buf)
- snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname);
- kfree(header);
- return buf;
-}
-
-/* Wait queue for tomoyo_query_list. */
+/* Wait queue for kernel -> userspace notification. */
static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait);
-
-/* Lock for manipulating tomoyo_query_list. */
-static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+/* Wait queue for userspace -> kernel notification. */
+static DECLARE_WAIT_QUEUE_HEAD(tomoyo_answer_wait);
/* Structure for query. */
struct tomoyo_query {
struct list_head list;
+ struct tomoyo_domain_info *domain;
char *query;
- int query_len;
+ size_t query_len;
unsigned int serial;
- int timer;
- int answer;
+ u8 timer;
+ u8 answer;
+ u8 retry;
};
/* The list for "struct tomoyo_query". */
static LIST_HEAD(tomoyo_query_list);
+/* Lock for manipulating tomoyo_query_list. */
+static DEFINE_SPINLOCK(tomoyo_query_list_lock);
+
/*
* Number of "struct file" referring /sys/kernel/security/tomoyo/query
* interface.
@@ -1502,10 +1921,82 @@ static LIST_HEAD(tomoyo_query_list);
static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
/**
+ * tomoyo_truncate - Truncate a line.
+ *
+ * @str: String to truncate.
+ *
+ * Returns length of truncated @str.
+ */
+static int tomoyo_truncate(char *str)
+{
+ char *start = str;
+ while (*(unsigned char *) str > (unsigned char) ' ')
+ str++;
+ *str = '\0';
+ return strlen(start) + 1;
+}
+
+/**
+ * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ * @header: Lines containing ACL.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
+{
+ char *buffer;
+ char *realpath = NULL;
+ char *argv0 = NULL;
+ char *symlink = NULL;
+ char *cp = strchr(header, '\n');
+ int len;
+ if (!cp)
+ return;
+ cp = strchr(cp + 1, '\n');
+ if (!cp)
+ return;
+ *cp++ = '\0';
+ len = strlen(cp) + 1;
+ /* strstr() will return NULL if ordering is wrong. */
+ if (*cp == 'f') {
+ argv0 = strstr(header, " argv[]={ \"");
+ if (argv0) {
+ argv0 += 10;
+ len += tomoyo_truncate(argv0) + 14;
+ }
+ realpath = strstr(header, " exec={ realpath=\"");
+ if (realpath) {
+ realpath += 8;
+ len += tomoyo_truncate(realpath) + 6;
+ }
+ symlink = strstr(header, " symlink.target=\"");
+ if (symlink)
+ len += tomoyo_truncate(symlink + 1) + 1;
+ }
+ buffer = kmalloc(len, GFP_NOFS);
+ if (!buffer)
+ return;
+ snprintf(buffer, len - 1, "%s", cp);
+ if (realpath)
+ tomoyo_addprintf(buffer, len, " exec.%s", realpath);
+ if (argv0)
+ tomoyo_addprintf(buffer, len, " exec.argv[0]=%s", argv0);
+ if (symlink)
+ tomoyo_addprintf(buffer, len, "%s", symlink);
+ tomoyo_normalize_line(buffer);
+ if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
+ false))
+ tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
+ kfree(buffer);
+}
+
+/**
* tomoyo_supervisor - Ask for the supervisor's decision.
*
- * @r: Pointer to "struct tomoyo_request_info".
- * @fmt: The printf()'s format string, followed by parameters.
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @fmt: The printf()'s format string, followed by parameters.
*
* Returns 0 if the supervisor decided to permit the access request which
* violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the
@@ -1515,88 +2006,80 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
{
va_list args;
- int error = -EPERM;
- int pos;
+ int error;
int len;
static unsigned int tomoyo_serial;
- struct tomoyo_query *entry = NULL;
+ struct tomoyo_query entry = { };
bool quota_exceeded = false;
- char *header;
+ va_start(args, fmt);
+ len = vsnprintf((char *) &len, 1, fmt, args) + 1;
+ va_end(args);
+ /* Write /sys/kernel/security/tomoyo/audit. */
+ va_start(args, fmt);
+ tomoyo_write_log2(r, len, fmt, args);
+ va_end(args);
+ /* Nothing more to do if granted. */
+ if (r->granted)
+ return 0;
+ if (r->mode)
+ tomoyo_update_stat(r->mode);
switch (r->mode) {
- char *buffer;
+ case TOMOYO_CONFIG_ENFORCING:
+ error = -EPERM;
+ if (atomic_read(&tomoyo_query_observers))
+ break;
+ goto out;
case TOMOYO_CONFIG_LEARNING:
- if (!tomoyo_domain_quota_is_ok(r))
- return 0;
- va_start(args, fmt);
- len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4;
- va_end(args);
- buffer = kmalloc(len, GFP_NOFS);
- if (!buffer)
- return 0;
- va_start(args, fmt);
- vsnprintf(buffer, len - 1, fmt, args);
- va_end(args);
- tomoyo_normalize_line(buffer);
- tomoyo_write_domain2(buffer, r->domain, false);
- kfree(buffer);
+ error = 0;
+ /* Check max_learning_entry parameter. */
+ if (tomoyo_domain_quota_is_ok(r))
+ break;
/* fall through */
- case TOMOYO_CONFIG_PERMISSIVE:
+ default:
return 0;
}
- if (!r->domain)
- r->domain = tomoyo_domain();
- if (!atomic_read(&tomoyo_query_observers))
- return -EPERM;
+ /* Get message. */
va_start(args, fmt);
- len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32;
+ entry.query = tomoyo_init_log(r, len, fmt, args);
va_end(args);
- header = tomoyo_init_audit_log(&len, r);
- if (!header)
- goto out;
- entry = kzalloc(sizeof(*entry), GFP_NOFS);
- if (!entry)
+ if (!entry.query)
goto out;
- entry->query = kzalloc(len, GFP_NOFS);
- if (!entry->query)
+ entry.query_len = strlen(entry.query) + 1;
+ if (!error) {
+ tomoyo_add_entry(r->domain, entry.query);
goto out;
- len = ksize(entry->query);
+ }
+ len = tomoyo_round2(entry.query_len);
+ entry.domain = r->domain;
spin_lock(&tomoyo_query_list_lock);
- if (tomoyo_quota_for_query && tomoyo_query_memory_size + len +
- sizeof(*entry) >= tomoyo_quota_for_query) {
+ if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] &&
+ tomoyo_memory_used[TOMOYO_MEMORY_QUERY] + len
+ >= tomoyo_memory_quota[TOMOYO_MEMORY_QUERY]) {
quota_exceeded = true;
} else {
- tomoyo_query_memory_size += len + sizeof(*entry);
- entry->serial = tomoyo_serial++;
+ entry.serial = tomoyo_serial++;
+ entry.retry = r->retry;
+ tomoyo_memory_used[TOMOYO_MEMORY_QUERY] += len;
+ list_add_tail(&entry.list, &tomoyo_query_list);
}
spin_unlock(&tomoyo_query_list_lock);
if (quota_exceeded)
goto out;
- pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s",
- entry->serial, r->retry, header);
- kfree(header);
- header = NULL;
- va_start(args, fmt);
- vsnprintf(entry->query + pos, len - 1 - pos, fmt, args);
- entry->query_len = strlen(entry->query) + 1;
- va_end(args);
- spin_lock(&tomoyo_query_list_lock);
- list_add_tail(&entry->list, &tomoyo_query_list);
- spin_unlock(&tomoyo_query_list_lock);
/* Give 10 seconds for supervisor's opinion. */
- for (entry->timer = 0;
- atomic_read(&tomoyo_query_observers) && entry->timer < 100;
- entry->timer++) {
- wake_up(&tomoyo_query_wait);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ / 10);
- if (entry->answer)
+ while (entry.timer < 10) {
+ wake_up_all(&tomoyo_query_wait);
+ if (wait_event_interruptible_timeout
+ (tomoyo_answer_wait, entry.answer ||
+ !atomic_read(&tomoyo_query_observers), HZ))
break;
+ else
+ entry.timer++;
}
spin_lock(&tomoyo_query_list_lock);
- list_del(&entry->list);
- tomoyo_query_memory_size -= len + sizeof(*entry);
+ list_del(&entry.list);
+ tomoyo_memory_used[TOMOYO_MEMORY_QUERY] -= len;
spin_unlock(&tomoyo_query_list_lock);
- switch (entry->answer) {
+ switch (entry.answer) {
case 3: /* Asked to retry by administrator. */
error = TOMOYO_RETRY_REQUEST;
r->retry++;
@@ -1605,22 +2088,39 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
/* Granted by administrator. */
error = 0;
break;
- case 0:
- /* Timed out. */
- break;
default:
- /* Rejected by administrator. */
+ /* Timed out or rejected by administrator. */
break;
}
- out:
- if (entry)
- kfree(entry->query);
- kfree(entry);
- kfree(header);
+out:
+ kfree(entry.query);
return error;
}
/**
+ * tomoyo_find_domain_by_qid - Get domain by query id.
+ *
+ * @serial: Query ID assigned by tomoyo_supervisor().
+ *
+ * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ */
+static struct tomoyo_domain_info *tomoyo_find_domain_by_qid
+(unsigned int serial)
+{
+ struct tomoyo_query *ptr;
+ struct tomoyo_domain_info *domain = NULL;
+ spin_lock(&tomoyo_query_list_lock);
+ list_for_each_entry(ptr, &tomoyo_query_list, list) {
+ if (ptr->serial != serial || ptr->answer)
+ continue;
+ domain = ptr->domain;
+ break;
+ }
+ spin_unlock(&tomoyo_query_list_lock);
+ return domain;
+}
+
+/**
* tomoyo_poll_query - poll() for /sys/kernel/security/tomoyo/query.
*
* @file: Pointer to "struct file".
@@ -1663,8 +2163,8 @@ static int tomoyo_poll_query(struct file *file, poll_table *wait)
static void tomoyo_read_query(struct tomoyo_io_buffer *head)
{
struct list_head *tmp;
- int pos = 0;
- int len = 0;
+ unsigned int pos = 0;
+ size_t len = 0;
char *buf;
if (head->r.w_pos)
return;
@@ -1687,7 +2187,7 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head)
head->r.query_index = 0;
return;
}
- buf = kzalloc(len, GFP_NOFS);
+ buf = kzalloc(len + 32, GFP_NOFS);
if (!buf)
return;
pos = 0;
@@ -1703,7 +2203,8 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head)
* can change, but I don't care.
*/
if (len == ptr->query_len)
- memmove(buf, ptr->query, len);
+ snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial,
+ ptr->retry, ptr->query);
break;
}
spin_unlock(&tomoyo_query_list_lock);
@@ -1760,29 +2261,107 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
static void tomoyo_read_version(struct tomoyo_io_buffer *head)
{
if (!head->r.eof) {
- tomoyo_io_printf(head, "2.3.0");
+ tomoyo_io_printf(head, "2.5.0");
head->r.eof = true;
}
}
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = {
+ [TOMOYO_STAT_POLICY_UPDATES] = "update:",
+ [TOMOYO_STAT_POLICY_LEARNING] = "violation in learning mode:",
+ [TOMOYO_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:",
+ [TOMOYO_STAT_POLICY_ENFORCING] = "violation in enforcing mode:",
+};
+
+/* String table for /sys/kernel/security/tomoyo/stat interface. */
+static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = {
+ [TOMOYO_MEMORY_POLICY] = "policy:",
+ [TOMOYO_MEMORY_AUDIT] = "audit log:",
+ [TOMOYO_MEMORY_QUERY] = "query message:",
+};
+
+/* Timestamp counter for last updated. */
+static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT];
+/* Counter for number of updates. */
+static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT];
+
+/**
+ * tomoyo_update_stat - Update statistic counters.
+ *
+ * @index: Index for policy type.
+ *
+ * Returns nothing.
+ */
+void tomoyo_update_stat(const u8 index)
+{
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ /*
+ * I don't use atomic operations because race condition is not fatal.
+ */
+ tomoyo_stat_updated[index]++;
+ tomoyo_stat_modified[index] = tv.tv_sec;
+}
+
/**
- * tomoyo_read_self_domain - Get the current process's domainname.
+ * tomoyo_read_stat - Read statistic data.
*
* @head: Pointer to "struct tomoyo_io_buffer".
*
- * Returns the current process's domainname.
+ * Returns nothing.
*/
-static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
+static void tomoyo_read_stat(struct tomoyo_io_buffer *head)
{
- if (!head->r.eof) {
- /*
- * tomoyo_domain()->domainname != NULL
- * because every process belongs to a domain and
- * the domain's name cannot be NULL.
- */
- tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name);
- head->r.eof = true;
+ u8 i;
+ unsigned int total = 0;
+ if (head->r.eof)
+ return;
+ for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) {
+ tomoyo_io_printf(head, "Policy %-30s %10u",
+ tomoyo_policy_headers[i],
+ tomoyo_stat_updated[i]);
+ if (tomoyo_stat_modified[i]) {
+ struct tomoyo_time stamp;
+ tomoyo_convert_time(tomoyo_stat_modified[i], &stamp);
+ tomoyo_io_printf(head, " (Last: %04u/%02u/%02u "
+ "%02u:%02u:%02u)",
+ stamp.year, stamp.month, stamp.day,
+ stamp.hour, stamp.min, stamp.sec);
+ }
+ tomoyo_set_lf(head);
+ }
+ for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) {
+ unsigned int used = tomoyo_memory_used[i];
+ total += used;
+ tomoyo_io_printf(head, "Memory used by %-22s %10u",
+ tomoyo_memory_headers[i], used);
+ used = tomoyo_memory_quota[i];
+ if (used)
+ tomoyo_io_printf(head, " (Quota: %10u)", used);
+ tomoyo_set_lf(head);
}
+ tomoyo_io_printf(head, "Total memory used: %10u\n",
+ total);
+ head->r.eof = true;
+}
+
+/**
+ * tomoyo_write_stat - Set memory quota.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns 0.
+ */
+static int tomoyo_write_stat(struct tomoyo_io_buffer *head)
+{
+ char *data = head->write_buf;
+ u8 i;
+ if (tomoyo_str_starts(&data, "Memory used by "))
+ for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++)
+ if (tomoyo_str_starts(&data, tomoyo_memory_headers[i]))
+ sscanf(data, "%u", &tomoyo_memory_quota[i]);
+ return 0;
}
/**
@@ -1791,9 +2370,7 @@ static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
* @type: Type of interface.
* @file: Pointer to "struct file".
*
- * Associates policy handler and returns 0 on success, -ENOMEM otherwise.
- *
- * Caller acquires tomoyo_read_lock().
+ * Returns 0 on success, negative value otherwise.
*/
int tomoyo_open_control(const u8 type, struct file *file)
{
@@ -1814,14 +2391,10 @@ int tomoyo_open_control(const u8 type, struct file *file)
head->write = tomoyo_write_exception;
head->read = tomoyo_read_exception;
break;
- case TOMOYO_SELFDOMAIN:
- /* /sys/kernel/security/tomoyo/self_domain */
- head->read = tomoyo_read_self_domain;
- break;
- case TOMOYO_DOMAIN_STATUS:
- /* /sys/kernel/security/tomoyo/.domain_status */
- head->write = tomoyo_write_domain_profile;
- head->read = tomoyo_read_domain_profile;
+ case TOMOYO_AUDIT:
+ /* /sys/kernel/security/tomoyo/audit */
+ head->poll = tomoyo_poll_log;
+ head->read = tomoyo_read_log;
break;
case TOMOYO_PROCESS_STATUS:
/* /sys/kernel/security/tomoyo/.process_status */
@@ -1833,11 +2406,11 @@ int tomoyo_open_control(const u8 type, struct file *file)
head->read = tomoyo_read_version;
head->readbuf_size = 128;
break;
- case TOMOYO_MEMINFO:
- /* /sys/kernel/security/tomoyo/meminfo */
- head->write = tomoyo_write_memory_quota;
- head->read = tomoyo_read_memory_counter;
- head->readbuf_size = 512;
+ case TOMOYO_STAT:
+ /* /sys/kernel/security/tomoyo/stat */
+ head->write = tomoyo_write_stat;
+ head->read = tomoyo_read_stat;
+ head->readbuf_size = 1024;
break;
case TOMOYO_PROFILE:
/* /sys/kernel/security/tomoyo/profile */
@@ -1887,26 +2460,16 @@ int tomoyo_open_control(const u8 type, struct file *file)
return -ENOMEM;
}
}
- if (type != TOMOYO_QUERY)
- head->reader_idx = tomoyo_read_lock();
- file->private_data = head;
- /*
- * Call the handler now if the file is
- * /sys/kernel/security/tomoyo/self_domain
- * so that the user can use
- * cat < /sys/kernel/security/tomoyo/self_domain"
- * to know the current process's domainname.
- */
- if (type == TOMOYO_SELFDOMAIN)
- tomoyo_read_control(file, NULL, 0);
/*
* If the file is /sys/kernel/security/tomoyo/query , increment the
* observer counter.
* The obserber counter is used by tomoyo_supervisor() to see if
* there is some process monitoring /sys/kernel/security/tomoyo/query.
*/
- else if (type == TOMOYO_QUERY)
+ if (type == TOMOYO_QUERY)
atomic_inc(&tomoyo_query_observers);
+ file->private_data = head;
+ tomoyo_notify_gc(head, true);
return 0;
}
@@ -1917,7 +2480,8 @@ int tomoyo_open_control(const u8 type, struct file *file)
* @wait: Pointer to "poll_table".
*
* Waits for read readiness.
- * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd .
+ * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd and
+ * /sys/kernel/security/tomoyo/audit is handled by /usr/sbin/tomoyo-auditd.
*/
int tomoyo_poll_control(struct file *file, poll_table *wait)
{
@@ -1928,21 +2492,58 @@ int tomoyo_poll_control(struct file *file, poll_table *wait)
}
/**
+ * tomoyo_set_namespace_cursor - Set namespace to read.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns nothing.
+ */
+static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head)
+{
+ struct list_head *ns;
+ if (head->type != TOMOYO_EXCEPTIONPOLICY &&
+ head->type != TOMOYO_PROFILE)
+ return;
+ /*
+ * If this is the first read, or reading previous namespace finished
+ * and has more namespaces to read, update the namespace cursor.
+ */
+ ns = head->r.ns;
+ if (!ns || (head->r.eof && ns->next != &tomoyo_namespace_list)) {
+ /* Clearing is OK because tomoyo_flush() returned true. */
+ memset(&head->r, 0, sizeof(head->r));
+ head->r.ns = ns ? ns->next : tomoyo_namespace_list.next;
+ }
+}
+
+/**
+ * tomoyo_has_more_namespace - Check for unread namespaces.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true if we have more entries to print, false otherwise.
+ */
+static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head)
+{
+ return (head->type == TOMOYO_EXCEPTIONPOLICY ||
+ head->type == TOMOYO_PROFILE) && head->r.eof &&
+ head->r.ns->next != &tomoyo_namespace_list;
+}
+
+/**
* tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.
*
- * @file: Pointer to "struct file".
+ * @head: Pointer to "struct tomoyo_io_buffer".
* @buffer: Poiner to buffer to write to.
* @buffer_len: Size of @buffer.
*
* Returns bytes read on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
*/
-int tomoyo_read_control(struct file *file, char __user *buffer,
- const int buffer_len)
+ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
+ const int buffer_len)
{
int len;
- struct tomoyo_io_buffer *head = file->private_data;
+ int idx;
if (!head->read)
return -ENOSYS;
@@ -1950,64 +2551,157 @@ int tomoyo_read_control(struct file *file, char __user *buffer,
return -EINTR;
head->read_user_buf = buffer;
head->read_user_buf_avail = buffer_len;
+ idx = tomoyo_read_lock();
if (tomoyo_flush(head))
/* Call the policy handler. */
- head->read(head);
- tomoyo_flush(head);
+ do {
+ tomoyo_set_namespace_cursor(head);
+ head->read(head);
+ } while (tomoyo_flush(head) &&
+ tomoyo_has_more_namespace(head));
+ tomoyo_read_unlock(idx);
len = head->read_user_buf - buffer;
mutex_unlock(&head->io_sem);
return len;
}
/**
+ * tomoyo_parse_policy - Parse a policy line.
+ *
+ * @head: Poiter to "struct tomoyo_io_buffer".
+ * @line: Line to parse.
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line)
+{
+ /* Delete request? */
+ head->w.is_delete = !strncmp(line, "delete ", 7);
+ if (head->w.is_delete)
+ memmove(line, line + 7, strlen(line + 7) + 1);
+ /* Selecting namespace to update. */
+ if (head->type == TOMOYO_EXCEPTIONPOLICY ||
+ head->type == TOMOYO_PROFILE) {
+ if (*line == '<') {
+ char *cp = strchr(line, ' ');
+ if (cp) {
+ *cp++ = '\0';
+ head->w.ns = tomoyo_assign_namespace(line);
+ memmove(line, cp, strlen(cp) + 1);
+ } else
+ head->w.ns = NULL;
+ } else
+ head->w.ns = &tomoyo_kernel_namespace;
+ /* Don't allow updating if namespace is invalid. */
+ if (!head->w.ns)
+ return -ENOENT;
+ }
+ /* Do the update. */
+ return head->write(head);
+}
+
+/**
* tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.
*
- * @file: Pointer to "struct file".
+ * @head: Pointer to "struct tomoyo_io_buffer".
* @buffer: Pointer to buffer to read from.
* @buffer_len: Size of @buffer.
*
* Returns @buffer_len on success, negative value otherwise.
- *
- * Caller holds tomoyo_read_lock().
*/
-int tomoyo_write_control(struct file *file, const char __user *buffer,
- const int buffer_len)
+ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
+ const char __user *buffer, const int buffer_len)
{
- struct tomoyo_io_buffer *head = file->private_data;
int error = buffer_len;
- int avail_len = buffer_len;
+ size_t avail_len = buffer_len;
char *cp0 = head->write_buf;
-
+ int idx;
if (!head->write)
return -ENOSYS;
if (!access_ok(VERIFY_READ, buffer, buffer_len))
return -EFAULT;
- /* Don't allow updating policies by non manager programs. */
- if (head->write != tomoyo_write_pid &&
- head->write != tomoyo_write_domain && !tomoyo_manager())
- return -EPERM;
if (mutex_lock_interruptible(&head->io_sem))
return -EINTR;
+ head->read_user_buf_avail = 0;
+ idx = tomoyo_read_lock();
/* Read a line and dispatch it to the policy handler. */
while (avail_len > 0) {
char c;
- if (head->write_avail >= head->writebuf_size - 1) {
- error = -ENOMEM;
- break;
- } else if (get_user(c, buffer)) {
+ if (head->w.avail >= head->writebuf_size - 1) {
+ const int len = head->writebuf_size * 2;
+ char *cp = kzalloc(len, GFP_NOFS);
+ if (!cp) {
+ error = -ENOMEM;
+ break;
+ }
+ memmove(cp, cp0, head->w.avail);
+ kfree(cp0);
+ head->write_buf = cp;
+ cp0 = cp;
+ head->writebuf_size = len;
+ }
+ if (get_user(c, buffer)) {
error = -EFAULT;
break;
}
buffer++;
avail_len--;
- cp0[head->write_avail++] = c;
+ cp0[head->w.avail++] = c;
if (c != '\n')
continue;
- cp0[head->write_avail - 1] = '\0';
- head->write_avail = 0;
+ cp0[head->w.avail - 1] = '\0';
+ head->w.avail = 0;
tomoyo_normalize_line(cp0);
- head->write(head);
+ if (!strcmp(cp0, "reset")) {
+ head->w.ns = &tomoyo_kernel_namespace;
+ head->w.domain = NULL;
+ memset(&head->r, 0, sizeof(head->r));
+ continue;
+ }
+ /* Don't allow updating policies by non manager programs. */
+ switch (head->type) {
+ case TOMOYO_PROCESS_STATUS:
+ /* This does not write anything. */
+ break;
+ case TOMOYO_DOMAINPOLICY:
+ if (tomoyo_select_domain(head, cp0))
+ continue;
+ /* fall through */
+ case TOMOYO_EXCEPTIONPOLICY:
+ if (!strcmp(cp0, "select transition_only")) {
+ head->r.print_transition_related_only = true;
+ continue;
+ }
+ /* fall through */
+ default:
+ if (!tomoyo_manager()) {
+ error = -EPERM;
+ goto out;
+ }
+ }
+ switch (tomoyo_parse_policy(head, cp0)) {
+ case -EPERM:
+ error = -EPERM;
+ goto out;
+ case 0:
+ switch (head->type) {
+ case TOMOYO_DOMAINPOLICY:
+ case TOMOYO_EXCEPTIONPOLICY:
+ case TOMOYO_STAT:
+ case TOMOYO_PROFILE:
+ case TOMOYO_MANAGER:
+ tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
}
+out:
+ tomoyo_read_unlock(idx);
mutex_unlock(&head->io_sem);
return error;
}
@@ -2015,35 +2709,20 @@ int tomoyo_write_control(struct file *file, const char __user *buffer,
/**
* tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.
*
- * @file: Pointer to "struct file".
- *
- * Releases memory and returns 0.
+ * @head: Pointer to "struct tomoyo_io_buffer".
*
- * Caller looses tomoyo_read_lock().
+ * Returns 0.
*/
-int tomoyo_close_control(struct file *file)
+int tomoyo_close_control(struct tomoyo_io_buffer *head)
{
- struct tomoyo_io_buffer *head = file->private_data;
- const bool is_write = !!head->write_buf;
-
/*
* If the file is /sys/kernel/security/tomoyo/query , decrement the
* observer counter.
*/
- if (head->type == TOMOYO_QUERY)
- atomic_dec(&tomoyo_query_observers);
- else
- tomoyo_read_unlock(head->reader_idx);
- /* Release memory used for policy I/O. */
- kfree(head->read_buf);
- head->read_buf = NULL;
- kfree(head->write_buf);
- head->write_buf = NULL;
- kfree(head);
- head = NULL;
- file->private_data = NULL;
- if (is_write)
- tomoyo_run_gc();
+ if (head->type == TOMOYO_QUERY &&
+ atomic_dec_and_test(&tomoyo_query_observers))
+ wake_up_all(&tomoyo_answer_wait);
+ tomoyo_notify_gc(head, false);
return 0;
}
@@ -2055,27 +2734,90 @@ void tomoyo_check_profile(void)
struct tomoyo_domain_info *domain;
const int idx = tomoyo_read_lock();
tomoyo_policy_loaded = true;
- /* Check all profiles currently assigned to domains are defined. */
+ printk(KERN_INFO "TOMOYO: 2.5.0\n");
list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
const u8 profile = domain->profile;
- if (tomoyo_profile_ptr[profile])
+ const struct tomoyo_policy_namespace *ns = domain->ns;
+ if (ns->profile_version != 20110903)
+ printk(KERN_ERR
+ "Profile version %u is not supported.\n",
+ ns->profile_version);
+ else if (!ns->profile_ptr[profile])
+ printk(KERN_ERR
+ "Profile %u (used by '%s') is not defined.\n",
+ profile, domain->domainname->name);
+ else
continue;
- printk(KERN_ERR "You need to define profile %u before using it.\n",
- profile);
- printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ "
+ printk(KERN_ERR
+ "Userland tools for TOMOYO 2.5 must be installed and "
+ "policy must be initialized.\n");
+ printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ "
"for more information.\n");
- panic("Profile %u (used by '%s') not defined.\n",
- profile, domain->domainname->name);
+ panic("STOP!");
}
tomoyo_read_unlock(idx);
- if (tomoyo_profile_version != 20090903) {
- printk(KERN_ERR "You need to install userland programs for "
- "TOMOYO 2.3 and initialize policy configuration.\n");
- printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ "
- "for more information.\n");
- panic("Profile version %u is not supported.\n",
- tomoyo_profile_version);
- }
- printk(KERN_INFO "TOMOYO: 2.3.0\n");
printk(KERN_INFO "Mandatory Access Control activated.\n");
}
+
+/**
+ * tomoyo_load_builtin_policy - Load built-in policy.
+ *
+ * Returns nothing.
+ */
+void __init tomoyo_load_builtin_policy(void)
+{
+ /*
+ * This include file is manually created and contains built-in policy
+ * named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy",
+ * "tomoyo_builtin_domain_policy", "tomoyo_builtin_manager",
+ * "tomoyo_builtin_stat" in the form of "static char [] __initdata".
+ */
+#include "builtin-policy.h"
+ u8 i;
+ const int idx = tomoyo_read_lock();
+ for (i = 0; i < 5; i++) {
+ struct tomoyo_io_buffer head = { };
+ char *start = "";
+ switch (i) {
+ case 0:
+ start = tomoyo_builtin_profile;
+ head.type = TOMOYO_PROFILE;
+ head.write = tomoyo_write_profile;
+ break;
+ case 1:
+ start = tomoyo_builtin_exception_policy;
+ head.type = TOMOYO_EXCEPTIONPOLICY;
+ head.write = tomoyo_write_exception;
+ break;
+ case 2:
+ start = tomoyo_builtin_domain_policy;
+ head.type = TOMOYO_DOMAINPOLICY;
+ head.write = tomoyo_write_domain;
+ break;
+ case 3:
+ start = tomoyo_builtin_manager;
+ head.type = TOMOYO_MANAGER;
+ head.write = tomoyo_write_manager;
+ break;
+ case 4:
+ start = tomoyo_builtin_stat;
+ head.type = TOMOYO_STAT;
+ head.write = tomoyo_write_stat;
+ break;
+ }
+ while (1) {
+ char *end = strchr(start, '\n');
+ if (!end)
+ break;
+ *end = '\0';
+ tomoyo_normalize_line(start);
+ head.write_buf = start;
+ tomoyo_parse_policy(&head, start);
+ start = end + 1;
+ }
+ }
+ tomoyo_read_unlock(idx);
+#ifdef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
+ tomoyo_check_profile();
+#endif
+}