]> git.karo-electronics.de Git - linux-beck.git/blobdiff - security/tomoyo/common.c
TOMOYO: Allow using executable's realpath and symlink's target as conditions.
[linux-beck.git] / security / tomoyo / common.c
index 7bc0d1d958675282bb1a38a9a9861a666dd62926..69d6b59f593709c57ca966292ea6be9e1b5b217f 100644 (file)
@@ -48,6 +48,67 @@ const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
        [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
 };
 
+/* 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_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 PREFERENCE keyword. */
 static const char * const tomoyo_pref_keywords[TOMOYO_MAX_PREF] = {
        [TOMOYO_PREF_MAX_AUDIT_LOG]      = "max_audit_log",
@@ -294,15 +355,37 @@ 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)
+{
+       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)
 {
-       tomoyo_set_space(head);
        if (ptr->group) {
                tomoyo_set_string(head, "@");
                tomoyo_set_string(head, ptr->group->group_name->name);
@@ -325,8 +408,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)
@@ -339,6 +422,21 @@ 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.
  *
@@ -1003,6 +1101,101 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head)
                                    is_delete);
 }
 
+/**
+ * tomoyo_print_condition - Print condition part.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @cond: Pointer to "struct tomoyo_condition".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
+                                  const struct tomoyo_condition *cond)
+{
+       switch (head->r.cond_step) {
+       case 0:
+               head->r.cond_index = 0;
+               head->r.cond_step++;
+               /* 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);
+                       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_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_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;
+               head->r.cond_step++;
+               /* fall through */
+       case 3:
+               tomoyo_set_lf(head);
+               return true;
+       }
+       return false;
+}
+
 /**
  * tomoyo_set_group - Print "acl_group " header keyword and category name.
  *
@@ -1037,6 +1230,8 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
        bool first = true;
        u8 bit;
 
+       if (head->r.print_cond_part)
+               goto print_cond_part;
        if (acl->is_deleted)
                return true;
        if (!tomoyo_flush(head))
@@ -1135,7 +1330,18 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
                tomoyo_print_name_union(head, &ptr->fs_type);
                tomoyo_print_number_union(head, &ptr->flags);
        }
-       tomoyo_set_lf(head);
+       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);
+       }
        return true;
 }
 
@@ -1212,73 +1418,6 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)
        head->r.eof = true;
 }
 
-/**
- * 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 && (!tomoyo_policy_loaded ||
-                      head->w.ns->profile_ptr[(u8) profile]))
-               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.
  *
@@ -1559,6 +1698,22 @@ static DEFINE_SPINLOCK(tomoyo_query_list_lock);
  */
 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.
  *
@@ -1570,6 +1725,8 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
 static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
 {
        char *buffer;
+       char *realpath = NULL;
+       char *symlink = NULL;
        char *cp = strchr(header, '\n');
        int len;
        if (!cp)
@@ -1579,10 +1736,25 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
                return;
        *cp++ = '\0';
        len = strlen(cp) + 1;
+       /* strstr() will return NULL if ordering is wrong. */
+       if (*cp == 'f') {
+               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 (symlink)
+               tomoyo_addprintf(buffer, len, "%s", symlink);
        tomoyo_normalize_line(buffer);
        if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
                                  false))
@@ -1994,11 +2166,6 @@ int tomoyo_open_control(const u8 type, struct file *file)
                /* /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;
-               break;
        case TOMOYO_PROCESS_STATUS:
                /* /sys/kernel/security/tomoyo/.process_status */
                head->write = tomoyo_write_pid;
@@ -2291,7 +2458,6 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
                        switch (head->type) {
                        case TOMOYO_DOMAINPOLICY:
                        case TOMOYO_EXCEPTIONPOLICY:
-                       case TOMOYO_DOMAIN_STATUS:
                        case TOMOYO_STAT:
                        case TOMOYO_PROFILE:
                        case TOMOYO_MANAGER:
@@ -2361,3 +2527,66 @@ void tomoyo_check_profile(void)
        tomoyo_read_unlock(idx);
        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
+}