efl/src/lib/ecore_con/efl_net_ssl_ctx-openssl.c

397 lines
13 KiB
C

#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/dh.h>
struct _Efl_Net_Ssl_Ctx
{
SSL_CTX *ssl_ctx;
Eina_Bool did_certificates;
Eina_Bool is_dialer;
};
#if OPENSSL_VERSION_NUMBER < 0x10100000L
# define TLS_server_method TLSv1_2_server_method
# define TLS_client_method TLSv1_2_client_method
#endif
#define EFL_NET_SSL_CONTEXT_CIPHERS "aRSA+HIGH:+kEDH:+kRSA:!kSRP:!kPSK:+3DES:!MD5"
#define _efl_net_ssl_ctx_check_errors() \
__efl_net_ssl_ctx_check_errors(__FILE__, __LINE__, __func__)
static unsigned long
__efl_net_ssl_ctx_check_errors(const char *file, int line, const char *fname)
{
unsigned long first = 0;
do
{
const char *_ssl_err_file, *_ssl_err_data;
int _ssl_err_line, _ssl_err_flags;
unsigned long _ssl_err = ERR_get_error_line_data(&_ssl_err_file, &_ssl_err_line, &_ssl_err_data, &_ssl_err_flags);
if (!_ssl_err) break;
if (!first) first = _ssl_err;
eina_log_print(_ecore_con_log_dom, EINA_LOG_LEVEL_ERR, file, fname, line,
"OpenSSL error %s:%d%s%s: %s",
_ssl_err_file, _ssl_err_line,
(_ssl_err_flags & ERR_TXT_STRING) ? " " : "",
(_ssl_err_flags & ERR_TXT_STRING) ? _ssl_err_data : "",
ERR_reason_error_string(_ssl_err));
}
while (1);
return first;
}
static Eina_Error
_efl_net_ssl_ctx_load_lists(Efl_Net_Ssl_Ctx *ctx, Efl_Net_Ssl_Ctx_Config cfg)
{
Eina_List *n, *n_next;
const char *path;
unsigned certificates_count = eina_list_count(*cfg.certificates);
unsigned private_keys_count = eina_list_count(*cfg.private_keys);
unsigned certificate_revocation_lists_count = eina_list_count(*cfg.certificate_revocation_lists);
unsigned certificate_authorities_count = eina_list_count(*cfg.certificate_authorities);
long err_ssl;
const char *err_file;
const char *err_data;
int err_line, err_flags;
X509_STORE *x509_store;
X509_LOOKUP *x509_lookup;
unsigned long x509_store_flags = 0;
#ifdef X509_V_FLAG_TRUSTED_FIRST
x509_store_flags |= X509_V_FLAG_TRUSTED_FIRST;
#endif
if (cfg.load_defaults)
{
if (SSL_CTX_set_default_verify_paths(ctx->ssl_ctx) != 1)
{
_efl_net_ssl_ctx_check_errors();
ERR("ssl_ctx=%p could not load default paths", ctx);
return ENOSYS;
}
DBG("ssl_ctx=%p loaded default paths", ctx);
}
else
DBG("ssl_ctx=%p did not load default paths", ctx);
EINA_LIST_FOREACH_SAFE(*cfg.certificates, n, n_next, path)
{
if ((SSL_CTX_use_certificate_file(ctx->ssl_ctx, path, SSL_FILETYPE_PEM) != 1) &&
(SSL_CTX_use_certificate_file(ctx->ssl_ctx, path, SSL_FILETYPE_ASN1) != 1))
{
err_ssl = ERR_peek_error_line_data(&err_file, &err_line, &err_data, &err_flags);
_efl_net_ssl_ctx_check_errors();
_efl_net_ssl_ctx_check_errors();
ERR("ssl_ctx=%p could not use certificate from %s [%s:%d%s%s '%s']",
ctx, path,
err_file, err_line,
(err_flags & ERR_TXT_STRING) ? " " : "",
(err_flags & ERR_TXT_STRING) ? err_data : "",
ERR_reason_error_string(err_ssl));
eina_stringshare_del(path);
*cfg.certificates = eina_list_remove_list(*cfg.certificates, n);
continue;
}
DBG("ssl_ctx=%p loaded certificate '%s'", ctx, path);
ctx->did_certificates = EINA_TRUE;
}
if (certificates_count && !*cfg.certificates)
{
ERR("ssl_ctx=%p none of the required certificates were loaded!", ctx);
return EINVAL;
}
EINA_LIST_FOREACH_SAFE(*cfg.private_keys, n, n_next, path)
{
if ((SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, path, SSL_FILETYPE_PEM) != 1) &&
(SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, path, SSL_FILETYPE_ASN1) != 1))
{
err_ssl = ERR_peek_error_line_data(&err_file, &err_line, &err_data, &err_flags);
_efl_net_ssl_ctx_check_errors();
ERR("ssl_ctx=%p could not use private key from %s [%s:%d%s%s '%s']",
ctx, path,
err_file, err_line,
(err_flags & ERR_TXT_STRING) ? " " : "",
(err_flags & ERR_TXT_STRING) ? err_data : "",
ERR_reason_error_string(err_ssl));
eina_stringshare_del(path);
*cfg.private_keys = eina_list_remove_list(*cfg.private_keys, n);
continue;
}
if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1)
{
err_ssl = ERR_peek_error_line_data(&err_file, &err_line, &err_data, &err_flags);
_efl_net_ssl_ctx_check_errors();
ERR("ssl_ctx=%p could not check private key from %s [%s:%d%s%s '%s']",
ctx, path,
err_file, err_line,
(err_flags & ERR_TXT_STRING) ? " " : "",
(err_flags & ERR_TXT_STRING) ? err_data : "",
ERR_reason_error_string(err_ssl));
continue;
}
DBG("ssl_ctx=%p loaded private key '%s'", ctx, path);
}
if (private_keys_count && !*cfg.private_keys)
{
ERR("ssl_ctx=%p none of the required private keys were loaded!", ctx);
return EINVAL;
}
x509_store = SSL_CTX_get_cert_store(ctx->ssl_ctx);
if (!x509_store)
{
_efl_net_ssl_ctx_check_errors();
ERR("ssl_ctx=%p SSL has no X509 certificate store", ctx);
return ENOSYS;
}
x509_lookup = X509_STORE_add_lookup(x509_store, X509_LOOKUP_file());
if (!x509_lookup)
{
_efl_net_ssl_ctx_check_errors();
ERR("ssl_ctx=%p could not add X509 file lookup", ctx);
return ENOSYS;
}
EINA_LIST_FOREACH_SAFE(*cfg.certificate_revocation_lists, n, n_next, path)
{
if ((X509_load_crl_file(x509_lookup, path, X509_FILETYPE_PEM) != 1) &&
(X509_load_crl_file(x509_lookup, path, X509_FILETYPE_ASN1) != 1))
{
err_ssl = ERR_peek_error_line_data(&err_file, &err_line, &err_data, &err_flags);
_efl_net_ssl_ctx_check_errors();
ERR("ssl_ctx=%p could not use certificate revocation lists from %s [%s:%d%s%s '%s']",
ctx, path,
err_file, err_line,
(err_flags & ERR_TXT_STRING) ? " " : "",
(err_flags & ERR_TXT_STRING) ? err_data : "",
ERR_reason_error_string(err_ssl));
eina_stringshare_del(path);
*cfg.certificate_revocation_lists = eina_list_remove_list(*cfg.certificate_revocation_lists, n);
continue;
}
DBG("ssl_ctx=%p loaded certificate revocation lists '%s'", ctx, path);
x509_store_flags |= X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL;
}
if (certificate_revocation_lists_count && !*cfg.certificate_revocation_lists)
{
ERR("ssl_ctx=%p none of the required certificate revocation lists were loaded!", ctx);
return EINVAL;
}
X509_STORE_set_flags(x509_store, x509_store_flags);
EINA_LIST_FOREACH_SAFE(*cfg.certificate_authorities, n, n_next, path)
{
struct stat st;
const char *cafile = NULL, *cadir = NULL;
if (stat(path, &st) != 0)
{
ERR("ssl_ctx=%p could not load certificate authorities from '%s': %s", ctx, path, eina_error_msg_get(errno));
eina_stringshare_del(path);
*cfg.certificate_authorities = eina_list_remove_list(*cfg.certificate_authorities, n);
continue;
}
else if (S_ISDIR(st.st_mode)) cadir = path;
else cafile = path;
if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, cafile, cadir) != 1)
{
err_ssl = ERR_peek_error_line_data(&err_file, &err_line, &err_data, &err_flags);
_efl_net_ssl_ctx_check_errors();
ERR("ssl_ctx=%p could not use certificate authorities from %s [%s:%d%s%s '%s']",
ctx, path,
err_file, err_line,
(err_flags & ERR_TXT_STRING) ? " " : "",
(err_flags & ERR_TXT_STRING) ? err_data : "",
ERR_reason_error_string(err_ssl));
eina_stringshare_del(path);
*cfg.certificate_authorities = eina_list_remove_list(*cfg.certificate_authorities, n);
continue;
}
DBG("ssl_ctx=%p loaded certificate authorities '%s'", ctx, path);
}
if (certificate_authorities_count && !*cfg.certificate_authorities)
{
ERR("ssl_ctx=%p none of the required certificate authorities were loaded!", ctx);
return EINVAL;
}
if (!ctx->did_certificates)
{
if (!SSL_CTX_set_cipher_list(ctx->ssl_ctx, EFL_NET_SSL_CONTEXT_CIPHERS))
{
err_ssl = ERR_peek_error_line_data(&err_file, &err_line, &err_data, &err_flags);
_efl_net_ssl_ctx_check_errors();
ERR("ssl_ctx=%p Could not set ciphers '%s' [%s:%d%s%s '%s']",
ctx, EFL_NET_SSL_CONTEXT_CIPHERS,
err_file, err_line,
(err_flags & ERR_TXT_STRING) ? " " : "",
(err_flags & ERR_TXT_STRING) ? err_data : "",
ERR_reason_error_string(err_ssl));
return EINVAL;
}
}
return 0;
}
static void *
efl_net_ssl_ctx_connection_new(Efl_Net_Ssl_Ctx *ctx)
{
return SSL_new(ctx->ssl_ctx);
}
static Eina_Error
efl_net_ssl_ctx_setup(Efl_Net_Ssl_Ctx *ctx, Efl_Net_Ssl_Ctx_Config cfg)
{
Eina_Error err;
unsigned long options;
EINA_SAFETY_ON_TRUE_RETURN_VAL(ctx->ssl_ctx != NULL, EALREADY);
ctx->is_dialer = cfg.is_dialer;
if (ctx->is_dialer)
{
switch (cfg.cipher)
{
case EFL_NET_SSL_CIPHER_AUTO:
ctx->ssl_ctx = SSL_CTX_new(TLS_client_method());
break;
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
case EFL_NET_SSL_CIPHER_TLSV1:
ctx->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
break;
case EFL_NET_SSL_CIPHER_TLSV1_1:
ctx->ssl_ctx = SSL_CTX_new(TLSv1_1_client_method());
break;
case EFL_NET_SSL_CIPHER_TLSV1_2:
ctx->ssl_ctx = SSL_CTX_new(TLSv1_2_client_method());
break;
#else
case EFL_NET_SSL_CIPHER_TLSV1:
case EFL_NET_SSL_CIPHER_TLSV1_1:
case EFL_NET_SSL_CIPHER_TLSV1_2:
ctx->ssl_ctx = SSL_CTX_new(TLS_client_method());
break;
#endif
default:
ERR("ssl_ctx=%p unsupported cipher %d", ctx, cfg.cipher);
return EINVAL;
}
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx->ssl_ctx, ENOSYS);
}
else
{
switch (cfg.cipher)
{
case EFL_NET_SSL_CIPHER_AUTO:
ctx->ssl_ctx = SSL_CTX_new(TLS_server_method());
break;
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
case EFL_NET_SSL_CIPHER_TLSV1:
ctx->ssl_ctx = SSL_CTX_new(TLSv1_server_method());
break;
case EFL_NET_SSL_CIPHER_TLSV1_1:
ctx->ssl_ctx = SSL_CTX_new(TLSv1_1_server_method());
break;
case EFL_NET_SSL_CIPHER_TLSV1_2:
ctx->ssl_ctx = SSL_CTX_new(TLSv1_2_server_method());
break;
#else
case EFL_NET_SSL_CIPHER_TLSV1:
case EFL_NET_SSL_CIPHER_TLSV1_1:
case EFL_NET_SSL_CIPHER_TLSV1_2:
ctx->ssl_ctx = SSL_CTX_new(TLS_server_method());
break;
#endif
default:
ERR("ssl_ctx=%p unsupported cipher %d", ctx, cfg.cipher);
return EINVAL;
}
EINA_SAFETY_ON_NULL_RETURN_VAL(ctx->ssl_ctx, ENOSYS);
}
options = SSL_CTX_get_options(ctx->ssl_ctx);
options |= SSL_OP_NO_SSLv2;
options |= SSL_OP_SINGLE_DH_USE;
options |= SSL_OP_NO_SSLv3;
SSL_CTX_set_options(ctx->ssl_ctx, options);
err = _efl_net_ssl_ctx_load_lists(ctx, cfg);
if (err)
{
ERR("ssl_ctx=%p failed to load certificate, private keys, CRL or CA", ctx);
goto error;
}
return 0;
error:
SSL_CTX_free(ctx->ssl_ctx);
ctx->ssl_ctx = NULL;
return err;
}
static void
efl_net_ssl_ctx_teardown(Efl_Net_Ssl_Ctx *ctx)
{
if (ctx->ssl_ctx)
{
SSL_CTX_free(ctx->ssl_ctx);
ctx->ssl_ctx = NULL;
}
}
static Eina_Error
efl_net_ssl_ctx_verify_mode_set(Efl_Net_Ssl_Ctx *ctx, Efl_Net_Ssl_Verify_Mode verify_mode)
{
int ssl_mode;
switch (verify_mode)
{
case EFL_NET_SSL_VERIFY_MODE_NONE:
ssl_mode = SSL_VERIFY_NONE;
break;
case EFL_NET_SSL_VERIFY_MODE_OPTIONAL:
ssl_mode = SSL_VERIFY_PEER;
break;
case EFL_NET_SSL_VERIFY_MODE_REQUIRED:
ssl_mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
break;
default:
ERR("unknown verify_mode=%d", verify_mode);
return EINVAL;
}
SSL_CTX_set_verify(ctx->ssl_ctx, ssl_mode, SSL_CTX_get_verify_callback(ctx->ssl_ctx));
return 0;
}
static Eina_Error
efl_net_ssl_ctx_hostname_verify_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, Eina_Bool hostname_verify EINA_UNUSED)
{
return 0;
}
static Eina_Error
efl_net_ssl_ctx_hostname_set(Efl_Net_Ssl_Ctx *ctx EINA_UNUSED, const char *hostname EINA_UNUSED)
{
return 0;
}