- Added option to add minimum accepted SSL/TLS protocol version

diff --git a/ChangeLog b/ChangeLog
index 28f2847..37d32e1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -28,6 +28,7 @@
    * Added predefined DHM groups from RFC 5114
    * Added simple SSL session cache implementation
    * Added ServerName extension parsing (SNI) at server side
+   * Added option to add minimum accepted SSL/TLS protocol version
 
 Changes
    * Removed redundant POLARSSL_DEBUG_MSG define
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index 1254615..c48a89d 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -90,6 +90,7 @@
 #define POLARSSL_ERR_SSL_HW_ACCEL_FAILED                   -0x7F80  /**< Hardware acceleration function returned with error */
 #define POLARSSL_ERR_SSL_HW_ACCEL_FALLTHROUGH              -0x6F80  /**< Hardware acceleration function skipped / left alone data */
 #define POLARSSL_ERR_SSL_COMPRESSION_FAILED                -0x6F00  /**< Processing of the compression / decompression failed */
+#define POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION           -0x6E80  /**< Handshake protocol not within min/max boundaries */
 
 /*
  * Various constants
@@ -389,6 +390,8 @@
 
     int max_major_ver;          /*!< max. major version from client   */
     int max_minor_ver;          /*!< max. minor version from client   */
+    int min_minor_ver;          /*!< min. major version accepted      */
+    int min_major_ver;          /*!< min. minor version accepted      */
 
     /*
      * Callbacks (RNG, debug, I/O, verification)
@@ -828,6 +831,19 @@
  */
 void ssl_set_max_version( ssl_context *ssl, int major, int minor );
 
+
+/**
+ * \brief          Set the minimum accepted SSL/TLS protocol version
+ *                 (Default: SSL_MAJOR_VERSION_3, SSL_MINOR_VERSION_0)
+ *
+ * \param ssl      SSL context
+ * \param major    Major version number (only SSL_MAJOR_VERSION_3 supported)
+ * \param minor    Minor version number (SSL_MINOR_VERSION_0,
+ *                 SSL_MINOR_VERSION_1 and SSL_MINOR_VERSION_2,
+ *                 SSL_MINOR_VERSION_3 supported)
+ */
+void ssl_set_min_version( ssl_context *ssl, int major, int minor );
+
 /**
  * \brief          Enable / Disable renegotiation support for connection
  *                 (Default: SSL_RENEGOTIATION_ENABLED)
diff --git a/library/error.c b/library/error.c
index d4d8500..a5eaaba 100644
--- a/library/error.c
+++ b/library/error.c
@@ -303,6 +303,8 @@
             snprintf( buf, buflen, "SSL - Hardware acceleration function skipped / left alone data" );
         if( use_ret == -(POLARSSL_ERR_SSL_COMPRESSION_FAILED) )
             snprintf( buf, buflen, "SSL - Processing of the compression / decompression failed" );
+        if( use_ret == -(POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION) )
+            snprintf( buf, buflen, "SSL - Handshake protocol not within min/max boundaries" );
 #endif /* POLARSSL_SSL_TLS_C */
 
 #if defined(POLARSSL_X509_PARSE_C)
diff --git a/library/ssl_cli.c b/library/ssl_cli.c
index 3e1b056..07b31d9 100644
--- a/library/ssl_cli.c
+++ b/library/ssl_cli.c
@@ -413,6 +413,18 @@
 
     ssl->minor_ver = buf[5];
 
+    if( ssl->minor_ver < ssl->min_minor_ver )
+    {
+        SSL_DEBUG_MSG( 1, ( "server only supports ssl smaller than minimum"
+                            " [%d:%d] < [%d:%d]", ssl->major_ver, ssl->minor_ver,
+                            buf[4], buf[5] ) );
+
+        ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL,
+                                     SSL_ALERT_MSG_PROTOCOL_VERSION );
+
+        return( POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION );
+    }
+
 #if defined(POLARSSL_DEBUG_C)
     t = ( (time_t) buf[6] << 24 )
       | ( (time_t) buf[7] << 16 )
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index da34015..408d510 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -222,6 +222,18 @@
     ssl->minor_ver = ( buf[5] <= SSL_MINOR_VERSION_3 )
                      ? buf[5]  : SSL_MINOR_VERSION_3;
 
