changeset 940:83b5c4362c53

Provide SSL/TLS min and max protocol configuration options Allow configuration of a range of SSL protocols independently for accepting incoming connections (listening) and making outgoing connections. The default minimum version is TLS v1.1 which has the effect of disabling SSL v3 and earlier by default. The minimum version accepted by the options is SSL v3 and thus it is not possible to enable SSL v2. The default maximum version is to use OpenSSL's default value. The maximum version accepted by the options is TLS v1.2. The code will need to be updated to support any newer versions of TLS as they become available in OpenSSL. Based on work by Matthias Hunstock. Cc: Matthias Hunstock <matthias.hunstock@tu-ilmenau.de> Signed-off-by: Simon Horman <horms@verge.net.au> --- v3 * Rebase onto tip from v2.1 v2 * Allow max version of "" to be specified as an option, translate this to NULL which means that OpenSSL's default will be used. "" is not allowed for min versions as we wish to enforce an absolute minimum of SSLv3.
author Simon Horman <horms@verge.net.au>
date Wed, 11 May 2016 08:57:22 +0900
parents 2f6a8ca3d5d4
children 440bad5e5ba4
files configure.ac etc/perdition/perdition.conf perdition/options.c perdition/options.h perdition/perdition.8 perdition/ssl.c perdition/ssl.h
diffstat 7 files changed, 452 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Sun Jun 14 14:58:45 2015 +0900
+++ b/configure.ac	Wed May 11 08:57:22 2016 +0900
@@ -211,6 +211,9 @@
 
 if test "$enable_ssl" = "yes"; then
    AC_DEFINE(WITH_SSL_SUPPORT, 1, Compile with SSL/TLS support)
