Example #1
0
struct iked_message *
ikev2_msg_copy(struct iked *env, struct iked_message *msg)
{
	struct iked_message		*m = NULL;
	struct ibuf			*buf;
	size_t				 len;
	void				*ptr;

	if (ibuf_size(msg->msg_data) < msg->msg_offset)
		return (NULL);
	len = ibuf_size(msg->msg_data) - msg->msg_offset;

	if ((ptr = ibuf_seek(msg->msg_data, msg->msg_offset, len)) == NULL ||
	    (m = malloc(sizeof(*m))) == NULL ||
	    (buf = ikev2_msg_init(env, m, &msg->msg_peer, msg->msg_peerlen,
	     &msg->msg_local, msg->msg_locallen, msg->msg_response)) == NULL ||
	    ibuf_add(buf, ptr, len))
		return (NULL);

	m->msg_fd = msg->msg_fd;
	m->msg_msgid = msg->msg_msgid;
	m->msg_offset = msg->msg_offset;
	m->msg_sa = msg->msg_sa;

	return (m);
}
Example #2
0
int
ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src)
{
	int			 ret = -1;
	size_t			 integrlen, tmplen;
	struct ibuf		*integr, *prf, *tmp = NULL;
	u_int8_t		*ptr;

	log_debug("%s: message length %d", __func__, ibuf_size(src));
	print_hex(ibuf_data(src), 0, ibuf_size(src));

	if (sa == NULL ||
	    sa->sa_integr == NULL) {
		log_debug("%s: invalid SA", __func__);
		return (-1);
	}

	if (sa->sa_hdr.sh_initiator) {
		integr = sa->sa_key_iauth;
		prf = sa->sa_key_iprf;
	} else {
		integr = sa->sa_key_rauth;
		prf = sa->sa_key_rprf;
	}

	integrlen = hash_length(sa->sa_integr);

	log_debug("%s: integrity checksum length %d", __func__,
	    integrlen);

	/*
	 * Validate packet checksum
	 */
	if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
		goto done;

	hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr));
	hash_init(sa->sa_integr);
	hash_update(sa->sa_integr, ibuf_data(src),
	    ibuf_size(src) - integrlen);
	hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);

	if (tmplen != integrlen) {
		log_debug("%s: hash failure", __func__);
		goto done;
	}

	if ((ptr = ibuf_seek(src,
	    ibuf_size(src) - integrlen, integrlen)) == NULL)
		goto done;
	memcpy(ptr, ibuf_data(tmp), tmplen);

	print_hex(ibuf_data(tmp), 0, ibuf_size(tmp));

	ret = 0;
 done:
	ibuf_release(tmp);

	return (ret);
}
Example #3
0
int
ikev2_msg_send(struct iked *env, struct iked_message *msg)
{
	struct ibuf		*buf = msg->msg_data;
	u_int32_t		 natt = 0x00000000;
	struct ike_header	*hdr;

	if (buf == NULL || (hdr = ibuf_seek(msg->msg_data,
	    msg->msg_offset, sizeof(*hdr))) == NULL)
		return (-1);

	log_info("%s: %s from %s to %s, %ld bytes", __func__,
	    print_map(hdr->ike_exchange, ikev2_exchange_map),
	    print_host(&msg->msg_local, NULL, 0),
	    print_host(&msg->msg_peer, NULL, 0),
	    ibuf_length(buf));

	if (msg->msg_natt || (msg->msg_sa && msg->msg_sa->sa_natt)) {
		if (ibuf_prepend(buf, &natt, sizeof(natt)) == -1) {
			log_debug("%s: failed to set NAT-T", __func__);
			return (-1);
		}
	}

	if ((sendto(msg->msg_fd, ibuf_data(buf), ibuf_size(buf), 0,
	    (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen)) == -1) {
		log_warn("%s: sendto", __func__);
		return (-1);
	}

	return (0);
}
int
ikev2_pld_eap(struct iked *env, struct ikev2_payload *pld,
    struct iked_message *msg, size_t offset, size_t left)
{
	struct eap_header		 hdr;
	struct eap_message		*eap = NULL;
	struct iked_sa			*sa = msg->msg_sa;
	size_t				 len;

	if (ikev2_validate_eap(msg, offset, left, pld, &hdr))
		return (-1);
	len = betoh16(hdr.eap_length);

	if (len < sizeof(*eap)) {
		log_info("%s: %s id %d length %d", __func__,
		    print_map(hdr.eap_code, eap_code_map),
		    hdr.eap_id, betoh16(hdr.eap_length));
	} else {
		/* Now try to get the indicated length */
		if ((eap = ibuf_seek(msg->msg_data, offset, len)) == NULL) {
			log_debug("%s: invalid EAP length", __func__);
			return (-1);
		}

		log_info("%s: %s id %d length %d EAP-%s", __func__,
		    print_map(eap->eap_code, eap_code_map),
		    eap->eap_id, betoh16(eap->eap_length),
		    print_map(eap->eap_type, eap_type_map));
	}

	if (eap_parse(env, sa, &hdr, msg->msg_response) == -1)
		return (-1);

	return (0);
}
Example #5
0
int
ikev2_msg_send(struct iked *env, struct iked_message *msg)
{
	struct iked_sa		*sa = msg->msg_sa;
	struct ibuf		*buf = msg->msg_data;
	u_int32_t		 natt = 0x00000000;
	int			 isnatt = 0;
	struct ike_header	*hdr;
	struct iked_message	*m;

	if (buf == NULL || (hdr = ibuf_seek(msg->msg_data,
	    msg->msg_offset, sizeof(*hdr))) == NULL)
		return (-1);

	isnatt = (msg->msg_natt || (msg->msg_sa && msg->msg_sa->sa_natt));

	log_info("%s: %s from %s to %s, %ld bytes%s", __func__,
	    print_map(hdr->ike_exchange, ikev2_exchange_map),
	    print_host(&msg->msg_local, NULL, 0),
	    print_host(&msg->msg_peer, NULL, 0),
	    ibuf_length(buf), isnatt ? ", NAT-T" : "");

	if (isnatt) {
		if (ibuf_prepend(buf, &natt, sizeof(natt)) == -1) {
			log_debug("%s: failed to set NAT-T", __func__);
			return (-1);
		}
		msg->msg_offset += sizeof(natt);
	}

	if ((sendto(msg->msg_fd, ibuf_data(buf), ibuf_size(buf), 0,
	    (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen)) == -1) {
		log_warn("%s: sendto", __func__);
		return (-1);
	}

	if (!sa)
		return (0);

	if ((m = ikev2_msg_copy(env, msg)) == NULL) {
		log_debug("%s: failed to copy a message", __func__);
		return (-1);
	}
	m->msg_exchange = hdr->ike_exchange;

	if (hdr->ike_flags & IKEV2_FLAG_RESPONSE) {
		TAILQ_INSERT_TAIL(&sa->sa_responses, m, msg_entry);
		timer_initialize(env, &m->msg_timer,
		    ikev2_msg_response_timeout, m);
		timer_register(env, &m->msg_timer, IKED_RESPONSE_TIMEOUT);
	} else {
		TAILQ_INSERT_TAIL(&sa->sa_requests, m, msg_entry);
		timer_initialize(env, &m->msg_timer,
		    ikev2_msg_retransmit_timeout, m);
		timer_register(env, &m->msg_timer, IKED_RETRANSMIT_TIMEOUT);
	}

	return (0);
}
Example #6
0
int
ikev2_msg_frompeer(struct iked_message *msg)
{
	struct iked_sa		*sa = msg->msg_sa;
	struct ike_header	*hdr;

	msg = msg->msg_parent;

	if (sa == NULL ||
	    (hdr = ibuf_seek(msg->msg_data, 0, sizeof(*hdr))) == NULL)
		return (0);

	if (!sa->sa_hdr.sh_initiator &&
	    (hdr->ike_flags & IKEV2_FLAG_INITIATOR))
		return (1);
	else if (sa->sa_hdr.sh_initiator &&
	    (hdr->ike_flags & IKEV2_FLAG_INITIATOR) == 0)
		return (1);

	return (0);
}
Example #7
0
/* DVMRP neighbors2 packet handling */
int
send_nbrs2(struct iface *iface, struct in_addr addr, void *data, int len)
{
	struct sockaddr_in	 dst;
	struct ibuf		*buf;
	struct dvmrp_hdr	*dvmrp_hdr;
	int			 ret = 0;

	log_debug("send_nbrs2: interface %s addr %s",
	    iface->name, inet_ntoa(addr));

	if (iface->passive)
		return (0);

	if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL)
		fatal("send_nbrs2");

	/* DVMRP header */
	if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_GRAFT_ACK))
		goto fail;

	dst.sin_family = AF_INET;
	dst.sin_len = sizeof(struct sockaddr_in);
	dst.sin_addr.s_addr = addr.s_addr;

	/* update chksum */
	dvmrp_hdr = ibuf_seek(buf, 0, sizeof(dvmrp_hdr));
	dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos);

	ret = send_packet(iface, buf->buf, buf->wpos, &dst);
	ibuf_free(buf);
	return (ret);
