Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7762fbff authored by Tetsuo Handa's avatar Tetsuo Handa Committed by James Morris
Browse files

TOMOYO: Add pathname grouping support.



This patch adds pathname grouping support, which is useful for grouping
pathnames that cannot be represented using /\{dir\}/ pattern.

Signed-off-by: default avatarTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent ba0c1709
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o
obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o
+87 −13
Original line number Diff line number Diff line
@@ -75,6 +75,49 @@ static int tomoyo_read_control(struct file *file, char __user *buffer,
static int tomoyo_write_control(struct file *file, const char __user *buffer,
				const int buffer_len);

/**
 * tomoyo_parse_name_union - Parse a tomoyo_name_union.
 *
 * @filename: Name or name group.
 * @ptr:      Pointer to "struct tomoyo_name_union".
 *
 * Returns true on success, false otherwise.
 */
bool tomoyo_parse_name_union(const char *filename,
			     struct tomoyo_name_union *ptr)
{
	if (!tomoyo_is_correct_path(filename, 0, 0, 0))
		return false;
	if (filename[0] == '@') {
		ptr->group = tomoyo_get_path_group(filename + 1);
		ptr->is_group = true;
		return ptr->group != NULL;
	}
	ptr->filename = tomoyo_get_name(filename);
	ptr->is_group = false;
	return ptr->filename != NULL;
}

/**
 * tomoyo_print_name_union - Print a tomoyo_name_union.
 *
 * @head: Pointer to "struct tomoyo_io_buffer".
 * @ptr:  Pointer to "struct tomoyo_name_union".
 *
 * Returns true on success, false otherwise.
 */
static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head,
				 const struct tomoyo_name_union *ptr)
{
	int pos = head->read_avail;
	if (pos && head->read_buf[pos - 1] == ' ')
		head->read_avail--;
	if (ptr->is_group)
		return tomoyo_io_printf(head, " @%s",
					ptr->group->group_name->name);
	return tomoyo_io_printf(head, " %s", ptr->filename->name);
}

/**
 * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.
 *
@@ -171,6 +214,33 @@ static void tomoyo_normalize_line(unsigned char *buffer)
	*dp = '\0';
}

/**
 * tomoyo_tokenize - Tokenize string.
 *
 * @buffer: The line to tokenize.
 * @w:      Pointer to "char *".
 * @size:   Sizeof @w .
 *
 * Returns true on success, false otherwise.
 */
bool tomoyo_tokenize(char *buffer, char *w[], size_t size)
{
	int count = size / sizeof(char *);
	int i;
	for (i = 0; i < count; i++)
		w[i] = "";
	for (i = 0; i < count; i++) {
		char *cp = strchr(buffer, ' ');
		if (cp)
			*cp = '\0';
		w[i] = buffer;
		if (!cp)
			break;
		buffer = cp + 1;
	}
	return i < count || !*buffer;
}

