Skip to content

Commit

Permalink
Merge branch 'mptcp-reject-invalid-mp_join-requests-right-away'
Browse files Browse the repository at this point in the history
Florian Westphal says:

====================
mptcp: reject invalid mp_join requests right away

At the moment MPTCP can detect an invalid join request (invalid token,
max number of subflows reached, and so on) right away but cannot reject
the connection until the 3WHS has completed.
Instead the connection will complete and the subflow is reset afterwards.

To send the reset most information is already available, but we don't have
good spot where the reset could be sent:

1. The ->init_req callback is too early and also doesn't allow to return an
   error that could be used to inform the TCP stack that the SYN should be
   dropped.

2. The ->route_req callback lacks the skb needed to send a reset.

3. The ->send_synack callback is the best fit from the available hooks,
   but its called after the request socket has been inserted into the queue
   already. This means we'd have to remove it again right away.

From a technical point of view, the second hook would be best:
 1. Its before insertion into listener queue.
 2. If it returns NULL TCP will drop the packet for us.

Problem is that we'd have to pass the skb to the function just for MPTCP.

Paolo suggested to merge init_req and route_req callbacks instead:
This makes all info available to MPTCP -- a return value of NULL drops the
packet and MPTCP can send the reset if needed.

Because 'route_req' has a 'const struct sock *', this means either removal
of const qualifier, or a bit of code churn to pass 'const' in security land.

This does the latter; I did not find any spots that need write access to struct
sock.

To recap, the two alternatives are:
1. Solve it entirely in MPTCP: use the ->send_synack callback to
   unlink the request socket from the listener & drop it.
2. Avoid 'security' churn by removing the const qualifier.
====================

Link: https://lore.kernel.org/r/20201130153631.21872-1-fw@strlen.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Jakub Kicinski committed Dec 3, 2020
2 parents d4bff72 + 3ecfbe3 commit a4390e9
Show file tree
Hide file tree
Showing 15 changed files with 91 additions and 50 deletions.
2 changes: 1 addition & 1 deletion include/linux/lsm_audit.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