+    if( ssl->minor_ver < ssl->min_minor_ver )
+    {
+        SSL_DEBUG_MSG( 1, ( "client only supports ssl smaller than minimum"
+                            " [%d:%d] < [%d:%d]", ssl->major_ver, ssl->minor_ver,
+                            buf[4], buf[5] ) );
+
+        ssl_send_alert_message( ssl, SSL_ALERT_LEVEL_FATAL,
+                                     SSL_ALERT_MSG_PROTOCOL_VERSION );
+
+        return( POLARSSL_ERR_SSL_BAD_HS_PROTOCOL_VERSION );
+    }
+
     ssl->max_major_ver = buf[4];
     ssl->max_minor_ver = buf[5];
 
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 5ae581f..b66e046 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -2855,6 +2855,9 @@
     ssl->rsa_sign = ssl_rsa_sign;
     ssl->rsa_key_len = ssl_rsa_key_len;
 
+    ssl->min_major_ver = SSL_MAJOR_VERSION_3;
+    ssl->min_minor_ver = SSL_MINOR_VERSION_0;
+
 #if defined(POLARSSL_DHM_C)
     if( ( ret = mpi_read_string( &ssl->dhm_P, 16,
                                  POLARSSL_DHM_RFC5114_MODP_1024_P) ) != 0 ||
@@ -3133,6 +3136,12 @@
     ssl->max_minor_ver = minor;
 }
 
+void ssl_set_min_version( ssl_context *ssl, int major, int minor )
+{
+    ssl->min_major_ver = major;
+    ssl->min_minor_ver = minor;
+}
+
 void ssl_set_renegotiation( ssl_context *ssl, int renegotiation )
 {
     ssl->disable_renegotiation = renegotiation;
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index a6915e2..ae98b1b 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -52,6 +52,8 @@
 #define DFL_FORCE_CIPHER        0
 #define DFL_RENEGOTIATION       SSL_RENEGOTIATION_ENABLED
 #define DFL_ALLOW_LEGACY        SSL_LEGACY_NO_RENEGOTIATION
+#define DFL_MIN_VERSION         -1
+#define DFL_MAX_VERSION         -1
 
 #define GET_REQUEST "GET %s HTTP/1.0\r\n\r\n"
 
@@ -71,6 +73,8 @@
     int force_ciphersuite[2];   /* protocol/ciphersuite to use, or all      */
     int renegotiation;          /* enable / disable renegotiation           */
     int allow_legacy;           /* allow legacy renegotiation               */
+    int min_version;            /* minimum protocol version accepted        */
+    int max_version;            /* maximum protocol version accepted        */
 } opt;
 
 void my_debug( void *ctx, int level, const char *str )
@@ -142,6 +146,12 @@
     "    request_page=%%s     default: \".\"\n"             \
     "    renegotiation=%%d    default: 1 (enabled)\n"       \
     "    allow_legacy=%%d     default: 0 (disabled)\n"      \
+    "\n"                                                    \
+    "    min_version=%%s      default: \"\" (ssl3)\n"       \
+    "    max_version=%%s      default: \"\" (tls1_2)\n"     \
+    "    force_version=%%s    default: \"\" (none)\n"       \
+    "                        options: ssl3, tls1, tls1_1, tls1_2\n" \
+    "\n"                                                    \
     "    force_ciphersuite=<name>    default: all enabled\n"\
     " acceptable ciphersuite names:\n"
 
@@ -215,6 +225,8 @@
     opt.force_ciphersuite[0]= DFL_FORCE_CIPHER;
     opt.renegotiation       = DFL_RENEGOTIATION;
     opt.allow_legacy        = DFL_ALLOW_LEGACY;
+    opt.min_version         = DFL_MIN_VERSION;
+    opt.max_version         = DFL_MAX_VERSION;
 
     for( i = 1; i < argc; i++ )
     {
@@ -271,6 +283,57 @@
             if( opt.allow_legacy < 0 || opt.allow_legacy > 1 )
                 goto usage;
         }
+        else if( strcmp( p, "min_version" ) == 0 )
+        {
+            if( strcmp( q, "ssl3" ) == 0 )
+                opt.min_version = SSL_MINOR_VERSION_0;
+            else if( strcmp( q, "tls1" ) == 0 )
+                opt.min_version = SSL_MINOR_VERSION_1;
+            else if( strcmp( q, "tls1_1" ) == 0 )
+                opt.min_version = SSL_MINOR_VERSION_2;
+            else if( strcmp( q, "tls1_2" ) == 0 )
+                opt.min_version = SSL_MINOR_VERSION_3;
+            else
+                goto usage;
+        }
+        else if( strcmp( p, "max_version" ) == 0 )
+        {
+            if( strcmp( q, "ssl3" ) == 0 )
+                opt.max_version = SSL_MINOR_VERSION_0;
+            else if( strcmp( q, "tls1" ) == 0 )
+                opt.max_version = SSL_MINOR_VERSION_1;
+            else if( strcmp( q, "tls1_1" ) == 0 )
+                opt.max_version = SSL_MINOR_VERSION_2;
+            else if( strcmp( q, "tls1_2" ) == 0 )
+                opt.max_version = SSL_MINOR_VERSION_3;
+            else
+                goto usage;
+        }
+        else if( strcmp( p, "force_version" ) == 0 )
+        {
+            if( strcmp( q, "ssl3" ) == 0 )
+            {
+                opt.min_version = SSL_MINOR_VERSION_0;
+                opt.max_version = SSL_MINOR_VERSION_0;
+            }
+            else if( strcmp( q, "tls1" ) == 0 )
+            {
+                opt.min_version = SSL_MINOR_VERSION_1;
+                opt.max_version = SSL_MINOR_VERSION_1;
+            }
+            else if( strcmp( q, "tls1_1" ) == 0 )
+            {
+                opt.min_version = SSL_MINOR_VERSION_2;
+                opt.max_version = SSL_MINOR_VERSION_2;
+            }
+            else if( strcmp( q, "tls1_2" ) == 0 )
+            {
+                opt.min_version = SSL_MINOR_VERSION_3;
+                opt.max_version = SSL_MINOR_VERSION_3;
+            }
+            else
+                goto usage;
+        }
         else
             goto usage;
     }