+   AC_CHECK_LIB(ssl, SSL_CTX_set_min_proto_version)
+   AC_CHECK_DECLS([SSL_CTX_set_min_proto_version], [], [], [[#include <openssl/ssl.h>]])
+   AC_CHECK_DECLS([SSL_CTX_set_max_proto_version], [], [], [[#include <openssl/ssl.h>]])
 else
   ssl_lib=""
   ssl_includes=""
--- a/etc/perdition/perdition.conf	Sun Jun 14 14:58:45 2015 +0900
+++ b/etc/perdition/perdition.conf	Wed May 11 08:57:22 2016 +0900
@@ -408,3 +408,30 @@
 # to connect to the server.
 #ssl_no_cn_verify
 
+# ssl_listen_min_proto_version PROTOCOL_VERSION:
+# Minimum permited SSL/TLS protocol version when accepting incoming
+# connections.
+# May not be empty ("").
+# (default "tlsv1")
+#ssl_listen_min_proto_version "tlsv1"
+
+# ssl_outgoing_min_proto_version PROTOCOL_VERSION:
+# Minimum permited SSL/TLS protocol version when making outgoing
+# connections.
+# May not be empty ("").
+# (default "tlsv1")
+#ssl_outgoing_min_proto_version "tlsv1"
+
+# ssl_listen_max_proto_version PROTOCOL_VERSION:
+# Maximum permited SSL/TLS protocol version when accepting incomaxg
+# connections.
+# If empty ("") then openssl's default will be used.
+# (default "")
+#ssl_listen_max_proto_version "tlsv1.2"
+
+# ssl_outgoing_max_proto_version PROTOCOL_VERSION:
+# Maximum permited SSL/TLS protocol version when making outgoing
+# connections.
+# If empty ("") then openssl's default will be used.
+# (default "")
+#ssl_outgoing_max_proto_version "tlsv1.2"
--- a/perdition/options.c	Sun Jun 14 14:58:45 2015 +0900
+++ b/perdition/options.c	Wed May 11 08:57:22 2016 +0900
@@ -34,6 +34,7 @@
 #include "options.h"
 #include "config_file.h"
 #include "perdition_globals.h"
+#include "ssl.h"
 
 #ifdef DMALLOC
 #include <dmalloc.h>
@@ -490,6 +491,14 @@
       TAG_SSL_PASSPHRASE_FD, NULL, NULL},
     {"ssl_passphrase_file",         '\0', POPT_ARG_STRING, NULL,
       TAG_SSL_PASSPHRASE_FILE, NULL, NULL},
+    {"ssl_listen_min_proto_version", '\0', POPT_ARG_STRING, NULL,
+      TAG_SSL_LISTEN_MIN_PROTO_VERSION, NULL, NULL},
+    {"ssl_outgoing_min_proto_version", '\0', POPT_ARG_STRING, NULL,
+      TAG_SSL_OUTGOING_MIN_PROTO_VERSION, NULL, NULL},
+    {"ssl_listen_max_proto_version", '\0', POPT_ARG_STRING, NULL,
+      TAG_SSL_LISTEN_MAX_PROTO_VERSION, NULL, NULL},
+    {"ssl_outgoing_max_proto_version", '\0', POPT_ARG_STRING, NULL,
+      TAG_SSL_OUTGOING_MAX_PROTO_VERSION, NULL, NULL},
     {NULL,                           0,   0,               NULL,
      0, NULL, NULL}
   };
@@ -621,6 +630,14 @@
 		    &i, 0, OPT_NOT_SET);
     opt_p(&(opt.ssl_passphrase_file),DEFAULT_SSL_PASSPHRASE_FILE,
 		    &i, 0, OPT_NOT_SET);
+    opt_p(&(opt.ssl_listen_min_proto_version),
+	  DEFAULT_SSL_LISTEN_MIN_PROTO_VERSION, &i, 0, OPT_NOT_SET);
+    opt_p(&(opt.ssl_outgoing_min_proto_version),
+	  DEFAULT_SSL_OUTGOING_MIN_PROTO_VERSION, &i, 0, OPT_NOT_SET);
+    opt_p(&(opt.ssl_listen_max_proto_version),
+	  DEFAULT_SSL_LISTEN_MAX_PROTO_VERSION, &i, 0, OPT_NOT_SET);
+    opt_p(&(opt.ssl_outgoing_max_proto_version),
+	  DEFAULT_SSL_OUTGOING_MAX_PROTO_VERSION, &i, 0, OPT_NOT_SET);
 #endif /* WITH_SSL_SUPPORT */
   }
 
@@ -1021,6 +1038,38 @@
 	NO_SSL_OPT("ssl_passphrase_file");
 #endif /* WITH_SSL_SUPPORT */
         break;
+      case TAG_SSL_LISTEN_MIN_PROTO_VERSION:
+#ifdef WITH_SSL_SUPPORT
+        opt_p(&(opt.ssl_listen_min_proto_version), optarg, &(opt.ssl_mask),
+			MASK_SSL_LISTEN_MIN_PROTO_VERSION, f);
+#else /* WITH_SSL_SUPPORT */
+	NO_SSL_OPT("ssl_listen_min_proto_version");
+#endif /* WITH_SSL_SUPPORT */
+        break;
+      case TAG_SSL_OUTGOING_MIN_PROTO_VERSION:
+#ifdef WITH_SSL_SUPPORT
+        opt_p(&(opt.ssl_outgoing_min_proto_version), optarg, &(opt.ssl_mask),
+			MASK_SSL_OUTGOING_MIN_PROTO_VERSION, f);
+#else /* WITH_SSL_SUPPORT */
+	NO_SSL_OPT("ssl_outgoing_min_proto_version");
+#endif /* WITH_SSL_SUPPORT */
+        break;
+      case TAG_SSL_LISTEN_MAX_PROTO_VERSION:
+#ifdef WITH_SSL_SUPPORT
+        opt_p(&(opt.ssl_listen_max_proto_version), optarg, &(opt.ssl_mask),
+			MASK_SSL_LISTEN_MAX_PROTO_VERSION, f);
+#else /* WITH_SSL_SUPPORT */
+	NO_SSL_OPT("ssl_listen_max_proto_version");
+#endif /* WITH_SSL_SUPPORT */
+        break;
+      case TAG_SSL_OUTGOING_MAX_PROTO_VERSION:
+#ifdef WITH_SSL_SUPPORT
+        opt_p(&(opt.ssl_outgoing_max_proto_version), optarg, &(opt.ssl_mask),
+			MASK_SSL_OUTGOING_MAX_PROTO_VERSION, f);
+#else /* WITH_SSL_SUPPORT */
+	NO_SSL_OPT("ssl_outgoing_max_proto_version");
+#endif /* WITH_SSL_SUPPORT */
+        break;
       default:
         VANESSA_LOGGER_DEBUG_RAW("Unknown Option");
         break;
@@ -1040,6 +1089,128 @@
     }
   }
 
