Merge support for 1/n-1 record splitting
diff --git a/ChangeLog b/ChangeLog
index dadca0e..0ea522e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -28,6 +28,7 @@
    * Add compile-time option POLARSSL_X509_MAX_INTERMEDIATE_CA to limit the
      length of an X.509 verification chain.
    * Support for renegotiation can now be disabled at compile-time
+   * Support for 1/n-1 record splitting, a countermeasure against BEAST.
 
 Bugfix
    * Stack buffer overflow if ctr_drbg_update() is called with too large
diff --git a/include/polarssl/check_config.h b/include/polarssl/check_config.h
index 80b037e..cce7b2b 100644
--- a/include/polarssl/check_config.h
+++ b/include/polarssl/check_config.h
@@ -277,6 +277,11 @@
 #error "POLARSSL_SSL_SESSION_TICKETS_C defined, but not all prerequisites"
 #endif
 
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING) && \
+    !defined(POLARSSL_SSL_PROTO_SSL3) && !defined(POLARSSL_SSL_PROTO_TLS1)
+#error "POLARSSL_SSL_CBC_RECORD_SPLITTING defined, but not all prerequisites"
+#endif
+
 #if defined(POLARSSL_SSL_SERVER_NAME_INDICATION) && \
         !defined(POLARSSL_X509_CRT_PARSE_C)
 #error "POLARSSL_SSL_SERVER_NAME_INDICATION defined, but not all prerequisites"
diff --git a/include/polarssl/config.h b/include/polarssl/config.h
index b155e26..04a5b26 100644
--- a/include/polarssl/config.h
+++ b/include/polarssl/config.h
@@ -887,6 +887,18 @@
 //#define POLARSSL_SSL_HW_RECORD_ACCEL
 
 /**
+ * \def POLARSSL_SSL_CBC_RECORD_SPLITTING
+ *
+ * Enable 1/n-1 record splitting for CBC mode in SSLv3 and TLS 1.0.
+ *
+ * This is a countermeasure to the BEAST attack, which also minimizes the risk
+ * of interoperability issues compared to sending 0-length records.
+ *
+ * Comment this macro to disable 1/n-1 record splitting.
+ */
+#define POLARSSL_SSL_CBC_RECORD_SPLITTING
+
+/**
  * \def POLARSSL_SSL_DISABLE_RENEGOTIATION
  *
  * Disable support for TLS renegotiation.
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index bf2527a..5f4b5f7 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -253,6 +253,9 @@
 #define SSL_SESSION_TICKETS_DISABLED     0
 #define SSL_SESSION_TICKETS_ENABLED      1
 
+#define SSL_CBC_RECORD_SPLITTING_DISABLED   -1
+#define SSL_CBC_RECORD_SPLITTING_ENABLED     0
+
 /**
  * \name SECTION: Module settings
  *
@@ -832,6 +835,10 @@
 #if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH)
     unsigned char mfl_code;     /*!< MaxFragmentLength chosen by us   */
 #endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+    char split_done;            /*!< flag for record splitting:
