From f8b449e5e56db20b6d1ff457483bbaac7613deda Mon Sep 17 00:00:00 2001
From: Fabian Mauchle <fabian.mauchle@switch.ch>
Date: Tue, 11 Aug 2020 11:53:08 +0200
Subject: [PATCH] add user configurable tls versions

---
 ChangeLog             |  1 +
 radsecproxy.conf.5.in | 20 ++++++++++++
 tlscommon.c           | 75 +++++++++++++++++++++++++++++++++++++++++++
 tlscommon.h           |  4 +++
 4 files changed, 100 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index b0bc1a3..df04a89 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,7 @@ unreleased chanes
 	- Accept multiple source* configs for IPv4/v6
 	- Specify source per server
 	- User configurable cipher-list and ciphersuites
+	- User configurable TLS versions
 
 	Misc:
 	- Move radsecproxy manpage to section 8
diff --git a/radsecproxy.conf.5.in b/radsecproxy.conf.5.in
index 1caef01..a2ce537 100644
--- a/radsecproxy.conf.5.in
+++ b/radsecproxy.conf.5.in
@@ -811,6 +811,26 @@ Specify the \fIciphersuites\fR to be used for TLS1.3. See
 .BR openssl-ciphers (1).
 .br
 Note this requires openssl 1.1.1
