diff options
author | Alessandro Ghedini <alessandro@ghedini.me> | 2015-08-01 00:04:16 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2015-08-01 14:22:28 +0200 |
commit | db9a7d810f9ee4c9cc49731f5fd9bdeae68d7eaa (patch) | |
tree | 48c335726aecd0dad52cc3e640f5015e3ff9e2e7 | |
parent | bafeb014e0d6a418f3de9db1481d4d1d133a8d17 (diff) | |
download | gnutls-db9a7d810f9ee4c9cc49731f5fd9bdeae68d7eaa.tar.gz |
handshake: check for TLS_FALLBACK_SCSV
If TLS_FALLBACK_SCSV was sent by the client during the handshake, and
the advertised protocol version is lower than GNUTLS_TLS_VERSION_MAX,
send the "Inappropriate fallback" fatal alert and abort the handshake.
This mechanism was defined in RFC7507.
-rw-r--r-- | lib/algorithms.h | 3 | ||||
-rw-r--r-- | lib/gnutls_alert.c | 6 | ||||
-rw-r--r-- | lib/gnutls_errors.c | 3 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 44 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 3 |
5 files changed, 39 insertions, 20 deletions
diff --git a/lib/algorithms.h b/lib/algorithms.h index b628d630bc..f9f0be19ce 100644 --- a/lib/algorithms.h +++ b/lib/algorithms.h @@ -28,6 +28,9 @@ #define GNUTLS_RENEGO_PROTECTION_REQUEST_MAJOR 0x00 #define GNUTLS_RENEGO_PROTECTION_REQUEST_MINOR 0xFF +#define GNUTLS_FALLBACK_SCSV_MAJOR 0x56 +#define GNUTLS_FALLBACK_SCSV_MINOR 0x00 + /* would allow for 256 ciphersuites */ #define MAX_CIPHERSUITE_SIZE 512 diff --git a/lib/gnutls_alert.c b/lib/gnutls_alert.c index 2744bf8f92..fb3a9d624b 100644 --- a/lib/gnutls_alert.c +++ b/lib/gnutls_alert.c @@ -67,6 +67,8 @@ static const gnutls_alert_entry sup_alerts[] = { ALERT_ENTRY(GNUTLS_A_SSL3_NO_CERTIFICATE, N_("No certificate (SSL 3.0)")), ALERT_ENTRY(GNUTLS_A_INTERNAL_ERROR, N_("Internal error")), + ALERT_ENTRY(GNUTLS_A_INAPPROPRIATE_FALLBACK, + N_("Inappropriate fallback")), ALERT_ENTRY(GNUTLS_A_NO_RENEGOTIATION, N_("No renegotiation is allowed")), ALERT_ENTRY(GNUTLS_A_CERTIFICATE_UNOBTAINABLE, @@ -275,6 +277,10 @@ int gnutls_error_to_alert(int err, int *level) ret = GNUTLS_A_INTERNAL_ERROR; _level = GNUTLS_AL_FATAL; break; + case GNUTLS_E_INAPPROPRIATE_FALLBACK: + ret = GNUTLS_A_INAPPROPRIATE_FALLBACK; + _level = GNUTLS_AL_FATAL; + break; case GNUTLS_E_OPENPGP_GETKEY_FAILED: ret = GNUTLS_A_CERTIFICATE_UNOBTAINABLE; _level = GNUTLS_AL_FATAL; diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index 9838b758ad..885427b980 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -73,6 +73,9 @@ static const gnutls_error_entry error_entries[] = { GNUTLS_E_INVALID_SESSION), ERROR_ENTRY(N_("GnuTLS internal error."), GNUTLS_E_INTERNAL_ERROR), + ERROR_ENTRY(N_( + "A connection with inappropriate fallback was attempted."), + GNUTLS_E_INAPPROPRIATE_FALLBACK), ERROR_ENTRY(N_("An illegal TLS extension was received."), GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION), ERROR_ENTRY(N_("A TLS fatal alert has been received."), diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index 2566bceb35..9b92c1356b 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -909,28 +909,32 @@ _gnutls_server_select_suite(gnutls_session_t session, uint8_t * data, * supported by the peer. */ - /* First, check for safe renegotiation SCSV. - */ - if (session->internals.priorities.sr != SR_DISABLED) { - unsigned int offset; - - for (offset = 0; offset < datalen; offset += 2) { - /* TLS_RENEGO_PROTECTION_REQUEST = { 0x00, 0xff } */ - if (data[offset] == - GNUTLS_RENEGO_PROTECTION_REQUEST_MAJOR - && data[offset + 1] == - GNUTLS_RENEGO_PROTECTION_REQUEST_MINOR) { - _gnutls_handshake_log - ("HSK[%p]: Received safe renegotiation CS\n", - session); - retval = _gnutls_ext_sr_recv_cs(session); - if (retval < 0) { - gnutls_assert(); - return retval; - } - break; + for (i = 0; i < datalen; i += 2) { + /* TLS_RENEGO_PROTECTION_REQUEST = { 0x00, 0xff } */ + if (session->internals.priorities.sr != SR_DISABLED && + data[i] == GNUTLS_RENEGO_PROTECTION_REQUEST_MAJOR && + data[i + 1] == GNUTLS_RENEGO_PROTECTION_REQUEST_MINOR) { + _gnutls_handshake_log + ("HSK[%p]: Received safe renegotiation CS\n", + session); + retval = _gnutls_ext_sr_recv_cs(session); + if (retval < 0) { + gnutls_assert(); + return retval; } } + + /* TLS_FALLBACK_SCSV */ + if (data[i] == GNUTLS_FALLBACK_SCSV_MAJOR && + data[i + 1] == GNUTLS_FALLBACK_SCSV_MINOR) { + _gnutls_handshake_log + ("HSK[%p]: Received fallback CS\n", + session); + + if (gnutls_protocol_get_version(session) != + GNUTLS_TLS_VERSION_MAX) + return GNUTLS_E_INAPPROPRIATE_FALLBACK; + } } pk_algos_size = MAX_ALGOS; diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 58da371f57..36173cabe8 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -372,6 +372,7 @@ typedef enum { * @GNUTLS_A_INSUFFICIENT_SECURITY: Insufficient security. * @GNUTLS_A_USER_CANCELED: User canceled. * @GNUTLS_A_INTERNAL_ERROR: Internal error. + * @GNUTLS_A_INAPPROPRIATE_FALLBACK: Inappropriate fallback, * @GNUTLS_A_NO_RENEGOTIATION: No renegotiation is allowed. * @GNUTLS_A_CERTIFICATE_UNOBTAINABLE: Could not retrieve the * specified certificate. @@ -409,6 +410,7 @@ typedef enum { GNUTLS_A_PROTOCOL_VERSION = 70, GNUTLS_A_INSUFFICIENT_SECURITY, GNUTLS_A_INTERNAL_ERROR = 80, + GNUTLS_A_INAPPROPRIATE_FALLBACK = 86, GNUTLS_A_USER_CANCELED = 90, GNUTLS_A_NO_RENEGOTIATION = 100, GNUTLS_A_UNSUPPORTED_EXTENSION = 110, @@ -2389,6 +2391,7 @@ int gnutls_fips140_mode_enabled(void); #define GNUTLS_E_PKCS1_WRONG_PAD -57 #define GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION -58 #define GNUTLS_E_INTERNAL_ERROR -59 +#define GNUTLS_E_INAPPROPRIATE_FALLBACK -60 /*GNUTLS_A_INAPPROPRIATE_FALLBACK*/ #define GNUTLS_E_DH_PRIME_UNACCEPTABLE -63 #define GNUTLS_E_FILE_ERROR -64 #define GNUTLS_E_TOO_MANY_EMPTY_PACKETS -78 |