fail:
	log_warn("send_nbrs2");
	ibuf_free(buf);
	return (-1);
}
Example #8
0
int
ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld,
    struct iked_message *msg, off_t offset)
{
	struct iked_childsa	**peersas = NULL;
	struct iked_sa		*sa = msg->msg_sa;
	struct ikev2_delete	*del, *localdel;
	struct ibuf		*resp = NULL;
	u_int64_t		*localspi = NULL;
	u_int64_t		 spi64, spi = 0;
	u_int32_t		 spi32;
	u_int8_t		*buf, *msgbuf = ibuf_data(msg->msg_data);
	size_t			 found = 0, failed = 0;
	int			 cnt, i, len, sz, ret = -1;

	/* Skip if it's a reply and we don't have to deal with it */
	if (ikev2_msg_frompeer(msg) && sa &&
	    (sa->sa_stateflags & IKED_REQ_INF)) {
		sa->sa_stateflags &= ~IKED_REQ_INF;
		if ((sa->sa_stateflags & IKED_REQ_DELETE) == 0)
			return (0);
	}

	if ((del = ibuf_seek(msg->msg_data, offset, sizeof(*del))) == NULL)
		return (-1);
	cnt = betoh16(del->del_nspi);
	sz = del->del_spisize;

	log_debug("%s: proto %s spisize %d nspi %d",
	    __func__, print_map(del->del_protoid, ikev2_saproto_map),
	    sz, cnt);