+                                     -1 disabled, 0 todo, 1 done      */
+#endif
 
     /*
      * PKI layer
@@ -1528,6 +1535,21 @@
 int ssl_set_truncated_hmac( ssl_context *ssl, int truncate );
 #endif /* POLARSSL_SSL_TRUNCATED_HMAC */
 
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+/**
+ * \brief          Enable / Disable 1/n-1 record splitting
+ *                 (Default: SSL_CBC_RECORD_SPLITTING_ENABLED)
+ *
+ * \note           Only affects SSLv3 and TLS 1.0, not higher versions.
+ *                 Does not affect non-CBC ciphersuites in any version.
+ *
+ * \param ssl      SSL context
+ * \param split    SSL_CBC_RECORD_SPLITTING_ENABLED or
+ *                 SSL_CBC_RECORD_SPLITTING_DISABLED
+ */
+void ssl_set_cbc_record_splitting( ssl_context *ssl, char split );
+#endif /* POLARSSL_SSL_CBC_RECORD_SPLITTING */
+
 #if defined(POLARSSL_SSL_SESSION_TICKETS)
 /**
  * \brief          Enable / Disable session tickets
@@ -1796,6 +1818,10 @@
  * \note           When this function returns POLARSSL_ERR_NET_WANT_WRITE,
  *                 it must be called later with the *same* arguments,
  *                 until it returns a positive value.
+ *
+ * \note           This function may write less than the number of bytes
+ *                 requested if len is greater than the maximum record length.
+ *                 For arbitrary-sized messages, it should be called in a loop.
  */
 int ssl_write( ssl_context *ssl, const unsigned char *buf, size_t len );
 
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 91ce32d..22bd697 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -3717,6 +3717,10 @@
     ssl->out_msgtype = 0;
     ssl->out_msglen = 0;
     ssl->out_left = 0;
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+    if( ssl->split_done != SSL_CBC_RECORD_SPLITTING_DISABLED )
+        ssl->split_done = 0;
+#endif
 
     ssl->transform_in = NULL;
     ssl->transform_out = NULL;
@@ -4263,6 +4267,13 @@
 }
 #endif /* POLARSSL_SSL_TRUNCATED_HMAC */
 
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+void ssl_set_cbc_record_splitting( ssl_context *ssl, char split )
+{
+    ssl->split_done = split;
+}
+#endif
+
 void ssl_legacy_renegotiation( ssl_context *ssl, int allow_legacy )
 {
     ssl->allow_legacy_renegotiation = allow_legacy;
@@ -4741,7 +4752,11 @@
 /*
  * Send application data to be encrypted by the SSL layer
  */
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+static int ssl_write_real( ssl_context *ssl, const unsigned char *buf, size_t len )
+#else
 int ssl_write( ssl_context *ssl, const unsigned char *buf, size_t len )
+#endif
 {
     int ret;
     size_t n;
@@ -4811,6 +4826,45 @@
 }
 
 /*
+ * Write application data, doing 1/n-1 splitting if necessary.
+ *
+ * With non-blocking I/O, ssl_write_real() may return WANT_WRITE,
+ * then the caller will call us again with the same arguments, so
+ * remember wether we already did the split or not.
+ */
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+int ssl_write( ssl_context *ssl, const unsigned char *buf, size_t len )
+{
+    int ret;
+
+    if( ssl->split_done == SSL_CBC_RECORD_SPLITTING_DISABLED ||
+        len <= 1 ||
+        ssl->minor_ver > SSL_MINOR_VERSION_1 ||
+        cipher_get_cipher_mode( &ssl->transform_out->cipher_ctx_enc )
+                                != POLARSSL_MODE_CBC )
+    {
+        return( ssl_write_real( ssl, buf, len ) );
+    }
+
+    if( ssl->split_done == 0 )
+    {
+        ssl->split_done = 1;
+        if( ( ret = ssl_write_real( ssl, buf, 1 ) ) < 0 )
+            return( ret );
+    }
+
+    if( ssl->split_done == 1 )
+    {
+        ssl->split_done = 0;
+        if( ( ret = ssl_write_real( ssl, buf + 1, len - 1 ) ) < 0 )
+            return( ret );
+    }
+
+    return( ret + 1 );
+}
+#endif /* POLARSSL_SSL_CBC_RECORD_SPLITTING */
+
+/*
  * Notify the peer that the connection is being closed
  */
 int ssl_close_notify( ssl_context *ssl )
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 2ad6158..07dee7f 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -91,6 +91,7 @@
 #define DFL_AUTH_MODE           SSL_VERIFY_REQUIRED
 #define DFL_MFL_CODE            SSL_MAX_FRAG_LEN_NONE
 #define DFL_TRUNC_HMAC          0
+#define DFL_RECSPLIT            -1
 #define DFL_RECONNECT           0
 #define DFL_RECO_DELAY          0
 #define DFL_TICKETS             SSL_SESSION_TICKETS_ENABLED
@@ -131,6 +132,7 @@
     int auth_mode;              /* verify mode for connection               */
     unsigned char mfl_code;     /* code for maximum fragment length         */
     int trunc_hmac;             /* negotiate truncated hmac or not          */
+    int recsplit;               /* enable record splitting?                 */
     int reconnect;              /* attempt to resume session                */
     int reco_delay;             /* delay in seconds before resuming session */
     int tickets;                /* enable / disable session tickets         */
@@ -275,6 +277,13 @@
 #define USAGE_MAX_FRAG_LEN ""
 #endif /* POLARSSL_SSL_MAX_FRAGMENT_LENGTH */
 
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+#define USAGE_RECSPLIT \
+    "    recplit=%%d          default: (library default)\n"
+#else
+#define USAGE_RECSPLIT
+#endif
+
 #if defined(POLARSSL_TIMING_C)
 #define USAGE_TIME \
     "    reco_delay=%%d       default: 0 seconds\n"
@@ -350,6 +359,7 @@
     USAGE_FALLBACK                                          \
     USAGE_EMS                                               \
     USAGE_ETM                                               \
+    USAGE_RECSPLIT                                          \
     "\n"                                                    \
     "    min_version=%%s      default: \"\" (ssl3)\n"       \
     "    max_version=%%s      default: \"\" (tls1_2)\n"     \
@@ -446,6 +456,7 @@
     opt.auth_mode           = DFL_AUTH_MODE;
     opt.mfl_code            = DFL_MFL_CODE;
     opt.trunc_hmac          = DFL_TRUNC_HMAC;
+    opt.recsplit            = DFL_RECSPLIT;
     opt.reconnect           = DFL_RECONNECT;
     opt.reco_delay          = DFL_RECO_DELAY;
     opt.tickets             = DFL_TICKETS;
@@ -671,6 +682,12 @@
             if( opt.trunc_hmac < 0 || opt.trunc_hmac > 1 )
                 goto usage;
         }
