diff --git a/src/quic.c b/src/quic.c index 5e1a752..870d70b 100644 --- a/src/quic.c +++ b/src/quic.c @@ -1,4 +1,5 @@ #include "quic.h" +#include "tls.h" #include "logging.h" @@ -316,6 +317,53 @@ int gen_fake_udp(struct udp_fake_type type, return 0; } +struct tls_verdict parse_quic_decrypted( + const struct section_config_t *section, + const uint8_t *decrypted_message, uint32_t decrypted_message_len +) { + const uint8_t *curptr = decrypted_message; + ssize_t curptr_len = decrypted_message_len; + ssize_t fret; + int ret; + struct tls_verdict tlsv = {0}; + struct quic_frame_crypto fr_cr; + + while (curptr_len > 0) { + uint8_t type = curptr[0]; + switch (type) { + case QUIC_FRAME_PADDING: + case QUIC_FRAME_PING: + curptr++, curptr_len--; + break; + case QUIC_FRAME_CRYPTO: + fret = quic_parse_crypto(&fr_cr, curptr, curptr_len); + if (fret < 0) + break; + curptr += fret; + curptr_len -= fret; + + ret = analyze_tls_message( + section, fr_cr.payload, fr_cr.payload_length, &tlsv + ); + switch (ret) { + case TLS_MESSAGE_ANALYZE_GOTO_NEXT: + break; + case TLS_MESSAGE_ANALYZE_FOUND: + case TLS_MESSAGE_ANALYZE_INVALID: + default: + goto out; + + } + break; + default: + goto out; + } + } + +out: + return tlsv; +} + int detect_udp_filtered(const struct section_config_t *section, const uint8_t *payload, uint32_t plen) { const void *iph; @@ -341,14 +389,14 @@ int detect_udp_filtered(const struct section_config_t *section, const struct quic_lhdr *qch; uint32_t qch_len; struct quic_cids qci; - const uint8_t *quic_raw_payload; - uint32_t quic_raw_plen; + const uint8_t *quic_in_payload; + uint32_t quic_in_plen; lgtrace_addp("QUIC probe"); ret = quic_parse_data((uint8_t *)data, dlen, &qch, &qch_len, &qci, - &quic_raw_payload, &quic_raw_plen); + &quic_in_payload, &quic_in_plen); if (ret < 0) { lgtrace_addp("QUIC undefined type"); @@ -358,10 +406,43 @@ int detect_udp_filtered(const struct section_config_t *section, lgtrace_addp("QUIC detected"); - if (quic_check_is_initial(qch)) { - lgtrace_addp("QUIC initial message"); + if (!quic_check_is_initial(qch)) { + lgtrace_addp("QUIC not initial"); + goto match_port; + } + + uint8_t *decrypted_payload; + uint32_t decrypted_payload_len; + const uint8_t *decrypted_message; + uint32_t decrypted_message_len; + struct tls_verdict tlsv; + + lgtrace_addp("QUIC initial message"); + ret = quic_parse_initial_message( + data, dlen, + &decrypted_payload, &decrypted_payload_len, + &decrypted_message, &decrypted_message_len + ); + + if (ret < 0) { + goto match_port; + } + + tlsv = parse_quic_decrypted(section, + decrypted_message, decrypted_message_len + ); + + if (tlsv.sni_len != 0) { + lgdebugmsg("QUIC SNI detected: %.*s", tlsv.sni_len, tlsv.sni_ptr); + } + + if (tlsv.target_sni) { + lgdebugmsg("QUIC target SNI detected: %.*s", tlsv.sni_len, tlsv.sni_ptr); + free(decrypted_payload); goto approve; } + + free(decrypted_payload); } match_port: diff --git a/src/quic.h b/src/quic.h index 320cd7a..64a080f 100644 --- a/src/quic.h +++ b/src/quic.h @@ -43,6 +43,7 @@ #define QUIC_FRAME_CRYPTO 0x06 #define QUIC_FRAME_PADDING 0x00 +#define QUIC_FRAME_PING 0x01 #define QUIC_V1 1 // RFC 9000 #define QUIC_V2 0x6b3343cf // RFC 9369 @@ -200,6 +201,14 @@ int quic_parse_initial_message( const uint8_t **udecrypted_message, uint32_t *udecrypted_message_len ); +/** + * Like analyze_tls_data for QUIC + */ +struct tls_verdict parse_quic_decrypted( + const struct section_config_t *section, + const uint8_t *decrypted_message, uint32_t decrypted_message_len +); + // Like fail_packet for TCP int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen); diff --git a/src/tls.h b/src/tls.h index 3079dc0..5cbd65b 100644 --- a/src/tls.h +++ b/src/tls.h @@ -26,6 +26,9 @@ struct tls_verdict { #define TLS_MESSAGE_ANALYZE_FOUND 0 #define TLS_MESSAGE_ANALYZE_GOTO_NEXT 1 +/** + * Analyzes each TLS Client Hello message (inside TLS Record or QUIC CRYPTO FRAME) + */ int analyze_tls_message( const struct section_config_t *section, const uint8_t *message_data, diff --git a/test/quic.c b/test/quic.c index 45e46eb..3e3feae 100644 --- a/test/quic.c +++ b/test/quic.c @@ -58,6 +58,10 @@ TEST(QuicTest, Test_decrypts) // tag is left TEST_ASSERT_EQUAL(16, decrypted_payload + decrypted_payload_len - curptr); + +#undef free + free(decrypted_payload); +#define free unity_free } TEST(QuicTest, Test_crypto_parser_valid) @@ -81,7 +85,9 @@ TEST(QuicTest, Test_crypto_parser_tls) struct tls_verdict tlsv; fret = quic_parse_crypto(&fr_cr, (const uint8_t *)quic_decrypted_crypto, sizeof(quic_decrypted_crypto)); + TEST_ASSERT_GREATER_OR_EQUAL(0, fret); ret = analyze_tls_message(&sconf, fr_cr.payload, fr_cr.payload_length, &tlsv); + TEST_ASSERT_GREATER_OR_EQUAL(0, ret); TEST_ASSERT_EQUAL_STRING_LEN("example.com", tlsv.sni_ptr, 11); } @@ -129,6 +135,29 @@ TEST(QuicTest, Test_varlength_parser) TEST_ASSERT_EQUAL(0, mlen); } +TEST(QuicTest, Test_parse_quic_decrypted) +{ + int ret; + uint8_t *decrypted_payload; + uint32_t decrypted_payload_len; + const uint8_t *decrypted_message; + uint32_t decrypted_message_len; + struct tls_verdict tlsv = {0}; + + ret = quic_parse_initial_message( + (const uint8_t *)quic_testing_payload, sizeof(quic_testing_payload) - 1, + &decrypted_payload, &decrypted_payload_len, + &decrypted_message, &decrypted_message_len + ); + TEST_ASSERT_EQUAL(ret, 0); + + tlsv = parse_quic_decrypted(&sconf, decrypted_message, decrypted_message_len); + TEST_ASSERT_EQUAL_STRING_LEN("example.com", tlsv.sni_ptr, 11); +#undef free + free(decrypted_payload); +#define free unity_free +} + TEST_GROUP_RUNNER(QuicTest) { RUN_TEST_CASE(QuicTest, Test_decrypts); @@ -136,4 +165,5 @@ TEST_GROUP_RUNNER(QuicTest) RUN_TEST_CASE(QuicTest, Test_crypto_parser_tls); RUN_TEST_CASE(QuicTest, Test_crypto_parser_invalid); RUN_TEST_CASE(QuicTest, Test_varlength_parser); + RUN_TEST_CASE(QuicTest, Test_parse_quic_decrypted) }