+  {
+	int min_ver = 0;
+	int max_ver = 0;
+
+	if (opt.ssl_listen_min_proto_version) {
+		VANESSA_LOGGER_DEBUG_RAW("min");
+		min_ver = perdition_parse_ssl_proto_version(
+				opt.ssl_listen_min_proto_version);
+		if (min_ver < 0) {
+			VANESSA_LOGGER_DEBUG_RAW("Unknown ssl_listen_min_proto_version");
+			if (f & OPT_ERR) {
+				usage(-1);
+			} else {
+				poptFreeContext(context);
+				return -1;
+			}
+		}
+	}
+
+	/*
+	 * Translate empty max proto version to NULL which results in
+	 * OpenSSL's defaults being used.
+	 * This differs from min proto version handling where "" is
+	 * not translated and rejected by
+	 * perdition_parse_ssl_proto_version() so that a minimum protocol
+	 * version of at least SSLv3 is enforced.
+	 */
+	if (opt.ssl_listen_max_proto_version &&
+	    *opt.ssl_listen_max_proto_version == '\0') {
+		free(opt.ssl_listen_max_proto_version);
+		opt.ssl_listen_max_proto_version = NULL;
+	}
+
+	if (opt.ssl_listen_max_proto_version) {
+		max_ver = perdition_parse_ssl_proto_version(
+				opt.ssl_listen_max_proto_version);
+		if (max_ver < 0) {
+			VANESSA_LOGGER_DEBUG_RAW("Unknown ssl_listen_max_proto_version");
+			if (f & OPT_ERR) {
+				usage(-1);
+			} else {
+				poptFreeContext(context);
+				return -1;
+			}
+		}
+	}
+
+	if (opt.ssl_listen_min_proto_version &&
+	    opt.ssl_listen_max_proto_version && min_ver > max_ver) {
+		VANESSA_LOGGER_DEBUG_RAW("ssl_listen_min_proto_version is "
+					 "greater than "
+					 "ssl_listen_max_proto_version");
+		if (f&OPT_ERR) {
+			usage(-1);
+		} else {
+			poptFreeContext(context);
+			return -1;
+		}
+	}
+
+  }
+
+  {
+	int min_ver = 0;
+	int max_ver = 0;
+
+	if (opt.ssl_listen_min_proto_version) {
+		min_ver = perdition_parse_ssl_proto_version(
+				opt.ssl_listen_min_proto_version);
+		if (min_ver < 0) {
+			VANESSA_LOGGER_DEBUG_RAW("Unknown ssl_outgoing_min_proto_version");
+			if (f & OPT_ERR) {
+				usage(-1);
+			} else {
+				poptFreeContext(context);
+				return -1;
+			}
+		}
+	}
+
+	/*
+	 * Translate empty max proto version to NULL which results in
+	 * OpenSSL's defaults being used.
+	 * This differs from min proto version handling where "" is
+	 * not translated and rejected by
+	 * perdition_parse_ssl_proto_version() so that a minimum protocol
+	 * version of at least SSLv3 is enforced.
+	 */
+	if (opt.ssl_outgoing_max_proto_version &&
+	    *opt.ssl_outgoing_max_proto_version == '\0') {
+		free(opt.ssl_outgoing_max_proto_version);
+		opt.ssl_outgoing_max_proto_version = NULL;
+	}
+
+	if (opt.ssl_outgoing_max_proto_version) {
+		max_ver = perdition_parse_ssl_proto_version(
+				opt.ssl_outgoing_max_proto_version);
+		if (max_ver < 0) {
+			VANESSA_LOGGER_DEBUG_RAW("Unknown ssl_outgoing_max_proto_version");
+			if (f & OPT_ERR) {
+				usage(-1);
+			} else {
+				poptFreeContext(context);
+				return -1;
+			}
+		}
+	}
+
+	if (opt.ssl_listen_min_proto_version &&
+	    opt.ssl_outgoing_max_proto_version && min_ver > max_ver) {
+		VANESSA_LOGGER_DEBUG_RAW("ssl_outgoing_min_proto_version is "
+					 "greater than "
+					 "ssl_outgoing_max_proto_version");
+		if (f & OPT_ERR) {
+			usage(-1);
+		} else {
+			poptFreeContext(context);
+			return -1;
+		}
+	}
+  }
+
   if (c < -1) {
     VANESSA_LOGGER_DEBUG_UNSAFE( "%s: %s",
       poptBadOption(context, POPT_BADOPTION_NOALIAS), poptStrerror(c));
@@ -1411,6 +1582,10 @@
 		 "ssl_no_cn_verify=\"%s\" "
 		 "ssl_passphrase_fd=%d, "
 		 "ssl_passphrase_file=\"%s\", "
+		 "ssl_listen_min_proto_version=\"%s\", "
+		 "ssl_outgoing_min_proto_version=\"%s\", "
+		 "ssl_listen_max_proto_version=\"%s\", "
+		 "ssl_outgoing_max_proto_version=\"%s\", "
 		 "(ssl_mask=0x%08x) ",
 		 ssl_mode,
 		 OPT_STR(opt.ssl_ca_file),
@@ -1429,6 +1604,10 @@
 		 BIN_OPT_STR(opt.ssl_no_cn_verify),
 		 opt.ssl_passphrase_fd,
 		 OPT_STR(opt.ssl_passphrase_file),
+		 OPT_STR(opt.ssl_listen_min_proto_version),
+		 OPT_STR(opt.ssl_outgoing_min_proto_version),
+		 OPT_STR(opt.ssl_listen_max_proto_version),
+		 OPT_STR(opt.ssl_outgoing_max_proto_version),
 		 opt.ssl_mask);
 	out[MAX_LINE_LENGTH - 1] = '\0';
 