struct lsm_network_audit {
int netif;
struct sock *sk;
const struct sock *sk;
u16 family;
__be16 dport;
__be16 sport;
Expand Down
2 changes: 1 addition & 1 deletion include/linux/lsm_hook_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ LSM_HOOK(void, LSM_RET_VOID, sk_clone_security, const struct sock *sk,
struct sock *newsk)
LSM_HOOK(void, LSM_RET_VOID, sk_getsecid, struct sock *sk, u32 *secid)
LSM_HOOK(void, LSM_RET_VOID, sock_graft, struct sock *sk, struct socket *parent)
LSM_HOOK(int, 0, inet_conn_request, struct sock *sk, struct sk_buff *skb,
LSM_HOOK(int, 0, inet_conn_request, const struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
LSM_HOOK(void, LSM_RET_VOID, inet_csk_clone, struct sock *newsk,
const struct request_sock *req)
Expand Down
4 changes: 2 additions & 2 deletions include/linux/security.h
Original file line number Diff line number Diff line change
Expand Up @@ -1358,7 +1358,7 @@ void security_sk_clone(const struct sock *sk, struct sock *newsk);
void security_sk_classify_flow(struct sock *sk, struct flowi *fl);
void security_req_classify_flow(const struct request_sock *req, struct flowi *fl);
void security_sock_graft(struct sock*sk, struct socket *parent);
int security_inet_conn_request(struct sock *sk,
int security_inet_conn_request(const struct sock *sk,
struct sk_buff *skb, struct request_sock *req);
void security_inet_csk_clone(struct sock *newsk,
const struct request_sock *req);
Expand Down Expand Up @@ -1519,7 +1519,7 @@ static inline void security_sock_graft(struct sock *sk, struct socket *parent)
{
}

static inline int security_inet_conn_request(struct sock *sk,
static inline int security_inet_conn_request(const struct sock *sk,
struct sk_buff *skb, struct request_sock *req)
{
return 0;
Expand Down
9 changes: 4 additions & 5 deletions include/net/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -2007,15 +2007,14 @@ struct tcp_request_sock_ops {
const struct sock *sk,
const struct sk_buff *skb);
#endif
void (*init_req)(struct request_sock *req,
const struct sock *sk_listener,
struct sk_buff *skb);
#ifdef CONFIG_SYN_COOKIES
__u32 (*cookie_init_seq)(const struct sk_buff *skb,
__u16 *mss);
#endif
struct dst_entry *(*route_req)(const struct sock *sk, struct flowi *fl,
const struct request_sock *req);
struct dst_entry *(*route_req)(const struct sock *sk,
struct sk_buff *skb,
struct flowi *fl,
struct request_sock *req);
u32 (*init_seq)(const struct sk_buff *skb);
u32 (*init_ts_off)(const struct net *net, const struct sk_buff *skb);
int (*send_synack)(const struct sock *sk, struct dst_entry *dst,
Expand Down
9 changes: 2 additions & 7 deletions net/ipv4/tcp_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -6799,18 +6799,13 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
/* Note: tcp_v6_init_req() might override ir_iif for link locals */
inet_rsk(req)->ir_iif = inet_request_bound_dev_if(sk, skb);

af_ops->init_req(req, sk, skb);

if (security_inet_conn_request(sk, skb, req))
dst = af_ops->route_req(sk, skb, &fl, req);
if (!dst)
goto drop_and_free;

if (tmp_opt.tstamp_ok)
tcp_rsk(req)->ts_off = af_ops->init_ts_off(net, skb);

dst = af_ops->route_req(sk, &fl, req);
if (!dst)
goto drop_and_free;

if (!want_cookie && !isn) {
/* Kill the following clause, if you dislike this way. */
if (!net->ipv4.sysctl_tcp_syncookies &&
Expand Down
9 changes: 7 additions & 2 deletions net/ipv4/tcp_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -1444,9 +1444,15 @@ static void tcp_v4_init_req(struct request_sock *req,
}

static struct dst_entry *tcp_v4_route_req(const struct sock *sk,
struct sk_buff *skb,
struct flowi *fl,
const struct request_sock *req)
struct request_sock *req)
{
tcp_v4_init_req(req, sk, skb);

if (security_inet_conn_request(sk, skb, req))
return NULL;

return inet_csk_route_req(sk, &fl->u.ip4, req);
}

Expand All @@ -1466,7 +1472,6 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
.req_md5_lookup = tcp_v4_md5_lookup,
.calc_md5_hash = tcp_v4_md5_hash_skb,
#endif
.init_req = tcp_v4_init_req,
#ifdef CONFIG_SYN_COOKIES
.cookie_init_seq = cookie_v4_init_sequence,
#endif
Expand Down
9 changes: 7 additions & 2 deletions net/ipv6/tcp_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -828,9 +828,15 @@ static void tcp_v6_init_req(struct request_sock *req,
}

static struct dst_entry *tcp_v6_route_req(const struct sock *sk,
struct sk_buff *skb,
struct flowi *fl,
const struct request_sock *req)
struct request_sock *req)
{
tcp_v6_init_req(req, sk, skb);

if (security_inet_conn_request(sk, skb, req))
return NULL;

return inet6_csk_route_req(sk, &fl->u.ip6, req, IPPROTO_TCP);
}

Expand All @@ -851,7 +857,6 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
.req_md5_lookup = tcp_v6_md5_lookup,
.calc_md5_hash = tcp_v6_md5_hash_skb,
#endif
.init_req = tcp_v6_init_req,
#ifdef CONFIG_SYN_COOKIES
.cookie_init_seq = cookie_v6_init_sequence,
#endif
Expand Down
75 changes: 56 additions & 19 deletions net/mptcp/subflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,14 @@ static int __subflow_init_req(struct request_sock *req, const struct sock *sk_li
return 0;
}

static void subflow_init_req(struct request_sock *req,
const struct sock *sk_listener,
struct sk_buff *skb)
/* Init mptcp request socket.
*
* Returns an error code if a JOIN has failed and a TCP reset
* should be sent.
*/
static int subflow_init_req(struct request_sock *req,
const struct sock *sk_listener,
struct sk_buff *skb)
{
struct mptcp_subflow_context *listener = mptcp_subflow_ctx(sk_listener);
struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req);
Expand All @@ -125,15 +130,15 @@ static void subflow_init_req(struct request_sock *req,

ret = __subflow_init_req(req, sk_listener);
if (ret)
return;
return 0;

mptcp_get_options(skb, &mp_opt);

if (mp_opt.mp_capable) {
SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_MPCAPABLEPASSIVE);

if (mp_opt.mp_join)
return;
return 0;
} else if (mp_opt.mp_join) {
SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINSYNRX);
}
Expand All @@ -157,7 +162,7 @@ static void subflow_init_req(struct request_sock *req,
} else {
subflow_req->mp_capable = 1;
}
return;
return 0;
}

err = mptcp_token_new_request(req);
Expand All @@ -175,14 +180,20 @@ static void subflow_init_req(struct request_sock *req,
subflow_req->remote_nonce = mp_opt.nonce;
subflow_req->msk = subflow_token_join_request(req, skb);

if (unlikely(req->syncookie) && subflow_req->msk) {
/* Can't fall back to TCP in this case. */
if (!subflow_req->msk)
return -EPERM;

if (unlikely(req->syncookie)) {
if (mptcp_can_accept_new_subflow(subflow_req->msk))
subflow_init_req_cookie_join_save(subflow_req, skb);
}

pr_debug("token=%u, remote_nonce=%u msk=%p", subflow_req->token,
subflow_req->remote_nonce, subflow_req->msk);
}

return 0;
}

int mptcp_subflow_init_cookie_req(struct request_sock *req,
Expand Down Expand Up @@ -228,27 +239,53 @@ int mptcp_subflow_init_cookie_req(struct request_sock *req,
}
EXPORT_SYMBOL_GPL(mptcp_subflow_init_cookie_req);

static void subflow_v4_init_req(struct request_sock *req,
const struct sock *sk_listener,
struct sk_buff *skb)
static struct dst_entry *subflow_v4_route_req(const struct sock *sk,
struct sk_buff *skb,
struct flowi *fl,
struct request_sock *req)
{
struct dst_entry *dst;
int err;

tcp_rsk(req)->is_mptcp = 1;

tcp_request_sock_ipv4_ops.init_req(req, sk_listener, skb);
dst = tcp_request_sock_ipv4_ops.route_req(sk, skb, fl, req);
if (!dst)
return NULL;

err = subflow_init_req(req, sk, skb);
if (err == 0)
return dst;

subflow_init_req(req, sk_listener, skb);
dst_release(dst);
if (!req->syncookie)
tcp_request_sock_ops.send_reset(sk, skb);
return NULL;
}

#if IS_ENABLED(CONFIG_MPTCP_IPV6)
static void subflow_v6_init_req(struct request_sock *req,
const struct sock *sk_listener,
struct sk_buff *skb)
static struct dst_entry *subflow_v6_route_req(const struct sock *sk,
struct sk_buff *skb,
struct flowi *fl,
struct request_sock *req)
{
struct dst_entry *dst;
int err;

tcp_rsk(req)->is_mptcp = 1;

tcp_request_sock_ipv6_ops.init_req(req, sk_listener, skb);
dst = tcp_request_sock_ipv6_ops.route_req(sk, skb, fl, req);
if (!dst)
return NULL;

err = subflow_init_req(req, sk, skb);
if (err == 0)
return dst;

subflow_init_req(req, sk_listener, skb);
dst_release(dst);
if (!req->syncookie)
tcp6_request_sock_ops.send_reset(sk, skb);
return NULL;
}
#endif

Expand Down Expand Up @@ -1388,7 +1425,7 @@ void __init mptcp_subflow_init(void)
panic("MPTCP: failed to init subflow request sock ops\n");

subflow_request_sock_ipv4_ops = tcp_request_sock_ipv4_ops;
subflow_request_sock_ipv4_ops.init_req = subflow_v4_init_req;
subflow_request_sock_ipv4_ops.route_req = subflow_v4_route_req;

subflow_specific = ipv4_specific;
subflow_specific.conn_request = subflow_v4_conn_request;
Expand All @@ -1397,7 +1434,7 @@ void __init mptcp_subflow_init(void)

#if IS_ENABLED(CONFIG_MPTCP_IPV6)
subflow_request_sock_ipv6_ops = tcp_request_sock_ipv6_ops;
subflow_request_sock_ipv6_ops.init_req = subflow_v6_init_req;
subflow_request_sock_ipv6_ops.route_req = subflow_v6_route_req;

subflow_v6_specific = ipv6_specific;
subflow_v6_specific.conn_request = subflow_v6_conn_request;
Expand Down
2 changes: 1 addition & 1 deletion security/apparmor/include/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,6 @@ int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request,
struct socket *sock);

int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
u32 secid, struct sock *sk);
u32 secid, const struct sock *sk);

#endif /* __AA_NET_H */
2 changes: 1 addition & 1 deletion security/apparmor/lsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1147,7 +1147,7 @@ static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
}

#ifdef CONFIG_NETWORK_SECMARK
static int apparmor_inet_conn_request(struct sock *sk, struct sk_buff *skb,
static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
struct aa_sk_ctx *ctx = SK_CTX(sk);
Expand Down
6 changes: 3 additions & 3 deletions security/apparmor/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ static int apparmor_secmark_init(struct aa_secmark *secmark)
}

static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
struct common_audit_data *sa, struct sock *sk)
struct common_audit_data *sa)
{
int i, ret;
struct aa_perms perms = { };
Expand Down Expand Up @@ -244,13 +244,13 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
}

