Here's the update. What changed: - fixed cleanup of fragments in SA - fixed retransmission of fragmented messages - adjusted copyright headers - Added some comments
I also included Stuart's manpage parts as well as some line breaks. We've been testing this version and haven't found anything off so far, looking forward to your results. Regards, Tobias Index: regress/sbin/iked/parser/common.c =================================================================== RCS file: /mount/openbsd/cvs/src/regress/sbin/iked/parser/common.c,v retrieving revision 1.1 diff -u -p -u -r1.1 common.c --- regress/sbin/iked/parser/common.c 29 May 2017 20:59:28 -0000 1.1 +++ regress/sbin/iked/parser/common.c 1 Apr 2019 12:46:40 -0000 @@ -166,6 +166,11 @@ config_add_proposal(struct iked_proposal return (NULL); } +void config_free_fragments(struct iked_frag *frag) +{ + return; +} + int ikev2_send_informational(struct iked *env, struct iked_message *msg) { Index: regress/sbin/iked/parser/test_parser_fuzz.c =================================================================== RCS file: /mount/openbsd/cvs/src/regress/sbin/iked/parser/test_parser_fuzz.c,v retrieving revision 1.2 diff -u -p -u -r1.2 test_parser_fuzz.c --- regress/sbin/iked/parser/test_parser_fuzz.c 22 Mar 2018 21:11:49 -0000 1.2 +++ regress/sbin/iked/parser/test_parser_fuzz.c 1 Apr 2019 12:46:40 -0000 @@ -183,6 +183,50 @@ u_int8_t ts_pld[] = { 0xac, 0x28, 0x7d, 0x00, 0xac, 0x28, 0x7d, 0xff }; +uint8_t skf_1of1_pld[] = { + 0x21, 0x00, 0x01, 0x98, 0x00, 0x01, 0x00, 0x01, 0x14, 0x77, + 0x25, 0x7b, 0x82, 0xc0, 0xdb, 0x0b, 0x24, 0x36, 0x36, 0x13, + 0x36, 0xe4, 0x99, 0xad, 0xf5, 0xaf, 0x26, 0x6f, 0x47, 0xd2, + 0x0d, 0x65, 0xe1, 0xa8, 0xcb, 0x35, 0x1e, 0x53, 0xce, 0x6d, + 0x8e, 0xf9, 0xe4, 0x51, 0xe3, 0x27, 0x10, 0x43, 0x38, 0x84, + 0x54, 0x1d, 0x7a, 0x1a, 0x89, 0x34, 0x06, 0xb3, 0x62, 0x86, + 0x98, 0x3b, 0x39, 0x91, 0x6e, 0xe8, 0x65, 0x3e, 0x31, 0xa8, + 0x08, 0xfe, 0x83, 0x56, 0x30, 0xd3, 0xe0, 0xfd, 0x73, 0x92, + 0x85, 0x2d, 0xae, 0x1d, 0x7d, 0xdb, 0x47, 0x05, 0x57, 0xe7, + 0x8e, 0xc5, 0xa5, 0x1b, 0x0e, 0x85, 0x1f, 0x12, 0x6d, 0xe6, + 0xdb, 0x3a, 0x3e, 0x99, 0xd1, 0x23, 0x41, 0xa4, 0x1c, 0x46, + 0x38, 0xd1, 0xa8, 0x84, 0x96, 0x13, 0xdb, 0x2a, 0x1d, 0x3b, + 0xb8, 0xd2, 0x04, 0xb3, 0x0d, 0xb4, 0x71, 0x90, 0xdb, 0xf6, + 0x2d, 0x60, 0x01, 0xc2, 0xb2, 0x89, 0xbd, 0xe9, 0x95, 0x7b, + 0x53, 0xa4, 0x94, 0x7e, 0x12, 0xe9, 0x5f, 0xfc, 0x51, 0x17, + 0x94, 0x3e, 0xba, 0xc2, 0xa5, 0x4d, 0x3a, 0x4d, 0x4b, 0x95, + 0x6d, 0x91, 0xc2, 0xb0, 0x2d, 0xb7, 0x24, 0xe8, 0x3b, 0xbd, + 0xe0, 0xcc, 0x09, 0x50, 0x11, 0x83, 0xc0, 0xcd, 0x29, 0x33, + 0xd5, 0x8f, 0x8a, 0xd1, 0xe3, 0xe8, 0x4f, 0x6a, 0x10, 0x4a, + 0x64, 0x97, 0x0f, 0x38, 0x58, 0x8d, 0x7f, 0x5d, 0xb4, 0x6b, + 0xa0, 0x42, 0x5e, 0x95, 0xe6, 0x08, 0x3e, 0x01, 0xf8, 0x82, + 0x90, 0x81, 0xd4, 0x70, 0xb5, 0xb2, 0x8c, 0x64, 0xa9, 0x56, + 0xdd, 0xc2, 0xda, 0xe1, 0xd3, 0xad, 0xf8, 0x5b, 0x99, 0x0b, + 0x19, 0x5e, 0x88, 0x0d, 0x81, 0x04, 0x4d, 0xc1, 0x43, 0x41, + 0xf1, 0xd3, 0x45, 0x65, 0x62, 0x70, 0x2f, 0xfa, 0x62, 0xbe, + 0x7d, 0xf4, 0x94, 0x91, 0xe0, 0xbb, 0xb1, 0xbc, 0xe5, 0x27, + 0xc8, 0x15, 0xd4, 0xcb, 0x82, 0x97, 0x15, 0x46, 0x82, 0xbb, + 0x48, 0xbb, 0x16, 0x25, 0xbe, 0x82, 0xe4, 0x27, 0x80, 0xf3, + 0xc2, 0x92, 0x3b, 0xd6, 0xc3, 0x65, 0x20, 0xec, 0x50, 0xdb, + 0x6a, 0xcb, 0x47, 0x73, 0xf7, 0x98, 0xf1, 0x66, 0x5e, 0xc4, + 0xe9, 0x87, 0xf8, 0xcb, 0x1e, 0x06, 0xa7, 0x67, 0xf5, 0xec, + 0x73, 0xe5, 0xc7, 0x4d, 0xc2, 0x90, 0xe4, 0xdf, 0x9d, 0x1f, + 0x05, 0x67, 0x99, 0xd6, 0xf0, 0xc4, 0x20, 0xbc, 0xf8, 0xf5, + 0x3e, 0x19, 0xe9, 0x3a, 0x12, 0xe1, 0xcc, 0x9f, 0x81, 0x55, + 0x1e, 0xad, 0xc8, 0xa3, 0xe5, 0x98, 0xbe, 0xe0, 0x4d, 0xb7, + 0x6b, 0xd5, 0xbe, 0x6a, 0x3d, 0x76, 0xb6, 0xe2, 0xa5, 0xa7, + 0x96, 0x68, 0xeb, 0x91, 0xee, 0x02, 0xfc, 0xe4, 0x01, 0xc3, + 0x24, 0xda, 0x4c, 0xff, 0x10, 0x27, 0x78, 0xb0, 0x0b, 0x55, + 0x5c, 0xce, 0x62, 0x7d, 0x33, 0x2b, 0x25, 0x99, 0xaa, 0x99, + 0xea, 0xa3, 0x1d, 0xd8, 0x2b, 0x57, 0xb5, 0xe4, 0x04, 0x21, + 0x75, 0xd9, 0xc4, 0xd0, 0x3d, 0xa1, 0xa5, 0x8f +}; + u_int8_t sk_pld[] = { 0x21, 0x00, 0x01, 0x94, 0x14, 0x77, 0x25, 0x7b, 0x82, 0xc0, 0xdb, 0x0b, 0x24, 0x36, 0x36, 0x13, 0x36, 0xe4, 0x99, 0xad, @@ -441,6 +485,25 @@ parser_fuzz_tests(void) ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); set_length(ibuf_data(data), ibuf_size(data)); + print_hex(ibuf_data(data), 0, ibuf_size(data)); + prepare_header(&hdr, data); + prepare_message(&msg, data); + ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); + fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | + FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | + FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | + FUZZ_BASE64, + ibuf_data(data), ibuf_size(data)); + ibuf_free(data); + perform_test(fuzz); + TEST_DONE(); + + TEST_START("fuzz skf_1of1 payload"); + ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); + ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); + ASSERT_INT_EQ(ibuf_add(data, skf_1of1_pld, sizeof(skf_1of1_pld)), 0); + set_length(ibuf_data(data), ibuf_size(data)); + set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_SKF); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); Index: sbin/iked/config.c =================================================================== RCS file: /mount/openbsd/cvs/src/sbin/iked/config.c,v retrieving revision 1.49 diff -u -p -u -r1.49 config.c --- sbin/iked/config.c 27 Nov 2017 18:39:35 -0000 1.49 +++ sbin/iked/config.c 1 Apr 2019 14:58:00 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: config.c,v 1.49 2017/11/27 18:39:35 patrick Exp $ */ /* + * Copyright (c) 2019 Tobias Heider <tobias.hei...@stusta.de> * Copyright (c) 2010-2013 Reyk Floeter <r...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -94,12 +95,29 @@ 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_total; i++) { + if (frag->frag_arr[i] != NULL) + free(frag->frag_arr[i]->frag_data); + free(frag->frag_arr[i]); + } + free(frag->frag_arr); + 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); @@ -836,6 +854,30 @@ config_getmobike(struct iked *env, struc log_debug("%s: %smobike", __func__, env->sc_mobike ? "" : "no "); return (0); } + +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 config_setocsp(struct iked *env) Index: sbin/iked/iked.c =================================================================== RCS file: /mount/openbsd/cvs/src/sbin/iked/iked.c,v retrieving revision 1.36 diff -u -p -u -r1.36 iked.c --- sbin/iked/iked.c 27 Nov 2017 18:39:35 -0000 1.36 +++ sbin/iked/iked.c 1 Apr 2019 14:59:14 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: iked.c,v 1.36 2017/11/27 18:39:35 patrick Exp $ */ /* + * Copyright (c) 2019 Tobias Heider <tobias.hei...@stusta.de> * Copyright (c) 2010-2013 Reyk Floeter <r...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -251,6 +252,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 +284,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: sbin/iked/iked.conf.5 =================================================================== RCS file: /mount/openbsd/cvs/src/sbin/iked/iked.conf.5,v retrieving revision 1.53 diff -u -p -u -r1.53 iked.conf.5 --- sbin/iked/iked.conf.5 31 Jan 2018 13:25:55 -0000 1.53 +++ sbin/iked/iked.conf.5 1 Apr 2019 15:03:43 -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: sbin/iked/iked.h =================================================================== RCS file: /mount/openbsd/cvs/src/sbin/iked/iked.h,v retrieving revision 1.119 diff -u -p -u -r1.119 iked.h --- sbin/iked/iked.h 6 Aug 2018 06:30:06 -0000 1.119 +++ sbin/iked/iked.h 1 Apr 2019 14:56:58 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: iked.h,v 1.119 2018/08/06 06:30:06 mestre Exp $ */ /* + * Copyright (c) 2019 Tobias Heider <tobias.hei...@stusta.de> * Copyright (c) 2010-2013 Reyk Floeter <r...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -362,6 +363,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 +393,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 +463,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 +621,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 +673,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 +722,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 +882,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: sbin/iked/ikev2.c =================================================================== RCS file: /mount/openbsd/cvs/src/sbin/iked/ikev2.c,v retrieving revision 1.168 diff -u -p -u -r1.168 ikev2.c --- sbin/iked/ikev2.c 27 Feb 2019 06:33:56 -0000 1.168 +++ sbin/iked/ikev2.c 1 Apr 2019 14:53:26 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: ikev2.c,v 1.168 2019/02/27 06:33:56 sthen Exp $ */ /* + * Copyright (c) 2019 Tobias Heider <tobias.hei...@stusta.de> * Copyright (c) 2010-2013 Reyk Floeter <r...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -144,6 +145,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 +205,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 +404,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 +447,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 +460,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 +488,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 +810,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 +1032,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 */ @@ -1652,6 +1669,30 @@ ikev2_add_mobike(struct iked *env, struc return (len); } + +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_sighashnotify(struct ibuf *e, struct ikev2_payload **pld, ssize_t len) @@ -2283,6 +2324,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 +2451,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 +2536,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: sbin/iked/ikev2.h =================================================================== RCS file: /mount/openbsd/cvs/src/sbin/iked/ikev2.h,v retrieving revision 1.28 diff -u -p -u -r1.28 ikev2.h --- sbin/iked/ikev2.h 27 Feb 2019 06:33:57 -0000 1.28 +++ sbin/iked/ikev2.h 1 Apr 2019 14:56:14 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: ikev2.h,v 1.28 2019/02/27 06:33:57 sthen Exp $ */ /* + * Copyright (c) 2019 Tobias Heider <tobias.hei...@stusta.de> * Copyright (c) 2010-2013 Reyk Floeter <r...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -78,6 +79,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 +105,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 +250,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: sbin/iked/ikev2_msg.c =================================================================== RCS file: /mount/openbsd/cvs/src/sbin/iked/ikev2_msg.c,v retrieving revision 1.53 diff -u -p -u -r1.53 ikev2_msg.c --- sbin/iked/ikev2_msg.c 27 Nov 2017 18:39:35 -0000 1.53 +++ sbin/iked/ikev2_msg.c 1 Apr 2019 14:55:35 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: ikev2_msg.c,v 1.53 2017/11/27 18:39:35 patrick Exp $ */ /* + * Copyright (c) 2019 Tobias Heider <tobias.hei...@stusta.de> * Copyright (c) 2010-2013 Reyk Floeter <r...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -46,6 +47,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 +620,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 +634,26 @@ 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); + + /* Estimated maximum packet size (with 0 < padding < blocklen) */ + 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 +663,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 +720,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 +1138,41 @@ 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, m)) + return -1; + count++; + } + } + return count; } int Index: sbin/iked/ikev2_pld.c =================================================================== RCS file: /mount/openbsd/cvs/src/sbin/iked/ikev2_pld.c,v retrieving revision 1.70 diff -u -p -u -r1.70 ikev2_pld.c --- sbin/iked/ikev2_pld.c 22 Mar 2018 21:11:49 -0000 1.70 +++ sbin/iked/ikev2_pld.c 1 Apr 2019 14:55:07 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: ikev2_pld.c,v 1.70 2018/03/22 21:11:49 patrick Exp $ */ /* + * Copyright (c) 2019 Tobias Heider <tobias.hei...@stusta.de> * Copyright (c) 2010-2013 Reyk Floeter <r...@openbsd.org> * Copyright (c) 2014 Hans-Joerg Hoexer * @@ -95,6 +96,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 +254,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 +274,9 @@ 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 +1260,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 +1611,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; + 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 = recallocarray(NULL, 0, frag_total, + sizeof(struct iked_frag_entry*)); + 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 = calloc(1, 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; + } + + sa_frag->frag_arr[frag_num-1] = el; + el->frag_size = elen; + el->frag_data = calloc(1, elen); + if (el->frag_data == NULL) { + log_debug("%s: Failed allocating new fragment data: %zu of %zu", + __func__, frag_num, frag_total); + goto done; + } + + /* 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; + 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 */ + offset = 0; + for (i = 0; i < sa_frag->frag_total; i++) { + if ((el = sa_frag->frag_arr[i]) == NULL) + fatalx("Tried to reassemble shallow frag_arr"); + 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 +1779,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: sbin/iked/parse.y =================================================================== RCS file: /mount/openbsd/cvs/src/sbin/iked/parse.y,v retrieving revision 1.78 diff -u -p -u -r1.78 parse.y --- sbin/iked/parse.y 13 Feb 2019 22:57:07 -0000 1.78 +++ sbin/iked/parse.y 1 Apr 2019 14:52:26 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: parse.y,v 1.78 2019/02/13 22:57:07 deraadt Exp $ */ /* + * Copyright (c) 2019 Tobias Heider <tobias.hei...@stusta.de> * Copyright (c) 2010-2013 Reyk Floeter <r...@openbsd.org> * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoe...@openbsd.org> * Copyright (c) 2002, 2003, 2004 Henning Brauer <henn...@openbsd.org> @@ -105,6 +106,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 +397,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 +458,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 +1172,7 @@ lookup(char *s) { "esp", ESP }, { "file", FILENAME }, { "flow", FLOW }, + { "fragmentation", FRAGMENTATION }, { "from", FROM }, { "group", GROUP }, { "ike", IKEV1 }, @@ -1181,6 +1187,7 @@ lookup(char *s) { "local", LOCAL }, { "mobike", MOBIKE }, { "name", NAME }, + { "nofragmentation", NOFRAGMENTATION }, { "nomobike", NOMOBIKE }, { "ocsp", OCSP }, { "passive", PASSIVE }, @@ -1579,6 +1586,7 @@ parse_config(const char *filename, struc free(ocsp_url); mobike = 1; + fragmentation = 0; decouple = passive = 0; ocsp_url = NULL; @@ -1592,6 +1600,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: sbin/iked/types.h =================================================================== RCS file: /mount/openbsd/cvs/src/sbin/iked/types.h,v retrieving revision 1.29 diff -u -p -u -r1.29 types.h --- sbin/iked/types.h 27 Nov 2017 18:39:35 -0000 1.29 +++ sbin/iked/types.h 1 Apr 2019 15:00:45 -0000 @@ -1,6 +1,7 @@ /* $OpenBSD: types.h,v 1.29 2017/11/27 18:39:35 patrick Exp $ */ /* + * Copyright (c) 2019 Tobias Heider <tobias.hei...@stusta.de> * Copyright (c) 2010-2013 Reyk Floeter <r...@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -103,6 +104,7 @@ enum imsg_type { IMSG_CTL_ACTIVE, IMSG_CTL_PASSIVE, IMSG_CTL_MOBIKE, + IMSG_CTL_FRAGMENTATION, IMSG_COMPILE, IMSG_UDP_SOCKET, IMSG_PFKEY_SOCKET,