@@ -1727,6 +1906,26 @@
     " --ssl_passphrase_file FILENAME:\n"
     "    File from with the passphrase for the certificate is read.\n"
     "    (default \"%s\")\n"
+    " --ssl_listen_min_proto_version PROTOCOL_VERSION:\n"
+    "    Minimum permited SSL/TLS protocol version when accepting incomming\n"
+    "    connections.\n"
+    "    May not be empty (\"\").\n"
+    "    (default \"%s\")\n"
+    " --ssl_outgoing_min_proto_version PROTOCOL_VERSION:\n"
+    "    Minimum permited SSL/TLS protocol version when making outgoing\n"
+    "    connections.\n"
+    "    May not be empty (\"\").\n"
+    "    (default \"%s\")\n"
+    " --ssl_listen_max_proto_version PROTOCOL_VERSION:\n"
+    "    Maximum permited SSL/TLS protocol version when accepting incoming\n"
+    "    connections.\n"
+    "    If empty (\"\") then openssl's default will be used.\n"
+    "    (default \"%s\")\n"
+    " --ssl_outgoing_max_proto_version PROTOCOL_VERSION:\n"
+    "    Maximum permited SSL/TLS protocol version when making outgoing\n"
+    "    connections.\n"
+    "    If empty (\"\") then openssl's default will be used.\n"
+    "    (default \"%s\")\n"
 #endif /* WITH_SSL_SUPPORT */
     "\n"
     " Notes: Default value for binary flags is off.\n"
@@ -1772,7 +1971,11 @@
     OPT_STR(DEFAULT_SSL_LISTEN_CIPHERS),
     OPT_STR(DEFAULT_SSL_OUTGOING_CIPHERS),
     DEFAULT_SSL_PASSPHRASE_FD,
