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