summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Strauss <gstrauss@gluelogic.com>2020-09-09 02:52:34 -0400
committerGlenn Strauss <gstrauss@gluelogic.com>2020-10-11 12:19:26 -0400
commitc3a85c9bf56d8e3d3fc83a5fdb19c22bbf15ab61 (patch)
treeebcec04aa1bae4571f99edd957e7eab55023ad0d
parent1d9709b8abb342b0978a215825cfc78cd2365a11 (diff)
downloadlighttpd-git-c3a85c9bf56d8e3d3fc83a5fdb19c22bbf15ab61.tar.gz
[mod_wolfssl] standalone module
standalone module forked from mod_openssl
-rw-r--r--configure.ac50
-rw-r--r--src/CMakeLists.txt25
-rw-r--r--src/Makefile.am35
-rw-r--r--src/SConscript2
-rw-r--r--src/meson.build2
-rw-r--r--src/mod_openssl.c791
-rw-r--r--src/mod_wolfssl.c3447
-rw-r--r--src/rand.c10
-rw-r--r--src/sys-crypto-md.h8
-rw-r--r--src/sys-crypto.h2
10 files changed, 3552 insertions, 820 deletions
diff --git a/configure.ac b/configure.ac
index 0846e4bb..81e5ffd4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -640,6 +640,7 @@ AC_ARG_WITH([openssl],
[WITH_OPENSSL=no]
)
AC_MSG_RESULT([$WITH_OPENSSL])
+AM_CONDITIONAL([BUILD_WITH_OPENSSL], test ! "$WITH_OPENSSL" = no)
if test "$WITH_OPENSSL" != no; then
if test "$WITH_OPENSSL" != yes; then
@@ -690,13 +691,15 @@ if test "$WITH_OPENSSL" != no; then
[AC_MSG_ERROR([openssl crypto library not found. install it or build without --with-openssl])]
)
AC_CHECK_LIB([ssl], [SSL_new],
- [SSL_LIB="-lssl -lcrypto"],
+ [OPENSSL_LIBS="${openssl_append_LDFLAGS} -lssl -lcrypto"],
[AC_MSG_ERROR([openssl ssl library not found. install it or build without --with-openssl])],
[ -lcrypto "$DL_LIB" ]
)
AC_DEFINE([HAVE_LIBSSL], [1], [Have libssl])
- AC_SUBST([SSL_LIB])
+ OPENSSL_CFLAGS="${openssl_append_CPPFLAGS}"
+ AC_SUBST([OPENSSL_CFLAGS])
+ AC_SUBST([OPENSSL_LIBS])
AC_SUBST([CRYPTO_LIB])
fi
@@ -711,20 +714,23 @@ AC_ARG_WITH([wolfssl],
[WITH_WOLFSSL=no]
)
AC_MSG_RESULT([$WITH_WOLFSSL])
+AM_CONDITIONAL([BUILD_WITH_WOLFSSL], test ! "$WITH_WOLFSSL" = no)
if test "$WITH_WOLFSSL" != no; then
- if test "$WITH_WOLFSSL" = yes; then
- WITH_WOLFSSL="/usr/local"
+ CPPFLAGS_SAVE="${CPPFLAGS}"
+ LDFLAGS_SAVE="${LDFLAGS}"
+ if test "$WITH_WOLFSSL" != yes; then
+ WOLFSSL_CFLAGS="-I$WITH_WOLFSSL/include -I$WITH_WOLFSSL/include/wolfssl"
+ WOLFSSL_LIBS="-L$WITH_WOLFSSL/lib -lwolfssl"
+ CPPFLAGS="${CPPFLAGS} $WOLFSSL_CFLAGS"
+ LDFLAGS="${LDFLAGS} $WOLFSSL_LIBS"
fi
- CPPFLAGS="${CPPFLAGS} -I$WITH_WOLFSSL/include -I$WITH_WOLFSSL/include/wolfssl"
- LDFLAGS="${LDFLAGS} -L$WITH_WOLFSSL/lib"
-
AC_CHECK_HEADERS([wolfssl/ssl.h], [], [
AC_MSG_ERROR([wolfssl headers not found. install them or build without --with-wolfssl])
])
AC_CHECK_LIB([wolfssl], [wolfSSL_Init],
- [CRYPTO_LIB="-lwolfssl"],
+ [WOLFSSL_CRYPTO_LIB="-lwolfssl"],
[AC_MSG_ERROR([wolfssl crypto library not found. install it or build without --with-wolfssl])]
)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
@@ -733,17 +739,16 @@ if test "$WITH_WOLFSSL" != no; then
#error HAVE_LIGHTY macro not defined
#endif
]])], [], [AC_MSG_ERROR([wolfssl must be built with ./configure --enable-lighty])])
- SSL_LIB="-lwolfssl"
-
- AC_SUBST([SSL_LIB])
- AC_SUBST([CRYPTO_LIB])
-fi
-AM_CONDITIONAL([BUILD_WITH_OPENSSL],
- [test "$WITH_OPENSSL" != no || test "$WITH_WOLFSSL" != no])
-
-if test "$WITH_OPENSSL" != no && test "$WITH_WOLFSSL" != no; then
- AC_MSG_ERROR([lighttpd should not be built with both --with-openssl and --with-wolfssl])
+ AC_SUBST([WOLFSSL_CFLAGS])
+ AC_SUBST([WOLFSSL_LIBS])
+ if test "$WITH_OPENSSL" = no; then
+ CRYPTO_LIB="$WOLFSSL_CRYPTO_LIB"
+ AC_SUBST([CRYPTO_LIB])
+ else
+ CPPFLAGS="${CPPFLAGS_SAVE}"
+ LDFLAGS="${LDFLAGS_SAVE}"
+ fi
fi
dnl Check for mbedTLS
@@ -773,7 +778,7 @@ if test "x$use_mbedtls" = "xyes"; then
AC_CHECK_LIB(mbedcrypto,mbedtls_base64_encode,
[AC_CHECK_LIB(mbedx509, mbedtls_x509_get_name,
[AC_CHECK_LIB(mbedtls, mbedtls_cipher_info_from_type,
- [MTLS_LIB="-lmbedtls -lmbedx509 -lmbedcrypto"
+ [MTLS_LIBS="-lmbedtls -lmbedx509 -lmbedcrypto"
CRYPTO_LIB="-lmbedcrypto"
AC_DEFINE(HAVE_LIBMBEDTLS, [1], [Have libmbedtls library])
AC_DEFINE(HAVE_LIBMBEDX509, [1], [Have libmbedx509 library])
@@ -782,7 +787,7 @@ if test "x$use_mbedtls" = "xyes"; then
],[],[-lmbedcrypto "$DL_LIB"])
],[],[])
LIBS="$OLDLIBS"
- AC_SUBST(MTLS_LIB)
+ AC_SUBST(MTLS_LIBS)
AC_SUBST(CRYPTO_LIB)
fi
@@ -1706,7 +1711,7 @@ lighty_track_feature "pam" "mod_authn_pam" \
'test "$WITH_PAM" != no'
lighty_track_feature "network-openssl" "mod_openssl" \
- 'test "$WITH_OPENSSL" != no || test "$WITH_WOLFSSL" != no'
+ 'test "$WITH_OPENSSL" != no'
lighty_track_feature "network-mbedtls" "mod_mbedtls" \
'test "$WITH_MBEDTLS" != no'
@@ -1717,6 +1722,9 @@ lighty_track_feature "network-gnutls" "mod_gnutls" \
lighty_track_feature "network-nss" "mod_nss" \
'test "$WITH_NSS" != no'
+lighty_track_feature "network-wolfssl" "mod_wolfssl" \
+ 'test "$WITH_WOLFSSL" != no'
+
lighty_track_feature "auth-crypt" "" \
'test "$found_crypt" != no'
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b50b02ed..2d614fae 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -313,6 +313,7 @@ if(WITH_OPENSSL)
if(HAVE_LIBCRYPTO)
set(CRYPTO_LIBRARY crypto)
check_library_exists(ssl SSL_new "" HAVE_LIBSSL)
+ set(HAVE_OPENSSL 1)
endif()
endif()
else()
@@ -362,6 +363,7 @@ if(WITH_WOLFSSL)
if(HAVE_LIBCRYPTO)
set(CRYPTO_LIBRARY ${WOLFSSL_LIBRARY})
add_definitions(-DHAVE_WOLFSSL_SSL_H)
+ set(HAVE_WOLFSSL 1)
endif()
set(CMAKE_REQUIRED_INCLUDES)
set(CMAKE_REQUIRED_LIBRARIES)
@@ -1115,16 +1117,7 @@ if(NOT BUILD_STATIC)
endif()
if(NOT ${CRYPTO_LIBRARY} EQUAL "")
- if(NOT WITH_WOLFSSL)
- target_link_libraries(lighttpd ssl)
- endif()
target_link_libraries(lighttpd ${CRYPTO_LIBRARY})
- add_and_install_library(mod_openssl "mod_openssl.c")
- if(NOT WITH_WOLFSSL)
- set(L_MOD_OPENSSL ${L_MOD_OPENSSL} ssl)
- endif()
- set(L_MOD_OPENSSL ${L_MOD_OPENSSL} ${CRYPTO_LIBRARY})
- target_link_libraries(mod_openssl ${L_MOD_OPENSSL})
target_link_libraries(mod_auth ${CRYPTO_LIBRARY})
set(L_MOD_AUTHN_FILE ${L_MOD_AUTHN_FILE} ${CRYPTO_LIBRARY})
target_link_libraries(mod_authn_file ${L_MOD_AUTHN_FILE})
@@ -1132,15 +1125,23 @@ if(NOT ${CRYPTO_LIBRARY} EQUAL "")
target_link_libraries(mod_wstunnel ${CRYPTO_LIBRARY})
endif()
+if(HAVE_OPENSSL)
+ add_and_install_library(mod_openssl "mod_openssl.c")
+ set(L_MOD_OPENSSL ${L_MOD_OPENSSL} ssl crypto)
+ target_link_libraries(mod_openssl ${L_MOD_OPENSSL})
+endif()
+
+if(HAVE_WOLFSSL)
+ add_and_install_library(mod_wolfssl "mod_wolfssl.c")
+ target_link_libraries(mod_wolfssl wolfssl)
+endif()
+
if(HAVE_LIBGNUTLS)
add_and_install_library(mod_gnutls "mod_gnutls.c")
target_link_libraries(mod_gnutls gnutls)
endif()
if(HAVE_LIBMBEDTLS AND HAVE_LIBMEDCRYPTO AND HAVE_LIBMEDX509)
- target_link_libraries(lighttpd mbedtls)
- target_link_libraries(lighttpd mbedcrypto)
- target_link_libraries(lighttpd mbedx509)
add_and_install_library(mod_mbedtls "mod_mbedtls.c")
set(L_MOD_MBEDTLS ${L_MOD_MBEDTLS} mbedtls mbedcrypto mbedx509)
target_link_libraries(mod_mbedtls ${L_MOD_MBEDTLS})
diff --git a/src/Makefile.am b/src/Makefile.am
index 0ba84a9d..24456646 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -392,14 +392,15 @@ if BUILD_WITH_OPENSSL
lib_LTLIBRARIES += mod_openssl.la
mod_openssl_la_SOURCES = mod_openssl.c
mod_openssl_la_LDFLAGS = $(common_module_ldflags)
-mod_openssl_la_LIBADD = $(SSL_LIB) $(common_libadd)
+mod_openssl_la_LIBADD = $(OPENSSL_LIBS) $(common_libadd)
+mod_openssl_la_CPPFLAGS = $(OPENSSL_CFLAGS)
endif
if BUILD_WITH_MBEDTLS
lib_LTLIBRARIES += mod_mbedtls.la
mod_mbedtls_la_SOURCES = mod_mbedtls.c
mod_mbedtls_la_LDFLAGS = $(common_module_ldflags)
-mod_mbedtls_la_LIBADD = $(MTLS_LIB) $(common_libadd)
+mod_mbedtls_la_LIBADD = $(MTLS_LIBS) $(common_libadd)
endif
if BUILD_WITH_GNUTLS
@@ -418,6 +419,14 @@ mod_nss_la_LIBADD = $(NSS_LIBS) $(common_libadd)
mod_nss_la_CPPFLAGS = $(NSS_CFLAGS)
endif
+if BUILD_WITH_WOLFSSL
+lib_LTLIBRARIES += mod_wolfssl.la
+mod_wolfssl_la_SOURCES = mod_wolfssl.c
+mod_wolfssl_la_LDFLAGS = $(common_module_ldflags)
+mod_wolfssl_la_LIBADD = $(WOLFSSL_LIBS) $(common_libadd)
+mod_wolfssl_la_CPPFLAGS = $(WOLFSSL_CFLAGS)
+endif
+
lib_LTLIBRARIES += mod_rewrite.la
mod_rewrite_la_SOURCES = mod_rewrite.c
@@ -567,7 +576,27 @@ lighttpd_LDADD += $(DBI_LIBS)
endif
if BUILD_WITH_OPENSSL
lighttpd_SOURCES += mod_openssl.c
-lighttpd_LDADD += $(SSL_LIB)
+lighttpd_CPPFLAGS += $(OPENSSL_CFLAGS)
+lighttpd_LDADD += $(OPENSSL_LIBS)
+endif
+if BUILD_WITH_MBEDTLS
+lighttpd_SOURCES += mod_mbedtls.c
+lighttpd_LDADD += $(MTLS_LIBS)
+endif
+if BUILD_WITH_GNUTLS
+lighttpd_SOURCES += mod_gnutls.c
+lighttpd_CPPFLAGS += $(GNUTLS_CFLAGS)
+lighttpd_LDADD += $(GNUTLS_LIBS)
+endif
+if BUILD_WITH_NSS
+lighttpd_SOURCES += mod_nss.c
+lighttpd_CPPFLAGS += $(NSS_CFLAGS)
+lighttpd_LDADD += $(NSS_LIBS)
+endif
+if BUILD_WITH_WOLFSSL
+lighttpd_SOURCES += mod_wolfssl.c
+lighttpd_CPPFLAGS += $(WOLFSSL_CFLAGS)
+lighttpd_LDADD += $(WOLFSSL_LIBS)
endif
if BUILD_WITH_MEMCACHED
lighttpd_CPPFLAGS += $(MEMCACHED_CFLAGS)
diff --git a/src/SConscript b/src/SConscript
index a0766ce8..d212a7b8 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -178,7 +178,7 @@ if env['with_openssl']:
modules['mod_openssl'] = { 'src' : [ 'mod_openssl.c' ], 'lib' : [ env['LIBSSL'], env['LIBCRYPTO'] ] }
if env['with_wolfssl']:
- modules['mod_openssl'] = { 'src' : [ 'mod_openssl.c' ], 'lib' : [ env['LIBCRYPTO'], 'm' ] }
+ modules['mod_wolfssl'] = { 'src' : [ 'mod_wolfssl.c' ], 'lib' : [ env['LIBCRYPTO'], 'm' ] }
if env['with_mbedtls']:
modules['mod_mbedtls'] = { 'src' : [ 'mod_mbedtls.c' ], 'lib' : [ env['LIBSSL'], env['LIBX509'], env['LIBCRYPTO'] ] }
diff --git a/src/meson.build b/src/meson.build
index 110c71fd..d7d0ce91 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1026,7 +1026,7 @@ endif
if get_option('with_wolfssl') != 'false'
modules += [
- [ 'mod_openssl', [ 'mod_openssl.c' ], libcrypto ],
+ [ 'mod_wolfssl', [ 'mod_wolfssl.c' ], libcrypto ],
]
endif
diff --git a/src/mod_openssl.c b/src/mod_openssl.c
index 671c7671..63e77b0d 100644
--- a/src/mod_openssl.c
+++ b/src/mod_openssl.c
@@ -56,21 +56,6 @@
#undef OPENSSL_NO_STDIO /* for X509_STORE_load_locations() */
#endif
-#ifdef WOLFSSL_OPTIONS_H
-/* WolfSSL defines OPENSSL_VERSION_NUMBER 0x10001040L for OPENSSL_ALL
- * or HAVE_LIGHTY. WolfSSL does not provide many interfaces added in
- * OpenSSL 1.0.2, including SSL_CTX_set_cert_cb(), so it is curious that
- * WolFSSL defines OPENSSL_VERSION_NUMBER 0x10100000L for WOLFSSL_APACHE_HTTPD*/
-#ifndef OPENSSL_EXTRA
-#define OPENSSL_EXTRA
-#endif
-#ifndef OPENSSL_ALL
-#define OPENSSL_ALL
-#endif
-#include <wolfssl/ssl.h>
-#include <wolfssl/crl.h>
-#endif
-
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/bn.h>
@@ -100,56 +85,6 @@
#endif
#endif
-#ifdef WOLFSSL_VERSION
-
-#ifdef HAVE_ALPN
-#undef OPENSSL_NO_TLSEXT
-#else
-#define OPENSSL_NO_TLSEXT
-#endif
-#ifdef HAVE_SESSION_TICKET
-#define TLSEXT_TYPE_session_ticket
-#endif
-static char global_err_buf[WOLFSSL_MAX_ERROR_SZ];
-#undef ERR_error_string
-#define ERR_error_string(e,b) \
- (wolfSSL_ERR_error_string_n((e),global_err_buf,WOLFSSL_MAX_ERROR_SZ), \
- global_err_buf)
-/* WolfSSL does not provide OPENSSL_cleanse() */
-#define OPENSSL_cleanse(x,sz) safe_memclear((x),(sz))
-#define SSL_set_read_ahead(x,y) ((void)(y)) /*WolfSSL no SSL_set_read_ahead()*/
-
-#if 0 /* symbols and definitions requires WolfSSL built with -DOPENSSL_EXTRA */
-#define SSL_TLSEXT_ERR_OK 0
-#define SSL_TLSEXT_ERR_ALERT_FATAL alert_fatal
-#define SSL_TLSEXT_ERR_NOACK alert_warning
-
-WOLFSSL_API void wolfSSL_set_verify_depth(WOLFSSL *ssl,int depth);
-
-WOLFSSL_API void wolfSSL_X509_NAME_free(WOLFSSL_X509_NAME* name);
-#define X509_NAME_cmp wolfSSL_X509_NAME_cmp
-WOLFSSL_API int wolfSSL_X509_NAME_cmp(const WOLFSSL_X509_NAME* x, const WOLFSSL_X509_NAME* y);
-WOLFSSL_API WOLFSSL_X509_NAME* wolfSSL_X509_NAME_dup(WOLFSSL_X509_NAME*);
-WOLFSSL_API char* wolfSSL_X509_get_name_oneline(WOLFSSL_X509_NAME*, char*, int);
-
-#undef OBJ_sn2nid
-#define OBJ_sn2nid(sn) wc_OBJ_sn2nid(sn)
-
-WOLFSSL_API const char* wolfSSL_OBJ_nid2sn(int n);
-WOLFSSL_API int wolfSSL_OBJ_obj2nid(const WOLFSSL_ASN1_OBJECT *o);
-WOLFSSL_API WOLFSSL_ASN1_OBJECT * wolfSSL_X509_NAME_ENTRY_get_object(WOLFSSL_X509_NAME_ENTRY *ne);
-#define X509_NAME_get_entry wolfSSL_X509_NAME_get_entry
-WOLFSSL_API WOLFSSL_X509_NAME_ENTRY *wolfSSL_X509_NAME_get_entry(WOLFSSL_X509_NAME *name, int loc);
-#endif
-
-#if 0 /* symbols and definitions requires WolfSSL built with -DOPENSSL_ALL */
-WOLFSSL_API WOLF_STACK_OF(WOLFSSL_X509_NAME) *wolfSSL_dup_CA_list( WOLF_STACK_OF(WOLFSSL_X509_NAME) *sk );
-/*wolfSSL_sk_X509_NAME_new()*/
-/*wolfSSL_sk_X509_NAME_push()*/
-#endif
-
-#endif /* WOLFSSL_VERSION */
-
#include "base.h"
#include "fdevent.h"
#include "http_header.h"
@@ -160,15 +95,9 @@ WOLFSSL_API WOLF_STACK_OF(WOLFSSL_X509_NAME) *wolfSSL_dup_CA_list( WOLF_STACK_OF
typedef struct {
/* SNI per host: with COMP_SERVER_SOCKET, COMP_HTTP_SCHEME, COMP_HTTP_HOST */
- #ifdef WOLFSSL_VERSION
- buffer *ssl_pemfile_pkey;
- buffer *ssl_pemfile_x509;
- buffer **ssl_pemfile_chain;
- #else
EVP_PKEY *ssl_pemfile_pkey;
X509 *ssl_pemfile_x509;
STACK_OF(X509) *ssl_pemfile_chain;
- #endif
buffer *ssl_stapling;
const buffer *ssl_pemfile;
const buffer *ssl_privkey;
@@ -519,23 +448,17 @@ ssl_tlsext_status_cb(SSL *ssl, void *arg)
int len = (int)buffer_string_length(ssl_stapling);
- #ifdef WOLFSSL_VERSION /* WolfSSL does not require copy */
- uint8_t *ocsp_resp = (uint8_t *)ssl_stapling->ptr;
- #else
/* OpenSSL and LibreSSL require copy (BoringSSL, too, if using compat API)*/
uint8_t *ocsp_resp = OPENSSL_malloc(len);
if (NULL == ocsp_resp)
return SSL_TLSEXT_ERR_NOACK; /* ignore OCSP request if error occurs */
memcpy(ocsp_resp, ssl_stapling->ptr, len);
- #endif
if (!SSL_set_tlsext_status_ocsp_resp(ssl, ocsp_resp, len)) {
log_error(hctx->r->conf.errh, __FILE__, __LINE__,
"SSL: failed to set OCSP response for TLS server name %s: %s",
hctx->r->uri.authority.ptr, ERR_error_string(ERR_get_error(), NULL));
- #ifndef WOLFSSL_VERSION /* WolfSSL does not require copy */
OPENSSL_free(ocsp_resp);
- #endif
return SSL_TLSEXT_ERR_NOACK; /* ignore OCSP request if error occurs */
/*return SSL_TLSEXT_ERR_ALERT_FATAL;*/
}
@@ -548,9 +471,6 @@ ssl_tlsext_status_cb(SSL *ssl, void *arg)
INIT_FUNC(mod_openssl_init)
{
plugin_data_singleton = (plugin_data *)calloc(1, sizeof(plugin_data));
- #ifdef DEBUG_WOLFSSL
- wolfSSL_Debugging_ON();
- #endif
return plugin_data_singleton;
}
@@ -560,19 +480,12 @@ static int mod_openssl_init_once_openssl (server *srv)
if (ssl_is_init) return 1;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L \
- && !defined(LIBRESSL_VERSION_NUMBER) \
- && !defined(WOLFSSL_VERSION)
+ && !defined(LIBRESSL_VERSION_NUMBER)
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS
|OPENSSL_INIT_LOAD_CRYPTO_STRINGS,NULL);
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
|OPENSSL_INIT_ADD_ALL_DIGESTS
|OPENSSL_INIT_LOAD_CONFIG, NULL);
- #elif defined(WOLFSSL_VERSION)
- if (wolfSSL_Init() != WOLFSSL_SUCCESS) {
- log_error(srv->errh, __FILE__, __LINE__,
- "SSL: wolfSSL_Init() failed");
- return 0;
- }
#else
SSL_load_error_strings();
SSL_library_init();
@@ -603,15 +516,9 @@ static void mod_openssl_free_openssl (void)
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10100000L \
- && !defined(LIBRESSL_VERSION_NUMBER) \
- && !defined(WOLFSSL_VERSION)
+ && !defined(LIBRESSL_VERSION_NUMBER)
/*(OpenSSL libraries handle thread init and deinit)
* https://github.com/openssl/openssl/pull/1048 */
- #elif defined(WOLFSSL_VERSION)
- if (wolfSSL_Cleanup() != WOLFSSL_SUCCESS) {
- log_error(plugin_data_singleton->srv->errh, __FILE__, __LINE__,
- "SSL: wolfSSL_Cleanup() failed");
- }
#else
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
@@ -628,18 +535,6 @@ static void mod_openssl_free_openssl (void)
}
-#ifdef WOLFSSL_VERSION
-static void
-mod_wolfssl_free_der_certs (buffer **certs)
-{
- if (NULL == certs) return;
- for (int i = 0; NULL != certs[i]; ++i)
- buffer_free(certs[i]);
- free(certs);
-}
-#endif
-
-
static void
mod_openssl_free_config (server *srv, plugin_data * const p)
{
@@ -668,15 +563,9 @@ mod_openssl_free_config (server *srv, plugin_data * const p)
case 0: /* ssl.pemfile */
if (cpv->vtype == T_CONFIG_LOCAL) {
plugin_cert *pc = cpv->v.v;
- #ifdef WOLFSSL_VERSION
- buffer_free(pc->ssl_pemfile_pkey);
- /*buffer_free(pc->ssl_pemfile_x509);*//*(part of chain)*/
- mod_wolfssl_free_der_certs(pc->ssl_pemfile_chain);
- #else
EVP_PKEY_free(pc->ssl_pemfile_pkey);
X509_free(pc->ssl_pemfile_x509);
sk_X509_pop_free(pc->ssl_pemfile_chain, X509_free);
- #endif
buffer_free(pc->ssl_stapling);
}
break;
@@ -700,375 +589,6 @@ mod_openssl_free_config (server *srv, plugin_data * const p)
}
-#ifdef WOLFSSL_VERSION
-
-
-/* WolfSSL OpenSSL compat API does not wipe temp mem used; write our own */
-/* (pemfile might contain private key)*/
-/* code here is based on similar code in mod_nss */
-#include "base64.h"
-
-#define PEM_BEGIN "-----BEGIN "
-#define PEM_END "-----END "
-#define PEM_BEGIN_CERT "-----BEGIN CERTIFICATE-----"
-#define PEM_END_CERT "-----END CERTIFICATE-----"
-#define PEM_BEGIN_TRUSTED_CERT "-----BEGIN TRUSTED CERTIFICATE-----"
-#define PEM_END_TRUSTED_CERT "-----END TRUSTED CERTIFICATE-----"
-#define PEM_BEGIN_PKEY "-----BEGIN PRIVATE KEY-----"
-#define PEM_END_PKEY "-----END PRIVATE KEY-----"
-#define PEM_BEGIN_EC_PKEY "-----BEGIN EC PRIVATE KEY-----"
-#define PEM_END_EC_PKEY "-----END EC PRIVATE KEY-----"
-#define PEM_BEGIN_RSA_PKEY "-----BEGIN RSA PRIVATE KEY-----"
-#define PEM_END_RSA_PKEY "-----END RSA PRIVATE KEY-----"
-#define PEM_BEGIN_DSA_PKEY "-----BEGIN DSA PRIVATE KEY-----"
-#define PEM_END_DSA_PKEY "-----END DSA PRIVATE KEY-----"
-#define PEM_BEGIN_ANY_PKEY "-----BEGIN ANY PRIVATE KEY-----"
-#define PEM_END_ANY_PKEY "-----END ANY PRIVATE KEY-----"
-/* (not implemented: support to get password from user for encrypted key) */
-#define PEM_BEGIN_ENCRYPTED_PKEY "-----BEGIN ENCRYPTED PRIVATE KEY-----"
-#define PEM_END_ENCRYPTED_PKEY "-----END ENCRYPTED PRIVATE KEY-----"
-
-#define PEM_BEGIN_X509_CRL "-----BEGIN X509 CRL-----"
-#define PEM_END_X509_CRL "-----END X509 CRL-----"
-
-
-static buffer *
-mod_wolfssl_load_pem_file (const char *fn, log_error_st *errh, buffer ***chain)
-{
- off_t dlen = 512*1024*1024;/*(arbitrary limit: 512 MB file; expect < 1 MB)*/
- char *data = fdevent_load_file(fn, &dlen, errh, malloc, free);
- if (NULL == data) return NULL;
-
- buffer **certs = NULL;
- int rc = -1;
- do {
- int count = 0;
- char *b = data;
- for (; (b = strstr(b, PEM_BEGIN_CERT)); b += sizeof(PEM_BEGIN_CERT)-1)
- ++count;
- b = data;
- for (; (b = strstr(b, PEM_BEGIN_TRUSTED_CERT));
- b += sizeof(PEM_BEGIN_TRUSTED_CERT)-1)
- ++count;
- if (0 == count) {
- rc = 0;
- break;
- }
-
- certs = malloc((count+1) * sizeof(buffer *));
- force_assert(NULL != certs);
- certs[count] = NULL;
- for (int i = 0; i < count; ++i)
- certs[i] = buffer_init();
-
- buffer *der;
- int i = 0;
- for (char *e = data; (b = strstr(e, PEM_BEGIN_CERT)); ++i) {
- b += sizeof(PEM_BEGIN_CERT)-1;
- if (*b == '\r') ++b;
- if (*b == '\n') ++b;
- e = strstr(b, PEM_END_CERT);
- if (NULL == e) break;
- uint32_t len = (uint32_t)(e - b);
- e += sizeof(PEM_END_CERT)-1;
- if (i >= count) break; /*(should not happen)*/
- der = certs[i];
- if (NULL == buffer_append_base64_decode(der,b,len,BASE64_STANDARD))
- break;
- }
- for (char *e = data; (b = strstr(e, PEM_BEGIN_TRUSTED_CERT)); ++i) {
- b += sizeof(PEM_BEGIN_TRUSTED_CERT)-1;
- if (*b == '\r') ++b;
- if (*b == '\n') ++b;
- e = strstr(b, PEM_END_TRUSTED_CERT);
- if (NULL == e) break;
- uint32_t len = (uint32_t)(e - b);
- e += sizeof(PEM_END_TRUSTED_CERT)-1;
- if (i >= count) break; /*(should not happen)*/
- der = certs[i];
- if (NULL == buffer_append_base64_decode(der,b,len,BASE64_STANDARD))
- break;
- }
- if (i == count)
- rc = 0;
- else
- errno = EIO;
- } while (0);
-
- if (dlen) safe_memclear(data, dlen);
- free(data);
-
- if (rc < 0) {
- log_perror(errh, __FILE__, __LINE__, "error loading %s", fn);
- mod_wolfssl_free_der_certs(certs);
- certs = NULL;
- }
-
- *chain = certs;
- return certs ? certs[0] : NULL;
-}
-
-
-static buffer *
-mod_wolfssl_evp_pkey_load_pem_file (const char *fn, log_error_st *errh)
-{
- off_t dlen = 512*1024*1024;/*(arbitrary limit: 512 MB file; expect < 1 MB)*/
- char *data = fdevent_load_file(fn, &dlen, errh, malloc, free);
- if (NULL == data) return NULL;
-
- buffer *pkey = NULL;
- int rc = -1;
- do {
- /*(expecting single private key in file, so first match)*/
- char *b, *e;
- if ((b = strstr(data, PEM_BEGIN_PKEY))
- && (e = strstr(b, PEM_END_PKEY)))
- b += sizeof(PEM_BEGIN_PKEY)-1;
- else if ((b = strstr(data, PEM_BEGIN_EC_PKEY))
- && (e = strstr(b, PEM_END_EC_PKEY)))
- b += sizeof(PEM_BEGIN_EC_PKEY)-1;
- else if ((b = strstr(data, PEM_BEGIN_RSA_PKEY))
- && (e = strstr(b, PEM_END_RSA_PKEY)))
- b += sizeof(PEM_BEGIN_RSA_PKEY)-1;
- else if ((b = strstr(data, PEM_BEGIN_DSA_PKEY))
- && (e = strstr(b, PEM_END_DSA_PKEY)))
- b += sizeof(PEM_BEGIN_DSA_PKEY)-1;
- else if ((b = strstr(data, PEM_BEGIN_ANY_PKEY))
- && (e = strstr(b, PEM_END_ANY_PKEY)))
- b += sizeof(PEM_BEGIN_ANY_PKEY)-1;
- else
- break;
- if (*b == '\r') ++b;
- if (*b == '\n') ++b;
-
- pkey = buffer_init();
- size_t len = (size_t)(e - b);
- if (NULL == buffer_append_base64_decode(pkey, b, len, BASE64_STANDARD))
- break;
- rc = 0;
- } while (0);
-
- if (dlen) safe_memclear(data, dlen);
- free(data);
-
- if (rc < 0) {
- log_error(errh, __FILE__, __LINE__, "%s() %s", __func__, fn);
- return NULL;
- }
-
- return pkey;
-}
-
-
-static int
-mod_wolfssl_CTX_use_certificate_chain_file (WOLFSSL_CTX *ssl_ctx, const char *fn, log_error_st *errh)
-{
- /* (While it should be possible to parse DERs from (buffer **)
- * s->pc->ssl_pemfile_chain, it is simpler to re-read file and use the
- * built-in wolfSSL_CTX_use_certificate_chain_buffer() interface) */
- off_t dlen = 4*1024*1024;/*(arbitrary limit: 4 MB file; expect < 1 KB)*/
- char *data = fdevent_load_file(fn, &dlen, errh, malloc, free);
- if (NULL == data) return -1;
-
- int rc = wolfSSL_CTX_use_certificate_chain_buffer(ssl_ctx,
- (unsigned char *)data,
- (long)dlen);
-
- if (dlen) safe_memclear(data, dlen);
- free(data);
-
- if (rc == WOLFSSL_SUCCESS)
- return 1;
-
- log_error(errh, __FILE__, __LINE__,
- "SSL: %s %s", ERR_error_string(rc, NULL), fn);
- return 0;
-}
-
-
-static STACK_OF(X509_NAME) *
-mod_wolfssl_load_client_CA_file (const buffer *ssl_ca_file, log_error_st *errh)
-{
- /* similar to wolfSSL_load_client_CA_file(), plus some processing */
- buffer **certs = NULL;
- if (NULL == mod_wolfssl_load_pem_file(ssl_ca_file->ptr, errh, &certs)) {
- #ifdef __clang_analyzer__
- mod_wolfssl_free_der_certs(certs); /*unnecessary; quiet clang analyzer*/
- #endif
- return NULL;
- }
-
- WOLF_STACK_OF(WOLFSSL_X509_NAME) *canames = wolfSSL_sk_X509_NAME_new(NULL);
- if (NULL == canames) {
- mod_wolfssl_free_der_certs(certs);
- return NULL;
- }
-
- for (int i = 0; NULL != certs[i]; ++i) {
- WOLFSSL_X509 *ca =
- wolfSSL_X509_load_certificate_buffer((unsigned char *)certs[i]->ptr,
- (int)
- buffer_string_length(certs[i]),
- WOLFSSL_FILETYPE_ASN1);
- WOLFSSL_X509_NAME *subj = NULL;
- if (NULL == ca
- || NULL == (subj = wolfSSL_X509_get_subject_name(ca))
- || 0 != wolfSSL_sk_X509_NAME_push(canames,
- wolfSSL_X509_NAME_dup(subj))) {
- log_error(errh, __FILE__, __LINE__,
- "SSL: couldn't read X509 certificates from '%s'",
- ssl_ca_file->ptr);
- if (subj) wolfSSL_X509_NAME_free(subj);
- if (ca) wolfSSL_X509_free(ca);
- wolfSSL_sk_X509_NAME_free(canames);
- mod_wolfssl_free_der_certs(certs);
- return NULL;
- }
-
- wolfSSL_X509_free(ca);
- }
-
- mod_wolfssl_free_der_certs(certs);
- return canames;
-}
-
-
-static plugin_cacerts *
-mod_wolfssl_load_cacerts (const buffer *ssl_ca_file, log_error_st *errh)
-{
- /* similar to mod_wolfSSL_load_client_CA_file(), plus some processing */
- /* similar to wolfSSL_load_client_CA_file(), plus some processing */
- buffer **certs = NULL;
- if (NULL == mod_wolfssl_load_pem_file(ssl_ca_file->ptr, errh, &certs)) {
- #ifdef __clang_analyzer__
- mod_wolfssl_free_der_certs(certs); /*unnecessary; quiet clang analyzer*/
- #endif
- return NULL;
- }
-
- WOLFSSL_X509_STORE *castore = wolfSSL_X509_STORE_new();
- if (NULL == castore) {
- mod_wolfssl_free_der_certs(certs);
- return NULL;
- }
-
- WOLF_STACK_OF(WOLFSSL_X509_NAME) *canames = wolfSSL_sk_X509_NAME_new(NULL);
- if (NULL == canames) {
- wolfSSL_X509_STORE_free(castore);
- mod_wolfssl_free_der_certs(certs);
- return NULL;
- }
-
- for (int i = 0; NULL != certs[i]; ++i) {
- WOLFSSL_X509 *ca =
- wolfSSL_X509_load_certificate_buffer((unsigned char *)certs[i]->ptr,
- (int)
- buffer_string_length(certs[i]),
- WOLFSSL_FILETYPE_ASN1);
- WOLFSSL_X509_NAME *subj = NULL;
- if (NULL == ca || !wolfSSL_X509_STORE_add_cert(castore, ca)
- || NULL == (subj = wolfSSL_X509_get_subject_name(ca))
- || 0 != wolfSSL_sk_X509_NAME_push(canames,
- wolfSSL_X509_NAME_dup(subj))) {
- log_error(errh, __FILE__, __LINE__,
- "SSL: couldn't read X509 certificates from '%s'",
- ssl_ca_file->ptr);
- if (subj) wolfSSL_X509_NAME_free(subj);
- if (ca) wolfSSL_X509_free(ca);
- wolfSSL_sk_X509_NAME_free(canames);
- wolfSSL_X509_STORE_free(castore);
- mod_wolfssl_free_der_certs(certs);
- return NULL;
- }
-
- wolfSSL_X509_free(ca);
- }
-
- mod_wolfssl_free_der_certs(certs);
-
- plugin_cacerts *cacerts = malloc(sizeof(plugin_cacerts));
- force_assert(cacerts);
-
- cacerts->names = canames;
- cacerts->certs = castore;
- return cacerts;
-}
-
-
-static int
-mod_wolfssl_load_cacrls (WOLFSSL_CTX *ssl_ctx, const buffer *ssl_ca_crl_file, server *srv)
-{
- #ifdef HAVE_CRL /* <wolfssl/options.h> */
- int rc = wolfSSL_CTX_EnableCRL(ssl_ctx,
- WOLFSSL_CRL_CHECK | WOLFSSL_CRL_CHECKALL);
- if (rc != WOLFSSL_SUCCESS) return 0;
-
- const char *fn = ssl_ca_crl_file->ptr;
- off_t dlen = 512*1024*1024;/*(arbitrary limit: 512 MB file; expect < 1 MB)*/
- char *data = fdevent_load_file(fn, &dlen, srv->errh, malloc, free);
- if (NULL == data) return 0;
-
- rc = wolfSSL_CTX_LoadCRLBuffer(ssl_ctx, (byte *)data, (long)dlen,
- WOLFSSL_FILETYPE_PEM);
-
- if (dlen) safe_memclear(data, dlen);
- free(data);
-
- if (rc == WOLFSSL_SUCCESS)
- return 1;
-
- log_error(srv->errh, __FILE__, __LINE__,
- "SSL: %s %s", ERR_error_string(rc, NULL), fn);
- return 0;
- #else
- UNUSED(ssl_ctx);
- log_error(srv->errh, __FILE__, __LINE__,
- "WolfSSL not built with CRL support; ignoring %s", ssl_ca_crl_file->ptr);
- return WOLFSSL_FAILURE;
- #endif
-}
-
-
-static int
-mod_wolfssl_load_verify_locn (SSL_CTX *ssl_ctx, const buffer *b, server *srv)
-{
- const char *fn = b->ptr;
- off_t dlen = 512*1024*1024;/*(arbitrary limit: 512 MB file; expect < 1 MB)*/
- char *data = fdevent_load_file(fn, &dlen, srv->errh, malloc, free);
- if (NULL == data) return 0;
-
- int rc = wolfSSL_CTX_load_verify_buffer(ssl_ctx, (unsigned char *)data,
- (long)dlen, WOLFSSL_FILETYPE_PEM);
-
- if (dlen) safe_memclear(data, dlen);
- free(data);
-
- if (rc == WOLFSSL_SUCCESS)
- return 1;
-
- log_error(srv->errh, __FILE__, __LINE__,
- "SSL: %s %s", ERR_error_string(rc, NULL), fn);
- return 0;
-}
-
-
-static int
-mod_wolfssl_load_ca_files (SSL_CTX *ssl_ctx, plugin_data *p, server *srv)
-{
- /* load all ssl.ca-files specified in the config into each SSL_CTX */
-
- for (uint32_t i = 0, used = p->cafiles->used; i < used; ++i) {
- const buffer *b = &((data_string *)p->cafiles->data[i])->value;
- if (!mod_wolfssl_load_verify_locn(ssl_ctx, b, srv))
- return 0;
- }
- return 1;
-}
-
-
-#else /* !defined(WOLFSSL_VERSION) */
-
-
/* use memory from openssl secure heap for temporary buffers, returned storage
* (pemfile might contain a private key in addition to certificate chain)
* Interfaces similar to those constructed in include/openssl/pem.h for
@@ -1122,26 +642,18 @@ PEM_ASN1_read_bio_secmem(d2i_of_void *d2i, const char *name, BIO *bp, void **x,
static X509 *
PEM_read_bio_X509_secmem(BIO *bp, X509 **x, pem_password_cb *cb, void *u)
{
- #ifdef WOLFSSL_VERSION /* WolfSSL limitation; does not wipe temp mem used */
- return wolfSSL_PEM_read_bio_X509(bp, x, cb, u);
- #else
return PEM_ASN1_read_bio_secmem((d2i_of_void *)d2i_X509,
PEM_STRING_X509,
bp, (void **)x, cb, u);
- #endif
}
static X509 *
PEM_read_bio_X509_AUX_secmem(BIO *bp, X509 **x, pem_password_cb *cb, void *u)
{
- #ifdef WOLFSSL_VERSION /* WolfSSL limitation; does not wipe temp mem used */
- return wolfSSL_PEM_read_bio_X509_AUX(bp, x, cb, u);
- #else
return PEM_ASN1_read_bio_secmem((d2i_of_void *)d2i_X509_AUX,
PEM_STRING_X509_TRUSTED,
bp, (void **)x, cb, u);
- #endif
}
@@ -1278,9 +790,6 @@ mod_openssl_load_ca_files (SSL_CTX *ssl_ctx, plugin_data *p, server *srv)
#endif
-#endif /* !defined(WOLFSSL_VERSION) */
-
-
FREE_FUNC(mod_openssl_free)
{
plugin_data *p = p_d;
@@ -1370,10 +879,6 @@ mod_openssl_patch_config (request_st * const r, plugin_config * const pconf)
static int
safer_X509_NAME_oneline(X509_NAME *name, char *buf, size_t sz)
{
- #ifdef WOLFSSL_VERSION
- if (wolfSSL_X509_get_name_oneline(name, buf, (int)sz))
- return (int)strlen(buf);
- #else
BIO *bio = BIO_new(BIO_s_mem());
if (bio) {
int len = X509_NAME_print_ex(bio, name, 0, XN_FLAG_ONELINE);
@@ -1381,7 +886,6 @@ safer_X509_NAME_oneline(X509_NAME *name, char *buf, size_t sz)
BIO_free(bio);
return len; /*return value has similar semantics to that of snprintf()*/
}
- #endif
else {
buf[0] = '\0';
return -1;
@@ -1403,14 +907,7 @@ ssl_info_callback (const SSL *ssl, int where, int ret)
* "TLSv1.3 unexpected InfoCallback after handshake completed" */
if (0 != (where & SSL_CB_HANDSHAKE_DONE)) {
/* SSL_version() is valid after initial handshake completed */
- #ifdef WOLFSSL_VERSION
- SSL *ssl_nonconst;
- *(const SSL **)&ssl_nonconst = ssl;
- if (wolfSSL_GetVersion(ssl_nonconst) >= WOLFSSL_TLSV1_3)
- #else
- if (SSL_version(ssl) >= TLS1_3_VERSION)
- #endif
- {
+ if (SSL_version(ssl) >= TLS1_3_VERSION) {
/* https://wiki.openssl.org/index.php/TLS1.3
* "Renegotiation is not possible in a TLSv1.3 connection" */
handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl);
@@ -1529,14 +1026,7 @@ mod_openssl_cert_cb (SSL *ssl, void *arg)
/* first set certificate!
* setting private key checks whether certificate matches it */
- #ifdef WOLFSSL_VERSION
- buffer *cert = pc->ssl_pemfile_x509;
- if (1 != wolfSSL_use_certificate_ASN1(ssl, (unsigned char *)cert->ptr,
- (int)buffer_string_length(cert)))
- #else
- if (1 != SSL_use_certificate(ssl, pc->ssl_pemfile_x509))
- #endif
- {
+ if (1 != SSL_use_certificate(ssl, pc->ssl_pemfile_x509)) {
log_error(hctx->r->conf.errh, __FILE__, __LINE__,
"SSL: failed to set certificate for TLS server name %s: %s",
hctx->r->uri.authority.ptr, ERR_error_string(ERR_get_error(), NULL));
@@ -1575,15 +1065,7 @@ mod_openssl_cert_cb (SSL *ssl, void *arg)
#endif
#endif
- #ifdef WOLFSSL_VERSION
- buffer *pkey = pc->ssl_pemfile_pkey;
- if (1 != wolfSSL_use_PrivateKey_buffer(ssl, (unsigned char *)pkey->ptr,
- (int)buffer_string_length(pkey),
- WOLFSSL_FILETYPE_ASN1))
- #else
- if (1 != SSL_use_PrivateKey(ssl, pc->ssl_pemfile_pkey))
- #endif
- {
+ if (1 != SSL_use_PrivateKey(ssl, pc->ssl_pemfile_pkey)) {
log_error(hctx->r->conf.errh, __FILE__, __LINE__,
"SSL: failed to set private key for TLS server name %s: %s",
hctx->r->uri.authority.ptr, ERR_error_string(ERR_get_error(), NULL));
@@ -1618,14 +1100,12 @@ mod_openssl_cert_cb (SSL *ssl, void *arg)
/* WTH openssl? SSL_set_client_CA_list() calls set0_CA_list(),
* but there is no set1_CA_list() to simply up the reference count
* (without needing to duplicate the list) */
- #ifndef WOLFSSL_VERSION /* WolfSSL limitation */
/* WolfSSL does not support setting per-session CA list;
* limitation is to per-CTX CA list, and is not changed after SNI */
STACK_OF(X509_NAME) * const cert_names = hctx->conf.ssl_ca_dn_file
? hctx->conf.ssl_ca_dn_file
: hctx->conf.ssl_ca_file->names;
SSL_set_client_CA_list(ssl, SSL_dup_CA_list(cert_names));
- #endif
int mode = SSL_VERIFY_PEER;
if (hctx->conf.ssl_verifyclient_enforce)
mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
@@ -1718,18 +1198,10 @@ network_ssl_servername_callback (SSL *ssl, int *al, void *srv)
UNUSED(al);
UNUSED(srv);
- #ifdef WOLFSSL_VERSION
- const char *servername;
- size_t len = (size_t)
- wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, (void **)&servername);
- if (0 == len)
- return SSL_TLSEXT_ERR_NOACK; /* client did not provide SNI */
- #else
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (NULL == servername)
return SSL_TLSEXT_ERR_NOACK; /* client did not provide SNI */
size_t len = strlen(servername);
- #endif
int read_ahead = hctx->conf.ssl_read_ahead;
int rc = mod_openssl_SNI(hctx, servername, len);
if (!read_ahead && hctx->conf.ssl_read_ahead)
@@ -1740,7 +1212,6 @@ network_ssl_servername_callback (SSL *ssl, int *al, void *srv)
#endif
-#ifndef WOLFSSL_VERSION
static X509 *
mod_openssl_load_pem_file (const char *file, log_error_st *errh, STACK_OF(X509) **chain)
{
@@ -1774,10 +1245,8 @@ mod_openssl_load_pem_file (const char *file, log_error_st *errh, STACK_OF(X509)
free(data);
return x;
}
-#endif
-#ifndef WOLFSSL_VERSION
static EVP_PKEY *
mod_openssl_evp_pkey_load_pem_file (const char *file, log_error_st *errh)
{
@@ -1802,19 +1271,10 @@ mod_openssl_evp_pkey_load_pem_file (const char *file, log_error_st *errh)
return x;
}
-#endif
#ifndef OPENSSL_NO_OCSP
-#ifdef WOLFSSL_VERSION
-#define OCSP_RESPONSE OcspResponse
-#define OCSP_RESPONSE_free wolfSSL_OCSP_RESPONSE_free
-#define d2i_OCSP_RESPONSE_bio wolfSSL_d2i_OCSP_RESPONSE_bio
-#define d2i_OCSP_RESPONSE wolfSSL_d2i_OCSP_RESPONSE
-#define i2d_OCSP_RESPONSE wolfSSL_i2d_OCSP_RESPONSE
-#endif
-
static buffer *
mod_openssl_load_stapling_file (const char *file, log_error_st *errh, buffer *b)
{
@@ -1839,8 +1299,7 @@ mod_openssl_load_stapling_file (const char *file, log_error_st *errh, buffer *b)
char *data = fdevent_load_file(file, &dlen, errh, malloc, free);
if (NULL == data) return NULL;
- #if defined(BORINGSSL_API_VERSION) \
- || defined(WOLFSSL_VERSION)
+ #if defined(BORINGSSL_API_VERSION)
if (NULL == b)
b = buffer_init();
@@ -1887,7 +1346,7 @@ mod_openssl_load_stapling_file (const char *file, log_error_st *errh, buffer *b)
}
-#if !defined(BORINGSSL_API_VERSION) && !defined(WOLFSSL_VERSION)
+#if !defined(BORINGSSL_API_VERSION)
static time_t
mod_openssl_asn1_time_to_posix (ASN1_TIME *asn1time)
{
@@ -1996,7 +1455,7 @@ mod_openssl_asn1_time_to_posix (ASN1_TIME *asn1time)
static time_t
mod_openssl_ocsp_next_update (plugin_cert *pc)
{
- #if defined(BORINGSSL_API_VERSION) || defined(WOLFSSL_VERSION)
+ #if defined(BORINGSSL_API_VERSION)
UNUSED(pc);
return (time_t)-1; /*(not implemented)*/
#else
@@ -2012,31 +1471,7 @@ mod_openssl_ocsp_next_update (plugin_cert *pc)
/* XXX: should save and evaluate cert status returned by these calls */
ASN1_TIME *nextupd = NULL;
- #ifdef WOLFSSL_VERSION /* WolfSSL limitation */
- /* WolfSSL does not provide OCSP_resp_get0() OCSP_single_get0_status() */
- /* (inactive code path; alternative path followed in #if above for WolfSSL)
- * (chain not currently available in mod_openssl when used with WolfSSL)
- * (For WolfSSL, pc->ssl_pemfile_chain might not be filled in with actual
- * chain, but is used to store (buffer **) of DER decoded from PEM certs
- * read from ssl.pemfile, which may be a single cert, pc->ssl_pemfile_x509.
- * The chain is not calculated or filled in if single cert, and neither are
- * (X509 *), though (X509 *) could be temporarily created to calculated
- * (OCSP_CERTID *), which additionally could be calculated once at startup)
- */
- OCSP_CERTID *id = (NULL != pc->ssl_pemfile_chain)
- ? OCSP_cert_to_id(NULL, pc->ssl_pemfile_x509,
- sk_X509_value(pc->ssl_pemfile_chain, 0))
- : NULL;
- if (id == NULL) {
- OCSP_BASICRESP_free(bs);
- OCSP_RESPONSE_free(ocsp);
- return (time_t)-1;
- }
- OCSP_resp_find_status(bs, id, NULL, NULL, NULL, NULL, &nextupd);
- OCSP_CERTID_free(id);
- #else
OCSP_single_get0_status(OCSP_resp_get0(bs, 0), NULL, NULL, NULL, &nextupd);
- #endif
time_t t = nextupd ? mod_openssl_asn1_time_to_posix(nextupd) : (time_t)-1;
/* Note: trust external process which creates ssl.stapling-file to verify
@@ -2155,23 +1590,6 @@ network_openssl_load_pemfile (server *srv, const buffer *pemfile, const buffer *
{
if (!mod_openssl_init_once_openssl(srv)) return NULL;
- #ifdef WOLFSSL_VERSION
- buffer **ssl_pemfile_chain = NULL;
- buffer *ssl_pemfile_x509 =
- mod_wolfssl_load_pem_file(pemfile->ptr, srv->errh, &ssl_pemfile_chain);
- if (NULL == ssl_pemfile_x509)
- return NULL;
-
- buffer *ssl_pemfile_pkey =
- mod_wolfssl_evp_pkey_load_pem_file(privkey->ptr, srv->errh);
- if (NULL == ssl_pemfile_pkey) {
- /*buffer_free(ssl_pemfile_x509);*//*(part of chain)*/
- mod_wolfssl_free_der_certs(ssl_pemfile_chain);
- return NULL;
- }
-
- /* X509_check_private_key() is a stub func (not implemented) in WolfSSL */
- #else
STACK_OF(X509) *ssl_pemfile_chain = NULL;
X509 *ssl_pemfile_x509 =
mod_openssl_load_pem_file(pemfile->ptr, srv->errh, &ssl_pemfile_chain);
@@ -2196,7 +1614,6 @@ network_openssl_load_pemfile (server *srv, const buffer *pemfile, const buffer *
sk_X509_pop_free(ssl_pemfile_chain, X509_free);
return NULL;
}
- #endif
plugin_cert *pc = malloc(sizeof(plugin_cert));
force_assert(pc);
@@ -2210,14 +1627,7 @@ network_openssl_load_pemfile (server *srv, const buffer *pemfile, const buffer *
pc->ssl_stapling_loadts = 0;
pc->ssl_stapling_nextts = 0;
#ifndef OPENSSL_NO_OCSP
- #ifdef WOLFSSL_VERSION
- /*(not implemented for WolfSSL, though could convert the DER to (X509 *),
- * check Must-Staple, and then destroy (X509 *))*/
- (void)mod_openssl_crt_must_staple(NULL);
- pc->must_staple = 0;
- #else
pc->must_staple = mod_openssl_crt_must_staple(ssl_pemfile_x509);
- #endif
#else
pc->must_staple = 0;
#endif
@@ -2253,15 +1663,9 @@ mod_openssl_acme_tls_1 (SSL *ssl, handler_ctx *hctx)
buffer * const b = hctx->tmp_buf;
const buffer * const name = &hctx->r->uri.authority;
log_error_st * const errh = hctx->r->conf.errh;
- #ifdef WOLFSSL_VERSION
- buffer *ssl_pemfile_x509 = NULL;
- buffer *ssl_pemfile_pkey = NULL;
- buffer **ssl_pemfile_chain = NULL;
- #else
X509 *ssl_pemfile_x509 = NULL;
STACK_OF(X509) *ssl_pemfile_chain = NULL;
EVP_PKEY *ssl_pemfile_pkey = NULL;
- #endif
size_t len;
int rc = SSL_TLSEXT_ERR_ALERT_FATAL;
@@ -2286,13 +1690,8 @@ mod_openssl_acme_tls_1 (SSL *ssl, handler_ctx *hctx)
do {
buffer_append_string_len(b, CONST_STR_LEN(".crt.pem"));
- #ifdef WOLFSSL_VERSION
- ssl_pemfile_x509 =
- mod_wolfssl_load_pem_file(b->ptr, errh, &ssl_pemfile_chain);
- #else
ssl_pemfile_x509 =
mod_openssl_load_pem_file(b->ptr, errh, &ssl_pemfile_chain);
- #endif
if (NULL == ssl_pemfile_x509) {
log_error(errh, __FILE__, __LINE__,
"SSL: Failed to load acme-tls/1 pemfile: %s", b->ptr);
@@ -2301,11 +1700,7 @@ mod_openssl_acme_tls_1 (SSL *ssl, handler_ctx *hctx)
buffer_string_set_length(b, len); /*(remove ".crt.pem")*/
buffer_append_string_len(b, CONST_STR_LEN(".key.pem"));
- #ifdef WOLFSSL_VERSION
- ssl_pemfile_pkey = mod_wolfssl_evp_pkey_load_pem_file(b->ptr, errh);
- #else
ssl_pemfile_pkey = mod_openssl_evp_pkey_load_pem_file(b->ptr, errh);
- #endif
if (NULL == ssl_pemfile_pkey) {
log_error(errh, __FILE__, __LINE__,
"SSL: Failed to load acme-tls/1 pemfile: %s", b->ptr);
@@ -2324,14 +1719,7 @@ mod_openssl_acme_tls_1 (SSL *ssl, handler_ctx *hctx)
/* first set certificate!
* setting private key checks whether certificate matches it */
- #ifdef WOLFSSL_VERSION
- buffer *cert = ssl_pemfile_x509;
- if (1 != wolfSSL_use_certificate_ASN1(ssl, (unsigned char *)cert->ptr,
- (int)buffer_string_length(cert)))
- #else
- if (1 != SSL_use_certificate(ssl, ssl_pemfile_x509))
- #endif
- {
+ if (1 != SSL_use_certificate(ssl, ssl_pemfile_x509)) {
log_error(errh, __FILE__, __LINE__,
"SSL: failed to set acme-tls/1 certificate for TLS server "
"name %s: %s", name->ptr, ERR_error_string(ERR_get_error(),NULL));
@@ -2339,24 +1727,11 @@ mod_openssl_acme_tls_1 (SSL *ssl, handler_ctx *hctx)
}
if (ssl_pemfile_chain) {
- #ifndef WOLFSSL_VERSION /* WolfSSL limitation */
- /* WolfSSL does not support setting per-session chain;
- * limitation is to per-CTX chain, and so chain is not provided for
- * "acme-tls/1" (might be non-issue; chain might not be present) */
SSL_set0_chain(ssl, ssl_pemfile_chain);
ssl_pemfile_chain = NULL;
- #endif
}
- #ifdef WOLFSSL_VERSION
- buffer *pkey = ssl_pemfile_pkey;
- if (1 != wolfSSL_use_PrivateKey_buffer(ssl, (unsigned char *)pkey->ptr,
- (int)buffer_string_length(pkey),
- WOLFSSL_FILETYPE_ASN1))
- #else
- if (1 != SSL_use_PrivateKey(ssl, ssl_pemfile_pkey))
- #endif
- {
+ if (1 != SSL_use_PrivateKey(ssl, ssl_pemfile_pkey)) {
log_error(errh, __FILE__, __LINE__,
"SSL: failed to set acme-tls/1 private key for TLS server "
"name %s: %s", name->ptr, ERR_error_string(ERR_get_error(),NULL));
@@ -2368,16 +1743,10 @@ mod_openssl_acme_tls_1 (SSL *ssl, handler_ctx *hctx)
rc = SSL_TLSEXT_ERR_OK;
} while (0);
- #ifdef WOLFSSL_VERSION
- if (ssl_pemfile_pkey) buffer_free(ssl_pemfile_pkey);
- /*if (ssl_pemfile_x509) buffer_free(ssl_pemfile_x509);*//*(part of chain)*/
- mod_wolfssl_free_der_certs(ssl_pemfile_chain);
- #else
if (ssl_pemfile_pkey) EVP_PKEY_free(ssl_pemfile_pkey);
if (ssl_pemfile_x509) X509_free(ssl_pemfile_x509);
if (ssl_pemfile_chain)
sk_X509_pop_free(ssl_pemfile_chain, X509_free);
- #endif
return rc;
}
@@ -2456,8 +1825,7 @@ mod_openssl_alpn_select_cb (SSL *ssl, const unsigned char **out, unsigned char *
#if defined(BORINGSSL_API_VERSION) \
- || defined(LIBRESSL_VERSION_NUMBER) \
- || defined(WOLFSSL_VERSION)
+ || defined(LIBRESSL_VERSION_NUMBER)
static int
mod_openssl_ssl_conf_cmd (server *srv, plugin_config_socket *s);
#endif
@@ -2511,8 +1879,7 @@ network_openssl_ssl_conf_cmd (server *srv, plugin_config_socket *s)
return rc;
#elif defined(BORINGSSL_API_VERSION) \
- || defined(LIBRESSL_VERSION_NUMBER) \
- || defined(WOLFSSL_VERSION)
+ || defined(LIBRESSL_VERSION_NUMBER)
return mod_openssl_ssl_conf_cmd(srv, s);
@@ -2531,7 +1898,7 @@ network_openssl_ssl_conf_cmd (server *srv, plugin_config_socket *s)
#if OPENSSL_VERSION_NUMBER < 0x10100000L \
|| defined(LIBRESSL_VERSION_NUMBER)
#define DH_set0_pqg(dh, dh_p, NULL, dh_g) \
- ((dh)->p = (dh_p), (dh)->g = (dh_g), (dh_p) != NULL && (dh_g != NULL))
+ ((dh)->p = (dh_p), (dh)->g = (dh_g), (dh_p) != NULL && (dh_g) != NULL)
#endif
/* https://tools.ietf.org/html/rfc7919#appendix-A.1
* A.1. ffdhe2048
@@ -2656,8 +2023,7 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
| SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
| SSL_OP_NO_COMPRESSION;
- #if OPENSSL_VERSION_NUMBER >= 0x10100000L \
- || defined(WOLFSSL_VERSION)
+ #if OPENSSL_VERSION_NUMBER >= 0x10100000L
s->ssl_ctx = (!s->ssl_use_sslv2 && !s->ssl_use_sslv3)
? SSL_CTX_new(TLS_server_method())
: SSL_CTX_new(SSLv23_server_method());
@@ -2685,12 +2051,10 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
return -1;
}
- #if !defined(WOLFSSL_VERSION) || !defined(NO_SESSION_CACHE)
/* disable session cache; session tickets are preferred */
SSL_CTX_set_session_cache_mode(s->ssl_ctx, SSL_SESS_CACHE_OFF
| SSL_SESS_CACHE_NO_AUTO_CLEAR
| SSL_SESS_CACHE_NO_INTERNAL);
- #endif
if (s->ssl_empty_fragments) {
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
@@ -2706,7 +2070,6 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
SSL_CTX_set_options(s->ssl_ctx, ssloptions);
SSL_CTX_set_info_callback(s->ssl_ctx, ssl_info_callback);
- #ifndef HAVE_WOLFSSL_SSL_H /*(wolfSSL does not support SSLv2)*/
if (!s->ssl_use_sslv2 && 0 != SSL_OP_NO_SSLv2) {
/* disable SSLv2 */
if ((SSL_OP_NO_SSLv2
@@ -2717,7 +2080,6 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
return -1;
}
}
- #endif
if (!s->ssl_use_sslv3 && 0 != SSL_OP_NO_SSLv3) {
/* disable SSLv3 */
@@ -2752,25 +2114,6 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
{
DH *dh;
/* Support for Diffie-Hellman key exchange */
- #ifdef WOLFSSL_VERSION
- if (!buffer_string_is_empty(s->ssl_dh_file)) {
- const char *fn = s->ssl_dh_file->ptr;
- off_t dlen = 1*1024*1024;/*(arbitrary limit: 1 MB; expect < 1 KB)*/
- char *data = fdevent_load_file(fn, &dlen, srv->errh, malloc, free);
- int rc = (NULL != data) ? 0 : -1;
- if (0 == rc)
- wolfSSL_CTX_SetTmpDH_buffer(s->ssl_ctx, (unsigned char *)data,
- (long)dlen, WOLFSSL_FILETYPE_PEM);
- if (dlen) safe_memclear(data, dlen);
- free(data);
- if (rc < 0) {
- log_error(srv->errh, __FILE__, __LINE__,
- "SSL: Unable to read DH params from file %s",
- s->ssl_dh_file->ptr);
- return -1;
- }
- }
- #else
if (!buffer_string_is_empty(s->ssl_dh_file)) {
/* DH parameters from file */
BIO *bio;
@@ -2790,7 +2133,6 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
SSL_CTX_set_tmp_dh(s->ssl_ctx,dh);
DH_free(dh);
}
- #endif
else {
dh = get_dh2048();
if (dh == NULL) {
@@ -2846,11 +2188,7 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
* XXX: This might be a bit excessive, but are all trusted CAs
* TODO: prefer to load on-demand in mod_openssl_cert_cb()
* for openssl >= 1.0.2 */
- #ifdef WOLFSSL_VERSION
- if (!mod_wolfssl_load_ca_files(s->ssl_ctx, p, srv))
- #else
if (!mod_openssl_load_ca_files(s->ssl_ctx, p, srv))
- #endif
return -1;
if (s->ssl_verifyclient) {
@@ -2874,15 +2212,6 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
if (s->ssl_verifyclient_enforce) {
mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
}
- #ifdef WOLFSSL_VERSION
- wolfSSL_CTX_set_verify(s->ssl_ctx, mode, verify_callback);
- wolfSSL_CTX_set_verify_depth(s->ssl_ctx,
- s->ssl_verifyclient_depth + 1);
- if (!buffer_string_is_empty(s->ssl_ca_crl_file)) {
- if (!mod_wolfssl_load_cacrls(s->ssl_ctx,s->ssl_ca_crl_file,srv))
- return -1;
- }
- #else
SSL_CTX_set_verify(s->ssl_ctx, mode, verify_callback);
SSL_CTX_set_verify_depth(s->ssl_ctx, s->ssl_verifyclient_depth + 1);
if (!buffer_string_is_empty(s->ssl_ca_crl_file)) {
@@ -2890,14 +2219,8 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
if (!mod_openssl_load_cacrls(store, s->ssl_ca_crl_file, srv))
return -1;
}
- #endif
}
- #ifdef WOLFSSL_VERSION
- if (1 != mod_wolfssl_CTX_use_certificate_chain_file(
- s->ssl_ctx, s->pc->ssl_pemfile->ptr, srv->errh))
- return -1;
- #else
if (1 != SSL_CTX_use_certificate_chain_file(s->ssl_ctx,
s->pc->ssl_pemfile->ptr)) {
log_error(srv->errh, __FILE__, __LINE__,
@@ -2905,18 +2228,8 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
s->pc->ssl_pemfile->ptr);
return -1;
}
- #endif
- #ifdef WOLFSSL_VERSION
- buffer *k = s->pc->ssl_pemfile_pkey;
- if (1 != wolfSSL_CTX_use_PrivateKey_buffer(s->ssl_ctx,
- (unsigned char *)k->ptr,
- (int)buffer_string_length(k),
- WOLFSSL_FILETYPE_ASN1))
- #else
- if (1 != SSL_CTX_use_PrivateKey(s->ssl_ctx, s->pc->ssl_pemfile_pkey))
- #endif
- {
+ if (1 != SSL_CTX_use_PrivateKey(s->ssl_ctx, s->pc->ssl_pemfile_pkey)) {
log_error(srv->errh, __FILE__, __LINE__,
"SSL: %s %s %s", ERR_error_string(ERR_get_error(), NULL),
s->pc->ssl_pemfile->ptr, s->pc->ssl_privkey->ptr);
@@ -2946,10 +2259,6 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
#ifndef OPENSSL_NO_TLSEXT
#ifdef SSL_CLIENT_HELLO_SUCCESS
SSL_CTX_set_client_hello_cb(s->ssl_ctx,mod_openssl_client_hello_cb,srv);
- #elif defined(WOLFSSL_VERSION)
- wolfSSL_CTX_set_servername_callback(
- s->ssl_ctx, network_ssl_servername_callback);
- wolfSSL_CTX_set_servername_arg(s->ssl_ctx, srv);
#else
if (!SSL_CTX_set_tlsext_servername_callback(
s->ssl_ctx, network_ssl_servername_callback) ||
@@ -2972,11 +2281,6 @@ network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
if (!s->ssl_use_sslv3 && !s->ssl_use_sslv2
&& !SSL_CTX_set_min_proto_version(s->ssl_ctx, TLS1_2_VERSION))
return -1;
- #elif defined(WOLFSSL_VERSION)
- if (!s->ssl_use_sslv3 && !s->ssl_use_sslv2
- && wolfSSL_CTX_SetMinVersion(s->ssl_ctx, WOLFSSL_TLSV1_2)
- != WOLFSSL_SUCCESS)
- return -1;
#endif
if (s->ssl_conf_cmd && s->ssl_conf_cmd->used) {
@@ -3028,12 +2332,7 @@ mod_openssl_set_defaults_sockets(server *srv, plugin_data *p)
T_CONFIG_UNSET,
T_CONFIG_SCOPE_UNSET }
};
- #ifdef WOLFSSL_VERSION /* WolfSSL does not have mapping for "HIGH" */
- /* cipher list is (current) output of "openssl ciphers HIGH" */
- static const buffer default_ssl_cipher_list = { CONST_STR_LEN("TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-CCM8:ECDHE-ECDSA-AES256-CCM:DHE-RSA-AES256-CCM8:DHE-RSA-AES256-CCM:ECDHE-ECDSA-ARIA256-GCM-SHA384:ECDHE-ARIA256-GCM-SHA384:DHE-DSS-ARIA256-GCM-SHA384:DHE-RSA-ARIA256-GCM-SHA384:ADH-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-CCM8:ECDHE-ECDSA-AES128-CCM:DHE-RSA-AES128-CCM8:DHE-RSA-AES128-CCM:ECDHE-ECDSA-ARIA128-GCM-SHA256:ECDHE-ARIA128-GCM-SHA256:DHE-DSS-ARIA128-GCM-SHA256:DHE-RSA-ARIA128-GCM-SHA256:ADH-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:ECDHE-ECDSA-CAMELLIA256-SHA384:ECDHE-RSA-CAMELLIA256-SHA384:DHE-RSA-CAMELLIA256-SHA256:DHE-DSS-CAMELLIA256-SHA256:ADH-AES256-SHA256:ADH-CAMELLIA256-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:ECDHE-ECDSA-CAMELLIA128-SHA256:ECDHE-RSA-CAMELLIA128-SHA256:DHE-RSA-CAMELLIA128-SHA256:DHE-DSS-CAMELLIA128-SHA256:ADH-AES128-SHA256:ADH-CAMELLIA128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:AECDH-AES256-SHA:ADH-AES256-SHA:ADH-CAMELLIA256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:AECDH-AES128-SHA:ADH-AES128-SHA:ADH-CAMELLIA128-SHA:RSA-PSK-AES256-GCM-SHA384:DHE-PSK-AES256-GCM-SHA384:RSA-PSK-CHACHA20-POLY1305:DHE-PSK-CHACHA20-POLY1305:ECDHE-PSK-CHACHA20-POLY1305:DHE-PSK-AES256-CCM8:DHE-PSK-AES256-CCM:RSA-PSK-ARIA256-GCM-SHA384:DHE-PSK-ARIA256-GCM-SHA384:AES256-GCM-SHA384:AES256-CCM8:AES256-CCM:ARIA256-GCM-SHA384:PSK-AES256-GCM-SHA384:PSK-CHACHA20-POLY1305:PSK-AES256-CCM8:PSK-AES256-CCM:PSK-ARIA256-GCM-SHA384:RSA-PSK-AES128-GCM-SHA256:DHE-PSK-AES128-GCM-SHA256:DHE-PSK-AES128-CCM8:DHE-PSK-AES128-CCM:RSA-PSK-ARIA128-GCM-SHA256:DHE-PSK-ARIA128-GCM-SHA256:AES128-GCM-SHA256:AES128-CCM8:AES128-CCM:ARIA128-GCM-SHA256:PSK-AES128-GCM-SHA256:PSK-AES128-CCM8:PSK-AES128-CCM:PSK-ARIA128-GCM-SHA256:AES256-SHA256:CAMELLIA256-SHA256:AES128-SHA256:CAMELLIA128-SHA256:ECDHE-PSK-AES256-CBC-SHA384:ECDHE-PSK-AES256-CBC-SHA:SRP-DSS-AES-256-CBC-SHA:SRP-RSA-AES-256-CBC-SHA:SRP-AES-256-CBC-SHA:RSA-PSK-AES256-CBC-SHA384:DHE-PSK-AES256-CBC-SHA384:RSA-PSK-AES256-CBC-SHA:DHE-PSK-AES256-CBC-SHA:ECDHE-PSK-CAMELLIA256-SHA384:RSA-PSK-CAMELLIA256-SHA384:DHE-PSK-CAMELLIA256-SHA384:AES256-SHA:CAMELLIA256-SHA:PSK-AES256-CBC-SHA384:PSK-AES256-CBC-SHA:PSK-CAMELLIA256-SHA384:ECDHE-PSK-AES128-CBC-SHA256:ECDHE-PSK-AES128-CBC-SHA:SRP-DSS-AES-128-CBC-SHA:SRP-RSA-AES-128-CBC-SHA:SRP-AES-128-CBC-SHA:RSA-PSK-AES128-CBC-SHA256:DHE-PSK-AES128-CBC-SHA256:RSA-PSK-AES128-CBC-SHA:DHE-PSK-AES128-CBC-SHA:ECDHE-PSK-CAMELLIA128-SHA256:RSA-PSK-CAMELLIA128-SHA256:DHE-PSK-CAMELLIA128-SHA256:AES128-SHA:CAMELLIA128-SHA:PSK-AES128-CBC-SHA256:PSK-AES128-CBC-SHA:PSK-CAMELLIA128-SHA256"), 0 };
- #else
static const buffer default_ssl_cipher_list = { CONST_STR_LEN("HIGH"), 0 };
- #endif
p->ssl_ctxs = calloc(srv->config_context->used, sizeof(plugin_ssl_ctx));
force_assert(p->ssl_ctxs);
@@ -3337,11 +2636,7 @@ SETDEFAULTS_FUNC(mod_openssl_set_defaults)
if (buffer_string_is_empty(cpv->v.b)) break;
if (!mod_openssl_init_once_openssl(srv)) return HANDLER_ERROR;
ssl_ca_file = cpv->v.b;
- #ifdef WOLFSSL_VERSION
- cpv->v.v = mod_wolfssl_load_cacerts(ssl_ca_file, srv->errh);
- #else
cpv->v.v = mod_openssl_load_cacerts(ssl_ca_file, srv->errh);
- #endif
if (NULL != cpv->v.v) {
cpv->vtype = T_CONFIG_LOCAL;
ca_store = ((plugin_cacerts *)cpv->v.v)->certs;
@@ -3357,12 +2652,7 @@ SETDEFAULTS_FUNC(mod_openssl_set_defaults)
if (buffer_string_is_empty(cpv->v.b)) break;
if (!mod_openssl_init_once_openssl(srv)) return HANDLER_ERROR;
ssl_ca_dn_file = cpv->v.b;
- #ifdef WOLFSSL_VERSION
- cpv->v.v = mod_wolfssl_load_client_CA_file(ssl_ca_dn_file,
- srv->errh);
- #else
cpv->v.v = SSL_load_client_CA_file(ssl_ca_dn_file->ptr);
- #endif
if (NULL != cpv->v.v) {
cpv->vtype = T_CONFIG_LOCAL;
}
@@ -4083,17 +3373,6 @@ https_add_ssl_client_entries (request_st * const r, handler_ctx * const hctx)
}
{
- #ifdef WOLFSSL_VERSION
- byte buf[64];
- int bsz = (int)sizeof(buf);
- if (wolfSSL_X509_get_serial_number(xs, buf, &bsz) == WOLFSSL_SUCCESS) {
- char serialHex[128+1];
- li_tohex_uc(serialHex, sizeof(serialHex), (char *)buf, (size_t)bsz);
- http_header_env_set(r,
- CONST_STR_LEN("SSL_CLIENT_M_SERIAL"),
- serialHex, strlen(serialHex));
- }
- #else
ASN1_INTEGER *xsn = X509_get_serialNumber(xs);
BIGNUM *serialBN = ASN1_INTEGER_to_BN(xsn, NULL);
char *serialHex = BN_bn2hex(serialBN);
@@ -4102,7 +3381,6 @@ https_add_ssl_client_entries (request_st * const r, handler_ctx * const hctx)
serialHex, strlen(serialHex));
OPENSSL_free(serialHex);
BN_free(serialBN);
- #endif
}
if (!buffer_string_is_empty(hctx->conf.ssl_verifyclient_username)) {
@@ -4124,19 +3402,6 @@ https_add_ssl_client_entries (request_st * const r, handler_ctx * const hctx)
}
if (hctx->conf.ssl_verifyclient_export_cert) {
- #ifdef WOLFSSL_VERSION
- int dersz, pemsz;
- const unsigned char *der = wolfSSL_X509_get_der(xs, &dersz);
- pemsz = der ? wc_DerToPemEx(der, dersz, NULL, 0, NULL, CERT_TYPE) : 0;
- if (pemsz > 0) {
- buffer_string_prepare_copy(tb, pemsz);
- if (0 == wc_DerToPemEx(der, dersz, (byte *)tb->ptr, pemsz,
- NULL, CERT_TYPE))
- http_header_env_set(r,
- CONST_STR_LEN("SSL_CLIENT_CERT"),
- tb->ptr, (uint32_t)pemsz);
- }
- #else
BIO *bio;
if (NULL != (bio = BIO_new(BIO_s_mem()))) {
PEM_write_bio_X509(bio, xs);
@@ -4150,7 +3415,6 @@ https_add_ssl_client_entries (request_st * const r, handler_ctx * const hctx)
CONST_STR_LEN("SSL_CLIENT_CERT"),
CONST_BUF_LEN(tb));
}
- #endif
}
X509_free(xs);
}
@@ -4170,11 +3434,7 @@ http_cgi_ssl_env (request_st * const r, handler_ctx * const hctx)
char buf[LI_ITOSTRING_LENGTH];
s = SSL_CIPHER_get_name(cipher);
http_header_env_set(r, CONST_STR_LEN("SSL_CIPHER"), s, strlen(s));
- #ifdef WOLFSSL_VERSION
- usekeysize = wolfSSL_CIPHER_get_bits(cipher, &algkeysize);
- #else
usekeysize = SSL_CIPHER_get_bits(cipher, &algkeysize);
- #endif
if (0 == algkeysize) algkeysize = usekeysize;
http_header_env_set(r, CONST_STR_LEN("SSL_CIPHER_USEKEYSIZE"),
buf, li_itostrn(buf, sizeof(buf), usekeysize));
@@ -4273,8 +3533,7 @@ int mod_openssl_plugin_init (plugin *p)
#if defined(BORINGSSL_API_VERSION) \
- || defined(LIBRESSL_VERSION_NUMBER) \
- || defined(WOLFSSL_VERSION)
+ || defined(LIBRESSL_VERSION_NUMBER)
static int
mod_openssl_ssl_conf_proto_val (server *srv, plugin_config_socket *s, const buffer *b, int max)
@@ -4428,30 +3687,14 @@ mod_openssl_ssl_conf_cmd (server *srv, plugin_config_socket *s)
if (minb) {
int n = mod_openssl_ssl_conf_proto_val(srv, s, minb, 0);
- #ifndef WOLFSSL_VERSION
if (!SSL_CTX_set_min_proto_version(s->ssl_ctx, n))
rc = -1;
- #else
- /*(wolfSSL_CTX_SetMinVersion() alt uses enums with different values)*/
- switch (n) {
- case SSL3_VERSION: n = WOLFSSL_SSLV3; break;
- case TLS1_VERSION: n = WOLFSSL_TLSV1; break;
- case TLS1_1_VERSION: n = WOLFSSL_TLSV1_1; break;
- case TLS1_2_VERSION: n = WOLFSSL_TLSV1_2; break;
- case TLS1_3_VERSION: n = WOLFSSL_TLSV1_3; break;
- default: rc = -1; break;
- }
- if (wolfSSL_CTX_SetMinVersion(s->ssl_ctx, n) != WOLFSSL_SUCCESS)
- rc = -1;
- #endif
}
if (maxb) {
- #ifndef WOLFSSL_VERSION /*WolfSSL max ver is set at WolfSSL compile-time*/
int x = mod_openssl_ssl_conf_proto_val(srv, s, maxb, 1);
if (!SSL_CTX_set_max_proto_version(s->ssl_ctx, x))
rc = -1;
- #endif
}
if (cipherstring) {
@@ -4476,4 +3719,4 @@ mod_openssl_ssl_conf_cmd (server *srv, plugin_config_socket *s)
return rc;
}
-#endif /* BORINGSSL_API_VERSION || LIBRESSL_VERSION_NUMBER || WOLFSSL_VERSION */
+#endif /* BORINGSSL_API_VERSION || LIBRESSL_VERSION_NUMBER */
diff --git a/src/mod_wolfssl.c b/src/mod_wolfssl.c
new file mode 100644
index 00000000..7106ab19
--- /dev/null
+++ b/src/mod_wolfssl.c
@@ -0,0 +1,3447 @@
+/*
+ * mod_wolfssl - wolfSSL support for lighttpd
+ *
+ * Copyright(c) 2020 Glenn Strauss gstrauss()gluelogic.com All rights reserved
+ * License: BSD 3-clause (same as lighttpd)
+ */
+/*
+ * Note: If session tickets are -not- disabled with
+ * ssl.openssl.ssl-conf-cmd = ("Options" => "-SessionTicket")
+ * mod_wolfssl rotates server ticket encryption key (STEK) every 8 hours
+ * and keeps the prior two STEKs around, so ticket lifetime is 24 hours.
+ * This is fine for use with a single lighttpd instance, but with multiple
+ * lighttpd workers, no coordinated STEK (server ticket encryption key)
+ * rotation occurs unless ssl.stek-file is defined and maintained (preferred),
+ * or if some external job restarts lighttpd. Restarting lighttpd generates a
+ * new key that is shared by lighttpd workers for the lifetime of the new key.
+ * If the rotation period expires and lighttpd has not been restarted, and if
+ * ssl.stek-file is not in use, then lighttpd workers will generate new
+ * independent keys, making session tickets less effective for session
+ * resumption, since clients have a lower chance for future connections to
+ * reach the same lighttpd worker. However, things will still work, and a new
+ * session will be created if session resumption fails. Admins should plan to
+ * restart lighttpd at least every 8 hours if session tickets are enabled and
+ * multiple lighttpd workers are configured. Since that is likely disruptive,
+ * if multiple lighttpd workers are configured, ssl.stek-file should be
+ * defined and the file maintained externally.
+ */
+#include "first.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Note: mod_wolfssl.c is forked from mod_openssl.c
+ * Many internal symbol names in mod_wolfssl.c retain the mod_openssl_* prefix
+ * (wolfSSL provides an OpenSSL compatibility layer)
+ */
+
+#include "sys-crypto.h"
+#include <wolfssl/options.h>
+
+/* WolfSSL defines OPENSSL_VERSION_NUMBER 0x10001040L for OPENSSL_ALL
+ * or HAVE_LIGHTY. WolfSSL does not provide many interfaces added in
+ * OpenSSL 1.0.2, including SSL_CTX_set_cert_cb(), so it is curious that
+ * WolfSSL defines OPENSSL_VERSION_NUMBER 0x10100000L for WOLFSSL_APACHE_HTTPD*/
+#ifndef OPENSSL_EXTRA
+#define OPENSSL_EXTRA
+#endif
+#ifndef OPENSSL_ALL
+#define OPENSSL_ALL
+#endif
+#include <wolfssl/ssl.h>
+
+static char global_err_buf[WOLFSSL_MAX_ERROR_SZ];
+#undef ERR_error_string
+#define ERR_error_string(e,b) \
+ (wolfSSL_ERR_error_string_n((e),global_err_buf,WOLFSSL_MAX_ERROR_SZ), \
+ global_err_buf)
+/* WolfSSL does not provide OPENSSL_cleanse() */
+#define OPENSSL_cleanse(x,sz) safe_memclear((x),(sz))
+
+#if 0 /* symbols and definitions requires WolfSSL built with -DOPENSSL_EXTRA */
+#define SSL_TLSEXT_ERR_OK 0
+#define SSL_TLSEXT_ERR_ALERT_FATAL alert_fatal
+#define SSL_TLSEXT_ERR_NOACK alert_warning
+
+WOLFSSL_API void wolfSSL_set_verify_depth(WOLFSSL *ssl,int depth);
+
+WOLFSSL_API void wolfSSL_X509_NAME_free(WOLFSSL_X509_NAME* name);
+WOLFSSL_API int wolfSSL_X509_NAME_cmp(const WOLFSSL_X509_NAME* x, const WOLFSSL_X509_NAME* y);
+WOLFSSL_API WOLFSSL_X509_NAME* wolfSSL_X509_NAME_dup(WOLFSSL_X509_NAME*);
+WOLFSSL_API char* wolfSSL_X509_get_name_oneline(WOLFSSL_X509_NAME*, char*, int);
+
+WOLFSSL_API const char* wolfSSL_OBJ_nid2sn(int n);
+WOLFSSL_API int wolfSSL_OBJ_obj2nid(const WOLFSSL_ASN1_OBJECT *o);
+WOLFSSL_API WOLFSSL_ASN1_OBJECT * wolfSSL_X509_NAME_ENTRY_get_object(WOLFSSL_X509_NAME_ENTRY *ne);
+WOLFSSL_API WOLFSSL_X509_NAME_ENTRY *wolfSSL_X509_NAME_get_entry(WOLFSSL_X509_NAME *name, int loc);
+#endif
+
+#if 0 /* symbols and definitions requires WolfSSL built with -DOPENSSL_ALL */
+WOLFSSL_API WOLF_STACK_OF(WOLFSSL_X509_NAME) *wolfSSL_dup_CA_list( WOLF_STACK_OF(WOLFSSL_X509_NAME) *sk );
+/*wolfSSL_sk_X509_NAME_new()*/
+/*wolfSSL_sk_X509_NAME_push()*/
+#endif
+
+#include "base.h"
+#include "fdevent.h"
+#include "http_header.h"
+#include "http_kv.h"
+#include "log.h"
+#include "plugin.h"
+#include "safe_memclear.h"
+
+typedef struct {
+ /* SNI per host: with COMP_SERVER_SOCKET, COMP_HTTP_SCHEME, COMP_HTTP_HOST */
+ buffer *ssl_pemfile_pkey;
+ buffer *ssl_pemfile_x509;
+ buffer **ssl_pemfile_chain;
+ buffer *ssl_stapling;
+ const buffer *ssl_pemfile;
+ const buffer *ssl_privkey;
+ const buffer *ssl_stapling_file;
+ time_t ssl_stapling_loadts;
+ time_t ssl_stapling_nextts;
+ char must_staple;
+} plugin_cert;
+
+typedef struct {
+ WOLFSSL_CTX *ssl_ctx;
+} plugin_ssl_ctx;
+
+typedef struct {
+ STACK_OF(X509_NAME) *names;
+ X509_STORE *certs;
+} plugin_cacerts;
+
+typedef struct {
+ WOLFSSL_CTX *ssl_ctx; /* output from network_init_ssl() */
+
+ /*(used only during startup; not patched)*/
+ unsigned char ssl_enabled; /* only interesting for setting up listening sockets. don't use at runtime */
+ unsigned char ssl_honor_cipher_order; /* determine SSL cipher in server-preferred order, not client-order */
+ unsigned char ssl_empty_fragments; /* whether to not set SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */
+ unsigned char ssl_use_sslv2;
+ unsigned char ssl_use_sslv3;
+ const buffer *ssl_cipher_list;
+ const buffer *ssl_dh_file;
+ const buffer *ssl_ec_curve;
+ array *ssl_conf_cmd;
+
+ /*(copied from plugin_data for socket ssl_ctx config)*/
+ const plugin_cert *pc;
+ const plugin_cacerts *ssl_ca_file;
+ STACK_OF(X509_NAME) *ssl_ca_dn_file;
+ const buffer *ssl_ca_crl_file;
+ unsigned char ssl_verifyclient;
+ unsigned char ssl_verifyclient_enforce;
+ unsigned char ssl_verifyclient_depth;
+ unsigned char ssl_read_ahead;
+ unsigned char ssl_disable_client_renegotiation;
+} plugin_config_socket; /*(used at startup during configuration)*/
+
+typedef struct {
+ /* SNI per host: w/ COMP_SERVER_SOCKET, COMP_HTTP_SCHEME, COMP_HTTP_HOST */
+ plugin_cert *pc;
+ const plugin_cacerts *ssl_ca_file;
+ STACK_OF(X509_NAME) *ssl_ca_dn_file;
+ const buffer *ssl_ca_crl_file;
+
+ unsigned char ssl_verifyclient;
+ unsigned char ssl_verifyclient_enforce;
+ unsigned char ssl_verifyclient_depth;
+ unsigned char ssl_verifyclient_export_cert;
+ unsigned char ssl_read_ahead;
+ unsigned char ssl_log_noise;
+ unsigned char ssl_disable_client_renegotiation;
+ const buffer *ssl_verifyclient_username;
+ const buffer *ssl_acme_tls_1;
+} plugin_config;
+
+typedef struct {
+ PLUGIN_DATA;
+ plugin_ssl_ctx *ssl_ctxs;
+ plugin_config defaults;
+ server *srv;
+ array *cafiles;
+ const char *ssl_stek_file;
+} plugin_data;
+
+static int ssl_is_init;
+/* need assigned p->id for deep access of module handler_ctx for connection
+ * i.e. handler_ctx *hctx = con->plugin_ctx[plugin_data_singleton->id]; */
+static plugin_data *plugin_data_singleton;
+#define LOCAL_SEND_BUFSIZE (16 * 1024)
+static char *local_send_buffer;
+
+typedef struct {
+ WOLFSSL *ssl;
+ request_st *r;
+ connection *con;
+ short renegotiations; /* count of SSL_CB_HANDSHAKE_START */
+ short close_notify;
+ unsigned short alpn;
+ plugin_config conf;
+ buffer *tmp_buf;
+ log_error_st *errh;
+} handler_ctx;
+
+
+static handler_ctx *
+handler_ctx_init (void)
+{
+ handler_ctx *hctx = calloc(1, sizeof(*hctx));
+ force_assert(hctx);
+ return hctx;
+}
+
+
+static void
+handler_ctx_free (handler_ctx *hctx)
+{
+ if (hctx->ssl) SSL_free(hctx->ssl);
+ free(hctx);
+}
+
+
+#ifdef HAVE_SESSION_TICKET
+/* ssl/ssl_local.h */
+#define TLSEXT_KEYNAME_LENGTH 16
+#define TLSEXT_TICK_KEY_LENGTH 32
+
+/* openssl has a huge number of interfaces, but not the most useful;
+ * construct our own session ticket encryption key structure */
+typedef struct tlsext_ticket_key_st {
+ time_t active_ts; /* tickets not issued w/ key until activation timestamp */
+ time_t expire_ts; /* key not valid after expiration timestamp */
+ unsigned char tick_key_name[TLSEXT_KEYNAME_LENGTH];
+ unsigned char tick_hmac_key[TLSEXT_TICK_KEY_LENGTH];
+ unsigned char tick_aes_key[TLSEXT_TICK_KEY_LENGTH];
+} tlsext_ticket_key_t;
+
+static tlsext_ticket_key_t session_ticket_keys[4];
+static time_t stek_rotate_ts;
+
+
+static int
+mod_openssl_session_ticket_key_generate (time_t active_ts, time_t expire_ts)
+{
+ /* openssl RAND_*bytes() functions are called multiple times since the
+ * funcs might have a 32-byte limit on number of bytes returned each call
+ *
+ * (Note: session ticket encryption key generation is not expected to fail)
+ *
+ * 3 keys are stored in session_ticket_keys[]
+ * The 4th element of session_ticket_keys[] is used for STEK construction
+ */
+ /*(RAND_priv_bytes() not in openssl 1.1.0; introduced in openssl 1.1.1)*/
+ #define RAND_priv_bytes(x,sz) RAND_bytes((x),(sz))
+ if (RAND_bytes(session_ticket_keys[3].tick_key_name,
+ TLSEXT_KEYNAME_LENGTH) <= 0
+ || RAND_priv_bytes(session_ticket_keys[3].tick_hmac_key,
+ TLSEXT_TICK_KEY_LENGTH) <= 0
+ || RAND_priv_bytes(session_ticket_keys[3].tick_aes_key,
+ TLSEXT_TICK_KEY_LENGTH) <= 0)
+ return 0;
+ session_ticket_keys[3].active_ts = active_ts;
+ session_ticket_keys[3].expire_ts = expire_ts;
+ return 1;
+}
+
+
+static void
+mod_openssl_session_ticket_key_rotate (void)
+{
+ /* discard oldest key (session_ticket_keys[2]) and put newest key first
+ * 3 keys are stored in session_ticket_keys[0], [1], [2]
+ * session_ticket_keys[3] is used to construct and pass new STEK */
+
+ session_ticket_keys[2] = session_ticket_keys[1];
+ session_ticket_keys[1] = session_ticket_keys[0];
+ /*memmove(session_ticket_keys+1,
+ session_ticket_keys+0, sizeof(tlsext_ticket_key_t)*2);*/
+ session_ticket_keys[0] = session_ticket_keys[3];
+
+ OPENSSL_cleanse(session_ticket_keys+3, sizeof(tlsext_ticket_key_t));
+}
+
+
+static tlsext_ticket_key_t *
+tlsext_ticket_key_get (void)
+{
+ const time_t cur_ts = log_epoch_secs;
+ const int e = sizeof(session_ticket_keys)/sizeof(*session_ticket_keys) - 1;
+ for (int i = 0; i < e; ++i) {
+ if (session_ticket_keys[i].active_ts > cur_ts) continue;
+ if (session_ticket_keys[i].expire_ts < cur_ts) continue;
+ return &session_ticket_keys[i];
+ }
+ return NULL;
+}
+
+
+static tlsext_ticket_key_t *
+tlsext_ticket_key_find (unsigned char key_name[16], int *refresh)
+{
+ *refresh = 0;
+ const time_t cur_ts = log_epoch_secs;
+ const int e = sizeof(session_ticket_keys)/sizeof(*session_ticket_keys) - 1;
+ for (int i = 0; i < e; ++i) {
+ if (session_ticket_keys[i].expire_ts < cur_ts) continue;
+ if (0 == memcmp(session_ticket_keys[i].tick_key_name, key_name, 16))
+ return &session_ticket_keys[i];
+ if (session_ticket_keys[i].active_ts <= cur_ts)
+ *refresh = 1; /* newer active key is available */
+ }
+ return NULL;
+}
+
+
+static void
+tlsext_ticket_wipe_expired (const time_t cur_ts)
+{
+ const int e = sizeof(session_ticket_keys)/sizeof(*session_ticket_keys) - 1;
+ for (int i = 0; i < e; ++i) {
+ if (session_ticket_keys[i].expire_ts != 0
+ && session_ticket_keys[i].expire_ts < cur_ts)
+ OPENSSL_cleanse(session_ticket_keys+i, sizeof(tlsext_ticket_key_t));
+ }
+}
+
+
+/* based on reference implementation from openssl 1.1.1g man page
+ * man SSL_CTX_set_tlsext_ticket_key_cb
+ * but openssl code uses EVP_aes_256_cbc() instead of EVP_aes_128_cbc()
+ */
+static int
+ssl_tlsext_ticket_key_cb (SSL *s, unsigned char key_name[16],
+ unsigned char iv[EVP_MAX_IV_LENGTH],
+ EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc)
+{
+ UNUSED(s);
+ if (enc) { /* create new session */
+ tlsext_ticket_key_t *k = tlsext_ticket_key_get();
+ if (NULL == k)
+ return 0; /* current key does not exist or is not valid */
+ memcpy(key_name, k->tick_key_name, 16);
+ if (RAND_bytes(iv, EVP_MAX_IV_LENGTH) <= 0)
+ return -1; /* insufficient random */
+ EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, k->tick_aes_key, iv);
+ HMAC_Init_ex(hctx, k->tick_hmac_key, sizeof(k->tick_hmac_key),
+ EVP_sha256(), NULL);
+ return 1;
+ }
+ else { /* retrieve session */
+ int refresh;
+ tlsext_ticket_key_t *k = tlsext_ticket_key_find(key_name, &refresh);
+ if (NULL == k)
+ return 0;
+ HMAC_Init_ex(hctx, k->tick_hmac_key, sizeof(k->tick_hmac_key),
+ EVP_sha256(), NULL);
+ EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, k->tick_aes_key, iv);
+ return refresh ? 2 : 1;
+ /* 'refresh' will trigger issuing new ticket for session
+ * even though the current ticket is still valid */
+ }
+}
+
+
+static int
+mod_openssl_session_ticket_key_file (const char *fn)
+{
+ /* session ticket encryption key (STEK)
+ *
+ * STEK file should be stored in non-persistent storage,
+ * e.g. /dev/shm/lighttpd/stek-file (in memory)
+ * with appropriate permissions set to keep stek-file from being
+ * read by other users. Where possible, systems should also be
+ * configured without swap.
+ *
+ * admin should schedule an independent job to periodically
+ * generate new STEK up to 3 times during key lifetime
+ * (lighttpd stores up to 3 keys)
+ *
+ * format of binary file is:
+ * 4-byte - format version (always 0; for use if format changes)
+ * 4-byte - activation timestamp
+ * 4-byte - expiration timestamp
+ * 16-byte - session ticket key name
+ * 32-byte - session ticket HMAC encrpytion key
+ * 32-byte - session ticket AES encrpytion key
+ *
+ * STEK file can be created with a command such as:
+ * dd if=/dev/random bs=1 count=80 status=none | \
+ * perl -e 'print pack("iii",0,time()+300,time()+86400),<>' \
+ * > STEK-file.$$ && mv STEK-file.$$ STEK-file
+ *
+ * The above delays activation time by 5 mins (+300 sec) to allow file to
+ * be propagated to other machines. (admin must handle this independently)
+ * If STEK generation is performed immediately prior to starting lighttpd,
+ * admin should activate keys immediately (without +300).
+ */
+ int buf[23]; /* 92 bytes */
+ int fd = fdevent_open_cloexec(fn, 1, O_RDONLY, 0);
+ if (fd < 0)
+ return 0;
+
+ ssize_t rd = read(fd, buf, sizeof(buf));
+ close(fd);
+
+ int rc = 0; /*(will retry on next check interval upon any error)*/
+ if (rd == sizeof(buf) && buf[0] == 0) { /*(format version 0)*/
+ session_ticket_keys[3].active_ts = buf[1];
+ session_ticket_keys[3].expire_ts = buf[2];
+ #ifndef __COVERITY__ /* intentional; hide from Coverity Scan */
+ /* intentionally copy 80 bytes into consecutive arrays
+ * tick_key_name[], tick_hmac_key[], tick_aes_key[] */
+ memcpy(&session_ticket_keys[3].tick_key_name, buf+3, 80);
+ #endif
+ rc = 1;
+ }
+
+ OPENSSL_cleanse(buf, sizeof(buf));
+ return rc;
+}
+
+
+static void
+mod_openssl_session_ticket_key_check (const plugin_data *p, const time_t cur_ts)
+{
+ int rotate = 0;
+ if (p->ssl_stek_file) {
+ struct stat st;
+ if (0 == stat(p->ssl_stek_file, &st) && st.st_mtime > stek_rotate_ts)
+ rotate = mod_openssl_session_ticket_key_file(p->ssl_stek_file);
+ tlsext_ticket_wipe_expired(cur_ts);
+ }
+ else if (cur_ts - 28800 >= stek_rotate_ts) /*(8 hours)*/
+ rotate = mod_openssl_session_ticket_key_generate(cur_ts, cur_ts+86400);
+
+ if (rotate) {
+ mod_openssl_session_ticket_key_rotate();
+ stek_rotate_ts = cur_ts;
+ }
+}
+
+#endif /* HAVE_SESSION_TICKET */
+
+
+#ifndef OPENSSL_NO_OCSP
+static int
+ssl_tlsext_status_cb(SSL *ssl, void *arg)
+{
+ #ifdef SSL_get_tlsext_status_type
+ if (TLSEXT_STATUSTYPE_ocsp != SSL_get_tlsext_status_type(ssl))
+ return SSL_TLSEXT_ERR_NOACK; /* ignore if not client OCSP request */
+ #endif
+
+ handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl);
+ buffer *ssl_stapling = hctx->conf.pc->ssl_stapling;
+ if (NULL == ssl_stapling) return SSL_TLSEXT_ERR_NOACK;
+ UNUSED(arg);
+
+ int len = (int)buffer_string_length(ssl_stapling);
+
+ /* WolfSSL does not require copy */
+ uint8_t *ocsp_resp = (uint8_t *)ssl_stapling->ptr;
+
+ if (!SSL_set_tlsext_status_ocsp_resp(ssl, ocsp_resp, len)) {
+ log_error(hctx->r->conf.errh, __FILE__, __LINE__,
+ "SSL: failed to set OCSP response for TLS server name %s: %s",
+ hctx->r->uri.authority.ptr, ERR_error_string(ERR_get_error(), NULL));
+ return SSL_TLSEXT_ERR_NOACK; /* ignore OCSP request if error occurs */
+ /*return SSL_TLSEXT_ERR_ALERT_FATAL;*/
+ }
+ return SSL_TLSEXT_ERR_OK;
+}
+#endif
+
+
+INIT_FUNC(mod_openssl_init)
+{
+ plugin_data_singleton = (plugin_data *)calloc(1, sizeof(plugin_data));
+ #ifdef DEBUG_WOLFSSL
+ wolfSSL_Debugging_ON();
+ #endif
+ return plugin_data_singleton;
+}
+
+
+static int mod_openssl_init_once_openssl (server *srv)
+{
+ if (ssl_is_init) return 1;
+
+ if (wolfSSL_Init() != WOLFSSL_SUCCESS) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: wolfSSL_Init() failed");
+ return 0;
+ }
+ ssl_is_init = 1;
+
+ if (0 == RAND_status()) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: not enough entropy in the pool");
+ return 0;
+ }
+
+ local_send_buffer = malloc(LOCAL_SEND_BUFSIZE);
+ force_assert(NULL != local_send_buffer);
+
+ return 1;
+}
+
+
+static void mod_openssl_free_openssl (void)
+{
+ if (!ssl_is_init) return;
+
+ #ifdef HAVE_SESSION_TICKET
+ OPENSSL_cleanse(session_ticket_keys, sizeof(session_ticket_keys));
+ stek_rotate_ts = 0;
+ #endif
+
+ if (wolfSSL_Cleanup() != WOLFSSL_SUCCESS) {
+ log_error(plugin_data_singleton->srv->errh, __FILE__, __LINE__,
+ "SSL: wolfSSL_Cleanup() failed");
+ }
+
+ free(local_send_buffer);
+ ssl_is_init = 0;
+}
+
+
+static void
+mod_wolfssl_free_der_certs (buffer **certs)
+{
+ if (NULL == certs) return;
+ for (int i = 0; NULL != certs[i]; ++i)
+ buffer_free(certs[i]);
+ free(certs);
+}
+
+
+static void
+mod_openssl_free_config (server *srv, plugin_data * const p)
+{
+ array_free(p->cafiles);
+
+ if (NULL != p->ssl_ctxs) {
+ SSL_CTX * const ssl_ctx_global_scope = p->ssl_ctxs->ssl_ctx;
+ /* free ssl_ctx from $SERVER["socket"] (if not copy of global scope) */
+ for (uint32_t i = 1; i < srv->config_context->used; ++i) {
+ plugin_ssl_ctx * const s = p->ssl_ctxs + i;
+ if (s->ssl_ctx && s->ssl_ctx != ssl_ctx_global_scope)
+ SSL_CTX_free(s->ssl_ctx);
+ }
+ /* free ssl_ctx from global scope */
+ if (ssl_ctx_global_scope)
+ SSL_CTX_free(ssl_ctx_global_scope);
+ free(p->ssl_ctxs);
+ }
+
+ if (NULL == p->cvlist) return;
+ /* (init i to 0 if global context; to 1 to skip empty global context) */
+ for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
+ config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
+ for (; -1 != cpv->k_id; ++cpv) {
+ switch (cpv->k_id) {
+ case 0: /* ssl.pemfile */
+ if (cpv->vtype == T_CONFIG_LOCAL) {
+ plugin_cert *pc = cpv->v.v;
+ buffer_free(pc->ssl_pemfile_pkey);
+ /*buffer_free(pc->ssl_pemfile_x509);*//*(part of chain)*/
+ mod_wolfssl_free_der_certs(pc->ssl_pemfile_chain);
+ buffer_free(pc->ssl_stapling);
+ }
+ break;
+ case 2: /* ssl.ca-file */
+ if (cpv->vtype == T_CONFIG_LOCAL) {
+ plugin_cacerts *cacerts = cpv->v.v;
+ sk_X509_NAME_pop_free(cacerts->names, X509_NAME_free);
+ X509_STORE_free(cacerts->certs);
+ free(cacerts);
+ }
+ break;
+ case 3: /* ssl.ca-dn-file */
+ if (cpv->vtype == T_CONFIG_LOCAL)
+ sk_X509_NAME_pop_free(cpv->v.v, X509_NAME_free);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+
+/* WolfSSL OpenSSL compat API does not wipe temp mem used; write our own */
+/* (pemfile might contain private key)*/
+/* code here is based on similar code in mod_nss */
+#include "base64.h"
+
+#define PEM_BEGIN "-----BEGIN "
+#define PEM_END "-----END "
+#define PEM_BEGIN_CERT "-----BEGIN CERTIFICATE-----"
+#define PEM_END_CERT "-----END CERTIFICATE-----"
+#define PEM_BEGIN_TRUSTED_CERT "-----BEGIN TRUSTED CERTIFICATE-----"
+#define PEM_END_TRUSTED_CERT "-----END TRUSTED CERTIFICATE-----"
+#define PEM_BEGIN_PKEY "-----BEGIN PRIVATE KEY-----"
+#define PEM_END_PKEY "-----END PRIVATE KEY-----"
+#define PEM_BEGIN_EC_PKEY "-----BEGIN EC PRIVATE KEY-----"
+#define PEM_END_EC_PKEY "-----END EC PRIVATE KEY-----"
+#define PEM_BEGIN_RSA_PKEY "-----BEGIN RSA PRIVATE KEY-----"
+#define PEM_END_RSA_PKEY "-----END RSA PRIVATE KEY-----"
+#define PEM_BEGIN_DSA_PKEY "-----BEGIN DSA PRIVATE KEY-----"
+#define PEM_END_DSA_PKEY "-----END DSA PRIVATE KEY-----"
+#define PEM_BEGIN_ANY_PKEY "-----BEGIN ANY PRIVATE KEY-----"
+#define PEM_END_ANY_PKEY "-----END ANY PRIVATE KEY-----"
+/* (not implemented: support to get password from user for encrypted key) */
+#define PEM_BEGIN_ENCRYPTED_PKEY "-----BEGIN ENCRYPTED PRIVATE KEY-----"
+#define PEM_END_ENCRYPTED_PKEY "-----END ENCRYPTED PRIVATE KEY-----"
+
+#define PEM_BEGIN_X509_CRL "-----BEGIN X509 CRL-----"
+#define PEM_END_X509_CRL "-----END X509 CRL-----"
+
+
+static buffer *
+mod_wolfssl_load_pem_file (const char *fn, log_error_st *errh, buffer ***chain)
+{
+ off_t dlen = 512*1024*1024;/*(arbitrary limit: 512 MB file; expect < 1 MB)*/
+ char *data = fdevent_load_file(fn, &dlen, errh, malloc, free);
+ if (NULL == data) return NULL;
+
+ buffer **certs = NULL;
+ int rc = -1;
+ do {
+ int count = 0;
+ char *b = data;
+ for (; (b = strstr(b, PEM_BEGIN_CERT)); b += sizeof(PEM_BEGIN_CERT)-1)
+ ++count;
+ b = data;
+ for (; (b = strstr(b, PEM_BEGIN_TRUSTED_CERT));
+ b += sizeof(PEM_BEGIN_TRUSTED_CERT)-1)
+ ++count;
+ if (0 == count) {
+ rc = 0;
+ break;
+ }
+
+ certs = malloc((count+1) * sizeof(buffer *));
+ force_assert(NULL != certs);
+ certs[count] = NULL;
+ for (int i = 0; i < count; ++i)
+ certs[i] = buffer_init();
+
+ buffer *der;
+ int i = 0;
+ for (char *e = data; (b = strstr(e, PEM_BEGIN_CERT)); ++i) {
+ b += sizeof(PEM_BEGIN_CERT)-1;
+ if (*b == '\r') ++b;
+ if (*b == '\n') ++b;
+ e = strstr(b, PEM_END_CERT);
+ if (NULL == e) break;
+ uint32_t len = (uint32_t)(e - b);
+ e += sizeof(PEM_END_CERT)-1;
+ if (i >= count) break; /*(should not happen)*/
+ der = certs[i];
+ if (NULL == buffer_append_base64_decode(der,b,len,BASE64_STANDARD))
+ break;
+ }
+ for (char *e = data; (b = strstr(e, PEM_BEGIN_TRUSTED_CERT)); ++i) {
+ b += sizeof(PEM_BEGIN_TRUSTED_CERT)-1;
+ if (*b == '\r') ++b;
+ if (*b == '\n') ++b;
+ e = strstr(b, PEM_END_TRUSTED_CERT);
+ if (NULL == e) break;
+ uint32_t len = (uint32_t)(e - b);
+ e += sizeof(PEM_END_TRUSTED_CERT)-1;
+ if (i >= count) break; /*(should not happen)*/
+ der = certs[i];
+ if (NULL == buffer_append_base64_decode(der,b,len,BASE64_STANDARD))
+ break;
+ }
+ if (i == count)
+ rc = 0;
+ else
+ errno = EIO;
+ } while (0);
+
+ if (dlen) safe_memclear(data, dlen);
+ free(data);
+
+ if (rc < 0) {
+ log_perror(errh, __FILE__, __LINE__, "error loading %s", fn);
+ mod_wolfssl_free_der_certs(certs);
+ certs = NULL;
+ }
+
+ *chain = certs;
+ return certs ? certs[0] : NULL;
+}
+
+
+static buffer *
+mod_wolfssl_evp_pkey_load_pem_file (const char *fn, log_error_st *errh)
+{
+ off_t dlen = 512*1024*1024;/*(arbitrary limit: 512 MB file; expect < 1 MB)*/
+ char *data = fdevent_load_file(fn, &dlen, errh, malloc, free);
+ if (NULL == data) return NULL;
+
+ buffer *pkey = NULL;
+ int rc = -1;
+ do {
+ /*(expecting single private key in file, so first match)*/
+ char *b, *e;
+ if ((b = strstr(data, PEM_BEGIN_PKEY))
+ && (e = strstr(b, PEM_END_PKEY)))
+ b += sizeof(PEM_BEGIN_PKEY)-1;
+ else if ((b = strstr(data, PEM_BEGIN_EC_PKEY))
+ && (e = strstr(b, PEM_END_EC_PKEY)))
+ b += sizeof(PEM_BEGIN_EC_PKEY)-1;
+ else if ((b = strstr(data, PEM_BEGIN_RSA_PKEY))
+ && (e = strstr(b, PEM_END_RSA_PKEY)))
+ b += sizeof(PEM_BEGIN_RSA_PKEY)-1;
+ else if ((b = strstr(data, PEM_BEGIN_DSA_PKEY))
+ && (e = strstr(b, PEM_END_DSA_PKEY)))
+ b += sizeof(PEM_BEGIN_DSA_PKEY)-1;
+ else if ((b = strstr(data, PEM_BEGIN_ANY_PKEY))
+ && (e = strstr(b, PEM_END_ANY_PKEY)))
+ b += sizeof(PEM_BEGIN_ANY_PKEY)-1;
+ else
+ break;
+ if (*b == '\r') ++b;
+ if (*b == '\n') ++b;
+
+ pkey = buffer_init();
+ size_t len = (size_t)(e - b);
+ if (NULL == buffer_append_base64_decode(pkey, b, len, BASE64_STANDARD))
+ break;
+ rc = 0;
+ } while (0);
+
+ if (dlen) safe_memclear(data, dlen);
+ free(data);
+
+ if (rc < 0) {
+ log_error(errh, __FILE__, __LINE__, "%s() %s", __func__, fn);
+ return NULL;
+ }
+
+ return pkey;
+}
+
+
+static int
+mod_wolfssl_CTX_use_certificate_chain_file (WOLFSSL_CTX *ssl_ctx, const char *fn, log_error_st *errh)
+{
+ /* (While it should be possible to parse DERs from (buffer **)
+ * s->pc->ssl_pemfile_chain, it is simpler to re-read file and use the
+ * built-in wolfSSL_CTX_use_certificate_chain_buffer() interface) */
+ off_t dlen = 4*1024*1024;/*(arbitrary limit: 4 MB file; expect < 1 KB)*/
+ char *data = fdevent_load_file(fn, &dlen, errh, malloc, free);
+ if (NULL == data) return -1;
+
+ int rc = wolfSSL_CTX_use_certificate_chain_buffer(ssl_ctx,
+ (unsigned char *)data,
+ (long)dlen);
+
+ if (dlen) safe_memclear(data, dlen);
+ free(data);
+
+ if (rc == WOLFSSL_SUCCESS)
+ return 1;
+
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: %s %s", ERR_error_string(rc, NULL), fn);
+ return 0;
+}
+
+
+static STACK_OF(X509_NAME) *
+mod_wolfssl_load_client_CA_file (const buffer *ssl_ca_file, log_error_st *errh)
+{
+ /* similar to wolfSSL_load_client_CA_file(), plus some processing */
+ buffer **certs = NULL;
+ if (NULL == mod_wolfssl_load_pem_file(ssl_ca_file->ptr, errh, &certs)) {
+ #ifdef __clang_analyzer__
+ mod_wolfssl_free_der_certs(certs); /*unnecessary; quiet clang analyzer*/
+ #endif
+ return NULL;
+ }
+
+ WOLF_STACK_OF(WOLFSSL_X509_NAME) *canames = wolfSSL_sk_X509_NAME_new(NULL);
+ if (NULL == canames) {
+ mod_wolfssl_free_der_certs(certs);
+ return NULL;
+ }
+
+ for (int i = 0; NULL != certs[i]; ++i) {
+ WOLFSSL_X509 *ca =
+ wolfSSL_X509_load_certificate_buffer((unsigned char *)certs[i]->ptr,
+ (int)
+ buffer_string_length(certs[i]),
+ WOLFSSL_FILETYPE_ASN1);
+ WOLFSSL_X509_NAME *subj = NULL;
+ if (NULL == ca
+ || NULL == (subj = wolfSSL_X509_get_subject_name(ca))
+ || 0 != wolfSSL_sk_X509_NAME_push(canames,
+ wolfSSL_X509_NAME_dup(subj))) {
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: couldn't read X509 certificates from '%s'",
+ ssl_ca_file->ptr);
+ if (subj) wolfSSL_X509_NAME_free(subj);
+ if (ca) wolfSSL_X509_free(ca);
+ wolfSSL_sk_X509_NAME_free(canames);
+ mod_wolfssl_free_der_certs(certs);
+ return NULL;
+ }
+
+ wolfSSL_X509_free(ca);
+ }
+
+ mod_wolfssl_free_der_certs(certs);
+ return canames;
+}
+
+
+static plugin_cacerts *
+mod_wolfssl_load_cacerts (const buffer *ssl_ca_file, log_error_st *errh)
+{
+ /* similar to mod_wolfSSL_load_client_CA_file(), plus some processing */
+ /* similar to wolfSSL_load_client_CA_file(), plus some processing */
+ buffer **certs = NULL;
+ if (NULL == mod_wolfssl_load_pem_file(ssl_ca_file->ptr, errh, &certs)) {
+ #ifdef __clang_analyzer__
+ mod_wolfssl_free_der_certs(certs); /*unnecessary; quiet clang analyzer*/
+ #endif
+ return NULL;
+ }
+
+ WOLFSSL_X509_STORE *castore = wolfSSL_X509_STORE_new();
+ if (NULL == castore) {
+ mod_wolfssl_free_der_certs(certs);
+ return NULL;
+ }
+
+ WOLF_STACK_OF(WOLFSSL_X509_NAME) *canames = wolfSSL_sk_X509_NAME_new(NULL);
+ if (NULL == canames) {
+ wolfSSL_X509_STORE_free(castore);
+ mod_wolfssl_free_der_certs(certs);
+ return NULL;
+ }
+
+ for (int i = 0; NULL != certs[i]; ++i) {
+ WOLFSSL_X509 *ca =
+ wolfSSL_X509_load_certificate_buffer((unsigned char *)certs[i]->ptr,
+ (int)
+ buffer_string_length(certs[i]),
+ WOLFSSL_FILETYPE_ASN1);
+ WOLFSSL_X509_NAME *subj = NULL;
+ if (NULL == ca || !wolfSSL_X509_STORE_add_cert(castore, ca)
+ || NULL == (subj = wolfSSL_X509_get_subject_name(ca))
+ || 0 != wolfSSL_sk_X509_NAME_push(canames,
+ wolfSSL_X509_NAME_dup(subj))) {
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: couldn't read X509 certificates from '%s'",
+ ssl_ca_file->ptr);
+ if (subj) wolfSSL_X509_NAME_free(subj);
+ if (ca) wolfSSL_X509_free(ca);
+ wolfSSL_sk_X509_NAME_free(canames);
+ wolfSSL_X509_STORE_free(castore);
+ mod_wolfssl_free_der_certs(certs);
+ return NULL;
+ }
+
+ wolfSSL_X509_free(ca);
+ }
+
+ mod_wolfssl_free_der_certs(certs);
+
+ plugin_cacerts *cacerts = malloc(sizeof(plugin_cacerts));
+ force_assert(cacerts);
+
+ cacerts->names = canames;
+ cacerts->certs = castore;
+ return cacerts;
+}
+
+
+static int
+mod_wolfssl_load_cacrls (WOLFSSL_CTX *ssl_ctx, const buffer *ssl_ca_crl_file, server *srv)
+{
+ #ifdef HAVE_CRL /* <wolfssl/options.h> */
+ int rc = wolfSSL_CTX_EnableCRL(ssl_ctx,
+ WOLFSSL_CRL_CHECK | WOLFSSL_CRL_CHECKALL);
+ if (rc != WOLFSSL_SUCCESS) return 0;
+
+ const char *fn = ssl_ca_crl_file->ptr;
+ off_t dlen = 512*1024*1024;/*(arbitrary limit: 512 MB file; expect < 1 MB)*/
+ char *data = fdevent_load_file(fn, &dlen, srv->errh, malloc, free);
+ if (NULL == data) return 0;
+
+ rc = wolfSSL_CTX_LoadCRLBuffer(ssl_ctx, (byte *)data, (long)dlen,
+ WOLFSSL_FILETYPE_PEM);
+
+ if (dlen) safe_memclear(data, dlen);
+ free(data);
+
+ if (rc == WOLFSSL_SUCCESS)
+ return 1;
+
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: %s %s", ERR_error_string(rc, NULL), fn);
+ return 0;
+ #else
+ UNUSED(ssl_ctx);
+ log_error(srv->errh, __FILE__, __LINE__,
+ "WolfSSL not built with CRL support; ignoring %s", ssl_ca_crl_file->ptr);
+ return WOLFSSL_FAILURE;
+ #endif
+}
+
+
+static int
+mod_wolfssl_load_verify_locn (SSL_CTX *ssl_ctx, const buffer *b, server *srv)
+{
+ const char *fn = b->ptr;
+ off_t dlen = 512*1024*1024;/*(arbitrary limit: 512 MB file; expect < 1 MB)*/
+ char *data = fdevent_load_file(fn, &dlen, srv->errh, malloc, free);
+ if (NULL == data) return 0;
+
+ int rc = wolfSSL_CTX_load_verify_buffer(ssl_ctx, (unsigned char *)data,
+ (long)dlen, WOLFSSL_FILETYPE_PEM);
+
+ if (dlen) safe_memclear(data, dlen);
+ free(data);
+
+ if (rc == WOLFSSL_SUCCESS)
+ return 1;
+
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: %s %s", ERR_error_string(rc, NULL), fn);
+ return 0;
+}
+
+
+static int
+mod_wolfssl_load_ca_files (SSL_CTX *ssl_ctx, plugin_data *p, server *srv)
+{
+ /* load all ssl.ca-files specified in the config into each SSL_CTX */
+
+ for (uint32_t i = 0, used = p->cafiles->used; i < used; ++i) {
+ const buffer *b = &((data_string *)p->cafiles->data[i])->value;
+ if (!mod_wolfssl_load_verify_locn(ssl_ctx, b, srv))
+ return 0;
+ }
+ return 1;
+}
+
+
+FREE_FUNC(mod_openssl_free)
+{
+ plugin_data *p = p_d;
+ if (NULL == p->srv) return;
+ mod_openssl_free_config(p->srv, p);
+ mod_openssl_free_openssl();
+}
+
+
+static void
+mod_openssl_merge_config_cpv (plugin_config * const pconf, const config_plugin_value_t * const cpv)
+{
+ switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
+ case 0: /* ssl.pemfile */
+ if (cpv->vtype == T_CONFIG_LOCAL)
+ pconf->pc = cpv->v.v;
+ break;
+ case 1: /* ssl.privkey */
+ break;
+ case 2: /* ssl.ca-file */
+ if (cpv->vtype == T_CONFIG_LOCAL)
+ pconf->ssl_ca_file = cpv->v.v;
+ break;
+ case 3: /* ssl.ca-dn-file */
+ if (cpv->vtype == T_CONFIG_LOCAL)
+ pconf->ssl_ca_dn_file = cpv->v.v;
+ break;
+ case 4: /* ssl.ca-crl-file */
+ pconf->ssl_ca_crl_file = cpv->v.b;
+ break;
+ case 5: /* ssl.read-ahead */
+ pconf->ssl_read_ahead = (0 != cpv->v.u);
+ break;
+ case 6: /* ssl.disable-client-renegotiation */
+ pconf->ssl_disable_client_renegotiation = (0 != cpv->v.u);
+ break;
+ case 7: /* ssl.verifyclient.activate */
+ pconf->ssl_verifyclient = (0 != cpv->v.u);
+ break;
+ case 8: /* ssl.verifyclient.enforce */
+ pconf->ssl_verifyclient_enforce = (0 != cpv->v.u);
+ break;
+ case 9: /* ssl.verifyclient.depth */
+ pconf->ssl_verifyclient_depth = (unsigned char)cpv->v.shrt;
+ break;
+ case 10:/* ssl.verifyclient.username */
+ pconf->ssl_verifyclient_username = cpv->v.b;
+ break;
+ case 11:/* ssl.verifyclient.exportcert */
+ pconf->ssl_verifyclient_export_cert = (0 != cpv->v.u);
+ break;
+ case 12:/* ssl.acme-tls-1 */
+ pconf->ssl_acme_tls_1 = cpv->v.b;
+ break;
+ case 13:/* ssl.stapling-file */
+ break;
+ case 14:/* debug.log-ssl-noise */
+ pconf->ssl_log_noise = (0 != cpv->v.u);
+ break;
+ default:/* should not happen */
+ return;
+ }
+}
+
+
+static void
+mod_openssl_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv)
+{
+ do {
+ mod_openssl_merge_config_cpv(pconf, cpv);
+ } while ((++cpv)->k_id != -1);
+}
+
+
+static void
+mod_openssl_patch_config (request_st * const r, plugin_config * const pconf)
+{
+ plugin_data * const p = plugin_data_singleton;
+ memcpy(pconf, &p->defaults, sizeof(plugin_config));
+ for (int i = 1, used = p->nconfig; i < used; ++i) {
+ if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
+ mod_openssl_merge_config(pconf, p->cvlist + p->cvlist[i].v.u2[0]);
+ }
+}
+
+
+static int
+safer_X509_NAME_oneline(X509_NAME *name, char *buf, size_t sz)
+{
+ if (wolfSSL_X509_get_name_oneline(name, buf, (int)sz))
+ return (int)strlen(buf);
+ else {
+ buf[0] = '\0';
+ return -1;
+ }
+}
+
+
+static void
+ssl_info_callback (const SSL *ssl, int where, int ret)
+{
+ UNUSED(ret);
+
+ if (0 != (where & SSL_CB_HANDSHAKE_START)) {
+ handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl);
+ if (hctx->renegotiations >= 0) ++hctx->renegotiations;
+ }
+ #ifdef TLS1_3_VERSION
+ /* https://github.com/openssl/openssl/issues/5721
+ * "TLSv1.3 unexpected InfoCallback after handshake completed" */
+ if (0 != (where & SSL_CB_HANDSHAKE_DONE)) {
+ /* SSL_version() is valid after initial handshake completed */
+ SSL *ssl_nonconst;
+ *(const SSL **)&ssl_nonconst = ssl;
+ if (wolfSSL_GetVersion(ssl_nonconst) >= WOLFSSL_TLSV1_3) {
+ /* https://wiki.openssl.org/index.php/TLS1.3
+ * "Renegotiation is not possible in a TLSv1.3 connection" */
+ handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl);
+ hctx->renegotiations = -1;
+ }
+ }
+ #endif
+}
+
+/* https://wiki.openssl.org/index.php/Manual:SSL_CTX_set_verify(3)#EXAMPLES */
+static int
+verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
+{
+ char buf[256];
+ X509 *err_cert;
+ int err, depth;
+ SSL *ssl;
+ handler_ctx *hctx;
+
+ err = X509_STORE_CTX_get_error(ctx);
+ depth = X509_STORE_CTX_get_error_depth(ctx);
+
+ /*
+ * Retrieve the pointer to the SSL of the connection currently treated
+ * and the application specific data stored into the SSL object.
+ */
+ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+ hctx = (handler_ctx *) SSL_get_app_data(ssl);
+
+ /*
+ * Catch a too long certificate chain. The depth limit set using
+ * wolfSSL_CTX_set_verify_depth() is by purpose set to "limit+1" so
+ * that whenever the "depth>verify_depth" condition is met, we
+ * have violated the limit and want to log this error condition.
+ * We must do it here, because the CHAIN_TOO_LONG error would not
+ * be found explicitly; only errors introduced by cutting off the
+ * additional certificates would be logged.
+ */
+ if (depth > hctx->conf.ssl_verifyclient_depth) {
+ preverify_ok = 0;
+ err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+ X509_STORE_CTX_set_error(ctx, err);
+ }
+
+ if (preverify_ok && 0 == depth && NULL != hctx->conf.ssl_ca_dn_file) {
+ /* verify that client cert is issued by CA in ssl.ca-dn-file
+ * if both ssl.ca-dn-file and ssl.ca-file were configured */
+ STACK_OF(X509_NAME) * const cert_names = hctx->conf.ssl_ca_dn_file;
+ X509_NAME *issuer;
+ err_cert = ctx->current_cert;/*wolfSSL_X509_STORE_CTX_get_current_cert*/
+ if (NULL == err_cert) return !hctx->conf.ssl_verifyclient_enforce;
+ issuer = X509_get_issuer_name(err_cert);
+ #if 0 /*(?desirable/undesirable to have cert_names sorted?)*/
+ if (-1 != sk_X509_NAME_find(cert_names, issuer))
+ return preverify_ok; /* match */
+ #else
+ for (int i = 0, len = sk_X509_NAME_num(cert_names); i < len; ++i) {
+ if (0 == wolfSSL_X509_NAME_cmp(sk_X509_NAME_value(cert_names, i),
+ issuer))
+ return preverify_ok; /* match */
+ }
+ #endif
+
+ preverify_ok = 0;
+ err = X509_V_ERR_CERT_REJECTED;
+ X509_STORE_CTX_set_error(ctx, err);
+ }
+
+ if (preverify_ok) {
+ return preverify_ok;
+ }
+
+ err_cert = ctx->current_cert; /*wolfSSL_X509_STORE_CTX_get_current_cert()*/
+ if (NULL == err_cert) return !hctx->conf.ssl_verifyclient_enforce;
+ safer_X509_NAME_oneline(X509_get_subject_name(err_cert),buf,sizeof(buf));
+ log_error_st *errh = hctx->r->conf.errh;
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: verify error:num=%d:%s:depth=%d:subject=%s",
+ err, X509_verify_cert_error_string(err), depth, buf);
+
+ /*
+ * At this point, err contains the last verification error. We can use
+ * it for something special
+ */
+ if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY ||
+ err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT)) {
+ safer_X509_NAME_oneline(X509_get_issuer_name(err_cert),buf,sizeof(buf));
+ log_error(errh, __FILE__, __LINE__, "SSL: issuer=%s", buf);
+ }
+
+ return !hctx->conf.ssl_verifyclient_enforce;
+}
+
+static int
+mod_openssl_cert_cb (SSL *ssl, void *arg)
+{
+ handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl);
+ plugin_cert *pc = hctx->conf.pc;
+ UNUSED(arg);
+
+ if (NULL == pc->ssl_pemfile_x509 || NULL == pc->ssl_pemfile_pkey) {
+ /* x509/pkey available <=> pemfile was set <=> pemfile got patched:
+ * so this should never happen, unless you nest $SERVER["socket"] */
+ log_error(hctx->r->conf.errh, __FILE__, __LINE__,
+ "SSL: no certificate/private key for TLS server name %s",
+ hctx->r->uri.authority.ptr);
+ return 0;
+ }
+
+ /* first set certificate!
+ * setting private key checks whether certificate matches it */
+ buffer *cert = pc->ssl_pemfile_x509;
+ if (1 != wolfSSL_use_certificate_ASN1(ssl, (unsigned char *)cert->ptr,
+ (int)buffer_string_length(cert))) {
+ log_error(hctx->r->conf.errh, __FILE__, __LINE__,
+ "SSL: failed to set certificate for TLS server name %s: %s",
+ hctx->r->uri.authority.ptr, ERR_error_string(ERR_get_error(), NULL));
+ return 0;
+ }
+
+ buffer *pkey = pc->ssl_pemfile_pkey;
+ if (1 != wolfSSL_use_PrivateKey_buffer(ssl, (unsigned char *)pkey->ptr,
+ (int)buffer_string_length(pkey),
+ WOLFSSL_FILETYPE_ASN1)) {
+ log_error(hctx->r->conf.errh, __FILE__, __LINE__,
+ "SSL: failed to set private key for TLS server name %s: %s",
+ hctx->r->uri.authority.ptr, ERR_error_string(ERR_get_error(), NULL));
+ return 0;
+ }
+
+ if (hctx->conf.ssl_verifyclient) {
+ if (NULL == hctx->conf.ssl_ca_file) {
+ log_error(hctx->r->conf.errh, __FILE__, __LINE__,
+ "SSL: can't verify client without ssl.ca-file "
+ "for TLS server name %s", hctx->r->uri.authority.ptr);
+ return 0;
+ }
+ /* WolfSSL does not support setting per-session CA list;
+ * limitation is to per-CTX CA list, and is not changed after SNI */
+ int mode = SSL_VERIFY_PEER;
+ if (hctx->conf.ssl_verifyclient_enforce)
+ mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ wolfSSL_set_verify(ssl, mode, verify_callback);
+ wolfSSL_set_verify_depth(ssl, hctx->conf.ssl_verifyclient_depth + 1);
+ }
+ else {
+ wolfSSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
+ }
+
+ return 1;
+}
+
+#ifdef HAVE_TLS_EXTENSIONS
+static int
+mod_openssl_SNI (handler_ctx *hctx, const char *servername, size_t len)
+{
+ request_st * const r = hctx->r;
+ if (len >= 1024) { /*(expecting < 256; TLSEXT_MAXLEN_host_name is 255)*/
+ log_error(r->conf.errh, __FILE__, __LINE__,
+ "SSL: SNI name too long %.*s", (int)len, servername);
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+
+ /* use SNI to patch mod_openssl config and then reset COMP_HTTP_HOST */
+ buffer_copy_string_len(&r->uri.authority, servername, len);
+ buffer_to_lower(&r->uri.authority);
+ #if 0
+ /*(r->uri.authority used below for configuration before request read;
+ * revisit for h2)*/
+ if (0 != http_request_host_policy(&r->uri.authority,
+ r->conf.http_parseopts, 443))
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ #endif
+
+ r->conditional_is_valid |= (1 << COMP_HTTP_SCHEME)
+ | (1 << COMP_HTTP_HOST);
+ mod_openssl_patch_config(r, &hctx->conf);
+ /* reset COMP_HTTP_HOST so that conditions re-run after request hdrs read */
+ /*(done in response.c:config_cond_cache_reset() after request hdrs read)*/
+ /*config_cond_cache_reset_item(r, COMP_HTTP_HOST);*/
+ /*buffer_clear(&r->uri.authority);*/
+
+ return (mod_openssl_cert_cb(hctx->ssl, NULL) == 1)
+ ? SSL_TLSEXT_ERR_OK
+ : SSL_TLSEXT_ERR_ALERT_FATAL;
+}
+
+static int
+network_ssl_servername_callback (SSL *ssl, int *al, void *srv)
+{
+ handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl);
+ buffer_copy_string(&hctx->r->uri.scheme, "https");
+ UNUSED(al);
+ UNUSED(srv);
+
+ const char *servername;
+ size_t len = (size_t)
+ wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, (void **)&servername);
+ if (0 == len)
+ return SSL_TLSEXT_ERR_NOACK; /* client did not provide SNI */
+ #if 0 /* WolfSSL does not provide per-session SSL_set_read_ahead() */
+ int read_ahead = hctx->conf.ssl_read_ahead;
+ int rc = mod_openssl_SNI(hctx, servername, len);
+ if (!read_ahead && hctx->conf.ssl_read_ahead)
+ SSL_set_read_ahead(ssl, hctx->conf.ssl_read_ahead);
+ return rc;
+ #else
+ return mod_openssl_SNI(hctx, servername, len);
+ #endif
+}
+#endif
+
+
+#ifndef OPENSSL_NO_OCSP
+
+#define OCSP_RESPONSE OcspResponse
+#define OCSP_RESPONSE_free wolfSSL_OCSP_RESPONSE_free
+#define d2i_OCSP_RESPONSE_bio wolfSSL_d2i_OCSP_RESPONSE_bio
+#define d2i_OCSP_RESPONSE wolfSSL_d2i_OCSP_RESPONSE
+#define i2d_OCSP_RESPONSE wolfSSL_i2d_OCSP_RESPONSE
+
+static buffer *
+mod_openssl_load_stapling_file (const char *file, log_error_st *errh, buffer *b)
+{
+ /* load stapling .der into buffer *b only if successful
+ *
+ * Note: for some TLS libs, the OCSP stapling response is not copied when
+ * assigned to a session (and is reasonable since not changed frequently)
+ * - BoringSSL SSL_set_ocsp_response()
+ * - WolfSSL SSL_set_tlsext_status_ocsp_resp() (differs from OpenSSL API)
+ * Therefore, there is a potential race condition if the OCSP response is
+ * assigned to the session during the handshake and the Server Hello is
+ * partially sent, AND (unlikely, if possible at all), the TLS library is
+ * in the middle of reading this OSCP response buffer. If the OCSP response
+ * is replaced due to an updated ssl.stapling-file (checked periodically),
+ * AND the buffer is resized, this would be a problem. Resizing the buffer
+ * is unlikely since updated OSCP response for same certificate are
+ * typically the same size with the signature and dates refreshed.
+ */
+
+ /* load raw .der file */
+ off_t dlen = 1*1024*1024;/*(arbitrary limit: 1 MB file; expect < 1 KB)*/
+ char *data = fdevent_load_file(file, &dlen, errh, malloc, free);
+ if (NULL == data) return NULL;
+
+ if (NULL == b)
+ b = buffer_init();
+ else if (b->ptr)
+ free(b->ptr);
+ b->ptr = data;
+ b->used = (uint32_t)dlen;
+ b->size = (uint32_t)dlen+1;
+ return b;
+}
+
+
+static time_t
+mod_openssl_asn1_time_to_posix (ASN1_TIME *asn1time)
+{
+ /* Note: this does not check for integer overflow of time_t! */
+ int day, sec;
+ return wolfSSL_ASN1_TIME_diff(&day, &sec, NULL, asn1time)
+ ? log_epoch_secs + day*86400 + sec
+ : (time_t)-1;
+}
+
+
+static time_t
+mod_openssl_ocsp_next_update (plugin_cert *pc)
+{
+ #if defined(WOLFSSL_VERSION)
+ /* XXX: future TODO */
+ UNUSED(pc);
+ (void)mod_openssl_asn1_time_to_posix(NULL);
+ return (time_t)-1; /*(not implemented)*/
+ #else
+ buffer *der = pc->ssl_stapling;
+ const unsigned char *p = (unsigned char *)der->ptr; /*(p gets modified)*/
+ OCSP_RESPONSE *ocsp = d2i_OCSP_RESPONSE(NULL,&p,buffer_string_length(der));
+ if (NULL == ocsp) return (time_t)-1;
+ OCSP_BASICRESP *bs = OCSP_response_get1_basic(ocsp);
+ if (NULL == bs) {
+ OCSP_RESPONSE_free(ocsp);
+ return (time_t)-1;
+ }
+
+ /* XXX: should save and evaluate cert status returned by these calls */
+ ASN1_TIME *nextupd = NULL;
+ #ifdef WOLFSSL_VERSION /* WolfSSL limitation */
+ /* WolfSSL does not provide OCSP_resp_get0() OCSP_single_get0_status() */
+ /* (inactive code path; alternative path followed in #if above for WolfSSL)
+ * (chain not currently available in mod_openssl when used with WolfSSL)
+ * (For WolfSSL, pc->ssl_pemfile_chain might not be filled in with actual
+ * chain, but is used to store (buffer **) of DER decoded from PEM certs
+ * read from ssl.pemfile, which may be a single cert, pc->ssl_pemfile_x509.
+ * The chain is not calculated or filled in if single cert, and neither are
+ * (X509 *), though (X509 *) could be temporarily created to calculated
+ * (OCSP_CERTID *), which additionally could be calculated once at startup)
+ */
+ OCSP_CERTID *id = (NULL != pc->ssl_pemfile_chain)
+ ? OCSP_cert_to_id(NULL, pc->ssl_pemfile_x509,
+ sk_X509_value(pc->ssl_pemfile_chain, 0))
+ : NULL;
+ if (id == NULL) {
+ OCSP_BASICRESP_free(bs);
+ OCSP_RESPONSE_free(ocsp);
+ return (time_t)-1;
+ }
+ OCSP_resp_find_status(bs, id, NULL, NULL, NULL, NULL, &nextupd);
+ OCSP_CERTID_free(id);
+ #else
+ OCSP_single_get0_status(OCSP_resp_get0(bs, 0), NULL, NULL, NULL, &nextupd);
+ #endif
+ time_t t = nextupd ? mod_openssl_asn1_time_to_posix(nextupd) : (time_t)-1;
+
+ /* Note: trust external process which creates ssl.stapling-file to verify
+ * (as well as to validate certificate status)
+ * future: verify OCSP response here to double-check */
+
+ OCSP_BASICRESP_free(bs);
+ OCSP_RESPONSE_free(ocsp);
+
+ return t;
+ #endif
+}
+
+
+static int
+mod_openssl_reload_stapling_file (server *srv, plugin_cert *pc, const time_t cur_ts)
+{
+ buffer *b = mod_openssl_load_stapling_file(pc->ssl_stapling_file->ptr,
+ srv->errh, pc->ssl_stapling);
+ if (!b) return 0;
+
+ pc->ssl_stapling = b; /*(unchanged unless orig was NULL)*/
+ pc->ssl_stapling_loadts = cur_ts;
+ pc->ssl_stapling_nextts = mod_openssl_ocsp_next_update(pc);
+ if (pc->ssl_stapling_nextts == (time_t)-1) {
+ /* "Next Update" might not be provided by OCSP responder
+ * Use 3600 sec (1 hour) in that case. */
+ /* retry in 1 hour if unable to determine Next Update */
+ pc->ssl_stapling_nextts = cur_ts + 3600;
+ pc->ssl_stapling_loadts = 0;
+ }
+
+ return 1;
+}
+
+
+static int
+mod_openssl_refresh_stapling_file (server *srv, plugin_cert *pc, const time_t cur_ts)
+{
+ if (pc->ssl_stapling && pc->ssl_stapling_nextts - 256 > cur_ts)
+ return 1; /* skip check for refresh unless close to expire */
+ struct stat st;
+ if (0 != stat(pc->ssl_stapling_file->ptr, &st)
+ || st.st_mtime <= pc->ssl_stapling_loadts) {
+ if (pc->ssl_stapling_nextts < cur_ts) {
+ /* discard expired OCSP stapling response */
+ buffer_free(pc->ssl_stapling);
+ pc->ssl_stapling = NULL;
+ if (pc->must_staple) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "certificate marked OCSP Must-Staple, "
+ "but OCSP response expired from ssl.stapling-file %s",
+ pc->ssl_stapling_file->ptr);
+ }
+ }
+ return 1;
+ }
+ return mod_openssl_reload_stapling_file(srv, pc, cur_ts);
+}
+
+
+static void
+mod_openssl_refresh_stapling_files (server *srv, const plugin_data *p, const time_t cur_ts)
+{
+ /* future: might construct array of (plugin_cert *) at startup
+ * to avoid the need to search for them here */
+ for (int i = 0, used = p->nconfig; i < used; ++i) {
+ const config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
+ for (; cpv->k_id != -1; ++cpv) {
+ if (cpv->k_id != 0) continue; /* k_id == 0 for ssl.pemfile */
+ if (cpv->vtype != T_CONFIG_LOCAL) continue;
+ plugin_cert *pc = cpv->v.v;
+ if (!buffer_string_is_empty(pc->ssl_stapling_file))
+ mod_openssl_refresh_stapling_file(srv, pc, cur_ts);
+ }
+ }
+}
+
+
+static int
+mod_openssl_crt_must_staple (const X509 *crt)
+{
+ /* XXX: TODO: not implemented */
+ UNUSED(crt);
+ return 0;
+}
+
+#endif /* OPENSSL_NO_OCSP */
+
+
+static plugin_cert *
+network_openssl_load_pemfile (server *srv, const buffer *pemfile, const buffer *privkey, const buffer *ssl_stapling_file)
+{
+ if (!mod_openssl_init_once_openssl(srv)) return NULL;
+
+ buffer **ssl_pemfile_chain = NULL;
+ buffer *ssl_pemfile_x509 =
+ mod_wolfssl_load_pem_file(pemfile->ptr, srv->errh, &ssl_pemfile_chain);
+ if (NULL == ssl_pemfile_x509)
+ return NULL;
+
+ buffer *ssl_pemfile_pkey =
+ mod_wolfssl_evp_pkey_load_pem_file(privkey->ptr, srv->errh);
+ if (NULL == ssl_pemfile_pkey) {
+ /*buffer_free(ssl_pemfile_x509);*//*(part of chain)*/
+ mod_wolfssl_free_der_certs(ssl_pemfile_chain);
+ return NULL;
+ }
+
+ /* X509_check_private_key() is a stub func (not implemented) in WolfSSL */
+
+ plugin_cert *pc = malloc(sizeof(plugin_cert));
+ force_assert(pc);
+ pc->ssl_pemfile_pkey = ssl_pemfile_pkey;
+ pc->ssl_pemfile_x509 = ssl_pemfile_x509;
+ pc->ssl_pemfile_chain= ssl_pemfile_chain;
+ pc->ssl_pemfile = pemfile;
+ pc->ssl_privkey = privkey;
+ pc->ssl_stapling = NULL;
+ pc->ssl_stapling_file= ssl_stapling_file;
+ pc->ssl_stapling_loadts = 0;
+ pc->ssl_stapling_nextts = 0;
+ #ifndef OPENSSL_NO_OCSP
+ /*(not implemented for WolfSSL, though could convert the DER to (X509 *),
+ * check Must-Staple, and then destroy (X509 *))*/
+ (void)mod_openssl_crt_must_staple(NULL);
+ pc->must_staple = 0;
+ #else
+ pc->must_staple = 0;
+ #endif
+
+ if (!buffer_string_is_empty(pc->ssl_stapling_file)) {
+ #ifndef OPENSSL_NO_OCSP
+ if (!mod_openssl_reload_stapling_file(srv, pc, log_epoch_secs)) {
+ /* continue without OCSP response if there is an error */
+ }
+ #else
+ log_error(srv->errh, __FILE__, __LINE__, "SSL:"
+ "OCSP stapling not supported; ignoring %s",
+ pc->ssl_stapling_file->ptr);
+ #endif
+ }
+ else if (pc->must_staple) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "certificate %s marked OCSP Must-Staple, "
+ "but ssl.stapling-file not provided", pemfile->ptr);
+ }
+
+ return pc;
+}
+
+
+#ifdef HAVE_TLS_EXTENSIONS
+
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+
+static int
+mod_openssl_acme_tls_1 (SSL *ssl, handler_ctx *hctx)
+{
+ buffer * const b = hctx->tmp_buf;
+ const buffer * const name = &hctx->r->uri.authority;
+ log_error_st * const errh = hctx->r->conf.errh;
+ buffer *ssl_pemfile_x509 = NULL;
+ buffer *ssl_pemfile_pkey = NULL;
+ buffer **ssl_pemfile_chain = NULL;
+ size_t len;
+ int rc = SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ /* check if acme-tls/1 protocol is enabled (path to dir of cert(s) is set)*/
+ if (buffer_string_is_empty(hctx->conf.ssl_acme_tls_1))
+ return SSL_TLSEXT_ERR_NOACK; /*(reuse value here for not-configured)*/
+ buffer_copy_buffer(b, hctx->conf.ssl_acme_tls_1);
+ buffer_append_slash(b);
+
+ /* check if SNI set server name (required for acme-tls/1 protocol)
+ * and perform simple path checks for no '/'
+ * and no leading '.' (e.g. ignore "." or ".." or anything beginning '.') */
+ if (buffer_string_is_empty(name)) return rc;
+ if (NULL != strchr(name->ptr, '/')) return rc;
+ if (name->ptr[0] == '.') return rc;
+ #if 0
+ if (0 != http_request_host_policy(name,hctx->r->conf.http_parseopts,443))
+ return rc;
+ #endif
+ buffer_append_string_buffer(b, name);
+ len = buffer_string_length(b);
+
+ do {
+ buffer_append_string_len(b, CONST_STR_LEN(".crt.pem"));
+ ssl_pemfile_x509 =
+ mod_wolfssl_load_pem_file(b->ptr, errh, &ssl_pemfile_chain);
+ if (NULL == ssl_pemfile_x509) {
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: Failed to load acme-tls/1 pemfile: %s", b->ptr);
+ break;
+ }
+
+ buffer_string_set_length(b, len); /*(remove ".crt.pem")*/
+ buffer_append_string_len(b, CONST_STR_LEN(".key.pem"));
+ ssl_pemfile_pkey = mod_wolfssl_evp_pkey_load_pem_file(b->ptr, errh);
+ if (NULL == ssl_pemfile_pkey) {
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: Failed to load acme-tls/1 pemfile: %s", b->ptr);
+ break;
+ }
+
+ #if 0 /* redundant with below? */
+ if (!X509_check_private_key(ssl_pemfile_x509, ssl_pemfile_pkey)) {
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: Private key does not match acme-tls/1 "
+ "certificate public key, reason: %s %s"
+ ERR_error_string(ERR_get_error(), NULL), b->ptr);
+ break;
+ }
+ #endif
+
+ /* first set certificate!
+ * setting private key checks whether certificate matches it */
+ buffer *cert = ssl_pemfile_x509;
+ if (1 != wolfSSL_use_certificate_ASN1(ssl, (unsigned char *)cert->ptr,
+ (int)buffer_string_length(cert))){
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: failed to set acme-tls/1 certificate for TLS server "
+ "name %s: %s", name->ptr, ERR_error_string(ERR_get_error(),NULL));
+ break;
+ }
+
+ if (ssl_pemfile_chain) {
+ /* WolfSSL limitation */
+ /* WolfSSL does not support setting per-session chain;
+ * limitation is to per-CTX chain, and so chain is not provided for
+ * "acme-tls/1" (might be non-issue; chain might not be present) */
+ }
+
+ buffer *pkey = ssl_pemfile_pkey;
+ if (1 != wolfSSL_use_PrivateKey_buffer(ssl, (unsigned char *)pkey->ptr,
+ (int)buffer_string_length(pkey),
+ WOLFSSL_FILETYPE_ASN1)) {
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: failed to set acme-tls/1 private key for TLS server "
+ "name %s: %s", name->ptr, ERR_error_string(ERR_get_error(),NULL));
+ break;
+ }
+
+ hctx->conf.ssl_verifyclient_enforce = 0;
+ wolfSSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
+ rc = SSL_TLSEXT_ERR_OK;
+ } while (0);
+
+ if (ssl_pemfile_pkey) buffer_free(ssl_pemfile_pkey);
+ /*if (ssl_pemfile_x509) buffer_free(ssl_pemfile_x509);*//*(part of chain)*/
+ mod_wolfssl_free_der_certs(ssl_pemfile_chain);
+
+ return rc;
+}
+
+enum {
+ MOD_OPENSSL_ALPN_HTTP11 = 1
+ ,MOD_OPENSSL_ALPN_HTTP10 = 2
+ ,MOD_OPENSSL_ALPN_H2 = 3
+ ,MOD_OPENSSL_ALPN_ACME_TLS_1 = 4
+};
+
+/* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids */
+static int
+mod_openssl_alpn_select_cb (SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
+{
+ handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl);
+ unsigned short proto;
+ UNUSED(arg);
+
+ for (unsigned int i = 0, n; i < inlen; i += n) {
+ n = in[i++];
+ if (i+n > inlen || 0 == n) break;
+ switch (n) {
+ case 2: /* "h2" */
+ if (in[i] == 'h' && in[i+1] == '2') {
+ if (!hctx->r->conf.h2proto) continue;
+ proto = MOD_OPENSSL_ALPN_H2;
+ hctx->r->http_version = HTTP_VERSION_2;
+ break;
+ }
+ continue;
+ case 8: /* "http/1.1" "http/1.0" */
+ if (0 == memcmp(in+i, "http/1.", 7)) {
+ if (in[i+7] == '1') {
+ proto = MOD_OPENSSL_ALPN_HTTP11;
+ break;
+ }
+ if (in[i+7] == '0') {
+ proto = MOD_OPENSSL_ALPN_HTTP10;
+ break;
+ }
+ }
+ continue;
+ case 10: /* "acme-tls/1" */
+ if (0 == memcmp(in+i, "acme-tls/1", 10)) {
+ int rc = mod_openssl_acme_tls_1(ssl, hctx);
+ if (rc == SSL_TLSEXT_ERR_OK) {
+ proto = MOD_OPENSSL_ALPN_ACME_TLS_1;
+ break;
+ }
+ /* (use SSL_TLSEXT_ERR_NOACK for not-configured) */
+ if (rc == SSL_TLSEXT_ERR_NOACK) continue;
+ return rc;
+ }
+ continue;
+ default:
+ continue;
+ }
+
+ hctx->alpn = proto;
+ *out = in+i;
+ *outlen = n;
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ return SSL_TLSEXT_ERR_NOACK;
+}
+
+#endif /* TLSEXT_TYPE_application_layer_protocol_negotiation */
+
+#endif /* HAVE_TLS_EXTENSIONS */
+
+
+static int
+mod_openssl_ssl_conf_cmd (server *srv, plugin_config_socket *s);
+
+
+#ifndef NO_DH
+/* wolfSSL provides wolfSSL_DH_set0_pqg() for
+ * Apache w/ OPENSSL_VERSION_NUMBER >= 0x10100000L
+ * but does not provide most other openssl 1.1.0+ interfaces
+ * and get_dh2048() might not be necessary if wolfSSL defines
+ * HAVE_TLS_EXTENSIONS HAVE_DH_DEFAULT_PARAMS HAVE_FFDHE HAVE_SUPPORTED_CURVES*/
+#define DH_set0_pqg(dh, dh_p, NULL, dh_g) \
+ ((dh)->p = (dh_p), (dh)->g = (dh_g), (dh_p) != NULL && (dh_g) != NULL)
+/* https://tools.ietf.org/html/rfc7919#appendix-A.1
+ * A.1. ffdhe2048
+ *
+ * https://ssl-config.mozilla.org/ffdhe2048.txt
+ * C code generated with: openssl dhparam -C -in ffdhe2048.txt
+ */
+static DH *get_dh2048(void)
+{
+ static unsigned char dhp_2048[] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAD, 0xF8,
+ 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, 0xAF, 0xDC, 0x56, 0x20,
+ 0x27, 0x3D, 0x3C, 0xF1, 0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D,
+ 0x36, 0x95, 0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB,
+ 0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, 0x7D, 0x2F,
+ 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, 0xF6, 0x81, 0xB2, 0x02,
+ 0xAE, 0xC4, 0x61, 0x7A, 0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD,
+ 0x65, 0x61, 0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0,
+ 0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, 0xB5, 0x57,
+ 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, 0x98, 0x4F, 0x0C, 0x70,
+ 0xE0, 0xE6, 0x8B, 0x77, 0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF,
+ 0xE8, 0x72, 0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35,
+ 0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, 0xBC, 0x0A,
+ 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, 0xD1, 0x08, 0xA9, 0x4B,
+ 0xB2, 0xC8, 0xE3, 0xFB, 0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7,
+ 0xF4, 0x68, 0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4,
+ 0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, 0x0B, 0x07,
+ 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, 0x9E, 0x02, 0xFC, 0xE1,
+ 0xCD, 0xF7, 0xE2, 0xEC, 0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34,
+ 0x2F, 0x61, 0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF,
+ 0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, 0xC3, 0xFE,
+ 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, 0x3B, 0xB5, 0xFC, 0xBC,
+ 0x2E, 0xC2, 0x20, 0x05, 0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16,
+ 0x83, 0xB2, 0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA,
+ 0x88, 0x6B, 0x42, 0x38, 0x61, 0x28, 0x5C, 0x97, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+ };
+ static unsigned char dhg_2048[] = {
+ 0x02
+ };
+ DH *dh = DH_new();
+ BIGNUM *p, *g;
+
+ if (dh == NULL)
+ return NULL;
+ p = BN_bin2bn(dhp_2048, sizeof(dhp_2048), NULL);
+ g = BN_bin2bn(dhg_2048, sizeof(dhg_2048), NULL);
+ if (p == NULL || g == NULL || !DH_set0_pqg(dh, p, NULL, g)) {
+ DH_free(dh);
+ BN_free(p);
+ BN_free(g);
+ return NULL;
+ }
+ return dh;
+}
+#endif
+
+
+static int
+mod_openssl_ssl_conf_curves(server *srv, plugin_config_socket *s, const buffer *ssl_ec_curve)
+{
+ #ifndef OPENSSL_NO_ECDH
+ int nid = 0;
+ /* Support for Elliptic-Curve Diffie-Hellman key exchange */
+ if (!buffer_string_is_empty(ssl_ec_curve)) {
+ /* OpenSSL only supports the "named curves"
+ * from RFC 4492, section 5.1.1. */
+ nid = wolfSSL_OBJ_sn2nid((char *) ssl_ec_curve->ptr);
+ if (nid == 0) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: Unknown curve name %s", ssl_ec_curve->ptr);
+ return 0;
+ }
+ }
+ else {
+ /* Default curve */
+ nid = wolfSSL_OBJ_sn2nid("prime256v1");
+ }
+ if (nid) {
+ EC_KEY *ecdh;
+ ecdh = EC_KEY_new_by_curve_name(nid);
+ if (ecdh == NULL) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: Unable to create curve %s", ssl_ec_curve->ptr);
+ return 0;
+ }
+ SSL_CTX_set_tmp_ecdh(s->ssl_ctx, ecdh);
+ SSL_CTX_set_options(s->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
+ EC_KEY_free(ecdh);
+ }
+ #endif
+ UNUSED(srv);
+ UNUSED(s);
+ UNUSED(ssl_ec_curve);
+
+ return 1;
+}
+
+
+static int
+network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p)
+{
+ /* load SSL certificates */
+
+ #ifndef SSL_OP_NO_COMPRESSION
+ #define SSL_OP_NO_COMPRESSION 0
+ #endif
+ #ifndef SSL_MODE_RELEASE_BUFFERS /* OpenSSL >= 1.0.0 */
+ #define SSL_MODE_RELEASE_BUFFERS 0
+ #endif
+ long ssloptions = SSL_OP_ALL
+ | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
+ | SSL_OP_NO_COMPRESSION;
+
+ s->ssl_ctx = (!s->ssl_use_sslv2 && !s->ssl_use_sslv3)
+ ? SSL_CTX_new(TLS_server_method())
+ : SSL_CTX_new(SSLv23_server_method());
+ if (NULL == s->ssl_ctx) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: %s", ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ #ifdef SSL_OP_NO_RENEGOTIATION /* openssl 1.1.0 */
+ if (s->ssl_disable_client_renegotiation)
+ ssloptions |= SSL_OP_NO_RENEGOTIATION;
+ #endif
+
+ /* completely useless identifier;
+ * required for client cert verification to work with sessions */
+ if (0 == SSL_CTX_set_session_id_context(
+ s->ssl_ctx,(const unsigned char*)CONST_STR_LEN("lighttpd"))){
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: failed to set session context: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ #if !defined(NO_SESSION_CACHE)
+ /* disable session cache; session tickets are preferred */
+ SSL_CTX_set_session_cache_mode(s->ssl_ctx, SSL_SESS_CACHE_OFF
+ | SSL_SESS_CACHE_NO_AUTO_CLEAR
+ | SSL_SESS_CACHE_NO_INTERNAL);
+ #endif
+
+ if (s->ssl_empty_fragments) {
+ #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+ ssloptions &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
+ #else
+ ssloptions &= ~0x00000800L; /* hardcode constant */
+ log_error(srv->errh, __FILE__, __LINE__,
+ "WARNING: SSL: 'insert empty fragments' not supported by the "
+ "openssl version used to compile lighttpd with");
+ #endif
+ }
+
+ SSL_CTX_set_options(s->ssl_ctx, ssloptions);
+ SSL_CTX_set_info_callback(s->ssl_ctx, ssl_info_callback);
+
+ /*(wolfSSL does not support SSLv2)*/
+
+ if (!s->ssl_use_sslv3 && 0 != SSL_OP_NO_SSLv3) {
+ /* disable SSLv3 */
+ if ((SSL_OP_NO_SSLv3
+ & SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv3))
+ != SSL_OP_NO_SSLv3) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: %s", ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+ }
+
+ if (!buffer_string_is_empty(s->ssl_cipher_list)) {
+ /* Disable support for low encryption ciphers */
+ if (SSL_CTX_set_cipher_list(s->ssl_ctx,s->ssl_cipher_list->ptr)!=1){
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: %s", ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ if (s->ssl_honor_cipher_order) {
+ SSL_CTX_set_options(s->ssl_ctx,SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+ }
+
+ #ifdef SSL_OP_PRIORITIZE_CHACHA /*(openssl 1.1.1)*/
+ if (s->ssl_honor_cipher_order)
+ SSL_CTX_set_options(s->ssl_ctx, SSL_OP_PRIORITIZE_CHACHA);
+ #endif
+
+ #ifndef NO_DH
+ {
+ DH *dh;
+ /* Support for Diffie-Hellman key exchange */
+ if (!buffer_string_is_empty(s->ssl_dh_file)) {
+ const char *fn = s->ssl_dh_file->ptr;
+ off_t dlen = 1*1024*1024;/*(arbitrary limit: 1 MB; expect < 1 KB)*/
+ char *data = fdevent_load_file(fn, &dlen, srv->errh, malloc, free);
+ int rc = (NULL != data) ? 0 : -1;
+ if (0 == rc)
+ wolfSSL_CTX_SetTmpDH_buffer(s->ssl_ctx, (unsigned char *)data,
+ (long)dlen, WOLFSSL_FILETYPE_PEM);
+ if (dlen) safe_memclear(data, dlen);
+ free(data);
+ if (rc < 0) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: Unable to read DH params from file %s",
+ s->ssl_dh_file->ptr);
+ return -1;
+ }
+ }
+ else {
+ dh = get_dh2048();
+ if (dh == NULL) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: get_dh2048() failed");
+ return -1;
+ }
+ SSL_CTX_set_tmp_dh(s->ssl_ctx,dh);
+ DH_free(dh);
+ }
+ SSL_CTX_set_options(s->ssl_ctx,SSL_OP_SINGLE_DH_USE);
+ }
+ #else
+ if (!buffer_string_is_empty(s->ssl_dh_file)) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: openssl compiled without DH support, "
+ "can't load parameters from %s", s->ssl_dh_file->ptr);
+ }
+ #endif
+
+ if (!mod_openssl_ssl_conf_curves(srv, s, s->ssl_ec_curve))
+ return -1;
+
+ #ifdef HAVE_SESSION_TICKET
+ SSL_CTX_set_tlsext_ticket_key_cb(s->ssl_ctx, ssl_tlsext_ticket_key_cb);
+ #endif
+
+ #ifndef OPENSSL_NO_OCSP
+ SSL_CTX_set_tlsext_status_cb(s->ssl_ctx, ssl_tlsext_status_cb);
+ #endif
+
+ /* load all ssl.ca-files specified in the config into each SSL_CTX
+ * XXX: This might be a bit excessive, but are all trusted CAs
+ * TODO: prefer to load on-demand in mod_openssl_cert_cb()
+ * for openssl >= 1.0.2 */
+ if (!mod_wolfssl_load_ca_files(s->ssl_ctx, p, srv))
+ return -1;
+
+ if (s->ssl_verifyclient) {
+ if (NULL == s->ssl_ca_file) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: You specified ssl.verifyclient.activate "
+ "but no ssl.ca-file");
+ return -1;
+ }
+ /* WTH wolfssl? wolfSSL_dup_CA_list() is a stub which returns NULL
+ * and so DN names in cert request are not set here.
+ * (A patch has been submitted to WolfSSL to correct this)
+ * https://github.com/wolfSSL/wolfssl/pull/3098 */
+ STACK_OF(X509_NAME) * const cert_names = s->ssl_ca_dn_file
+ ? s->ssl_ca_dn_file
+ : s->ssl_ca_file->names;
+ wolfSSL_CTX_set_client_CA_list(s->ssl_ctx,
+ wolfSSL_dup_CA_list(cert_names));
+ int mode = SSL_VERIFY_PEER;
+ if (s->ssl_verifyclient_enforce) {
+ mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ }
+ wolfSSL_CTX_set_verify(s->ssl_ctx, mode, verify_callback);
+ wolfSSL_CTX_set_verify_depth(s->ssl_ctx,
+ s->ssl_verifyclient_depth + 1);
+ if (!buffer_string_is_empty(s->ssl_ca_crl_file)) {
+ if (!mod_wolfssl_load_cacrls(s->ssl_ctx,s->ssl_ca_crl_file,srv))
+ return -1;
+ }
+ }
+
+ if (1 != mod_wolfssl_CTX_use_certificate_chain_file(
+ s->ssl_ctx, s->pc->ssl_pemfile->ptr, srv->errh))
+ return -1;
+
+ buffer *k = s->pc->ssl_pemfile_pkey;
+ if (1 != wolfSSL_CTX_use_PrivateKey_buffer(s->ssl_ctx,
+ (unsigned char *)k->ptr,
+ (int)buffer_string_length(k),
+ WOLFSSL_FILETYPE_ASN1)) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: %s %s %s", ERR_error_string(ERR_get_error(), NULL),
+ s->pc->ssl_pemfile->ptr, s->pc->ssl_privkey->ptr);
+ return -1;
+ }
+
+ if (SSL_CTX_check_private_key(s->ssl_ctx) != 1) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: Private key does not match the certificate public key, "
+ "reason: %s %s %s", ERR_error_string(ERR_get_error(), NULL),
+ s->pc->ssl_pemfile->ptr, s->pc->ssl_privkey->ptr);
+ return -1;
+ }
+
+ SSL_CTX_set_default_read_ahead(s->ssl_ctx, s->ssl_read_ahead);
+ SSL_CTX_set_mode(s->ssl_ctx, SSL_CTX_get_mode(s->ssl_ctx)
+ | SSL_MODE_ENABLE_PARTIAL_WRITE
+ | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
+ | SSL_MODE_RELEASE_BUFFERS);
+
+ #ifdef HAVE_TLS_EXTENSIONS
+ wolfSSL_CTX_set_servername_callback(
+ s->ssl_ctx, network_ssl_servername_callback);
+ wolfSSL_CTX_set_servername_arg(s->ssl_ctx, srv);
+
+ #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+ SSL_CTX_set_alpn_select_cb(s->ssl_ctx,mod_openssl_alpn_select_cb,NULL);
+ #endif
+ #endif
+
+ if (!s->ssl_use_sslv3 && !s->ssl_use_sslv2
+ && wolfSSL_CTX_SetMinVersion(s->ssl_ctx, WOLFSSL_TLSV1_2)
+ != WOLFSSL_SUCCESS)
+ return -1;
+
+ if (s->ssl_conf_cmd && s->ssl_conf_cmd->used) {
+ if (0 != mod_openssl_ssl_conf_cmd(srv, s)) return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+mod_openssl_set_defaults_sockets(server *srv, plugin_data *p)
+{
+ static const config_plugin_keys_t cpk[] = {
+ { CONST_STR_LEN("ssl.engine"),
+ T_CONFIG_BOOL,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.cipher-list"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.honor-cipher-order"),
+ T_CONFIG_BOOL,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.dh-file"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.ec-curve"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.openssl.ssl-conf-cmd"),
+ T_CONFIG_ARRAY_KVSTRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.pemfile"), /* included to process global scope */
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.empty-fragments"),
+ T_CONFIG_BOOL,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.use-sslv2"),
+ T_CONFIG_BOOL,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.use-sslv3"),
+ T_CONFIG_BOOL,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.stek-file"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_SERVER }
+ ,{ NULL, 0,
+ T_CONFIG_UNSET,
+ T_CONFIG_SCOPE_UNSET }
+ };
+ /* WolfSSL does not have mapping for "HIGH" */
+ /* cipher list is (current) output of "openssl ciphers HIGH" */
+ static const buffer default_ssl_cipher_list = { CONST_STR_LEN("TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-CCM8:ECDHE-ECDSA-AES256-CCM:DHE-RSA-AES256-CCM8:DHE-RSA-AES256-CCM:ECDHE-ECDSA-ARIA256-GCM-SHA384:ECDHE-ARIA256-GCM-SHA384:DHE-DSS-ARIA256-GCM-SHA384:DHE-RSA-ARIA256-GCM-SHA384:ADH-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-CCM8:ECDHE-ECDSA-AES128-CCM:DHE-RSA-AES128-CCM8:DHE-RSA-AES128-CCM:ECDHE-ECDSA-ARIA128-GCM-SHA256:ECDHE-ARIA128-GCM-SHA256:DHE-DSS-ARIA128-GCM-SHA256:DHE-RSA-ARIA128-GCM-SHA256:ADH-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:ECDHE-ECDSA-CAMELLIA256-SHA384:ECDHE-RSA-CAMELLIA256-SHA384:DHE-RSA-CAMELLIA256-SHA256:DHE-DSS-CAMELLIA256-SHA256:ADH-AES256-SHA256:ADH-CAMELLIA256-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:ECDHE-ECDSA-CAMELLIA128-SHA256:ECDHE-RSA-CAMELLIA128-SHA256:DHE-RSA-CAMELLIA128-SHA256:DHE-DSS-CAMELLIA128-SHA256:ADH-AES128-SHA256:ADH-CAMELLIA128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:AECDH-AES256-SHA:ADH-AES256-SHA:ADH-CAMELLIA256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:AECDH-AES128-SHA:ADH-AES128-SHA:ADH-CAMELLIA128-SHA:RSA-PSK-AES256-GCM-SHA384:DHE-PSK-AES256-GCM-SHA384:RSA-PSK-CHACHA20-POLY1305:DHE-PSK-CHACHA20-POLY1305:ECDHE-PSK-CHACHA20-POLY1305:DHE-PSK-AES256-CCM8:DHE-PSK-AES256-CCM:RSA-PSK-ARIA256-GCM-SHA384:DHE-PSK-ARIA256-GCM-SHA384:AES256-GCM-SHA384:AES256-CCM8:AES256-CCM:ARIA256-GCM-SHA384:PSK-AES256-GCM-SHA384:PSK-CHACHA20-POLY1305:PSK-AES256-CCM8:PSK-AES256-CCM:PSK-ARIA256-GCM-SHA384:RSA-PSK-AES128-GCM-SHA256:DHE-PSK-AES128-GCM-SHA256:DHE-PSK-AES128-CCM8:DHE-PSK-AES128-CCM:RSA-PSK-ARIA128-GCM-SHA256:DHE-PSK-ARIA128-GCM-SHA256:AES128-GCM-SHA256:AES128-CCM8:AES128-CCM:ARIA128-GCM-SHA256:PSK-AES128-GCM-SHA256:PSK-AES128-CCM8:PSK-AES128-CCM:PSK-ARIA128-GCM-SHA256:AES256-SHA256:CAMELLIA256-SHA256:AES128-SHA256:CAMELLIA128-SHA256:ECDHE-PSK-AES256-CBC-SHA384:ECDHE-PSK-AES256-CBC-SHA:SRP-DSS-AES-256-CBC-SHA:SRP-RSA-AES-256-CBC-SHA:SRP-AES-256-CBC-SHA:RSA-PSK-AES256-CBC-SHA384:DHE-PSK-AES256-CBC-SHA384:RSA-PSK-AES256-CBC-SHA:DHE-PSK-AES256-CBC-SHA:ECDHE-PSK-CAMELLIA256-SHA384:RSA-PSK-CAMELLIA256-SHA384:DHE-PSK-CAMELLIA256-SHA384:AES256-SHA:CAMELLIA256-SHA:PSK-AES256-CBC-SHA384:PSK-AES256-CBC-SHA:PSK-CAMELLIA256-SHA384:ECDHE-PSK-AES128-CBC-SHA256:ECDHE-PSK-AES128-CBC-SHA:SRP-DSS-AES-128-CBC-SHA:SRP-RSA-AES-128-CBC-SHA:SRP-AES-128-CBC-SHA:RSA-PSK-AES128-CBC-SHA256:DHE-PSK-AES128-CBC-SHA256:RSA-PSK-AES128-CBC-SHA:DHE-PSK-AES128-CBC-SHA:ECDHE-PSK-CAMELLIA128-SHA256:RSA-PSK-CAMELLIA128-SHA256:DHE-PSK-CAMELLIA128-SHA256:AES128-SHA:CAMELLIA128-SHA:PSK-AES128-CBC-SHA256:PSK-AES128-CBC-SHA:PSK-CAMELLIA128-SHA256"), 0 };
+
+ p->ssl_ctxs = calloc(srv->config_context->used, sizeof(plugin_ssl_ctx));
+ force_assert(p->ssl_ctxs);
+
+ int rc = HANDLER_GO_ON;
+ plugin_data_base srvplug;
+ memset(&srvplug, 0, sizeof(srvplug));
+ plugin_data_base * const ps = &srvplug;
+ if (!config_plugin_values_init(srv, ps, cpk, "mod_openssl"))
+ return HANDLER_ERROR;
+
+ plugin_config_socket defaults;
+ memset(&defaults, 0, sizeof(defaults));
+ defaults.ssl_honor_cipher_order = 1;
+ defaults.ssl_cipher_list = &default_ssl_cipher_list;
+
+ /* process and validate config directives for global and $SERVER["socket"]
+ * (init i to 0 if global context; to 1 to skip empty global context) */
+ for (int i = !ps->cvlist[0].v.u2[1]; i < ps->nconfig; ++i) {
+ config_cond_info cfginfo;
+ config_get_config_cond_info(&cfginfo, (uint32_t)ps->cvlist[i].k_id);
+ int is_socket_scope = (0 == i || cfginfo.comp == COMP_SERVER_SOCKET);
+ int count_not_engine = 0;
+
+ plugin_config_socket conf;
+ memcpy(&conf, &defaults, sizeof(conf));
+
+ /*(preserve prior behavior; not inherited)*/
+ /*(forcing inheritance might break existing configs where SSL is enabled
+ * by default in the global scope, but not $SERVER["socket"]=="*:80") */
+ conf.ssl_enabled = 0;
+
+ config_plugin_value_t *cpv = ps->cvlist + ps->cvlist[i].v.u2[0];
+ for (; -1 != cpv->k_id; ++cpv) {
+ /* ignore ssl.pemfile (k_id=6); included to process global scope */
+ if (!is_socket_scope && cpv->k_id != 6) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "%s is valid only in global scope or "
+ "$SERVER[\"socket\"] condition", cpk[cpv->k_id].k);
+ continue;
+ }
+ ++count_not_engine;
+ switch (cpv->k_id) {
+ case 0: /* ssl.engine */
+ conf.ssl_enabled = (0 != cpv->v.u);
+ --count_not_engine;
+ break;
+ case 1: /* ssl.cipher-list */
+ conf.ssl_cipher_list = cpv->v.b;
+ break;
+ case 2: /* ssl.honor-cipher-order */
+ conf.ssl_honor_cipher_order = (0 != cpv->v.u);
+ break;
+ case 3: /* ssl.dh-file */
+ conf.ssl_dh_file = cpv->v.b;
+ break;
+ case 4: /* ssl.ec-curve */
+ conf.ssl_ec_curve = cpv->v.b;
+ break;
+ case 5: /* ssl.openssl.ssl-conf-cmd */
+ *(const array **)&conf.ssl_conf_cmd = cpv->v.a;
+ break;
+ case 6: /* ssl.pemfile */
+ /* ignore here; included to process global scope when
+ * ssl.pemfile is set, but ssl.engine is not "enable" */
+ break;
+ case 7: /* ssl.empty-fragments */
+ conf.ssl_empty_fragments = (0 != cpv->v.u);
+ log_error(srv->errh, __FILE__, __LINE__, "SSL: "
+ "ssl.empty-fragments is deprecated and will soon be "
+ "removed. It is disabled by default.");
+ if (conf.ssl_empty_fragments)
+ log_error(srv->errh, __FILE__, __LINE__, "SSL: "
+ "If needed, use: ssl.openssl.ssl-conf-cmd = "
+ "(\"Options\" => \"EmptyFragments\")");
+ log_error(srv->errh, __FILE__, __LINE__, "SSL: "
+ "ssl.empty-fragments is a "
+ "counter-measure against a SSL 3.0/TLS 1.0 protocol "
+ "vulnerability affecting CBC ciphers, which cannot be handled"
+ " by some broken (Microsoft) SSL implementations.");
+ break;
+ case 8: /* ssl.use-sslv2 */
+ conf.ssl_use_sslv2 = (0 != cpv->v.u);
+ log_error(srv->errh, __FILE__, __LINE__, "SSL: "
+ "ssl.use-sslv2 is deprecated and will soon be removed. "
+ "It is disabled by default. "
+ "Many modern TLS libraries no longer support SSLv2.");
+ break;
+ case 9: /* ssl.use-sslv3 */
+ conf.ssl_use_sslv3 = (0 != cpv->v.u);
+ log_error(srv->errh, __FILE__, __LINE__, "SSL: "
+ "ssl.use-sslv3 is deprecated and will soon be removed. "
+ "It is disabled by default. "
+ "Many modern TLS libraries no longer support SSLv3.");
+ if (conf.ssl_use_sslv3)
+ log_error(srv->errh, __FILE__, __LINE__, "SSL: "
+ "If needed, use: ssl.openssl.ssl-conf-cmd = "
+ "(\"MinProtocol\" => \"SSLv3\")");
+ break;
+ case 10:/* ssl.stek-file */
+ if (!buffer_is_empty(cpv->v.b))
+ p->ssl_stek_file = cpv->v.b->ptr;
+ break;
+ default:/* should not happen */
+ break;
+ }
+ }
+ if (HANDLER_GO_ON != rc) break;
+ if (0 == i) memcpy(&defaults, &conf, sizeof(conf));
+
+ if (0 != i && !conf.ssl_enabled) continue;
+
+ /* fill plugin_config_socket with global context then $SERVER["socket"]
+ * only for directives directly in current $SERVER["socket"] condition*/
+
+ /*conf.pc = p->defaults.pc;*/
+ conf.ssl_ca_file = p->defaults.ssl_ca_file;
+ conf.ssl_ca_dn_file = p->defaults.ssl_ca_dn_file;
+ conf.ssl_ca_crl_file = p->defaults.ssl_ca_crl_file;
+ conf.ssl_verifyclient = p->defaults.ssl_verifyclient;
+ conf.ssl_verifyclient_enforce = p->defaults.ssl_verifyclient_enforce;
+ conf.ssl_verifyclient_depth = p->defaults.ssl_verifyclient_depth;
+ conf.ssl_read_ahead = p->defaults.ssl_read_ahead;
+
+ int sidx = ps->cvlist[i].k_id;
+ for (int j = !p->cvlist[0].v.u2[1]; j < p->nconfig; ++j) {
+ if (p->cvlist[j].k_id != sidx) continue;
+ /*if (0 == sidx) break;*//*(repeat to get ssl_pemfile,ssl_privkey)*/
+ cpv = p->cvlist + p->cvlist[j].v.u2[0];
+ for (; -1 != cpv->k_id; ++cpv) {
+ ++count_not_engine;
+ switch (cpv->k_id) {
+ case 0: /* ssl.pemfile */
+ if (cpv->vtype == T_CONFIG_LOCAL)
+ conf.pc = cpv->v.v;
+ break;
+ case 2: /* ssl.ca-file */
+ if (cpv->vtype == T_CONFIG_LOCAL)
+ conf.ssl_ca_file = cpv->v.v;
+ break;
+ case 3: /* ssl.ca-dn-file */
+ if (cpv->vtype == T_CONFIG_LOCAL)
+ conf.ssl_ca_dn_file = cpv->v.v;
+ break;
+ case 4: /* ssl.ca-crl-file */
+ conf.ssl_ca_crl_file = cpv->v.b;
+ break;
+ case 5: /* ssl.read-ahead */
+ conf.ssl_read_ahead = (0 != cpv->v.u);
+ break;
+ case 6: /* ssl.disable-client-renegotiation */
+ conf.ssl_disable_client_renegotiation = (0 != cpv->v.u);
+ break;
+ case 7: /* ssl.verifyclient.activate */
+ conf.ssl_verifyclient = (0 != cpv->v.u);
+ break;
+ case 8: /* ssl.verifyclient.enforce */
+ conf.ssl_verifyclient_enforce = (0 != cpv->v.u);
+ break;
+ case 9: /* ssl.verifyclient.depth */
+ conf.ssl_verifyclient_depth = (unsigned char)cpv->v.shrt;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ if (NULL == conf.pc) {
+ if (0 == i && !conf.ssl_enabled) continue;
+ if (0 != i) {
+ /* inherit ssl settings from global scope
+ * (if only ssl.engine = "enable" and no other ssl.* settings)
+ * (This is for convenience when defining both IPv4 and IPv6
+ * and desiring to inherit the ssl config from global context
+ * without having to duplicate the directives)*/
+ if (count_not_engine) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "ssl.pemfile has to be set in same $SERVER[\"socket\"] scope "
+ "as other ssl.* directives, unless only ssl.engine is set, "
+ "inheriting ssl.* from global scope");
+ rc = HANDLER_ERROR;
+ continue;
+ }
+ plugin_ssl_ctx * const s = p->ssl_ctxs + sidx;
+ *s = *p->ssl_ctxs;/*(copy struct of ssl_ctx from global scope)*/
+ continue;
+ }
+ /* PEM file is required */
+ log_error(srv->errh, __FILE__, __LINE__,
+ "ssl.pemfile has to be set when ssl.engine = \"enable\"");
+ rc = HANDLER_ERROR;
+ continue;
+ }
+
+ /* configure ssl_ctx for socket */
+
+ /*conf.ssl_ctx = NULL;*//*(filled by network_init_ssl() even on error)*/
+ if (0 == network_init_ssl(srv, &conf, p)) {
+ plugin_ssl_ctx * const s = p->ssl_ctxs + sidx;
+ s->ssl_ctx = conf.ssl_ctx;
+ }
+ else {
+ SSL_CTX_free(conf.ssl_ctx);
+ rc = HANDLER_ERROR;
+ }
+ }
+
+ #ifdef HAVE_SESSION_TICKET
+ if (rc == HANDLER_GO_ON && ssl_is_init)
+ mod_openssl_session_ticket_key_check(p, log_epoch_secs);
+ #endif
+
+ free(srvplug.cvlist);
+ return rc;
+}
+
+
+SETDEFAULTS_FUNC(mod_openssl_set_defaults)
+{
+ static const config_plugin_keys_t cpk[] = {
+ { CONST_STR_LEN("ssl.pemfile"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.privkey"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.ca-file"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.ca-dn-file"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.ca-crl-file"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.read-ahead"),
+ T_CONFIG_BOOL,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.disable-client-renegotiation"),
+ T_CONFIG_BOOL,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.verifyclient.activate"),
+ T_CONFIG_BOOL,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.verifyclient.enforce"),
+ T_CONFIG_BOOL,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.verifyclient.depth"),
+ T_CONFIG_SHORT,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.verifyclient.username"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.verifyclient.exportcert"),
+ T_CONFIG_BOOL,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.acme-tls-1"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("ssl.stapling-file"),
+ T_CONFIG_STRING,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ CONST_STR_LEN("debug.log-ssl-noise"),
+ T_CONFIG_BOOL,
+ T_CONFIG_SCOPE_CONNECTION }
+ ,{ NULL, 0,
+ T_CONFIG_UNSET,
+ T_CONFIG_SCOPE_UNSET }
+ };
+
+ plugin_data * const p = p_d;
+ p->srv = srv;
+ p->cafiles = array_init(0);
+ if (!config_plugin_values_init(srv, p, cpk, "mod_openssl"))
+ return HANDLER_ERROR;
+
+ const buffer *default_ssl_ca_crl_file = NULL;
+
+ /* process and validate config directives
+ * (init i to 0 if global context; to 1 to skip empty global context) */
+ for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
+ config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
+ config_plugin_value_t *pemfile = NULL;
+ config_plugin_value_t *privkey = NULL;
+ const buffer *ssl_stapling_file = NULL;
+ const buffer *ssl_ca_file = NULL;
+ const buffer *ssl_ca_dn_file = NULL;
+ const buffer *ssl_ca_crl_file = NULL;
+ X509_STORE *ca_store = NULL;
+ for (; -1 != cpv->k_id; ++cpv) {
+ switch (cpv->k_id) {
+ case 0: /* ssl.pemfile */
+ if (!buffer_string_is_empty(cpv->v.b)) pemfile = cpv;
+ break;
+ case 1: /* ssl.privkey */
+ if (!buffer_string_is_empty(cpv->v.b)) privkey = cpv;
+ break;
+ case 2: /* ssl.ca-file */
+ if (buffer_string_is_empty(cpv->v.b)) break;
+ if (!mod_openssl_init_once_openssl(srv)) return HANDLER_ERROR;
+ ssl_ca_file = cpv->v.b;
+ cpv->v.v = mod_wolfssl_load_cacerts(ssl_ca_file, srv->errh);
+ if (NULL != cpv->v.v) {
+ cpv->vtype = T_CONFIG_LOCAL;
+ ca_store = ((plugin_cacerts *)cpv->v.v)->certs;
+ }
+ else {
+ log_error(srv->errh, __FILE__, __LINE__, "SSL: %s %s",
+ ERR_error_string(ERR_get_error(), NULL),
+ ssl_ca_file->ptr);
+ return HANDLER_ERROR;
+ }
+ break;
+ case 3: /* ssl.ca-dn-file */
+ if (buffer_string_is_empty(cpv->v.b)) break;
+ if (!mod_openssl_init_once_openssl(srv)) return HANDLER_ERROR;
+ ssl_ca_dn_file = cpv->v.b;
+ cpv->v.v = mod_wolfssl_load_client_CA_file(ssl_ca_dn_file,
+ srv->errh);
+ if (NULL != cpv->v.v) {
+ cpv->vtype = T_CONFIG_LOCAL;
+ }
+ else {
+ log_error(srv->errh, __FILE__, __LINE__, "SSL: %s %s",
+ ERR_error_string(ERR_get_error(), NULL),
+ ssl_ca_dn_file->ptr);
+ return HANDLER_ERROR;
+ }
+ break;
+ case 4: /* ssl.ca-crl-file */
+ if (buffer_string_is_empty(cpv->v.b)) break;
+ ssl_ca_crl_file = cpv->v.b;
+ if (0 == i) default_ssl_ca_crl_file = cpv->v.b;
+ break;
+ case 5: /* ssl.read-ahead */
+ case 6: /* ssl.disable-client-renegotiation */
+ case 7: /* ssl.verifyclient.activate */
+ case 8: /* ssl.verifyclient.enforce */
+ break;
+ case 9: /* ssl.verifyclient.depth */
+ if (cpv->v.shrt > 255) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "%s is absurdly large (%hu); limiting to 255",
+ cpk[cpv->k_id].k, cpv->v.shrt);
+ cpv->v.shrt = 255;
+ }
+ break;
+ case 10:/* ssl.verifyclient.username */
+ case 11:/* ssl.verifyclient.exportcert */
+ case 12:/* ssl.acme-tls-1 */
+ break;
+ case 13:/* ssl.stapling-file */
+ ssl_stapling_file = cpv->v.b;
+ break;
+ case 14:/* debug.log-ssl-noise */
+ break;
+ default:/* should not happen */
+ break;
+ }
+ }
+
+ /* p->cafiles for legacy only */
+ /* load all ssl.ca-files into a single chain */
+ /*(certificate load order might matter)*/
+ if (ssl_ca_dn_file)
+ array_insert_value(p->cafiles, CONST_BUF_LEN(ssl_ca_dn_file));
+ if (ssl_ca_file)
+ array_insert_value(p->cafiles, CONST_BUF_LEN(ssl_ca_file));
+ UNUSED(ca_store);
+ UNUSED(ssl_ca_crl_file);
+ UNUSED(default_ssl_ca_crl_file);
+
+ if (pemfile) {
+ #ifndef HAVE_TLS_EXTENSIONS
+ config_cond_info cfginfo;
+ uint32_t j = (uint32_t)p->cvlist[i].k_id;
+ config_get_config_cond_info(&cfginfo, j);
+ if (j > 0 && (COMP_SERVER_SOCKET != cfginfo.comp
+ || cfginfo.cond != CONFIG_COND_EQ)) {
+ if (COMP_HTTP_HOST == cfginfo.comp)
+ log_error(srv->errh, __FILE__, __LINE__, "SSL:"
+ "can't use ssl.pemfile with $HTTP[\"host\"], "
+ "as openssl version does not support TLS extensions");
+ else
+ log_error(srv->errh, __FILE__, __LINE__, "SSL:"
+ "ssl.pemfile only works in SSL socket binding context "
+ "as openssl version does not support TLS extensions");
+ return HANDLER_ERROR;
+ }
+ #endif
+ if (NULL == privkey) privkey = pemfile;
+ pemfile->v.v =
+ network_openssl_load_pemfile(srv, pemfile->v.b, privkey->v.b,
+ ssl_stapling_file);
+ if (pemfile->v.v)
+ pemfile->vtype = T_CONFIG_LOCAL;
+ else
+ return HANDLER_ERROR;
+ }
+ }
+
+ p->defaults.ssl_verifyclient = 0;
+ p->defaults.ssl_verifyclient_enforce = 1;
+ p->defaults.ssl_verifyclient_depth = 9;
+ p->defaults.ssl_verifyclient_export_cert = 0;
+ p->defaults.ssl_disable_client_renegotiation = 1;
+ p->defaults.ssl_read_ahead = 0;
+
+ /* initialize p->defaults from global config context */
+ if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
+ const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
+ if (-1 != cpv->k_id)
+ mod_openssl_merge_config(&p->defaults, cpv);
+ }
+
+ return mod_openssl_set_defaults_sockets(srv, p);
+}
+
+
+static int
+load_next_chunk (chunkqueue * const cq, off_t max_bytes,
+ const char ** const data, size_t * const data_len,
+ log_error_st * const errh)
+{
+ chunk *c = cq->first;
+
+ /* local_send_buffer is a static buffer of size (LOCAL_SEND_BUFSIZE)
+ *
+ * it has to stay at the same location all the time to satisfy the needs
+ * of SSL_write to pass the SAME parameter in case of a _WANT_WRITE
+ *
+ * buffer is allocated once, is NOT realloced (note: not thread-safe)
+ *
+ * (Note: above restriction no longer true since SSL_CTX_set_mode() is
+ * called with SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)
+ * */
+
+ force_assert(NULL != c);
+
+ switch (c->type) {
+ case MEM_CHUNK:
+ *data = NULL;
+ *data_len = 0;
+ do {
+ size_t have;
+
+ force_assert(c->offset >= 0
+ && c->offset <= (off_t)buffer_string_length(c->mem));
+
+ have = buffer_string_length(c->mem) - c->offset;
+
+ /* copy small mem chunks into single large buffer before SSL_write()
+ * to reduce number times write() called underneath SSL_write() and
+ * potentially reduce number of packets generated if TCP_NODELAY */
+ if (*data_len) {
+ size_t space = LOCAL_SEND_BUFSIZE - *data_len;
+ if (have > space)
+ have = space;
+ if (have > (size_t)max_bytes - *data_len)
+ have = (size_t)max_bytes - *data_len;
+ if (*data != local_send_buffer) {
+ memcpy(local_send_buffer, *data, *data_len);
+ *data = local_send_buffer;
+ }
+ memcpy(local_send_buffer+*data_len,c->mem->ptr+c->offset,have);
+ *data_len += have;
+ continue;
+ }
+
+ if ((off_t) have > max_bytes) have = max_bytes;
+
+ *data = c->mem->ptr + c->offset;
+ *data_len = have;
+ } while ((c = c->next) && c->type == MEM_CHUNK
+ && *data_len < LOCAL_SEND_BUFSIZE
+ && (off_t) *data_len < max_bytes);
+ return 0;
+
+ case FILE_CHUNK:
+ if (0 != chunkqueue_open_file_chunk(cq, errh)) return -1;
+
+ {
+ off_t offset, toSend;
+
+ force_assert(c->offset >= 0 && c->offset <= c->file.length);
+ offset = c->file.start + c->offset;
+ toSend = c->file.length - c->offset;
+
+ if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE;
+ if (toSend > max_bytes) toSend = max_bytes;
+
+ if (-1 == lseek(c->file.fd, offset, SEEK_SET)) {
+ log_perror(errh, __FILE__, __LINE__, "lseek");
+ return -1;
+ }
+ if (-1 == (toSend = read(c->file.fd, local_send_buffer, toSend))) {
+ log_perror(errh, __FILE__, __LINE__, "read");
+ return -1;
+ }
+
+ *data = local_send_buffer;
+ *data_len = toSend;
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int
+mod_openssl_close_notify(handler_ctx *hctx);
+
+
+static int
+connection_write_cq_ssl (connection *con, chunkqueue *cq, off_t max_bytes)
+{
+ handler_ctx *hctx = con->plugin_ctx[plugin_data_singleton->id];
+ SSL *ssl = hctx->ssl;
+ log_error_st * const errh = hctx->errh;
+
+ if (0 != hctx->close_notify) return mod_openssl_close_notify(hctx);
+
+ chunkqueue_remove_finished_chunks(cq);
+
+ while (max_bytes > 0 && NULL != cq->first) {
+ const char *data;
+ size_t data_len;
+ int wr;
+
+ if (0 != load_next_chunk(cq,max_bytes,&data,&data_len,errh)) return -1;
+
+ /**
+ * SSL_write man-page
+ *
+ * WARNING
+ * When an SSL_write() operation has to be repeated because of
+ * SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be
+ * repeated with the same arguments.
+ */
+
+ ERR_clear_error();
+ wr = SSL_write(ssl, data, data_len);
+
+ if (hctx->renegotiations > 1
+ && hctx->conf.ssl_disable_client_renegotiation) {
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: renegotiation initiated by client, killing connection");
+ return -1;
+ }
+
+ if (wr <= 0) {
+ int ssl_r;
+ unsigned long err;
+
+ switch ((ssl_r = SSL_get_error(ssl, wr))) {
+ case SSL_ERROR_WANT_READ:
+ con->is_readable = -1;
+ return 0; /* try again later */
+ case SSL_ERROR_WANT_WRITE:
+ con->is_writable = -1;
+ return 0; /* try again later */
+ case SSL_ERROR_SYSCALL:
+ /* perhaps we have error waiting in our error-queue */
+ if (0 != (err = ERR_get_error())) {
+ do {
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: %d %d %s",ssl_r,wr,ERR_error_string(err,NULL));
+ } while((err = ERR_get_error()));
+ } else if (wr == -1) {
+ /* no, but we have errno */
+ switch(errno) {
+ case EPIPE:
+ case ECONNRESET:
+ return -2;
+ default:
+ log_perror(errh, __FILE__, __LINE__,
+ "SSL: %d %d", ssl_r, wr);
+ break;
+ }
+ } else {
+ /* neither error-queue nor errno ? */
+ log_perror(errh, __FILE__, __LINE__,
+ "SSL (error): %d %d", ssl_r, wr);
+ }
+ break;
+
+ case SSL_ERROR_ZERO_RETURN:
+ /* clean shutdown on the remote side */
+
+ if (wr == 0) return -2;
+
+ /* fall through */
+ default:
+ while((err = ERR_get_error())) {
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: %d %d %s", ssl_r, wr, ERR_error_string(err, NULL));
+ }
+ break;
+ }
+ return -1;
+ }
+
+ chunkqueue_mark_written(cq, wr);
+ max_bytes -= wr;
+
+ if ((size_t) wr < data_len) break; /* try again later */
+ }
+
+ return 0;
+}
+
+
+static int
+connection_read_cq_ssl (connection *con, chunkqueue *cq, off_t max_bytes)
+{
+ handler_ctx *hctx = con->plugin_ctx[plugin_data_singleton->id];
+ int len;
+ char *mem = NULL;
+ size_t mem_len = 0;
+
+ UNUSED(max_bytes);
+
+ if (0 != hctx->close_notify) return mod_openssl_close_notify(hctx);
+
+ ERR_clear_error();
+ do {
+ len = SSL_pending(hctx->ssl);
+ mem_len = len < 2048 ? 2048 : (size_t)len;
+ chunk * const ckpt = cq->last;
+ mem = chunkqueue_get_memory(cq, &mem_len);
+
+ len = SSL_read(hctx->ssl, mem, mem_len);
+ if (len > 0) {
+ chunkqueue_use_memory(cq, ckpt, len);
+ con->bytes_read += len;
+ } else {
+ chunkqueue_use_memory(cq, ckpt, 0);
+ }
+
+ if (hctx->renegotiations > 1
+ && hctx->conf.ssl_disable_client_renegotiation) {
+ log_error(hctx->errh, __FILE__, __LINE__,
+ "SSL: renegotiation initiated by client, killing connection");
+ return -1;
+ }
+
+ #ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+ if (hctx->alpn) {
+ if (hctx->alpn == MOD_OPENSSL_ALPN_ACME_TLS_1) {
+ chunkqueue_reset(cq);
+ /* initiate handshake in order to send ServerHello.
+ * Once TLS handshake is complete, return -1 to result in
+ * CON_STATE_ERROR so that socket connection is quickly closed*/
+ if (1 == SSL_do_handshake(hctx->ssl)) return -1;
+ len = -1;
+ break;
+ }
+ hctx->alpn = 0;
+ }
+ #endif
+ } while (len > 0
+ && (hctx->conf.ssl_read_ahead || SSL_pending(hctx->ssl) > 0));
+
+ if (len < 0) {
+ int oerrno = errno;
+ int rc, ssl_err;
+ switch ((rc = SSL_get_error(hctx->ssl, len))) {
+ case SSL_ERROR_WANT_WRITE:
+ con->is_writable = -1;
+ /* fall through */
+ case SSL_ERROR_WANT_READ:
+ con->is_readable = 0;
+
+ /* the manual says we have to call SSL_read with the same arguments
+ * next time. we ignore this restriction; no one has complained
+ * about it in 1.5 yet, so it probably works anyway.
+ */
+
+ return 0;
+ case SSL_ERROR_SYSCALL:
+ /**
+ * man SSL_get_error()
+ *
+ * SSL_ERROR_SYSCALL
+ * Some I/O error occurred. The OpenSSL error queue may contain
+ * more information on the error. If the error queue is empty
+ * (i.e. ERR_get_error() returns 0), ret can be used to find out
+ * more about the error: If ret == 0, an EOF was observed that
+ * violates the protocol. If ret == -1, the underlying BIO
+ * reported an I/O error (for socket I/O on Unix systems, consult
+ * errno for details).
+ *
+ */
+ while((ssl_err = ERR_get_error())) {
+ /* get all errors from the error-queue */
+ log_error(hctx->errh, __FILE__, __LINE__,
+ "SSL: %d %s", rc, ERR_error_string(ssl_err, NULL));
+ }
+
+ switch(oerrno) {
+ case ECONNRESET:
+ if (!hctx->conf.ssl_log_noise) break;
+ /* fall through */
+ default:
+ /* (oerrno should be something like ECONNABORTED not 0
+ * if client disconnected before anything was sent
+ * (e.g. TCP connection probe), but it does not appear
+ * that openssl provides such notification, not even
+ * something like SSL_R_SSL_HANDSHAKE_FAILURE) */
+ if (0==oerrno && 0==cq->bytes_in && !hctx->conf.ssl_log_noise)
+ break;
+
+ log_error(hctx->errh, __FILE__, __LINE__,
+ "SSL: %d %d %d %s", len, rc, oerrno, strerror(oerrno));
+ break;
+ }
+
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ /* clean shutdown on the remote side */
+
+ if (rc == 0) {
+ /* FIXME: later */
+ }
+
+ /* fall through */
+ default:
+ while((ssl_err = ERR_get_error())) {
+ switch (ERR_GET_REASON(ssl_err)) {
+ case SSL_R_SSL_HANDSHAKE_FAILURE:
+ #ifdef SSL_R_UNEXPECTED_EOF_WHILE_READING
+ case SSL_R_UNEXPECTED_EOF_WHILE_READING:
+ #endif
+ #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_CA
+ case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
+ #endif
+ #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN
+ case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
+ #endif
+ #ifdef SSL_R_SSLV3_ALERT_BAD_CERTIFICATE
+ case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
+ #endif
+ if (!hctx->conf.ssl_log_noise) continue;
+ break;
+ default:
+ break;
+ }
+ /* get all errors from the error-queue */
+ log_error(hctx->errh, __FILE__, __LINE__,
+ "SSL: %d %s", rc, ERR_error_string(ssl_err, NULL));
+ }
+ break;
+ }
+ return -1;
+ } else if (len == 0) {
+ con->is_readable = 0;
+ /* the other end close the connection -> KEEP-ALIVE */
+
+ return -2;
+ } else {
+ return 0;
+ }
+}
+
+
+CONNECTION_FUNC(mod_openssl_handle_con_accept)
+{
+ server_socket *srv_sock = con->srv_socket;
+ if (!srv_sock->is_ssl) return HANDLER_GO_ON;
+
+ plugin_data *p = p_d;
+ handler_ctx * const hctx = handler_ctx_init();
+ request_st * const r = &con->request;
+ hctx->r = r;
+ hctx->con = con;
+ hctx->tmp_buf = con->srv->tmp_buf;
+ hctx->errh = r->conf.errh;
+ con->plugin_ctx[p->id] = hctx;
+
+ plugin_ssl_ctx * const s = p->ssl_ctxs + srv_sock->sidx;
+ hctx->ssl = SSL_new(s->ssl_ctx);
+ if (NULL != hctx->ssl
+ && SSL_set_app_data(hctx->ssl, hctx)
+ && SSL_set_fd(hctx->ssl, con->fd)) {
+ SSL_set_accept_state(hctx->ssl);
+ con->network_read = connection_read_cq_ssl;
+ con->network_write = connection_write_cq_ssl;
+ con->proto_default_port = 443; /* "https" */
+ mod_openssl_patch_config(r, &hctx->conf);
+ return HANDLER_GO_ON;
+ }
+ else {
+ log_error(r->conf.errh, __FILE__, __LINE__,
+ "SSL: %s", ERR_error_string(ERR_get_error(), NULL));
+ return HANDLER_ERROR;
+ }
+}
+
+
+static void
+mod_openssl_detach(handler_ctx *hctx)
+{
+ /* step aside from further SSL processing
+ * (used after handle_connection_shut_wr hook) */
+ /* future: might restore prior network_read and network_write fn ptrs */
+ hctx->con->is_ssl_sock = 0;
+ /* if called after handle_connection_shut_wr hook, shutdown SHUT_WR */
+ if (-1 == hctx->close_notify) shutdown(hctx->con->fd, SHUT_WR);
+ hctx->close_notify = 1;
+}
+
+
+CONNECTION_FUNC(mod_openssl_handle_con_shut_wr)
+{
+ plugin_data *p = p_d;
+ handler_ctx *hctx = con->plugin_ctx[p->id];
+ if (NULL == hctx) return HANDLER_GO_ON;
+
+ hctx->close_notify = -2;
+ if (SSL_is_init_finished(hctx->ssl)) {
+ mod_openssl_close_notify(hctx);
+ }
+ else {
+ mod_openssl_detach(hctx);
+ }
+
+ return HANDLER_GO_ON;
+}
+
+
+static int
+mod_openssl_close_notify(handler_ctx *hctx)
+{
+ int ret, ssl_r;
+ unsigned long err;
+ log_error_st *errh;
+
+ if (1 == hctx->close_notify) return -2;
+
+ ERR_clear_error();
+ switch ((ret = SSL_shutdown(hctx->ssl))) {
+ case 1:
+ mod_openssl_detach(hctx);
+ return -2;
+ case 0:
+ /* Drain SSL read buffers in case pending records need processing.
+ * Limit to reading next record to avoid denial of service when CPU
+ * processing TLS is slower than arrival speed of TLS data packets.
+ * (unless hctx->conf.ssl_read_ahead is set)
+ *
+ * references:
+ *
+ * "New session ticket breaks bidirectional shutdown of TLS 1.3 connection"
+ * https://github.com/openssl/openssl/issues/6262
+ *
+ * The peer is still allowed to send data after receiving the
+ * "close notify" event. If the peer did send data it need to be
+ * processed by calling SSL_read() before calling SSL_shutdown() a
+ * second time. SSL_read() will indicate the end of the peer data by
+ * returning <= 0 and SSL_get_error() returning
+ * SSL_ERROR_ZERO_RETURN. It is recommended to call SSL_read()
+ * between SSL_shutdown() calls.
+ *
+ * Additional discussion in "Auto retry in shutdown"
+ * https://github.com/openssl/openssl/pull/6340
+ */
+ ssl_r = SSL_pending(hctx->ssl);
+ if (ssl_r) {
+ do {
+ char buf[4096];
+ ret = SSL_read(hctx->ssl, buf, (int)sizeof(buf));
+ } while (ret > 0 && (hctx->conf.ssl_read_ahead||(ssl_r-=ret)));
+ }
+
+ ERR_clear_error();
+ switch ((ret = SSL_shutdown(hctx->ssl))) {
+ case 1:
+ mod_openssl_detach(hctx);
+ return -2;
+ case 0:
+ hctx->close_notify = -1;
+ return 0;
+ default:
+ break;
+ }
+
+ /* fall through */
+ default:
+
+ if (!SSL_is_init_finished(hctx->ssl)) {
+ mod_openssl_detach(hctx);
+ return -2;
+ }
+
+ switch ((ssl_r = SSL_get_error(hctx->ssl, ret))) {
+ case SSL_ERROR_ZERO_RETURN:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_READ:
+ hctx->close_notify = -1;
+ return 0; /* try again later */
+ case SSL_ERROR_SYSCALL:
+ /* perhaps we have error waiting in our error-queue */
+ errh = hctx->r->conf.errh;
+ if (0 != (err = ERR_get_error())) {
+ do {
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: %d %d %s",ssl_r,ret,ERR_error_string(err,NULL));
+ } while((err = ERR_get_error()));
+ } else if (errno != 0) {
+ /*ssl bug (see lighttpd ticket #2213): sometimes errno==0*/
+ switch(errno) {
+ case EPIPE:
+ case ECONNRESET:
+ break;
+ default:
+ log_perror(errh, __FILE__, __LINE__,
+ "SSL (error): %d %d", ssl_r, ret);
+ break;
+ }
+ }
+
+ break;
+ default:
+ errh = hctx->r->conf.errh;
+ while((err = ERR_get_error())) {
+ log_error(errh, __FILE__, __LINE__,
+ "SSL: %d %d %s", ssl_r, ret, ERR_error_string(err, NULL));
+ }
+
+ break;
+ }
+ }
+ ERR_clear_error();
+ hctx->close_notify = -1;
+ return ret;
+}
+
+
+CONNECTION_FUNC(mod_openssl_handle_con_close)
+{
+ plugin_data *p = p_d;
+ handler_ctx *hctx = con->plugin_ctx[p->id];
+ if (NULL != hctx) {
+ con->plugin_ctx[p->id] = NULL;
+ handler_ctx_free(hctx);
+ }
+
+ return HANDLER_GO_ON;
+}
+
+
+static void
+https_add_ssl_client_entries (request_st * const r, handler_ctx * const hctx)
+{
+ buffer * const tb = r->tmp_buf;
+ X509 *xs;
+ X509_NAME *xn;
+ int i, nentries;
+
+ long vr = SSL_get_verify_result(hctx->ssl);
+ if (vr != X509_V_OK) {
+ char errstr[256];
+ ERR_error_string_n(vr, errstr, sizeof(errstr));
+ buffer_copy_string_len(tb, CONST_STR_LEN("FAILED:"));
+ buffer_append_string(tb, errstr);
+ http_header_env_set(r,
+ CONST_STR_LEN("SSL_CLIENT_VERIFY"),
+ CONST_BUF_LEN(tb));
+ return;
+ } else if (!(xs = SSL_get_peer_certificate(hctx->ssl))) {
+ http_header_env_set(r,
+ CONST_STR_LEN("SSL_CLIENT_VERIFY"),
+ CONST_STR_LEN("NONE"));
+ return;
+ } else {
+ http_header_env_set(r,
+ CONST_STR_LEN("SSL_CLIENT_VERIFY"),
+ CONST_STR_LEN("SUCCESS"));
+ }
+
+ xn = X509_get_subject_name(xs);
+ {
+ char buf[256];
+ int len = safer_X509_NAME_oneline(xn, buf, sizeof(buf));
+ if (len > 0) {
+ if (len >= (int)sizeof(buf)) len = (int)sizeof(buf)-1;
+ http_header_env_set(r,
+ CONST_STR_LEN("SSL_CLIENT_S_DN"),
+ buf, (size_t)len);
+ }
+ }
+ buffer_copy_string_len(tb, CONST_STR_LEN("SSL_CLIENT_S_DN_"));
+ for (i = 0, nentries = X509_NAME_entry_count(xn); i < nentries; ++i) {
+ int xobjnid;
+ const char * xobjsn;
+ X509_NAME_ENTRY *xe;
+
+ if (!(xe = wolfSSL_X509_NAME_get_entry(xn, i))) {
+ continue;
+ }
+ xobjnid = OBJ_obj2nid((ASN1_OBJECT*)X509_NAME_ENTRY_get_object(xe));
+ xobjsn = OBJ_nid2sn(xobjnid);
+ if (xobjsn) {
+ buffer_string_set_length(tb, sizeof("SSL_CLIENT_S_DN_")-1);
+ buffer_append_string(tb, xobjsn);
+ http_header_env_set(r,
+ CONST_BUF_LEN(tb),
+ (const char*)X509_NAME_ENTRY_get_data(xe)->data,
+ X509_NAME_ENTRY_get_data(xe)->length);
+ }
+ }
+
+ {
+ byte buf[64];
+ int bsz = (int)sizeof(buf);
+ if (wolfSSL_X509_get_serial_number(xs, buf, &bsz) == WOLFSSL_SUCCESS) {
+ char serialHex[128+1];
+ li_tohex_uc(serialHex, sizeof(serialHex), (char *)buf, (size_t)bsz);
+ http_header_env_set(r,
+ CONST_STR_LEN("SSL_CLIENT_M_SERIAL"),
+ serialHex, strlen(serialHex));
+ }
+ }
+
+ if (!buffer_string_is_empty(hctx->conf.ssl_verifyclient_username)) {
+ /* pick one of the exported values as "REMOTE_USER", for example
+ * ssl.verifyclient.username = "SSL_CLIENT_S_DN_UID"
+ * or
+ * ssl.verifyclient.username = "SSL_CLIENT_S_DN_emailAddress"
+ */
+ const buffer *varname = hctx->conf.ssl_verifyclient_username;
+ const buffer *vb = http_header_env_get(r, CONST_BUF_LEN(varname));
+ if (vb) { /* same as http_auth.c:http_auth_setenv() */
+ http_header_env_set(r,
+ CONST_STR_LEN("REMOTE_USER"),
+ CONST_BUF_LEN(vb));
+ http_header_env_set(r,
+ CONST_STR_LEN("AUTH_TYPE"),
+ CONST_STR_LEN("SSL_CLIENT_VERIFY"));
+ }
+ }
+
+ if (hctx->conf.ssl_verifyclient_export_cert) {
+ int dersz, pemsz;
+ const unsigned char *der = wolfSSL_X509_get_der(xs, &dersz);
+ pemsz = der ? wc_DerToPemEx(der, dersz, NULL, 0, NULL, CERT_TYPE) : 0;
+ if (pemsz > 0) {
+ buffer_string_prepare_copy(tb, pemsz);
+ if (0 == wc_DerToPemEx(der, dersz, (byte *)tb->ptr, pemsz,
+ NULL, CERT_TYPE))
+ http_header_env_set(r,
+ CONST_STR_LEN("SSL_CLIENT_CERT"),
+ tb->ptr, (uint32_t)pemsz);
+ }
+ }
+ X509_free(xs);
+}
+
+
+static void
+http_cgi_ssl_env (request_st * const r, handler_ctx * const hctx)
+{
+ const char *s;
+ const SSL_CIPHER *cipher;
+
+ s = SSL_get_version(hctx->ssl);
+ http_header_env_set(r, CONST_STR_LEN("SSL_PROTOCOL"), s, strlen(s));
+
+ if ((cipher = SSL_get_current_cipher(hctx->ssl))) {
+ int usekeysize, algkeysize = 0;
+ char buf[LI_ITOSTRING_LENGTH];
+ s = SSL_CIPHER_get_name(cipher);
+ http_header_env_set(r, CONST_STR_LEN("SSL_CIPHER"), s, strlen(s));
+ usekeysize = wolfSSL_CIPHER_get_bits(cipher, &algkeysize);
+ if (0 == algkeysize) algkeysize = usekeysize;
+ http_header_env_set(r, CONST_STR_LEN("SSL_CIPHER_USEKEYSIZE"),
+ buf, li_itostrn(buf, sizeof(buf), usekeysize));
+ http_header_env_set(r, CONST_STR_LEN("SSL_CIPHER_ALGKEYSIZE"),
+ buf, li_itostrn(buf, sizeof(buf), algkeysize));
+ }
+}
+
+
+REQUEST_FUNC(mod_openssl_handle_request_env)
+{
+ plugin_data *p = p_d;
+ /* simple flag for request_env_patched */
+ if (r->plugin_ctx[p->id]) return HANDLER_GO_ON;
+ handler_ctx *hctx = r->con->plugin_ctx[p->id];
+ if (NULL == hctx) return HANDLER_GO_ON;
+ r->plugin_ctx[p->id] = (void *)(uintptr_t)1u;
+
+ http_cgi_ssl_env(r, hctx);
+ if (hctx->conf.ssl_verifyclient) {
+ https_add_ssl_client_entries(r, hctx);
+ }
+
+ return HANDLER_GO_ON;
+}
+
+
+REQUEST_FUNC(mod_openssl_handle_uri_raw)
+{
+ /* mod_openssl must be loaded prior to mod_auth
+ * if mod_openssl is configured to set REMOTE_USER based on client cert */
+ /* mod_openssl must be loaded after mod_extforward
+ * if mod_openssl config is based on lighttpd.conf remote IP conditional
+ * using remote IP address set by mod_extforward, *unless* PROXY protocol
+ * is enabled with extforward.hap-PROXY = "enable", in which case the
+ * reverse is true: mod_extforward must be loaded after mod_openssl */
+ plugin_data *p = p_d;
+ handler_ctx *hctx = r->con->plugin_ctx[p->id];
+ if (NULL == hctx) return HANDLER_GO_ON;
+
+ mod_openssl_patch_config(r, &hctx->conf);
+ if (hctx->conf.ssl_verifyclient) {
+ mod_openssl_handle_request_env(r, p);
+ }
+
+ return HANDLER_GO_ON;
+}
+
+
+REQUEST_FUNC(mod_openssl_handle_request_reset)
+{
+ plugin_data *p = p_d;
+ r->plugin_ctx[p->id] = NULL; /* simple flag for request_env_patched */
+ return HANDLER_GO_ON;
+}
+
+
+TRIGGER_FUNC(mod_openssl_handle_trigger) {
+ const plugin_data * const p = p_d;
+ const time_t cur_ts = log_epoch_secs;
+ if (cur_ts & 0x3f) return HANDLER_GO_ON; /*(continue once each 64 sec)*/
+ UNUSED(srv);
+ UNUSED(p);
+
+ #ifdef HAVE_SESSION_TICKET
+ mod_openssl_session_ticket_key_check(p, cur_ts);
+ #endif
+
+ #ifndef OPENSSL_NO_OCSP
+ mod_openssl_refresh_stapling_files(srv, p, cur_ts);
+ #endif
+
+ return HANDLER_GO_ON;
+}
+
+
+int mod_wolfssl_plugin_init (plugin *p);
+int mod_wolfssl_plugin_init (plugin *p)
+{
+ p->version = LIGHTTPD_VERSION_ID;
+ p->name = "wolfssl";
+ p->init = mod_openssl_init;
+ p->cleanup = mod_openssl_free;
+ p->priv_defaults= mod_openssl_set_defaults;
+
+ p->handle_connection_accept = mod_openssl_handle_con_accept;
+ p->handle_connection_shut_wr = mod_openssl_handle_con_shut_wr;
+ p->handle_connection_close = mod_openssl_handle_con_close;
+ p->handle_uri_raw = mod_openssl_handle_uri_raw;
+ p->handle_request_env = mod_openssl_handle_request_env;
+ p->handle_request_reset = mod_openssl_handle_request_reset;
+ p->handle_trigger = mod_openssl_handle_trigger;
+
+ return 0;
+}
+
+
+static int
+mod_openssl_ssl_conf_proto_val (server *srv, plugin_config_socket *s, const buffer *b, int max)
+{
+ if (NULL == b) /* default: min TLSv1.2, max TLSv1.3 */
+ #ifdef TLS1_3_VERSION
+ return max ? TLS1_3_VERSION : TLS1_2_VERSION;
+ #else
+ return TLS1_2_VERSION;
+ #endif
+ else if (buffer_eq_icase_slen(b, CONST_STR_LEN("None"))) /*"disable" limit*/
+ return max
+ ?
+ #ifdef TLS1_3_VERSION
+ TLS1_3_VERSION
+ #else
+ TLS1_2_VERSION
+ #endif
+ : (s->ssl_use_sslv3 ? SSL3_VERSION : TLS1_VERSION);
+ else if (buffer_eq_icase_slen(b, CONST_STR_LEN("SSLv3")))
+ return SSL3_VERSION;
+ else if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.0")))
+ return TLS1_VERSION;
+ else if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.1")))
+ return TLS1_1_VERSION;
+ else if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.2")))
+ return TLS1_2_VERSION;
+ #ifdef TLS1_3_VERSION
+ else if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.3")))
+ return TLS1_3_VERSION;
+ #endif
+ else {
+ if (buffer_eq_icase_slen(b, CONST_STR_LEN("DTLSv1"))
+ || buffer_eq_icase_slen(b, CONST_STR_LEN("DTLSv1.2")))
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: ssl.openssl.ssl-conf-cmd %s %s ignored",
+ max ? "MaxProtocol" : "MinProtocol", b->ptr);
+ else
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: ssl.openssl.ssl-conf-cmd %s %s invalid; ignored",
+ max ? "MaxProtocol" : "MinProtocol", b->ptr);
+ }
+ #ifdef TLS1_3_VERSION
+ return max ? TLS1_3_VERSION : TLS1_2_VERSION;
+ #else
+ return TLS1_2_VERSION;
+ #endif
+}
+
+
+static int
+mod_openssl_ssl_conf_cmd (server *srv, plugin_config_socket *s)
+{
+ /* reference:
+ * https://www.openssl.org/docs/man1.1.1/man3/SSL_CONF_cmd.html */
+ int rc = 0;
+ buffer *cipherstring = NULL;
+ /*buffer *ciphersuites = NULL;*/
+ buffer *minb = NULL;
+ buffer *maxb = NULL;
+ buffer *curves = NULL;
+
+ for (size_t i = 0; i < s->ssl_conf_cmd->used; ++i) {
+ data_string *ds = (data_string *)s->ssl_conf_cmd->data[i];
+ if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("CipherString")))
+ cipherstring = &ds->value;
+ #if 0
+ else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Ciphersuites")))
+ ciphersuites = &ds->value;
+ #endif
+ else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Curves"))
+ || buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Groups")))
+ curves = &ds->value;
+ else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("MaxProtocol")))
+ maxb = &ds->value;
+ else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("MinProtocol")))
+ minb = &ds->value;
+ else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Protocol"))) {
+ /* openssl config for Protocol=... is complex and deprecated */
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: ssl.openssl.ssl-conf-cmd %s ignored; "
+ "use MinProtocol=... and MaxProtocol=... instead",
+ ds->key.ptr);
+ }
+ else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Options"))) {
+ for (char *v = ds->value.ptr, *e; *v; v = e) {
+ while (*v == ' ' || *v == '\t' || *v == ',') ++v;
+ int flag = 1;
+ if (*v == '-') {
+ flag = 0;
+ ++v;
+ }
+ for (e = v; light_isalpha(*e); ++e) ;
+ switch ((int)(e-v)) {
+ case 11:
+ if (buffer_eq_icase_ssn(v, "Compression", 11)) {
+ if (flag)
+ SSL_CTX_clear_options(s->ssl_ctx,
+ SSL_OP_NO_COMPRESSION);
+ else
+ SSL_CTX_set_options(s->ssl_ctx,
+ SSL_OP_NO_COMPRESSION);
+ continue;
+ }
+ break;
+ case 13:
+ if (buffer_eq_icase_ssn(v, "SessionTicket", 13)) {
+ if (flag)
+ SSL_CTX_clear_options(s->ssl_ctx,
+ SSL_OP_NO_TICKET);
+ else
+ SSL_CTX_set_options(s->ssl_ctx,
+ SSL_OP_NO_TICKET);
+ continue;
+ }
+ break;
+ case 16:
+ if (buffer_eq_icase_ssn(v, "ServerPreference", 16)) {
+ if (flag)
+ SSL_CTX_set_options(s->ssl_ctx,
+ SSL_OP_CIPHER_SERVER_PREFERENCE);
+ else
+ SSL_CTX_clear_options(s->ssl_ctx,
+ SSL_OP_CIPHER_SERVER_PREFERENCE);
+ s->ssl_honor_cipher_order = flag;
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+ /* warn if not explicitly handled or ignored above */
+ if (!flag) --v;
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: ssl.openssl.ssl-conf-cmd Options %.*s "
+ "ignored", (int)(e-v), v);
+ }
+ }
+ #if 0
+ else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("..."))) {
+ }
+ #endif
+ else {
+ /* warn if not explicitly handled or ignored above */
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: ssl.openssl.ssl-conf-cmd %s ignored",
+ ds->key.ptr);
+ }
+
+ }
+
+ if (minb) {
+ int n = mod_openssl_ssl_conf_proto_val(srv, s, minb, 0);
+ /*(wolfSSL_CTX_SetMinVersion() alt uses enums with different values)*/
+ switch (n) {
+ case SSL3_VERSION: n = WOLFSSL_SSLV3; break;
+ case TLS1_VERSION: n = WOLFSSL_TLSV1; break;
+ case TLS1_1_VERSION: n = WOLFSSL_TLSV1_1; break;
+ case TLS1_2_VERSION: n = WOLFSSL_TLSV1_2; break;
+ case TLS1_3_VERSION: n = WOLFSSL_TLSV1_3; break;
+ default: rc = -1; break;
+ }
+ if (wolfSSL_CTX_SetMinVersion(s->ssl_ctx, n) != WOLFSSL_SUCCESS)
+ rc = -1;
+ }
+
+ if (maxb) {
+ /* WolfSSL max ver is set at WolfSSL compile-time */
+ }
+
+ if (cipherstring) {
+ /* Disable support for low encryption ciphers */
+ buffer_append_string_len(cipherstring,
+ CONST_STR_LEN(":!aNULL:!eNULL:!EXP"));
+ if (SSL_CTX_set_cipher_list(s->ssl_ctx, s->ssl_cipher_list->ptr) != 1) {
+ log_error(srv->errh, __FILE__, __LINE__,
+ "SSL: %s", ERR_error_string(ERR_get_error(), NULL));
+ rc = -1;
+ }
+
+ if (s->ssl_honor_cipher_order)
+ SSL_CTX_set_options(s->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+
+ if (curves) {
+ if (!mod_openssl_ssl_conf_curves(srv, s, curves))
+ rc = -1;
+ }
+
+ return rc;
+}
diff --git a/src/rand.c b/src/rand.c
index 1aa95b21..ab515874 100644
--- a/src/rand.c
+++ b/src/rand.c
@@ -37,16 +37,16 @@
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#endif
-#ifdef USE_WOLFSSL_CRYPTO
-#undef USE_OPENSSL_CRYPTO
-#undef USE_GNUTLS_CRYPTO
-#include <wolfssl/wolfcrypt/random.h>
-#endif
#ifdef USE_OPENSSL_CRYPTO
+#undef USE_WOLFSSL_CRYPTO
#undef USE_GNUTLS_CRYPTO
#include <openssl/opensslv.h> /* OPENSSL_VERSION_NUMBER */
#include <openssl/rand.h>
#endif
+#ifdef USE_WOLFSSL_CRYPTO
+#undef USE_GNUTLS_CRYPTO
+#include <wolfssl/wolfcrypt/random.h>
+#endif
#ifdef USE_GNUTLS_CRYPTO
#include <gnutls/crypto.h>
#endif
diff --git a/src/sys-crypto-md.h b/src/sys-crypto-md.h
index b528c256..a3f8b3e6 100644
--- a/src/sys-crypto-md.h
+++ b/src/sys-crypto-md.h
@@ -144,7 +144,7 @@ SHA256_Update(SHA256_CTX *ctx, const void *data, size_t length)
}
#endif
-#elif defined(USE_WOLFSSL_CRYPTO)
+#elif defined(USE_WOLFSSL_CRYPTO) && !defined(USE_OPENSSL_CRYPTO)
/* WolfSSL compatibility API for OpenSSL unnecessarily bounces through an extra
* layer of indirection. However, to avoid conflicting typedefs when includers
@@ -164,12 +164,14 @@ SHA256_Update(SHA256_CTX *ctx, const void *data, size_t length)
static inline int
MD4_Init(MD4_CTX *ctx)
{
- return (0 == wc_InitMd4((Md4 *)ctx));
+ wc_InitMd4((Md4 *)ctx);
+ return 1;
}
static inline int
MD4_Final(unsigned char *digest, MD4_CTX *ctx)
{
- return (0 == wc_Md4Final((Md4 *)ctx, digest));
+ wc_Md4Final((Md4 *)ctx, digest);
+ return 1;
}
static inline void
MD4_Update(MD4_CTX *ctx, const void *data, size_t length)
diff --git a/src/sys-crypto.h b/src/sys-crypto.h
index 7ea3a29a..4175ee55 100644
--- a/src/sys-crypto.h
+++ b/src/sys-crypto.h
@@ -7,6 +7,7 @@
#define USE_OPENSSL_CRYPTO
#endif
+#ifndef USE_OPENSSL_CRYPTO
#ifdef HAVE_WOLFSSL_SSL_H
#define USE_LIB_CRYPTO
#define USE_WOLFSSL_CRYPTO
@@ -17,6 +18,7 @@
* for use by lighttpd */
#include <wolfssl/options.h>
#endif
+#endif
#ifdef HAVE_LIBMBEDCRYPTO
#define USE_LIB_CRYPTO