#include #include #include 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__, __FUNCTION__) 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; }