TOMOYO: Allow using owner/group etc. of file objects as conditions.
authorTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Fri, 8 Jul 2011 04:22:41 +0000 (13:22 +0900)
committerJames Morris <jmorris@namei.org>
Mon, 11 Jul 2011 01:05:32 +0000 (11:05 +1000)
This patch adds support for permission checks using file object's DAC
attributes (e.g. owner/group) when checking file's pathnames. Hooks for passing
file object's pointers are in the last patch of this pathset.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
security/tomoyo/audit.c
security/tomoyo/common.c
security/tomoyo/common.h
security/tomoyo/condition.c

index 9381d0e..4973edd 100644 (file)
@@ -9,6 +9,35 @@
 #include "common.h"
 #include <linux/slab.h>
 
+/**
+ * tomoyo_filetype - Get string representation of file type.
+ *
+ * @mode: Mode value for stat().
+ *
+ * Returns file type string.
+ */
+static inline const char *tomoyo_filetype(const mode_t mode)
+{
+       switch (mode & S_IFMT) {
+       case S_IFREG:
+       case 0:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FILE];
+       case S_IFDIR:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_DIRECTORY];
+       case S_IFLNK:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SYMLINK];
+       case S_IFIFO:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FIFO];
+       case S_IFSOCK:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SOCKET];
+       case S_IFBLK:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_BLOCK_DEV];
+       case S_IFCHR:
+               return tomoyo_condition_keyword[TOMOYO_TYPE_IS_CHAR_DEV];
+       }
+       return "unknown"; /* This should not happen. */
+}
+
 /**
  * tomoyo_print_header - Get header line of audit log.
  *
@@ -23,9 +52,11 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r)
 {
        struct tomoyo_time stamp;
        const pid_t gpid = task_pid_nr(current);
+       struct tomoyo_obj_info *obj = r->obj;
        static const int tomoyo_buffer_len = 4096;
        char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
        int pos;
+       u8 i;
        if (!buffer)
                return NULL;
        {
@@ -44,6 +75,47 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r)
                       current_uid(), current_gid(), current_euid(),
                       current_egid(), current_suid(), current_sgid(),
                       current_fsuid(), current_fsgid());
+       if (!obj)
+               goto no_obj_info;
+       if (!obj->validate_done) {
+               tomoyo_get_attributes(obj);
+               obj->validate_done = true;
+       }
+       for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) {
+               struct tomoyo_mini_stat *stat;
+               unsigned int dev;
+               mode_t mode;
+               if (!obj->stat_valid[i])
+                       continue;
+               stat = &obj->stat[i];
+               dev = stat->dev;
+               mode = stat->mode;
+               if (i & 1) {
+                       pos += snprintf(buffer + pos,
+                                       tomoyo_buffer_len - 1 - pos,
+                                       " path%u.parent={ uid=%u gid=%u "
+                                       "ino=%lu perm=0%o }", (i >> 1) + 1,
+                                       stat->uid, stat->gid, (unsigned long)
+                                       stat->ino, stat->mode & S_IALLUGO);
+                       continue;
+               }
+               pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+                               " path%u={ uid=%u gid=%u ino=%lu major=%u"
+                               " minor=%u perm=0%o type=%s", (i >> 1) + 1,
+                               stat->uid, stat->gid, (unsigned long)
+                               stat->ino, MAJOR(dev), MINOR(dev),
+                               mode & S_IALLUGO, tomoyo_filetype(mode));
+               if (S_ISCHR(mode) || S_ISBLK(mode)) {
+                       dev = stat->rdev;
+                       pos += snprintf(buffer + pos,
+                                       tomoyo_buffer_len - 1 - pos,
+                                       " dev_major=%u dev_minor=%u",
+                                       MAJOR(dev), MINOR(dev));
+               }
+               pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
+                               " }");
+       }
+no_obj_info:
        if (pos < tomoyo_buffer_len - 1)
                return buffer;
        kfree(buffer);
index 32ce170..ec02d2a 100644 (file)
@@ -60,6 +60,51 @@ const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = {
        [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_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. */
