openconnect-unknown/esp-seqno.c

1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2015 Intel Corporation.
5  *
6  * Author: David Woodhouse <dwmw2@infradead.org>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  */
17 
18 #include <config.h>
19 
20 #include <stdint.h>
21 #include <inttypes.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 
25 #include "openconnect-internal.h"
26 
27 #define DTLS_EMPTY_BITMAP		(0xFFFFFFFFFFFFFFFFULL)
28 
29 /* Eventually we're going to have to have more than one incoming ESP
30    context at a time, to allow for the overlap period during a rekey.
31    So pass the 'esp' even though for now it's redundant. */
32 int verify_packet_seqno(struct openconnect_info *vpninfo,
33 			struct esp *esp, uint32_t seq)
34 {
35 	/*
36 	 * For incoming, esp->seq is the next *expected* packet, being
37 	 * the sequence number *after* the latest we have received.
38 	 *
39 	 * Since it must always be true that packet esp->seq-1 has been
40 	 * received, so there's no need to explicitly record that.
41 	 *
42 	 * So the backlog bitmap covers the 64 packets prior to that,
43 	 * with the LSB representing packet (esp->seq - 2), and the MSB
44 	 * representing (esp->seq - 65). A received packet is represented
45 	 * by a zero bit, and a missing packet is represented by a one.
46 	 *
47 	 * Thus we can allow out-of-order reception of packets that are
48 	 * within a reasonable interval of the latest packet received.
49 	 */
50 
51 	if (seq == esp->seq) {
52 		/* The common case. This is the packet we expected next. */
53 		esp->seq_backlog <<= 1;
54 
55 		/* This might reach a value higher than the 32-bit ESP sequence
56 		 * numbers can actually reach. Which is fine. When that
57 		 * happens, we'll do the right thing and just not accept any
58 		 * newer packets. Someone needs to start a new epoch. */
59 		esp->seq++;
60 		vpn_progress(vpninfo, PRG_TRACE,
61 			     _("Accepting expected ESP packet with seq %u\n"),
62 			     seq);
63 		return 0;
64 	} else if (seq > esp->seq) {
65 		/* The packet we were expecting has gone missing; this one is newer.
66 		 * We always advance the window to accommodate it. */
67 		uint32_t delta = seq - esp->seq;
68 
69 		if (delta >= 64) {
70 			/* We jumped a long way into the future. We have not seen
71 			 * any of the previous 32 packets so set the backlog bitmap
72 			 * to all ones. */
73 			esp->seq_backlog = DTLS_EMPTY_BITMAP;
74 		} else if (delta == 63) {
75 			/* Avoid undefined behaviour that shifting by 64 would incur.
76 			 * The (clear) top bit represents the packet which is currently
77 			 * esp->seq - 1, which we know was already received. */
78 			esp->seq_backlog = DTLS_EMPTY_BITMAP >> 1;
79 		} else {
80 			/* We have missed (delta) packets. Shift the backlog by that
81 			 * amount *plus* the one we would have shifted it anyway if
82 			 * we'd received the packet we were expecting. The zero bit
83 			 * representing the packet which is currently esp->seq - 1,
84 			 * which we know has been received, ends up at bit position
85 			 * (1<<delta). Then we set all the bits lower than that, which
86 			 * represent the missing packets. */
87 			esp->seq_backlog <<= delta + 1;
88 			esp->seq_backlog |= (1ULL << delta) - 1;
89 		}
90 		vpn_progress(vpninfo, PRG_TRACE,
91 			     _("Accepting later-than-expected ESP packet with seq %u (expected %" PRIu64 ")\n"),
92 			     seq, esp->seq);
93 		esp->seq = (uint64_t)seq + 1;
94 		return 0;
95 	} else {
96 		/* This packet is older than the one we were expecting. By how much...? */
97 		uint32_t delta = esp->seq - seq;
98 
99 		/* delta==0 is the overflow case where esp->seq is 0x100000000 and seq is 0 */
100 		if (delta > 65 || delta == 0) {
101 			/* Too old. We can't know if it's a replay. */
102 			if (vpninfo->esp_replay_protect) {
103 				vpn_progress(vpninfo, PRG_DEBUG,
104 					     _("Discarding ancient ESP packet with seq %u (expected %" PRIu64 ")\n"),
105 					     seq, esp->seq);
106 				return -EINVAL;
107 			} else {
108 				vpn_progress(vpninfo, PRG_DEBUG,
109 					     _("Tolerating ancient ESP packet with seq %u (expected %" PRIu64 ")\n"),
110 					     seq, esp->seq);
111 				return 0;
112 			}
113 		} else if (delta == 1) {
114 			/* Not in the bitmask since it is by definition already received. */
115 		replayed:
116 			if (vpninfo->esp_replay_protect) {
117 				vpn_progress(vpninfo, PRG_DEBUG,
118 					     _("Discarding replayed ESP packet with seq %u\n"),
119 					     seq);
120 				return -EINVAL;
121 			} else {
122 				vpn_progress(vpninfo, PRG_DEBUG,
123 					     _("Tolerating replayed ESP packet with seq %u\n"),
124 					     seq);
125 				return 0;
126 			}
127 		} else {
128 			/* Within the backlog window, so we remember whether we've seen it or not. */
129 			uint64_t mask = 1ULL << (delta - 2);
130 
131 			if (!(esp->seq_backlog & mask))
132 				goto replayed;
133 
134 			esp->seq_backlog &= ~mask;
135 			vpn_progress(vpninfo, PRG_TRACE,
136 				     _("Accepting out-of-order ESP packet with seq %u (expected %" PRIu64 ")\n"),
137 				     seq, esp->seq);
138 			return 0;
139 		}
140 	}
141 }
142