From 74cffb63433a0f82fb0b1be3c7ae4eca8a176a38 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 20 Apr 2021 13:52:44 +0100 Subject: [PATCH] Add DTLS_ESTABLISHED state Cisco DTLS is preauthenticated by PSK or the session resume hack, but with anonymous DTLS there will be authentication over the DTLS channel once it has been connected. Add a new state DTLS_ESTABLISHED, and let DTLS_CONNECTED mean merely *connected* at the transport level. ESP used to use DTLS_CONNECTING state to indicate that the ESP probes had been received and we were ready to tell the server (over the TCP channel) to switch over to use ESP. Use DTLS_CONNECTED for that state now, since it's a better fit. And DTLS_ESTABLISHED when we've actually told the server to change. Signed-off-by: David Woodhouse --- dtls.c | 11 +++++++++-- esp.c | 4 ++-- gpst.c | 13 +++++++------ library.c | 2 +- oncp.c | 12 ++++++------ openconnect-internal.h | 5 +++-- pulse.c | 8 ++++---- 7 files changed, 32 insertions(+), 23 deletions(-) diff --git a/dtls.c b/dtls.c index fa1bb981..96754486 100644 --- a/dtls.c +++ b/dtls.c @@ -245,8 +245,11 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) if (vpninfo->dtls_state == DTLS_CONNECTING) { dtls_try_handshake(vpninfo, timeout); - vpninfo->delay_tunnel_reason = "DTLS MTU detection"; - return 0; + if (vpninfo->dtls_state != DTLS_CONNECTED) { + vpninfo->delay_tunnel_reason = "DTLS MTU detection"; + return 0; + } + return 1; } if (vpninfo->dtls_state == DTLS_SLEEPING) { @@ -262,6 +265,10 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) return 0; } + /* Nothing to do here for Cisco DTLS as it is preauthenticated */ + if (vpninfo->dtls_state == DTLS_CONNECTED) + vpninfo->dtls_state = DTLS_ESTABLISHED; + while (readable) { int len = MAX(16384, vpninfo->ip_info.mtu); unsigned char *buf; diff --git a/esp.c b/esp.c index 630e8a70..30743cdd 100644 --- a/esp.c +++ b/esp.c @@ -232,7 +232,7 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) if (vpninfo->dtls_state == DTLS_SLEEPING) { vpn_progress(vpninfo, PRG_INFO, _("ESP session established with server\n")); - vpninfo->dtls_state = DTLS_CONNECTING; + vpninfo->dtls_state = DTLS_CONNECTED; } continue; } @@ -263,7 +263,7 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) } } - if (vpninfo->dtls_state != DTLS_CONNECTED) + if (vpninfo->dtls_state != DTLS_ESTABLISHED) return 0; switch (keepalive_action(&vpninfo->dtls_times, timeout)) { diff --git a/gpst.c b/gpst.c index 6b61ff76..2ae20db1 100644 --- a/gpst.c +++ b/gpst.c @@ -1044,11 +1044,12 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) * it if the ESP tunnel is connected or connecting. */ switch (vpninfo->dtls_state) { - case DTLS_CONNECTING: + case DTLS_CONNECTING: /* Can never happen */ + case DTLS_CONNECTED: openconnect_close_https(vpninfo, 0); /* don't keep stale HTTPS socket */ vpn_progress(vpninfo, PRG_INFO, _("ESP tunnel connected; exiting HTTPS mainloop.\n")); - vpninfo->dtls_state = DTLS_CONNECTED; + vpninfo->dtls_state = DTLS_ESTABLISHED; /* Now that we are connected, let's ensure timeout is less than * or equal to DTLS DPD/keepalive else we might over sleep, eg * if timeout is set to DTLS attempt period from ESP mainloop, @@ -1057,7 +1058,7 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) if (*timeout > vpninfo->dtls_times.dpd * 1000) *timeout = vpninfo->dtls_times.dpd * 1000; /* fall through */ - case DTLS_CONNECTED: + case DTLS_ESTABLISHED: /* Rekey or check-and-resubmit HIP if needed */ if (keepalive_action(&vpninfo->ssl_times, timeout) == KA_REKEY) goto do_rekey; @@ -1263,7 +1264,7 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) case KA_KEEPALIVE: /* No need to send an explicit keepalive if we have real data to send */ - if (vpninfo->dtls_state != DTLS_CONNECTED && + if (vpninfo->dtls_state != DTLS_ESTABLISHED && vpninfo->outgoing_queue.head) break; /* fall through */ @@ -1276,7 +1277,7 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) /* Service outgoing packet queue */ - while (vpninfo->dtls_state != DTLS_CONNECTED && + while (vpninfo->dtls_state != DTLS_ESTABLISHED && (vpninfo->current_ssl_pkt = dequeue_packet(&vpninfo->outgoing_queue))) { struct pkt *this = vpninfo->current_ssl_pkt; @@ -1356,7 +1357,7 @@ int gpst_esp_send_probes(struct openconnect_info *vpninfo) monitor_except_fd(vpninfo, dtls); } - for (seq=1; seq <= (vpninfo->dtls_state==DTLS_CONNECTED ? 1 : 3); seq++) { + for (seq=1; seq <= (vpninfo->dtls_state==DTLS_ESTABLISHED ? 1 : 3); seq++) { memset(pkt, 0, sizeof(*pkt) + sizeof(*iph) + ICMP_MINLEN + sizeof(magic_ping_payload)); pkt->len = sizeof(struct ip) + ICMP_MINLEN + sizeof(magic_ping_payload); diff --git a/library.c b/library.c index 2d1c58c4..c3cdec52 100644 --- a/library.c +++ b/library.c @@ -1184,7 +1184,7 @@ const char *openconnect_get_dtls_compression(struct openconnect_info * vpninfo) const char *openconnect_get_dtls_cipher(struct openconnect_info *vpninfo) { - if (vpninfo->dtls_state != DTLS_CONNECTED || !vpninfo->dtls_ssl) { + if (vpninfo->dtls_state < DTLS_CONNECTED || !vpninfo->dtls_ssl) { #if defined(OPENCONNECT_GNUTLS) gnutls_free(vpninfo->dtls_cipher_desc); #else diff --git a/oncp.c b/oncp.c index 75f2be18..9e2610ee 100644 --- a/oncp.c +++ b/oncp.c @@ -1071,7 +1071,7 @@ int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) *sent* over the TCP channel. */ vpn_progress(vpninfo, PRG_TRACE, _("Sent ESP enable control packet\n")); - vpninfo->dtls_state = DTLS_CONNECTED; + vpninfo->dtls_state = DTLS_ESTABLISHED; work_done = 1; } else { free(vpninfo->current_ssl_pkt); @@ -1137,7 +1137,7 @@ int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) case KA_KEEPALIVE: /* No need to send an explicit keepalive if we have real data to send */ - if (vpninfo->dtls_state != DTLS_CONNECTED && vpninfo->outgoing_queue) + if (vpninfo->dtls_state != DTLS_ESTABLISHED && vpninfo->outgoing_queue) break; vpn_progress(vpninfo, PRG_DEBUG, _("Send CSTP Keepalive\n")); @@ -1153,7 +1153,7 @@ int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) * via ESP once the enable message has been *sent* over the * TCP channel. Assign it directly to current_ssl_pkt so that * we can use it in-place and match against it above. */ - if (vpninfo->dtls_state == DTLS_CONNECTING) { + if (vpninfo->dtls_state == DTLS_CONNECTED) { vpninfo->current_ssl_pkt = (struct pkt *)&esp_enable_pkt; goto handle_outgoing; } @@ -1163,7 +1163,7 @@ int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) goto handle_outgoing; /* Service outgoing packet queue, if no DTLS */ - while (vpninfo->dtls_state != DTLS_CONNECTED && + while (vpninfo->dtls_state != DTLS_ESTABLISHED && (vpninfo->current_ssl_pkt = dequeue_packet(&vpninfo->outgoing_queue))) { struct pkt *this = vpninfo->current_ssl_pkt; @@ -1214,7 +1214,7 @@ int oncp_bye(struct openconnect_info *vpninfo, const char *reason) void oncp_esp_close(struct openconnect_info *vpninfo) { /* Tell server to stop sending on ESP channel */ - if (vpninfo->dtls_state >= DTLS_CONNECTING) + if (vpninfo->dtls_state >= DTLS_CONNECTED) queue_esp_control(vpninfo, 0); esp_close(vpninfo); } @@ -1241,7 +1241,7 @@ int oncp_esp_send_probes(struct openconnect_info *vpninfo) if (!pkt) return -ENOMEM; - for (seq=1; seq <= (vpninfo->dtls_state==DTLS_CONNECTED ? 1 : 2); seq++) { + for (seq=1; seq <= (vpninfo->dtls_state==DTLS_ESTABLISHED ? 1 : 2); seq++) { pkt->len = 1; pkt->data[0] = 0; pktlen = construct_esp_packet(vpninfo, pkt, diff --git a/openconnect-internal.h b/openconnect-internal.h index 3f9699b3..f03d17ae 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -190,8 +190,9 @@ struct pkt { #define DTLS_SECRET 1 /* Secret is present, ready to attempt DTLS */ #define DTLS_DISABLED 2 /* DTLS was disabled on the *client* side */ #define DTLS_SLEEPING 3 /* For ESP, sometimes sending probes */ -#define DTLS_CONNECTING 4 /* ESP probe received; must tell server */ -#define DTLS_CONNECTED 5 /* Server informed and should be sending ESP */ +#define DTLS_CONNECTING 4 /* DTLS (re)handshaking. Not used for ESP */ +#define DTLS_CONNECTED 5 /* Transport connected but not yet enabled */ +#define DTLS_ESTABLISHED 6 /* Data path fully established */ /* Not to be confused with OC_PROTO_xxx flags which are library-visible */ #define PROTO_ANYCONNECT 0 diff --git a/pulse.c b/pulse.c index 82d4fd15..86de1958 100644 --- a/pulse.c +++ b/pulse.c @@ -2826,7 +2826,7 @@ int pulse_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) case KA_KEEPALIVE: /* No need to send an explicit keepalive if we have real data to send */ - if (vpninfo->dtls_state != DTLS_CONNECTED && + if (vpninfo->dtls_state != DTLS_ESTABLISHED && vpninfo->outgoing_queue.head) break; @@ -2839,11 +2839,11 @@ int pulse_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) ; } #endif - if (vpninfo->dtls_state == DTLS_CONNECTING) { + if (vpninfo->dtls_state == DTLS_CONNECTED) { /* We don't currently do anything to make the server start sending * data packets in ESP instead of over IF-T/TLS. Just go straight * to CONNECTED mode. */ - vpninfo->dtls_state = DTLS_CONNECTED; + vpninfo->dtls_state = DTLS_ESTABLISHED; work_done = 1; } @@ -2856,7 +2856,7 @@ int pulse_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) } /* Service outgoing packet queue, if no DTLS */ - while (vpninfo->dtls_state != DTLS_CONNECTED && + while (vpninfo->dtls_state != DTLS_ESTABLISHED && (vpninfo->current_ssl_pkt = dequeue_packet(&vpninfo->outgoing_queue))) { struct pkt *this = vpninfo->current_ssl_pkt; -- 2.50.1