+        else if( strcmp( p, "recsplit" ) == 0 )
+        {
+            opt.recsplit = atoi( q );
+            if( opt.recsplit < 0 || opt.recsplit > 1 )
+                goto usage;
+        }
         else
             goto usage;
     }
@@ -963,6 +980,13 @@
         ssl_set_encrypt_then_mac( &ssl, opt.etm );
 #endif
 
+#if defined(POLARSSL_SSL_CBC_RECORD_SPLITTING)
+    if( opt.recsplit != DFL_RECSPLIT )
+        ssl_set_cbc_record_splitting( &ssl, opt.recsplit
+                                    ? SSL_CBC_RECORD_SPLITTING_ENABLED
+                                    : SSL_CBC_RECORD_SPLITTING_DISABLED );
+#endif
+
 #if defined(POLARSSL_SSL_ALPN)
     if( opt.alpn_string != NULL )
         if( ( ret = ssl_set_alpn_protocols( &ssl, alpn_list ) ) != 0 )
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 456dbab..cd4f6a6 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -662,6 +662,62 @@
             -s "received FALLBACK_SCSV" \
             -S "inapropriate fallback"
 
+# Tests for CBC 1/n-1 record splitting
+
+run_test    "CBC Record splitting: TLS 1.2, no splitting" \
+            "$P_SRV" \
+            "$P_CLI force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA \
+             request_size=123 force_version=tls1_2" \
+            0 \
+            -s "Read from client: 123 bytes read" \
+            -S "Read from client: 1 bytes read" \
+            -S "122 bytes read"
+
+run_test    "CBC Record splitting: TLS 1.1, no splitting" \
+            "$P_SRV" \
+            "$P_CLI force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA \
+             request_size=123 force_version=tls1_1" \
+            0 \
+            -s "Read from client: 123 bytes read" \
+            -S "Read from client: 1 bytes read" \
+            -S "122 bytes read"
+
+run_test    "CBC Record splitting: TLS 1.0, splitting" \
+            "$P_SRV" \
+            "$P_CLI force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA \
+             request_size=123 force_version=tls1" \
+            0 \
+            -S "Read from client: 123 bytes read" \
+            -s "Read from client: 1 bytes read" \
+            -s "122 bytes read"
+
+run_test    "CBC Record splitting: SSLv3, splitting" \
+            "$P_SRV" \
+            "$P_CLI force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA \
+             request_size=123 force_version=ssl3" \
+            0 \
+            -S "Read from client: 123 bytes read" \
+            -s "Read from client: 1 bytes read" \
+            -s "122 bytes read"
+
+run_test    "CBC Record splitting: TLS 1.0 RC4, no splitting" \
+            "$P_SRV" \
+            "$P_CLI force_ciphersuite=TLS-RSA-WITH-RC4-128-SHA \
+             request_size=123 force_version=tls1" \
+            0 \
+            -s "Read from client: 123 bytes read" \
+            -S "Read from client: 1 bytes read" \
+            -S "122 bytes read"
+
+run_test    "CBC Record splitting: TLS 1.0, splitting disabled" \
+            "$P_SRV" \
+            "$P_CLI force_ciphersuite=TLS-RSA-WITH-AES-128-CBC-SHA \
+             request_size=123 force_version=tls1 recsplit=0" \
+            0 \
+            -s "Read from client: 123 bytes read" \
+            -S "Read from client: 1 bytes read" \
+            -S "122 bytes read"
+
 # Tests for Session Tickets
 
 run_test    "Session resume using tickets: basic" \