	buf = msgbuf + offset + sizeof(*del);
	len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(*del);

	print_hex(buf, 0, len);

	switch (sz) {
	case 4:
	case 8:
		break;
	default:
		if (ikev2_msg_frompeer(msg) &&
		    del->del_protoid == IKEV2_SAPROTO_IKE) {
			/* Send an empty informational response */
			if ((resp = ibuf_static()) == NULL)
				goto done;
			ret = ikev2_send_ike_e(env, sa, resp,
			    IKEV2_PAYLOAD_NONE,
			    IKEV2_EXCHANGE_INFORMATIONAL, 1);
			ibuf_release(resp);
			sa_state(env, sa, IKEV2_STATE_CLOSED);
			return (ret);
		}
		log_debug("%s: invalid SPI size", __func__);
		return (-1);
	}

	if ((len / sz) != cnt) {
		log_debug("%s: invalid payload length %d/%d != %d",
		    __func__, len, sz, cnt);
		return (-1);
	}

	if (ikev2_msg_frompeer(msg) &&
	    ((peersas = calloc(cnt, sizeof(struct iked_childsa *))) == NULL ||
	     (localspi = calloc(cnt, sizeof(u_int64_t))) == NULL)) {
		log_warn("%s", __func__);
		goto done;
	}

	for (i = 0; i < cnt; i++) {
		switch (sz) {
		case 4:
			memcpy(&spi32, buf + (i * sz), sizeof(spi32));
			spi = betoh32(spi32);
			break;
		case 8:
			memcpy(&spi64, buf + (i * sz), sizeof(spi64));
			spi = betoh64(spi64);
			break;
		}

		log_debug("%s: spi %s", __func__, print_spi(spi, sz));

		if (peersas == NULL || sa == NULL)
			continue;

		if ((peersas[i] = childsa_lookup(sa, spi,
		    del->del_protoid)) == NULL) {
			log_warnx("%s: CHILD SA doesn't exist for spi %s",
			    __func__, print_spi(spi, del->del_spisize));
			goto done;
		}

		if (ikev2_childsa_delete(env, sa, del->del_protoid, spi,
		    &localspi[i], 0) == -1)
			failed++;
		else
			found++;

		/*
		 * Flows are left in the require mode so that it would be
		 * possible to quickly negotiate a new Child SA
		 */
	}

	/* Parsed outgoing message? */
	if (!ikev2_msg_frompeer(msg))
		goto done;

	if (sa && (sa->sa_stateflags & IKED_REQ_DELETE)) {
		/* Finish rekeying */
		sa->sa_stateflags &= ~IKED_REQ_DELETE;
		ret = 0;
		goto done;
	}

	/* Response to the INFORMATIONAL with Delete payload */

