From cdc32a49a172a6a04a3f7bc28db5f1a36f64a2ba Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 7 Apr 2022 22:22:36 +0100 Subject: [PATCH] AnyConnect: Generate EC keys for X-AnyConnect-STRAP-{DH-,}Pubkey These are needed for the external browser SAML support. Signed-off-by: David Woodhouse --- cstp.c | 13 ++++++ gnutls.c | 90 ++++++++++++++++++++++++++++++++++++++++++ library.c | 5 ++- openconnect-internal.h | 8 ++++ openssl.c | 73 ++++++++++++++++++++++++++++++++++ 5 files changed, 187 insertions(+), 2 deletions(-) diff --git a/cstp.c b/cstp.c index d390973f..000b8730 100644 --- a/cstp.c +++ b/cstp.c @@ -1246,6 +1246,19 @@ void cstp_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *b if (vpninfo->try_http_auth) buf_append(buf, "X-Support-HTTP-Auth: true\r\n"); + if (!vpninfo->strap_pubkey || !vpninfo->strap_dh_pubkey) { + int err = generate_strap_keys(vpninfo); + if (err) { + buf->error = err; + return; + } + } + + buf_append(buf, "X-AnyConnect-STRAP-Pubkey: %s\r\n", + vpninfo->strap_pubkey); + buf_append(buf, "X-AnyConnect-STRAP-DH-Pubkey: %s\r\n", + vpninfo->strap_dh_pubkey); + append_mobile_headers(vpninfo, buf); } diff --git a/gnutls.c b/gnutls.c index 91b95f33..6d8abd79 100644 --- a/gnutls.c +++ b/gnutls.c @@ -2797,3 +2797,93 @@ void destroy_eap_ttls(struct openconnect_info *vpninfo, void *sess) { gnutls_deinit(sess); } + +static int generate_strap_key(gnutls_privkey_t *key, char **pubkey) +{ + int bits, pk, err; + gnutls_pubkey_t pkey = NULL; + gnutls_datum_t pdata = { }; + struct oc_text_buf *buf = NULL; + +#if GNUTLS_VERSION_NUMBER >= 0x030500 + pk = gnutls_ecc_curve_get_pk(GNUTLS_ECC_CURVE_SECP256R1); +#else + pk = GNUTLS_PK_EC; +#endif + bits = GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_SECP256R1); + + err = gnutls_privkey_init(key); + if (err) + goto out; + + err = gnutls_privkey_generate(*key, pk, bits, 0); + if (err) + goto out; + + err = gnutls_pubkey_init(&pkey); + if (err) + goto out; + + err = gnutls_pubkey_import_privkey(pkey, *key, + GNUTLS_KEY_KEY_AGREEMENT, 0); + if (err) + goto out; + + err = gnutls_pubkey_export2(pkey, GNUTLS_X509_FMT_DER, &pdata); + if (err) + goto out; + + buf = buf_alloc(); + buf_append_base64(buf, pdata.data, pdata.size, 0); + if (buf_error(buf)) { + err = GNUTLS_E_MEMORY_ERROR; + goto out; + } + + *pubkey = buf->data; + buf->data = NULL; + out: + buf_free(buf); + gnutls_free(pdata.data); + gnutls_pubkey_deinit(pkey); + if (err) { + gnutls_privkey_deinit(*key); + *key = NULL; + *pubkey = NULL; + } + return err; +} + +int generate_strap_keys(struct openconnect_info *vpninfo) +{ + int err; + + err = generate_strap_key(&vpninfo->strap_key, &vpninfo->strap_pubkey); + if (err) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to generate STRAP key: %s\n"), + gnutls_strerror(err)); + free_strap_keys(vpninfo); + return -EIO; + } + + err = generate_strap_key(&vpninfo->strap_dh_key, &vpninfo->strap_dh_pubkey); + if (err) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to generate STRAP DH key: %s\n"), + gnutls_strerror(err)); + free_strap_keys(vpninfo); + return -EIO; + } + return 0; +} + +void free_strap_keys(struct openconnect_info *vpninfo) +{ + if (vpninfo->strap_key) + gnutls_privkey_deinit(vpninfo->strap_key); + if (vpninfo->strap_dh_key) + gnutls_privkey_deinit(vpninfo->strap_dh_key); + + vpninfo->strap_key = vpninfo->strap_dh_key = NULL; +} diff --git a/library.c b/library.c index 85171476..cd5fd9ee 100644 --- a/library.c +++ b/library.c @@ -590,11 +590,12 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo) closesocket(vpninfo->cmd_fd); closesocket(vpninfo->cmd_fd_write); } - + free_strap_keys(vpninfo); + free(vpninfo->strap_pubkey); + free(vpninfo->strap_dh_pubkey); free(vpninfo->ppp); buf_free(vpninfo->ppp_tls_connect_req); buf_free(vpninfo->ppp_dtls_connect_req); - #ifdef HAVE_ICONV if (vpninfo->ic_utf8_to_legacy != (iconv_t)-1) iconv_close(vpninfo->ic_utf8_to_legacy); diff --git a/openconnect-internal.h b/openconnect-internal.h index ea7f1cad..f066246a 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -573,13 +573,19 @@ struct openconnect_info { SSL_CTX *https_ctx; SSL *https_ssl; BIO_METHOD *ttls_bio_meth; + EC_KEY *strap_key; + EC_KEY *strap_dh_key; #elif defined(OPENCONNECT_GNUTLS) gnutls_session_t https_sess; gnutls_session_t eap_ttls_sess; gnutls_certificate_credentials_t https_cred; gnutls_psk_client_credentials_t psk_cred; char local_cert_md5[MD5_SIZE * 2 + 1]; /* For CSD */ + gnutls_privkey_t strap_key; + gnutls_privkey_t strap_dh_key; #endif /* OPENCONNECT_GNUTLS */ + char *strap_pubkey; + char *strap_dh_pubkey; char *ciphersuite_config; struct oc_text_buf *ttls_pushbuf; uint8_t ttls_eap_ident; @@ -1424,6 +1430,8 @@ int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge); int openconnect_install_ctx_verify(struct openconnect_info *vpninfo, SSL_CTX *ctx); #endif +void free_strap_keys(struct openconnect_info *vpninfo); +int generate_strap_keys(struct openconnect_info *vpninfo); /* mainloop.c */ int tun_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable, int did_work); diff --git a/openssl.c b/openssl.c index 3205dbd7..7085429c 100644 --- a/openssl.c +++ b/openssl.c @@ -2247,3 +2247,76 @@ void destroy_eap_ttls(struct openconnect_info *vpninfo, void *ttls) /* Leave the BIO_METH for now. It may get reused and we don't want to * have to call BIO_get_new_index() more times than is necessary */ } + +static int generate_strap_key(EC_KEY **key, EC_GROUP *grp, char **pubkey) +{ + struct oc_text_buf *buf = NULL; + unsigned char *der = NULL; + int len; + + *key = EC_KEY_new(); + if (!*key) + return -EIO; + + if (!EC_KEY_set_group(*key, grp)) + return -EIO; + + if (!EC_KEY_generate_key(*key)) + return -EIO; + + len = i2d_EC_PUBKEY(*key, &der); + buf = buf_alloc(); + buf_append_base64(buf, der, len, 0); + free(der); + if (buf_error(buf)) + return buf_free(buf); + + *pubkey = buf->data; + buf->data = NULL; + buf_free(buf); + return 0; +} + +int generate_strap_keys(struct openconnect_info *vpninfo) +{ + int err; + EC_GROUP *grp = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + + if (!grp) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to create prime256v1 EC group\n")); + return -EIO; + } + + err = generate_strap_key(&vpninfo->strap_key, grp, &vpninfo->strap_pubkey); + if (err) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to generate STRAP key")); + openconnect_report_ssl_errors(vpninfo); + free_strap_keys(vpninfo); + EC_GROUP_free(grp); + return -EIO; + } + + err = generate_strap_key(&vpninfo->strap_dh_key, grp, &vpninfo->strap_dh_pubkey); + if (err) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to generate STRAP DH key\n")); + openconnect_report_ssl_errors(vpninfo); + free_strap_keys(vpninfo); + EC_GROUP_free(grp); + return -EIO; + } + EC_GROUP_free(grp); + return 0; +} + +void free_strap_keys(struct openconnect_info *vpninfo) +{ + if (vpninfo->strap_key) + EC_KEY_free(vpninfo->strap_key); + if (vpninfo->strap_dh_key) + EC_KEY_free(vpninfo->strap_dh_key); + + vpninfo->strap_key = vpninfo->strap_dh_key = NULL; +} -- 2.49.0