diff --git a/src/openvpn/auth_token.c b/src/openvpn/auth_token.c index a694e81cff5..b97e0c53968 100644 --- a/src/openvpn/auth_token.c +++ b/src/openvpn/auth_token.c @@ -35,15 +35,15 @@ auth_token_kt(void) } void -add_session_token_env(struct tls_session *session, struct tls_multi *multi, - const struct user_pass *up) +add_session_token_env(struct tls_session *session, struct tls_multi *multi, const struct user_pass *up) { if (!multi->opt.auth_token_generate) { return; } - int auth_token_state_flags = session->key[KS_PRIMARY].auth_token_state_flags; + struct key_state *ks = tls_select_encryption_key_init(multi); + int auth_token_state_flags = ks->auth_token_state_flags; const char *state; @@ -81,7 +81,7 @@ add_session_token_env(struct tls_session *session, struct tls_multi *multi, state = "Invalid"; } - setenv_str(session->opt->es, "session_state", state); + setenv_str(multi->opt.es, "session_state", state); /* We had a valid session id before */ const char *session_id_source; @@ -111,7 +111,7 @@ add_session_token_env(struct tls_session *session, struct tls_multi *multi, memcpy(session_id, session_id_source + strlen(SESSION_ID_PREFIX), AUTH_TOKEN_SESSION_ID_LEN * 8 / 6); - setenv_str(session->opt->es, "session_id", session_id); + setenv_str(multi->opt.es, "session_id", session_id); } void @@ -217,8 +217,8 @@ generate_auth_token(const struct user_pass *up, struct tls_multi *multi) * a new token with the empty username since we do not want to loose * the information that the username cannot be trusted */ - struct key_state *ks = &multi->session[TM_ACTIVE].key[KS_PRIMARY]; - if (ks->auth_token_state_flags & AUTH_TOKEN_VALID_EMPTYUSER) + struct key_state *ks = tls_select_encryption_key_init(multi); + if (ks && ks->auth_token_state_flags & AUTH_TOKEN_VALID_EMPTYUSER) { hmac_ctx_update(ctx, (const uint8_t *)"", 0); } @@ -415,10 +415,15 @@ void check_send_auth_token(struct context *c) { struct tls_multi *multi = c->c2.tls_multi; - struct tls_session *session = &multi->session[TM_ACTIVE]; - if (get_primary_key(multi)->state < S_GENERATED_KEYS - || get_primary_key(multi)->authenticated != KS_AUTH_TRUE) + if (!multi) + { + return; + } + + struct key_state *ks = tls_select_encryption_key_init(multi); + + if (ks->state < S_GENERATED_KEYS || ks->authenticated != KS_AUTH_TRUE) { /* the currently active session is still in renegotiation or another * not fully authorized state. We are either very close to a @@ -447,11 +452,11 @@ check_send_auth_token(struct context *c) generate_auth_token(&up, multi); - resend_auth_token_renegotiation(multi, session); + resend_auth_token_renegotiation(multi); } void -resend_auth_token_renegotiation(struct tls_multi *multi, struct tls_session *session) +resend_auth_token_renegotiation(struct tls_multi *multi) { /* * Auth token already sent to client, update auth-token on client. diff --git a/src/openvpn/auth_token.h b/src/openvpn/auth_token.h index 1e4a4135cdc..08055013a60 100644 --- a/src/openvpn/auth_token.h +++ b/src/openvpn/auth_token.h @@ -128,7 +128,7 @@ is_auth_token(const char *password) * @param multi Pointer the multi object of the TLS session * @param session Pointer to the TLS session itself */ -void resend_auth_token_renegotiation(struct tls_multi *multi, struct tls_session *session); +void resend_auth_token_renegotiation(struct tls_multi *multi); /** diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h index 1a6358dcc53..b87216e9da7 100644 --- a/src/openvpn/buffer.h +++ b/src/openvpn/buffer.h @@ -413,9 +413,7 @@ has_digit(const char *src) static inline void secure_memzero(void *data, size_t len) { -#if defined(_WIN32) - SecureZeroMemory(data, len); -#elif defined(__GNUC__) || defined(__clang__) +#if defined(__GNUC__) || defined(__clang__) memset(data, 0, len); __asm__ __volatile__("" : : "r"(data) : "memory"); #else diff --git a/src/openvpn/common.h b/src/openvpn/common.h index 7779614cccc..29fc661cc74 100644 --- a/src/openvpn/common.h +++ b/src/openvpn/common.h @@ -35,11 +35,7 @@ typedef uint64_t counter_type; * Time intervals */ typedef int interval_t; - -/* - * Used as an upper bound for timeouts. - */ -#define BIG_TIMEOUT (60 * 60 * 24 * 7) /* one week (in seconds) */ +#define LOOP_WAIT 7 /* * Printf formats for special types diff --git a/src/openvpn/console_builtin.c b/src/openvpn/console_builtin.c index 2a925d62f0f..7c5ff9780a1 100644 --- a/src/openvpn/console_builtin.c +++ b/src/openvpn/console_builtin.c @@ -41,101 +41,6 @@ #include #endif -#ifdef _WIN32 - -#include "win32.h" - -/** - * Get input from a Windows console. - * - * @param prompt Prompt to display to the user - * @param echo Should the user input be displayed in the console - * @param input Pointer to the buffer the user input will be saved - * @param capacity Size of the buffer for the user input - * - * @return Return false on input error, or if service - * exit event is signaled. - */ -static bool -get_console_input_win32(const char *prompt, const bool echo, char *input, const int capacity) -{ - ASSERT(prompt); - ASSERT(input); - ASSERT(capacity > 0); - - input[0] = '\0'; - - HANDLE in = GetStdHandle(STD_INPUT_HANDLE); - int orig_stderr = get_orig_stderr(); /* guaranteed to be always valid */ - if ((in == INVALID_HANDLE_VALUE) || win32_service_interrupt(&win32_signal) - || (_write(orig_stderr, prompt, (unsigned int)strlen(prompt)) == -1)) - { - msg(M_WARN | M_ERRNO, "get_console_input_win32(): unexpected error"); - return false; - } - - bool is_console = (GetFileType(in) == FILE_TYPE_CHAR); - DWORD flags_save = 0; - int status = 0; - WCHAR *winput; - - if (is_console) - { - if (GetConsoleMode(in, &flags_save)) - { - DWORD flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT; - if (echo) - { - flags |= ENABLE_ECHO_INPUT; - } - SetConsoleMode(in, flags); - } - else - { - is_console = 0; - } - } - - DWORD len = 0; - - if (is_console) - { - winput = malloc(capacity * sizeof(WCHAR)); - if (winput == NULL) - { - return false; - } - - status = ReadConsoleW(in, winput, capacity, &len, NULL); - WideCharToMultiByte(CP_UTF8, 0, winput, len, input, capacity, NULL, NULL); - free(winput); - } - else - { - status = ReadFile(in, input, capacity, &len, NULL); - } - - string_null_terminate(input, (int)len, capacity); - chomp(input); - - if (!echo) - { - _write(orig_stderr, "\r\n", 2); - } - if (is_console) - { - SetConsoleMode(in, flags_save); - } - if (status && !win32_service_interrupt(&win32_signal)) - { - return true; - } - - return false; -} - -#endif /* _WIN32 */ - #ifdef HAVE_TERMIOS_H @@ -198,9 +103,7 @@ get_console_input(const char *prompt, const bool echo, char *input, const int ca ASSERT(capacity > 0); input[0] = '\0'; -#if defined(_WIN32) - return get_console_input_win32(prompt, echo, input, capacity); -#elif defined(HAVE_TERMIOS_H) +#if defined(HAVE_TERMIOS_H) bool restore_tty = false; struct termios tty_tmp, tty_save; @@ -258,9 +161,9 @@ get_console_input(const char *prompt, const bool echo, char *input, const int ca } close_tty(fp); -#else /* if defined(_WIN32) */ +#else msg(M_FATAL, "Sorry, but I can't get console input on this OS (%s)", prompt); -#endif /* if defined(_WIN32) */ +#endif return ret; } diff --git a/src/openvpn/crypto_backend.h b/src/openvpn/crypto_backend.h index e95752aa77f..a3b09658842 100644 --- a/src/openvpn/crypto_backend.h +++ b/src/openvpn/crypto_backend.h @@ -32,9 +32,6 @@ #ifdef ENABLE_CRYPTO_OPENSSL #include "crypto_openssl.h" #endif -#ifdef ENABLE_CRYPTO_MBEDTLS -#include "crypto_mbedtls.h" -#endif #include "basic.h" #include "buffer.h" diff --git a/src/openvpn/crypto_epoch.c b/src/openvpn/crypto_epoch.c index f34dc8cc69c..a6bdc3fb8a0 100644 --- a/src/openvpn/crypto_epoch.c +++ b/src/openvpn/crypto_epoch.c @@ -304,6 +304,9 @@ epoch_replace_update_recv_key(struct crypto_options *co, uint16_t new_epoch) { epoch_key_iterate(&co->epoch_key_send); } + + msg(M_INFO, "INFO epoch_replace_update_recv_key: epoch < new_epoch"); + epoch_init_send_key_ctx(co); } @@ -402,14 +405,15 @@ epoch_check_send_iterate(struct crypto_options *opt) { if (opt->epoch_key_send.epoch == UINT16_MAX) { + msg(M_INFO, "INFO epoch_check_send_iterate: epoch == UINT16_MAX"); /* limit of epoch keys reached, cannot move to a newer key anymore */ return; } if (opt->aead_usage_limit) { - if (aead_usage_limit_reached(opt->aead_usage_limit, &opt->key_ctx_bi.encrypt, - opt->packet_id.send.id)) + if (aead_usage_limit_reached(opt->aead_usage_limit, &opt->key_ctx_bi.encrypt, opt->packet_id.send.id)) { + msg(M_INFO, "INFO epoch_check_send_iterate: aead_usage_limit_reached"); /* Send key limit reached */ epoch_iterate_send_key(opt); } @@ -429,10 +433,10 @@ epoch_check_send_iterate(struct crypto_options *opt) * decryption fail warn limit. * */ else if (opt->key_ctx_bi.encrypt.epoch == opt->key_ctx_bi.decrypt.epoch - && (aead_usage_limit_reached(opt->aead_usage_limit, &opt->key_ctx_bi.decrypt, - opt->packet_id.rec.id) + && (aead_usage_limit_reached(opt->aead_usage_limit, &opt->key_ctx_bi.decrypt, opt->packet_id.rec.id) || cipher_decrypt_verify_fail_warn(&opt->key_ctx_bi.decrypt))) { + msg(M_INFO, "INFO epoch_check_send_iterate: cipher_decrypt_verify_fail_warn"); /* Receive key limit reached. Increase our own send key to signal * that we want to use a new epoch. Peer should then also move its * key but is not required to do this */ @@ -442,6 +446,7 @@ epoch_check_send_iterate(struct crypto_options *opt) if (opt->packet_id.send.id == PACKET_ID_EPOCH_MAX) { + msg(M_INFO, "INFO epoch_check_send_iterate: send.id == PACKET_ID_EPOCH_MAX"); epoch_iterate_send_key(opt); } } diff --git a/src/openvpn/crypto_mbedtls.c b/src/openvpn/crypto_mbedtls.c index 6688d48a136..e69de29bb2d 100644 --- a/src/openvpn/crypto_mbedtls.c +++ b/src/openvpn/crypto_mbedtls.c @@ -1,1128 +0,0 @@ -/* - * 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-2025 OpenVPN Inc - * Copyright (C) 2010-2021 Sentyron B.V. - * - * 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, see . - */ - -/** - * @file - * Data Channel Cryptography mbed TLS-specific backend interface - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "syshead.h" - -#if defined(ENABLE_CRYPTO_MBEDTLS) - -#include "errlevel.h" -#include "basic.h" -#include "buffer.h" -#include "crypto.h" -#include "integer.h" -#include "crypto_backend.h" -#include "otime.h" -#include "misc.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* - * - * Hardware engine support. Allows loading/unloading of engines. - * - */ - -void -crypto_init_lib_engine(const char *engine_name) -{ - msg(M_WARN, "Note: mbed TLS hardware crypto engine functionality is not " - "available"); -} - -provider_t * -crypto_load_provider(const char *provider) -{ - if (provider) - { - msg(M_WARN, "Note: mbed TLS provider functionality is not available"); - } - return NULL; -} - -void -crypto_unload_provider(const char *provname, provider_t *provider) -{ -} - -/* - * - * Functions related to the core crypto library - * - */ - -void -crypto_init_lib(void) -{ -} - -void -crypto_uninit_lib(void) -{ -} - -void -crypto_clear_error(void) -{ -} - -bool -mbed_log_err(unsigned int flags, int errval, const char *prefix) -{ - if (0 != errval) - { - char errstr[256]; - mbedtls_strerror(errval, errstr, sizeof(errstr)); - - if (NULL == prefix) - { - prefix = "mbed TLS error"; - } - msg(flags, "%s: %s", prefix, errstr); - } - - return 0 == errval; -} - -bool -mbed_log_func_line(unsigned int flags, int errval, const char *func, int line) -{ - char prefix[256]; - - if (snprintf(prefix, sizeof(prefix), "%s:%d", func, line) >= sizeof(prefix)) - { - return mbed_log_err(flags, errval, func); - } - - return mbed_log_err(flags, errval, prefix); -} - - -#ifdef DMALLOC -void -crypto_init_dmalloc(void) -{ - msg(M_ERR, "Error: dmalloc support is not available for mbed TLS."); -} -#endif /* DMALLOC */ - -const cipher_name_pair cipher_name_translation_table[] = { - { "BF-CBC", "BLOWFISH-CBC" }, - { "BF-CFB", "BLOWFISH-CFB64" }, - { "CAMELLIA-128-CFB", "CAMELLIA-128-CFB128" }, - { "CAMELLIA-192-CFB", "CAMELLIA-192-CFB128" }, - { "CAMELLIA-256-CFB", "CAMELLIA-256-CFB128" } -}; -const size_t cipher_name_translation_table_count = - sizeof(cipher_name_translation_table) / sizeof(*cipher_name_translation_table); - -void -show_available_ciphers(void) -{ - const int *ciphers = mbedtls_cipher_list(); - -#ifndef ENABLE_SMALL - printf("The following ciphers and cipher modes are available for use\n" - "with " PACKAGE_NAME ". Each cipher shown below may be used as a\n" - "parameter to the --data-ciphers (or --cipher) option. Using a\n" - "GCM or CBC mode is recommended. In static key mode only CBC\n" - "mode is allowed.\n\n"); -#endif - - while (*ciphers != 0) - { - const mbedtls_cipher_info_t *info = mbedtls_cipher_info_from_type(*ciphers); - const char *name = mbedtls_cipher_info_get_name(info); - if (info && name && !cipher_kt_insecure(name) - && (cipher_kt_mode_aead(name) || cipher_kt_mode_cbc(name))) - { - print_cipher(name); - } - ciphers++; - } - - printf("\nThe following ciphers have a block size of less than 128 bits, \n" - "and are therefore deprecated. Do not use unless you have to.\n\n"); - ciphers = mbedtls_cipher_list(); - while (*ciphers != 0) - { - const mbedtls_cipher_info_t *info = mbedtls_cipher_info_from_type(*ciphers); - const char *name = mbedtls_cipher_info_get_name(info); - if (info && name && cipher_kt_insecure(name) - && (cipher_kt_mode_aead(name) || cipher_kt_mode_cbc(name))) - { - print_cipher(name); - } - ciphers++; - } - printf("\n"); -} - -void -show_available_digests(void) -{ - const int *digests = mbedtls_md_list(); - -#ifndef ENABLE_SMALL - printf("The following message digests are available for use with\n" PACKAGE_NAME - ". A message digest is used in conjunction with\n" - "the HMAC function, to authenticate received packets.\n" - "You can specify a message digest as parameter to\n" - "the --auth option.\n\n"); -#endif - - while (*digests != 0) - { - const mbedtls_md_info_t *info = mbedtls_md_info_from_type(*digests); - - if (info) - { - printf("%s %d bit default key\n", mbedtls_md_get_name(info), - mbedtls_md_get_size(info) * 8); - } - digests++; - } - printf("\n"); -} - -void -show_available_engines(void) -{ - printf("Sorry, mbed TLS hardware crypto engine functionality is not " - "available\n"); -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - -bool -crypto_pem_encode(const char *name, struct buffer *dst, const struct buffer *src, - struct gc_arena *gc) -{ - /* 1000 chars is the PEM line length limit (+1 for tailing NUL) */ - char header[1000 + 1] = { 0 }; - char footer[1000 + 1] = { 0 }; - - if (snprintf(header, sizeof(header), "-----BEGIN %s-----\n", name) >= sizeof(header)) - { - return false; - } - if (snprintf(footer, sizeof(footer), "-----END %s-----\n", name) >= sizeof(footer)) - { - return false; - } - - size_t out_len = 0; - if (MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL - != mbedtls_pem_write_buffer(header, footer, BPTR(src), BLEN(src), NULL, 0, &out_len)) - { - return false; - } - - /* We set the size buf to out_len-1 to NOT include the 0 byte that - * mbedtls_pem_write_buffer in its length calculation */ - *dst = alloc_buf_gc(out_len, gc); - if (!mbed_ok(mbedtls_pem_write_buffer(header, footer, BPTR(src), BLEN(src), BPTR(dst), - BCAP(dst), &out_len)) - || !buf_inc_len(dst, out_len - 1)) - { - CLEAR(*dst); - return false; - } - - return true; -} - -bool -crypto_pem_decode(const char *name, struct buffer *dst, const struct buffer *src) -{ - /* 1000 chars is the PEM line length limit (+1 for tailing NUL) */ - char header[1000 + 1] = { 0 }; - char footer[1000 + 1] = { 0 }; - - if (snprintf(header, sizeof(header), "-----BEGIN %s-----", name) >= sizeof(header)) - { - return false; - } - if (snprintf(footer, sizeof(footer), "-----END %s-----", name) >= sizeof(footer)) - { - return false; - } - - /* mbed TLS requires the src to be null-terminated */ - /* allocate a new buffer to avoid modifying the src buffer */ - struct gc_arena gc = gc_new(); - struct buffer input = alloc_buf_gc(BLEN(src) + 1, &gc); - buf_copy(&input, src); - buf_null_terminate(&input); - - size_t use_len = 0; - mbedtls_pem_context ctx = { 0 }; - bool ret = - mbed_ok(mbedtls_pem_read_buffer(&ctx, header, footer, BPTR(&input), NULL, 0, &use_len)); - size_t buf_size = 0; - const unsigned char *buf = mbedtls_pem_get_buffer(&ctx, &buf_size); - if (ret && !buf_write(dst, buf, buf_size)) - { - ret = false; - msg(M_WARN, "PEM decode error: destination buffer too small"); - } - - mbedtls_pem_free(&ctx); - gc_free(&gc); - return ret; -} - -/* - * - * Random number functions, used in cases where we want - * reasonably strong cryptographic random number generation - * without depleting our entropy pool. Used for random - * IV values and a number of other miscellaneous tasks. - * - */ - -/* - * Initialise the given ctr_drbg context, using a personalisation string and an - * entropy gathering function. - */ -mbedtls_ctr_drbg_context * -rand_ctx_get(void) -{ - static mbedtls_entropy_context ec = { 0 }; - static mbedtls_ctr_drbg_context cd_ctx = { 0 }; - static bool rand_initialised = false; - - if (!rand_initialised) - { - struct gc_arena gc = gc_new(); - struct buffer pers_string = alloc_buf_gc(100, &gc); - - /* - * Personalisation string, should be as unique as possible (see NIST - * 800-90 section 8.7.1). We have very little information at this stage. - * Include Program Name, memory address of the context and PID. - */ - buf_printf(&pers_string, "OpenVPN %0u %p %s", platform_getpid(), &cd_ctx, - time_string(0, 0, 0, &gc)); - - /* Initialise mbed TLS RNG, and built-in entropy sources */ - mbedtls_entropy_init(&ec); - - mbedtls_ctr_drbg_init(&cd_ctx); - if (!mbed_ok(mbedtls_ctr_drbg_seed(&cd_ctx, mbedtls_entropy_func, &ec, BPTR(&pers_string), - BLEN(&pers_string)))) - { - msg(M_FATAL, "Failed to initialize random generator"); - } - - gc_free(&gc); - rand_initialised = true; - } - - return &cd_ctx; -} - -#ifdef ENABLE_PREDICTION_RESISTANCE -void -rand_ctx_enable_prediction_resistance(void) -{ - mbedtls_ctr_drbg_context *cd_ctx = rand_ctx_get(); - - mbedtls_ctr_drbg_set_prediction_resistance(cd_ctx, 1); -} -#endif /* ENABLE_PREDICTION_RESISTANCE */ - -int -rand_bytes(uint8_t *output, int len) -{ - mbedtls_ctr_drbg_context *rng_ctx = rand_ctx_get(); - - while (len > 0) - { - const size_t blen = min_int(len, MBEDTLS_CTR_DRBG_MAX_REQUEST); - if (0 != mbedtls_ctr_drbg_random(rng_ctx, output, blen)) - { - return 0; - } - - output += blen; - len -= blen; - } - - return 1; -} - -/* - * - * Generic cipher key type functions - * - */ -static const mbedtls_cipher_info_t * -cipher_get(const char *ciphername) -{ - ASSERT(ciphername); - - const mbedtls_cipher_info_t *cipher = NULL; - - ciphername = translate_cipher_name_from_openvpn(ciphername); - cipher = mbedtls_cipher_info_from_string(ciphername); - return cipher; -} - -bool -cipher_valid_reason(const char *ciphername, const char **reason) -{ - ASSERT(reason); - - const mbedtls_cipher_info_t *cipher = cipher_get(ciphername); - - if (NULL == cipher) - { - msg(D_LOW, "Cipher algorithm '%s' not found", ciphername); - *reason = "disabled because unknown"; - return false; - } - - const size_t key_bytelen = mbedtls_cipher_info_get_key_bitlen(cipher) / 8; - if (key_bytelen > MAX_CIPHER_KEY_LENGTH) - { - msg(D_LOW, - "Cipher algorithm '%s' uses a default key size (%zu bytes) " - "which is larger than " PACKAGE_NAME "'s current maximum key size " - "(%d bytes)", - ciphername, key_bytelen, MAX_CIPHER_KEY_LENGTH); - *reason = "disabled due to key size too large"; - return false; - } - - *reason = NULL; - return true; -} - -const char * -cipher_kt_name(const char *ciphername) -{ - const mbedtls_cipher_info_t *cipher_kt = cipher_get(ciphername); - if (NULL == cipher_kt) - { - return "[null-cipher]"; - } - - return translate_cipher_name_to_openvpn(mbedtls_cipher_info_get_name(cipher_kt)); -} - -int -cipher_kt_key_size(const char *ciphername) -{ - const mbedtls_cipher_info_t *cipher_kt = cipher_get(ciphername); - - if (NULL == cipher_kt) - { - return 0; - } - - return (int)mbedtls_cipher_info_get_key_bitlen(cipher_kt) / 8; -} - -int -cipher_kt_iv_size(const char *ciphername) -{ - const mbedtls_cipher_info_t *cipher_kt = cipher_get(ciphername); - - if (NULL == cipher_kt) - { - return 0; - } - return (int)mbedtls_cipher_info_get_iv_size(cipher_kt); -} - -int -cipher_kt_block_size(const char *ciphername) -{ - const mbedtls_cipher_info_t *cipher_kt = cipher_get(ciphername); - if (NULL == cipher_kt) - { - return 0; - } - return (int)mbedtls_cipher_info_get_block_size(cipher_kt); -} - -int -cipher_kt_tag_size(const char *ciphername) -{ - if (cipher_kt_mode_aead(ciphername)) - { - return OPENVPN_AEAD_TAG_LENGTH; - } - return 0; -} - -bool -cipher_kt_insecure(const char *ciphername) -{ - const mbedtls_cipher_info_t *cipher_kt = cipher_get(ciphername); - if (!cipher_kt) - { - return true; - } - - return !(cipher_kt_block_size(ciphername) >= 128 / 8 -#ifdef MBEDTLS_CHACHAPOLY_C - || mbedtls_cipher_info_get_type(cipher_kt) == MBEDTLS_CIPHER_CHACHA20_POLY1305 -#endif - ); -} - -static mbedtls_cipher_mode_t -cipher_kt_mode(const mbedtls_cipher_info_t *cipher_kt) -{ - ASSERT(NULL != cipher_kt); - return mbedtls_cipher_info_get_mode(cipher_kt); -} - -bool -cipher_kt_mode_cbc(const char *ciphername) -{ - const mbedtls_cipher_info_t *cipher = cipher_get(ciphername); - return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_CBC; -} - -bool -cipher_kt_mode_ofb_cfb(const char *ciphername) -{ - const mbedtls_cipher_info_t *cipher = cipher_get(ciphername); - return cipher - && (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB - || cipher_kt_mode(cipher) == OPENVPN_MODE_CFB); -} - -bool -cipher_kt_mode_aead(const char *ciphername) -{ - const mbedtls_cipher_info_t *cipher = cipher_get(ciphername); - return cipher - && (cipher_kt_mode(cipher) == OPENVPN_MODE_GCM -#ifdef MBEDTLS_CHACHAPOLY_C - || cipher_kt_mode(cipher) == MBEDTLS_MODE_CHACHAPOLY -#endif - ); -} - - -/* - * - * Generic cipher context functions - * - */ - -mbedtls_cipher_context_t * -cipher_ctx_new(void) -{ - mbedtls_cipher_context_t *ctx; - ALLOC_OBJ(ctx, mbedtls_cipher_context_t); - return ctx; -} - -void -cipher_ctx_free(mbedtls_cipher_context_t *ctx) -{ - mbedtls_cipher_free(ctx); - free(ctx); -} - -void -cipher_ctx_init(mbedtls_cipher_context_t *ctx, const uint8_t *key, const char *ciphername, - crypto_operation_t enc) -{ - ASSERT(NULL != ciphername && NULL != ctx); - CLEAR(*ctx); - - const mbedtls_cipher_info_t *kt = cipher_get(ciphername); - ASSERT(kt); - size_t key_bitlen = mbedtls_cipher_info_get_key_bitlen(kt); - - if (!mbed_ok(mbedtls_cipher_setup(ctx, kt))) - { - msg(M_FATAL, "mbed TLS cipher context init #1"); - } - - if (!mbed_ok(mbedtls_cipher_setkey(ctx, key, (int)key_bitlen, enc))) - { - msg(M_FATAL, "mbed TLS cipher set key"); - } - - if (mbedtls_cipher_info_get_mode(kt) == MBEDTLS_MODE_CBC) - { - if (!mbed_ok(mbedtls_cipher_set_padding_mode(ctx, MBEDTLS_PADDING_PKCS7))) - { - msg(M_FATAL, "mbed TLS cipher set padding mode"); - } - } - - /* make sure we used a big enough key */ - ASSERT(mbedtls_cipher_get_key_bitlen(ctx) <= key_bitlen); -} - -int -cipher_ctx_iv_length(const mbedtls_cipher_context_t *ctx) -{ - return mbedtls_cipher_get_iv_size(ctx); -} - -int -cipher_ctx_get_tag(cipher_ctx_t *ctx, uint8_t *tag, int tag_len) -{ - if (tag_len > SIZE_MAX) - { - return 0; - } - - if (!mbed_ok(mbedtls_cipher_write_tag(ctx, (unsigned char *)tag, tag_len))) - { - return 0; - } - - return 1; -} - -int -cipher_ctx_block_size(const mbedtls_cipher_context_t *ctx) -{ - return (int)mbedtls_cipher_get_block_size(ctx); -} - -int -cipher_ctx_mode(const mbedtls_cipher_context_t *ctx) -{ - ASSERT(NULL != ctx); - - return mbedtls_cipher_get_cipher_mode(ctx); -} - -bool -cipher_ctx_mode_cbc(const cipher_ctx_t *ctx) -{ - return ctx && cipher_ctx_mode(ctx) == OPENVPN_MODE_CBC; -} - - -bool -cipher_ctx_mode_ofb_cfb(const cipher_ctx_t *ctx) -{ - return ctx - && (cipher_ctx_mode(ctx) == OPENVPN_MODE_OFB - || cipher_ctx_mode(ctx) == OPENVPN_MODE_CFB); -} - -bool -cipher_ctx_mode_aead(const cipher_ctx_t *ctx) -{ - return ctx - && (cipher_ctx_mode(ctx) == OPENVPN_MODE_GCM -#ifdef MBEDTLS_CHACHAPOLY_C - || cipher_ctx_mode(ctx) == MBEDTLS_MODE_CHACHAPOLY -#endif - ); -} - -int -cipher_ctx_reset(mbedtls_cipher_context_t *ctx, const uint8_t *iv_buf) -{ - if (!mbed_ok(mbedtls_cipher_reset(ctx))) - { - return 0; - } - - if (!mbed_ok(mbedtls_cipher_set_iv(ctx, iv_buf, (size_t)mbedtls_cipher_get_iv_size(ctx)))) - { - return 0; - } - - return 1; -} - -int -cipher_ctx_update_ad(cipher_ctx_t *ctx, const uint8_t *src, int src_len) -{ - if (src_len > SIZE_MAX) - { - return 0; - } - - if (!mbed_ok(mbedtls_cipher_update_ad(ctx, src, src_len))) - { - return 0; - } - - return 1; -} - -int -cipher_ctx_update(mbedtls_cipher_context_t *ctx, uint8_t *dst, int *dst_len, uint8_t *src, - int src_len) -{ - size_t s_dst_len = *dst_len; - - if (!mbed_ok(mbedtls_cipher_update(ctx, src, (size_t)src_len, dst, &s_dst_len))) - { - return 0; - } - - *dst_len = s_dst_len; - - return 1; -} - -int -cipher_ctx_final(mbedtls_cipher_context_t *ctx, uint8_t *dst, int *dst_len) -{ - size_t s_dst_len = *dst_len; - - if (!mbed_ok(mbedtls_cipher_finish(ctx, dst, &s_dst_len))) - { - return 0; - } - - *dst_len = s_dst_len; - - return 1; -} - -int -cipher_ctx_final_check_tag(mbedtls_cipher_context_t *ctx, uint8_t *dst, int *dst_len, uint8_t *tag, - size_t tag_len) -{ - size_t olen = 0; - - if (MBEDTLS_DECRYPT != mbedtls_cipher_get_operation(ctx)) - { - return 0; - } - - if (tag_len > SIZE_MAX) - { - return 0; - } - - if (!mbed_ok(mbedtls_cipher_finish(ctx, dst, &olen))) - { - msg(D_CRYPT_ERRORS, "%s: cipher_ctx_final() failed", __func__); - return 0; - } - - if (olen > INT_MAX) - { - return 0; - } - *dst_len = olen; - - if (!mbed_ok(mbedtls_cipher_check_tag(ctx, (const unsigned char *)tag, tag_len))) - { - return 0; - } - - return 1; -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop -#endif - -/* - * - * Generic message digest information functions - * - */ - - -static const mbedtls_md_info_t * -md_get(const char *digest) -{ - const mbedtls_md_info_t *md = NULL; - ASSERT(digest); - - md = mbedtls_md_info_from_string(digest); - if (!md) - { - msg(M_FATAL, "Message hash algorithm '%s' not found", digest); - } - if (mbedtls_md_get_size(md) > MAX_HMAC_KEY_LENGTH) - { - msg(M_FATAL, - "Message hash algorithm '%s' uses a default hash size (%d bytes) which is larger than " PACKAGE_NAME - "'s current maximum hash size (%d bytes)", - digest, mbedtls_md_get_size(md), MAX_HMAC_KEY_LENGTH); - } - return md; -} - -bool -md_valid(const char *digest) -{ - const mbedtls_md_info_t *md = mbedtls_md_info_from_string(digest); - return md != NULL; -} - -const char * -md_kt_name(const char *mdname) -{ - if (!strcmp("none", mdname)) - { - return "[null-digest]"; - } - const mbedtls_md_info_t *kt = md_get(mdname); - return mbedtls_md_get_name(kt); -} - -unsigned char -md_kt_size(const char *mdname) -{ - if (!strcmp("none", mdname)) - { - return 0; - } - const mbedtls_md_info_t *kt = md_get(mdname); - return mbedtls_md_get_size(kt); -} - -/* - * - * Generic message digest functions - * - */ - -int -md_full(const char *mdname, const uint8_t *src, int src_len, uint8_t *dst) -{ - const mbedtls_md_info_t *kt = md_get(mdname); - return 0 == mbedtls_md(kt, src, src_len, dst); -} - -mbedtls_md_context_t * -md_ctx_new(void) -{ - mbedtls_md_context_t *ctx; - ALLOC_OBJ_CLEAR(ctx, mbedtls_md_context_t); - return ctx; -} - -void -md_ctx_free(mbedtls_md_context_t *ctx) -{ - free(ctx); -} - -void -md_ctx_init(mbedtls_md_context_t *ctx, const char *mdname) -{ - const mbedtls_md_info_t *kt = md_get(mdname); - ASSERT(NULL != ctx && NULL != kt); - - mbedtls_md_init(ctx); - ASSERT(0 == mbedtls_md_setup(ctx, kt, 0)); - ASSERT(0 == mbedtls_md_starts(ctx)); -} - -void -md_ctx_cleanup(mbedtls_md_context_t *ctx) -{ - mbedtls_md_free(ctx); -} - -int -md_ctx_size(const mbedtls_md_context_t *ctx) -{ - if (NULL == ctx) - { - return 0; - } - return (int)mbedtls_md_get_size(mbedtls_md_info_from_ctx(ctx)); -} - -void -md_ctx_update(mbedtls_md_context_t *ctx, const uint8_t *src, size_t src_len) -{ - ASSERT(0 == mbedtls_md_update(ctx, src, src_len)); -} - -void -md_ctx_final(mbedtls_md_context_t *ctx, uint8_t *dst) -{ - ASSERT(0 == mbedtls_md_finish(ctx, dst)); - mbedtls_md_free(ctx); -} - - -/* - * - * Generic HMAC functions - * - */ - - -/* - * TODO: re-enable dmsg for crypto debug - */ - -mbedtls_md_context_t * -hmac_ctx_new(void) -{ - mbedtls_md_context_t *ctx; - ALLOC_OBJ(ctx, mbedtls_md_context_t); - return ctx; -} - -void -hmac_ctx_free(mbedtls_md_context_t *ctx) -{ - free(ctx); -} - -void -hmac_ctx_init(mbedtls_md_context_t *ctx, const uint8_t *key, const char *mdname) -{ - const mbedtls_md_info_t *kt = md_get(mdname); - ASSERT(NULL != kt && NULL != ctx); - - mbedtls_md_init(ctx); - int key_len = mbedtls_md_get_size(kt); - ASSERT(0 == mbedtls_md_setup(ctx, kt, 1)); - ASSERT(0 == mbedtls_md_hmac_starts(ctx, key, key_len)); - - /* make sure we used a big enough key */ - ASSERT(mbedtls_md_get_size(kt) <= key_len); -} - -void -hmac_ctx_cleanup(mbedtls_md_context_t *ctx) -{ - mbedtls_md_free(ctx); -} - -int -hmac_ctx_size(mbedtls_md_context_t *ctx) -{ - if (NULL == ctx) - { - return 0; - } - return mbedtls_md_get_size(mbedtls_md_info_from_ctx(ctx)); -} - -void -hmac_ctx_reset(mbedtls_md_context_t *ctx) -{ - ASSERT(0 == mbedtls_md_hmac_reset(ctx)); -} - -void -hmac_ctx_update(mbedtls_md_context_t *ctx, const uint8_t *src, int src_len) -{ - ASSERT(0 == mbedtls_md_hmac_update(ctx, src, src_len)); -} - -void -hmac_ctx_final(mbedtls_md_context_t *ctx, uint8_t *dst) -{ - ASSERT(0 == mbedtls_md_hmac_finish(ctx, dst)); -} - -int -memcmp_constant_time(const void *a, const void *b, size_t size) -{ - /* mbed TLS has a no const time memcmp function that it exposes - * via its APIs like OpenSSL does with CRYPTO_memcmp - * Adapt the function that mbedtls itself uses in - * mbedtls_safer_memcmp as it considers that to be safe */ - volatile const unsigned char *A = (volatile const unsigned char *)a; - volatile const unsigned char *B = (volatile const unsigned char *)b; - volatile unsigned char diff = 0; - - for (size_t i = 0; i < size; i++) - { - unsigned char x = A[i], y = B[i]; - diff |= x ^ y; - } - - return diff; -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - -/* - * Generate the hash required by for the \c tls1_PRF function. - * - * @param md_kt Message digest to use - * @param sec Secret to base the hash on - * @param sec_len Length of the secret - * @param seed Seed to hash - * @param seed_len Length of the seed - * @param out Output buffer - * @param olen Length of the output buffer - */ -static void -tls1_P_hash(const mbedtls_md_info_t *md_kt, const uint8_t *sec, size_t sec_len, const uint8_t *seed, - size_t seed_len, uint8_t *out, size_t olen) -{ - struct gc_arena gc = gc_new(); - uint8_t A1[MAX_HMAC_KEY_LENGTH]; - -#ifdef ENABLE_DEBUG - /* used by the D_SHOW_KEY_SOURCE, guarded with ENABLE_DEBUG to avoid unused - * variables warnings if compiled with --enable-small */ - const size_t olen_orig = olen; - const uint8_t *out_orig = out; -#endif - - hmac_ctx_t *ctx = hmac_ctx_new(); - hmac_ctx_t *ctx_tmp = hmac_ctx_new(); - - dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash sec: %s", format_hex(sec, sec_len, 0, &gc)); - dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash seed: %s", format_hex(seed, seed_len, 0, &gc)); - - unsigned int chunk = mbedtls_md_get_size(md_kt); - unsigned int A1_len = mbedtls_md_get_size(md_kt); - - /* This is the only place where we init an HMAC with a key that is not - * equal to its size, therefore we init the hmac ctx manually here */ - mbedtls_md_init(ctx); - ASSERT(0 == mbedtls_md_setup(ctx, md_kt, 1)); - ASSERT(0 == mbedtls_md_hmac_starts(ctx, sec, sec_len)); - - mbedtls_md_init(ctx_tmp); - ASSERT(0 == mbedtls_md_setup(ctx_tmp, md_kt, 1)); - ASSERT(0 == mbedtls_md_hmac_starts(ctx_tmp, sec, sec_len)); - - hmac_ctx_update(ctx, seed, seed_len); - hmac_ctx_final(ctx, A1); - - for (;;) - { - hmac_ctx_reset(ctx); - hmac_ctx_reset(ctx_tmp); - hmac_ctx_update(ctx, A1, A1_len); - hmac_ctx_update(ctx_tmp, A1, A1_len); - hmac_ctx_update(ctx, seed, seed_len); - - if (olen > chunk) - { - hmac_ctx_final(ctx, out); - out += chunk; - olen -= chunk; - hmac_ctx_final(ctx_tmp, A1); /* calc the next A1 value */ - } - else /* last one */ - { - hmac_ctx_final(ctx, A1); - memcpy(out, A1, olen); - break; - } - } - hmac_ctx_cleanup(ctx); - hmac_ctx_free(ctx); - hmac_ctx_cleanup(ctx_tmp); - hmac_ctx_free(ctx_tmp); - secure_memzero(A1, sizeof(A1)); - - dmsg(D_SHOW_KEY_SOURCE, "tls1_P_hash out: %s", format_hex(out_orig, olen_orig, 0, &gc)); - gc_free(&gc); -} - -/* - * Use the TLS PRF function for generating data channel keys. - * This code is based on the OpenSSL library. - * - * TLS generates keys as such: - * - * master_secret[48] = PRF(pre_master_secret[48], "master secret", - * ClientHello.random[32] + ServerHello.random[32]) - * - * key_block[] = PRF(SecurityParameters.master_secret[48], - * "key expansion", - * SecurityParameters.server_random[32] + - * SecurityParameters.client_random[32]); - * - * Notes: - * - * (1) key_block contains a full set of 4 keys. - * (2) The pre-master secret is generated by the client. - */ -bool -ssl_tls1_PRF(const uint8_t *label, size_t label_len, const uint8_t *sec, size_t slen, uint8_t *out1, - size_t olen) -{ - struct gc_arena gc = gc_new(); - const md_kt_t *md5 = md_get("MD5"); - const md_kt_t *sha1 = md_get("SHA1"); - - uint8_t *out2 = (uint8_t *)gc_malloc(olen, false, &gc); - - size_t len = slen / 2; - const uint8_t *S1 = sec; - const uint8_t *S2 = &(sec[len]); - len += (slen & 1); /* add for odd, make longer */ - - tls1_P_hash(md5, S1, len, label, label_len, out1, olen); - tls1_P_hash(sha1, S2, len, label, label_len, out2, olen); - - for (size_t i = 0; i < olen; i++) - { - out1[i] ^= out2[i]; - } - - secure_memzero(out2, olen); - - dmsg(D_SHOW_KEY_SOURCE, "tls1_PRF out[%zu]: %s", olen, format_hex(out1, olen, 0, &gc)); - - gc_free(&gc); - return true; -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop -#endif - -#endif /* ENABLE_CRYPTO_MBEDTLS */ diff --git a/src/openvpn/crypto_mbedtls.h b/src/openvpn/crypto_mbedtls.h index 71514fc76f7..e69de29bb2d 100644 --- a/src/openvpn/crypto_mbedtls.h +++ b/src/openvpn/crypto_mbedtls.h @@ -1,148 +0,0 @@ -/* - * 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-2025 OpenVPN Inc - * Copyright (C) 2010-2021 Sentyron B.V. - * - * 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, see . - */ - -/** - * @file - * Data Channel Cryptography mbed TLS-specific backend interface - */ - -#ifndef CRYPTO_MBEDTLS_H_ -#define CRYPTO_MBEDTLS_H_ - -#include -#include -#include -#include - -/** Generic message digest key type %context. */ -typedef mbedtls_md_info_t md_kt_t; - -/** Generic cipher %context. */ -typedef mbedtls_cipher_context_t cipher_ctx_t; - -/** Generic message digest %context. */ -typedef mbedtls_md_context_t md_ctx_t; - -/** Generic HMAC %context. */ -typedef mbedtls_md_context_t hmac_ctx_t; - -/* Use a dummy type for the provider */ -typedef void provider_t; - -/** Maximum length of an IV */ -#define OPENVPN_MAX_IV_LENGTH MBEDTLS_MAX_IV_LENGTH - -/** Cipher is in CBC mode */ -#define OPENVPN_MODE_CBC MBEDTLS_MODE_CBC - -/** Cipher is in OFB mode */ -#define OPENVPN_MODE_OFB MBEDTLS_MODE_OFB - -/** Cipher is in CFB mode */ -#define OPENVPN_MODE_CFB MBEDTLS_MODE_CFB - -/** Cipher is in GCM mode */ -#define OPENVPN_MODE_GCM MBEDTLS_MODE_GCM - -typedef mbedtls_operation_t crypto_operation_t; - -/** Cipher should encrypt */ -#define OPENVPN_OP_ENCRYPT MBEDTLS_ENCRYPT - -/** Cipher should decrypt */ -#define OPENVPN_OP_DECRYPT MBEDTLS_DECRYPT - -#define MD4_DIGEST_LENGTH 16 -#define MD5_DIGEST_LENGTH 16 -#define SHA_DIGEST_LENGTH 20 -#define SHA256_DIGEST_LENGTH 32 - -/** - * Returns a singleton instance of the mbed TLS random number generator. - * - * For PolarSSL/mbed TLS 1.1+, this is the CTR_DRBG random number generator. If it - * hasn't been initialised yet, the RNG will be initialised using the default - * entropy sources. Aside from the default platform entropy sources, an - * additional entropy source, the HAVEGE random number generator will also be - * added. During initialisation, a personalisation string will be added based - * on the time, the PID, and a pointer to the random context. - */ -mbedtls_ctr_drbg_context *rand_ctx_get(void); - -#ifdef ENABLE_PREDICTION_RESISTANCE -/** - * Enable prediction resistance on the random number generator. - */ -void rand_ctx_enable_prediction_resistance(void); - -#endif - -/** - * Log the supplied mbed TLS error, prefixed by supplied prefix. - * - * @param flags Flags to indicate error type and priority. - * @param errval mbed TLS error code to convert to error message. - * @param prefix Prefix to mbed TLS error message. - * - * @returns true if no errors are detected, false otherwise. - */ -bool mbed_log_err(unsigned int flags, int errval, const char *prefix); - -/** - * Log the supplied mbed TLS error, prefixed by function name and line number. - * - * @param flags Flags to indicate error type and priority. - * @param errval mbed TLS error code to convert to error message. - * @param func Function name where error was reported. - * @param line Line number where error was reported. - * - * @returns true if no errors are detected, false otherwise. - */ -bool mbed_log_func_line(unsigned int flags, int errval, const char *func, int line); - -/** Wraps mbed_log_func_line() to prevent function calls for non-errors */ -static inline bool -mbed_log_func_line_lite(unsigned int flags, int errval, const char *func, int line) -{ - if (errval) - { - return mbed_log_func_line(flags, errval, func, line); - } - return true; -} - -/** - * Check errval and log on error. - * - * Convenience wrapper to put around mbed TLS library calls, e.g. - * if (!mbed_ok (mbedtls_ssl_func())) return 0; - * or - * ASSERT (mbed_ok (mbedtls_ssl_func())); - * - * @param errval mbed TLS error code to convert to error message. - * - * @returns true if no errors are detected, false otherwise. - */ -#define mbed_ok(errval) mbed_log_func_line_lite(D_CRYPT_ERRORS, errval, __func__, __LINE__) - -#endif /* CRYPTO_MBEDTLS_H_ */ diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c index ec0269c950e..f74c47ddd61 100644 --- a/src/openvpn/crypto_openssl.c +++ b/src/openvpn/crypto_openssl.c @@ -57,10 +57,6 @@ #include #endif -#if defined(_WIN32) && defined(OPENSSL_NO_EC) -#error Windows build with OPENSSL_NO_EC: disabling EC key is not supported. -#endif - #ifdef _MSC_VER /* mute ossl3 deprecation warnings treated as errors in msvc */ #pragma warning(disable : 4996) diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c index b18b9d4206c..e69de29bb2d 100644 --- a/src/openvpn/cryptoapi.c +++ b/src/openvpn/cryptoapi.c @@ -1,629 +0,0 @@ -/* - * Copyright (c) 2004 Peter 'Luna' Runestig - * Copyright (c) 2018 Selva Nair - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modifi- - * cation, are permitted provided that the following conditions are met: - * - * o Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * o Redistributions in binary form must reproduce the above copyright no- - * tice, this list of conditions and the following disclaimer in the do- - * cumentation and/or other materials provided with the distribution. - * - * o The names of the contributors may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LI- - * ABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUEN- - * TIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEV- - * ER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABI- - * LITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "syshead.h" - -#ifdef ENABLE_CRYPTOAPI - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "buffer.h" -#include "openssl_compat.h" -#include "win32.h" -#include "xkey_common.h" -#include "crypto_openssl.h" - -#ifndef HAVE_XKEY_PROVIDER - -int -SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) -{ - msg(M_NONFATAL, "ERROR: this binary was built without cryptoapicert support"); - return 0; -} - -#else /* HAVE_XKEY_PROVIDER */ - -static XKEY_EXTERNAL_SIGN_fn xkey_cng_sign; - -typedef struct _CAPI_DATA -{ - const CERT_CONTEXT *cert_context; - HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov; - EVP_PKEY *pubkey; - DWORD key_spec; - BOOL free_crypt_prov; - int ref_count; -} CAPI_DATA; - -/* - * Translate OpenSSL hash OID to CNG algorithm name. Returns - * "UNKNOWN" for unsupported algorithms and NULL for MD5+SHA1 - * mixed hash used in TLS 1.1 and earlier. - */ -static const wchar_t * -cng_hash_algo(int md_type) -{ - const wchar_t *alg = L"UNKNOWN"; - switch (md_type) - { - case NID_md5: - alg = BCRYPT_MD5_ALGORITHM; - break; - - case NID_sha1: - alg = BCRYPT_SHA1_ALGORITHM; - break; - - case NID_sha256: - alg = BCRYPT_SHA256_ALGORITHM; - break; - - case NID_sha384: - alg = BCRYPT_SHA384_ALGORITHM; - break; - - case NID_sha512: - alg = BCRYPT_SHA512_ALGORITHM; - break; - - case NID_md5_sha1: - case 0: - alg = NULL; - break; - - default: - msg(M_WARN | M_INFO, "cryptoapicert: Unknown hash type NID=0x%x", md_type); - break; - } - return alg; -} - -static void -CAPI_DATA_free(CAPI_DATA *cd) -{ - if (!cd || cd->ref_count-- > 0) - { - return; - } - if (cd->free_crypt_prov && cd->crypt_prov) - { - if (cd->key_spec == CERT_NCRYPT_KEY_SPEC) - { - NCryptFreeObject(cd->crypt_prov); - } - else - { - CryptReleaseContext(cd->crypt_prov, 0); - } - } - if (cd->cert_context) - { - CertFreeCertificateContext(cd->cert_context); - } - EVP_PKEY_free(cd->pubkey); /* passing NULL is okay */ - - free(cd); -} - -/** - * Parse a hex string with optional embedded spaces into - * a byte array. - * @param p pointer to the input string - * @param arr on output contains the parsed bytes - * @param capacity capacity of the byte array arr - * @returns the number of bytes parsed or 0 on error - */ -int -parse_hexstring(const char *p, unsigned char *arr, size_t capacity) -{ - int i = 0; - for (; *p && i < capacity; p += 2) - { - /* skip spaces */ - while (*p == ' ') - { - p++; - } - if (!*p) /* ending with spaces is not an error */ - { - break; - } - - if (!isxdigit(p[0]) || !isxdigit(p[1]) || sscanf(p, "%2hhx", &arr[i++]) != 1) - { - return 0; - } - } - return i; -} - -static void * -decode_object(struct gc_arena *gc, LPCSTR struct_type, const CRYPT_OBJID_BLOB *val, DWORD flags, - DWORD *cb) -{ - /* get byte count for decoding */ - BYTE *buf; - if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, struct_type, val->pbData, - val->cbData, flags, NULL, cb)) - { - return NULL; - } - - /* do the actual decode */ - buf = gc_malloc(*cb, false, gc); - if (!CryptDecodeObject(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, struct_type, val->pbData, - val->cbData, flags, buf, cb)) - { - return NULL; - } - - return buf; -} - -static const CRYPT_OID_INFO * -find_oid(DWORD keytype, const void *key, DWORD groupid) -{ - const CRYPT_OID_INFO *info = NULL; - - /* try proper resolve, also including AD */ - info = CryptFindOIDInfo(keytype, (void *)key, groupid); - - /* fall back to all groups if not found yet */ - if (!info && groupid) - { - info = CryptFindOIDInfo(keytype, (void *)key, 0); - } - - return info; -} - -static bool -test_certificate_template(const char *cert_prop, const CERT_CONTEXT *cert_ctx) -{ - const CERT_INFO *info = cert_ctx->pCertInfo; - const CERT_EXTENSION *ext; - DWORD cbext; - void *pvext; - struct gc_arena gc = gc_new(); - const WCHAR *tmpl_name = wide_string(cert_prop, &gc); - - /* check for V2 extension (Windows 2003+) */ - ext = CertFindExtension(szOID_CERTIFICATE_TEMPLATE, info->cExtension, info->rgExtension); - if (ext) - { - pvext = decode_object(&gc, X509_CERTIFICATE_TEMPLATE, &ext->Value, 0, &cbext); - if (pvext && cbext >= sizeof(CERT_TEMPLATE_EXT)) - { - const CERT_TEMPLATE_EXT *cte = (const CERT_TEMPLATE_EXT *)pvext; - if (!stricmp(cert_prop, cte->pszObjId)) - { - /* found direct OID match with certificate property specified */ - gc_free(&gc); - return true; - } - - const CRYPT_OID_INFO *tmpl_oid = - find_oid(CRYPT_OID_INFO_NAME_KEY, tmpl_name, CRYPT_TEMPLATE_OID_GROUP_ID); - if (tmpl_oid && !stricmp(tmpl_oid->pszOID, cte->pszObjId)) - { - /* found OID match in extension against resolved key */ - gc_free(&gc); - return true; - } - } - } - - /* no extension found, exit */ - gc_free(&gc); - return false; -} - -static const CERT_CONTEXT * -find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) -{ - /* Find, and use, the desired certificate from the store. The - * 'cert_prop' certificate search string can look like this: - * SUBJ: - * THUMB:, e.g. - * THUMB:f6 49 24 41 01 b4 fb 44 0c ce f4 36 ae d0 c4 c9 df 7a b6 28 - * TMPL: