Boundaries check for server hello parsing.

-- added totalSz to the function parameters
-- INCOMPLETE DATA checked only once with hello size against buffer size
-- BUFFER_ERROR returned in case of message overflow (piece larger than the hello size)
-- OPAQUE16_LEN used whenever 2 bytes are needed.
-- Session id checking improved.
This commit is contained in:
Moisés Guimarães
2014-02-16 12:15:10 -03:00
parent c03263ae70
commit 95bc954273

View File

@@ -70,7 +70,8 @@ CYASSL_CALLBACKS needs LARGE_STATIC_BUFFERS, please add LARGE_STATIC_BUFFERS
#ifndef NO_CYASSL_CLIENT #ifndef NO_CYASSL_CLIENT
static int DoHelloVerifyRequest(CYASSL* ssl, const byte* input, word32*); static int DoHelloVerifyRequest(CYASSL* ssl, const byte* input, word32*);
static int DoServerHello(CYASSL* ssl, const byte* input, word32*, word32); static int DoServerHello(CYASSL* ssl, const byte* input, word32*, word32,
word32);
static int DoServerKeyExchange(CYASSL* ssl, const byte* input, word32*); static int DoServerKeyExchange(CYASSL* ssl, const byte* input, word32*);
#ifndef NO_CERTS #ifndef NO_CERTS
static int DoCertificateRequest(CYASSL* ssl, const byte* input,word32*); static int DoCertificateRequest(CYASSL* ssl, const byte* input,word32*);
@@ -3778,7 +3779,7 @@ static int DoHandShakeMsgType(CYASSL* ssl, byte* input, word32* inOutIdx,
case server_hello: case server_hello:
CYASSL_MSG("processing server hello"); CYASSL_MSG("processing server hello");
ret = DoServerHello(ssl, input, inOutIdx, size); ret = DoServerHello(ssl, input, inOutIdx, totalSz, size);
break; break;
#ifndef NO_CERTS #ifndef NO_CERTS
@@ -7459,31 +7460,44 @@ static void PickHashSigAlgo(CYASSL* ssl,
static int DoServerHello(CYASSL* ssl, const byte* input, word32* inOutIdx, static int DoServerHello(CYASSL* ssl, const byte* input, word32* inOutIdx,
word32 helloSz) word32 totalSz, word32 helloSz)
{ {
byte b; byte b;
byte compression;
ProtocolVersion pv; ProtocolVersion pv;
word32 i = *inOutIdx; byte compression;
word32 begin = i; word32 i = *inOutIdx;
word32 begin = i;
#ifdef CYASSL_CALLBACKS #ifdef CYASSL_CALLBACKS
if (ssl->hsInfoOn) AddPacketName("ServerHello", &ssl->handShakeInfo); if (ssl->hsInfoOn) AddPacketName("ServerHello", &ssl->handShakeInfo);
if (ssl->toInfoOn) AddLateName("ServerHello", &ssl->timeoutInfo); if (ssl->toInfoOn) AddLateName("ServerHello", &ssl->timeoutInfo);
#endif #endif
XMEMCPY(&pv, input + i, sizeof(pv));
i += (word32)sizeof(pv); /* make sure can read the server hello */
if (begin + helloSz > totalSz)
return INCOMPLETE_DATA;
/* protocol version, random and session id length check */
if ((i - begin) + OPAQUE16_LEN + RAN_LEN + ENUM_LEN > helloSz)
return BUFFER_ERROR;
/* protocol version */
XMEMCPY(&pv, input + i, OPAQUE16_LEN);
i += OPAQUE16_LEN;
if (pv.minor > ssl->version.minor) { if (pv.minor > ssl->version.minor) {
CYASSL_MSG("Server using higher version, fatal error"); CYASSL_MSG("Server using higher version, fatal error");
return VERSION_ERROR; return VERSION_ERROR;
} }
else if (pv.minor < ssl->version.minor) { else if (pv.minor < ssl->version.minor) {
CYASSL_MSG("server using lower version"); CYASSL_MSG("server using lower version");
if (!ssl->options.downgrade) { if (!ssl->options.downgrade) {
CYASSL_MSG(" no downgrade allowed, fatal error"); CYASSL_MSG(" no downgrade allowed, fatal error");
return VERSION_ERROR; return VERSION_ERROR;
} }
else if (pv.minor == SSLv3_MINOR) {
if (pv.minor == SSLv3_MINOR) {
/* turn off tls */ /* turn off tls */
CYASSL_MSG(" downgrading to SSLv3"); CYASSL_MSG(" downgrading to SSLv3");
ssl->options.tls = 0; ssl->options.tls = 0;
@@ -7501,14 +7515,31 @@ static void PickHashSigAlgo(CYASSL* ssl,
ssl->version.minor = TLSv1_1_MINOR; ssl->version.minor = TLSv1_1_MINOR;
} }
} }
/* random */
XMEMCPY(ssl->arrays->serverRandom, input + i, RAN_LEN); XMEMCPY(ssl->arrays->serverRandom, input + i, RAN_LEN);
i += RAN_LEN; i += RAN_LEN;
/* session id */
b = input[i++]; b = input[i++];
if (b) {
if (b == ID_LEN) {
if ((i - begin) + ID_LEN > helloSz)
return BUFFER_ERROR;
XMEMCPY(ssl->arrays->sessionID, input + i, min(b, ID_LEN)); XMEMCPY(ssl->arrays->sessionID, input + i, min(b, ID_LEN));
i += b; i += ID_LEN;
ssl->options.haveSessionId = 1; ssl->options.haveSessionId = 1;
} }
else if (b) {
CYASSL_MSG("Invalid session ID size");
return BUFFER_ERROR; /* session ID nor 0 neither 32 bytes long */
}
/* suite and compression */
if ((i - begin) + OPAQUE16_LEN + ENUM_LEN > helloSz)
return BUFFER_ERROR;
ssl->options.cipherSuite0 = input[i++]; ssl->options.cipherSuite0 = input[i++];
ssl->options.cipherSuite = input[i++]; ssl->options.cipherSuite = input[i++];
compression = input[i++]; compression = input[i++];
@@ -7519,17 +7550,23 @@ static void PickHashSigAlgo(CYASSL* ssl,
} }
*inOutIdx = i; *inOutIdx = i;
/* tls extensions */
if ( (i - begin) < helloSz) { if ( (i - begin) < helloSz) {
#ifdef HAVE_TLS_EXTENSIONS #ifdef HAVE_TLS_EXTENSIONS
if (IsTLS(ssl)) { if (IsTLS(ssl)) {
int ret = 0; int ret = 0;
word16 totalExtSz; word16 totalExtSz;
Suites clSuites; /* just for compatibility right now */ Suites clSuites; /* just for compatibility right now */
if ((i - begin) + OPAQUE16_LEN > helloSz)
return BUFFER_ERROR;
ato16(&input[i], &totalExtSz); ato16(&input[i], &totalExtSz);
i += LENGTH_SZ; i += OPAQUE16_LEN;
if (totalExtSz > helloSz + begin - i)
return INCOMPLETE_DATA; if ((i - begin) + totalExtSz > helloSz)
return BUFFER_ERROR;
if ((ret = TLSX_Parse(ssl, (byte *) input + i, if ((ret = TLSX_Parse(ssl, (byte *) input + i,
totalExtSz, 0, &clSuites))) totalExtSz, 0, &clSuites)))
@@ -7540,7 +7577,7 @@ static void PickHashSigAlgo(CYASSL* ssl,
} }
else else
#endif #endif
*inOutIdx = begin + helloSz; /* skip extensions */ *inOutIdx = begin + helloSz; /* skip extensions */
} }
ssl->options.serverState = SERVER_HELLO_COMPLETE; ssl->options.serverState = SERVER_HELLO_COMPLETE;
@@ -7549,7 +7586,8 @@ static void PickHashSigAlgo(CYASSL* ssl,
if (ssl->options.haveSessionId && XMEMCMP(ssl->arrays->sessionID, if (ssl->options.haveSessionId && XMEMCMP(ssl->arrays->sessionID,
ssl->session.sessionID, ID_LEN) == 0) { ssl->session.sessionID, ID_LEN) == 0) {
if (SetCipherSpecs(ssl) == 0) { if (SetCipherSpecs(ssl) == 0) {
int ret = -1; int ret = -1;
XMEMCPY(ssl->arrays->masterSecret, XMEMCPY(ssl->arrays->masterSecret,
ssl->session.masterSecret, SECRET_LEN); ssl->session.masterSecret, SECRET_LEN);
#ifdef NO_OLD_TLS #ifdef NO_OLD_TLS
@@ -7563,6 +7601,7 @@ static void PickHashSigAlgo(CYASSL* ssl,
ret = DeriveKeys(ssl); ret = DeriveKeys(ssl);
#endif #endif
ssl->options.serverState = SERVER_HELLODONE_COMPLETE; ssl->options.serverState = SERVER_HELLODONE_COMPLETE;
return ret; return ret;
} }
else { else {
@@ -7580,6 +7619,7 @@ static void PickHashSigAlgo(CYASSL* ssl,
DtlsPoolReset(ssl); DtlsPoolReset(ssl);
} }
#endif #endif
return SetCipherSpecs(ssl); return SetCipherSpecs(ssl);
} }
@@ -10076,8 +10116,10 @@ static void PickHashSigAlgo(CYASSL* ssl,
ssl->options.resuming = 1; /* client wants to resume */ ssl->options.resuming = 1; /* client wants to resume */
CYASSL_MSG("Client wants to resume session"); CYASSL_MSG("Client wants to resume session");
} }
else if (b) else if (b) {
CYASSL_MSG("Invalid session ID size");
return BUFFER_ERROR; /* session ID nor 0 neither 32 bytes long */ return BUFFER_ERROR; /* session ID nor 0 neither 32 bytes long */
}
#ifdef CYASSL_DTLS #ifdef CYASSL_DTLS
/* cookie */ /* cookie */