@@ -425,6 +488,11 @@
 
     ssl_set_hostname( &ssl, opt.server_name );
 
+    if( opt.min_version != -1 )
+        ssl_set_min_version( &ssl, SSL_MAJOR_VERSION_3, opt.min_version );
+    if( opt.max_version != -1 )
+        ssl_set_max_version( &ssl, SSL_MAJOR_VERSION_3, opt.max_version );
+
     /*
      * 4. Handshake
      */
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index b98bff7..5076be3 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -59,6 +59,7 @@
 #define DFL_FORCE_CIPHER        0
 #define DFL_RENEGOTIATION       SSL_RENEGOTIATION_ENABLED
 #define DFL_ALLOW_LEGACY        SSL_LEGACY_NO_RENEGOTIATION
+#define DFL_MIN_VERSION         -1
 
 #define HTTP_RESPONSE \
     "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" \
@@ -79,6 +80,7 @@
     int force_ciphersuite[2];   /* protocol/ciphersuite to use, or all      */
     int renegotiation;          /* enable / disable renegotiation           */
     int allow_legacy;           /* allow legacy renegotiation               */
+    int min_version;            /* minimum protocol version accepted        */
 } opt;
 
 void my_debug( void *ctx, int level, const char *str )
@@ -110,6 +112,8 @@
     "    request_page=%%s     default: \".\"\n"             \
     "    renegotiation=%%d    default: 1 (enabled)\n"       \
     "    allow_legacy=%%d     default: 0 (disabled)\n"      \
+    "    min_version=%%s      default: \"ssl3\"\n"          \
+    "                        options: ssl3, tls1, tls1_1, tls1_2\n" \
     "    force_ciphersuite=<name>    default: all enabled\n"\
     " acceptable ciphersuite names:\n"
 
@@ -189,6 +193,7 @@
     opt.force_ciphersuite[0]= DFL_FORCE_CIPHER;
     opt.renegotiation       = DFL_RENEGOTIATION;
     opt.allow_legacy        = DFL_ALLOW_LEGACY;
+    opt.min_version         = DFL_MIN_VERSION;
 
     for( i = 1; i < argc; i++ )
     {
@@ -241,6 +246,19 @@
             if( opt.allow_legacy < 0 || opt.allow_legacy > 1 )
                 goto usage;
         }
+        else if( strcmp( p, "min_version" ) == 0 )
+        {
+            if( strcmp( q, "ssl3" ) == 0 )
+                opt.min_version = SSL_MINOR_VERSION_0;
+            else if( strcmp( q, "tls1" ) == 0 )
+                opt.min_version = SSL_MINOR_VERSION_1;
+            else if( strcmp( q, "tls1_1" ) == 0 )
+                opt.min_version = SSL_MINOR_VERSION_2;
+            else if( strcmp( q, "tls1_2" ) == 0 )
+                opt.min_version = SSL_MINOR_VERSION_3;
+            else
+                goto usage;
+        }
         else
             goto usage;
     }
@@ -395,6 +413,9 @@
                             POLARSSL_DHM_RFC5114_MODP_2048_G );
 #endif
 
+    if( opt.min_version != -1 )
+        ssl_set_min_version( &ssl, SSL_MAJOR_VERSION_3, opt.min_version );
+
     printf( " ok\n" );
 
 reset:
@@ -464,7 +485,7 @@
         if( ret != POLARSSL_ERR_NET_WANT_READ && ret != POLARSSL_ERR_NET_WANT_WRITE )
         {
             printf( " failed\n  ! ssl_handshake returned -0x%x\n\n", -ret );
-            goto exit;
+            goto reset;
         }
     }