]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Use the client hello session identifier to transmit the client identifier
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 6 Oct 2018 05:44:12 +0000 (07:44 +0200)
committerDavid Woodhouse <dwmw2@infradead.org>
Sat, 13 Oct 2018 12:53:24 +0000 (05:53 -0700)
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 <nmav@gnutls.org>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
gnutls-dtls.c
openconnect-internal.h
openssl-dtls.c

index b30abb29fa441b4a02683da4143965e13699bfc6..215c7f4b36c9d450b548ea56a33ff94bc43c9ddd 100644 (file)
@@ -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);
 
index 2fac50bc65615d393a8390b9c8d7b378e4bfc541..74ed6e079f4bd56d7bd1e062eecab1a0221cf877 100644 (file)
@@ -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;
 
index aa3eacb9206f920753bd1ccb64cc4705f60abd9f..9dffeb677658e8592069f13c37930bb571e91009 100644 (file)
@@ -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);