-    OPT_STR(DEFAULT_SSL_PASSPHRASE_FILE)
+    OPT_STR(DEFAULT_SSL_PASSPHRASE_FILE),
+    OPT_STR(DEFAULT_SSL_LISTEN_MIN_PROTO_VERSION),
+    OPT_STR(DEFAULT_SSL_OUTGOING_MIN_PROTO_VERSION),
+    OPT_STR(DEFAULT_SSL_LISTEN_MAX_PROTO_VERSION),
+    OPT_STR(DEFAULT_SSL_OUTGOING_MAX_PROTO_VERSION)
 #endif /* WITH_SSL_SUPPORT */
   );
 
--- a/perdition/options.h	Sun Jun 14 14:58:45 2015 +0900
+++ b/perdition/options.h	Wed May 11 08:57:22 2016 +0900
@@ -182,6 +182,10 @@
 #define DEFAULT_SSL_NO_CN_VERIFY             0
 #define DEFAULT_SSL_PASSPHRASE_FD            0
 #define DEFAULT_SSL_PASSPHRASE_FILE          NULL
+#define DEFAULT_SSL_LISTEN_MIN_PROTO_VERSION "tlsv1"
+#define DEFAULT_SSL_OUTGOING_MIN_PROTO_VERSION "tlsv1"
+#define DEFAULT_SSL_LISTEN_MAX_PROTO_VERSION NULL
+#define DEFAULT_SSL_OUTGOING_MAX_PROTO_VERSION NULL
 #endif /* WITH_SSL_SUPPORT */
 
 
@@ -251,6 +255,10 @@
   int             ssl_no_cn_verify;
   int             ssl_passphrase_fd;
   char            *ssl_passphrase_file;
+  char            *ssl_listen_min_proto_version;
+  char            *ssl_outgoing_min_proto_version;
+  char            *ssl_listen_max_proto_version;
+  char            *ssl_outgoing_max_proto_version;
   flag_t          ssl_mask;
 } options_t;
 
@@ -320,6 +328,10 @@
 #define MASK_SSL_PASSPHRASE_FD                 (flag_t) 0x00008000
 #define MASK_SSL_PASSPHRASE_FILE               (flag_t) 0x00010000
 #define MASK_SSL_DH_PARAMS_FILE                (flag_t) 0x00020000
+#define MASK_SSL_LISTEN_MIN_PROTO_VERSION      (flag_t) 0x00040000
+#define MASK_SSL_OUTGOING_MIN_PROTO_VERSION    (flag_t) 0x00080000
+#define MASK_SSL_LISTEN_MAX_PROTO_VERSION      (flag_t) 0x00100000
+#define MASK_SSL_OUTGOING_MAX_PROTO_VERSION    (flag_t) 0x00200000
 #endif /* WITH_SSL_SUPPORT */
 
 /* 
@@ -359,6 +371,10 @@
 #define TAG_POP_CAPABILITY                     (int) 156
 #define TAG_TCP_KEEPALIVE                      (int) 157
 #define TAG_SSL_DH_PARAMS_FILE                 (int) 158
+#define TAG_SSL_LISTEN_MIN_PROTO_VERSION       (int) 159
+#define TAG_SSL_OUTGOING_MIN_PROTO_VERSION     (int) 160
+#define TAG_SSL_LISTEN_MAX_PROTO_VERSION       (int) 161
+#define TAG_SSL_OUTGOING_MAX_PROTO_VERSION     (int) 162
 
 /*Flag values for options()*/
 #define OPT_ERR         (flag_t) 0x1  /*Print error to stderr, enable help*/
--- a/perdition/perdition.8	Sun Jun 14 14:58:45 2015 +0900
+++ b/perdition/perdition.8	Wed May 11 08:57:22 2016 +0900
@@ -641,6 +641,69 @@
 be specified.
 (default NULL, no file)
 .TP
