Page MenuHomeFreeBSD

D8468.id22078.diff
No OneTemporary

D8468.id22078.diff

Index: net/pfkeyv2.h
===================================================================
--- net/pfkeyv2.h
+++ net/pfkeyv2.h
@@ -97,10 +97,11 @@
u_int16_t sadb_sa_len;
u_int16_t sadb_sa_exttype;
u_int32_t sadb_sa_spi;
- u_int8_t sadb_sa_replay;
+ u_int32_t sadb_sa_replay;
u_int8_t sadb_sa_state;
u_int8_t sadb_sa_auth;
u_int8_t sadb_sa_encrypt;
+ u_int8_t sadb_sa_reserved[5];
u_int32_t sadb_sa_flags;
};
@@ -150,8 +151,7 @@
struct sadb_prop {
u_int16_t sadb_prop_len;
u_int16_t sadb_prop_exttype;
- u_int8_t sadb_prop_replay;
- u_int8_t sadb_prop_reserved[3];
+ u_int32_t sadb_prop_replay;
};
struct sadb_comb {
Index: netipsec/ipsec.c
===================================================================
--- netipsec/ipsec.c
+++ netipsec/ipsec.c
@@ -251,7 +251,6 @@
#endif
static void ipsec_delpcbpolicy(struct inpcbpolicy *);
static struct secpolicy *ipsec_deepcopy_policy(struct secpolicy *src);
-static void vshiftl(unsigned char *, int, int);
MALLOC_DEFINE(M_IPSEC_INPCB, "inpcbpolicy", "inpcb-resident ipsec policy");
@@ -1476,57 +1475,70 @@
* beforehand).
* 0 (zero) is returned if packet disallowed, 1 if packet permitted.
*
- * Based on RFC 2401.
+ * Based on RFC 6479. Blocks are 32 bits unsigned integers
*/
+
+#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(u_int32_t seq, struct secasvar *sav)
{
const struct secreplay *replay;
- u_int32_t diff;
- int fr;
- u_int32_t wsizeb; /* Constant: bits of window size. */
- int frlast; /* Constant: last frame. */
+ u_int32_t wsizeb; /* Constant: window size. */
+ int ret, index, bit_location;
IPSEC_ASSERT(sav != NULL, ("Null SA"));
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state"));
+ SECASVAR_LOCK(sav);
+
+ ret = 0;
replay = sav->replay;
+ /* No need to check replay if disabled. */
if (replay->wsize == 0)
- return (1); /* No need to check replay. */
+ goto allowed;
/* Constant. */
- frlast = replay->wsize - 1;
wsizeb = replay->wsize << 3;
/* Sequence number of 0 is invalid. */
if (seq == 0)
- return (0);
+ goto end;
/* First time is always okay. */
if (replay->count == 0)
- return (1);
-
- if (seq > replay->lastseq) {
- /* Larger sequences are okay. */
- return (1);
- } else {
- /* seq is equal or less than lastseq. */
- diff = replay->lastseq - seq;
+ goto allowed;
- /* Over range to check, i.e. too old or wrapped. */
- if (diff >= wsizeb)
- return (0);
-
- fr = frlast - diff / 8;
-
- /* This packet already seen? */
- if ((replay->bitmap)[fr] & (1 << (diff % 8)))
- return (0);
+ /* Larger sequences are okay. */
+ if (seq > replay->lastseq)
+ goto allowed;
+
+ /* Over range to check, i.e. too old or wrapped. */
+ if (replay->lastseq - seq >= wsizeb)
+ goto end;
+
+ /* The sequence is inside the sliding window
+ * now check the bit in the bitmap
+ * bit location only depends on the sequence number
+ */
+ bit_location = seq & IPSEC_BITMAP_LOC_MASK;
+ index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS)
+ & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
+
+ /* This packet already seen? */
+ if ((replay->bitmap)[index] & (1 << bit_location))
+ goto end;
+
+allowed:
+ ret = 1;
+end:
+ SECASVAR_UNLOCK(sav);
- /* Out of order but good. */
- return (1);
- }
+ return (ret);
}
/*
@@ -1539,72 +1551,61 @@
{
char buf[128];
struct secreplay *replay;
- u_int32_t diff;
- int fr;
- u_int32_t wsizeb; /* Constant: bits of window size. */
- int frlast; /* Constant: last frame. */
+ u_int32_t wsizeb; /* Constant: window size. */
+ int ret, diff, index, bit_location;
IPSEC_ASSERT(sav != NULL, ("Null SA"));
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state"));
+ SECASVAR_LOCK(sav);
+
+ ret = 1;
replay = sav->replay;
if (replay->wsize == 0)
goto ok; /* No need to check replay. */
/* Constant. */
- frlast = replay->wsize - 1;
wsizeb = replay->wsize << 3;
/* Sequence number of 0 is invalid. */
if (seq == 0)
- return (1);
+ goto end;
- /* First time. */
- if (replay->count == 0) {
- replay->lastseq = seq;
- bzero(replay->bitmap, replay->wsize);
- (replay->bitmap)[frlast] = 1;
+ /* The packet is too old, no need to update */
+ if (wsizeb + seq < replay->lastseq)
goto ok;
- }
+ /* Now update the bit */
+ index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS);
+
+ /* First check if the sequence number is in the range */
if (seq > replay->lastseq) {
- /* seq is larger than lastseq. */
- diff = seq - replay->lastseq;
+ int id;
+ int index_cur = replay->lastseq >> IPSEC_REDUNDANT_BIT_SHIFTS;
- /* New larger sequence number. */
- if (diff < wsizeb) {
- /* In window. */
- /* Set bit for this packet. */
- vshiftl(replay->bitmap, diff, replay->wsize);
- (replay->bitmap)[frlast] |= 1;
- } else {
- /* This packet has a "way larger". */
- bzero(replay->bitmap, replay->wsize);
- (replay->bitmap)[frlast] = 1;
+ diff = index - index_cur;
+ if (diff > replay->bitmap_size) {
+ /* something unusual in this case */
+ diff = replay->bitmap_size;
}
- replay->lastseq = seq;
- /* Larger is good. */
- } else {
- /* seq is equal or less than lastseq. */
- diff = replay->lastseq - seq;
-
- /* Over range to check, i.e. too old or wrapped. */
- if (diff >= wsizeb)
- return (1);
+ for (id = 0; id < diff; ++id) {
+ replay->bitmap[(id + index_cur + 1)
+ & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size)] = 0;
+ }
- fr = frlast - diff / 8;
+ replay->lastseq = seq;
+ }
- /* This packet already seen? */
- if ((replay->bitmap)[fr] & (1 << (diff % 8)))
- return (1);
+ index &= IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
+ bit_location = seq & IPSEC_BITMAP_LOC_MASK;
- /* Mark as seen. */
- (replay->bitmap)[fr] |= (1 << (diff % 8));
+ /* this packet has already been received */
+ if (replay->bitmap[index] & (1 << bit_location))
+ goto end;
- /* Out of order but good. */
- }
+ replay->bitmap[index] |= (1 << bit_location);
ok:
if (replay->count == ~0) {
@@ -1614,39 +1615,18 @@
/* Don't increment, no more packets accepted. */
if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0)
- return (1);
+ goto end;
ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n",
__func__, replay->overflow,
ipsec_logsastr(sav, buf, sizeof(buf))));
}
- replay->count++;
-
- return (0);
-}
+ ret = 0;
-/*
- * Shift variable length buffer to left.
- * IN: bitmap: pointer to the buffer
- * nbit: the number of to shift.
- * wsize: buffer size (bytes).
- */
-static void
-vshiftl(unsigned char *bitmap, int nbit, int wsize)
-{
- int s, j, i;
- unsigned char over;
-
- for (j = 0; j < nbit; j += 8) {
- s = (nbit - j < 8) ? (nbit - j): 8;
- bitmap[0] <<= s;
- for (i = 1; i < wsize; i++) {
- over = (bitmap[i] >> (8 - s));
- bitmap[i] <<= s;
- bitmap[i-1] |= over;
- }
- }
+end:
+ SECASVAR_UNLOCK(sav);
+ return (ret);
}
/* Return a printable string for the address. */
Index: netipsec/key.c
===================================================================
--- netipsec/key.c
+++ netipsec/key.c
@@ -2940,6 +2940,8 @@
sav->sched = NULL;
}
if (sav->replay != NULL) {
+ if (sav->replay->bitmap != NULL)
+ free(sav->replay->bitmap, M_IPSEC_MISC);
free(sav->replay, M_IPSEC_MISC);
sav->replay = NULL;
}
@@ -3121,16 +3123,42 @@
/* replay window */
if ((sa0->sadb_sa_flags & SADB_X_EXT_OLD) == 0) {
+
sav->replay = (struct secreplay *)
- malloc(sizeof(struct secreplay)+sa0->sadb_sa_replay, M_IPSEC_MISC, M_NOWAIT|M_ZERO);
+ malloc(sizeof(struct secreplay), M_IPSEC_MISC, M_NOWAIT|M_ZERO);
if (sav->replay == NULL) {
ipseclog((LOG_DEBUG, "%s: No more memory.\n",
__func__));
error = ENOBUFS;
goto fail;
}
- if (sa0->sadb_sa_replay != 0)
- sav->replay->bitmap = (caddr_t)(sav->replay+1);
+
+ if (sa0->sadb_sa_replay != 0) {
+ u_int32_t bitmap_size; /* number of 32b blocks to be allocated */
+
+ if (sa0->sadb_sa_replay > IPSEC_MAX_REPLAY_WSIZE) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ /* RFC 6479:
+ * - the allocated replay window size must be a power of two
+ * - use an extra 32b block as a redundant window
+ */
+ bitmap_size = 1;
+ while (sa0->sadb_sa_replay + 4 > bitmap_size)
+ bitmap_size <<= 1;
+ bitmap_size = bitmap_size/4;
+
+ sav->replay->bitmap = malloc(bitmap_size*sizeof(u_int32_t), M_IPSEC_MISC, M_NOWAIT|M_ZERO);
+ if (sav->replay->bitmap == NULL) {
+ ipseclog((LOG_DEBUG, "%s: No more memory.\n",
+ __func__));
+ error = ENOBUFS;
+ goto fail;
+ }
+ sav->replay->bitmap_size = bitmap_size;
+ }
sav->replay->wsize = sa0->sadb_sa_replay;
}
}
@@ -3405,6 +3433,7 @@
{
struct mbuf *result = NULL, *tres = NULL, *m;
int i;
+ u_int32_t replay_count;
int dumporder[] = {
SADB_EXT_SA, SADB_X_EXT_SA2,
SADB_EXT_LIFETIME_HARD, SADB_EXT_LIFETIME_SOFT,
@@ -3435,8 +3464,10 @@
break;
case SADB_X_EXT_SA2:
- m = key_setsadbxsa2(sav->sah->saidx.mode,
- sav->replay ? sav->replay->count : 0,
+ SECASVAR_LOCK(sav);
+ replay_count = sav->replay ? sav->replay->count : 0;
+ SECASVAR_UNLOCK(sav);
+ m = key_setsadbxsa2(sav->sah->saidx.mode, replay_count,
sav->sah->saidx.reqid);
if (!m)
goto fail;
@@ -3634,7 +3665,9 @@
p->sadb_sa_len = PFKEY_UNIT64(len);
p->sadb_sa_exttype = SADB_EXT_SA;
p->sadb_sa_spi = sav->spi;
+ SECASVAR_LOCK(sav);
p->sadb_sa_replay = (sav->replay != NULL ? sav->replay->wsize : 0);
+ SECASVAR_UNLOCK(sav);
p->sadb_sa_state = sav->state;
p->sadb_sa_auth = sav->alg_auth;
p->sadb_sa_encrypt = sav->alg_enc;
@@ -6853,6 +6886,7 @@
int len;
int error = -1;
struct sadb_lifetime *lt;
+ u_int32_t replay_count;
IPSEC_ASSERT (sav != NULL, ("null sav"));
IPSEC_ASSERT (sav->sah != NULL, ("null sa header"));
@@ -6876,8 +6910,10 @@
m_cat(result, m);
/* create SA extension */
- m = key_setsadbxsa2(sav->sah->saidx.mode,
- sav->replay ? sav->replay->count : 0,
+ SECASVAR_LOCK(sav);
+ replay_count = sav->replay ? sav->replay->count : 0;
+ SECASVAR_UNLOCK(sav);
+ m = key_setsadbxsa2(sav->sah->saidx.mode, replay_count,
sav->sah->saidx.reqid);
if (!m) {
error = ENOBUFS;
Index: netipsec/key_debug.c
===================================================================
--- netipsec/key_debug.c
+++ netipsec/key_debug.c
@@ -570,8 +570,11 @@
if (sav->key_enc != NULL)
kdebug_sadb_key((struct sadb_ext *)sav->key_enc);
- if (sav->replay != NULL)
+ if (sav->replay != NULL) {
+ SECASVAR_LOCK(sav);
kdebug_secreplay(sav->replay);
+ SECASVAR_UNLOCK(sav);
+ }
if (sav->lft_c != NULL)
kdebug_sec_lifetime(sav->lft_c);
if (sav->lft_h != NULL)
@@ -595,8 +598,8 @@
if (rpl == NULL)
panic("%s: NULL pointer was passed.\n", __func__);
- printf(" secreplay{ count=%u wsize=%u seq=%u lastseq=%u",
- rpl->count, rpl->wsize, rpl->seq, rpl->lastseq);
+ printf(" secreplay{ count=%u bitmap_size=%u wsize=%u seq=%u lastseq=%u",
+ rpl->count, rpl->bitmap_size, rpl->wsize, rpl->seq, rpl->lastseq);
if (rpl->bitmap == NULL) {
printf(" }\n");
@@ -605,7 +608,7 @@
printf("\n bitmap { ");
- for (len = 0; len < rpl->wsize; len++) {
+ for (len = 0; len < rpl->bitmap_size*4; len++) {
for (l = 7; l >= 0; l--)
printf("%u", (((rpl->bitmap)[len] >> l) & 1) ? 1 : 0);
}
Index: netipsec/keydb.h
===================================================================
--- netipsec/keydb.h
+++ netipsec/keydb.h
@@ -35,6 +35,8 @@
#ifdef _KERNEL
+#include <sys/mutex.h>
+
#include <netipsec/key_var.h>
#ifndef _SOCKADDR_UNION_DEFINED
@@ -106,7 +108,10 @@
struct auth_hash;
struct comp_algo;
-/* Security Association */
+/* Security Association
+ * (m) locked by mtx
+ * (c) read only except during creation / free
+ */
struct secasvar {
LIST_ENTRY(secasvar) chain;
struct mtx lock; /* update/access lock */
@@ -127,7 +132,7 @@
size_t schedlen;
uint64_t cntr; /* counter for GCM and CTR */
- struct secreplay *replay; /* replay prevention */
+ struct secreplay *replay; /* (m) replay prevention */
time_t created; /* for lifetime */
struct seclifetime *lft_c; /* CURRENT lifetime, it's constant. */
@@ -171,12 +176,14 @@
#define SAV_ISCTRORGCM(_sav) (SAV_ISCTR((_sav)) || SAV_ISGCM((_sav)))
/* replay prevention */
+#define IPSEC_MAX_REPLAY_WSIZE 536870908 /* (UINT32_MAX+1 - 32)/8 */
struct secreplay {
u_int32_t count;
u_int wsize; /* window size, i.g. 4 bytes */
u_int32_t seq; /* used by sender */
u_int32_t lastseq; /* used by receiver */
- caddr_t bitmap; /* used by receiver */
+ u_int32_t *bitmap; /* used by receiver */
+ u_int bitmap_size; /* size of the bitmap array */
int overflow; /* overflow flag */
};
Index: netipsec/xform_ah.c
===================================================================
--- netipsec/xform_ah.c
+++ netipsec/xform_ah.c
@@ -961,8 +961,11 @@
/* Insert packet replay counter, as requested. */
if (sav->replay) {
+ SECASVAR_LOCK(sav);
+
if (sav->replay->count == ~0 &&
(sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+ SECASVAR_UNLOCK(sav);
DPRINTF(("%s: replay counter wrapped for SA %s/%08lx\n",
__func__, ipsec_address(&sav->sah->saidx.dst, buf,
sizeof(buf)), (u_long) ntohl(sav->spi)));
@@ -976,6 +979,8 @@
#endif
sav->replay->count++;
ah->ah_seq = htonl(sav->replay->count);
+
+ SECASVAR_UNLOCK(sav);
}
/* Get crypto descriptors. */
Index: netipsec/xform_esp.c
===================================================================
--- netipsec/xform_esp.c
+++ netipsec/xform_esp.c
@@ -762,12 +762,14 @@
if (sav->replay) {
u_int32_t replay;
+ SECASVAR_LOCK(sav);
#ifdef REGRESSION
/* Emulate replay attack when ipsec_replay is TRUE. */
if (!V_ipsec_replay)
#endif
sav->replay->count++;
replay = htonl(sav->replay->count);
+ SECASVAR_UNLOCK(sav);
bcopy((caddr_t) &replay,
mtod(mo, caddr_t) + roff + sizeof(u_int32_t),
sizeof(u_int32_t));

File Metadata

Mime Type
text/plain
Expires
Wed, Mar 4, 12:54 AM (18 h, 52 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29218924
Default Alt Text
D8468.id22078.diff (13 KB)

Event Timeline