diff options
| author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2010-05-17 10:06:58 +0900 | 
|---|---|---|
| committer | James Morris <jmorris@namei.org> | 2010-08-02 15:33:35 +1000 | 
| commit | 4c3e9e2ded48bcf696a45945ea7d25bb15b873fd (patch) | |
| tree | 0be326f0f90b0279ae83594e9244c3739d348df1 /security/tomoyo | |
| parent | babcd37821fba57048b30151969d28303f2a8b6b (diff) | |
| download | linux-4c3e9e2ded48bcf696a45945ea7d25bb15b873fd.tar.bz2 | |
TOMOYO: Add numeric values grouping support.
This patch adds numeric values grouping support, which is useful for grouping
numeric values such as file's UID, DAC's mode, ioctl()'s cmd number.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/tomoyo')
| -rw-r--r-- | security/tomoyo/Makefile | 2 | ||||
| -rw-r--r-- | security/tomoyo/common.c | 161 | ||||
| -rw-r--r-- | security/tomoyo/common.h | 61 | ||||
| -rw-r--r-- | security/tomoyo/file.c | 14 | ||||
| -rw-r--r-- | security/tomoyo/gc.c | 41 | ||||
| -rw-r--r-- | security/tomoyo/number_group.c | 176 | 
6 files changed, 454 insertions, 1 deletions
| diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 4fb39030f6bd..4d1b5af4f1f7 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -1 +1 @@ -obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o +obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o number_group.o diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index b5dbdc9ff73c..d82c2978b1be 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -119,6 +119,159 @@ static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head,  }  /** + * tomoyo_parse_ulong - Parse an "unsigned long" value. + * + * @result: Pointer to "unsigned long". + * @str:    Pointer to string to parse. + * + * Returns value type on success, 0 otherwise. + * + * The @src is updated to point the first character after the value + * on success. + */ +u8 tomoyo_parse_ulong(unsigned long *result, char **str) +{ +	const char *cp = *str; +	char *ep; +	int base = 10; +	if (*cp == '0') { +		char c = *(cp + 1); +		if (c == 'x' || c == 'X') { +			base = 16; +			cp += 2; +		} else if (c >= '0' && c <= '7') { +			base = 8; +			cp++; +		} +	} +	*result = simple_strtoul(cp, &ep, base); +	if (cp == ep) +		return 0; +	*str = ep; +	switch (base) { +	case 16: +		return TOMOYO_VALUE_TYPE_HEXADECIMAL; +	case 8: +		return TOMOYO_VALUE_TYPE_OCTAL; +	default: +		return TOMOYO_VALUE_TYPE_DECIMAL; +	} +} + +/** + * tomoyo_print_ulong - Print an "unsigned long" value. + * + * @buffer:     Pointer to buffer. + * @buffer_len: Size of @buffer. + * @value:      An "unsigned long" value. + * @type:       Type of @value. + * + * Returns nothing. + */ +void tomoyo_print_ulong(char *buffer, const int buffer_len, +			const unsigned long value, const u8 type) +{ +	if (type == TOMOYO_VALUE_TYPE_DECIMAL) +		snprintf(buffer, buffer_len, "%lu", value); +	else if (type == TOMOYO_VALUE_TYPE_OCTAL) +		snprintf(buffer, buffer_len, "0%lo", value); +	else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL) +		snprintf(buffer, buffer_len, "0x%lX", value); +	else +		snprintf(buffer, buffer_len, "type(%u)", type); +} + +/** + * tomoyo_print_number_union - Print a tomoyo_number_union. + * + * @head:       Pointer to "struct tomoyo_io_buffer". + * @ptr:        Pointer to "struct tomoyo_number_union". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_print_number_union(struct tomoyo_io_buffer *head, +			       const struct tomoyo_number_union *ptr) +{ +	unsigned long min; +	unsigned long max; +	u8 min_type; +	u8 max_type; +	if (!tomoyo_io_printf(head, " ")) +		return false; +	if (ptr->is_group) +		return tomoyo_io_printf(head, "@%s", +					ptr->group->group_name->name); +	min_type = ptr->min_type; +	max_type = ptr->max_type; +	min = ptr->values[0]; +	max = ptr->values[1]; +	switch (min_type) { +	case TOMOYO_VALUE_TYPE_HEXADECIMAL: +		if (!tomoyo_io_printf(head, "0x%lX", min)) +			return false; +		break; +	case TOMOYO_VALUE_TYPE_OCTAL: +		if (!tomoyo_io_printf(head, "0%lo", min)) +			return false; +		break; +	default: +		if (!tomoyo_io_printf(head, "%lu", min)) +			return false; +		break; +	} +	if (min == max && min_type == max_type) +		return true; +	switch (max_type) { +	case TOMOYO_VALUE_TYPE_HEXADECIMAL: +		return tomoyo_io_printf(head, "-0x%lX", max); +	case TOMOYO_VALUE_TYPE_OCTAL: +		return tomoyo_io_printf(head, "-0%lo", max); +	default: +		return tomoyo_io_printf(head, "-%lu", max); +	} +} + +/** + * tomoyo_parse_number_union - Parse a tomoyo_number_union. + * + * @data: Number or number range or number group. + * @ptr:  Pointer to "struct tomoyo_number_union". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) +{ +	u8 type; +	unsigned long v; +	memset(num, 0, sizeof(*num)); +	if (data[0] == '@') { +		if (!tomoyo_is_correct_path(data, 0, 0, 0)) +			return false; +		num->group = tomoyo_get_number_group(data + 1); +		num->is_group = true; +		return num->group != NULL; +	} +	type = tomoyo_parse_ulong(&v, &data); +	if (!type) +		return false; +	num->values[0] = v; +	num->min_type = type; +	if (!*data) { +		num->values[1] = v; +		num->max_type = type; +		return true; +	} +	if (*data++ != '-') +		return false; +	type = tomoyo_parse_ulong(&v, &data); +	if (!type || *data) +		return false; +	num->values[1] = v; +	num->max_type = type; +	return true; +} + +/**   * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.   *   * @str: Pointer to the string. @@ -1750,6 +1903,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)  		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); +	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NUMBER_GROUP)) +		return tomoyo_write_number_group_policy(data, is_delete);  	return -EINVAL;  } @@ -1812,6 +1967,12 @@ static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)  			head->read_var2 = NULL;  			head->read_step = 10;  		case 10: +			if (!tomoyo_read_number_group_policy(head)) +				break; +			head->read_var1 = NULL; +			head->read_var2 = NULL; +			head->read_step = 11; +		case 11:  			head->read_eof = true;  			break;  		default: diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 9f1ae5e3ba51..33d3072f9bb4 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -55,6 +55,7 @@ struct linux_binprm;  #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_NUMBER_GROUP              "number_group "  #define TOMOYO_KEYWORD_SELECT                    "select "  #define TOMOYO_KEYWORD_USE_PROFILE               "use_profile "  #define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ  "ignore_global_allow_read" @@ -62,6 +63,12 @@ struct linux_binprm;  #define TOMOYO_ROOT_NAME                         "<kernel>"  #define TOMOYO_ROOT_NAME_LEN                     (sizeof(TOMOYO_ROOT_NAME) - 1) +/* Value type definition. */ +#define TOMOYO_VALUE_TYPE_INVALID     0 +#define TOMOYO_VALUE_TYPE_DECIMAL     1 +#define TOMOYO_VALUE_TYPE_OCTAL       2 +#define TOMOYO_VALUE_TYPE_HEXADECIMAL 3 +  /* Index numbers for Access Controls. */  enum tomoyo_mac_index {  	TOMOYO_MAC_FOR_FILE,  /* domain_policy.conf */ @@ -211,6 +218,14 @@ struct tomoyo_name_union {  	u8 is_group;  }; +struct tomoyo_number_union { +	unsigned long values[2]; +	struct tomoyo_number_group *group; +	u8 min_type; +	u8 max_type; +	u8 is_group; +}; +  /* Structure for "path_group" directive. */  struct tomoyo_path_group {  	struct list_head list; @@ -219,6 +234,14 @@ struct tomoyo_path_group {  	atomic_t users;  }; +/* Structure for "number_group" directive. */ +struct tomoyo_number_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; @@ -226,6 +249,13 @@ struct tomoyo_path_group_member {  	const struct tomoyo_path_info *member_name;  }; +/* Structure for "number_group" directive. */ +struct tomoyo_number_group_member { +	struct list_head list; +	bool is_deleted; +	struct tomoyo_number_union number; +}; +  /*   * tomoyo_acl_info is a structure which is used for holding   * @@ -554,9 +584,18 @@ bool tomoyo_parse_name_union(const char *filename,  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 value matches the given number_group. */ +bool tomoyo_number_matches_group(const unsigned long min, +				 const unsigned long max, +				 const struct tomoyo_number_group *group);  /* 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); + +bool tomoyo_print_number_union(struct tomoyo_io_buffer *head, +			       const struct tomoyo_number_union *ptr); +bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num); +  /* Read "alias" entry in exception policy. */  bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head);  /* @@ -570,6 +609,8 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head);  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 "number_group" entry in exception policy. */ +bool tomoyo_read_number_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. */ @@ -614,6 +655,8 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete);  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); +/* Create "number_group" entry in exception policy. */ +int tomoyo_write_number_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. */ @@ -623,6 +666,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *  /* Allocate memory for "struct tomoyo_path_group". */  struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name); +struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name);  /* Check mode for specified functionality. */  unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, @@ -632,6 +676,8 @@ void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);  /* Run policy loader when /sbin/init starts. */  void tomoyo_load_policy(const char *filename); +void tomoyo_put_number_union(struct tomoyo_number_union *ptr); +  /* Convert binary string to ascii string. */  int tomoyo_encode(char *buffer, int buflen, const char *str); @@ -697,6 +743,7 @@ extern struct srcu_struct tomoyo_ss;  extern struct list_head tomoyo_domain_list;  extern struct list_head tomoyo_path_group_list; +extern struct list_head tomoyo_number_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; @@ -773,6 +820,12 @@ static inline void tomoyo_put_path_group(struct tomoyo_path_group *group)  		atomic_dec(&group->users);  } +static inline void tomoyo_put_number_group(struct tomoyo_number_group *group) +{ +	if (group) +		atomic_dec(&group->users); +} +  static inline struct tomoyo_domain_info *tomoyo_domain(void)  {  	return current_cred()->security; @@ -797,6 +850,14 @@ static inline bool tomoyo_is_same_name_union  		p1->is_group == p2->is_group;  } +static inline bool tomoyo_is_same_number_union +(const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2) +{ +	return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1] +		&& p1->group == p2->group && p1->min_type == p2->min_type && +		p1->max_type == p2->max_type && 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)  { diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 1c6f8238ec47..2dffe0730918 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -76,6 +76,20 @@ static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info  	return false;  } +void tomoyo_put_number_union(struct tomoyo_number_union *ptr) +{ +	if (ptr && ptr->is_group) +		tomoyo_put_number_group(ptr->group); +} + +bool tomoyo_compare_number_union(const unsigned long value, +				 const struct tomoyo_number_union *ptr) +{ +	if (ptr->is_group) +		return tomoyo_number_matches_group(value, value, ptr->group); +	return value >= ptr->values[0] && value <= ptr->values[1]; +} +  /**   * tomoyo_path2keyword - Get the name of single path operation.   * diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index b9cc71b04314..6a48197f6ce5 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -14,6 +14,8 @@  enum tomoyo_gc_id {  	TOMOYO_ID_PATH_GROUP,  	TOMOYO_ID_PATH_GROUP_MEMBER, +	TOMOYO_ID_NUMBER_GROUP, +	TOMOYO_ID_NUMBER_GROUP_MEMBER,  	TOMOYO_ID_DOMAIN_INITIALIZER,  	TOMOYO_ID_DOMAIN_KEEPER,  	TOMOYO_ID_ALIAS, @@ -162,6 +164,16 @@ static void tomoyo_del_path_group(struct tomoyo_path_group *group)  	tomoyo_put_name(group->group_name);  } +static void tomoyo_del_number_group_member(struct tomoyo_number_group_member +					   *member) +{ +} + +static void tomoyo_del_number_group(struct tomoyo_number_group *group) +{ +	tomoyo_put_name(group->group_name); +} +  static void tomoyo_collect_entry(void)  {  	if (mutex_lock_interruptible(&tomoyo_policy_lock)) @@ -329,6 +341,29 @@ static void tomoyo_collect_entry(void)  				break;  		}  	} +	{ +		struct tomoyo_number_group *group; +		list_for_each_entry_rcu(group, &tomoyo_number_group_list, list) { +			struct tomoyo_number_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_NUMBER_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_NUMBER_GROUP, group)) +				list_del_rcu(&group->list); +			else +				break; +		} +	}  	mutex_unlock(&tomoyo_policy_lock);  } @@ -376,6 +411,12 @@ static void tomoyo_kfree_entry(void)  		case TOMOYO_ID_PATH_GROUP:  			tomoyo_del_path_group(p->element);  			break; +		case TOMOYO_ID_NUMBER_GROUP_MEMBER: +			tomoyo_del_number_group_member(p->element); +			break; +		case TOMOYO_ID_NUMBER_GROUP: +			tomoyo_del_number_group(p->element); +			break;  		default:  			printk(KERN_WARNING "Unknown type\n");  			break; diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c new file mode 100644 index 000000000000..c49792e09e81 --- /dev/null +++ b/security/tomoyo/number_group.c @@ -0,0 +1,176 @@ +/* + * security/tomoyo/number_group.c + * + * Copyright (C) 2005-2009  NTT DATA CORPORATION + */ + +#include <linux/slab.h> +#include "common.h" + +/* The list for "struct tomoyo_number_group". */ +LIST_HEAD(tomoyo_number_group_list); + +/** + * tomoyo_get_number_group - Allocate memory for "struct tomoyo_number_group". + * + * @group_name: The name of number group. + * + * Returns pointer to "struct tomoyo_number_group" on success, + * NULL otherwise. + */ +struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name) +{ +	struct tomoyo_number_group *entry = NULL; +	struct tomoyo_number_group *group = NULL; +	const struct tomoyo_path_info *saved_group_name; +	int error = -ENOMEM; +	if (!tomoyo_is_correct_path(group_name, 0, 0, 0) || +	    !group_name[0]) +		return NULL; +	saved_group_name = tomoyo_get_name(group_name); +	if (!saved_group_name) +		return NULL; +	entry = kzalloc(sizeof(*entry), GFP_NOFS); +	if (mutex_lock_interruptible(&tomoyo_policy_lock)) +		goto out; +	list_for_each_entry_rcu(group, &tomoyo_number_group_list, list) { +		if (saved_group_name != group->group_name) +			continue; +		atomic_inc(&group->users); +		error = 0; +		break; +	} +	if (error && tomoyo_memory_ok(entry)) { +		INIT_LIST_HEAD(&entry->member_list); +		entry->group_name = saved_group_name; +		saved_group_name = NULL; +		atomic_set(&entry->users, 1); +		list_add_tail_rcu(&entry->list, &tomoyo_number_group_list); +		group = entry; +		entry = NULL; +		error = 0; +	} +	mutex_unlock(&tomoyo_policy_lock); + out: +	tomoyo_put_name(saved_group_name); +	kfree(entry); +	return !error ? group : NULL; +} + +/** + * tomoyo_write_number_group_policy - Write "struct tomoyo_number_group" list. + * + * @data:      String to parse. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, nagative value otherwise. + */ +int tomoyo_write_number_group_policy(char *data, const bool is_delete) +{ +	struct tomoyo_number_group *group; +	struct tomoyo_number_group_member e = { }; +	struct tomoyo_number_group_member *member; +	int error = is_delete ? -ENOENT : -ENOMEM; +	char *w[2]; +	if (!tomoyo_tokenize(data, w, sizeof(w))) +		return -EINVAL; +	if (!tomoyo_parse_number_union(w[1], &e.number)) +		return -EINVAL; +	if (e.number.is_group || e.number.values[0] > e.number.values[1]) { +		tomoyo_put_number_union(&e.number); +		return -EINVAL; +	} +	group = tomoyo_get_number_group(w[0]); +	if (!group) +		return -ENOMEM; +	if (mutex_lock_interruptible(&tomoyo_policy_lock)) +		goto out; +	list_for_each_entry_rcu(member, &group->member_list, list) { +		if (memcmp(&member->number, &e.number, sizeof(e.number))) +			continue; +		member->is_deleted = is_delete; +		error = 0; +		break; +	} +	if (!is_delete && error) { +		struct tomoyo_number_group_member *entry = +			tomoyo_commit_ok(&e, sizeof(e)); +		if (entry) { +			list_add_tail_rcu(&entry->list, &group->member_list); +			error = 0; +		} +	} +	mutex_unlock(&tomoyo_policy_lock); + out: +	tomoyo_put_number_group(group); +	return error; +} + +/** + * tomoyo_read_number_group_policy - Read "struct tomoyo_number_group" list. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head) +{ +	struct list_head *gpos; +	struct list_head *mpos; +	list_for_each_cookie(gpos, head->read_var1, &tomoyo_number_group_list) { +		struct tomoyo_number_group *group; +		const char *name; +		group = list_entry(gpos, struct tomoyo_number_group, list); +		name = group->group_name->name; +		list_for_each_cookie(mpos, head->read_var2, +				     &group->member_list) { +			int pos; +			const struct tomoyo_number_group_member *member +				= list_entry(mpos, +					     struct tomoyo_number_group_member, +					     list); +			if (member->is_deleted) +				continue; +			pos = head->read_avail; +			if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_NUMBER_GROUP +					      "%s", name) || +			    !tomoyo_print_number_union(head, &member->number) || +			    !tomoyo_io_printf(head, "\n")) { +				head->read_avail = pos; +				return false; +			} +		} +	} +	return true; +} + +/** + * tomoyo_number_matches_group - Check whether the given number matches members of the given number group. + * + * @min:   Min number. + * @max:   Max number. + * @group: Pointer to "struct tomoyo_number_group". + * + * Returns true if @min and @max partially overlaps @group, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_number_matches_group(const unsigned long min, +				 const unsigned long max, +				 const struct tomoyo_number_group *group) +{ +	struct tomoyo_number_group_member *member; +	bool matched = false; +	list_for_each_entry_rcu(member, &group->member_list, list) { +		if (member->is_deleted) +			continue; +		if (min > member->number.values[1] || +		    max < member->number.values[0]) +			continue; +		matched = true; +		break; +	} +	return matched; +} |