/**
 * tomoyo_is_correct_path - Validate a pathname.
 * @filename:     The pathname to check.
@@ -1368,21 +1438,20 @@ static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head,
{
	int pos;
	u8 bit;
	const char *filename;
	const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16);

	filename = ptr->filename->name;
	for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
		const char *msg;
		if (!(perm & (1 << bit)))
			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;
		msg = tomoyo_path2keyword(bit);
		pos = head->read_avail;
		if (!tomoyo_io_printf(head, "allow_%s %s\n", msg, filename))
		if (!tomoyo_io_printf(head, "allow_%s ",
				      tomoyo_path2keyword(bit)) ||
		    !tomoyo_print_name_union(head, &ptr->name) ||
		    !tomoyo_io_printf(head, "\n"))
			goto out;
	}
	head->read_bit = 0;
@@ -1405,21 +1474,18 @@ static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head,
				   struct tomoyo_path2_acl *ptr)
{
	int pos;
	const char *filename1;
	const char *filename2;
	const u8 perm = ptr->perm;
	u8 bit;

	filename1 = ptr->filename1->name;
	filename2 = ptr->filename2->name;
	for (bit = head->read_bit; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
		const char *msg;
		if (!(perm & (1 << bit)))
			continue;
		msg = tomoyo_path22keyword(bit);
		pos = head->read_avail;
		if (!tomoyo_io_printf(head, "allow_%s %s %s\n", msg,
				      filename1, filename2))
		if (!tomoyo_io_printf(head, "allow_%s ",
				      tomoyo_path22keyword(bit)) ||
		    !tomoyo_print_name_union(head, &ptr->name1) ||
		    !tomoyo_print_name_union(head, &ptr->name2) ||
		    !tomoyo_io_printf(head, "\n"))
			goto out;
	}
	head->read_bit = 0;
@@ -1682,6 +1748,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
		return tomoyo_write_pattern_policy(data, is_delete);
	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE))
		return tomoyo_write_no_rewrite_policy(data, is_delete);
	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP))
		return tomoyo_write_path_group_policy(data, is_delete);
	return -EINVAL;
}

@@ -1738,6 +1806,12 @@ static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
			head->read_var2 = NULL;
			head->read_step = 9;
		case 9:
			if (!tomoyo_read_path_group_policy(head))
				break;
			head->read_var1 = NULL;
			head->read_var2 = NULL;
			head->read_step = 10;
		case 10:
			head->read_eof = true;
			break;
		default:
+85 −9
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ struct linux_binprm;
#define TOMOYO_KEYWORD_KEEP_DOMAIN               "keep_domain "
#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN      "no_initialize_domain "
#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN            "no_keep_domain "
#define TOMOYO_KEYWORD_PATH_GROUP                "path_group "
#define TOMOYO_KEYWORD_SELECT                    "select "
#define TOMOYO_KEYWORD_USE_PROFILE               "use_profile "
#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ  "ignore_global_allow_read"
@@ -204,6 +205,27 @@ struct tomoyo_path_info_with_data {
	char barrier2[16]; /* Safeguard for overrun. */
};

struct tomoyo_name_union {
	const struct tomoyo_path_info *filename;
	struct tomoyo_path_group *group;
	u8 is_group;
};

/* Structure for "path_group" directive. */
struct tomoyo_path_group {
	struct list_head list;
	const struct tomoyo_path_info *group_name;
	struct list_head member_list;
	atomic_t users;
};

/* Structure for "path_group" directive. */
struct tomoyo_path_group_member {
	struct list_head list;
	bool is_deleted;
	const struct tomoyo_path_info *member_name;
};

/*
 * tomoyo_acl_info is a structure which is used for holding
 *
@@ -274,7 +296,7 @@ struct tomoyo_domain_info {
 *
 *  (1) "head" which is a "struct tomoyo_acl_info".
 *  (2) "perm" which is a bitmask of permitted operations.
 *  (3) "filename" is the pathname.
 *  (3) "name" is the pathname.
 *
 * Directives held by this structure are "allow_read/write", "allow_execute",
 * "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir",
@@ -287,8 +309,7 @@ struct tomoyo_path_acl {
	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */
	u8 perm_high;
	u16 perm;
	/* Pointer to single pathname. */
	const struct tomoyo_path_info *filename;
	struct tomoyo_name_union name;
};

/*
@@ -298,8 +319,8 @@ struct tomoyo_path_acl {
 *
 *  (1) "head" which is a "struct tomoyo_acl_info".
 *  (2) "perm" which is a bitmask of permitted operations.
 *  (3) "filename1" is the source/old pathname.
 *  (4) "filename2" is the destination/new pathname.
 *  (3) "name1" is the source/old pathname.
 *  (4) "name2" is the destination/new pathname.
 *
 * Directives held by this structure are "allow_rename", "allow_link" and
 * "allow_pivot_root".
@@ -307,10 +328,8 @@ struct tomoyo_path_acl {
struct tomoyo_path2_acl {
	struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */
	u8 perm;
	/* Pointer to single pathname. */
	const struct tomoyo_path_info *filename1;
	/* Pointer to single pathname. */
	const struct tomoyo_path_info *filename2;
	struct tomoyo_name_union name1;
	struct tomoyo_name_union name2;
};