+.B \-\-ssl_listen_ciphers STRING:
+Cipher list when listening for SSL or TLS connections as per
+ciphers(1). If empty ("") then openssl's default will be used.
+.br
+(default "")
+.TP
+.B \-\-ssl_outgoing_ciphers STRING:
+Cipher list when making outgoing SSL or TLS connections as per 
+ciphers(1). If empty ("") then openssl's default will be used.
+.br
+(default "")
+.TP
+.B \-\-ssl_no_cert_verify:
+Don't cryptographically verify the certificates.
+Used for SSL or TLS outgoing connections.
+.TP
+.B \-\-ssl_no_client_cert_verify:
+Don't cryptographically verify the end-user's certificate.
+Used for SSL or TLS outgoing connections.
+.TP
+.B \-\-ssl_no_cn_verify:
+Don't verify the real-server's common name with the name used.
+to connect to the server. Used for SSL or TLS outgoing connections.
+.TP
+.B \-\-ssl_passphrase_fd N:
+File descriptor to read the passphrase for the certificate from.
+Only the first line will be read.
+Only one of ssl_passphrase_fd and ssl_passphrase_file may
+be specified.
+(default 0)
+.TP
+.B \-\-ssl_listen_min_proto_version PROTOCOL_VERSIONS:
+Minimum permited SSL/TLS protocol version when accepting incomming
+connections. May not be empty ("").
+.sp
+The valid protocol versions are sslv3, tlsv1, tlsv1.1 and tlsv1.2.
+.sp
+(default "tlsv1")
+.TP
+.B \-\-ssl_outgoing_min_proto_version PROTOCOL_VERSIONS:
+Minimum permited SSL/TLS protocol version when making outgoing
+connections. May not be empty ("").
+.sp
+The valid protocol versions are sslv3, tlsv1, tlsv1.1 and tlsv1.2.
+.sp
+(default "tlsv1")
+.TP
+.B \-\-ssl_listen_max_proto_version PROTOCOL_VERSIONS:
+Maximum permited SSL/TLS protocol version when accepting incommaxg
+connections. If empty ("") then openssl's default will be used.
+.sp
+The valid protocol versions are sslv3, tlsv1, tlsv1.1 and tlsv1.2.
+.sp
+(default "")
+.TP
+.B \-\-ssl_outgoing_max_proto_version PROTOCOL_VERSIONS:
+Maximum permited SSL/TLS protocol version when making outgoing
+connections. If empty ("") then openssl's default will be used.
+.sp
+The valid protocol versions are sslv3, tlsv1, tlsv1.1 and tlsv1.2.
+.sp
+(default "")
+.TP
 Notes: 
 Default value for binary flags is off.
 .br
--- a/perdition/ssl.c	Sun Jun 14 14:58:45 2015 +0900
+++ b/perdition/ssl.c	Wed May 11 08:57:22 2016 +0900
@@ -139,6 +139,23 @@
 	return strlen(buf);
 }
 