int apparmor_secmark_check(struct aa_label *label, char *op, u32 request,
u32 secid, struct sock *sk)
u32 secid, const struct sock *sk)
{
struct aa_profile *profile;
DEFINE_AUDIT_SK(sa, op, sk);

return fn_for_each_confined(label, profile,
aa_secmark_perm(profile, request, secid,
&sa, sk));
&sa));
}
#endif
4 changes: 2 additions & 2 deletions security/lsm_audit.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,


static inline void print_ipv6_addr(struct audit_buffer *ab,
struct in6_addr *addr, __be16 port,
const struct in6_addr *addr, __be16 port,
char *name1, char *name2)
{
if (!ipv6_addr_any(addr))
Expand Down Expand Up @@ -322,7 +322,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
}
case LSM_AUDIT_DATA_NET:
if (a->u.net->sk) {
struct sock *sk = a->u.net->sk;
const struct sock *sk = a->u.net->sk;
struct unix_sock *u;
struct unix_address *addr;
int len = 0;
Expand Down
2 changes: 1 addition & 1 deletion security/security.c
Original file line number Diff line number Diff line change
Expand Up @@ -2225,7 +2225,7 @@ void security_sock_graft(struct sock *sk, struct socket *parent)
}
EXPORT_SYMBOL(security_sock_graft);

