@@ -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;
@@ -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) {
@@ -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);
@@ -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) {
@@ -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);
@@ -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);
@@ -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);