Commit 5234b9f7 authored by James Morris's avatar James Morris

[LSM]: Networking socket SKB receive hook.

parent d1e13e50
......@@ -684,6 +684,12 @@ struct swap_info_struct;
* @sock contains the socket structure.
* @how contains the flag indicating how future sends and receives are handled.
* Return 0 if permission is granted.
* @socket_sock_rcv_skb:
* Check permissions on incoming network packets. This hook is distinct
* from Netfilter's IP input hooks since it is the first time that the
* incoming sk_buff @skb has been associated with a particular socket, @sk.
* @sk contains the sock (not socket) associated with the incoming sk_buff.
* @skb contains the incoming network data.
*
* Security hooks affecting all System V IPC operations.
*
......@@ -1073,6 +1079,7 @@ struct security_operations {
int (*socket_getsockopt) (struct socket * sock, int level, int optname);
int (*socket_setsockopt) (struct socket * sock, int level, int optname);
int (*socket_shutdown) (struct socket * sock, int how);
int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb);
#endif /* CONFIG_SECURITY_NETWORK */
};
......@@ -2312,6 +2319,12 @@ static inline int security_socket_shutdown(struct socket * sock, int how)
{
return security_ops->socket_shutdown(sock, how);
}
static inline int security_sock_rcv_skb (struct sock * sk,
struct sk_buff * skb)
{
return security_ops->socket_sock_rcv_skb (sk, skb);
}
#else /* CONFIG_SECURITY_NETWORK */
static inline int security_socket_create (int family, int type, int protocol)
{
......@@ -2394,6 +2407,11 @@ static inline int security_socket_shutdown(struct socket * sock, int how)
{
return 0;
}
static inline int security_sock_rcv_skb (struct sock * sk,
struct sk_buff * skb)
{
return 0;
}
#endif /* CONFIG_SECURITY_NETWORK */
#endif /* ! __LINUX_SECURITY_H */
......
......@@ -44,6 +44,7 @@
#include <linux/netdevice.h>
#include <linux/skbuff.h> /* struct sk_buff */
#include <linux/security.h>
#ifdef CONFIG_FILTER
#include <linux/filter.h>
......@@ -458,28 +459,45 @@ extern void sock_init_data(struct socket *sock, struct sock *sk);
#ifdef CONFIG_FILTER
/**
* sk_filter - run a packet through a socket filter
* __sk_filter - run a packet through a socket filter
* @sk: sock associated with &sk_buff
* @skb: buffer to filter
* @filter: filter to apply
* @needlock: set to 1 if the sock is not locked by caller.
*
* Run the filter code and then cut skb->data to correct size returned by
* sk_run_filter. If pkt_len is 0 we toss packet. If skb->len is smaller
* than pkt_len we keep whole skb->data. This is the socket level
* wrapper to sk_run_filter. It returns 0 if the packet should
* be accepted or 1 if the packet should be tossed.
* be accepted or -EPERM if the packet should be tossed.
*
* This function should not be called directly, use sk_filter instead
* to ensure that the LSM security check is also performed.
*/
static inline int sk_filter(struct sk_buff *skb, struct sk_filter *filter)
static inline int __sk_filter(struct sock *sk, struct sk_buff *skb, int needlock)
{
int pkt_len;
int err = 0;
if (sk->filter) {
struct sk_filter *filter;
pkt_len = sk_run_filter(skb, filter->insns, filter->len);
if(!pkt_len)
return 1; /* Toss Packet */
if (needlock)
bh_lock_sock(sk);
filter = sk->filter;
if (filter) {
int pkt_len = sk_run_filter(skb, filter->insns,
filter->len);
if (!pkt_len)
err = -EPERM;
else
skb_trim(skb, pkt_len);
}
return 0;
if (needlock)
bh_unlock_sock(sk);
}
return err;
}
/**
......@@ -506,8 +524,26 @@ static inline void sk_filter_charge(struct sock *sk, struct sk_filter *fp)
atomic_add(sk_filter_len(fp), &sk->omem_alloc);
}
#else
static inline int __sk_filter(struct sock *sk, struct sk_buff *skb, int needlock)
{
return 0;
}
#endif /* CONFIG_FILTER */
static inline int sk_filter(struct sock *sk, struct sk_buff *skb, int needlock)
{
int err;
err = security_sock_rcv_skb(sk, skb);
if (err)
return err;
return __sk_filter(sk, skb, needlock);
}
/*
* Socket reference counting postulates.
*
......@@ -712,36 +748,31 @@ static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
static inline int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
int err = 0;
/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
number of warnings when compiling with -W --ANK
*/
if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf)
return -ENOMEM;
#ifdef CONFIG_FILTER
if (sk->filter) {
int err = 0;
struct sk_filter *filter;
if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf) {
err = -ENOMEM;
goto out;
}
/* It would be deadlock, if sock_queue_rcv_skb is used
with socket lock! We assume that users of this
function are lock free.
*/
bh_lock_sock(sk);
if ((filter = sk->filter) != NULL && sk_filter(skb, filter))
err = -EPERM;
bh_unlock_sock(sk);
err = sk_filter(sk, skb, 1);
if (err)
return err; /* Toss packet */
}
#endif /* CONFIG_FILTER */
goto out;
skb->dev = NULL;
skb_set_owner_r(skb, sk);
skb_queue_tail(&sk->receive_queue, skb);
if (!sk->dead)
sk->data_ready(sk,skb->len);
return 0;
out:
return err;
}
static inline int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb)
......
......@@ -566,26 +566,19 @@ static void dn_nsp_linkservice(struct sock *sk, struct sk_buff *skb)
*/
static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig, struct sk_buff_head *queue)
{
#ifdef CONFIG_FILTER
struct sk_filter *filter;
#endif
int err;
/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
number of warnings when compiling with -W --ANK
*/
if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf
)
return -ENOMEM;
if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf) {
err = -ENOMEM;
goto out;
}
#ifdef CONFIG_FILTER
if (sk->filter) {
int err = 0;
if ((filter = sk->filter) != NULL && sk_filter(skb, sk->filter))
err = -EPERM; /* Toss packet */
err = sk_filter(sk, skb, 0);
if (err)
return err;
}
#endif /* CONFIG_FILTER */
goto out;
skb_set_owner_r(skb, sk);
skb_queue_tail(queue, skb);
......@@ -603,8 +596,8 @@ static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig
(sig == SIGURG) ? POLL_PRI : POLL_IN);
}
read_unlock(&sk->callback_lock);
return 0;
out:
return err;
}
static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb)
......
......@@ -1697,12 +1697,6 @@ static int tcp_v4_checksum_init(struct sk_buff *skb)
*/
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
#ifdef CONFIG_FILTER
struct sk_filter *filter = sk->filter;
if (filter && sk_filter(skb, filter))
goto discard;
#endif /* CONFIG_FILTER */
if (sk->state == TCP_ESTABLISHED) { /* Fast path */
TCP_CHECK_TIMER(sk);
if (tcp_rcv_established(sk, skb, skb->h.th, skb->len))
......@@ -1805,6 +1799,9 @@ int tcp_v4_rcv(struct sk_buff *skb)
if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_and_relse;
if (sk_filter(sk, skb, 0))
goto discard_and_relse;
skb->dev = NULL;
bh_lock_sock(sk);
......
......@@ -1470,9 +1470,6 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
{
struct ipv6_pinfo *np = inet6_sk(sk);
struct tcp_opt *tp;
#ifdef CONFIG_FILTER
struct sk_filter *filter;
#endif
struct sk_buff *opt_skb = NULL;
/* Imagine: socket is IPv6. IPv4 packet arrives,
......@@ -1486,11 +1483,8 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
if (skb->protocol == htons(ETH_P_IP))
return tcp_v4_do_rcv(sk, skb);
#ifdef CONFIG_FILTER
filter = sk->filter;
if (filter && sk_filter(skb, filter))
if (sk_filter(sk, skb, 0))
goto discard;
#endif /* CONFIG_FILTER */
/*
* socket locking is here for SMP purposes as backlog rcv
......@@ -1641,6 +1635,9 @@ static int tcp_v6_rcv(struct sk_buff *skb)
if(sk->state == TCP_TIME_WAIT)
goto do_time_wait;
if (sk_filter(sk, skb, 0))
goto discard_and_relse;
skb->dev = NULL;
bh_lock_sock(sk);
......@@ -1672,6 +1669,10 @@ static int tcp_v6_rcv(struct sk_buff *skb)
kfree_skb(skb);
return 0;
discard_and_relse:
sock_put(sk);
goto discard_it;
do_time_wait:
if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
TCP_INC_STATS_BH(TcpInErrs);
......
......@@ -159,6 +159,10 @@ int sctp_rcv(struct sk_buff *skb)
if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_release;
ret = sk_filter(sk, skb, 1);
if (ret)
goto discard_release;
/* Create an SCTP packet structure. */
chunk = sctp_chunkify(skb, asoc, sk);
if (!chunk) {
......
......@@ -674,6 +674,10 @@ static int dummy_socket_shutdown (struct socket *sock, int how)
return 0;
}
static int dummy_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb)
{
return 0;
}
#endif /* CONFIG_SECURITY_NETWORK */
static int dummy_register_security (const char *name, struct security_operations *ops)
......@@ -819,6 +823,7 @@ void security_fixup_ops (struct security_operations *ops)
set_to_dummy_if_null(ops, socket_setsockopt);
set_to_dummy_if_null(ops, socket_getsockopt);
set_to_dummy_if_null(ops, socket_shutdown);
set_to_dummy_if_null(ops, socket_sock_rcv_skb);
#endif /* CONFIG_SECURITY_NETWORK */
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment