summaryrefslogtreecommitdiff
path: root/src/openvpn/crypto_openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpn/crypto_openssl.c')
-rw-r--r--src/openvpn/crypto_openssl.c285
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 */