Commit b245be1f authored by Willem de Bruijn's avatar Willem de Bruijn Committed by David S. Miller

net-timestamp: no-payload only sysctl

Tx timestamps are looped onto the error queue on top of an skb. This
mechanism leaks packet headers to processes unless the no-payload
options SOF_TIMESTAMPING_OPT_TSONLY is set.

Add a sysctl that optionally drops looped timestamp with data. This
only affects processes without CAP_NET_RAW.

The policy is checked when timestamps are generated in the stack.
It is possible for timestamps with data to be reported after the
sysctl is set, if these were queued internally earlier.

No vulnerability is immediately known that exploits knowledge
gleaned from packet headers, but it may still be preferable to allow
administrators to lock down this path at the cost of possible
breakage of legacy applications.
Signed-off-by: default avatarWillem de Bruijn <willemb@google.com>

----

Changes
  (v1 -> v2)
  - test socket CAP_NET_RAW instead of capable(CAP_NET_RAW)
  (rfc -> v1)
  - document the sysctl in Documentation/sysctl/net.txt
  - fix access control race: read .._OPT_TSONLY only once,
        use same value for permission check and skb generation.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 49ca0d8b
...@@ -97,6 +97,14 @@ rmem_max ...@@ -97,6 +97,14 @@ rmem_max
The maximum receive socket buffer size in bytes. The maximum receive socket buffer size in bytes.
tstamp_allow_data
-----------------
Allow processes to receive tx timestamps looped together with the original
packet contents. If disabled, transmit timestamp requests from unprivileged
processes are dropped unless socket option SOF_TIMESTAMPING_OPT_TSONLY is set.
Default: 1 (on)
wmem_default wmem_default
------------ ------------
......
...@@ -2239,6 +2239,7 @@ bool sk_net_capable(const struct sock *sk, int cap); ...@@ -2239,6 +2239,7 @@ bool sk_net_capable(const struct sock *sk, int cap);
extern __u32 sysctl_wmem_max; extern __u32 sysctl_wmem_max;
extern __u32 sysctl_rmem_max; extern __u32 sysctl_rmem_max;
extern int sysctl_tstamp_allow_data;
extern int sysctl_optmem_max; extern int sysctl_optmem_max;
extern __u32 sysctl_wmem_default; extern __u32 sysctl_wmem_default;
......
...@@ -74,6 +74,8 @@ ...@@ -74,6 +74,8 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <trace/events/skb.h> #include <trace/events/skb.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/capability.h>
#include <linux/user_namespace.h>
struct kmem_cache *skbuff_head_cache __read_mostly; struct kmem_cache *skbuff_head_cache __read_mostly;
static struct kmem_cache *skbuff_fclone_cache __read_mostly; static struct kmem_cache *skbuff_fclone_cache __read_mostly;
...@@ -3690,11 +3692,28 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb, ...@@ -3690,11 +3692,28 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb,
kfree_skb(skb); kfree_skb(skb);
} }
static bool skb_may_tx_timestamp(struct sock *sk, bool tsonly)
{
bool ret;
if (likely(sysctl_tstamp_allow_data || tsonly))
return true;
read_lock_bh(&sk->sk_callback_lock);
ret = sk->sk_socket && sk->sk_socket->file &&
file_ns_capable(sk->sk_socket->file, &init_user_ns, CAP_NET_RAW);
read_unlock_bh(&sk->sk_callback_lock);
return ret;
}
void skb_complete_tx_timestamp(struct sk_buff *skb, void skb_complete_tx_timestamp(struct sk_buff *skb,
struct skb_shared_hwtstamps *hwtstamps) struct skb_shared_hwtstamps *hwtstamps)
{ {
struct sock *sk = skb->sk; struct sock *sk = skb->sk;
if (!skb_may_tx_timestamp(sk, false))
return;
/* take a reference to prevent skb_orphan() from freeing the socket */ /* take a reference to prevent skb_orphan() from freeing the socket */
sock_hold(sk); sock_hold(sk);
...@@ -3712,7 +3731,7 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb, ...@@ -3712,7 +3731,7 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb,
struct sk_buff *skb; struct sk_buff *skb;
bool tsonly = sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TSONLY; bool tsonly = sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TSONLY;
if (!sk) if (!sk || !skb_may_tx_timestamp(sk, tsonly))
return; return;
if (tsonly) if (tsonly)
......
...@@ -325,6 +325,8 @@ __u32 sysctl_rmem_default __read_mostly = SK_RMEM_MAX; ...@@ -325,6 +325,8 @@ __u32 sysctl_rmem_default __read_mostly = SK_RMEM_MAX;
int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512); int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512);
EXPORT_SYMBOL(sysctl_optmem_max); EXPORT_SYMBOL(sysctl_optmem_max);
int sysctl_tstamp_allow_data __read_mostly = 1;
struct static_key memalloc_socks = STATIC_KEY_INIT_FALSE; struct static_key memalloc_socks = STATIC_KEY_INIT_FALSE;
EXPORT_SYMBOL_GPL(memalloc_socks); EXPORT_SYMBOL_GPL(memalloc_socks);
...@@ -840,6 +842,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname, ...@@ -840,6 +842,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
if (val & SOF_TIMESTAMPING_OPT_ID && if (val & SOF_TIMESTAMPING_OPT_ID &&
!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)) { !(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)) {
if (sk->sk_protocol == IPPROTO_TCP) { if (sk->sk_protocol == IPPROTO_TCP) {
......
...@@ -321,6 +321,15 @@ static struct ctl_table net_core_table[] = { ...@@ -321,6 +321,15 @@ static struct ctl_table net_core_table[] = {
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec .proc_handler = proc_dointvec
}, },
{
.procname = "tstamp_allow_data",
.data = &sysctl_tstamp_allow_data,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = &zero,
.extra2 = &one
},
#ifdef CONFIG_RPS #ifdef CONFIG_RPS
{ {
.procname = "rps_sock_flow_entries", .procname = "rps_sock_flow_entries",
......
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