index 958d433..5a0fced 100644 (file)
@@ -54,10 +54,66 @@ enum tomoyo_conditions_index {
        TOMOYO_TASK_FSGID,           /* current_fsgid() */
        TOMOYO_TASK_PID,             /* sys_getpid()   */
        TOMOYO_TASK_PPID,            /* sys_getppid()  */
+       TOMOYO_TYPE_IS_SOCKET,       /* S_IFSOCK */
+       TOMOYO_TYPE_IS_SYMLINK,      /* S_IFLNK */
+       TOMOYO_TYPE_IS_FILE,         /* S_IFREG */
+       TOMOYO_TYPE_IS_BLOCK_DEV,    /* S_IFBLK */
+       TOMOYO_TYPE_IS_DIRECTORY,    /* S_IFDIR */
+       TOMOYO_TYPE_IS_CHAR_DEV,     /* S_IFCHR */
+       TOMOYO_TYPE_IS_FIFO,         /* S_IFIFO */
+       TOMOYO_MODE_SETUID,          /* S_ISUID */
+       TOMOYO_MODE_SETGID,          /* S_ISGID */
+       TOMOYO_MODE_STICKY,          /* S_ISVTX */
+       TOMOYO_MODE_OWNER_READ,      /* S_IRUSR */
+       TOMOYO_MODE_OWNER_WRITE,     /* S_IWUSR */
+       TOMOYO_MODE_OWNER_EXECUTE,   /* S_IXUSR */
+       TOMOYO_MODE_GROUP_READ,      /* S_IRGRP */
+       TOMOYO_MODE_GROUP_WRITE,     /* S_IWGRP */
+       TOMOYO_MODE_GROUP_EXECUTE,   /* S_IXGRP */
+       TOMOYO_MODE_OTHERS_READ,     /* S_IROTH */
+       TOMOYO_MODE_OTHERS_WRITE,    /* S_IWOTH */
+       TOMOYO_MODE_OTHERS_EXECUTE,  /* S_IXOTH */
+       TOMOYO_PATH1_UID,
+       TOMOYO_PATH1_GID,
+       TOMOYO_PATH1_INO,
+       TOMOYO_PATH1_MAJOR,
+       TOMOYO_PATH1_MINOR,
+       TOMOYO_PATH1_PERM,
+       TOMOYO_PATH1_TYPE,
+       TOMOYO_PATH1_DEV_MAJOR,
+       TOMOYO_PATH1_DEV_MINOR,
+       TOMOYO_PATH2_UID,
+       TOMOYO_PATH2_GID,
+       TOMOYO_PATH2_INO,
+       TOMOYO_PATH2_MAJOR,
+       TOMOYO_PATH2_MINOR,
+       TOMOYO_PATH2_PERM,
+       TOMOYO_PATH2_TYPE,
+       TOMOYO_PATH2_DEV_MAJOR,
+       TOMOYO_PATH2_DEV_MINOR,
+       TOMOYO_PATH1_PARENT_UID,
+       TOMOYO_PATH1_PARENT_GID,
+       TOMOYO_PATH1_PARENT_INO,
+       TOMOYO_PATH1_PARENT_PERM,
+       TOMOYO_PATH2_PARENT_UID,
+       TOMOYO_PATH2_PARENT_GID,
+       TOMOYO_PATH2_PARENT_INO,
+       TOMOYO_PATH2_PARENT_PERM,
        TOMOYO_MAX_CONDITION_KEYWORD,
        TOMOYO_NUMBER_UNION,
 };
 
