Beispiel #1
0
/*
 * ESP input processing, called (eventually) through the protocol switch.
 */
int
esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff)
{
	struct auth_hash *esph = (struct auth_hash *) tdb->tdb_authalgxform;
	struct enc_xform *espx = (struct enc_xform *) tdb->tdb_encalgxform;
	struct tdb_crypto *tc;
	int plen, alen, hlen;
	struct m_tag *mtag;
	u_int32_t btsx;

	struct cryptodesc *crde = NULL, *crda = NULL;
	struct cryptop *crp;

	/* Determine the ESP header length */
	if (tdb->tdb_flags & TDBF_NOREPLAY)
		hlen = sizeof(u_int32_t) + tdb->tdb_ivlen; /* "old" ESP */
	else
		hlen = 2 * sizeof(u_int32_t) + tdb->tdb_ivlen; /* "new" ESP */

	if (esph)
		alen = AH_HMAC_HASHLEN;
	else
		alen = 0;

	plen = m->m_pkthdr.len - (skip + hlen + alen);
	if (plen <= 0) {
		DPRINTF(("esp_input: invalid payload length\n"));
		espstat.esps_badilen++;
		m_freem(m);
		return EINVAL;
	}

	if (espx) {
		/*
		 * Verify payload length is multiple of encryption algorithm
		 * block size.
		 */
		if (plen & (espx->blocksize - 1)) {
			DPRINTF(("esp_input(): payload of %d octets not a multiple of %d octets, SA %s/%08x\n", plen, espx->blocksize, ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
			espstat.esps_badilen++;
			m_freem(m);
			return EINVAL;
		}
	}

	/* Replay window checking, if appropriate -- no value commitment. */
	if ((tdb->tdb_wnd > 0) && (!(tdb->tdb_flags & TDBF_NOREPLAY))) {
		m_copydata(m, skip + sizeof(u_int32_t), sizeof(u_int32_t),
		    (unsigned char *) &btsx);
		btsx = ntohl(btsx);

		switch (checkreplaywindow32(btsx, 0, &(tdb->tdb_rpl),
		    tdb->tdb_wnd, &(tdb->tdb_bitmap), 0)) {
		case 0: /* All's well */
			break;

		case 1:
			m_freem(m);
			DPRINTF(("esp_input(): replay counter wrapped for SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
			espstat.esps_wrap++;
			return EACCES;

		case 2:
		case 3:
			DPRINTF(("esp_input(): duplicate packet received in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
			m_freem(m);
			return EACCES;

		default:
			m_freem(m);
			DPRINTF(("esp_input(): bogus value from checkreplaywindow32() in SA %s/%08x\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi)));
			espstat.esps_replay++;
			return EACCES;
		}
	}

	/* Update the counters */
	tdb->tdb_cur_bytes += m->m_pkthdr.len - skip - hlen - alen;
	espstat.esps_ibytes += m->m_pkthdr.len - skip - hlen - alen;

	/* Hard expiration */
	if ((tdb->tdb_flags & TDBF_BYTES) &&
	    (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes))	{
		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD);
		tdb_delete(tdb);
		m_freem(m);
		return ENXIO;
	}

	/* Notify on soft expiration */
	if ((tdb->tdb_flags & TDBF_SOFT_BYTES) &&
	    (tdb->tdb_cur_bytes >= tdb->tdb_soft_bytes)) {
		pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT);
		tdb->tdb_flags &= ~TDBF_SOFT_BYTES;       /* Turn off checking */
	}

#ifdef notyet
	/* 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)) {
		struct tdb_ident *tdbi;

		tdbi = (struct tdb_ident *) (mtag + 1);
		if (tdbi->proto == tdb->tdb_sproto && tdbi->spi == tdb->tdb_spi &&
		    !bcmp(&tdbi->dst, &tdb->tdb_dst, sizeof(union sockaddr_union)))
			break;
	}
#else
	mtag = NULL;
#endif

	/* Get crypto descriptors */
	crp = crypto_getreq(esph && espx ? 2 : 1);
	if (crp == NULL) {
		m_freem(m);
		DPRINTF(("esp_input(): failed to acquire crypto descriptors\n"));
		espstat.esps_crypto++;
		return ENOBUFS;
	}

	/* Get IPsec-specific opaque pointer */
	if (esph == NULL || mtag != NULL)
		tc = malloc(sizeof(*tc), M_XDATA, M_NOWAIT | M_ZERO);
	else
		tc = malloc(sizeof(*tc) + alen, M_XDATA, M_NOWAIT | M_ZERO);
	if (tc == NULL)	{
		m_freem(m);
		crypto_freereq(crp);
		DPRINTF(("esp_input(): failed to allocate tdb_crypto\n"));
		espstat.esps_crypto++;
		return ENOBUFS;
	}

	tc->tc_ptr = (caddr_t) mtag;

	if (esph) {
		crda = crp->crp_desc;
		crde = crda->crd_next;

		/* Authentication descriptor */
		crda->crd_skip = skip;
		crda->crd_len = m->m_pkthdr.len - (skip + alen);
		crda->crd_inject = m->m_pkthdr.len - alen;

		crda->crd_alg = esph->type;
		crda->crd_key = tdb->tdb_amxkey;
		crda->crd_klen = tdb->tdb_amxkeylen * 8;

		/* Copy the authenticator */
		if (mtag == NULL)
			m_copydata(m, m->m_pkthdr.len - alen, alen, (caddr_t) (tc + 1));
	} else
		crde = crp->crp_desc;

	/* Crypto operation descriptor */
	crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
	crp->crp_flags = CRYPTO_F_IMBUF;
	crp->crp_buf = (caddr_t) m;
	crp->crp_callback = (int (*) (struct cryptop *)) esp_input_cb;
	crp->crp_sid = tdb->tdb_cryptoid;
	crp->crp_opaque = (caddr_t) tc;

	/* These are passed as-is to the callback */
	tc->tc_skip = skip;
	tc->tc_protoff = protoff;
	tc->tc_spi = tdb->tdb_spi;
	tc->tc_proto = tdb->tdb_sproto;
	bcopy(&tdb->tdb_dst, &tc->tc_dst, sizeof(union sockaddr_union));

	/* Decryption descriptor */
	if (espx) {
		crde->crd_skip = skip + hlen;
		crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
		crde->crd_inject = skip + hlen - tdb->tdb_ivlen;

		if (tdb->tdb_flags & TDBF_HALFIV) {
			/* Copy half-IV from packet */
			m_copydata(m, crde->crd_inject, tdb->tdb_ivlen, crde->crd_iv);

			/* Cook IV */
			for (btsx = 0; btsx < tdb->tdb_ivlen; btsx++)
				crde->crd_iv[tdb->tdb_ivlen + btsx] = ~crde->crd_iv[btsx];

			crde->crd_flags |= CRD_F_IV_EXPLICIT;
		}

		crde->crd_alg = espx->type;
		crde->crd_key = tdb->tdb_emxkey;
		crde->crd_klen = tdb->tdb_emxkeylen * 8;
		/* XXX Rounds ? */
	}

	if (mtag == NULL)
		return crypto_dispatch(crp);
	else
		return esp_input_cb(crp);
}
Beispiel #2
0
/*
 * ESP input processing, called (eventually) through the protocol switch.
 */
static int
esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
{
	struct auth_hash *esph;
	struct enc_xform *espx;
	struct tdb_ident *tdbi;
	struct tdb_crypto *tc;
	int plen, alen, hlen;
	struct m_tag *mtag;
	struct newesp *esp;

	struct cryptodesc *crde;
	struct cryptop *crp;

	IPSEC_ASSERT(sav != NULL, ("null SA"));
	IPSEC_ASSERT(sav->tdb_encalgxform != NULL, ("null encoding xform"));

	/* Valid IP Packet length ? */
	if ( (skip&3) || (m->m_pkthdr.len&3) ){
		DPRINTF(("%s: misaligned packet, skip %u pkt len %u",
				__func__, skip, m->m_pkthdr.len));
		ESPSTAT_INC(esps_badilen);
		m_freem(m);
		return EINVAL;
	}

	/* XXX don't pullup, just copy header */
	IP6_EXTHDR_GET(esp, struct newesp *, m, skip, sizeof (struct newesp));

	esph = sav->tdb_authalgxform;
	espx = sav->tdb_encalgxform;

	/* 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;
	/* Authenticator hash size */
	if (esph != NULL) {
		switch (esph->type) {
		case CRYPTO_SHA2_256_HMAC:
		case CRYPTO_SHA2_384_HMAC:
		case CRYPTO_SHA2_512_HMAC:
			alen = esph->hashsize/2;
			break;
		default:
			alen = AH_HMAC_HASHLEN;
			break;
		}
	}else
		alen = 0;

	/*
	 * Verify payload length is multiple of encryption algorithm
	 * block size.
	 *
	 * NB: This works for the null algorithm because the blocksize
	 *     is 4 and all packets must be 4-byte aligned regardless
	 *     of the algorithm.
	 */
	plen = m->m_pkthdr.len - (skip + hlen + alen);
	if ((plen & (espx->blocksize - 1)) || (plen <= 0)) {
		DPRINTF(("%s: payload of %d octets not a multiple of %d octets,"
		    "  SA %s/%08lx\n", __func__,
		    plen, espx->blocksize,
		    ipsec_address(&sav->sah->saidx.dst),
		    (u_long) ntohl(sav->spi)));
		ESPSTAT_INC(esps_badilen);
		m_freem(m);
		return EINVAL;
	}

	/*
	 * Check sequence number.
	 */
	if (esph && sav->replay && !ipsec_chkreplay(ntohl(esp->esp_seq), sav)) {
		DPRINTF(("%s: packet replay check for %s\n", __func__,
		    ipsec_logsastr(sav)));	/*XXX*/
		ESPSTAT_INC(esps_replay);
		m_freem(m);
		return ENOBUFS;		/*XXX*/
	}

	/* Update the counters */
	ESPSTAT_ADD(esps_ibytes, m->m_pkthdr.len - (skip + hlen + alen));

	/* 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;
	}

	/* Get crypto descriptors */
	crp = crypto_getreq(esph && espx ? 2 : 1);
	if (crp == NULL) {
		DPRINTF(("%s: failed to acquire crypto descriptors\n",
			__func__));
		ESPSTAT_INC(esps_crypto);
		m_freem(m);
		return ENOBUFS;
	}

	/* Get IPsec-specific opaque pointer */
	if (esph == NULL || mtag != NULL)
		tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
		    M_XDATA, M_NOWAIT|M_ZERO);
	else
		tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto) + alen,
		    M_XDATA, M_NOWAIT|M_ZERO);
	if (tc == NULL) {
		crypto_freereq(crp);
		DPRINTF(("%s: failed to allocate tdb_crypto\n", __func__));
		ESPSTAT_INC(esps_crypto);
		m_freem(m);
		return ENOBUFS;
	}

	tc->tc_ptr = (caddr_t) mtag;

	if (esph) {
		struct cryptodesc *crda = crp->crp_desc;

		IPSEC_ASSERT(crda != NULL, ("null ah crypto descriptor"));

		/* Authentication descriptor */
		crda->crd_skip = skip;
		crda->crd_len = m->m_pkthdr.len - (skip + alen);
		crda->crd_inject = m->m_pkthdr.len - alen;

		crda->crd_alg = esph->type;
		crda->crd_key = sav->key_auth->key_data;
		crda->crd_klen = _KEYBITS(sav->key_auth);

		/* Copy the authenticator */
		if (mtag == NULL)
			m_copydata(m, m->m_pkthdr.len - alen, alen,
				   (caddr_t) (tc + 1));

		/* Chain authentication request */
		crde = crda->crd_next;
	} else {
		crde = crp->crp_desc;
	}

	/* Crypto operation descriptor */
	crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
	crp->crp_flags = CRYPTO_F_IMBUF | CRYPTO_F_CBIFSYNC;
	crp->crp_buf = (caddr_t) m;
	crp->crp_callback = esp_input_cb;
	crp->crp_sid = sav->tdb_cryptoid;
	crp->crp_opaque = (caddr_t) tc;

	/* These are passed as-is to the callback */
	tc->tc_spi = sav->spi;
	tc->tc_dst = sav->sah->saidx.dst;
	tc->tc_proto = sav->sah->saidx.proto;
	tc->tc_protoff = protoff;
	tc->tc_skip = skip;
	KEY_ADDREFSA(sav);
	tc->tc_sav = sav;

	/* Decryption descriptor */
	if (espx) {
		IPSEC_ASSERT(crde != NULL, ("null esp crypto descriptor"));
		crde->crd_skip = skip + hlen;
		crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
		crde->crd_inject = skip + hlen - sav->ivlen;

		crde->crd_alg = espx->type;
		crde->crd_key = sav->key_enc->key_data;
		crde->crd_klen = _KEYBITS(sav->key_enc);
		/* XXX Rounds ? */
	}

	if (mtag == NULL)
		return crypto_dispatch(crp);
	else
		return esp_input_cb(crp);
}
/*
 * ESP input processing, called (eventually) through the protocol switch.
 */
static int
esp_input(struct mbuf *m, const struct secasvar *sav, int skip, int protoff)
{
    const struct auth_hash *esph;
    const struct enc_xform *espx;
    struct tdb_ident *tdbi;
    struct tdb_crypto *tc;
    int plen, alen, hlen, error;
    struct m_tag *mtag;
    struct newesp *esp;

    struct cryptodesc *crde;
    struct cryptop *crp;

    IPSEC_SPLASSERT_SOFTNET("esp_input");

    IPSEC_ASSERT(sav != NULL, ("esp_input: null SA"));
    IPSEC_ASSERT(sav->tdb_encalgxform != NULL,
                 ("esp_input: null encoding xform"));
    IPSEC_ASSERT((skip&3) == 0 && (m->m_pkthdr.len&3) == 0,
                 ("esp_input: misaligned packet, skip %u pkt len %u",
                  skip, m->m_pkthdr.len));

    /* XXX don't pullup, just copy header */
    IP6_EXTHDR_GET(esp, struct newesp *, m, skip, sizeof (struct newesp));

    esph = sav->tdb_authalgxform;
    espx = sav->tdb_encalgxform;

    /* 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;
    /* Authenticator hash size */
    alen = esph ? esph->authsize : 0;

    /*
     * Verify payload length is multiple of encryption algorithm
     * block size.
     *
     * NB: This works for the null algorithm because the blocksize
     *     is 4 and all packets must be 4-byte aligned regardless
     *     of the algorithm.
     */
    plen = m->m_pkthdr.len - (skip + hlen + alen);
    if ((plen & (espx->blocksize - 1)) || (plen <= 0)) {
        DPRINTF(("esp_input: "
                 "payload of %d octets not a multiple of %d octets,"
                 "  SA %s/%08lx\n",
                 plen, espx->blocksize,
                 ipsec_address(&sav->sah->saidx.dst),
                 (u_long) ntohl(sav->spi)));
        ESP_STATINC(ESP_STAT_BADILEN);
        m_freem(m);
        return EINVAL;
    }

    /*
     * Check sequence number.
     */
    if (esph && sav->replay && !ipsec_chkreplay(ntohl(esp->esp_seq), sav)) {
        DPRINTF(("esp_input: packet replay check for %s\n",
                 ipsec_logsastr(sav)));	/*XXX*/
        ESP_STATINC(ESP_STAT_REPLAY);
        m_freem(m);
        return ENOBUFS;		/*XXX*/
    }

    /* Update the counters */
    ESP_STATADD(ESP_STAT_IBYTES, m->m_pkthdr.len - skip - hlen - alen);

    /* 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 &&
                !memcmp(&tdbi->dst, &sav->sah->saidx.dst,
                        sizeof(union sockaddr_union)))
            break;
    }

    /* Get crypto descriptors */
    crp = crypto_getreq(esph && espx ? 2 : 1);
    if (crp == NULL) {
        DPRINTF(("esp_input: failed to acquire crypto descriptors\n"));
        ESP_STATINC(ESP_STAT_CRYPTO);
        m_freem(m);
        return ENOBUFS;
    }

    /* Get IPsec-specific opaque pointer */
    if (esph == NULL || mtag != NULL)
        tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
                                          M_XDATA, M_NOWAIT|M_ZERO);
    else
        tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto) + alen,
                                          M_XDATA, M_NOWAIT|M_ZERO);
    if (tc == NULL) {
        crypto_freereq(crp);
        DPRINTF(("esp_input: failed to allocate tdb_crypto\n"));
        ESP_STATINC(ESP_STAT_CRYPTO);
        m_freem(m);
        return ENOBUFS;
    }

    error = m_makewritable(&m, 0, m->m_pkthdr.len, M_NOWAIT);
    if (error) {
        m_freem(m);
        free(tc, M_XDATA);
        crypto_freereq(crp);
        DPRINTF(("esp_input: m_makewritable failed\n"));
        ESP_STATINC(ESP_STAT_CRYPTO);
        return error;
    }

    tc->tc_ptr = mtag;

    if (esph) {
        struct cryptodesc *crda = crp->crp_desc;

        IPSEC_ASSERT(crda != NULL, ("esp_input: null ah crypto descriptor"));

        /* Authentication descriptor */
        crda->crd_skip = skip;
        if (espx && espx->type == CRYPTO_AES_GCM_16)
            crda->crd_len = hlen - sav->ivlen;
        else
            crda->crd_len = m->m_pkthdr.len - (skip + alen);
        crda->crd_inject = m->m_pkthdr.len - alen;

        crda->crd_alg = esph->type;
        if (espx && (espx->type == CRYPTO_AES_GCM_16 ||
                     espx->type == CRYPTO_AES_GMAC)) {
            crda->crd_key = _KEYBUF(sav->key_enc);
            crda->crd_klen = _KEYBITS(sav->key_enc);
        } else {
            crda->crd_key = _KEYBUF(sav->key_auth);
            crda->crd_klen = _KEYBITS(sav->key_auth);
        }

        /* Copy the authenticator */
        if (mtag == NULL)
            m_copydata(m, m->m_pkthdr.len - alen, alen,
                       (tc + 1));

        /* Chain authentication request */
        crde = crda->crd_next;
    } else {
        crde = crp->crp_desc;
    }

    /* Crypto operation descriptor */
    crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
    crp->crp_flags = CRYPTO_F_IMBUF;
    crp->crp_buf = m;
    crp->crp_callback = esp_input_cb;
    crp->crp_sid = sav->tdb_cryptoid;
    crp->crp_opaque = tc;

    /* These are passed as-is to the callback */
    tc->tc_spi = sav->spi;
    tc->tc_dst = sav->sah->saidx.dst;
    tc->tc_proto = sav->sah->saidx.proto;
    tc->tc_protoff = protoff;
    tc->tc_skip = skip;

    /* Decryption descriptor */
    if (espx) {
        IPSEC_ASSERT(crde != NULL, ("esp_input: null esp crypto descriptor"));
        crde->crd_skip = skip + hlen;
        if (espx->type == CRYPTO_AES_GMAC)
            crde->crd_len = 0;
        else
            crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
        crde->crd_inject = skip + hlen - sav->ivlen;

        crde->crd_alg = espx->type;
        crde->crd_key = _KEYBUF(sav->key_enc);
        crde->crd_klen = _KEYBITS(sav->key_enc);
        /* XXX Rounds ? */
    }

    if (mtag == NULL)
        return crypto_dispatch(crp);
    else
        return esp_input_cb(crp);
}