diff options
author | Glenn Strauss <gstrauss@gluelogic.com> | 2020-03-18 23:21:19 -0400 |
---|---|---|
committer | Glenn Strauss <gstrauss@gluelogic.com> | 2020-07-08 22:51:31 -0400 |
commit | cb753ec5b51bda3f5630409e18205f874ecce7c7 (patch) | |
tree | e959902183887aa49a4ac53590ce93d3e9e5a337 | |
parent | 7de51cc77bc0d7ed7f93e65ee9cbd91c5f963c1b (diff) | |
download | lighttpd-git-cb753ec5b51bda3f5630409e18205f874ecce7c7.tar.gz |
[mod_mbedtls] mbedTLS option for TLS
(experimental)
mod_mbedtls supports most ssl.* config options supported by mod_openssl
thx Ward Willats for the initial discussion and attempt in the comments
https://redmine.lighttpd.net/boards/3/topics/7029
-rw-r--r-- | SConstruct | 12 | ||||
-rw-r--r-- | configure.ac | 45 | ||||
-rw-r--r-- | meson_options.txt | 5 | ||||
-rw-r--r-- | src/CMakeLists.txt | 29 | ||||
-rw-r--r-- | src/Makefile.am | 8 | ||||
-rw-r--r-- | src/SConscript | 3 | ||||
-rw-r--r-- | src/algo_sha1.h | 23 | ||||
-rw-r--r-- | src/configfile.c | 4 | ||||
-rw-r--r-- | src/meson.build | 18 | ||||
-rw-r--r-- | src/mod_auth.c | 16 | ||||
-rw-r--r-- | src/mod_authn_file.c | 32 | ||||
-rw-r--r-- | src/mod_mbedtls.c | 3478 | ||||
-rw-r--r-- | src/mod_secdownload.c | 34 | ||||
-rw-r--r-- | src/network.c | 10 | ||||
-rw-r--r-- | src/rand.c | 67 | ||||
-rw-r--r-- | src/server.c | 15 | ||||
-rw-r--r-- | src/sys-crypto.h | 6 |
17 files changed, 3792 insertions, 13 deletions
@@ -252,6 +252,7 @@ vars.AddVariables( BoolVariable('with_memcached', 'enable memcached support', 'no'), PackageVariable('with_mysql', 'enable mysql support', 'no'), BoolVariable('with_openssl', 'enable openssl support', 'no'), + PackageVariable('with_mbedtls', 'enable mbedTLS support', 'no'), PackageVariable('with_wolfssl', 'enable wolfSSL support', 'no'), BoolVariable('with_nettle', 'enable Nettle support', 'no'), BoolVariable('with_pam', 'enable PAM auth support', 'no'), @@ -341,6 +342,7 @@ if 1: LIBSQLITE3 = '', LIBSSL = '', LIBUUID = '', + LIBX509 = '', LIBXML2 = '', LIBZ = '', ) @@ -609,6 +611,16 @@ if 1: LIBCRYPTO = 'wolfssl', ) + if env['with_mbedtls']: + if not autoconf.CheckLibWithHeader('mbedtls', 'mbedtls/ssl.h', 'C'): + fail("Couldn't find mbedtls") + autoconf.env.Append( + CPPFLAGS = [ '-DHAVE_LIBMBEDCRYPTO' ], + LIBSSL = 'mbedtls', + LIBX509 = 'mbedx509', + LIBCRYPTO = 'mbedcrypto', + ) + if env['with_nettle']: if not autoconf.CheckLibWithHeader('nettle', 'nettle/nettle-types.h', 'C'): fail("Couldn't find Nettle") diff --git a/configure.ac b/configure.ac index 83d273b8..4b309e91 100644 --- a/configure.ac +++ b/configure.ac @@ -686,7 +686,7 @@ if test "$WITH_OPENSSL" != no; then [ -lcrypto "$DL_LIB" ] ) - AC_DEFINE([HAVE_LIBSSL], [], [Have libssl]) + AC_DEFINE([HAVE_LIBSSL], [1], [Have libssl]) AC_SUBST([SSL_LIB]) AC_SUBST([CRYPTO_LIB]) fi @@ -737,6 +737,46 @@ 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]) fi +dnl Check for mbedTLS +AC_MSG_NOTICE([----------------------------------------]) +AC_MSG_CHECKING([for mbedTLS]) +AC_ARG_WITH([mbedtls], + AC_HELP_STRING([--with-mbedtls@<:@=DIR@:>@],[Include mbedTLS support. DIR points to the installation root. (default no)]), + [WITH_MBEDTLS=$withval], + [WITH_MBEDTLS=no] +) + +if test "$WITH_MBEDTLS" != "no"; then + use_mbedtls=yes + if test "$WITH_MBEDTLS" != "yes"; then + CPPFLAGS="$CPPFLAGS -I$WITH_MBEDTLS/include" + LDFLAGS="$LDFLAGS -L$WITH_MBEDTLS/lib" + fi +else + use_mbedtls=no +fi +AC_MSG_RESULT([$use_mbedtls]) +AM_CONDITIONAL(BUILD_WITH_MBEDTLS, test ! $WITH_MBEDTLS = no) + +if test "x$use_mbedtls" = "xyes"; then + AC_CHECK_HEADERS([mbedtls/ssl.h]) + OLDLIBS="$LIBS" + 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" + CRYPTO_LIB="-lmbedcrypto" + AC_DEFINE(HAVE_LIBMBEDTLS, [1], [Have libmbedtls library]) + AC_DEFINE(HAVE_LIBMBEDX509, [1], [Have libmbedx509 library]) + AC_DEFINE(HAVE_LIBMBEDCRYPTO, [1], [Have libmbedcrypto library]) ], + [],[-lmbedcrypto "$DL_LIB"]) + ],[],[-lmbedcrypto "$DL_LIB"]) + ],[],[]) + LIBS="$OLDLIBS" + AC_SUBST(MTLS_LIB) + AC_SUBST(CRYPTO_LIB) +fi + dnl Check for Nettle (and overwrite CRYPTO_LIB if set by OpenSSL or wolfSSL) AC_MSG_NOTICE([----------------------------------------]) AC_MSG_CHECKING([for Nettle]) @@ -1560,6 +1600,9 @@ lighty_track_feature "pam" "mod_authn_pam" \ lighty_track_feature "network-openssl" "mod_openssl" \ 'test "$WITH_OPENSSL" != no || test "$WITH_WOLFSSL" != no' +lighty_track_feature "network-mbedtls" "mod_mbedtls" \ + 'test "$WITH_MBEDTLS" != no' + lighty_track_feature "auth-crypt" "" \ 'test "$found_crypt" != no' diff --git a/meson_options.txt b/meson_options.txt index 92d3f4a9..17918e8b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -53,6 +53,11 @@ option('with_maxminddb', value: false, description: 'with MaxMind GeoIP2-support mod_maxminddb [default: off]', ) +option('with_mbedtls', + type: 'string', + value: 'false', + description: 'with mbedTLS-support [default: off]', +) option('with_memcached', type: 'boolean', value: false, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fbfb5d93..18008b84 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,7 @@ option(WITH_XATTR "with xattr-support for the stat-cache [default: off]") option(WITH_MYSQL "with mysql-support for mod_vhostdb_mysql [default: off]") option(WITH_PGSQL "with postgres-support for mod_vhostdb_pgsql [default: off]") option(WITH_DBI "with dbi-support for mod_vhostdb_dbi [default: off]") +option(WITH_MBEDTLS "with mbedTLS-support [default: off]") option(WITH_OPENSSL "with openssl-support [default: off]") option(WITH_WOLFSSL "with wolfSSL-support [default: off]") option(WITH_NETTLE "with Nettle-support [default: off]") @@ -371,6 +372,24 @@ if(WITH_OPENSSL AND WITH_WOLFSSL) message(FATAL_ERROR "lighttpd should not be built with both --with-openssl and --with-wolfssl") endif() +if(WITH_MBEDTLS) + check_include_files(mbedtls/ssl.h HAVE_MBEDTLS_SSL_H) + if(HAVE_MBEDTLS_SSL_H) + check_library_exists(mbedcrypto mbedtls_base64_encode "" HAVE_LIBMBEDCRYPTO) + if(HAVE_LIBMEDTLSCRYPTO) + check_library_exists(mbedtls mbedtls_cipher_info_from_type "" HAVE_LIBMBEDTLS) + if(HAVE_LIBMBEDTLS) + check_library_exists(mbedx509 mbedtls_x509_get_name "" HAVE_LIBMBEDX509) + endif() + endif() + endif() +else() + unset(HAVE_MBEDTLS_SSL_H) + unset(HAVE_LIBMBEDCRYPTO) + unset(HAVE_LIBMBEDTLS) + unset(HAVE_LIBMEDX509) +endif() + if(WITH_NETTLE) if(APPLE) set(CMAKE_REQUIRED_INCLUDES /opt/local/include) @@ -1070,6 +1089,16 @@ if(NOT ${CRYPTO_LIBRARY} EQUAL "") target_link_libraries(mod_wstunnel ${CRYPTO_LIBRARY}) 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}) + # not doing "cross module" linkage yet (e.g. mod_authn, secdownload) +endif() + if(WITH_LIBEV) target_link_libraries(lighttpd ${LIBEV_LDFLAGS}) add_target_properties(lighttpd COMPILE_FLAGS ${LIBEV_CFLAGS}) diff --git a/src/Makefile.am b/src/Makefile.am index f9afabad..e9267c08 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -391,6 +391,14 @@ mod_openssl_la_LDFLAGS = $(common_module_ldflags) mod_openssl_la_LIBADD = $(SSL_LIB) $(common_libadd) 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) +endif + + lib_LTLIBRARIES += mod_rewrite.la mod_rewrite_la_SOURCES = mod_rewrite.c mod_rewrite_la_LDFLAGS = $(common_module_ldflags) diff --git a/src/SConscript b/src/SConscript index 989eb6c7..c540fc48 100644 --- a/src/SConscript +++ b/src/SConscript @@ -179,6 +179,9 @@ if env['with_openssl']: if env['with_wolfssl']: modules['mod_openssl'] = { 'src' : [ 'mod_openssl.c' ], 'lib' : [ env['LIBCRYPTO'], 'm' ] } +if env['with_mbedtls']: + modules['mod_mbedtls'] = { 'src' : [ 'mod_mbedtls.c' ], 'lib' : [ env['LIBSSL'], env['LIBX509'], env['LIBCRYPTO'] ] } + staticenv = env.Clone(CPPFLAGS=[ env['CPPFLAGS'], '-DLIGHTTPD_STATIC' ]) ## all the core-sources + the modules diff --git a/src/algo_sha1.h b/src/algo_sha1.h index a89efb90..34b9780d 100644 --- a/src/algo_sha1.h +++ b/src/algo_sha1.h @@ -4,6 +4,12 @@ #include "sys-crypto.h" /* USE_LIB_CRYPTO */ #ifdef USE_LIB_CRYPTO +#if (!defined(USE_MBEDTLS_CRYPTO) || defined(MBEDTLS_SHA1_C)) +#define USE_LIB_CRYPTO_SHA1 +#endif +#endif + +#ifdef USE_LIB_CRYPTO_SHA1 #ifdef USE_NETTLE_CRYPTO #include <nettle/sha.h> @@ -21,6 +27,23 @@ SHA1_Update(SHA_CTX *ctx, const void *data, size_t length) sha1_update(ctx, length, data); } +#elif defined(USE_MBEDTLS_CRYPTO) && defined(MBEDTLS_SHA1_C) + +#include <mbedtls/sha1.h> +#ifndef SHA_DIGEST_LENGTH +#define SHA_DIGEST_LENGTH 20 +#endif +typedef struct mbedtls_sha1_context SHA_CTX; +#define SHA1_Init(ctx) \ + (mbedtls_sha1_init(ctx), mbedtls_sha1_starts_ret(ctx)) +#define SHA1_Final(digest, ctx) \ + (mbedtls_sha1_finish_ret((ctx),(digest)), mbedtls_sha1_free(ctx)) +static void +SHA1_Update(SHA_CTX *ctx, const void *data, size_t length) +{ + mbedtls_sha1_update_ret(ctx, data, length); +} + #elif defined(USE_OPENSSL_CRYPTO) #include <openssl/sha.h> diff --git a/src/configfile.c b/src/configfile.c index fbe99745..8e415045 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -307,6 +307,8 @@ static void config_compat_module_load (server *srv) { append_mod_staticfile = 0; else if (buffer_eq_slen(m, CONST_STR_LEN("mod_dirlisting"))) append_mod_dirlisting = 0; + else if (buffer_eq_slen(m, CONST_STR_LEN("mod_mbedtls"))) + append_mod_openssl = 0; else if (buffer_eq_slen(m, CONST_STR_LEN("mod_openssl"))) append_mod_openssl = 0; else if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_file"))) @@ -702,7 +704,7 @@ static int config_insert_srvconf(server *srv) { break; case 30:/* ssl.engine */ ssl_enabled = (0 != cpv->v.u); - #ifndef USE_OPENSSL_CRYPTO + #if !defined(USE_OPENSSL_CRYPTO) && !defined(USE_MBEDTLS_CRYPTO) if (ssl_enabled) { log_error(srv->errh, __FILE__, __LINE__, "ssl support is missing; " diff --git a/src/meson.build b/src/meson.build index 71e0cabe..ddbb67ad 100644 --- a/src/meson.build +++ b/src/meson.build @@ -395,6 +395,7 @@ if get_option('with_mysql') endif libssl = [] +libx509 = [] libcrypto = [] if get_option('with_openssl') # manual search: @@ -420,6 +421,17 @@ if get_option('with_wolfssl') != 'false' libcrypto += libwolfssl_includes_dep conf_data.set('HAVE_WOLFSSL_SSL_H', true) endif +if get_option('with_mbedtls') + # manual search: + # header: mbedtls/ssl.h + # function: mbedtls_cipher_info_from_type (-lmbedtls) + # function: mbedtls_x509_get_name (-lmbedx509) + # function: mbedtls_base64_encode (-lmbedcrypto) + libssl = [ dependency('libmbedtls') ] + libx509 = [ dependency('libmbedx509') ] + libcrypto = [ dependency('libmbedcrypto') ] + conf_data.set('HAVE_LIBMBEDCRYPTO', true) +endif if get_option('with_nettle') != 'false' # manual search: # header: nettle/nettle-types.h @@ -973,6 +985,12 @@ if get_option('with_wolfssl') != 'false' ] endif +if get_option('with_mbedtls') != 'false' + modules += [ + [ 'mod_mbedtls', [ 'mod_mbedtls.c' ], libssl + libx509 + libcrypto ], + ] +endif + if get_option('with_pam') modules += [ [ 'mod_authn_pam', [ 'mod_authn_pam.c' ], libpam ], diff --git a/src/mod_auth.c b/src/mod_auth.c index 172a6a49..7a3fceef 100644 --- a/src/mod_auth.c +++ b/src/mod_auth.c @@ -37,6 +37,22 @@ SHA512_256_Update(SHA512_CTX *ctx, const void *data, size_t length) sha512_256_update(ctx, length, data); } +#elif defined(USE_MBEDTLS_CRYPTO) + +#include <mbedtls/sha256.h> +#ifdef MBEDTLS_SHA256_C +typedef struct mbedtls_sha256_context SHA256_CTX; +#define SHA256_Init(ctx) \ + (mbedtls_sha256_init(ctx), mbedtls_sha256_starts_ret((ctx),0)) +#define SHA256_Final(digest, ctx) \ + (mbedtls_sha256_finish_ret((ctx),(digest)), mbedtls_sha256_free(ctx)) +static void +SHA256_Update(SHA256_CTX *ctx, const void *data, size_t length) +{ + mbedtls_sha256_update_ret(ctx, data, length); +} +#endif + #elif defined(USE_OPENSSL_CRYPTO) #include <openssl/sha.h> diff --git a/src/mod_authn_file.c b/src/mod_authn_file.c index 571178b2..7d8de7b2 100644 --- a/src/mod_authn_file.c +++ b/src/mod_authn_file.c @@ -58,6 +58,38 @@ SHA512_256_Update(SHA512_CTX *ctx, const void *data, size_t length) sha512_256_update(ctx, length, data); } +#elif defined(USE_MBEDTLS_CRYPTO) + +#include <mbedtls/md4.h> +#ifdef MBEDTLS_MD4_C +typedef struct mbedtls_md4_context MD4_CTX; +#define MD4_Init(ctx) \ + (mbedtls_md4_init(ctx), mbedtls_md4_starts_ret(ctx)) +#define MD4_Final(digest, ctx) \ + (mbedtls_md4_finish_ret((ctx),(digest)), mbedtls_md4_free(ctx)) +static void +MD4_Update(MD4_CTX *ctx, const void *data, size_t length) +{ + mbedtls_md4_update_ret(ctx, data, length); +} +#else /*(mbedTLS built without MD4)*/ +#define NO_MD4 +#endif + +#include <mbedtls/sha256.h> +#ifdef MBEDTLS_SHA256_C +typedef struct mbedtls_sha256_context SHA256_CTX; +#define SHA256_Init(ctx) \ + (mbedtls_sha256_init(ctx), mbedtls_sha256_starts_ret((ctx),0)) +#define SHA256_Final(digest, ctx) \ + (mbedtls_sha256_finish_ret((ctx),(digest)), mbedtls_sha256_free(ctx)) +static void +SHA256_Update(SHA256_CTX *ctx, const void *data, size_t length) +{ + mbedtls_sha256_update_ret(ctx, data, length); +} +#endif + #elif defined(USE_OPENSSL_CRYPTO) #include <openssl/md4.h> diff --git a/src/mod_mbedtls.c b/src/mod_mbedtls.c new file mode 100644 index 00000000..48bfa870 --- /dev/null +++ b/src/mod_mbedtls.c @@ -0,0 +1,3478 @@ +/* + * mod_mbedtls - mbedTLS support for lighttpd + * + * Copyright(c) 2020 Glenn Strauss gstrauss()gluelogic.com All rights reserved + * License: BSD 3-clause (same as lighttpd) + */ +/* + * reference: + * https://tls.mbed.org/high-level-design + * https://tls.mbed.org/tech-updates/blog/mbedtls-2.0-defaults-best-practices + * mbedTLS header files (mbedtls/ssl.h and others) are extremely well-documented + * https://tls.mbed.org/api/ (generated from mbedTLS headers and code) + * + * mbedTLS limitations: + * - mbedTLS does not currently support TLSv1.3 + * - mbedTLS does not currently support OCSP + * https://tls.mbed.org/discussions/feature-request/ocsp-stapling + * TLS/DTLS: OCSP Stapling support #880 + * https://github.com/ARMmbed/mbedtls/issues/880 + * Add support for writing OCSP requests and parsing OCSP responses #1197 + * https://github.com/ARMmbed/mbedtls/issues/1197 + * + * future possible enhancements to lighttpd mod_mbedtls: + * - session cache (though session tickets are implemented) + * sample code in mbedtls:programs/ssl/ssl_server2.c + * + * Note: session tickets are disabled by default + * If enabled, mbedtls rotates the session ticket key according to 2x timeout + * set with mbedtls_ssl_ticket_setup() (currently 43200 sec in mod_mbedtls). + * This is fine for use with a single lighttpd instance, but with multiple + * lighttpd workers, no coordinated STEK (server ticket encryption key) + * rotation occurs other than by (some external job) restarting 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, 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 24 hours if session tickets are enabled and + * multiple lighttpd workers are configured. + */ +#include "first.h" + +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> /* vsnprintf() */ +#include <string.h> +#include <unistd.h> + +#include <mbedtls/ctr_drbg.h> +#include <mbedtls/dhm.h> +#include <mbedtls/error.h> +#include <mbedtls/entropy.h> +#include <mbedtls/oid.h> +#include <mbedtls/pem.h> +#include <mbedtls/ssl.h> +#include <mbedtls/ssl_internal.h> /* struct mbedtls_ssl_transform */ +#include <mbedtls/x509.h> +#include <mbedtls/x509_crt.h> +#include <mbedtls/version.h> + +#if MBEDTLS_VERSION_NUMBER >= 0x02040000 /* mbedtls 2.04.0 */ +#include <mbedtls/net_sockets.h> +#else +#include <mbedtls/net.h> +#endif + +#if defined(MBEDTLS_SSL_TICKET_C) +#include <mbedtls/ssl_ticket.h> +#endif + +#ifndef MBEDTLS_X509_CRT_PARSE_C +#error "lighttpd requires that mbedtls be built with MBEDTLS_X509_CRT_PARSE_C" +#endif + +#include "base.h" +#include "http_header.h" +#include "log.h" +#include "plugin.h" + +typedef struct { + /* SNI per host: with COMP_SERVER_SOCKET, COMP_HTTP_SCHEME, COMP_HTTP_HOST */ + mbedtls_pk_context ssl_pemfile_pkey;/* parsed private key structure */ + mbedtls_x509_crt ssl_pemfile_x509; /* parsed public key structure */ + const buffer *ssl_pemfile; + const buffer *ssl_privkey; + int8_t need_chain; +} plugin_cert; + +typedef struct { + mbedtls_ssl_config *ssl_ctx; /* context shared between mbedtls_ssl_CONTEXT structures */ + int *ciphersuites; + mbedtls_ecp_group_id *curves; +} plugin_ssl_ctx; + +typedef struct { + mbedtls_ssl_config *ssl_ctx; /* output from network_init_ssl() */ + int *ciphersuites; /* output from network_init_ssl() */ + mbedtls_ecp_group_id *curves; /* 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; + 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; + const buffer *ssl_acme_tls_1; + array *ssl_conf_cmd; + + /*(copied from plugin_data for socket ssl_ctx config)*/ + plugin_cert *pc; + mbedtls_pk_context *ssl_pemfile_pkey; /* parsed private key structure */ + mbedtls_x509_crt *ssl_pemfile_x509; /* parsed public key structure */ + mbedtls_x509_crt *ssl_ca_file; + const buffer *ssl_pemfile; + const buffer *ssl_privkey; + unsigned char ssl_session_ticket; + unsigned char ssl_verifyclient; + unsigned char ssl_verifyclient_enforce; + unsigned char ssl_verifyclient_depth; +} 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; + mbedtls_pk_context *ssl_pemfile_pkey; /* parsed private key structure */ + mbedtls_x509_crt *ssl_pemfile_x509; /* parsed public key structure */ + const buffer *ssl_pemfile; + mbedtls_x509_crt *ssl_ca_file; + mbedtls_x509_crt *ssl_ca_dn_file; + mbedtls_x509_crl *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; + /* NIST counter-mode deterministic random byte generator */ + mbedtls_ctr_drbg_context ctr_drbg; + /* entropy collection and state management */ + mbedtls_entropy_context entropy; + #if defined(MBEDTLS_SSL_SESSION_TICKETS) + mbedtls_ssl_ticket_context ticket_ctx; + #endif +} 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 = r->plugin_ctx[plugin_data_singleton->id]; */ +static plugin_data *plugin_data_singleton; +#define LOCAL_SEND_BUFSIZE MBEDTLS_SSL_MAX_CONTENT_LEN +static char *local_send_buffer; + +typedef struct { + mbedtls_ssl_context ssl; /* mbedtls request/connection context */ + request_st *r; + connection *con; + int8_t request_env_patched; + int8_t close_notify; + unsigned short alpn; + int handshake_done; + size_t pending_write; + plugin_config conf; + buffer *tmp_buf; + mbedtls_pk_context *acme_tls_1_pkey; + mbedtls_x509_crt *acme_tls_1_x509; +} 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) +{ + mbedtls_ssl_free(&hctx->ssl); + if (hctx->acme_tls_1_pkey) { + mbedtls_pk_free(hctx->acme_tls_1_pkey); + free(hctx->acme_tls_1_pkey); + } + if (hctx->acme_tls_1_x509) { + mbedtls_x509_crt_free(hctx->acme_tls_1_x509); + free(hctx->acme_tls_1_x509); + } + free(hctx); +} + + +#ifdef MBEDTLS_ERROR_C +__attribute_cold__ +static void elog(log_error_st * const errh, + const char * const file, const int line, + const int rc, const char * const msg) +{ + /* error logging convenience function that decodes mbedtls result codes */ + char buf[256]; + mbedtls_strerror(rc, buf, sizeof(buf)); + log_error(errh, file, line, "MTLS: %s: %s (-0x%04x)", msg, buf, -rc); +} +#else +#define elog(errh, file, line, rc, msg) \ + log_error((errh), (file), (line), "MTLS: %s: (-0x%04x)", (msg), -(rc)) +#endif + + +__attribute_cold__ +__attribute_format__((__printf__, 5, 6)) +static void elogf(log_error_st * const errh, + const char * const file, const int line, + const int rc, const char * const fmt, ...) +{ + char msg[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + elog(errh, file, line, rc, msg); +} + + +INIT_FUNC(mod_mbedtls_init) +{ + plugin_data_singleton = (plugin_data *)calloc(1, sizeof(plugin_data)); + #if defined(MBEDTLS_SSL_SESSION_TICKETS) + mbedtls_ssl_ticket_init(&plugin_data_singleton->ticket_ctx); + #endif + return plugin_data_singleton; +} + + +static int mod_mbedtls_init_once_mbedtls (server *srv) +{ + if (ssl_is_init) return 1; + ssl_is_init = 1; + + plugin_data * const p = plugin_data_singleton; + mbedtls_ctr_drbg_init(&p->ctr_drbg); /* init empty NSIT random num gen */ + mbedtls_entropy_init(&p->entropy); /* init empty entropy collection struct + .. could add sources here too */ + + int rc = /* init RNG */ + mbedtls_ctr_drbg_seed(&p->ctr_drbg, /* random number generator */ + mbedtls_entropy_func, /* default entropy func */ + &p->entropy, /* entropy context */ + NULL, 0); /* no personalization data */ + if (0 != rc) { + elog(srv->errh, __FILE__,__LINE__, rc, + "Init of random number generator failed"); + return 0; + } + + local_send_buffer = malloc(LOCAL_SEND_BUFSIZE); + force_assert(NULL != local_send_buffer); + + return 1; +} + + +static void mod_mbedtls_free_mbedtls (void) +{ + if (!ssl_is_init) return; + + plugin_data * const p = plugin_data_singleton; + mbedtls_ctr_drbg_free(&p->ctr_drbg); + mbedtls_entropy_free(&p->entropy); + #if defined(MBEDTLS_SSL_SESSION_TICKETS) + mbedtls_ssl_ticket_free(&p->ticket_ctx); + #endif + + free(local_send_buffer); + ssl_is_init = 0; +} + + +static void +mod_mbedtls_free_config (server *srv, plugin_data * const p) +{ + if (NULL != p->ssl_ctxs) { + mbedtls_ssl_config * 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) { + mbedtls_ssl_config_free(s->ssl_ctx); + free(s->ciphersuites); + free(s->curves); + } + } + /* free ssl_ctx from global scope */ + if (ssl_ctx_global_scope) { + mbedtls_ssl_config_free(ssl_ctx_global_scope); + free(p->ssl_ctxs[0].ciphersuites); + free(p->ssl_ctxs[0].curves); + } + 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; + mbedtls_pk_free(&pc->ssl_pemfile_pkey); + mbedtls_x509_crt_free(&pc->ssl_pemfile_x509); + } + break; + case 2: /* ssl.ca-file */ + case 3: /* ssl.ca-dn-file */ + if (cpv->vtype == T_CONFIG_LOCAL) { + mbedtls_x509_crt *cacert = cpv->v.v; + mbedtls_x509_crt_free(cacert); + free(cacert); + } + break; + case 4: /* ssl.ca-dn-file */ + if (cpv->vtype == T_CONFIG_LOCAL) { + mbedtls_x509_crl *crl = cpv->v.v; + mbedtls_x509_crl_free(crl); + free(crl); + } + break; + default: + break; + } + } + } +} + + +FREE_FUNC(mod_mbedtls_free) +{ + plugin_data *p = p_d; + if (NULL == p->srv) return; + mod_mbedtls_free_config(p->srv, p); + mod_mbedtls_free_mbedtls(); +} + + +static void +mod_mbedtls_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 */ + if (cpv->vtype == T_CONFIG_LOCAL) + pconf->ssl_ca_dn_file = cpv->v.v; + 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:/* debug.log-ssl-noise */ + pconf->ssl_log_noise = (unsigned char)cpv->v.shrt; + break; + default:/* should not happen */ + return; + } +} + + +static void +mod_mbedtls_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) +{ + do { + mod_mbedtls_merge_config_cpv(pconf, cpv); + } while ((++cpv)->k_id != -1); +} + + +static void +mod_mbedtls_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_mbedtls_merge_config(pconf, p->cvlist + p->cvlist[i].v.u2[0]); + } +} + + +__attribute_pure__ +static int +mod_mbedtls_crt_is_self_issued (const mbedtls_x509_crt * const crt) +{ + const mbedtls_x509_buf * const issuer = &crt->issuer_raw; + const mbedtls_x509_buf * const subject = &crt->subject_raw; + return subject->len == issuer->len + && 0 == memcmp(issuer->p, subject->p, subject->len); +} + + +static int +mod_mbedtls_construct_crt_chain (mbedtls_x509_crt *leaf, mbedtls_x509_crt *store, log_error_st *errh) +{ + /* Historically, openssl will use the cert chain in (SSL_CTX *) if a cert + * does not have a chain configured in (SSL *). While similar behavior + * could be achieved with mbedtls_x509_crt_parse_file(crt, ssl_ca_file->ptr) + * instead attempt to do better and build a proper, ordered cert chain. */ + + if (leaf->next) return 0; /*(presume chain has already been provided)*/ + if (store == NULL) return 0;/*(unable to proceed; chain may be incomplete)*/ + + /* attempt to construct certificate chain from certificate store */ + for (mbedtls_x509_crt *crt = leaf; crt; ) { + const mbedtls_x509_buf * const issuer = &crt->issuer_raw; + + /*(walk entire store in case certs are not properly sorted)*/ + for (crt = store; crt; crt = crt->next) { + /* The raw issuer/subject data (DER) is used for quick comparison */ + /* (see comments in mod_mbedtls_verify_cb())*/ + const mbedtls_x509_buf * const subject = &crt->subject_raw; + if (issuer->len != subject->len + || 0 != memcmp(subject->p, issuer->p, issuer->len)) continue; + + /* root cert is end condition; omit from chain of intermediates */ + if (mod_mbedtls_crt_is_self_issued(crt)) + return 0; + + int rc = + #if MBEDTLS_VERSION_NUMBER >= 0x02110000 /* mbedtls 2.17.0 */ + /* save memory by eliding copy of already-loaded raw DER */ + mbedtls_x509_crt_parse_der_nocopy(leaf, crt->raw.p, crt->raw.len); + #else + mbedtls_x509_crt_parse_der(leaf, crt->raw.p, crt->raw.len); + #endif + if (0 != rc) { /*(failure not unexpected since already parsed)*/ + elog(errh, __FILE__, __LINE__, rc, "cert copy failed"); + return rc; + } + break; + } + } + + return 0; /*(no error, though cert chain may or may not be complete)*/ +} + + +static int +mod_mbedtls_verify_cb (void *arg, mbedtls_x509_crt *crt, int depth, uint32_t *flags) +{ + handler_ctx * const hctx = (handler_ctx *)arg; + + if (depth > hctx->conf.ssl_verifyclient_depth) { + log_error(hctx->r->conf.errh, __FILE__, __LINE__, + "MTLS: client cert chain too long"); + *flags |= MBEDTLS_X509_BADCERT_OTHER; /* cert chain too long */ + } + else if (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 */ + /* The raw issuer/subject data (DER) is used for quick comparison. */ + const size_t len = crt->issuer_raw.len; + mbedtls_x509_crt *chain = hctx->conf.ssl_ca_dn_file; + do { + #if 0 /* x509_name_cmp() is not a public func in mbedtls */ + if (0 == x509_name_cmp(&crt->issuer, &chain->subject)) + break; + #else + if (len == chain->subject_raw.len + && 0 == memcmp(chain->subject_raw.p, crt->issuer_raw.p, len)) + break; + #endif + } while ((chain = chain->next)); + + if (NULL == chain) + *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED; + } + if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) { + log_error(hctx->r->conf.errh, __FILE__, __LINE__, + "MTLS: client cert not trusted"); + } + + return 0; +} + + +#ifdef MBEDTLS_SSL_SERVER_NAME_INDICATION +static int +mod_mbedtls_SNI (void *arg, mbedtls_ssl_context *ssl, const unsigned char *servername, size_t len) +{ + handler_ctx * const hctx = (handler_ctx *) arg; + buffer_copy_string(&hctx->r->uri.scheme, "https"); + + request_st * const r = hctx->r; + if (len >= 1024) { /*(expecting < 256; TLSEXT_MAXLEN_host_name is 255)*/ + log_error(r->conf.errh, __FILE__, __LINE__, + "MTLS: SNI name too long %.*s", (int)len, servername); + return MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO; + } + + /* use SNI to patch mod_mbedtls config and then reset COMP_HTTP_HOST */ + buffer_copy_string_len(&r->uri.authority, (const char *)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 MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO; + #endif + + const buffer * const ssl_pemfile = hctx->conf.pc->ssl_pemfile; + + r->conditional_is_valid |= (1 << COMP_HTTP_SCHEME) + | (1 << COMP_HTTP_HOST); + + mod_mbedtls_patch_config(r, &hctx->conf); + /* reset COMP_HTTP_HOST so that conditions re-run after request hdrs read */ + /*(done in configfile-glue.c:config_cond_cache_reset() after request hdrs read)*/ + /*config_cond_cache_reset_item(r, COMP_HTTP_HOST);*/ + /*buffer_clear(&r->uri.authority);*/ + + /*(compare strings as ssl.pemfile might repeat same file in lighttpd.conf + * and mod_mbedtls does not attempt to de-dup)*/ + if (!buffer_is_equal(hctx->conf.pc->ssl_pemfile, ssl_pemfile)) { + /* if needed, attempt to construct certificate chain for server cert */ + if (hctx->conf.pc->need_chain) { + hctx->conf.pc->need_chain = 0; /*(attempt once to complete chain)*/ + mbedtls_x509_crt *ssl_cred = &hctx->conf.pc->ssl_pemfile_x509; + mbedtls_x509_crt *store = hctx->conf.ssl_ca_file; + if (!mod_mbedtls_construct_crt_chain(ssl_cred, store, r->conf.errh)) + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + /* reconfigure to use SNI-specific cert */ + int rc = + mbedtls_ssl_set_hs_own_cert(ssl, + &hctx->conf.pc->ssl_pemfile_x509, + &hctx->conf.pc->ssl_pemfile_pkey); + if (0 != rc) { + elogf(r->conf.errh, __FILE__, __LINE__, rc, + "failed to set SNI certificate for TLS server name %s", + r->uri.authority.ptr); + return MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO; + } + } + + return 0; +} +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + + +static int +mod_mbedtls_conf_verify (handler_ctx *hctx, mbedtls_ssl_config *ssl_ctx) +{ + if (NULL == hctx->conf.ssl_ca_file) { + log_error(hctx->r->conf.errh, __FILE__, __LINE__, + "MTLS: can't verify client without ssl.ca-file " + "for TLS server name %s", + hctx->r->uri.authority.ptr); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* send ssl_ca_dn_file (if set) in client certificate request + * (later changed to ssl_ca_file before client certificate verification) */ + mbedtls_x509_crt *ca_certs = hctx->conf.ssl_ca_dn_file + ? hctx->conf.ssl_ca_dn_file + : hctx->conf.ssl_ca_file; + + mbedtls_ssl_context * const ssl = &hctx->ssl; + mbedtls_ssl_set_hs_ca_chain(ssl, ca_certs, hctx->conf.ssl_ca_crl_file); + #if MBEDTLS_VERSION_NUMBER >= 0x02120000 /* mbedtls 2.18.0 */ + mbedtls_ssl_set_verify(ssl, mod_mbedtls_verify_cb, hctx); + #else + mbedtls_ssl_conf_verify(ssl_ctx, mod_mbedtls_verify_cb, hctx); + #endif + return 0; +} + + +static void * +network_mbedtls_load_pemfile (server *srv, const buffer *pemfile, const buffer *privkey) +{ + mbedtls_x509_crt ssl_pemfile_x509; /* parsed public key structure */ + mbedtls_pk_context ssl_pemfile_pkey; /* parsed private key structure */ + int rc; + + mbedtls_x509_crt_init(&ssl_pemfile_x509); /* init cert structure */ + rc = mbedtls_x509_crt_parse_file(&ssl_pemfile_x509, pemfile->ptr); + if (0 != rc) { + elogf(srv->errh, __FILE__, __LINE__, rc, + "PEM file cert read failed (%s)", pemfile->ptr); + return NULL; + } + + mbedtls_pk_init(&ssl_pemfile_pkey); /* init private key context */ + rc = mbedtls_pk_parse_keyfile(&ssl_pemfile_pkey, privkey->ptr, NULL); + if (0 != rc) { + elogf(srv->errh, __FILE__, __LINE__, rc, + "PEM file private key read failed %s", privkey->ptr); + mbedtls_x509_crt_free(&ssl_pemfile_x509); + return NULL; + } + + rc = mbedtls_pk_check_pair(&ssl_pemfile_x509.pk, &ssl_pemfile_pkey); + if (0 != rc) { + elogf(srv->errh, __FILE__, __LINE__, rc, + "PEM cert and private key did not verify (%s) (%s)", + pemfile->ptr, privkey->ptr); + mbedtls_pk_free(&ssl_pemfile_pkey); + mbedtls_x509_crt_free(&ssl_pemfile_x509); + return NULL; + } + + 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 = pemfile; + pc->ssl_privkey = privkey; + pc->need_chain = (ssl_pemfile_x509.next == NULL + && !mod_mbedtls_crt_is_self_issued(&ssl_pemfile_x509)); + mbedtls_platform_zeroize(&ssl_pemfile_pkey, sizeof(ssl_pemfile_pkey)); + return pc; +} + + +#ifdef MBEDTLS_SSL_ALPN + +static int +mod_mbedtls_acme_tls_1 (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; + mbedtls_x509_crt *ssl_pemfile_x509 = NULL; + mbedtls_pk_context *ssl_pemfile_pkey = NULL; + size_t len; + int rc = MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO; + + /* 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 0; /*(should not happen)*/ + 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 = malloc(sizeof(*ssl_pemfile_x509)); + force_assert(ssl_pemfile_x509); + mbedtls_x509_crt_init(ssl_pemfile_x509); /* init cert structure */ + rc = mbedtls_x509_crt_parse_file(ssl_pemfile_x509, b->ptr); + if (0 != rc) { + elogf(errh, __FILE__, __LINE__, rc, + "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 = malloc(sizeof(*ssl_pemfile_pkey)); + force_assert(ssl_pemfile_pkey); + mbedtls_pk_init(ssl_pemfile_pkey); /* init private key context */ + rc = mbedtls_pk_parse_keyfile(ssl_pemfile_pkey, b->ptr, NULL); + if (0 != rc) { + elogf(errh, __FILE__, __LINE__, rc, + "Failed to load acme-tls/1 pemfile: %s", b->ptr); + break; + } + + rc = mbedtls_ssl_set_hs_own_cert(&hctx->ssl, + ssl_pemfile_x509, ssl_pemfile_pkey); + if (0 != rc) { + elogf(errh, __FILE__, __LINE__, rc, + "failed to set acme-tls/1 certificate for TLS server " + "name %s", name->ptr); + break; + } + + hctx->acme_tls_1_pkey = ssl_pemfile_pkey; /* save ptr and free later */ + hctx->acme_tls_1_x509 = ssl_pemfile_x509; /* save ptr and free later */ + return 0; + + } while (0); + + if (ssl_pemfile_pkey) { + mbedtls_pk_free(ssl_pemfile_pkey); + free(ssl_pemfile_pkey); + } + if (ssl_pemfile_x509) { + mbedtls_x509_crt_free(ssl_pemfile_x509); + free(ssl_pemfile_x509); + } + + return rc; +} + + +enum { + MOD_MBEDTLS_ALPN_HTTP11 = 1 + ,MOD_MBEDTLS_ALPN_HTTP10 = 2 + ,MOD_MBEDTLS_ALPN_H2 = 3 + ,MOD_MBEDTLS_ALPN_ACME_TLS_1 = 4 +}; + + +static int +mod_mbedtls_alpn_select_cb (handler_ctx *hctx, const char *in) +{ + const int n = (int)strlen(in); + const int i = 0; + unsigned short proto; + + switch (n) { + #if 0 + case 2: /* "h2" */ + if (in[i] == 'h' && in[i+1] == '2') { + proto = MOD_MBEDTLS_ALPN_H2; + break; + } + return 0; + #endif + case 8: /* "http/1.1" "http/1.0" */ + if (0 == memcmp(in+i, "http/1.", 7)) { + if (in[i+7] == '1') { + proto = MOD_MBEDTLS_ALPN_HTTP11; + break; + } + if (in[i+7] == '0') { + proto = MOD_MBEDTLS_ALPN_HTTP10; + break; + } + } + return 0; + case 10: /* "acme-tls/1" */ + if (0 == memcmp(in+i, "acme-tls/1", 10)) { + int rc = mod_mbedtls_acme_tls_1(hctx); + if (0 == rc) { + proto = MOD_MBEDTLS_ALPN_ACME_TLS_1; + break; + } + return rc; + } + return 0; + default: + return 0; + } + + hctx->alpn = proto; + return 0; +} + +#endif /* MBEDTLS_SSL_ALPN */ + + +static int +mod_mbedtls_ssl_conf_ciphersuites (server *srv, plugin_config_socket *s, buffer *ciphersuites, const buffer *cipherstring); + + +static int +mod_mbedtls_ssl_conf_curves(server *srv, plugin_config_socket *s, const buffer *curvelist); + + +static void +mod_mbedtls_ssl_conf_proto (server *srv, plugin_config_socket *s, const buffer *b, int max); + + +static int +mod_mbedtls_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; + + 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; + else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Ciphersuites"))) + ciphersuites = &ds->value; + else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Curves")) + || buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("Groups"))) { + if (!mod_mbedtls_ssl_conf_curves(srv, s, &ds->value)) + rc = -1; + } + else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("MaxProtocol"))) + mod_mbedtls_ssl_conf_proto(srv, s, &ds->value, 1); /* max */ + else if (buffer_eq_icase_slen(&ds->key, CONST_STR_LEN("MinProtocol"))) + mod_mbedtls_ssl_conf_proto(srv, s, &ds->value, 0); /* min */ + 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__, + "MTLS: 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)) { + /* mbedtls defaults to no record compression unless + * mbedtls is built with MBEDTLS_ZLIB_SUPPORT, which + * is deprecated and slated for removal*/ + if (!flag) continue; + } + break; + case 13: + if (buffer_eq_icase_ssn(v, "SessionTicket", 13)) { + s->ssl_session_ticket = flag; + continue; + } + break; + case 16: + if (buffer_eq_icase_ssn(v, "ServerPreference", 16)) { + /* Note: The server uses its own preferences + * over the preference of the client unless + * MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE defined! */ + 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__, + "MTLS: 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__, + "MTLS: ssl.openssl.ssl-conf-cmd %s ignored", + ds->key.ptr); + } + } + + if (!mod_mbedtls_ssl_conf_ciphersuites(srv, s, ciphersuites, cipherstring)) + rc = -1; + + return rc; +} + + +static int +network_init_ssl (server *srv, plugin_config_socket *s, plugin_data *p) +{ + int rc; + + s->ssl_ctx = malloc(sizeof(mbedtls_ssl_config)); + force_assert(s->ssl_ctx); + mbedtls_ssl_config_init(s->ssl_ctx); + + /* set the RNG in the ssl config context, using the default random func */ + mbedtls_ssl_conf_rng(s->ssl_ctx, mbedtls_ctr_drbg_random, &p->ctr_drbg); + + /* mbedtls defaults to disable client renegotiation + * mbedtls defaults to no record compression unless mbedtls is built + * with MBEDTLS_ZLIB_SUPPORT, which is deprecated and slated for removal + * MBEDTLS_SSL_PRESET_SUITEB is stricter than MBEDTLS_SSL_PRESET_DEFAULT + * (and is attempted to be supported in mod_mbedtls_ssl_conf_ciphersuites()) + * explanation: https://github.com/ARMmbed/mbedtls/issues/1591 + * reference: RFC 6460 */ + rc = mbedtls_ssl_config_defaults(s->ssl_ctx, + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (0 != rc) { + elog(srv->errh, __FILE__,__LINE__, rc, + "Init of ssl config context defaults failed"); + return -1; + } + + /* mbedtls defaults minimum accepted SSL/TLS protocol version TLS v1.0 + * use of SSL v3 should be avoided, and SSL v2 is not supported */ + if (s->ssl_use_sslv3) + mbedtls_ssl_conf_min_version(s->ssl_ctx, MBEDTLS_SSL_MAJOR_VERSION_3, + MBEDTLS_SSL_MINOR_VERSION_0); + + if (!buffer_string_is_empty(s->ssl_cipher_list)) { + if (!mod_mbedtls_ssl_conf_ciphersuites(srv,s,NULL,s->ssl_cipher_list)) + return -1; + } + + if (!buffer_string_is_empty(s->ssl_dh_file)) { + mbedtls_dhm_context dhm; + mbedtls_dhm_init(&dhm); + rc = mbedtls_dhm_parse_dhmfile(&dhm, s->ssl_dh_file->ptr); + if (0 != rc) + elogf(srv->errh, __FILE__,__LINE__, rc, + "mbedtls_dhm_parse_dhmfile() %s", s->ssl_dh_file->ptr); + else { + rc = mbedtls_ssl_conf_dh_param_ctx(s->ssl_ctx, &dhm); + if (0 != rc) + elogf(srv->errh, __FILE__,__LINE__, rc, + "mbedtls_ssl_conf_dh_param_ctx() %s", s->ssl_dh_file->ptr); + } + mbedtls_dhm_free(&dhm); + if (0 != rc) + return -1; + } + + if (!buffer_string_is_empty(s->ssl_ec_curve)) { + if (!mod_mbedtls_ssl_conf_curves(srv, s, s->ssl_ec_curve)) + return -1; + } + + /* if needed, attempt to construct certificate chain for server cert */ + if (s->pc->need_chain) { + s->pc->need_chain = 0; /*(attempt once to complete chain)*/ + if (!mod_mbedtls_construct_crt_chain(s->ssl_pemfile_x509, + s->ssl_ca_file, srv->errh)) + return -1; + } + + rc = mbedtls_ssl_conf_own_cert(s->ssl_ctx, + s->ssl_pemfile_x509, s->ssl_pemfile_pkey); + if (0 != rc) { + elogf(srv->errh, __FILE__, __LINE__, rc, + "PEM cert and private key did not verify (%s) (%s)", + s->ssl_pemfile->ptr, s->ssl_privkey->ptr); + return -1; + } + + #ifdef MBEDTLS_SSL_ALPN + /* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids */ + static const char *alpn_protos_http_acme[] = { + "http/1.1" + ,"http/1.0" + ,"acme-tls/1" + ,NULL + }; + static const char *alpn_protos_http[] = { + "http/1.1" + ,"http/1.0" + ,NULL + }; + const char **alpn_protos = (!buffer_string_is_empty(s->ssl_acme_tls_1)) + ? alpn_protos_http_acme + : alpn_protos_http; + rc = mbedtls_ssl_conf_alpn_protocols(s->ssl_ctx, alpn_protos); + if (0 != rc) { + elog(srv->errh, __FILE__, __LINE__, rc, "error setting ALPN protocols"); + return -1; + } + #endif + + if (!s->ssl_use_sslv3 && !s->ssl_use_sslv2) + mod_mbedtls_ssl_conf_proto(srv, s, NULL, 0); /* min */ + + if (s->ssl_conf_cmd && s->ssl_conf_cmd->used) { + if (0 != mod_mbedtls_ssl_conf_cmd(srv, s)) return -1; + } + + /* server preference is used (default) unless mbedtls is built with + * MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE defined (not default) */ + #ifndef MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE + if (!s->ssl_honor_cipher_order) + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: " + "ignoring ssl.honor-cipher-order; mbedtls uses server preference " + "unless mbedtls built MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE"); + #else + if (s->ssl_honor_cipher_order) + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: " + "ignoring ssl.honor-cipher-order; mbedtls uses client preference " + "since mbedtls built MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE"); + #endif + + #if defined(MBEDTLS_SSL_SESSION_TICKETS) + if (s->ssl_session_ticket && !p->ticket_ctx.ticket_lifetime) { /*init once*/ + rc = mbedtls_ssl_ticket_setup(&p->ticket_ctx, mbedtls_ctr_drbg_random, + &p->ctr_drbg, MBEDTLS_CIPHER_AES_256_GCM, + 43200); /* ticket timeout: 12 hours */ + if (0 != rc) { + elog(srv->errh, __FILE__,__LINE__, rc, + "mbedtls_ssl_ticket_setup()"); + return -1; + } + } + + #if defined(MBEDTLS_SSL_TICKET_C) + if (s->ssl_session_ticket) + mbedtls_ssl_conf_session_tickets_cb(s->ssl_ctx, + mbedtls_ssl_ticket_write, + mbedtls_ssl_ticket_parse, + &p->ticket_ctx); + #endif + #endif /* MBEDTLS_SSL_SESSION_TICKETS */ + + return 0; +} + + +static int +mod_mbedtls_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 } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } + }; + static const buffer default_ssl_cipher_list = { CONST_STR_LEN("HIGH"), 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_mbedtls")) + return HANDLER_ERROR; + + plugin_config_socket defaults; + memset(&defaults, 0, sizeof(defaults)); + #ifndef MBEDTLS_SSL_SRV_RESPECT_CLIENT_PREFERENCE + defaults.ssl_honor_cipher_order = 1; + #endif + defaults.ssl_session_ticket = 0; /* disabled by default */ + 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=10); included to process global scope */ + if (!is_socket_scope && cpv->k_id != 10) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: %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__, + "MTLS: ignoring ssl.empty-fragments; openssl-specific " + "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__, "MTLS: " + "ssl.use-sslv2 is deprecated and will soon be removed. " + "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__, "MTLS: " + "ssl.use-sslv3 is deprecated and will soon be removed. " + "Many modern TLS libraries no longer support SSLv3. " + "If needed, use: " + "ssl.openssl.ssl-conf-cmd = (\"MinProtocol\" => \"SSLv3\")"); + 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.ssl_pemfile_pkey = p->defaults.ssl_pemfile_pkey;*/ + /*conf.ssl_pemfile_x509 = p->defaults.ssl_pemfile_x509;*/ + 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_acme_tls_1 = p->defaults.ssl_acme_tls_1; + + 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) { + plugin_cert *pc = cpv->v.v; + conf.pc = pc; + conf.ssl_pemfile_pkey = &pc->ssl_pemfile_pkey; + conf.ssl_pemfile_x509 = &pc->ssl_pemfile_x509; + conf.ssl_pemfile = pc->ssl_pemfile; + conf.ssl_privkey = pc->ssl_privkey; + } + break; + case 2: /* ssl.ca-file */ + if (cpv->vtype == T_CONFIG_LOCAL) + conf.ssl_ca_file = cpv->v.v; + 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; + case 13:/* ssl.acme-tls-1 */ + conf.ssl_acme_tls_1 = cpv->v.b; + break; + default: + break; + } + } + break; + } + + if (NULL == conf.ssl_pemfile_x509) { + 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__, + "MTLS: 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__, + "MTLS: ssl.pemfile has to be set when ssl.engine = \"enable\""); + rc = HANDLER_ERROR; + continue; + } + + /* (initialize once if module enabled) */ + if (!mod_mbedtls_init_once_mbedtls(srv)) { + rc = HANDLER_ERROR; + break; + } + + /* 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; + s->ciphersuites = conf.ciphersuites; + s->curves = conf.curves; + } + else { + mbedtls_ssl_config_free(conf.ssl_ctx); + free(conf.ciphersuites); + free(conf.curves); + rc = HANDLER_ERROR; + } + } + + free(srvplug.cvlist); + return rc; +} + + +SETDEFAULTS_FUNC(mod_mbedtls_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("debug.log-ssl-noise"), + T_CONFIG_SHORT, + T_CONFIG_SCOPE_CONNECTION } + ,{ NULL, 0, + T_CONFIG_UNSET, + T_CONFIG_SCOPE_UNSET } + }; + + plugin_data * const p = p_d; + p->srv = srv; + if (!config_plugin_values_init(srv, p, cpk, "mod_mbedtls")) + return HANDLER_ERROR; + + /* 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; + 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 */ + case 3: /* ssl.ca-dn-file */ + #if 0 /* defer; not necessary for pemfile parsing */ + if (!mod_mbedtls_init_once_mbedtls(srv)) return HANDLER_ERROR; + #endif + if (!buffer_string_is_empty(cpv->v.b)) { + mbedtls_x509_crt *cacert = calloc(1, sizeof(*cacert)); + force_assert(cacert); + mbedtls_x509_crt_init(cacert); + int rc = mbedtls_x509_crt_parse_file(cacert, cpv->v.b->ptr); + if (0 == rc) { + cpv->vtype = T_CONFIG_LOCAL; + cpv->v.v = cacert; + } + else { + elogf(srv->errh, __FILE__, __LINE__, rc, + "%s = %s", cpk[cpv->k_id].k, cpv->v.b->ptr); + mbedtls_x509_crt_free(cacert); + free(cacert); + return HANDLER_ERROR; + } + } + break; + case 4: /* ssl.ca-crl-file */ + if (!buffer_string_is_empty(cpv->v.b)) { + mbedtls_x509_crl *crl = malloc(sizeof(*crl)); + force_assert(crl); + mbedtls_x509_crl_init(crl); + int rc = mbedtls_x509_crl_parse_file(crl, cpv->v.b->ptr); + if (0 == rc) { + cpv->vtype = T_CONFIG_LOCAL; + cpv->v.v = crl; + } + else { + elogf(srv->errh, __FILE__, __LINE__, rc, + "CRL file read failed (%s)", cpv->v.b->ptr); + free(crl); + return HANDLER_ERROR; + } + } + 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__, + "MTLS: %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 */ + case 13:/* debug.log-ssl-noise */ + break; + default:/* should not happen */ + break; + } + } + + if (pemfile) { + if (NULL == privkey) privkey = pemfile; + pemfile->v.v = + network_mbedtls_load_pemfile(srv, pemfile->v.b, privkey->v.b); + 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_mbedtls_merge_config(&p->defaults, cpv); + } + + #ifndef MBEDTLS_ERROR_C + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: No error strings available. " + "Compile mbedtls with MBEDTLS_ERROR_C to enable."); + #endif + + return mod_mbedtls_set_defaults_sockets(srv, p); +} + + +static int +load_next_chunk (request_st * const r, chunkqueue * const cq, off_t max_bytes, + const char ** const data, size_t * const data_len) +{ + chunk *c = cq->first; + + /* local_send_buffer is a static buffer of size (LOCAL_SEND_BUFSIZE) + * + * buffer is allocated once, is NOT realloced (note: not thread-safe) + * */ + + 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 mbedtls_ssl_write() to reduce number times + * write() called underneath mbedtls_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, r->conf.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(r->conf.errh, __FILE__, __LINE__, "lseek"); + return -1; + } + if (-1 == (toSend = read(c->file.fd, local_send_buffer, toSend))) { + log_perror(r->conf.errh, __FILE__, __LINE__, "read"); + return -1; + } + + *data = local_send_buffer; + *data_len = toSend; + } + return 0; + } + + return -1; +} + + +__attribute_cold__ +static int +mod_mbedtls_ssl_write_err(connection *con, handler_ctx *hctx, int wr, size_t wr_len) +{ + switch (wr) { + case MBEDTLS_ERR_SSL_WANT_READ: + con->is_readable = -1; + break; /* try again later */ + case MBEDTLS_ERR_SSL_WANT_WRITE: + con->is_writable = -1; + break; /* try again later */ + case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + break; /* try again later */ + case MBEDTLS_ERR_NET_CONN_RESET: + if (hctx->conf.ssl_log_noise) + elog(hctx->r->conf.errh, __FILE__, __LINE__, wr, + "peer closed connection"); + return -1; + default: + elog(hctx->r->conf.errh, __FILE__, __LINE__, wr, __func__); + return -1; + } + + if (0 != hctx->ssl.out_left) /* partial write; save attempted wr_len */ + hctx->pending_write = wr_len; + + return 0; /* try again later */ +} + + +static int +mod_mbedtls_close_notify(handler_ctx *hctx); + + +static int +connection_write_cq_ssl (connection *con, chunkqueue *cq, off_t max_bytes) +{ + request_st * const r = &con->request; + handler_ctx *hctx = r->plugin_ctx[plugin_data_singleton->id]; + mbedtls_ssl_context * const ssl = &hctx->ssl; + + if (hctx->pending_write) { + int wr = (int)hctx->pending_write; + if (0 != ssl->out_left) { + /*(would prefer mbedtls_ssl_flush_output() from ssl_internal.h)*/ + size_t data_len = hctx->pending_write; + wr = mbedtls_ssl_write(ssl, NULL, data_len); + if (wr <= 0) + return mod_mbedtls_ssl_write_err(con, hctx, wr, data_len); + max_bytes -= wr; + } + hctx->pending_write = 0; + chunkqueue_mark_written(cq, wr); + } + + if (0 != hctx->close_notify) return mod_mbedtls_close_notify(hctx); + + chunkqueue_remove_finished_chunks(cq); + + const int lim = mbedtls_ssl_get_max_out_record_payload(ssl); + if (lim < 0) return mod_mbedtls_ssl_write_err(con, hctx, lim, 0); + + while (max_bytes > 0 && NULL != cq->first) { + const char *data; + size_t data_len; + int wr; + + if (0 != load_next_chunk(r, cq, max_bytes, &data, &data_len)) return -1; + + /* mbedtls_ssl_write() copies the data, up to max record size, but if + * (temporarily) unable to write the entire record, it is documented + * that the caller must call mbedtls_ssl_write() again, later, with the + * same arguments. This appears to be because mbedtls_ssl_context does + * not keep track of the original size of the caller data that + * mbedtls_ssl_write() attempted to write (and may have transformed to + * a different size). The func may return MBEDTLS_ERR_SSL_WANT_READ or + * MBEDTLS_ERR_SSL_WANT_WRITE to indicate that the caller should wait + * for the fd to be readable/writable before calling the func again, + * which is why those (temporary) errors are returned instead of telling + * the caller that the data was successfully copied. When the record is + * written successfully, the return value is supposed to indicate the + * number of (originally submitted) bytes written, but since that value + * is unknown (not saved), the caller's len parameter is reflected back, + * which is why the caller must call the func again with the same args. + * Additionally, to be accurate, the size must fit into a record which + * is why we restrict ourselves to sending max out record payload each + * iteration. + */ + + int wr_total = 0; + do { + size_t wr_len = (data_len > (size_t)lim) ? (size_t)lim : data_len; + wr = mbedtls_ssl_write(ssl, (const unsigned char *)data, wr_len); + if (wr <= 0) { + if (wr_total) chunkqueue_mark_written(cq, wr_total); + return mod_mbedtls_ssl_write_err(con, hctx, wr, wr_len); + } + wr_total += wr; + data += wr; + } while ((data_len -= wr)); + chunkqueue_mark_written(cq, wr_total); + max_bytes -= wr_total; + } + + return 0; +} + + +static int +mod_mbedtls_ssl_handshake (handler_ctx *hctx) +{ + int rc = 0; + + /* overwrite callback with hctx each time we enter here, before handshake + * (Some callbacks are on mbedtls_ssl_config, not mbedtls_ssl_context) + * (Not thread-safe if config (mbedtls_ssl_config *ssl_ctx) is shared) + * (XXX: there is probably a better way to do this...) */ + /* (alternative: save ssl_ctx in hctx in mod_mbedtls_handle_con_accept()) */ + mbedtls_ssl_config *ssl_ctx; + *(const mbedtls_ssl_config **)&ssl_ctx = hctx->ssl.conf; + #ifdef MBEDTLS_SSL_SERVER_NAME_INDICATION + mbedtls_ssl_conf_sni(ssl_ctx, mod_mbedtls_SNI, hctx); + #endif + + if (hctx->ssl.state < MBEDTLS_SSL_SERVER_HELLO) { + while (hctx->ssl.state != MBEDTLS_SSL_SERVER_HELLO + && hctx->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) { + rc = mbedtls_ssl_handshake_step(&hctx->ssl); + if (0 != rc) break; + } + if (0 == rc && hctx->ssl.state == MBEDTLS_SSL_SERVER_HELLO) { + #ifdef MBEDTLS_SSL_ALPN + if (!buffer_string_is_empty(hctx->conf.ssl_acme_tls_1)) { + const char *alpn = mbedtls_ssl_get_alpn_protocol(&hctx->ssl); + if (NULL != alpn) + rc = mod_mbedtls_alpn_select_cb(hctx, alpn); + } + #endif + } + } + + /* overwrite callback with hctx each time we enter here, before handshake + * (Some callbacks are on mbedtls_ssl_config, not mbedtls_ssl_context) + * (Not thread-safe if config (mbedtls_ssl_config *ssl_ctx) is shared) + * (XXX: there is probably a better way to do this...) */ + if (0 == rc && hctx->conf.ssl_verifyclient + && hctx->ssl.state >= MBEDTLS_SSL_SERVER_HELLO /*(after SNI and ALPN)*/ + && hctx->ssl.state <= MBEDTLS_SSL_SERVER_HELLO_DONE + && hctx->alpn != MOD_MBEDTLS_ALPN_ACME_TLS_1) { /*(not "acme-tls/1")*/ + int mode = (hctx->conf.ssl_verifyclient_enforce) + ? MBEDTLS_SSL_VERIFY_REQUIRED + : MBEDTLS_SSL_VERIFY_OPTIONAL; + mbedtls_ssl_set_hs_authmode(&hctx->ssl, mode); + while (hctx->ssl.state != MBEDTLS_SSL_CERTIFICATE_REQUEST + && hctx->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) { + rc = mbedtls_ssl_handshake_step(&hctx->ssl); + if (0 != rc) break; + } + if (0 == rc && hctx->ssl.state == MBEDTLS_SSL_CERTIFICATE_REQUEST) { + rc = mod_mbedtls_conf_verify(hctx, ssl_ctx); + if (0 == rc) + rc = mbedtls_ssl_handshake_step(&hctx->ssl); + /* reconfigure CA trust chain after sending client certificate + * request (if ssl_ca_dn_file is set), before client certificate + * verification (MBEDTLS_SSL_CERTIFICATE_VERIFY) */ + if (0 == rc && hctx->conf.ssl_ca_dn_file + && hctx->ssl.state == MBEDTLS_SSL_SERVER_HELLO_DONE) { + mbedtls_ssl_context * const ssl = &hctx->ssl; + mbedtls_x509_crt *ca_certs = hctx->conf.ssl_ca_file; + mbedtls_x509_crl *ca_crl = hctx->conf.ssl_ca_crl_file; + mbedtls_ssl_set_hs_ca_chain(ssl, ca_certs, ca_crl); + } + } + } + + if (0 == rc && hctx->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER) { + rc = mbedtls_ssl_handshake(&hctx->ssl); + } + + switch (rc) { + case 0: + hctx->handshake_done = 1; + #ifdef MBEDTLS_SSL_ALPN + if (hctx->alpn == MOD_MBEDTLS_ALPN_ACME_TLS_1) { + /* Once TLS handshake is complete, return -1 to result in + * CON_STATE_ERROR so that socket connection is quickly closed */ + return -1; + } + hctx->alpn = 0; + #endif + return 1; /* continue reading */ + case MBEDTLS_ERR_SSL_WANT_WRITE: + hctx->con->is_writable = -1; + __attribute_fallthrough__ + case MBEDTLS_ERR_SSL_WANT_READ: + hctx->con->is_readable = 0; + return 0; + case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + return 0; + case MBEDTLS_ERR_SSL_CLIENT_RECONNECT: + return -1; + case MBEDTLS_ERR_NET_CONN_RESET: + case MBEDTLS_ERR_SSL_CONN_EOF: + if (!hctx->conf.ssl_log_noise) return -1; + __attribute_fallthrough__ + default: + elog(hctx->r->conf.errh, __FILE__, __LINE__, rc, __func__); + return -1; + } +} + + +static int +connection_read_cq_ssl (connection *con, chunkqueue *cq, off_t max_bytes) +{ + request_st * const r = &con->request; + handler_ctx *hctx = r->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_mbedtls_close_notify(hctx); + + if (!hctx->handshake_done) { + int rc = mod_mbedtls_ssl_handshake(hctx); + if (1 != rc) return rc; /* !hctx->handshake_done; not done, or error */ + } + + do { + len = mbedtls_ssl_get_bytes_avail(&hctx->ssl); + mem_len = len < 2048 ? 2048 : (size_t)len; + chunk * const ckpt = cq->last; + mem = chunkqueue_get_memory(cq, &mem_len); + + len = mbedtls_ssl_read(&hctx->ssl, (unsigned char *)mem, mem_len); + if (len > 0) { + chunkqueue_use_memory(cq, ckpt, len); + con->bytes_read += len; + } else { + chunkqueue_use_memory(cq, ckpt, 0); + } + } while (len > 0 + && mbedtls_ssl_check_pending(&hctx->ssl)); + + if (len < 0) { + int rc = len; + switch (rc) { + case MBEDTLS_ERR_SSL_WANT_WRITE: + con->is_writable = -1; + __attribute_fallthrough__ + case MBEDTLS_ERR_SSL_WANT_READ: + con->is_readable = 0; + return 0; + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + case MBEDTLS_ERR_SSL_CONN_EOF: + /* XXX: future: save state to avoid future read after response? */ + con->is_readable = 0; + r->keep_alive = 0; + return -2; + case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS: + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + return 0; + case MBEDTLS_ERR_SSL_CLIENT_RECONNECT: + return -1; + case MBEDTLS_ERR_NET_CONN_RESET: + if (!hctx->conf.ssl_log_noise) return -1; + __attribute_fallthrough__ + default: + elog(r->conf.errh, __FILE__, __LINE__, rc, "Reading mbedtls"); + return -1; + } + } else if (len == 0) { + con->is_readable = 0; + /* the other end closed the connection -> KEEP-ALIVE */ + + return -2; + } else { + return 0; + } +} + + +static void +mod_mbedtls_debug_cb(void *ctx, int level, + const char *file, int line, + const char *str) +{ + if (level < (intptr_t)ctx) /* level */ + log_error(plugin_data_singleton->srv->errh,file,line,"MTLS: %s",str); +} + + +CONNECTION_FUNC(mod_mbedtls_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; + r->plugin_ctx[p->id] = hctx; + + plugin_ssl_ctx * const s = p->ssl_ctxs + srv_sock->sidx; + mbedtls_ssl_init(&hctx->ssl); + int rc = mbedtls_ssl_setup(&hctx->ssl, s->ssl_ctx); + if (0 == rc) { + con->network_read = connection_read_cq_ssl; + con->network_write = connection_write_cq_ssl; + con->proto_default_port = 443; /* "https" */ + mod_mbedtls_patch_config(r, &hctx->conf); + } + else { + elog(r->conf.errh, __FILE__, __LINE__, rc, "ssl_setup() failed"); + return HANDLER_ERROR; + } + + mbedtls_ssl_set_bio(&hctx->ssl, (mbedtls_net_context *)&con->fd, + mbedtls_net_send, mbedtls_net_recv, NULL); + + /* (mbedtls_ssl_config *) is shared across multiple connections, which may + * overlap, and so this debug setting is not reset upon connection close. + * Once enabled, debug hook will remain so for this mbedtls_ssl_config */ + if (hctx->conf.ssl_log_noise) /* volume level for debug message callback */ + mbedtls_ssl_conf_dbg(s->ssl_ctx, mod_mbedtls_debug_cb, + (void *)(intptr_t)hctx->conf.ssl_log_noise); + + /* (mbedtls_ssl_config *) is shared across multiple connections, which may + * overlap, and so renegotiation setting is not reset upon connection close. + * Once enabled, renegotiation will remain so for this mbedtls_ssl_config. + * mbedtls defaults to disable client renegotiation + * (MBEDTLS_SSL_RENEGOTIATION_DISABLED) + * and it is recommended to leave it disabled (lighttpd mbedtls default) */ + if (!hctx->conf.ssl_disable_client_renegotiation) + mbedtls_ssl_conf_renegotiation(s->ssl_ctx, + MBEDTLS_SSL_RENEGOTIATION_ENABLED); + + return HANDLER_GO_ON; +} + + +static void +mod_mbedtls_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_mbedtls_handle_con_shut_wr) +{ + request_st * const r = &con->request; + plugin_data *p = p_d; + handler_ctx *hctx = r->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + + hctx->close_notify = -2; + if (hctx->handshake_done) { + mod_mbedtls_close_notify(hctx); + } + else { + mod_mbedtls_detach(hctx); + } + + return HANDLER_GO_ON; +} + + +static int +mod_mbedtls_close_notify (handler_ctx *hctx) +{ + if (1 == hctx->close_notify) return -2; + + int rc = mbedtls_ssl_close_notify(&hctx->ssl); + switch (rc) { + case 0: + mod_mbedtls_detach(hctx); + return -2; + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + return 0; + default: + elog(hctx->r->conf.errh, __FILE__, __LINE__, rc, + "mbedtls_ssl_close_notify()"); + mbedtls_ssl_session_reset(&hctx->ssl); + mod_mbedtls_detach(hctx); + return -1; + } +} + + +CONNECTION_FUNC(mod_mbedtls_handle_con_close) +{ + request_st * const r = &con->request; + plugin_data *p = p_d; + handler_ctx *hctx = r->plugin_ctx[p->id]; + if (NULL != hctx) { + if (1 != hctx->close_notify) + mod_mbedtls_close_notify(hctx); /*(one final try)*/ + handler_ctx_free(hctx); + r->plugin_ctx[p->id] = NULL; + } + + return HANDLER_GO_ON; +} + + +#if defined(MBEDTLS_PEM_WRITE_C) +__attribute_noinline__ +static void +https_add_ssl_client_cert (request_st * const r, const mbedtls_x509_crt * const peer) +{ + #define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n" + #define PEM_END_CRT "-----END CERTIFICATE-----\n" + unsigned char buf[4096]; + size_t olen; + if (0 == mbedtls_pem_write_buffer(PEM_BEGIN_CRT, PEM_END_CRT, + peer->raw.p, peer->raw.len, + buf, sizeof(buf), &olen)) + http_header_env_set(r, + CONST_STR_LEN("SSL_CLIENT_CERT"), + (char *)buf, olen); +} +#endif + + +static void +https_add_ssl_client_entries (request_st * const r, handler_ctx * const hctx) +{ + /* Note: starting with mbedtls-2.17.0, peer cert is not available here if + * MBEDTLS_SSL_KEEP_PEER_CERTIFICATE *is not* defined at compile time, + * though default behavior is to have it defined. However, since earlier + * versions do keep the cert, but not set this define, attempt to retrieve + * the peer cert and check for NULL before using it. */ + const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&hctx->ssl); + buffer * const tb = r->tmp_buf; + char buf[512]; + int n; + + uint32_t rc = (NULL != crt) + ? mbedtls_ssl_get_verify_result(&hctx->ssl) + : 0xFFFFFFFF; + if (0xFFFFFFFF == rc) { /*(e.g. no cert, or verify result not available)*/ + http_header_env_set(r, + CONST_STR_LEN("SSL_CLIENT_VERIFY"), + CONST_STR_LEN("NONE")); + return; + } + else if (0 != rc) { + /* get failure string and translate newline to ':', removing last one */ + n = mbedtls_x509_crt_verify_info(buf, sizeof(buf), "", rc); + buffer_copy_string_len(tb, CONST_STR_LEN("FAILED:")); + if (n > 0) { + for (char *nl = buf; NULL != (nl = strchr(nl, '\n')); ++nl) + nl[0] = ('\0' == nl[1] ? (--n, '\0') : ':'); + buffer_append_string_len(tb, buf, n); + } + http_header_env_set(r, + CONST_STR_LEN("SSL_CLIENT_VERIFY"), + CONST_BUF_LEN(tb)); + return; + } + else { + http_header_env_set(r, + CONST_STR_LEN("SSL_CLIENT_VERIFY"), + CONST_STR_LEN("SUCCESS")); + } + + n = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject); + if (n > 0 && n < (int)sizeof(buf)) { + http_header_env_set(r, + CONST_STR_LEN("SSL_CLIENT_S_DN"), + buf, n); + } + + /* add components of client Subject DN */ + /* code block is similar to mbedtls_x509_dn_gets() */ + /*(reuse buf; sizeof(buf) > MBEDTLS_X509_MAX_DN_NAME_SIZE, which is 256)*/ + buffer_copy_string_len(tb, CONST_STR_LEN("SSL_CLIENT_S_DN_")); + const mbedtls_x509_name *name = &crt->subject; + while (name != NULL) { + if (!name->oid.p) { + name = name->next; + continue; + } + + const char *short_name = NULL; + if (0 != mbedtls_oid_get_attr_short_name(&name->oid, &short_name)) + continue; + buffer_string_set_length(tb, sizeof("SSL_CLIENT_S_DN_")-1); + buffer_append_string(tb, short_name); + + const mbedtls_x509_name *nm = name; + n = 0; + do { + if (nm != name && n < (int)sizeof(buf)-1) + buf[n++] = ','; + for (size_t i = 0; i < nm->val.len && n < (int)sizeof(buf)-1; ++n) { + unsigned char c = nm->val.p[i]; + buf[n] = (c < 32 || c == 127 || (c > 128 && c < 160)) ? '?' : c; + } + buf[n] = '\0'; + } while (nm->next_merged && nm->next && (nm = nm->next)); + if (n == sizeof(buf)-1) + while (nm->next_merged && nm->next) nm = nm->next; + name = nm->next; + + http_header_env_set(r, + CONST_BUF_LEN(tb), + buf, n); + } + + n = mbedtls_x509_serial_gets(buf, sizeof(buf), &crt->serial); + if (n > 0 && n < (int)sizeof(buf)) { + http_header_env_set(r, + CONST_STR_LEN("SSL_CLIENT_M_SERIAL"), + buf, n); + } + + 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 defined(MBEDTLS_PEM_WRITE_C) + /* if (NULL != crt) (e.g. not PSK-based ciphersuite) */ + if (hctx->conf.ssl_verifyclient_export_cert && NULL != crt) + https_add_ssl_client_cert(r, crt); + #endif +} + + +static void +http_cgi_ssl_env (request_st * const r, handler_ctx * const hctx) +{ + #if 1 + /* XXX: reaching into ssl_internal.h here for mbedtls_ssl_transform */ + const mbedtls_cipher_info_t *cipher_info = + hctx->ssl.transform->cipher_ctx_enc.cipher_info; + #else + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + mbedtls_ssl_ciphersuite_from_id(hctx->ssl.session->ciphersuite); + const mbedtls_cipher_info_t *cipher_info = + mbedtls_cipher_info_from_type(ciphersuite_info->cipher); + #endif + const char *s; + + s = mbedtls_ssl_get_version(&hctx->ssl); + http_header_env_set(r, CONST_STR_LEN("SSL_PROTOCOL"), s, strlen(s)); + + /*s = ciphersuite_info->name;*/ /*mbedtls_ssl_get_ciphersuite(&hctx->ssl);*/ + s = cipher_info->name; + http_header_env_set(r, CONST_STR_LEN("SSL_CIPHER"), s, strlen(s)); + + if (cipher_info != NULL) { + /* SSL_CIPHER_ALGKEYSIZE - Number of cipher bits (possible) */ + /* SSL_CIPHER_USEKEYSIZE - Number of cipher bits (actually used) */ + /* XXX: is usekeysize correct? XXX: reaching into ssl_internal.h here */ + int usekeysize = hctx->ssl.transform->cipher_ctx_enc.key_bitlen; + unsigned int algkeysize = cipher_info->key_bitlen; + char buf[LI_ITOSTRING_LENGTH]; + 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_utostrn(buf, sizeof(buf), algkeysize)); + } +} + + +REQUEST_FUNC(mod_mbedtls_handle_request_env) +{ + plugin_data *p = p_d; + handler_ctx *hctx = r->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + if (hctx->request_env_patched) return HANDLER_GO_ON; + hctx->request_env_patched = 1; + + 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_mbedtls_handle_uri_raw) +{ + /* mod_mbedtls must be loaded prior to mod_auth + * if mod_mbedtls is configured to set REMOTE_USER based on client cert */ + /* mod_mbedtls must be loaded after mod_extforward + * if mod_mbedtls 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_mbedtls */ + plugin_data *p = p_d; + handler_ctx *hctx = r->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + + mod_mbedtls_patch_config(r, &hctx->conf); + if (hctx->conf.ssl_verifyclient) { + mod_mbedtls_handle_request_env(r, p); + } + + return HANDLER_GO_ON; +} + + +REQUEST_FUNC(mod_mbedtls_handle_request_reset) +{ + plugin_data *p = p_d; + handler_ctx *hctx = r->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + + hctx->request_env_patched = 0; + return HANDLER_GO_ON; +} + + +int mod_mbedtls_plugin_init (plugin *p); +int mod_mbedtls_plugin_init (plugin *p) +{ + p->version = LIGHTTPD_VERSION_ID; + p->name = "mbedtls"; + p->init = mod_mbedtls_init; + p->cleanup = mod_mbedtls_free; + p->priv_defaults= mod_mbedtls_set_defaults; + + p->handle_connection_accept = mod_mbedtls_handle_con_accept; + p->handle_connection_shut_wr = mod_mbedtls_handle_con_shut_wr; + p->handle_connection_close = mod_mbedtls_handle_con_close; + p->handle_uri_raw = mod_mbedtls_handle_uri_raw; + p->handle_request_env = mod_mbedtls_handle_request_env; + p->connection_reset = mod_mbedtls_handle_request_reset; + + return 0; +} + + +/* cipher suites (taken from mbedtls/ssl_ciphersuites.[ch]) */ + +static const int suite_CHACHAPOLY_ephemeral[] = { + /* Chacha-Poly ephemeral suites */ + MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 +}; + +static const int suite_AES_256_ephemeral[] = { + /* All AES-256 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8 +}; + +static const int suite_CAMELLIA_256_ephemeral[] = { + /* All CAMELLIA-256 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA +}; + +static const int suite_ARIA_256_ephemeral[] = { + /* All ARIA-256 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 +}; + +static const int suite_AES_128_ephemeral[] = { + /* All AES-128 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8 +}; + +static const int suite_CAMELLIA_128_ephemeral[] = { + /* All CAMELLIA-128 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA +}; + +static const int suite_ARIA_128_ephemeral[] = { + /* All ARIA-128 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 +}; + +static const int suite_PSK_ephemeral[] = { + /* The PSK ephemeral suites */ + MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384, + + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 +}; + +#if 0 +static const int suite_ECJPAKE[] = { + /* The ECJPAKE suite */ + MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 +}; +#endif + +static const int suite_AES_256[] = { + /* All AES-256 suites */ + MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_AES_256_CCM, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8 +}; + +static const int suite_CAMELLIA_256[] = { + /* All CAMELLIA-256 suites */ + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 +}; + +static const int suite_ARIA_256[] = { + /* All ARIA-256 suites */ + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384 +}; + +static const int suite_AES_128[] = { + /* All AES-128 suites */ + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CCM, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8 +}; + +static const int suite_CAMELLIA_128[] = { + /* All CAMELLIA-128 suites */ + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 +}; + +static const int suite_ARIA_128[] = { + /* All ARIA-128 suites */ + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256 +}; + +static const int suite_RSA_PSK[] = { + /* The RSA PSK suites */ + MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384, + + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 +}; + +static const int suite_PSK[] = { + /* The PSK suites */ + MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CCM, + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8, + MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384, + + MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CCM, + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8, + MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256 +}; + +static const int suite_3DES[] = { + /* 3DES suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_3DES_EDE_CBC_SHA +}; + +static const int suite_RC4[] = { + /* RC4 suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_RC4_128_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_RC4_128_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_RC4_128_SHA, + MBEDTLS_TLS_RSA_WITH_RC4_128_SHA, + MBEDTLS_TLS_RSA_WITH_RC4_128_MD5, + MBEDTLS_TLS_ECDH_RSA_WITH_RC4_128_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_RC4_128_SHA, + MBEDTLS_TLS_PSK_WITH_RC4_128_SHA +}; + +static const int suite_weak[] = { + /* Weak suites */ + MBEDTLS_TLS_DHE_RSA_WITH_DES_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_DES_CBC_SHA +}; + +static const int suite_null[] = { + /* NULL suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA, + + MBEDTLS_TLS_RSA_WITH_NULL_SHA256, + MBEDTLS_TLS_RSA_WITH_NULL_SHA, + MBEDTLS_TLS_RSA_WITH_NULL_MD5, + MBEDTLS_TLS_ECDH_RSA_WITH_NULL_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA, + MBEDTLS_TLS_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_PSK_WITH_NULL_SHA +}; + +/* TLSv1.2 cipher list (supported in mbedtls) + * marked with minimum version MBEDTLS_SSL_MINOR_VERSION_3 in + * ciphersuite_definitions[] and then sorted by ciphersuite_preference[] + * from mbedtls library/ssl_ciphersuites.c */ +static const int suite_TLSv12[] = { + MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8, + MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_AES_256_CCM, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CCM, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CCM, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8, + MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CCM, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8, + MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256 +}; + +/* TLSv1.0 cipher list (supported in mbedtls) + * marked with minimum version MBEDTLS_SSL_MINOR_VERSION_1 in + * ciphersuite_definitions[] and then sorted by ciphersuite_preference[] + * from mbedtls library/ssl_ciphersuites.c */ +/* XXX: intentionally not including overlapping eNULL ciphers */ +static const int suite_TLSv10[] = { + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_RC4_128_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_RC4_128_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_RC4_128_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_RC4_128_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_RC4_128_SHA +}; + +/* SSLv3 cipher list (supported in mbedtls) + * marked with minimum version MBEDTLS_SSL_MINOR_VERSION_0 in + * ciphersuite_definitions[] and then sorted by ciphersuite_preference[] + * from mbedtls library/ssl_ciphersuites.c */ +/* XXX: intentionally not including overlapping eNULL ciphers */ +static const int suite_SSLv3[] = { + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_3DES_EDE_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_RC4_128_SHA, + MBEDTLS_TLS_RSA_WITH_RC4_128_SHA, + MBEDTLS_TLS_RSA_WITH_RC4_128_MD5, + MBEDTLS_TLS_PSK_WITH_RC4_128_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_DES_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_DES_CBC_SHA +}; + +/* HIGH cipher list (mapped from openssl list to mbedtls) */ +static const int suite_HIGH[] = { + MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_AES_256_CCM, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CCM, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CCM, + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8, + MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CCM, + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8, + MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256, +}; + + +/* true if RC4 or weak or NULL cipher suite + * (These ciphersuites are excluded from openssl "DEFAULT") + * This is a subset of ciphers excluded for mod_openssl "!aNULL:!eNULL:!EXP" */ +static int +mod_mbedtls_ssl_is_weak_ciphersuite (int id) +{ + for (uint32_t i = 0; i < sizeof(suite_RC4)/sizeof(suite_RC4[0]); ++i) { + if (id == suite_RC4[i]) return 1; + } + for (uint32_t i = 0; i < sizeof(suite_weak)/sizeof(suite_weak[0]); ++i) { + if (id == suite_weak[i]) return 1; + } + for (uint32_t i = 0; i < sizeof(suite_null)/sizeof(suite_null[0]); ++i) { + if (id == suite_null[i]) return 1; + } + return 0; +} + + +static int +mod_mbedtls_ssl_DEFAULT_ciphersuite (server *srv, int *ids, int nids, int idsz) +{ + /* obtain default ciphersuite list and filter out weak or NULL */ + const int *dids = mbedtls_ssl_list_ciphersuites(); + int i = 0; + while (dids[i] != 0) ++i; + + if (i >= idsz - (nids + 1)) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: error: too many ciphersuites during list expand"); + return -1; + } + + for (i = 0; dids[i] != 0; ++i) { + if (!mod_mbedtls_ssl_is_weak_ciphersuite(dids[i])) + ids[++nids] = dids[i]; + } + + return nids; +} + + +static int +mod_mbedtls_ssl_append_ciphersuite (server *srv, int *ids, int nids, int idsz, const int *x, int xsz) +{ + if (xsz >= idsz - (nids + 1)) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: error: too many ciphersuites during list expand"); + return -1; + } + + for (int i = 0; i < xsz; ++i) + ids[++nids] = x[i]; + + return nids; +} + + +static int +mod_mbedtls_ssl_conf_ciphersuites (server *srv, plugin_config_socket *s, buffer *ciphersuites, const buffer *cipherstring) +{ + /* reference: https://www.openssl.org/docs/man1.1.1/man1/ciphers.html + * Attempt to parse *some* keywords from Ciphersuites and CipherString + * !!! openssl uses a *different* naming scheme than does mbedTLS !!! + * Since Ciphersuites in openssl takes only TLSv1.3 suites, and mbedTLS + * does not currently support TLSv1.3, mapping of those names is not + * currently provided. Note that CipherString does allow cipher suites to + * be listed, and this code does not currently attempt to provide mapping */ + + char n[128]; /*(most ciphersuite names are about 40 chars)*/ + int ids[512]; + int nids = -1; + const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1); + int crt_profile_default = 0; + + if (ciphersuites) { + buffer *b = ciphersuites; + buffer_to_upper(b); /*(ciphersuites are all uppercase (currently))*/ + for (const char *p, *e = b->ptr-1; e && (e = strchr((p = e+1),':')); ) { + size_t len = e ? (size_t)(e - p) : strlen(p); + if (len >= sizeof(n)) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: skipped ciphersuite; too long: %.*s", + (int)len, p); + continue; + } + memcpy(n, p, len); + n[len] = '\0'; + + int id = mbedtls_ssl_get_ciphersuite_id(n); + if (0 == id) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: skipped ciphersuite; not recognized: %.*s", + (int)len, n); + continue; + } + + /* allow any ciphersuite if explicitly listed, even weak or eNULL */ + #if 0 + if (mod_mbedtls_ssl_is_weak_ciphersuite(id)) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: skipped ciphersuite; weak or NULL suite: %.*s", + (int)len, n); + continue; + } + #endif + + if (nids >= idsz) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: skipped ciphersuite; too many listed: %.*s", + (int)len, n); + continue; + } + + ids[++nids] = id; + } + } + + /* XXX: openssl config for CipherString=... is excessively complex. + * If there is a need to enable specific ciphersuites, then that + * can be accomplished with mod_mbedtls by specifying the list in + * Ciphersuites=... in the ssl.openssl.ssl-conf-cmd directive. + * (Alternatively, build mbedtls with specific set of cipher suites + * or modify mod_mbedtls code to specify the precise list). + * + * The tokens parsed here are a quick attempt to handle a few cases + * + * XXX: not done: could make a list of ciphers with bitflag of attributes + * to make future combining easier */ + if (cipherstring) { + const buffer *b = cipherstring; + const char *e = b->ptr; + + /* XXX: not done: no checking for duplication of ciphersuites + * even if tokens overlap or are repeated */ + + /* XXX: not done: might walk string and build up exclude list of !xxxxx + * ciphersuites and walk string again, excluding as result list built */ + + /* manually handle first token, since one-offs apply */ + /* (openssl syntax NOT fully supported) */ + int default_suite = 0; + if (0 == strncmp(e, "!ALL", 4) || 0 == strncmp(e, "-ALL", 4)) { + /* "!ALL" excluding all ciphers does not make sense; ignore */ + e += sizeof("!ALL")-1; /* same as sizeof("-ALL")-1 */ + } + else if (0 == strncmp(e, CONST_STR_LEN("!DEFAULT")) + || 0 == strncmp(e, CONST_STR_LEN("-DEFAULT"))) { + /* "!DEFAULT" excluding default ciphers is empty list; no effect */ + e += sizeof("!DEFAULT")-1; /* same as sizeof("-DEFAULT")-1 */ + } + else if (0 == strncmp(e, CONST_STR_LEN("DEFAULT"))) { + e += sizeof("DEFAULT")-1; + default_suite = 1; + } + else if (0 == /* effectively the same as "DEFAULT" */ + strncmp(e, CONST_STR_LEN("ALL:!COMPLEMENTOFDEFAULT:!eNULL"))) { + e += sizeof("ALL:!COMPLEMENTOFDEFAULT:!eNULL")-1; + default_suite = 1; + } + else if (0 == strncmp(e, CONST_STR_LEN("SUITEB128")) + || 0 == strncmp(e, CONST_STR_LEN("SUITEB128ONLY")) + || 0 == strncmp(e, CONST_STR_LEN("SUITEB192"))) { + mbedtls_ssl_conf_cert_profile(s->ssl_ctx, + &mbedtls_x509_crt_profile_suiteb); + /* re-initialize mbedtls_ssl_config defaults */ + mbedtls_mpi_free(&s->ssl_ctx->dhm_P); + mbedtls_mpi_free(&s->ssl_ctx->dhm_G); + int rc = mbedtls_ssl_config_defaults(s->ssl_ctx, + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_SUITEB); + if (0 != rc) { + elog(srv->errh, __FILE__,__LINE__, rc, + "Init of ssl config context SUITEB defaults failed"); + return 0; + } + e += (0 == strncmp(e, CONST_STR_LEN("SUITEB128ONLY"))) + ? sizeof("SUITEB128ONLY")-1 + : sizeof("SUITEB128")-1; + if (*e) + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: ignoring cipher string after SUITEB: %s", e); + return 1; + } + + if (e != b->ptr && *e != ':' && *e != '\0') { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: error: missing support for cipher list: %s", b->ptr); + return 0; + } + + if (default_suite) { + crt_profile_default = 1; + nids = + mod_mbedtls_ssl_DEFAULT_ciphersuite(srv, ids, nids, idsz); + if (-1 == nids) return 0; + } + + /* not handled: "ALL" is "DEFAULT" and "RC4" */ + /* not handled: "COMPLEMENTOFALL" is "eNULL" */ + + int rc = 1; + if (e == b->ptr || *e == '\0') --e; /*initial condition for loop below*/ + for (const char *p; e && (e = strchr((p = e+1),':')); ) { + size_t len = e ? (size_t)(e - p) : strlen(p); + if (len >= sizeof(n)) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: skipped ciphersuite; too long: %.*s", + (int)len, p); + continue; + } + char c = (*p == '!' || *p == '-' || *p == '+') ? *p : 0; + size_t nlen = c ? len-1 : len; + memcpy(n, c ? p+1 : p, nlen); + n[nlen] = '\0'; + + /* not handled: !xxxxx -xxxxx and most +xxxxx */ + if (c) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: error: missing support for cipher list: %s", b->ptr); + } + + /* ignore @STRENGTH sorting and ignore @SECLEVEL=n */ + char *a = strchr(n, '@'); + if (a) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: ignored %s in %.*s", a, (int)len, p); + *a = '\0'; + nlen = (size_t)(a - n); + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("TLSv1.2"))) { + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_TLSv12, + (int)(sizeof(suite_TLSv12)/sizeof(*suite_TLSv12))); + if (-1 == nids) return 0; + continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("TLSv1.0"))) { + crt_profile_default = 1; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_TLSv10, + (int)(sizeof(suite_TLSv10)/sizeof(*suite_TLSv10))); + if (-1 == nids) return 0; + continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("SSLv3"))) { + crt_profile_default = 1; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_SSLv3, + (int)(sizeof(suite_SSLv3)/sizeof(*suite_SSLv3))); + if (-1 == nids) return 0; + continue; + } + + /* handle a popular recommendations + * ssl.cipher-list = "EECDH+AESGCM:EDH+AESGCM" + * ssl.cipher-list = "AES256+EECDH:AES256+EDH" + * which uses AES hardware acceleration built into popular CPUs */ + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("ECDHE+AESGCM")) + || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("EECDH+AESGCM"))) { + if (nids + 4 >= idsz) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: error: too many ciphersuites during list expand"); + return 0; + } + ids[++nids] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; + ids[++nids] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; + ids[++nids] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; + ids[++nids] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256; + continue; + } + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("DHE+AESGCM")) + || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("EDH+AESGCM"))) { + if (nids + 2 >= idsz) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: error: too many ciphersuites during list expand"); + return 0; + } + ids[++nids] = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384; + ids[++nids] = MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256; + continue; + } + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES256+EECDH"))) { + if (nids + 8 >= idsz) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: error: too many ciphersuites during list expand"); + return 0; + } + ids[++nids] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384; + ids[++nids] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384; + ids[++nids] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM; + ids[++nids] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384; + ids[++nids] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384; + ids[++nids] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA; + ids[++nids] = MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA; + ids[++nids] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8; + continue; + } + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES256+EDH"))) { + if (nids + 5 >= idsz) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: error: too many ciphersuites during list expand"); + return 0; + } + ids[++nids] = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384; + ids[++nids] = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM; + ids[++nids] = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; + ids[++nids] = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA; + ids[++nids] = MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8; + continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("HIGH"))) { + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_HIGH, + (int)(sizeof(suite_HIGH)/sizeof(*suite_HIGH))); + if (-1 == nids) return 0; + continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES256")) + || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES"))) { + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_AES_256_ephemeral, + (int)(sizeof(suite_AES_256_ephemeral) + /sizeof(*suite_AES_256_ephemeral))); + if (-1 == nids) return 0; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_AES_256, + (int)(sizeof(suite_AES_256)/sizeof(*suite_AES_256))); + if (-1 == nids) return 0; + /* XXX: not done: AES256 PSK suites */ + if (nlen == sizeof("AES256")-1) continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES128")) + || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("AES"))) { + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_AES_128_ephemeral, + (int)(sizeof(suite_AES_128_ephemeral) + /sizeof(*suite_AES_128_ephemeral))); + if (-1 == nids) return 0; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_AES_128, + (int)(sizeof(suite_AES_128)/sizeof(*suite_AES_128))); + if (-1 == nids) return 0; + /* XXX: not done: AES128 PSK suites */ + continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("CAMELLIA256")) + || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("CAMELLIA"))) { + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_CAMELLIA_256_ephemeral, + (int)(sizeof(suite_CAMELLIA_256_ephemeral) + /sizeof(*suite_CAMELLIA_256_ephemeral))); + if (-1 == nids) return 0; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_CAMELLIA_256, + (int)(sizeof(suite_CAMELLIA_256) + /sizeof(*suite_CAMELLIA_256))); + if (-1 == nids) return 0; + /* XXX: not done: CAMELLIA256 PSK suites */ + if (nlen == sizeof("CAMELLIA256")-1) continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("CAMELLIA128")) + || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("CAMELLIA"))) { + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_CAMELLIA_128_ephemeral, + (int)(sizeof(suite_CAMELLIA_128_ephemeral) + /sizeof(*suite_CAMELLIA_128_ephemeral))); + if (-1 == nids) return 0; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_CAMELLIA_128, + (int)(sizeof(suite_CAMELLIA_128) + /sizeof(*suite_CAMELLIA_128))); + if (-1 == nids) return 0; + /* XXX: not done: CAMELLIA128 PSK suites */ + continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("ARIA256")) + || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("ARIA"))) { + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_ARIA_256_ephemeral, + (int)(sizeof(suite_ARIA_256_ephemeral) + /sizeof(*suite_ARIA_256_ephemeral))); + if (-1 == nids) return 0; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_ARIA_256, + (int)(sizeof(suite_ARIA_256)/sizeof(*suite_ARIA_256))); + if (-1 == nids) return 0; + /* XXX: not done: ARIA256 PSK suites */ + if (nlen == sizeof("ARIA256")-1) continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("ARIA128")) + || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("ARIA"))) { + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_ARIA_128_ephemeral, + (int)(sizeof(suite_ARIA_128_ephemeral) + /sizeof(*suite_ARIA_128_ephemeral))); + if (-1 == nids) return 0; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_ARIA_128, + (int)(sizeof(suite_ARIA_128)/sizeof(*suite_ARIA_128))); + if (-1 == nids) return 0; + /* XXX: not done: ARIA128 PSK suites */ + continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("CHACHA20"))) { + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_CHACHAPOLY_ephemeral, + (int)(sizeof(suite_CHACHAPOLY_ephemeral) + /sizeof(*suite_CHACHAPOLY_ephemeral))); + if (-1 == nids) return 0; + /* XXX: not done: CHACHA20 PSK suites */ + continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("PSK"))) { + /* XXX: intentionally not including overlapping eNULL ciphers */ + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_PSK_ephemeral, + (int)(sizeof(suite_PSK_ephemeral) + /sizeof(*suite_PSK_ephemeral))); + if (-1 == nids) return 0; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_RSA_PSK, + (int)(sizeof(suite_RSA_PSK)/sizeof(*suite_RSA_PSK))); + if (-1 == nids) return 0; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_PSK, + (int)(sizeof(suite_PSK)/sizeof(*suite_PSK))); + if (-1 == nids) return 0; + continue; + } + + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("3DES"))) { + crt_profile_default = 1; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_3DES, + (int)(sizeof(suite_3DES)/sizeof(*suite_3DES))); + if (-1 == nids) return 0; + continue; + } + + /* not recommended, but permitted if explicitly requested */ + /* "RC4" is same as openssl "COMPLEMENTOFALL" */ + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("RC4"))) { + crt_profile_default = 1; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_RC4, + (int)(sizeof(suite_RC4)/sizeof(*suite_RC4))); + if (-1 == nids) return 0; + continue; + } + + /* not recommended, but permitted if explicitly requested */ + if (buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("NULL")) + || buffer_eq_icase_ss(n, nlen, CONST_STR_LEN("eNULL"))) { + crt_profile_default = 1; + nids = mod_mbedtls_ssl_append_ciphersuite(srv, ids, nids, idsz, + suite_null, + (int)(sizeof(suite_null)/sizeof(*suite_null))); + if (-1 == nids) return 0; + continue; + } + + if (*e != ':' && *e != '\0') { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: error: missing support for cipher list: %.*s", + (int)len, p); + rc = 0; + continue; + } + } + if (0 == rc) return 0; + } + + if (-1 == nids) { + /* Do not set a default if ssl.cipher-list was set (and we are + * are processing ssl.openssl.ssl-conf-cmd, not ssl.cipher-list) */ + if (cipherstring != s->ssl_cipher_list + && !buffer_string_is_empty(s->ssl_cipher_list)) + return 1; + + /* obtain default ciphersuite list and filter out RC4, weak, and NULL */ + nids = + mod_mbedtls_ssl_DEFAULT_ciphersuite(srv, ids, nids, + sizeof(ids)/sizeof(*ids)); + if (-1 == nids) return 0; + } + + ids[++nids] = 0; /* terminate list */ + ++nids; + + if (!crt_profile_default) + mbedtls_ssl_conf_cert_profile(s->ssl_ctx, + &mbedtls_x509_crt_profile_next); + + /* ciphersuites list must be persistent for lifetime of mbedtls_ssl_config*/ + s->ciphersuites = malloc(nids * sizeof(int)); + force_assert(s->ciphersuites); + memcpy(s->ciphersuites, ids, nids * sizeof(int)); + + mbedtls_ssl_conf_ciphersuites(s->ssl_ctx, s->ciphersuites); + return 1; +} + + +static int +mod_mbedtls_ssl_append_curve (server *srv, mbedtls_ecp_group_id *ids, int nids, int idsz, const mbedtls_ecp_group_id id) +{ + if (1 >= idsz - (nids + 1)) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: error: too many curves during list expand"); + return -1; + } + + ids[++nids] = id; + + return nids; +} + + +static int +mod_mbedtls_ssl_conf_curves(server *srv, plugin_config_socket *s, const buffer *curvelist) +{ + mbedtls_ecp_group_id ids[512]; + int nids = -1; + const int idsz = (int)(sizeof(ids)/sizeof(*ids)-1); + const mbedtls_ecp_curve_info * const curve_info = mbedtls_ecp_curve_list(); + + const buffer * const b = curvelist; + for (const char *n, *e = b->ptr-1; e && (e = strchr((n = e+1),':')); ) { + size_t len = e ? (size_t)(e - n) : strlen(n); + /* similar to mbedtls_ecp_curve_info_from_name() */ + const mbedtls_ecp_curve_info *info; + for (info = curve_info; info->grp_id != MBEDTLS_ECP_DP_NONE; ++info) { + if (0 == strncmp(info->name, n, len) && info->name[len] == '\0') + break; + } + if (info->grp_id == MBEDTLS_ECP_DP_NONE) { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: unrecognized curve: %.*s; ignored", (int)len, n); + continue; + } + + nids = mod_mbedtls_ssl_append_curve(srv, ids, nids, idsz, info->grp_id); + if (-1 == nids) return 0; + } + + /* XXX: mod_openssl configures "prime256v1" if curve list not specified, + * but mbedtls provides a list of supported curves if not explicitly set */ + if (-1 == nids) return 1; /* empty list; no-op */ + + ids[++nids] = MBEDTLS_ECP_DP_NONE; /* terminate list */ + ++nids; + + /* curves list must be persistent for lifetime of mbedtls_ssl_config */ + s->curves = malloc(nids * sizeof(mbedtls_ecp_group_id)); + force_assert(s->curves); + memcpy(s->curves, ids, nids * sizeof(mbedtls_ecp_group_id)); + + mbedtls_ssl_conf_curves(s->ssl_ctx, s->curves); + return 1; +} + + +static void +mod_mbedtls_ssl_conf_proto (server *srv, plugin_config_socket *s, const buffer *b, int max) +{ + int v = MBEDTLS_SSL_MINOR_VERSION_3; /* default: TLS v1.2 */ + if (NULL == b) /* default: min TLSv1.2, max TLSv1.2 */ + v = max ? MBEDTLS_SSL_MINOR_VERSION_3 : MBEDTLS_SSL_MINOR_VERSION_3; + else if (buffer_eq_icase_slen(b, CONST_STR_LEN("None"))) /*"disable" limit*/ + v = max + ? MBEDTLS_SSL_MINOR_VERSION_3 /* TLS v1.2 */ + : s->ssl_use_sslv3 + ? MBEDTLS_SSL_MINOR_VERSION_0 /* SSL v3.0 */ + : MBEDTLS_SSL_MINOR_VERSION_1; /* TLS v1.0 */ + else if (buffer_eq_icase_slen(b, CONST_STR_LEN("SSLv3"))) + v = MBEDTLS_SSL_MINOR_VERSION_0; /* SSL v3.0 */ + else if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.0"))) + v = MBEDTLS_SSL_MINOR_VERSION_1; /* TLS v1.0 */ + else if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.1"))) + v = MBEDTLS_SSL_MINOR_VERSION_2; /* TLS v1.1 */ + else if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.2"))) + v = MBEDTLS_SSL_MINOR_VERSION_3; /* TLS v1.2 */ + else { + if (buffer_eq_icase_slen(b, CONST_STR_LEN("TLSv1.3"))) + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: ssl.openssl.ssl-conf-cmd %s TLSv1.3 not supported " + "by mod_mbedtls; using TLSv1.2", + max ? "MaxProtocol" : "MinProtocol"); + 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__, + "MTLS: ssl.openssl.ssl-conf-cmd %s %s ignored", + max ? "MaxProtocol" : "MinProtocol", b->ptr); + return; + } + else { + log_error(srv->errh, __FILE__, __LINE__, + "MTLS: ssl.openssl.ssl-conf-cmd %s %s invalid; ignored", + max ? "MaxProtocol" : "MinProtocol", b->ptr); + return; + } + } + + max + ? mbedtls_ssl_conf_max_version(s->ssl_ctx,MBEDTLS_SSL_MAJOR_VERSION_3,v) + : mbedtls_ssl_conf_min_version(s->ssl_ctx,MBEDTLS_SSL_MAJOR_VERSION_3,v); +} diff --git a/src/mod_secdownload.c b/src/mod_secdownload.c index ba816827..4b594e28 100644 --- a/src/mod_secdownload.c +++ b/src/mod_secdownload.c @@ -15,6 +15,8 @@ #ifdef USE_LIB_CRYPTO #if defined(USE_NETTLE_CRYPTO) #include <nettle/hmac.h> +#elif defined(USE_MBEDTLS_CRYPTO) +#include <mbedtls/md.h> #elif defined(USE_OPENSSL_CRYPTO) #include <openssl/evp.h> #include <openssl/hmac.h> @@ -180,8 +182,8 @@ static int secdl_verify_mac(plugin_config *config, const char* protected_path, c return const_time_memeq((char *)HA1, (char *)md5bin, sizeof(md5bin)); } + #ifdef USE_LIB_CRYPTO case SECDL_HMAC_SHA1: -#ifdef USE_LIB_CRYPTO { unsigned char digest[20]; char base64_digest[27]; @@ -191,6 +193,18 @@ static int secdl_verify_mac(plugin_config *config, const char* protected_path, c hmac_sha1_set_key(&ctx, buffer_string_length(config->secret), (const uint8_t *)config->secret->ptr); hmac_sha1_update(&ctx, strlen(protected_path), (const uint8_t *)protected_path); hmac_sha1_digest(&ctx, sizeof(digest), (uint8_t *)digest); + #elif defined(USE_MBEDTLS_CRYPTO) && defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA1_C) + int rc = + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), + (const unsigned char *)config->secret->ptr, + buffer_string_length(config->secret), + (const unsigned char *)protected_path, + strlen(protected_path), digest); + if (0 != rc) { + log_error(errh, __FILE__, __LINE__, + "hmac-sha1: HMAC() failed"); + return 0; + } #elif defined(USE_OPENSSL_CRYPTO) if (NULL == HMAC( EVP_sha1(), @@ -209,10 +223,8 @@ static int secdl_verify_mac(plugin_config *config, const char* protected_path, c return (27 == maclen) && const_time_memeq(mac, base64_digest, 27); } -#endif break; case SECDL_HMAC_SHA256: -#ifdef USE_LIB_CRYPTO { unsigned char digest[32]; char base64_digest[43]; @@ -222,6 +234,18 @@ static int secdl_verify_mac(plugin_config *config, const char* protected_path, c hmac_sha256_set_key(&ctx, buffer_string_length(config->secret), (const uint8_t *)config->secret->ptr); hmac_sha256_update(&ctx, strlen(protected_path), (const uint8_t *)protected_path); hmac_sha256_digest(&ctx, sizeof(digest), (uint8_t *)digest); + #elif defined(USE_MBEDTLS_CRYPTO) && defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA256_C) + int rc = + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), + (const unsigned char *)config->secret->ptr, + buffer_string_length(config->secret), + (const unsigned char *)protected_path, + strlen(protected_path), digest); + if (0 != rc) { + log_error(errh, __FILE__, __LINE__, + "hmac-sha256: HMAC() failed"); + return 0; + } #elif defined(USE_OPENSSL_CRYPTO) if (NULL == HMAC( EVP_sha256(), @@ -240,7 +264,9 @@ static int secdl_verify_mac(plugin_config *config, const char* protected_path, c return (43 == maclen) && const_time_memeq(mac, base64_digest, 43); } -#endif + break; + #endif + default: break; } diff --git a/src/network.c b/src/network.c index 05d1f074..432a0011 100644 --- a/src/network.c +++ b/src/network.c @@ -142,6 +142,7 @@ typedef struct { /* global or per-socket config; not patched per connection */ int listen_backlog; unsigned char ssl_enabled; + unsigned char mbedtls_enabled; /* TODO: more integration needed ... */ unsigned char use_ipv6; unsigned char set_v6only; /* set_v6only is only a temporary option */ unsigned char defer_accept; @@ -264,7 +265,7 @@ static int network_server_init(server *srv, network_socket_config *s, buffer *ho memcpy(&srv_socket->addr, &addr, addr_len); srv_socket->fd = -1; srv_socket->sidx = sidx; - srv_socket->is_ssl = s->ssl_enabled; + srv_socket->is_ssl = (s->ssl_enabled || s->mbedtls_enabled); srv_socket->srv = srv; srv_socket->srv_token = buffer_init_buffer(host_token); @@ -383,7 +384,7 @@ static int network_server_init(server *srv, network_socket_config *s, buffer *ho return -1; } - if (s->ssl_enabled) { + if (s->ssl_enabled || s->mbedtls_enabled) { #ifdef TCP_DEFER_ACCEPT } else if (s->defer_accept) { int v = s->defer_accept; @@ -514,6 +515,11 @@ int network_init(server *srv, int stdin_fd) { ,{ CONST_STR_LEN("server.set-v6only"), T_CONFIG_BOOL, T_CONFIG_SCOPE_CONNECTION } + #if 0 /* TODO: more integration needed ... */ + ,{ CONST_STR_LEN("mbedtls.engine"), + T_CONFIG_BOOL, + T_CONFIG_SCOPE_CONNECTION } + #endif ,{ NULL, 0, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } @@ -1,6 +1,7 @@ #include "first.h" #include "rand.h" +#include "buffer.h" #include "fdevent.h" #include "safe_memclear.h" @@ -15,11 +16,17 @@ #include "sys-crypto.h" /* USE_LIB_CRYPTO */ #ifdef USE_NETTLE_CRYPTO +#undef USE_MBEDTLS_CRYPTO #undef USE_OPENSSL_CRYPTO #include <nettle/knuth-lfib.h> #include <nettle/arcfour.h> #include <nettle/yarrow.h> #endif +#ifdef USE_MBEDTLS_CRYPTO +#undef USE_OPENSSL_CRYPTO +#include <mbedtls/ctr_drbg.h> +#include <mbedtls/entropy.h> +#endif #ifdef USE_OPENSSL_CRYPTO #include <openssl/opensslv.h> /* OPENSSL_VERSION_NUMBER */ #include <openssl/rand.h> @@ -132,6 +139,14 @@ static int li_rand_device_bytes (unsigned char *buf, int num) static int li_rand_inited; static unsigned short xsubi[3]; +#ifdef USE_MBEDTLS_CRYPTO +#ifdef MBEDTLS_ENTROPY_C +static mbedtls_entropy_context entropy; +#ifdef MBEDTLS_CTR_DRBG_C +static mbedtls_ctr_drbg_context ctr_drbg; +#endif +#endif +#endif #ifdef USE_NETTLE_CRYPTO static struct knuth_lfib_ctx knuth_lfib_ctx; static struct arcfour_ctx arcfour_ctx; @@ -206,10 +221,33 @@ static void li_rand_init (void) RAND_poll(); RAND_seed(xsubi, (int)sizeof(xsubi)); #endif + #ifdef USE_MBEDTLS_CRYPTO + #ifdef MBEDTLS_ENTROPY_C + mbedtls_entropy_init(&entropy); + #ifdef MBEDTLS_CTR_DRBG_C + mbedtls_ctr_drbg_init(&ctr_drbg); + int rc = + mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (unsigned char *)xsubi, sizeof(xsubi)); + if (0 != rc) /*(not expecting built-in entropy function to fail)*/ + log_failed_assert(__FILE__, __LINE__, "mbedtls_ctr_drbg_seed() failed"); + #endif + #endif + #endif } void li_rand_reseed (void) { + #ifdef USE_MBEDTLS_CRYPTO + if (li_rand_inited) { + #ifdef MBEDTLS_ENTROPY_C + #ifdef MBEDTLS_CTR_DRBG_C + mbedtls_ctr_drbg_free(&ctr_drbg); + #endif + mbedtls_entropy_free(&entropy); + #endif + } + #endif if (li_rand_inited) li_rand_init(); } @@ -229,6 +267,13 @@ int li_rand_pseudo (void) nettle_arcfour_crypt(&arcfour_ctx, sizeof(i), (uint8_t *)&i, (uint8_t *)&i); if (i) return i; /*(cond to avoid compiler warning for code after return)*/ #endif + #ifdef USE_MBEDTLS_CRYPTO + #ifdef MBEDTLS_CTR_DRBG_C + int i; + if (0 == mbedtls_ctr_drbg_random(&ctr_drbg, (unsigned char *)&i, sizeof(i))) + return i; + #endif + #endif #ifdef HAVE_ARC4RANDOM_BUF return (int)arc4random(); #elif defined(HAVE_SRANDOM) @@ -246,6 +291,11 @@ int li_rand_pseudo (void) void li_rand_pseudo_bytes (unsigned char *buf, int num) { + #ifdef USE_MBEDTLS_CRYPTO + #ifdef MBEDTLS_CTR_DRBG_C + if (0 == mbedtls_ctr_drbg_random(&ctr_drbg, buf, (size_t)num)) return; + #endif + #endif for (int i = 0; i < num; ++i) buf[i] = li_rand_pseudo() & 0xFF; } @@ -273,6 +323,15 @@ int li_rand_bytes (unsigned char *buf, int num) return rc; } #endif + #ifdef USE_MBEDTLS_CRYPTO + #ifdef MBEDTLS_ENTROPY_C + /*(each call <= MBEDTLS_ENTROPY_BLOCK_SIZE; could implement loop here)*/ + if (num <= MBEDTLS_ENTROPY_BLOCK_SIZE + && 0 == mbedtls_entropy_func(&entropy, buf, (size_t)num)) { + return 1; + } + #endif + #endif if (1 == li_rand_device_bytes(buf, num)) { return 1; } @@ -291,5 +350,13 @@ void li_rand_cleanup (void) RAND_cleanup(); #endif #endif + #ifdef USE_MBEDTLS_CRYPTO + #ifdef MBEDTLS_ENTROPY_C + #ifdef MBEDTLS_CTR_DRBG_C + mbedtls_ctr_drbg_free(&ctr_drbg); + #endif + mbedtls_entropy_free(&entropy); + #endif + #endif safe_memclear(xsubi, sizeof(xsubi)); } diff --git a/src/server.c b/src/server.c index 410f5b2f..0c74a1c7 100644 --- a/src/server.c +++ b/src/server.c @@ -71,8 +71,8 @@ static const buffer default_server_tag = { CONST_STR_LEN(PACKAGE_DESC), 0 }; #endif #include "sys-crypto.h" -#ifdef USE_OPENSSL_CRYPTO -#define USE_SSL +#if defined(USE_OPENSSL_CRYPTO) \ + || defined(USE_MBEDTLS_CRYPTO) #define TEXT_SSL " (ssl)" #else #define TEXT_SSL @@ -430,10 +430,15 @@ static void show_features (void) { #else "\t- crypt support\n" #endif -#ifdef USE_SSL - "\t+ SSL support\n" +#ifdef USE_OPENSSL_CRYPTO + "\t+ OpenSSL support\n" +#else + "\t- OpenSSL support\n" +#endif +#ifdef USE_MBEDTLS_CRYPTO + "\t+ mbedTLS support\n" #else - "\t- SSL support\n" + "\t- mbedTLS support\n" #endif #ifdef HAVE_LIBPCRE "\t+ PCRE support\n" diff --git a/src/sys-crypto.h b/src/sys-crypto.h index 5d15533c..ec321cc4 100644 --- a/src/sys-crypto.h +++ b/src/sys-crypto.h @@ -18,6 +18,12 @@ #include <wolfssl/options.h> #endif +#ifdef HAVE_LIBMBEDCRYPTO +#define USE_LIB_CRYPTO +#define USE_MBEDTLS_CRYPTO +#include <mbedtls/config.h> +#endif + #ifdef HAVE_NETTLE_NETTLE_TYPES_H #define USE_LIB_CRYPTO #define USE_NETTLE_CRYPTO |