diff mbox

[RFC] LSM hook for post recvmsg.

Message ID 201007170114.GFC57373.SQJHOVtLFMOFFO@I-love.SAKURA.ne.jp
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

Tetsuo Handa July 16, 2010, 4:14 p.m. UTC
Hello, David. Thank you for giving me suggestions at Japan Linux Symposium 2009.
As TOMOYO is getting functional and AppArmor is about to join mainline, I'd like
to resume discussions regarding LSM hooks for post accept()/recvmsg() operations.

Below is a patch for post recvmsg() operation. I modified the patch to call
skb_recv_datagram() again (for udp_recvmsg(), raw_recvmsg(), udpv6_recvmsg())
if LSM dicided to drop the message. (Regarding rawv6_recvmsg(), I didn't do so
in accordance with the comment at "csum_copy_err:".)
What do you think about this verion?

Regards.

--
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

Comments

David Miller July 16, 2010, 7:35 p.m. UTC | #1
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Date: Sat, 17 Jul 2010 01:14:38 +0900

> Below is a patch for post recvmsg() operation. I modified the patch to call
> skb_recv_datagram() again (for udp_recvmsg(), raw_recvmsg(), udpv6_recvmsg())
> if LSM dicided to drop the message. (Regarding rawv6_recvmsg(), I didn't do so
> in accordance with the comment at "csum_copy_err:".)
> What do you think about this verion?

This looks fine, but regardless of that comment I think the IPV6 raw recvmsg()
should loop just as the IPV4 one does in your patch.
--
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
diff mbox

Patch

diff --git a/include/linux/security.h b/include/linux/security.h
index 723a93d..409c44d 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -879,6 +879,12 @@  static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
  *	@size contains the size of message structure.
  *	@flags contains the operational flags.
  *	Return 0 if permission is granted.
+ * @socket_post_recvmsg:
+ *	Check permission after receiving a message from a socket.
+ *	The message is discarded if permission is not granted.
+ *	@sk contains the sock structure.
+ *	@skb contains the sk_buff structure.
+ *	Return 0 if permission is granted.
  * @socket_getsockname:
  *	Check permission before the local address (name) of the socket object
  *	@sock is retrieved.
@@ -1575,6 +1581,7 @@  struct security_operations {
 			       struct msghdr *msg, int size);
 	int (*socket_recvmsg) (struct socket *sock,
 			       struct msghdr *msg, int size, int flags);
+	int (*socket_post_recvmsg) (struct sock *sk, struct sk_buff *skb);
 	int (*socket_getsockname) (struct socket *sock);
 	int (*socket_getpeername) (struct socket *sock);
 	int (*socket_getsockopt) (struct socket *sock, int level, int optname);
@@ -2526,6 +2533,7 @@  int security_socket_accept(struct socket *sock, struct socket *newsock);
 int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size);
 int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
 			    int size, int flags);
+int security_socket_post_recvmsg(struct sock *sk, struct sk_buff *skb);
 int security_socket_getsockname(struct socket *sock);
 int security_socket_getpeername(struct socket *sock);
 int security_socket_getsockopt(struct socket *sock, int level, int optname);
@@ -2617,6 +2625,12 @@  static inline int security_socket_recvmsg(struct socket *sock,
 	return 0;
 }
 
+static inline int security_socket_post_recvmsg(struct sock *sk,
+					       struct sk_buff *skb)
+{
+	return 0;
+}
+
 static inline int security_socket_getsockname(struct socket *sock)
 {
 	return 0;
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 2c7a163..69652d4 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -676,9 +676,15 @@  static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 		goto out;
 	}
 
