diff --git a/configure.ac b/configure.ac index 17e3f4871..221c5aa63 100644 --- a/configure.ac +++ b/configure.ac @@ -1061,6 +1061,29 @@ then AM_CFLAGS="$AM_CFLAGS -DHAVE_TLS_EXTENSIONS -DHAVE_SNI" fi +# Maximum Fragment Length +AC_ARG_ENABLE([maxfragment], + [ --enable-maxfragment Enable Maximum Fragment Length (default: disabled)], + [ ENABLED_MAX_FRAGMENT=$enableval ], + [ ENABLED_MAX_FRAGMENT=no ] + ) + +if test "x$ENABLED_MAX_FRAGMENT" = "xyes" +then + AM_CFLAGS="$AM_CFLAGS -DHAVE_TLS_EXTENSIONS -DHAVE_MAX_FRAGMENT" +fi + +# TLS Extensions +AC_ARG_ENABLE([tlsx], + [ --enable-tlsx Enable all TLS Extensions (default: disabled)], + [ ENABLED_TLSX=$enableval ], + [ ENABLED_TLSX=no ] + ) + +if test "x$ENABLED_TLSX" = "xyes" +then + AM_CFLAGS="$AM_CFLAGS -DHAVE_TLS_EXTENSIONS -DHAVE_SNI -DHAVE_MAX_FRAGMENT" +fi #valgrind AC_ARG_ENABLE([valgrind], @@ -1429,6 +1452,8 @@ echo " * Persistent session cache: $ENABLED_SAVESESSION" echo " * Persistent cert cache: $ENABLED_SAVECERT" echo " * NTRU: $ENABLED_NTRU" echo " * SNI: $ENABLED_SNI" +echo " * Maximum Fragment Length: $ENABLED_MAX_FRAGMENT" +echo " * All TLS Extensions: $ENABLED_TLSX" echo " * valgrind unit tests: $ENABLED_VALGRIND" echo " * LIBZ: $ENABLED_LIBZ" echo " * Examples: $ENABLED_EXAMPLES" diff --git a/cyassl/error.h b/cyassl/error.h index 3f60642c9..5526e0761 100644 --- a/cyassl/error.h +++ b/cyassl/error.h @@ -30,95 +30,96 @@ #endif enum CyaSSL_ErrorCodes { - INPUT_CASE_ERROR = -201, /* process input state error */ - PREFIX_ERROR = -202, /* bad index to key rounds */ - MEMORY_ERROR = -203, /* out of memory */ - VERIFY_FINISHED_ERROR = -204, /* verify problem on finished */ - VERIFY_MAC_ERROR = -205, /* verify mac problem */ - PARSE_ERROR = -206, /* parse error on header */ - UNKNOWN_HANDSHAKE_TYPE = -207, /* weird handshake type */ - SOCKET_ERROR_E = -208, /* error state on socket */ - SOCKET_NODATA = -209, /* expected data, not there */ - INCOMPLETE_DATA = -210, /* don't have enough data to - complete task */ - UNKNOWN_RECORD_TYPE = -211, /* unknown type in record hdr */ - DECRYPT_ERROR = -212, /* error during decryption */ - FATAL_ERROR = -213, /* recvd alert fatal error */ - ENCRYPT_ERROR = -214, /* error during encryption */ - FREAD_ERROR = -215, /* fread problem */ - NO_PEER_KEY = -216, /* need peer's key */ - NO_PRIVATE_KEY = -217, /* need the private key */ - RSA_PRIVATE_ERROR = -218, /* error during rsa priv op */ - NO_DH_PARAMS = -219, /* server missing DH params */ - BUILD_MSG_ERROR = -220, /* build message failure */ + INPUT_CASE_ERROR = -201, /* process input state error */ + PREFIX_ERROR = -202, /* bad index to key rounds */ + MEMORY_ERROR = -203, /* out of memory */ + VERIFY_FINISHED_ERROR = -204, /* verify problem on finished */ + VERIFY_MAC_ERROR = -205, /* verify mac problem */ + PARSE_ERROR = -206, /* parse error on header */ + UNKNOWN_HANDSHAKE_TYPE = -207, /* weird handshake type */ + SOCKET_ERROR_E = -208, /* error state on socket */ + SOCKET_NODATA = -209, /* expected data, not there */ + INCOMPLETE_DATA = -210, /* don't have enough data to + complete task */ + UNKNOWN_RECORD_TYPE = -211, /* unknown type in record hdr */ + DECRYPT_ERROR = -212, /* error during decryption */ + FATAL_ERROR = -213, /* recvd alert fatal error */ + ENCRYPT_ERROR = -214, /* error during encryption */ + FREAD_ERROR = -215, /* fread problem */ + NO_PEER_KEY = -216, /* need peer's key */ + NO_PRIVATE_KEY = -217, /* need the private key */ + RSA_PRIVATE_ERROR = -218, /* error during rsa priv op */ + NO_DH_PARAMS = -219, /* server missing DH params */ + BUILD_MSG_ERROR = -220, /* build message failure */ - BAD_HELLO = -221, /* client hello malformed */ - DOMAIN_NAME_MISMATCH = -222, /* peer subject name mismatch */ - WANT_READ = -223, /* want read, call again */ - NOT_READY_ERROR = -224, /* handshake layer not ready */ - PMS_VERSION_ERROR = -225, /* pre m secret version error */ - VERSION_ERROR = -226, /* record layer version error */ - WANT_WRITE = -227, /* want write, call again */ - BUFFER_ERROR = -228, /* malformed buffer input */ - VERIFY_CERT_ERROR = -229, /* verify cert error */ - VERIFY_SIGN_ERROR = -230, /* verify sign error */ - CLIENT_ID_ERROR = -231, /* psk client identity error */ - SERVER_HINT_ERROR = -232, /* psk server hint error */ - PSK_KEY_ERROR = -233, /* psk key error */ - ZLIB_INIT_ERROR = -234, /* zlib init error */ - ZLIB_COMPRESS_ERROR = -235, /* zlib compression error */ - ZLIB_DECOMPRESS_ERROR = -236, /* zlib decompression error */ + BAD_HELLO = -221, /* client hello malformed */ + DOMAIN_NAME_MISMATCH = -222, /* peer subject name mismatch */ + WANT_READ = -223, /* want read, call again */ + NOT_READY_ERROR = -224, /* handshake layer not ready */ + PMS_VERSION_ERROR = -225, /* pre m secret version error */ + VERSION_ERROR = -226, /* record layer version error */ + WANT_WRITE = -227, /* want write, call again */ + BUFFER_ERROR = -228, /* malformed buffer input */ + VERIFY_CERT_ERROR = -229, /* verify cert error */ + VERIFY_SIGN_ERROR = -230, /* verify sign error */ + CLIENT_ID_ERROR = -231, /* psk client identity error */ + SERVER_HINT_ERROR = -232, /* psk server hint error */ + PSK_KEY_ERROR = -233, /* psk key error */ + ZLIB_INIT_ERROR = -234, /* zlib init error */ + ZLIB_COMPRESS_ERROR = -235, /* zlib compression error */ + ZLIB_DECOMPRESS_ERROR = -236, /* zlib decompression error */ - GETTIME_ERROR = -237, /* gettimeofday failed ??? */ - GETITIMER_ERROR = -238, /* getitimer failed ??? */ - SIGACT_ERROR = -239, /* sigaction failed ??? */ - SETITIMER_ERROR = -240, /* setitimer failed ??? */ - LENGTH_ERROR = -241, /* record layer length error */ - PEER_KEY_ERROR = -242, /* can't decode peer key */ - ZERO_RETURN = -243, /* peer sent close notify */ - SIDE_ERROR = -244, /* wrong client/server type */ - NO_PEER_CERT = -245, /* peer didn't send key */ - NTRU_KEY_ERROR = -246, /* NTRU key error */ - NTRU_DRBG_ERROR = -247, /* NTRU drbg error */ - NTRU_ENCRYPT_ERROR = -248, /* NTRU encrypt error */ - NTRU_DECRYPT_ERROR = -249, /* NTRU decrypt error */ - ECC_CURVETYPE_ERROR = -250, /* Bad ECC Curve Type */ - ECC_CURVE_ERROR = -251, /* Bad ECC Curve */ - ECC_PEERKEY_ERROR = -252, /* Bad Peer ECC Key */ - ECC_MAKEKEY_ERROR = -253, /* Bad Make ECC Key */ - ECC_EXPORT_ERROR = -254, /* Bad ECC Export Key */ - ECC_SHARED_ERROR = -255, /* Bad ECC Shared Secret */ - BAD_MUTEX_ERROR = -256, /* Bad mutex */ - NOT_CA_ERROR = -257, /* Not a CA cert error */ - BAD_PATH_ERROR = -258, /* Bad path for opendir */ - BAD_CERT_MANAGER_ERROR = -259, /* Bad Cert Manager */ - OCSP_CERT_REVOKED = -260, /* OCSP Certificate revoked */ - CRL_CERT_REVOKED = -261, /* CRL Certificate revoked */ - CRL_MISSING = -262, /* CRL Not loaded */ - MONITOR_RUNNING_E = -263, /* CRL Monitor already running */ - THREAD_CREATE_E = -264, /* Thread Create Error */ - OCSP_NEED_URL = -265, /* OCSP need an URL for lookup */ - OCSP_CERT_UNKNOWN = -266, /* OCSP responder doesn't know */ - OCSP_LOOKUP_FAIL = -267, /* OCSP lookup not successful */ - MAX_CHAIN_ERROR = -268, /* max chain depth exceeded */ - COOKIE_ERROR = -269, /* dtls cookie error */ - SEQUENCE_ERROR = -270, /* dtls sequence error */ - SUITES_ERROR = -271, /* suites pointer error */ - SSL_NO_PEM_HEADER = -272, /* no PEM header found */ - OUT_OF_ORDER_E = -273, /* out of order message */ - BAD_KEA_TYPE_E = -274, /* bad KEA type found */ - SANITY_CIPHER_E = -275, /* sanity check on cipher error */ - RECV_OVERFLOW_E = -276, /* RXCB returned more than rqed */ - GEN_COOKIE_E = -277, /* Generate Cookie Error */ - NO_PEER_VERIFY = -278, /* Need peer cert verify Error */ - FWRITE_ERROR = -279, /* fwrite problem */ - CACHE_MATCH_ERROR = -280, /* chache hdr match error */ - UNKNOWN_SNI_HOST_NAME_E = -281, /* Unrecognized host name Error */ + GETTIME_ERROR = -237, /* gettimeofday failed ??? */ + GETITIMER_ERROR = -238, /* getitimer failed ??? */ + SIGACT_ERROR = -239, /* sigaction failed ??? */ + SETITIMER_ERROR = -240, /* setitimer failed ??? */ + LENGTH_ERROR = -241, /* record layer length error */ + PEER_KEY_ERROR = -242, /* can't decode peer key */ + ZERO_RETURN = -243, /* peer sent close notify */ + SIDE_ERROR = -244, /* wrong client/server type */ + NO_PEER_CERT = -245, /* peer didn't send key */ + NTRU_KEY_ERROR = -246, /* NTRU key error */ + NTRU_DRBG_ERROR = -247, /* NTRU drbg error */ + NTRU_ENCRYPT_ERROR = -248, /* NTRU encrypt error */ + NTRU_DECRYPT_ERROR = -249, /* NTRU decrypt error */ + ECC_CURVETYPE_ERROR = -250, /* Bad ECC Curve Type */ + ECC_CURVE_ERROR = -251, /* Bad ECC Curve */ + ECC_PEERKEY_ERROR = -252, /* Bad Peer ECC Key */ + ECC_MAKEKEY_ERROR = -253, /* Bad Make ECC Key */ + ECC_EXPORT_ERROR = -254, /* Bad ECC Export Key */ + ECC_SHARED_ERROR = -255, /* Bad ECC Shared Secret */ + BAD_MUTEX_ERROR = -256, /* Bad mutex */ + NOT_CA_ERROR = -257, /* Not a CA cert error */ + BAD_PATH_ERROR = -258, /* Bad path for opendir */ + BAD_CERT_MANAGER_ERROR = -259, /* Bad Cert Manager */ + OCSP_CERT_REVOKED = -260, /* OCSP Certificate revoked */ + CRL_CERT_REVOKED = -261, /* CRL Certificate revoked */ + CRL_MISSING = -262, /* CRL Not loaded */ + MONITOR_RUNNING_E = -263, /* CRL Monitor already running */ + THREAD_CREATE_E = -264, /* Thread Create Error */ + OCSP_NEED_URL = -265, /* OCSP need an URL for lookup */ + OCSP_CERT_UNKNOWN = -266, /* OCSP responder doesn't know */ + OCSP_LOOKUP_FAIL = -267, /* OCSP lookup not successful */ + MAX_CHAIN_ERROR = -268, /* max chain depth exceeded */ + COOKIE_ERROR = -269, /* dtls cookie error */ + SEQUENCE_ERROR = -270, /* dtls sequence error */ + SUITES_ERROR = -271, /* suites pointer error */ + SSL_NO_PEM_HEADER = -272, /* no PEM header found */ + OUT_OF_ORDER_E = -273, /* out of order message */ + BAD_KEA_TYPE_E = -274, /* bad KEA type found */ + SANITY_CIPHER_E = -275, /* sanity check on cipher error */ + RECV_OVERFLOW_E = -276, /* RXCB returned more than rqed */ + GEN_COOKIE_E = -277, /* Generate Cookie Error */ + NO_PEER_VERIFY = -278, /* Need peer cert verify Error */ + FWRITE_ERROR = -279, /* fwrite problem */ + CACHE_MATCH_ERROR = -280, /* chache hdr match error */ + UNKNOWN_SNI_HOST_NAME_E = -281, /* Unrecognized host name Error */ + UNKNOWN_MAX_FRAG_LEN_E = -282, /* Unrecognized max frag len Error */ /* add strings to SetErrorString !!!!! */ /* begin negotiation parameter errors */ - UNSUPPORTED_SUITE = -290, /* unsupported cipher suite */ - MATCH_SUITE_ERROR = -291 /* can't match cipher suite */ + UNSUPPORTED_SUITE = -290, /* unsupported cipher suite */ + MATCH_SUITE_ERROR = -291 /* can't match cipher suite */ /* end negotiation parameter errors only 10 for now */ /* add strings to SetErrorString !!!!! */ }; diff --git a/cyassl/internal.h b/cyassl/internal.h index 39947748a..0f2333755 100644 --- a/cyassl/internal.h +++ b/cyassl/internal.h @@ -1116,9 +1116,13 @@ typedef struct CYASSL_DTLS_CTX { #ifdef HAVE_TLS_EXTENSIONS typedef enum { - SERVER_NAME_INDICATION = 0,/* +#ifdef HAVE_SNI + SERVER_NAME_INDICATION = 0, +#endif +#ifdef HAVE_MAX_FRAGMENT MAX_FRAGMENT_LENGTH = 1, - CLIENT_CERTIFICATE_URL = 2, +#endif + /*CLIENT_CERTIFICATE_URL = 2, TRUSTED_CA_KEYS = 3, TRUNCATED_HMAC = 4, STATUS_REQUEST = 5, @@ -1157,7 +1161,7 @@ typedef struct SNI { struct SNI* next; /* List Behavior */ #ifndef NO_CYASSL_SERVER byte options; /* Behaviour options */ - byte matched; /* Matching result */ + byte status; /* Matching result */ #endif } SNI; @@ -1165,13 +1169,22 @@ CYASSL_LOCAL int TLSX_UseSNI(TLSX** extensions, byte type, const void* data, word16 size); #ifndef NO_CYASSL_SERVER -CYASSL_LOCAL byte TLSX_SNI_Matched(TLSX* extensions, byte type); -CYASSL_LOCAL void TLSX_SNI_SetOptions(TLSX* extensions, byte type, +CYASSL_LOCAL void TLSX_SNI_SetOptions(TLSX* extensions, byte type, byte options); +CYASSL_LOCAL byte TLSX_SNI_Status(TLSX* extensions, byte type); +CYASSL_LOCAL word16 TLSX_SNI_GetRequest(TLSX* extensions, byte type, + void** data); #endif #endif /* HAVE_SNI */ +/* Maximum Fragment Length */ +#ifdef HAVE_MAX_FRAGMENT + +CYASSL_LOCAL int TLSX_UseMaxFragment(TLSX** extensions, byte mfl); + +#endif /* HAVE_MAX_FRAGMENT */ + #endif /* HAVE_TLS_EXTENSIONS */ /* CyaSSL context type */ @@ -1778,6 +1791,9 @@ struct CYASSL { #endif #ifdef HAVE_TLS_EXTENSIONS TLSX* extensions; /* RFC 6066 TLS Extensions data */ +#ifdef HAVE_MAX_FRAGMENT + word16 max_fragment; +#endif #endif CYASSL_ALERT_HISTORY alert_history; }; diff --git a/cyassl/ssl.h b/cyassl/ssl.h index 9989674d3..6c548a4fb 100644 --- a/cyassl/ssl.h +++ b/cyassl/ssl.h @@ -929,7 +929,9 @@ CYASSL_API void CyaSSL_FreeArrays(CYASSL*); CYASSL_API int CyaSSL_UseCavium(CYASSL*, int devId); CYASSL_API int CyaSSL_CTX_UseCavium(CYASSL_CTX*, int devId); -/* tls extensions */ +/* TLS Extensions */ + +/* Server Name Indication */ #ifdef HAVE_SNI /* SNI types */ enum { @@ -948,14 +950,41 @@ enum { CYASSL_SNI_ANSWER_ON_MISMATCH = 0x02 /* fake match on mismatch flag */ }; -CYASSL_API unsigned char CyaSSL_SNI_Matched(CYASSL* ssl, unsigned char type); - CYASSL_API void CyaSSL_SNI_SetOptions(CYASSL* ssl, unsigned char type, unsigned char options); CYASSL_API void CyaSSL_CTX_SNI_SetOptions(CYASSL_CTX* ctx, unsigned char type, unsigned char options); -#endif -#endif + +/* SNI status */ +enum { + CYASSL_SNI_NO_MATCH = 0, + CYASSL_SNI_FAKE_MATCH = 1, /* if CYASSL_SNI_ANSWER_ON_MISMATCH is enabled */ + CYASSL_SNI_REAL_MATCH = 2 +}; + +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); + +#endif /* NO_CYASSL_SERVER */ +#endif /* HAVE_SNI */ + +/* Maximum Fragment Length */ +#ifdef HAVE_MAX_FRAGMENT +/* Fragment lengths */ +enum { + CYASSL_MFL_2_9 = 1, /* 512 bytes */ + CYASSL_MFL_2_10 = 2, /* 1024 bytes */ + CYASSL_MFL_2_11 = 3, /* 2048 bytes */ + CYASSL_MFL_2_12 = 4, /* 4096 bytes */ + CYASSL_MFL_2_13 = 5 /* 8192 bytes *//* CyaSSL ONLY!!! */ +}; + +CYASSL_API int CyaSSL_UseMaxFragment(CYASSL* ssl, unsigned char mfl); +CYASSL_API int CyaSSL_CTX_UseMaxFragment(CYASSL_CTX* ctx, unsigned char mfl); +#endif /* HAVE_MAX_FRAGMENT */ + #define CYASSL_CRL_MONITOR 0x01 /* monitor this dir flag */ #define CYASSL_CRL_START_MON 0x02 /* start monitoring flag */ diff --git a/cyassl/test.h b/cyassl/test.h index f7259d092..86033d846 100644 --- a/cyassl/test.h +++ b/cyassl/test.h @@ -158,12 +158,23 @@ typedef struct tcp_ready { void InitTcpReady(tcp_ready*); void FreeTcpReady(tcp_ready*); +typedef CYASSL_METHOD* (*method_provider)(void); +typedef void (*ctx_callback)(CYASSL_CTX* ctx); +typedef void (*ssl_callback)(CYASSL* ssl); + +typedef struct callback_functions { + method_provider method; + ctx_callback ctx_ready; + ssl_callback ssl_ready; + ssl_callback on_result; +} callback_functions; typedef struct func_args { int argc; char** argv; int return_code; tcp_ready* signal; + callback_functions *callbacks; } func_args; void wait_tcp_ready(func_args*); diff --git a/src/internal.c b/src/internal.c index 4ad42fdbd..ea57791e2 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1444,6 +1444,9 @@ int InitSSL(CYASSL* ssl, CYASSL_CTX* ctx) #ifdef HAVE_TLS_EXTENSIONS ssl->extensions = NULL; +#ifdef HAVE_MAX_FRAGMENT + ssl->max_fragment = MAX_RECORD_SIZE; +#endif #endif ssl->rng = NULL; @@ -2619,8 +2622,13 @@ static int GetRecordHeader(CYASSL* ssl, const byte* input, word32* inOutIdx, #endif #endif /* record layer length check */ +#ifdef HAVE_MAX_FRAGMENT + if (*size > (ssl->max_fragment + MAX_COMP_EXTRA + MAX_MSG_EXTRA)) + return LENGTH_ERROR; +#else if (*size > (MAX_RECORD_SIZE + MAX_COMP_EXTRA + MAX_MSG_EXTRA)) return LENGTH_ERROR; +#endif /* verify record type here as well */ switch ((enum ContentType)rh->type) { @@ -2942,8 +2950,13 @@ static int DoCertificate(CYASSL* ssl, byte* input, word32* inOutIdx) c24to32(&input[i], &certSz); i += CERT_HEADER_SZ; +#ifdef HAVE_MAX_FRAGMENT + if (listSz > ssl->max_fragment || certSz > ssl->max_fragment) + return BUFFER_E; +#else if (listSz > MAX_RECORD_SIZE || certSz > MAX_RECORD_SIZE) return BUFFER_E; +#endif certs[totalCerts].length = certSz; certs[totalCerts].buffer = input + i; @@ -5348,7 +5361,11 @@ int SendData(CYASSL* ssl, const void* data, int sz) } for (;;) { +#ifdef HAVE_MAX_FRAGMENT + int len = min(sz - sent, min(ssl->max_fragment, OUTPUT_RECORD_SIZE)); +#else int len = min(sz - sent, OUTPUT_RECORD_SIZE); +#endif byte* out; byte* sendBuffer = (byte*)data + sent; /* may switch on comp */ int buffSz = len; /* may switch on comp */ diff --git a/src/ssl.c b/src/ssl.c index 006beaf3e..38a94b1e7 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -454,7 +454,12 @@ static int CyaSSL_read_internal(CYASSL* ssl, void* data, int sz, int peek) ssl->dtls_expected_rx = max(sz + 100, MAX_MTU); #endif +#ifdef HAVE_MAX_FRAGMENT + ret = ReceiveData(ssl, (byte*)data, + min(sz, min(ssl->max_fragment, OUTPUT_RECORD_SIZE)), peek); +#else ret = ReceiveData(ssl, (byte*)data, min(sz, OUTPUT_RECORD_SIZE), peek); +#endif CYASSL_LEAVE("CyaSSL_read_internal()", ret); @@ -528,10 +533,6 @@ int CyaSSL_CTX_UseSNI(CYASSL_CTX* ctx, byte type, const void* data, word16 size) } #ifndef NO_CYASSL_SERVER -byte CyaSSL_SNI_Matched(CYASSL* ssl, byte type) -{ - return TLSX_SNI_Matched(ssl ? ssl->extensions : NULL, type); -} void CyaSSL_SNI_SetOptions(CYASSL* ssl, byte type, byte options) { @@ -544,10 +545,46 @@ void CyaSSL_CTX_SNI_SetOptions(CYASSL_CTX* ctx, byte type, byte options) if (ctx && ctx->extensions) TLSX_SNI_SetOptions(ctx->extensions, type, options); } -#endif + +byte CyaSSL_SNI_Status(CYASSL* ssl, byte type) +{ + return TLSX_SNI_Status(ssl ? ssl->extensions : NULL, type); +} + +word16 CyaSSL_SNI_GetRequest(CYASSL* ssl, byte type, void** data) +{ + if (data) + *data = NULL; + + if (ssl && ssl->extensions) + return TLSX_SNI_GetRequest(ssl->extensions, type, data); + + return 0; +} + +#endif /* NO_CYASSL_SERVER */ #endif /* HAVE_SNI */ + +#ifdef MAX_FRAGMENT_LENGTH +int CyaSSL_UseMaxFragment(CYASSL* ssl, byte mfl) +{ + if (ssl == NULL) + return BAD_FUNC_ARG; + + return TLSX_UseMaxFragment(ssl->extensions, mfl); +} + +int CyaSSL_CTX_UseMaxFragment(CYASSL_CTX* ctx, byte mfl) +{ + if (ctx == NULL) + return BAD_FUNC_ARG; + + return TLSX_UseMaxFragment(ctx->extensions, mfl); +} +#endif /* HAVE_MAX_FRAGMENT */ + #ifndef CYASSL_LEANPSK int CyaSSL_send(CYASSL* ssl, const void* data, int sz, int flags) { diff --git a/src/tls.c b/src/tls.c index 6e5965595..07575a8fb 100644 --- a/src/tls.c +++ b/src/tls.c @@ -585,7 +585,7 @@ static int TLSX_SNI_Append(SNI** list, byte type, const void* data, word16 size) #ifndef NO_CYASSL_SERVER sni->options = 0; - sni->matched = 0; + sni->status = CYASSL_SNI_NO_MATCH; #endif *list = sni; @@ -654,24 +654,24 @@ static SNI* TLSX_SNI_Find(SNI *list, byte type) } #ifndef NO_CYASSL_SERVER -static void TLSX_SNI_SetMatched(TLSX* extensions, byte type) +static void TLSX_SNI_SetStatus(TLSX* extensions, byte type, byte status) { TLSX* extension = TLSX_Find(extensions, SERVER_NAME_INDICATION); SNI* sni = TLSX_SNI_Find(extension ? extension->data : NULL, type); if (sni) { - sni->matched = 1; + sni->status = status; CYASSL_MSG("SNI did match!"); } } -byte TLSX_SNI_Matched(TLSX* extensions, byte type) +byte TLSX_SNI_Status(TLSX* extensions, byte type) { TLSX* extension = TLSX_Find(extensions, SERVER_NAME_INDICATION); SNI* sni = TLSX_SNI_Find(extension ? extension->data : NULL, type); if (sni) - return sni->matched; + return sni->status; return 0; } @@ -742,11 +742,14 @@ static int TLSX_SNI_Parse(CYASSL* ssl, byte* input, word16 length, (const char *) input + offset, size) == 0); if (matched || sni->options & CYASSL_SNI_ANSWER_ON_MISMATCH) { - int r = TLSX_UseSNI(&ssl->extensions, type, (byte *) "", 0); + int r = TLSX_UseSNI(&ssl->extensions, + type, input + offset, size); if (r) return r; /* throw error */ - if (matched) TLSX_SNI_SetMatched(ssl->extensions, type); + TLSX_SNI_SetStatus(ssl->extensions, type, + matched ? CYASSL_SNI_REAL_MATCH : CYASSL_SNI_FAKE_MATCH); + } else if (!(sni->options & CYASSL_SNI_CONTINUE_ON_MISMATCH)) { SendAlert(ssl, alert_fatal, unrecognized_name); @@ -797,7 +800,7 @@ int TLSX_UseSNI(TLSX** extensions, byte type, const void* data, word16 size) extension->data = (void*) sni; /* look for another server name of the same type to remove (replacement) */ - while ((sni = sni->next)) { + do { if (sni->next && sni->next->type == type) { SNI *next = sni->next; @@ -806,12 +809,28 @@ int TLSX_UseSNI(TLSX** extensions, byte type, const void* data, word16 size) break; } - } + } while ((sni = sni->next)); return 0; } #ifndef NO_CYASSL_SERVER +word16 TLSX_SNI_GetRequest(TLSX* extensions, byte type, void** data) +{ + TLSX* extension = TLSX_Find(extensions, SERVER_NAME_INDICATION); + SNI* sni = TLSX_SNI_Find(extension ? extension->data : NULL, type); + + if (sni && sni->status != CYASSL_SNI_NO_MATCH) { + switch (sni->type) { + case CYASSL_SNI_HOST_NAME: + *data = sni->data.host_name; + return XSTRLEN(*data); + } + } + + return 0; +} + void TLSX_SNI_SetOptions(TLSX* extensions, byte type, byte options) { TLSX* extension = TLSX_Find(extensions, SERVER_NAME_INDICATION); @@ -829,13 +848,113 @@ void TLSX_SNI_SetOptions(TLSX* extensions, byte type, byte options) #else -#define SNI_FREE_ALL(x) -#define SNI_GET_SIZE(x) 0 -#define SNI_WRITE(x) 0 -#define SNI_PARSE(x) 0 +#define SNI_FREE_ALL(list) +#define SNI_GET_SIZE(list) 0 +#define SNI_WRITE(a, b) 0 +#define SNI_PARSE(a, b, c, d) 0 #endif /* HAVE_SNI */ +#ifdef HAVE_MAX_FRAGMENT + +static word16 TLSX_MFL_Write(byte* data, byte* output) +{ + output[0] = data[0]; + + return ENUM_LEN; +} + +static int TLSX_MFL_Parse(CYASSL* ssl, byte* input, word16 length, + byte isRequest) +{ + if (length != ENUM_LEN) + return INCOMPLETE_DATA; + + switch (*input) { + case CYASSL_MFL_2_9 : ssl->max_fragment = 512; break; + case CYASSL_MFL_2_10: ssl->max_fragment = 1024; break; + case CYASSL_MFL_2_11: ssl->max_fragment = 2048; break; + case CYASSL_MFL_2_12: ssl->max_fragment = 4096; break; + case CYASSL_MFL_2_13: ssl->max_fragment = 8192; break; + + default: + SendAlert(ssl, alert_fatal, illegal_parameter); + + return UNKNOWN_MAX_FRAG_LEN_E; + } + +#ifndef NO_CYASSL_SERVER + if (isRequest) { + int r = TLSX_UseMaxFragment(&ssl->extensions, *input); + + if (r) return r; /* throw error */ + + TLSX_SetResponse(ssl, MAX_FRAGMENT_LENGTH); + } +#endif + + return 0; +} + +int TLSX_UseMaxFragment(TLSX** extensions, byte mfl) +{ + TLSX* extension = NULL; + byte* data = NULL; + int ret = 0; + + if (extensions == NULL) + return BAD_FUNC_ARG; + + if (CYASSL_MFL_2_9 <= mfl && mfl <= CYASSL_MFL_2_12) { + if ((data = XMALLOC(ENUM_LEN, 0, DYNAMIC_TYPE_TLSX)) == NULL) + return MEMORY_E; + + data[0] = mfl; + } else + return BAD_FUNC_ARG; + + /* push new MFL extension. */ + if ((ret = TLSX_Append(extensions, MAX_FRAGMENT_LENGTH)) != 0) { + XFREE(data, 0, DYNAMIC_TYPE_TLSX); + return ret; + } + + /* place new mfl to extension data. */ + extension = *extensions; + extension->data = (void*) data; + + /* remove duplicated extensions */ + do { + if (extension->next && extension->next->type == MAX_FRAGMENT_LENGTH) { + TLSX *next = extension->next; + + extension->next = next->next; + next->next = NULL; + + TLSX_FreeAll(next); + + break; + } + } while ((extension = extension->next)); + + return 0; +} + + +#define MFL_FREE_ALL(data) XFREE(data, 0, DYNAMIC_TYPE_TLSX) +#define MFL_GET_SIZE(data) ENUM_LEN +#define MFL_WRITE TLSX_MFL_Write +#define MFL_PARSE TLSX_MFL_Parse + +#else + +#define MFL_FREE_ALL(a) +#define MFL_GET_SIZE(a) 0 +#define MFL_WRITE(a, b) 0 +#define MFL_PARSE(a, b, c, d) 0 + +#endif /* HAVE_MAX_FRAGMENT */ + TLSX* TLSX_Find(TLSX* list, TLSX_Type type) { TLSX* extension = list; @@ -857,6 +976,10 @@ void TLSX_FreeAll(TLSX* list) case SERVER_NAME_INDICATION: SNI_FREE_ALL((SNI *) extension->data); break; + + case MAX_FRAGMENT_LENGTH: + MFL_FREE_ALL(extension->data); + break; } XFREE(extension, 0, DYNAMIC_TYPE_TLSX); @@ -889,6 +1012,9 @@ static word16 TLSX_GetSize(TLSX* list, byte* semaphore, byte isRequest) if (isRequest) length += SNI_GET_SIZE((SNI *) extension->data); break; + case MAX_FRAGMENT_LENGTH: + length += MFL_GET_SIZE(extension->data); + break; } TURN_ON(semaphore, extension->type); @@ -924,6 +1050,11 @@ static word16 TLSX_Write(TLSX* list, byte* output, byte* semaphore, offset += SNI_WRITE((SNI *) extension->data, output + offset); break; + + case MAX_FRAGMENT_LENGTH: + offset += MFL_WRITE((byte *) extension->data, + output + offset); + break; } /* writing extension data length */ @@ -1078,6 +1209,12 @@ int TLSX_Parse(CYASSL* ssl, byte* input, word16 length, byte isRequest, ret = SNI_PARSE(ssl, input + offset, size, isRequest); break; + case MAX_FRAGMENT_LENGTH: + CYASSL_MSG("Max Fragment Length extension received"); + + ret = MFL_PARSE(ssl, input + offset, size, isRequest); + break; + case HELLO_EXT_SIG_ALGO: if (isRequest) { /* do not mess with offset inside the switch! */ diff --git a/tests/api.c b/tests/api.c index fe17fa562..8cd10d5f4 100644 --- a/tests/api.c +++ b/tests/api.c @@ -68,6 +68,13 @@ static int test_lvl(CYASSL_CTX *ctx, const char* file, const char* path, THREAD_RETURN CYASSL_THREAD test_server_nofail(void*); void test_client_nofail(void*); + +void run_cyassl_client(void* args); +THREAD_RETURN CYASSL_THREAD run_cyassl_server(void* args); + +void test_CyaSSL_client_server(callback_functions* client_callbacks, + callback_functions* server_callbacks); + static const char* bogusFile = "/dev/null"; #endif @@ -223,8 +230,105 @@ int test_CyaSSL_CTX_new(CYASSL_METHOD *method) #ifdef HAVE_TLS_EXTENSIONS #ifdef HAVE_SNI +static void use_SNI_at_ctx(CYASSL_CTX* ctx) +{ + byte type = CYASSL_SNI_HOST_NAME; + char name[] = "www.yassl.com"; + + AssertIntEQ(0, CyaSSL_CTX_UseSNI(ctx, type, (void *) name, XSTRLEN(name))); +} + +static void use_SNI_at_ssl(CYASSL* ssl) +{ + byte type = CYASSL_SNI_HOST_NAME; + char name[] = "www.yassl.com"; + + AssertIntEQ(0, CyaSSL_UseSNI(ssl, type, (void *) name, XSTRLEN(name))); +} + +static void different_SNI_at_ssl(CYASSL* ssl) +{ + byte type = CYASSL_SNI_HOST_NAME; + char name[] = "ww2.yassl.com"; + + AssertIntEQ(0, CyaSSL_UseSNI(ssl, type, (void *) name, XSTRLEN(name))); +} + +static void use_SNI_WITH_CONTINUE_at_ssl(CYASSL* ssl) +{ + byte type = CYASSL_SNI_HOST_NAME; + + use_SNI_at_ssl(ssl); + + CyaSSL_SNI_SetOptions(ssl, type, CYASSL_SNI_CONTINUE_ON_MISMATCH); +} + +static void use_SNI_WITH_FAKE_ANSWER_at_ssl(CYASSL* ssl) +{ + byte type = CYASSL_SNI_HOST_NAME; + + use_SNI_at_ssl(ssl); + + CyaSSL_SNI_SetOptions(ssl, type, CYASSL_SNI_ANSWER_ON_MISMATCH); +} + +static void verify_SNI_abort_on_client(CYASSL* ssl) +{ + /* FATAL_ERROR */ + AssertIntEQ(-213, CyaSSL_get_error(ssl, 0)); +} + +static void verify_SNI_abort_on_server(CYASSL* ssl) +{ + /* UNKNOWN_SNI_HOST_NAME_E */ + AssertIntEQ(-281, CyaSSL_get_error(ssl, 0)); +} + +static void verify_SNI_no_matching(CYASSL* ssl) +{ + byte type = CYASSL_SNI_HOST_NAME; + char* request = (char*) &type; /* to be overwriten */ + + AssertIntEQ(CYASSL_SNI_NO_MATCH, CyaSSL_SNI_Status(ssl, type)); + + AssertNotNull(request); + AssertIntEQ(0, CyaSSL_SNI_GetRequest(ssl, type, (void**) &request)); + AssertNull(request); +} + +static void verify_SNI_real_matching(CYASSL* ssl) +{ + byte type = CYASSL_SNI_HOST_NAME; + char* request = NULL; + char name[] = "www.yassl.com"; + word16 length = XSTRLEN(name); + + AssertIntEQ(CYASSL_SNI_REAL_MATCH, CyaSSL_SNI_Status(ssl, type)); + + AssertIntEQ(length, CyaSSL_SNI_GetRequest(ssl, type, (void**) &request)); + AssertNotNull(request); + AssertStrEQ(name, request); +} + +static void verify_SNI_fake_matching(CYASSL* ssl) +{ + byte type = CYASSL_SNI_HOST_NAME; + char* request = NULL; + char name[] = "ww2.yassl.com"; + word16 length = XSTRLEN(name); + + AssertIntEQ(CYASSL_SNI_FAKE_MATCH, CyaSSL_SNI_Status(ssl, type)); + + AssertIntEQ(length, CyaSSL_SNI_GetRequest(ssl, type, (void**) &request)); + AssertNotNull(request); + AssertStrEQ(name, request); +} + void test_CyaSSL_UseSNI(void) { + callback_functions client_callbacks = {CyaSSLv23_client_method, 0, 0, 0}; + callback_functions server_callbacks = {CyaSSLv23_server_method, 0, 0, 0}; + CYASSL_CTX *ctx = CyaSSL_CTX_new(CyaSSLv23_client_method()); CYASSL *ssl = CyaSSL_new(ctx); @@ -245,6 +349,40 @@ void test_CyaSSL_UseSNI(void) CyaSSL_free(ssl); CyaSSL_CTX_free(ctx); + + /* Testing success case at ctx */ + client_callbacks.ctx_ready = server_callbacks.ctx_ready = use_SNI_at_ctx; + server_callbacks.on_result = verify_SNI_real_matching; + + test_CyaSSL_client_server(&client_callbacks, &server_callbacks); + + /* Testing success case at ssl */ + client_callbacks.ctx_ready = server_callbacks.ctx_ready = NULL; + client_callbacks.ssl_ready = server_callbacks.ssl_ready = use_SNI_at_ssl; + + test_CyaSSL_client_server(&client_callbacks, &server_callbacks); + + /* Testing default mismatch behaviour */ + client_callbacks.ssl_ready = different_SNI_at_ssl; + client_callbacks.on_result = verify_SNI_abort_on_client; + server_callbacks.on_result = verify_SNI_abort_on_server; + + test_CyaSSL_client_server(&client_callbacks, &server_callbacks); + client_callbacks.on_result = NULL; + + /* Testing continue on mismatch */ + client_callbacks.ssl_ready = different_SNI_at_ssl; + server_callbacks.ssl_ready = use_SNI_WITH_CONTINUE_at_ssl; + server_callbacks.on_result = verify_SNI_no_matching; + + test_CyaSSL_client_server(&client_callbacks, &server_callbacks); + + /* Testing fake answer on mismatch */ + server_callbacks.ssl_ready = use_SNI_WITH_FAKE_ANSWER_at_ssl; + server_callbacks.on_result = verify_SNI_fake_matching; + + test_CyaSSL_client_server(&client_callbacks, &server_callbacks); + } #endif /* HAVE_SNI */ #endif /* HAVE_TLS_EXTENSIONS */ @@ -789,9 +927,173 @@ done2: return; } +void run_cyassl_client(void* args) +{ + callback_functions* callbacks = ((func_args*)args)->callbacks; + + CYASSL_CTX* ctx = CyaSSL_CTX_new(callbacks->method()); + CYASSL* ssl = NULL; + SOCKET_T sfd = 0; + + char msg[] = "hello cyassl server!"; + int len = (int) XSTRLEN(msg); + char input[1024]; + int idx; + + ((func_args*)args)->return_code = TEST_FAIL; + +#ifdef OPENSSL_EXTRA + CyaSSL_CTX_set_default_passwd_cb(ctx, PasswordCallBack); +#endif + + AssertIntEQ(SSL_SUCCESS, CyaSSL_CTX_load_verify_locations(ctx, caCert, 0)); + + AssertIntEQ(SSL_SUCCESS, + CyaSSL_CTX_use_certificate_file(ctx, cliCert, SSL_FILETYPE_PEM)); + + AssertIntEQ(SSL_SUCCESS, + CyaSSL_CTX_use_PrivateKey_file(ctx, cliKey, SSL_FILETYPE_PEM)); + + if (callbacks->ctx_ready) + callbacks->ctx_ready(ctx); + + tcp_connect(&sfd, yasslIP, yasslPort, 0); + + ssl = CyaSSL_new(ctx); + CyaSSL_set_fd(ssl, sfd); + + if (callbacks->ssl_ready) + callbacks->ssl_ready(ssl); + + if (CyaSSL_connect(ssl) != SSL_SUCCESS) { + int err = CyaSSL_get_error(ssl, 0); + char buffer[80]; + printf("error = %d, %s\n", err, CyaSSL_ERR_error_string(err, buffer)); + + } else { + AssertIntEQ(len, CyaSSL_write(ssl, msg, len)); + + if (0 < (idx = CyaSSL_read(ssl, input, sizeof(input)-1))) { + input[idx] = 0; + printf("Server response: %s\n", input); + } + } + + if (callbacks->on_result) + callbacks->on_result(ssl); + + CyaSSL_free(ssl); + CyaSSL_CTX_free(ctx); + CloseSocket(sfd); + ((func_args*)args)->return_code = TEST_SUCCESS; +} + +THREAD_RETURN CYASSL_THREAD run_cyassl_server(void* args) +{ + callback_functions* callbacks = ((func_args*)args)->callbacks; + + CYASSL_CTX* ctx = CyaSSL_CTX_new(callbacks->method()); + CYASSL* ssl = NULL; + SOCKET_T sfd = 0; + SOCKET_T cfd = 0; + + char msg[] = "I hear you fa shizzle!"; + int len = (int) XSTRLEN(msg); + char input[1024]; + int idx; + + ((func_args*)args)->return_code = TEST_FAIL; + + CyaSSL_CTX_set_verify(ctx, + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); + +#ifdef OPENSSL_EXTRA + CyaSSL_CTX_set_default_passwd_cb(ctx, PasswordCallBack); +#endif + AssertIntEQ(SSL_SUCCESS, CyaSSL_CTX_load_verify_locations(ctx, cliCert, 0)); + + AssertIntEQ(SSL_SUCCESS, + CyaSSL_CTX_use_certificate_file(ctx, svrCert, SSL_FILETYPE_PEM)); + + AssertIntEQ(SSL_SUCCESS, + CyaSSL_CTX_use_PrivateKey_file(ctx, svrKey, SSL_FILETYPE_PEM)); + + if (callbacks->ctx_ready) + callbacks->ctx_ready(ctx); + + ssl = CyaSSL_new(ctx); + + tcp_accept(&sfd, &cfd, (func_args*)args, yasslPort, 0, 0); + CloseSocket(sfd); + + CyaSSL_set_fd(ssl, cfd); + +#ifdef NO_PSK + #if !defined(NO_FILESYSTEM) && defined(OPENSSL_EXTRA) + CyaSSL_SetTmpDH_file(ssl, dhParam, SSL_FILETYPE_PEM); + #else + SetDH(ssl); /* will repick suites with DHE, higher priority than PSK */ + #endif +#endif + + if (callbacks->ssl_ready) + callbacks->ssl_ready(ssl); + + /* AssertIntEQ(SSL_SUCCESS, CyaSSL_accept(ssl)); */ + if (CyaSSL_accept(ssl) != SSL_SUCCESS) { + int err = CyaSSL_get_error(ssl, 0); + char buffer[80]; + printf("error = %d, %s\n", err, CyaSSL_ERR_error_string(err, buffer)); + + } else { + if (0 < (idx = CyaSSL_read(ssl, input, sizeof(input)-1))) { + input[idx] = 0; + printf("Client message: %s\n", input); + } + + AssertIntEQ(len, CyaSSL_write(ssl, msg, len)); + + CyaSSL_shutdown(ssl); + } + + if (callbacks->on_result) + callbacks->on_result(ssl); + + CyaSSL_free(ssl); + CyaSSL_CTX_free(ctx); + CloseSocket(cfd); + + ((func_args*)args)->return_code = TEST_SUCCESS; + + return 0; +} + +void test_CyaSSL_client_server(callback_functions* client_callbacks, + callback_functions* server_callbacks) +{ + tcp_ready ready; + func_args client_args; + func_args server_args; + THREAD_TYPE serverThread; + + StartTCP(); + + client_args.callbacks = client_callbacks; + server_args.callbacks = server_callbacks; + + /* RUN Server side */ + InitTcpReady(&ready); + server_args.signal = &ready; + start_thread(run_cyassl_server, &server_args, &serverThread); + wait_tcp_ready(&server_args); + + /* RUN Client side */ + run_cyassl_client(&client_args); + join_thread(serverThread); + + FreeTcpReady(&ready); +} #endif /* NO_FILESYSTEM */ - - diff --git a/tests/suites.c b/tests/suites.c index 01a6e0a4f..72e23e1ba 100644 --- a/tests/suites.c +++ b/tests/suites.c @@ -119,8 +119,8 @@ static int execute_test_case(int svr_argc, char** svr_argv, int cli_argc, char** cli_argv, int addNoVerify, int addNonBlocking) { - func_args cliArgs = {cli_argc, cli_argv, 0, NULL}; - func_args svrArgs = {svr_argc, svr_argv, 0, NULL}; + func_args cliArgs = {cli_argc, cli_argv, 0, NULL, NULL}; + func_args svrArgs = {svr_argc, svr_argv, 0, NULL, NULL}; tcp_ready ready; THREAD_TYPE serverThread;