1*2117af45Stobhe /* $OpenBSD: ikev2_msg.c,v 1.101 2024/03/02 16:16:07 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
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
ikev2_msg_cb(int fd,short event,void * arg)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
ikev1_recv(struct iked * env,struct iked_message * msg)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 *
ikev2_msg_init(struct iked * env,struct iked_message * msg,struct sockaddr_storage * peer,socklen_t peerlen,struct sockaddr_storage * local,socklen_t locallen,int response)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 *
ikev2_msg_copy(struct iked * env,struct iked_message * msg)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
ikev2_msg_cleanup(struct iked * env,struct iked_message * msg)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);
20625b39a47Stobhe free(msg->msg_eap.eam_user);
20752b3354cStobhe free(msg->msg_cp_addr);
20852b3354cStobhe free(msg->msg_cp_addr6);
2099ef39cf4Stobhe free(msg->msg_cp_dns);
21026d7dba1Sreyk
2113642bd88Smikeb msg->msg_nonce = NULL;
2123642bd88Smikeb msg->msg_ke = NULL;
2133642bd88Smikeb msg->msg_auth.id_buf = NULL;
2149ce164edStobhe msg->msg_peerid.id_buf = NULL;
215e3f5cf2eSpatrick msg->msg_localid.id_buf = NULL;
2163642bd88Smikeb msg->msg_cert.id_buf = NULL;
217a30a01d6Stobhe for (i = 0; i < IKED_SCERT_MAX; i++)
218a30a01d6Stobhe msg->msg_scert[i].id_buf = NULL;
219d4bcf9ebSreyk msg->msg_cookie = NULL;
220c0b327e6Spatrick msg->msg_cookie2 = NULL;
221da56c325Stobhe msg->msg_del_buf = NULL;
22225b39a47Stobhe msg->msg_eap.eam_user = NULL;
22352b3354cStobhe msg->msg_cp_addr = NULL;
22452b3354cStobhe msg->msg_cp_addr6 = NULL;
2259ef39cf4Stobhe msg->msg_cp_dns = NULL;
226d4bcf9ebSreyk
22726d7dba1Sreyk config_free_proposals(&msg->msg_proposals, 0);
2283e395450Stobhe while ((cr = SIMPLEQ_FIRST(&msg->msg_certreqs))) {
229be2b38f5Sclaudio ibuf_free(cr->cr_data);
2303e395450Stobhe SIMPLEQ_REMOVE_HEAD(&msg->msg_certreqs, cr_entry);
231eb2389caStobhe free(cr);
232eb2389caStobhe }
23326d7dba1Sreyk }
23426d7dba1Sreyk
235763023d6Sreyk if (msg->msg_data != NULL) {
236be2b38f5Sclaudio ibuf_free(msg->msg_data);
237763023d6Sreyk msg->msg_data = NULL;
238763023d6Sreyk }
239763023d6Sreyk }
240763023d6Sreyk
241fde46d6eSreyk int
ikev2_msg_valid_ike_sa(struct iked * env,struct ike_header * oldhdr,struct iked_message * msg)242fde46d6eSreyk ikev2_msg_valid_ike_sa(struct iked *env, struct ike_header *oldhdr,
243fde46d6eSreyk struct iked_message *msg)
244fde46d6eSreyk {
2456e1880a3Smarkus if (msg->msg_sa != NULL && msg->msg_policy != NULL) {
24669aac1baSmikeb if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSED)
24769aac1baSmikeb return (-1);
2486e1880a3Smarkus /*
2496e1880a3Smarkus * Only permit informational requests from initiator
2506e1880a3Smarkus * on closing SAs (for DELETE).
2516e1880a3Smarkus */
2526e1880a3Smarkus if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSING) {
2536e1880a3Smarkus if (((oldhdr->ike_flags &
2546e1880a3Smarkus (IKEV2_FLAG_INITIATOR|IKEV2_FLAG_RESPONSE)) ==
2556e1880a3Smarkus IKEV2_FLAG_INITIATOR) &&
2566e1880a3Smarkus (oldhdr->ike_exchange ==
2576e1880a3Smarkus IKEV2_EXCHANGE_INFORMATIONAL))
258fde46d6eSreyk return (0);
2596e1880a3Smarkus return (-1);
2606e1880a3Smarkus }
2616e1880a3Smarkus return (0);
2626e1880a3Smarkus }
263fde46d6eSreyk
264fde46d6eSreyk /* Always fail */
265fde46d6eSreyk return (-1);
266fde46d6eSreyk }
267fde46d6eSreyk
268fde46d6eSreyk int
ikev2_msg_send(struct iked * env,struct iked_message * msg)269d9c13a0aSmikeb ikev2_msg_send(struct iked *env, struct iked_message *msg)
270fde46d6eSreyk {
271c45fd413Smikeb struct iked_sa *sa = msg->msg_sa;
272fde46d6eSreyk struct ibuf *buf = msg->msg_data;
273d09d3a7dSreyk uint32_t natt = 0x00000000;
27412c9fd31Sreyk int isnatt = 0;
275d09d3a7dSreyk uint8_t exchange, flags;
276fde46d6eSreyk struct ike_header *hdr;
277c45fd413Smikeb struct iked_message *m;
278fde46d6eSreyk
279fde46d6eSreyk if (buf == NULL || (hdr = ibuf_seek(msg->msg_data,
280fde46d6eSreyk msg->msg_offset, sizeof(*hdr))) == NULL)
281fde46d6eSreyk return (-1);
282fde46d6eSreyk
2830804246aStobhe isnatt = (msg->msg_natt || (sa && sa->sa_natt));
28412c9fd31Sreyk
285670a137dSmikeb exchange = hdr->ike_exchange;
286670a137dSmikeb flags = hdr->ike_flags;
28753684dccStobhe logit(exchange == IKEV2_EXCHANGE_INFORMATIONAL ? LOG_DEBUG : LOG_INFO,
2880d262a08Stobhe "%ssend %s %s %u peer %s local %s, %zu bytes%s",
289ecea226bStobhe SPI_IH(hdr),
290670a137dSmikeb print_map(exchange, ikev2_exchange_map),
291ecea226bStobhe (flags & IKEV2_FLAG_RESPONSE) ? "res" : "req",
2927f7372eaSmarkus betoh32(hdr->ike_msgid),
29314e2a040Stb print_addr(&msg->msg_peer),
29414e2a040Stb print_addr(&msg->msg_local),
295eef6c82aSclaudio ibuf_size(buf), isnatt ? ", NAT-T" : "");
296fde46d6eSreyk
29712c9fd31Sreyk if (isnatt) {
29837e80bc6Sclaudio struct ibuf *new;
29937e80bc6Sclaudio if ((new = ibuf_new(&natt, sizeof(natt))) == NULL) {
300fde46d6eSreyk log_debug("%s: failed to set NAT-T", __func__);
301fde46d6eSreyk return (-1);
302fde46d6eSreyk }
303f6f27851Sclaudio if (ibuf_add_buf(new, buf) == -1) {
30437e80bc6Sclaudio ibuf_free(new);
30537e80bc6Sclaudio log_debug("%s: failed to set NAT-T", __func__);
30637e80bc6Sclaudio return (-1);
30737e80bc6Sclaudio }
30837e80bc6Sclaudio ibuf_free(buf);
30937e80bc6Sclaudio buf = msg->msg_data = new;
310fde46d6eSreyk }
311d9c13a0aSmikeb
3125ec2ede8Svgross if (sendtofrom(msg->msg_fd, ibuf_data(buf), ibuf_size(buf), 0,
3135ec2ede8Svgross (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen,
3145ec2ede8Svgross (struct sockaddr *)&msg->msg_local, msg->msg_locallen) == -1) {
3150804246aStobhe log_warn("%s: sendtofrom", __func__);
3160804246aStobhe if (sa != NULL && errno == EADDRNOTAVAIL) {
3170804246aStobhe sa_state(env, sa, IKEV2_STATE_CLOSING);
3180804246aStobhe timer_del(env, &sa->sa_timer);
3190804246aStobhe timer_set(env, &sa->sa_timer,
3200804246aStobhe ikev2_ike_sa_timeout, sa);
3210804246aStobhe timer_add(env, &sa->sa_timer,
322a6fc7f59Shenning IKED_IKE_SA_DELETE_TIMEOUT);
323a6fc7f59Shenning }
324b41cc0c8Stobhe ikestat_inc(env, ikes_msg_send_failures);
325b41cc0c8Stobhe } else
326b41cc0c8Stobhe ikestat_inc(env, ikes_msg_sent);
327fde46d6eSreyk
3280804246aStobhe if (sa == NULL)
329c45fd413Smikeb return (0);
330c45fd413Smikeb
331c45fd413Smikeb if ((m = ikev2_msg_copy(env, msg)) == NULL) {
332c45fd413Smikeb log_debug("%s: failed to copy a message", __func__);
333c45fd413Smikeb return (-1);
334c45fd413Smikeb }
335670a137dSmikeb m->msg_exchange = exchange;
336c45fd413Smikeb
337670a137dSmikeb if (flags & IKEV2_FLAG_RESPONSE) {
3386e264ad0Stobhe if (ikev2_msg_enqueue(env, &sa->sa_responses, m,
3396e264ad0Stobhe IKED_RESPONSE_TIMEOUT) != 0) {
3406e264ad0Stobhe ikev2_msg_cleanup(env, m);
3416e264ad0Stobhe free(m);
3426e264ad0Stobhe return (-1);
3436e264ad0Stobhe }
344c45fd413Smikeb } else {
3456e264ad0Stobhe if (ikev2_msg_enqueue(env, &sa->sa_requests, m,
3466e264ad0Stobhe IKED_RETRANSMIT_TIMEOUT) != 0) {
3476e264ad0Stobhe ikev2_msg_cleanup(env, m);
3486e264ad0Stobhe free(m);
3496e264ad0Stobhe return (-1);
3506e264ad0Stobhe }
351c45fd413Smikeb }
352c45fd413Smikeb
353fde46d6eSreyk return (0);
354fde46d6eSreyk }
355fde46d6eSreyk
356d09d3a7dSreyk uint32_t
ikev2_msg_id(struct iked * env,struct iked_sa * sa)357c45fd413Smikeb ikev2_msg_id(struct iked *env, struct iked_sa *sa)
358fde46d6eSreyk {
359d09d3a7dSreyk uint32_t id = sa->sa_reqid;
360fde46d6eSreyk
36110650a52Smikeb if (++sa->sa_reqid == UINT32_MAX) {
362fde46d6eSreyk /* XXX we should close and renegotiate the connection now */
363fde46d6eSreyk log_debug("%s: IKEv2 message sequence overflow", __func__);
364fde46d6eSreyk }
36510650a52Smikeb return (id);
366fde46d6eSreyk }
367fde46d6eSreyk
36815863c3aStobhe /*
36915863c3aStobhe * Calculate the final sizes of the IKEv2 header and the encrypted payload
37015863c3aStobhe * header. This must be done before encryption to make sure the correct
37115863c3aStobhe * headers are authenticated.
37215863c3aStobhe */
37315863c3aStobhe int
ikev2_msg_encrypt_prepare(struct iked_sa * sa,struct ikev2_payload * pld,struct ibuf * buf,struct ibuf * e,struct ike_header * hdr,uint8_t firstpayload,int fragmentation)37415863c3aStobhe ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld,
37515863c3aStobhe struct ibuf *buf, struct ibuf *e, struct ike_header *hdr,
37615863c3aStobhe uint8_t firstpayload, int fragmentation)
37715863c3aStobhe {
37815863c3aStobhe size_t len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen;
37915863c3aStobhe
38015863c3aStobhe if (sa == NULL ||
38115863c3aStobhe sa->sa_encr == NULL ||
38215863c3aStobhe sa->sa_integr == NULL) {
38315863c3aStobhe log_debug("%s: invalid SA", __func__);
38415863c3aStobhe return (-1);
38515863c3aStobhe }
38615863c3aStobhe
38715863c3aStobhe len = ibuf_size(e);
38815863c3aStobhe blocklen = cipher_length(sa->sa_encr);
38915863c3aStobhe integrlen = hash_length(sa->sa_integr);
39015863c3aStobhe ivlen = cipher_ivlength(sa->sa_encr);
39115863c3aStobhe encrlen = roundup(len + 1, blocklen);
39215863c3aStobhe outlen = cipher_outlength(sa->sa_encr, encrlen);
39315863c3aStobhe pldlen = ivlen + outlen + integrlen;
39415863c3aStobhe
39515863c3aStobhe if (ikev2_next_payload(pld,
39615863c3aStobhe pldlen + (fragmentation ? sizeof(struct ikev2_frag_payload) : 0),
39715863c3aStobhe firstpayload) == -1)
39815863c3aStobhe return (-1);
39915863c3aStobhe if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1)
40015863c3aStobhe return (-1);
40115863c3aStobhe
40215863c3aStobhe return (0);
40315863c3aStobhe }
40415863c3aStobhe
405fde46d6eSreyk struct ibuf *
ikev2_msg_encrypt(struct iked * env,struct iked_sa * sa,struct ibuf * src,struct ibuf * aad)40615863c3aStobhe ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src,
40715863c3aStobhe struct ibuf *aad)
408fde46d6eSreyk {
4099ebe96b0Stobhe size_t len, encrlen, integrlen, blocklen,
410fde46d6eSreyk outlen;
411d09d3a7dSreyk uint8_t *buf, pad = 0, *ptr;
41288d75aadSreyk struct ibuf *encr, *dst = NULL, *out = NULL;
413fde46d6eSreyk
414fde46d6eSreyk buf = ibuf_data(src);
415fde46d6eSreyk len = ibuf_size(src);
416fde46d6eSreyk
417328746baSreyk log_debug("%s: decrypted length %zu", __func__, len);
418fde46d6eSreyk print_hex(buf, 0, len);
419fde46d6eSreyk
420fde46d6eSreyk if (sa == NULL ||
421fde46d6eSreyk sa->sa_encr == NULL ||
422fde46d6eSreyk sa->sa_integr == NULL) {
423fde46d6eSreyk log_debug("%s: invalid SA", __func__);
424fde46d6eSreyk goto done;
425fde46d6eSreyk }
426fde46d6eSreyk
42788d75aadSreyk if (sa->sa_hdr.sh_initiator)
428fde46d6eSreyk encr = sa->sa_key_iencr;
42988d75aadSreyk else
430fde46d6eSreyk encr = sa->sa_key_rencr;
431fde46d6eSreyk
432fde46d6eSreyk blocklen = cipher_length(sa->sa_encr);
433fde46d6eSreyk integrlen = hash_length(sa->sa_integr);
434fde46d6eSreyk encrlen = roundup(len + sizeof(pad), blocklen);
435fde46d6eSreyk pad = encrlen - (len + sizeof(pad));
436fde46d6eSreyk
437fde46d6eSreyk /*
438fde46d6eSreyk * Pad the payload and encrypt it
439fde46d6eSreyk */
440fde46d6eSreyk if (pad) {
44156c4e216Sclaudio if ((ptr = ibuf_reserve(src, pad)) == NULL)
442fde46d6eSreyk goto done;
443fde46d6eSreyk arc4random_buf(ptr, pad);
444fde46d6eSreyk }
445fde46d6eSreyk if (ibuf_add(src, &pad, sizeof(pad)) != 0)
446fde46d6eSreyk goto done;
447fde46d6eSreyk
448328746baSreyk log_debug("%s: padded length %zu", __func__, ibuf_size(src));
449dca9e784Sclaudio print_hexbuf(src);
450fde46d6eSreyk
451eef6c82aSclaudio cipher_setkey(sa->sa_encr, ibuf_data(encr), ibuf_size(encr));
45288d75aadSreyk cipher_setiv(sa->sa_encr, NULL, 0); /* XXX ivlen */
45381b8fecaStobhe if (cipher_init_encrypt(sa->sa_encr) == -1) {
45481b8fecaStobhe log_info("%s: error initiating cipher.", __func__);
45581b8fecaStobhe goto done;
45681b8fecaStobhe }
457fde46d6eSreyk
458fde46d6eSreyk if ((dst = ibuf_dup(sa->sa_encr->encr_iv)) == NULL)
459fde46d6eSreyk goto done;
460fde46d6eSreyk
461fde46d6eSreyk if ((out = ibuf_new(NULL,
462fde46d6eSreyk cipher_outlength(sa->sa_encr, encrlen))) == NULL)
463fde46d6eSreyk goto done;
464fde46d6eSreyk
465fde46d6eSreyk outlen = ibuf_size(out);
46615863c3aStobhe
46715863c3aStobhe /* Add AAD for AEAD ciphers */
46815863c3aStobhe if (sa->sa_integr->hash_isaead)
469eef6c82aSclaudio cipher_aad(sa->sa_encr, ibuf_data(aad), ibuf_size(aad),
470eef6c82aSclaudio &outlen);
47115863c3aStobhe
47281b8fecaStobhe if (cipher_update(sa->sa_encr, ibuf_data(src), encrlen,
47381b8fecaStobhe ibuf_data(out), &outlen) == -1) {
47481b8fecaStobhe log_info("%s: error updating cipher.", __func__);
47581b8fecaStobhe goto done;
47681b8fecaStobhe }
477fde46d6eSreyk
47815863c3aStobhe if (cipher_final(sa->sa_encr) == -1) {
47915863c3aStobhe log_info("%s: encryption failed.", __func__);
48015863c3aStobhe goto done;
48115863c3aStobhe }
48215863c3aStobhe
483fde46d6eSreyk if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0)
484fde46d6eSreyk goto done;
485fde46d6eSreyk
48656c4e216Sclaudio if ((ptr = ibuf_reserve(dst, integrlen)) == NULL)
487fde46d6eSreyk goto done;
4888fbd7fcbSdoug explicit_bzero(ptr, integrlen);
489fde46d6eSreyk
490328746baSreyk log_debug("%s: length %zu, padding %d, output length %zu",
491fde46d6eSreyk __func__, len + sizeof(pad), pad, ibuf_size(dst));
492dca9e784Sclaudio print_hexbuf(dst);
493fde46d6eSreyk
494be2b38f5Sclaudio ibuf_free(src);
495be2b38f5Sclaudio ibuf_free(out);
496fde46d6eSreyk return (dst);
497fde46d6eSreyk done:
498be2b38f5Sclaudio ibuf_free(src);
499be2b38f5Sclaudio ibuf_free(out);
500be2b38f5Sclaudio ibuf_free(dst);
501fde46d6eSreyk return (NULL);
502fde46d6eSreyk }
503fde46d6eSreyk
504fde46d6eSreyk int
ikev2_msg_integr(struct iked * env,struct iked_sa * sa,struct ibuf * src)505fde46d6eSreyk ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src)
506fde46d6eSreyk {
507fde46d6eSreyk int ret = -1;
508fde46d6eSreyk size_t integrlen, tmplen;
50988d75aadSreyk struct ibuf *integr, *tmp = NULL;
510d09d3a7dSreyk uint8_t *ptr;
511fde46d6eSreyk
512328746baSreyk log_debug("%s: message length %zu", __func__, ibuf_size(src));
513dca9e784Sclaudio print_hexbuf(src);
514fde46d6eSreyk
515fde46d6eSreyk if (sa == NULL ||
51615863c3aStobhe sa->sa_encr == NULL ||
517fde46d6eSreyk sa->sa_integr == NULL) {
518fde46d6eSreyk log_debug("%s: invalid SA", __func__);
519fde46d6eSreyk return (-1);
520fde46d6eSreyk }
521fde46d6eSreyk
522fde46d6eSreyk integrlen = hash_length(sa->sa_integr);
523328746baSreyk log_debug("%s: integrity checksum length %zu", __func__,
524fde46d6eSreyk integrlen);
525fde46d6eSreyk
526fde46d6eSreyk /*
527fde46d6eSreyk * Validate packet checksum
528fde46d6eSreyk */
529fde46d6eSreyk if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
530fde46d6eSreyk goto done;
531fde46d6eSreyk
53215863c3aStobhe if (!sa->sa_integr->hash_isaead) {
53315863c3aStobhe if (sa->sa_hdr.sh_initiator)
53415863c3aStobhe integr = sa->sa_key_iauth;
53515863c3aStobhe else
53615863c3aStobhe integr = sa->sa_key_rauth;
53715863c3aStobhe
53815863c3aStobhe hash_setkey(sa->sa_integr, ibuf_data(integr),
53915863c3aStobhe ibuf_size(integr));
540fde46d6eSreyk hash_init(sa->sa_integr);
541fde46d6eSreyk hash_update(sa->sa_integr, ibuf_data(src),
542fde46d6eSreyk ibuf_size(src) - integrlen);
543fde46d6eSreyk hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
544fde46d6eSreyk
545fde46d6eSreyk if (tmplen != integrlen) {
546fde46d6eSreyk log_debug("%s: hash failure", __func__);
547fde46d6eSreyk goto done;
548fde46d6eSreyk }
54915863c3aStobhe } else {
55015863c3aStobhe /* Append AEAD tag */
55115863c3aStobhe if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp)))
55215863c3aStobhe goto done;
55315863c3aStobhe }
554fde46d6eSreyk
555fde46d6eSreyk if ((ptr = ibuf_seek(src,
556fde46d6eSreyk ibuf_size(src) - integrlen, integrlen)) == NULL)
557fde46d6eSreyk goto done;
55815863c3aStobhe memcpy(ptr, ibuf_data(tmp), integrlen);
559fde46d6eSreyk
560dca9e784Sclaudio print_hexbuf(tmp);
561fde46d6eSreyk
562fde46d6eSreyk ret = 0;
563fde46d6eSreyk done:
564be2b38f5Sclaudio ibuf_free(tmp);
565fde46d6eSreyk
566fde46d6eSreyk return (ret);
567fde46d6eSreyk }
568fde46d6eSreyk
569fde46d6eSreyk struct ibuf *
ikev2_msg_decrypt(struct iked * env,struct iked_sa * sa,struct ibuf * msg,struct ibuf * src)570fde46d6eSreyk ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa,
571fde46d6eSreyk struct ibuf *msg, struct ibuf *src)
572fde46d6eSreyk {
573e0696045Sreyk ssize_t ivlen, encrlen, integrlen, blocklen,
574fde46d6eSreyk outlen, tmplen;
57515863c3aStobhe uint8_t pad = 0, *ptr, *integrdata;
576fde46d6eSreyk struct ibuf *integr, *encr, *tmp = NULL, *out = NULL;
577fde46d6eSreyk off_t ivoff, encroff, integroff;
578fde46d6eSreyk
579fde46d6eSreyk if (sa == NULL ||
580fde46d6eSreyk sa->sa_encr == NULL ||
581fde46d6eSreyk sa->sa_integr == NULL) {
582fde46d6eSreyk log_debug("%s: invalid SA", __func__);
583dca9e784Sclaudio print_hexbuf(src);
584fde46d6eSreyk goto done;
585fde46d6eSreyk }
586fde46d6eSreyk
587fde46d6eSreyk if (!sa->sa_hdr.sh_initiator) {
588fde46d6eSreyk encr = sa->sa_key_iencr;
589fde46d6eSreyk integr = sa->sa_key_iauth;
590fde46d6eSreyk } else {
591fde46d6eSreyk encr = sa->sa_key_rencr;
592fde46d6eSreyk integr = sa->sa_key_rauth;
593fde46d6eSreyk }
594fde46d6eSreyk
595fde46d6eSreyk blocklen = cipher_length(sa->sa_encr);
596fde46d6eSreyk ivlen = cipher_ivlength(sa->sa_encr);
597fde46d6eSreyk ivoff = 0;
598fde46d6eSreyk integrlen = hash_length(sa->sa_integr);
599fde46d6eSreyk integroff = ibuf_size(src) - integrlen;
600fde46d6eSreyk encroff = ivlen;
601fde46d6eSreyk encrlen = ibuf_size(src) - integrlen - ivlen;
602fde46d6eSreyk
603e0696045Sreyk if (encrlen < 0 || integroff < 0) {
604e0696045Sreyk log_debug("%s: invalid integrity value", __func__);
605e0696045Sreyk goto done;
606e0696045Sreyk }
607e0696045Sreyk
608328746baSreyk log_debug("%s: IV length %zd", __func__, ivlen);
609fde46d6eSreyk print_hex(ibuf_data(src), 0, ivlen);
610328746baSreyk log_debug("%s: encrypted payload length %zd", __func__, encrlen);
611fde46d6eSreyk print_hex(ibuf_data(src), encroff, encrlen);
612328746baSreyk log_debug("%s: integrity checksum length %zd", __func__, integrlen);
613fde46d6eSreyk print_hex(ibuf_data(src), integroff, integrlen);
614fde46d6eSreyk
615fde46d6eSreyk /*
616fde46d6eSreyk * Validate packet checksum
617fde46d6eSreyk */
61815863c3aStobhe if (!sa->sa_integr->hash_isaead) {
619d0e3d0abStobhe if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL)
620fde46d6eSreyk goto done;
621fde46d6eSreyk
622a699afcaSclaudio hash_setkey(sa->sa_integr, ibuf_data(integr),
623eef6c82aSclaudio ibuf_size(integr));
624fde46d6eSreyk hash_init(sa->sa_integr);
625fde46d6eSreyk hash_update(sa->sa_integr, ibuf_data(msg),
626fde46d6eSreyk ibuf_size(msg) - integrlen);
627a699afcaSclaudio hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen);
628fde46d6eSreyk
62915863c3aStobhe integrdata = ibuf_seek(src, integroff, integrlen);
63015863c3aStobhe if (integrdata == NULL)
63115863c3aStobhe goto done;
632a699afcaSclaudio if (memcmp(ibuf_data(tmp), integrdata, integrlen) != 0) {
633fde46d6eSreyk log_debug("%s: integrity check failed", __func__);
634fde46d6eSreyk goto done;
635fde46d6eSreyk }
636fde46d6eSreyk
637b0eeedd0Smikeb log_debug("%s: integrity check succeeded", __func__);
638a699afcaSclaudio print_hex(ibuf_data(tmp), 0, tmplen);
639fde46d6eSreyk
640be2b38f5Sclaudio ibuf_free(tmp);
641fde46d6eSreyk tmp = NULL;
64215863c3aStobhe }
643fde46d6eSreyk
644fde46d6eSreyk /*
645fde46d6eSreyk * Decrypt the payload and strip any padding
646fde46d6eSreyk */
647fde46d6eSreyk if ((encrlen % blocklen) != 0) {
648fde46d6eSreyk log_debug("%s: unaligned encrypted payload", __func__);
649fde46d6eSreyk goto done;
650fde46d6eSreyk }
651fde46d6eSreyk
652eef6c82aSclaudio cipher_setkey(sa->sa_encr, ibuf_data(encr), ibuf_size(encr));
6535001b11bSclaudio cipher_setiv(sa->sa_encr, ibuf_seek(src, ivoff, ivlen), ivlen);
65481b8fecaStobhe if (cipher_init_decrypt(sa->sa_encr) == -1) {
65581b8fecaStobhe log_info("%s: error initiating cipher.", __func__);
65681b8fecaStobhe goto done;
65781b8fecaStobhe }
658fde46d6eSreyk
65915863c3aStobhe /* Set AEAD tag */
66015863c3aStobhe if (sa->sa_integr->hash_isaead) {
66115863c3aStobhe integrdata = ibuf_seek(src, integroff, integrlen);
66215863c3aStobhe if (integrdata == NULL)
66315863c3aStobhe goto done;
66415863c3aStobhe if (cipher_settag(sa->sa_encr, integrdata, integrlen)) {
66515863c3aStobhe log_info("%s: failed to set tag.", __func__);
66615863c3aStobhe goto done;
66715863c3aStobhe }
66815863c3aStobhe }
66915863c3aStobhe
670fde46d6eSreyk if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr,
671fde46d6eSreyk encrlen))) == NULL)
672fde46d6eSreyk goto done;
673fde46d6eSreyk
67415863c3aStobhe /*
67515863c3aStobhe * Add additional authenticated data for AEAD ciphers
67615863c3aStobhe */
67715863c3aStobhe if (sa->sa_integr->hash_isaead) {
678eef6c82aSclaudio log_debug("%s: AAD length %zu", __func__,
679eef6c82aSclaudio ibuf_size(msg) - ibuf_size(src));
680eef6c82aSclaudio print_hex(ibuf_data(msg), 0, ibuf_size(msg) - ibuf_size(src));
68115863c3aStobhe cipher_aad(sa->sa_encr, ibuf_data(msg),
682eef6c82aSclaudio ibuf_size(msg) - ibuf_size(src), &outlen);
68315863c3aStobhe }
68415863c3aStobhe
685eef6c82aSclaudio if ((outlen = ibuf_size(out)) != 0) {
6865001b11bSclaudio if (cipher_update(sa->sa_encr, ibuf_seek(src, encroff, encrlen),
68781b8fecaStobhe encrlen, ibuf_data(out), &outlen) == -1) {
68881b8fecaStobhe log_info("%s: error updating cipher.", __func__);
68981b8fecaStobhe goto done;
69081b8fecaStobhe }
6913189733aSmikeb
692fde46d6eSreyk ptr = ibuf_seek(out, outlen - 1, 1);
693fde46d6eSreyk pad = *ptr;
694e0696045Sreyk }
695fde46d6eSreyk
69615863c3aStobhe if (cipher_final(sa->sa_encr) == -1) {
69715863c3aStobhe log_info("%s: decryption failed.", __func__);
69815863c3aStobhe goto done;
69915863c3aStobhe }
70015863c3aStobhe
701328746baSreyk log_debug("%s: decrypted payload length %zd/%zd padding %d",
702fde46d6eSreyk __func__, outlen, encrlen, pad);
703dca9e784Sclaudio print_hexbuf(out);
704fde46d6eSreyk
70565c540d0Spatrick /* Strip padding and padding length */
70665c540d0Spatrick if (ibuf_setsize(out, outlen - pad - 1) != 0)
707fde46d6eSreyk goto done;
708fde46d6eSreyk
709be2b38f5Sclaudio ibuf_free(src);
710fde46d6eSreyk return (out);
711fde46d6eSreyk done:
712be2b38f5Sclaudio ibuf_free(tmp);
713be2b38f5Sclaudio ibuf_free(out);
714be2b38f5Sclaudio ibuf_free(src);
715fde46d6eSreyk return (NULL);
716fde46d6eSreyk }
717fde46d6eSreyk
718fde46d6eSreyk int
ikev2_check_frag_oversize(struct iked_sa * sa,struct ibuf * buf)71965c540d0Spatrick ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf) {
72065c540d0Spatrick size_t len = ibuf_length(buf);
72165c540d0Spatrick sa_family_t sa_fam;
72265c540d0Spatrick size_t max;
72365c540d0Spatrick size_t ivlen, integrlen, blocklen;
72465c540d0Spatrick
72515863c3aStobhe if (sa == NULL ||
72615863c3aStobhe sa->sa_encr == NULL ||
72715863c3aStobhe sa->sa_integr == NULL) {
72815863c3aStobhe log_debug("%s: invalid SA", __func__);
72915863c3aStobhe return (-1);
73015863c3aStobhe }
73115863c3aStobhe
73265c540d0Spatrick sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
73365c540d0Spatrick
73465c540d0Spatrick max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
73565c540d0Spatrick : IKEV2_MAXLEN_IPV6_FRAG;
73665c540d0Spatrick
73765c540d0Spatrick blocklen = cipher_length(sa->sa_encr);
73865c540d0Spatrick ivlen = cipher_ivlength(sa->sa_encr);
73965c540d0Spatrick integrlen = hash_length(sa->sa_integr);
74065c540d0Spatrick
74165c540d0Spatrick /* Estimated maximum packet size (with 0 < padding < blocklen) */
74265c540d0Spatrick return ((len + ivlen + blocklen + integrlen) >= max) && sa->sa_frag;
74365c540d0Spatrick }
74465c540d0Spatrick
74565c540d0Spatrick int
ikev2_msg_send_encrypt(struct iked * env,struct iked_sa * sa,struct ibuf ** ep,uint8_t exchange,uint8_t firstpayload,int response)746c45fd413Smikeb ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf **ep,
747d09d3a7dSreyk uint8_t exchange, uint8_t firstpayload, int response)
748fde46d6eSreyk {
749fde46d6eSreyk struct iked_message resp;
750fde46d6eSreyk struct ike_header *hdr;
751fde46d6eSreyk struct ikev2_payload *pld;
752fde46d6eSreyk struct ibuf *buf, *e = *ep;
753fde46d6eSreyk int ret = -1;
754fde46d6eSreyk
75565c540d0Spatrick /* Check if msg needs to be fragmented */
75665c540d0Spatrick if (ikev2_check_frag_oversize(sa, e)) {
75765c540d0Spatrick return ikev2_send_encrypted_fragments(env, sa, e, exchange,
75865c540d0Spatrick firstpayload, response);
75965c540d0Spatrick }
76065c540d0Spatrick
761c45fd413Smikeb if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr,
762c45fd413Smikeb sa->sa_peer.addr.ss_len, &sa->sa_local.addr,
763c45fd413Smikeb sa->sa_local.addr.ss_len, response)) == NULL)
764fde46d6eSreyk goto done;
765fde46d6eSreyk
76630904802Spatrick resp.msg_msgid = response ? sa->sa_msgid_current : ikev2_msg_id(env, sa);
767c45fd413Smikeb
768fde46d6eSreyk /* IKE header */
769c45fd413Smikeb if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, IKEV2_PAYLOAD_SK,
770c45fd413Smikeb exchange, response ? IKEV2_FLAG_RESPONSE : 0)) == NULL)
771fde46d6eSreyk goto done;
772fde46d6eSreyk
773fde46d6eSreyk if ((pld = ikev2_add_payload(buf)) == NULL)
774fde46d6eSreyk goto done;
775fde46d6eSreyk
77615863c3aStobhe if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1)
77715863c3aStobhe goto done;
77815863c3aStobhe
779fde46d6eSreyk /* Encrypt message and add as an E payload */
78015863c3aStobhe if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
781fde46d6eSreyk log_debug("%s: encryption failed", __func__);
782fde46d6eSreyk goto done;
783fde46d6eSreyk }
784f6f27851Sclaudio if (ibuf_add_buf(buf, e) != 0)
785fde46d6eSreyk goto done;
786fde46d6eSreyk
787fde46d6eSreyk /* Add integrity checksum (HMAC) */
788fde46d6eSreyk if (ikev2_msg_integr(env, sa, buf) != 0) {
789fde46d6eSreyk log_debug("%s: integrity checksum failed", __func__);
790fde46d6eSreyk goto done;
791fde46d6eSreyk }
792fde46d6eSreyk
793fde46d6eSreyk resp.msg_data = buf;
794fde46d6eSreyk resp.msg_sa = sa;
795d9c13a0aSmikeb resp.msg_fd = sa->sa_fd;
796fde46d6eSreyk TAILQ_INIT(&resp.msg_proposals);
797fde46d6eSreyk
798fde46d6eSreyk (void)ikev2_pld_parse(env, hdr, &resp, 0);
799fde46d6eSreyk
800d9c13a0aSmikeb ret = ikev2_msg_send(env, &resp);
801fde46d6eSreyk
802fde46d6eSreyk done:
803fde46d6eSreyk /* e is cleaned up by the calling function */
804fde46d6eSreyk *ep = e;
805763023d6Sreyk ikev2_msg_cleanup(env, &resp);
806fde46d6eSreyk
807fde46d6eSreyk return (ret);
808fde46d6eSreyk }
809fde46d6eSreyk
81065c540d0Spatrick int
ikev2_send_encrypted_fragments(struct iked * env,struct iked_sa * sa,struct ibuf * in,uint8_t exchange,uint8_t firstpayload,int response)81165c540d0Spatrick ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa,
81265c540d0Spatrick struct ibuf *in, uint8_t exchange, uint8_t firstpayload, int response) {
81365c540d0Spatrick struct iked_message resp;
8144387480dStobhe struct ibuf *buf, *e = NULL;
81565c540d0Spatrick struct ike_header *hdr;
81665c540d0Spatrick struct ikev2_payload *pld;
81765c540d0Spatrick struct ikev2_frag_payload *frag;
81865c540d0Spatrick sa_family_t sa_fam;
81965c540d0Spatrick size_t ivlen, integrlen, blocklen;
820ab8e3451Sderaadt size_t max_len, left, offset=0;
82165c540d0Spatrick size_t frag_num = 1, frag_total;
82265c540d0Spatrick uint8_t *data;
82365c540d0Spatrick uint32_t msgid;
82465c540d0Spatrick int ret = -1;
82565c540d0Spatrick
82615863c3aStobhe if (sa == NULL ||
82715863c3aStobhe sa->sa_encr == NULL ||
82815863c3aStobhe sa->sa_integr == NULL) {
82915863c3aStobhe log_debug("%s: invalid SA", __func__);
83068468697Smbuhl ikestat_inc(env, ikes_frag_send_failures);
83168468697Smbuhl return ret;
83215863c3aStobhe }
83315863c3aStobhe
83465c540d0Spatrick sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family;
83565c540d0Spatrick
83665c540d0Spatrick left = ibuf_length(in);
83765c540d0Spatrick
83865c540d0Spatrick /* Calculate max allowed size of a fragments payload */
83965c540d0Spatrick blocklen = cipher_length(sa->sa_encr);
84065c540d0Spatrick ivlen = cipher_ivlength(sa->sa_encr);
84165c540d0Spatrick integrlen = hash_length(sa->sa_integr);
84265c540d0Spatrick max_len = (sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG
84365c540d0Spatrick : IKEV2_MAXLEN_IPV6_FRAG)
84465c540d0Spatrick - ivlen - blocklen - integrlen;
84565c540d0Spatrick
84665c540d0Spatrick /* Total number of fragments to send */
84765c540d0Spatrick frag_total = (left / max_len) + 1;
84865c540d0Spatrick
84965c540d0Spatrick msgid = response ? sa->sa_msgid_current : ikev2_msg_id(env, sa);
85065c540d0Spatrick
85165c540d0Spatrick while (frag_num <= frag_total) {
85265c540d0Spatrick if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr,
85365c540d0Spatrick sa->sa_peer.addr.ss_len, &sa->sa_local.addr,
85465c540d0Spatrick sa->sa_local.addr.ss_len, response)) == NULL)
85565c540d0Spatrick goto done;
85665c540d0Spatrick
85765c540d0Spatrick resp.msg_msgid = msgid;
85865c540d0Spatrick
85965c540d0Spatrick /* IKE header */
86065c540d0Spatrick if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid,
86165c540d0Spatrick IKEV2_PAYLOAD_SKF, exchange, response ? IKEV2_FLAG_RESPONSE
86265c540d0Spatrick : 0)) == NULL)
86365c540d0Spatrick goto done;
86465c540d0Spatrick
86565c540d0Spatrick /* Payload header */
86665c540d0Spatrick if ((pld = ikev2_add_payload(buf)) == NULL)
86765c540d0Spatrick goto done;
86865c540d0Spatrick
86965c540d0Spatrick /* Fragment header */
87056c4e216Sclaudio if ((frag = ibuf_reserve(buf, sizeof(*frag))) == NULL) {
87165c540d0Spatrick log_debug("%s: failed to add SKF fragment header",
87265c540d0Spatrick __func__);
87365c540d0Spatrick goto done;
87465c540d0Spatrick }
87565c540d0Spatrick frag->frag_num = htobe16(frag_num);
87665c540d0Spatrick frag->frag_total = htobe16(frag_total);
87765c540d0Spatrick
87865c540d0Spatrick /* Encrypt message and add as an E payload */
87965c540d0Spatrick data = ibuf_seek(in, offset, 0);
880d8ea035bSderaadt if ((e = ibuf_new(data, MINIMUM(left, max_len))) == NULL) {
88165c540d0Spatrick goto done;
88265c540d0Spatrick }
88315863c3aStobhe
88415863c3aStobhe if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr,
88515863c3aStobhe firstpayload, 1) == -1)
88615863c3aStobhe goto done;
88715863c3aStobhe
88815863c3aStobhe if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) {
88965c540d0Spatrick log_debug("%s: encryption failed", __func__);
89065c540d0Spatrick goto done;
89165c540d0Spatrick }
892f6f27851Sclaudio if (ibuf_add_buf(buf, e) != 0)
89365c540d0Spatrick goto done;
89465c540d0Spatrick
89565c540d0Spatrick /* Add integrity checksum (HMAC) */
89665c540d0Spatrick if (ikev2_msg_integr(env, sa, buf) != 0) {
89765c540d0Spatrick log_debug("%s: integrity checksum failed", __func__);
89865c540d0Spatrick goto done;
89965c540d0Spatrick }
90065c540d0Spatrick
90165c540d0Spatrick log_debug("%s: Fragment %zu of %zu has size of %zu bytes.",
90265c540d0Spatrick __func__, frag_num, frag_total,
90365c540d0Spatrick ibuf_size(buf) - sizeof(*hdr));
904dca9e784Sclaudio print_hexbuf(buf);
90565c540d0Spatrick
90665c540d0Spatrick resp.msg_data = buf;
90765c540d0Spatrick resp.msg_sa = sa;
90865c540d0Spatrick resp.msg_fd = sa->sa_fd;
90965c540d0Spatrick TAILQ_INIT(&resp.msg_proposals);
91065c540d0Spatrick
91165c540d0Spatrick if (ikev2_msg_send(env, &resp) == -1)
91265c540d0Spatrick goto done;
91365c540d0Spatrick
914b41cc0c8Stobhe ikestat_inc(env, ikes_frag_sent);
915b41cc0c8Stobhe
916d8ea035bSderaadt offset += MINIMUM(left, max_len);
917d8ea035bSderaadt left -= MINIMUM(left, max_len);
91865c540d0Spatrick frag_num++;
91965c540d0Spatrick
92065c540d0Spatrick /* MUST be zero after first fragment */
92165c540d0Spatrick firstpayload = 0;
92265c540d0Spatrick
92365c540d0Spatrick ikev2_msg_cleanup(env, &resp);
924be2b38f5Sclaudio ibuf_free(e);
92565c540d0Spatrick e = NULL;
92665c540d0Spatrick }
92765c540d0Spatrick
92865c540d0Spatrick return 0;
92965c540d0Spatrick done:
93065c540d0Spatrick ikev2_msg_cleanup(env, &resp);
931be2b38f5Sclaudio ibuf_free(e);
932b41cc0c8Stobhe ikestat_inc(env, ikes_frag_send_failures);
93365c540d0Spatrick return ret;
93465c540d0Spatrick }
93565c540d0Spatrick
936fde46d6eSreyk struct ibuf *
ikev2_msg_auth(struct iked * env,struct iked_sa * sa,int response)937fde46d6eSreyk ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response)
938fde46d6eSreyk {
939fde46d6eSreyk struct ibuf *authmsg = NULL, *nonce, *prfkey, *buf;
940d09d3a7dSreyk uint8_t *ptr;
941fde46d6eSreyk struct iked_id *id;
942fde46d6eSreyk size_t tmplen;
943fde46d6eSreyk
944fde46d6eSreyk /*
945fde46d6eSreyk * Create the payload to be signed/MAC'ed for AUTH
946fde46d6eSreyk */
947fde46d6eSreyk
948fde46d6eSreyk if (!response) {
949fde46d6eSreyk if ((nonce = sa->sa_rnonce) == NULL ||
950fde46d6eSreyk (sa->sa_iid.id_type == 0) ||
951fde46d6eSreyk (prfkey = sa->sa_key_iprf) == NULL ||
952fde46d6eSreyk (buf = sa->sa_1stmsg) == NULL)
953fde46d6eSreyk return (NULL);
954fde46d6eSreyk id = &sa->sa_iid;
955fde46d6eSreyk } else {
956fde46d6eSreyk if ((nonce = sa->sa_inonce) == NULL ||
957fde46d6eSreyk (sa->sa_rid.id_type == 0) ||
958fde46d6eSreyk (prfkey = sa->sa_key_rprf) == NULL ||
959fde46d6eSreyk (buf = sa->sa_2ndmsg) == NULL)
960fde46d6eSreyk return (NULL);
961fde46d6eSreyk id = &sa->sa_rid;
962fde46d6eSreyk }
963fde46d6eSreyk
964fde46d6eSreyk if ((authmsg = ibuf_dup(buf)) == NULL)
965fde46d6eSreyk return (NULL);
966f6f27851Sclaudio if (ibuf_add_buf(authmsg, nonce) != 0)
967fde46d6eSreyk goto fail;
968fde46d6eSreyk
969fde46d6eSreyk if ((hash_setkey(sa->sa_prf, ibuf_data(prfkey),
970fde46d6eSreyk ibuf_size(prfkey))) == NULL)
971fde46d6eSreyk goto fail;
972fde46d6eSreyk
973d0e3d0abStobhe /* require non-truncating hash */
974d0e3d0abStobhe if (hash_keylength(sa->sa_prf) != hash_length(sa->sa_prf))
975d0e3d0abStobhe goto fail;
976d0e3d0abStobhe
97756c4e216Sclaudio if ((ptr = ibuf_reserve(authmsg, hash_keylength(sa->sa_prf))) == NULL)
978fde46d6eSreyk goto fail;
979fde46d6eSreyk
980fde46d6eSreyk hash_init(sa->sa_prf);
981fde46d6eSreyk hash_update(sa->sa_prf, ibuf_data(id->id_buf), ibuf_size(id->id_buf));
982fde46d6eSreyk hash_final(sa->sa_prf, ptr, &tmplen);
983fde46d6eSreyk
984fde46d6eSreyk if (tmplen != hash_length(sa->sa_prf))
985fde46d6eSreyk goto fail;
986fde46d6eSreyk
987328746baSreyk log_debug("%s: %s auth data length %zu",
988fde46d6eSreyk __func__, response ? "responder" : "initiator",
989fde46d6eSreyk ibuf_size(authmsg));
990dca9e784Sclaudio print_hexbuf(authmsg);
991fde46d6eSreyk
992fde46d6eSreyk return (authmsg);
993fde46d6eSreyk
994fde46d6eSreyk fail:
995be2b38f5Sclaudio ibuf_free(authmsg);
996fde46d6eSreyk return (NULL);
997fde46d6eSreyk }
998fde46d6eSreyk
999fde46d6eSreyk int
ikev2_msg_authverify(struct iked * env,struct iked_sa * sa,struct iked_auth * auth,uint8_t * buf,size_t len,struct ibuf * authmsg)1000fde46d6eSreyk ikev2_msg_authverify(struct iked *env, struct iked_sa *sa,
1001d09d3a7dSreyk struct iked_auth *auth, uint8_t *buf, size_t len, struct ibuf *authmsg)
1002fde46d6eSreyk {
1003d09d3a7dSreyk uint8_t *key, *psk = NULL;
1004fde46d6eSreyk ssize_t keylen;
1005fde46d6eSreyk struct iked_id *id;
1006fde46d6eSreyk struct iked_dsa *dsa = NULL;
1007fde46d6eSreyk int ret = -1;
1008d09d3a7dSreyk uint8_t keytype;
1009fde46d6eSreyk
1010fde46d6eSreyk if (sa->sa_hdr.sh_initiator)
1011fde46d6eSreyk id = &sa->sa_rcert;
1012fde46d6eSreyk else
1013fde46d6eSreyk id = &sa->sa_icert;
1014fde46d6eSreyk
1015fde46d6eSreyk if ((dsa = dsa_verify_new(auth->auth_method, sa->sa_prf)) == NULL) {
1016fde46d6eSreyk log_debug("%s: invalid auth method", __func__);
1017fde46d6eSreyk return (-1);
1018fde46d6eSreyk }
1019fde46d6eSreyk
1020fde46d6eSreyk switch (auth->auth_method) {
1021fde46d6eSreyk case IKEV2_AUTH_SHARED_KEY_MIC:
1022fde46d6eSreyk if (!auth->auth_length) {
1023fde46d6eSreyk log_debug("%s: no pre-shared key found", __func__);
1024fde46d6eSreyk goto done;
1025fde46d6eSreyk }
1026fde46d6eSreyk if ((keylen = ikev2_psk(sa, auth->auth_data,
1027fde46d6eSreyk auth->auth_length, &psk)) == -1) {
1028fde46d6eSreyk log_debug("%s: failed to get PSK", __func__);
1029fde46d6eSreyk goto done;
1030fde46d6eSreyk }
1031fde46d6eSreyk key = psk;
1032fde46d6eSreyk keytype = 0;
1033fde46d6eSreyk break;
1034fde46d6eSreyk default:
1035202133c5Sreyk if (!id->id_type || !ibuf_length(id->id_buf)) {
1036fde46d6eSreyk log_debug("%s: no cert found", __func__);
1037fde46d6eSreyk goto done;
1038fde46d6eSreyk }
1039fde46d6eSreyk key = ibuf_data(id->id_buf);
1040fde46d6eSreyk keylen = ibuf_size(id->id_buf);
1041fde46d6eSreyk keytype = id->id_type;
1042fde46d6eSreyk break;
1043fde46d6eSreyk }
1044fde46d6eSreyk
1045328746baSreyk log_debug("%s: method %s keylen %zd type %s", __func__,
1046fde46d6eSreyk print_map(auth->auth_method, ikev2_auth_map), keylen,
1047fde46d6eSreyk print_map(id->id_type, ikev2_cert_map));
1048fde46d6eSreyk
1049fde46d6eSreyk if (dsa_setkey(dsa, key, keylen, keytype) == NULL ||
105048b975e3Smarkus dsa_init(dsa, buf, len) != 0 ||
1051fde46d6eSreyk dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) {
1052fde46d6eSreyk log_debug("%s: failed to compute digital signature", __func__);
1053fde46d6eSreyk goto done;
1054fde46d6eSreyk }
1055fde46d6eSreyk
1056fde46d6eSreyk if ((ret = dsa_verify_final(dsa, buf, len)) == 0) {
1057fde46d6eSreyk log_debug("%s: authentication successful", __func__);
1058fde46d6eSreyk sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS);
10594a986ab9Smarkus sa_stateflags(sa, IKED_REQ_AUTHVALID);
1060fde46d6eSreyk } else {
1061fde46d6eSreyk log_debug("%s: authentication failed", __func__);
1062fde46d6eSreyk sa_state(env, sa, IKEV2_STATE_AUTH_REQUEST);
1063fde46d6eSreyk }
1064fde46d6eSreyk
1065fde46d6eSreyk done:
1066fde46d6eSreyk free(psk);
1067fde46d6eSreyk dsa_free(dsa);
1068fde46d6eSreyk
1069fde46d6eSreyk return (ret);
1070fde46d6eSreyk }
1071fde46d6eSreyk
1072fde46d6eSreyk int
ikev2_msg_authsign(struct iked * env,struct iked_sa * sa,struct iked_auth * auth,struct ibuf * authmsg)1073fde46d6eSreyk ikev2_msg_authsign(struct iked *env, struct iked_sa *sa,
1074fde46d6eSreyk struct iked_auth *auth, struct ibuf *authmsg)
1075fde46d6eSreyk {
1076d09d3a7dSreyk uint8_t *key, *psk = NULL;
10775e4d3a37Sreyk ssize_t keylen, siglen;
1078fde46d6eSreyk struct iked_hash *prf = sa->sa_prf;
1079fde46d6eSreyk struct iked_id *id;
1080fde46d6eSreyk struct iked_dsa *dsa = NULL;
1081fde46d6eSreyk struct ibuf *buf;
1082fde46d6eSreyk int ret = -1;
1083d09d3a7dSreyk uint8_t keytype;
1084fde46d6eSreyk
1085fde46d6eSreyk if (sa->sa_hdr.sh_initiator)
1086fde46d6eSreyk id = &sa->sa_icert;
1087fde46d6eSreyk else
1088fde46d6eSreyk id = &sa->sa_rcert;
1089fde46d6eSreyk
1090fde46d6eSreyk if ((dsa = dsa_sign_new(auth->auth_method, prf)) == NULL) {
1091fde46d6eSreyk log_debug("%s: invalid auth method", __func__);
1092fde46d6eSreyk return (-1);
1093fde46d6eSreyk }
1094fde46d6eSreyk
1095fde46d6eSreyk switch (auth->auth_method) {
1096fde46d6eSreyk case IKEV2_AUTH_SHARED_KEY_MIC:
1097fde46d6eSreyk if (!auth->auth_length) {
1098fde46d6eSreyk log_debug("%s: no pre-shared key found", __func__);
1099fde46d6eSreyk goto done;
1100fde46d6eSreyk }
1101fde46d6eSreyk if ((keylen = ikev2_psk(sa, auth->auth_data,
1102fde46d6eSreyk auth->auth_length, &psk)) == -1) {
1103fde46d6eSreyk log_debug("%s: failed to get PSK", __func__);
1104fde46d6eSreyk goto done;
1105fde46d6eSreyk }
1106fde46d6eSreyk key = psk;
1107fde46d6eSreyk keytype = 0;
1108fde46d6eSreyk break;
1109fde46d6eSreyk default:
1110fde46d6eSreyk if (id == NULL) {
1111fde46d6eSreyk log_debug("%s: no cert found", __func__);
1112fde46d6eSreyk goto done;
1113fde46d6eSreyk }
1114fde46d6eSreyk key = ibuf_data(id->id_buf);
1115fde46d6eSreyk keylen = ibuf_size(id->id_buf);
1116fde46d6eSreyk keytype = id->id_type;
1117fde46d6eSreyk break;
1118fde46d6eSreyk }
1119fde46d6eSreyk
1120fde46d6eSreyk if (dsa_setkey(dsa, key, keylen, keytype) == NULL ||
112148b975e3Smarkus dsa_init(dsa, NULL, 0) != 0 ||
1122fde46d6eSreyk dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) {
1123fde46d6eSreyk log_debug("%s: failed to compute digital signature", __func__);
1124fde46d6eSreyk goto done;
1125fde46d6eSreyk }
1126fde46d6eSreyk
1127be2b38f5Sclaudio ibuf_free(sa->sa_localauth.id_buf);
1128fde46d6eSreyk sa->sa_localauth.id_buf = NULL;
1129fde46d6eSreyk
1130fde46d6eSreyk if ((buf = ibuf_new(NULL, dsa_length(dsa))) == NULL) {
1131fde46d6eSreyk log_debug("%s: failed to get auth buffer", __func__);
1132fde46d6eSreyk goto done;
1133fde46d6eSreyk }
1134fde46d6eSreyk
11355e4d3a37Sreyk if ((siglen = dsa_sign_final(dsa,
11365e4d3a37Sreyk ibuf_data(buf), ibuf_size(buf))) < 0) {
1137fde46d6eSreyk log_debug("%s: failed to create auth signature", __func__);
1138be2b38f5Sclaudio ibuf_free(buf);
1139fde46d6eSreyk goto done;
1140fde46d6eSreyk }
1141fde46d6eSreyk
11425e4d3a37Sreyk if (ibuf_setsize(buf, siglen) < 0) {
11435e4d3a37Sreyk log_debug("%s: failed to set auth signature size to %zd",
11445e4d3a37Sreyk __func__, siglen);
1145be2b38f5Sclaudio ibuf_free(buf);
11465e4d3a37Sreyk goto done;
11475e4d3a37Sreyk }
11485e4d3a37Sreyk
1149fde46d6eSreyk sa->sa_localauth.id_type = auth->auth_method;
1150fde46d6eSreyk sa->sa_localauth.id_buf = buf;
1151fde46d6eSreyk
1152fde46d6eSreyk ret = 0;
1153fde46d6eSreyk done:
1154fde46d6eSreyk free(psk);
1155fde46d6eSreyk dsa_free(dsa);
1156fde46d6eSreyk
1157fde46d6eSreyk return (ret);
1158fde46d6eSreyk }
1159ae494144Sreyk
1160ae494144Sreyk int
ikev2_msg_frompeer(struct iked_message * msg)1161ae494144Sreyk ikev2_msg_frompeer(struct iked_message *msg)
1162ae494144Sreyk {
1163ae494144Sreyk struct iked_sa *sa = msg->msg_sa;
1164ae494144Sreyk struct ike_header *hdr;
1165ae494144Sreyk
116626d7dba1Sreyk msg = msg->msg_parent;
11671b0d4946Sreyk
1168ae494144Sreyk if (sa == NULL ||
1169ae494144Sreyk (hdr = ibuf_seek(msg->msg_data, 0, sizeof(*hdr))) == NULL)
1170ae494144Sreyk return (0);
1171ae494144Sreyk
1172ae494144Sreyk if (!sa->sa_hdr.sh_initiator &&
1173ae494144Sreyk (hdr->ike_flags & IKEV2_FLAG_INITIATOR))
1174ae494144Sreyk return (1);
1175ae494144Sreyk else if (sa->sa_hdr.sh_initiator &&
1176ae494144Sreyk (hdr->ike_flags & IKEV2_FLAG_INITIATOR) == 0)
1177ae494144Sreyk return (1);
1178ae494144Sreyk
1179ae494144Sreyk return (0);
1180ae494144Sreyk }
1181ae494144Sreyk
1182ae494144Sreyk struct iked_socket *
ikev2_msg_getsocket(struct iked * env,int af,int natt)118312c9fd31Sreyk ikev2_msg_getsocket(struct iked *env, int af, int natt)
1184ae494144Sreyk {
1185ae494144Sreyk switch (af) {
1186ae494144Sreyk case AF_INET:
118712c9fd31Sreyk return (env->sc_sock4[natt ? 1 : 0]);
1188ae494144Sreyk case AF_INET6:
118912c9fd31Sreyk return (env->sc_sock6[natt ? 1 : 0]);
1190ae494144Sreyk }
1191ae494144Sreyk
1192ae494144Sreyk log_debug("%s: af socket %d not available", __func__, af);
1193ae494144Sreyk return (NULL);
1194ae494144Sreyk }
1195c45fd413Smikeb
11966e264ad0Stobhe int
ikev2_msg_enqueue(struct iked * env,struct iked_msgqueue * queue,struct iked_message * msg,int timeout)11976e264ad0Stobhe ikev2_msg_enqueue(struct iked *env, struct iked_msgqueue *queue,
11986e264ad0Stobhe struct iked_message *msg, int timeout)
11996e264ad0Stobhe {
12006e264ad0Stobhe struct iked_msg_retransmit *mr;
12016e264ad0Stobhe
12026e264ad0Stobhe if ((mr = ikev2_msg_lookup(env, queue, msg, msg->msg_exchange)) ==
12036e264ad0Stobhe NULL) {
12046e264ad0Stobhe if ((mr = calloc(1, sizeof(*mr))) == NULL)
12056e264ad0Stobhe return (-1);
12066e264ad0Stobhe TAILQ_INIT(&mr->mrt_frags);
12076e264ad0Stobhe mr->mrt_tries = 0;
12086e264ad0Stobhe
12096e264ad0Stobhe timer_set(env, &mr->mrt_timer, msg->msg_response ?
12106e264ad0Stobhe ikev2_msg_response_timeout : ikev2_msg_retransmit_timeout,
12116e264ad0Stobhe mr);
12126e264ad0Stobhe timer_add(env, &mr->mrt_timer, timeout);
12136e264ad0Stobhe
12146e264ad0Stobhe TAILQ_INSERT_TAIL(queue, mr, mrt_entry);
12156e264ad0Stobhe }
12166e264ad0Stobhe
12176e264ad0Stobhe TAILQ_INSERT_TAIL(&mr->mrt_frags, msg, msg_entry);
12186e264ad0Stobhe
12196e264ad0Stobhe return 0;
12206e264ad0Stobhe }
12216e264ad0Stobhe
1222c45fd413Smikeb void
ikev2_msg_prevail(struct iked * env,struct iked_msgqueue * queue,struct iked_message * msg)1223c45fd413Smikeb ikev2_msg_prevail(struct iked *env, struct iked_msgqueue *queue,
1224c45fd413Smikeb struct iked_message *msg)
1225c45fd413Smikeb {
12266e264ad0Stobhe struct iked_msg_retransmit *mr, *mrtmp;
1227c45fd413Smikeb
12286e264ad0Stobhe TAILQ_FOREACH_SAFE(mr, queue, mrt_entry, mrtmp) {
12296e264ad0Stobhe if (TAILQ_FIRST(&mr->mrt_frags)->msg_msgid < msg->msg_msgid)
12306e264ad0Stobhe ikev2_msg_dispose(env, queue, mr);
1231c45fd413Smikeb }
1232c45fd413Smikeb }
1233c45fd413Smikeb
1234c45fd413Smikeb void
ikev2_msg_dispose(struct iked * env,struct iked_msgqueue * queue,struct iked_msg_retransmit * mr)1235c45fd413Smikeb ikev2_msg_dispose(struct iked *env, struct iked_msgqueue *queue,
12366e264ad0Stobhe struct iked_msg_retransmit *mr)
1237c45fd413Smikeb {
12386e264ad0Stobhe struct iked_message *m;
12396e264ad0Stobhe
12406e264ad0Stobhe while ((m = TAILQ_FIRST(&mr->mrt_frags)) != NULL) {
12416e264ad0Stobhe TAILQ_REMOVE(&mr->mrt_frags, m, msg_entry);
12426e264ad0Stobhe ikev2_msg_cleanup(env, m);
12436e264ad0Stobhe free(m);
12446e264ad0Stobhe }
12456e264ad0Stobhe
12466e264ad0Stobhe timer_del(env, &mr->mrt_timer);
12476e264ad0Stobhe TAILQ_REMOVE(queue, mr, mrt_entry);
12486e264ad0Stobhe free(mr);
1249c45fd413Smikeb }
1250c45fd413Smikeb
1251c45fd413Smikeb void
ikev2_msg_flushqueue(struct iked * env,struct iked_msgqueue * queue)1252c45fd413Smikeb ikev2_msg_flushqueue(struct iked *env, struct iked_msgqueue *queue)
1253c45fd413Smikeb {
12546e264ad0Stobhe struct iked_msg_retransmit *mr = NULL;
1255c45fd413Smikeb
12566e264ad0Stobhe while ((mr = TAILQ_FIRST(queue)) != NULL)
12576e264ad0Stobhe ikev2_msg_dispose(env, queue, mr);
1258c45fd413Smikeb }
1259c45fd413Smikeb
12606e264ad0Stobhe struct iked_msg_retransmit *
ikev2_msg_lookup(struct iked * env,struct iked_msgqueue * queue,struct iked_message * msg,uint8_t exchange)126156d51042Smikeb ikev2_msg_lookup(struct iked *env, struct iked_msgqueue *queue,
12626e264ad0Stobhe struct iked_message *msg, uint8_t exchange)
1263c45fd413Smikeb {
12646e264ad0Stobhe struct iked_msg_retransmit *mr = NULL;
1265c45fd413Smikeb
12666e264ad0Stobhe TAILQ_FOREACH(mr, queue, mrt_entry) {
12676e264ad0Stobhe if (TAILQ_FIRST(&mr->mrt_frags)->msg_msgid ==
12686e264ad0Stobhe msg->msg_msgid &&
12696e264ad0Stobhe TAILQ_FIRST(&mr->mrt_frags)->msg_exchange == exchange)
1270c45fd413Smikeb break;
1271c45fd413Smikeb }
1272c45fd413Smikeb
12736e264ad0Stobhe return (mr);
127462cdfd0dStobhe }
127562cdfd0dStobhe
1276c45fd413Smikeb int
ikev2_msg_retransmit_response(struct iked * env,struct iked_sa * sa,struct iked_message * msg,struct ike_header * hdr)1277c45fd413Smikeb ikev2_msg_retransmit_response(struct iked *env, struct iked_sa *sa,
1278*2117af45Stobhe struct iked_message *msg, struct ike_header *hdr)
1279c45fd413Smikeb {
12806e264ad0Stobhe struct iked_msg_retransmit *mr = NULL;
12816e264ad0Stobhe struct iked_message *m = NULL;
12826e264ad0Stobhe
1283*2117af45Stobhe if ((mr = ikev2_msg_lookup(env, &sa->sa_responses, msg,
1284*2117af45Stobhe hdr->ike_exchange)) == NULL)
1285f336206eStobhe return (-2); /* not found */
12866e264ad0Stobhe
1287*2117af45Stobhe if (hdr->ike_nextpayload == IKEV2_PAYLOAD_SKF) {
1288*2117af45Stobhe /* only retransmit for fragment number one */
1289*2117af45Stobhe if (ikev2_pld_parse_quick(env, hdr, msg,
1290*2117af45Stobhe msg->msg_offset) != 0 || msg->msg_frag_num != 1) {
1291*2117af45Stobhe log_debug("%s: ignoring fragment", SPI_SA(sa, __func__));
1292*2117af45Stobhe return (0);
1293*2117af45Stobhe }
1294*2117af45Stobhe log_debug("%s: first fragment", SPI_SA(sa, __func__));
1295*2117af45Stobhe }
1296*2117af45Stobhe
12976e264ad0Stobhe TAILQ_FOREACH(m, &mr->mrt_frags, msg_entry) {
12986e264ad0Stobhe if (sendtofrom(m->msg_fd, ibuf_data(m->msg_data),
12996e264ad0Stobhe ibuf_size(m->msg_data), 0,
13006e264ad0Stobhe (struct sockaddr *)&m->msg_peer, m->msg_peerlen,
13016e264ad0Stobhe (struct sockaddr *)&m->msg_local, m->msg_locallen) == -1) {
13025ec2ede8Svgross log_warn("%s: sendtofrom", __func__);
1303b41cc0c8Stobhe ikestat_inc(env, ikes_msg_send_failures);
1304c45fd413Smikeb return (-1);
1305c45fd413Smikeb }
1306f3610affStobhe log_info("%sretransmit %s res %u local %s peer %s",
1307f3610affStobhe SPI_SA(sa, NULL),
1308*2117af45Stobhe print_map(hdr->ike_exchange, ikev2_exchange_map),
13096e264ad0Stobhe m->msg_msgid,
131014e2a040Stb print_addr(&m->msg_local),
131114e2a040Stb print_addr(&m->msg_peer));
13126e264ad0Stobhe }
1313c45fd413Smikeb
13146e264ad0Stobhe timer_add(env, &mr->mrt_timer, IKED_RESPONSE_TIMEOUT);
1315b41cc0c8Stobhe ikestat_inc(env, ikes_retransmit_response);
1316c45fd413Smikeb return (0);
1317c45fd413Smikeb }
1318c45fd413Smikeb
1319c45fd413Smikeb void
ikev2_msg_response_timeout(struct iked * env,void * arg)1320c45fd413Smikeb ikev2_msg_response_timeout(struct iked *env, void *arg)
1321c45fd413Smikeb {
13226e264ad0Stobhe struct iked_msg_retransmit *mr = arg;
13236e264ad0Stobhe struct iked_sa *sa;
1324c45fd413Smikeb
13256e264ad0Stobhe sa = TAILQ_FIRST(&mr->mrt_frags)->msg_sa;
13266e264ad0Stobhe ikev2_msg_dispose(env, &sa->sa_responses, mr);
1327c45fd413Smikeb }
1328c45fd413Smikeb
1329c45fd413Smikeb void
ikev2_msg_retransmit_timeout(struct iked * env,void * arg)1330c45fd413Smikeb ikev2_msg_retransmit_timeout(struct iked *env, void *arg)
1331c45fd413Smikeb {
13326e264ad0Stobhe struct iked_msg_retransmit *mr = arg;
13336e264ad0Stobhe struct iked_message *msg = TAILQ_FIRST(&mr->mrt_frags);
1334c45fd413Smikeb struct iked_sa *sa = msg->msg_sa;
1335c45fd413Smikeb
13366e264ad0Stobhe if (mr->mrt_tries < IKED_RETRANSMIT_TRIES) {
13376e264ad0Stobhe TAILQ_FOREACH(msg, &mr->mrt_frags, msg_entry) {
13385ec2ede8Svgross if (sendtofrom(msg->msg_fd, ibuf_data(msg->msg_data),
1339c45fd413Smikeb ibuf_size(msg->msg_data), 0,
13405ec2ede8Svgross (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen,
13415ec2ede8Svgross (struct sockaddr *)&msg->msg_local,
13425ec2ede8Svgross msg->msg_locallen) == -1) {
13435ec2ede8Svgross log_warn("%s: sendtofrom", __func__);
134484a9a21bStobhe ikev2_ike_sa_setreason(sa, "retransmit failed");
1345c45fd413Smikeb sa_free(env, sa);
1346b41cc0c8Stobhe ikestat_inc(env, ikes_msg_send_failures);
1347c45fd413Smikeb return;
1348c45fd413Smikeb }
13496e264ad0Stobhe log_info("%sretransmit %d %s req %u peer %s "
13506e264ad0Stobhe "local %s", SPI_SA(sa, NULL), mr->mrt_tries + 1,
1351f3610affStobhe print_map(msg->msg_exchange, ikev2_exchange_map),
1352f3610affStobhe msg->msg_msgid,
135314e2a040Stb print_addr(&msg->msg_peer),
135414e2a040Stb print_addr(&msg->msg_local));
13556e264ad0Stobhe }
13566e264ad0Stobhe /* Exponential timeout */
13576e264ad0Stobhe timer_add(env, &mr->mrt_timer,
13586e264ad0Stobhe IKED_RETRANSMIT_TIMEOUT * (2 << (mr->mrt_tries++)));
1359b41cc0c8Stobhe ikestat_inc(env, ikes_retransmit_request);
13608d81c769Smikeb } else {
1361f3610affStobhe log_debug("%s: retransmit limit reached for req %u",
13627f7372eaSmarkus __func__, msg->msg_msgid);
136384a9a21bStobhe ikev2_ike_sa_setreason(sa, "retransmit limit reached");
1364b41cc0c8Stobhe ikestat_inc(env, ikes_retransmit_limit);
1365c45fd413Smikeb sa_free(env, sa);
1366c45fd413Smikeb }
13678d81c769Smikeb }
1368