diff options
Diffstat (limited to 'sys/netipsec/xform_ah.c')
-rw-r--r-- | sys/netipsec/xform_ah.c | 214 |
1 files changed, 93 insertions, 121 deletions
diff --git a/sys/netipsec/xform_ah.c b/sys/netipsec/xform_ah.c index afa452c..0512d21 100644 --- a/sys/netipsec/xform_ah.c +++ b/sys/netipsec/xform_ah.c @@ -80,11 +80,11 @@ (((sav)->flags & SADB_X_EXT_OLD) ? \ sizeof (struct ah) : sizeof (struct ah) + sizeof (u_int32_t)) /* - * Return authenticator size in bytes. The old protocol is known - * to use a fixed 16-byte authenticator. The new algorithm use 12-byte - * authenticator. + * Return authenticator size in bytes, based on a field in the + * algorithm descriptor. */ -#define AUTHSIZE(sav) ah_authsize(sav) +#define AUTHSIZE(sav) ((sav->flags & SADB_X_EXT_OLD) ? 16 : \ + xform_ah_authsize((sav)->tdb_authalgxform)) VNET_DEFINE(int, ah_enable) = 1; /* control flow of packets with AH */ VNET_DEFINE(int, ah_cleartos) = 1; /* clear ip_tos when doing AH calc */ @@ -110,27 +110,35 @@ static unsigned char ipseczeroes[256]; /* larger than an ip6 extension hdr */ static int ah_input_cb(struct cryptop*); static int ah_output_cb(struct cryptop*); -static int -ah_authsize(struct secasvar *sav) +int +xform_ah_authsize(struct auth_hash *esph) { + int alen; - IPSEC_ASSERT(sav != NULL, ("%s: sav == NULL", __func__)); + if (esph == NULL) + return 0; - if (sav->flags & SADB_X_EXT_OLD) - return 16; + switch (esph->type) { + case CRYPTO_SHA2_256_HMAC: + case CRYPTO_SHA2_384_HMAC: + case CRYPTO_SHA2_512_HMAC: + alen = esph->hashsize / 2; /* RFC4868 2.3 */ + break; + + case CRYPTO_AES_128_NIST_GMAC: + case CRYPTO_AES_192_NIST_GMAC: + case CRYPTO_AES_256_NIST_GMAC: + alen = esph->hashsize; + break; - switch (sav->alg_auth) { - case SADB_X_AALG_SHA2_256: - return 16; - case SADB_X_AALG_SHA2_384: - return 24; - case SADB_X_AALG_SHA2_512: - return 32; default: - return AH_HMAC_HASHLEN; + alen = AH_HMAC_HASHLEN; + break; } - /* NOTREACHED */ + + return alen; } + /* * NB: this is public for use by the PF_KEY support. */ @@ -158,6 +166,12 @@ ah_algorithm_lookup(int alg) return &auth_hash_hmac_sha2_384; case SADB_X_AALG_SHA2_512: return &auth_hash_hmac_sha2_512; + case SADB_X_AALG_AES128GMAC: + return &auth_hash_nist_gmac_aes_128; + case SADB_X_AALG_AES192GMAC: + return &auth_hash_nist_gmac_aes_192; + case SADB_X_AALG_AES256GMAC: + return &auth_hash_nist_gmac_aes_256; } return NULL; } @@ -565,12 +579,11 @@ ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out) static int ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) { + char buf[128]; struct auth_hash *ahx; - struct tdb_ident *tdbi; struct tdb_crypto *tc; - struct m_tag *mtag; struct newah *ah; - int hl, rplen, authsize; + int hl, rplen, authsize, error; struct cryptodesc *crda; struct cryptop *crp; @@ -596,7 +609,7 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) if (sav->replay && !ipsec_chkreplay(ntohl(ah->ah_seq), sav)) { AHSTAT_INC(ahs_replay); DPRINTF(("%s: packet replay failure: %s\n", __func__, - ipsec_logsastr(sav))); + ipsec_logsastr(sav, buf, sizeof(buf)))); m_freem(m); return ENOBUFS; } @@ -607,10 +620,10 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) authsize = AUTHSIZE(sav); if (hl != authsize + rplen - sizeof (struct ah)) { DPRINTF(("%s: bad authenticator length %u (expecting %lu)" - " for packet in SA %s/%08lx\n", __func__, - hl, (u_long) (authsize + rplen - sizeof (struct ah)), - ipsec_address(&sav->sah->saidx.dst), - (u_long) ntohl(sav->spi))); + " for packet in SA %s/%08lx\n", __func__, hl, + (u_long) (authsize + rplen - sizeof (struct ah)), + ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)), + (u_long) ntohl(sav->spi))); AHSTAT_INC(ahs_badauthl); m_freem(m); return EACCES; @@ -638,27 +651,9 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) crda->crd_klen = _KEYBITS(sav->key_auth); crda->crd_key = sav->key_auth->key_data; - /* Find out if we've already done crypto. */ - for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, NULL); - mtag != NULL; - mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, mtag)) { - tdbi = (struct tdb_ident *) (mtag + 1); - if (tdbi->proto == sav->sah->saidx.proto && - tdbi->spi == sav->spi && - !bcmp(&tdbi->dst, &sav->sah->saidx.dst, - sizeof (union sockaddr_union))) - break; - } - /* Allocate IPsec-specific opaque crypto info. */ - if (mtag == NULL) { - tc = (struct tdb_crypto *) malloc(sizeof (struct tdb_crypto) + - skip + rplen + authsize, M_XDATA, M_NOWAIT|M_ZERO); - } else { - /* Hash verification has already been done successfully. */ - tc = (struct tdb_crypto *) malloc(sizeof (struct tdb_crypto), - M_XDATA, M_NOWAIT|M_ZERO); - } + tc = (struct tdb_crypto *) malloc(sizeof (struct tdb_crypto) + + skip + rplen + authsize, M_XDATA, M_NOWAIT | M_ZERO); if (tc == NULL) { DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__)); AHSTAT_INC(ahs_crypto); @@ -667,29 +662,24 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) return ENOBUFS; } - /* Only save information if crypto processing is needed. */ - if (mtag == NULL) { - int error; + /* + * Save the authenticator, the skipped portion of the packet, + * and the AH header. + */ + m_copydata(m, 0, skip + rplen + authsize, (caddr_t)(tc+1)); + + /* Zeroize the authenticator on the packet. */ + m_copyback(m, skip + rplen, authsize, ipseczeroes); - /* - * Save the authenticator, the skipped portion of the packet, - * and the AH header. - */ - m_copydata(m, 0, skip + rplen + authsize, (caddr_t)(tc+1)); - - /* Zeroize the authenticator on the packet. */ - m_copyback(m, skip + rplen, authsize, ipseczeroes); - - /* "Massage" the packet headers for crypto processing. */ - error = ah_massage_headers(&m, sav->sah->saidx.dst.sa.sa_family, - skip, ahx->type, 0); - if (error != 0) { - /* NB: mbuf is free'd by ah_massage_headers */ - AHSTAT_INC(ahs_hdrops); - free(tc, M_XDATA); - crypto_freereq(crp); - return error; - } + /* "Massage" the packet headers for crypto processing. */ + error = ah_massage_headers(&m, sav->sah->saidx.dst.sa.sa_family, + skip, ahx->type, 0); + if (error != 0) { + /* NB: mbuf is free'd by ah_massage_headers */ + AHSTAT_INC(ahs_hdrops); + free(tc, M_XDATA); + crypto_freereq(crp); + return (error); } /* Crypto operation descriptor. */ @@ -707,14 +697,9 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) tc->tc_nxt = ah->ah_nxt; tc->tc_protoff = protoff; tc->tc_skip = skip; - tc->tc_ptr = (caddr_t) mtag; /* Save the mtag we've identified. */ KEY_ADDREFSA(sav); tc->tc_sav = sav; - - if (mtag == NULL) - return crypto_dispatch(crp); - else - return ah_input_cb(crp); + return (crypto_dispatch(crp)); } /* @@ -723,13 +708,13 @@ ah_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff) static int ah_input_cb(struct cryptop *crp) { + char buf[INET6_ADDRSTRLEN]; int rplen, error, skip, protoff; unsigned char calc[AH_ALEN_MAX]; struct mbuf *m; struct cryptodesc *crd; struct auth_hash *ahx; struct tdb_crypto *tc; - struct m_tag *mtag; struct secasvar *sav; struct secasindex *saidx; u_int8_t nxt; @@ -743,7 +728,6 @@ ah_input_cb(struct cryptop *crp) skip = tc->tc_skip; nxt = tc->tc_nxt; protoff = tc->tc_protoff; - mtag = (struct m_tag *) tc->tc_ptr; m = (struct mbuf *) crp->crp_buf; sav = tc->tc_sav; @@ -789,34 +773,22 @@ ah_input_cb(struct cryptop *crp) /* Copy authenticator off the packet. */ m_copydata(m, skip + rplen, authsize, calc); - /* - * If we have an mtag, we don't need to verify the authenticator -- - * it has been verified by an IPsec-aware NIC. - */ - if (mtag == NULL) { - ptr = (caddr_t) (tc + 1); - - /* Verify authenticator. */ - if (bcmp(ptr + skip + rplen, calc, authsize)) { - DPRINTF(("%s: authentication hash mismatch for packet " - "in SA %s/%08lx\n", __func__, - ipsec_address(&saidx->dst), - (u_long) ntohl(sav->spi))); - AHSTAT_INC(ahs_badauth); - error = EACCES; - goto bad; - } - - /* Fix the Next Protocol field. */ - ((u_int8_t *) ptr)[protoff] = nxt; - - /* Copyback the saved (uncooked) network headers. */ - m_copyback(m, 0, skip, ptr); - } else { - /* Fix the Next Protocol field. */ - m_copyback(m, protoff, sizeof(u_int8_t), &nxt); + /* Verify authenticator. */ + ptr = (caddr_t) (tc + 1); + if (timingsafe_bcmp(ptr + skip + rplen, calc, authsize)) { + DPRINTF(("%s: authentication hash mismatch for packet " + "in SA %s/%08lx\n", __func__, + ipsec_address(&saidx->dst, buf, sizeof(buf)), + (u_long) ntohl(sav->spi))); + AHSTAT_INC(ahs_badauth); + error = EACCES; + goto bad; } + /* Fix the Next Protocol field. */ + ((u_int8_t *) ptr)[protoff] = nxt; + /* Copyback the saved (uncooked) network headers. */ + m_copyback(m, 0, skip, ptr); free(tc, M_XDATA), tc = NULL; /* No longer needed */ /* @@ -845,8 +817,8 @@ ah_input_cb(struct cryptop *crp) error = m_striphdr(m, skip, rplen + authsize); if (error) { DPRINTF(("%s: mangled mbuf chain for SA %s/%08lx\n", __func__, - ipsec_address(&saidx->dst), (u_long) ntohl(sav->spi))); - + ipsec_address(&saidx->dst, buf, sizeof(buf)), + (u_long) ntohl(sav->spi))); AHSTAT_INC(ahs_hdrops); goto bad; } @@ -854,12 +826,12 @@ ah_input_cb(struct cryptop *crp) switch (saidx->dst.sa.sa_family) { #ifdef INET6 case AF_INET6: - error = ipsec6_common_input_cb(m, sav, skip, protoff, mtag); + error = ipsec6_common_input_cb(m, sav, skip, protoff); break; #endif #ifdef INET case AF_INET: - error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag); + error = ipsec4_common_input_cb(m, sav, skip, protoff); break; #endif default: @@ -885,13 +857,10 @@ bad: * AH output routine, called by ipsec[46]_process_packet(). */ static int -ah_output( - struct mbuf *m, - struct ipsecrequest *isr, - struct mbuf **mp, - int skip, - int protoff) +ah_output(struct mbuf *m, struct ipsecrequest *isr, struct mbuf **mp, + int skip, int protoff) { + char buf[INET6_ADDRSTRLEN]; struct secasvar *sav; struct auth_hash *ahx; struct cryptodesc *crda; @@ -929,7 +898,7 @@ ah_output( DPRINTF(("%s: unknown/unsupported protocol family %u, " "SA %s/%08lx\n", __func__, sav->sah->saidx.dst.sa.sa_family, - ipsec_address(&sav->sah->saidx.dst), + ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)), (u_long) ntohl(sav->spi))); AHSTAT_INC(ahs_nopf); error = EPFNOSUPPORT; @@ -939,7 +908,7 @@ ah_output( if (rplen + authsize + m->m_pkthdr.len > maxpacketsize) { DPRINTF(("%s: packet in SA %s/%08lx got too big " "(len %u, max len %u)\n", __func__, - ipsec_address(&sav->sah->saidx.dst), + ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)), (u_long) ntohl(sav->spi), rplen + authsize + m->m_pkthdr.len, maxpacketsize)); AHSTAT_INC(ahs_toobig); @@ -953,7 +922,7 @@ ah_output( m = m_unshare(m, M_NOWAIT); if (m == NULL) { DPRINTF(("%s: cannot clone mbuf chain, SA %s/%08lx\n", __func__, - ipsec_address(&sav->sah->saidx.dst), + ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)), (u_long) ntohl(sav->spi))); AHSTAT_INC(ahs_hdrops); error = ENOBUFS; @@ -966,7 +935,7 @@ ah_output( DPRINTF(("%s: failed to inject %u byte AH header for SA " "%s/%08lx\n", __func__, rplen + authsize, - ipsec_address(&sav->sah->saidx.dst), + ipsec_address(&sav->sah->saidx.dst, buf, sizeof(buf)), (u_long) ntohl(sav->spi))); AHSTAT_INC(ahs_hdrops); /*XXX differs from openbsd */ error = ENOBUFS; @@ -993,9 +962,8 @@ ah_output( if (sav->replay->count == ~0 && (sav->flags & SADB_X_EXT_CYCSEQ) == 0) { DPRINTF(("%s: replay counter wrapped for SA %s/%08lx\n", - __func__, - ipsec_address(&sav->sah->saidx.dst), - (u_long) ntohl(sav->spi))); + __func__, ipsec_address(&sav->sah->saidx.dst, buf, + sizeof(buf)), (u_long) ntohl(sav->spi))); AHSTAT_INC(ahs_wrap); error = EINVAL; goto bad; @@ -1098,6 +1066,7 @@ ah_output( crp->crp_opaque = (caddr_t) tc; /* These are passed as-is to the callback. */ + key_addref(isr->sp); tc->tc_isr = isr; KEY_ADDREFSA(sav); tc->tc_sav = sav; @@ -1135,6 +1104,7 @@ ah_output_cb(struct cryptop *crp) m = (struct mbuf *) crp->crp_buf; isr = tc->tc_isr; + IPSEC_ASSERT(isr->sp != NULL, ("NULL isr->sp")); IPSECREQUEST_LOCK(isr); sav = tc->tc_sav; /* With the isr lock released SA pointer can be updated. */ @@ -1198,16 +1168,18 @@ ah_output_cb(struct cryptop *crp) error = ipsec_process_done(m, isr); KEY_FREESAV(&sav); IPSECREQUEST_UNLOCK(isr); - return error; + KEY_FREESP(&isr->sp); + return (error); bad: if (sav) KEY_FREESAV(&sav); IPSECREQUEST_UNLOCK(isr); + KEY_FREESP(&isr->sp); if (m) m_freem(m); free(tc, M_XDATA); crypto_freereq(crp); - return error; + return (error); } static struct xformsw ah_xformsw = { |