From 8761afd49ebff8ae04c1a7888af090177441d07d Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Fri, 8 Jul 2011 13:22:41 +0900 Subject: TOMOYO: Allow using owner/group etc. of file objects as conditions. 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 Signed-off-by: James Morris --- security/tomoyo/audit.c | 72 +++++++++++++ security/tomoyo/common.c | 45 ++++++++ security/tomoyo/common.h | 91 ++++++++++++++++ security/tomoyo/condition.c | 249 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 457 insertions(+) diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index 9381d0e..4973edd 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c @@ -10,6 +10,35 @@ #include /** + * 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. * * @r: Pointer to "struct tomoyo_request_info". @@ -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); diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 32ce170..ec02d2a 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -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. */ diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 958d433..5a0fced 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -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); diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index 0692df3..ac7ebeb 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c @@ -244,6 +244,54 @@ out: } /** + * 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. * * @r: Pointer to "struct tomoyo_request_info". @@ -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; -- cgit v1.1