-	skb = skb_recv_datagram(sk, flags, noblock, &err);
-	if (!skb)
-		goto out;
+	for (;;) {
+		skb = skb_recv_datagram(sk, flags, noblock, &err);
+		if (!skb)
+			goto out;
+		err = security_socket_post_recvmsg(sk, skb);
+		if (likely(!err))
+			break;
+		skb_kill_datagram(sk, skb, flags);
+	}
 
 	copied = skb->len;
 	if (len < copied) {
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 5858574..9145685 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1125,6 +1125,7 @@  int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
 	int err;
 	int is_udplite = IS_UDPLITE(sk);
 	bool slow;
+	bool update_stat;
 
 	/*
 	 *	Check any passed addresses
@@ -1140,6 +1141,12 @@  try_again:
 				  &peeked, &err);
 	if (!skb)
 		goto out;
+	err = security_socket_post_recvmsg(sk, skb);
+	if (err) {
+		update_stat = false;
+		goto csum_copy_err;
+	}
+	update_stat = true;
 
 	ulen = skb->len - sizeof(struct udphdr);
 	if (len > ulen)
@@ -1200,7 +1207,7 @@  out:
 
 csum_copy_err:
 	slow = lock_sock_fast(sk);
-	if (!skb_kill_datagram(sk, skb, flags))
+	if (!skb_kill_datagram(sk, skb, flags) && update_stat)
 		UDP_INC_STATS_USER(sock_net(sk), UDP_MIB_INERRORS, is_udplite);
 	unlock_sock_fast(sk, slow);
 
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 4a4dcbe..135d4ed 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -467,6 +467,9 @@  static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
 	skb = skb_recv_datagram(sk, flags, noblock, &err);
 	if (!skb)
 		goto out;
+	err = security_socket_post_recvmsg(sk, skb);
+	if (unlikely(err))
+		goto csum_copy_err;
 
 	copied = skb->len;
 	if (copied > len) {
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 87be586..6cae276 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -329,6 +329,7 @@  int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
 	int is_udplite = IS_UDPLITE(sk);
 	int is_udp4;
 	bool slow;
+	bool update_stat;
 
 	if (addr_len)
 		*addr_len=sizeof(struct sockaddr_in6);
@@ -344,6 +345,12 @@  try_again:
 				  &peeked, &err);
 	if (!skb)
 		goto out;
+	err = security_socket_post_recvmsg(sk, skb);
+	if (err) {
+		update_stat = false;
+		goto csum_copy_err;
+	}
+	update_stat = true;
 
 	ulen = skb->len - sizeof(struct udphdr);
 	if (len > ulen)
@@ -426,7 +433,7 @@  out:
 
 csum_copy_err:
 	slow = lock_sock_fast(sk);
-	if (!skb_kill_datagram(sk, skb, flags)) {
+	if (!skb_kill_datagram(sk, skb, flags) && update_stat) {
 		if (is_udp4)
 			UDP_INC_STATS_USER(sock_net(sk),
 					UDP_MIB_INERRORS, is_udplite);
diff --git a/security/capability.c b/security/capability.c
index 4aeb699..709aea3 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -597,6 +597,11 @@  static int cap_socket_recvmsg(struct socket *sock, struct msghdr *msg,
 	return 0;
 }
 
+static int cap_socket_post_recvmsg(struct sock *sk, struct sk_buff *skb)
+{
+	return 0;
+}
+
 static int cap_socket_getsockname(struct socket *sock)
 {
 	return 0;
@@ -1001,6 +1006,7 @@  void __init security_fixup_ops(struct security_operations *ops)
 	set_to_cap_if_null(ops, socket_accept);
 	set_to_cap_if_null(ops, socket_sendmsg);
 	set_to_cap_if_null(ops, socket_recvmsg);
+	set_to_cap_if_null(ops, socket_post_recvmsg);
 	set_to_cap_if_null(ops, socket_getsockname);
 	set_to_cap_if_null(ops, socket_getpeername);
 	set_to_cap_if_null(ops, socket_setsockopt);
diff --git a/security/security.c b/security/security.c
index e8c87b8..4291bd7 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1037,6 +1037,12 @@  int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
 	return security_ops->socket_recvmsg(sock, msg, size, flags);
 }
 
+int security_socket_post_recvmsg(struct sock *sk, struct sk_buff *skb)
+{
+	return security_ops->socket_post_recvmsg(sk, skb);
+}
+EXPORT_SYMBOL(security_socket_post_recvmsg);
+
 int security_socket_getsockname(struct socket *sock)
 {
 	return security_ops->socket_getsockname(sock);