Skip to content

Commit

Permalink
Merge branch 'tcp-ao-fixes'
Browse files Browse the repository at this point in the history
Dmitry Safonov says:

====================
TCP-AO fixes

Changes from v4:
- Dropped 2 patches on which there's no consensus. They will require
  more work TBD if they may made acceptable. Those are:
  o "net/tcp: Allow removing current/rnext TCP-AO keys on TCP_LISTEN sockets"
  o "net/tcp: Store SNEs + SEQs on ao_info"

Changes from v3:
- Don't restrict adding any keys on TCP-AO connection in VRF, but only
  the ones that don't match l3index (David)

Changes from v2:
- rwlocks are problematic in net code (Paolo)
  Changed the SNE code to avoid spin/rw locks on RX/TX fastpath by
  double-accounting SEQ numbers for TCP-AO enabled connections.

Changes from v1:
- Use tcp_can_repair_sock() helper to limit TCP_AO_REPAIR (Eric)
- Instead of hook to listen() syscall, allow removing current/rnext keys
  on TCP_LISTEN (addressing Eric's objection)
- Add sne_lock to protect snd_sne/rcv_sne
- Don't move used_tcp_ao in struct tcp_request_sock (Eric)

I've been working on TCP-AO key-rotation selftests and as a result
exercised some corner-cases that are not usually met in production.

Here are a bunch of semi-related fixes:
- Documentation typo (reported by Markus Elfring)
- Proper alignment for TCP-AO option in TCP header that has MAC length
  of non 4 bytes (now a selftest with randomized maclen/algorithm/etc
  passes)
- 3 uAPI restricting patches that disallow more things to userspace in
  order to prevent it shooting itself in any parts of the body
- SNEs READ_ONCE()/WRITE_ONCE() that went missing by my human factor
- Avoid storing MAC length from SYN header as SYN-ACK will use
  rnext_key.maclen (drops an extra check that fails on new selftests)
====================

Link: https://lore.kernel.org/r/
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
  • Loading branch information
Paolo Abeni committed Dec 6, 2023
2 parents 6b07b52 + 9396c4e commit 3142dbf
Show file tree
Hide file tree
Showing 10 changed files with 41 additions and 26 deletions.
2 changes: 1 addition & 1 deletion Documentation/networking/tcp_ao.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ also [6.1]::
when it is no longer considered permitted.

Linux TCP-AO will try its best to prevent you from removing a key that's
being used, considering it a key management failure. But sine keeping
being used, considering it a key management failure. But since keeping
an outdated key may become a security issue and as a peer may
unintentionally prevent the removal of an old key by always setting
it as RNextKeyID - a forced key removal mechanism is provided, where
Expand Down
8 changes: 2 additions & 6 deletions include/linux/tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ struct tcp_request_sock {
#ifdef CONFIG_TCP_AO
u8 ao_keyid;
u8 ao_rcv_next;
u8 maclen;
bool used_tcp_ao;
#endif
};

Expand All @@ -180,14 +180,10 @@ static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *req)

static inline bool tcp_rsk_used_ao(const struct request_sock *req)
{
/* The real length of MAC is saved in the request socket,
* signing anything with zero-length makes no sense, so here is
* a little hack..
*/
#ifndef CONFIG_TCP_AO
return false;
#else
return tcp_rsk(req)->maclen != 0;
return tcp_rsk(req)->used_tcp_ao;
#endif
}

Expand Down
6 changes: 6 additions & 0 deletions include/net/tcp_ao.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,17 @@ static inline int tcp_ao_maclen(const struct tcp_ao_key *key)
return key->maclen;
}

/* Use tcp_ao_len_aligned() for TCP header calculations */
static inline int tcp_ao_len(const struct tcp_ao_key *key)
{
return tcp_ao_maclen(key) + sizeof(struct tcp_ao_hdr);
}

static inline int tcp_ao_len_aligned(const struct tcp_ao_key *key)
{
return round_up(tcp_ao_len(key), 4);
}

