From patchwork Fri Jul 16 16:14:38 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tetsuo Handa X-Patchwork-Id: 59113 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.180.67]) by ozlabs.org (Postfix) with ESMTP id 816ABB6F07 for ; Sat, 17 Jul 2010 02:15:04 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S966029Ab0GPQOo (ORCPT ); Fri, 16 Jul 2010 12:14:44 -0400 Received: from wine.ocn.ne.jp ([122.1.235.145]:51384 "EHLO smtp.wine.ocn.ne.jp" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965948Ab0GPQOm (ORCPT ); Fri, 16 Jul 2010 12:14:42 -0400 Received: from CLAMP (p3129-ipbf3101marunouchi.tokyo.ocn.ne.jp [122.29.22.129]) by smtp.wine.ocn.ne.jp (Postfix) with ESMTP id B3C3D3BA7; Sat, 17 Jul 2010 01:14:40 +0900 (JST) To: davem@davemloft.net Cc: netdev@vger.kernel.org Subject: [RFC] LSM hook for post recvmsg. From: Tetsuo Handa Message-Id: <201007170114.GFC57373.SQJHOVtLFMOFFO@I-love.SAKURA.ne.jp> X-Mailer: Winbiff [Version 2.51 PL2] X-Accept-Language: ja,en,zh Date: Sat, 17 Jul 2010 01:14:38 +0900 Mime-Version: 1.0 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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 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);