Hi Stuart, I'm glad to see people are using this. There's some smaller fixes that I haven't sent to the list yet, so probably I'll send an updated diff on monday.
Regards, Tobias On 3/30/19 6:43 PM, Stuart Henderson wrote: > This diff hasn't gone anywhere recently - I've been using it since > Tobias posted it with no problems. Any comments on whether it should > go in, and if so, before/after 6.5? The feature is disabled by default. > > Index: config.c > =================================================================== > RCS file: /cvs/src/sbin/iked/config.c,v > retrieving revision 1.49 > diff -u -p -r1.49 config.c > --- config.c 27 Nov 2017 18:39:35 -0000 1.49 > +++ config.c 30 Mar 2019 17:41:33 -0000 > @@ -94,12 +94,30 @@ config_free_kex(struct iked_kex *kex) > } > > void > +config_free_fragments(struct iked_frag *frag) > +{ > + size_t i; > + if (frag && frag->frag_arr) { > + for (i = 0; i < frag->frag_count; i++) { > + free(frag->frag_arr[i]->frag_data); > + frag->frag_arr[i]->frag_data = NULL; > + free(frag->frag_arr[i]); > + frag->frag_arr[i] = NULL; > + } > + free(frag->frag_arr); > + frag->frag_arr = NULL; > + bzero(frag, sizeof(struct iked_frag)); > + } > +} > + > +void > config_free_sa(struct iked *env, struct iked_sa *sa) > { > timer_del(env, &sa->sa_timer); > timer_del(env, &sa->sa_keepalive); > timer_del(env, &sa->sa_rekey); > > + config_free_fragments(&sa->sa_fragments); > config_free_proposals(&sa->sa_proposals, 0); > config_free_childsas(env, &sa->sa_childsas, NULL, NULL); > sa_free_flows(env, &sa->sa_flows); > @@ -925,6 +943,29 @@ config_setkeys(struct iked *env) > EVP_PKEY_free(key); > > return (ret); > +} > + > +int > +config_setfragmentation(struct iked *env) > +{ > + unsigned int boolval; > + > + boolval = env->sc_frag; > + proc_compose(&env->sc_ps, PROC_IKEV2, IMSG_CTL_FRAGMENTATION, > + &boolval, sizeof(boolval)); > + return (0); > +} > + > +int > +config_getfragmentation(struct iked *env, struct imsg *imsg) > +{ > + unsigned int boolval; > + > + IMSG_SIZE_CHECK(imsg, &boolval); > + memcpy(&boolval, imsg->data, sizeof(boolval)); > + env->sc_frag = boolval; > + log_debug("%s: %sfragmentation", __func__, env->sc_frag ? "" : "no "); > + return (0); > } > > int > Index: iked.c > =================================================================== > RCS file: /cvs/src/sbin/iked/iked.c,v > retrieving revision 1.36 > diff -u -p -r1.36 iked.c > --- iked.c 27 Nov 2017 18:39:35 -0000 1.36 > +++ iked.c 30 Mar 2019 17:41:33 -0000 > @@ -251,6 +251,7 @@ parent_configure(struct iked *env) > fatal("pledge"); > > config_setmobike(env); > + config_setfragmentation(env); > config_setcoupled(env, env->sc_decoupled ? 0 : 1); > config_setmode(env, env->sc_passive ? 1 : 0); > config_setocsp(env); > @@ -282,6 +283,7 @@ parent_reload(struct iked *env, int rese > config_setcompile(env, PROC_IKEV2); > > config_setmobike(env); > + config_setfragmentation(env); > config_setcoupled(env, env->sc_decoupled ? 0 : 1); > config_setmode(env, env->sc_passive ? 1 : 0); > config_setocsp(env); > Index: iked.conf.5 > =================================================================== > RCS file: /cvs/src/sbin/iked/iked.conf.5,v > retrieving revision 1.53 > diff -u -p -r1.53 iked.conf.5 > --- iked.conf.5 31 Jan 2018 13:25:55 -0000 1.53 > +++ iked.conf.5 30 Mar 2019 17:41:33 -0000 > @@ -136,6 +136,12 @@ This is the default. > .It Ic set decouple > Don't load the negotiated SAs and flows from the kernel. > This mode is only useful for testing and debugging. > +.It Ic set fragmentation > +Enable IKEv2 Message Fragmentation (RFC 7383) support. > +This allows IKEv2 to operate in environments that might block IP fragments. > +.It Ic set nofragmentation > +Disables IKEv2 Message Fragmentation support. > +This is the default. > .It Ic set mobike > Enable MOBIKE (RFC 4555) support. > This is the default. > Index: iked.h > =================================================================== > RCS file: /cvs/src/sbin/iked/iked.h,v > retrieving revision 1.119 > diff -u -p -r1.119 iked.h > --- iked.h 6 Aug 2018 06:30:06 -0000 1.119 > +++ iked.h 30 Mar 2019 17:41:33 -0000 > @@ -362,6 +362,21 @@ struct iked_kex { > struct ibuf *kex_dhpeer; /* pointer to i or r */ > }; > > +struct iked_frag_entry { > + uint8_t *frag_data; > + size_t frag_size; > +}; > + > +struct iked_frag { > + struct iked_frag_entry **frag_arr; /* list of fragment > buffers */ > + size_t frag_count; /* number of fragments > received */ > +#define IKED_FRAG_TOTAL_MAX 111 /* upper limit of frag_total (64kB / > 576B) */ > + size_t frag_total; /* total numbe of > fragments */ > + size_t frag_total_size; > + uint8_t frag_nextpayload; > + > +}; > + > struct iked_sa { > struct iked_sahdr sa_hdr; > uint32_t sa_msgid; /* Last request rcvd */ > @@ -377,6 +392,8 @@ struct iked_sa { > struct iked_addr sa_local; > int sa_fd; > > + struct iked_frag sa_fragments; > + > int sa_natt; /* for IKE messages */ > int sa_udpencap; /* for pfkey */ > int sa_usekeepalive;/* NAT-T keepalive */ > @@ -445,6 +462,7 @@ struct iked_sa { > uint16_t sa_cpi_in; /* IPcomp incoming*/ > > int sa_mobike; /* MOBIKE */ > + int sa_frag; /* fragmentation */ > > struct iked_timer sa_timer; /* SA timeouts */ > #define IKED_IKE_SA_EXCHANGE_TIMEOUT 300 /* 5 minutes */ > @@ -602,6 +620,7 @@ struct iked { > uint8_t sc_decoupled; > > uint8_t sc_mobike; /* MOBIKE */ > + uint8_t sc_frag; /* fragmentation */ > > struct iked_policies sc_policies; > struct iked_policy *sc_defaultcon; > @@ -653,6 +672,7 @@ int control_listen(struct control_sock > struct iked_policy * > config_new_policy(struct iked *); > void config_free_kex(struct iked_kex *); > +void config_free_fragments(struct iked_frag *frag); > void config_free_sa(struct iked *, struct iked_sa *); > struct iked_sa * > config_new_sa(struct iked *, int); > @@ -701,6 +721,8 @@ int config_setkeys(struct iked *); > int config_getkey(struct iked *, struct imsg *); > int config_setmobike(struct iked *); > int config_getmobike(struct iked *, struct imsg *); > +int config_setfragmentation(struct iked *); > +int config_getfragmentation(struct iked *, struct imsg *); > > /* policy.c */ > void policy_init(struct iked *); > @@ -859,6 +881,12 @@ void ikev2_msg_flushqueue(struct iked * > struct iked_message * > ikev2_msg_lookup(struct iked *, struct iked_msgqueue *, > struct iked_message *, struct ike_header *); > +void ikev2_msg_lookup_dispose_all(struct iked *env, > + struct iked_msgqueue *queue, struct iked_message *msg, > + struct ike_header *hdr); > +int ikev2_msg_lookup_retransmit_all(struct iked *env, > + struct iked_msgqueue *queue, struct iked_message *msg, > + struct ike_header *hdr, struct iked_sa *sa); > > /* ikev2_pld.c */ > int ikev2_pld_parse(struct iked *, struct ike_header *, > Index: ikev2.c > =================================================================== > RCS file: /cvs/src/sbin/iked/ikev2.c,v > retrieving revision 1.168 > diff -u -p -r1.168 ikev2.c > --- ikev2.c 27 Feb 2019 06:33:56 -0000 1.168 > +++ ikev2.c 30 Mar 2019 17:41:33 -0000 > @@ -144,6 +144,8 @@ ssize_t ikev2_add_sighashnotify(struct i > ssize_t); > ssize_t ikev2_add_nat_detection(struct iked *, struct ibuf *, > struct ikev2_payload **, struct iked_message *, ssize_t); > +ssize_t ikev2_add_fragmentation(struct iked *, struct ibuf *, > + struct ikev2_payload **, struct iked_message *, ssize_t); > > ssize_t ikev2_add_mobike(struct iked *, struct ibuf *, > struct ikev2_payload **, ssize_t, struct iked_sa *); > @@ -202,6 +204,8 @@ ikev2_dispatch_parent(int fd, struct pri > return (0); > case IMSG_CTL_MOBIKE: > return (config_getmobike(env, imsg)); > + case IMSG_CTL_FRAGMENTATION: > + return (config_getfragmentation(env, imsg)); > case IMSG_UDP_SOCKET: > return (config_getsocket(env, imsg, ikev2_msg_cb)); > case IMSG_PFKEY_SOCKET: > @@ -399,9 +403,9 @@ void > ikev2_recv(struct iked *env, struct iked_message *msg) > { > struct ike_header *hdr; > - struct iked_message *m; > struct iked_sa *sa; > unsigned int initiator, flag = 0; > + int r; > > hdr = ibuf_seek(msg->msg_data, msg->msg_offset, sizeof(*hdr)); > > @@ -442,7 +446,8 @@ ikev2_recv(struct iked *env, struct iked > if (msg->msg_msgid > sa->sa_reqid) > return; > if (hdr->ike_exchange != IKEV2_EXCHANGE_INFORMATIONAL && > - !ikev2_msg_lookup(env, &sa->sa_requests, msg, hdr)) > + !ikev2_msg_lookup(env, &sa->sa_requests, msg, hdr) && > + sa->sa_fragments.frag_count == 0) > return; > if (flag) { > if ((sa->sa_stateflags & flag) == 0) > @@ -454,10 +459,9 @@ ikev2_recv(struct iked *env, struct iked > initiator = 1; > } > /* > - * There's no need to keep the request around anymore > + * There's no need to keep the request (fragments) around > anymore > */ > - if ((m = ikev2_msg_lookup(env, &sa->sa_requests, msg, hdr))) > - ikev2_msg_dispose(env, &sa->sa_requests, m); > + ikev2_msg_lookup_dispose_all(env, &sa->sa_requests, msg, hdr); > } else { > /* > * IKE_SA_INIT is special since it always uses the message id 0. > @@ -483,14 +487,16 @@ ikev2_recv(struct iked *env, struct iked > /* > * See if we have responded to this request before > */ > - if ((m = ikev2_msg_lookup(env, &sa->sa_responses, msg, hdr))) { > - if (ikev2_msg_retransmit_response(env, sa, m)) { > + if ((r = ikev2_msg_lookup_retransmit_all(env, &sa->sa_responses, > + msg, hdr, sa)) != 0) { > + if (r == -1) { > log_warn("%s: failed to retransmit a " > "response", __func__); > sa_free(env, sa); > } > return; > - } else if (sa->sa_msgid_set && msg->msg_msgid == sa->sa_msgid) { > + } else if (sa->sa_msgid_set && msg->msg_msgid == sa->sa_msgid && > + !(sa->sa_fragments.frag_count)) { > /* > * Response is being worked on, most likely we're > * waiting for the CA process to get back to us > @@ -803,6 +809,9 @@ ikev2_init_recv(struct iked *env, struct > return; > } > > + if (sa->sa_fragments.frag_count != 0) > + return; > + > if (!ikev2_msg_frompeer(msg)) > return; > > @@ -1022,6 +1031,13 @@ ikev2_init_ike_sa_peer(struct iked *env, > goto done; > len = ibuf_size(sa->sa_inonce); > > + /* Fragmentation Notify */ > + if (env->sc_frag) { > + if ((len = ikev2_add_fragmentation(env, buf, &pld, &req, len)) > + == -1) > + goto done; > + } > + > if ((env->sc_opts & IKED_OPT_NONATT) == 0) { > if (ntohs(port) == IKED_NATT_PORT) { > /* Enforce NAT-T on the initiator side */ > @@ -1973,6 +1989,29 @@ ikev2_add_cp(struct iked *env, struct ik > } > > ssize_t > +ikev2_add_fragmentation(struct iked *env, struct ibuf *buf, > + struct ikev2_payload **pld, struct iked_message *msg, ssize_t len) > +{ > + struct ikev2_notify *n; > + uint8_t *ptr; > + > + if (*pld != NULL) > + if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) > + return (-1); > + if ((*pld = ikev2_add_payload(buf)) == NULL) > + return (-1); > + len = sizeof(*n); > + if ((ptr = ibuf_advance(buf, len)) == NULL) > + return (-1); > + n = (struct ikev2_notify *) ptr; > + n->n_protoid = 0; > + n->n_spisize = 0; > + n->n_type = htobe16(IKEV2_N_FRAGMENTATION_SUPPORTED); > + > + return (len); > +} > + > +ssize_t > ikev2_add_proposals(struct iked *env, struct iked_sa *sa, struct ibuf *buf, > struct iked_proposals *proposals, uint8_t protoid, int initiator, > int sendikespi, int skipdh) > @@ -2283,6 +2322,9 @@ ikev2_resp_recv(struct iked *env, struct > if ((sa = msg->msg_sa) == NULL) > return; > > + if (sa->sa_fragments.frag_count !=0) > + return; > + > if (msg->msg_natt && sa->sa_natt == 0) { > log_debug("%s: NAT-T message received, updated SA", __func__); > sa->sa_natt = 1; > @@ -2407,6 +2449,13 @@ ikev2_resp_ike_sa_init(struct iked *env, > goto done; > len = ibuf_size(sa->sa_rnonce); > > + /* Fragmentation Notify*/ > + if (sa->sa_frag) { > + if ((len = ikev2_add_fragmentation(env, buf, &pld, &resp, len)) > + == -1) > + goto done; > + } > + > if ((env->sc_opts & IKED_OPT_NONATT) == 0 && > msg->msg_local.ss_family != AF_UNSPEC) { > if ((len = ikev2_add_nat_detection(env, buf, &pld, &resp, len)) > @@ -2485,6 +2534,7 @@ ikev2_send_auth_failed(struct iked *env, > timer_del(env, &sa->sa_timer); > timer_set(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa); > timer_add(env, &sa->sa_timer, IKED_IKE_SA_DELETE_TIMEOUT); > + config_free_fragments(&sa->sa_fragments); > > return (ret); > } > Index: ikev2.h > =================================================================== > RCS file: /cvs/src/sbin/iked/ikev2.h,v > retrieving revision 1.28 > diff -u -p -r1.28 ikev2.h > --- ikev2.h 27 Feb 2019 06:33:57 -0000 1.28 > +++ ikev2.h 30 Mar 2019 17:41:33 -0000 > @@ -78,6 +78,11 @@ struct ikev2_payload { > uint16_t pld_length; /* Payload length with header */ > } __packed; > > +struct ikev2_frag_payload { > + uint16_t frag_num; /* current fragment message > number */ > + uint16_t frag_total; /* total number of fragment > messages */ > +} __packed; > + > #define IKEV2_CRITICAL_PAYLOAD 0x01 /* First bit in the reserved > field */ > > /* IKEv2 payload types */ > @@ -99,6 +104,7 @@ struct ikev2_payload { > #define IKEV2_PAYLOAD_CP 47 /* Configuration Payload */ > #define IKEV2_PAYLOAD_EAP 48 /* Extensible Authentication */ > #define IKEV2_PAYLOAD_GSPM 49 /* RFC6467 Generic Secure Password */ > +#define IKEV2_PAYLOAD_SKF 53 /* RFC7383 Encrypted Fragment Payload */ > > extern struct iked_constmap ikev2_payload_map[]; > > @@ -243,6 +249,11 @@ extern struct iked_constmap ikev2_xforma > #define IKEV2_XFORMDH_X_CURVE25519 1034 /* > draft-ietf-ipsecme-safecurves-00 */ > > extern struct iked_constmap ikev2_xformdh_map[]; > + > +#define IKEV2_IPV4_OVERHEAD (20 + 8 + 28) /* IPv4 + UDP + IKE_HDR*/ > +#define IKEV2_MAXLEN_IPV4_FRAG (576 - IKEV2_IPV4_OVERHEAD) > +#define IKEV2_IPV6_OVERHEAD (40 + 8 + 28) /* IPv6 + UDP + IKE_HDR*/ > +#define IKEV2_MAXLEN_IPV6_FRAG (1280 - IKEV2_IPV6_OVERHEAD) > > #define IKEV2_XFORMESN_NONE 0 /* No ESN */ > #define IKEV2_XFORMESN_ESN 1 /* ESN */ > Index: ikev2_msg.c > =================================================================== > RCS file: /cvs/src/sbin/iked/ikev2_msg.c,v > retrieving revision 1.53 > diff -u -p -r1.53 ikev2_msg.c > --- ikev2_msg.c 27 Nov 2017 18:39:35 -0000 1.53 > +++ ikev2_msg.c 30 Mar 2019 17:41:33 -0000 > @@ -46,6 +46,9 @@ > void ikev1_recv(struct iked *, struct iked_message *); > void ikev2_msg_response_timeout(struct iked *, void *); > void ikev2_msg_retransmit_timeout(struct iked *, void *); > +int ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf); > +int ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa, > + struct ibuf *in,uint8_t exchange, uint8_t firstpayload, int > response); > > void > ikev2_msg_cb(int fd, short event, void *arg) > @@ -616,7 +619,8 @@ ikev2_msg_decrypt(struct iked *env, stru > __func__, outlen, encrlen, pad); > print_hex(ibuf_data(out), 0, ibuf_size(out)); > > - if (ibuf_setsize(out, outlen) != 0) > + /* Strip padding and padding length */ > + if (ibuf_setsize(out, outlen - pad - 1) != 0) > goto done; > > ibuf_release(src); > @@ -629,6 +633,25 @@ ikev2_msg_decrypt(struct iked *env, stru > } > > int > +ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf) { > + size_t len = ibuf_length(buf); > + sa_family_t sa_fam; > + size_t max; > + size_t ivlen, integrlen, blocklen; > + > + sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family; > + > + max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG > + : IKEV2_MAXLEN_IPV6_FRAG; > + > + blocklen = cipher_length(sa->sa_encr); > + ivlen = cipher_ivlength(sa->sa_encr); > + integrlen = hash_length(sa->sa_integr); > + > + return ((len + ivlen + blocklen + integrlen) >= max) && sa->sa_frag; > +} > + > +int > ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf > **ep, > uint8_t exchange, uint8_t firstpayload, int response) > { > @@ -638,6 +661,12 @@ ikev2_msg_send_encrypt(struct iked *env, > struct ibuf *buf, *e = *ep; > int ret = -1; > > + /* Check if msg needs to be fragmented */ > + if (ikev2_check_frag_oversize(sa, e)) { > + return ikev2_send_encrypted_fragments(env, sa, e, exchange, > + firstpayload, response); > + } > + > if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr, > sa->sa_peer.addr.ss_len, &sa->sa_local.addr, > sa->sa_local.addr.ss_len, response)) == NULL) > @@ -689,6 +718,123 @@ ikev2_msg_send_encrypt(struct iked *env, > return (ret); > } > > +int > +ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa, > + struct ibuf *in, uint8_t exchange, uint8_t firstpayload, int response) { > + struct iked_message resp; > + struct ibuf *buf, *e; > + struct ike_header *hdr; > + struct ikev2_payload *pld; > + struct ikev2_frag_payload *frag; > + sa_family_t sa_fam; > + size_t ivlen, integrlen, blocklen; > + size_t max_len, left, offset=0;; > + size_t frag_num = 1, frag_total; > + uint8_t *data; > + uint32_t msgid; > + int ret = -1; > + > + sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family; > + > + left = ibuf_length(in); > + > + /* Calculate max allowed size of a fragments payload */ > + blocklen = cipher_length(sa->sa_encr); > + ivlen = cipher_ivlength(sa->sa_encr); > + integrlen = hash_length(sa->sa_integr); > + max_len = (sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG > + : IKEV2_MAXLEN_IPV6_FRAG) > + - ivlen - blocklen - integrlen; > + > + /* Total number of fragments to send */ > + frag_total = (left / max_len) + 1; > + > + msgid = response ? sa->sa_msgid : ikev2_msg_id(env, sa); > + > + while (frag_num <= frag_total) { > + if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr, > + sa->sa_peer.addr.ss_len, &sa->sa_local.addr, > + sa->sa_local.addr.ss_len, response)) == NULL) > + goto done; > + > + resp.msg_msgid = msgid; > + > + /* IKE header */ > + if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, > + IKEV2_PAYLOAD_SKF, exchange, response ? IKEV2_FLAG_RESPONSE > + : 0)) == NULL) > + goto done; > + > + /* Payload header */ > + if ((pld = ikev2_add_payload(buf)) == NULL) > + goto done; > + > + /* Fragment header */ > + if ((frag = ibuf_advance(buf, sizeof(*frag))) == NULL) { > + log_debug("%s: failed to add SKF fragment header", > + __func__); > + goto done; > + } > + frag->frag_num = htobe16(frag_num); > + frag->frag_total = htobe16(frag_total); > + > + /* Encrypt message and add as an E payload */ > + data = ibuf_seek(in, offset, 0); > + if((e=ibuf_new(data, MIN(left, max_len))) == NULL) { > + goto done; > + } > + if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) { > + log_debug("%s: encryption failed", __func__); > + goto done; > + } > + if (ibuf_cat(buf, e) != 0) > + goto done; > + > + if (ikev2_next_payload(pld, ibuf_size(e) + sizeof(*frag), > + firstpayload) == -1) > + goto done; > + > + if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) > + goto done; > + > + /* Add integrity checksum (HMAC) */ > + if (ikev2_msg_integr(env, sa, buf) != 0) { > + log_debug("%s: integrity checksum failed", __func__); > + goto done; > + } > + > + log_debug("%s: Fragment %zu of %zu has size of %zu bytes.", > + __func__, frag_num, frag_total, > + ibuf_size(buf) - sizeof(*hdr)); > + print_hex(ibuf_data(buf), 0, ibuf_size(buf)); > + > + resp.msg_data = buf; > + resp.msg_sa = sa; > + resp.msg_fd = sa->sa_fd; > + TAILQ_INIT(&resp.msg_proposals); > + > + if (ikev2_msg_send(env, &resp) == -1) > + goto done; > + > + offset += MIN(left, max_len); > + left -= MIN(left, max_len); > + frag_num++; > + > + /* MUST be zero after first fragment */ > + firstpayload = 0; > + > + ikev2_msg_cleanup(env, &resp); > + ibuf_release(e); > + e = NULL; > + } > + > + return 0; > +done: > + ikev2_msg_cleanup(env, &resp); > + ibuf_release(e); > + return ret; > +} > + > struct ibuf * > ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response) > { > @@ -990,6 +1136,42 @@ ikev2_msg_lookup(struct iked *env, struc > } > > return (m); > +} > + > +void > +ikev2_msg_lookup_dispose_all(struct iked *env, struct iked_msgqueue *queue, > + struct iked_message *msg, struct ike_header *hdr) > +{ > + struct iked_message *m = NULL, *tmp = NULL; > + > + TAILQ_FOREACH_SAFE(m, queue, msg_entry, tmp) { > + if (m->msg_msgid == msg->msg_msgid && > + m->msg_exchange == hdr->ike_exchange) { > + TAILQ_REMOVE(queue, m, msg_entry); > + timer_del(env, &m->msg_timer); > + ikev2_msg_cleanup(env, m); > + free(m); > + } > + } > +} > + > +int > +ikev2_msg_lookup_retransmit_all(struct iked *env, struct iked_msgqueue > *queue, > + struct iked_message *msg, struct ike_header *hdr, struct iked_sa *sa) > +{ > + struct iked_message *m = NULL, *tmp = NULL; > + int count = 0; > + > + TAILQ_FOREACH_SAFE(m, queue, msg_entry, tmp) { > + if (m->msg_msgid == msg->msg_msgid && > + m->msg_exchange == hdr->ike_exchange) { > + if (ikev2_msg_retransmit_response(env, sa, msg)) { > + return -1; > + } > + count++; > + } > + } > + return count; > } > > int > Index: ikev2_pld.c > =================================================================== > RCS file: /cvs/src/sbin/iked/ikev2_pld.c,v > retrieving revision 1.70 > diff -u -p -r1.70 ikev2_pld.c > --- ikev2_pld.c 22 Mar 2018 21:11:49 -0000 1.70 > +++ ikev2_pld.c 30 Mar 2019 17:41:33 -0000 > @@ -95,6 +95,10 @@ int ikev2_pld_auth(struct iked *, struc > struct iked_message *, size_t, size_t); > int ikev2_pld_e(struct iked *, struct ikev2_payload *, > struct iked_message *, size_t, size_t); > +int ikev2_pld_ef(struct iked *env, struct ikev2_payload *pld, > + struct iked_message *msg, size_t offset, size_t left); > +int ikev2_frags_reassemble(struct iked *env, > + struct ikev2_payload *pld, struct iked_message *msg); > int ikev2_validate_cp(struct iked_message *, size_t, size_t, > struct ikev2_cp *); > int ikev2_pld_cp(struct iked *, struct ikev2_payload *, > @@ -249,6 +253,9 @@ ikev2_pld_payloads(struct iked *env, str > case IKEV2_PAYLOAD_SK: > ret = ikev2_pld_e(env, &pld, msg, offset, left); > break; > + case IKEV2_PAYLOAD_SKF: > + ret = ikev2_pld_ef(env, &pld, msg, offset, left); > + break; > case IKEV2_PAYLOAD_CP | IKED_E: > ret = ikev2_pld_cp(env, &pld, msg, offset, left); > break; > @@ -266,8 +273,8 @@ ikev2_pld_payloads(struct iked *env, str > return (-1); > } > > - /* Encrypted payload must appear last */ > - if (payload == IKEV2_PAYLOAD_SK) > + /* Encrypted payloads must appear last */ > + if ((payload == IKEV2_PAYLOAD_SK) || (payload == > IKEV2_PAYLOAD_SKF)) > return (0); > > payload = pld.pld_nextpayload; > @@ -1251,6 +1258,23 @@ ikev2_pld_notify(struct iked *env, struc > } > msg->msg_parent->msg_cookie = msg->msg_cookie; > break; > + case IKEV2_N_FRAGMENTATION_SUPPORTED: > + if (msg->msg_e) { > + log_debug("%s: N_FRAGMENTATION_SUPPORTED encrypted", > + __func__); > + return (-1); > + } > + if (len != 0) { > + log_debug("%s: ignoring malformed fragmentation" > + " notification: %zu", __func__, len); > + return (0); > + } > + if (!env->sc_frag) { > + log_debug("%s: fragmentation disabled", __func__); > + return (0); > + } > + msg->msg_sa->sa_frag = 1; > + break; > case IKEV2_N_SIGNATURE_HASH_ALGORITHMS: > if (msg->msg_e) { > log_debug("%s: SIGNATURE_HASH_ALGORITHMS: encrypted", > @@ -1585,6 +1609,164 @@ ikev2_pld_ts(struct iked *env, struct ik > } > > int > +ikev2_pld_ef(struct iked *env, struct ikev2_payload *pld, > + struct iked_message *msg, size_t offset, size_t left) > +{ > + struct iked_sa *sa = msg->msg_sa; > + struct iked_frag *sa_frag = &sa->sa_fragments; > + struct iked_frag_entry *el = NULL; > + struct ikev2_frag_payload frag; > + uint8_t *msgbuf = ibuf_data(msg->msg_data); > + uint8_t *buf; > + struct ibuf *e = NULL; > + size_t frag_num, frag_total; > + size_t len; > + int ret = -1; > + ssize_t elen; > + > + buf = msgbuf + offset; > + memcpy(&frag, buf, sizeof(frag)); > + frag_num = betoh16(frag.frag_num); > + frag_total = betoh16(frag.frag_total); > + > + offset += sizeof(frag); > + buf = msgbuf + offset; > + len = left - sizeof(frag); > + > + /* Limit number of total fragments to avoid DOS */ > + if (frag_total > IKED_FRAG_TOTAL_MAX ) { > + log_debug("%s: Total Fragments too big %zu", > + __func__, frag_total); > + goto dropall; > + } > + > + /* Check sanity of fragment header */ > + if (frag_num == 0 || frag_total == 0) { > + log_debug("%s: Malformed fragment received: %zu of %zu", > + __func__, frag_num, frag_total); > + goto done; > + } > + log_debug("%s: Received fragment: %zu of %zu", > + __func__, frag_num, frag_total); > + > + /* Check new fragmented message */ > + if (sa_frag->frag_arr == NULL) { > + sa_frag->frag_arr = reallocarray(NULL, frag_total, > sizeof(uint8_t*)); > + bzero(sa_frag->frag_arr, frag_total * sizeof(uint8_t*)); > + sa_frag->frag_total = frag_total; > + sa_frag->frag_nextpayload = pld->pld_nextpayload; > + } > + > + /* Drop all fragments if frag_num or frag_total don't match */ > + if (frag_num > sa_frag->frag_total || frag_total > sa_frag->frag_total) > + goto dropall; > + > + /* Silent drop if fragment already stored */ > + if (sa_frag->frag_arr[frag_num-1] != NULL) > + goto done; > + > + /* Decrypt fragment */ > + if ((e = ibuf_new(buf, len)) == NULL) > + goto done; > + > + if ((e = ikev2_msg_decrypt(env, msg->msg_sa, msg->msg_data, e)) > + == NULL ) { > + log_debug("%s: Failed to decrypt fragment: %zu of %zu", > + __func__, frag_num, frag_total); > + goto done; > + } > + elen = ibuf_length(e); > + > + /* Insert new list element */ > + el = malloc(sizeof(struct iked_frag_entry)); > + if (el == NULL) { > + log_debug("%s: Failed allocating new fragment: %zu of %zu", > + __func__, frag_num, frag_total); > + goto done; > + } > + bzero(el, sizeof(*el)); > + > + sa_frag->frag_arr[frag_num-1] = el; > + el->frag_size = elen; > + el->frag_data = malloc(elen); > + if (el->frag_data == NULL) { > + log_debug("%s: Failed allocating new fragment data: %zu of %zu", > + __func__, frag_num, frag_total); > + goto done; > + } > + bzero(el->frag_data, sizeof(elen)); > + > + /* Copy plaintext to fragment */ > + memcpy(el->frag_data, ibuf_seek(e, 0, 0), elen); > + sa_frag->frag_total_size += elen; > + sa_frag->frag_count++; > + > + /* If all frags are received start reassembly */ > + if (sa_frag->frag_count == sa_frag->frag_total) { > + log_debug("%s: All fragments received: %zu of %zu", > + __func__, frag_num, frag_total); > + ret = ikev2_frags_reassemble(env, pld, msg); > + } else { > + ret = 0; > + } > +done: > + ibuf_release(e); > + return (ret); > +dropall: > + config_free_fragments(sa_frag); > + ibuf_release(e); > + return -1; > +} > + > +int > +ikev2_frags_reassemble(struct iked *env, struct ikev2_payload *pld, > + struct iked_message *msg) > +{ > + struct iked_frag *sa_frag = &msg->msg_sa->sa_fragments; > + struct ibuf *e = NULL; > + struct iked_frag_entry *el; > + size_t offset = 0; > + size_t i; > + struct iked_message emsg; > + int ret = -1; > + > + /* Reassemble fragments to single buffer */ > + if ((e = ibuf_new(NULL, sa_frag->frag_total_size)) == NULL) { > + log_debug("%s: Failed allocating SK buffer.", __func__); > + goto done; > + } > + > + /* Empty queue to new buffer */ > + for (i = 0; i < sa_frag->frag_total; i++) { > + el = sa_frag->frag_arr[i]; > + memcpy(ibuf_seek(e, offset, 0), el->frag_data, el->frag_size); > + offset += el->frag_size; > + } > + > + log_debug("%s: Defragmented length %zd", __func__, > + sa_frag->frag_total_size); > + print_hex(ibuf_data(e), 0, sa_frag->frag_total_size); > + > + /* > + * Parse decrypted payload > + */ > + bzero(&emsg, sizeof(emsg)); > + memcpy(&emsg, msg, sizeof(*msg)); > + emsg.msg_data = e; > + emsg.msg_e = 1; > + emsg.msg_parent = msg; > + TAILQ_INIT(&emsg.msg_proposals); > + > + ret = ikev2_pld_payloads(env, &emsg, 0, ibuf_size(e), > + sa_frag->frag_nextpayload); > +done: > + config_free_fragments(sa_frag); > + ibuf_release(e); > + > + return (ret); > +} > + > +int > ikev2_pld_e(struct iked *env, struct ikev2_payload *pld, > struct iked_message *msg, size_t offset, size_t left) > { > @@ -1595,6 +1777,13 @@ ikev2_pld_e(struct iked *env, struct ike > uint8_t *buf; > size_t len; > int ret = -1; > + > + if (sa->sa_fragments.frag_arr != NULL) { > + log_warn("%s: Received SK payload when SKFs are in queue.", > + __func__); > + config_free_fragments(&sa->sa_fragments); > + return (ret); > + } > > buf = msgbuf + offset; > len = left; > Index: parse.y > =================================================================== > RCS file: /cvs/src/sbin/iked/parse.y,v > retrieving revision 1.78 > diff -u -p -r1.78 parse.y > --- parse.y 13 Feb 2019 22:57:07 -0000 1.78 > +++ parse.y 30 Mar 2019 17:41:33 -0000 > @@ -105,6 +105,7 @@ static int rules = 0; > static int passive = 0; > static int decouple = 0; > static int mobike = 1; > +static int fragmentation = 0; > static char *ocsp_url = NULL; > > struct ipsec_xf { > @@ -395,6 +396,7 @@ typedef struct { > %token IKEV1 FLOW SA TCPMD5 TUNNEL TRANSPORT COUPLE DECOUPLE SET > %token INCLUDE LIFETIME BYTES INET INET6 QUICK SKIP DEFAULT > %token IPCOMP OCSP IKELIFETIME MOBIKE NOMOBIKE > +%token FRAGMENTATION NOFRAGMENTATION > %token <v.string> STRING > %token <v.number> NUMBER > %type <v.string> string > @@ -455,6 +457,8 @@ set : SET ACTIVE { passive = 0; } > | SET PASSIVE { passive = 1; } > | SET COUPLE { decouple = 0; } > | SET DECOUPLE { decouple = 1; } > + | SET FRAGMENTATION { fragmentation = 1; } > + | SET NOFRAGMENTATION { fragmentation = 0; } > | SET MOBIKE { mobike = 1; } > | SET NOMOBIKE { mobike = 0; } > | SET OCSP STRING { > @@ -1167,6 +1171,7 @@ lookup(char *s) > { "esp", ESP }, > { "file", FILENAME }, > { "flow", FLOW }, > + { "fragmentation", FRAGMENTATION }, > { "from", FROM }, > { "group", GROUP }, > { "ike", IKEV1 }, > @@ -1181,6 +1186,7 @@ lookup(char *s) > { "local", LOCAL }, > { "mobike", MOBIKE }, > { "name", NAME }, > + { "nofragmentation", NOFRAGMENTATION }, > { "nomobike", NOMOBIKE }, > { "ocsp", OCSP }, > { "passive", PASSIVE }, > @@ -1579,6 +1585,7 @@ parse_config(const char *filename, struc > free(ocsp_url); > > mobike = 1; > + fragmentation = 0; > decouple = passive = 0; > ocsp_url = NULL; > > @@ -1592,6 +1599,7 @@ parse_config(const char *filename, struc > env->sc_passive = passive ? 1 : 0; > env->sc_decoupled = decouple ? 1 : 0; > env->sc_mobike = mobike; > + env->sc_frag = fragmentation; > env->sc_ocsp_url = ocsp_url; > > if (!rules) > Index: types.h > =================================================================== > RCS file: /cvs/src/sbin/iked/types.h,v > retrieving revision 1.29 > diff -u -p -r1.29 types.h > --- types.h 27 Nov 2017 18:39:35 -0000 1.29 > +++ types.h 30 Mar 2019 17:41:33 -0000 > @@ -103,6 +103,7 @@ enum imsg_type { > IMSG_CTL_ACTIVE, > IMSG_CTL_PASSIVE, > IMSG_CTL_MOBIKE, > + IMSG_CTL_FRAGMENTATION, > IMSG_COMPILE, > IMSG_UDP_SOCKET, > IMSG_PFKEY_SOCKET, >