	if ((resp = ibuf_static()) == NULL)
		goto done;

	if (found) {
		if ((localdel = ibuf_advance(resp, sizeof(*localdel))) == NULL)
			goto done;

		localdel->del_protoid = del->del_protoid;
		localdel->del_spisize = del->del_spisize;
		localdel->del_nspi = htobe16(found);

		for (i = 0; i < cnt; i++) {
			switch (sz) {
			case 4:
				spi32 = htobe32(localspi[i]);
				if (ibuf_add(resp, &spi32, sizeof(spi32)) != 0)
					goto done;
				break;
			case 8:
				spi64 = htobe64(localspi[i]);
				if (ibuf_add(resp, &spi64, sizeof(spi64)) != 0)
					goto done;
				break;
			}
		}

		log_warnx("%s: deleted %d spis", __func__, found);
	}

	if (found) {
		ret = ikev2_send_ike_e(env, sa, resp, IKEV2_PAYLOAD_DELETE,
		    IKEV2_EXCHANGE_INFORMATIONAL, 1);
	} else {
		/* XXX should we send an INVALID_SPI notification? */
		ret = 0;
	}

 done:
	if (localspi)
		free(localspi);
	if (peersas)
		free(peersas);
	ibuf_release(resp);
	return (ret);
}
Example #9
0
int
ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld,
    struct iked_message *msg, off_t offset)
{
	struct ikev2_notify	*n;
	u_int8_t		*buf, md[SHA_DIGEST_LENGTH];
	size_t			 len;
	u_int32_t		 spi32;
	u_int64_t		 spi64;
	struct iked_spi		*rekey;
	u_int16_t		 type;
	u_int16_t		 group;

	if ((n = ibuf_seek(msg->msg_data, offset, sizeof(*n))) == NULL)
		return (-1);
	type = betoh16(n->n_type);

	log_debug("%s: protoid %s spisize %d type %s",
	    __func__,
	    print_map(n->n_protoid, ikev2_saproto_map), n->n_spisize,
	    print_map(type, ikev2_n_map));

	len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(*n);
	if ((buf = ibuf_seek(msg->msg_data, offset + sizeof(*n), len)) == NULL)
		return (-1);

	print_hex(buf, 0, len);

	if (!ikev2_msg_frompeer(msg))
		return (0);

	switch (type) {
	case IKEV2_N_NAT_DETECTION_SOURCE_IP:
	case IKEV2_N_NAT_DETECTION_DESTINATION_IP:
		if (ikev2_nat_detection(env, msg, md, sizeof(md), type) == -1)
			return (-1);
		if (len != sizeof(md) || memcmp(buf, md, len) != 0) {
			log_debug("%s: %s detected NAT, enabling "
			    "UDP encapsulation", __func__,
			    print_map(type, ikev2_n_map));

			/*
			 * Enable UDP encapsulation of ESP packages if
			 * the check detected NAT.
			 */
			if (msg->msg_sa != NULL)
				msg->msg_sa->sa_udpencap = 1;
		}
		print_hex(md, 0, sizeof(md));
		break;
	case IKEV2_N_INVALID_KE_PAYLOAD:
		if (len != sizeof(group)) {
			log_debug("%s: malformed notification", __func__);
			return (-1);
		}
		if (!msg->msg_sa->sa_hdr.sh_initiator) {
			log_debug("%s: not an initiator", __func__);
			sa_free(env, msg->msg_sa);
			msg->msg_sa = NULL;
			return (-1);
		}
		memcpy(&group, buf, len);
		group = betoh16(group);
		if ((msg->msg_policy->pol_peerdh = group_get(group))
		    == NULL) {
			log_debug("%s: unable to select DH group %d", __func__,
			    group);
			return (-1);
		}
		log_debug("%s: responder selected DH group %d", __func__,
		    group);
		sa_free(env, msg->msg_sa);
		msg->msg_sa = NULL;
		timer_initialize(env, &env->sc_inittmr, ikev2_init_ike_sa,
		    NULL);
		timer_register(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL);
		break;
	case IKEV2_N_NO_ADDITIONAL_SAS:
		/* This makes sense for Child SAs only atm */
		if (msg->msg_sa->sa_stateflags & IKED_REQ_CHILDSA) {
			ikev2_disable_rekeying(env, msg->msg_sa);
			msg->msg_sa->sa_stateflags &= ~IKED_REQ_CHILDSA;
		}
		break;
	case IKEV2_N_REKEY_SA:
		if (len != n->n_spisize) {
			log_debug("%s: malformed notification", __func__);
			return (-1);
		}
		rekey = &msg->msg_parent->msg_rekey;
		if (rekey->spi != 0) {
			log_debug("%s: rekeying of multiple SAs not supported",
			    __func__);
			return (-1);
		}
		switch (n->n_spisize) {
		case 4:
			memcpy(&spi32, buf, len);
			rekey->spi = betoh32(spi32);
			break;
		case 8:
			memcpy(&spi64, buf, len);
			rekey->spi = betoh64(spi64);
			break;
		default:
			log_debug("%s: invalid spi size %d", __func__,
			    n->n_spisize);
			return (-1);
		}
		rekey->spi_size = n->n_spisize;
		rekey->spi_protoid = n->n_protoid;

		log_debug("%s: rekey %s spi %s", __func__,
		    print_map(n->n_protoid, ikev2_saproto_map),
		    print_spi(rekey->spi, n->n_spisize));
		break;
	}

