From ba18f8b03e57e4b4bd7c49dc36b368cab84ca4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mois=C3=A9s=20Guimar=C3=A3es?= Date: Tue, 19 Nov 2013 17:13:32 -0300 Subject: [PATCH] added new function to retrieve SNI from a buffer. --- cyassl/internal.h | 2 + cyassl/ssl.h | 4 ++ src/ssl.c | 10 ++++ src/tls.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++ tests/api.c | 53 ++++++++++++++++++++ 5 files changed, 193 insertions(+) diff --git a/cyassl/internal.h b/cyassl/internal.h index 53e60c309..4d25bda6a 100644 --- a/cyassl/internal.h +++ b/cyassl/internal.h @@ -1153,6 +1153,8 @@ CYASSL_LOCAL void TLSX_SNI_SetOptions(TLSX* extensions, byte type, CYASSL_LOCAL byte TLSX_SNI_Status(TLSX* extensions, byte type); CYASSL_LOCAL word16 TLSX_SNI_GetRequest(TLSX* extensions, byte type, void** data); +CYASSL_LOCAL int TLSX_SNI_GetFromBuffer(const byte* buffer, word32 bufferSz, + byte type, byte* sni, word32* inOutSz); #endif #endif /* HAVE_SNI */ diff --git a/cyassl/ssl.h b/cyassl/ssl.h index 6e2e0a889..f462ab1d2 100644 --- a/cyassl/ssl.h +++ b/cyassl/ssl.h @@ -1187,6 +1187,10 @@ CYASSL_API unsigned char CyaSSL_SNI_Status(CYASSL* ssl, unsigned char type); CYASSL_API unsigned short CyaSSL_SNI_GetRequest(CYASSL *ssl, unsigned char type, void** data); +CYASSL_API int CyaSSL_SNI_GetFromBuffer( + const unsigned char* buffer, unsigned int bufferSz, + unsigned char type, unsigned char* sni, unsigned int* inOutSz); + #endif /* NO_CYASSL_SERVER */ #endif /* HAVE_SNI */ diff --git a/src/ssl.c b/src/ssl.c index afbc15b74..3cf3d4600 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -568,6 +568,16 @@ word16 CyaSSL_SNI_GetRequest(CYASSL* ssl, byte type, void** data) return 0; } + +int CyaSSL_SNI_GetFromBuffer(const byte* buffer, word32 bufferSz, byte type, + byte* sni, word32* inOutSz) +{ + if (buffer && bufferSz > 0 && sni && inOutSz && inOutSz > 0) + return TLSX_SNI_GetFromBuffer(buffer, bufferSz, type, sni, inOutSz); + + return BAD_FUNC_ARG; +} + #endif /* NO_CYASSL_SERVER */ #endif /* HAVE_SNI */ diff --git a/src/tls.c b/src/tls.c index 878e2e003..e1bae7c24 100644 --- a/src/tls.c +++ b/src/tls.c @@ -376,6 +376,14 @@ static INLINE void ato16(const byte* c, word16* u16) { *u16 = (c[0] << 8) | (c[1]); } + +#ifdef HAVE_SNI +/* convert a 24 bit integer into a 32 bit one */ +static INLINE void c24to32(const word24 u24, word32* u32) +{ + *u32 = (u24[0] << 16) | (u24[1] << 8) | u24[2]; +} +#endif #endif /* convert 32 bit integer to opaque */ @@ -854,6 +862,122 @@ void TLSX_SNI_SetOptions(TLSX* extensions, byte type, byte options) if (sni) sni->options = options; } + +int TLSX_SNI_GetFromBuffer(const byte* buffer, word32 bufferSz, + byte type, byte* sni, word32* inOutSz) +{ + word32 offset = 0; + word32 len32 = 0; + word16 len16 = 0; + + if (bufferSz < RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + CLIENT_HELLO_FIRST) + return INCOMPLETE_DATA; + + /* TLS record header */ + if ((enum ContentType) buffer[offset++] != handshake) + return BUFFER_ERROR; + + if (buffer[offset++] != SSLv3_MAJOR) + return BUFFER_ERROR; + + if (buffer[offset++] < TLSv1_MINOR) + return BUFFER_ERROR; + + ato16(buffer + offset, &len16); + offset += OPAQUE16_LEN; + + if (offset + len16 > bufferSz) + return INCOMPLETE_DATA; + + /* Handshake header */ + if ((enum HandShakeType) buffer[offset] != client_hello) + return BUFFER_ERROR; + + c24to32(buffer + offset + 1, &len32); + offset += HANDSHAKE_HEADER_SZ; + + if (offset + len32 > bufferSz) + return INCOMPLETE_DATA; + + /* client hello */ + offset += VERSION_SZ + RAN_LEN; /* version, random */ + + if (offset + buffer[offset] > bufferSz) + return INCOMPLETE_DATA; + + offset += ENUM_LEN + buffer[offset]; /* session id */ + + ato16(buffer + offset, &len16); + offset += OPAQUE16_LEN; /* cypher suites len */ + + if (offset + len16 > bufferSz) + return INCOMPLETE_DATA; + + offset += len16; /* cypher suites */ + + if (offset + buffer[offset] > bufferSz) + return INCOMPLETE_DATA; + + offset += ENUM_LEN + buffer[offset]; /* compression methods */ + + ato16(buffer + offset, &len16); + offset += OPAQUE16_LEN; /* EXTENSIONS LEN */ + + if (offset + len16 > bufferSz) + return INCOMPLETE_DATA; + + while (len16 > OPAQUE16_LEN + OPAQUE16_LEN) { + word16 extType; + word16 extLen; + + ato16(buffer + offset, &extType); + offset += OPAQUE16_LEN; + + ato16(buffer + offset, &extLen); + offset += OPAQUE16_LEN; + + if (offset + extLen > bufferSz) + return INCOMPLETE_DATA; + + if (extType != SERVER_NAME_INDICATION) { + offset += extLen; + continue; + } else { + word16 listLen; + + ato16(buffer + offset, &listLen); + offset += OPAQUE16_LEN; + + if (offset + listLen > bufferSz) + return INCOMPLETE_DATA; + + while (listLen > ENUM_LEN + OPAQUE16_LEN) { + byte sniType = buffer[offset++]; + word16 sniLen; + + ato16(buffer + offset, &sniLen); + offset += OPAQUE16_LEN; + + if (offset + sniLen > bufferSz) + return INCOMPLETE_DATA; + + if (sniType != type) { + offset += sniLen; + continue; + } + + *inOutSz = min(sniLen, *inOutSz); + XMEMCPY(sni, buffer + offset, *inOutSz); + + break; + } + + break; + } + } + + return 0; +} #endif #define SNI_FREE_ALL TLSX_SNI_FreeAll diff --git a/tests/api.c b/tests/api.c index ecd89a5ba..47ba6ea4b 100644 --- a/tests/api.c +++ b/tests/api.c @@ -332,6 +332,57 @@ static void verify_SNI_fake_matching(CYASSL* ssl) AssertStrEQ(name, request); } +static void test_CyaSSL_SNI_GetFromBuffer(void) +{ + byte buffer[] = { /* api.textmate.org */ + 0x16, 0x03, 0x01, 0x00, 0xc6, 0x01, 0x00, 0x00, 0xc2, 0x03, 0x03, 0x52, + 0x8b, 0x7b, 0xca, 0x69, 0xec, 0x97, 0xd5, 0x08, 0x03, 0x50, 0xfe, 0x3b, + 0x99, 0xc3, 0x20, 0xce, 0xa5, 0xf6, 0x99, 0xa5, 0x71, 0xf9, 0x57, 0x7f, + 0x04, 0x38, 0xf6, 0x11, 0x0b, 0xb8, 0xd3, 0x00, 0x00, 0x5e, 0x00, 0xff, + 0xc0, 0x24, 0xc0, 0x23, 0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x07, 0xc0, 0x08, + 0xc0, 0x28, 0xc0, 0x27, 0xc0, 0x14, 0xc0, 0x13, 0xc0, 0x11, 0xc0, 0x12, + 0xc0, 0x26, 0xc0, 0x25, 0xc0, 0x2a, 0xc0, 0x29, 0xc0, 0x05, 0xc0, 0x04, + 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x0f, 0xc0, 0x0e, 0xc0, 0x0c, 0xc0, 0x0d, + 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x05, 0x00, 0x04, 0x00, 0x35, + 0x00, 0x0a, 0x00, 0x67, 0x00, 0x6b, 0x00, 0x33, 0x00, 0x39, 0x00, 0x16, + 0x00, 0xaf, 0x00, 0xae, 0x00, 0x8d, 0x00, 0x8c, 0x00, 0x8a, 0x00, 0x8b, + 0x00, 0xb1, 0x00, 0xb0, 0x00, 0x2c, 0x00, 0x3b, 0x01, 0x00, 0x00, 0x3b, + 0x00, 0x00, 0x00, 0x15, 0x00, 0x13, 0x00, 0x00, 0x10, 0x61, 0x70, 0x69, + 0x2e, 0x74, 0x65, 0x78, 0x74, 0x6d, 0x61, 0x74, 0x65, 0x2e, 0x6f, 0x72, + 0x67, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x0c, 0x00, + 0x0a, 0x05, 0x01, 0x04, 0x01, 0x02, 0x01, 0x04, 0x03, 0x02, 0x03 + }; + + byte buffer2[] = { /* www.paypal.com */ + 0x16, 0x03, 0x03, 0x00, 0x64, 0x01, 0x00, 0x00, 0x60, 0x03, 0x03, 0x5c, + 0xc4, 0xb3, 0x8c, 0x87, 0xef, 0xa4, 0x09, 0xe0, 0x02, 0xab, 0x86, 0xca, + 0x76, 0xf0, 0x9e, 0x01, 0x65, 0xf6, 0xa6, 0x06, 0x13, 0x1d, 0x0f, 0xa5, + 0x79, 0xb0, 0xd4, 0x77, 0x22, 0xeb, 0x1a, 0x00, 0x00, 0x16, 0x00, 0x6b, + 0x00, 0x67, 0x00, 0x39, 0x00, 0x33, 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35, + 0x00, 0x2f, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x13, 0x00, 0x11, 0x00, 0x00, 0x0e, 0x77, 0x77, 0x77, + 0x2e, 0x70, 0x61, 0x79, 0x70, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x00, + 0x0d, 0x00, 0x06, 0x00, 0x04, 0x04, 0x01, 0x02, 0x01 + }; + + byte result[32] = {0}; + word32 length = 32; + +// AssertIntEQ(-228, CyaSSL_SNI_GetFromBuffer((const byte*) "\x16\x03\x00\x00\x01", 5, 0, +// result, &length)); + + AssertIntEQ(0, CyaSSL_SNI_GetFromBuffer(buffer, sizeof(buffer), 0, result, + &length)); + result[length] = 0; + AssertStrEQ("api.textmate.org", (const char*) result); + + AssertIntEQ(0, CyaSSL_SNI_GetFromBuffer(buffer2, sizeof(buffer2), 0, result, + &length)); + result[length] = 0; + AssertStrEQ("www.paypal.com", (const char*) result); +} + void test_CyaSSL_UseSNI(void) { callback_functions client_callbacks = {CyaSSLv23_client_method, 0, 0, 0}; @@ -390,6 +441,8 @@ void test_CyaSSL_UseSNI(void) server_callbacks.on_result = verify_SNI_fake_matching; test_CyaSSL_client_server(&client_callbacks, &server_callbacks); + + test_CyaSSL_SNI_GetFromBuffer(); } #endif /* HAVE_SNI */