Merge branch 'tmp-dtls12' into 'master'

Added support for DTLS1.2 with anyconnect clients

Closes #193 and #188

See merge request openconnect/ocserv!92
This commit is contained in:
Nikos Mavrogiannopoulos
2019-01-10 13:43:58 +00:00
5 changed files with 121 additions and 13 deletions

1
NEWS
View File

@@ -1,6 +1,7 @@
* Version 0.12.2 (unreleased)
- Added support for AES256-SHA legacy cipher. This allows the anyconnect
clients to use AES256.
- Added support for the DTLS1.2 protocol hack used by in Anyconnect clients.
* Version 0.12.1 (released 2018-05-12)

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

@@ -90,7 +90,6 @@ static const dtls_ciphersuite_st ciphersuites[] = {
.gnutls_mac = GNUTLS_MAC_AEAD,
.gnutls_kx = GNUTLS_KX_RSA,
.gnutls_cipher = GNUTLS_CIPHER_AES_128_GCM,
.txt_version = "3.2.7",
.server_prio = 80},
{
.oc_name = CS_AES256_GCM,
@@ -101,7 +100,6 @@ static const dtls_ciphersuite_st ciphersuites[] = {
.gnutls_kx = GNUTLS_KX_RSA,
.gnutls_cipher = GNUTLS_CIPHER_AES_256_GCM,
.server_prio = 90,
.txt_version = "3.2.7",
},
{
.oc_name = "AES256-SHA",
@@ -135,6 +133,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 +254,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 +376,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 +387,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);
@@ -370,10 +408,6 @@ void header_value_check(struct worker_st *ws, struct http_req_st *req)
i < sizeof(ciphersuites) / sizeof(ciphersuites[0]);
i++) {
if (strcmp(token, ciphersuites[i].oc_name) == 0) {
if (ciphersuites[i].txt_version != NULL && gnutls_check_version(ciphersuites[i].txt_version) == NULL) {
continue; /* not supported */
}
if (cand == NULL ||
cand->server_prio < ciphersuites[i].server_prio ||
(want_cipher != -1 && want_cipher == ciphersuites[i].gnutls_cipher &&
@@ -397,6 +431,76 @@ 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 (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

@@ -274,7 +274,7 @@ static int setup_dtls_psk_keys(gnutls_session_t session, struct worker_st *ws)
return 0;
}
static int setup_dtls0_9_keys(gnutls_session_t session, struct worker_st *ws)
static int setup_legacy_dtls_keys(gnutls_session_t session, struct worker_st *ws)
{
int ret;
gnutls_datum_t master =
@@ -351,8 +351,8 @@ static int setup_dtls_connection(struct worker_st *ws)
oclog(ws, LOG_INFO, "CISCO client compatibility (dtls-legacy) is disabled; will not setup a DTLS session");
goto fail;
}
oclog(ws, LOG_INFO, "setting up DTLS-0.9 connection");
ret = setup_dtls0_9_keys(session, ws);
oclog(ws, LOG_INFO, "setting up legacy DTLS (resumption) connection");
ret = setup_legacy_dtls_keys(session, ws);
}
if (ret < 0) {
@@ -2208,7 +2208,8 @@ 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",
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);

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,12 +101,12 @@ 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;
unsigned gnutls_mac;
unsigned gnutls_version;
const char *txt_version;
} dtls_ciphersuite_st;
#ifdef HAVE_GSSAPI