From: Nikos Mavrogiannopoulos Date: Sat, 6 Oct 2018 05:44:12 +0000 (+0200) Subject: Use the client hello session identifier to transmit the client identifier X-Git-Tag: v8.00~37 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=0787d693f7d43b0345b10dd1540c832f0cd353b4;p=users%2Fdwmw2%2Fopenconnect.git Use the client hello session identifier to transmit the client identifier Currently the openconnect (protocol) client uses a custom extension to provide information to the server on which session it was previously associated with. However, a private extension cannot be defined in IETF without going through a tedious standardization process involving the TLS working group. To avoid that process we should provide the client identifier on the DTLS session using alternative methods. In TLS 1.3 (and DTLS) the session ID field was made obsolete, and as such we can use it to place the client identifier instead of an extension field. We can do it safely because (1) there is no session resumption -in the dtls1.2 or earlier sense- and (2) ocserv is already checking this field for that value due to the old protocol format. Resolves #5 Signed-off-by: Nikos Mavrogiannopoulos Signed-off-by: David Woodhouse --- diff --git a/gnutls-dtls.c b/gnutls-dtls.c index b30abb29..215c7f4b 100644 --- a/gnutls-dtls.c +++ b/gnutls-dtls.c @@ -194,6 +194,18 @@ static int start_dtls_psk_handshake(struct openconnect_info *vpninfo, int dtls_f goto fail; } + /* set our session identifier match the application ID; we do that in addition + * to the extension which contains the same information in order to deprecate + * the latter. The reason is that the session ID field is a field not used + * with TLS1.3 (and DTLS1.3), and as such we can rely on it being available to + * us, while avoiding a custom extension which requires standardization. + */ + if (vpninfo->dtls_app_id_size > 0) { + gnutls_datum_t id = {vpninfo->dtls_app_id, vpninfo->dtls_app_id_size}; + + gnutls_session_set_id(dtls_ssl, &id); + } + gnutls_transport_set_ptr(dtls_ssl, (gnutls_transport_ptr_t)(intptr_t)dtls_fd); diff --git a/openconnect-internal.h b/openconnect-internal.h index 2fac50bc..74ed6e07 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -197,6 +197,8 @@ struct oc_text_buf { int error; }; +#define TLS_MASTER_KEY_SIZE 48 + #define RECONNECT_INTERVAL_MIN 10 #define RECONNECT_INTERVAL_MAX 100 @@ -551,7 +553,7 @@ struct openconnect_info { int dtls_need_reconnect; struct keepalive_info dtls_times; unsigned char dtls_session_id[32]; - unsigned char dtls_secret[48]; + unsigned char dtls_secret[TLS_MASTER_KEY_SIZE]; unsigned char dtls_app_id[32]; unsigned dtls_app_id_size; diff --git a/openssl-dtls.c b/openssl-dtls.c index aa3eacb9..9dffeb67 100644 --- a/openssl-dtls.c +++ b/openssl-dtls.c @@ -187,22 +187,38 @@ static void buf_append_OCTET_STRING(struct oc_text_buf *buf, void *data, int len } static SSL_SESSION *generate_dtls_session(struct openconnect_info *vpninfo, - int dtlsver, const SSL_CIPHER *cipher) + int dtlsver, const SSL_CIPHER *cipher, + unsigned rnd_key) { struct oc_text_buf *buf = buf_alloc(); SSL_SESSION *dtls_session; const unsigned char *asn; uint16_t cid; + uint8_t rnd_secret[TLS_MASTER_KEY_SIZE]; buf_append_bytes(buf, "\x30\x80", 2); // SEQUENCE, indeterminate length buf_append_INTEGER(buf, 1 /* SSL_SESSION_ASN1_VERSION */); buf_append_INTEGER(buf, dtlsver); store_be16(&cid, SSL_CIPHER_get_id(cipher) & 0xffff); buf_append_OCTET_STRING(buf, &cid, 2); - buf_append_OCTET_STRING(buf, vpninfo->dtls_session_id, - sizeof(vpninfo->dtls_session_id)); - buf_append_OCTET_STRING(buf, vpninfo->dtls_secret, - sizeof(vpninfo->dtls_secret)); + if (rnd_key) { + buf_append_OCTET_STRING(buf, vpninfo->dtls_app_id, + vpninfo->dtls_app_id_size); + + if (openconnect_random(rnd_secret, sizeof(rnd_secret))) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to generate random key\n")); + buf_free(buf); + return NULL; + } + buf_append_OCTET_STRING(buf, rnd_secret, sizeof(rnd_secret)); + } else { + buf_append_OCTET_STRING(buf, vpninfo->dtls_session_id, + sizeof(vpninfo->dtls_session_id)); + + buf_append_OCTET_STRING(buf, vpninfo->dtls_secret, + sizeof(vpninfo->dtls_secret)); + } /* If the length actually fits in one byte (which it should), do * it that way. Else, leave it indeterminate and add two * end-of-contents octets to mark the end of the SEQUENCE. */ @@ -233,9 +249,11 @@ static SSL_SESSION *generate_dtls_session(struct openconnect_info *vpninfo, } #else /* OpenSSL before 1.1 */ static SSL_SESSION *generate_dtls_session(struct openconnect_info *vpninfo, - int dtlsver, const SSL_CIPHER *cipher) + int dtlsver, const SSL_CIPHER *cipher, + unsigned rnd_key) { SSL_SESSION *dtls_session = SSL_SESSION_new(); + if (!dtls_session) { vpn_progress(vpninfo, PRG_ERR, _("Initialise DTLSv1 session failed\n")); @@ -243,13 +261,33 @@ static SSL_SESSION *generate_dtls_session(struct openconnect_info *vpninfo, } dtls_session->ssl_version = dtlsver; - dtls_session->master_key_length = sizeof(vpninfo->dtls_secret); - memcpy(dtls_session->master_key, vpninfo->dtls_secret, - sizeof(vpninfo->dtls_secret)); + dtls_session->master_key_length = TLS_MASTER_KEY_SIZE; + + if (rnd_key) { + if (openconnect_random(dtls_session->master_key, TLS_MASTER_KEY_SIZE)) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to generate random key\n")); + return NULL; + } + + if (vpninfo->dtls_app_id_size > sizeof(dtls_session->session_id)) { + vpn_progress(vpninfo, PRG_ERR, + _("Too large application ID size\n")); + return NULL; + } + + dtls_session->session_id_length = vpninfo->dtls_app_id_size; + memcpy(dtls_session->session_id, vpninfo->dtls_app_id, + vpninfo->dtls_app_id_size); + } else { + memcpy(dtls_session->master_key, vpninfo->dtls_secret, + sizeof(vpninfo->dtls_secret)); + + dtls_session->session_id_length = sizeof(vpninfo->dtls_session_id); + memcpy(dtls_session->session_id, vpninfo->dtls_session_id, + sizeof(vpninfo->dtls_session_id)); + } - dtls_session->session_id_length = sizeof(vpninfo->dtls_session_id); - memcpy(dtls_session->session_id, vpninfo->dtls_session_id, - sizeof(vpninfo->dtls_session_id)); dtls_session->cipher = (SSL_CIPHER *)cipher; dtls_session->cipher_id = cipher->id; @@ -310,6 +348,13 @@ static int pskident_parse(SSL *s, unsigned int ext_type, const unsigned char *in #endif +#if OPENSSL_VERSION_NUMBER < 0x10002000L +static const SSL_CIPHER *SSL_CIPHER_find(SSL *ssl, const unsigned char *ptr) +{ + return ssl->method->get_cipher_by_char(ptr); +} +#endif + int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd) { STACK_OF(SSL_CIPHER) *ciphers; @@ -391,6 +436,7 @@ int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd) pskident_parse, vpninfo); /* For SSL_CTX_set_cipher_list() */ cipher = "PSK"; + #endif } /* If we don't readahead, then we do short reads and throw @@ -411,6 +457,7 @@ int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd) SSL_set_connect_state(dtls_ssl); SSL_set_app_data(dtls_ssl, vpninfo); + if (dtlsver) { ciphers = SSL_get_ciphers(dtls_ssl); if (dtlsver != 0 && sk_SSL_CIPHER_num(ciphers) != 1) { @@ -424,7 +471,7 @@ int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd) /* We're going to "resume" a session which never existed. Fake it... */ dtls_session = generate_dtls_session(vpninfo, dtlsver, - sk_SSL_CIPHER_value(ciphers, 0)); + sk_SSL_CIPHER_value(ciphers, 0), 0); if (!dtls_session) { SSL_CTX_free(vpninfo->dtls_ctx); SSL_free(dtls_ssl); @@ -433,7 +480,6 @@ int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd) return -EINVAL; } - /* Add the generated session to the SSL */ if (!SSL_set_session(dtls_ssl, dtls_session)) { vpn_progress(vpninfo, PRG_ERR, _("SSL_set_session() failed with old protocol version 0x%x\n" @@ -448,11 +494,40 @@ int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd) SSL_SESSION_free(dtls_session); return -EINVAL; } + /* We don't need our own refcount on it any more */ + SSL_SESSION_free(dtls_session); + } else if (vpninfo->dtls_app_id_size > 0) { + const uint8_t cs[2] = {0x00, 0x2F}; /* RSA-AES-128 */ + /* we generate a session with a random key which cannot be resumed; + * we want to set the client identifier we received from the server + * as a session ID. */ + dtls_session = generate_dtls_session(vpninfo, DTLS1_VERSION, + SSL_CIPHER_find(dtls_ssl, cs), + 1); + if (!dtls_session) { + SSL_CTX_free(vpninfo->dtls_ctx); + SSL_free(dtls_ssl); + vpninfo->dtls_ctx = NULL; + vpninfo->dtls_attempt_period = 0; + return -EINVAL; + } + + if (!SSL_set_session(dtls_ssl, dtls_session)) { + vpn_progress(vpninfo, PRG_ERR, + _("SSL_set_session() failed\n")); + SSL_CTX_free(vpninfo->dtls_ctx); + SSL_free(dtls_ssl); + vpninfo->dtls_ctx = NULL; + vpninfo->dtls_attempt_period = 0; + SSL_SESSION_free(dtls_session); + return -EINVAL; + } /* We don't need our own refcount on it any more */ SSL_SESSION_free(dtls_session); } + dtls_bio = BIO_new_socket(dtls_fd, BIO_NOCLOSE); /* Set non-blocking */ BIO_set_nbio(dtls_bio, 1);