+int perdition_parse_ssl_proto_version(const char *str)
+{
+	if (!strcasecmp(str, "sslv3")) {
+		return SSL3_VERSION;
+	}
+	if (!strcasecmp(str, "tlsv1")) {
+		return TLS1_VERSION;
+	}
+	if (!strcasecmp(str, "tlsv1.1")) {
+		return TLS1_1_VERSION;
+	}
+	if (!strcasecmp(str, "tlsv1.2")) {
+		return TLS1_2_VERSION;
+	}
+
+	return -1;
+}
 
 /**********************************************************************
  * perdition_ssl_ctx
@@ -491,6 +508,72 @@
 	return(verify);
 }
 
+#if HAVE_DECL_SSL_CTX_SET_MIN_PROTO_VERSION == 0
+static int
+perdition_ssl_ctx_set_min_proto_version(SSL_CTX *ssl_ctx, int min_version)
+{
+	long options = SSL_OP_NO_SSLv2;
+
+	switch (min_version) {
+	case TLS1_2_VERSION:
+		options |= SSL_OP_NO_TLSv1_1;
+		/* fall-through */
+	case TLS1_1_VERSION:
+		options |= SSL_OP_NO_TLSv1;
+		/* fall-through */
+	case TLS1_VERSION:
+		options |= SSL_OP_NO_SSLv3;
+		/* fall-through */
+	case SSL3_VERSION:
+		/* Nothing more to do */
+		break;
+	default:
+		PERDITION_DEBUG_SSL_ERR("Unknown minumum version");
+		return 0;
+	}
+
+	SSL_CTX_set_options(ssl_ctx, options);
+	return 1;
+}
+
+#define SSL_CTX_set_min_proto_version perdition_ssl_ctx_set_min_proto_version
+#endif
+
+#if HAVE_DECL_SSL_CTX_SET_MAX_PROTO_VERSION == 0
+static int
+perdition_ssl_ctx_set_max_proto_version(SSL_CTX *ssl_ctx, int max_version)
+{
+	long options = 0;
+
+	switch (max_version) {
+	case SSL3_VERSION:
+		options |= SSL_OP_NO_TLSv1;
+		/* fall-through */
+	case TLS1_VERSION:
+		options |= SSL_OP_NO_TLSv1_1;
+		/* fall-through */
+	case TLS1_1_VERSION:
+		options |= SSL_OP_NO_TLSv1_2;
+		/* fall-through */
+	case TLS1_2_VERSION:
+		/* Nothing more to do */
+		break;
+	default:
+		PERDITION_DEBUG_SSL_ERR("Unknown maximum version");
+		return 0;
+	}
+
+	SSL_CTX_set_options(ssl_ctx, options);
+
+	VANESSA_LOGGER_ERR("warning: protocol versions greater than tlsv1.2 "
+			   "may still be allowed if supported by the SSL/TLS "
+			   "implementation");
+	return 1;
+}
+
+#define SSL_CTX_set_max_proto_version perdition_ssl_ctx_set_max_proto_version
+#endif
+
 SSL_CTX *perdition_ssl_ctx(const char *ca_file, const char *ca_path, 
 		const char *cert, const char *privkey, 
 		const char *ca_chain_file, const char *dh_params_file,
@@ -543,6 +626,60 @@
 	}
 
 	/*
+	 * Set minimum protocol version
+	 */
+	{
+		const char *ver_str;
+
+		if (flag == PERDITION_SSL_CLIENT)
+			ver_str = opt.ssl_outgoing_min_proto_version;
+		else
+			ver_str = opt.ssl_listen_min_proto_version;
+
+		if (ver_str) {
+			int ver_no = perdition_parse_ssl_proto_version(ver_str);
+
+
+			if (ver_no < 0) {
+				VANESSA_LOGGER_DEBUG("perdition_parse_ssl_proto_version");
+				goto err;
+			}
+
+			if (!SSL_CTX_set_min_proto_version(ssl_ctx, ver_no)) {
+				VANESSA_LOGGER_DEBUG("SSL_CTX_set_min_proto_version");
+				goto err;
+			}
+		}
+	}
+
+	/*
+	 * Set maximum protocol version
+	 */
+	{
+		const char *ver_str;
+
+		if (flag == PERDITION_SSL_CLIENT)
+			ver_str = opt.ssl_outgoing_max_proto_version;
+		else
+			ver_str = opt.ssl_listen_max_proto_version;
+
+		if (ver_str) {
+			int ver_no = perdition_parse_ssl_proto_version(ver_str);
+
+
+			if (ver_no < 0) {
+				VANESSA_LOGGER_DEBUG("perdition_parse_ssl_proto_version");
+				goto err;
+			}
+
+			if (!SSL_CTX_set_max_proto_version(ssl_ctx, ver_no)) {
+				VANESSA_LOGGER_DEBUG("SSL_CTX_set_max_proto_version");
+				goto err;
+			}
+		}
+	}
+
+	/*
 	 * Load the Diffie-Hellman parameters:
 	 */
 	if (flag & PERDITION_SSL_SERVER &&
--- a/perdition/ssl.h	Sun Jun 14 14:58:45 2015 +0900
+++ b/perdition/ssl.h	Wed May 11 08:57:22 2016 +0900
@@ -39,6 +39,8 @@
 #define PERDITION_SSL_CLIENT (flag_t) 0x1
 #define PERDITION_SSL_SERVER (flag_t) 0x2
 
+int perdition_parse_ssl_proto_version(const char *str);
+
 /**********************************************************************
  * perdition_ssl_ctx
  * Create an SSL context