/*
@@ -514,6 +533,9 @@ struct tomoyo_policy_manager_entry {

/********** Function prototypes. **********/

/* Check whether the given name matches the given name_union. */
bool tomoyo_compare_name_union(const struct tomoyo_path_info *name,
			       const struct tomoyo_name_union *ptr);
/* Check whether the domain has too many ACL entries to hold. */
bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain);
/* Transactional sprintf() for policy dump. */
@@ -526,6 +548,12 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
			    const s8 pattern_type, const s8 end_type);
/* Check whether the token can be a domainname. */
bool tomoyo_is_domain_def(const unsigned char *buffer);
bool tomoyo_parse_name_union(const char *filename,
			     struct tomoyo_name_union *ptr);
/* Check whether the given filename matches the given path_group. */
bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
			       const struct tomoyo_path_group *group,
			       const bool may_use_pattern);
/* Check whether the given filename matches the given pattern. */
bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
				 const struct tomoyo_path_info *pattern);
@@ -540,10 +568,14 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head);
bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head);
/* Read "file_pattern" entry in exception policy. */
bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head);
/* Read "path_group" entry in exception policy. */
bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head);
/* Read "allow_read" entry in exception policy. */
bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head);
/* Read "deny_rewrite" entry in exception policy. */
bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head);
/* Tokenize a line. */
bool tomoyo_tokenize(char *buffer, char *w[], size_t size);
/* Write domain policy violation warning message to console? */
bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
/* Convert double path operation to operation name. */
@@ -580,12 +612,18 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete);
int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete);
/* Create "file_pattern" entry in exception policy. */
int tomoyo_write_pattern_policy(char *data, const bool is_delete);
/* Create "path_group" entry in exception policy. */
int tomoyo_write_path_group_policy(char *data, const bool is_delete);
/* Find a domain by the given name. */
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
/* Find or create a domain by the given name. */
struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
							    domainname,
							    const u8 profile);

/* Allocate memory for "struct tomoyo_path_group". */
struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name);

/* Check mode for specified functionality. */
unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
				const u8 index);
@@ -642,6 +680,9 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1,
int tomoyo_check_rewrite_permission(struct file *filp);
int tomoyo_find_next_domain(struct linux_binprm *bprm);

/* Drop refcount on tomoyo_name_union. */
void tomoyo_put_name_union(struct tomoyo_name_union *ptr);

/* Run garbage collector. */
void tomoyo_run_gc(void);

@@ -655,6 +696,7 @@ extern struct srcu_struct tomoyo_ss;
/* The list for "struct tomoyo_domain_info". */
extern struct list_head tomoyo_domain_list;

extern struct list_head tomoyo_path_group_list;
extern struct list_head tomoyo_domain_initializer_list;
extern struct list_head tomoyo_domain_keeper_list;
extern struct list_head tomoyo_alias_list;
@@ -725,6 +767,12 @@ static inline void tomoyo_put_name(const struct tomoyo_path_info *name)
	}
}

static inline void tomoyo_put_path_group(struct tomoyo_path_group *group)
{
	if (group)
		atomic_dec(&group->users);
}

static inline struct tomoyo_domain_info *tomoyo_domain(void)
{
	return current_cred()->security;
@@ -736,6 +784,34 @@ static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
	return task_cred_xxx(task, security);
}

static inline bool tomoyo_is_same_acl_head(const struct tomoyo_acl_info *p1,
					   const struct tomoyo_acl_info *p2)
{
	return p1->type == p2->type;
}

static inline bool tomoyo_is_same_name_union
(const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2)
{
	return p1->filename == p2->filename && p1->group == p2->group &&
		p1->is_group == p2->is_group;
}

static inline bool tomoyo_is_same_path_acl(const struct tomoyo_path_acl *p1,
					   const struct tomoyo_path_acl *p2)
{
	return tomoyo_is_same_acl_head(&p1->head, &p2->head) &&
		tomoyo_is_same_name_union(&p1->name, &p2->name);
}