static inline unsigned int tcp_ao_digest_size(struct tcp_ao_key *key)
{
return key->digest_size;
Expand Down
6 changes: 6 additions & 0 deletions net/ipv4/tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3610,6 +3610,10 @@ int do_tcp_setsockopt(struct sock *sk, int level, int optname,
break;

case TCP_AO_REPAIR:
if (!tcp_can_repair_sock(sk)) {
err = -EPERM;
break;
}
err = tcp_ao_set_repair(sk, optval, optlen);
break;
#ifdef CONFIG_TCP_AO
Expand Down Expand Up @@ -4309,6 +4313,8 @@ int do_tcp_getsockopt(struct sock *sk, int level,
}
#endif
case TCP_AO_REPAIR:
if (!tcp_can_repair_sock(sk))
return -EPERM;
return tcp_ao_get_repair(sk, optval, optlen);
case TCP_AO_GET_KEYS:
case TCP_AO_INFO: {
Expand Down
17 changes: 13 additions & 4 deletions net/ipv4/tcp_ao.c
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,7 @@ void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
const struct tcp_ao_hdr *aoh;
struct tcp_ao_key *key;

treq->maclen = 0;
treq->used_tcp_ao = false;

if (tcp_parse_auth_options(th, NULL, &aoh) || !aoh)
return;
Expand All @@ -863,7 +863,7 @@ void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,

treq->ao_rcv_next = aoh->keyid;
treq->ao_keyid = aoh->rnext_keyid;
treq->maclen = tcp_ao_maclen(key);
treq->used_tcp_ao = true;
}

static enum skb_drop_reason
Expand Down Expand Up @@ -1100,7 +1100,7 @@ void tcp_ao_connect_init(struct sock *sk)
ao_info->current_key = key;
if (!ao_info->rnext_key)
ao_info->rnext_key = key;
tp->tcp_header_len += tcp_ao_len(key);
tp->tcp_header_len += tcp_ao_len_aligned(key);

ao_info->lisn = htonl(tp->write_seq);
ao_info->snd_sne = 0;
Expand Down Expand Up @@ -1346,7 +1346,7 @@ static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key)
syn_tcp_option_space -= TCPOLEN_MSS_ALIGNED;
syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED;
syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED;
if (tcp_ao_len(key) > syn_tcp_option_space) {
if (tcp_ao_len_aligned(key) > syn_tcp_option_space) {
err = -EMSGSIZE;
goto err_kfree;
}
Expand Down Expand Up @@ -1608,6 +1608,15 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
if (!dev || !l3index)
return -EINVAL;

if (!bound_dev_if || bound_dev_if != cmd.ifindex) {
/* tcp_ao_established_key() doesn't expect having
* non peer-matching key on an established TCP-AO
* connection.
*/
if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)))
return -EINVAL;
}

