From 73470f0b3c0b3dc8ecce6823a9d2d630ccca76bb Mon Sep 17 00:00:00 2001 From: Aaron Jones Date: Sat, 17 Sep 2016 17:03:21 +0000 Subject: [PATCH] OpenSSL: Forward-port release/3.5 improvements The changes made in this file are detailed in release/3.5 commits 1f30c8943bf2ca3 through 92c04c6b9dbdd11 inclusive. Some slight alterations are necessary; librb instead of libratbox, snprintf() instead of rb_snprintf(), etc. Also, release/4 has offline certificate digesting functionality. For more insight, simply diff the openssl.c between release/3.5 and release/4. --- librb/src/openssl.c | 1192 ++++++++++++++++-------------------- librb/src/openssl_ratbox.h | 102 +++ 2 files changed, 638 insertions(+), 656 deletions(-) create mode 100644 librb/src/openssl_ratbox.h diff --git a/librb/src/openssl.c b/librb/src/openssl.c index 62f731ad..7a7aeb8d 100644 --- a/librb/src/openssl.c +++ b/librb/src/openssl.c @@ -1,9 +1,10 @@ /* * librb: a library used by ircd-ratbox and other things - * openssl.c: openssl related code + * openssl.c: OpenSSL backend * * Copyright (C) 2007-2008 ircd-ratbox development team * Copyright (C) 2007-2008 Aaron Sethman + * Copyright (C) 2015-2016 Aaron Jones * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,270 +30,188 @@ #include #include -#include -#include -#include -#include -#include -#include + +#include "openssl_ratbox.h" + +typedef enum +{ + RB_FD_TLS_DIRECTION_IN = 0, + RB_FD_TLS_DIRECTION_OUT = 1 +} rb_fd_tls_direction; + +#define SSL_P(x) ((SSL *)((x)->ssl)) + + + +static SSL_CTX *ssl_ctx = NULL; + +struct ssl_connect +{ + CNCB *callback; + void *data; + int timeout; +}; + +static const char *rb_ssl_strerror(unsigned long); +static void rb_ssl_connect_realcb(rb_fde_t *, int, struct ssl_connect *); + + /* - * This is a mess but what can you do when the library authors - * refuse to play ball with established conventions? + * Internal OpenSSL-specific code */ -#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x20020002L) -# define LRB_HAVE_TLS_METHOD_API 1 -#else -# if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) -# define LRB_HAVE_TLS_METHOD_API 1 -# endif -#endif - -/* - * Use SSL_CTX_set_ecdh_auto() in OpenSSL 1.0.2 only - * Use SSL_CTX_set1_curves_list() in OpenSSL 1.0.2 and above - * TODO: Merge this into the block above if LibreSSL implements them - */ -#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10002000L) -# define LRB_HAVE_TLS_SET_CURVES 1 -# if (OPENSSL_VERSION_NUMBER < 0x10100000L) -# define LRB_HAVE_TLS_ECDH_AUTO 1 -# endif -#endif - -/* - * More LibreSSL compatibility mess - * Used in rb_get_ssl_info() below. - */ -#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) - /* OpenSSL 1.1.0+ */ -# define LRB_SSL_VTEXT_COMPILETIME OPENSSL_VERSION_TEXT -# define LRB_SSL_VTEXT_RUNTIME OpenSSL_version(OPENSSL_VERSION) -# define LRB_SSL_VNUM_COMPILETIME OPENSSL_VERSION_NUMBER -# define LRB_SSL_VNUM_RUNTIME OpenSSL_version_num() -# define LRB_SSL_FULL_VERSION_INFO 1 -#else -/* - * "Full version info" above means we have access to all 4 pieces of information. - * - * For the below, this is not the case; LibreSSL version number at runtime returns - * the wrong version number, and OpenSSL version text at compile time does not exist. - * Thus, we only reliably have version text at runtime, and version number at compile - * time. - */ -# if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x20200000L) - /* LibreSSL 2.2.0+ */ -# define LRB_SSL_VTEXT_RUNTIME SSLeay_version(SSLEAY_VERSION) -# define LRB_SSL_VNUM_COMPILETIME LIBRESSL_VERSION_NUMBER -# else - /* OpenSSL < 1.1.0 or LibreSSL < 2.2.0 */ -# define LRB_SSL_VTEXT_RUNTIME SSLeay_version(SSLEAY_VERSION) -# define LRB_SSL_VNUM_COMPILETIME SSLEAY_VERSION_NUMBER -# endif -#endif - -static SSL_CTX *ssl_server_ctx = NULL; -static SSL_CTX *ssl_client_ctx = NULL; -static int librb_index = -1; static unsigned long -get_last_err(void) +rb_ssl_last_err(void) { - unsigned long t_err, err = 0; - err = ERR_get_error(); - if(err == 0) - return 0; + unsigned long err_saved, err = 0; - while((t_err = ERR_get_error()) > 0) - err = t_err; + while((err_saved = ERR_get_error()) != 0) + err = err_saved; return err; } -void -rb_ssl_shutdown(rb_fde_t *F) +static void +rb_ssl_init_fd(rb_fde_t *const F, const rb_fd_tls_direction dir) { - int i; - if(F == NULL || F->ssl == NULL) + (void) rb_ssl_last_err(); + + F->ssl = SSL_new(ssl_ctx); + + if(F->ssl == NULL) + { + rb_lib_log("%s: SSL_new: %s", __func__, rb_ssl_strerror(rb_ssl_last_err())); + rb_close(F); return; - SSL_set_shutdown((SSL *) F->ssl, SSL_RECEIVED_SHUTDOWN); - - for(i = 0; i < 4; i++) - { - if(SSL_shutdown((SSL *) F->ssl)) - break; } - get_last_err(); - SSL_free((SSL *) F->ssl); -} -unsigned int -rb_ssl_handshake_count(rb_fde_t *F) -{ - return F->handshake_count; -} - -void -rb_ssl_clear_handshake_count(rb_fde_t *F) -{ - F->handshake_count = 0; -} - -static void -rb_ssl_timeout(rb_fde_t *F, void *notused) -{ - lrb_assert(F->accept != NULL); - F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data); -} - - -static void -rb_ssl_info_callback(SSL * ssl, int where, int ret) -{ - if(where & SSL_CB_HANDSHAKE_START) + switch(dir) + { + case RB_FD_TLS_DIRECTION_IN: + SSL_set_accept_state(SSL_P(F)); + break; + case RB_FD_TLS_DIRECTION_OUT: + SSL_set_connect_state(SSL_P(F)); + break; + } + + SSL_set_fd(SSL_P(F), rb_get_fd(F)); +} + +static void +rb_ssl_accept_common(rb_fde_t *const F, void *const data) +{ + lrb_assert(F != NULL); + lrb_assert(F->accept != NULL); + lrb_assert(F->accept->callback != NULL); + lrb_assert(F->ssl != NULL); + + (void) rb_ssl_last_err(); + + int ret = SSL_do_handshake(SSL_P(F)); + int err = SSL_get_error(SSL_P(F), ret); + + if(ret == 1) { - rb_fde_t *F = SSL_get_ex_data(ssl, librb_index); - if(F == NULL) - return; F->handshake_count++; + + rb_settimeout(F, 0, NULL, NULL); + rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL); + + struct acceptdata *const ad = F->accept; + F->accept = NULL; + ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data); + rb_free(ad); + + return; } + if(ret == -1 && err == SSL_ERROR_WANT_READ) + { + rb_setselect(F, RB_SELECT_READ, rb_ssl_accept_common, NULL); + return; + } + if(ret == -1 && err == SSL_ERROR_WANT_WRITE) + { + rb_setselect(F, RB_SELECT_WRITE, rb_ssl_accept_common, NULL); + return; + } + + errno = EIO; + F->ssl_errno = (unsigned long) err; + F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data); } static void -rb_setup_ssl_cb(rb_fde_t *F) +rb_ssl_connect_common(rb_fde_t *const F, void *const data) { - SSL_set_ex_data(F->ssl, librb_index, (char *)F); - SSL_set_info_callback((SSL *) F->ssl, (void (*)(const SSL *,int,int))rb_ssl_info_callback); -} + lrb_assert(F != NULL); + lrb_assert(F->ssl != NULL); -static void -rb_ssl_tryaccept(rb_fde_t *F, void *data) -{ - int ssl_err; - lrb_assert(F->accept != NULL); - int flags; - struct acceptdata *ad; + (void) rb_ssl_last_err(); - if(!SSL_is_init_finished((SSL *) F->ssl)) + int ret = SSL_do_handshake(SSL_P(F)); + int err = SSL_get_error(SSL_P(F), ret); + + if(ret == 1) { - if((ssl_err = SSL_accept((SSL *) F->ssl)) <= 0) - { - switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err)) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - if(ssl_err == SSL_ERROR_WANT_WRITE) - flags = RB_SELECT_WRITE; - else - flags = RB_SELECT_READ; - F->ssl_errno = get_last_err(); - rb_setselect(F, flags, rb_ssl_tryaccept, NULL); - break; - case SSL_ERROR_SYSCALL: - F->accept->callback(F, RB_ERROR, NULL, 0, F->accept->data); - break; - default: - F->ssl_errno = get_last_err(); - F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data); - break; - } - return; - } + F->handshake_count++; + + rb_settimeout(F, 0, NULL, NULL); + rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL); + + rb_ssl_connect_realcb(F, RB_OK, data); + + return; } - rb_settimeout(F, 0, NULL, NULL); - rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL); - - ad = F->accept; - F->accept = NULL; - ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data); - rb_free(ad); - -} - - -static void -rb_ssl_accept_common(rb_fde_t *new_F) -{ - int ssl_err; - if((ssl_err = SSL_accept((SSL *) new_F->ssl)) <= 0) + if(ret == -1 && err == SSL_ERROR_WANT_READ) { - switch (ssl_err = SSL_get_error((SSL *) new_F->ssl, ssl_err)) - { - case SSL_ERROR_SYSCALL: - if(rb_ignore_errno(errno)) - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - { - new_F->ssl_errno = get_last_err(); - rb_setselect(new_F, RB_SELECT_READ | RB_SELECT_WRITE, - rb_ssl_tryaccept, NULL); - return; - } - default: - new_F->ssl_errno = get_last_err(); - new_F->accept->callback(new_F, RB_ERROR_SSL, NULL, 0, new_F->accept->data); - return; - } + rb_setselect(F, RB_SELECT_READ, rb_ssl_connect_common, data); + return; } - else + if(ret == -1 && err == SSL_ERROR_WANT_WRITE) { - rb_ssl_tryaccept(new_F, NULL); + rb_setselect(F, RB_SELECT_WRITE, rb_ssl_connect_common, data); + return; } + + errno = EIO; + F->ssl_errno = (unsigned long) err; + rb_ssl_connect_realcb(F, RB_ERROR_SSL, data); } -void -rb_ssl_start_accepted(rb_fde_t *new_F, ACCB * cb, void *data, int timeout) +static const char * +rb_ssl_strerror(const unsigned long err) { - new_F->type |= RB_FD_SSL; - new_F->ssl = SSL_new(ssl_server_ctx); - new_F->accept = rb_malloc(sizeof(struct acceptdata)); + static char errbuf[512]; - new_F->accept->callback = cb; - new_F->accept->data = data; - rb_settimeout(new_F, timeout, rb_ssl_timeout, NULL); + ERR_error_string_n(err, errbuf, sizeof errbuf); - new_F->accept->addrlen = 0; - SSL_set_fd((SSL *) new_F->ssl, rb_get_fd(new_F)); - rb_setup_ssl_cb(new_F); - rb_ssl_accept_common(new_F); + return errbuf; } - - - -void -rb_ssl_accept_setup(rb_fde_t *F, rb_fde_t *new_F, struct sockaddr *st, int addrlen) +static int +verify_accept_all_cb(const int preverify_ok, X509_STORE_CTX *const x509_ctx) { - new_F->type |= RB_FD_SSL; - new_F->ssl = SSL_new(ssl_server_ctx); - new_F->accept = rb_malloc(sizeof(struct acceptdata)); - - new_F->accept->callback = F->accept->callback; - new_F->accept->data = F->accept->data; - rb_settimeout(new_F, 10, rb_ssl_timeout, NULL); - memcpy(&new_F->accept->S, st, addrlen); - new_F->accept->addrlen = addrlen; - - SSL_set_fd((SSL *) new_F->ssl, rb_get_fd(new_F)); - rb_setup_ssl_cb(new_F); - rb_ssl_accept_common(new_F); + return 1; } static ssize_t -rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size_t count) +rb_ssl_read_or_write(const int r_or_w, rb_fde_t *const F, void *const rbuf, const void *const wbuf, const size_t count) { ssize_t ret; unsigned long err; - SSL *ssl = F->ssl; + + (void) rb_ssl_last_err(); if(r_or_w == 0) - ret = (ssize_t) SSL_read(ssl, rbuf, (int)count); + ret = (ssize_t) SSL_read(SSL_P(F), rbuf, (int)count); else - ret = (ssize_t) SSL_write(ssl, wbuf, (int)count); + ret = (ssize_t) SSL_write(SSL_P(F), wbuf, (int)count); if(ret < 0) { - switch (SSL_get_error(ssl, ret)) + switch(SSL_get_error(SSL_P(F), ret)) { case SSL_ERROR_WANT_READ: errno = EAGAIN; @@ -303,7 +222,7 @@ rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size case SSL_ERROR_ZERO_RETURN: return 0; case SSL_ERROR_SYSCALL: - err = get_last_err(); + err = rb_ssl_last_err(); if(err == 0) { F->ssl_errno = 0; @@ -311,9 +230,10 @@ rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size } break; default: - err = get_last_err(); + err = rb_ssl_last_err(); break; } + F->ssl_errno = err; if(err > 0) { @@ -325,489 +245,288 @@ rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size return ret; } -ssize_t -rb_ssl_read(rb_fde_t *F, void *buf, size_t count) -{ - return rb_ssl_read_or_write(0, F, buf, NULL, count); -} - -ssize_t -rb_ssl_write(rb_fde_t *F, const void *buf, size_t count) -{ - return rb_ssl_read_or_write(1, F, NULL, buf, count); -} - static int -verify_accept_all_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) +make_certfp(X509 *const cert, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method) { - return 1; + unsigned int hashlen = 0; + const EVP_MD *md_type = NULL; + const ASN1_ITEM *item = NULL; + void *data = NULL; + + switch(method) + { + case RB_SSL_CERTFP_METH_CERT_SHA1: + hashlen = RB_SSL_CERTFP_LEN_SHA1; + md_type = EVP_sha1(); + item = ASN1_ITEM_rptr(X509); + data = cert; + break; + case RB_SSL_CERTFP_METH_CERT_SHA256: + hashlen = RB_SSL_CERTFP_LEN_SHA256; + md_type = EVP_sha256(); + item = ASN1_ITEM_rptr(X509); + data = cert; + break; + case RB_SSL_CERTFP_METH_CERT_SHA512: + hashlen = RB_SSL_CERTFP_LEN_SHA512; + md_type = EVP_sha512(); + item = ASN1_ITEM_rptr(X509); + data = cert; + break; + case RB_SSL_CERTFP_METH_SPKI_SHA256: + hashlen = RB_SSL_CERTFP_LEN_SHA256; + md_type = EVP_sha256(); + item = ASN1_ITEM_rptr(X509_PUBKEY); + data = X509_get_X509_PUBKEY(cert); + break; + case RB_SSL_CERTFP_METH_SPKI_SHA512: + hashlen = RB_SSL_CERTFP_LEN_SHA512; + md_type = EVP_sha512(); + item = ASN1_ITEM_rptr(X509_PUBKEY); + data = X509_get_X509_PUBKEY(cert); + break; + default: + return 0; + } + + if(ASN1_item_digest(item, md_type, data, certfp, &hashlen) != 1) + return 0; + + return (int) hashlen; } -static const char * -get_ssl_error(unsigned long err) -{ - static char buf[512]; - ERR_error_string_n(err, buf, sizeof buf); - return buf; + +/* + * External OpenSSL-specific code + */ + +void +rb_ssl_shutdown(rb_fde_t *const F) +{ + if(F == NULL || F->ssl == NULL) + return; + + (void) rb_ssl_last_err(); + + for(int i = 0; i < 4; i++) + { + int ret = SSL_shutdown(SSL_P(F)); + int err = SSL_get_error(SSL_P(F), ret); + + if(ret >= 0 || (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)) + break; + } + + SSL_free(SSL_P(F)); + F->ssl = NULL; } int rb_init_ssl(void) { - char librb_data[] = "librb data"; - - /* - * OpenSSL 1.1.0 and above automatically initialises itself with sane defaults - */ - #if defined(LIBRESSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x10100000L) - SSL_library_init(); +#ifndef LRB_SSL_NO_EXPLICIT_INIT + (void) SSL_library_init(); SSL_load_error_strings(); - #endif - - librb_index = SSL_get_ex_new_index(0, librb_data, NULL, NULL, NULL); +#endif + rb_lib_log("%s: OpenSSL backend initialised", __func__); return 1; } int -rb_setup_ssl_server(const char *certfile, const char *keyfile, const char *dhfile, const char *cipher_list) +rb_setup_ssl_server(const char *const certfile, const char *keyfile, + const char *const dhfile, const char *cipherlist) { - const char librb_ciphers[] = "kEECDH+HIGH:kEDH+HIGH:HIGH:!aNULL"; - - SSL_CTX *ssl_server_ctx_new; - SSL_CTX *ssl_client_ctx_new; - - #ifdef LRB_HAVE_TLS_SET_CURVES - const char librb_curves[] = "P-521:P-384:P-256"; - #endif - if(certfile == NULL) { - rb_lib_log("rb_setup_ssl_server: No certificate file"); + rb_lib_log("%s: no certificate file specified", __func__); return 0; } if(keyfile == NULL) keyfile = certfile; - if(cipher_list == NULL) - cipher_list = librb_ciphers; + if(cipherlist == NULL) + cipherlist = rb_default_ciphers; + + + (void) rb_ssl_last_err(); #ifdef LRB_HAVE_TLS_METHOD_API - if((ssl_server_ctx_new = SSL_CTX_new(TLS_server_method())) == NULL) + SSL_CTX *const ssl_ctx_new = SSL_CTX_new(TLS_method()); #else - if((ssl_server_ctx_new = SSL_CTX_new(SSLv23_server_method())) == NULL) + SSL_CTX *const ssl_ctx_new = SSL_CTX_new(SSLv23_method()); #endif + + if(ssl_ctx_new == NULL) { - rb_lib_log("rb_init_openssl: Unable to initialize OpenSSL server context: %s", - get_ssl_error(ERR_get_error())); + rb_lib_log("%s: SSL_CTX_new: %s", __func__, rb_ssl_strerror(rb_ssl_last_err())); return 0; } - #ifdef LRB_HAVE_TLS_METHOD_API - if((ssl_client_ctx_new = SSL_CTX_new(TLS_client_method())) == NULL) - #else - if((ssl_client_ctx_new = SSL_CTX_new(SSLv23_client_method())) == NULL) - #endif + if(SSL_CTX_use_certificate_chain_file(ssl_ctx_new, certfile) != 1) { - rb_lib_log("rb_init_openssl: Unable to initialize OpenSSL client context: %s", - get_ssl_error(ERR_get_error())); + rb_lib_log("%s: SSL_CTX_use_certificate_chain_file ('%s'): %s", __func__, certfile, + rb_ssl_strerror(rb_ssl_last_err())); - SSL_CTX_free(ssl_server_ctx_new); + SSL_CTX_free(ssl_ctx_new); return 0; } - #ifndef LRB_HAVE_TLS_METHOD_API - SSL_CTX_set_options(ssl_server_ctx_new, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); - SSL_CTX_set_options(ssl_client_ctx_new, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); - #endif - - #ifdef SSL_OP_SINGLE_DH_USE - SSL_CTX_set_options(ssl_server_ctx_new, SSL_OP_SINGLE_DH_USE); - #endif - - #ifdef SSL_OP_SINGLE_ECDH_USE - SSL_CTX_set_options(ssl_server_ctx_new, SSL_OP_SINGLE_ECDH_USE); - #endif - - #ifdef SSL_OP_NO_TICKET - SSL_CTX_set_options(ssl_server_ctx_new, SSL_OP_NO_TICKET); - SSL_CTX_set_options(ssl_client_ctx_new, SSL_OP_NO_TICKET); - #endif - - #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE - SSL_CTX_set_options(ssl_server_ctx_new, SSL_OP_CIPHER_SERVER_PREFERENCE); - #endif - - SSL_CTX_set_verify(ssl_server_ctx_new, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_accept_all_cb); - SSL_CTX_set_session_cache_mode(ssl_server_ctx_new, SSL_SESS_CACHE_OFF); - - #ifdef LRB_HAVE_TLS_SET_CURVES - SSL_CTX_set1_curves_list(ssl_server_ctx_new, librb_curves); - #endif - - #ifdef LRB_HAVE_TLS_ECDH_AUTO - SSL_CTX_set_ecdh_auto(ssl_server_ctx_new, 1); - #endif - - /* - * Set manual ECDHE curve on OpenSSL 1.0.0 & 1.0.1, but make sure it's actually available - */ - #if (OPENSSL_VERSION_NUMBER >= 0x10000000L) && (OPENSSL_VERSION_NUMBER < 0x10002000L) && !defined(OPENSSL_NO_ECDH) - EC_KEY *key = EC_KEY_new_by_curve_name(NID_secp384r1); - if (key) { - SSL_CTX_set_tmp_ecdh(ssl_server_ctx_new, key); - EC_KEY_free(key); - } - #endif - - SSL_CTX_set_cipher_list(ssl_server_ctx_new, cipher_list); - SSL_CTX_set_cipher_list(ssl_client_ctx_new, cipher_list); - - if(!SSL_CTX_use_certificate_chain_file(ssl_server_ctx_new, certfile) || !SSL_CTX_use_certificate_chain_file(ssl_client_ctx_new, certfile)) + if(SSL_CTX_use_PrivateKey_file(ssl_ctx_new, keyfile, SSL_FILETYPE_PEM) != 1) { - rb_lib_log("rb_setup_ssl_server: Error loading certificate file [%s]: %s", certfile, - get_ssl_error(ERR_get_error())); + rb_lib_log("%s: SSL_CTX_use_PrivateKey_file ('%s'): %s", __func__, keyfile, + rb_ssl_strerror(rb_ssl_last_err())); - SSL_CTX_free(ssl_server_ctx_new); - SSL_CTX_free(ssl_client_ctx_new); + SSL_CTX_free(ssl_ctx_new); return 0; } - if(!SSL_CTX_use_PrivateKey_file(ssl_server_ctx_new, keyfile, SSL_FILETYPE_PEM) || !SSL_CTX_use_PrivateKey_file(ssl_client_ctx_new, keyfile, SSL_FILETYPE_PEM)) + if(dhfile == NULL) { - rb_lib_log("rb_setup_ssl_server: Error loading keyfile [%s]: %s", keyfile, - get_ssl_error(ERR_get_error())); - - SSL_CTX_free(ssl_server_ctx_new); - SSL_CTX_free(ssl_client_ctx_new); - return 0; + rb_lib_log("%s: no DH parameters file specified", __func__); } - - if(dhfile != NULL) + else { - /* DH parameters aren't necessary, but they are nice..if they didn't pass one..that is their problem */ - FILE *fp = fopen(dhfile, "r"); - DH *dh = NULL; + FILE *const dhf = fopen(dhfile, "r"); + DH *dhp = NULL; - if(fp == NULL) + if(dhf == NULL) { - rb_lib_log("rb_setup_ssl_server: Error loading DH params file [%s]: %s", - dhfile, strerror(errno)); + rb_lib_log("%s: fopen ('%s'): %s", __func__, dhfile, strerror(errno)); } - else if(PEM_read_DHparams(fp, &dh, NULL, NULL) == NULL) + else if(PEM_read_DHparams(dhf, &dhp, NULL, NULL) == NULL) { - rb_lib_log("rb_setup_ssl_server: Error loading DH params file [%s]: %s", - dhfile, get_ssl_error(ERR_get_error())); - fclose(fp); + rb_lib_log("%s: PEM_read_DHparams ('%s'): %s", __func__, dhfile, + rb_ssl_strerror(rb_ssl_last_err())); + fclose(dhf); } else { - SSL_CTX_set_tmp_dh(ssl_server_ctx_new, dh); - DH_free(dh); - fclose(fp); + SSL_CTX_set_tmp_dh(ssl_ctx_new, dhp); + DH_free(dhp); + fclose(dhf); } } - if (ssl_server_ctx) - SSL_CTX_free(ssl_server_ctx); + if(SSL_CTX_set_cipher_list(ssl_ctx_new, cipherlist) != 1) + { + rb_lib_log("%s: SSL_CTX_set_cipher_list: could not configure any ciphers", __func__); + SSL_CTX_free(ssl_ctx_new); + return 0; + } - if (ssl_client_ctx) - SSL_CTX_free(ssl_client_ctx); + SSL_CTX_set_session_cache_mode(ssl_ctx_new, SSL_SESS_CACHE_OFF); + SSL_CTX_set_verify(ssl_ctx_new, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_accept_all_cb); - ssl_server_ctx = ssl_server_ctx_new; - ssl_client_ctx = ssl_client_ctx_new; + #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + (void) SSL_CTX_clear_options(ssl_ctx_new, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); + #endif + #ifndef LRB_HAVE_TLS_METHOD_API + (void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + #endif + + #ifdef SSL_OP_NO_TICKET + (void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_NO_TICKET); + #endif + + #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE + (void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_CIPHER_SERVER_PREFERENCE); + #endif + + #ifdef SSL_OP_SINGLE_DH_USE + (void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_SINGLE_DH_USE); + #endif + + #ifdef SSL_OP_SINGLE_ECDH_USE + (void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_SINGLE_ECDH_USE); + #endif + + #ifdef LRB_HAVE_TLS_ECDH_AUTO + (void) SSL_CTX_set_ecdh_auto(ssl_ctx_new, 1); + #endif + + #ifdef LRB_HAVE_TLS_SET_CURVES + (void) SSL_CTX_set1_curves_list(ssl_ctx_new, rb_default_curves); + #else + # if (OPENSSL_VERSION_NUMBER >= 0x10000000L) && !defined(OPENSSL_NO_ECDH) && defined(NID_secp384r1) + EC_KEY *const ec_key = EC_KEY_new_by_curve_name(NID_secp384r1); + if(ec_key != NULL) + { + SSL_CTX_set_tmp_ecdh(ssl_ctx_new, ec_key); + EC_KEY_free(ec_key); + } + else + rb_lib_log("%s: EC_KEY_new_by_curve_name failed; will not enable ECDHE- ciphers", __func__); + # else + rb_lib_log("%s: OpenSSL built without ECDH support; will not enable ECDHE- ciphers", __func__); + # endif + #endif + + + if(ssl_ctx) + SSL_CTX_free(ssl_ctx); + + ssl_ctx = ssl_ctx_new; + + + rb_lib_log("%s: TLS configuration successful", __func__); return 1; } int -rb_ssl_listen(rb_fde_t *F, int backlog, int defer_accept) +rb_init_prng(const char *const path, prng_seed_t seed_type) { - int result; + (void) rb_ssl_last_err(); - result = rb_listen(F, backlog, defer_accept); - F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL; + if(seed_type == RB_PRNG_FILE && RAND_load_file(path, -1) < 0) + rb_lib_log("%s: RAND_load_file: %s", __func__, rb_ssl_strerror(rb_ssl_last_err())); - return result; -} - -struct ssl_connect -{ - CNCB *callback; - void *data; - int timeout; -}; - -static void -rb_ssl_connect_realcb(rb_fde_t *F, int status, struct ssl_connect *sconn) -{ - F->connect->callback = sconn->callback; - F->connect->data = sconn->data; - rb_free(sconn); - rb_connect_callback(F, status); -} - -static void -rb_ssl_tryconn_timeout_cb(rb_fde_t *F, void *data) -{ - rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data); -} - -static void -rb_ssl_tryconn_cb(rb_fde_t *F, void *data) -{ - struct ssl_connect *sconn = data; - int ssl_err; - if(!SSL_is_init_finished((SSL *) F->ssl)) + if(RAND_status() != 1) { - if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0) - { - switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err)) - { - case SSL_ERROR_SYSCALL: - if(rb_ignore_errno(errno)) - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - { - F->ssl_errno = get_last_err(); - rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, - rb_ssl_tryconn_cb, sconn); - return; - } - default: - F->ssl_errno = get_last_err(); - rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn); - return; - } - } - else - { - rb_ssl_connect_realcb(F, RB_OK, sconn); - } - } -} - -static void -rb_ssl_tryconn(rb_fde_t *F, int status, void *data) -{ - struct ssl_connect *sconn = data; - int ssl_err; - if(status != RB_OK) - { - rb_ssl_connect_realcb(F, status, sconn); - return; + rb_lib_log("%s: RAND_status: %s", __func__, rb_ssl_strerror(rb_ssl_last_err())); + return 0; } - F->type |= RB_FD_SSL; - F->ssl = SSL_new(ssl_client_ctx); - SSL_set_fd((SSL *) F->ssl, F->fd); - rb_setup_ssl_cb(F); - rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); - if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0) - { - switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err)) - { - case SSL_ERROR_SYSCALL: - if(rb_ignore_errno(errno)) - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - { - F->ssl_errno = get_last_err(); - rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, - rb_ssl_tryconn_cb, sconn); - return; - } - default: - F->ssl_errno = get_last_err(); - rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn); - return; - } - } - else - { - rb_ssl_connect_realcb(F, RB_OK, sconn); - } -} - -void -rb_connect_tcp_ssl(rb_fde_t *F, struct sockaddr *dest, - struct sockaddr *clocal, CNCB * callback, void *data, int timeout) -{ - struct ssl_connect *sconn; - if(F == NULL) - return; - - sconn = rb_malloc(sizeof(struct ssl_connect)); - sconn->data = data; - sconn->callback = callback; - sconn->timeout = timeout; - rb_connect_tcp(F, dest, clocal, rb_ssl_tryconn, sconn, timeout); -} - -void -rb_ssl_start_connected(rb_fde_t *F, CNCB * callback, void *data, int timeout) -{ - struct ssl_connect *sconn; - int ssl_err; - if(F == NULL) - return; - - sconn = rb_malloc(sizeof(struct ssl_connect)); - sconn->data = data; - sconn->callback = callback; - sconn->timeout = timeout; - F->connect = rb_malloc(sizeof(struct conndata)); - F->connect->callback = callback; - F->connect->data = data; - F->type |= RB_FD_SSL; - F->ssl = SSL_new(ssl_client_ctx); - - SSL_set_fd((SSL *) F->ssl, F->fd); - rb_setup_ssl_cb(F); - rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); - if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0) - { - switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err)) - { - case SSL_ERROR_SYSCALL: - if(rb_ignore_errno(errno)) - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - { - F->ssl_errno = get_last_err(); - rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, - rb_ssl_tryconn_cb, sconn); - return; - } - default: - F->ssl_errno = get_last_err(); - rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn); - return; - } - } - else - { - rb_ssl_connect_realcb(F, RB_OK, sconn); - } + rb_lib_log("%s: PRNG initialised", __func__); + return 1; } int -rb_init_prng(const char *path, prng_seed_t seed_type) +rb_get_random(void *const buf, const size_t length) { - if(seed_type == RB_PRNG_DEFAULT) - { -#ifdef _WIN32 - RAND_screen(); -#endif - return RAND_status(); - } - if(path == NULL) - return RAND_status(); + (void) rb_ssl_last_err(); - switch (seed_type) + if(RAND_bytes(buf, (int) length) != 1) { - case RB_PRNG_FILE: - if(RAND_load_file(path, -1) == -1) - return -1; - break; -#ifdef _WIN32 - case RB_PRNGWIN32: - RAND_screen(); - break; -#endif - default: - return -1; + rb_lib_log("%s: RAND_bytes: %s", __func__, rb_ssl_strerror(rb_ssl_last_err())); + return 0; } - return RAND_status(); -} - -int -rb_get_random(void *buf, size_t length) -{ - int ret; - - if((ret = RAND_bytes(buf, length)) == 0) - { - /* remove the error from the queue */ - ERR_get_error(); - } - return ret; + return 1; } const char * -rb_get_ssl_strerror(rb_fde_t *F) +rb_get_ssl_strerror(rb_fde_t *const F) { - return get_ssl_error(F->ssl_errno); -} - -static int -make_certfp(X509 *cert, uint8_t certfp[RB_SSL_CERTFP_LEN], int method) -{ - const ASN1_ITEM *it; - const EVP_MD *evp; - void *data; - unsigned int len; - - switch(method) - { - case RB_SSL_CERTFP_METH_CERT_SHA1: - it = ASN1_ITEM_rptr(X509); - evp = EVP_sha1(); - data = cert; - len = RB_SSL_CERTFP_LEN_SHA1; - break; - case RB_SSL_CERTFP_METH_CERT_SHA256: - it = ASN1_ITEM_rptr(X509); - evp = EVP_sha256(); - data = cert; - len = RB_SSL_CERTFP_LEN_SHA256; - break; - case RB_SSL_CERTFP_METH_CERT_SHA512: - it = ASN1_ITEM_rptr(X509); - evp = EVP_sha512(); - data = cert; - len = RB_SSL_CERTFP_LEN_SHA512; - break; - case RB_SSL_CERTFP_METH_SPKI_SHA256: - it = ASN1_ITEM_rptr(X509_PUBKEY); - evp = EVP_sha256(); - data = X509_get_X509_PUBKEY(cert); - len = RB_SSL_CERTFP_LEN_SHA256; - break; - case RB_SSL_CERTFP_METH_SPKI_SHA512: - it = ASN1_ITEM_rptr(X509_PUBKEY); - evp = EVP_sha512(); - data = X509_get_X509_PUBKEY(cert); - len = RB_SSL_CERTFP_LEN_SHA512; - break; - default: - return 0; - } - - if (ASN1_item_digest(it, evp, data, certfp, &len) != 1) - len = 0; - return (int) len; + return rb_ssl_strerror(F->ssl_errno); } int -rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method) +rb_get_ssl_certfp(rb_fde_t *const F, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method) { + if(F->ssl == NULL) + return 0; + + X509 *const peer_cert = SSL_get_peer_certificate(SSL_P(F)); + if(peer_cert == NULL) + return 0; + int len = 0; - X509 *cert; - int res; - if (F->ssl == NULL) - return 0; - - cert = SSL_get_peer_certificate((SSL *) F->ssl); - if(cert == NULL) - return 0; - - res = SSL_get_verify_result((SSL *) F->ssl); - switch(res) + switch(SSL_get_verify_result(SSL_P(F))) { case X509_V_OK: case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: @@ -815,73 +534,234 @@ rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method) case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_CERT_UNTRUSTED: - len = make_certfp(cert, certfp, method); - - default: /* to silence code inspectors */ - break; + len = make_certfp(peer_cert, certfp, method); + default: + X509_free(peer_cert); + return len; } - - X509_free(cert); - return len; } int -rb_get_ssl_certfp_file(const char *filename, uint8_t certfp[RB_SSL_CERTFP_LEN], int method) +rb_get_ssl_certfp_file(const char *const filename, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method) { - X509 *cert; - FILE *f = fopen(filename, "r"); - - if (!f) + FILE *const fp = fopen(filename, "r"); + if (fp == NULL) return -1; - cert = PEM_read_X509(f, NULL, NULL, NULL); - fclose(f); - - if (cert) { - unsigned int len = make_certfp(cert, certfp, method); - X509_free(cert); - return len; + X509 *const cert = PEM_read_X509(fp, NULL, NULL, NULL); + if (cert == NULL) + { + fclose(fp); + return 0; } - return 0; + + int len = make_certfp(cert, certfp, method); + + X509_free(cert); + fclose(fp); + + return len; } +void +rb_get_ssl_info(char *const buf, const size_t len) +{ +#ifdef LRB_SSL_FULL_VERSION_INFO + if(LRB_SSL_VNUM_RUNTIME == LRB_SSL_VNUM_COMPILETIME) + (void) snprintf(buf, len, "OpenSSL: compiled 0x%lx, library %s", + LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_COMPILETIME); + else + (void) snprintf(buf, len, "OpenSSL: compiled (0x%lx, %s), library (0x%lx, %s)", + LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_COMPILETIME, + LRB_SSL_VNUM_RUNTIME, LRB_SSL_VTEXT_RUNTIME); +#else + (void) snprintf(buf, len, "OpenSSL: compiled 0x%lx, library %s", + LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_RUNTIME); +#endif +} + +const char * +rb_ssl_get_cipher(rb_fde_t *const F) +{ + if(F == NULL || F->ssl == NULL) + return NULL; + + static char buf[512]; + + const char *const version = SSL_get_version(SSL_P(F)); + const char *const cipher = SSL_get_cipher_name(SSL_P(F)); + + (void) snprintf(buf, sizeof buf, "%s, %s", version, cipher); + + return buf; +} + +ssize_t +rb_ssl_read(rb_fde_t *const F, void *const buf, const size_t count) +{ + return rb_ssl_read_or_write(0, F, buf, NULL, count); +} + +ssize_t +rb_ssl_write(rb_fde_t *const F, const void *const buf, const size_t count) +{ + return rb_ssl_read_or_write(1, F, NULL, buf, count); +} + + + +/* + * Internal library-agnostic code + */ + +static void +rb_ssl_connect_realcb(rb_fde_t *const F, const int status, struct ssl_connect *const sconn) +{ + lrb_assert(F->connect != NULL); + + F->connect->callback = sconn->callback; + F->connect->data = sconn->data; + + rb_connect_callback(F, status); + rb_free(sconn); +} + +static void +rb_ssl_timeout_cb(rb_fde_t *const F, void *const data) +{ + lrb_assert(F->accept != NULL); + lrb_assert(F->accept->callback != NULL); + + F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data); +} + +static void +rb_ssl_tryconn_timeout_cb(rb_fde_t *const F, void *const data) +{ + rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data); +} + +static void +rb_ssl_tryconn(rb_fde_t *const F, const int status, void *const data) +{ + lrb_assert(F != NULL); + + struct ssl_connect *const sconn = data; + + if(status != RB_OK) + { + rb_ssl_connect_realcb(F, status, sconn); + return; + } + + F->type |= RB_FD_SSL; + + rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); + rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT); + rb_ssl_connect_common(F, sconn); +} + + + +/* + * External library-agnostic code + */ + int rb_supports_ssl(void) { return 1; } -void -rb_get_ssl_info(char *buf, size_t len) +unsigned int +rb_ssl_handshake_count(rb_fde_t *const F) { -#ifdef LRB_SSL_FULL_VERSION_INFO - if (LRB_SSL_VNUM_RUNTIME == LRB_SSL_VNUM_COMPILETIME) - snprintf(buf, len, "OpenSSL: compiled 0x%lx, library %s", - LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_COMPILETIME); - else - snprintf(buf, len, "OpenSSL: compiled (0x%lx, %s), library (0x%lx, %s)", - LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_COMPILETIME, - LRB_SSL_VNUM_RUNTIME, LRB_SSL_VTEXT_RUNTIME); -#else - snprintf(buf, len, "OpenSSL: compiled 0x%lx, library %s", - LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_RUNTIME); -#endif + return F->handshake_count; } -const char * -rb_ssl_get_cipher(rb_fde_t *F) +void +rb_ssl_clear_handshake_count(rb_fde_t *const F) { - if(F == NULL || F->ssl == NULL) - return NULL; + F->handshake_count = 0; +} - static char buf[512]; +void +rb_ssl_start_accepted(rb_fde_t *const F, ACCB *const cb, void *const data, const int timeout) +{ + F->type |= RB_FD_SSL; - const char *version = SSL_get_version(F->ssl); - const char *cipher = SSL_get_cipher_name(F->ssl); + F->accept = rb_malloc(sizeof(struct acceptdata)); + F->accept->callback = cb; + F->accept->data = data; + F->accept->addrlen = 0; + (void) memset(&F->accept->S, 0x00, sizeof F->accept->S); - (void) snprintf(buf, sizeof buf, "%s, %s", version, cipher); + rb_settimeout(F, timeout, rb_ssl_timeout_cb, NULL); + rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_IN); + rb_ssl_accept_common(F, NULL); +} - return buf; +void +rb_ssl_accept_setup(rb_fde_t *const srv_F, rb_fde_t *const cli_F, struct sockaddr *const st, const int addrlen) +{ + cli_F->type |= RB_FD_SSL; + + cli_F->accept = rb_malloc(sizeof(struct acceptdata)); + cli_F->accept->callback = srv_F->accept->callback; + cli_F->accept->data = srv_F->accept->data; + cli_F->accept->addrlen = (rb_socklen_t) addrlen; + (void) memset(&cli_F->accept->S, 0x00, sizeof cli_F->accept->S); + (void) memcpy(&cli_F->accept->S, st, (size_t) addrlen); + + rb_settimeout(cli_F, 10, rb_ssl_timeout_cb, NULL); + rb_ssl_init_fd(cli_F, RB_FD_TLS_DIRECTION_IN); + rb_ssl_accept_common(cli_F, NULL); +} + +int +rb_ssl_listen(rb_fde_t *const F, const int backlog, const int defer_accept) +{ + int result = rb_listen(F, backlog, defer_accept); + + F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL; + + return result; +} + +void +rb_connect_tcp_ssl(rb_fde_t *const F, struct sockaddr *const dest, struct sockaddr *const clocal, + CNCB *const callback, void *const data, const int timeout) +{ + if(F == NULL) + return; + + struct ssl_connect *const sconn = rb_malloc(sizeof *sconn); + sconn->data = data; + sconn->callback = callback; + sconn->timeout = timeout; + + rb_connect_tcp(F, dest, clocal, rb_ssl_tryconn, sconn, timeout); +} + +void +rb_ssl_start_connected(rb_fde_t *const F, CNCB *const callback, void *const data, const int timeout) +{ + if(F == NULL) + return; + + struct ssl_connect *const sconn = rb_malloc(sizeof *sconn); + sconn->data = data; + sconn->callback = callback; + sconn->timeout = timeout; + + F->connect = rb_malloc(sizeof(struct conndata)); + F->connect->callback = callback; + F->connect->data = data; + F->type |= RB_FD_SSL; + + rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); + rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT); + rb_ssl_connect_common(F, sconn); } #endif /* HAVE_OPENSSL */ diff --git a/librb/src/openssl_ratbox.h b/librb/src/openssl_ratbox.h new file mode 100644 index 00000000..b2d033c9 --- /dev/null +++ b/librb/src/openssl_ratbox.h @@ -0,0 +1,102 @@ +/* + * libratbox: a library used by ircd-ratbox and other things + * openssl_ratbox.h: OpenSSL backend data + * + * Copyright (C) 2015-2016 Aaron Jones + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + */ + +#ifndef LRB_OPENSSL_H_INC +#define LRB_OPENSSL_H_INC 1 + +#include +#include +#include +#include +#include +#include + +#include + +/* + * A long time ago, in a world far away, OpenSSL had a well-established mechanism for ensuring compatibility with + * regards to added, changed, and removed functions, by having an SSLEAY_VERSION_NUMBER macro. This was then + * renamed to OPENSSL_VERSION_NUMBER, but the old macro was kept around for compatibility until OpenSSL version + * 1.1.0. + * + * Then the OpenBSD developers decided that having OpenSSL in their codebase was a bad idea. They forked it to + * create LibreSSL, gutted all of the functionality they didn't want or need, and generally improved the library + * a lot. Then, as the OpenBSD developers are want to do, they packaged up LibreSSL for release to other + * operating systems, as LibreSSL Portable. Think along the lines of OpenSSH where they have also done this. + * + * The fun part of this story ends there. LibreSSL has an OPENSSL_VERSION_NUMBER macro, but they have set it to a + * stupidly high value, version 2.0. OpenSSL version 2.0 does not exist, and LibreSSL 2.2 does not implement + * everything OpenSSL 1.0.2 or 1.1.0 do. This completely breaks the entire purpose of the macro. + * + * The ifdef soup below is for LibreSSL compatibility. Please find whoever thought setting OPENSSL_VERSION_NUMBER + * to a version that does not exist was a good idea. Encourage them to realise that it is not. -- amdj + */ + +#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) +# define LRB_SSL_NO_EXPLICIT_INIT 1 +#endif + +#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10002000L) +# define LRB_HAVE_TLS_SET_CURVES 1 +# if (OPENSSL_VERSION_NUMBER < 0x10100000L) +# define LRB_HAVE_TLS_ECDH_AUTO 1 +# endif +#endif + +#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x20020002L) +# define LRB_HAVE_TLS_METHOD_API 1 +#else +# if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) +# define LRB_HAVE_TLS_METHOD_API 1 +# endif +#endif + +#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) +# define LRB_SSL_VTEXT_COMPILETIME OPENSSL_VERSION_TEXT +# define LRB_SSL_VTEXT_RUNTIME OpenSSL_version(OPENSSL_VERSION) +# define LRB_SSL_VNUM_COMPILETIME OPENSSL_VERSION_NUMBER +# define LRB_SSL_VNUM_RUNTIME OpenSSL_version_num() +# define LRB_SSL_FULL_VERSION_INFO 1 +#else +# if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x20200000L) +# define LRB_SSL_VTEXT_RUNTIME SSLeay_version(SSLEAY_VERSION) +# define LRB_SSL_VNUM_COMPILETIME LIBRESSL_VERSION_NUMBER +# else +# define LRB_SSL_VTEXT_RUNTIME SSLeay_version(SSLEAY_VERSION) +# define LRB_SSL_VNUM_COMPILETIME SSLEAY_VERSION_NUMBER +# endif +#endif + + + +/* + * Default supported ciphersuites (if the user does not provide any) and curves (OpenSSL 1.0.2+) + * Hardcoded secp384r1 (P-384) is used on OpenSSL 1.0.0 and 1.0.1 (if available). + */ + +static const char rb_default_ciphers[] = "kEECDH+HIGH:kEDH+HIGH:HIGH:!aNULL"; +#ifdef LRB_HAVE_TLS_SET_CURVES +static const char rb_default_curves[] = "P-521:P-384:P-256"; +#endif + +#endif /* LRB_OPENSSL_H_INC */