From owner-svn-src-head@freebsd.org Fri Nov 25 14:44:51 2016 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 16EA3C54039; Fri, 25 Nov 2016 14:44:51 +0000 (UTC) (envelope-from fabient@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id D9D3EE02; Fri, 25 Nov 2016 14:44:50 +0000 (UTC) (envelope-from fabient@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id uAPEioP5066032; Fri, 25 Nov 2016 14:44:50 GMT (envelope-from fabient@FreeBSD.org) Received: (from fabient@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id uAPEinKb066023; Fri, 25 Nov 2016 14:44:49 GMT (envelope-from fabient@FreeBSD.org) Message-Id: <201611251444.uAPEinKb066023@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: fabient set sender to fabient@FreeBSD.org using -f From: Fabien Thomas Date: Fri, 25 Nov 2016 14:44:49 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r309144 - in head: lib/libipsec sys/net sys/netipsec X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 25 Nov 2016 14:44:51 -0000 Author: fabient Date: Fri Nov 25 14:44:49 2016 New Revision: 309144 URL: https://svnweb.freebsd.org/changeset/base/309144 Log: IPsec RFC6479 support for replay window sizes up to 2^32 - 32 packets. Since the previous algorithm, based on bit shifting, does not scale with large replay windows, the algorithm used here is based on RFC 6479: IPsec Anti-Replay Algorithm without Bit Shifting. The replay window will be fast to be updated, but will cost as many bits in RAM as its size. The previous implementation did not provide a lock on the replay window, which may lead to replay issues. Reviewed by: ae Obtained from: emeric.poupon@stormshield.eu Sponsored by: Stormshield Differential Revision: https://reviews.freebsd.org/D8468 Modified: head/lib/libipsec/pfkey.c head/lib/libipsec/pfkey_dump.c head/sys/net/pfkeyv2.h head/sys/netipsec/ipsec.c head/sys/netipsec/key.c head/sys/netipsec/key_debug.c head/sys/netipsec/keydb.h head/sys/netipsec/xform_ah.c head/sys/netipsec/xform_esp.c Modified: head/lib/libipsec/pfkey.c ============================================================================== --- head/lib/libipsec/pfkey.c Fri Nov 25 13:49:33 2016 (r309143) +++ head/lib/libipsec/pfkey.c Fri Nov 25 14:44:49 2016 (r309144) @@ -1776,6 +1776,7 @@ pfkey_align(msg, mhp) case SADB_EXT_SPIRANGE: case SADB_X_EXT_POLICY: case SADB_X_EXT_SA2: + case SADB_X_EXT_SA_REPLAY: mhp[ext->sadb_ext_type] = (caddr_t)ext; break; case SADB_X_EXT_NAT_T_TYPE: Modified: head/lib/libipsec/pfkey_dump.c ============================================================================== --- head/lib/libipsec/pfkey_dump.c Fri Nov 25 13:49:33 2016 (r309143) +++ head/lib/libipsec/pfkey_dump.c Fri Nov 25 14:44:49 2016 (r309144) @@ -219,6 +219,7 @@ pfkey_sadump(m) struct sadb_key *m_auth, *m_enc; struct sadb_ident *m_sid, *m_did; struct sadb_sens *m_sens; + struct sadb_x_sa_replay *m_sa_replay; /* check pfkey message. */ if (pfkey_align(m, mhp)) { @@ -243,6 +244,7 @@ pfkey_sadump(m) m_sid = (struct sadb_ident *)mhp[SADB_EXT_IDENTITY_SRC]; m_did = (struct sadb_ident *)mhp[SADB_EXT_IDENTITY_DST]; m_sens = (struct sadb_sens *)mhp[SADB_EXT_SENSITIVITY]; + m_sa_replay = (struct sadb_x_sa_replay *)mhp[SADB_X_EXT_SA_REPLAY]; /* source address */ if (m_saddr == NULL) { @@ -306,7 +308,8 @@ pfkey_sadump(m) /* replay windoe size & flags */ printf("\tseq=0x%08x replay=%u flags=0x%08x ", m_sa2->sadb_x_sa2_sequence, - m_sa->sadb_sa_replay, + m_sa_replay ? (m_sa_replay->sadb_x_sa_replay_replay >> 3) : + m_sa->sadb_sa_replay, m_sa->sadb_sa_flags); /* state */ Modified: head/sys/net/pfkeyv2.h ============================================================================== --- head/sys/net/pfkeyv2.h Fri Nov 25 13:49:33 2016 (r309143) +++ head/sys/net/pfkeyv2.h Fri Nov 25 14:44:49 2016 (r309144) @@ -283,6 +283,14 @@ struct sadb_x_nat_t_frag { }; _Static_assert(sizeof(struct sadb_x_nat_t_frag) == 8, "struct size mismatch"); +/* Additional large replay window support + */ +struct sadb_x_sa_replay { + u_int16_t sadb_x_sa_replay_len; + u_int16_t sadb_x_sa_replay_exttype; + u_int32_t sadb_x_sa_replay_replay; /* in packets */ +}; +_Static_assert(sizeof(struct sadb_x_sa_replay) == 8, "struct size mismatch"); #define SADB_EXT_RESERVED 0 #define SADB_EXT_SA 1 @@ -311,7 +319,8 @@ _Static_assert(sizeof(struct sadb_x_nat_ #define SADB_X_EXT_NAT_T_OAI 23 /* Peer's NAT_OA for src of SA. */ #define SADB_X_EXT_NAT_T_OAR 24 /* Peer's NAT_OA for dst of SA. */ #define SADB_X_EXT_NAT_T_FRAG 25 /* Manual MTU override. */ -#define SADB_EXT_MAX 25 +#define SADB_X_EXT_SA_REPLAY 26 /* Replay window override. */ +#define SADB_EXT_MAX 26 #define SADB_SATYPE_UNSPEC 0 #define SADB_SATYPE_AH 2 Modified: head/sys/netipsec/ipsec.c ============================================================================== --- head/sys/netipsec/ipsec.c Fri Nov 25 13:49:33 2016 (r309143) +++ head/sys/netipsec/ipsec.c Fri Nov 25 14:44:49 2016 (r309144) @@ -251,7 +251,6 @@ static int ipsec6_setspidx_ipaddr(const #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 @@ ipsec_hdrsiz(const struct mbuf *m, u_int * 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 @@ ipsec_updatereplay(u_int32_t seq, struct { 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 @@ ok: /* 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. */ Modified: head/sys/netipsec/key.c ============================================================================== --- head/sys/netipsec/key.c Fri Nov 25 13:49:33 2016 (r309143) +++ head/sys/netipsec/key.c Fri Nov 25 14:44:49 2016 (r309144) @@ -243,7 +243,10 @@ static const int minsize[] = { sizeof(struct sadb_address), /* SADB_X_EXT_NAT_T_OAI */ sizeof(struct sadb_address), /* SADB_X_EXT_NAT_T_OAR */ sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */ + sizeof(struct sadb_x_sa_replay), /* SADB_X_EXT_SA_REPLAY */ }; +_Static_assert(sizeof(minsize)/sizeof(int) == SADB_EXT_MAX + 1, "minsize size mismatch"); + static const int maxsize[] = { sizeof(struct sadb_msg), /* SADB_EXT_RESERVED */ sizeof(struct sadb_sa), /* SADB_EXT_SA */ @@ -271,7 +274,9 @@ static const int maxsize[] = { 0, /* SADB_X_EXT_NAT_T_OAI */ 0, /* SADB_X_EXT_NAT_T_OAR */ sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */ + sizeof(struct sadb_x_sa_replay), /* SADB_X_EXT_SA_REPLAY */ }; +_Static_assert(sizeof(maxsize)/sizeof(int) == SADB_EXT_MAX + 1, "minsize size mismatch"); static VNET_DEFINE(int, ipsec_esp_keymin) = 256; static VNET_DEFINE(int, ipsec_esp_auth) = 0; @@ -472,6 +477,7 @@ static void key_porttosaddr(struct socka #define KEY_PORTTOSADDR(saddr, port) \ key_porttosaddr((struct sockaddr *)(saddr), (port)) static struct mbuf *key_setsadbxsa2(u_int8_t, u_int32_t, u_int32_t); +static struct mbuf *key_setsadbxsareplay(u_int32_t); static struct mbuf *key_setsadbxpolicy(u_int16_t, u_int8_t, u_int32_t, u_int32_t); static struct seckey *key_dup_keymsg(const struct sadb_key *, u_int, @@ -2940,6 +2946,8 @@ key_cleansav(struct secasvar *sav) 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; } @@ -3108,6 +3116,7 @@ key_setsaval(struct secasvar *sav, struc /* SA */ if (mhp->ext[SADB_EXT_SA] != NULL) { const struct sadb_sa *sa0; + u_int32_t replay; sa0 = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA]; if (mhp->extlen[SADB_EXT_SA] < sizeof(*sa0)) { @@ -3119,19 +3128,57 @@ key_setsaval(struct secasvar *sav, struc sav->alg_enc = sa0->sadb_sa_encrypt; sav->flags = sa0->sadb_sa_flags; - /* 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); - if (sav->replay == NULL) { + /* Optional replay window */ + replay = 0; + if ((sa0->sadb_sa_flags & SADB_X_EXT_OLD) == 0) + replay = sa0->sadb_sa_replay; + if ((mhp->ext[SADB_X_EXT_SA_REPLAY]) != NULL) { + replay = ((const struct sadb_x_sa_replay *) + mhp->ext[SADB_X_EXT_SA_REPLAY])->sadb_x_sa_replay_replay; + + if (replay > UINT32_MAX - 32) { + ipseclog((LOG_DEBUG, "%s: replay window too big.\n", + __func__)); + error = EINVAL; + goto fail; + } + + replay = (replay + 7) >> 3; + } + + sav->replay = (struct secreplay *) + 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 (replay != 0) { + /* number of 32b blocks to be allocated */ + u_int32_t bitmap_size; + + /* 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 (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; } - if (sa0->sadb_sa_replay != 0) - sav->replay->bitmap = (caddr_t)(sav->replay+1); - sav->replay->wsize = sa0->sadb_sa_replay; + sav->replay->bitmap_size = bitmap_size; + sav->replay->wsize = replay; } } @@ -3406,7 +3453,7 @@ key_setdumpsa(struct secasvar *sav, u_in struct mbuf *result = NULL, *tres = NULL, *m; int i; int dumporder[] = { - SADB_EXT_SA, SADB_X_EXT_SA2, + SADB_EXT_SA, SADB_X_EXT_SA2, SADB_X_EXT_SA_REPLAY, SADB_EXT_LIFETIME_HARD, SADB_EXT_LIFETIME_SOFT, SADB_EXT_LIFETIME_CURRENT, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST, SADB_EXT_ADDRESS_PROXY, SADB_EXT_KEY_AUTH, @@ -3419,6 +3466,7 @@ key_setdumpsa(struct secasvar *sav, u_in SADB_X_EXT_NAT_T_FRAG, #endif }; + u_int32_t replay_count; m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt); if (m == NULL) @@ -3435,13 +3483,25 @@ key_setdumpsa(struct secasvar *sav, u_in 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; break; + case SADB_X_EXT_SA_REPLAY: + if (sav->replay == NULL || + sav->replay->wsize <= UINT8_MAX) + continue; + + m = key_setsadbxsareplay(sav->replay->wsize); + if (!m) + goto fail; + break; + case SADB_EXT_ADDRESS_SRC: m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC, &sav->sah->saidx.src.sa, @@ -3634,7 +3694,9 @@ key_setsadbsa(struct secasvar *sav) p->sadb_sa_len = PFKEY_UNIT64(len); p->sadb_sa_exttype = SADB_EXT_SA; p->sadb_sa_spi = sav->spi; - p->sadb_sa_replay = (sav->replay != NULL ? sav->replay->wsize : 0); + p->sadb_sa_replay = sav->replay ? + (sav->replay->wsize > UINT8_MAX ? + UINT8_MAX : sav->replay->wsize) : 0; p->sadb_sa_state = sav->state; p->sadb_sa_auth = sav->alg_auth; p->sadb_sa_encrypt = sav->alg_enc; @@ -3719,6 +3781,32 @@ key_setsadbxsa2(u_int8_t mode, u_int32_t return m; } +/* + * Set data into sadb_x_sa_replay. + */ +static struct mbuf * +key_setsadbxsareplay(u_int32_t replay) +{ + struct mbuf *m; + struct sadb_x_sa_replay *p; + size_t len; + + len = PFKEY_ALIGN8(sizeof(struct sadb_x_sa_replay)); + m = m_get2(len, M_NOWAIT, MT_DATA, 0); + if (m == NULL) + return (NULL); + m_align(m, len); + m->m_len = len; + p = mtod(m, struct sadb_x_sa_replay *); + + bzero(p, len); + p->sadb_x_sa_replay_len = PFKEY_UNIT64(len); + p->sadb_x_sa_replay_exttype = SADB_X_EXT_SA_REPLAY; + p->sadb_x_sa_replay_replay = (replay << 3); + + return m; +} + #ifdef IPSEC_NAT_T /* * Set a type in sadb_x_nat_t_type. @@ -6853,6 +6941,7 @@ key_expire(struct secasvar *sav, int har 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 +6965,11 @@ key_expire(struct secasvar *sav, int har 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; @@ -6885,6 +6977,15 @@ key_expire(struct secasvar *sav, int har } m_cat(result, m); + if (sav->replay && sav->replay->wsize > UINT8_MAX) { + m = key_setsadbxsareplay(sav->replay->wsize); + if (!m) { + error = ENOBUFS; + goto fail; + } + m_cat(result, m); + } + /* create lifetime extension (current and soft) */ len = PFKEY_ALIGN8(sizeof(*lt)) * 2; m = m_get2(len, M_NOWAIT, MT_DATA, 0); @@ -7560,6 +7661,7 @@ key_align(struct mbuf *m, struct sadb_ms case SADB_X_EXT_NAT_T_OAR: case SADB_X_EXT_NAT_T_FRAG: #endif + case SADB_X_EXT_SA_REPLAY: /* duplicate check */ /* * XXX Are there duplication payloads of either Modified: head/sys/netipsec/key_debug.c ============================================================================== --- head/sys/netipsec/key_debug.c Fri Nov 25 13:49:33 2016 (r309143) +++ head/sys/netipsec/key_debug.c Fri Nov 25 14:44:49 2016 (r309144) @@ -570,8 +570,11 @@ kdebug_secasv(struct secasvar *sav) 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 @@ kdebug_secreplay(struct secreplay *rpl) 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 @@ kdebug_secreplay(struct secreplay *rpl) 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); } Modified: head/sys/netipsec/keydb.h ============================================================================== --- head/sys/netipsec/keydb.h Fri Nov 25 13:49:33 2016 (r309143) +++ head/sys/netipsec/keydb.h Fri Nov 25 14:44:49 2016 (r309144) @@ -35,6 +35,8 @@ #ifdef _KERNEL +#include + #include #ifndef _SOCKADDR_UNION_DEFINED @@ -170,14 +172,18 @@ struct secasvar { #define SAV_ISCTR(_sav) ((_sav)->alg_enc == SADB_X_EALG_AESCTR) #define SAV_ISCTRORGCM(_sav) (SAV_ISCTR((_sav)) || SAV_ISGCM((_sav))) -/* replay prevention */ +/* Replay prevention, protected by SECASVAR_LOCK: + * (m) locked by mtx + * (c) read only except during creation / free + */ 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 */ - int overflow; /* overflow flag */ + u_int32_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_int32_t *bitmap; /* (m) used by receiver */ + u_int bitmap_size; /* (c) size of the bitmap array */ + int overflow; /* (m) overflow flag */ }; /* socket table due to send PF_KEY messages. */ Modified: head/sys/netipsec/xform_ah.c ============================================================================== --- head/sys/netipsec/xform_ah.c Fri Nov 25 13:49:33 2016 (r309143) +++ head/sys/netipsec/xform_ah.c Fri Nov 25 14:44:49 2016 (r309144) @@ -961,8 +961,11 @@ ah_output(struct mbuf *m, struct ipsecre /* 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 @@ ah_output(struct mbuf *m, struct ipsecre #endif sav->replay->count++; ah->ah_seq = htonl(sav->replay->count); + + SECASVAR_UNLOCK(sav); } /* Get crypto descriptors. */ Modified: head/sys/netipsec/xform_esp.c ============================================================================== --- head/sys/netipsec/xform_esp.c Fri Nov 25 13:49:33 2016 (r309143) +++ head/sys/netipsec/xform_esp.c Fri Nov 25 14:44:49 2016 (r309144) @@ -762,12 +762,14 @@ esp_output(struct mbuf *m, struct ipsecr 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));