netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sabrina Dubroca <sd@queasysnail.net>
To: Antonio Quartulli <antonio@openvpn.net>
Cc: netdev@vger.kernel.org, kuba@kernel.org, pabeni@redhat.com,
	ryazanov.s.a@gmail.com, edumazet@google.com, andrew@lunn.ch
Subject: Re: [PATCH net-next v6 12/25] ovpn: implement packet processing
Date: Mon, 2 Sep 2024 16:42:11 +0200	[thread overview]
Message-ID: <ZtXOw-NcL9lvwWa8@hog> (raw)
In-Reply-To: <20240827120805.13681-13-antonio@openvpn.net>

2024-08-27, 14:07:52 +0200, Antonio Quartulli wrote:
> +/* this swap is not atomic, but there will be a very short time frame where the

Since we're under a mutex, I think we might get put to sleep for a
not-so-short time frame.

> + * old_secondary key won't be available. This should not be a big deal as most

I could be misreading the code, but isn't it old_primary that's
unavailable during the swap? rcu_replace_pointer overwrites
cs->primary, so before the final assign, both slots contain
old_secondary?

> + * likely both peers are already using the new primary at this point.
> + */
> +void ovpn_crypto_key_slots_swap(struct ovpn_crypto_state *cs)
> +{
> +	const struct ovpn_crypto_key_slot *old_primary, *old_secondary;
> +
> +	mutex_lock(&cs->mutex);
> +
> +	old_secondary = rcu_dereference_protected(cs->secondary,
> +						  lockdep_is_held(&cs->mutex));
> +	old_primary = rcu_replace_pointer(cs->primary, old_secondary,
> +					  lockdep_is_held(&cs->mutex));
> +	rcu_assign_pointer(cs->secondary, old_primary);
> +
> +	pr_debug("key swapped: %u <-> %u\n",
> +		 old_primary ? old_primary->key_id : 0,
> +		 old_secondary ? old_secondary->key_id : 0);
> +
> +	mutex_unlock(&cs->mutex);
> +}