/* It's still possible to bind after adding keys or even
* re-bind to a different dev (with CAP_NET_RAW).
* So, no reason to return error here, rather try to be
Expand Down
5 changes: 3 additions & 2 deletions net/ipv4/tcp_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -7182,11 +7182,12 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh))
goto drop_and_release; /* Invalid TCP options */
if (aoh) {
tcp_rsk(req)->maclen = aoh->length - sizeof(struct tcp_ao_hdr);
tcp_rsk(req)->used_tcp_ao = true;
tcp_rsk(req)->ao_rcv_next = aoh->keyid;
tcp_rsk(req)->ao_keyid = aoh->rnext_keyid;

} else {
tcp_rsk(req)->maclen = 0;
tcp_rsk(req)->used_tcp_ao = false;
}
#endif
tcp_rsk(req)->snt_isn = isn;
Expand Down
4 changes: 2 additions & 2 deletions net/ipv4/tcp_ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ static bool tcp_v4_ao_sign_reset(const struct sock *sk, struct sk_buff *skb,

reply_options[0] = htonl((TCPOPT_AO << 24) | (tcp_ao_len(key) << 16) |
(aoh->rnext_keyid << 8) | keyid);
arg->iov[0].iov_len += round_up(tcp_ao_len(key), 4);
arg->iov[0].iov_len += tcp_ao_len_aligned(key);
reply->doff = arg->iov[0].iov_len / 4;

if (tcp_ao_hash_hdr(AF_INET, (char *)&reply_options[1],
Expand Down Expand Up @@ -978,7 +978,7 @@ static void tcp_v4_send_ack(const struct sock *sk,
(tcp_ao_len(key->ao_key) << 16) |
(key->ao_key->sndid << 8) |
key->rcv_next);
arg.iov[0].iov_len += round_up(tcp_ao_len(key->ao_key), 4);
arg.iov[0].iov_len += tcp_ao_len_aligned(key->ao_key);
rep.th.doff = arg.iov[0].iov_len / 4;

tcp_ao_hash_hdr(AF_INET, (char *)&rep.opt[offset],
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/tcp_minisocks.c
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
ao_key = treq->af_specific->ao_lookup(sk, req,
tcp_rsk(req)->ao_keyid, -1);
if (ao_key)
newtp->tcp_header_len += tcp_ao_len(ao_key);
newtp->tcp_header_len += tcp_ao_len_aligned(ao_key);
#endif
if (skb->len >= TCP_MSS_DEFAULT + newtp->tcp_header_len)
newicsk->icsk_ack.last_seg_size = skb->len - newtp->tcp_header_len;
Expand Down
15 changes: 6 additions & 9 deletions net/ipv4/tcp_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,7 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb,
timestamps = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_timestamps);
if (tcp_key_is_ao(key)) {
opts->options |= OPTION_AO;
remaining -= tcp_ao_len(key->ao_key);
remaining -= tcp_ao_len_aligned(key->ao_key);
}
}

Expand Down Expand Up @@ -915,7 +915,7 @@ static unsigned int tcp_synack_options(const struct sock *sk,
ireq->tstamp_ok &= !ireq->sack_ok;
} else if (tcp_key_is_ao(key)) {
opts->options |= OPTION_AO;
remaining -= tcp_ao_len(key->ao_key);
remaining -= tcp_ao_len_aligned(key->ao_key);
ireq->tstamp_ok &= !ireq->sack_ok;
}

Expand Down Expand Up @@ -982,7 +982,7 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
size += TCPOLEN_MD5SIG_ALIGNED;
} else if (tcp_key_is_ao(key)) {
opts->options |= OPTION_AO;
size += tcp_ao_len(key->ao_key);
size += tcp_ao_len_aligned(key->ao_key);
}

if (likely(tp->rx_opt.tstamp_ok)) {
Expand Down Expand Up @@ -3720,7 +3720,6 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
if (tcp_rsk_used_ao(req)) {
#ifdef CONFIG_TCP_AO
struct tcp_ao_key *ao_key = NULL;
u8 maclen = tcp_rsk(req)->maclen;
u8 keyid = tcp_rsk(req)->ao_keyid;

ao_key = tcp_sk(sk)->af_specific->ao_lookup(sk, req_to_sk(req),
Expand All @@ -3730,13 +3729,11 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,
* for another peer-matching key, but the peer has requested
* ao_keyid (RFC5925 RNextKeyID), so let's keep it simple here.
*/
if (unlikely(!ao_key || tcp_ao_maclen(ao_key) != maclen)) {
u8 key_maclen = ao_key ? tcp_ao_maclen(ao_key) : 0;

if (unlikely(!ao_key)) {
rcu_read_unlock();
kfree_skb(skb);
net_warn_ratelimited("TCP-AO: the keyid %u with maclen %u|%u from SYN packet is not present - not sending SYNACK\n",
keyid, maclen, key_maclen);
net_warn_ratelimited("TCP-AO: the keyid %u from SYN packet is not present - not sending SYNACK\n",
keyid);
return NULL;
}
key.ao_key = ao_key;
Expand Down
2 changes: 1 addition & 1 deletion net/ipv6/tcp_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,7 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32
if (tcp_key_is_md5(key))
tot_len += TCPOLEN_MD5SIG_ALIGNED;
if (tcp_key_is_ao(key))
tot_len += tcp_ao_len(key->ao_key);
tot_len += tcp_ao_len_aligned(key->ao_key);

#ifdef CONFIG_MPTCP
if (rst && !tcp_key_is_md5(key)) {
Expand Down

0 comments on commit 3142dbf

Please sign in to comment.