+.RE
+
+.BR "TlsVersion " (
+.IR version " | " minversion : maxversion " )"
+.br
+.BR "DtlsVersion " (
+.IR version " | " minversion : maxversion " )"
+.RS
+Specify the TLS/DTLS protocol \fIversion\fR to be used.
+.br
+Specify the range of allowed protocol versions between \fIminversion\fR and 
+\fImaxversion\fR (inclusive). If either is left out, any version up to, or 
+starting from this version is allowed. E.g. "TLS1_2:" will allow TLSv1.2 or later.
+.br
+Currently supported values are
+.BR SSL3 , TLS1 , TLS1_1 , TLS1_2 , TLS1_3
+for TLS and
+.BR DTLS1 , DTLS1_1
+for DTLS.
+
 
 
 .SH "REWRITE BLOCK"
diff --git a/tlscommon.c b/tlscommon.c
index 1cb7b42..7bec744 100644
--- a/tlscommon.c
+++ b/tlscommon.c
@@ -349,6 +349,8 @@ static SSL_CTX *tlscreatectx(uint8_t type, struct tls *conf) {
 #if OPENSSL_VERSION_NUMBER >= 0x10100000
         /* TLS_method() was introduced in OpenSSL 1.1.0. */
         ctx = SSL_CTX_new(TLS_method());
+        SSL_CTX_set_min_proto_version(ctx, conf->tlsminversion);
+        SSL_CTX_set_max_proto_version(ctx, conf->tlsmaxversion);
 #else
         /* No TLS_method(), use SSLv23_method() and disable SSLv2 and SSLv3. */
         ctx = SSL_CTX_new(SSLv23_method());
@@ -364,6 +366,10 @@ static SSL_CTX *tlscreatectx(uint8_t type, struct tls *conf) {
 #if OPENSSL_VERSION_NUMBER >= 0x10002000
         /* DTLS_method() seems to have been introduced in OpenSSL 1.0.2. */
         ctx = SSL_CTX_new(DTLS_method());
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+        SSL_CTX_set_min_proto_version(ctx, conf->dtlsminversion);
+        SSL_CTX_set_max_proto_version(ctx, conf->dtlsmaxversion);
+#endif
 #else
         ctx = SSL_CTX_new(DTLSv1_method());
 #endif
@@ -769,9 +775,54 @@ int verifyconfcert(X509 *cert, struct clsrvconf *conf) {
     return ok;
 }
 
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+static int parse_tls_version(const char* version) {
+    if (!strcasecmp("SSL3", version)) {
+        return SSL3_VERSION;
+    } else if (!strcasecmp("TLS1", version)) {
+        return TLS1_VERSION;
+    } else if (!strcasecmp("TLS1_1", version)) {
+        return TLS1_1_VERSION;
+    } else if (!strcasecmp("TLS1_2", version)) {
+        return TLS1_2_VERSION;
+#if OPENSSL_VERSION_NUMBER >= 0x10101000
+    } else if (!strcasecmp("TLS1_3", version)) {
+        return TLS1_3_VERSION;
+#endif
+    } else if (!strcasecmp("DTLS1", version)) {
+        return DTLS1_VERSION;
+    } else if (!strcasecmp("DTLS1_2", version)) {
+        return DTLS1_2_VERSION;
+    } else if (!strcasecmp("", version)) {
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+static int conf_tls_version(const char *version, int *min, int *max) {
+    char *ver, *s, *smin, *smax;
+    ver = stringcopy(version, strlen(version));
+    s = strchr(ver, ':');
+    if (!s) {
+        smin = smax = ver;
+    } else {
+        *s =  '\0';
+        smin = ver;
+        smax = s+1;
+    }
+    *min = parse_tls_version(smin);
+    *max = parse_tls_version(smax);
+    free(ver);
+    return *min >=0 && *max >=0 && (*max == 0 || *min <= *max);
+}
+#endif
+
 int conftls_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val) {
     struct tls *conf;
     long int expiry = LONG_MIN;
+    char *tlsversion = NULL;
+    char *dtlsversion = NULL;
 
     debug(DBG_DBG, "conftls_cb called for %s", block);
 
@@ -793,6 +844,8 @@ int conftls_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *v
 			  "PolicyOID", CONF_MSTR, &conf->policyoids,
               "CipherList", CONF_STR, &conf->cipherlist,
               "CipherSuites", CONF_STR, &conf->ciphersuites,
+              "TlsVersion", CONF_STR, &tlsversion,
+              "DtlsVersion", CONF_STR, &dtlsversion,
 			  NULL
 	    )) {
 	debug(DBG_ERR, "conftls_cb: configuration error in block %s", val);
@@ -813,6 +866,26 @@ int conftls_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *v
 	}
 	conf->cacheexpiry = expiry;
     }
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+    conf->tlsminversion = TLS1_1_VERSION;
+    if (tlsversion) {
+        if(!conf_tls_version(tlsversion, &conf->tlsminversion, &conf->tlsmaxversion)) {
+            debug(DBG_ERR, "error in block %s, invalid TlsVersion %s", val, tlsversion);
+            goto errexit;
+        }
+        free (tlsversion);
+    }
+    if (dtlsversion) {
+        if(!conf_tls_version(dtlsversion, &conf->dtlsminversion, &conf->dtlsmaxversion)) {
+            debug(DBG_ERR, "error in block %s, invalid DtlsVersion %s", val, dtlsversion);
+            goto errexit;
+        }
+        free (dtlsversion);
+    }
+#else
+        debug(DBG_ERR, "error in block %s, setting tls/dtls version requires openssl 1.1.0 or later", val);
+        goto errexit;
+#endif
 
     conf->name = stringcopy(val, 0);
     if (!conf->name) {
@@ -839,6 +912,8 @@ int conftls_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *v
     free(conf->certkeyfile);
     free(conf->certkeypwd);
     freegconfmstr(conf->policyoids);
+    free(tlsversion);
+    free(dtlsversion);
     free(conf);
     return 0;
 }
diff --git a/tlscommon.h b/tlscommon.h
index 721d5be..5e0562d 100644
--- a/tlscommon.h
+++ b/tlscommon.h
@@ -21,6 +21,10 @@ struct tls {
     char *cipherlist;
     char *ciphersuites;
     uint32_t cacheexpiry;
+    int tlsminversion;
+    int tlsmaxversion;
+    int dtlsminversion;
+    int dtlsmaxversion;
     uint32_t tlsexpiry;
     uint32_t dtlsexpiry;
     X509_VERIFY_PARAM *vpm;