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; ...@@ -684,6 +684,12 @@ struct swap_info_struct;
* @sock contains the socket structure. * @sock contains the socket structure.
* @how contains the flag indicating how future sends and receives are handled. * @how contains the flag indicating how future sends and receives are handled.
* Return 0 if permission is granted. * 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. * Security hooks affecting all System V IPC operations.
* *
...@@ -1073,6 +1079,7 @@ struct security_operations { ...@@ -1073,6 +1079,7 @@ struct security_operations {
int (*socket_getsockopt) (struct socket * sock, int level, int optname); int (*socket_getsockopt) (struct socket * sock, int level, int optname);
int (*socket_setsockopt) (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_shutdown) (struct socket * sock, int how);
int (*socket_sock_rcv_skb) (struct sock * sk, struct sk_buff * skb);
#endif /* CONFIG_SECURITY_NETWORK */ #endif /* CONFIG_SECURITY_NETWORK */
}; };
...@@ -2312,6 +2319,12 @@ static inline int security_socket_shutdown(struct socket * sock, int how) ...@@ -2312,6 +2319,12 @@ static inline int security_socket_shutdown(struct socket * sock, int how)
{ {
return security_ops->socket_shutdown(sock, 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 */ #else /* CONFIG_SECURITY_NETWORK */
static inline int security_socket_create (int family, int type, int protocol) 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) ...@@ -2394,6 +2407,11 @@ static inline int security_socket_shutdown(struct socket * sock, int how)
{ {
return 0; return 0;
} }
static inline int security_sock_rcv_skb (struct sock * sk,
struct sk_buff * skb)
{
return 0;
}
#endif /* CONFIG_SECURITY_NETWORK */ #endif /* CONFIG_SECURITY_NETWORK */
#endif /* ! __LINUX_SECURITY_H */ #endif /* ! __LINUX_SECURITY_H */
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/skbuff.h> /* struct sk_buff */ #include <linux/skbuff.h> /* struct sk_buff */
#include <linux/security.h>
#ifdef CONFIG_FILTER #ifdef CONFIG_FILTER
#include <linux/filter.h> #include <linux/filter.h>
...@@ -458,28 +459,45 @@ extern void sock_init_data(struct socket *sock, struct sock *sk); ...@@ -458,28 +459,45 @@ extern void sock_init_data(struct socket *sock, struct sock *sk);
#ifdef CONFIG_FILTER #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 * @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 * 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 * 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 * 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 * 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)
{
int pkt_len;
pkt_len = sk_run_filter(skb, filter->insns, filter->len); static inline int __sk_filter(struct sock *sk, struct sk_buff *skb, int needlock)
if(!pkt_len) {
return 1; /* Toss Packet */ int err = 0;
else
skb_trim(skb, pkt_len);
return 0; if (sk->filter) {
struct sk_filter *filter;
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);
}
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) ...@@ -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); 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 */ #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. * Socket reference counting postulates.
* *
...@@ -712,36 +748,31 @@ static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk) ...@@ -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) 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 /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
number of warnings when compiling with -W --ANK number of warnings when compiling with -W --ANK
*/ */
if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf) if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf) {
return -ENOMEM; err = -ENOMEM;
goto out;
#ifdef CONFIG_FILTER
if (sk->filter) {
int err = 0;
struct sk_filter *filter;
/* 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);
if (err)
return err; /* Toss packet */
} }
#endif /* CONFIG_FILTER */
/* It would be deadlock, if sock_queue_rcv_skb is used
with socket lock! We assume that users of this
function are lock free.
*/
err = sk_filter(sk, skb, 1);
if (err)
goto out;
skb->dev = NULL; skb->dev = NULL;
skb_set_owner_r(skb, sk); skb_set_owner_r(skb, sk);
skb_queue_tail(&sk->receive_queue, skb); skb_queue_tail(&sk->receive_queue, skb);
if (!sk->dead) if (!sk->dead)
sk->data_ready(sk,skb->len); 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) 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) ...@@ -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) static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig, struct sk_buff_head *queue)
{ {
#ifdef CONFIG_FILTER int err;
struct sk_filter *filter;
#endif
/* Cast skb->rcvbuf to unsigned... It's pointless, but reduces /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
number of warnings when compiling with -W --ANK number of warnings when compiling with -W --ANK
*/ */
if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf) {
) err = -ENOMEM;
return -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 */
if (err)
return err;
} }
#endif /* CONFIG_FILTER */
err = sk_filter(sk, skb, 0);
if (err)
goto out;
skb_set_owner_r(skb, sk); skb_set_owner_r(skb, sk);
skb_queue_tail(queue, skb); skb_queue_tail(queue, skb);
...@@ -603,8 +596,8 @@ static __inline__ int dn_queue_skb(struct sock *sk, struct sk_buff *skb, int sig ...@@ -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); (sig == SIGURG) ? POLL_PRI : POLL_IN);
} }
read_unlock(&sk->callback_lock); read_unlock(&sk->callback_lock);
out:
return 0; return err;
} }
static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb) 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) ...@@ -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) 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 */ if (sk->state == TCP_ESTABLISHED) { /* Fast path */
TCP_CHECK_TIMER(sk); TCP_CHECK_TIMER(sk);
if (tcp_rcv_established(sk, skb, skb->h.th, skb->len)) if (tcp_rcv_established(sk, skb, skb->h.th, skb->len))
...@@ -1805,6 +1799,9 @@ int tcp_v4_rcv(struct sk_buff *skb) ...@@ -1805,6 +1799,9 @@ int tcp_v4_rcv(struct sk_buff *skb)
if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_and_relse; goto discard_and_relse;
if (sk_filter(sk, skb, 0))
goto discard_and_relse;
skb->dev = NULL; skb->dev = NULL;
bh_lock_sock(sk); bh_lock_sock(sk);
......
...@@ -1470,9 +1470,6 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -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 ipv6_pinfo *np = inet6_sk(sk);
struct tcp_opt *tp; struct tcp_opt *tp;
#ifdef CONFIG_FILTER
struct sk_filter *filter;
#endif
struct sk_buff *opt_skb = NULL; struct sk_buff *opt_skb = NULL;
/* Imagine: socket is IPv6. IPv4 packet arrives, /* Imagine: socket is IPv6. IPv4 packet arrives,
...@@ -1486,11 +1483,8 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -1486,11 +1483,8 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
if (skb->protocol == htons(ETH_P_IP)) if (skb->protocol == htons(ETH_P_IP))
return tcp_v4_do_rcv(sk, skb); return tcp_v4_do_rcv(sk, skb);
#ifdef CONFIG_FILTER if (sk_filter(sk, skb, 0))
filter = sk->filter;
if (filter && sk_filter(skb, filter))
goto discard; goto discard;
#endif /* CONFIG_FILTER */
/* /*
* socket locking is here for SMP purposes as backlog rcv * socket locking is here for SMP purposes as backlog rcv
...@@ -1641,6 +1635,9 @@ static int tcp_v6_rcv(struct sk_buff *skb) ...@@ -1641,6 +1635,9 @@ static int tcp_v6_rcv(struct sk_buff *skb)
if(sk->state == TCP_TIME_WAIT) if(sk->state == TCP_TIME_WAIT)
goto do_time_wait; goto do_time_wait;
if (sk_filter(sk, skb, 0))
goto discard_and_relse;
skb->dev = NULL; skb->dev = NULL;
bh_lock_sock(sk); bh_lock_sock(sk);
...@@ -1672,6 +1669,10 @@ static int tcp_v6_rcv(struct sk_buff *skb) ...@@ -1672,6 +1669,10 @@ static int tcp_v6_rcv(struct sk_buff *skb)
kfree_skb(skb); kfree_skb(skb);
return 0; return 0;
discard_and_relse:
sock_put(sk);
goto discard_it;
do_time_wait: do_time_wait:
if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) { if (skb->len < (th->doff<<2) || tcp_checksum_complete(skb)) {
TCP_INC_STATS_BH(TcpInErrs); TCP_INC_STATS_BH(TcpInErrs);
......
...@@ -159,6 +159,10 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -159,6 +159,10 @@ int sctp_rcv(struct sk_buff *skb)
if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb)) if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_release; goto discard_release;
ret = sk_filter(sk, skb, 1);
if (ret)
goto discard_release;
/* Create an SCTP packet structure. */ /* Create an SCTP packet structure. */
chunk = sctp_chunkify(skb, asoc, sk); chunk = sctp_chunkify(skb, asoc, sk);
if (!chunk) { if (!chunk) {
......
...@@ -674,6 +674,10 @@ static int dummy_socket_shutdown (struct socket *sock, int how) ...@@ -674,6 +674,10 @@ static int dummy_socket_shutdown (struct socket *sock, int how)
return 0; return 0;
} }
static int dummy_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb)
{
return 0;
}
#endif /* CONFIG_SECURITY_NETWORK */ #endif /* CONFIG_SECURITY_NETWORK */
static int dummy_register_security (const char *name, struct security_operations *ops) static int dummy_register_security (const char *name, struct security_operations *ops)
...@@ -819,6 +823,7 @@ void security_fixup_ops (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_setsockopt);
set_to_dummy_if_null(ops, socket_getsockopt); set_to_dummy_if_null(ops, socket_getsockopt);
set_to_dummy_if_null(ops, socket_shutdown); set_to_dummy_if_null(ops, socket_shutdown);
set_to_dummy_if_null(ops, socket_sock_rcv_skb);
#endif /* CONFIG_SECURITY_NETWORK */ #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