Mercurial > dovecot > core-2.2
changeset 22860:ad9c924ec91f
lib-ssl-iostream: Add ssl_protocols_to_min_protocol()
This detects minimum SSL protocol version from the ssl_protocols
setting.
author | Martti Rannanjärvi <martti.rannanjarvi@dovecot.fi> |
---|---|
date | Sat, 11 Nov 2017 04:28:57 +0200 |
parents | c7aa25186973 |
children | 898ef4d4ee48 |
files | src/lib-ssl-iostream/Makefile.am src/lib-ssl-iostream/iostream-openssl-common.c src/lib-ssl-iostream/iostream-openssl.h src/lib-ssl-iostream/test-ssl-iostream.c |
diffstat | 4 files changed, 158 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-ssl-iostream/Makefile.am Thu Nov 30 11:15:50 2017 +0200 +++ b/src/lib-ssl-iostream/Makefile.am Sat Nov 11 04:28:57 2017 +0200 @@ -20,6 +20,28 @@ iostream-openssl-params.c \ istream-openssl.c \ ostream-openssl.c + +test_programs = test-ssl-iostream +noinst_PROGRAMS = $(test_programs) + +check-local: + for bin in $(test_programs); do \ + if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ + done + +LIBDOVECOT_TEST_DEPS = \ + libssl_iostream_openssl.la \ + libssl_iostream.la \ + ../lib-test/libtest.la \ + ../lib/liblib.la +LIBDOVECOT_TEST = \ + $(LIBDOVECOT_TEST_DEPS) \ + $(MODULE_LIBS) + +test_ssl_iostream_LDADD = $(LIBDOVECOT_TEST) +test_ssl_iostream_DEPENDENCIES = $(LIBDOVECOT_TEST_DEPS) +test_ssl_iostream_CFLAGS = $(AM_CPPFLAGS) +test_ssl_iostream_SOURCES = test-ssl-iostream.c endif libssl_iostream_la_SOURCES = \
--- a/src/lib-ssl-iostream/iostream-openssl-common.c Thu Nov 30 11:15:50 2017 +0200 +++ b/src/lib-ssl-iostream/iostream-openssl-common.c Sat Nov 11 04:28:57 2017 +0200 @@ -18,6 +18,67 @@ DOVECOT_SSL_PROTO_ALL = 0x1f }; +#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION +static const struct { + const char *name; + int version; +} protocol_versions[] = { + { SSL_TXT_SSLV3, SSL3_VERSION }, + { SSL_TXT_TLSV1, TLS1_VERSION }, + { SSL_TXT_TLSV1_1, TLS1_1_VERSION }, + { SSL_TXT_TLSV1_2, TLS1_2_VERSION }, +}; +int ssl_protocols_to_min_protocol(const char *ssl_protocols, + int *min_protocol_r, const char **error_r) +{ + /* Array where -1 = disable, 0 = not found, 1 = enable */ + int protos[N_ELEMENTS(protocol_versions)]; + memset(protos, 0, sizeof(protos)); + bool explicit_enable = FALSE; + + const char *const *tmp = t_strsplit_spaces(ssl_protocols, ", "); + for (; *tmp != NULL; tmp++) { + const char *p = *tmp; + bool enable = TRUE; + if (p[0] == '!') { + enable = FALSE; + ++p; + } + for (unsigned i = 0; i < N_ELEMENTS(protocol_versions); i++) { + if (strcmp(p, protocol_versions[i].name) == 0) { + if (enable) { + protos[i] = 1; + explicit_enable = TRUE; + } else { + protos[i] = -1; + } + goto found; + } + } + *error_r = t_strdup_printf("Unrecognized protocol '%s'", p); + return -1; + + found:; + } + + unsigned min = N_ELEMENTS(protocol_versions); + for (unsigned i = 0; i < N_ELEMENTS(protocol_versions); i++) { + if (explicit_enable) { + if (protos[i] > 0) + min = I_MIN(min, i); + } else if (protos[i] == 0) + min = I_MIN(min, i); + } + if (min == N_ELEMENTS(protocol_versions)) { + *error_r = "All protocols disabled"; + return -1; + } + + *min_protocol_r = protocol_versions[min].version; + return 0; +} +#endif /* HAVE_SSL_CTX_SET_MIN_PROTO_VERSION */ + int openssl_get_protocol_options(const char *protocols) { const char *const *tmp;
--- a/src/lib-ssl-iostream/iostream-openssl.h Thu Nov 30 11:15:50 2017 +0200 +++ b/src/lib-ssl-iostream/iostream-openssl.h Sat Nov 11 04:28:57 2017 +0200 @@ -80,6 +80,18 @@ #define OPENSSL_ALL_PROTOCOL_OPTIONS \ (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1) +#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION +/* min_protocol_r is the version int for SSL_CTX_set_min_proto_version(). + Return 0 on success, and -1 on failure. + + If ssl_protocols only disables protocols like "!SSLv3 !TLSv1", then all the + remaining protocols are considered enabled. If it enables some protocols + like "TLSv1.1 TLSv1.2", then only the explicitly enabled protocols are + considered enabled. */ +int ssl_protocols_to_min_protocol(const char *ssl_protocols, + int *min_protocol_r, const char **error_r); +#endif + /* Sync plain_input/plain_output streams with BIOs. Returns TRUE if at least one byte was read/written. */ bool openssl_iostream_bio_sync(struct ssl_iostream *ssl_io);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-ssl-iostream/test-ssl-iostream.c Sat Nov 11 04:28:57 2017 +0200 @@ -0,0 +1,63 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "test-common.h" +#include "iostream-openssl.h" + +#include <stdio.h> + +#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION + +struct test { + /* ssl_protocols input */ + const char *s; + /* expected output */ + int min; + int ret; +}; + +static const struct test tests[] = { + { "!TLSv1 !TLSv1.2", SSL3_VERSION, 0 }, + { "!SSLv3", TLS1_VERSION, 0 }, + { "SSLv3", SSL3_VERSION, 0 }, + { "!SSLv3 !TLSv1 !TLSv1.2", TLS1_1_VERSION, 0 }, + { "!SSLv3 !TLSv1 !TLSv1.1 !TLSv1.2", 0, -1}, + { "TLSv1.1 TLSv1.2", TLS1_1_VERSION, 0 }, + { "TLSv1.1", TLS1_1_VERSION, 0 }, + { "TLSv1.1 !SSLv3", TLS1_1_VERSION, 0 }, + { "TLSv1.2 !TLSv1.1", TLS1_2_VERSION, 0 }, +}; + +static +void test_ssl_protocols_to_min_protocol(void) +{ + test_begin("test_ssl_protocols_to_min_protocol"); + for (unsigned i = 0; i < N_ELEMENTS(tests); ++i) { + const struct test *t = &tests[i]; + const char *error; + int min, ret; + ret = ssl_protocols_to_min_protocol(t->s, &min, &error); + if (ret >= 0 && t->min != min) + i_debug("%s (exp,actual): min(%d,%d) ret(%d,%d)", + t->s, t->min, min, t->ret, ret); + test_assert_idx(t->ret == ret, i); + if (ret < 0) + continue; + test_assert_idx(t->min == min, i); + } + test_end(); +} + +int main(void) { + static void (*test_functions[])(void) = { + test_ssl_protocols_to_min_protocol, + NULL, + }; + return test_run(test_functions); +} + +#else /* HAVE_SSL_CTX_SET_MIN_PROTO_VERSION */ +int main(void) { + return 0; +} +#endif /* HAVE_SSL_CTX_SET_MIN_PROTO_VERSION */