Merge changes for leaner memory footprint
diff --git a/ChangeLog b/ChangeLog
index 142ea30..dc008f8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -20,6 +20,8 @@
* Optimize for RAM usage in example config.h for NSA Suite B profile.
* Add POLARSSL_REMOVE_ARC4_CIPHERSUITES to allow removing RC4 ciphersuites
from the default list (inactive by default).
+ * Add server-side enforcement of sent renegotiation requests
+ (ssl_set_renegotiation_enforced())
* Add SSL_CIPHERSUITES config.h flag to allow specifying a list of
ciphersuites to use and save some memory if the list is small.
@@ -61,6 +63,8 @@
exchange that caused some handshakes to fail with other implementations.
(Failure rate <= 1/255 with common DHM moduli.)
* Disable broken Sparc64 bn_mul assembly (found by Florian Obser).
+ * Fix base64_decode() to return and check length correctly (in case of
+ tight buffers)
= PolarSSL 1.3.7 released on 2014-05-02
Features
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h
index f1de499..730dc39 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -222,6 +222,9 @@
#define SSL_RENEGOTIATION_DISABLED 0
#define SSL_RENEGOTIATION_ENABLED 1
+#define SSL_RENEGOTIATION_NOT_ENFORCED -1
+#define SSL_RENEGO_MAX_RECORDS_DEFAULT 16
+
#define SSL_LEGACY_NO_RENEGOTIATION 0
#define SSL_LEGACY_ALLOW_RENEGOTIATION 1
#define SSL_LEGACY_BREAK_HANDSHAKE 2
@@ -682,6 +685,7 @@
*/
int state; /*!< SSL handshake: current state */
int renegotiation; /*!< Initial or renegotiation */
+ int renego_records_seen; /*!< Records since renego request */
int major_ver; /*!< equal to SSL_MAJOR_VERSION_3 */
int minor_ver; /*!< either 0 (SSL3) or 1 (TLS1.0) */
@@ -806,6 +810,7 @@
int verify_result; /*!< verification result */
int disable_renegotiation; /*!< enable/disable renegotiation */
int allow_legacy_renegotiation; /*!< allow legacy renegotiation */
+ int renego_max_records; /*!< grace period for renegotiation */
const int *ciphersuite_list[4]; /*!< allowed ciphersuites / version */
#if defined(POLARSSL_SSL_SET_CURVES)
const ecp_group_id *curve_list; /*!< allowed curves */
@@ -1484,6 +1489,33 @@
void ssl_legacy_renegotiation( ssl_context *ssl, int allow_legacy );
/**
+ * \brief Enforce server-requested renegotiation.
+ * (Default: enforced, max_records = 16)
+ * (No effect on client.)
+ *
+ * When a server requests a renegotiation, the client can
+ * comply or ignore the request. This function allows the
+ * server to decide if it should enforce its renegotiation
+ * requests by closing the connection if the client doesn't
+ * initiate a renegotiation.
+ *
+ * However, records could already be in transit from the
+ * client to the server when the request is emitted. In order
+ * to increase reliability, the server can accept a number of
+ * records containing application data before the ClientHello
+ * that was requested.
+ *
+ * The optimal value is highly dependent on the specific usage
+ * scenario.
+ *
+ * \param ssl SSL context
+ * \param max_records Use SSL_RENEGOTIATION_NOT_ENFORCED if you don't want to
+ * enforce renegotiation, or a non-negative value to enforce
+ * it but allow for a grace period of max_records records.
+ */
+void ssl_set_renegotiation_enforced( ssl_context *ssl, int max_records );
+
+/**
* \brief Return the number of data bytes available to read
*
* \param ssl SSL context
diff --git a/library/base64.c b/library/base64.c
index 5b1418b..39a8323 100644
--- a/library/base64.c
+++ b/library/base64.c
@@ -172,6 +172,7 @@
return( 0 );
n = ( ( n * 6 ) + 7 ) >> 3;
+ n -= j;
if( dst == NULL || *dlen < n )
{
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 373a1f5..8040f90 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -3055,7 +3055,10 @@
ssl->handshake = NULL;
if( ssl->renegotiation == SSL_RENEGOTIATION )
+ {
ssl->renegotiation = SSL_RENEGOTIATION_DONE;
+ ssl->renego_records_seen = 0;
+ }
/*
* Switch in our now active transform context
@@ -3345,6 +3348,8 @@
ssl_set_ciphersuites( ssl, ssl_list_ciphersuites() );
+ ssl->renego_max_records = SSL_RENEGO_MAX_RECORDS_DEFAULT;
+
#if defined(POLARSSL_DHM_C)
if( ( ret = mpi_read_string( &ssl->dhm_P, 16,
POLARSSL_DHM_RFC5114_MODP_1024_P) ) != 0 ||
@@ -3435,6 +3440,8 @@
ssl->transform_in = NULL;
ssl->transform_out = NULL;
+ ssl->renego_records_seen = 0;
+
memset( ssl->out_ctr, 0, SSL_BUFFER_LEN );
memset( ssl->in_ctr, 0, SSL_BUFFER_LEN );
@@ -3947,6 +3954,11 @@
ssl->allow_legacy_renegotiation = allow_legacy;
}
+void ssl_set_renegotiation_enforced( ssl_context *ssl, int max_records )
+{
+ ssl->renego_max_records = max_records;
+}
+
#if defined(POLARSSL_SSL_SESSION_TICKETS)
int ssl_set_session_tickets( ssl_context *ssl, int use_tickets )
{
@@ -4291,9 +4303,15 @@
}
else if( ssl->renegotiation == SSL_RENEGOTIATION_PENDING )
{
- SSL_DEBUG_MSG( 1, ( "renegotiation requested, "
- "but not honored by client" ) );
- return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE );
+ ssl->renego_records_seen++;
+
+ if( ssl->renego_max_records >= 0 &&
+ ssl->renego_records_seen > ssl->renego_max_records )
+ {
+ SSL_DEBUG_MSG( 1, ( "renegotiation requested, "
+ "but not honored by client" ) );
+ return( POLARSSL_ERR_SSL_UNEXPECTED_MESSAGE );
+ }
}
else if( ssl->in_msgtype != SSL_MSG_APPLICATION_DATA )
{
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 935a7a5..c470363 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -99,6 +99,7 @@
#define DFL_RENEGOTIATION SSL_RENEGOTIATION_DISABLED
#define DFL_ALLOW_LEGACY SSL_LEGACY_NO_RENEGOTIATION
#define DFL_RENEGOTIATE 0
+#define DFL_RENEGO_DELAY -2
#define DFL_MIN_VERSION -1
#define DFL_MAX_VERSION -1
#define DFL_AUTH_MODE SSL_VERIFY_OPTIONAL
@@ -157,6 +158,7 @@
int renegotiation; /* enable / disable renegotiation */
int allow_legacy; /* allow legacy renegotiation */
int renegotiate; /* attempt renegotiation? */
+ int renego_delay; /* delay before enforcing renegotiation */
int min_version; /* minimum protocol version accepted */
int max_version; /* maximum protocol version accepted */
int auth_mode; /* verify mode for connection */
@@ -674,6 +676,7 @@
opt.renegotiation = DFL_RENEGOTIATION;
opt.allow_legacy = DFL_ALLOW_LEGACY;
opt.renegotiate = DFL_RENEGOTIATE;
+ opt.renego_delay = DFL_RENEGO_DELAY;
opt.min_version = DFL_MIN_VERSION;
opt.max_version = DFL_MAX_VERSION;
opt.auth_mode = DFL_AUTH_MODE;
@@ -763,6 +766,10 @@
if( opt.renegotiate < 0 || opt.renegotiate > 1 )
goto usage;
}
+ else if( strcmp( p, "renego_delay" ) == 0 )
+ {
+ opt.renego_delay = atoi( q );
+ }
else if( strcmp( p, "min_version" ) == 0 )
{
if( strcmp( q, "ssl3" ) == 0 )
@@ -1262,6 +1269,8 @@
ssl_set_renegotiation( &ssl, opt.renegotiation );
ssl_legacy_renegotiation( &ssl, opt.allow_legacy );
+ if( opt.renego_delay != DFL_RENEGO_DELAY )
+ ssl_set_renegotiation_enforced( &ssl, opt.renego_delay );
#if defined(POLARSSL_X509_CRT_PARSE_C)
if( strcmp( opt.ca_path, "none" ) != 0 &&
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 102a5b5..d4a4143 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -616,9 +616,11 @@
-c "found renegotiation extension" \
-c "=> renegotiate" \
-S "=> renegotiate" \
- -S "write hello request"
+ -S "write hello request" \
+ -c "SSL - An unexpected message was received from our peer" \
+ -c "failed"
-run_test "Renegotiation #5 (server-initiated, client-rejected)" \
+run_test "Renegotiation #5 (server-initiated, client-rejected, default)" \
"$P_SRV debug_level=4 renegotiation=1 renegotiate=1" \
"$P_CLI debug_level=4 renegotiation=0" \
0 \
@@ -630,9 +632,73 @@
-C "=> renegotiate" \
-S "=> renegotiate" \
-s "write hello request" \
+ -S "SSL - An unexpected message was received from our peer" \
+ -S "failed"
+
+run_test "Renegotiation #6 (server-initiated, client-rejected, not enforced)" \
+ "$P_SRV debug_level=4 renegotiation=1 renegotiate=1 \
+ renego_delay=-1" \
+ "$P_CLI debug_level=4 renegotiation=0" \
+ 0 \
+ -C "client hello, adding renegotiation extension" \
+ -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
+ -S "found renegotiation extension" \
+ -s "server hello, secure renegotiation extension" \
+ -c "found renegotiation extension" \
+ -C "=> renegotiate" \
+ -S "=> renegotiate" \
+ -s "write hello request" \
+ -S "SSL - An unexpected message was received from our peer" \
+ -S "failed"
+
+run_test "Renegotiation #7 (server-initiated, client-rejected, delay 1)" \
+ "$P_SRV debug_level=4 renegotiation=1 renegotiate=1 \
+ renego_delay=1" \
+ "$P_CLI debug_level=4 renegotiation=0" \
+ 0 \
+ -C "client hello, adding renegotiation extension" \
+ -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
+ -S "found renegotiation extension" \
+ -s "server hello, secure renegotiation extension" \
+ -c "found renegotiation extension" \
+ -C "=> renegotiate" \
+ -S "=> renegotiate" \
+ -s "write hello request" \
+ -S "SSL - An unexpected message was received from our peer" \
+ -S "failed"
+
+run_test "Renegotiation #8 (server-initiated, client-rejected, delay 0)" \
+ "$P_SRV debug_level=4 renegotiation=1 renegotiate=1 \
+ renego_delay=0" \
+ "$P_CLI debug_level=4 renegotiation=0" \
+ 0 \
+ -C "client hello, adding renegotiation extension" \
+ -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
+ -S "found renegotiation extension" \
+ -s "server hello, secure renegotiation extension" \
+ -c "found renegotiation extension" \
+ -C "=> renegotiate" \
+ -S "=> renegotiate" \
+ -s "write hello request" \
-s "SSL - An unexpected message was received from our peer" \
-s "failed"
+run_test "Renegotiation #9 (server-initiated, client-accepted, delay 0)" \
+ "$P_SRV debug_level=4 renegotiation=1 renegotiate=1 \
+ renego_delay=0" \
+ "$P_CLI debug_level=4 renegotiation=1" \
+ 0 \
+ -c "client hello, adding renegotiation extension" \
+ -s "received TLS_EMPTY_RENEGOTIATION_INFO" \
+ -s "found renegotiation extension" \
+ -s "server hello, secure renegotiation extension" \
+ -c "found renegotiation extension" \
+ -c "=> renegotiate" \
+ -s "=> renegotiate" \
+ -s "write hello request" \
+ -S "SSL - An unexpected message was received from our peer" \
+ -S "failed"
+
# Tests for auth_mode
run_test "Authentication #1 (server badcert, client required)" \
diff --git a/tests/suites/test_suite_base64.data b/tests/suites/test_suite_base64.data
index c400ccb..4508d89 100644
--- a/tests/suites/test_suite_base64.data
+++ b/tests/suites/test_suite_base64.data
@@ -55,6 +55,33 @@
Base64 decode (Invalid char after equal signs)
base64_decode:"zm=masd":"":POLARSSL_ERR_BASE64_INVALID_CHARACTER
+Base64 encode hex #1
+base64_encode_hex:"010203040506070809":"AQIDBAUGBwgJ":13:0
+
+Base64 encode hex #2 (buffer too small)
+base64_encode_hex:"010203040506070809":"AQIDBAUGBwgJ":12:POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL
+
+Base64 encode hex #3
+base64_encode_hex:"0102030405060708":"AQIDBAUGBwg=":13:0
+
+Base64 encode hex #4
+base64_encode_hex:"01020304050607":"AQIDBAUGBw==":13:0
+
+Base64 decode hex #1
+base64_decode_hex:"AQIDBAUGBwgJ":"010203040506070809":9:0
+
+Base64 decode hex #2 (buffer too small)
+base64_decode_hex:"AQIDBAUGBwgJ":"010203040506070809":8:POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL
+
+Base64 decode hex #3
+base64_decode_hex:"AQIDBAUGBwg=":"0102030405060708":8:0
+
+Base64 decode hex #4
+base64_decode_hex:"AQIDBAUGBw==":"01020304050607":7:0
+
+Base64 decode hex #5 (buffer too small)
+base64_decode_hex:"AQIDBAUGBw==":"01020304050607":6:POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL
+
Base64 Selftest
depends_on:POLARSSL_SELF_TEST
base64_selftest:
diff --git a/tests/suites/test_suite_base64.function b/tests/suites/test_suite_base64.function
index 3ba59dc..a9a580c 100644
--- a/tests/suites/test_suite_base64.function
+++ b/tests/suites/test_suite_base64.function
@@ -48,6 +48,45 @@
}
/* END_CASE */
+/* BEGIN_CASE */
+void base64_encode_hex( char *src_hex, char *dst, int dst_buf_size,
+ int result )
+{
+ unsigned char *src, *res;
+ size_t len = dst_buf_size, src_len;
+
+ src = unhexify_alloc( src_hex, &src_len );
+ res = zero_alloc( dst_buf_size );
+
+ TEST_ASSERT( base64_encode( res, &len, src, src_len ) == result );
+ if( result == 0 )
+ {
+ TEST_ASSERT( len == strlen( dst ) );
+ TEST_ASSERT( memcmp( dst, res, len ) == 0 );
+ }
+}
+/* END_CASE */
+
+/* BEGIN_CASE */
+void base64_decode_hex( char *src, char *dst_hex, int dst_buf_size,
+ int result )
+{
+ unsigned char *dst, *res;
+ size_t len = dst_buf_size, dst_len;
+
+ dst = unhexify_alloc( dst_hex, &dst_len );
+ res = zero_alloc( dst_buf_size );
+
+ TEST_ASSERT( base64_decode( res, &len, (unsigned char *) src,
+ strlen( src ) ) == result );
+ if( result == 0 )
+ {
+ TEST_ASSERT( len == dst_len );
+ TEST_ASSERT( memcmp( dst, res, len ) == 0 );
+ }
+}
+/* END_CASE */
+
/* BEGIN_CASE depends_on:POLARSSL_SELF_TEST */
void base64_selftest()
{