Commit 8761afd4 authored by Tetsuo Handa's avatar Tetsuo Handa Committed by James Morris
Browse files

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: default avatarTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent 2066a361
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -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);
+45 −0
Original line number Diff line number Diff line
@@ -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. */
+91 −0
Original line number Diff line number Diff line
@@ -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);
+249 −0
Original line number Diff line number Diff line
@@ -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;