@@ -2087,7 +2143,8 @@
 
 run_test    "Small packet TLS 1.2 BlockCipher larger MAC" \
             "$P_SRV" \
-            "$P_CLI request_size=1 force_version=tls1_2 force_ciphersuite=TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384" \
+            "$P_CLI request_size=1 force_version=tls1_2 \
+             force_ciphersuite=TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384" \
             0 \
             -s "Read from client: 1 bytes read"
 
@@ -2132,7 +2189,7 @@
 
 run_test    "Large packet SSLv3 BlockCipher" \
             "$P_SRV" \
-            "$P_CLI request_size=16384 force_version=ssl3 \
+            "$P_CLI request_size=16384 force_version=ssl3 recsplit=0 \
              force_ciphersuite=TLS-RSA-WITH-AES-256-CBC-SHA" \
             0 \
             -s "Read from client: 16384 bytes read"
@@ -2146,14 +2203,14 @@
 
 run_test    "Large packet TLS 1.0 BlockCipher" \
             "$P_SRV" \
-            "$P_CLI request_size=16384 force_version=tls1 \
+            "$P_CLI request_size=16384 force_version=tls1 recsplit=0 \
              force_ciphersuite=TLS-RSA-WITH-AES-256-CBC-SHA" \
             0 \
             -s "Read from client: 16384 bytes read"
 
 run_test    "Large packet TLS 1.0 BlockCipher truncated MAC" \
             "$P_SRV" \
-            "$P_CLI request_size=16384 force_version=tls1 \
+            "$P_CLI request_size=16384 force_version=tls1 recsplit=0 \
              force_ciphersuite=TLS-RSA-WITH-AES-256-CBC-SHA \
              trunc_hmac=1" \
             0 \
@@ -2206,7 +2263,8 @@
 
 run_test    "Large packet TLS 1.2 BlockCipher larger MAC" \
             "$P_SRV" \
-            "$P_CLI request_size=16384 force_version=tls1_2 force_ciphersuite=TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384" \
+            "$P_CLI request_size=16384 force_version=tls1_2 \
+             force_ciphersuite=TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384" \
             0 \
             -s "Read from client: 16384 bytes read"