git: 81877287a99e - main - if_ovpn: ensure we never re-use sequence numbers

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Tue, 23 May 2023 14:12:58 UTC
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=81877287a99e55b47a7045efa76945f368e90917

commit 81877287a99e55b47a7045efa76945f368e90917
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2023-05-20 17:43:49 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-05-23 14:11:26 +0000

    if_ovpn: ensure we never re-use sequence numbers
    
    if_ovpn already notified userpsace when there was a risk of sequence
    number re-use, but it trusted userspace to actually rotate the key.
    
    Convert the internal sequence number counter to 64 bits so we can detect
    overflows and then refuse to send packets.
    
    Event:          BSDCan 2023
    Reviewed by:    Leon Dang <ldang@netgate.com>
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D40187
---
 sys/net/if_ovpn.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/sys/net/if_ovpn.c b/sys/net/if_ovpn.c
index 3aec073c052e..49c8c8e9677a 100644
--- a/sys/net/if_ovpn.c
+++ b/sys/net/if_ovpn.c
@@ -93,7 +93,7 @@ struct ovpn_kkey_dir {
 	 * strictly higher than this.
 	 */
 	uint32_t		rx_seq;
-	uint32_t		tx_seq;
+	uint64_t		tx_seq;
 
 	/* Seen packets, relative to rx_seq. bit(0) will always be 0. */
 	uint64_t		rx_window;
@@ -1830,6 +1830,7 @@ ovpn_transmit_to_peer(struct ifnet *ifp, struct mbuf *m,
 	struct ovpn_softc *sc;
 	struct cryptop *crp;
 	uint32_t af, seq;
+	uint64_t seq64;
 	size_t len, ovpn_hdr_len;
 	int tunnel_len;
 	int ret;
@@ -1873,11 +1874,24 @@ ovpn_transmit_to_peer(struct ifnet *ifp, struct mbuf *m,
 	ohdr->opcode |= key->peerid;
 	ohdr->opcode = htonl(ohdr->opcode);
 
-	seq = atomic_fetchadd_32(&peer->keys[OVPN_KEY_SLOT_PRIMARY].encrypt->tx_seq, 1);
-	if (seq == OVPN_SEQ_ROTATE)
+	seq64 = atomic_fetchadd_64(&peer->keys[OVPN_KEY_SLOT_PRIMARY].encrypt->tx_seq, 1);
+	if (seq64 == OVPN_SEQ_ROTATE) {
 		ovpn_notify_key_rotation(sc, peer);
+	} else if (seq64 > UINT32_MAX) {
+		/* We've wrapped, give up on this packet. */
+		if (_ovpn_lock_trackerp != NULL)
+			OVPN_RUNLOCK(sc);
+		OVPN_COUNTER_ADD(sc, nomem_data_pkts_out, 1);
+
+		/* Let's avoid (very unlikely, but still) wraparounds of the
+		 * 64-bit counter taking us back to 0. */
+		atomic_set_64(&peer->keys[OVPN_KEY_SLOT_PRIMARY].encrypt->tx_seq,
+		    UINT32_MAX);
+
+		return (ENOBUFS);
+	}
 
-	seq = htonl(seq);
+	seq = htonl(seq64 & UINT32_MAX);
 	ohdr->seq = seq;
 
 	OVPN_PEER_COUNTER_ADD(peer, pkt_out, 1);