summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlessandro Ghedini <alessandro@ghedini.me>2015-08-01 00:04:16 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2015-08-01 14:22:28 +0200
commitdb9a7d810f9ee4c9cc49731f5fd9bdeae68d7eaa (patch)
tree48c335726aecd0dad52cc3e640f5015e3ff9e2e7
parentbafeb014e0d6a418f3de9db1481d4d1d133a8d17 (diff)
downloadgnutls-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.h3
-rw-r--r--lib/gnutls_alert.c6
-rw-r--r--lib/gnutls_errors.c3
-rw-r--r--lib/gnutls_handshake.c44
-rw-r--r--lib/includes/gnutls/gnutls.h.in3
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