diff options
Diffstat (limited to 'src/openvpn/crypto_openssl.c')
-rw-r--r-- | src/openvpn/crypto_openssl.c | 285 |
1 files changed, 214 insertions, 71 deletions
diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c index 3abcc99..c60d4a5 100644 --- a/src/openvpn/crypto_openssl.c +++ b/src/openvpn/crypto_openssl.c @@ -34,7 +34,7 @@ #include "syshead.h" -#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) +#if defined(ENABLE_CRYPTO_OPENSSL) #include "basic.h" #include "buffer.h" @@ -43,6 +43,7 @@ #include "crypto_backend.h" #include "openssl_compat.h" +#include <openssl/conf.h> #include <openssl/des.h> #include <openssl/err.h> #include <openssl/evp.h> @@ -63,6 +64,7 @@ #endif #if HAVE_OPENSSL_ENGINE +#include <openssl/ui.h> #include <openssl/engine.h> static bool engine_initialized = false; /* GLOBAL */ @@ -148,6 +150,11 @@ crypto_init_lib_engine(const char *engine_name) void crypto_init_lib(void) { +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL); +#else + OPENSSL_config(NULL); +#endif /* * If you build the OpenSSL library and OpenVPN with * CRYPTO_MDEBUG, you will get a listing of OpenSSL @@ -202,12 +209,12 @@ crypto_print_openssl_errors(const unsigned int flags) else if (ERR_GET_REASON(err) == SSL_R_UNSUPPORTED_PROTOCOL) { msg(D_CRYPT_ERRORS, "TLS error: Unsupported protocol. This typically " - "indicates that client and server have no common TLS version enabled. " - "This can be caused by mismatched tls-version-min and tls-version-max " - "options on client and server. " - "If your OpenVPN client is between v2.3.6 and v2.3.2 try adding " - "tls-version-min 1.0 to the client configuration to use TLS 1.0+ " - "instead of TLS 1.0 only"); + "indicates that client and server have no common TLS version enabled. " + "This can be caused by mismatched tls-version-min and tls-version-max " + "options on client and server. " + "If your OpenVPN client is between v2.3.6 and v2.3.2 try adding " + "tls-version-min 1.0 to the client configuration to use TLS 1.0+ " + "instead of TLS 1.0 only"); } msg(flags, "OpenSSL: %s", ERR_error_string(err, NULL)); } @@ -254,6 +261,7 @@ const cipher_name_pair cipher_name_translation_table[] = { { "AES-128-GCM", "id-aes128-GCM" }, { "AES-192-GCM", "id-aes192-GCM" }, { "AES-256-GCM", "id-aes256-GCM" }, + { "CHACHA20-POLY1305", "ChaCha20-Poly1305" }, }; const size_t cipher_name_translation_table_count = sizeof(cipher_name_translation_table) / sizeof(*cipher_name_translation_table); @@ -265,27 +273,7 @@ cipher_name_cmp(const void *a, const void *b) const EVP_CIPHER *const *cipher_a = a; const EVP_CIPHER *const *cipher_b = b; - const char *cipher_name_a = - translate_cipher_name_to_openvpn(EVP_CIPHER_name(*cipher_a)); - const char *cipher_name_b = - translate_cipher_name_to_openvpn(EVP_CIPHER_name(*cipher_b)); - - return strcmp(cipher_name_a, cipher_name_b); -} - -static void -print_cipher(const EVP_CIPHER *cipher) -{ - const char *var_key_size = - (EVP_CIPHER_flags(cipher) & EVP_CIPH_VARIABLE_LENGTH) ? - " by default" : ""; - const char *ssl_only = cipher_kt_mode_cbc(cipher) ? - "" : ", TLS client/server mode only"; - - printf("%s (%d bit key%s, %d bit block%s)\n", - translate_cipher_name_to_openvpn(EVP_CIPHER_name(cipher)), - EVP_CIPHER_key_length(cipher) * 8, var_key_size, - cipher_kt_block_size(cipher) * 8, ssl_only); + return strcmp(cipher_kt_name(*cipher_a), cipher_kt_name(*cipher_b)); } void @@ -299,11 +287,11 @@ show_available_ciphers(void) size_t num_ciphers = 0; #ifndef ENABLE_SMALL printf("The following ciphers and cipher modes are available for use\n" - "with " PACKAGE_NAME ". Each cipher shown below may be use as a\n" - "parameter to the --cipher option. The default key size is\n" - "shown as well as whether or not it can be changed with the\n" - "--keysize directive. Using a CBC or GCM mode is recommended.\n" - "In static key mode only CBC mode is allowed.\n\n"); + "with " PACKAGE_NAME ". Each cipher shown below may be used as a\n" + "parameter to the --data-ciphers (or --cipher) option. The\n" + "default key size is shown as well as whether or not it can be\n" + "changed with the --keysize directive. Using a GCM or CBC mode\n" + "is recommended. In static key mode only CBC mode is allowed.\n\n"); #endif for (nid = 0; nid < 10000; ++nid) @@ -313,9 +301,7 @@ show_available_ciphers(void) #ifdef ENABLE_OFB_CFB_MODE || cipher_kt_mode_ofb_cfb(cipher) #endif -#ifdef HAVE_AEAD_CIPHER_MODES || cipher_kt_mode_aead(cipher) -#endif )) { cipher_list[num_ciphers++] = cipher; @@ -327,10 +313,12 @@ show_available_ciphers(void) } } - qsort(cipher_list, num_ciphers, sizeof(*cipher_list), cipher_name_cmp); + /* cast to non-const to prevent warning */ + qsort((EVP_CIPHER *)cipher_list, num_ciphers, sizeof(*cipher_list), cipher_name_cmp); - for (i = 0; i < num_ciphers; i++) { - if (cipher_kt_block_size(cipher_list[i]) >= 128/8) + for (i = 0; i < num_ciphers; i++) + { + if (!cipher_kt_insecure(cipher_list[i])) { print_cipher(cipher_list[i]); } @@ -338,8 +326,9 @@ show_available_ciphers(void) 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"); - for (i = 0; i < num_ciphers; i++) { - if (cipher_kt_block_size(cipher_list[i]) < 128/8) + for (i = 0; i < num_ciphers; i++) + { + if (cipher_kt_insecure(cipher_list[i])) { print_cipher(cipher_list[i]); } @@ -396,6 +385,88 @@ show_available_engines(void) #endif } + +bool +crypto_pem_encode(const char *name, struct buffer *dst, + const struct buffer *src, struct gc_arena *gc) +{ + bool ret = false; + BIO *bio = BIO_new(BIO_s_mem()); + if (!bio || !PEM_write_bio(bio, name, "", BPTR(src), BLEN(src))) + { + ret = false; + goto cleanup; + } + + BUF_MEM *bptr; + BIO_get_mem_ptr(bio, &bptr); + + *dst = alloc_buf_gc(bptr->length, gc); + ASSERT(buf_write(dst, bptr->data, bptr->length)); + + ret = true; +cleanup: + if (!BIO_free(bio)) + { + ret = false; + } + + return ret; +} + +bool +crypto_pem_decode(const char *name, struct buffer *dst, + const struct buffer *src) +{ + bool ret = false; + + BIO *bio = BIO_new_mem_buf((char *)BPTR(src), BLEN(src)); + if (!bio) + { + crypto_msg(M_FATAL, "Cannot open memory BIO for PEM decode"); + } + + char *name_read = NULL; + char *header_read = NULL; + uint8_t *data_read = NULL; + long data_read_len = 0; + if (!PEM_read_bio(bio, &name_read, &header_read, &data_read, + &data_read_len)) + { + dmsg(D_CRYPT_ERRORS, "%s: PEM decode failed", __func__); + goto cleanup; + } + + if (strcmp(name, name_read)) + { + dmsg(D_CRYPT_ERRORS, + "%s: unexpected PEM name (got '%s', expected '%s')", + __func__, name_read, name); + goto cleanup; + } + + uint8_t *dst_data = buf_write_alloc(dst, data_read_len); + if (!dst_data) + { + dmsg(D_CRYPT_ERRORS, "%s: dst too small (%i, needs %li)", __func__, + BCAP(dst), data_read_len); + goto cleanup; + } + memcpy(dst_data, data_read, data_read_len); + + ret = true; +cleanup: + OPENSSL_free(name_read); + OPENSSL_free(header_read); + OPENSSL_free(data_read); + if (!BIO_free(bio)) + { + ret = false; + } + + return ret; +} + /* * * Random number functions, used in cases where we want @@ -515,6 +586,7 @@ cipher_kt_get(const char *ciphername) ASSERT(ciphername); + ciphername = translate_cipher_name_from_openvpn(ciphername); cipher = EVP_get_cipherbyname(ciphername); if (NULL == cipher) @@ -543,7 +615,9 @@ cipher_kt_name(const EVP_CIPHER *cipher_kt) { return "[null-cipher]"; } - return EVP_CIPHER_name(cipher_kt); + + const char *name = EVP_CIPHER_name(cipher_kt); + return translate_cipher_name_to_openvpn(name); } int @@ -574,7 +648,7 @@ cipher_kt_block_size(const EVP_CIPHER *cipher) int block_size = EVP_CIPHER_block_size(cipher); - orig_name = cipher_kt_name(cipher); + orig_name = EVP_CIPHER_name(cipher); if (!orig_name) { goto cleanup; @@ -613,6 +687,16 @@ cipher_kt_tag_size(const EVP_CIPHER *cipher_kt) } } +bool +cipher_kt_insecure(const EVP_CIPHER *cipher) +{ + return !(cipher_kt_block_size(cipher) >= 128 / 8 +#ifdef NID_chacha20_poly1305 + || EVP_CIPHER_nid(cipher) == NID_chacha20_poly1305 +#endif + ); +} + int cipher_kt_mode(const EVP_CIPHER *cipher_kt) { @@ -624,11 +708,8 @@ bool cipher_kt_mode_cbc(const cipher_kt_t *cipher) { return cipher && cipher_kt_mode(cipher) == OPENVPN_MODE_CBC -#ifdef EVP_CIPH_FLAG_AEAD_CIPHER /* Exclude AEAD cipher modes, they require a different API */ - && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) -#endif - ; + && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER); } bool @@ -636,21 +717,28 @@ cipher_kt_mode_ofb_cfb(const cipher_kt_t *cipher) { return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_OFB || cipher_kt_mode(cipher) == OPENVPN_MODE_CFB) -#ifdef EVP_CIPH_FLAG_AEAD_CIPHER /* Exclude AEAD cipher modes, they require a different API */ - && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER) -#endif - ; + && !(EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER); } bool cipher_kt_mode_aead(const cipher_kt_t *cipher) { -#ifdef HAVE_AEAD_CIPHER_MODES - return cipher && (cipher_kt_mode(cipher) == OPENVPN_MODE_GCM); -#else - return false; + if (cipher) + { + switch (EVP_CIPHER_nid(cipher)) + { + case NID_aes_128_gcm: + case NID_aes_192_gcm: + case NID_aes_256_gcm: +#ifdef NID_chacha20_poly1305 + case NID_chacha20_poly1305: #endif + return true; + } + } + + return false; } /* @@ -708,11 +796,7 @@ cipher_ctx_iv_length(const EVP_CIPHER_CTX *ctx) int cipher_ctx_get_tag(EVP_CIPHER_CTX *ctx, uint8_t *tag_buf, int tag_size) { -#ifdef HAVE_AEAD_CIPHER_MODES return EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_size, tag_buf); -#else - ASSERT(0); -#endif } int @@ -735,7 +819,7 @@ cipher_ctx_get_cipher_kt(const cipher_ctx_t *ctx) int -cipher_ctx_reset(EVP_CIPHER_CTX *ctx, uint8_t *iv_buf) +cipher_ctx_reset(EVP_CIPHER_CTX *ctx, const uint8_t *iv_buf) { return EVP_CipherInit_ex(ctx, NULL, NULL, NULL, iv_buf, -1); } @@ -743,16 +827,12 @@ cipher_ctx_reset(EVP_CIPHER_CTX *ctx, uint8_t *iv_buf) int cipher_ctx_update_ad(EVP_CIPHER_CTX *ctx, const uint8_t *src, int src_len) { -#ifdef HAVE_AEAD_CIPHER_MODES int len; if (!EVP_CipherUpdate(ctx, NULL, &len, src, src_len)) { crypto_msg(M_FATAL, "%s: EVP_CipherUpdate() failed", __func__); } return 1; -#else /* ifdef HAVE_AEAD_CIPHER_MODES */ - ASSERT(0); -#endif } int @@ -776,7 +856,6 @@ int cipher_ctx_final_check_tag(EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len, uint8_t *tag, size_t tag_len) { -#ifdef HAVE_AEAD_CIPHER_MODES ASSERT(tag_len < SIZE_MAX); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_len, tag)) { @@ -784,9 +863,6 @@ cipher_ctx_final_check_tag(EVP_CIPHER_CTX *ctx, uint8_t *dst, int *dst_len, } return cipher_ctx_final(ctx, dst, dst_len); -#else /* ifdef HAVE_AEAD_CIPHER_MODES */ - ASSERT(0); -#endif } void @@ -837,10 +913,10 @@ md_kt_name(const EVP_MD *kt) return EVP_MD_name(kt); } -int +unsigned char md_kt_size(const EVP_MD *kt) { - return EVP_MD_size(kt); + return (unsigned char)EVP_MD_size(kt); } @@ -866,7 +942,8 @@ md_ctx_new(void) return ctx; } -void md_ctx_free(EVP_MD_CTX *ctx) +void +md_ctx_free(EVP_MD_CTX *ctx) { EVP_MD_CTX_free(ctx); } @@ -972,4 +1049,70 @@ hmac_ctx_final(HMAC_CTX *ctx, uint8_t *dst) HMAC_Final(ctx, dst, &in_hmac_len); } -#endif /* ENABLE_CRYPTO && ENABLE_CRYPTO_OPENSSL */ +int +memcmp_constant_time(const void *a, const void *b, size_t size) +{ + return CRYPTO_memcmp(a, b, size); +} + +#if HAVE_OPENSSL_ENGINE +static int +ui_reader(UI *ui, UI_STRING *uis) +{ + SSL_CTX *ctx = UI_get0_user_data(ui); + + if (UI_get_string_type(uis) == UIT_PROMPT) + { + pem_password_cb *cb = SSL_CTX_get_default_passwd_cb(ctx); + void *d = SSL_CTX_get_default_passwd_cb_userdata(ctx); + char password[64]; + + cb(password, sizeof(password), 0, d); + UI_set_result(ui, uis, password); + + return 1; + } + return 0; +} +#endif + +EVP_PKEY * +engine_load_key(const char *file, SSL_CTX *ctx) +{ +#if HAVE_OPENSSL_ENGINE + UI_METHOD *ui; + EVP_PKEY *pkey; + + if (!engine_persist) + { + return NULL; + } + + /* this will print out the error from BIO_read */ + crypto_msg(M_INFO, "PEM_read_bio failed, now trying engine method to load private key"); + + ui = UI_create_method("openvpn"); + if (!ui) + { + crypto_msg(M_FATAL, "Engine UI creation failed"); + return NULL; + } + + UI_method_set_reader(ui, ui_reader); + + ENGINE_init(engine_persist); + pkey = ENGINE_load_private_key(engine_persist, file, ui, ctx); + ENGINE_finish(engine_persist); + if (!pkey) + { + crypto_msg(M_FATAL, "Engine could not load key file"); + } + + UI_destroy_method(ui); + return pkey; +#else /* if HAVE_OPENSSL_ENGINE */ + return NULL; +#endif /* if HAVE_OPENSSL_ENGINE */ +} + +#endif /* ENABLE_CRYPTO_OPENSSL */ |