+
+/* Index numbers for stat(). */
+enum tomoyo_path_stat_index {
+       /* Do not change this order. */
+       TOMOYO_PATH1,
+       TOMOYO_PATH1_PARENT,
+       TOMOYO_PATH2,
+       TOMOYO_PATH2_PARENT,
+       TOMOYO_MAX_PATH_STAT
+};
+
 /* Index numbers for operation mode. */
 enum tomoyo_mode_index {
        TOMOYO_CONFIG_DISABLED,
@@ -290,6 +346,11 @@ struct tomoyo_policy_namespace;
 
 /* Structure for request info. */
 struct tomoyo_request_info {
+       /*
+        * For holding parameters specific to operations which deal files.
+        * NULL if not dealing files.
+        */
+       struct tomoyo_obj_info *obj;
        struct tomoyo_domain_info *domain;
        /* For holding parameters. */
        union {
@@ -388,6 +449,35 @@ struct tomoyo_number_group {
        struct tomoyo_number_union number;
 };
 
+/* Subset of "struct stat". Used by conditional ACL and audit logs. */
+struct tomoyo_mini_stat {
+       uid_t uid;
+       gid_t gid;
+       ino_t ino;
+       mode_t mode;
+       dev_t dev;
+       dev_t rdev;
+};
+
+/* Structure for attribute checks in addition to pathname checks. */
+struct tomoyo_obj_info {
+       /*
+        * True if tomoyo_get_attributes() was already called, false otherwise.
+        */
+       bool validate_done;
+       /* True if @stat[] is valid. */
+       bool stat_valid[TOMOYO_MAX_PATH_STAT];
+       /* First pathname. Initialized with { NULL, NULL } if no path. */
+       struct path path1;
+       /* Second pathname. Initialized with { NULL, NULL } if no path. */
+       struct path path2;
+       /*
+        * Information on @path1, @path1's parent directory, @path2, @path2's
+        * parent directory.
+        */
+       struct tomoyo_mini_stat stat[TOMOYO_MAX_PATH_STAT];
+};
+
 /* Structure for entries which follows "struct tomoyo_condition". */
 struct tomoyo_condition_element {
        /* Left hand operand. */
@@ -733,6 +823,7 @@ void tomoyo_check_profile(void);
 void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp);
 void tomoyo_del_condition(struct list_head *element);
 void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
+void tomoyo_get_attributes(struct tomoyo_obj_info *obj);
 void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns);
 void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
         __printf(2, 3);
index 0692df3..ac7ebeb 100644 (file)
@@ -243,6 +243,54 @@ out:
        return NULL;
 }
 
+/**
+ * tomoyo_get_attributes - Revalidate "struct inode".
+ *
+ * @obj: Pointer to "struct tomoyo_obj_info".
+ *
+ * Returns nothing.
+ */
+void tomoyo_get_attributes(struct tomoyo_obj_info *obj)
+{
+       u8 i;
+       struct dentry *dentry = NULL;
+
+       for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) {
+               struct inode *inode;
+               switch (i) {
+               case TOMOYO_PATH1:
+                       dentry = obj->path1.dentry;
+                       if (!dentry)
+                               continue;
+                       break;
+               case TOMOYO_PATH2:
+                       dentry = obj->path2.dentry;
+                       if (!dentry)
+                               continue;
+                       break;
+               default:
+                       if (!dentry)
+                               continue;
+                       dentry = dget_parent(dentry);
+                       break;
+               }
+               inode = dentry->d_inode;
+               if (inode) {
+                       struct tomoyo_mini_stat *stat = &obj->stat[i];
+                       stat->uid  = inode->i_uid;
+                       stat->gid  = inode->i_gid;
+                       stat->ino  = inode->i_ino;
+                       stat->mode = inode->i_mode;
+                       stat->dev  = inode->i_sb->s_dev;
+                       stat->rdev = inode->i_rdev;
+                       obj->stat_valid[i] = true;
+               }
+               if (i & 1) /* i == TOMOYO_PATH1_PARENT ||
+                             i == TOMOYO_PATH2_PARENT */
+                       dput(dentry);
+       }
+}
+
 /**
  * tomoyo_condition - Check condition part.
  *
@@ -261,16 +309,19 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
        unsigned long max_v[2] = { 0, 0 };
        const struct tomoyo_condition_element *condp;
        const struct tomoyo_number_union *numbers_p;
+       struct tomoyo_obj_info *obj;
        u16 condc;
        if (!cond)
                return true;
        condc = cond->condc;
+       obj = r->obj;
        condp = (struct tomoyo_condition_element *) (cond + 1);
        numbers_p = (const struct tomoyo_number_union *) (condp + condc);
        for (i = 0; i < condc; i++) {
                const bool match = condp->equals;
                const u8 left = condp->left;
                const u8 right = condp->right;
+               bool is_bitop[2] = { false, false };
                u8 j;
                condp++;
                /* Check numeric or bit-op expressions. */
@@ -308,14 +359,185 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
                        case TOMOYO_TASK_PPID:
                                value = tomoyo_sys_getppid();
                                break;
