| /* |
| * OpenVPN -- An application to securely tunnel IP networks |
| * over a single TCP/UDP port, with support for SSL/TLS-based |
| * session authentication and key exchange, |
| * packet encryption, packet authentication, and |
| * packet compression. |
| * |
| * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
| * Copyright (C) 2010-2018 Fox Crypto B.V. <openvpn@fox-it.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| /** |
| * @file Control Channel OpenSSL Backend |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #elif defined(_MSC_VER) |
| #include "config-msvc.h" |
| #endif |
| |
| #include "syshead.h" |
| |
| #if defined(ENABLE_CRYPTO_OPENSSL) |
| |
| #include "errlevel.h" |
| #include "buffer.h" |
| #include "misc.h" |
| #include "manage.h" |
| #include "memdbg.h" |
| #include "ssl_backend.h" |
| #include "ssl_common.h" |
| #include "base64.h" |
| #include "openssl_compat.h" |
| |
| #ifdef ENABLE_CRYPTOAPI |
| #include "cryptoapi.h" |
| #endif |
| |
| #include "ssl_verify_openssl.h" |
| |
| #include <openssl/bn.h> |
| #include <openssl/crypto.h> |
| #include <openssl/dh.h> |
| #include <openssl/dsa.h> |
| #include <openssl/err.h> |
| #include <openssl/pkcs12.h> |
| #include <openssl/rsa.h> |
| #include <openssl/x509.h> |
| #include <openssl/ssl.h> |
| #ifndef OPENSSL_NO_EC |
| #include <openssl/ec.h> |
| #endif |
| |
| /* |
| * Allocate space in SSL objects in which to store a struct tls_session |
| * pointer back to parent. |
| * |
| */ |
| |
| int mydata_index; /* GLOBAL */ |
| |
| void |
| tls_init_lib(void) |
| { |
| #if (OPENSSL_VERSION_NUMBER < 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) |
| SSL_library_init(); |
| #ifndef ENABLE_SMALL |
| SSL_load_error_strings(); |
| #endif |
| OpenSSL_add_all_algorithms(); |
| #endif |
| mydata_index = SSL_get_ex_new_index(0, "struct session *", NULL, NULL, NULL); |
| ASSERT(mydata_index >= 0); |
| } |
| |
| void |
| tls_free_lib(void) |
| { |
| #if (OPENSSL_VERSION_NUMBER < 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) |
| EVP_cleanup(); |
| #ifndef ENABLE_SMALL |
| ERR_free_strings(); |
| #endif |
| #endif |
| } |
| |
| void |
| tls_clear_error(void) |
| { |
| ERR_clear_error(); |
| } |
| |
| void |
| tls_ctx_server_new(struct tls_root_ctx *ctx) |
| { |
| ASSERT(NULL != ctx); |
| |
| ctx->ctx = SSL_CTX_new(SSLv23_server_method()); |
| |
| if (ctx->ctx == NULL) |
| { |
| crypto_msg(M_FATAL, "SSL_CTX_new SSLv23_server_method"); |
| } |
| if (ERR_peek_error() != 0) |
| { |
| crypto_msg(M_WARN, "Warning: TLS server context initialisation " |
| "has warnings."); |
| } |
| } |
| |
| void |
| tls_ctx_client_new(struct tls_root_ctx *ctx) |
| { |
| ASSERT(NULL != ctx); |
| |
| ctx->ctx = SSL_CTX_new(SSLv23_client_method()); |
| |
| if (ctx->ctx == NULL) |
| { |
| crypto_msg(M_FATAL, "SSL_CTX_new SSLv23_client_method"); |
| } |
| if (ERR_peek_error() != 0) |
| { |
| crypto_msg(M_WARN, "Warning: TLS client context initialisation " |
| "has warnings."); |
| } |
| } |
| |
| void |
| tls_ctx_free(struct tls_root_ctx *ctx) |
| { |
| ASSERT(NULL != ctx); |
| if (NULL != ctx->ctx) |
| { |
| SSL_CTX_free(ctx->ctx); |
| } |
| ctx->ctx = NULL; |
| } |
| |
| bool |
| tls_ctx_initialised(struct tls_root_ctx *ctx) |
| { |
| ASSERT(NULL != ctx); |
| return NULL != ctx->ctx; |
| } |
| |
| void |
| key_state_export_keying_material(struct key_state_ssl *ssl, |
| struct tls_session *session) |
| { |
| if (session->opt->ekm_size > 0) |
| { |
| unsigned int size = session->opt->ekm_size; |
| struct gc_arena gc = gc_new(); |
| unsigned char *ekm = (unsigned char *) gc_malloc(size, true, &gc); |
| |
| if (SSL_export_keying_material(ssl->ssl, ekm, size, |
| session->opt->ekm_label, |
| session->opt->ekm_label_size, |
| NULL, 0, 0)) |
| { |
| unsigned int len = (size * 2) + 2; |
| |
| const char *key = format_hex_ex(ekm, size, len, 0, NULL, &gc); |
| setenv_str(session->opt->es, "exported_keying_material", key); |
| |
| dmsg(D_TLS_DEBUG_MED, "%s: exported keying material: %s", |
| __func__, key); |
| } |
| else |
| { |
| msg(M_WARN, "WARNING: Export keying material failed!"); |
| setenv_del(session->opt->es, "exported_keying_material"); |
| } |
| gc_free(&gc); |
| } |
| } |
| |
| /* |
| * Print debugging information on SSL/TLS session negotiation. |
| */ |
| |
| #ifndef INFO_CALLBACK_SSL_CONST |
| #define INFO_CALLBACK_SSL_CONST const |
| #endif |
| static void |
| info_callback(INFO_CALLBACK_SSL_CONST SSL *s, int where, int ret) |
| { |
| if (where & SSL_CB_LOOP) |
| { |
| dmsg(D_HANDSHAKE_VERBOSE, "SSL state (%s): %s", |
| where & SSL_ST_CONNECT ? "connect" : |
| where &SSL_ST_ACCEPT ? "accept" : |
| "undefined", SSL_state_string_long(s)); |
| } |
| else if (where & SSL_CB_ALERT) |
| { |
| dmsg(D_HANDSHAKE_VERBOSE, "SSL alert (%s): %s: %s", |
| where & SSL_CB_READ ? "read" : "write", |
| SSL_alert_type_string_long(ret), |
| SSL_alert_desc_string_long(ret)); |
| } |
| } |
| |
| /* |
| * Return maximum TLS version supported by local OpenSSL library. |
| * Assume that presence of SSL_OP_NO_TLSvX macro indicates that |
| * TLSvX is supported. |
| */ |
| int |
| tls_version_max(void) |
| { |
| #if defined(TLS1_3_VERSION) |
| /* If this is defined we can safely assume TLS 1.3 support */ |
| return TLS_VER_1_3; |
| #elif OPENSSL_VERSION_NUMBER >= 0x10100000L |
| /* |
| * If TLS_VER_1_3 is not defined, we were compiled against a version that |
| * did not support TLS 1.3. |
| * |
| * However, the library we are *linked* against might be OpenSSL 1.1.1 |
| * and therefore supports TLS 1.3. This needs to be checked at runtime |
| * since we can be compiled against 1.1.0 and then the library can be |
| * upgraded to 1.1.1. |
| * We only need to check this for OpenSSL versions that can be |
| * upgraded to 1.1.1 without recompile (>= 1.1.0) |
| */ |
| if (OpenSSL_version_num() >= 0x1010100fL) |
| { |
| return TLS_VER_1_3; |
| } |
| else |
| { |
| return TLS_VER_1_2; |
| } |
| #elif defined(TLS1_2_VERSION) || defined(SSL_OP_NO_TLSv1_2) |
| return TLS_VER_1_2; |
| #elif defined(TLS1_1_VERSION) || defined(SSL_OP_NO_TLSv1_1) |
| return TLS_VER_1_1; |
| #else /* if defined(TLS1_3_VERSION) */ |
| return TLS_VER_1_0; |
| #endif |
| } |
| |
| /** Convert internal version number to openssl version number */ |
| static int |
| openssl_tls_version(int ver) |
| { |
| if (ver == TLS_VER_1_0) |
| { |
| return TLS1_VERSION; |
| } |
| else if (ver == TLS_VER_1_1) |
| { |
| return TLS1_1_VERSION; |
| } |
| else if (ver == TLS_VER_1_2) |
| { |
| return TLS1_2_VERSION; |
| } |
| else if (ver == TLS_VER_1_3) |
| { |
| /* |
| * Supporting the library upgraded to TLS1.3 without recompile |
| * is enough to support here with a simple constant that the same |
| * as in the TLS 1.3, so spec it is very unlikely that OpenSSL |
| * will change this constant |
| */ |
| #ifndef TLS1_3_VERSION |
| /* |
| * We do not want to define TLS_VER_1_3 if not defined |
| * since other parts of the code use the existance of this macro |
| * as proxy for TLS 1.3 support |
| */ |
| return 0x0304; |
| #else |
| return TLS1_3_VERSION; |
| #endif |
| } |
| return 0; |
| } |
| |
| static bool |
| tls_ctx_set_tls_versions(struct tls_root_ctx *ctx, unsigned int ssl_flags) |
| { |
| int tls_ver_min = openssl_tls_version( |
| (ssl_flags >> SSLF_TLS_VERSION_MIN_SHIFT) & SSLF_TLS_VERSION_MIN_MASK); |
| int tls_ver_max = openssl_tls_version( |
| (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK); |
| |
| if (!tls_ver_min) |
| { |
| /* Enforce at least TLS 1.0 */ |
| int cur_min = SSL_CTX_get_min_proto_version(ctx->ctx); |
| tls_ver_min = cur_min < TLS1_VERSION ? TLS1_VERSION : cur_min; |
| } |
| |
| if (!SSL_CTX_set_min_proto_version(ctx->ctx, tls_ver_min)) |
| { |
| msg(D_TLS_ERRORS, "%s: failed to set minimum TLS version", __func__); |
| return false; |
| } |
| |
| if (tls_ver_max && !SSL_CTX_set_max_proto_version(ctx->ctx, tls_ver_max)) |
| { |
| msg(D_TLS_ERRORS, "%s: failed to set maximum TLS version", __func__); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool |
| tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags) |
| { |
| ASSERT(NULL != ctx); |
| |
| /* process SSL options */ |
| long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_NO_TICKET; |
| #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE |
| sslopt |= SSL_OP_CIPHER_SERVER_PREFERENCE; |
| #endif |
| sslopt |= SSL_OP_NO_COMPRESSION; |
| /* Disable TLS renegotiations. OpenVPN's renegotiation creates new SSL |
| * session and does not depend on this feature. And TLS renegotiations have |
| * been problematic in the past */ |
| #ifdef SSL_OP_NO_RENEGOTIATION |
| sslopt |= SSL_OP_NO_RENEGOTIATION; |
| #endif |
| |
| SSL_CTX_set_options(ctx->ctx, sslopt); |
| |
| if (!tls_ctx_set_tls_versions(ctx, ssl_flags)) |
| { |
| return false; |
| } |
| |
| #ifdef SSL_MODE_RELEASE_BUFFERS |
| SSL_CTX_set_mode(ctx->ctx, SSL_MODE_RELEASE_BUFFERS); |
| #endif |
| SSL_CTX_set_session_cache_mode(ctx->ctx, SSL_SESS_CACHE_OFF); |
| SSL_CTX_set_default_passwd_cb(ctx->ctx, pem_password_callback); |
| |
| /* Require peer certificate verification */ |
| int verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; |
| if (ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED) |
| { |
| verify_flags = 0; |
| } |
| else if (ssl_flags & SSLF_CLIENT_CERT_OPTIONAL) |
| { |
| verify_flags = SSL_VERIFY_PEER; |
| } |
| SSL_CTX_set_verify(ctx->ctx, verify_flags, verify_callback); |
| |
| SSL_CTX_set_info_callback(ctx->ctx, info_callback); |
| |
| return true; |
| } |
| |
| void |
| convert_tls_list_to_openssl(char *openssl_ciphers, size_t len,const char *ciphers) |
| { |
| /* Parse supplied cipher list and pass on to OpenSSL */ |
| size_t begin_of_cipher, end_of_cipher; |
| |
| const char *current_cipher; |
| size_t current_cipher_len; |
| |
| const tls_cipher_name_pair *cipher_pair; |
| |
| size_t openssl_ciphers_len = 0; |
| openssl_ciphers[0] = '\0'; |
| |
| /* Translate IANA cipher suite names to OpenSSL names */ |
| begin_of_cipher = end_of_cipher = 0; |
| for (; begin_of_cipher < strlen(ciphers); begin_of_cipher = end_of_cipher) |
| { |
| end_of_cipher += strcspn(&ciphers[begin_of_cipher], ":"); |
| cipher_pair = tls_get_cipher_name_pair(&ciphers[begin_of_cipher], end_of_cipher - begin_of_cipher); |
| |
| if (NULL == cipher_pair) |
| { |
| /* No translation found, use original */ |
| current_cipher = &ciphers[begin_of_cipher]; |
| current_cipher_len = end_of_cipher - begin_of_cipher; |
| |
| /* Issue warning on missing translation */ |
| /* %.*s format specifier expects length of type int, so guarantee */ |
| /* that length is small enough and cast to int. */ |
| msg(D_LOW, "No valid translation found for TLS cipher '%.*s'", |
| constrain_int(current_cipher_len, 0, 256), current_cipher); |
| } |
| else |
| { |
| /* Use OpenSSL name */ |
| current_cipher = cipher_pair->openssl_name; |
| current_cipher_len = strlen(current_cipher); |
| |
| if (end_of_cipher - begin_of_cipher == current_cipher_len |
| && 0 != memcmp(&ciphers[begin_of_cipher], cipher_pair->iana_name, |
| end_of_cipher - begin_of_cipher)) |
| { |
| /* Non-IANA name used, show warning */ |
| msg(M_WARN, "Deprecated TLS cipher name '%s', please use IANA name '%s'", cipher_pair->openssl_name, cipher_pair->iana_name); |
| } |
| } |
| |
| /* Make sure new cipher name fits in cipher string */ |
| if ((SIZE_MAX - openssl_ciphers_len) < current_cipher_len |
| || (len - 1) < (openssl_ciphers_len + current_cipher_len)) |
| { |
| msg(M_FATAL, |
| "Failed to set restricted TLS cipher list, too long (>%d).", |
| (int)(len - 1)); |
| } |
| |
| /* Concatenate cipher name to OpenSSL cipher string */ |
| memcpy(&openssl_ciphers[openssl_ciphers_len], current_cipher, current_cipher_len); |
| openssl_ciphers_len += current_cipher_len; |
| openssl_ciphers[openssl_ciphers_len] = ':'; |
| openssl_ciphers_len++; |
| |
| end_of_cipher++; |
| } |
| |
| if (openssl_ciphers_len > 0) |
| { |
| openssl_ciphers[openssl_ciphers_len-1] = '\0'; |
| } |
| } |
| |
| void |
| tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers) |
| { |
| if (ciphers == NULL) |
| { |
| /* Use sane default TLS cipher list */ |
| if (!SSL_CTX_set_cipher_list(ctx->ctx, |
| /* Use openssl's default list as a basis */ |
| "DEFAULT" |
| /* Disable export ciphers and openssl's 'low' and 'medium' ciphers */ |
| ":!EXP:!LOW:!MEDIUM" |
| /* Disable static (EC)DH keys (no forward secrecy) */ |
| ":!kDH:!kECDH" |
| /* Disable DSA private keys */ |
| ":!DSS" |
| /* Disable unsupported TLS modes */ |
| ":!PSK:!SRP:!kRSA")) |
| { |
| crypto_msg(M_FATAL, "Failed to set default TLS cipher list."); |
| } |
| return; |
| } |
| |
| char openssl_ciphers[4096]; |
| convert_tls_list_to_openssl(openssl_ciphers, sizeof(openssl_ciphers), ciphers); |
| |
| ASSERT(NULL != ctx); |
| |
| /* Set OpenSSL cipher list */ |
| if (!SSL_CTX_set_cipher_list(ctx->ctx, openssl_ciphers)) |
| { |
| crypto_msg(M_FATAL, "Failed to set restricted TLS cipher list: %s", openssl_ciphers); |
| } |
| } |
| |
| void |
| convert_tls13_list_to_openssl(char *openssl_ciphers, size_t len, |
| const char *ciphers) |
| { |
| /* |
| * OpenSSL (and official IANA) cipher names have _ in them. We |
| * historically used names with - in them. Silently convert names |
| * with - to names with _ to support both |
| */ |
| if (strlen(ciphers) >= (len - 1)) |
| { |
| msg(M_FATAL, |
| "Failed to set restricted TLS 1.3 cipher list, too long (>%d).", |
| (int) (len - 1)); |
| } |
| |
| strncpy(openssl_ciphers, ciphers, len); |
| |
| for (size_t i = 0; i < strlen(openssl_ciphers); i++) |
| { |
| if (openssl_ciphers[i] == '-') |
| { |
| openssl_ciphers[i] = '_'; |
| } |
| } |
| } |
| |
| void |
| tls_ctx_restrict_ciphers_tls13(struct tls_root_ctx *ctx, const char *ciphers) |
| { |
| if (ciphers == NULL) |
| { |
| /* default cipher list of OpenSSL 1.1.1 is sane, do not set own |
| * default as we do with tls-cipher */ |
| return; |
| } |
| |
| #if !defined(TLS1_3_VERSION) |
| crypto_msg(M_WARN, "Not compiled with OpenSSL 1.1.1 or higher. " |
| "Ignoring TLS 1.3 only tls-ciphersuites '%s' setting.", |
| ciphers); |
| #else |
| ASSERT(NULL != ctx); |
| |
| char openssl_ciphers[4096]; |
| convert_tls13_list_to_openssl(openssl_ciphers, sizeof(openssl_ciphers), |
| ciphers); |
| |
| if (!SSL_CTX_set_ciphersuites(ctx->ctx, openssl_ciphers)) |
| { |
| crypto_msg(M_FATAL, "Failed to set restricted TLS 1.3 cipher list: %s", |
| openssl_ciphers); |
| } |
| #endif |
| } |
| |
| void |
| tls_ctx_set_cert_profile(struct tls_root_ctx *ctx, const char *profile) |
| { |
| #ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL |
| /* OpenSSL does not have certificate profiles, but a complex set of |
| * callbacks that we could try to implement to achieve something similar. |
| * For now, use OpenSSL's security levels to achieve similar (but not equal) |
| * behaviour. */ |
| if (!profile || 0 == strcmp(profile, "legacy")) |
| { |
| SSL_CTX_set_security_level(ctx->ctx, 1); |
| } |
| else if (0 == strcmp(profile, "preferred")) |
| { |
| SSL_CTX_set_security_level(ctx->ctx, 2); |
| } |
| else if (0 == strcmp(profile, "suiteb")) |
| { |
| SSL_CTX_set_security_level(ctx->ctx, 3); |
| SSL_CTX_set_cipher_list(ctx->ctx, "SUITEB128"); |
| } |
| else |
| { |
| msg(M_FATAL, "ERROR: Invalid cert profile: %s", profile); |
| } |
| #else /* ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL */ |
| if (profile) |
| { |
| msg(M_WARN, "WARNING: OpenSSL 1.0.2 does not support --tls-cert-profile" |
| ", ignoring user-set profile: '%s'", profile); |
| } |
| #endif /* ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL */ |
| } |
| |
| void |
| tls_ctx_set_tls_groups(struct tls_root_ctx *ctx, const char *groups) |
| { |
| ASSERT(ctx); |
| struct gc_arena gc = gc_new(); |
| /* This method could be as easy as |
| * SSL_CTX_set1_groups_list(ctx->ctx, groups) |
| * but OpenSSL does not like the name secp256r1 for prime256v1 |
| * This is one of the important curves. |
| * To support the same name for OpenSSL and mbedTLS, we do |
| * this dance. |
| */ |
| |
| int groups_count = get_num_elements(groups, ':'); |
| |
| int *glist; |
| /* Allocate an array for them */ |
| ALLOC_ARRAY_CLEAR_GC(glist, int, groups_count, &gc); |
| |
| /* Parse allowed ciphers, getting IDs */ |
| int glistlen = 0; |
| char *tmp_groups = string_alloc(groups, &gc); |
| |
| const char *token; |
| while ((token = strsep(&tmp_groups, ":"))) |
| { |
| if (streq(token, "secp256r1")) |
| { |
| token = "prime256v1"; |
| } |
| int nid = OBJ_sn2nid(token); |
| |
| if (nid == 0) |
| { |
| msg(M_WARN, "Warning unknown curve/group specified: %s", token); |
| } |
| else |
| { |
| glist[glistlen] = nid; |
| glistlen++; |
| } |
| } |
| |
| if (!SSL_CTX_set1_groups(ctx->ctx, glist, glistlen)) |
| { |
| crypto_msg(M_FATAL, "Failed to set allowed TLS group list: %s", |
| groups); |
| } |
| gc_free(&gc); |
| } |
| |
| void |
| tls_ctx_check_cert_time(const struct tls_root_ctx *ctx) |
| { |
| int ret; |
| const X509 *cert; |
| |
| ASSERT(ctx); |
| |
| cert = SSL_CTX_get0_certificate(ctx->ctx); |
| |
| if (cert == NULL) |
| { |
| return; /* Nothing to check if there is no certificate */ |
| } |
| |
| ret = X509_cmp_time(X509_get0_notBefore(cert), NULL); |
| if (ret == 0) |
| { |
| msg(D_TLS_DEBUG_MED, "Failed to read certificate notBefore field."); |
| } |
| if (ret > 0) |
| { |
| msg(M_WARN, "WARNING: Your certificate is not yet valid!"); |
| } |
| |
| ret = X509_cmp_time(X509_get0_notAfter(cert), NULL); |
| if (ret == 0) |
| { |
| msg(D_TLS_DEBUG_MED, "Failed to read certificate notAfter field."); |
| } |
| if (ret < 0) |
| { |
| msg(M_WARN, "WARNING: Your certificate has expired!"); |
| } |
| } |
| |
| void |
| tls_ctx_load_dh_params(struct tls_root_ctx *ctx, const char *dh_file, |
| bool dh_file_inline) |
| { |
| DH *dh; |
| BIO *bio; |
| |
| ASSERT(NULL != ctx); |
| |
| if (dh_file_inline) |
| { |
| if (!(bio = BIO_new_mem_buf((char *)dh_file, -1))) |
| { |
| crypto_msg(M_FATAL, "Cannot open memory BIO for inline DH parameters"); |
| } |
| } |
| else |
| { |
| /* Get Diffie Hellman Parameters */ |
| if (!(bio = BIO_new_file(dh_file, "r"))) |
| { |
| crypto_msg(M_FATAL, "Cannot open %s for DH parameters", dh_file); |
| } |
| } |
| |
| dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); |
| BIO_free(bio); |
| |
| if (!dh) |
| { |
| crypto_msg(M_FATAL, "Cannot load DH parameters from %s", |
| print_key_filename(dh_file, dh_file_inline)); |
| } |
| if (!SSL_CTX_set_tmp_dh(ctx->ctx, dh)) |
| { |
| crypto_msg(M_FATAL, "SSL_CTX_set_tmp_dh"); |
| } |
| |
| msg(D_TLS_DEBUG_LOW, "Diffie-Hellman initialized with %d bit key", |
| 8 * DH_size(dh)); |
| |
| DH_free(dh); |
| } |
| |
| void |
| tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name |
| ) |
| { |
| #ifndef OPENSSL_NO_EC |
| int nid = NID_undef; |
| EC_KEY *ecdh = NULL; |
| const char *sname = NULL; |
| |
| /* Generate a new ECDH key for each SSL session (for non-ephemeral ECDH) */ |
| SSL_CTX_set_options(ctx->ctx, SSL_OP_SINGLE_ECDH_USE); |
| |
| if (curve_name != NULL) |
| { |
| /* Use user supplied curve if given */ |
| msg(D_TLS_DEBUG, "Using user specified ECDH curve (%s)", curve_name); |
| nid = OBJ_sn2nid(curve_name); |
| } |
| else |
| { |
| #if (OPENSSL_VERSION_NUMBER < 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) |
| |
| /* OpenSSL 1.0.2 and newer can automatically handle ECDH parameter |
| * loading */ |
| SSL_CTX_set_ecdh_auto(ctx->ctx, 1); |
| |
| /* OpenSSL 1.1.0 and newer have always ecdh auto loading enabled, |
| * so do nothing */ |
| #endif |
| return; |
| } |
| |
| /* Translate NID back to name , just for kicks */ |
| sname = OBJ_nid2sn(nid); |
| if (sname == NULL) |
| { |
| sname = "(Unknown)"; |
| } |
| |
| /* Create new EC key and set as ECDH key */ |
| if (NID_undef == nid || NULL == (ecdh = EC_KEY_new_by_curve_name(nid))) |
| { |
| /* Creating key failed, fall back on sane default */ |
| ecdh = EC_KEY_new_by_curve_name(NID_secp384r1); |
| const char *source = (NULL == curve_name) ? |
| "extract curve from certificate" : "use supplied curve"; |
| msg(D_TLS_DEBUG_LOW, |
| "Failed to %s (%s), using secp384r1 instead.", source, sname); |
| sname = OBJ_nid2sn(NID_secp384r1); |
| } |
| |
| if (!SSL_CTX_set_tmp_ecdh(ctx->ctx, ecdh)) |
| { |
| crypto_msg(M_FATAL, "SSL_CTX_set_tmp_ecdh: cannot add curve"); |
| } |
| |
| msg(D_TLS_DEBUG_LOW, "ECDH curve %s added", sname); |
| |
| EC_KEY_free(ecdh); |
| #else /* ifndef OPENSSL_NO_EC */ |
| msg(D_LOW, "Your OpenSSL library was built without elliptic curve support." |
| " Skipping ECDH parameter loading."); |
| #endif /* OPENSSL_NO_EC */ |
| } |
| |
| int |
| tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file, |
| bool pkcs12_file_inline, bool load_ca_file) |
| { |
| FILE *fp; |
| EVP_PKEY *pkey; |
| X509 *cert; |
| STACK_OF(X509) *ca = NULL; |
| PKCS12 *p12; |
| int i; |
| char password[256]; |
| |
| ASSERT(NULL != ctx); |
| |
| if (pkcs12_file_inline) |
| { |
| BIO *b64 = BIO_new(BIO_f_base64()); |
| BIO *bio = BIO_new_mem_buf((void *) pkcs12_file, |
| (int) strlen(pkcs12_file)); |
| ASSERT(b64 && bio); |
| BIO_push(b64, bio); |
| p12 = d2i_PKCS12_bio(b64, NULL); |
| if (!p12) |
| { |
| crypto_msg(M_FATAL, "Error reading inline PKCS#12 file"); |
| } |
| BIO_free(b64); |
| BIO_free(bio); |
| } |
| else |
| { |
| /* Load the PKCS #12 file */ |
| if (!(fp = platform_fopen(pkcs12_file, "rb"))) |
| { |
| crypto_msg(M_FATAL, "Error opening file %s", pkcs12_file); |
| } |
| p12 = d2i_PKCS12_fp(fp, NULL); |
| fclose(fp); |
| if (!p12) |
| { |
| crypto_msg(M_FATAL, "Error reading PKCS#12 file %s", pkcs12_file); |
| } |
| } |
| |
| /* Parse the PKCS #12 file */ |
| if (!PKCS12_parse(p12, "", &pkey, &cert, &ca)) |
| { |
| pem_password_callback(password, sizeof(password) - 1, 0, NULL); |
| /* Reparse the PKCS #12 file with password */ |
| ca = NULL; |
| if (!PKCS12_parse(p12, password, &pkey, &cert, &ca)) |
| { |
| #ifdef ENABLE_MANAGEMENT |
| if (management && (ERR_GET_REASON(ERR_peek_error()) == PKCS12_R_MAC_VERIFY_FAILURE)) |
| { |
| management_auth_failure(management, UP_TYPE_PRIVATE_KEY, NULL); |
| } |
| #endif |
| PKCS12_free(p12); |
| return 1; |
| } |
| } |
| PKCS12_free(p12); |
| |
| /* Load Certificate */ |
| if (!SSL_CTX_use_certificate(ctx->ctx, cert)) |
| { |
| crypto_msg(M_FATAL, "Cannot use certificate"); |
| } |
| |
| /* Load Private Key */ |
| if (!SSL_CTX_use_PrivateKey(ctx->ctx, pkey)) |
| { |
| crypto_msg(M_FATAL, "Cannot use private key"); |
| } |
| |
| /* Check Private Key */ |
| if (!SSL_CTX_check_private_key(ctx->ctx)) |
| { |
| crypto_msg(M_FATAL, "Private key does not match the certificate"); |
| } |
| |
| /* Set Certificate Verification chain */ |
| if (load_ca_file) |
| { |
| /* Add CAs from PKCS12 to the cert store and mark them as trusted. |
| * They're also used to fill in the chain of intermediate certs as |
| * necessary. |
| */ |
| if (ca && sk_X509_num(ca)) |
| { |
| for (i = 0; i < sk_X509_num(ca); i++) |
| { |
| X509_STORE *cert_store = SSL_CTX_get_cert_store(ctx->ctx); |
| if (!X509_STORE_add_cert(cert_store,sk_X509_value(ca, i))) |
| { |
| crypto_msg(M_FATAL,"Cannot add certificate to certificate chain (X509_STORE_add_cert)"); |
| } |
| if (!SSL_CTX_add_client_CA(ctx->ctx, sk_X509_value(ca, i))) |
| { |
| crypto_msg(M_FATAL,"Cannot add certificate to client CA list (SSL_CTX_add_client_CA)"); |
| } |
| } |
| } |
| } |
| else |
| { |
| /* If trusted CA certs were loaded from a PEM file, and we ignore the |
| * ones in PKCS12, do load PKCS12-provided certs to the client extra |
| * certs chain just in case they include intermediate CAs needed to |
| * prove my identity to the other end. This does not make them trusted. |
| */ |
| if (ca && sk_X509_num(ca)) |
| { |
| for (i = 0; i < sk_X509_num(ca); i++) |
| { |
| if (!SSL_CTX_add_extra_chain_cert(ctx->ctx,sk_X509_value(ca, i))) |
| { |
| crypto_msg(M_FATAL, "Cannot add extra certificate to chain (SSL_CTX_add_extra_chain_cert)"); |
| } |
| } |
| } |
| } |
| return 0; |
| } |
| |
| #ifdef ENABLE_CRYPTOAPI |
| void |
| tls_ctx_load_cryptoapi(struct tls_root_ctx *ctx, const char *cryptoapi_cert) |
| { |
| ASSERT(NULL != ctx); |
| |
| /* Load Certificate and Private Key */ |
| if (!SSL_CTX_use_CryptoAPI_certificate(ctx->ctx, cryptoapi_cert)) |
| { |
| crypto_msg(M_FATAL, "Cannot load certificate \"%s\" from Microsoft Certificate Store", cryptoapi_cert); |
| } |
| } |
| #endif /* ENABLE_CRYPTOAPI */ |
| |
| static void |
| tls_ctx_add_extra_certs(struct tls_root_ctx *ctx, BIO *bio, bool optional) |
| { |
| X509 *cert; |
| while (true) |
| { |
| cert = NULL; |
| if (!PEM_read_bio_X509(bio, &cert, NULL, NULL)) |
| { |
| /* a PEM_R_NO_START_LINE "Error" indicates that no certificate |
| * is found in the buffer. If loading more certificates is |
| * optional, break without raising an error |
| */ |
| if (optional |
| && ERR_GET_REASON(ERR_peek_error()) == PEM_R_NO_START_LINE) |
| { |
| /* remove that error from error stack */ |
| (void)ERR_get_error(); |
| break; |
| } |
| |
| /* Otherwise, bail out with error */ |
| crypto_msg(M_FATAL, "Error reading extra certificate"); |
| } |
| /* takes ownership of cert like a set1 method */ |
| if (SSL_CTX_add_extra_chain_cert(ctx->ctx, cert) != 1) |
| { |
| crypto_msg(M_FATAL, "Error adding extra certificate"); |
| } |
| /* We loaded at least one certificate, so loading more is optional */ |
| optional = true; |
| } |
| } |
| |
| void |
| tls_ctx_load_cert_file(struct tls_root_ctx *ctx, const char *cert_file, |
| bool cert_file_inline) |
| { |
| BIO *in = NULL; |
| X509 *x = NULL; |
| int ret = 0; |
| |
| ASSERT(NULL != ctx); |
| |
| if (cert_file_inline) |
| { |
| in = BIO_new_mem_buf((char *) cert_file, -1); |
| } |
| else |
| { |
| in = BIO_new_file(cert_file, "r"); |
| } |
| |
| if (in == NULL) |
| { |
| SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_SYS_LIB); |
| goto end; |
| } |
| |
| x = PEM_read_bio_X509(in, NULL, |
| SSL_CTX_get_default_passwd_cb(ctx->ctx), |
| SSL_CTX_get_default_passwd_cb_userdata(ctx->ctx)); |
| if (x == NULL) |
| { |
| SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PEM_LIB); |
| goto end; |
| } |
| |
| ret = SSL_CTX_use_certificate(ctx->ctx, x); |
| if (ret) |
| { |
| tls_ctx_add_extra_certs(ctx, in, true); |
| } |
| |
| end: |
| if (!ret) |
| { |
| if (cert_file_inline) |
| { |
| crypto_msg(M_FATAL, "Cannot load inline certificate file"); |
| } |
| else |
| { |
| crypto_msg(M_FATAL, "Cannot load certificate file %s", cert_file); |
| } |
| } |
| else |
| { |
| crypto_print_openssl_errors(M_DEBUG); |
| } |
| |
| if (in != NULL) |
| { |
| BIO_free(in); |
| } |
| if (x) |
| { |
| X509_free(x); |
| } |
| } |
| |
| int |
| tls_ctx_load_priv_file(struct tls_root_ctx *ctx, const char *priv_key_file, |
| bool priv_key_file_inline) |
| { |
| SSL_CTX *ssl_ctx = NULL; |
| BIO *in = NULL; |
| EVP_PKEY *pkey = NULL; |
| int ret = 1; |
| |
| ASSERT(NULL != ctx); |
| |
| ssl_ctx = ctx->ctx; |
| |
| if (priv_key_file_inline) |
| { |
| in = BIO_new_mem_buf((char *) priv_key_file, -1); |
| } |
| else |
| { |
| in = BIO_new_file(priv_key_file, "r"); |
| } |
| |
| if (!in) |
| { |
| goto end; |
| } |
| |
| pkey = PEM_read_bio_PrivateKey(in, NULL, |
| SSL_CTX_get_default_passwd_cb(ctx->ctx), |
| SSL_CTX_get_default_passwd_cb_userdata(ctx->ctx)); |
| if (!pkey) |
| { |
| pkey = engine_load_key(priv_key_file, ctx->ctx); |
| } |
| |
| if (!pkey || !SSL_CTX_use_PrivateKey(ssl_ctx, pkey)) |
| { |
| #ifdef ENABLE_MANAGEMENT |
| if (management && (ERR_GET_REASON(ERR_peek_error()) == EVP_R_BAD_DECRYPT)) |
| { |
| management_auth_failure(management, UP_TYPE_PRIVATE_KEY, NULL); |
| } |
| #endif |
| crypto_msg(M_WARN, "Cannot load private key file %s", |
| print_key_filename(priv_key_file, priv_key_file_inline)); |
| goto end; |
| } |
| |
| /* Check Private Key */ |
| if (!SSL_CTX_check_private_key(ssl_ctx)) |
| { |
| crypto_msg(M_FATAL, "Private key does not match the certificate"); |
| } |
| ret = 0; |
| |
| end: |
| if (pkey) |
| { |
| EVP_PKEY_free(pkey); |
| } |
| if (in) |
| { |
| BIO_free(in); |
| } |
| return ret; |
| } |
| |
| void |
| backend_tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file, |
| bool crl_inline) |
| { |
| BIO *in = NULL; |
| |
| X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx->ctx); |
| if (!store) |
| { |
| crypto_msg(M_FATAL, "Cannot get certificate store"); |
| } |
| |
| /* Always start with a cleared CRL list, for that we |
| * we need to manually find the CRL object from the stack |
| * and remove it */ |
| STACK_OF(X509_OBJECT) *objs = X509_STORE_get0_objects(store); |
| for (int i = 0; i < sk_X509_OBJECT_num(objs); i++) |
| { |
| X509_OBJECT *obj = sk_X509_OBJECT_value(objs, i); |
| ASSERT(obj); |
| if (X509_OBJECT_get_type(obj) == X509_LU_CRL) |
| { |
| sk_X509_OBJECT_delete(objs, i); |
| X509_OBJECT_free(obj); |
| } |
| } |
| |
| X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); |
| |
| if (crl_inline) |
| { |
| in = BIO_new_mem_buf((char *) crl_file, -1); |
| } |
| else |
| { |
| in = BIO_new_file(crl_file, "r"); |
| } |
| |
| if (in == NULL) |
| { |
| msg(M_WARN, "CRL: cannot read: %s", |
| print_key_filename(crl_file, crl_inline)); |
| goto end; |
| } |
| |
| int num_crls_loaded = 0; |
| while (true) |
| { |
| X509_CRL *crl = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL); |
| if (crl == NULL) |
| { |
| /* |
| * PEM_R_NO_START_LINE can be considered equivalent to EOF. |
| */ |
| bool eof = ERR_GET_REASON(ERR_peek_error()) == PEM_R_NO_START_LINE; |
| /* but warn if no CRLs have been loaded */ |
| if (num_crls_loaded > 0 && eof) |
| { |
| /* remove that error from error stack */ |
| (void)ERR_get_error(); |
| break; |
| } |
| |
| crypto_msg(M_WARN, "CRL: cannot read CRL from file %s", |
| print_key_filename(crl_file, crl_inline)); |
| break; |
| } |
| |
| if (!X509_STORE_add_crl(store, crl)) |
| { |
| X509_CRL_free(crl); |
| crypto_msg(M_WARN, "CRL: cannot add %s to store", |
| print_key_filename(crl_file, crl_inline)); |
| break; |
| } |
| X509_CRL_free(crl); |
| num_crls_loaded++; |
| } |
| msg(M_INFO, "CRL: loaded %d CRLs from file %s", num_crls_loaded, crl_file); |
| end: |
| BIO_free(in); |
| } |
| |
| |
| #ifdef ENABLE_MANAGEMENT |
| |
| /* encrypt */ |
| static int |
| rsa_pub_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) |
| { |
| ASSERT(0); |
| return -1; |
| } |
| |
| /* verify arbitrary data */ |
| static int |
| rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) |
| { |
| ASSERT(0); |
| return -1; |
| } |
| |
| /* decrypt */ |
| static int |
| rsa_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) |
| { |
| ASSERT(0); |
| return -1; |
| } |
| |
| /* called at RSA_free */ |
| static int |
| openvpn_extkey_rsa_finish(RSA *rsa) |
| { |
| /* meth was allocated in tls_ctx_use_management_external_key() ; since |
| * this function is called when the parent RSA object is destroyed, |
| * it is no longer used after this point so kill it. */ |
| const RSA_METHOD *meth = RSA_get_method(rsa); |
| RSA_meth_free((RSA_METHOD *)meth); |
| return 1; |
| } |
| |
| /* |
| * Convert OpenSSL's constant to the strings used in the management |
| * interface query |
| */ |
| const char * |
| get_rsa_padding_name(const int padding) |
| { |
| switch (padding) |
| { |
| case RSA_PKCS1_PADDING: |
| return "RSA_PKCS1_PADDING"; |
| |
| case RSA_NO_PADDING: |
| return "RSA_NO_PADDING"; |
| |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| /** |
| * Pass the input hash in 'dgst' to management and get the signature back. |
| * |
| * @param dgst hash to be signed |
| * @param dgstlen len of data in dgst |
| * @param sig On successful return signature is in sig. |
| * @param siglen length of buffer sig |
| * @param algorithm padding/hashing algorithm for the signature |
| * |
| * @return signature length or -1 on error. |
| */ |
| static int |
| get_sig_from_man(const unsigned char *dgst, unsigned int dgstlen, |
| unsigned char *sig, unsigned int siglen, |
| const char *algorithm) |
| { |
| char *in_b64 = NULL; |
| char *out_b64 = NULL; |
| int len = -1; |
| |
| int bencret = openvpn_base64_encode(dgst, dgstlen, &in_b64); |
| |
| if (management && bencret > 0) |
| { |
| out_b64 = management_query_pk_sig(management, in_b64, algorithm); |
| |
| } |
| if (out_b64) |
| { |
| len = openvpn_base64_decode(out_b64, sig, siglen); |
| } |
| |
| free(in_b64); |
| free(out_b64); |
| return len; |
| } |
| |
| /* sign arbitrary data */ |
| static int |
| rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, |
| int padding) |
| { |
| unsigned int len = RSA_size(rsa); |
| int ret = -1; |
| |
| if (padding != RSA_PKCS1_PADDING && padding != RSA_NO_PADDING) |
| { |
| RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); |
| return -1; |
| } |
| |
| ret = get_sig_from_man(from, flen, to, len, get_rsa_padding_name(padding)); |
| |
| return (ret == len) ? ret : -1; |
| } |
| |
| static int |
| tls_ctx_use_external_rsa_key(struct tls_root_ctx *ctx, EVP_PKEY *pkey) |
| { |
| RSA *rsa = NULL; |
| RSA *pub_rsa; |
| RSA_METHOD *rsa_meth; |
| |
| ASSERT(NULL != ctx); |
| |
| pub_rsa = EVP_PKEY_get0_RSA(pkey); |
| ASSERT(NULL != pub_rsa); |
| |
| /* allocate custom RSA method object */ |
| rsa_meth = RSA_meth_new("OpenVPN external private key RSA Method", |
| RSA_METHOD_FLAG_NO_CHECK); |
| check_malloc_return(rsa_meth); |
| RSA_meth_set_pub_enc(rsa_meth, rsa_pub_enc); |
| RSA_meth_set_pub_dec(rsa_meth, rsa_pub_dec); |
| RSA_meth_set_priv_enc(rsa_meth, rsa_priv_enc); |
| RSA_meth_set_priv_dec(rsa_meth, rsa_priv_dec); |
| RSA_meth_set_init(rsa_meth, NULL); |
| RSA_meth_set_finish(rsa_meth, openvpn_extkey_rsa_finish); |
| RSA_meth_set0_app_data(rsa_meth, NULL); |
| |
| /* allocate RSA object */ |
| rsa = RSA_new(); |
| if (rsa == NULL) |
| { |
| SSLerr(SSL_F_SSL_USE_PRIVATEKEY, ERR_R_MALLOC_FAILURE); |
| goto err; |
| } |
| |
| /* initialize RSA object */ |
| const BIGNUM *n = NULL; |
| const BIGNUM *e = NULL; |
| RSA_get0_key(pub_rsa, &n, &e, NULL); |
| RSA_set0_key(rsa, BN_dup(n), BN_dup(e), NULL); |
| RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY); |
| if (!RSA_set_method(rsa, rsa_meth)) |
| { |
| RSA_meth_free(rsa_meth); |
| goto err; |
| } |
| /* from this point rsa_meth will get freed with rsa */ |
| |
| /* bind our custom RSA object to ssl_ctx */ |
| if (!SSL_CTX_use_RSAPrivateKey(ctx->ctx, rsa)) |
| { |
| goto err; |
| } |
| |
| RSA_free(rsa); /* doesn't necessarily free, just decrements refcount */ |
| return 1; |
| |
| err: |
| if (rsa) |
| { |
| RSA_free(rsa); |
| } |
| else |
| { |
| if (rsa_meth) |
| { |
| RSA_meth_free(rsa_meth); |
| } |
| } |
| return 0; |
| } |
| |
| #if ((OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) \ |
| || LIBRESSL_VERSION_NUMBER > 0x2090000fL) \ |
| && !defined(OPENSSL_NO_EC) |
| |
| /* called when EC_KEY is destroyed */ |
| static void |
| openvpn_extkey_ec_finish(EC_KEY *ec) |
| { |
| /* release the method structure */ |
| const EC_KEY_METHOD *ec_meth = EC_KEY_get_method(ec); |
| EC_KEY_METHOD_free((EC_KEY_METHOD *) ec_meth); |
| } |
| |
| /* EC_KEY_METHOD callback: sign(). |
| * Sign the hash using EC key and return DER encoded signature in sig, |
| * its length in siglen. Return value is 1 on success, 0 on error. |
| */ |
| static int |
| ecdsa_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char *sig, |
| unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec) |
| { |
| int capacity = ECDSA_size(ec); |
| /* |
| * ECDSA does not seem to have proper constants for paddings since |
| * there are only signatures without padding at the moment, use |
| * a generic ECDSA for the moment |
| */ |
| int len = get_sig_from_man(dgst, dgstlen, sig, capacity, "ECDSA"); |
| |
| if (len > 0) |
| { |
| *siglen = len; |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* EC_KEY_METHOD callback: sign_setup(). We do no precomputations */ |
| static int |
| ecdsa_sign_setup(EC_KEY *ec, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp) |
| { |
| return 1; |
| } |
| |
| /* EC_KEY_METHOD callback: sign_sig(). |
| * Sign the hash and return the result as a newly allocated ECDS_SIG |
| * struct or NULL on error. |
| */ |
| static ECDSA_SIG * |
| ecdsa_sign_sig(const unsigned char *dgst, int dgstlen, const BIGNUM *in_kinv, |
| const BIGNUM *in_r, EC_KEY *ec) |
| { |
| ECDSA_SIG *ecsig = NULL; |
| unsigned int len = ECDSA_size(ec); |
| struct gc_arena gc = gc_new(); |
| |
| unsigned char *buf = gc_malloc(len, false, &gc); |
| if (ecdsa_sign(0, dgst, dgstlen, buf, &len, NULL, NULL, ec) != 1) |
| { |
| goto out; |
| } |
| /* const char ** should be avoided: not up to us, so we cast our way through */ |
| ecsig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&buf, len); |
| |
| out: |
| gc_free(&gc); |
| return ecsig; |
| } |
| |
| static int |
| tls_ctx_use_external_ec_key(struct tls_root_ctx *ctx, EVP_PKEY *pkey) |
| { |
| EC_KEY *ec = NULL; |
| EVP_PKEY *privkey = NULL; |
| EC_KEY_METHOD *ec_method; |
| |
| ASSERT(ctx); |
| |
| ec_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); |
| if (!ec_method) |
| { |
| goto err; |
| } |
| |
| /* Among init methods, we only need the finish method */ |
| EC_KEY_METHOD_set_init(ec_method, NULL, openvpn_extkey_ec_finish, NULL, NULL, NULL, NULL); |
| EC_KEY_METHOD_set_sign(ec_method, ecdsa_sign, ecdsa_sign_setup, ecdsa_sign_sig); |
| |
| ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pkey)); |
| if (!ec) |
| { |
| EC_KEY_METHOD_free(ec_method); |
| goto err; |
| } |
| if (!EC_KEY_set_method(ec, ec_method)) |
| { |
| EC_KEY_METHOD_free(ec_method); |
| goto err; |
| } |
| /* from this point ec_method will get freed when ec is freed */ |
| |
| privkey = EVP_PKEY_new(); |
| if (!EVP_PKEY_assign_EC_KEY(privkey, ec)) |
| { |
| goto err; |
| } |
| /* from this point ec will get freed when privkey is freed */ |
| |
| if (!SSL_CTX_use_PrivateKey(ctx->ctx, privkey)) |
| { |
| ec = NULL; /* avoid double freeing it below */ |
| goto err; |
| } |
| |
| EVP_PKEY_free(privkey); /* this will down ref privkey and ec */ |
| return 1; |
| |
| err: |
| /* Reach here only when ec and privkey can be independenly freed */ |
| if (privkey) |
| { |
| EVP_PKEY_free(privkey); |
| } |
| if (ec) |
| { |
| EC_KEY_free(ec); |
| } |
| return 0; |
| } |
| #endif /* OPENSSL_VERSION_NUMBER > 1.1.0 dev && !defined(OPENSSL_NO_EC) */ |
| |
| int |
| tls_ctx_use_management_external_key(struct tls_root_ctx *ctx) |
| { |
| int ret = 1; |
| |
| ASSERT(NULL != ctx); |
| |
| X509 *cert = SSL_CTX_get0_certificate(ctx->ctx); |
| |
| ASSERT(NULL != cert); |
| |
| /* get the public key */ |
| EVP_PKEY *pkey = X509_get0_pubkey(cert); |
| ASSERT(pkey); /* NULL before SSL_CTX_use_certificate() is called */ |
| |
| if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) |
| { |
| if (!tls_ctx_use_external_rsa_key(ctx, pkey)) |
| { |
| goto cleanup; |
| } |
| } |
| #if ((OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)) \ |
| || LIBRESSL_VERSION_NUMBER > 0x2090000fL) \ |
| && !defined(OPENSSL_NO_EC) |
| else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) |
| { |
| if (!tls_ctx_use_external_ec_key(ctx, pkey)) |
| { |
| goto cleanup; |
| } |
| } |
| else |
| { |
| crypto_msg(M_WARN, "management-external-key requires an RSA or EC certificate"); |
| goto cleanup; |
| } |
| #else /* OPENSSL_VERSION_NUMBER > 1.1.0 dev && !defined(OPENSSL_NO_EC) */ |
| else |
| { |
| crypto_msg(M_WARN, "management-external-key requires an RSA certificate"); |
| goto cleanup; |
| } |
| #endif /* OPENSSL_VERSION_NUMBER > 1.1.0 dev && !defined(OPENSSL_NO_EC) */ |
| |
| ret = 0; |
| cleanup: |
| if (ret) |
| { |
| crypto_msg(M_FATAL, "Cannot enable SSL external private key capability"); |
| } |
| return ret; |
| } |
| |
| #endif /* ifdef ENABLE_MANAGEMENT */ |
| |
| static int |
| sk_x509_name_cmp(const X509_NAME *const *a, const X509_NAME *const *b) |
| { |
| return X509_NAME_cmp(*a, *b); |
| } |
| |
| void |
| tls_ctx_load_ca(struct tls_root_ctx *ctx, const char *ca_file, |
| bool ca_file_inline, const char *ca_path, bool tls_server) |
| { |
| STACK_OF(X509_INFO) *info_stack = NULL; |
| STACK_OF(X509_NAME) *cert_names = NULL; |
| X509_LOOKUP *lookup = NULL; |
| X509_STORE *store = NULL; |
| X509_NAME *xn = NULL; |
| BIO *in = NULL; |
| int i, added = 0, prev = 0; |
| |
| ASSERT(NULL != ctx); |
| |
| store = SSL_CTX_get_cert_store(ctx->ctx); |
| if (!store) |
| { |
| crypto_msg(M_FATAL, "Cannot get certificate store"); |
| } |
| |
| /* Try to add certificates and CRLs from ca_file */ |
| if (ca_file) |
| { |
| if (ca_file_inline) |
| { |
| in = BIO_new_mem_buf((char *)ca_file, -1); |
| } |
| else |
| { |
| in = BIO_new_file(ca_file, "r"); |
| } |
| |
| if (in) |
| { |
| info_stack = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL); |
| } |
| |
| if (info_stack) |
| { |
| for (i = 0; i < sk_X509_INFO_num(info_stack); i++) |
| { |
| X509_INFO *info = sk_X509_INFO_value(info_stack, i); |
| if (info->crl) |
| { |
| X509_STORE_add_crl(store, info->crl); |
| } |
| |
| if (tls_server && !info->x509) |
| { |
| crypto_msg(M_FATAL, "X509 name was missing in TLS mode"); |
| } |
| |
| if (info->x509) |
| { |
| X509_STORE_add_cert(store, info->x509); |
| added++; |
| |
| if (!tls_server) |
| { |
| continue; |
| } |
| |
| /* Use names of CAs as a client CA list */ |
| if (cert_names == NULL) |
| { |
| cert_names = sk_X509_NAME_new(sk_x509_name_cmp); |
| if (!cert_names) |
| { |
| continue; |
| } |
| } |
| |
| xn = X509_get_subject_name(info->x509); |
| if (!xn) |
| { |
| continue; |
| } |
| |
| /* Don't add duplicate CA names */ |
| if (sk_X509_NAME_find(cert_names, xn) == -1) |
| { |
| xn = X509_NAME_dup(xn); |
| if (!xn) |
| { |
| continue; |
| } |
| sk_X509_NAME_push(cert_names, xn); |
| } |
| } |
| |
| if (tls_server) |
| { |
| int cnum = sk_X509_NAME_num(cert_names); |
| if (cnum != (prev + 1)) |
| { |
| crypto_msg(M_WARN, |
| "Cannot load CA certificate file %s (entry %d did not validate)", |
| print_key_filename(ca_file, ca_file_inline), |
| added); |
| } |
| prev = cnum; |
| } |
| } |
| sk_X509_INFO_pop_free(info_stack, X509_INFO_free); |
| } |
| |
| if (tls_server) |
| { |
| SSL_CTX_set_client_CA_list(ctx->ctx, cert_names); |
| } |
| |
| if (!added) |
| { |
| crypto_msg(M_FATAL, |
| "Cannot load CA certificate file %s (no entries were read)", |
| print_key_filename(ca_file, ca_file_inline)); |
| } |
| |
| if (tls_server) |
| { |
| int cnum = sk_X509_NAME_num(cert_names); |
| if (cnum != added) |
| { |
| crypto_msg(M_FATAL, "Cannot load CA certificate file %s (only %d " |
| "of %d entries were valid X509 names)", |
| print_key_filename(ca_file, ca_file_inline), cnum, |
| added); |
| } |
| } |
| |
| if (in) |
| { |
| BIO_free(in); |
| } |
| } |
| |
| /* Set a store for certs (CA & CRL) with a lookup on the "capath" hash directory */ |
| if (ca_path) |
| { |
| lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()); |
| if (lookup && X509_LOOKUP_add_dir(lookup, ca_path, X509_FILETYPE_PEM)) |
| { |
| msg(M_WARN, "WARNING: experimental option --capath %s", ca_path); |
| } |
| else |
| { |
| crypto_msg(M_FATAL, "Cannot add lookup at --capath %s", ca_path); |
| } |
| X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); |
| } |
| } |
| |
| void |
| tls_ctx_load_extra_certs(struct tls_root_ctx *ctx, const char *extra_certs_file, |
| bool extra_certs_file_inline) |
| { |
| BIO *in; |
| if (extra_certs_file_inline) |
| { |
| in = BIO_new_mem_buf((char *)extra_certs_file, -1); |
| } |
| else |
| { |
| in = BIO_new_file(extra_certs_file, "r"); |
| } |
| |
| if (in == NULL) |
| { |
| crypto_msg(M_FATAL, "Cannot load extra-certs file: %s", |
| print_key_filename(extra_certs_file, |
| extra_certs_file_inline)); |
| |
| } |
| else |
| { |
| tls_ctx_add_extra_certs(ctx, in, false); |
| } |
| |
| BIO_free(in); |
| } |
| |
| /* ************************************** |
| * |
| * Key-state specific functions |
| * |
| ***************************************/ |
| /* |
| * |
| * BIO functions |
| * |
| */ |
| |
| #ifdef BIO_DEBUG |
| |
| #warning BIO_DEBUG defined |
| |
| static FILE *biofp; /* GLOBAL */ |
| static bool biofp_toggle; /* GLOBAL */ |
| static time_t biofp_last_open; /* GLOBAL */ |
| static const int biofp_reopen_interval = 600; /* GLOBAL */ |
| |
| static void |
| close_biofp(void) |
| { |
| if (biofp) |
| { |
| ASSERT(!fclose(biofp)); |
| biofp = NULL; |
| } |
| } |
| |
| static void |
| open_biofp(void) |
| { |
| const time_t current = time(NULL); |
| const pid_t pid = getpid(); |
| |
| if (biofp_last_open + biofp_reopen_interval < current) |
| { |
| close_biofp(); |
| } |
| if (!biofp) |
| { |
| char fn[256]; |
| openvpn_snprintf(fn, sizeof(fn), "bio/%d-%d.log", pid, biofp_toggle); |
| biofp = fopen(fn, "w"); |
| ASSERT(biofp); |
| biofp_last_open = time(NULL); |
| biofp_toggle ^= 1; |
| } |
| } |
| |
| static void |
| bio_debug_data(const char *mode, BIO *bio, const uint8_t *buf, int len, const char *desc) |
| { |
| struct gc_arena gc = gc_new(); |
| if (len > 0) |
| { |
| open_biofp(); |
| fprintf(biofp, "BIO_%s %s time=%" PRIi64 " bio=" ptr_format " len=%d data=%s\n", |
| mode, desc, (int64_t)time(NULL), (ptr_type)bio, len, format_hex(buf, len, 0, &gc)); |
| fflush(biofp); |
| } |
| gc_free(&gc); |
| } |
| |
| static void |
| bio_debug_oc(const char *mode, BIO *bio) |
| { |
| open_biofp(); |
| fprintf(biofp, "BIO %s time=%" PRIi64 " bio=" ptr_format "\n", |
| mode, (int64_t)time(NULL), (ptr_type)bio); |
| fflush(biofp); |
| } |
| |
| #endif /* ifdef BIO_DEBUG */ |
| |
| /* |
| * Write to an OpenSSL BIO in non-blocking mode. |
| */ |
| static int |
| bio_write(BIO *bio, const uint8_t *data, int size, const char *desc) |
| { |
| int i; |
| int ret = 0; |
| ASSERT(size >= 0); |
| if (size) |
| { |
| /* |
| * Free the L_TLS lock prior to calling BIO routines |
| * so that foreground thread can still call |
| * tls_pre_decrypt or tls_pre_encrypt, |
| * allowing tunnel packet forwarding to continue. |
| */ |
| #ifdef BIO_DEBUG |
| bio_debug_data("write", bio, data, size, desc); |
| #endif |
| i = BIO_write(bio, data, size); |
| |
| if (i < 0) |
| { |
| if (BIO_should_retry(bio)) |
| { |
| } |
| else |
| { |
| crypto_msg(D_TLS_ERRORS, "TLS ERROR: BIO write %s error", desc); |
| ret = -1; |
| ERR_clear_error(); |
| } |
| } |
| else if (i != size) |
| { |
| crypto_msg(D_TLS_ERRORS, "TLS ERROR: BIO write %s incomplete %d/%d", |
| desc, i, size); |
| ret = -1; |
| ERR_clear_error(); |
| } |
| else |
| { /* successful write */ |
| dmsg(D_HANDSHAKE_VERBOSE, "BIO write %s %d bytes", desc, i); |
| ret = 1; |
| } |
| } |
| return ret; |
| } |
| |
| /* |
| * Inline functions for reading from and writing |
| * to BIOs. |
| */ |
| |
| static void |
| bio_write_post(const int status, struct buffer *buf) |
| { |
| if (status == 1) /* success status return from bio_write? */ |
| { |
| memset(BPTR(buf), 0, BLEN(buf)); /* erase data just written */ |
| buf->len = 0; |
| } |
| } |
| |
| /* |
| * Read from an OpenSSL BIO in non-blocking mode. |
| */ |
| static int |
| bio_read(BIO *bio, struct buffer *buf, int maxlen, const char *desc) |
| { |
| int i; |
| int ret = 0; |
| ASSERT(buf->len >= 0); |
| if (buf->len) |
| { |
| } |
| else |
| { |
| int len = buf_forward_capacity(buf); |
| if (maxlen < len) |
| { |
| len = maxlen; |
| } |
| |
| /* |
| * BIO_read brackets most of the serious RSA |
| * key negotiation number crunching. |
| */ |
| i = BIO_read(bio, BPTR(buf), len); |
| |
| VALGRIND_MAKE_READABLE((void *) &i, sizeof(i)); |
| |
| #ifdef BIO_DEBUG |
| bio_debug_data("read", bio, BPTR(buf), i, desc); |
| #endif |
| if (i < 0) |
| { |
| if (BIO_should_retry(bio)) |
| { |
| } |
| else |
| { |
| crypto_msg(D_TLS_ERRORS, "TLS_ERROR: BIO read %s error", desc); |
| buf->len = 0; |
| ret = -1; |
| ERR_clear_error(); |
| } |
| } |
| else if (!i) |
| { |
| buf->len = 0; |
| } |
| else |
| { /* successful read */ |
| dmsg(D_HANDSHAKE_VERBOSE, "BIO read %s %d bytes", desc, i); |
| buf->len = i; |
| ret = 1; |
| VALGRIND_MAKE_READABLE((void *) BPTR(buf), BLEN(buf)); |
| } |
| } |
| return ret; |
| } |
| |
| void |
| key_state_ssl_init(struct key_state_ssl *ks_ssl, const struct tls_root_ctx *ssl_ctx, bool is_server, struct tls_session *session) |
| { |
| ASSERT(NULL != ssl_ctx); |
| ASSERT(ks_ssl); |
| CLEAR(*ks_ssl); |
| |
| ks_ssl->ssl = SSL_new(ssl_ctx->ctx); |
| if (!ks_ssl->ssl) |
| { |
| crypto_msg(M_FATAL, "SSL_new failed"); |
| } |
| |
| /* put session * in ssl object so we can access it |
| * from verify callback*/ |
| SSL_set_ex_data(ks_ssl->ssl, mydata_index, session); |
| |
| ASSERT((ks_ssl->ssl_bio = BIO_new(BIO_f_ssl()))); |
| ASSERT((ks_ssl->ct_in = BIO_new(BIO_s_mem()))); |
| ASSERT((ks_ssl->ct_out = BIO_new(BIO_s_mem()))); |
| |
| #ifdef BIO_DEBUG |
| bio_debug_oc("open ssl_bio", ks_ssl->ssl_bio); |
| bio_debug_oc("open ct_in", ks_ssl->ct_in); |
| bio_debug_oc("open ct_out", ks_ssl->ct_out); |
| #endif |
| |
| if (is_server) |
| { |
| SSL_set_accept_state(ks_ssl->ssl); |
| } |
| else |
| { |
| SSL_set_connect_state(ks_ssl->ssl); |
| } |
| |
| SSL_set_bio(ks_ssl->ssl, ks_ssl->ct_in, ks_ssl->ct_out); |
| BIO_set_ssl(ks_ssl->ssl_bio, ks_ssl->ssl, BIO_NOCLOSE); |
| } |
| |
| void |
| key_state_ssl_free(struct key_state_ssl *ks_ssl) |
| { |
| if (ks_ssl->ssl) |
| { |
| #ifdef BIO_DEBUG |
| bio_debug_oc("close ssl_bio", ks_ssl->ssl_bio); |
| bio_debug_oc("close ct_in", ks_ssl->ct_in); |
| bio_debug_oc("close ct_out", ks_ssl->ct_out); |
| #endif |
| BIO_free_all(ks_ssl->ssl_bio); |
| SSL_free(ks_ssl->ssl); |
| } |
| } |
| |
| int |
| key_state_write_plaintext(struct key_state_ssl *ks_ssl, struct buffer *buf) |
| { |
| int ret = 0; |
| perf_push(PERF_BIO_WRITE_PLAINTEXT); |
| |
| #ifdef ENABLE_CRYPTO_OPENSSL |
| ASSERT(NULL != ks_ssl); |
| |
| ret = bio_write(ks_ssl->ssl_bio, BPTR(buf), BLEN(buf), |
| "tls_write_plaintext"); |
| bio_write_post(ret, buf); |
| #endif /* ENABLE_CRYPTO_OPENSSL */ |
| |
| perf_pop(); |
| return ret; |
| } |
| |
| int |
| key_state_write_plaintext_const(struct key_state_ssl *ks_ssl, const uint8_t *data, int len) |
| { |
| int ret = 0; |
| perf_push(PERF_BIO_WRITE_PLAINTEXT); |
| |
| ASSERT(NULL != ks_ssl); |
| |
| ret = bio_write(ks_ssl->ssl_bio, data, len, "tls_write_plaintext_const"); |
| |
| perf_pop(); |
| return ret; |
| } |
| |
| int |
| key_state_read_ciphertext(struct key_state_ssl *ks_ssl, struct buffer *buf, |
| int maxlen) |
| { |
| int ret = 0; |
| perf_push(PERF_BIO_READ_CIPHERTEXT); |
| |
| ASSERT(NULL != ks_ssl); |
| |
| ret = bio_read(ks_ssl->ct_out, buf, maxlen, "tls_read_ciphertext"); |
| |
| perf_pop(); |
| return ret; |
| } |
| |
| int |
| key_state_write_ciphertext(struct key_state_ssl *ks_ssl, struct buffer *buf) |
| { |
| int ret = 0; |
| perf_push(PERF_BIO_WRITE_CIPHERTEXT); |
| |
| ASSERT(NULL != ks_ssl); |
| |
| ret = bio_write(ks_ssl->ct_in, BPTR(buf), BLEN(buf), "tls_write_ciphertext"); |
| bio_write_post(ret, buf); |
| |
| perf_pop(); |
| return ret; |
| } |
| |
| int |
| key_state_read_plaintext(struct key_state_ssl *ks_ssl, struct buffer *buf, |
| int maxlen) |
| { |
| int ret = 0; |
| perf_push(PERF_BIO_READ_PLAINTEXT); |
| |
| ASSERT(NULL != ks_ssl); |
| |
| ret = bio_read(ks_ssl->ssl_bio, buf, maxlen, "tls_read_plaintext"); |
| |
| perf_pop(); |
| return ret; |
| } |
| |
| /** |
| * Print human readable information about the certifcate into buf |
| * @param cert the certificate being used |
| * @param buf output buffer |
| * @param buflen output buffer length |
| */ |
| static void |
| print_cert_details(X509 *cert, char *buf, size_t buflen) |
| { |
| const char *curve = ""; |
| const char *type = "(error getting type)"; |
| EVP_PKEY *pkey = X509_get_pubkey(cert); |
| |
| if (pkey == NULL) |
| { |
| buf[0] = 0; |
| return; |
| } |
| |
| int typeid = EVP_PKEY_id(pkey); |
| |
| #ifndef OPENSSL_NO_EC |
| if (typeid == EVP_PKEY_EC && EVP_PKEY_get0_EC_KEY(pkey) != NULL) |
| { |
| EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey); |
| const EC_GROUP *group = EC_KEY_get0_group(ec); |
| |
| int nid = EC_GROUP_get_curve_name(group); |
| if (nid == 0 || (curve = OBJ_nid2sn(nid)) == NULL) |
| { |
| curve = "(error getting curve name)"; |
| } |
| } |
| #endif |
| if (EVP_PKEY_id(pkey) != 0) |
| { |
| int typeid = EVP_PKEY_id(pkey); |
| type = OBJ_nid2sn(typeid); |
| |
| /* OpenSSL reports rsaEncryption, dsaEncryption and |
| * id-ecPublicKey, map these values to nicer ones */ |
| if (typeid == EVP_PKEY_RSA) |
| { |
| type = "RSA"; |
| } |
| else if (typeid == EVP_PKEY_DSA) |
| { |
| type = "DSA"; |
| } |
| else if (typeid == EVP_PKEY_EC) |
| { |
| /* EC gets the curve appended after the type */ |
| type = "EC, curve "; |
| } |
| else if (type == NULL) |
| { |
| type = "unknown type"; |
| } |
| } |
| |
| char sig[128] = { 0 }; |
| int signature_nid = X509_get_signature_nid(cert); |
| if (signature_nid != 0) |
| { |
| openvpn_snprintf(sig, sizeof(sig), ", signature: %s", |
| OBJ_nid2sn(signature_nid)); |
| } |
| |
| openvpn_snprintf(buf, buflen, ", peer certificate: %d bit %s%s%s", |
| EVP_PKEY_bits(pkey), type, curve, sig); |
| |
| EVP_PKEY_free(pkey); |
| } |
| |
| /* ************************************** |
| * |
| * Information functions |
| * |
| * Print information for the end user. |
| * |
| ***************************************/ |
| void |
| print_details(struct key_state_ssl *ks_ssl, const char *prefix) |
| { |
| const SSL_CIPHER *ciph; |
| char s1[256]; |
| char s2[256]; |
| |
| s1[0] = s2[0] = 0; |
| ciph = SSL_get_current_cipher(ks_ssl->ssl); |
| openvpn_snprintf(s1, sizeof(s1), "%s %s, cipher %s %s", |
| prefix, |
| SSL_get_version(ks_ssl->ssl), |
| SSL_CIPHER_get_version(ciph), |
| SSL_CIPHER_get_name(ciph)); |
| X509 *cert = SSL_get_peer_certificate(ks_ssl->ssl); |
| |
| if (cert) |
| { |
| print_cert_details(cert, s2, sizeof(s2)); |
| X509_free(cert); |
| } |
| msg(D_HANDSHAKE, "%s%s", s1, s2); |
| } |
| |
| void |
| show_available_tls_ciphers_list(const char *cipher_list, |
| const char *tls_cert_profile, |
| bool tls13) |
| { |
| struct tls_root_ctx tls_ctx; |
| |
| tls_ctx.ctx = SSL_CTX_new(SSLv23_method()); |
| if (!tls_ctx.ctx) |
| { |
| crypto_msg(M_FATAL, "Cannot create SSL_CTX object"); |
| } |
| |
| #if defined(TLS1_3_VERSION) |
| if (tls13) |
| { |
| SSL_CTX_set_min_proto_version(tls_ctx.ctx, |
| openssl_tls_version(TLS_VER_1_3)); |
| tls_ctx_restrict_ciphers_tls13(&tls_ctx, cipher_list); |
| } |
| else |
| #endif |
| { |
| SSL_CTX_set_max_proto_version(tls_ctx.ctx, TLS1_2_VERSION); |
| tls_ctx_restrict_ciphers(&tls_ctx, cipher_list); |
| } |
| |
| tls_ctx_set_cert_profile(&tls_ctx, tls_cert_profile); |
| |
| SSL *ssl = SSL_new(tls_ctx.ctx); |
| if (!ssl) |
| { |
| crypto_msg(M_FATAL, "Cannot create SSL object"); |
| } |
| |
| #if (OPENSSL_VERSION_NUMBER < 0x1010000fL) \ |
| || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER <= 0x2090000fL) |
| STACK_OF(SSL_CIPHER) *sk = SSL_get_ciphers(ssl); |
| #else |
| STACK_OF(SSL_CIPHER) *sk = SSL_get1_supported_ciphers(ssl); |
| #endif |
| for (int i = 0; i < sk_SSL_CIPHER_num(sk); i++) |
| { |
| const SSL_CIPHER *c = sk_SSL_CIPHER_value(sk, i); |
| |
| const char *cipher_name = SSL_CIPHER_get_name(c); |
| |
| const tls_cipher_name_pair *pair = |
| tls_get_cipher_name_pair(cipher_name, strlen(cipher_name)); |
| |
| if (tls13) |
| { |
| printf("%s\n", cipher_name); |
| } |
| else if (NULL == pair) |
| { |
| /* No translation found, print warning */ |
| printf("%s (No IANA name known to OpenVPN, use OpenSSL name.)\n", |
| cipher_name); |
| } |
| else |
| { |
| printf("%s\n", pair->iana_name); |
| } |
| } |
| #if (OPENSSL_VERSION_NUMBER >= 0x1010000fL) |
| sk_SSL_CIPHER_free(sk); |
| #endif |
| SSL_free(ssl); |
| SSL_CTX_free(tls_ctx.ctx); |
| } |
| |
| /* |
| * Show the Elliptic curves that are available for us to use |
| * in the OpenSSL library. |
| */ |
| void |
| show_available_curves(void) |
| { |
| printf("Consider using openssl 'ecparam -list_curves' as\n" |
| "alternative to running this command.\n"); |
| #ifndef OPENSSL_NO_EC |
| EC_builtin_curve *curves = NULL; |
| size_t crv_len = 0; |
| size_t n = 0; |
| |
| crv_len = EC_get_builtin_curves(NULL, 0); |
| ALLOC_ARRAY(curves, EC_builtin_curve, crv_len); |
| if (EC_get_builtin_curves(curves, crv_len)) |
| { |
| printf("\nAvailable Elliptic curves/groups:\n"); |
| for (n = 0; n < crv_len; n++) |
| { |
| const char *sname; |
| sname = OBJ_nid2sn(curves[n].nid); |
| if (sname == NULL) |
| { |
| sname = ""; |
| } |
| |
| printf("%s\n", sname); |
| } |
| } |
| else |
| { |
| crypto_msg(M_FATAL, "Cannot get list of builtin curves"); |
| } |
| free(curves); |
| #else /* ifndef OPENSSL_NO_EC */ |
| msg(M_WARN, "Your OpenSSL library was built without elliptic curve support. " |
| "No curves available."); |
| #endif /* ifndef OPENSSL_NO_EC */ |
| } |
| |
| void |
| get_highest_preference_tls_cipher(char *buf, int size) |
| { |
| SSL_CTX *ctx; |
| SSL *ssl; |
| const char *cipher_name; |
| |
| ctx = SSL_CTX_new(SSLv23_method()); |
| if (!ctx) |
| { |
| crypto_msg(M_FATAL, "Cannot create SSL_CTX object"); |
| } |
| ssl = SSL_new(ctx); |
| if (!ssl) |
| { |
| crypto_msg(M_FATAL, "Cannot create SSL object"); |
| } |
| |
| cipher_name = SSL_get_cipher_list(ssl, 0); |
| strncpynt(buf, cipher_name, size); |
| |
| SSL_free(ssl); |
| SSL_CTX_free(ctx); |
| } |
| |
| const char * |
| get_ssl_library_version(void) |
| { |
| return OpenSSL_version(OPENSSL_VERSION); |
| } |
| |
| #endif /* defined(ENABLE_CRYPTO_OPENSSL) */ |