static inline bool tomoyo_is_same_path2_acl(const struct tomoyo_path2_acl *p1,
					    const struct tomoyo_path2_acl *p2)
{
	return tomoyo_is_same_acl_head(&p1->head, &p2->head) &&
		tomoyo_is_same_name_union(&p1->name1, &p2->name1) &&
		tomoyo_is_same_name_union(&p1->name2, &p2->name2);
}

static inline bool tomoyo_is_same_domain_initializer_entry
(const struct tomoyo_domain_initializer_entry *p1,
 const struct tomoyo_domain_initializer_entry *p2)
+43 −28
Original line number Diff line number Diff line
@@ -45,6 +45,37 @@ static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
	[TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
};

void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
{
	if (!ptr)
		return;
	if (ptr->is_group)
		tomoyo_put_path_group(ptr->group);
	else
		tomoyo_put_name(ptr->filename);
}

bool tomoyo_compare_name_union(const struct tomoyo_path_info *name,
			       const struct tomoyo_name_union *ptr)
{
	if (ptr->is_group)
		return tomoyo_path_matches_group(name, ptr->group, 1);
	return tomoyo_path_matches_pattern(name, ptr->filename);
}

static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info
					      *name,
					      const struct tomoyo_name_union
					      *ptr, const bool may_use_pattern)
{
	if (ptr->is_group)
		return tomoyo_path_matches_group(name, ptr->group,
						 may_use_pattern);
	if (may_use_pattern || !ptr->filename->is_patterned)
		return tomoyo_path_matches_pattern(name, ptr->filename);
	return false;
}