+                       case TOMOYO_TYPE_IS_SOCKET:
+                               value = S_IFSOCK;
+                               break;
+                       case TOMOYO_TYPE_IS_SYMLINK:
+                               value = S_IFLNK;
+                               break;
+                       case TOMOYO_TYPE_IS_FILE:
+                               value = S_IFREG;
+                               break;
+                       case TOMOYO_TYPE_IS_BLOCK_DEV:
+                               value = S_IFBLK;
+                               break;
+                       case TOMOYO_TYPE_IS_DIRECTORY:
+                               value = S_IFDIR;
+                               break;
+                       case TOMOYO_TYPE_IS_CHAR_DEV:
+                               value = S_IFCHR;
+                               break;
+                       case TOMOYO_TYPE_IS_FIFO:
+                               value = S_IFIFO;
+                               break;
+                       case TOMOYO_MODE_SETUID:
+                               value = S_ISUID;
+                               break;
+                       case TOMOYO_MODE_SETGID:
+                               value = S_ISGID;
+                               break;
+                       case TOMOYO_MODE_STICKY:
+                               value = S_ISVTX;
+                               break;
+                       case TOMOYO_MODE_OWNER_READ:
+                               value = S_IRUSR;
+                               break;
+                       case TOMOYO_MODE_OWNER_WRITE:
+                               value = S_IWUSR;
+                               break;
+                       case TOMOYO_MODE_OWNER_EXECUTE:
+                               value = S_IXUSR;
+                               break;
+                       case TOMOYO_MODE_GROUP_READ:
+                               value = S_IRGRP;
+                               break;
+                       case TOMOYO_MODE_GROUP_WRITE:
+                               value = S_IWGRP;
+                               break;
+                       case TOMOYO_MODE_GROUP_EXECUTE:
+                               value = S_IXGRP;
+                               break;
+                       case TOMOYO_MODE_OTHERS_READ:
+                               value = S_IROTH;
+                               break;
+                       case TOMOYO_MODE_OTHERS_WRITE:
+                               value = S_IWOTH;
+                               break;
+                       case TOMOYO_MODE_OTHERS_EXECUTE:
+                               value = S_IXOTH;
+                               break;
                        case TOMOYO_NUMBER_UNION:
                                /* Fetch values later. */
                                break;
                        default:
+                               if (!obj)
+                                       goto out;
+                               if (!obj->validate_done) {
+                                       tomoyo_get_attributes(obj);
+                                       obj->validate_done = true;
+                               }
+                               {
+                                       u8 stat_index;
+                                       struct tomoyo_mini_stat *stat;
+                                       switch (index) {
+                                       case TOMOYO_PATH1_UID:
+                                       case TOMOYO_PATH1_GID:
+                                       case TOMOYO_PATH1_INO:
+                                       case TOMOYO_PATH1_MAJOR:
+                                       case TOMOYO_PATH1_MINOR:
+                                       case TOMOYO_PATH1_TYPE:
+                                       case TOMOYO_PATH1_DEV_MAJOR:
+                                       case TOMOYO_PATH1_DEV_MINOR:
+                                       case TOMOYO_PATH1_PERM:
+                                               stat_index = TOMOYO_PATH1;
+                                               break;
+                                       case TOMOYO_PATH2_UID:
+                                       case TOMOYO_PATH2_GID:
+                                       case TOMOYO_PATH2_INO:
+                                       case TOMOYO_PATH2_MAJOR:
+                                       case TOMOYO_PATH2_MINOR:
+                                       case TOMOYO_PATH2_TYPE:
+                                       case TOMOYO_PATH2_DEV_MAJOR:
+                                       case TOMOYO_PATH2_DEV_MINOR:
+                                       case TOMOYO_PATH2_PERM:
+                                               stat_index = TOMOYO_PATH2;
+                                               break;
+                                       case TOMOYO_PATH1_PARENT_UID:
+                                       case TOMOYO_PATH1_PARENT_GID:
+                                       case TOMOYO_PATH1_PARENT_INO:
+                                       case TOMOYO_PATH1_PARENT_PERM:
+                                               stat_index =
+                                                       TOMOYO_PATH1_PARENT;
+                                               break;
+                                       case TOMOYO_PATH2_PARENT_UID:
+                                       case TOMOYO_PATH2_PARENT_GID:
+                                       case TOMOYO_PATH2_PARENT_INO:
+                                       case TOMOYO_PATH2_PARENT_PERM:
+                                               stat_index =
+                                                       TOMOYO_PATH2_PARENT;
+                                               break;
+                                       default:
+                                               goto out;
+                                       }
+                                       if (!obj->stat_valid[stat_index])
+                                               goto out;
+                                       stat = &obj->stat[stat_index];
+                                       switch (index) {
+                                       case TOMOYO_PATH1_UID:
+                                       case TOMOYO_PATH2_UID:
+                                       case TOMOYO_PATH1_PARENT_UID:
+                                       case TOMOYO_PATH2_PARENT_UID:
+                                               value = stat->uid;
+                                               break;
+                                       case TOMOYO_PATH1_GID:
+                                       case TOMOYO_PATH2_GID:
+                                       case TOMOYO_PATH1_PARENT_GID:
+                                       case TOMOYO_PATH2_PARENT_GID:
+                                               value = stat->gid;
+                                               break;
+                                       case TOMOYO_PATH1_INO:
+                                       case TOMOYO_PATH2_INO:
+                                       case TOMOYO_PATH1_PARENT_INO:
+                                       case TOMOYO_PATH2_PARENT_INO:
+                                               value = stat->ino;
+                                               break;
+                                       case TOMOYO_PATH1_MAJOR:
+                                       case TOMOYO_PATH2_MAJOR:
+                                               value = MAJOR(stat->dev);
+                                               break;
+                                       case TOMOYO_PATH1_MINOR:
+                                       case TOMOYO_PATH2_MINOR:
+                                               value = MINOR(stat->dev);
+                                               break;
+                                       case TOMOYO_PATH1_TYPE:
+                                       case TOMOYO_PATH2_TYPE:
+                                               value = stat->mode & S_IFMT;
+                                               break;
+                                       case TOMOYO_PATH1_DEV_MAJOR:
+                                       case TOMOYO_PATH2_DEV_MAJOR:
+                                               value = MAJOR(stat->rdev);
+                                               break;
+                                       case TOMOYO_PATH1_DEV_MINOR:
+                                       case TOMOYO_PATH2_DEV_MINOR:
+                                               value = MINOR(stat->rdev);
+                                               break;
+                                       case TOMOYO_PATH1_PERM:
+                                       case TOMOYO_PATH2_PERM:
+                                       case TOMOYO_PATH1_PARENT_PERM:
+                                       case TOMOYO_PATH2_PARENT_PERM:
+                                               value = stat->mode & S_IALLUGO;
+                                               break;
+                                       }
+                               }
                                break;
                        }
                        max_v[j] = value;
                        min_v[j] = value;
