aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMarcin Wojtas <mw@FreeBSD.org>2020-10-16 11:24:12 +0000
committerMarcin Wojtas <mw@FreeBSD.org>2020-10-16 11:24:12 +0000
commit8b7f39947c4437c48365e9aa38696225bb854112 (patch)
tree4dba8e18a65a1bd2a410ff795662c41510b1c04a /sys
parent1c34dcb5325a19a983812abf137de47fc051ece8 (diff)
downloadsrc-8b7f39947c4437c48365e9aa38696225bb854112.tar.gz
src-8b7f39947c4437c48365e9aa38696225bb854112.zip
Notes
Diffstat (limited to 'sys')
-rw-r--r--sys/netipsec/ipsec.c293
-rw-r--r--sys/netipsec/ipsec.h2
-rw-r--r--sys/netipsec/key_debug.c4
-rw-r--r--sys/netipsec/keydb.h5
-rw-r--r--sys/netipsec/xform_ah.c9
-rw-r--r--sys/netipsec/xform_esp.c5
6 files changed, 222 insertions, 96 deletions
diff --git a/sys/netipsec/ipsec.c b/sys/netipsec/ipsec.c
index 7d336641b96e..cd24750607ea 100644
--- a/sys/netipsec/ipsec.c
+++ b/sys/netipsec/ipsec.c
@@ -1173,6 +1173,66 @@ ipsec_hdrsiz_inpcb(struct inpcb *inp)
return (sz);
}
+
+#define IPSEC_BITMAP_INDEX_MASK(w) (w - 1)
+#define IPSEC_REDUNDANT_BIT_SHIFTS 5
+#define IPSEC_REDUNDANT_BITS (1 << IPSEC_REDUNDANT_BIT_SHIFTS)
+#define IPSEC_BITMAP_LOC_MASK (IPSEC_REDUNDANT_BITS - 1)
+
+/*
+ * Functions below are responsible for checking and updating bitmap.
+ * These are used to separate ipsec_chkreplay() and ipsec_updatereplay()
+ * from window implementation
+ *
+ * Based on RFC 6479. Blocks are 32 bits unsigned integers
+ */
+
+static inline int
+check_window(const struct secreplay *replay, uint64_t seq)
+{
+ int index, bit_location;
+
+ bit_location = seq & IPSEC_BITMAP_LOC_MASK;
+ index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS)
+ & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
+
+ /* This packet already seen? */
+ return ((replay->bitmap)[index] & (1 << bit_location));
+}
+
+static inline void
+advance_window(const struct secreplay *replay, uint64_t seq)
+{
+ int i;
+ uint64_t index, index_cur, diff;
+
+ index_cur = replay->last >> IPSEC_REDUNDANT_BIT_SHIFTS;
+ index = seq >> IPSEC_REDUNDANT_BIT_SHIFTS;
+ diff = index - index_cur;
+
+ if (diff > replay->bitmap_size) {
+ /* something unusual in this case */
+ diff = replay->bitmap_size;
+ }
+
+ for (i = 0; i < diff; i++) {
+ replay->bitmap[(i + index_cur + 1)
+ & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size)] = 0;
+ }
+}
+
+static inline void
+set_window(const struct secreplay *replay, uint64_t seq)
+{
+ int index, bit_location;
+
+ bit_location = seq & IPSEC_BITMAP_LOC_MASK;
+ index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS)
+ & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
+
+ replay->bitmap[index] |= (1 << bit_location);
+}
+
/*
* Check the variable replay window.
* ipsec_chkreplay() performs replay check before ICV verification.
@@ -1181,20 +1241,17 @@ ipsec_hdrsiz_inpcb(struct inpcb *inp)
* beforehand).
* 0 (zero) is returned if packet disallowed, 1 if packet permitted.
*
- * Based on RFC 6479. Blocks are 32 bits unsigned integers
+ * Based on RFC 4303
*/
-#define IPSEC_BITMAP_INDEX_MASK(w) (w - 1)
-#define IPSEC_REDUNDANT_BIT_SHIFTS 5
-#define IPSEC_REDUNDANT_BITS (1 << IPSEC_REDUNDANT_BIT_SHIFTS)
-#define IPSEC_BITMAP_LOC_MASK (IPSEC_REDUNDANT_BITS - 1)
-
int
-ipsec_chkreplay(uint32_t seq, struct secasvar *sav)
+ipsec_chkreplay(uint32_t seq, uint32_t *seqhigh, struct secasvar *sav)
{
- const struct secreplay *replay;
- uint32_t wsizeb; /* Constant: window size. */
- int index, bit_location;
+ char buf[128];
+ struct secreplay *replay;
+ uint32_t window;
+ uint32_t tl, th, bl;
+ uint32_t seqh;
IPSEC_ASSERT(sav != NULL, ("Null SA"));
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state"));
@@ -1205,36 +1262,96 @@ ipsec_chkreplay(uint32_t seq, struct secasvar *sav)
if (replay->wsize == 0)
return (1);
- /* Constant. */
- wsizeb = replay->wsize << 3;
-
- /* Sequence number of 0 is invalid. */
- if (seq == 0)
+ /* Zero sequence number is not allowed. */
+ if (seq == 0 && replay->last == 0)
return (0);
- /* First time is always okay. */
- if (replay->count == 0)
- return (1);
+ window = replay->wsize << 3; /* Size of window */
+ tl = (uint32_t)replay->last; /* Top of window, lower part */
+ th = (uint32_t)(replay->last >> 32); /* Top of window, high part */
+ bl = tl - window + 1; /* Bottom of window, lower part */
+
+ /*
+ * We keep the high part intact when:
+ * 1) the seq is within [bl, 0xffffffff] and the whole window is
+ * within one subspace;
+ * 2) the seq is within [0, bl) and window spans two subspaces.
+ */
+ if ((tl >= window - 1 && seq >= bl) ||
+ (tl < window - 1 && seq < bl)) {
+ *seqhigh = th;
+ if (seq <= tl) {
+ /* Sequence number inside window - check against replay */
+ if (check_window(replay, seq))
+ return (0);
+ }
- /* Larger sequences are okay. */
- if (seq > replay->lastseq)
+ /* Sequence number above top of window or not found in bitmap */
return (1);
+ }
- /* Over range to check, i.e. too old or wrapped. */
- if (replay->lastseq - seq >= wsizeb)
- return (0);
+ /*
+ * If ESN is not enabled and packet with highest sequence number
+ * was received we should report overflow
+ */
+ if (tl == 0xffffffff && !(sav->flags & SADB_X_SAFLAGS_ESN)) {
+ /* Set overflow flag. */
+ replay->overflow++;
+
+ if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+ if (sav->sah->saidx.proto == IPPROTO_ESP)
+ ESPSTAT_INC(esps_wrap);
+ else if (sav->sah->saidx.proto == IPPROTO_AH)
+ AHSTAT_INC(ahs_wrap);
+ return (0);
+ }
- /* The sequence is inside the sliding window
- * now check the bit in the bitmap
- * bit location only depends on the sequence number
+ ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n",
+ __func__, replay->overflow,
+ ipsec_sa2str(sav, buf, sizeof(buf))));
+ }
+
+ /*
+ * Seq is within [bl, 0xffffffff] and bl is within
+ * [0xffffffff-window, 0xffffffff]. This means we got a seq
+ * which is within our replay window, but in the previous
+ * subspace.
*/
- bit_location = seq & IPSEC_BITMAP_LOC_MASK;
- index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS)
- & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
+ if (tl < window - 1 && seq >= bl) {
+ if (th == 0)
+ return (0);
+ *seqhigh = th - 1;
+ seqh = th - 1;
+ if (check_window(replay, seq))
+ return (0);
+ return (1);
+ }
+
+ /*
+ * Seq is within [0, bl) but the whole window is within one subspace.
+ * This means that seq has wrapped and is in next subspace
+ */
+ *seqhigh = th + 1;
+ seqh = th + 1;
+
+ /* Don't let high part wrap. */
+ if (seqh == 0) {
+ /* Set overflow flag. */
+ replay->overflow++;
+
+ if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+ if (sav->sah->saidx.proto == IPPROTO_ESP)
+ ESPSTAT_INC(esps_wrap);
+ else if (sav->sah->saidx.proto == IPPROTO_AH)
+ AHSTAT_INC(ahs_wrap);
+ return (0);
+ }
+
+ ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n",
+ __func__, replay->overflow,
+ ipsec_sa2str(sav, buf, sizeof(buf))));
+ }
- /* This packet already seen? */
- if ((replay->bitmap)[index] & (1 << bit_location))
- return (0);
return (1);
}
@@ -1246,84 +1363,90 @@ ipsec_chkreplay(uint32_t seq, struct secasvar *sav)
int
ipsec_updatereplay(uint32_t seq, struct secasvar *sav)
{
- char buf[128];
struct secreplay *replay;
- uint32_t wsizeb; /* Constant: window size. */
- int diff, index, bit_location;
+ uint32_t window;
+ uint32_t tl, th, bl;
+ uint32_t seqh;
IPSEC_ASSERT(sav != NULL, ("Null SA"));
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state"));
replay = sav->replay;
+ /* No need to check replay if disabled. */
if (replay->wsize == 0)
- goto ok; /* No need to check replay. */
-
- /* Constant. */
- wsizeb = replay->wsize << 3;
+ return (0);
- /* Sequence number of 0 is invalid. */
- if (seq == 0)
+ /* Zero sequence number is not allowed. */
+ if (seq == 0 && replay->last == 0)
return (1);
- /* The packet is too old, no need to update */
- if (wsizeb + seq < replay->lastseq)
- goto ok;
+ window = replay->wsize << 3; /* Size of window */
+ tl = (uint32_t)replay->last; /* Top of window, lower part */
+ th = (uint32_t)(replay->last >> 32); /* Top of window, high part */
+ bl = tl - window + 1; /* Bottom of window, lower part */
- /* Now update the bit */
- index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS);
-
- /* First check if the sequence number is in the range */
- if (seq > replay->lastseq) {
- int id;
- int index_cur = replay->lastseq >> IPSEC_REDUNDANT_BIT_SHIFTS;
-
- diff = index - index_cur;
- if (diff > replay->bitmap_size) {
- /* something unusual in this case */
- diff = replay->bitmap_size;
- }
-
- for (id = 0; id < diff; ++id) {
- replay->bitmap[(id + index_cur + 1)
- & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size)] = 0;
+ /*
+ * We keep the high part intact when:
+ * 1) the seq is within [bl, 0xffffffff] and the whole window is
+ * within one subspace;
+ * 2) the seq is within [0, bl) and window spans two subspaces.
+ */
+ if ((tl >= window - 1 && seq >= bl) ||
+ (tl < window - 1 && seq < bl)) {
+ seqh = th;
+ if (seq <= tl) {
+ /* Sequence number inside window - check against replay */
+ if (check_window(replay, seq))
+ return (1);
+ set_window(replay, seq);
+ } else {
+ advance_window(replay, ((uint64_t)seqh << 32) | seq);
+ set_window(replay, seq);
+ replay->last = ((uint64_t)seqh << 32) | seq;
}
- replay->lastseq = seq;
+ /* Sequence number above top of window or not found in bitmap */
+ replay->count++;
+ return (0);
}
- index &= IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
- bit_location = seq & IPSEC_BITMAP_LOC_MASK;
-
- /* this packet has already been received */
- if (replay->bitmap[index] & (1 << bit_location))
+ if (!(sav->flags & SADB_X_SAFLAGS_ESN))
return (1);
- replay->bitmap[index] |= (1 << bit_location);
-
-ok:
- if (replay->count == ~0) {
- /* Set overflow flag. */
- replay->overflow++;
-
- /* Don't increment, no more packets accepted. */
- if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
- if (sav->sah->saidx.proto == IPPROTO_AH)
- AHSTAT_INC(ahs_wrap);
- else if (sav->sah->saidx.proto == IPPROTO_ESP)
- ESPSTAT_INC(esps_wrap);
+ /*
+ * Seq is within [bl, 0xffffffff] and bl is within
+ * [0xffffffff-window, 0xffffffff]. This means we got a seq
+ * which is within our replay window, but in the previous
+ * subspace.
+ */
+ if (tl < window - 1 && seq >= bl) {
+ if (th == 0)
+ return (1);
+ if (check_window(replay, seq))
return (1);
- }
- ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n",
- __func__, replay->overflow,
- ipsec_sa2str(sav, buf, sizeof(buf))));
+ set_window(replay, seq);
+ replay->count++;
+ return (0);
}
+ /*
+ * Seq is within [0, bl) but the whole window is within one subspace.
+ * This means that seq has wrapped and is in next subspace
+ */
+ seqh = th + 1;
+
+ /* Don't let high part wrap. */
+ if (seqh == 0)
+ return (1);
+
+ advance_window(replay, ((uint64_t)seqh << 32) | seq);
+ set_window(replay, seq);
+ replay->last = ((uint64_t)seqh << 32) | seq;
replay->count++;
return (0);
}
-
int
ipsec_updateid(struct secasvar *sav, crypto_session_t *new,
crypto_session_t *old)
diff --git a/sys/netipsec/ipsec.h b/sys/netipsec/ipsec.h
index e7a359159aa0..c7a44d60f082 100644
--- a/sys/netipsec/ipsec.h
+++ b/sys/netipsec/ipsec.h
@@ -325,7 +325,7 @@ int udp_ipsec_output(struct mbuf *, struct secasvar *);
int udp_ipsec_input(struct mbuf *, int, int);
int udp_ipsec_pcbctl(struct inpcb *, struct sockopt *);
-int ipsec_chkreplay(uint32_t, struct secasvar *);
+int ipsec_chkreplay(uint32_t, uint32_t *, struct secasvar *);
int ipsec_updatereplay(uint32_t, struct secasvar *);
int ipsec_updateid(struct secasvar *, crypto_session_t *, crypto_session_t *);
int ipsec_initialized(void);
diff --git a/sys/netipsec/key_debug.c b/sys/netipsec/key_debug.c
index d746bfbbe800..4f03d78d434f 100644
--- a/sys/netipsec/key_debug.c
+++ b/sys/netipsec/key_debug.c
@@ -809,8 +809,8 @@ kdebug_secreplay(struct secreplay *rpl)
int len, l;
IPSEC_ASSERT(rpl != NULL, ("null rpl"));
- printf(" secreplay{ count=%u bitmap_size=%u wsize=%u seq=%u lastseq=%u",
- rpl->count, rpl->bitmap_size, rpl->wsize, rpl->seq, rpl->lastseq);
+ printf(" secreplay{ count=%lu bitmap_size=%u wsize=%u last=%lu",
+ rpl->count, rpl->bitmap_size, rpl->wsize, rpl->last);
if (rpl->bitmap == NULL) {
printf(" }\n");
diff --git a/sys/netipsec/keydb.h b/sys/netipsec/keydb.h
index 6993b4e4ff1d..e1a6403458ec 100644
--- a/sys/netipsec/keydb.h
+++ b/sys/netipsec/keydb.h
@@ -202,10 +202,9 @@ struct secasvar {
* (c) read only except during creation / free
*/
struct secreplay {
- u_int32_t count; /* (m) */
+ u_int64_t count; /* (m) */
u_int wsize; /* (c) window size, i.g. 4 bytes */
- u_int32_t seq; /* (m) used by sender */
- u_int32_t lastseq; /* (m) used by receiver */
+ u_int64_t last; /* (m) used by receiver */
u_int32_t *bitmap; /* (m) used by receiver */
u_int bitmap_size; /* (c) size of the bitmap array */
int overflow; /* (m) overflow flag */
diff --git a/sys/netipsec/xform_ah.c b/sys/netipsec/xform_ah.c
index a707930b046c..630805dd8cf9 100644
--- a/sys/netipsec/xform_ah.c
+++ b/sys/netipsec/xform_ah.c
@@ -534,6 +534,7 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
struct newah *ah;
crypto_session_t cryptoid;
int hl, rplen, authsize, ahsize, error;
+ uint32_t seqh;
IPSEC_ASSERT(sav != NULL, ("null SA"));
IPSEC_ASSERT(sav->key_auth != NULL, ("null authentication key"));
@@ -557,7 +558,7 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
/* Check replay window, if applicable. */
SECASVAR_LOCK(sav);
if (sav->replay != NULL && sav->replay->wsize != 0 &&
- ipsec_chkreplay(ntohl(ah->ah_seq), sav) == 0) {
+ ipsec_chkreplay(ntohl(ah->ah_seq), &seqh, sav) == 0) {
SECASVAR_UNLOCK(sav);
AHSTAT_INC(ahs_replay);
DPRINTF(("%s: packet replay failure: %s\n", __func__,
@@ -925,7 +926,9 @@ ah_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
/* Insert packet replay counter, as requested. */
SECASVAR_LOCK(sav);
if (sav->replay) {
- if (sav->replay->count == ~0 &&
+ if ((sav->replay->count == ~0 ||
+ (!(sav->flags & SADB_X_SAFLAGS_ESN) &&
+ ((uint32_t)sav->replay->count) == ~0)) &&
(sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
SECASVAR_UNLOCK(sav);
DPRINTF(("%s: replay counter wrapped for SA %s/%08lx\n",
@@ -940,7 +943,7 @@ ah_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
if (!V_ipsec_replay)
#endif
sav->replay->count++;
- ah->ah_seq = htonl(sav->replay->count);
+ ah->ah_seq = htonl((uint32_t)sav->replay->count);
}
cryptoid = sav->tdb_cryptoid;
SECASVAR_UNLOCK(sav);
diff --git a/sys/netipsec/xform_esp.c b/sys/netipsec/xform_esp.c
index 803d36da5f82..3caa72218ff0 100644
--- a/sys/netipsec/xform_esp.c
+++ b/sys/netipsec/xform_esp.c
@@ -262,6 +262,7 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
uint8_t *ivp;
crypto_session_t cryptoid;
int alen, error, hlen, plen;
+ uint32_t seqh;
IPSEC_ASSERT(sav != NULL, ("null SA"));
IPSEC_ASSERT(sav->tdb_encalgxform != NULL, ("null encoding xform"));
@@ -320,7 +321,7 @@ esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
*/
SECASVAR_LOCK(sav);
if (esph != NULL && sav->replay != NULL && sav->replay->wsize != 0) {
- if (ipsec_chkreplay(ntohl(esp->esp_seq), sav) == 0) {
+ if (ipsec_chkreplay(ntohl(esp->esp_seq), &seqh, sav) == 0) {
SECASVAR_UNLOCK(sav);
DPRINTF(("%s: packet replay check for %s\n", __func__,
ipsec_sa2str(sav, buf, sizeof(buf))));
@@ -740,7 +741,7 @@ esp_output(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
if (!V_ipsec_replay)
#endif
sav->replay->count++;
- replay = htonl(sav->replay->count);
+ replay = htonl((uint32_t)sav->replay->count);
bcopy((caddr_t) &replay, mtod(mo, caddr_t) + roff +
sizeof(uint32_t), sizeof(uint32_t));