libsec: implement server side SCSV preventing silly client fallbacks

silly clients (web*) reconnect when the handshake failed with a lower
protocol version, which allows downgrade attacks (POODLE). but instead
of stopping this madness, they invented a new magic TLSID to indicate
to the server that this connection attempt is a retry, and rely on the
server to notice and stop them from sabotaging themselfs.
This commit is contained in:
cinap_lenrek 2016-04-22 02:33:29 +02:00
parent 7b3334775e
commit 17a67eeb65

View file

@ -211,6 +211,7 @@ enum {
EProtocolVersion = 70, EProtocolVersion = 70,
EInsufficientSecurity = 71, EInsufficientSecurity = 71,
EInternalError = 80, EInternalError = 80,
EInappropriateFallback = 86,
EUserCanceled = 90, EUserCanceled = 90,
ENoRenegotiation = 100, ENoRenegotiation = 100,
EUnknownPSKidentity = 115, EUnknownPSKidentity = 115,
@ -295,6 +296,8 @@ enum {
TLS_PSK_WITH_CHACHA20_POLY1305 = 0xCCAB, TLS_PSK_WITH_CHACHA20_POLY1305 = 0xCCAB,
TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE, TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE,
TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C,
TLS_FALLBACK_SCSV = 0x5600,
}; };
// compression methods // compression methods
@ -446,6 +449,7 @@ static mpint* bytestomp(Bytes* bytes);
static void freebytes(Bytes* b); static void freebytes(Bytes* b);
static Ints* newints(int len); static Ints* newints(int len);
static void freeints(Ints* b); static void freeints(Ints* b);
static int lookupid(Ints* b, int id);
/* x509.c */ /* x509.c */
extern mpint* pkcs1padbuf(uchar *buf, int len, mpint *modulus); extern mpint* pkcs1padbuf(uchar *buf, int len, mpint *modulus);
@ -708,7 +712,11 @@ tlsServer2(int ctl, int hand,
tlsError(c, EIllegalParameter, "incompatible version"); tlsError(c, EIllegalParameter, "incompatible version");
goto Err; goto Err;
} }
if(c->version < ProtocolVersion
&& lookupid(m.u.clientHello.ciphers, TLS_FALLBACK_SCSV) >= 0){
tlsError(c, EInappropriateFallback, "inappropriate fallback");
goto Err;
}
cipher = okCipher(m.u.clientHello.ciphers, psklen > 0); cipher = okCipher(m.u.clientHello.ciphers, psklen > 0);
if(cipher < 0 || !setAlgs(c, cipher)) { if(cipher < 0 || !setAlgs(c, cipher)) {
tlsError(c, EHandshakeFailure, "no matching cipher suite"); tlsError(c, EHandshakeFailure, "no matching cipher suite");
@ -2019,10 +2027,10 @@ tlsError(TlsConnection *c, int err, char *fmt, ...)
va_end(arg); va_end(arg);
if(c->trace) if(c->trace)
c->trace("tlsError: %s\n", msg); c->trace("tlsError: %s\n", msg);
else if(c->erred) if(c->erred)
fprint(2, "double error: %r, %s", msg); fprint(2, "double error: %r, %s", msg);
else else
werrstr("tls: local %s", msg); errstr(msg, sizeof(msg));
c->erred = 1; c->erred = 1;
fprint(c->ctl, "alert %d", err); fprint(c->ctl, "alert %d", err);
} }
@ -2163,15 +2171,14 @@ setAlgs(TlsConnection *c, int a)
static int static int
okCipher(Ints *cv, int ispsk) okCipher(Ints *cv, int ispsk)
{ {
int i, j, c; int i, c;
for(i = 0; i < nelem(cipherAlgs); i++) { for(i = 0; i < nelem(cipherAlgs); i++) {
c = cipherAlgs[i].tlsid; c = cipherAlgs[i].tlsid;
if(!cipherAlgs[i].ok || isECDSA(c) || isDHE(c) || isPSK(c) != ispsk) if(!cipherAlgs[i].ok || isECDSA(c) || isDHE(c) || isPSK(c) != ispsk)
continue; continue;
for(j = 0; j < cv->len; j++) if(lookupid(cv, c) >= 0)
if(cv->data[j] == c) return c;
return c;
} }
return -1; return -1;
} }
@ -2179,13 +2186,12 @@ okCipher(Ints *cv, int ispsk)
static int static int
okCompression(Bytes *cv) okCompression(Bytes *cv)
{ {
int i, j, c; int i, c;
for(i = 0; i < nelem(compressors); i++) { for(i = 0; i < nelem(compressors); i++) {
c = compressors[i]; c = compressors[i];
for(j = 0; j < cv->len; j++) if(memchr(cv->data, c, cv->len) != nil)
if(cv->data[j] == c) return c;
return c;
} }
return -1; return -1;
} }
@ -3097,3 +3103,14 @@ freeints(Ints* b)
{ {
free(b); free(b);
} }
static int
lookupid(Ints* b, int id)
{
int i;
for(i=0; i<b->len; i++)
if(b->data[i] == id)
return i;
return -1;
}