#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "tlse.h" #include "chacha.h" #include "buffer.h" static int tls_cipher_is_fs(struct TLSContext *context, unsigned short cipher) { if (!context) { return 0; } if (context->tlsver == TLS_VERSION13) { switch (cipher) { case TLS_AES_128_GCM_SHA256: case TLS_AES_256_GCM_SHA384: case TLS_CHACHA20_POLY1305_SHA256: return 1; } return 0; } switch (cipher) { case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: if (context && context->certificates && context->certificates_count && context->ec_private_key) { return 1; } return 0; case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: if (context->version == TLS_V12 || context->version == DTLS_V12) { if (context && context->certificates && context->certificates_count && context->ec_private_key) { return 1; } } return 0; case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: return 1; case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: if (context->tlsver == TLS_VERSION12) { return 1; } break; } return 0; } static uint16_t get16(const unsigned char *buf) { uint16_t res; res = ((*buf) << 8) + (*(buf+1)); return res; } int tls_choose_cipher(struct TLSContext *context, const unsigned char *buf, int buf_len, int *scsv_set) { int i; if (scsv_set) { *scsv_set = 0; } if (!context) { return 0; } int selected_cipher = TLS_NO_COMMON_CIPHER; if (selected_cipher == TLS_NO_COMMON_CIPHER) { for (i = 0; i < buf_len; i += 2) { uint16_t cipher = get16(&buf[i]); if (tls_cipher_is_fs(context, cipher)) { selected_cipher = cipher; break; } } } for (i = 0; i < buf_len; i += 2) { uint16_t cipher = get16(&buf[i]); if (cipher == TLS_FALLBACK_SCSV) { if (scsv_set) { *scsv_set = 1; } if (selected_cipher != TLS_NO_COMMON_CIPHER) { break; } } } return selected_cipher; } int tls_parse_client_hello(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets) { *write_packets = 0; if (context->connection_status != 0 && context->connection_status != 4) { DEBUG_PRINT("UNEXPECTED HELLO MESSAGE\n"); return TLS_UNEXPECTED_MESSAGE; } int res = 0; int downgraded = 0; int hello_min_size = TLS_CLIENT_HELLO_MINSIZE; if (buf_len < hello_min_size) { return TLS_NEED_MORE_DATA; } /* big endian */ int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; res += 3; if (buf_len - res < bytes_to_follow) { return TLS_NEED_MORE_DATA; } if (buf_len - res < 2) { return TLS_NEED_MORE_DATA; } unsigned short version = get16(&buf[res]); res += 2; if (!tls_supported_version(version)) { return TLS_NOT_SAFE; } DEBUG_PRINT("VERSION REQUIRED BY REMOTE %x, VERSION NOW %x\n", (int) version, (int) context->version); memcpy(context->remote_random, &buf[res], TLS_CLIENT_RANDOM_SIZE); res += TLS_CLIENT_RANDOM_SIZE; unsigned char session_len = buf[res++]; if (buf_len - res < session_len) return TLS_NEED_MORE_DATA; if (session_len && session_len <= TLS_MAX_SESSION_ID) { memcpy(context->session, &buf[res], session_len); context->session_size = session_len; DEBUG_DUMP_HEX_LABEL("REMOTE SESSION ID: ", context->session, context->session_size); } else { context->session_size = 0; } res += session_len; const unsigned char *cipher_buffer = NULL; unsigned short cipher_len = 0; int scsv_set = 0; if (context->is_server) { if (buf_len - res < 2) { return TLS_NEED_MORE_DATA; } cipher_len = get16(&buf[res]); res += 2; if (buf_len - res < cipher_len) return TLS_NEED_MORE_DATA; /* faster than cipher_len % 2 */ /* TODO unlikely, let the compiler worry about it */ if (cipher_len & 1) { return TLS_BROKEN_PACKET; } cipher_buffer = &buf[res]; res += cipher_len; if (buf_len - res < 1) { return TLS_NEED_MORE_DATA; } unsigned char compression_list_size = buf[res++]; if (buf_len - res < compression_list_size) { return TLS_NEED_MORE_DATA; } /* no compression support */ res += compression_list_size; } else { /* client */ if (buf_len - res < 2) { return TLS_NEED_MORE_DATA; } unsigned short cipher = get16(&buf[res]); res += 2; context->cipher = cipher; if (!tls_cipher_supported(context, cipher)) { context->cipher = 0; DEBUG_PRINT("NO CIPHER SUPPORTED\n"); return TLS_NO_COMMON_CIPHER; } DEBUG_PRINT("CIPHER: %s\n", tls_cipher_name(context)); if (buf_len - res < 1) return TLS_NEED_MORE_DATA; unsigned char compression = buf[res++]; if (compression != 0) { DEBUG_PRINT("COMPRESSION NOT SUPPORTED\n"); return TLS_COMPRESSION_NOT_SUPPORTED; } } if (res > 0) { if (context->is_server) { *write_packets = 2; } if (context->connection_status != 4) { context->connection_status = 1; } } if (res > 2) { res += 2; } const unsigned char *key_share = NULL; unsigned short key_size = 0; while (buf_len - res >= 4) { /* have extensions */ unsigned short extension_type = get16(&buf[res]); res += 2; unsigned short extension_len = get16(&buf[res]); res += 2; DEBUG_PRINT("Extension: 0x0%x (%i), len: %i\n", (int) extension_type, (int) extension_type, (int) extension_len); if (extension_len) { /* SNI extension */ if (buf_len - res < extension_len) { return TLS_NEED_MORE_DATA; } if (extension_type == 0x00) { /* unsigned char sni_type = buf[res + 2]; */ uint16_t sni_host_len = get16(&buf[res+3]); if (buf_len - res - 5 < sni_host_len) { return TLS_NEED_MORE_DATA; } if (sni_host_len) { free(context->sni); context->sni = malloc(sni_host_len + 1); if (context->sni) { memcpy(context->sni, &buf[res + 5], sni_host_len); context->sni[sni_host_len] = 0; DEBUG_PRINT("SNI HOST INDICATOR: [%s]\n", context->sni); } } } else if (extension_type == 0x0A) { /* supported groups */ if (buf_len - res > 2) { uint16_t group_len = get16(&buf[res]); if (buf_len - res >= group_len + 2) { DEBUG_DUMP_HEX_LABEL ("SUPPORTED GROUPS", &buf[res + 2], group_len); int i; int selected = 0; for (i = 0; i < group_len; i += 2) { uint16_t iana_n = get16(&buf[res + 2 + i]); switch (iana_n) { case 23: context-> curve = &secp256r1; selected = 1; break; case 24: context-> curve = &secp384r1; selected = 1; break; #if 0 /* needs different implementation */ case 29: context->curve = &curve25519; selected = 1; break; #endif #if 0 /* do not use it anymore */ case 25: context->curve = &secp521r1; selected = 1; break; #endif } if (selected) { DEBUG_PRINT ("SELECTED CURVE %s\n", context-> curve-> name); break; } } } } } else if (extension_type == 0x10 && context->alpn && context->alpn_count) { if (buf_len - res > 2) { uint16_t alpn_len = get16(&buf[res]); if (alpn_len && alpn_len <= extension_len - 2) { unsigned char *alpn = (unsigned char *) &buf[res + 2]; int alpn_pos = 0; while (alpn_pos < alpn_len) { unsigned char alpn_size = alpn [alpn_pos++]; if (alpn_size + alpn_pos >= extension_len) break; if ((alpn_size) && (tls_alpn_contains (context, (char *) &alpn [alpn_pos], alpn_size))) { free (context-> negotiated_alpn); context-> negotiated_alpn = malloc (alpn_size + 1); if (context->negotiated_alpn) { memcpy (context-> negotiated_alpn, &alpn [alpn_pos], alpn_size); context-> negotiated_alpn [alpn_size] = 0; DEBUG_PRINT ("NEGOTIATED ALPN: %s\n", context-> negotiated_alpn); } break; } alpn_pos += alpn_size; /* ServerHello contains just one alpn */ if (!context-> is_server) break; } } } } else if (extension_type == 0x0D) { /* supported signatures */ DEBUG_DUMP_HEX_LABEL ("SUPPORTED SIGNATURES", &buf[res], extension_len); } else if (extension_type == 0x0B) { /* supported point formats */ DEBUG_DUMP_HEX_LABEL ("SUPPORTED POINT FORMATS", &buf[res], extension_len); } else if (extension_type == 0x2B) { /* supported versions */ if ((buf[res] == extension_len - 1) && (extension_len > 4)) { DEBUG_DUMP_HEX_LABEL ("SUPPORTED VERSIONS", &buf[res], extension_len); /* tls 1.3 draft version 28 */ int i; int limit = (int) buf[res]; if (limit == extension_len - 1) { limit--; for (i = 1; i < limit; i += 2) { if ((get16(&buf[res + i]) == 0x7F1C) || (get16(&buf[res + i]) == TLS_V13)) { context-> version = TLS_V13; context-> tls13_version = get16(&buf[res + i]); DEBUG_PRINT ("TLS 1.3 SUPPORTED\n"); break; } } } } } else if (extension_type == 0x2A) { /* early data */ DEBUG_DUMP_HEX_LABEL ("EXTENSION, EARLY DATA", &buf[res], extension_len); } else if (extension_type == 0x29) { /* pre shared key */ DEBUG_DUMP_HEX_LABEL ("EXTENSION, PRE SHARED KEY", &buf[res], extension_len); } else if (extension_type == 0x33) { /* key share */ key_size = get16(&buf[res]); if (key_size > extension_len - 2) { DEBUG_PRINT("BROKEN KEY SHARE\n"); return TLS_BROKEN_PACKET; } DEBUG_DUMP_HEX_LABEL ("EXTENSION, KEY SHARE", &buf[res], extension_len); key_share = &buf[res + 2]; } else if (extension_type == 0x0D) { /* signature algorithms */ DEBUG_DUMP_HEX_LABEL ("EXTENSION, SIGNATURE ALGORITHMS", &buf[res], extension_len); } else if (extension_type == 0x2D) { /* psk key exchange modes */ DEBUG_DUMP_HEX_LABEL ("EXTENSION, PSK KEY EXCHANGE MODES", &buf[res], extension_len); } res += extension_len; } } if (buf_len != res) { return TLS_NEED_MORE_DATA; } if (context->is_server && cipher_buffer && cipher_len) { int cipher = tls_choose_cipher(context, cipher_buffer, cipher_len, &scsv_set); if (cipher < 0) { DEBUG_PRINT("NO COMMON CIPHERS\n"); return cipher; } if (downgraded && scsv_set) { DEBUG_PRINT("NO DOWNGRADE (SCSV SET)\n"); tls_alert(context, 1, inappropriate_fallback); context->critical_error = 1; return TLS_NOT_SAFE; } context->cipher = cipher; } if (key_share && key_size && context->tlsver == TLS_VERSION13) { int key_share_err = tls_parse_key_share(context, key_share, key_size); if (key_share_err) { /* request hello retry */ if (context->connection_status != 4) { *write_packets = 5; context->hs_messages[1] = 0; context->connection_status = 4; return res; } else return key_share_err; } /* we have key share */ context->connection_status = 3; } return res; }