+                       switch (index) {
+                       case TOMOYO_MODE_SETUID:
+                       case TOMOYO_MODE_SETGID:
+                       case TOMOYO_MODE_STICKY:
+                       case TOMOYO_MODE_OWNER_READ:
+                       case TOMOYO_MODE_OWNER_WRITE:
+                       case TOMOYO_MODE_OWNER_EXECUTE:
+                       case TOMOYO_MODE_GROUP_READ:
+                       case TOMOYO_MODE_GROUP_WRITE:
+                       case TOMOYO_MODE_GROUP_EXECUTE:
+                       case TOMOYO_MODE_OTHERS_READ:
+                       case TOMOYO_MODE_OTHERS_WRITE:
+                       case TOMOYO_MODE_OTHERS_EXECUTE:
+                               is_bitop[j] = true;
+                       }
                }
                if (left == TOMOYO_NUMBER_UNION) {
                        /* Fetch values now. */
@@ -339,6 +561,33 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
                        }
                        goto out;
                }
+               /*
+                * Bit operation is valid only when counterpart value
+                * represents permission.
+                */
+               if (is_bitop[0] && is_bitop[1]) {
+                       goto out;
+               } else if (is_bitop[0]) {
+                       switch (right) {
+                       case TOMOYO_PATH1_PERM:
+                       case TOMOYO_PATH1_PARENT_PERM:
+                       case TOMOYO_PATH2_PERM:
+                       case TOMOYO_PATH2_PARENT_PERM:
+                               if (!(max_v[0] & max_v[1]) == !match)
+                                       continue;
+                       }
+                       goto out;
+               } else if (is_bitop[1]) {
+                       switch (left) {
+                       case TOMOYO_PATH1_PERM:
+                       case TOMOYO_PATH1_PARENT_PERM:
+                       case TOMOYO_PATH2_PERM:
+                       case TOMOYO_PATH2_PARENT_PERM:
+                               if (!(max_v[0] & max_v[1]) == !match)
+                                       continue;
+                       }
+                       goto out;
+               }
                /* Normal value range comparison. */
                if ((min_v[0] <= max_v[1] && max_v[0] >= min_v[1]) == match)
                        continue;