From patchwork Wed Apr 15 10:51:39 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tetsuo Handa X-Patchwork-Id: 25979 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by ozlabs.org (Postfix) with ESMTP id 7484FDE19B for ; Wed, 15 Apr 2009 20:51:58 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758879AbZDOKvr (ORCPT ); Wed, 15 Apr 2009 06:51:47 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1758813AbZDOKvq (ORCPT ); Wed, 15 Apr 2009 06:51:46 -0400 Received: from wine.ocn.ne.jp ([122.1.235.145]:64153 "EHLO smtp.wine.ocn.ne.jp" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1757934AbZDOKvn (ORCPT ); Wed, 15 Apr 2009 06:51:43 -0400 Received: from CLAMP (p1194-ipbf6009marunouchi.tokyo.ocn.ne.jp [114.145.72.194]) by smtp.wine.ocn.ne.jp (Postfix) with ESMTP id 5D59432BE; Wed, 15 Apr 2009 19:51:40 +0900 (JST) To: paul.moore@hp.com Cc: linux-security-module@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH 2/2] tomoyo: Add network access control support. From: Tetsuo Handa References: <200904141944.JFE64074.FHtOMOFQLFJOVS@I-love.SAKURA.ne.jp> <200904141859.57965.paul.moore@hp.com> <200904150512.n3F5CfDA008806@www262.sakura.ne.jp> In-Reply-To: <200904150512.n3F5CfDA008806@www262.sakura.ne.jp> Message-Id: <200904151951.GEI34847.OLVFJFMtHSOFOQ@I-love.SAKURA.ne.jp> X-Mailer: Winbiff [Version 2.50 PL2] X-Accept-Language: ja,en Date: Wed, 15 Apr 2009 19:51:39 +0900 Mime-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Subject: tomoyo: Add network access control support. TOMOYO checks permissions for below IPv4/IPv6 network operations. Binding TCP/UDP/RAW sockets. Listening TCP sockets. Accepting TCP sockets. Connecting TCP/UDP/RAW sockets. Sending UDP/RAW packets. Receiving UDP/RAW packets. TOMOYO uses security_socket_post_accept() and security_socket_post_recv_datagram() in order to implement a packet filtering with interactive enforcement. (Interactive enforcement is not implemented yet.) Signed-off-by: Kentaro Takeda Signed-off-by: Tetsuo Handa Signed-off-by: Toshiharu Harada --- security/tomoyo/Kconfig | 1 security/tomoyo/Makefile | 2 security/tomoyo/common.c | 118 +++++ security/tomoyo/common.h | 68 +++ security/tomoyo/network.c | 930 ++++++++++++++++++++++++++++++++++++++++++++++ security/tomoyo/tomoyo.c | 46 ++ security/tomoyo/tomoyo.h | 17 7 files changed, 1178 insertions(+), 4 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html --- security-testing-2.6.git.orig/security/tomoyo/Kconfig +++ security-testing-2.6.git/security/tomoyo/Kconfig @@ -3,6 +3,7 @@ config SECURITY_TOMOYO depends on SECURITY select SECURITYFS select SECURITY_PATH + select SECURITY_NETWORK default n help This selects TOMOYO Linux, pathname-based access control. --- security-testing-2.6.git.orig/security/tomoyo/Makefile +++ security-testing-2.6.git/security/tomoyo/Makefile @@ -1 +1 @@ -obj-y = common.o realpath.o tomoyo.o domain.o file.o +obj-y = common.o realpath.o tomoyo.o domain.o file.o network.o --- security-testing-2.6.git.orig/security/tomoyo/common.c +++ security-testing-2.6.git/security/tomoyo/common.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "realpath.h" #include "common.h" #include "tomoyo.h" @@ -35,6 +36,7 @@ static struct { const unsigned int max_value; } tomoyo_control_array[TOMOYO_MAX_CONTROL_INDEX] = { [TOMOYO_MAC_FOR_FILE] = { "MAC_FOR_FILE", 0, 3 }, + [TOMOYO_MAC_FOR_NETWORK] = { "MAC_FOR_NETWORK", 0, 3 }, [TOMOYO_MAX_ACCEPT_ENTRY] = { "MAX_ACCEPT_ENTRY", 2048, INT_MAX }, [TOMOYO_VERBOSE] = { "TOMOYO_VERBOSE", 1, 1 }, }; @@ -836,6 +838,9 @@ bool tomoyo_domain_quota_is_ok(struct to if (perm & (1 << TOMOYO_TYPE_RENAME_ACL)) count++; break; + case TOMOYO_TYPE_IP_NETWORK_ACL: + count++; + break; } } up_read(&tomoyo_domain_acl_info_list_lock); @@ -1290,6 +1295,8 @@ static int tomoyo_write_domain_policy(st TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ); return 0; } + if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_NETWORK)) + return tomoyo_write_network_policy(data, domain, is_delete); return tomoyo_write_file_policy(data, domain, is_delete); } @@ -1378,6 +1385,107 @@ static bool tomoyo_print_double_path_acl } /** + * tomoyo_print_ipv4_entry - Print IPv4 address of a network ACL entry. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_ip_network_acl_record". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_print_ipv4_entry(struct tomoyo_io_buffer *head, + struct tomoyo_ip_network_acl_record *ptr) +{ + const u32 min_address = htonl(ptr->u.ipv4.min); + const u32 max_address = htonl(ptr->u.ipv4.max); + if (!tomoyo_io_printf(head, "%pI4", &min_address)) + return false; + if (min_address != max_address + && !tomoyo_io_printf(head, "-%pI4", &max_address)) + return false; + return true; +} + +/** + * tomoyo_print_ipv6_entry - Print IPv6 address of a network ACL entry. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_ip_network_acl_record". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_print_ipv6_entry(struct tomoyo_io_buffer *head, + struct tomoyo_ip_network_acl_record *ptr) +{ + char buf[64]; + const struct in6_addr *min_address = ptr->u.ipv6.min; + const struct in6_addr *max_address = ptr->u.ipv6.max; + tomoyo_print_ipv6(buf, sizeof(buf), min_address); + if (!tomoyo_io_printf(head, "%s", buf)) + return false; + if (min_address != max_address) { + tomoyo_print_ipv6(buf, sizeof(buf), max_address); + if (!tomoyo_io_printf(head, "-%s", buf)) + return false; + } + return true; +} + +/** + * tomoyo_print_port_entry - Print port number of a network ACL entry. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_ip_network_acl_record". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_print_port_entry(struct tomoyo_io_buffer *head, + struct tomoyo_ip_network_acl_record *ptr) +{ + const u16 min_port = ptr->min_port; + const u16 max_port = ptr->max_port; + if (!tomoyo_io_printf(head, " %u", min_port)) + return false; + if (min_port != max_port && !tomoyo_io_printf(head, "-%u", max_port)) + return false; + if (!tomoyo_io_printf(head, "\n")) + return false; + return true; +} + +/** + * tomoyo_print_network_acl - Print a network ACL entry. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_ip_network_acl_record". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_print_network_acl(struct tomoyo_io_buffer *head, + struct tomoyo_ip_network_acl_record *ptr) +{ + int pos = head->read_avail; + if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_NETWORK "%s ", + tomoyo_net2keyword(ptr->operation_type))) + goto out; + switch (ptr->record_type) { + case TOMOYO_IP_RECORD_TYPE_IPv4: + if (!tomoyo_print_ipv4_entry(head, ptr)) + goto out; + break; + case TOMOYO_IP_RECORD_TYPE_IPv6: + if (!tomoyo_print_ipv6_entry(head, ptr)) + goto out; + break; + } + if (!tomoyo_print_port_entry(head, ptr)) + goto out; + return true; + out: + head->read_avail = pos; + return false; +} + +/** * tomoyo_print_entry - Print an ACL entry. * * @head: Pointer to "struct tomoyo_io_buffer". @@ -1406,6 +1514,13 @@ static bool tomoyo_print_entry(struct to head); return tomoyo_print_double_path_acl(head, acl); } + if (acl_type == TOMOYO_TYPE_IP_NETWORK_ACL) { + struct tomoyo_ip_network_acl_record *acl + = container_of(ptr, + struct tomoyo_ip_network_acl_record, + head); + return tomoyo_print_network_acl(head, acl); + } BUG(); /* This must not happen. */ return false; } @@ -2068,6 +2183,9 @@ void *tomoyo_alloc_acl_element(const u8 case TOMOYO_TYPE_DOUBLE_PATH_ACL: len = sizeof(struct tomoyo_double_path_acl_record); break; + case TOMOYO_TYPE_IP_NETWORK_ACL: + len = sizeof(struct tomoyo_ip_network_acl_record); + break; default: return NULL; } --- security-testing-2.6.git.orig/security/tomoyo/common.h +++ security-testing-2.6.git/security/tomoyo/common.h @@ -130,8 +130,59 @@ struct tomoyo_double_path_acl_record { const struct tomoyo_path_info *filename2; }; +/* Structure for "allow_network" directive. */ +struct tomoyo_ip_network_acl_record { + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_IP_NETWORK_ACL */ + /* + * operation_type takes one of the following constants. + * NETWORK_ACL_UDP_BIND for UDP's bind() operation. + * NETWORK_ACL_UDP_CONNECT for UDP's connect()/send()/recv() + * operation. + * NETWORK_ACL_TCP_BIND for TCP's bind() operation. + * NETWORK_ACL_TCP_LISTEN for TCP's listen() operation. + * NETWORK_ACL_TCP_CONNECT for TCP's connect() operation. + * NETWORK_ACL_TCP_ACCEPT for TCP's accept() operation. + * NETWORK_ACL_RAW_BIND for IP's bind() operation. + * NETWORK_ACL_RAW_CONNECT for IP's connect()/send()/recv() + * operation. + */ + u8 operation_type; + /* + * record_type takes one of the following constants. + * TOMOYO_IP_RECORD_TYPE_IPv4 + * if u points to an IPv4 address. + * TOMOYO_IP_RECORD_TYPE_IPv6 + * if u points to an IPv6 address. + */ + u8 record_type; + /* Start of port number range. */ + u16 min_port; + /* End of port number range. */ + u16 max_port; + union { + struct { + /* Start of IPv4 address range. Host endian. */ + u32 min; + /* End of IPv4 address range. Host endian. */ + u32 max; + } ipv4; + struct { + /* Start of IPv6 address range. Big endian. */ + const struct in6_addr *min; + /* End of IPv6 address range. Big endian. */ + const struct in6_addr *max; + } ipv6; + } u; +}; + +enum tomoyo_ip_record_type { + TOMOYO_IP_RECORD_TYPE_IPv4, + TOMOYO_IP_RECORD_TYPE_IPv6 +}; + /* Keywords for ACLs. */ #define TOMOYO_KEYWORD_ALIAS "alias " +#define TOMOYO_KEYWORD_ALLOW_NETWORK "allow_network " #define TOMOYO_KEYWORD_ALLOW_READ "allow_read " #define TOMOYO_KEYWORD_DELETE "delete " #define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite " @@ -149,9 +200,10 @@ struct tomoyo_double_path_acl_record { /* Index numbers for Access Controls. */ #define TOMOYO_MAC_FOR_FILE 0 /* domain_policy.conf */ -#define TOMOYO_MAX_ACCEPT_ENTRY 1 -#define TOMOYO_VERBOSE 2 -#define TOMOYO_MAX_CONTROL_INDEX 3 +#define TOMOYO_MAC_FOR_NETWORK 1 +#define TOMOYO_MAX_ACCEPT_ENTRY 2 +#define TOMOYO_VERBOSE 3 +#define TOMOYO_MAX_CONTROL_INDEX 4 /* Structure for reading/writing policy via securityfs interfaces. */ struct tomoyo_io_buffer { @@ -204,6 +256,9 @@ bool tomoyo_is_domain_def(const unsigned /* 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); +/* Print an IPv6 address. */ +void tomoyo_print_ipv6(char *buffer, const int buffer_len, + const struct in6_addr *ip); /* Read "alias" entry in exception policy. */ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head); /* @@ -275,6 +330,13 @@ void tomoyo_load_policy(const char *file /* Change "struct tomoyo_domain_info"->flags. */ void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain, const bool is_delete, const u8 flags); +/* Read "allow_network" entry in domain policy. */ +bool tomoyo_read_network_policy(struct tomoyo_io_buffer *head); +/* Create "allow_network" entry in domain policy. */ +int tomoyo_write_network_policy(char *data, struct tomoyo_domain_info *domain, + const bool is_delete); +/* Convert network operation index to network operation name. */ +const char *tomoyo_net2keyword(const u8 operation); /* strcmp() for "struct tomoyo_path_info" structure. */ static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a, --- /dev/null +++ security-testing-2.6.git/security/tomoyo/network.c @@ -0,0 +1,930 @@ +/* + * security/tomoyo/network.c + * + * Implementation of the Domain-Based Mandatory Access Control. + * + * Copyright (C) 2005-2009 NTT DATA CORPORATION + * + * Version: 2.3.0-pre 2009/04/15 + * + */ + +#include "common.h" +#include "tomoyo.h" +#include "realpath.h" + +#include +#include +#include +#include +#include +#include +#include + +/* Index numbers for Network Controls. */ +enum tomoyo_network_acl_index { + NETWORK_ACL_UDP_BIND, + NETWORK_ACL_UDP_CONNECT, + NETWORK_ACL_TCP_BIND, + NETWORK_ACL_TCP_LISTEN, + NETWORK_ACL_TCP_CONNECT, + NETWORK_ACL_TCP_ACCEPT, + NETWORK_ACL_RAW_BIND, + NETWORK_ACL_RAW_CONNECT +}; + +/** + * tomoyo_save_ipv6_address - Keep the given IPv6 address on the RAM. + * + * @addr: Pointer to "struct in6_addr". + * + * Returns pointer to "struct in6_addr" on success, NULL otherwise. + * + * The RAM is shared, so NEVER try to modify or kfree() the returned address. + */ +static const struct in6_addr *tomoyo_save_ipv6_address(const struct in6_addr * + addr) +{ + static const u8 tomoyo_block_size = 16; + struct tomoyo_addr_list { + /* Workaround for gcc 4.3's bug. */ + struct in6_addr addr[16]; /* = tomoyo_block_size */ + struct list_head list; + u32 in_use_count; + }; + static LIST_HEAD(tomoyo_address_list); + struct tomoyo_addr_list *ptr; + static DEFINE_MUTEX(lock); + u8 i = tomoyo_block_size; + if (!addr) + return NULL; + /* + * Since reader calls down_read(&tomoyo_domain_acl_info_list_lock), + * this lock can remain as local lock. + */ + mutex_lock(&lock); + list_for_each_entry(ptr, &tomoyo_address_list, list) { + for (i = 0; i < ptr->in_use_count; i++) { + if (!memcmp(&ptr->addr[i], addr, sizeof(*addr))) + goto ok; + } + if (i < tomoyo_block_size) + break; + } + if (i == tomoyo_block_size) { + ptr = tomoyo_alloc_element(sizeof(*ptr)); + if (!ptr) + goto ok; + list_add_tail(&ptr->list, &tomoyo_address_list); + i = 0; + } + ptr->addr[ptr->in_use_count++] = *addr; + ok: + mutex_unlock(&lock); + return ptr ? &ptr->addr[i] : NULL; +} + +/** + * tomoyo_parse_ip_address - Parse an IP address. + * + * @address: String to parse. + * @min: Pointer to store min address. + * @max: Pointer to store max address. + * + * Returns 2 if @address is an IPv6, 1 if @address is an IPv4, 0 otherwise. + */ +static int tomoyo_parse_ip_address(char *address, u16 *min, u16 *max) +{ + /* "%pI6" is not supported for sscanf(). */ + int count = sscanf(address, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx" + "-%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", + &min[0], &min[1], &min[2], &min[3], + &min[4], &min[5], &min[6], &min[7], + &max[0], &max[1], &max[2], &max[3], + &max[4], &max[5], &max[6], &max[7]); + if (count == 8 || count == 16) { + u8 i; + if (count == 8) + memmove(max, min, sizeof(u16) * 8); + for (i = 0; i < 8; i++) { + min[i] = htons(min[i]); + max[i] = htons(max[i]); + } + return 2; + } + /* "%pI4" is not supported for sscanf(). */ + count = sscanf(address, "%hu.%hu.%hu.%hu-%hu.%hu.%hu.%hu", + &min[0], &min[1], &min[2], &min[3], + &max[0], &max[1], &max[2], &max[3]); + if (count == 4 || count == 8) { + u32 ip = htonl((((u8) min[0]) << 24) + (((u8) min[1]) << 16) + + (((u8) min[2]) << 8) + (u8) min[3]); + memmove(min, &ip, sizeof(ip)); + if (count == 8) + ip = htonl((((u8) max[0]) << 24) + (((u8) max[1]) << 16) + + (((u8) max[2]) << 8) + (u8) max[3]); + memmove(max, &ip, sizeof(ip)); + return 1; + } + return 0; +} + +/** + * tomoyo_print_ipv6 - Print an IPv6 address. + * + * @buffer: Buffer to write to. + * @buffer_len: Size of @buffer. + * @ip: Pointer to "struct in6_addr". + * + * To make output shortest, TOMOYO doesn't use "%pI6". + * + * Returns nothing. + */ +void tomoyo_print_ipv6(char *buffer, const int buffer_len, + const struct in6_addr *ip) +{ + memset(buffer, 0, buffer_len); + snprintf(buffer, buffer_len - 1, "%x:%x:%x:%x:%x:%x:%x:%x", + ntohs(ip->s6_addr16[0]), ntohs(ip->s6_addr16[1]), + ntohs(ip->s6_addr16[2]), ntohs(ip->s6_addr16[3]), + ntohs(ip->s6_addr16[4]), ntohs(ip->s6_addr16[5]), + ntohs(ip->s6_addr16[6]), ntohs(ip->s6_addr16[7])); +} + +/** + * tomoyo_net2keyword - Convert network operation index to network operation name. + * + * @operation: Type of operation. + * + * Returns the name of operation. + */ +const char *tomoyo_net2keyword(const u8 operation) +{ + const char *keyword = "unknown"; + switch (operation) { + case NETWORK_ACL_UDP_BIND: + keyword = "UDP bind"; + break; + case NETWORK_ACL_UDP_CONNECT: + keyword = "UDP connect"; + break; + case NETWORK_ACL_TCP_BIND: + keyword = "TCP bind"; + break; + case NETWORK_ACL_TCP_LISTEN: + keyword = "TCP listen"; + break; + case NETWORK_ACL_TCP_CONNECT: + keyword = "TCP connect"; + break; + case NETWORK_ACL_TCP_ACCEPT: + keyword = "TCP accept"; + break; + case NETWORK_ACL_RAW_BIND: + keyword = "RAW bind"; + break; + case NETWORK_ACL_RAW_CONNECT: + keyword = "RAW connect"; + break; + } + return keyword; +} + +/** + * tomoyo_update_network_entry - Update "struct tomoyo_ip_network_acl_record" list. + * + * @operation: Type of operation. + * @record_type: Type of address. + * @min_address: Start of IPv4 or IPv6 address range. + * @max_address: End of IPv4 or IPv6 address range. + * @min_port: Start of port number range. + * @max_port: End of port number range. + * @domain: Pointer to "struct tomoyo_domain_info". + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_update_network_entry(const u8 operation, const u8 record_type, + const u32 *min_address, + const u32 *max_address, + const u16 min_port, const u16 max_port, + struct tomoyo_domain_info *domain, + const bool is_delete) +{ + struct tomoyo_acl_info *ptr; + struct tomoyo_ip_network_acl_record *acl; + int error = -ENOMEM; + /* using host byte order to allow u32 comparison than memcmp().*/ + const u32 min_ip = ntohl(*min_address); + const u32 max_ip = ntohl(*max_address); + const struct in6_addr *saved_min_address = NULL; + const struct in6_addr *saved_max_address = NULL; + if (!domain) + return -EINVAL; + if (record_type != TOMOYO_IP_RECORD_TYPE_IPv6) + goto not_ipv6; + saved_min_address = tomoyo_save_ipv6_address((struct in6_addr *) + min_address); + saved_max_address = tomoyo_save_ipv6_address((struct in6_addr *) + max_address); + if (!saved_min_address || !saved_max_address) + return -ENOMEM; + not_ipv6: + /***** EXCLUSIVE SECTION START *****/ + down_write(&tomoyo_domain_acl_info_list_lock); + if (is_delete) + goto delete; + list_for_each_entry(ptr, &domain->acl_info_list, list) { + if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_IP_NETWORK_ACL) + continue; + acl = container_of(ptr, struct tomoyo_ip_network_acl_record, + head); + if (acl->operation_type != operation || + acl->record_type != record_type || + acl->min_port != min_port || max_port != acl->max_port) + continue; + if (record_type == TOMOYO_IP_RECORD_TYPE_IPv4) { + if (acl->u.ipv4.min != min_ip || + max_ip != acl->u.ipv4.max) + continue; + } else if (record_type == TOMOYO_IP_RECORD_TYPE_IPv6) { + if (acl->u.ipv6.min != saved_min_address || + saved_max_address != acl->u.ipv6.max) + continue; + } + ptr->type &= ~TOMOYO_ACL_DELETED; + error = 0; + goto out; + } + /* Not found. Append it to the tail. */ + acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_IP_NETWORK_ACL); + if (!acl) + goto out; + acl->operation_type = operation; + acl->record_type = record_type; + if (record_type == TOMOYO_IP_RECORD_TYPE_IPv4) { + acl->u.ipv4.min = min_ip; + acl->u.ipv4.max = max_ip; + } else { + acl->u.ipv6.min = saved_min_address; + acl->u.ipv6.max = saved_max_address; + } + acl->min_port = min_port; + acl->max_port = max_port; + list_add_tail(&acl->head.list, &domain->acl_info_list); + error = 0; + goto out; + delete: + error = -ENOENT; + list_for_each_entry(ptr, &domain->acl_info_list, list) { + if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_IP_NETWORK_ACL) + continue; + acl = container_of(ptr, struct tomoyo_ip_network_acl_record, + head); + if (acl->operation_type != operation || + acl->record_type != record_type || + acl->min_port != min_port || max_port != acl->max_port) + continue; + if (record_type == TOMOYO_IP_RECORD_TYPE_IPv4) { + if (acl->u.ipv4.min != min_ip || + max_ip != acl->u.ipv4.max) + continue; + } else if (record_type == TOMOYO_IP_RECORD_TYPE_IPv6) { + if (acl->u.ipv6.min != saved_min_address || + saved_max_address != acl->u.ipv6.max) + continue; + } + ptr->type |= TOMOYO_ACL_DELETED; + error = 0; + break; + } + out: + up_write(&tomoyo_domain_acl_info_list_lock); + /***** EXCLUSIVE SECTION START *****/ + return error; +} + +/** + * tomoyo_check_network_entry - Check permission for network operation. + * + * @is_ipv6: True if @address is an IPv6 address. + * @operation: Type of operation. + * @address: An IPv4 or IPv6 address. + * @port: Port number. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_check_network_entry(const bool is_ipv6, const u8 operation, + const u32 *address, const u16 port) +{ + struct tomoyo_acl_info *ptr; + const char *keyword = tomoyo_net2keyword(operation); + struct tomoyo_domain_info *domain = tomoyo_domain(); + const int mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_NETWORK); + const bool is_enforce = (mode == 3); + /* using host byte order to allow u32 comparison than memcmp().*/ + const u32 ip = ntohl(*address); + bool found = false; + char buf[64]; + if (!mode) + return 0; + down_read(&tomoyo_domain_acl_info_list_lock); + list_for_each_entry(ptr, &domain->acl_info_list, list) { + struct tomoyo_ip_network_acl_record *acl; + if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_IP_NETWORK_ACL) + continue; + acl = container_of(ptr, struct tomoyo_ip_network_acl_record, + head); + if (acl->operation_type != operation || port < acl->min_port || + acl->max_port < port) + continue; + if (acl->record_type == TOMOYO_IP_RECORD_TYPE_IPv4) { + if (is_ipv6 || + ip < acl->u.ipv4.min || acl->u.ipv4.max < ip) + continue; + } else { + if (!is_ipv6 || + memcmp(acl->u.ipv6.min, address, 16) > 0 || + memcmp(address, acl->u.ipv6.max, 16) > 0) + continue; + } + found = true; + break; + } + up_read(&tomoyo_domain_acl_info_list_lock); + if (found) + return 0; + memset(buf, 0, sizeof(buf)); + if (is_ipv6) + tomoyo_print_ipv6(buf, sizeof(buf), + (const struct in6_addr *) address); + else + snprintf(buf, sizeof(buf) - 1, "%pI4", address); + if (tomoyo_verbose_mode(domain)) + printk(KERN_WARNING "TOMOYO-%s: %s to %s %u denied for %s\n", + tomoyo_get_msg(is_enforce), keyword, buf, port, + tomoyo_get_last_name(domain)); + if (is_enforce) + return -EPERM; + if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) + tomoyo_update_network_entry(operation, is_ipv6 ? + TOMOYO_IP_RECORD_TYPE_IPv6 : + TOMOYO_IP_RECORD_TYPE_IPv4, + address, address, port, port, + domain, false); + return 0; +} + +/** + * tomoyo_write_network_policy - Write "struct tomoyo_ip_network_acl_record" list. + * + * @data: String to parse. + * @domain: Pointer to "struct tomoyo_domain_info". + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_network_policy(char *data, struct tomoyo_domain_info *domain, + const bool is_delete) +{ + u8 sock_type; + u8 operation; + u8 record_type; + u16 min_address[8]; + u16 max_address[8]; + u16 min_port; + u16 max_port; + u8 count; + char *cp1 = strchr(data, ' '); + char *cp2; + if (!cp1) + goto out; + cp1++; + if (!strncmp(data, "TCP ", 4)) + sock_type = SOCK_STREAM; + else if (!strncmp(data, "UDP ", 4)) + sock_type = SOCK_DGRAM; + else if (!strncmp(data, "RAW ", 4)) + sock_type = SOCK_RAW; + else + goto out; + cp2 = strchr(cp1, ' '); + if (!cp2) + goto out; + cp2++; + if (!strncmp(cp1, "bind ", 5)) + switch (sock_type) { + case SOCK_STREAM: + operation = NETWORK_ACL_TCP_BIND; + break; + case SOCK_DGRAM: + operation = NETWORK_ACL_UDP_BIND; + break; + default: + operation = NETWORK_ACL_RAW_BIND; + } + else if (!strncmp(cp1, "connect ", 8)) + switch (sock_type) { + case SOCK_STREAM: + operation = NETWORK_ACL_TCP_CONNECT; + break; + case SOCK_DGRAM: + operation = NETWORK_ACL_UDP_CONNECT; + break; + default: + operation = NETWORK_ACL_RAW_CONNECT; + } + else if (sock_type == SOCK_STREAM && !strncmp(cp1, "listen ", 7)) + operation = NETWORK_ACL_TCP_LISTEN; + else if (sock_type == SOCK_STREAM && !strncmp(cp1, "accept ", 7)) + operation = NETWORK_ACL_TCP_ACCEPT; + else + goto out; + cp1 = strchr(cp2, ' '); + if (!cp1) + goto out; + *cp1++ = '\0'; + switch (tomoyo_parse_ip_address(cp2, min_address, max_address)) { + case 2: + record_type = TOMOYO_IP_RECORD_TYPE_IPv6; + break; + case 1: + record_type = TOMOYO_IP_RECORD_TYPE_IPv4; + break; + default: + goto out; + } + if (strchr(cp1, ' ')) + goto out; + count = sscanf(cp1, "%hu-%hu", &min_port, &max_port); + if (count != 1 && count != 2) + goto out; + if (count == 1) + max_port = min_port; + return tomoyo_update_network_entry(operation, record_type, + (u32 *) min_address, + (u32 *) max_address, + min_port, max_port, domain, + is_delete); + out: + return -EINVAL; +} + +/** + * tomoyo_check_network_listen_acl - Check permission for listen() operation. + * + * @is_ipv6: True if @address is an IPv6 address. + * @address: An IPv4 or IPv6 address. + * @port: Port number. + * + * Returns 0 on success, negative value otherwise. + */ +static inline int tomoyo_check_network_listen_acl(const bool is_ipv6, + const u8 *address, + const u16 port) +{ + return tomoyo_check_network_entry(is_ipv6, NETWORK_ACL_TCP_LISTEN, + (const u32 *) address, ntohs(port)); +} + +/** + * tomoyo_check_network_connect_acl - Check permission for connect() operation. + * + * @is_ipv6: True if @address is an IPv6 address. + * @sock_type: Type of socket. (TCP or UDP or RAW) + * @address: An IPv4 or IPv6 address. + * @port: Port number. + * + * Returns 0 on success, negative value otherwise. + */ +static inline int tomoyo_check_network_connect_acl(const bool is_ipv6, + const int sock_type, + const u8 *address, + const u16 port) +{ + u8 operation; + switch (sock_type) { + case SOCK_STREAM: + operation = NETWORK_ACL_TCP_CONNECT; + break; + case SOCK_DGRAM: + operation = NETWORK_ACL_UDP_CONNECT; + break; + default: + operation = NETWORK_ACL_RAW_CONNECT; + } + return tomoyo_check_network_entry(is_ipv6, operation, + (const u32 *) address, ntohs(port)); +} + +/** + * tomoyo_check_network_bind_acl - Check permission for bind() operation. + * + * @is_ipv6: True if @address is an IPv6 address. + * @sock_type: Type of socket. (TCP or UDP or RAW) + * @address: An IPv4 or IPv6 address. + * @port: Port number. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_check_network_bind_acl(const bool is_ipv6, + const int sock_type, + const u8 *address, const u16 port) +{ + u8 operation; + switch (sock_type) { + case SOCK_STREAM: + operation = NETWORK_ACL_TCP_BIND; + break; + case SOCK_DGRAM: + operation = NETWORK_ACL_UDP_BIND; + break; + default: + operation = NETWORK_ACL_RAW_BIND; + } + return tomoyo_check_network_entry(is_ipv6, operation, + (const u32 *) address, ntohs(port)); +} + +/** + * tomoyo_check_network_accept_acl - Check permission for accept() operation. + * + * @is_ipv6: True if @address is an IPv6 address. + * @address: An IPv4 or IPv6 address. + * @port: Port number. + * + * Returns 0 on success, negative value otherwise. + */ +static inline int tomoyo_check_network_accept_acl(const bool is_ipv6, + const u8 *address, + const u16 port) +{ + return tomoyo_check_network_entry(is_ipv6, NETWORK_ACL_TCP_ACCEPT, + (const u32 *) address, ntohs(port)); +} + +/** + * tomoyo_check_network_sendmsg_acl - Check permission for sendmsg() operation. + * + * @is_ipv6: True if @address is an IPv6 address. + * @sock_type: Type of socket. (UDP or RAW) + * @address: An IPv4 or IPv6 address. + * @port: Port number. + * + * Returns 0 on success, negative value otherwise. + */ +static inline int tomoyo_check_network_sendmsg_acl(const bool is_ipv6, + const int sock_type, + const u8 *address, + const u16 port) +{ + u8 operation; + if (sock_type == SOCK_DGRAM) + operation = NETWORK_ACL_UDP_CONNECT; + else + operation = NETWORK_ACL_RAW_CONNECT; + return tomoyo_check_network_entry(is_ipv6, operation, + (const u32 *) address, ntohs(port)); +} + +/** + * tomoyo_check_network_recvmsg_acl - Check permission for recvmsg() operation. + * + * @is_ipv6: True if @address is an IPv6 address. + * @sock_type: Type of socket. (UDP or RAW) + * @address: An IPv4 or IPv6 address. + * @port: Port number. + * + * Returns 0 on success, negative value otherwise. + */ +static inline int tomoyo_check_network_recvmsg_acl(const bool is_ipv6, + const int sock_type, + const u8 *address, + const u16 port) +{ + const u8 operation + = (sock_type == SOCK_DGRAM) ? + NETWORK_ACL_UDP_CONNECT : NETWORK_ACL_RAW_CONNECT; + return tomoyo_check_network_entry(is_ipv6, operation, + (const u32 *) address, ntohs(port)); +} + +#define MAX_SOCK_ADDR 128 /* net/socket.c */ + +/** + * tomoyo_socket_listen_permission - Check permission for listening a TCP socket. + * + * @sock: Pointer to "struct socket". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_listen_permission(struct socket *sock) +{ + int error = 0; + char addr[MAX_SOCK_ADDR]; + int addr_len; + /* Nothing to do if I am a kernel service. */ + if (segment_eq(get_fs(), KERNEL_DS)) + return 0; + if (sock->type != SOCK_STREAM) + return 0; + switch (sock->sk->sk_family) { + case PF_INET: + case PF_INET6: + break; + default: + return 0; + } + if (sock->ops->getname(sock, (struct sockaddr *) addr, &addr_len, 0)) + return -EPERM; + switch (((struct sockaddr *) addr)->sa_family) { + struct sockaddr_in6 *addr6; + struct sockaddr_in *addr4; + case AF_INET6: + addr6 = (struct sockaddr_in6 *) addr; + error = tomoyo_check_network_listen_acl(true, addr6->sin6_addr. + s6_addr, + addr6->sin6_port); + break; + case AF_INET: + addr4 = (struct sockaddr_in *) addr; + error = tomoyo_check_network_listen_acl(false, + (u8 *) &addr4->sin_addr, + addr4->sin_port); + break; + } + return error; +} + +/** + * tomoyo_socket_connect_permission - Check permission for setting the remote IP address/port pair of a socket. + * + * @sock: Pointer to "struct socket". + * @addr: Pointer to "struct sockaddr". + * @len: Size of @addr in bytes. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_connect_permission(struct socket *sock, struct sockaddr *addr, + int addr_len) +{ + int error = 0; + const unsigned int type = sock->type; + /* Nothing to do if I am a kernel service. */ + if (segment_eq(get_fs(), KERNEL_DS)) + return 0; + switch (type) { + case SOCK_STREAM: + case SOCK_DGRAM: + case SOCK_RAW: + break; + default: + return 0; + } + switch (addr->sa_family) { + struct sockaddr_in6 *addr6; + struct sockaddr_in *addr4; + u16 port; + case AF_INET6: + if (addr_len < SIN6_LEN_RFC2133) + break; + addr6 = (struct sockaddr_in6 *) addr; + if (type != SOCK_RAW) + port = addr6->sin6_port; + else + port = htons(sock->sk->sk_protocol); + error = tomoyo_check_network_connect_acl(true, type, + addr6->sin6_addr. + s6_addr, port); + break; + case AF_INET: + if (addr_len < sizeof(struct sockaddr_in)) + break; + addr4 = (struct sockaddr_in *) addr; + if (type != SOCK_RAW) + port = addr4->sin_port; + else + port = htons(sock->sk->sk_protocol); + error = tomoyo_check_network_connect_acl(false, type, (u8 *) + &addr4->sin_addr, + port); + break; + } + return error; +} + +/** + * tomoyo_socket_bind_permission - Check permission for setting the local IP address/port pair of a socket. + * + * @sock: Pointer to "struct socket". + * @addr: Pointer to "struct sockaddr". + * @len: Size of @addr in bytes. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr, + int addr_len) +{ + int error = 0; + const unsigned int type = sock->type; + /* Nothing to do if I am a kernel service. */ + if (segment_eq(get_fs(), KERNEL_DS)) + return 0; + switch (type) { + case SOCK_STREAM: + case SOCK_DGRAM: + case SOCK_RAW: + break; + default: + return 0; + } + switch (addr->sa_family) { + struct sockaddr_in6 *addr6; + struct sockaddr_in *addr4; + u16 port; + case AF_INET6: + if (addr_len < SIN6_LEN_RFC2133) + break; + addr6 = (struct sockaddr_in6 *) addr; + if (type != SOCK_RAW) + port = addr6->sin6_port; + else + port = htons(sock->sk->sk_protocol); + error = tomoyo_check_network_bind_acl(true, type, + addr6->sin6_addr.s6_addr, + port); + break; + case AF_INET: + if (addr_len < sizeof(struct sockaddr_in)) + break; + addr4 = (struct sockaddr_in *) addr; + if (type != SOCK_RAW) + port = addr4->sin_port; + else + port = htons(sock->sk->sk_protocol); + error = tomoyo_check_network_bind_acl(false, type, + (u8 *) &addr4->sin_addr, + port); + break; + } + return error; +} + +/** + * tomoyo_socket_accept_permission - Check permission for accepting a TCP socket. + * + * @sock: Pointer to "struct socket". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_accept_permission(struct socket *sock) +{ + int error = 0; + struct sockaddr_storage addr; + int addr_len; + /* Nothing to do if I am a kernel service. */ + if (segment_eq(get_fs(), KERNEL_DS)) + return 0; + switch (sock->sk->sk_family) { + case PF_INET: + case PF_INET6: + break; + default: + return 0; + } + error = sock->ops->getname(sock, (struct sockaddr *) &addr, &addr_len, + 2); + if (error) + return error; + switch (((struct sockaddr *) &addr)->sa_family) { + struct sockaddr_in6 *addr6; + struct sockaddr_in *addr4; + case AF_INET6: + addr6 = (struct sockaddr_in6 *) &addr; + error = tomoyo_check_network_accept_acl(true, addr6->sin6_addr. + s6_addr, + addr6->sin6_port); + break; + case AF_INET: + addr4 = (struct sockaddr_in *) &addr; + error = tomoyo_check_network_accept_acl(false, (u8 *) &addr4-> + sin_addr, + addr4->sin_port); + break; + } + if (error) + error = -ECONNABORTED; /* Hope less harmful than -EPERM. */ + return error; +} + +/** + * tomoyo_socket_sendmsg_permission - Check permission for sending a datagram via a UDP or RAW socket. + * + * @sock: Pointer to "struct socket". + * @addr: Pointer to "struct sockaddr". + * @addr_len: Size of @addr in bytes. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_sendmsg_permission(struct socket *sock, struct sockaddr *addr, + int addr_len) +{ + int error = 0; + const int type = sock->type; + /* Nothing to do if I am a kernel service. */ + if (segment_eq(get_fs(), KERNEL_DS)) + return 0; + if (!addr || (type != SOCK_DGRAM && type != SOCK_RAW)) + return 0; + switch (addr->sa_family) { + struct sockaddr_in6 *addr6; + struct sockaddr_in *addr4; + u16 port; + case AF_INET6: + if (addr_len < SIN6_LEN_RFC2133) + break; + addr6 = (struct sockaddr_in6 *) addr; + if (type == SOCK_DGRAM) + port = addr6->sin6_port; + else + port = htons(sock->sk->sk_protocol); + error = tomoyo_check_network_sendmsg_acl(true, type, + addr6->sin6_addr. + s6_addr, port); + break; + case AF_INET: + if (addr_len < sizeof(struct sockaddr_in)) + break; + addr4 = (struct sockaddr_in *) addr; + if (type == SOCK_DGRAM) + port = addr4->sin_port; + else + port = htons(sock->sk->sk_protocol); + error = tomoyo_check_network_sendmsg_acl(false, type, (u8 *) + &addr4->sin_addr, + port); + break; + } + return error; +} + +/** + * tomoyo_socket_recv_datagram_permission - Check permission for receiving a datagram via a UDP or RAW socket. + * + * @sk: Pointer to "struct sock". + * @skb: Pointer to "struct sk_buff". + * @flags: Flags for recvmsg(). + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_recv_datagram_permission(struct sock *sk, struct sk_buff *skb, + const unsigned int flags) +{ + int error = 0; + const unsigned int type = sk->sk_type; + /* Nothing to do if I can't sleep. */ + if (in_atomic()) + return 0; + /* Nothing to do if I am a kernel service. */ + if (segment_eq(get_fs(), KERNEL_DS)) + return 0; + if (type != SOCK_DGRAM && type != SOCK_RAW) + return 0; + + switch (sk->sk_family) { + struct in6_addr sin6; + struct in_addr sin4; + u16 port; + case PF_INET6: + if (type == SOCK_DGRAM) { /* UDP IPv6 */ + if (skb->protocol == htons(ETH_P_IP)) { + ipv6_addr_set(&sin6, 0, 0, htonl(0xffff), + ip_hdr(skb)->saddr); + } else { + ipv6_addr_copy(&sin6, &ipv6_hdr(skb)->saddr); + } + port = udp_hdr(skb)->source; + } else { /* RAW IPv6 */ + ipv6_addr_copy(&sin6, &ipv6_hdr(skb)->saddr); + port = htons(sk->sk_protocol); + } + error = tomoyo_check_network_recvmsg_acl(true, type, + (u8 *) &sin6, port); + break; + case PF_INET: + if (type == SOCK_DGRAM) { /* UDP IPv4 */ + sin4.s_addr = ip_hdr(skb)->saddr; + port = udp_hdr(skb)->source; + } else { /* RAW IPv4 */ + sin4.s_addr = ip_hdr(skb)->saddr; + port = htons(sk->sk_protocol); + } + error = tomoyo_check_network_recvmsg_acl(false, type, + (u8 *) &sin4, port); + break; + } + if (!error) + return 0; + /* Hope less harmful than -EPERM. */ + return -EAGAIN; +} --- security-testing-2.6.git.orig/security/tomoyo/tomoyo.c +++ security-testing-2.6.git/security/tomoyo/tomoyo.c @@ -10,6 +10,7 @@ */ #include +#include #include "common.h" #include "tomoyo.h" #include "realpath.h" @@ -256,6 +257,45 @@ static int tomoyo_dentry_open(struct fil return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags); } +static int tomoyo_socket_bind(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + return tomoyo_socket_bind_permission(sock, address, addrlen); +} + +static int tomoyo_socket_connect(struct socket *sock, struct sockaddr *address, + int addrlen) +{ + return tomoyo_socket_connect_permission(sock, address, addrlen); +} + +static int tomoyo_socket_listen(struct socket *sock, int backlog) +{ + return tomoyo_socket_listen_permission(sock); +} + +static int tomoyo_socket_post_accept(struct socket *sock, + struct socket *newsock) +{ + return tomoyo_socket_accept_permission(newsock); +} + +static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg, + int size) +{ + return tomoyo_socket_sendmsg_permission(sock, (struct sockaddr *) + msg->msg_name, + msg->msg_namelen); +} + + +static int tomoyo_socket_post_recv_datagram(struct sock *sk, + struct sk_buff *skb, + unsigned int flags) +{ + return tomoyo_socket_recv_datagram_permission(sk, skb, flags); +} + static struct security_operations tomoyo_security_ops = { .name = "tomoyo", .cred_prepare = tomoyo_cred_prepare, @@ -274,6 +314,12 @@ static struct security_operations tomoyo .path_mknod = tomoyo_path_mknod, .path_link = tomoyo_path_link, .path_rename = tomoyo_path_rename, + .socket_bind = tomoyo_socket_bind, + .socket_listen = tomoyo_socket_listen, + .socket_connect = tomoyo_socket_connect, + .socket_sendmsg = tomoyo_socket_sendmsg, + .socket_post_recv_datagram = tomoyo_socket_post_recv_datagram, + .socket_post_accept = tomoyo_socket_post_accept, }; static int __init tomoyo_init(void) --- security-testing-2.6.git.orig/security/tomoyo/tomoyo.h +++ security-testing-2.6.git/security/tomoyo/tomoyo.h @@ -36,10 +36,27 @@ int tomoyo_check_rewrite_permission(stru int tomoyo_find_next_domain(struct linux_binprm *bprm, struct tomoyo_domain_info **next_domain); +struct sock; +struct sk_buff; +struct socket; +struct sockaddr; +int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr, + int addr_len); +int tomoyo_socket_connect_permission(struct socket *sock, struct sockaddr *addr, + int addr_len); +int tomoyo_socket_listen_permission(struct socket *sock); +int tomoyo_socket_accept_permission(struct socket *sock); +int tomoyo_socket_sendmsg_permission(struct socket *sock, struct sockaddr *addr, + int addr_len); +int tomoyo_socket_recv_datagram_permission(struct sock *sk, struct sk_buff *skb, + const unsigned int flags); + + /* Index numbers for Access Controls. */ #define TOMOYO_TYPE_SINGLE_PATH_ACL 0 #define TOMOYO_TYPE_DOUBLE_PATH_ACL 1 +#define TOMOYO_TYPE_IP_NETWORK_ACL 2 /* Index numbers for File Controls. */