[...]
> +int ovpn_aead_encrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
> +		      struct sk_buff *skb)
> +{
> +	const unsigned int tag_size = crypto_aead_authsize(ks->encrypt);
> +	const unsigned int head_size = ovpn_aead_encap_overhead(ks);
> +	DECLARE_CRYPTO_WAIT(wait);

nit: unused

> +	struct aead_request *req;
> +	struct sk_buff *trailer;
> +	struct scatterlist *sg;
> +	u8 iv[NONCE_SIZE];
> +	int nfrags, ret;
> +	u32 pktid, op;
> +
> +	/* Sample AEAD header format:
> +	 * 48000001 00000005 7e7046bd 444a7e28 cc6387b1 64a4d6c1 380275a...
> +	 * [ OP32 ] [seq # ] [             auth tag            ] [ payload ... ]
> +	 *          [4-byte
> +	 *          IV head]
> +	 */
> +
> +	/* check that there's enough headroom in the skb for packet
> +	 * encapsulation, after adding network header and encryption overhead
> +	 */
> +	if (unlikely(skb_cow_head(skb, OVPN_HEAD_ROOM + head_size)))
> +		return -ENOBUFS;
> +
> +	/* get number of skb frags and ensure that packet data is writable */
> +	nfrags = skb_cow_data(skb, 0, &trailer);
> +	if (unlikely(nfrags < 0))
> +		return nfrags;
> +
> +	if (unlikely(nfrags + 2 > (MAX_SKB_FRAGS + 2)))
> +		return -ENOSPC;
> +
> +	ovpn_skb_cb(skb)->ctx = kmalloc(sizeof(*ovpn_skb_cb(skb)->ctx),
> +					GFP_ATOMIC);
> +	if (unlikely(!ovpn_skb_cb(skb)->ctx))
> +		return -ENOMEM;

I think you should clear skb->cb (or at least ->ctx) at the start of
ovpn_aead_encrypt. I don't think it will be cleaned up by the previous
user, and if we fail before this alloc, we will possibly have bogus
values in ->ctx when we get to kfree(ovpn_skb_cb(skb)->ctx) at the end
of ovpn_encrypt_post.

(Similar comments around cb/ctx freeing and initialization apply to
ovpn_aead_decrypt and ovpn_decrypt_post)

> +	sg = ovpn_skb_cb(skb)->ctx->sg;
> +
> +	/* sg table:
> +	 * 0: op, wire nonce (AD, len=OVPN_OP_SIZE_V2+NONCE_WIRE_SIZE),
> +	 * 1, 2, 3, ..., n: payload,
> +	 * n+1: auth_tag (len=tag_size)
> +	 */
> +	sg_init_table(sg, nfrags + 2);
> +
> +	/* build scatterlist to encrypt packet payload */
> +	ret = skb_to_sgvec_nomark(skb, sg + 1, 0, skb->len);
> +	if (unlikely(nfrags != ret)) {
> +		kfree(sg);

This is the only location in this function (and ovpn_encrypt_post)
that frees sg. Is that correct? sg points to an array contained within
->ctx, I don't think you want to free that directly.

> +		return -EINVAL;
> +	}
> +
> +	/* append auth_tag onto scatterlist */
> +	__skb_push(skb, tag_size);
> +	sg_set_buf(sg + nfrags + 1, skb->data, tag_size);
> +
> +	/* obtain packet ID, which is used both as a first
> +	 * 4 bytes of nonce and last 4 bytes of associated data.
> +	 */
> +	ret = ovpn_pktid_xmit_next(&ks->pid_xmit, &pktid);
> +	if (unlikely(ret < 0)) {
> +		kfree(ovpn_skb_cb(skb)->ctx);

Isn't that going to cause a double-free when we get to the end of
ovpn_encrypt_post? Or even UAF when we try to get ks/peer at the
start?

> +		return ret;
> +	}
> +
> +	/* concat 4 bytes packet id and 8 bytes nonce tail into 12 bytes
> +	 * nonce
> +	 */
> +	ovpn_pktid_aead_write(pktid, &ks->nonce_tail_xmit, iv);
> +
> +	/* make space for packet id and push it to the front */
> +	__skb_push(skb, NONCE_WIRE_SIZE);
> +	memcpy(skb->data, iv, NONCE_WIRE_SIZE);
> +
> +	/* add packet op as head of additional data */
> +	op = ovpn_opcode_compose(OVPN_DATA_V2, ks->key_id, peer->id);
> +	__skb_push(skb, OVPN_OP_SIZE_V2);
> +	BUILD_BUG_ON(sizeof(op) != OVPN_OP_SIZE_V2);
> +	*((__force __be32 *)skb->data) = htonl(op);
> +
> +	/* AEAD Additional data */
> +	sg_set_buf(sg, skb->data, OVPN_OP_SIZE_V2 + NONCE_WIRE_SIZE);
> +
> +	req = aead_request_alloc(ks->encrypt, GFP_ATOMIC);
> +	if (unlikely(!req)) {
> +		kfree(ovpn_skb_cb(skb)->ctx);

Same here.

> +		return -ENOMEM;
> +	}
> +
> +	/* setup async crypto operation */
> +	aead_request_set_tfm(req, ks->encrypt);
> +	aead_request_set_callback(req, 0, ovpn_aead_encrypt_done, skb);
> +	aead_request_set_crypt(req, sg, sg, skb->len - head_size, iv);
> +	aead_request_set_ad(req, OVPN_OP_SIZE_V2 + NONCE_WIRE_SIZE);
> +
> +	ovpn_skb_cb(skb)->ctx->peer = peer;
> +	ovpn_skb_cb(skb)->ctx->req = req;
> +	ovpn_skb_cb(skb)->ctx->ks = ks;
> +
> +	/* encrypt it */
> +	return crypto_aead_encrypt(req);
> +}
> +
> +static void ovpn_aead_decrypt_done(void *data, int ret)
> +{
> +	struct sk_buff *skb = data;
> +
> +	aead_request_free(ovpn_skb_cb(skb)->ctx->req);

This function only gets called in the async case. Where's the
corresponding aead_request_free in the sync case? (same for encrypt)
This should be moved into ovpn_decrypt_post, I think.

> +	ovpn_decrypt_post(skb, ret);
> +}
> +
> +int ovpn_aead_decrypt(struct ovpn_peer *peer, struct ovpn_crypto_key_slot *ks,
> +		      struct sk_buff *skb)
> +{
> +	const unsigned int tag_size = crypto_aead_authsize(ks->decrypt);
> +	int ret, payload_len, nfrags;
> +	unsigned int payload_offset;
> +	DECLARE_CRYPTO_WAIT(wait);

nit: unused


[...]
> -static void ovpn_encrypt_post(struct sk_buff *skb, int ret)
> +void ovpn_encrypt_post(struct sk_buff *skb, int ret)
>  {
> -	struct ovpn_peer *peer = ovpn_skb_cb(skb)->peer;
> +	struct ovpn_crypto_key_slot *ks = ovpn_skb_cb(skb)->ctx->ks;
> +	struct ovpn_peer *peer = ovpn_skb_cb(skb)->ctx->peer;

ovpn_skb_cb(skb)->ctx may not have been set by ovpn_aead_encrypt.

> +
> +	/* encryption is happening asynchronously. This function will be
> +	 * called later by the crypto callback with a proper return value
> +	 */
> +	if (unlikely(ret == -EINPROGRESS))
> +		return;
>  
>  	if (unlikely(ret < 0))
>  		goto err;
>  
>  	skb_mark_not_on_list(skb);
>  
> +	kfree(ovpn_skb_cb(skb)->ctx);
> +
>  	switch (peer->sock->sock->sk->sk_protocol) {
>  	case IPPROTO_UDP:
>  		ovpn_udp_send_skb(peer->ovpn, peer, skb);
>  		break;
>  	default:
>  		/* no transport configured yet */
>  		goto err;

ovpn_skb_cb(skb)->ctx has just been freed before this switch, and here
we jump to err and free it again.

>  	}
>  	/* skb passed down the stack - don't free it */
>  	skb = NULL;
>  err:
>  	if (unlikely(skb)) {
>  		dev_core_stats_tx_dropped_inc(peer->ovpn->dev);
> -		kfree_skb(skb);
> +		kfree(ovpn_skb_cb(skb)->ctx);
>  	}
> +	kfree_skb(skb);
> +	ovpn_crypto_key_slot_put(ks);
>  	ovpn_peer_put(peer);
>  }

-- 
Sabrina


  reply	other threads:[~2024-09-02 14:42 UTC|newest]

Thread overview: 58+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-08-27 12:07 [PATCH net-next v6 00/25] Introducing OpenVPN Data Channel Offload Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 01/25] netlink: add NLA_POLICY_MAX_LEN macro Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 02/25] rtnetlink: don't crash on unregister if no dellink exists Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 03/25] net: introduce OpenVPN Data Channel Offload (ovpn) Antonio Quartulli
2024-09-05 14:38   ` Sabrina Dubroca
2024-09-06 12:26     ` Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 04/25] ovpn: add basic netlink support Antonio Quartulli
2024-09-06 19:26   ` Simon Horman
2024-09-09  8:35     ` Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 05/25] ovpn: add basic interface creation/destruction/management routines Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 06/25] ovpn: implement interface creation/destruction via netlink Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 07/25] ovpn: keep carrier always on Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 08/25] ovpn: introduce the ovpn_peer object Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 09/25] ovpn: introduce the ovpn_socket object Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 10/25] ovpn: implement basic TX path (UDP) Antonio Quartulli
2024-08-30 17:02   ` Sabrina Dubroca
2024-09-02 12:03     ` Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 11/25] ovpn: implement basic RX " Antonio Quartulli
2024-09-02 11:22   ` Sabrina Dubroca
2024-09-02 12:24     ` Antonio Quartulli
2024-09-06 19:18   ` Simon Horman
2024-09-09  8:37     ` Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 12/25] ovpn: implement packet processing Antonio Quartulli
2024-09-02 14:42   ` Sabrina Dubroca [this message]
2024-09-04 12:07     ` Antonio Quartulli
2024-09-04 15:01       ` Sabrina Dubroca
2024-09-06 13:19         ` Antonio Quartulli
2024-09-10 13:04           ` Sabrina Dubroca
2024-09-11 12:52             ` Antonio Quartulli
2024-09-11 13:30               ` Sabrina Dubroca
2024-09-12  8:33                 ` Antonio Quartulli
2024-09-22 19:51                   ` Sergey Ryazanov
2024-09-23 12:48                     ` Antonio Quartulli
2024-09-06 19:29   ` Simon Horman
2024-09-09  8:38     ` Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 13/25] ovpn: store tunnel and transport statistics Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 14/25] ovpn: implement TCP transport Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 15/25] ovpn: implement multi-peer support Antonio Quartulli
2024-09-03 14:40   ` Sabrina Dubroca
2024-09-04 10:10     ` Sabrina Dubroca
2024-09-06 13:26       ` Antonio Quartulli
2024-09-05  8:02     ` Antonio Quartulli
2024-09-05 10:47       ` Sabrina Dubroca
2024-09-09  9:12         ` Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 16/25] ovpn: implement peer lookup logic Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 17/25] ovpn: implement keepalive mechanism Antonio Quartulli
2024-09-03 15:17   ` Sabrina Dubroca
2024-09-09  9:17     ` Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 18/25] ovpn: add support for updating local UDP endpoint Antonio Quartulli
2024-08-27 12:07 ` [PATCH net-next v6 19/25] ovpn: add support for peer floating Antonio Quartulli
2024-09-05  9:55   ` Sabrina Dubroca
2024-09-09  8:52     ` Antonio Quartulli
2024-08-27 12:08 ` [PATCH net-next v6 20/25] ovpn: implement peer add/dump/delete via netlink Antonio Quartulli
2024-08-27 12:08 ` [PATCH net-next v6 21/25] ovpn: implement key add/del/swap " Antonio Quartulli
2024-08-27 12:08 ` [PATCH net-next v6 22/25] ovpn: kill key and notify userspace in case of IV exhaustion Antonio Quartulli
2024-08-27 12:08 ` [PATCH net-next v6 23/25] ovpn: notify userspace when a peer is deleted Antonio Quartulli
2024-08-27 12:08 ` [PATCH net-next v6 24/25] ovpn: add basic ethtool support Antonio Quartulli
2024-08-27 12:08 ` [PATCH net-next v6 25/25] testing/selftest: add test tool and scripts for ovpn module Antonio Quartulli

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZtXOw-NcL9lvwWa8@hog \
    --to=sd@queasysnail.net \
    --cc=andrew@lunn.ch \
    --cc=antonio@openvpn.net \
    --cc=edumazet@google.com \
    --cc=kuba@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=ryazanov.s.a@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).