worker-http: added support for anyconnect DTLS1.2 ciphersuites

This adds support for DTLS1.2 ciphersuite header as sent by anyconnect
clients.

Resolves #188
Resolves #193

Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
This commit is contained in:
Nikos Mavrogiannopoulos
2019-01-06 19:11:28 +01:00
committed by Nikos Mavrogiannopoulos
parent c441017f27
commit acdd6d156b
4 changed files with 121 additions and 3 deletions

View File

@@ -12,6 +12,7 @@ X-DTLS-Accept-Encoding, HEADER_DTLS_ENCODING
Connection, HEADER_CONNECTION
X-DTLS-Master-Secret, HEADER_MASTER_SECRET
X-DTLS-CipherSuite, HEADER_DTLS_CIPHERSUITE
X-DTLS12-CipherSuite, HEADER_DTLS12_CIPHERSUITE
X-CSTP-Base-MTU, HEADER_CSTP_BASE_MTU
X-CSTP-MTU, HEADER_CSTP_MTU
X-CSTP-Address-Type, HEADER_CSTP_ATYPE

View File

@@ -135,6 +135,39 @@ static const dtls_ciphersuite_st ciphersuites[] = {
}
};
/* In the following we use %NO_SESSION_HASH:%DISABLE_SAFE_RENEGOTIATION because certain
* versions of openssl send the extended master secret extension in this
* resumed session. Since the state of this extension is undefined
* (it's not a real session we are resuming), we explicitly disable this
* extension to avoid interop issues. Furthermore gnutls does seem to
* be sending the renegotiation extension which openssl doesn't like (see #193) */
#define WORKAROUND_STR "%NO_SESSION_HASH:%DISABLE_SAFE_RENEGOTIATION"
static const dtls_ciphersuite_st ciphersuites12[] = {
{
.oc_name = "AES128-GCM-SHA256",
.gnutls_name =
"NONE:+VERS-DTLS1.2:+COMP-NULL:+AES-128-GCM:+AEAD:+RSA:+SIGN-ALL:"WORKAROUND_STR,
.gnutls_version = GNUTLS_DTLS1_2,
.gnutls_mac = GNUTLS_MAC_AEAD,
.gnutls_kx = GNUTLS_KX_RSA,
.gnutls_cipher = GNUTLS_CIPHER_AES_128_GCM,
.dtls12_mode = 1,
.server_prio = 50
},
{
.oc_name = "AES256-GCM-SHA384",
.gnutls_name =
"NONE:+VERS-DTLS1.2:+COMP-NULL:+AES-256-GCM:+AEAD:+RSA:+SIGN-ALL:"WORKAROUND_STR,
.gnutls_version = GNUTLS_DTLS1_2,
.gnutls_mac = GNUTLS_MAC_AEAD,
.gnutls_kx = GNUTLS_KX_RSA,
.gnutls_cipher = GNUTLS_CIPHER_AES_256_GCM,
.dtls12_mode = 1,
.server_prio = 90
}
};
#ifdef HAVE_LZ4
/* Wrappers over LZ4 functions */
static
@@ -223,6 +256,7 @@ void header_value_check(struct worker_st *ws, struct http_req_st *req)
char *token, *value;
char *str, *p;
const dtls_ciphersuite_st *cand = NULL;
const dtls_ciphersuite_st *saved_ciphersuite;
const compression_method_st *comp_cand = NULL;
const compression_method_st **selected_comp;
int want_cipher;
@@ -344,7 +378,9 @@ void header_value_check(struct worker_st *ws, struct http_req_st *req)
break;
case HEADER_DTLS_CIPHERSUITE:
req->selected_ciphersuite = NULL;
if (req->use_psk || !WSCONFIG(ws)->dtls_legacy)
break;
str = (char *)value;
p = strstr(str, DTLS_PROTO_INDICATOR);
@@ -353,10 +389,14 @@ void header_value_check(struct worker_st *ws, struct http_req_st *req)
if (WSCONFIG(ws)->dtls_psk) {
req->use_psk = 1;
req->master_secret_set = 1; /* we don't need it */
req->selected_ciphersuite = NULL;
break;
}
}
if (req->selected_ciphersuite) /* if set via HEADER_DTLS12_CIPHERSUITE */
break;
if (ws->session != NULL) {
want_mac = gnutls_mac_get(ws->session);
want_cipher = gnutls_cipher_get(ws->session);
@@ -397,6 +437,80 @@ void header_value_check(struct worker_st *ws, struct http_req_st *req)
req->selected_ciphersuite = cand;
break;
case HEADER_DTLS12_CIPHERSUITE:
if (req->use_psk || !WSCONFIG(ws)->dtls_legacy)
break;
/* in gnutls 3.6.0+ there is a regression which makes
* anyconnect's openssl fail: https://gitlab.com/gnutls/gnutls/merge_requests/868
*/
#ifdef gnutls_check_version_numeric
if (!gnutls_check_version_numeric(3,6,6) &&
(!gnutls_check_version_numeric(3,3,0) || gnutls_check_version_numeric(3,6,0))) {
break;
}
#endif
str = (char *)value;
p = strstr(str, DTLS_PROTO_INDICATOR);
if (p != NULL && (p[sizeof(DTLS_PROTO_INDICATOR)-1] == 0 || p[sizeof(DTLS_PROTO_INDICATOR)-1] == ':')) {
/* OpenConnect DTLS setup was detected. */
if (WSCONFIG(ws)->dtls_psk) {
req->use_psk = 1;
req->master_secret_set = 1; /* we don't need it */
req->selected_ciphersuite = NULL;
break;
}
}
saved_ciphersuite = req->selected_ciphersuite;
req->selected_ciphersuite = NULL;
if (ws->session != NULL) {
want_mac = gnutls_mac_get(ws->session);
want_cipher = gnutls_cipher_get(ws->session);
} else {
want_mac = -1;
want_cipher = -1;
}
while ((token = strtok(str, ":")) != NULL) {
for (i = 0;
i < sizeof(ciphersuites12) / sizeof(ciphersuites12[0]);
i++) {
if (strcmp(token, ciphersuites12[i].oc_name) == 0) {
if (ciphersuites12[i].txt_version != NULL && gnutls_check_version(ciphersuites12[i].txt_version) == NULL) {
continue; /* not supported */
}
if (cand == NULL ||
cand->server_prio < ciphersuites12[i].server_prio ||
(want_cipher != -1 && want_cipher == ciphersuites12[i].gnutls_cipher &&
want_mac == ciphersuites12[i].gnutls_mac)) {
cand =
&ciphersuites12[i];
/* if our candidate matches the TLS session
* ciphersuite, we are finished */
if (want_cipher != -1) {
if (want_cipher == cand->gnutls_cipher &&
want_mac == cand->gnutls_mac)
goto ciphersuite12_finish;
}
}
}
}
str = NULL;
}
ciphersuite12_finish:
req->selected_ciphersuite = cand;
if (req->selected_ciphersuite == NULL && saved_ciphersuite)
req->selected_ciphersuite = saved_ciphersuite;
break;
#ifdef ENABLE_COMPRESSION
case HEADER_DTLS_ENCODING:
case HEADER_CSTP_ENCODING:

View File

@@ -2208,8 +2208,9 @@ static int connect_handler(worker_st * ws)
oclog(ws, LOG_INFO, "DTLS ciphersuite: %s",
ws->req.selected_ciphersuite->oc_name);
ret =
cstp_printf(ws, "X-DTLS-CipherSuite: %s\r\n",
ws->req.selected_ciphersuite->oc_name);
cstp_printf(ws, "X-DTLS%s-CipherSuite: %s\r\n",
(ws->req.selected_ciphersuite->dtls12_mode!=0)?"12":"",
ws->req.selected_ciphersuite->oc_name);
SEND_ERR(ret);
/* only send the X-DTLS-MTU in the legacy protocol, as there

View File

@@ -57,6 +57,7 @@ enum {
HEADER_DEVICE_TYPE,
HEADER_PLATFORM,
HEADER_DTLS_CIPHERSUITE,
HEADER_DTLS12_CIPHERSUITE,
HEADER_CONNECTION,
HEADER_FULL_IPV6,
HEADER_USER_AGENT,
@@ -100,6 +101,7 @@ typedef struct compression_method_st {
typedef struct dtls_ciphersuite_st {
const char* oc_name;
const char* gnutls_name; /* the gnutls priority string to set */
unsigned dtls12_mode;
unsigned server_prio; /* the highest the more we want to negotiate that */
unsigned gnutls_cipher;
unsigned gnutls_kx;