	return (0);
}
Example #10
0
/* database description packet handling */
int
send_db_description(struct nbr *nbr)
{
	struct in6_addr		 dst;
	struct db_dscrp_hdr	 dd_hdr;
	struct lsa_entry	*le, *nle;
	struct ibuf		*buf;
	int			 ret = 0;
	u_int8_t		 bits = 0;

	if ((buf = ibuf_open(nbr->iface->mtu - sizeof(struct ip))) == NULL)
		fatal("send_db_description");

	/* OSPF header */
	if (gen_ospf_hdr(buf, nbr->iface, PACKET_TYPE_DD))
		goto fail;

	/* reserve space for database description header */
	if (ibuf_reserve(buf, sizeof(dd_hdr)) == NULL)
		goto fail;

	switch (nbr->state) {
	case NBR_STA_DOWN:
	case NBR_STA_ATTEMPT:
	case NBR_STA_INIT:
	case NBR_STA_2_WAY:
	case NBR_STA_SNAP:
		log_debug("send_db_description: cannot send packet in state %s,"
		    " neighbor ID %s", nbr_state_name(nbr->state),
		    inet_ntoa(nbr->id));
		ret = -1;
		goto done;
	case NBR_STA_XSTRT:
		bits |= OSPF_DBD_MS | OSPF_DBD_M | OSPF_DBD_I;
		nbr->dd_more = 1;
		break;
	case NBR_STA_XCHNG:
		if (nbr->dd_master)
			bits |= OSPF_DBD_MS;
		else
			bits &= ~OSPF_DBD_MS;

		if (TAILQ_EMPTY(&nbr->db_sum_list)) {
			bits &= ~OSPF_DBD_M;
			nbr->dd_more = 0;
		} else {
			bits |= OSPF_DBD_M;
			nbr->dd_more = 1;
		}

		bits &= ~OSPF_DBD_I;

		/* build LSA list */
		for (le = TAILQ_FIRST(&nbr->db_sum_list); le != NULL &&
		    buf->wpos + sizeof(struct lsa_hdr) < buf->max; le = nle) {
			nbr->dd_end = nle = TAILQ_NEXT(le, entry);
			if (ibuf_add(buf, le->le_lsa, sizeof(struct lsa_hdr)))
				goto fail;
		}
		break;
	case NBR_STA_LOAD:
	case NBR_STA_FULL:
		if (nbr->dd_master)
			bits |= OSPF_DBD_MS;
		else
			bits &= ~OSPF_DBD_MS;
		bits &= ~OSPF_DBD_M;
		bits &= ~OSPF_DBD_I;

		nbr->dd_more = 0;
		break;
	default:
		fatalx("send_db_description: unknown neighbor state");
	}

	bzero(&dd_hdr, sizeof(dd_hdr));

	switch (nbr->iface->type) {
	case IF_TYPE_POINTOPOINT:
		inet_pton(AF_INET6, AllSPFRouters, &dst);
		dd_hdr.iface_mtu = htons(nbr->iface->mtu);
		break;
	case IF_TYPE_BROADCAST:
		dst = nbr->addr;
		dd_hdr.iface_mtu = htons(nbr->iface->mtu);
		break;
	case IF_TYPE_NBMA:
	case IF_TYPE_POINTOMULTIPOINT:
		/* XXX not supported */
		break;
	case IF_TYPE_VIRTUALLINK:
		dst = nbr->iface->dst;
		dd_hdr.iface_mtu = 0;
		break;
	default:
		fatalx("send_db_description: unknown interface type");
	}

	dd_hdr.opts = htonl(area_ospf_options(area_find(oeconf,
	    nbr->iface->area_id)));
	dd_hdr.bits = bits;
	dd_hdr.dd_seq_num = htonl(nbr->dd_seq_num);

	memcpy(ibuf_seek(buf, sizeof(struct ospf_hdr), sizeof(dd_hdr)),
	    &dd_hdr, sizeof(dd_hdr));

	/* calculate checksum */
	if (upd_ospf_hdr(buf, nbr->iface))
		goto fail;

	/* transmit packet */
	ret = send_packet(nbr->iface, buf->buf, buf->wpos, &dst);
done:
	ibuf_free(buf);
	return (ret);
fail:
	log_warn("send_db_description");
	ibuf_free(buf);
	return (-1);
}
Example #11
0
struct ibuf *
ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa,
    struct ibuf *msg, struct ibuf *src)
{
	ssize_t			 ivlen, encrlen, integrlen, blocklen,
				    outlen, tmplen;
	uint8_t			 pad = 0, *ptr;
	struct ibuf		*integr, *encr, *tmp = NULL, *out = NULL;
	off_t			 ivoff, encroff, integroff;

