xref: /openbsd/sbin/iked/ikev2_msg.c (revision 9ce164ed)
1*9ce164edStobhe /*	$OpenBSD: ikev2_msg.c,v 1.82 2021/11/27 21:50:05 tobhe Exp $	*/
2fde46d6eSreyk 
3fde46d6eSreyk /*
465c540d0Spatrick  * Copyright (c) 2019 Tobias Heider <tobias.heider@stusta.de>
5fcebd35dSreyk  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
6fde46d6eSreyk  *
7fde46d6eSreyk  * Permission to use, copy, modify, and distribute this software for any
8fde46d6eSreyk  * purpose with or without fee is hereby granted, provided that the above
9fde46d6eSreyk  * copyright notice and this permission notice appear in all copies.
10fde46d6eSreyk  *
11fde46d6eSreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12fde46d6eSreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13fde46d6eSreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14fde46d6eSreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15fde46d6eSreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16fde46d6eSreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17fde46d6eSreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18fde46d6eSreyk  */
19fde46d6eSreyk 
20b9fc9a72Sderaadt #include <sys/param.h>	/* roundup */
21fde46d6eSreyk #include <sys/queue.h>
22fde46d6eSreyk #include <sys/socket.h>
23fde46d6eSreyk #include <sys/uio.h>
24fde46d6eSreyk 
25fde46d6eSreyk #include <netinet/in.h>
26fde46d6eSreyk #include <arpa/inet.h>
27fde46d6eSreyk 
28fde46d6eSreyk #include <stdlib.h>
29fde46d6eSreyk #include <stdio.h>
3053684dccStobhe #include <syslog.h>
31fde46d6eSreyk #include <unistd.h>
32fde46d6eSreyk #include <string.h>
33fde46d6eSreyk #include <signal.h>
34fde46d6eSreyk #include <errno.h>
35fde46d6eSreyk #include <err.h>
36fde46d6eSreyk #include <event.h>
37fde46d6eSreyk 
38fde46d6eSreyk #include <openssl/sha.h>
39fde46d6eSreyk #include <openssl/evp.h>
40fde46d6eSreyk 
41fde46d6eSreyk #include "iked.h"
42fde46d6eSreyk #include "ikev2.h"
43fde46d6eSreyk #include "eap.h"
44fde46d6eSreyk #include "dh.h"
45fde46d6eSreyk 
4605256619Sreyk void	 ikev1_recv(struct iked *, struct iked_message *);
47c45fd413Smikeb void	 ikev2_msg_response_timeout(struct iked *, void *);
48c45fd413Smikeb void	 ikev2_msg_retransmit_timeout(struct iked *, void *);
49d56261e5Stobhe int	 ikev2_check_frag_oversize(struct iked_sa *, struct ibuf *);
50d56261e5Stobhe int	 ikev2_send_encrypted_fragments(struct iked *, struct iked_sa *,
51d56261e5Stobhe 	    struct ibuf *, uint8_t, uint8_t, int);
5215863c3aStobhe int	 ikev2_msg_encrypt_prepare(struct iked_sa *, struct ikev2_payload *,
5315863c3aStobhe 	    struct ibuf*, struct ibuf *, struct ike_header *, uint8_t, int);
54c45fd413Smikeb 
55fde46d6eSreyk void
56fde46d6eSreyk ikev2_msg_cb(int fd, short event, void *arg)
57fde46d6eSreyk {
58fde46d6eSreyk 	struct iked_socket	*sock = arg;
59fde46d6eSreyk 	struct iked		*env = sock->sock_env;
60fde46d6eSreyk 	struct iked_message	 msg;
61fde46d6eSreyk 	struct ike_header	 hdr;
62d09d3a7dSreyk 	uint32_t		 natt = 0x00000000;
63d09d3a7dSreyk 	uint8_t			 buf[IKED_MSGBUF_MAX];
64fde46d6eSreyk 	ssize_t			 len;
65fde46d6eSreyk 	off_t			 off;
66fde46d6eSreyk 
67fde46d6eSreyk 	bzero(&msg, sizeof(msg));
68fde46d6eSreyk 	bzero(buf, sizeof(buf));
69fde46d6eSreyk 
70fde46d6eSreyk 	msg.msg_peerlen = sizeof(msg.msg_peer);
71fde46d6eSreyk 	msg.msg_locallen = sizeof(msg.msg_local);
7226d7dba1Sreyk 	msg.msg_parent = &msg;
73fde46d6eSreyk 	memcpy(&msg.msg_local, &sock->sock_addr, sizeof(sock->sock_addr));
74fde46d6eSreyk 
75fde46d6eSreyk 	if ((len = recvfromto(fd, buf, sizeof(buf), 0,
76fde46d6eSreyk 	    (struct sockaddr *)&msg.msg_peer, &msg.msg_peerlen,
77fde46d6eSreyk 	    (struct sockaddr *)&msg.msg_local, &msg.msg_locallen)) <
78fde46d6eSreyk 	    (ssize_t)sizeof(natt))
79fde46d6eSreyk 		return;
80fde46d6eSreyk 
8147d6a31cSmarkus 	if (socket_getport((struct sockaddr *)&msg.msg_local) ==
8259c69d76Stobhe 	    env->sc_nattport) {
8334d94968Stedu 		if (memcmp(&natt, buf, sizeof(natt)) != 0)
84fde46d6eSreyk 			return;
85fde46d6eSreyk 		msg.msg_natt = 1;
86fde46d6eSreyk 		off = sizeof(natt);
87fde46d6eSreyk 	} else
88fde46d6eSreyk 		off = 0;
89fde46d6eSreyk 
90fde46d6eSreyk 	if ((size_t)(len - off) <= sizeof(hdr))
91fde46d6eSreyk 		return;
92fde46d6eSreyk 	memcpy(&hdr, buf + off, sizeof(hdr));
93fde46d6eSreyk 
94fde46d6eSreyk 	if ((msg.msg_data = ibuf_new(buf + off, len - off)) == NULL)
95fde46d6eSreyk 		return;
96fde46d6eSreyk 
97fde46d6eSreyk 	TAILQ_INIT(&msg.msg_proposals);
983e395450Stobhe 	SIMPLEQ_INIT(&msg.msg_certreqs);
99fde46d6eSreyk 	msg.msg_fd = fd;
10005256619Sreyk 
10105256619Sreyk 	if (hdr.ike_version == IKEV1_VERSION)
10205256619Sreyk 		ikev1_recv(env, &msg);
10305256619Sreyk 	else
104fde46d6eSreyk 		ikev2_recv(env, &msg);
105fde46d6eSreyk 
106763023d6Sreyk 	ikev2_msg_cleanup(env, &msg);
107fde46d6eSreyk }
108fde46d6eSreyk 
10905256619Sreyk void
11005256619Sreyk ikev1_recv(struct iked *env, struct iked_message *msg)
11105256619Sreyk {
11205256619Sreyk 	struct ike_header	*hdr;
11305256619Sreyk 
11405256619Sreyk 	if (ibuf_size(msg->msg_data) <= sizeof(*hdr)) {
11505256619Sreyk 		log_debug("%s: short message", __func__);
11605256619Sreyk 		return;
11705256619Sreyk 	}
11805256619Sreyk 
11905256619Sreyk 	hdr = (struct ike_header *)ibuf_data(msg->msg_data);
12005256619Sreyk 
12105256619Sreyk 	log_debug("%s: header ispi %s rspi %s"
12205256619Sreyk 	    " nextpayload %u version 0x%02x exchange %u flags 0x%02x"
12305256619Sreyk 	    " msgid %u length %u", __func__,
12405256619Sreyk 	    print_spi(betoh64(hdr->ike_ispi), 8),
12505256619Sreyk 	    print_spi(betoh64(hdr->ike_rspi), 8),
12605256619Sreyk 	    hdr->ike_nextpayload,
12705256619Sreyk 	    hdr->ike_version,
12805256619Sreyk 	    hdr->ike_exchange,
12905256619Sreyk 	    hdr->ike_flags,
13005256619Sreyk 	    betoh32(hdr->ike_msgid),
13105256619Sreyk 	    betoh32(hdr->ike_length));
13205256619Sreyk 
13305256619Sreyk 	log_debug("%s: IKEv1 not supported", __func__);
13405256619Sreyk }
13505256619Sreyk 
136fde46d6eSreyk struct ibuf *
137fde46d6eSreyk ikev2_msg_init(struct iked *env, struct iked_message *msg,
138fde46d6eSreyk     struct sockaddr_storage *peer, socklen_t peerlen,
139fde46d6eSreyk     struct sockaddr_storage *local, socklen_t locallen, int response)
140fde46d6eSreyk {
141fde46d6eSreyk 	bzero(msg, sizeof(*msg));
142fde46d6eSreyk 	memcpy(&msg->msg_peer, peer, peerlen);
143fde46d6eSreyk 	msg->msg_peerlen = peerlen;
144fde46d6eSreyk 	memcpy(&msg->msg_local, local, locallen);
145fde46d6eSreyk 	msg->msg_locallen = locallen;
146fde46d6eSreyk 	msg->msg_response = response ? 1 : 0;
147fde46d6eSreyk 	msg->msg_fd = -1;
148fde46d6eSreyk 	msg->msg_data = ibuf_static();
14926d7dba1Sreyk 	msg->msg_e = 0;
15026d7dba1Sreyk 	msg->msg_parent = msg;	/* has to be set */
151763023d6Sreyk 	TAILQ_INIT(&msg->msg_proposals);
152fde46d6eSreyk 
153fde46d6eSreyk 	return (msg->msg_data);
154fde46d6eSreyk }
155fde46d6eSreyk 
156c45fd413Smikeb struct iked_message *
157c45fd413Smikeb ikev2_msg_copy(struct iked *env, struct iked_message *msg)
158c45fd413Smikeb {
159c45fd413Smikeb 	struct iked_message		*m = NULL;
160c45fd413Smikeb 	struct ibuf			*buf;
161d39d09feSreyk 	size_t				 len;
16212c9fd31Sreyk 	void				*ptr;
163c45fd413Smikeb 
164d39d09feSreyk 	if (ibuf_size(msg->msg_data) < msg->msg_offset)
165d39d09feSreyk 		return (NULL);
166d39d09feSreyk 	len = ibuf_size(msg->msg_data) - msg->msg_offset;
167d39d09feSreyk 
1688f4d0788Stobhe 	if ((m = malloc(sizeof(*m))) == NULL)
1698f4d0788Stobhe 		return (NULL);
1708f4d0788Stobhe 
171d39d09feSreyk 	if ((ptr = ibuf_seek(msg->msg_data, msg->msg_offset, len)) == NULL ||
172c45fd413Smikeb 	    (buf = ikev2_msg_init(env, m, &msg->msg_peer, msg->msg_peerlen,
173c45fd413Smikeb 	     &msg->msg_local, msg->msg_locallen, msg->msg_response)) == NULL ||
1748f4d0788Stobhe 	    ibuf_add(buf, ptr, len)) {
1758f4d0788Stobhe 		free(m);
176c45fd413Smikeb 		return (NULL);
1778f4d0788Stobhe 	}
178c45fd413Smikeb 
179c45fd413Smikeb 	m->msg_fd = msg->msg_fd;
180c45fd413Smikeb 	m->msg_msgid = msg->msg_msgid;
181c45fd413Smikeb 	m->msg_offset = msg->msg_offset;
182c45fd413Smikeb 	m->msg_sa = msg->msg_sa;
183c45fd413Smikeb 
184c45fd413Smikeb 	return (m);
185c45fd413Smikeb }
186c45fd413Smikeb 
187763023d6Sreyk void
188763023d6Sreyk ikev2_msg_cleanup(struct iked *env, struct iked_message *msg)
189763023d6Sreyk {
190eb2389caStobhe 	struct iked_certreq	*cr;
191eb2389caStobhe 
19226d7dba1Sreyk 	if (msg == msg->msg_parent) {
19326d7dba1Sreyk 		ibuf_release(msg->msg_nonce);
19426d7dba1Sreyk 		ibuf_release(msg->msg_ke);
19526d7dba1Sreyk 		ibuf_release(msg->msg_auth.id_buf);
196*9ce164edStobhe 		ibuf_release(msg->msg_peerid.id_buf);
197e3f5cf2eSpatrick 		ibuf_release(msg->msg_localid.id_buf);
19826d7dba1Sreyk 		ibuf_release(msg->msg_cert.id_buf);
199d4bcf9ebSreyk 		ibuf_release(msg->msg_cookie);
200c0b327e6Spatrick 		ibuf_release(msg->msg_cookie2);
201da56c325Stobhe 		ibuf_release(msg->msg_del_buf);
20225b39a47Stobhe 		free(msg->msg_eap.eam_user);
20352b3354cStobhe 		free(msg->msg_cp_addr);
20452b3354cStobhe 		free(msg->msg_cp_addr6);
2059ef39cf4Stobhe 		free(msg->msg_cp_dns);
20626d7dba1Sreyk 
2073642bd88Smikeb 		msg->msg_nonce = NULL;
2083642bd88Smikeb 		msg->msg_ke = NULL;
2093642bd88Smikeb 		msg->msg_auth.id_buf = NULL;
210*9ce164edStobhe 		msg->msg_peerid.id_buf = NULL;
211e3f5cf2eSpatrick 		msg->msg_localid.id_buf = NULL;
2123642bd88Smikeb 		msg->msg_cert.id_buf = NULL;
213d4bcf9ebSreyk 		msg->msg_cookie = NULL;
214c0b327e6Spatrick 		msg->msg_cookie2 = NULL;
215da56c325Stobhe 		msg->msg_del_buf = NULL;
21625b39a47Stobhe 		msg->msg_eap.eam_user = NULL;
21752b3354cStobhe 		msg->msg_cp_addr = NULL;
21852b3354cStobhe 		msg->msg_cp_addr6 = NULL;
2199ef39cf4Stobhe 		msg->msg_cp_dns = NULL;
220d4bcf9ebSreyk 
22126d7dba1Sreyk 		config_free_proposals(&msg->msg_proposals, 0);
2223e395450Stobhe 		while ((cr = SIMPLEQ_FIRST(&msg->msg_certreqs))) {
223eb2389caStobhe 			ibuf_release(cr->cr_data);
2243e395450Stobhe 			SIMPLEQ_REMOVE_HEAD(&msg->msg_certreqs, cr_entry);
225eb2389caStobhe 			free(cr);
226eb2389caStobhe 		}
22726d7dba1Sreyk 	}
22826d7dba1Sreyk 
229763023d6Sreyk 	if (msg->msg_data != NULL) {
230763023d6Sreyk 		ibuf_release(msg->msg_data);
231763023d6Sreyk 		msg->msg_data = NULL;
232763023d6Sreyk 	}
233763023d6Sreyk }
234763023d6Sreyk 
235fde46d6eSreyk int
236fde46d6eSreyk ikev2_msg_valid_ike_sa(struct iked *env, struct ike_header *oldhdr,
237fde46d6eSreyk     struct iked_message *msg)
238fde46d6eSreyk {
2396e1880a3Smarkus 	if (msg->msg_sa != NULL && msg->msg_policy != NULL) {
24069aac1baSmikeb 		if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSED)
24169aac1baSmikeb 			return (-1);
2426e1880a3Smarkus 		/*
2436e1880a3Smarkus 		 * Only permit informational requests from initiator
2446e1880a3Smarkus 		 * on closing SAs (for DELETE).
2456e1880a3Smarkus 		 */
2466e1880a3Smarkus 		if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSING) {
2476e1880a3Smarkus 			if (((oldhdr->ike_flags &
2486e1880a3Smarkus 			    (IKEV2_FLAG_INITIATOR|IKEV2_FLAG_RESPONSE)) ==
2496e1880a3Smarkus 			    IKEV2_FLAG_INITIATOR) &&
2506e1880a3Smarkus 			    (oldhdr->ike_exchange ==
2516e1880a3Smarkus 			    IKEV2_EXCHANGE_INFORMATIONAL))
252fde46d6eSreyk 				return (0);
2536e1880a3Smarkus 			return (-1);
2546e1880a3Smarkus 		}
2556e1880a3Smarkus 		return (0);
2566e1880a3Smarkus 	}
257fde46d6eSreyk 
258fde46d6eSreyk 	/* Always fail */
259fde46d6eSreyk 	return (-1);
260fde46d6eSreyk }
261fde46d6eSreyk 
262fde46d6eSreyk int
263d9c13a0aSmikeb ikev2_msg_send(struct iked *env, struct iked_message *msg)
264fde46d6eSreyk {
265c45fd413Smikeb 	struct iked_sa		*sa = msg->msg_sa;
266fde46d6eSreyk 	struct ibuf		*buf = msg->msg_data;
267d09d3a7dSreyk 	uint32_t		 natt = 0x00000000;
26812c9fd31Sreyk 	int			 isnatt = 0;
269d09d3a7dSreyk 	uint8_t			 exchange, flags;
270fde46d6eSreyk 	struct ike_header	*hdr;
271c45fd413Smikeb 	struct iked_message	*m;
272fde46d6eSreyk 
273fde46d6eSreyk 	if (buf == NULL || (hdr = ibuf_seek(msg->msg_data,
274fde46d6eSreyk 	    msg->msg_offset, sizeof(*hdr))) == NULL)
275fde46d6eSreyk 		return (-1);
276fde46d6eSreyk 
2770804246aStobhe 	isnatt = (msg->msg_natt || (sa && sa->sa_natt));
27812c9fd31Sreyk 
279670a137dSmikeb 	exchange = hdr->ike_exchange;
280670a137dSmikeb 	flags = hdr->ike_flags;
28153684dccStobhe 	logit(exchange == IKEV2_EXCHANGE_INFORMATIONAL ?  LOG_DEBUG : LOG_INFO,
28253684dccStobhe 	    "%ssend %s %s %u peer %s local %s, %ld bytes%s",
283ecea226bStobhe 	    SPI_IH(hdr),
284670a137dSmikeb 	    print_map(exchange, ikev2_exchange_map),
285ecea226bStobhe 	    (flags & IKEV2_FLAG_RESPONSE) ? "res" : "req",
2867f7372eaSmarkus 	    betoh32(hdr->ike_msgid),
287ecea226bStobhe 	    print_host((struct sockaddr *)&msg->msg_peer, NULL, 0),
288ecea226bStobhe 	    print_host((struct sockaddr *)&msg->msg_local, NULL, 0),
28912c9fd31Sreyk 	    ibuf_length(buf), isnatt ? ", NAT-T" : "");
290fde46d6eSreyk 
29112c9fd31Sreyk 	if (isnatt) {
292fde46d6eSreyk 		if (ibuf_prepend(buf, &natt, sizeof(natt)) == -1) {
293fde46d6eSreyk 			log_debug("%s: failed to set NAT-T", __func__);
294fde46d6eSreyk 			return (-1);
295fde46d6eSreyk 		}
296fde46d6eSreyk 	}
297d9c13a0aSmikeb 
2985ec2ede8Svgross 	if (sendtofrom(msg->msg_fd, ibuf_data(buf), ibuf_size(buf), 0,
2995ec2ede8Svgross 	    (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen,
3005ec2ede8Svgross 	    (struct sockaddr *)&msg->msg_local, msg->msg_locallen) == -1) {
3010804246aStobhe 		log_warn("%s: sendtofrom", __func__);
3020804246aStobhe 		if (sa != NULL && errno == EADDRNOTAVAIL) {
3030804246aStobhe 			sa_state(env, sa, IKEV2_STATE_CLOSING);
3040804246aStobhe 			timer_del(env, &sa->sa_timer);
3050804246aStobhe 			timer_set(env, &sa->sa_timer,
3060804246aStobhe 			    ikev2_ike_sa_timeout, sa);
3070804246aStobhe 			timer_add(env, &sa->sa_timer,
308a6fc7f59Shenning 			    IKED_IKE_SA_DELETE_TIMEOUT);
309a6fc7f59Shenning 		}
310fde46d6eSreyk 	}
311fde46d6eSreyk 
3120804246aStobhe 	if (sa == NULL)
313c45fd413Smikeb 		return (0);
314c45fd413Smikeb 
315c45fd413Smikeb 	if ((m = ikev2_msg_copy(env, msg)) == NULL) {
316c45fd413Smikeb 		log_debug("%s: failed to copy a message", __func__);
317c45fd413Smikeb 		return (-1);
318c45fd413Smikeb 	}
319670a137dSmikeb 	m->msg_exchange = exchange;
320c45fd413Smikeb 
321670a137dSmikeb 	if (flags & IKEV2_FLAG_RESPONSE) {
322c45fd413Smikeb 		TAILQ_INSERT_TAIL(&sa->sa_responses, m, msg_entry);
323b3eeacebSmikeb 		timer_set(env, &m->msg_timer, ikev2_msg_response_timeout, m);
324b3eeacebSmikeb 		timer_add(env, &m->msg_timer, IKED_RESPONSE_TIMEOUT);
325c45fd413Smikeb 	} else {
326c45fd413Smikeb 		TAILQ_INSERT_TAIL(&sa->sa_requests, m, msg_entry);
327b3eeacebSmikeb 		timer_set(env, &m->msg_timer, ikev2_msg_retransmit_timeout, m);
328b3eeacebSmikeb 		timer_add(env, &m->msg_timer, IKED_RETRANSMIT_TIMEOUT);
329c45fd413Smikeb 	}
330c45fd413Smikeb 
331fde46d6eSreyk 	return (0);
332fde46d6eSreyk }
333fde46d6eSreyk 
334d09d3a7dSreyk uint32_t
335c45fd413Smikeb ikev2_msg_id(struct iked *env, struct iked_sa *sa)
336fde46d6eSreyk {
337d09d3a7dSreyk 	uint32_t		id = sa->sa_reqid;
338fde46d6eSreyk 
33910650a52Smikeb 	if (++sa->sa_reqid == UINT32_MAX) {
340fde46d6eSreyk 		/* XXX we should close and renegotiate the connection now */
341fde46d6eSreyk 		log_debug("%s: IKEv2 message sequence overflow", __func__);
342fde46d6eSreyk 	}
34310650a52Smikeb 	return (id);
344fde46d6eSreyk }
345fde46d6eSreyk 
34615863c3aStobhe /*
34715863c3aStobhe  * Calculate the final sizes of the IKEv2 header and the encrypted payload
34815863c3aStobhe  * header.  This must be done before encryption to make sure the correct
34915863c3aStobhe  * headers are authenticated.
35015863c3aStobhe  */
35115863c3aStobhe int
35215863c3aStobhe ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld,
35315863c3aStobhe     struct ibuf *buf, struct ibuf *e, struct ike_header *hdr,
35415863c3aStobhe     uint8_t firstpayload, int fragmentation)
35515863c3aStobhe {
35615863c3aStobhe 	size_t	 len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen;
35715863c3aStobhe 
35815863c3aStobhe 	if (sa == NULL ||
35915863c3aStobhe 	    sa->sa_encr == NULL ||
36015863c3aStobhe 	    sa->sa_integr == NULL) {
36115863c3aStobhe 		log_debug("%s: invalid SA", __func__);
36215863c3aStobhe 		return (-1);
36315863c3aStobhe 	}
36415863c3aStobhe 
36515863c3aStobhe 	len = ibuf_size(e);
36615863c3aStobhe 	blocklen = cipher_length(sa->sa_encr);
36715863c3aStobhe 	integrlen = hash_length(sa->sa_integr);
36815863c3aStobhe 	ivlen = cipher_ivlength(sa->sa_encr);
36915863c3aStobhe 	encrlen = roundup(len + 1, blocklen);
37015863c3aStobhe 	outlen = cipher_outlength(sa->sa_encr, encrlen);
37115863c3aStobhe 	pldlen = ivlen + outlen + integrlen;
37215863c3aStobhe 
37315863c3aStobhe 	if (ikev2_next_payload(pld,
37415863c3aStobhe 	    pldlen + (fragmentation ? sizeof(struct ikev2_frag_payload) : 0),
37515863c3aStobhe 	    firstpayload) == -1)
37615863c3aStobhe 		return (-1);
37715863c3aStobhe 	if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1)
37815863c3aStobhe 		return (-1);
37915863c3aStobhe 
38015863c3aStobhe 	return (0);
38115863c3aStobhe }
38215863c3aStobhe 
383fde46d6eSreyk struct ibuf *
38415863c3aStobhe ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src,
38515863c3aStobhe     struct ibuf *aad)
386fde46d6eSreyk {
3879ebe96b0Stobhe 	size_t			 len, encrlen, integrlen, blocklen,
388fde46d6eSreyk 				    outlen;
389d09d3a7dSreyk 	uint8_t			*buf, pad = 0, *ptr;
39088d75aadSreyk 	struct ibuf		*encr, *dst = NULL, *out = NULL;
391fde46d6eSreyk 
392fde46d6eSreyk 	buf = ibuf_data(src);
393fde46d6eSreyk 	len = ibuf_size(src);
394fde46d6eSreyk 
395328746baSreyk 	log_debug("%s: decrypted length %zu", __func__, len);
396fde46d6eSreyk 	print_hex(buf, 0, len);
397fde46d6eSreyk 
398fde46d6eSreyk 	if (sa == NULL ||
399fde46d6eSreyk 	    sa->sa_encr == NULL ||
400fde46d6eSreyk 	    sa->sa_integr == NULL) {
401fde46d6eSreyk 		log_debug("%s: invalid SA", __func__);
402fde46d6eSreyk 		goto done;
403fde46d6eSreyk 	}
404fde46d6eSreyk 
40588d75aadSreyk 	if (sa->sa_hdr.sh_initiator)
406fde46d6eSreyk 		encr = sa->sa_key_iencr;
40788d75aadSreyk 	else
408fde46d6eSreyk 		encr = sa->sa_key_rencr;
409fde46d6eSreyk 
410fde46d6eSreyk 	blocklen = cipher_length(sa->sa_encr);
411fde46d6eSreyk 	integrlen = hash_length(sa->sa_integr);
412fde46d6eSreyk 	encrlen = roundup(len + sizeof(pad), blocklen);
413fde46d6eSreyk 	pad = encrlen - (len + sizeof(pad));
414fde46d6eSreyk 
415fde46d6eSreyk 	/*
416fde46d6eSreyk 	 * Pad the payload and encrypt it
417fde46d6eSreyk 	 */
418fde46d6eSreyk 	if (pad) {
419fde46d6eSreyk 		if ((ptr = ibuf_advance(src, pad)) == NULL)
420fde46d6eSreyk 			goto done;
421fde46d6eSreyk 		arc4random_buf(ptr, pad);
422fde46d6eSreyk 	}
423fde46d6eSreyk 	if (ibuf_add(src, &pad, sizeof(pad)) != 0)
424fde46d6eSreyk 		goto done;
425fde46d6eSreyk 
426328746baSreyk 	log_debug("%s: padded length %zu", __func__, ibuf_size(src));
427fde46d6eSreyk 	print_hex(ibuf_data(src), 0, ibuf_size(src));
428fde46d6eSreyk 
429fde46d6eSreyk 	cipher_setkey(sa->sa_encr, encr->buf, ibuf_length(encr));
43088d75aadSreyk 	cipher_setiv(sa->sa_encr, NULL, 0);	/* XXX ivlen */
43181b8fecaStobhe 	if (cipher_init_encrypt(sa->sa_encr) == -1) {
43281b8fecaStobhe 		log_info("%s: error initiating cipher.", __func__);
43381b8fecaStobhe 		goto done;
43481b8fecaStobhe 	}
435fde46d6eSreyk 
436fde46d6eSreyk 	if ((dst = ibuf_dup(sa->sa_encr->encr_iv)) == NULL)
437fde46d6eSreyk 		goto done;
438fde46d6eSreyk 
439fde46d6eSreyk 	if ((out = ibuf_new(NULL,
440fde46d6eSreyk 	    cipher_outlength(sa->sa_encr, encrlen))) == NULL)
441fde46d6eSreyk 		goto done;
442fde46d6eSreyk 
443fde46d6eSreyk 	outlen = ibuf_size(out);
44415863c3aStobhe 
44515863c3aStobhe 	/* Add AAD for AEAD ciphers */
44615863c3aStobhe 	if (sa->sa_integr->hash_isaead)
44715863c3aStobhe 		cipher_aad(sa->sa_encr, ibuf_data(aad),
44815863c3aStobhe 		    ibuf_length(aad), &outlen);
44915863c3aStobhe 
45081b8fecaStobhe 	if (cipher_update(sa->sa_encr, ibuf_data(src), encrlen,
45181b8fecaStobhe 	    ibuf_data(out), &outlen) == -1) {
45281b8fecaStobhe 		log_info("%s: error updating cipher.", __func__);
45381b8fecaStobhe 		goto done;
45481b8fecaStobhe 	}
455fde46d6eSreyk 
45615863c3aStobhe 	if (cipher_final(sa->sa_encr) == -1) {
45715863c3aStobhe 		log_info("%s: encryption failed.", __func__);
45815863c3aStobhe 		goto done;
45915863c3aStobhe 	}
46015863c3aStobhe 
461fde46d6eSreyk 	if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0)
462fde46d6eSreyk 		goto done;
463fde46d6eSreyk 
464fde46d6eSreyk 	if ((ptr = ibuf_advance(dst, integrlen)) == NULL)
465fde46d6eSreyk 		goto done;
4668fbd7fcbSdoug 	explicit_bzero(ptr, integrlen);
467fde46d6eSreyk 
468328746baSreyk 	log_debug("%s: length %zu, padding %d, output length %zu",
469fde46d6eSreyk 	    __func__, len + sizeof(pad), pad, ibuf_size(dst));
470fde46d6eSreyk 	print_hex(ibuf_data(dst), 0, ibuf_size(dst));
471fde46d6eSreyk 
472fde46d6eSreyk 	ibuf_release(src);
473fde46d6eSreyk 	ibuf_release(out);
474fde46d6eSreyk 	return (dst);
475fde46d6eSreyk  done:
476fde46d6eSreyk 	ibuf_release(src);
477fde46d6eSreyk 	ibuf_release(out);
478fde46d6eSreyk 	ibuf_release(dst);
479fde46d6eSreyk 	return (NULL);
480fde46d6eSreyk }
481fde46d6eSreyk 
482fde46d6eSreyk int
483fde46d6eSreyk ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src)
484fde46d6eSreyk {
485fde46d6eSreyk 	int			 ret = -1;
486fde46d6eSreyk 	size_t			 integrlen, tmplen;
48788d75aadSreyk 	struct ibuf		*integr, *tmp = NULL;
488d09d3a7dSreyk 	uint8_t			*ptr;
489fde46d6eSreyk 
490328746baSreyk 	log_debug("%s: message length %zu", __func__, ibuf_size(src));
491fde46d6eSreyk 	print_hex(ibuf_data(src), 0, ibuf_size(src));
492fde46d6eSreyk 
493fde46d6eSreyk 	if (sa == NULL ||
49415863c3aStobhe 	    sa->sa_encr == NULL ||
495fde46d6eSreyk 	    sa->sa_integr == NULL) {
496fde46d6eSreyk 		log_debug("%s: invalid SA", __func__);
497fde46d6eSreyk 		return (-1);
498fde46d6eSreyk 	}
499fde46d6eSreyk 
500fde46d6eSreyk 	integrlen = hash_length(sa->sa_integr);
501328746baSreyk 	log_debug("%s: integrity checksum length %zu", __func__,
502fde46d6eSreyk 	    integrlen);
503fde46d6eSreyk 
504fde46d6eSreyk 	/*
505fde46d6eSreyk 	 * Validate packet checksum
506fde46d6eSreyk 	 */
507fde46d6eSreyk 	if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
508fde46d6eSreyk 		goto done;
509fde46d6eSreyk 
51015863c3aStobhe 	if (!sa->sa_integr->hash_isaead) {
51115863c3aStobhe 		if (sa->sa_hdr.sh_initiator)
51215863c3aStobhe 			integr = sa->sa_key_iauth;
51315863c3aStobhe 		else
51415863c3aStobhe 			integr = sa->sa_key_rauth;
51515863c3aStobhe 
51615863c3aStobhe 		hash_setkey(sa->sa_integr, ibuf_data(integr),
51715863c3aStobhe 		    ibuf_size(integr));
518fde46d6eSreyk 		hash_init(sa->sa_integr);
519fde46d6eSreyk 		hash_update(sa->sa_integr, ibuf_data(src),
520fde46d6eSreyk 		    ibuf_size(src) - integrlen);
521fde46d6eSreyk 		hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
522fde46d6eSreyk 
523fde46d6eSreyk 		if (tmplen != integrlen) {
524fde46d6eSreyk 			log_debug("%s: hash failure", __func__);
525fde46d6eSreyk 			goto done;
526fde46d6eSreyk 		}
52715863c3aStobhe 	} else {
52815863c3aStobhe 		/* Append AEAD tag */
52915863c3aStobhe 		if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp)))
53015863c3aStobhe 			goto done;
53115863c3aStobhe 	}
532fde46d6eSreyk 
533fde46d6eSreyk 	if ((ptr = ibuf_seek(src,
534fde46d6eSreyk 	    ibuf_size(src) - integrlen, integrlen)) == NULL)
535fde46d6eSreyk 		goto done;
53615863c3aStobhe 	memcpy(ptr, ibuf_data(tmp), integrlen);
537fde46d6eSreyk 
538fde46d6eSreyk 	print_hex(ibuf_data(tmp), 0, ibuf_size(tmp));
539fde46d6eSreyk 
540fde46d6eSreyk 	ret = 0;
541fde46d6eSreyk  done:
542fde46d6eSreyk 	ibuf_release(tmp);
543fde46d6eSreyk 
544fde46d6eSreyk 	return (ret);
545fde46d6eSreyk }
546fde46d6eSreyk 
547fde46d6eSreyk struct ibuf *
548fde46d6eSreyk ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa,
549fde46d6eSreyk     struct ibuf *msg, struct ibuf *src)
550fde46d6eSreyk {
551e0696045Sreyk 	ssize_t			 ivlen, encrlen, integrlen, blocklen,
552fde46d6eSreyk 				    outlen, tmplen;
55315863c3aStobhe 	uint8_t			 pad = 0, *ptr, *integrdata;
554fde46d6eSreyk 	struct ibuf		*integr, *encr, *tmp = NULL, *out = NULL;
555fde46d6eSreyk 	off_t			 ivoff, encroff, integroff;
556fde46d6eSreyk 
557fde46d6eSreyk 	if (sa == NULL ||
558fde46d6eSreyk 	    sa->sa_encr == NULL ||
559fde46d6eSreyk 	    sa->sa_integr == NULL) {
560fde46d6eSreyk 		log_debug("%s: invalid SA", __func__);
561fde46d6eSreyk 		print_hex(ibuf_data(src), 0, ibuf_size(src));
562fde46d6eSreyk 		goto done;
563fde46d6eSreyk 	}
564fde46d6eSreyk 
565fde46d6eSreyk 	if (!sa->sa_hdr.sh_initiator) {
566fde46d6eSreyk 		encr = sa->sa_key_iencr;
567fde46d6eSreyk 		integr = sa->sa_key_iauth;
568fde46d6eSreyk 	} else {
569fde46d6eSreyk 		encr = sa->sa_key_rencr;
570fde46d6eSreyk 		integr = sa->sa_key_rauth;
571fde46d6eSreyk 	}
572fde46d6eSreyk 
573fde46d6eSreyk 	blocklen = cipher_length(sa->sa_encr);
574fde46d6eSreyk 	ivlen = cipher_ivlength(sa->sa_encr);
575fde46d6eSreyk 	ivoff = 0;
576fde46d6eSreyk 	integrlen = hash_length(sa->sa_integr);
577fde46d6eSreyk 	integroff = ibuf_size(src) - integrlen;
578fde46d6eSreyk 	encroff = ivlen;
579fde46d6eSreyk 	encrlen = ibuf_size(src) - integrlen - ivlen;
580fde46d6eSreyk 
581e0696045Sreyk 	if (encrlen < 0 || integroff < 0) {
582e0696045Sreyk 		log_debug("%s: invalid integrity value", __func__);
583e0696045Sreyk 		goto done;
584e0696045Sreyk 	}
585e0696045Sreyk 
586328746baSreyk 	log_debug("%s: IV length %zd", __func__, ivlen);
587fde46d6eSreyk 	print_hex(ibuf_data(src), 0, ivlen);
588328746baSreyk 	log_debug("%s: encrypted payload length %zd", __func__, encrlen);
589fde46d6eSreyk 	print_hex(ibuf_data(src), encroff, encrlen);
590328746baSreyk 	log_debug("%s: integrity checksum length %zd", __func__, integrlen);
591fde46d6eSreyk 	print_hex(ibuf_data(src), integroff, integrlen);
592fde46d6eSreyk 
593fde46d6eSreyk 	/*
594fde46d6eSreyk 	 * Validate packet checksum
595fde46d6eSreyk 	 */
59615863c3aStobhe 	if (!sa->sa_integr->hash_isaead) {
597d0e3d0abStobhe 		if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
598fde46d6eSreyk 			goto done;
599fde46d6eSreyk 
600fde46d6eSreyk 		hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr));
601fde46d6eSreyk 		hash_init(sa->sa_integr);
602fde46d6eSreyk 		hash_update(sa->sa_integr, ibuf_data(msg),
603fde46d6eSreyk 		    ibuf_size(msg) - integrlen);
604fde46d6eSreyk 		hash_final(sa->sa_integr, tmp->buf, &tmplen);
605fde46d6eSreyk 
60615863c3aStobhe 		integrdata = ibuf_seek(src, integroff, integrlen);
60715863c3aStobhe 		if (integrdata == NULL)
60815863c3aStobhe 			goto done;
60915863c3aStobhe 		if (memcmp(tmp->buf, integrdata, integrlen) != 0) {
610fde46d6eSreyk 			log_debug("%s: integrity check failed", __func__);
611fde46d6eSreyk 			goto done;
612fde46d6eSreyk 		}
613fde46d6eSreyk 
614b0eeedd0Smikeb 		log_debug("%s: integrity check succeeded", __func__);
615fde46d6eSreyk 		print_hex(tmp->buf, 0, tmplen);
616fde46d6eSreyk 
617fde46d6eSreyk 		ibuf_release(tmp);
618fde46d6eSreyk 		tmp = NULL;
61915863c3aStobhe 	}
620fde46d6eSreyk 
621fde46d6eSreyk 	/*
622fde46d6eSreyk 	 * Decrypt the payload and strip any padding
623fde46d6eSreyk 	 */
624fde46d6eSreyk 	if ((encrlen % blocklen) != 0) {
625fde46d6eSreyk 		log_debug("%s: unaligned encrypted payload", __func__);
626fde46d6eSreyk 		goto done;
627fde46d6eSreyk 	}
628fde46d6eSreyk 
629fde46d6eSreyk 	cipher_setkey(sa->sa_encr, encr->buf, ibuf_length(encr));
630fde46d6eSreyk 	cipher_setiv(sa->sa_encr, ibuf_data(src) + ivoff, ivlen);
63181b8fecaStobhe 	if (cipher_init_decrypt(sa->sa_encr) == -1) {
63281b8fecaStobhe 		log_info("%s: error initiating cipher.", __func__);
63381b8fecaStobhe 		goto done;
63481b8fecaStobhe 	}
635fde46d6eSreyk 
63615863c3aStobhe 	/* Set AEAD tag */
63715863c3aStobhe 	if (sa->sa_integr->hash_isaead) {
63815863c3aStobhe 		integrdata = ibuf_seek(src, integroff, integrlen);
63915863c3aStobhe 		if (integrdata == NULL)
64015863c3aStobhe 			goto done;
64115863c3aStobhe 		if (cipher_settag(sa->sa_encr, integrdata, integrlen)) {
64215863c3aStobhe 			log_info("%s: failed to set tag.", __func__);
64315863c3aStobhe 			goto done;
64415863c3aStobhe 		}
64515863c3aStobhe 	}
64615863c3aStobhe 
647fde46d6eSreyk 	if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
648fde46d6eSreyk 	    encrlen))) == NULL)
649fde46d6eSreyk 		goto done;
650fde46d6eSreyk 
65115863c3aStobhe 	/*
65215863c3aStobhe 	 * Add additional authenticated data for AEAD ciphers
65315863c3aStobhe 	 */
65415863c3aStobhe 	if (sa->sa_integr->hash_isaead) {
65515863c3aStobhe 		log_debug("%s: AAD length %zu", __func__, ibuf_length(msg) - ibuf_length(src));
65615863c3aStobhe 		print_hex(ibuf_data(msg), 0, ibuf_length(msg) - ibuf_length(src));
65715863c3aStobhe 		cipher_aad(sa->sa_encr, ibuf_data(msg),
65815863c3aStobhe 		    ibuf_length(msg) - ibuf_length(src), &outlen);
65915863c3aStobhe 	}
66015863c3aStobhe 
661e0696045Sreyk 	if ((outlen = ibuf_length(out)) != 0) {
66281b8fecaStobhe 		if (cipher_update(sa->sa_encr, ibuf_data(src) + encroff,
66381b8fecaStobhe 		    encrlen, ibuf_data(out), &outlen) == -1) {
66481b8fecaStobhe 			log_info("%s: error updating cipher.", __func__);
66581b8fecaStobhe 			goto done;
66681b8fecaStobhe 		}
6673189733aSmikeb 
668fde46d6eSreyk 		ptr = ibuf_seek(out, outlen - 1, 1);
669fde46d6eSreyk 		pad = *ptr;
670e0696045Sreyk 	}
671fde46d6eSreyk 
67215863c3aStobhe 	if (cipher_final(sa->sa_encr) == -1) {
67315863c3aStobhe 		log_info("%s: decryption failed.", __func__);
67415863c3aStobhe 		goto done;
67515863c3aStobhe 	}
67615863c3aStobhe 
677328746baSreyk 	log_debug("%s: decrypted payload length %zd/%zd padding %d",
678fde46d6eSreyk 	    __func__, outlen, encrlen, pad);
679fde46d6eSreyk 	print_hex(ibuf_data(out), 0, ibuf_size(out));
680fde46d6eSreyk 
68165c540d0Spatrick 	/* Strip padding and padding length */
68265c540d0Spatrick 	if (ibuf_setsize(out, outlen - pad - 1) != 0)
683fde46d6eSreyk 		goto done;
684fde46d6eSreyk 
685fde46d6eSreyk 	ibuf_release(src);
686fde46d6eSreyk 	return (out);
687fde46d6eSreyk  done:
688fde46d6eSreyk 	ibuf_release(tmp);
689fde46d6eSreyk 	ibuf_release(out);
690fde46d6eSreyk 	ibuf_release(src);
691fde46d6eSreyk 	return (NULL);
692fde46d6eSreyk }
693fde46d6eSreyk 
694fde46d6eSreyk int
69565c540d0Spatrick ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf) {
69665c540d0Spatrick 	size_t		len = ibuf_length(buf);
69765c540d0Spatrick 	sa_family_t	sa_fam;
69865c540d0Spatrick 	size_t		max;
69965c540d0Spatrick 	size_t		ivlen, integrlen, blocklen;
70065c540d0Spatrick 
70115863c3aStobhe 	if (sa == NULL ||
70215863c3aStobhe 	    sa->sa_encr == NULL ||
70315863c3aStobhe 	    sa->sa_integr == NULL) {
70415863c3aStobhe 		log_debug("%s: invalid SA", __func__);
70515863c3aStobhe 		return (-1);
70615863c3aStobhe 	}
70715863c3aStobhe 
70865c540d0Spatrick 	sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
70965c540d0Spatrick 
71065c540d0Spatrick 	max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
71165c540d0Spatrick 	    : IKEV2_MAXLEN_IPV6_FRAG;
71265c540d0Spatrick 
71365c540d0Spatrick 	blocklen = cipher_length(sa->sa_encr);
71465c540d0Spatrick 	ivlen = cipher_ivlength(sa->sa_encr);
71565c540d0Spatrick 	integrlen = hash_length(sa->sa_integr);
71665c540d0Spatrick 
71765c540d0Spatrick 	/* Estimated maximum packet size (with 0 < padding < blocklen) */
71865c540d0Spatrick 	return ((len + ivlen + blocklen + integrlen) >= max) && sa->sa_frag;
71965c540d0Spatrick }
72065c540d0Spatrick 
72165c540d0Spatrick int
722c45fd413Smikeb ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf **ep,
723d09d3a7dSreyk     uint8_t exchange, uint8_t firstpayload, int response)
724fde46d6eSreyk {
725fde46d6eSreyk 	struct iked_message		 resp;
726fde46d6eSreyk 	struct ike_header		*hdr;
727fde46d6eSreyk 	struct ikev2_payload		*pld;
728fde46d6eSreyk 	struct ibuf			*buf, *e = *ep;
729fde46d6eSreyk 	int				 ret = -1;
730fde46d6eSreyk 
73165c540d0Spatrick 	/* Check if msg needs to be fragmented */
73265c540d0Spatrick 	if (ikev2_check_frag_oversize(sa, e)) {
73365c540d0Spatrick 		return ikev2_send_encrypted_fragments(env, sa, e, exchange,
73465c540d0Spatrick 		    firstpayload, response);
73565c540d0Spatrick 	}
73665c540d0Spatrick 
737c45fd413Smikeb 	if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr,
738c45fd413Smikeb 	    sa->sa_peer.addr.ss_len, &sa->sa_local.addr,
739c45fd413Smikeb 	    sa->sa_local.addr.ss_len, response)) == NULL)
740fde46d6eSreyk 		goto done;
741fde46d6eSreyk 
74230904802Spatrick 	resp.msg_msgid = response ? sa->sa_msgid_current : ikev2_msg_id(env, sa);
743c45fd413Smikeb 
744fde46d6eSreyk 	/* IKE header */
745c45fd413Smikeb 	if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, IKEV2_PAYLOAD_SK,
746c45fd413Smikeb 	    exchange, response ? IKEV2_FLAG_RESPONSE : 0)) == NULL)
747fde46d6eSreyk 		goto done;
748fde46d6eSreyk 
749fde46d6eSreyk 	if ((pld = ikev2_add_payload(buf)) == NULL)
750fde46d6eSreyk 		goto done;
751fde46d6eSreyk 
75215863c3aStobhe 	if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1)
75315863c3aStobhe 		goto done;
75415863c3aStobhe 
755fde46d6eSreyk 	/* Encrypt message and add as an E payload */
75615863c3aStobhe 	if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
757fde46d6eSreyk 		log_debug("%s: encryption failed", __func__);
758fde46d6eSreyk 		goto done;
759fde46d6eSreyk 	}
760fde46d6eSreyk 	if (ibuf_cat(buf, e) != 0)
761fde46d6eSreyk 		goto done;
762fde46d6eSreyk 
763fde46d6eSreyk 	/* Add integrity checksum (HMAC) */
764fde46d6eSreyk 	if (ikev2_msg_integr(env, sa, buf) != 0) {
765fde46d6eSreyk 		log_debug("%s: integrity checksum failed", __func__);
766fde46d6eSreyk 		goto done;
767fde46d6eSreyk 	}
768fde46d6eSreyk 
769fde46d6eSreyk 	resp.msg_data = buf;
770fde46d6eSreyk 	resp.msg_sa = sa;
771d9c13a0aSmikeb 	resp.msg_fd = sa->sa_fd;
772fde46d6eSreyk 	TAILQ_INIT(&resp.msg_proposals);
773fde46d6eSreyk 
774fde46d6eSreyk 	(void)ikev2_pld_parse(env, hdr, &resp, 0);
775fde46d6eSreyk 
776d9c13a0aSmikeb 	ret = ikev2_msg_send(env, &resp);
777fde46d6eSreyk 
778fde46d6eSreyk  done:
779fde46d6eSreyk 	/* e is cleaned up by the calling function */
780fde46d6eSreyk 	*ep = e;
781763023d6Sreyk 	ikev2_msg_cleanup(env, &resp);
782fde46d6eSreyk 
783fde46d6eSreyk 	return (ret);
784fde46d6eSreyk }
785fde46d6eSreyk 
78665c540d0Spatrick int
78765c540d0Spatrick ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
78865c540d0Spatrick     struct ibuf *in, uint8_t exchange, uint8_t firstpayload, int response) {
78965c540d0Spatrick 	struct iked_message		 resp;
7904387480dStobhe 	struct ibuf			*buf, *e = NULL;
79165c540d0Spatrick 	struct ike_header		*hdr;
79265c540d0Spatrick 	struct ikev2_payload		*pld;
79365c540d0Spatrick 	struct ikev2_frag_payload	*frag;
79465c540d0Spatrick 	sa_family_t			 sa_fam;
79565c540d0Spatrick 	size_t				 ivlen, integrlen, blocklen;
796ab8e3451Sderaadt 	size_t 				 max_len, left,  offset=0;
79765c540d0Spatrick 	size_t				 frag_num = 1, frag_total;
79865c540d0Spatrick 	uint8_t				*data;
79965c540d0Spatrick 	uint32_t			 msgid;
80065c540d0Spatrick 	int 				 ret = -1;
80165c540d0Spatrick 
80215863c3aStobhe 	if (sa == NULL ||
80315863c3aStobhe 	    sa->sa_encr == NULL ||
80415863c3aStobhe 	    sa->sa_integr == NULL) {
80515863c3aStobhe 		log_debug("%s: invalid SA", __func__);
80615863c3aStobhe 		goto done;
80715863c3aStobhe 	}
80815863c3aStobhe 
80965c540d0Spatrick 	sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
81065c540d0Spatrick 
81165c540d0Spatrick 	left = ibuf_length(in);
81265c540d0Spatrick 
81365c540d0Spatrick 	/* Calculate max allowed size of a fragments payload */
81465c540d0Spatrick 	blocklen = cipher_length(sa->sa_encr);
81565c540d0Spatrick 	ivlen = cipher_ivlength(sa->sa_encr);
81665c540d0Spatrick 	integrlen = hash_length(sa->sa_integr);
81765c540d0Spatrick 	max_len = (sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
81865c540d0Spatrick 	    : IKEV2_MAXLEN_IPV6_FRAG)
81965c540d0Spatrick                   - ivlen - blocklen - integrlen;
82065c540d0Spatrick 
82165c540d0Spatrick 	/* Total number of fragments to send */
82265c540d0Spatrick 	frag_total = (left / max_len) + 1;
82365c540d0Spatrick 
82465c540d0Spatrick 	msgid = response ? sa->sa_msgid_current : ikev2_msg_id(env, sa);
82565c540d0Spatrick 
82665c540d0Spatrick 	while (frag_num <= frag_total) {
82765c540d0Spatrick 		if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr,
82865c540d0Spatrick 		    sa->sa_peer.addr.ss_len, &sa->sa_local.addr,
82965c540d0Spatrick 		    sa->sa_local.addr.ss_len, response)) == NULL)
83065c540d0Spatrick 			goto done;
83165c540d0Spatrick 
83265c540d0Spatrick 		resp.msg_msgid = msgid;
83365c540d0Spatrick 
83465c540d0Spatrick 		/* IKE header */
83565c540d0Spatrick 		if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid,
83665c540d0Spatrick 		    IKEV2_PAYLOAD_SKF, exchange, response ? IKEV2_FLAG_RESPONSE
83765c540d0Spatrick 		        : 0)) == NULL)
83865c540d0Spatrick 			goto done;
83965c540d0Spatrick 
84065c540d0Spatrick 		/* Payload header */
84165c540d0Spatrick 		if ((pld = ikev2_add_payload(buf)) == NULL)
84265c540d0Spatrick 			goto done;
84365c540d0Spatrick 
84465c540d0Spatrick 		/* Fragment header */
84565c540d0Spatrick 		if ((frag = ibuf_advance(buf, sizeof(*frag))) == NULL) {
84665c540d0Spatrick 			log_debug("%s: failed to add SKF fragment header",
84765c540d0Spatrick 			    __func__);
84865c540d0Spatrick 			goto done;
84965c540d0Spatrick 		}
85065c540d0Spatrick 		frag->frag_num = htobe16(frag_num);
85165c540d0Spatrick 		frag->frag_total = htobe16(frag_total);
85265c540d0Spatrick 
85365c540d0Spatrick 		/* Encrypt message and add as an E payload */
85465c540d0Spatrick 		data = ibuf_seek(in, offset, 0);
85565c540d0Spatrick 		if ((e = ibuf_new(data, MIN(left, max_len))) == NULL) {
85665c540d0Spatrick 			goto done;
85765c540d0Spatrick 		}
85815863c3aStobhe 
85915863c3aStobhe 		if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr,
86015863c3aStobhe 		    firstpayload, 1) == -1)
86115863c3aStobhe 			goto done;
86215863c3aStobhe 
86315863c3aStobhe 		if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
86465c540d0Spatrick 			log_debug("%s: encryption failed", __func__);
86565c540d0Spatrick 			goto done;
86665c540d0Spatrick 		}
86765c540d0Spatrick 		if (ibuf_cat(buf, e) != 0)
86865c540d0Spatrick 			goto done;
86965c540d0Spatrick 
87065c540d0Spatrick 		/* Add integrity checksum (HMAC) */
87165c540d0Spatrick 		if (ikev2_msg_integr(env, sa, buf) != 0) {
87265c540d0Spatrick 			log_debug("%s: integrity checksum failed", __func__);
87365c540d0Spatrick 			goto done;
87465c540d0Spatrick 		}
87565c540d0Spatrick 
87665c540d0Spatrick 		log_debug("%s: Fragment %zu of %zu has size of %zu bytes.",
87765c540d0Spatrick 		    __func__, frag_num, frag_total,
87865c540d0Spatrick 		    ibuf_size(buf) - sizeof(*hdr));
87965c540d0Spatrick 		print_hex(ibuf_data(buf), 0,  ibuf_size(buf));
88065c540d0Spatrick 
88165c540d0Spatrick 		resp.msg_data = buf;
88265c540d0Spatrick 		resp.msg_sa = sa;
88365c540d0Spatrick 		resp.msg_fd = sa->sa_fd;
88465c540d0Spatrick 		TAILQ_INIT(&resp.msg_proposals);
88565c540d0Spatrick 
88665c540d0Spatrick 		if (ikev2_msg_send(env, &resp) == -1)
88765c540d0Spatrick 			goto done;
88865c540d0Spatrick 
88965c540d0Spatrick 		offset += MIN(left, max_len);
89065c540d0Spatrick 		left -= MIN(left, max_len);
89165c540d0Spatrick 		frag_num++;
89265c540d0Spatrick 
89365c540d0Spatrick 		/* MUST be zero after first fragment */
89465c540d0Spatrick 		firstpayload = 0;
89565c540d0Spatrick 
89665c540d0Spatrick 		ikev2_msg_cleanup(env, &resp);
89765c540d0Spatrick 		ibuf_release(e);
89865c540d0Spatrick 		e = NULL;
89965c540d0Spatrick 	}
90065c540d0Spatrick 
90165c540d0Spatrick 	return 0;
90265c540d0Spatrick done:
90365c540d0Spatrick 	ikev2_msg_cleanup(env, &resp);
90465c540d0Spatrick 	ibuf_release(e);
90565c540d0Spatrick 	return ret;
90665c540d0Spatrick }
90765c540d0Spatrick 
908fde46d6eSreyk struct ibuf *
909fde46d6eSreyk ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response)
910fde46d6eSreyk {
911fde46d6eSreyk 	struct ibuf		*authmsg = NULL, *nonce, *prfkey, *buf;
912d09d3a7dSreyk 	uint8_t			*ptr;
913fde46d6eSreyk 	struct iked_id		*id;
914fde46d6eSreyk 	size_t			 tmplen;
915fde46d6eSreyk 
916fde46d6eSreyk 	/*
917fde46d6eSreyk 	 * Create the payload to be signed/MAC'ed for AUTH
918fde46d6eSreyk 	 */
919fde46d6eSreyk 
920fde46d6eSreyk 	if (!response) {
921fde46d6eSreyk 		if ((nonce = sa->sa_rnonce) == NULL ||
922fde46d6eSreyk 		    (sa->sa_iid.id_type == 0) ||
923fde46d6eSreyk 		    (prfkey = sa->sa_key_iprf) == NULL ||
924fde46d6eSreyk 		    (buf = sa->sa_1stmsg) == NULL)
925fde46d6eSreyk 			return (NULL);
926fde46d6eSreyk 		id = &sa->sa_iid;
927fde46d6eSreyk 	} else {
928fde46d6eSreyk 		if ((nonce = sa->sa_inonce) == NULL ||
929fde46d6eSreyk 		    (sa->sa_rid.id_type == 0) ||
930fde46d6eSreyk 		    (prfkey = sa->sa_key_rprf) == NULL ||
931fde46d6eSreyk 		    (buf = sa->sa_2ndmsg) == NULL)
932fde46d6eSreyk 			return (NULL);
933fde46d6eSreyk 		id = &sa->sa_rid;
934fde46d6eSreyk 	}
935fde46d6eSreyk 
936fde46d6eSreyk 	if ((authmsg = ibuf_dup(buf)) == NULL)
937fde46d6eSreyk 		return (NULL);
938fde46d6eSreyk 	if (ibuf_cat(authmsg, nonce) != 0)
939fde46d6eSreyk 		goto fail;
940fde46d6eSreyk 
941fde46d6eSreyk 	if ((hash_setkey(sa->sa_prf, ibuf_data(prfkey),
942fde46d6eSreyk 	    ibuf_size(prfkey))) == NULL)
943fde46d6eSreyk 		goto fail;
944fde46d6eSreyk 
945d0e3d0abStobhe 	/* require non-truncating hash */
946d0e3d0abStobhe 	if (hash_keylength(sa->sa_prf) != hash_length(sa->sa_prf))
947d0e3d0abStobhe 		goto fail;
948d0e3d0abStobhe 
949d0e3d0abStobhe 	if ((ptr = ibuf_advance(authmsg, hash_keylength(sa->sa_prf))) == NULL)
950fde46d6eSreyk 		goto fail;
951fde46d6eSreyk 
952fde46d6eSreyk 	hash_init(sa->sa_prf);
953fde46d6eSreyk 	hash_update(sa->sa_prf, ibuf_data(id->id_buf), ibuf_size(id->id_buf));
954fde46d6eSreyk 	hash_final(sa->sa_prf, ptr, &tmplen);
955fde46d6eSreyk 
956fde46d6eSreyk 	if (tmplen != hash_length(sa->sa_prf))
957fde46d6eSreyk 		goto fail;
958fde46d6eSreyk 
959328746baSreyk 	log_debug("%s: %s auth data length %zu",
960fde46d6eSreyk 	    __func__, response ? "responder" : "initiator",
961fde46d6eSreyk 	    ibuf_size(authmsg));
962fde46d6eSreyk 	print_hex(ibuf_data(authmsg), 0, ibuf_size(authmsg));
963fde46d6eSreyk 
964fde46d6eSreyk 	return (authmsg);
965fde46d6eSreyk 
966fde46d6eSreyk  fail:
967fde46d6eSreyk 	ibuf_release(authmsg);
968fde46d6eSreyk 	return (NULL);
969fde46d6eSreyk }
970fde46d6eSreyk 
971fde46d6eSreyk int
972fde46d6eSreyk ikev2_msg_authverify(struct iked *env, struct iked_sa *sa,
973d09d3a7dSreyk     struct iked_auth *auth, uint8_t *buf, size_t len, struct ibuf *authmsg)
974fde46d6eSreyk {
975d09d3a7dSreyk 	uint8_t				*key, *psk = NULL;
976fde46d6eSreyk 	ssize_t				 keylen;
977fde46d6eSreyk 	struct iked_id			*id;
978fde46d6eSreyk 	struct iked_dsa			*dsa = NULL;
979fde46d6eSreyk 	int				 ret = -1;
980d09d3a7dSreyk 	uint8_t				 keytype;
981fde46d6eSreyk 
982fde46d6eSreyk 	if (sa->sa_hdr.sh_initiator)
983fde46d6eSreyk 		id = &sa->sa_rcert;
984fde46d6eSreyk 	else
985fde46d6eSreyk 		id = &sa->sa_icert;
986fde46d6eSreyk 
987fde46d6eSreyk 	if ((dsa = dsa_verify_new(auth->auth_method, sa->sa_prf)) == NULL) {
988fde46d6eSreyk 		log_debug("%s: invalid auth method", __func__);
989fde46d6eSreyk 		return (-1);
990fde46d6eSreyk 	}
991fde46d6eSreyk 
992fde46d6eSreyk 	switch (auth->auth_method) {
993fde46d6eSreyk 	case IKEV2_AUTH_SHARED_KEY_MIC:
994fde46d6eSreyk 		if (!auth->auth_length) {
995fde46d6eSreyk 			log_debug("%s: no pre-shared key found", __func__);
996fde46d6eSreyk 			goto done;
997fde46d6eSreyk 		}
998fde46d6eSreyk 		if ((keylen = ikev2_psk(sa, auth->auth_data,
999fde46d6eSreyk 		    auth->auth_length, &psk)) == -1) {
1000fde46d6eSreyk 			log_debug("%s: failed to get PSK", __func__);
1001fde46d6eSreyk 			goto done;
1002fde46d6eSreyk 		}
1003fde46d6eSreyk 		key = psk;
1004fde46d6eSreyk 		keytype = 0;
1005fde46d6eSreyk 		break;
1006fde46d6eSreyk 	default:
1007202133c5Sreyk 		if (!id->id_type || !ibuf_length(id->id_buf)) {
1008fde46d6eSreyk 			log_debug("%s: no cert found", __func__);
1009fde46d6eSreyk 			goto done;
1010fde46d6eSreyk 		}
1011fde46d6eSreyk 		key = ibuf_data(id->id_buf);
1012fde46d6eSreyk 		keylen = ibuf_size(id->id_buf);
1013fde46d6eSreyk 		keytype = id->id_type;
1014fde46d6eSreyk 		break;
1015fde46d6eSreyk 	}
1016fde46d6eSreyk 
1017328746baSreyk 	log_debug("%s: method %s keylen %zd type %s", __func__,
1018fde46d6eSreyk 	    print_map(auth->auth_method, ikev2_auth_map), keylen,
1019fde46d6eSreyk 	    print_map(id->id_type, ikev2_cert_map));
1020fde46d6eSreyk 
1021fde46d6eSreyk 	if (dsa_setkey(dsa, key, keylen, keytype) == NULL ||
102248b975e3Smarkus 	    dsa_init(dsa, buf, len) != 0 ||
1023fde46d6eSreyk 	    dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) {
1024fde46d6eSreyk 		log_debug("%s: failed to compute digital signature", __func__);
1025fde46d6eSreyk 		goto done;
1026fde46d6eSreyk 	}
1027fde46d6eSreyk 
1028fde46d6eSreyk 	if ((ret = dsa_verify_final(dsa, buf, len)) == 0) {
1029fde46d6eSreyk 		log_debug("%s: authentication successful", __func__);
1030fde46d6eSreyk 		sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS);
10314a986ab9Smarkus 		sa_stateflags(sa, IKED_REQ_AUTHVALID);
1032fde46d6eSreyk 	} else {
1033fde46d6eSreyk 		log_debug("%s: authentication failed", __func__);
1034fde46d6eSreyk 		sa_state(env, sa, IKEV2_STATE_AUTH_REQUEST);
1035fde46d6eSreyk 	}
1036fde46d6eSreyk 
1037fde46d6eSreyk  done:
1038fde46d6eSreyk 	free(psk);
1039fde46d6eSreyk 	dsa_free(dsa);
1040fde46d6eSreyk 
1041fde46d6eSreyk 	return (ret);
1042fde46d6eSreyk }
1043fde46d6eSreyk 
1044fde46d6eSreyk int
1045fde46d6eSreyk ikev2_msg_authsign(struct iked *env, struct iked_sa *sa,
1046fde46d6eSreyk     struct iked_auth *auth, struct ibuf *authmsg)
1047fde46d6eSreyk {
1048d09d3a7dSreyk 	uint8_t				*key, *psk = NULL;
10495e4d3a37Sreyk 	ssize_t				 keylen, siglen;
1050fde46d6eSreyk 	struct iked_hash		*prf = sa->sa_prf;
1051fde46d6eSreyk 	struct iked_id			*id;
1052fde46d6eSreyk 	struct iked_dsa			*dsa = NULL;
1053fde46d6eSreyk 	struct ibuf			*buf;
1054fde46d6eSreyk 	int				 ret = -1;
1055d09d3a7dSreyk 	uint8_t			 keytype;
1056fde46d6eSreyk 
1057fde46d6eSreyk 	if (sa->sa_hdr.sh_initiator)
1058fde46d6eSreyk 		id = &sa->sa_icert;
1059fde46d6eSreyk 	else
1060fde46d6eSreyk 		id = &sa->sa_rcert;
1061fde46d6eSreyk 
1062fde46d6eSreyk 	if ((dsa = dsa_sign_new(auth->auth_method, prf)) == NULL) {
1063fde46d6eSreyk 		log_debug("%s: invalid auth method", __func__);
1064fde46d6eSreyk 		return (-1);
1065fde46d6eSreyk 	}
1066fde46d6eSreyk 
1067fde46d6eSreyk 	switch (auth->auth_method) {
1068fde46d6eSreyk 	case IKEV2_AUTH_SHARED_KEY_MIC:
1069fde46d6eSreyk 		if (!auth->auth_length) {
1070fde46d6eSreyk 			log_debug("%s: no pre-shared key found", __func__);
1071fde46d6eSreyk 			goto done;
1072fde46d6eSreyk 		}
1073fde46d6eSreyk 		if ((keylen = ikev2_psk(sa, auth->auth_data,
1074fde46d6eSreyk 		    auth->auth_length, &psk)) == -1) {
1075fde46d6eSreyk 			log_debug("%s: failed to get PSK", __func__);
1076fde46d6eSreyk 			goto done;
1077fde46d6eSreyk 		}
1078fde46d6eSreyk 		key = psk;
1079fde46d6eSreyk 		keytype = 0;
1080fde46d6eSreyk 		break;
1081fde46d6eSreyk 	default:
1082fde46d6eSreyk 		if (id == NULL) {
1083fde46d6eSreyk 			log_debug("%s: no cert found", __func__);
1084fde46d6eSreyk 			goto done;
1085fde46d6eSreyk 		}
1086fde46d6eSreyk 		key = ibuf_data(id->id_buf);
1087fde46d6eSreyk 		keylen = ibuf_size(id->id_buf);
1088fde46d6eSreyk 		keytype = id->id_type;
1089fde46d6eSreyk 		break;
1090fde46d6eSreyk 	}
1091fde46d6eSreyk 
1092fde46d6eSreyk 	if (dsa_setkey(dsa, key, keylen, keytype) == NULL ||
109348b975e3Smarkus 	    dsa_init(dsa, NULL, 0) != 0 ||
1094fde46d6eSreyk 	    dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) {
1095fde46d6eSreyk 		log_debug("%s: failed to compute digital signature", __func__);
1096fde46d6eSreyk 		goto done;
1097fde46d6eSreyk 	}
1098fde46d6eSreyk 
1099fde46d6eSreyk 	ibuf_release(sa->sa_localauth.id_buf);
1100fde46d6eSreyk 	sa->sa_localauth.id_buf = NULL;
1101fde46d6eSreyk 
1102fde46d6eSreyk 	if ((buf = ibuf_new(NULL, dsa_length(dsa))) == NULL) {
1103fde46d6eSreyk 		log_debug("%s: failed to get auth buffer", __func__);
1104fde46d6eSreyk 		goto done;
1105fde46d6eSreyk 	}
1106fde46d6eSreyk 
11075e4d3a37Sreyk 	if ((siglen = dsa_sign_final(dsa,
11085e4d3a37Sreyk 	    ibuf_data(buf), ibuf_size(buf))) < 0) {
1109fde46d6eSreyk 		log_debug("%s: failed to create auth signature", __func__);
1110fde46d6eSreyk 		ibuf_release(buf);
1111fde46d6eSreyk 		goto done;
1112fde46d6eSreyk 	}
1113fde46d6eSreyk 
11145e4d3a37Sreyk 	if (ibuf_setsize(buf, siglen) < 0) {
11155e4d3a37Sreyk 		log_debug("%s: failed to set auth signature size to %zd",
11165e4d3a37Sreyk 		    __func__, siglen);
11175e4d3a37Sreyk 		ibuf_release(buf);
11185e4d3a37Sreyk 		goto done;
11195e4d3a37Sreyk 	}
11205e4d3a37Sreyk 
1121fde46d6eSreyk 	sa->sa_localauth.id_type = auth->auth_method;
1122fde46d6eSreyk 	sa->sa_localauth.id_buf = buf;
1123fde46d6eSreyk 
1124fde46d6eSreyk 	ret = 0;
1125fde46d6eSreyk  done:
1126fde46d6eSreyk 	free(psk);
1127fde46d6eSreyk 	dsa_free(dsa);
1128fde46d6eSreyk 
1129fde46d6eSreyk 	return (ret);
1130fde46d6eSreyk }
1131ae494144Sreyk 
1132ae494144Sreyk int
1133ae494144Sreyk ikev2_msg_frompeer(struct iked_message *msg)
1134ae494144Sreyk {
1135ae494144Sreyk 	struct iked_sa		*sa = msg->msg_sa;
1136ae494144Sreyk 	struct ike_header	*hdr;
1137ae494144Sreyk 
113826d7dba1Sreyk 	msg = msg->msg_parent;
11391b0d4946Sreyk 
1140ae494144Sreyk 	if (sa == NULL ||
1141ae494144Sreyk 	    (hdr = ibuf_seek(msg->msg_data, 0, sizeof(*hdr))) == NULL)
1142ae494144Sreyk 		return (0);
1143ae494144Sreyk 
1144ae494144Sreyk 	if (!sa->sa_hdr.sh_initiator &&
1145ae494144Sreyk 	    (hdr->ike_flags & IKEV2_FLAG_INITIATOR))
1146ae494144Sreyk 		return (1);
1147ae494144Sreyk 	else if (sa->sa_hdr.sh_initiator &&
1148ae494144Sreyk 	    (hdr->ike_flags & IKEV2_FLAG_INITIATOR) == 0)
1149ae494144Sreyk 		return (1);
1150ae494144Sreyk 
1151ae494144Sreyk 	return (0);
1152ae494144Sreyk }
1153ae494144Sreyk 
1154ae494144Sreyk struct iked_socket *
115512c9fd31Sreyk ikev2_msg_getsocket(struct iked *env, int af, int natt)
1156ae494144Sreyk {
1157ae494144Sreyk 	switch (af) {
1158ae494144Sreyk 	case AF_INET:
115912c9fd31Sreyk 		return (env->sc_sock4[natt ? 1 : 0]);
1160ae494144Sreyk 	case AF_INET6:
116112c9fd31Sreyk 		return (env->sc_sock6[natt ? 1 : 0]);
1162ae494144Sreyk 	}
1163ae494144Sreyk 
1164ae494144Sreyk 	log_debug("%s: af socket %d not available", __func__, af);
1165ae494144Sreyk 	return (NULL);
1166ae494144Sreyk }
1167c45fd413Smikeb 
1168c45fd413Smikeb void
1169c45fd413Smikeb ikev2_msg_prevail(struct iked *env, struct iked_msgqueue *queue,
1170c45fd413Smikeb     struct iked_message *msg)
1171c45fd413Smikeb {
117204ab0f1dSmikeb 	struct iked_message	*m, *mtmp;
1173c45fd413Smikeb 
117404ab0f1dSmikeb 	TAILQ_FOREACH_SAFE(m, queue, msg_entry, mtmp) {
1175c45fd413Smikeb 		if (m->msg_msgid < msg->msg_msgid)
1176c45fd413Smikeb 			ikev2_msg_dispose(env, queue, m);
1177c45fd413Smikeb 	}
1178c45fd413Smikeb }
1179c45fd413Smikeb 
1180c45fd413Smikeb void
1181c45fd413Smikeb ikev2_msg_dispose(struct iked *env, struct iked_msgqueue *queue,
1182c45fd413Smikeb     struct iked_message *msg)
1183c45fd413Smikeb {
1184c45fd413Smikeb 	TAILQ_REMOVE(queue, msg, msg_entry);
1185b3eeacebSmikeb 	timer_del(env, &msg->msg_timer);
1186c45fd413Smikeb 	ikev2_msg_cleanup(env, msg);
1187c45fd413Smikeb 	free(msg);
1188c45fd413Smikeb }
1189c45fd413Smikeb 
1190c45fd413Smikeb void
1191c45fd413Smikeb ikev2_msg_flushqueue(struct iked *env, struct iked_msgqueue *queue)
1192c45fd413Smikeb {
1193c45fd413Smikeb 	struct iked_message	*m = NULL;
1194c45fd413Smikeb 
1195c45fd413Smikeb 	while ((m = TAILQ_FIRST(queue)) != NULL)
1196c45fd413Smikeb 		ikev2_msg_dispose(env, queue, m);
1197c45fd413Smikeb }
1198c45fd413Smikeb 
1199c45fd413Smikeb struct iked_message *
120056d51042Smikeb ikev2_msg_lookup(struct iked *env, struct iked_msgqueue *queue,
120156d51042Smikeb     struct iked_message *msg, struct ike_header *hdr)
1202c45fd413Smikeb {
1203c45fd413Smikeb 	struct iked_message	*m = NULL;
1204c45fd413Smikeb 
1205c45fd413Smikeb 	TAILQ_FOREACH(m, queue, msg_entry) {
120656d51042Smikeb 		if (m->msg_msgid == msg->msg_msgid &&
120756d51042Smikeb 		    m->msg_exchange == hdr->ike_exchange)
1208c45fd413Smikeb 			break;
1209c45fd413Smikeb 	}
1210c45fd413Smikeb 
121156d51042Smikeb 	return (m);
1212c45fd413Smikeb }
1213c45fd413Smikeb 
121462cdfd0dStobhe void
121562cdfd0dStobhe ikev2_msg_lookup_dispose_all(struct iked *env, struct iked_msgqueue *queue,
121662cdfd0dStobhe     struct iked_message *msg, struct ike_header *hdr)
121762cdfd0dStobhe {
121862cdfd0dStobhe 	struct iked_message	*m = NULL, *tmp = NULL;
121962cdfd0dStobhe 
122062cdfd0dStobhe 	TAILQ_FOREACH_SAFE(m, queue, msg_entry, tmp) {
122162cdfd0dStobhe 		if (m->msg_msgid == msg->msg_msgid &&
122262cdfd0dStobhe 		    m->msg_exchange == hdr->ike_exchange) {
122362cdfd0dStobhe 			TAILQ_REMOVE(queue, m, msg_entry);
122462cdfd0dStobhe 			timer_del(env, &m->msg_timer);
122562cdfd0dStobhe 			ikev2_msg_cleanup(env, m);
122662cdfd0dStobhe 			free(m);
122762cdfd0dStobhe 		}
122862cdfd0dStobhe 	}
122962cdfd0dStobhe }
123062cdfd0dStobhe 
123162cdfd0dStobhe int
123262cdfd0dStobhe ikev2_msg_lookup_retransmit_all(struct iked *env, struct iked_msgqueue *queue,
123362cdfd0dStobhe     struct iked_message *msg, struct ike_header *hdr, struct iked_sa *sa)
123462cdfd0dStobhe {
123562cdfd0dStobhe 	struct iked_message	*m = NULL, *tmp = NULL;
123662cdfd0dStobhe 	int count = 0;
123762cdfd0dStobhe 
123862cdfd0dStobhe 	TAILQ_FOREACH_SAFE(m, queue, msg_entry, tmp) {
123962cdfd0dStobhe 		if (m->msg_msgid == msg->msg_msgid &&
124062cdfd0dStobhe 		    m->msg_exchange == hdr->ike_exchange) {
124162cdfd0dStobhe 			if (ikev2_msg_retransmit_response(env, sa, m))
124262cdfd0dStobhe 				return -1;
124362cdfd0dStobhe 			count++;
124462cdfd0dStobhe 		}
124562cdfd0dStobhe 	}
124662cdfd0dStobhe 	return count;
124762cdfd0dStobhe }
124862cdfd0dStobhe 
1249c45fd413Smikeb int
1250c45fd413Smikeb ikev2_msg_retransmit_response(struct iked *env, struct iked_sa *sa,
1251c45fd413Smikeb     struct iked_message *msg)
1252c45fd413Smikeb {
12535ec2ede8Svgross 	if (sendtofrom(msg->msg_fd, ibuf_data(msg->msg_data),
12545ec2ede8Svgross 	    ibuf_size(msg->msg_data), 0,
12555ec2ede8Svgross 	    (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen,
12565ec2ede8Svgross 	    (struct sockaddr *)&msg->msg_local, msg->msg_locallen) == -1) {
12575ec2ede8Svgross 		log_warn("%s: sendtofrom", __func__);
1258c45fd413Smikeb 		return (-1);
1259c45fd413Smikeb 	}
1260f3610affStobhe 	log_info("%sretransmit %s res %u local %s peer %s",
1261f3610affStobhe 	    SPI_SA(sa, NULL),
1262f3610affStobhe 	    print_map(msg->msg_exchange, ikev2_exchange_map),
1263f3610affStobhe 	    msg->msg_msgid,
1264f3610affStobhe 	    print_host((struct sockaddr *)&msg->msg_local, NULL, 0),
1265f3610affStobhe 	    print_host((struct sockaddr *)&msg->msg_peer, NULL, 0));
1266c45fd413Smikeb 
1267b3eeacebSmikeb 	timer_add(env, &msg->msg_timer, IKED_RESPONSE_TIMEOUT);
1268c45fd413Smikeb 	return (0);
1269c45fd413Smikeb }
1270c45fd413Smikeb 
1271c45fd413Smikeb void
1272c45fd413Smikeb ikev2_msg_response_timeout(struct iked *env, void *arg)
1273c45fd413Smikeb {
1274c45fd413Smikeb 	struct iked_message	*msg = arg;
1275c45fd413Smikeb 	struct iked_sa		*sa = msg->msg_sa;
1276c45fd413Smikeb 
1277c45fd413Smikeb 	ikev2_msg_dispose(env, &sa->sa_responses, msg);
1278c45fd413Smikeb }
1279c45fd413Smikeb 
1280c45fd413Smikeb void
1281c45fd413Smikeb ikev2_msg_retransmit_timeout(struct iked *env, void *arg)
1282c45fd413Smikeb {
1283c45fd413Smikeb 	struct iked_message	*msg = arg;
1284c45fd413Smikeb 	struct iked_sa		*sa = msg->msg_sa;
1285c45fd413Smikeb 
12868d81c769Smikeb 	if (msg->msg_tries < IKED_RETRANSMIT_TRIES) {
12875ec2ede8Svgross 		if (sendtofrom(msg->msg_fd, ibuf_data(msg->msg_data),
1288c45fd413Smikeb 		    ibuf_size(msg->msg_data), 0,
12895ec2ede8Svgross 		    (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen,
12905ec2ede8Svgross 		    (struct sockaddr *)&msg->msg_local,
12915ec2ede8Svgross 		    msg->msg_locallen) == -1) {
12925ec2ede8Svgross 			log_warn("%s: sendtofrom", __func__);
129384a9a21bStobhe 			ikev2_ike_sa_setreason(sa, "retransmit failed");
1294c45fd413Smikeb 			sa_free(env, sa);
1295c45fd413Smikeb 			return;
1296c45fd413Smikeb 		}
1297c45fd413Smikeb 		/* Exponential timeout */
1298b3eeacebSmikeb 		timer_add(env, &msg->msg_timer,
1299c45fd413Smikeb 		    IKED_RETRANSMIT_TIMEOUT * (2 << (msg->msg_tries++)));
1300f3610affStobhe 		log_info("%sretransmit %d %s req %u peer %s local %s",
1301f3610affStobhe 		    SPI_SA(sa, NULL),
1302f3610affStobhe 		    msg->msg_tries,
1303f3610affStobhe 		    print_map(msg->msg_exchange, ikev2_exchange_map),
1304f3610affStobhe 		    msg->msg_msgid,
1305f3610affStobhe 		    print_host((struct sockaddr *)&msg->msg_peer, NULL, 0),
1306f3610affStobhe 		    print_host((struct sockaddr *)&msg->msg_local, NULL, 0));
13078d81c769Smikeb 	} else {
1308f3610affStobhe 		log_debug("%s: retransmit limit reached for req %u",
13097f7372eaSmarkus 		    __func__, msg->msg_msgid);
131084a9a21bStobhe 		ikev2_ike_sa_setreason(sa, "retransmit limit reached");
1311c45fd413Smikeb 		sa_free(env, sa);
1312c45fd413Smikeb 	}
13138d81c769Smikeb }
1314