These functions checks whether TOMOYO can do permission checks or not. TOMOYO won't do permission checks if the process is a kernel thread or the process is not permitted to sleep. Signed-off-by: Kentaro Takeda Signed-off-by: Tetsuo Handa Signed-off-by: Toshiharu Harada Cc: linux-netdev --- include/linux/tomoyo_socket.h | 419 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 419 insertions(+) --- /dev/null +++ linux-2.6.25-rc8-mm1/include/linux/tomoyo_socket.h @@ -0,0 +1,419 @@ +/* + * include/linux/tomoyo_socket.h + * + * Implementation of the Domain-Based Mandatory Access Control. + * + * Copyright (C) 2005-2008 NTT DATA CORPORATION + * + * Version: 1.6.0 2008/04/01 + * + */ + +#ifndef _LINUX_TOMOYO_SOCKET_H +#define _LINUX_TOMOYO_SOCKET_H + +#include +#include +#include +#include + +#if defined(CONFIG_TOMOYO) + +#define false 0 +#define true 1 + +#define MAX_SOCK_ADDR 128 /* net/socket.c */ + +/* Check permission for creating a socket. */ +static inline int ccs_socket_create_permission(int family, int type, + int protocol) +{ + int error = 0; + /* Nothing to do if I am a kernel service. */ + if (segment_eq(get_fs(), KERNEL_DS)) + return 0; + if (family == PF_PACKET && !ccs_capable(TOMOYO_USE_PACKET_SOCKET)) + return -EPERM; + if (family == PF_ROUTE && !ccs_capable(TOMOYO_USE_ROUTE_SOCKET)) + return -EPERM; + if (family != PF_INET && family != PF_INET6) + return 0; + switch (type) { + case SOCK_STREAM: + if (!ccs_capable(TOMOYO_INET_STREAM_SOCKET_CREATE)) + error = -EPERM; + break; + case SOCK_DGRAM: + if (!ccs_capable(TOMOYO_USE_INET_DGRAM_SOCKET)) + error = -EPERM; + break; + case SOCK_RAW: + if (!ccs_capable(TOMOYO_USE_INET_RAW_SOCKET)) + error = -EPERM; + break; + } + return error; +} + +/* Check permission for listening a TCP socket. */ +static inline int ccs_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 (!ccs_capable(TOMOYO_INET_STREAM_SOCKET_LISTEN)) + return -EPERM; + 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 = ccs_check_network_listen_acl(true, + addr6->sin6_addr.s6_addr, + addr6->sin6_port); + break; + case AF_INET: + addr4 = (struct sockaddr_in *) addr; + error = ccs_check_network_listen_acl(false, + (u8 *) &addr4->sin_addr, + addr4->sin_port); + break; + } + return error; +} + +/* Check permission for setting the remote IP address/port pair of a socket. */ +static inline int ccs_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 = ccs_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 = ccs_check_network_connect_acl(false, type, + (u8 *) &addr4->sin_addr, + port); + break; + } + if (type != SOCK_STREAM) + return error; + switch (sock->sk->sk_family) { + case PF_INET: + case PF_INET6: + if (!ccs_capable(TOMOYO_INET_STREAM_SOCKET_CONNECT)) + error = -EPERM; + break; + } + return error; +} + +/* Check permission for setting the local IP address/port pair of a socket. */ +static inline int ccs_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 = ccs_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 = ccs_check_network_bind_acl(false, type, + (u8 *) &addr4->sin_addr, + port); + break; + } + return error; +} + +/* + * Check permission for accepting a TCP socket. + * + * Currently, the LSM hook for this purpose is not provided. + */ +static inline int ccs_socket_accept_permission(struct socket *sock, + struct sockaddr *addr) +{ + int error = 0; + 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, addr, &addr_len, 2); + if (error) + return error; + switch (addr->sa_family) { + struct sockaddr_in6 *addr6; + struct sockaddr_in *addr4; + case AF_INET6: + addr6 = (struct sockaddr_in6 *) addr; + error = ccs_check_network_accept_acl(true, + addr6->sin6_addr.s6_addr, + addr6->sin6_port); + break; + case AF_INET: + addr4 = (struct sockaddr_in *) addr; + error = ccs_check_network_accept_acl(false, + (u8 *) &addr4->sin_addr, + addr4->sin_port); + break; + } + return error; +} + +/* Check permission for sending a datagram via a UDP or RAW socket. */ +static inline int ccs_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 = ccs_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 = ccs_check_network_sendmsg_acl(false, type, + (u8 *) &addr4->sin_addr, + port); + break; + } + return error; +} + +/* + * Check permission for receiving a datagram via a UDP or RAW socket. + * + * Currently, the LSM hook for this purpose is not provided. + */ +static inline int ccs_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 didn't receive a datagram. */ + if (!skb) + return 0; + /* 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 = ccs_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 = ccs_check_network_recvmsg_acl(false, type, + (u8 *) &sin4, port); + break; + } + if (!error) + return 0; + lock_sock(sk); + /* + * Remove from queue if MSG_PEEK is used so that + * the head message from unwanted source in receive queue will not + * prevent the caller from picking up next message from wanted source + * when the caller is using MSG_PEEK flag for picking up. + */ + if (flags & MSG_PEEK) { + unsigned long cpu_flags; + /***** CRITICAL SECTION START *****/ + spin_lock_irqsave(&sk->sk_receive_queue.lock, cpu_flags); + if (skb == skb_peek(&sk->sk_receive_queue)) { + __skb_unlink(skb, &sk->sk_receive_queue); + atomic_dec(&skb->users); + } + spin_unlock_irqrestore(&sk->sk_receive_queue.lock, cpu_flags); + /***** CRITICAL SECTION END *****/ + } + /* Drop reference count. */ + skb_free_datagram(sk, skb); + release_sock(sk); + /* Hope less harmful than -EPERM. */ + return -EAGAIN; +} + +#else + +static inline int ccs_socket_create_permission(int family, int type, + int protocol) +{ + return 0; +} +static inline int ccs_socket_listen_permission(struct socket *sock) +{ + return 0; +} +static inline int ccs_socket_connect_permission(struct socket *sock, + struct sockaddr *addr, + int addr_len) +{ + return 0; +} +static inline int ccs_socket_bind_permission(struct socket *sock, + struct sockaddr *addr, + int addr_len) +{ + return 0; +} +static inline int ccs_socket_accept_permission(struct socket *sock, + struct sockaddr *addr) +{ + return 0; +} +static inline int ccs_socket_sendmsg_permission(struct socket *sock, + struct sockaddr *addr, + int addr_len) +{ + return 0; +} +static inline int ccs_socket_recv_datagram_permission(struct sock *sk, + struct sk_buff *skb, + const unsigned int flags) +{ + return 0; +} + +#endif + +#endif -- -- 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/