	if (sa == NULL ||
	    sa->sa_encr == NULL ||
	    sa->sa_integr == NULL) {
		log_debug("%s: invalid SA", __func__);
		print_hex(ibuf_data(src), 0, ibuf_size(src));
		goto done;
	}

	if (!sa->sa_hdr.sh_initiator) {
		encr = sa->sa_key_iencr;
		integr = sa->sa_key_iauth;
	} else {
		encr = sa->sa_key_rencr;
		integr = sa->sa_key_rauth;
	}

	blocklen = cipher_length(sa->sa_encr);
	ivlen = cipher_ivlength(sa->sa_encr);
	ivoff = 0;
	integrlen = hash_length(sa->sa_integr);
	integroff = ibuf_size(src) - integrlen;
	encroff = ivlen;
	encrlen = ibuf_size(src) - integrlen - ivlen;

	if (encrlen < 0 || integroff < 0) {
		log_debug("%s: invalid integrity value", __func__);
		goto done;
	}

	log_debug("%s: IV length %zd", __func__, ivlen);
	print_hex(ibuf_data(src), 0, ivlen);
	log_debug("%s: encrypted payload length %zd", __func__, encrlen);
	print_hex(ibuf_data(src), encroff, encrlen);
	log_debug("%s: integrity checksum length %zd", __func__, integrlen);
	print_hex(ibuf_data(src), integroff, integrlen);

	/*
	 * Validate packet checksum
	 */
	if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL)
		goto done;

	hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
	hash_init(sa->sa_integr);
	hash_update(sa->sa_integr, ibuf_data(msg),
	    ibuf_size(msg) - integrlen);
	hash_final(sa->sa_integr, tmp->buf, &tmplen);

	if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) {
		log_debug("%s: integrity check failed", __func__);
		goto done;
	}

	log_debug("%s: integrity check succeeded", __func__);
	print_hex(tmp->buf, 0, tmplen);

	ibuf_release(tmp);
	tmp = NULL;

	/*
	 * Decrypt the payload and strip any padding
	 */
	if ((encrlen % blocklen) != 0) {
		log_debug("%s: unaligned encrypted payload", __func__);
		goto done;
	}

	cipher_setkey(sa->sa_encr, encr->buf, ibuf_length(encr));
	cipher_setiv(sa->sa_encr, ibuf_data(src) + ivoff, ivlen);
	cipher_init_decrypt(sa->sa_encr);

	if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
	    encrlen))) == NULL)
		goto done;

	if ((outlen = ibuf_length(out)) != 0) {
		cipher_update(sa->sa_encr, ibuf_data(src) + encroff, encrlen,
		    ibuf_data(out), &outlen);

		ptr = ibuf_seek(out, outlen - 1, 1);
		pad = *ptr;
	}

	log_debug("%s: decrypted payload length %zd/%zd padding %d",
	    __func__, outlen, encrlen, pad);
	print_hex(ibuf_data(out), 0, ibuf_size(out));

	if (ibuf_setsize(out, outlen) != 0)
		goto done;

	ibuf_release(src);
	return (out);
 done:
	ibuf_release(tmp);
	ibuf_release(out);
	ibuf_release(src);
	return (NULL);
}