/**
 * tomoyo_path2keyword - Get the name of single path operation.
 *
@@ -637,13 +668,9 @@ static int tomoyo_path_acl2(const struct tomoyo_domain_info *domain,
			if (!(acl->perm_high & (perm >> 16)))
				continue;
		}
		if (may_use_pattern || !acl->filename->is_patterned) {
			if (!tomoyo_path_matches_pattern(filename,
							 acl->filename))
		if (!tomoyo_compare_name_union_pattern(filename, &acl->name,
                                                       may_use_pattern))
			continue;
		} else {
			continue;
		}
		error = 0;
		break;
	}
@@ -817,19 +844,14 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename,
		e.perm |= tomoyo_rw_mask;
	if (!domain)
		return -EINVAL;
	if (!tomoyo_is_correct_path(filename, 0, 0, 0))
	if (!tomoyo_parse_name_union(filename, &e.name))
		return -EINVAL;
	e.filename = tomoyo_get_name(filename);
	if (!e.filename)
		return -ENOMEM;
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
		struct tomoyo_path_acl *acl =
			container_of(ptr, struct tomoyo_path_acl, head);
		if (ptr->type != TOMOYO_TYPE_PATH_ACL)
			continue;
		if (acl->filename != e.filename)
		if (!tomoyo_is_same_path_acl(acl, &e))
			continue;
		if (is_delete) {
			if (perm <= 0xFFFF)
@@ -864,7 +886,7 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename,
	}
	mutex_unlock(&tomoyo_policy_lock);
 out:
	tomoyo_put_name(e.filename);
	tomoyo_put_name_union(&e.name);
	return error;
}

@@ -896,22 +918,15 @@ static int tomoyo_update_path2_acl(const u8 type, const char *filename1,

	if (!domain)
		return -EINVAL;
	if (!tomoyo_is_correct_path(filename1, 0, 0, 0) ||
	    !tomoyo_is_correct_path(filename2, 0, 0, 0))
		return -EINVAL;
	e.filename1 = tomoyo_get_name(filename1);
	e.filename2 = tomoyo_get_name(filename2);
	if (!e.filename1 || !e.filename2)
	if (!tomoyo_parse_name_union(filename1, &e.name1) ||
	    !tomoyo_parse_name_union(filename2, &e.name2))
		goto out;
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
		goto out;
	list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
		struct tomoyo_path2_acl *acl =
			container_of(ptr, struct tomoyo_path2_acl, head);
		if (ptr->type != TOMOYO_TYPE_PATH2_ACL)
			continue;
		if (acl->filename1 != e.filename1 ||
		    acl->filename2 != e.filename2)
		if (!tomoyo_is_same_path2_acl(acl, &e))
			continue;
		if (is_delete)
			acl->perm &= ~perm;
@@ -931,8 +946,8 @@ static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
	}
	mutex_unlock(&tomoyo_policy_lock);
 out:
	tomoyo_put_name(e.filename1);
	tomoyo_put_name(e.filename2);
	tomoyo_put_name_union(&e.name1);
	tomoyo_put_name_union(&e.name2);
	return error;
}

@@ -985,9 +1000,9 @@ static int tomoyo_path2_acl(const struct tomoyo_domain_info *domain,
		acl = container_of(ptr, struct tomoyo_path2_acl, head);
		if (!(acl->perm & perm))
			continue;
		if (!tomoyo_path_matches_pattern(filename1, acl->filename1))
		if (!tomoyo_compare_name_union(filename1, &acl->name1))
			continue;
		if (!tomoyo_path_matches_pattern(filename2, acl->filename2))
		if (!tomoyo_compare_name_union(filename2, &acl->name2))
			continue;
		error = 0;
		break;
+45 −3
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@
#include <linux/slab.h>

enum tomoyo_gc_id {
	TOMOYO_ID_PATH_GROUP,
	TOMOYO_ID_PATH_GROUP_MEMBER,
	TOMOYO_ID_DOMAIN_INITIALIZER,
	TOMOYO_ID_DOMAIN_KEEPER,
	TOMOYO_ID_ALIAS,
@@ -91,15 +93,15 @@ static void tomoyo_del_acl(struct tomoyo_acl_info *acl)
		{
			struct tomoyo_path_acl *entry
				= container_of(acl, typeof(*entry), head);
			tomoyo_put_name(entry->filename);
			tomoyo_put_name_union(&entry->name);
		}
		break;
	case TOMOYO_TYPE_PATH2_ACL:
		{
			struct tomoyo_path2_acl *entry
				= container_of(acl, typeof(*entry), head);
			tomoyo_put_name(entry->filename1);
			tomoyo_put_name(entry->filename2);
			tomoyo_put_name_union(&entry->name1);
			tomoyo_put_name_union(&entry->name2);
		}
		break;
	default:
@@ -149,6 +151,17 @@ static void tomoyo_del_name(const struct tomoyo_name_entry *ptr)
{
}

static void tomoyo_del_path_group_member(struct tomoyo_path_group_member
					 *member)
{
	tomoyo_put_name(member->member_name);
}

static void tomoyo_del_path_group(struct tomoyo_path_group *group)
{
	tomoyo_put_name(group->group_name);
}

static void tomoyo_collect_entry(void)
{
	if (mutex_lock_interruptible(&tomoyo_policy_lock))
@@ -293,6 +306,29 @@ static void tomoyo_collect_entry(void)
			}
		}
	}
	{
		struct tomoyo_path_group *group;
		list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) {
			struct tomoyo_path_group_member *member;
			list_for_each_entry_rcu(member, &group->member_list,
						list) {
				if (!member->is_deleted)
					continue;
				if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP_MEMBER,
						     member))
					list_del_rcu(&member->list);
				else
					break;
			}
			if (!list_empty(&group->member_list) ||
			    atomic_read(&group->users))
				continue;
			if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP, group))
				list_del_rcu(&group->list);
			else
				break;
		}
	}
	mutex_unlock(&tomoyo_policy_lock);
}

@@ -334,6 +370,12 @@ static void tomoyo_kfree_entry(void)
			if (!tomoyo_del_domain(p->element))
				continue;
			break;
		case TOMOYO_ID_PATH_GROUP_MEMBER:
			tomoyo_del_path_group_member(p->element);
			break;
		case TOMOYO_ID_PATH_GROUP:
			tomoyo_del_path_group(p->element);
			break;
		default:
			printk(KERN_WARNING "Unknown type\n");
			break;
Loading