login
Header Space

 
 

[TOMOYO #7 07/30] Some wrapper functions for socket operation.

Score:
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]
To: <akpm@...>, <linux-kernel@...>, <linux-security-module@...>
Cc: Kentaro Takeda <takedakn@...>, Tetsuo Handa <penguin-kernel@...>, Toshiharu Harada <haradats@...>, linux-netdev <netdev@...>
Date: Friday, April 4, 2008 - 8:22 am

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 <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
Cc: linux-netdev <netdev@vger.kernel.org>
---
 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 <net/ip.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
+#include <linux/uaccess.h>
+
+#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 netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Previous message: [thread] [date] [author]
Next message: [thread] [date] [author]

Messages in current thread:
[TOMOYO #7 07/30] Some wrapper functions for socket operation., Tetsuo Handa, (Fri Apr 4, 8:22 am)
speck-geostationary