xref: /openbsd-src/sbin/iked/ikev2_msg.c (revision fa353a8f9b716432e67aba95e7a4beea67b6c3b5)
1*fa353a8fSclaudio /*	$OpenBSD: ikev2_msg.c,v 1.103 2024/11/21 13:26:49 claudio 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 
20d8ea035bSderaadt #include <sys/types.h>
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>
34520fa7a1Stobhe #include <endian.h>
35fde46d6eSreyk #include <errno.h>
36fde46d6eSreyk #include <err.h>
37fde46d6eSreyk #include <event.h>
38fde46d6eSreyk 
39fde46d6eSreyk #include <openssl/sha.h>
40fde46d6eSreyk #include <openssl/evp.h>
41fde46d6eSreyk 
42fde46d6eSreyk #include "iked.h"
43fde46d6eSreyk #include "ikev2.h"
44fde46d6eSreyk #include "eap.h"
45fde46d6eSreyk #include "dh.h"
46fde46d6eSreyk 
4705256619Sreyk void	 ikev1_recv(struct iked *, struct iked_message *);
48c45fd413Smikeb void	 ikev2_msg_response_timeout(struct iked *, void *);
49c45fd413Smikeb void	 ikev2_msg_retransmit_timeout(struct iked *, void *);
50d56261e5Stobhe int	 ikev2_check_frag_oversize(struct iked_sa *, struct ibuf *);
51d56261e5Stobhe int	 ikev2_send_encrypted_fragments(struct iked *, struct iked_sa *,
52d56261e5Stobhe 	    struct ibuf *, uint8_t, uint8_t, int);
5315863c3aStobhe int	 ikev2_msg_encrypt_prepare(struct iked_sa *, struct ikev2_payload *,
5415863c3aStobhe 	    struct ibuf*, struct ibuf *, struct ike_header *, uint8_t, int);
55c45fd413Smikeb 
56fde46d6eSreyk void
57fde46d6eSreyk ikev2_msg_cb(int fd, short event, void *arg)
58fde46d6eSreyk {
59fde46d6eSreyk 	struct iked_socket	*sock = arg;
60fde46d6eSreyk 	struct iked		*env = sock->sock_env;
61fde46d6eSreyk 	struct iked_message	 msg;
62fde46d6eSreyk 	struct ike_header	 hdr;
63d09d3a7dSreyk 	uint32_t		 natt = 0x00000000;
64d09d3a7dSreyk 	uint8_t			 buf[IKED_MSGBUF_MAX];
65fde46d6eSreyk 	ssize_t			 len;
66fde46d6eSreyk 	off_t			 off;
67fde46d6eSreyk 
68fde46d6eSreyk 	bzero(&msg, sizeof(msg));
69fde46d6eSreyk 	bzero(buf, sizeof(buf));
70fde46d6eSreyk 
71fde46d6eSreyk 	msg.msg_peerlen = sizeof(msg.msg_peer);
72fde46d6eSreyk 	msg.msg_locallen = sizeof(msg.msg_local);
7326d7dba1Sreyk 	msg.msg_parent = &msg;
74fde46d6eSreyk 	memcpy(&msg.msg_local, &sock->sock_addr, sizeof(sock->sock_addr));
75fde46d6eSreyk 
76fde46d6eSreyk 	if ((len = recvfromto(fd, buf, sizeof(buf), 0,
77fde46d6eSreyk 	    (struct sockaddr *)&msg.msg_peer, &msg.msg_peerlen,
78fde46d6eSreyk 	    (struct sockaddr *)&msg.msg_local, &msg.msg_locallen)) <
79fde46d6eSreyk 	    (ssize_t)sizeof(natt))
80fde46d6eSreyk 		return;
81fde46d6eSreyk 
8247d6a31cSmarkus 	if (socket_getport((struct sockaddr *)&msg.msg_local) ==
8359c69d76Stobhe 	    env->sc_nattport) {
8434d94968Stedu 		if (memcmp(&natt, buf, sizeof(natt)) != 0)
85fde46d6eSreyk 			return;
86fde46d6eSreyk 		msg.msg_natt = 1;
87fde46d6eSreyk 		off = sizeof(natt);
88fde46d6eSreyk 	} else
89fde46d6eSreyk 		off = 0;
90fde46d6eSreyk 
91fde46d6eSreyk 	if ((size_t)(len - off) <= sizeof(hdr))
92fde46d6eSreyk 		return;
93fde46d6eSreyk 	memcpy(&hdr, buf + off, sizeof(hdr));
94fde46d6eSreyk 
95fde46d6eSreyk 	if ((msg.msg_data = ibuf_new(buf + off, len - off)) == NULL)
96fde46d6eSreyk 		return;
97fde46d6eSreyk 
98fde46d6eSreyk 	TAILQ_INIT(&msg.msg_proposals);
993e395450Stobhe 	SIMPLEQ_INIT(&msg.msg_certreqs);
100fde46d6eSreyk 	msg.msg_fd = fd;
10105256619Sreyk 
10205256619Sreyk 	if (hdr.ike_version == IKEV1_VERSION)
10305256619Sreyk 		ikev1_recv(env, &msg);
10405256619Sreyk 	else
105fde46d6eSreyk 		ikev2_recv(env, &msg);
106fde46d6eSreyk 
107763023d6Sreyk 	ikev2_msg_cleanup(env, &msg);
108fde46d6eSreyk }
109fde46d6eSreyk 
11005256619Sreyk void
11105256619Sreyk ikev1_recv(struct iked *env, struct iked_message *msg)
11205256619Sreyk {
11305256619Sreyk 	struct ike_header	*hdr;
11405256619Sreyk 
11505256619Sreyk 	if (ibuf_size(msg->msg_data) <= sizeof(*hdr)) {
11605256619Sreyk 		log_debug("%s: short message", __func__);
11705256619Sreyk 		return;
11805256619Sreyk 	}
11905256619Sreyk 
12005256619Sreyk 	hdr = (struct ike_header *)ibuf_data(msg->msg_data);
12105256619Sreyk 
12205256619Sreyk 	log_debug("%s: header ispi %s rspi %s"
12305256619Sreyk 	    " nextpayload %u version 0x%02x exchange %u flags 0x%02x"
12405256619Sreyk 	    " msgid %u length %u", __func__,
12505256619Sreyk 	    print_spi(betoh64(hdr->ike_ispi), 8),
12605256619Sreyk 	    print_spi(betoh64(hdr->ike_rspi), 8),
12705256619Sreyk 	    hdr->ike_nextpayload,
12805256619Sreyk 	    hdr->ike_version,
12905256619Sreyk 	    hdr->ike_exchange,
13005256619Sreyk 	    hdr->ike_flags,
13105256619Sreyk 	    betoh32(hdr->ike_msgid),
13205256619Sreyk 	    betoh32(hdr->ike_length));
13305256619Sreyk 
13405256619Sreyk 	log_debug("%s: IKEv1 not supported", __func__);
13505256619Sreyk }
13605256619Sreyk 
137fde46d6eSreyk struct ibuf *
138fde46d6eSreyk ikev2_msg_init(struct iked *env, struct iked_message *msg,
139fde46d6eSreyk     struct sockaddr_storage *peer, socklen_t peerlen,
140fde46d6eSreyk     struct sockaddr_storage *local, socklen_t locallen, int response)
141fde46d6eSreyk {
142fde46d6eSreyk 	bzero(msg, sizeof(*msg));
143fde46d6eSreyk 	memcpy(&msg->msg_peer, peer, peerlen);
144fde46d6eSreyk 	msg->msg_peerlen = peerlen;
145fde46d6eSreyk 	memcpy(&msg->msg_local, local, locallen);
146fde46d6eSreyk 	msg->msg_locallen = locallen;
147fde46d6eSreyk 	msg->msg_response = response ? 1 : 0;
148fde46d6eSreyk 	msg->msg_fd = -1;
149fde46d6eSreyk 	msg->msg_data = ibuf_static();
15026d7dba1Sreyk 	msg->msg_e = 0;
15126d7dba1Sreyk 	msg->msg_parent = msg;	/* has to be set */
152763023d6Sreyk 	TAILQ_INIT(&msg->msg_proposals);
153fde46d6eSreyk 
154fde46d6eSreyk 	return (msg->msg_data);
155fde46d6eSreyk }
156fde46d6eSreyk 
157c45fd413Smikeb struct iked_message *
158c45fd413Smikeb ikev2_msg_copy(struct iked *env, struct iked_message *msg)
159c45fd413Smikeb {
160c45fd413Smikeb 	struct iked_message		*m = NULL;
161c45fd413Smikeb 	struct ibuf			*buf;
162d39d09feSreyk 	size_t				 len;
16312c9fd31Sreyk 	void				*ptr;
164c45fd413Smikeb 
165d39d09feSreyk 	if (ibuf_size(msg->msg_data) < msg->msg_offset)
166d39d09feSreyk 		return (NULL);
167d39d09feSreyk 	len = ibuf_size(msg->msg_data) - msg->msg_offset;
168d39d09feSreyk 
1698f4d0788Stobhe 	if ((m = malloc(sizeof(*m))) == NULL)
1708f4d0788Stobhe 		return (NULL);
1718f4d0788Stobhe 
172d39d09feSreyk 	if ((ptr = ibuf_seek(msg->msg_data, msg->msg_offset, len)) == NULL ||
173c45fd413Smikeb 	    (buf = ikev2_msg_init(env, m, &msg->msg_peer, msg->msg_peerlen,
174c45fd413Smikeb 	     &msg->msg_local, msg->msg_locallen, msg->msg_response)) == NULL ||
1758f4d0788Stobhe 	    ibuf_add(buf, ptr, len)) {
1768f4d0788Stobhe 		free(m);
177c45fd413Smikeb 		return (NULL);
1788f4d0788Stobhe 	}
179c45fd413Smikeb 
180c45fd413Smikeb 	m->msg_fd = msg->msg_fd;
181c45fd413Smikeb 	m->msg_msgid = msg->msg_msgid;
182c45fd413Smikeb 	m->msg_offset = msg->msg_offset;
183c45fd413Smikeb 	m->msg_sa = msg->msg_sa;
184c45fd413Smikeb 
185c45fd413Smikeb 	return (m);
186c45fd413Smikeb }
187c45fd413Smikeb 
188763023d6Sreyk void
189763023d6Sreyk ikev2_msg_cleanup(struct iked *env, struct iked_message *msg)
190763023d6Sreyk {
191eb2389caStobhe 	struct iked_certreq	*cr;
192a30a01d6Stobhe 	int			 i;
193eb2389caStobhe 
19426d7dba1Sreyk 	if (msg == msg->msg_parent) {
195be2b38f5Sclaudio 		ibuf_free(msg->msg_nonce);
196be2b38f5Sclaudio 		ibuf_free(msg->msg_ke);
197be2b38f5Sclaudio 		ibuf_free(msg->msg_auth.id_buf);
198be2b38f5Sclaudio 		ibuf_free(msg->msg_peerid.id_buf);
199be2b38f5Sclaudio 		ibuf_free(msg->msg_localid.id_buf);
200be2b38f5Sclaudio 		ibuf_free(msg->msg_cert.id_buf);
201a30a01d6Stobhe 		for (i = 0; i < IKED_SCERT_MAX; i++)
202a30a01d6Stobhe 			ibuf_free(msg->msg_scert[i].id_buf);
203be2b38f5Sclaudio 		ibuf_free(msg->msg_cookie);
204be2b38f5Sclaudio 		ibuf_free(msg->msg_cookie2);
205be2b38f5Sclaudio 		ibuf_free(msg->msg_del_buf);
206f36db9c4Syasuoka 		ibuf_free(msg->msg_eapmsg);
20725b39a47Stobhe 		free(msg->msg_eap.eam_user);
20852b3354cStobhe 		free(msg->msg_cp_addr);
20952b3354cStobhe 		free(msg->msg_cp_addr6);
2109ef39cf4Stobhe 		free(msg->msg_cp_dns);
21126d7dba1Sreyk 
2123642bd88Smikeb 		msg->msg_nonce = NULL;
2133642bd88Smikeb 		msg->msg_ke = NULL;
2143642bd88Smikeb 		msg->msg_auth.id_buf = NULL;
2159ce164edStobhe 		msg->msg_peerid.id_buf = NULL;
216e3f5cf2eSpatrick 		msg->msg_localid.id_buf = NULL;
2173642bd88Smikeb 		msg->msg_cert.id_buf = NULL;
218a30a01d6Stobhe 		for (i = 0; i < IKED_SCERT_MAX; i++)
219a30a01d6Stobhe 			msg->msg_scert[i].id_buf = NULL;
220d4bcf9ebSreyk 		msg->msg_cookie = NULL;
221c0b327e6Spatrick 		msg->msg_cookie2 = NULL;
222da56c325Stobhe 		msg->msg_del_buf = NULL;
223f36db9c4Syasuoka 		msg->msg_eapmsg = NULL;
22425b39a47Stobhe 		msg->msg_eap.eam_user = NULL;
22552b3354cStobhe 		msg->msg_cp_addr = NULL;
22652b3354cStobhe 		msg->msg_cp_addr6 = NULL;
2279ef39cf4Stobhe 		msg->msg_cp_dns = NULL;
228d4bcf9ebSreyk 
22926d7dba1Sreyk 		config_free_proposals(&msg->msg_proposals, 0);
2303e395450Stobhe 		while ((cr = SIMPLEQ_FIRST(&msg->msg_certreqs))) {
231be2b38f5Sclaudio 			ibuf_free(cr->cr_data);
2323e395450Stobhe 			SIMPLEQ_REMOVE_HEAD(&msg->msg_certreqs, cr_entry);
233eb2389caStobhe 			free(cr);
234eb2389caStobhe 		}
23526d7dba1Sreyk 	}
23626d7dba1Sreyk 
237763023d6Sreyk 	if (msg->msg_data != NULL) {
238be2b38f5Sclaudio 		ibuf_free(msg->msg_data);
239763023d6Sreyk 		msg->msg_data = NULL;
240763023d6Sreyk 	}
241763023d6Sreyk }
242763023d6Sreyk 
243fde46d6eSreyk int
244fde46d6eSreyk ikev2_msg_valid_ike_sa(struct iked *env, struct ike_header *oldhdr,
245fde46d6eSreyk     struct iked_message *msg)
246fde46d6eSreyk {
2476e1880a3Smarkus 	if (msg->msg_sa != NULL && msg->msg_policy != NULL) {
24869aac1baSmikeb 		if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSED)
24969aac1baSmikeb 			return (-1);
2506e1880a3Smarkus 		/*
2516e1880a3Smarkus 		 * Only permit informational requests from initiator
2526e1880a3Smarkus 		 * on closing SAs (for DELETE).
2536e1880a3Smarkus 		 */
2546e1880a3Smarkus 		if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSING) {
2556e1880a3Smarkus 			if (((oldhdr->ike_flags &
2566e1880a3Smarkus 			    (IKEV2_FLAG_INITIATOR|IKEV2_FLAG_RESPONSE)) ==
2576e1880a3Smarkus 			    IKEV2_FLAG_INITIATOR) &&
2586e1880a3Smarkus 			    (oldhdr->ike_exchange ==
2596e1880a3Smarkus 			    IKEV2_EXCHANGE_INFORMATIONAL))
260fde46d6eSreyk 				return (0);
2616e1880a3Smarkus 			return (-1);
2626e1880a3Smarkus 		}
2636e1880a3Smarkus 		return (0);
2646e1880a3Smarkus 	}
265fde46d6eSreyk 
266fde46d6eSreyk 	/* Always fail */
267fde46d6eSreyk 	return (-1);
268fde46d6eSreyk }
269fde46d6eSreyk 
270fde46d6eSreyk int
271d9c13a0aSmikeb ikev2_msg_send(struct iked *env, struct iked_message *msg)
272fde46d6eSreyk {
273c45fd413Smikeb 	struct iked_sa		*sa = msg->msg_sa;
274fde46d6eSreyk 	struct ibuf		*buf = msg->msg_data;
275d09d3a7dSreyk 	uint32_t		 natt = 0x00000000;
27612c9fd31Sreyk 	int			 isnatt = 0;
277d09d3a7dSreyk 	uint8_t			 exchange, flags;
278fde46d6eSreyk 	struct ike_header	*hdr;
279c45fd413Smikeb 	struct iked_message	*m;
280fde46d6eSreyk 
281fde46d6eSreyk 	if (buf == NULL || (hdr = ibuf_seek(msg->msg_data,
282fde46d6eSreyk 	    msg->msg_offset, sizeof(*hdr))) == NULL)
283fde46d6eSreyk 		return (-1);
284fde46d6eSreyk 
2850804246aStobhe 	isnatt = (msg->msg_natt || (sa && sa->sa_natt));
28612c9fd31Sreyk 
287670a137dSmikeb 	exchange = hdr->ike_exchange;
288670a137dSmikeb 	flags = hdr->ike_flags;
28953684dccStobhe 	logit(exchange == IKEV2_EXCHANGE_INFORMATIONAL ?  LOG_DEBUG : LOG_INFO,
2900d262a08Stobhe 	    "%ssend %s %s %u peer %s local %s, %zu bytes%s",
291ecea226bStobhe 	    SPI_IH(hdr),
292670a137dSmikeb 	    print_map(exchange, ikev2_exchange_map),
293ecea226bStobhe 	    (flags & IKEV2_FLAG_RESPONSE) ? "res" : "req",
2947f7372eaSmarkus 	    betoh32(hdr->ike_msgid),
29514e2a040Stb 	    print_addr(&msg->msg_peer),
29614e2a040Stb 	    print_addr(&msg->msg_local),
297eef6c82aSclaudio 	    ibuf_size(buf), isnatt ? ", NAT-T" : "");
298fde46d6eSreyk 
29912c9fd31Sreyk 	if (isnatt) {
30037e80bc6Sclaudio 		struct ibuf *new;
30137e80bc6Sclaudio 		if ((new = ibuf_new(&natt, sizeof(natt))) == NULL) {
302fde46d6eSreyk 			log_debug("%s: failed to set NAT-T", __func__);
303fde46d6eSreyk 			return (-1);
304fde46d6eSreyk 		}
305*fa353a8fSclaudio 		if (ibuf_add_ibuf(new, buf) == -1) {
30637e80bc6Sclaudio 			ibuf_free(new);
30737e80bc6Sclaudio 			log_debug("%s: failed to set NAT-T", __func__);
30837e80bc6Sclaudio 			return (-1);
30937e80bc6Sclaudio 		}
31037e80bc6Sclaudio 		ibuf_free(buf);
31137e80bc6Sclaudio 		buf = msg->msg_data = new;
312fde46d6eSreyk 	}
313d9c13a0aSmikeb 
3145ec2ede8Svgross 	if (sendtofrom(msg->msg_fd, ibuf_data(buf), ibuf_size(buf), 0,
3155ec2ede8Svgross 	    (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen,
3165ec2ede8Svgross 	    (struct sockaddr *)&msg->msg_local, msg->msg_locallen) == -1) {
3170804246aStobhe 		log_warn("%s: sendtofrom", __func__);
3180804246aStobhe 		if (sa != NULL && errno == EADDRNOTAVAIL) {
3190804246aStobhe 			sa_state(env, sa, IKEV2_STATE_CLOSING);
3200804246aStobhe 			timer_del(env, &sa->sa_timer);
3210804246aStobhe 			timer_set(env, &sa->sa_timer,
3220804246aStobhe 			    ikev2_ike_sa_timeout, sa);
3230804246aStobhe 			timer_add(env, &sa->sa_timer,
324a6fc7f59Shenning 			    IKED_IKE_SA_DELETE_TIMEOUT);
325a6fc7f59Shenning 		}
326b41cc0c8Stobhe 		ikestat_inc(env, ikes_msg_send_failures);
327b41cc0c8Stobhe 	} else
328b41cc0c8Stobhe 		ikestat_inc(env, ikes_msg_sent);
329fde46d6eSreyk 
3300804246aStobhe 	if (sa == NULL)
331c45fd413Smikeb 		return (0);
332c45fd413Smikeb 
333c45fd413Smikeb 	if ((m = ikev2_msg_copy(env, msg)) == NULL) {
334c45fd413Smikeb 		log_debug("%s: failed to copy a message", __func__);
335c45fd413Smikeb 		return (-1);
336c45fd413Smikeb 	}
337670a137dSmikeb 	m->msg_exchange = exchange;
338c45fd413Smikeb 
339670a137dSmikeb 	if (flags & IKEV2_FLAG_RESPONSE) {
3406e264ad0Stobhe 		if (ikev2_msg_enqueue(env, &sa->sa_responses, m,
3416e264ad0Stobhe 		    IKED_RESPONSE_TIMEOUT) != 0) {
3426e264ad0Stobhe 			ikev2_msg_cleanup(env, m);
3436e264ad0Stobhe 			free(m);
3446e264ad0Stobhe 			return (-1);
3456e264ad0Stobhe 		}
346c45fd413Smikeb 	} else {
3476e264ad0Stobhe 		if (ikev2_msg_enqueue(env, &sa->sa_requests, m,
3486e264ad0Stobhe 		    IKED_RETRANSMIT_TIMEOUT) != 0) {
3496e264ad0Stobhe 			ikev2_msg_cleanup(env, m);
3506e264ad0Stobhe 			free(m);
3516e264ad0Stobhe 			return (-1);
3526e264ad0Stobhe 		}
353c45fd413Smikeb 	}
354c45fd413Smikeb 
355fde46d6eSreyk 	return (0);
356fde46d6eSreyk }
357fde46d6eSreyk 
358d09d3a7dSreyk uint32_t
359c45fd413Smikeb ikev2_msg_id(struct iked *env, struct iked_sa *sa)
360fde46d6eSreyk {
361d09d3a7dSreyk 	uint32_t		id = sa->sa_reqid;
362fde46d6eSreyk 
36310650a52Smikeb 	if (++sa->sa_reqid == UINT32_MAX) {
364fde46d6eSreyk 		/* XXX we should close and renegotiate the connection now */
365fde46d6eSreyk 		log_debug("%s: IKEv2 message sequence overflow", __func__);
366fde46d6eSreyk 	}
36710650a52Smikeb 	return (id);
368fde46d6eSreyk }
369fde46d6eSreyk 
37015863c3aStobhe /*
37115863c3aStobhe  * Calculate the final sizes of the IKEv2 header and the encrypted payload
37215863c3aStobhe  * header.  This must be done before encryption to make sure the correct
37315863c3aStobhe  * headers are authenticated.
37415863c3aStobhe  */
37515863c3aStobhe int
37615863c3aStobhe ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld,
37715863c3aStobhe     struct ibuf *buf, struct ibuf *e, struct ike_header *hdr,
37815863c3aStobhe     uint8_t firstpayload, int fragmentation)
37915863c3aStobhe {
38015863c3aStobhe 	size_t	 len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen;
38115863c3aStobhe 
38215863c3aStobhe 	if (sa == NULL ||
38315863c3aStobhe 	    sa->sa_encr == NULL ||
38415863c3aStobhe 	    sa->sa_integr == NULL) {
38515863c3aStobhe 		log_debug("%s: invalid SA", __func__);
38615863c3aStobhe 		return (-1);
38715863c3aStobhe 	}
38815863c3aStobhe 
38915863c3aStobhe 	len = ibuf_size(e);
39015863c3aStobhe 	blocklen = cipher_length(sa->sa_encr);
39115863c3aStobhe 	integrlen = hash_length(sa->sa_integr);
39215863c3aStobhe 	ivlen = cipher_ivlength(sa->sa_encr);
39315863c3aStobhe 	encrlen = roundup(len + 1, blocklen);
39415863c3aStobhe 	outlen = cipher_outlength(sa->sa_encr, encrlen);
39515863c3aStobhe 	pldlen = ivlen + outlen + integrlen;
39615863c3aStobhe 
39715863c3aStobhe 	if (ikev2_next_payload(pld,
39815863c3aStobhe 	    pldlen + (fragmentation ? sizeof(struct ikev2_frag_payload) : 0),
39915863c3aStobhe 	    firstpayload) == -1)
40015863c3aStobhe 		return (-1);
40115863c3aStobhe 	if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1)
40215863c3aStobhe 		return (-1);
40315863c3aStobhe 
40415863c3aStobhe 	return (0);
40515863c3aStobhe }
40615863c3aStobhe 
407fde46d6eSreyk struct ibuf *
40815863c3aStobhe ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src,
40915863c3aStobhe     struct ibuf *aad)
410fde46d6eSreyk {
4119ebe96b0Stobhe 	size_t			 len, encrlen, integrlen, blocklen,
412fde46d6eSreyk 				    outlen;
413d09d3a7dSreyk 	uint8_t			*buf, pad = 0, *ptr;
41488d75aadSreyk 	struct ibuf		*encr, *dst = NULL, *out = NULL;
415fde46d6eSreyk 
416fde46d6eSreyk 	buf = ibuf_data(src);
417fde46d6eSreyk 	len = ibuf_size(src);
418fde46d6eSreyk 
419328746baSreyk 	log_debug("%s: decrypted length %zu", __func__, len);
420fde46d6eSreyk 	print_hex(buf, 0, len);
421fde46d6eSreyk 
422fde46d6eSreyk 	if (sa == NULL ||
423fde46d6eSreyk 	    sa->sa_encr == NULL ||
424fde46d6eSreyk 	    sa->sa_integr == NULL) {
425fde46d6eSreyk 		log_debug("%s: invalid SA", __func__);
426fde46d6eSreyk 		goto done;
427fde46d6eSreyk 	}
428fde46d6eSreyk 
42988d75aadSreyk 	if (sa->sa_hdr.sh_initiator)
430fde46d6eSreyk 		encr = sa->sa_key_iencr;
43188d75aadSreyk 	else
432fde46d6eSreyk 		encr = sa->sa_key_rencr;
433fde46d6eSreyk 
434fde46d6eSreyk 	blocklen = cipher_length(sa->sa_encr);
435fde46d6eSreyk 	integrlen = hash_length(sa->sa_integr);
436fde46d6eSreyk 	encrlen = roundup(len + sizeof(pad), blocklen);
437fde46d6eSreyk 	pad = encrlen - (len + sizeof(pad));
438fde46d6eSreyk 
439fde46d6eSreyk 	/*
440fde46d6eSreyk 	 * Pad the payload and encrypt it
441fde46d6eSreyk 	 */
442fde46d6eSreyk 	if (pad) {
44356c4e216Sclaudio 		if ((ptr = ibuf_reserve(src, pad)) == NULL)
444fde46d6eSreyk 			goto done;
445fde46d6eSreyk 		arc4random_buf(ptr, pad);
446fde46d6eSreyk 	}
447fde46d6eSreyk 	if (ibuf_add(src, &pad, sizeof(pad)) != 0)
448fde46d6eSreyk 		goto done;
449fde46d6eSreyk 
450328746baSreyk 	log_debug("%s: padded length %zu", __func__, ibuf_size(src));
451dca9e784Sclaudio 	print_hexbuf(src);
452fde46d6eSreyk 
453eef6c82aSclaudio 	cipher_setkey(sa->sa_encr, ibuf_data(encr), ibuf_size(encr));
45488d75aadSreyk 	cipher_setiv(sa->sa_encr, NULL, 0);	/* XXX ivlen */
45581b8fecaStobhe 	if (cipher_init_encrypt(sa->sa_encr) == -1) {
45681b8fecaStobhe 		log_info("%s: error initiating cipher.", __func__);
45781b8fecaStobhe 		goto done;
45881b8fecaStobhe 	}
459fde46d6eSreyk 
460fde46d6eSreyk 	if ((dst = ibuf_dup(sa->sa_encr->encr_iv)) == NULL)
461fde46d6eSreyk 		goto done;
462fde46d6eSreyk 
463fde46d6eSreyk 	if ((out = ibuf_new(NULL,
464fde46d6eSreyk 	    cipher_outlength(sa->sa_encr, encrlen))) == NULL)
465fde46d6eSreyk 		goto done;
466fde46d6eSreyk 
467fde46d6eSreyk 	outlen = ibuf_size(out);
46815863c3aStobhe 
46915863c3aStobhe 	/* Add AAD for AEAD ciphers */
47015863c3aStobhe 	if (sa->sa_integr->hash_isaead)
471eef6c82aSclaudio 		cipher_aad(sa->sa_encr, ibuf_data(aad), ibuf_size(aad),
472eef6c82aSclaudio 		    &outlen);
47315863c3aStobhe 
47481b8fecaStobhe 	if (cipher_update(sa->sa_encr, ibuf_data(src), encrlen,
47581b8fecaStobhe 	    ibuf_data(out), &outlen) == -1) {
47681b8fecaStobhe 		log_info("%s: error updating cipher.", __func__);
47781b8fecaStobhe 		goto done;
47881b8fecaStobhe 	}
479fde46d6eSreyk 
48015863c3aStobhe 	if (cipher_final(sa->sa_encr) == -1) {
48115863c3aStobhe 		log_info("%s: encryption failed.", __func__);
48215863c3aStobhe 		goto done;
48315863c3aStobhe 	}
48415863c3aStobhe 
485fde46d6eSreyk 	if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0)
486fde46d6eSreyk 		goto done;
487fde46d6eSreyk 
48856c4e216Sclaudio 	if ((ptr = ibuf_reserve(dst, integrlen)) == NULL)
489fde46d6eSreyk 		goto done;
4908fbd7fcbSdoug 	explicit_bzero(ptr, integrlen);
491fde46d6eSreyk 
492328746baSreyk 	log_debug("%s: length %zu, padding %d, output length %zu",
493fde46d6eSreyk 	    __func__, len + sizeof(pad), pad, ibuf_size(dst));
494dca9e784Sclaudio 	print_hexbuf(dst);
495fde46d6eSreyk 
496be2b38f5Sclaudio 	ibuf_free(src);
497be2b38f5Sclaudio 	ibuf_free(out);
498fde46d6eSreyk 	return (dst);
499fde46d6eSreyk  done:
500be2b38f5Sclaudio 	ibuf_free(src);
501be2b38f5Sclaudio 	ibuf_free(out);
502be2b38f5Sclaudio 	ibuf_free(dst);
503fde46d6eSreyk 	return (NULL);
504fde46d6eSreyk }
505fde46d6eSreyk 
506fde46d6eSreyk int
507fde46d6eSreyk ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src)
508fde46d6eSreyk {
509fde46d6eSreyk 	int			 ret = -1;
510fde46d6eSreyk 	size_t			 integrlen, tmplen;
51188d75aadSreyk 	struct ibuf		*integr, *tmp = NULL;
512d09d3a7dSreyk 	uint8_t			*ptr;
513fde46d6eSreyk 
514328746baSreyk 	log_debug("%s: message length %zu", __func__, ibuf_size(src));
515dca9e784Sclaudio 	print_hexbuf(src);
516fde46d6eSreyk 
517fde46d6eSreyk 	if (sa == NULL ||
51815863c3aStobhe 	    sa->sa_encr == NULL ||
519fde46d6eSreyk 	    sa->sa_integr == NULL) {
520fde46d6eSreyk 		log_debug("%s: invalid SA", __func__);
521fde46d6eSreyk 		return (-1);
522fde46d6eSreyk 	}
523fde46d6eSreyk 
524fde46d6eSreyk 	integrlen = hash_length(sa->sa_integr);
525328746baSreyk 	log_debug("%s: integrity checksum length %zu", __func__,
526fde46d6eSreyk 	    integrlen);
527fde46d6eSreyk 
528fde46d6eSreyk 	/*
529fde46d6eSreyk 	 * Validate packet checksum
530fde46d6eSreyk 	 */
531fde46d6eSreyk 	if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
532fde46d6eSreyk 		goto done;
533fde46d6eSreyk 
53415863c3aStobhe 	if (!sa->sa_integr->hash_isaead) {
53515863c3aStobhe 		if (sa->sa_hdr.sh_initiator)
53615863c3aStobhe 			integr = sa->sa_key_iauth;
53715863c3aStobhe 		else
53815863c3aStobhe 			integr = sa->sa_key_rauth;
53915863c3aStobhe 
54015863c3aStobhe 		hash_setkey(sa->sa_integr, ibuf_data(integr),
54115863c3aStobhe 		    ibuf_size(integr));
542fde46d6eSreyk 		hash_init(sa->sa_integr);
543fde46d6eSreyk 		hash_update(sa->sa_integr, ibuf_data(src),
544fde46d6eSreyk 		    ibuf_size(src) - integrlen);
545fde46d6eSreyk 		hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
546fde46d6eSreyk 
547fde46d6eSreyk 		if (tmplen != integrlen) {
548fde46d6eSreyk 			log_debug("%s: hash failure", __func__);
549fde46d6eSreyk 			goto done;
550fde46d6eSreyk 		}
55115863c3aStobhe 	} else {
55215863c3aStobhe 		/* Append AEAD tag */
55315863c3aStobhe 		if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp)))
55415863c3aStobhe 			goto done;
55515863c3aStobhe 	}
556fde46d6eSreyk 
557fde46d6eSreyk 	if ((ptr = ibuf_seek(src,
558fde46d6eSreyk 	    ibuf_size(src) - integrlen, integrlen)) == NULL)
559fde46d6eSreyk 		goto done;
56015863c3aStobhe 	memcpy(ptr, ibuf_data(tmp), integrlen);
561fde46d6eSreyk 
562dca9e784Sclaudio 	print_hexbuf(tmp);
563fde46d6eSreyk 
564fde46d6eSreyk 	ret = 0;
565fde46d6eSreyk  done:
566be2b38f5Sclaudio 	ibuf_free(tmp);
567fde46d6eSreyk 
568fde46d6eSreyk 	return (ret);
569fde46d6eSreyk }
570fde46d6eSreyk 
571fde46d6eSreyk struct ibuf *
572fde46d6eSreyk ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa,
573fde46d6eSreyk     struct ibuf *msg, struct ibuf *src)
574fde46d6eSreyk {
575e0696045Sreyk 	ssize_t			 ivlen, encrlen, integrlen, blocklen,
576fde46d6eSreyk 				    outlen, tmplen;
57715863c3aStobhe 	uint8_t			 pad = 0, *ptr, *integrdata;
578fde46d6eSreyk 	struct ibuf		*integr, *encr, *tmp = NULL, *out = NULL;
579fde46d6eSreyk 	off_t			 ivoff, encroff, integroff;
580fde46d6eSreyk 
581fde46d6eSreyk 	if (sa == NULL ||
582fde46d6eSreyk 	    sa->sa_encr == NULL ||
583fde46d6eSreyk 	    sa->sa_integr == NULL) {
584fde46d6eSreyk 		log_debug("%s: invalid SA", __func__);
585dca9e784Sclaudio 		print_hexbuf(src);
586fde46d6eSreyk 		goto done;
587fde46d6eSreyk 	}
588fde46d6eSreyk 
589fde46d6eSreyk 	if (!sa->sa_hdr.sh_initiator) {
590fde46d6eSreyk 		encr = sa->sa_key_iencr;
591fde46d6eSreyk 		integr = sa->sa_key_iauth;
592fde46d6eSreyk 	} else {
593fde46d6eSreyk 		encr = sa->sa_key_rencr;
594fde46d6eSreyk 		integr = sa->sa_key_rauth;
595fde46d6eSreyk 	}
596fde46d6eSreyk 
597fde46d6eSreyk 	blocklen = cipher_length(sa->sa_encr);
598fde46d6eSreyk 	ivlen = cipher_ivlength(sa->sa_encr);
599fde46d6eSreyk 	ivoff = 0;
600fde46d6eSreyk 	integrlen = hash_length(sa->sa_integr);
601fde46d6eSreyk 	integroff = ibuf_size(src) - integrlen;
602fde46d6eSreyk 	encroff = ivlen;
603fde46d6eSreyk 	encrlen = ibuf_size(src) - integrlen - ivlen;
604fde46d6eSreyk 
605e0696045Sreyk 	if (encrlen < 0 || integroff < 0) {
606e0696045Sreyk 		log_debug("%s: invalid integrity value", __func__);
607e0696045Sreyk 		goto done;
608e0696045Sreyk 	}
609e0696045Sreyk 
610328746baSreyk 	log_debug("%s: IV length %zd", __func__, ivlen);
611fde46d6eSreyk 	print_hex(ibuf_data(src), 0, ivlen);
612328746baSreyk 	log_debug("%s: encrypted payload length %zd", __func__, encrlen);
613fde46d6eSreyk 	print_hex(ibuf_data(src), encroff, encrlen);
614328746baSreyk 	log_debug("%s: integrity checksum length %zd", __func__, integrlen);
615fde46d6eSreyk 	print_hex(ibuf_data(src), integroff, integrlen);
616fde46d6eSreyk 
617fde46d6eSreyk 	/*
618fde46d6eSreyk 	 * Validate packet checksum
619fde46d6eSreyk 	 */
62015863c3aStobhe 	if (!sa->sa_integr->hash_isaead) {
621d0e3d0abStobhe 		if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
622fde46d6eSreyk 			goto done;
623fde46d6eSreyk 
624a699afcaSclaudio 		hash_setkey(sa->sa_integr, ibuf_data(integr),
625eef6c82aSclaudio 		    ibuf_size(integr));
626fde46d6eSreyk 		hash_init(sa->sa_integr);
627fde46d6eSreyk 		hash_update(sa->sa_integr, ibuf_data(msg),
628fde46d6eSreyk 		    ibuf_size(msg) - integrlen);
629a699afcaSclaudio 		hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
630fde46d6eSreyk 
63115863c3aStobhe 		integrdata = ibuf_seek(src, integroff, integrlen);
63215863c3aStobhe 		if (integrdata == NULL)
63315863c3aStobhe 			goto done;
634a699afcaSclaudio 		if (memcmp(ibuf_data(tmp), integrdata, integrlen) != 0) {
635fde46d6eSreyk 			log_debug("%s: integrity check failed", __func__);
636fde46d6eSreyk 			goto done;
637fde46d6eSreyk 		}
638fde46d6eSreyk 
639b0eeedd0Smikeb 		log_debug("%s: integrity check succeeded", __func__);
640a699afcaSclaudio 		print_hex(ibuf_data(tmp), 0, tmplen);
641fde46d6eSreyk 
642be2b38f5Sclaudio 		ibuf_free(tmp);
643fde46d6eSreyk 		tmp = NULL;
64415863c3aStobhe 	}
645fde46d6eSreyk 
646fde46d6eSreyk 	/*
647fde46d6eSreyk 	 * Decrypt the payload and strip any padding
648fde46d6eSreyk 	 */
649fde46d6eSreyk 	if ((encrlen % blocklen) != 0) {
650fde46d6eSreyk 		log_debug("%s: unaligned encrypted payload", __func__);
651fde46d6eSreyk 		goto done;
652fde46d6eSreyk 	}
653fde46d6eSreyk 
654eef6c82aSclaudio 	cipher_setkey(sa->sa_encr, ibuf_data(encr), ibuf_size(encr));
6555001b11bSclaudio 	cipher_setiv(sa->sa_encr, ibuf_seek(src, ivoff, ivlen), ivlen);
65681b8fecaStobhe 	if (cipher_init_decrypt(sa->sa_encr) == -1) {
65781b8fecaStobhe 		log_info("%s: error initiating cipher.", __func__);
65881b8fecaStobhe 		goto done;
65981b8fecaStobhe 	}
660fde46d6eSreyk 
66115863c3aStobhe 	/* Set AEAD tag */
66215863c3aStobhe 	if (sa->sa_integr->hash_isaead) {
66315863c3aStobhe 		integrdata = ibuf_seek(src, integroff, integrlen);
66415863c3aStobhe 		if (integrdata == NULL)
66515863c3aStobhe 			goto done;
66615863c3aStobhe 		if (cipher_settag(sa->sa_encr, integrdata, integrlen)) {
66715863c3aStobhe 			log_info("%s: failed to set tag.", __func__);
66815863c3aStobhe 			goto done;
66915863c3aStobhe 		}
67015863c3aStobhe 	}
67115863c3aStobhe 
672fde46d6eSreyk 	if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
673fde46d6eSreyk 	    encrlen))) == NULL)
674fde46d6eSreyk 		goto done;
675fde46d6eSreyk 
67615863c3aStobhe 	/*
67715863c3aStobhe 	 * Add additional authenticated data for AEAD ciphers
67815863c3aStobhe 	 */
67915863c3aStobhe 	if (sa->sa_integr->hash_isaead) {
680eef6c82aSclaudio 		log_debug("%s: AAD length %zu", __func__,
681eef6c82aSclaudio 		    ibuf_size(msg) - ibuf_size(src));
682eef6c82aSclaudio 		print_hex(ibuf_data(msg), 0, ibuf_size(msg) - ibuf_size(src));
68315863c3aStobhe 		cipher_aad(sa->sa_encr, ibuf_data(msg),
684eef6c82aSclaudio 		    ibuf_size(msg) - ibuf_size(src), &outlen);
68515863c3aStobhe 	}
68615863c3aStobhe 
687eef6c82aSclaudio 	if ((outlen = ibuf_size(out)) != 0) {
6885001b11bSclaudio 		if (cipher_update(sa->sa_encr, ibuf_seek(src, encroff, encrlen),
68981b8fecaStobhe 		    encrlen, ibuf_data(out), &outlen) == -1) {
69081b8fecaStobhe 			log_info("%s: error updating cipher.", __func__);
69181b8fecaStobhe 			goto done;
69281b8fecaStobhe 		}
6933189733aSmikeb 
694fde46d6eSreyk 		ptr = ibuf_seek(out, outlen - 1, 1);
695fde46d6eSreyk 		pad = *ptr;
696e0696045Sreyk 	}
697fde46d6eSreyk 
69815863c3aStobhe 	if (cipher_final(sa->sa_encr) == -1) {
69915863c3aStobhe 		log_info("%s: decryption failed.", __func__);
70015863c3aStobhe 		goto done;
70115863c3aStobhe 	}
70215863c3aStobhe 
703328746baSreyk 	log_debug("%s: decrypted payload length %zd/%zd padding %d",
704fde46d6eSreyk 	    __func__, outlen, encrlen, pad);
705dca9e784Sclaudio 	print_hexbuf(out);
706fde46d6eSreyk 
70765c540d0Spatrick 	/* Strip padding and padding length */
70865c540d0Spatrick 	if (ibuf_setsize(out, outlen - pad - 1) != 0)
709fde46d6eSreyk 		goto done;
710fde46d6eSreyk 
711be2b38f5Sclaudio 	ibuf_free(src);
712fde46d6eSreyk 	return (out);
713fde46d6eSreyk  done:
714be2b38f5Sclaudio 	ibuf_free(tmp);
715be2b38f5Sclaudio 	ibuf_free(out);
716be2b38f5Sclaudio 	ibuf_free(src);
717fde46d6eSreyk 	return (NULL);
718fde46d6eSreyk }
719fde46d6eSreyk 
720fde46d6eSreyk int
72165c540d0Spatrick ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf) {
72265c540d0Spatrick 	size_t		len = ibuf_length(buf);
72365c540d0Spatrick 	sa_family_t	sa_fam;
72465c540d0Spatrick 	size_t		max;
72565c540d0Spatrick 	size_t		ivlen, integrlen, blocklen;
72665c540d0Spatrick 
72715863c3aStobhe 	if (sa == NULL ||
72815863c3aStobhe 	    sa->sa_encr == NULL ||
72915863c3aStobhe 	    sa->sa_integr == NULL) {
73015863c3aStobhe 		log_debug("%s: invalid SA", __func__);
73115863c3aStobhe 		return (-1);
73215863c3aStobhe 	}
73315863c3aStobhe 
73465c540d0Spatrick 	sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
73565c540d0Spatrick 
73665c540d0Spatrick 	max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
73765c540d0Spatrick 	    : IKEV2_MAXLEN_IPV6_FRAG;
73865c540d0Spatrick 
73965c540d0Spatrick 	blocklen = cipher_length(sa->sa_encr);
74065c540d0Spatrick 	ivlen = cipher_ivlength(sa->sa_encr);
74165c540d0Spatrick 	integrlen = hash_length(sa->sa_integr);
74265c540d0Spatrick 
74365c540d0Spatrick 	/* Estimated maximum packet size (with 0 < padding < blocklen) */
74465c540d0Spatrick 	return ((len + ivlen + blocklen + integrlen) >= max) && sa->sa_frag;
74565c540d0Spatrick }
74665c540d0Spatrick 
74765c540d0Spatrick int
748c45fd413Smikeb ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf **ep,
749d09d3a7dSreyk     uint8_t exchange, uint8_t firstpayload, int response)
750fde46d6eSreyk {
751fde46d6eSreyk 	struct iked_message		 resp;
752fde46d6eSreyk 	struct ike_header		*hdr;
753fde46d6eSreyk 	struct ikev2_payload		*pld;
754fde46d6eSreyk 	struct ibuf			*buf, *e = *ep;
755fde46d6eSreyk 	int				 ret = -1;
756fde46d6eSreyk 
75765c540d0Spatrick 	/* Check if msg needs to be fragmented */
75865c540d0Spatrick 	if (ikev2_check_frag_oversize(sa, e)) {
75965c540d0Spatrick 		return ikev2_send_encrypted_fragments(env, sa, e, exchange,
76065c540d0Spatrick 		    firstpayload, response);
76165c540d0Spatrick 	}
76265c540d0Spatrick 
763c45fd413Smikeb 	if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr,
764c45fd413Smikeb 	    sa->sa_peer.addr.ss_len, &sa->sa_local.addr,
765c45fd413Smikeb 	    sa->sa_local.addr.ss_len, response)) == NULL)
766fde46d6eSreyk 		goto done;
767fde46d6eSreyk 
76830904802Spatrick 	resp.msg_msgid = response ? sa->sa_msgid_current : ikev2_msg_id(env, sa);
769c45fd413Smikeb 
770fde46d6eSreyk 	/* IKE header */
771c45fd413Smikeb 	if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, IKEV2_PAYLOAD_SK,
772c45fd413Smikeb 	    exchange, response ? IKEV2_FLAG_RESPONSE : 0)) == NULL)
773fde46d6eSreyk 		goto done;
774fde46d6eSreyk 
775fde46d6eSreyk 	if ((pld = ikev2_add_payload(buf)) == NULL)
776fde46d6eSreyk 		goto done;
777fde46d6eSreyk 
77815863c3aStobhe 	if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1)
77915863c3aStobhe 		goto done;
78015863c3aStobhe 
781fde46d6eSreyk 	/* Encrypt message and add as an E payload */
78215863c3aStobhe 	if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
783fde46d6eSreyk 		log_debug("%s: encryption failed", __func__);
784fde46d6eSreyk 		goto done;
785fde46d6eSreyk 	}
786*fa353a8fSclaudio 	if (ibuf_add_ibuf(buf, e) != 0)
787fde46d6eSreyk 		goto done;
788fde46d6eSreyk 
789fde46d6eSreyk 	/* Add integrity checksum (HMAC) */
790fde46d6eSreyk 	if (ikev2_msg_integr(env, sa, buf) != 0) {
791fde46d6eSreyk 		log_debug("%s: integrity checksum failed", __func__);
792fde46d6eSreyk 		goto done;
793fde46d6eSreyk 	}
794fde46d6eSreyk 
795fde46d6eSreyk 	resp.msg_data = buf;
796fde46d6eSreyk 	resp.msg_sa = sa;
797d9c13a0aSmikeb 	resp.msg_fd = sa->sa_fd;
798fde46d6eSreyk 	TAILQ_INIT(&resp.msg_proposals);
799fde46d6eSreyk 
800fde46d6eSreyk 	(void)ikev2_pld_parse(env, hdr, &resp, 0);
801fde46d6eSreyk 
802d9c13a0aSmikeb 	ret = ikev2_msg_send(env, &resp);
803fde46d6eSreyk 
804fde46d6eSreyk  done:
805fde46d6eSreyk 	/* e is cleaned up by the calling function */
806fde46d6eSreyk 	*ep = e;
807763023d6Sreyk 	ikev2_msg_cleanup(env, &resp);
808fde46d6eSreyk 
809fde46d6eSreyk 	return (ret);
810fde46d6eSreyk }
811fde46d6eSreyk 
81265c540d0Spatrick int
81365c540d0Spatrick ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
81465c540d0Spatrick     struct ibuf *in, uint8_t exchange, uint8_t firstpayload, int response) {
81565c540d0Spatrick 	struct iked_message		 resp;
8164387480dStobhe 	struct ibuf			*buf, *e = NULL;
81765c540d0Spatrick 	struct ike_header		*hdr;
81865c540d0Spatrick 	struct ikev2_payload		*pld;
81965c540d0Spatrick 	struct ikev2_frag_payload	*frag;
82065c540d0Spatrick 	sa_family_t			 sa_fam;
82165c540d0Spatrick 	size_t				 ivlen, integrlen, blocklen;
822ab8e3451Sderaadt 	size_t				 max_len, left,  offset=0;
82365c540d0Spatrick 	size_t				 frag_num = 1, frag_total;
82465c540d0Spatrick 	uint8_t				*data;
82565c540d0Spatrick 	uint32_t			 msgid;
82665c540d0Spatrick 	int				 ret = -1;
82765c540d0Spatrick 
82815863c3aStobhe 	if (sa == NULL ||
82915863c3aStobhe 	    sa->sa_encr == NULL ||
83015863c3aStobhe 	    sa->sa_integr == NULL) {
83115863c3aStobhe 		log_debug("%s: invalid SA", __func__);
83268468697Smbuhl 		ikestat_inc(env, ikes_frag_send_failures);
83368468697Smbuhl 		return ret;
83415863c3aStobhe 	}
83515863c3aStobhe 
83665c540d0Spatrick 	sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
83765c540d0Spatrick 
83865c540d0Spatrick 	left = ibuf_length(in);
83965c540d0Spatrick 
84065c540d0Spatrick 	/* Calculate max allowed size of a fragments payload */
84165c540d0Spatrick 	blocklen = cipher_length(sa->sa_encr);
84265c540d0Spatrick 	ivlen = cipher_ivlength(sa->sa_encr);
84365c540d0Spatrick 	integrlen = hash_length(sa->sa_integr);
84465c540d0Spatrick 	max_len = (sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
84565c540d0Spatrick 	    : IKEV2_MAXLEN_IPV6_FRAG)
84665c540d0Spatrick 	    - ivlen - blocklen - integrlen;
84765c540d0Spatrick 
84865c540d0Spatrick 	/* Total number of fragments to send */
84965c540d0Spatrick 	frag_total = (left / max_len) + 1;
85065c540d0Spatrick 
85165c540d0Spatrick 	msgid = response ? sa->sa_msgid_current : ikev2_msg_id(env, sa);
85265c540d0Spatrick 
85365c540d0Spatrick 	while (frag_num <= frag_total) {
85465c540d0Spatrick 		if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr,
85565c540d0Spatrick 		    sa->sa_peer.addr.ss_len, &sa->sa_local.addr,
85665c540d0Spatrick 		    sa->sa_local.addr.ss_len, response)) == NULL)
85765c540d0Spatrick 			goto done;
85865c540d0Spatrick 
85965c540d0Spatrick 		resp.msg_msgid = msgid;
86065c540d0Spatrick 
86165c540d0Spatrick 		/* IKE header */
86265c540d0Spatrick 		if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid,
86365c540d0Spatrick 		    IKEV2_PAYLOAD_SKF, exchange, response ? IKEV2_FLAG_RESPONSE
86465c540d0Spatrick 		    : 0)) == NULL)
86565c540d0Spatrick 			goto done;
86665c540d0Spatrick 
86765c540d0Spatrick 		/* Payload header */
86865c540d0Spatrick 		if ((pld = ikev2_add_payload(buf)) == NULL)
86965c540d0Spatrick 			goto done;
87065c540d0Spatrick 
87165c540d0Spatrick 		/* Fragment header */
87256c4e216Sclaudio 		if ((frag = ibuf_reserve(buf, sizeof(*frag))) == NULL) {
87365c540d0Spatrick 			log_debug("%s: failed to add SKF fragment header",
87465c540d0Spatrick 			    __func__);
87565c540d0Spatrick 			goto done;
87665c540d0Spatrick 		}
87765c540d0Spatrick 		frag->frag_num = htobe16(frag_num);
87865c540d0Spatrick 		frag->frag_total = htobe16(frag_total);
87965c540d0Spatrick 
88065c540d0Spatrick 		/* Encrypt message and add as an E payload */
88165c540d0Spatrick 		data = ibuf_seek(in, offset, 0);
882d8ea035bSderaadt 		if ((e = ibuf_new(data, MINIMUM(left, max_len))) == NULL) {
88365c540d0Spatrick 			goto done;
88465c540d0Spatrick 		}
88515863c3aStobhe 
88615863c3aStobhe 		if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr,
88715863c3aStobhe 		    firstpayload, 1) == -1)
88815863c3aStobhe 			goto done;
88915863c3aStobhe 
89015863c3aStobhe 		if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
89165c540d0Spatrick 			log_debug("%s: encryption failed", __func__);
89265c540d0Spatrick 			goto done;
89365c540d0Spatrick 		}
894*fa353a8fSclaudio 		if (ibuf_add_ibuf(buf, e) != 0)
89565c540d0Spatrick 			goto done;
89665c540d0Spatrick 
89765c540d0Spatrick 		/* Add integrity checksum (HMAC) */
89865c540d0Spatrick 		if (ikev2_msg_integr(env, sa, buf) != 0) {
89965c540d0Spatrick 			log_debug("%s: integrity checksum failed", __func__);
90065c540d0Spatrick 			goto done;
90165c540d0Spatrick 		}
90265c540d0Spatrick 
90365c540d0Spatrick 		log_debug("%s: Fragment %zu of %zu has size of %zu bytes.",
90465c540d0Spatrick 		    __func__, frag_num, frag_total,
90565c540d0Spatrick 		    ibuf_size(buf) - sizeof(*hdr));
906dca9e784Sclaudio 		print_hexbuf(buf);
90765c540d0Spatrick 
90865c540d0Spatrick 		resp.msg_data = buf;
90965c540d0Spatrick 		resp.msg_sa = sa;
91065c540d0Spatrick 		resp.msg_fd = sa->sa_fd;
91165c540d0Spatrick 		TAILQ_INIT(&resp.msg_proposals);
91265c540d0Spatrick 
91365c540d0Spatrick 		if (ikev2_msg_send(env, &resp) == -1)
91465c540d0Spatrick 			goto done;
91565c540d0Spatrick 
916b41cc0c8Stobhe 		ikestat_inc(env, ikes_frag_sent);
917b41cc0c8Stobhe 
918d8ea035bSderaadt 		offset += MINIMUM(left, max_len);
919d8ea035bSderaadt 		left -= MINIMUM(left, max_len);
92065c540d0Spatrick 		frag_num++;
92165c540d0Spatrick 
92265c540d0Spatrick 		/* MUST be zero after first fragment */
92365c540d0Spatrick 		firstpayload = 0;
92465c540d0Spatrick 
92565c540d0Spatrick 		ikev2_msg_cleanup(env, &resp);
926be2b38f5Sclaudio 		ibuf_free(e);
92765c540d0Spatrick 		e = NULL;
92865c540d0Spatrick 	}
92965c540d0Spatrick 
93065c540d0Spatrick 	return 0;
93165c540d0Spatrick done:
93265c540d0Spatrick 	ikev2_msg_cleanup(env, &resp);
933be2b38f5Sclaudio 	ibuf_free(e);
934b41cc0c8Stobhe 	ikestat_inc(env, ikes_frag_send_failures);
93565c540d0Spatrick 	return ret;
93665c540d0Spatrick }
93765c540d0Spatrick 
938fde46d6eSreyk struct ibuf *
939fde46d6eSreyk ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response)
940fde46d6eSreyk {
941fde46d6eSreyk 	struct ibuf		*authmsg = NULL, *nonce, *prfkey, *buf;
942d09d3a7dSreyk 	uint8_t			*ptr;
943fde46d6eSreyk 	struct iked_id		*id;
944fde46d6eSreyk 	size_t			 tmplen;
945fde46d6eSreyk 
946fde46d6eSreyk 	/*
947fde46d6eSreyk 	 * Create the payload to be signed/MAC'ed for AUTH
948fde46d6eSreyk 	 */
949fde46d6eSreyk 
950fde46d6eSreyk 	if (!response) {
951fde46d6eSreyk 		if ((nonce = sa->sa_rnonce) == NULL ||
952fde46d6eSreyk 		    (sa->sa_iid.id_type == 0) ||
953fde46d6eSreyk 		    (prfkey = sa->sa_key_iprf) == NULL ||
954fde46d6eSreyk 		    (buf = sa->sa_1stmsg) == NULL)
955fde46d6eSreyk 			return (NULL);
956fde46d6eSreyk 		id = &sa->sa_iid;
957fde46d6eSreyk 	} else {
958fde46d6eSreyk 		if ((nonce = sa->sa_inonce) == NULL ||
959fde46d6eSreyk 		    (sa->sa_rid.id_type == 0) ||
960fde46d6eSreyk 		    (prfkey = sa->sa_key_rprf) == NULL ||
961fde46d6eSreyk 		    (buf = sa->sa_2ndmsg) == NULL)
962fde46d6eSreyk 			return (NULL);
963fde46d6eSreyk 		id = &sa->sa_rid;
964fde46d6eSreyk 	}
965fde46d6eSreyk 
966fde46d6eSreyk 	if ((authmsg = ibuf_dup(buf)) == NULL)
967fde46d6eSreyk 		return (NULL);
968*fa353a8fSclaudio 	if (ibuf_add_ibuf(authmsg, nonce) != 0)
969fde46d6eSreyk 		goto fail;
970fde46d6eSreyk 
971fde46d6eSreyk 	if ((hash_setkey(sa->sa_prf, ibuf_data(prfkey),
972fde46d6eSreyk 	    ibuf_size(prfkey))) == NULL)
973fde46d6eSreyk 		goto fail;
974fde46d6eSreyk 
975d0e3d0abStobhe 	/* require non-truncating hash */
976d0e3d0abStobhe 	if (hash_keylength(sa->sa_prf) != hash_length(sa->sa_prf))
977d0e3d0abStobhe 		goto fail;
978d0e3d0abStobhe 
97956c4e216Sclaudio 	if ((ptr = ibuf_reserve(authmsg, hash_keylength(sa->sa_prf))) == NULL)
980fde46d6eSreyk 		goto fail;
981fde46d6eSreyk 
982fde46d6eSreyk 	hash_init(sa->sa_prf);
983fde46d6eSreyk 	hash_update(sa->sa_prf, ibuf_data(id->id_buf), ibuf_size(id->id_buf));
984fde46d6eSreyk 	hash_final(sa->sa_prf, ptr, &tmplen);
985fde46d6eSreyk 
986fde46d6eSreyk 	if (tmplen != hash_length(sa->sa_prf))
987fde46d6eSreyk 		goto fail;
988fde46d6eSreyk 
989328746baSreyk 	log_debug("%s: %s auth data length %zu",
990fde46d6eSreyk 	    __func__, response ? "responder" : "initiator",
991fde46d6eSreyk 	    ibuf_size(authmsg));
992dca9e784Sclaudio 	print_hexbuf(authmsg);
993fde46d6eSreyk 
994fde46d6eSreyk 	return (authmsg);
995fde46d6eSreyk 
996fde46d6eSreyk  fail:
997be2b38f5Sclaudio 	ibuf_free(authmsg);
998fde46d6eSreyk 	return (NULL);
999fde46d6eSreyk }
1000fde46d6eSreyk 
1001fde46d6eSreyk int
1002fde46d6eSreyk ikev2_msg_authverify(struct iked *env, struct iked_sa *sa,
1003d09d3a7dSreyk     struct iked_auth *auth, uint8_t *buf, size_t len, struct ibuf *authmsg)
1004fde46d6eSreyk {
1005d09d3a7dSreyk 	uint8_t				*key, *psk = NULL;
1006fde46d6eSreyk 	ssize_t				 keylen;
1007fde46d6eSreyk 	struct iked_id			*id;
1008fde46d6eSreyk 	struct iked_dsa			*dsa = NULL;
1009fde46d6eSreyk 	int				 ret = -1;
1010d09d3a7dSreyk 	uint8_t				 keytype;
1011fde46d6eSreyk 
1012fde46d6eSreyk 	if (sa->sa_hdr.sh_initiator)
1013fde46d6eSreyk 		id = &sa->sa_rcert;
1014fde46d6eSreyk 	else
1015fde46d6eSreyk 		id = &sa->sa_icert;
1016fde46d6eSreyk 
1017fde46d6eSreyk 	if ((dsa = dsa_verify_new(auth->auth_method, sa->sa_prf)) == NULL) {
1018fde46d6eSreyk 		log_debug("%s: invalid auth method", __func__);
1019fde46d6eSreyk 		return (-1);
1020fde46d6eSreyk 	}
1021fde46d6eSreyk 
1022fde46d6eSreyk 	switch (auth->auth_method) {
1023fde46d6eSreyk 	case IKEV2_AUTH_SHARED_KEY_MIC:
1024fde46d6eSreyk 		if (!auth->auth_length) {
1025fde46d6eSreyk 			log_debug("%s: no pre-shared key found", __func__);
1026fde46d6eSreyk 			goto done;
1027fde46d6eSreyk 		}
1028fde46d6eSreyk 		if ((keylen = ikev2_psk(sa, auth->auth_data,
1029fde46d6eSreyk 		    auth->auth_length, &psk)) == -1) {
1030fde46d6eSreyk 			log_debug("%s: failed to get PSK", __func__);
1031fde46d6eSreyk 			goto done;
1032fde46d6eSreyk 		}
1033fde46d6eSreyk 		key = psk;
1034fde46d6eSreyk 		keytype = 0;
1035fde46d6eSreyk 		break;
1036fde46d6eSreyk 	default:
1037202133c5Sreyk 		if (!id->id_type || !ibuf_length(id->id_buf)) {
1038fde46d6eSreyk 			log_debug("%s: no cert found", __func__);
1039fde46d6eSreyk 			goto done;
1040fde46d6eSreyk 		}
1041fde46d6eSreyk 		key = ibuf_data(id->id_buf);
1042fde46d6eSreyk 		keylen = ibuf_size(id->id_buf);
1043fde46d6eSreyk 		keytype = id->id_type;
1044fde46d6eSreyk 		break;
1045fde46d6eSreyk 	}
1046fde46d6eSreyk 
1047328746baSreyk 	log_debug("%s: method %s keylen %zd type %s", __func__,
1048fde46d6eSreyk 	    print_map(auth->auth_method, ikev2_auth_map), keylen,
1049fde46d6eSreyk 	    print_map(id->id_type, ikev2_cert_map));
1050fde46d6eSreyk 
1051fde46d6eSreyk 	if (dsa_setkey(dsa, key, keylen, keytype) == NULL ||
105248b975e3Smarkus 	    dsa_init(dsa, buf, len) != 0 ||
1053fde46d6eSreyk 	    dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) {
1054fde46d6eSreyk 		log_debug("%s: failed to compute digital signature", __func__);
1055fde46d6eSreyk 		goto done;
1056fde46d6eSreyk 	}
1057fde46d6eSreyk 
1058fde46d6eSreyk 	if ((ret = dsa_verify_final(dsa, buf, len)) == 0) {
1059fde46d6eSreyk 		log_debug("%s: authentication successful", __func__);
1060fde46d6eSreyk 		sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS);
10614a986ab9Smarkus 		sa_stateflags(sa, IKED_REQ_AUTHVALID);
1062fde46d6eSreyk 	} else {
1063fde46d6eSreyk 		log_debug("%s: authentication failed", __func__);
1064fde46d6eSreyk 		sa_state(env, sa, IKEV2_STATE_AUTH_REQUEST);
1065fde46d6eSreyk 	}
1066fde46d6eSreyk 
1067fde46d6eSreyk  done:
1068fde46d6eSreyk 	free(psk);
1069fde46d6eSreyk 	dsa_free(dsa);
1070fde46d6eSreyk 
1071fde46d6eSreyk 	return (ret);
1072fde46d6eSreyk }
1073fde46d6eSreyk 
1074fde46d6eSreyk int
1075fde46d6eSreyk ikev2_msg_authsign(struct iked *env, struct iked_sa *sa,
1076fde46d6eSreyk     struct iked_auth *auth, struct ibuf *authmsg)
1077fde46d6eSreyk {
1078d09d3a7dSreyk 	uint8_t				*key, *psk = NULL;
10795e4d3a37Sreyk 	ssize_t				 keylen, siglen;
1080fde46d6eSreyk 	struct iked_hash		*prf = sa->sa_prf;
1081fde46d6eSreyk 	struct iked_id			*id;
1082fde46d6eSreyk 	struct iked_dsa			*dsa = NULL;
1083fde46d6eSreyk 	struct ibuf			*buf;
1084fde46d6eSreyk 	int				 ret = -1;
1085d09d3a7dSreyk 	uint8_t			 keytype;
1086fde46d6eSreyk 
1087fde46d6eSreyk 	if (sa->sa_hdr.sh_initiator)
1088fde46d6eSreyk 		id = &sa->sa_icert;
1089fde46d6eSreyk 	else
1090fde46d6eSreyk 		id = &sa->sa_rcert;
1091fde46d6eSreyk 
1092fde46d6eSreyk 	if ((dsa = dsa_sign_new(auth->auth_method, prf)) == NULL) {
1093fde46d6eSreyk 		log_debug("%s: invalid auth method", __func__);
1094fde46d6eSreyk 		return (-1);
1095fde46d6eSreyk 	}
1096fde46d6eSreyk 
1097fde46d6eSreyk 	switch (auth->auth_method) {
1098fde46d6eSreyk 	case IKEV2_AUTH_SHARED_KEY_MIC:
1099fde46d6eSreyk 		if (!auth->auth_length) {
1100fde46d6eSreyk 			log_debug("%s: no pre-shared key found", __func__);
1101fde46d6eSreyk 			goto done;
1102fde46d6eSreyk 		}
1103fde46d6eSreyk 		if ((keylen = ikev2_psk(sa, auth->auth_data,
1104fde46d6eSreyk 		    auth->auth_length, &psk)) == -1) {
1105fde46d6eSreyk 			log_debug("%s: failed to get PSK", __func__);
1106fde46d6eSreyk 			goto done;
1107fde46d6eSreyk 		}
1108fde46d6eSreyk 		key = psk;
1109fde46d6eSreyk 		keytype = 0;
1110fde46d6eSreyk 		break;
1111fde46d6eSreyk 	default:
1112fde46d6eSreyk 		if (id == NULL) {
1113fde46d6eSreyk 			log_debug("%s: no cert found", __func__);
1114fde46d6eSreyk 			goto done;
1115fde46d6eSreyk 		}
1116fde46d6eSreyk 		key = ibuf_data(id->id_buf);
1117fde46d6eSreyk 		keylen = ibuf_size(id->id_buf);
1118fde46d6eSreyk 		keytype = id->id_type;
1119fde46d6eSreyk 		break;
1120fde46d6eSreyk 	}
1121fde46d6eSreyk 
1122fde46d6eSreyk 	if (dsa_setkey(dsa, key, keylen, keytype) == NULL ||
112348b975e3Smarkus 	    dsa_init(dsa, NULL, 0) != 0 ||
1124fde46d6eSreyk 	    dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) {
1125fde46d6eSreyk 		log_debug("%s: failed to compute digital signature", __func__);
1126fde46d6eSreyk 		goto done;
1127fde46d6eSreyk 	}
1128fde46d6eSreyk 
1129be2b38f5Sclaudio 	ibuf_free(sa->sa_localauth.id_buf);
1130fde46d6eSreyk 	sa->sa_localauth.id_buf = NULL;
1131fde46d6eSreyk 
1132fde46d6eSreyk 	if ((buf = ibuf_new(NULL, dsa_length(dsa))) == NULL) {
1133fde46d6eSreyk 		log_debug("%s: failed to get auth buffer", __func__);
1134fde46d6eSreyk 		goto done;
1135fde46d6eSreyk 	}
1136fde46d6eSreyk 
11375e4d3a37Sreyk 	if ((siglen = dsa_sign_final(dsa,
11385e4d3a37Sreyk 	    ibuf_data(buf), ibuf_size(buf))) < 0) {
1139fde46d6eSreyk 		log_debug("%s: failed to create auth signature", __func__);
1140be2b38f5Sclaudio 		ibuf_free(buf);
1141fde46d6eSreyk 		goto done;
1142fde46d6eSreyk 	}
1143fde46d6eSreyk 
11445e4d3a37Sreyk 	if (ibuf_setsize(buf, siglen) < 0) {
11455e4d3a37Sreyk 		log_debug("%s: failed to set auth signature size to %zd",
11465e4d3a37Sreyk 		    __func__, siglen);
1147be2b38f5Sclaudio 		ibuf_free(buf);
11485e4d3a37Sreyk 		goto done;
11495e4d3a37Sreyk 	}
11505e4d3a37Sreyk 
1151fde46d6eSreyk 	sa->sa_localauth.id_type = auth->auth_method;
1152fde46d6eSreyk 	sa->sa_localauth.id_buf = buf;
1153fde46d6eSreyk 
1154fde46d6eSreyk 	ret = 0;
1155fde46d6eSreyk  done:
1156fde46d6eSreyk 	free(psk);
1157fde46d6eSreyk 	dsa_free(dsa);
1158fde46d6eSreyk 
1159fde46d6eSreyk 	return (ret);
1160fde46d6eSreyk }
1161ae494144Sreyk 
1162ae494144Sreyk int
1163ae494144Sreyk ikev2_msg_frompeer(struct iked_message *msg)
1164ae494144Sreyk {
1165ae494144Sreyk 	struct iked_sa		*sa = msg->msg_sa;
1166ae494144Sreyk 	struct ike_header	*hdr;
1167ae494144Sreyk 
116826d7dba1Sreyk 	msg = msg->msg_parent;
11691b0d4946Sreyk 
1170ae494144Sreyk 	if (sa == NULL ||
1171ae494144Sreyk 	    (hdr = ibuf_seek(msg->msg_data, 0, sizeof(*hdr))) == NULL)
1172ae494144Sreyk 		return (0);
1173ae494144Sreyk 
1174ae494144Sreyk 	if (!sa->sa_hdr.sh_initiator &&
1175ae494144Sreyk 	    (hdr->ike_flags & IKEV2_FLAG_INITIATOR))
1176ae494144Sreyk 		return (1);
1177ae494144Sreyk 	else if (sa->sa_hdr.sh_initiator &&
1178ae494144Sreyk 	    (hdr->ike_flags & IKEV2_FLAG_INITIATOR) == 0)
1179ae494144Sreyk 		return (1);
1180ae494144Sreyk 
1181ae494144Sreyk 	return (0);
1182ae494144Sreyk }
1183ae494144Sreyk 
1184ae494144Sreyk struct iked_socket *
118512c9fd31Sreyk ikev2_msg_getsocket(struct iked *env, int af, int natt)
1186ae494144Sreyk {
1187ae494144Sreyk 	switch (af) {
1188ae494144Sreyk 	case AF_INET:
118912c9fd31Sreyk 		return (env->sc_sock4[natt ? 1 : 0]);
1190ae494144Sreyk 	case AF_INET6:
119112c9fd31Sreyk 		return (env->sc_sock6[natt ? 1 : 0]);
1192ae494144Sreyk 	}
1193ae494144Sreyk 
1194ae494144Sreyk 	log_debug("%s: af socket %d not available", __func__, af);
1195ae494144Sreyk 	return (NULL);
1196ae494144Sreyk }
1197c45fd413Smikeb 
11986e264ad0Stobhe int
11996e264ad0Stobhe ikev2_msg_enqueue(struct iked *env, struct iked_msgqueue *queue,
12006e264ad0Stobhe     struct iked_message *msg, int timeout)
12016e264ad0Stobhe {
12026e264ad0Stobhe 	struct iked_msg_retransmit *mr;
12036e264ad0Stobhe 
12046e264ad0Stobhe 	if ((mr = ikev2_msg_lookup(env, queue, msg, msg->msg_exchange)) ==
12056e264ad0Stobhe 	    NULL) {
12066e264ad0Stobhe 		if ((mr = calloc(1, sizeof(*mr))) == NULL)
12076e264ad0Stobhe 			return (-1);
12086e264ad0Stobhe 		TAILQ_INIT(&mr->mrt_frags);
12096e264ad0Stobhe 		mr->mrt_tries = 0;
12106e264ad0Stobhe 
12116e264ad0Stobhe 		timer_set(env, &mr->mrt_timer, msg->msg_response ?
12126e264ad0Stobhe 		    ikev2_msg_response_timeout : ikev2_msg_retransmit_timeout,
12136e264ad0Stobhe 		    mr);
12146e264ad0Stobhe 		timer_add(env, &mr->mrt_timer, timeout);
12156e264ad0Stobhe 
12166e264ad0Stobhe 		TAILQ_INSERT_TAIL(queue, mr, mrt_entry);
12176e264ad0Stobhe 	}
12186e264ad0Stobhe 
12196e264ad0Stobhe 	TAILQ_INSERT_TAIL(&mr->mrt_frags, msg, msg_entry);
12206e264ad0Stobhe 
12216e264ad0Stobhe 	return 0;
12226e264ad0Stobhe }
12236e264ad0Stobhe 
1224c45fd413Smikeb void
1225c45fd413Smikeb ikev2_msg_prevail(struct iked *env, struct iked_msgqueue *queue,
1226c45fd413Smikeb     struct iked_message *msg)
1227c45fd413Smikeb {
12286e264ad0Stobhe 	struct iked_msg_retransmit	*mr, *mrtmp;
1229c45fd413Smikeb 
12306e264ad0Stobhe 	TAILQ_FOREACH_SAFE(mr, queue, mrt_entry, mrtmp) {
12316e264ad0Stobhe 		if (TAILQ_FIRST(&mr->mrt_frags)->msg_msgid < msg->msg_msgid)
12326e264ad0Stobhe 			ikev2_msg_dispose(env, queue, mr);
1233c45fd413Smikeb 	}
1234c45fd413Smikeb }
1235c45fd413Smikeb 
1236c45fd413Smikeb void
1237c45fd413Smikeb ikev2_msg_dispose(struct iked *env, struct iked_msgqueue *queue,
12386e264ad0Stobhe     struct iked_msg_retransmit *mr)
1239c45fd413Smikeb {
12406e264ad0Stobhe 	struct iked_message	*m;
12416e264ad0Stobhe 
12426e264ad0Stobhe 	while ((m = TAILQ_FIRST(&mr->mrt_frags)) != NULL) {
12436e264ad0Stobhe 		TAILQ_REMOVE(&mr->mrt_frags, m, msg_entry);
12446e264ad0Stobhe 		ikev2_msg_cleanup(env, m);
12456e264ad0Stobhe 		free(m);
12466e264ad0Stobhe 	}
12476e264ad0Stobhe 
12486e264ad0Stobhe 	timer_del(env, &mr->mrt_timer);
12496e264ad0Stobhe 	TAILQ_REMOVE(queue, mr, mrt_entry);
12506e264ad0Stobhe 	free(mr);
1251c45fd413Smikeb }
1252c45fd413Smikeb 
1253c45fd413Smikeb void
1254c45fd413Smikeb ikev2_msg_flushqueue(struct iked *env, struct iked_msgqueue *queue)
1255c45fd413Smikeb {
12566e264ad0Stobhe 	struct iked_msg_retransmit	*mr = NULL;
1257c45fd413Smikeb 
12586e264ad0Stobhe 	while ((mr = TAILQ_FIRST(queue)) != NULL)
12596e264ad0Stobhe 		ikev2_msg_dispose(env, queue, mr);
1260c45fd413Smikeb }
1261c45fd413Smikeb 
12626e264ad0Stobhe struct iked_msg_retransmit *
126356d51042Smikeb ikev2_msg_lookup(struct iked *env, struct iked_msgqueue *queue,
12646e264ad0Stobhe     struct iked_message *msg, uint8_t exchange)
1265c45fd413Smikeb {
12666e264ad0Stobhe 	struct iked_msg_retransmit	*mr = NULL;
1267c45fd413Smikeb 
12686e264ad0Stobhe 	TAILQ_FOREACH(mr, queue, mrt_entry) {
12696e264ad0Stobhe 		if (TAILQ_FIRST(&mr->mrt_frags)->msg_msgid ==
12706e264ad0Stobhe 		    msg->msg_msgid &&
12716e264ad0Stobhe 		    TAILQ_FIRST(&mr->mrt_frags)->msg_exchange == exchange)
1272c45fd413Smikeb 			break;
1273c45fd413Smikeb 	}
1274c45fd413Smikeb 
12756e264ad0Stobhe 	return (mr);
127662cdfd0dStobhe }
127762cdfd0dStobhe 
1278c45fd413Smikeb int
1279c45fd413Smikeb ikev2_msg_retransmit_response(struct iked *env, struct iked_sa *sa,
12802117af45Stobhe     struct iked_message *msg, struct ike_header *hdr)
1281c45fd413Smikeb {
12826e264ad0Stobhe 	struct iked_msg_retransmit	*mr = NULL;
12836e264ad0Stobhe 	struct iked_message		*m = NULL;
12846e264ad0Stobhe 
12852117af45Stobhe 	if ((mr = ikev2_msg_lookup(env, &sa->sa_responses, msg,
12862117af45Stobhe 	    hdr->ike_exchange)) == NULL)
1287f336206eStobhe 		return (-2);	/* not found */
12886e264ad0Stobhe 
12892117af45Stobhe 	if (hdr->ike_nextpayload == IKEV2_PAYLOAD_SKF) {
12902117af45Stobhe 		/* only retransmit for fragment number one */
12912117af45Stobhe 		if (ikev2_pld_parse_quick(env, hdr, msg,
12922117af45Stobhe 		    msg->msg_offset) != 0 || msg->msg_frag_num != 1) {
12932117af45Stobhe 			log_debug("%s: ignoring fragment", SPI_SA(sa, __func__));
12942117af45Stobhe 			return (0);
12952117af45Stobhe 		}
12962117af45Stobhe 		log_debug("%s: first fragment", SPI_SA(sa, __func__));
12972117af45Stobhe 	}
12982117af45Stobhe 
12996e264ad0Stobhe 	TAILQ_FOREACH(m, &mr->mrt_frags, msg_entry) {
13006e264ad0Stobhe 		if (sendtofrom(m->msg_fd, ibuf_data(m->msg_data),
13016e264ad0Stobhe 		    ibuf_size(m->msg_data), 0,
13026e264ad0Stobhe 		    (struct sockaddr *)&m->msg_peer, m->msg_peerlen,
13036e264ad0Stobhe 		    (struct sockaddr *)&m->msg_local, m->msg_locallen) == -1) {
13045ec2ede8Svgross 			log_warn("%s: sendtofrom", __func__);
1305b41cc0c8Stobhe 			ikestat_inc(env, ikes_msg_send_failures);
1306c45fd413Smikeb 			return (-1);
1307c45fd413Smikeb 		}
1308f3610affStobhe 		log_info("%sretransmit %s res %u local %s peer %s",
1309f3610affStobhe 		    SPI_SA(sa, NULL),
13102117af45Stobhe 		    print_map(hdr->ike_exchange, ikev2_exchange_map),
13116e264ad0Stobhe 		    m->msg_msgid,
131214e2a040Stb 		    print_addr(&m->msg_local),
131314e2a040Stb 		    print_addr(&m->msg_peer));
13146e264ad0Stobhe 	}
1315c45fd413Smikeb 
13166e264ad0Stobhe 	timer_add(env, &mr->mrt_timer, IKED_RESPONSE_TIMEOUT);
1317b41cc0c8Stobhe 	ikestat_inc(env, ikes_retransmit_response);
1318c45fd413Smikeb 	return (0);
1319c45fd413Smikeb }
1320c45fd413Smikeb 
1321c45fd413Smikeb void
1322c45fd413Smikeb ikev2_msg_response_timeout(struct iked *env, void *arg)
1323c45fd413Smikeb {
13246e264ad0Stobhe 	struct iked_msg_retransmit	*mr = arg;
13256e264ad0Stobhe 	struct iked_sa		*sa;
1326c45fd413Smikeb 
13276e264ad0Stobhe 	sa = TAILQ_FIRST(&mr->mrt_frags)->msg_sa;
13286e264ad0Stobhe 	ikev2_msg_dispose(env, &sa->sa_responses, mr);
1329c45fd413Smikeb }
1330c45fd413Smikeb 
1331c45fd413Smikeb void
1332c45fd413Smikeb ikev2_msg_retransmit_timeout(struct iked *env, void *arg)
1333c45fd413Smikeb {
13346e264ad0Stobhe 	struct iked_msg_retransmit *mr = arg;
13356e264ad0Stobhe 	struct iked_message	*msg = TAILQ_FIRST(&mr->mrt_frags);
1336c45fd413Smikeb 	struct iked_sa		*sa = msg->msg_sa;
1337c45fd413Smikeb 
13386e264ad0Stobhe 	if (mr->mrt_tries < IKED_RETRANSMIT_TRIES) {
13396e264ad0Stobhe 		TAILQ_FOREACH(msg, &mr->mrt_frags, msg_entry) {
13405ec2ede8Svgross 			if (sendtofrom(msg->msg_fd, ibuf_data(msg->msg_data),
1341c45fd413Smikeb 			    ibuf_size(msg->msg_data), 0,
13425ec2ede8Svgross 			    (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen,
13435ec2ede8Svgross 			    (struct sockaddr *)&msg->msg_local,
13445ec2ede8Svgross 			    msg->msg_locallen) == -1) {
13455ec2ede8Svgross 				log_warn("%s: sendtofrom", __func__);
134684a9a21bStobhe 				ikev2_ike_sa_setreason(sa, "retransmit failed");
1347c45fd413Smikeb 				sa_free(env, sa);
1348b41cc0c8Stobhe 				ikestat_inc(env, ikes_msg_send_failures);
1349c45fd413Smikeb 				return;
1350c45fd413Smikeb 			}
13516e264ad0Stobhe 			log_info("%sretransmit %d %s req %u peer %s "
13526e264ad0Stobhe 			    "local %s", SPI_SA(sa, NULL), mr->mrt_tries + 1,
1353f3610affStobhe 			    print_map(msg->msg_exchange, ikev2_exchange_map),
1354f3610affStobhe 			    msg->msg_msgid,
135514e2a040Stb 			    print_addr(&msg->msg_peer),
135614e2a040Stb 			    print_addr(&msg->msg_local));
13576e264ad0Stobhe 		}
13586e264ad0Stobhe 		/* Exponential timeout */
13596e264ad0Stobhe 		timer_add(env, &mr->mrt_timer,
13606e264ad0Stobhe 		    IKED_RETRANSMIT_TIMEOUT * (2 << (mr->mrt_tries++)));
1361b41cc0c8Stobhe 		ikestat_inc(env, ikes_retransmit_request);
13628d81c769Smikeb 	} else {
1363f3610affStobhe 		log_debug("%s: retransmit limit reached for req %u",
13647f7372eaSmarkus 		    __func__, msg->msg_msgid);
136584a9a21bStobhe 		ikev2_ike_sa_setreason(sa, "retransmit limit reached");
1366b41cc0c8Stobhe 		ikestat_inc(env, ikes_retransmit_limit);
1367c45fd413Smikeb 		sa_free(env, sa);
1368c45fd413Smikeb 	}
13698d81c769Smikeb }
1370