This patch contains code for handling IPv4/IPv6 address grouping. To be able to specify IP addresses more flexibly, I want to add IP address grouping support into TOMOYO 2.3.0. address_group TRUSTED_CLIENTS 192.168.1.1 address_group TRUSTED_CLIENTS 127.0.0.1 address_group TRUSTED_CLIENTS 0:0:0:0:0:0:0:1 address_group UNTRUSTED_CLIENTS 192.168.2.1-192.168.2.255 + allow_network TCP accept @TRUSTED_CLIENTS 1024-65535 ; set task.state[0]=1 allow_network TCP accept @UNTRUSTED_CLIENTS 1024-65535 ; set task.state[0]=0 Signed-off-by: Tetsuo Handa --- security/tomoyo/address_group.c | 270 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) --- /dev/null +++ security-testing-2.6/security/tomoyo/address_group.c @@ -0,0 +1,270 @@ +/* + * security/tomoyo/address_group.c + * + * Copyright (C) 2005-2009 NTT DATA CORPORATION + */ +#include "internal.h" + +/* The list for "struct tomoyo_address_group". */ +LIST_HEAD(tomoyo_address_group_list); + +/** + * tomoyo_get_address_group - Allocate memory for "struct tomoyo_address_group". + * + * @group_name: The name of address group. + * + * Returns pointer to "struct tomoyo_address_group" on success, + * NULL otherwise. + */ +struct tomoyo_address_group *tomoyo_get_address_group(const char *group_name) +{ + struct tomoyo_address_group *entry = NULL; + struct tomoyo_address_group *group; + 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_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(group, &tomoyo_address_group_list, list) { + if (saved_group_name != group->group_name) + continue; + atomic_inc(&group->users); + error = 0; + break; + } + if (error && tomoyo_memory_ok(entry, sizeof(*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_address_group_list); + group = entry; + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + tomoyo_put_name(saved_group_name); + kfree(entry); + return !error ? group : NULL; +} + +/** + * tomoyo_put_address_group - Delete memory for "struct tomoyo_address_group". + * + * @group: Pointer to "struct tomoyo_address_group". + */ +void tomoyo_put_address_group(struct tomoyo_address_group *group) +{ + struct tomoyo_address_group_member *member; + struct tomoyo_address_group_member *next_member; + LIST_HEAD(q); + bool can_delete_group = false; + if (!group) + return; + mutex_lock(&tomoyo_policy_lock); + if (atomic_dec_and_test(&group->users)) { + list_for_each_entry_safe(member, next_member, + &group->member_list, list) { + if (!member->is_deleted) + break; + list_del(&member->list); + list_add(&member->list, &q); + } + if (list_empty(&group->member_list)) { + list_del(&group->list); + can_delete_group = true; + } + } + mutex_unlock(&tomoyo_policy_lock); + list_for_each_entry_safe(member, next_member, &q, list) { + list_del(&member->list); + if (member->is_ipv6) { + tomoyo_put_ipv6_address(member->min.ipv6); + tomoyo_put_ipv6_address(member->max.ipv6); + } + tomoyo_memory_free(member, sizeof(*member)); + } + if (can_delete_group) { + tomoyo_put_name(group->group_name); + tomoyo_memory_free(group, sizeof(*group)); + } +} + +/** + * tomoyo_write_address_group_policy - Write "struct tomoyo_address_group" list. + * + * @data: String to parse. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_address_group_policy(char *data, const bool is_delete) +{ + struct tomoyo_address_group *group; + struct tomoyo_address_group_member *entry = NULL; + struct tomoyo_address_group_member *member; + struct tomoyo_address_group_member e = { }; + int error = is_delete ? -ENOENT : -ENOMEM; + u16 min_address[8]; + u16 max_address[8]; + char *w[2]; + if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) + return -EINVAL; + group = tomoyo_get_address_group(w[0]); + if (!group) + return -ENOMEM; + switch (tomoyo_parse_ip_address(w[1], min_address, max_address)) { + case 2: + e.is_ipv6 = true; + e.min.ipv6 = tomoyo_get_ipv6_address((struct in6_addr *) + min_address); + e.max.ipv6 = tomoyo_get_ipv6_address((struct in6_addr *) + max_address); + if (!e.min.ipv6 || !e.max.ipv6) + goto out; + break; + case 1: + e.min.ipv4 = ntohl(*(u32 *) min_address); + e.max.ipv4 = ntohl(*(u32 *) max_address); + break; + default: + goto out; + } + if (!is_delete) + entry = kmalloc(sizeof(e), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry_rcu(member, &group->member_list, list) { + if (tomoyo_memcmp(member, &e, offsetof(typeof(e), is_ipv6), + sizeof(e))) + continue; + member->is_deleted = is_delete; + error = 0; + break; + } + if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) { + list_add_tail_rcu(&entry->list, &group->member_list); + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + out: + if (e.is_ipv6) { + tomoyo_put_ipv6_address(e.min.ipv6); + tomoyo_put_ipv6_address(e.max.ipv6); + } + tomoyo_put_address_group(group); + return error; +} + +/** + * tomoyo_read_address_group_policy - Read "struct tomoyo_address_group" list. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_read_address_group_policy(struct tomoyo_io_buffer *head) +{ + struct list_head *gpos; + struct list_head *mpos; + bool done = true; + list_for_each_cookie(gpos, head->read_var1, + &tomoyo_address_group_list) { + struct tomoyo_address_group *group; + group = list_entry(gpos, struct tomoyo_address_group, list); + list_for_each_cookie(mpos, head->read_var2, + &group->member_list) { + char buf[128]; + struct tomoyo_address_group_member *member; + member = list_entry(mpos, + struct tomoyo_address_group_member, + list); + if (member->is_deleted) + continue; + if (member->is_ipv6) { + const struct in6_addr *min_address + = member->min.ipv6; + const struct in6_addr *max_address + = member->max.ipv6; + tomoyo_print_ipv6(buf, sizeof(buf), + min_address); + if (min_address != max_address) { + int len; + char *cp = buf + strlen(buf); + *cp++ = '-'; + len = strlen(buf); + tomoyo_print_ipv6(cp, + sizeof(buf) - len, + max_address); + } + } else { + const u32 min_address = member->min.ipv4; + const u32 max_address = member->max.ipv4; + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf) - 1, "%u.%u.%u.%u", + HIPQUAD(min_address)); + if (min_address != max_address) { + const int len = strlen(buf); + snprintf(buf + len, + sizeof(buf) - 1 - len, + "-%u.%u.%u.%u", + HIPQUAD(max_address)); + } + } + done = tomoyo_io_printf(head, + TOMOYO_KEYWORD_ADDRESS_GROUP + "%s %s\n", + group->group_name->name, buf); + if (!done) + break; + } + if (!done) + break; + } + return done; +} + +/** + * tomoyo_address_matches_group - Check whether the given address matches members of the given address group. + * + * @is_ipv6: True if @address is an IPv6 address. + * @address: An IPv4 or IPv6 address. + * @group: Pointer to "struct tomoyo_address_group". + * + * Returns true if @address matches addresses in @group group, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_address_matches_group(const bool is_ipv6, const u32 *address, + const struct tomoyo_address_group *group) +{ + struct tomoyo_address_group_member *member; + const u32 ip = ntohl(*address); + bool matched = false; + list_for_each_entry_rcu(member, &group->member_list, list) { + if (member->is_deleted) + continue; + if (member->is_ipv6) { + if (is_ipv6 && + memcmp(member->min.ipv6, address, 16) <= 0 && + memcmp(address, member->max.ipv6, 16) <= 0) { + matched = true; + break; + } + } else { + if (!is_ipv6 && + member->min.ipv4 <= ip && ip <= member->max.ipv4) { + matched = true; + break; + } + } + } + return matched; +} -- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/