/* * ESP input callback from the crypto driver. */ static int esp_input_cb(struct cryptop *crp) { u_int8_t lastthree[3], aalg[AH_HMAC_HASHLEN]; int s, hlen, skip, protoff, error; struct mbuf *m; struct cryptodesc *crd; struct auth_hash *esph; struct enc_xform *espx; struct tdb_crypto *tc; struct m_tag *mtag; struct secasvar *sav; struct secasindex *saidx; caddr_t ptr; crd = crp->crp_desc; KASSERT(crd != NULL, ("esp_input_cb: null crypto descriptor!")); tc = (struct tdb_crypto *) crp->crp_opaque; KASSERT(tc != NULL, ("esp_input_cb: null opaque crypto data area!")); skip = tc->tc_skip; protoff = tc->tc_protoff; mtag = (struct m_tag *) tc->tc_ptr; m = (struct mbuf *) crp->crp_buf; s = splnet(); sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi); if (sav == NULL) { espstat.esps_notdb++; DPRINTF(("esp_input_cb: SA expired while in crypto " "(SA %s/%08lx proto %u)\n", ipsec_address(&tc->tc_dst), (u_long) ntohl(tc->tc_spi), tc->tc_proto)); error = ENOBUFS; /*XXX*/ goto bad; } saidx = &sav->sah->saidx; KASSERT(saidx->dst.sa.sa_family == AF_INET || saidx->dst.sa.sa_family == AF_INET6, ("ah_input_cb: unexpected protocol family %u", saidx->dst.sa.sa_family)); esph = sav->tdb_authalgxform; espx = sav->tdb_encalgxform; /* Check for crypto errors */ if (crp->crp_etype) { /* Reset the session ID */ if (sav->tdb_cryptoid != 0) sav->tdb_cryptoid = crp->crp_sid; if (crp->crp_etype == EAGAIN) { KEY_FREESAV(&sav); splx(s); return crypto_dispatch(crp); } espstat.esps_noxform++; DPRINTF(("esp_input_cb: crypto error %d\n", crp->crp_etype)); error = crp->crp_etype; goto bad; } /* Shouldn't happen... */ if (m == NULL) { espstat.esps_crypto++; DPRINTF(("esp_input_cb: bogus returned buffer from crypto\n")); error = EINVAL; goto bad; } espstat.esps_hist[sav->alg_enc]++; /* If authentication was performed, check now. */ if (esph != NULL) { /* * If we have a tag, it means an IPsec-aware NIC did * the verification for us. Otherwise we need to * check the authentication calculation. */ ahstat.ahs_hist[sav->alg_auth]++; if (mtag == NULL) { /* Copy the authenticator from the packet */ m_copydata(m, m->m_pkthdr.len - esph->authsize, esph->authsize, aalg); ptr = (caddr_t) (tc + 1); /* Verify authenticator */ if (bcmp(ptr, aalg, esph->authsize) != 0) { DPRINTF(("esp_input_cb: " "authentication hash mismatch for packet in SA %s/%08lx\n", ipsec_address(&saidx->dst), (u_long) ntohl(sav->spi))); espstat.esps_badauth++; error = EACCES; goto bad; } } /* Remove trailing authenticator */ m_adj(m, -(esph->authsize)); } /* Release the crypto descriptors */ free(tc, M_XDATA), tc = NULL; crypto_freereq(crp), crp = NULL; /* * Packet is now decrypted. */ m->m_flags |= M_DECRYPTED; /* Determine the ESP header length */ if (sav->flags & SADB_X_EXT_OLD) hlen = sizeof (struct esp) + sav->ivlen; else hlen = sizeof (struct newesp) + sav->ivlen; /* Remove the ESP header and IV from the mbuf. */ error = m_striphdr(m, skip, hlen); if (error) { espstat.esps_hdrops++; DPRINTF(("esp_input_cb: bad mbuf chain, SA %s/%08lx\n", ipsec_address(&sav->sah->saidx.dst), (u_long) ntohl(sav->spi))); goto bad; } /* Save the last three bytes of decrypted data */ m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree); /* Verify pad length */ if (lastthree[1] + 2 > m->m_pkthdr.len - skip) { espstat.esps_badilen++; DPRINTF(("esp_input_cb: invalid padding length %d " "for %u byte packet in SA %s/%08lx\n", lastthree[1], m->m_pkthdr.len - skip, ipsec_address(&sav->sah->saidx.dst), (u_long) ntohl(sav->spi))); error = EINVAL; goto bad; } /* Verify correct decryption by checking the last padding bytes */ if ((sav->flags & SADB_X_EXT_PMASK) != SADB_X_EXT_PRAND) { if (lastthree[1] != lastthree[0] && lastthree[1] != 0) { espstat.esps_badenc++; DPRINTF(("esp_input_cb: decryption failed " "for packet in SA %s/%08lx\n", ipsec_address(&sav->sah->saidx.dst), (u_long) ntohl(sav->spi))); DPRINTF(("esp_input_cb: %x %x\n", lastthree[0], lastthree[1])); error = EINVAL; goto bad; } } /* Trim the mbuf chain to remove trailing authenticator and padding */ m_adj(m, -(lastthree[1] + 2)); /* Restore the Next Protocol field */ m_copyback(m, protoff, sizeof (u_int8_t), lastthree + 2); IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, mtag); KEY_FREESAV(&sav); splx(s); return error; bad: if (sav) KEY_FREESAV(&sav); splx(s); if (m != NULL) m_freem(m); if (tc != NULL) free(tc, M_XDATA); if (crp != NULL) crypto_freereq(crp); return error; }
+++ sys/netipsec/xform_ipcomp.c 29 Mar 2011 19:24:04 -0000 @@ -326,6 +326,14 @@ ipcomp_input_cb(struct cryptop *crp) /* Keep the next protocol field */ addr = (uint8_t*) mtod(m, struct ip *) + skip; nproto = ((struct ipcomp *) addr)->comp_nxt; + if (nproto == IPPROTO_IPCOMP || nproto == IPPROTO_AH || nproto == IPPROTO_ESP) { + IPCOMP_STATINC(IPCOMP_STAT_HDROPS); + DPRINTF(("ipcomp_input_cb: nested ipcomp, IPCA %s/%08lx\n", + ipsec_address(&sav->sah->saidx.dst), + (u_long) ntohl(sav->spi))); + error = EINVAL; + goto bad; + } /* Remove the IPCOMP header */ error = m_striphdr(m, skip, hlen); -------------- next part -------------- Index: sys/netinet6/ipcomp_input.c =================================================================== RCS file: /cvsroot/src/sys/netinet6/ipcomp_input.c,v retrieving revision 1.36 diff -u -u -p -r1.36 ipcomp_input.c --- sys/netinet6/ipcomp_input.c 5 May 2008 13:41:30 -0000 1.36 +++ sys/netinet6/ipcomp_input.c 29 Mar 2011 19:19:00 -0000 @@ -148,6 +148,13 @@ ipcomp4_input(m, va_alist) ipcomp = mtod(md, struct ipcomp *); ip = mtod(m, struct ip *); nxt = ipcomp->comp_nxt; + if (nxt == IPPROTO_IPCOMP || nxt == IPPROTO_AH || nxt == IPPROTO_ESP) { + /* nested ipcomp - possible attack, not likely useful */ + ipseclog((LOG_DEBUG, "IPv4 IPComp input: nested ipcomp "
/* * Potentially decap ESP in UDP frame. Check for an ESP header. * If present, strip the UDP header and push the result through IPSec. * * Returns error if mbuf consumed and/or processed, otherwise 0. */ int udp_ipsec_input(struct mbuf *m, int off, int af) { union sockaddr_union dst; struct secasvar *sav; struct udphdr *udp; struct ip *ip; uint32_t spi; int hlen; /* * Just return if packet doesn't have enough data. * We need at least [IP header + UDP header + ESP header]. * NAT-Keepalive packet has only one byte of payload, so it * by default will not be processed. */ if (m->m_pkthdr.len < off + sizeof(struct esp)) return (0); m_copydata(m, off, sizeof(uint32_t), (caddr_t)&spi); if (spi == 0) /* Non-ESP marker. */ return (0); /* * Find SA and check that it is configured for UDP * encapsulation. */ bzero(&dst, sizeof(dst)); dst.sa.sa_family = af; switch (af) { #ifdef INET case AF_INET: dst.sin.sin_len = sizeof(struct sockaddr_in); ip = mtod(m, struct ip *); ip->ip_p = IPPROTO_ESP; off = offsetof(struct ip, ip_p); hlen = ip->ip_hl << 2; dst.sin.sin_addr = ip->ip_dst; break; #endif #ifdef INET6 case AF_INET6: /* Not yet */ /* FALLTHROUGH */ #endif default: ESPSTAT_INC(esps_nopf); m_freem(m); return (EPFNOSUPPORT); } sav = key_allocsa(&dst, IPPROTO_ESP, spi); if (sav == NULL) { ESPSTAT_INC(esps_notdb); m_freem(m); return (ENOENT); } udp = mtodo(m, hlen); if (sav->natt == NULL || sav->natt->sport != udp->uh_sport || sav->natt->dport != udp->uh_dport) { /* XXXAE: should we check source address? */ ESPSTAT_INC(esps_notdb); key_freesav(&sav); m_freem(m); return (ENOENT); } /* * Remove the UDP header * Before: * <--- off ---> * +----+------+-----+ * | IP | UDP | ESP | * +----+------+-----+ * <-skip-> * After: * +----+-----+ * | IP | ESP | * +----+-----+ * <-skip-> */ m_striphdr(m, hlen, sizeof(*udp)); /* * We cannot yet update the cksums so clear any h/w cksum flags * as they are no longer valid. */ if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) m->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID | CSUM_PSEUDO_HDR); /* * We can update ip_len and ip_sum here, but ipsec4_input_cb() * will do this anyway, so don't touch them here. */ ESPSTAT_INC(esps_input); (*sav->tdb_xform->xf_input)(m, sav, hlen, off); return (EINPROGRESS); /* Consumed by IPsec. */ }