int security_inet_conn_request(struct sock *sk,
int security_inet_conn_request(const struct sock *sk,
struct sk_buff *skb, struct request_sock *req)
{
return call_int_hook(inet_conn_request, 0, sk, skb, req);
Expand Down
2 changes: 1 addition & 1 deletion security/selinux/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -5355,7 +5355,7 @@ static void selinux_sctp_sk_clone(struct sctp_endpoint *ep, struct sock *sk,
selinux_netlbl_sctp_sk_clone(sk, newsk);
}

static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
static int selinux_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
struct sk_security_struct *sksec = sk->sk_security;
Expand Down
4 changes: 2 additions & 2 deletions security/smack/smack_lsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -3864,7 +3864,7 @@ static inline struct smack_known *smack_from_skb(struct sk_buff *skb)
*
* Returns smack_known of the IP options or NULL if that won't work.
*/
static struct smack_known *smack_from_netlbl(struct sock *sk, u16 family,
static struct smack_known *smack_from_netlbl(const struct sock *sk, u16 family,
struct sk_buff *skb)
{
struct netlbl_lsm_secattr secattr;
Expand Down Expand Up @@ -4114,7 +4114,7 @@ static void smack_sock_graft(struct sock *sk, struct socket *parent)
* Returns 0 if a task with the packet label could write to
* the socket, otherwise an error code
*/
static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
static int smack_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
u16 family = sk->sk_family;
Expand Down

0 comments on commit a4390e9

Please sign in to comment.