Connect QUIC decryption to UDP processing

This commit is contained in:
Vadim Vetrov 2025-01-02 20:37:34 +03:00
parent e5153e9186
commit b11a183bb3
No known key found for this signature in database
GPG Key ID: E8A308689D7A73A5
4 changed files with 128 additions and 5 deletions

View File

@ -1,4 +1,5 @@
#include "quic.h" #include "quic.h"
#include "tls.h"
#include "logging.h" #include "logging.h"
@ -316,6 +317,53 @@ int gen_fake_udp(struct udp_fake_type type,
return 0; 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, int detect_udp_filtered(const struct section_config_t *section,
const uint8_t *payload, uint32_t plen) { const uint8_t *payload, uint32_t plen) {
const void *iph; const void *iph;
@ -341,14 +389,14 @@ int detect_udp_filtered(const struct section_config_t *section,
const struct quic_lhdr *qch; const struct quic_lhdr *qch;
uint32_t qch_len; uint32_t qch_len;
struct quic_cids qci; struct quic_cids qci;
const uint8_t *quic_raw_payload; const uint8_t *quic_in_payload;
uint32_t quic_raw_plen; uint32_t quic_in_plen;
lgtrace_addp("QUIC probe"); lgtrace_addp("QUIC probe");
ret = quic_parse_data((uint8_t *)data, dlen, ret = quic_parse_data((uint8_t *)data, dlen,
&qch, &qch_len, &qci, &qch, &qch_len, &qci,
&quic_raw_payload, &quic_raw_plen); &quic_in_payload, &quic_in_plen);
if (ret < 0) { if (ret < 0) {
lgtrace_addp("QUIC undefined type"); lgtrace_addp("QUIC undefined type");
@ -358,10 +406,43 @@ int detect_udp_filtered(const struct section_config_t *section,
lgtrace_addp("QUIC detected"); lgtrace_addp("QUIC detected");
if (quic_check_is_initial(qch)) { if (!quic_check_is_initial(qch)) {
lgtrace_addp("QUIC initial message"); 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; goto approve;
} }
free(decrypted_payload);
} }
match_port: match_port:

View File

@ -43,6 +43,7 @@
#define QUIC_FRAME_CRYPTO 0x06 #define QUIC_FRAME_CRYPTO 0x06
#define QUIC_FRAME_PADDING 0x00 #define QUIC_FRAME_PADDING 0x00
#define QUIC_FRAME_PING 0x01
#define QUIC_V1 1 // RFC 9000 #define QUIC_V1 1 // RFC 9000
#define QUIC_V2 0x6b3343cf // RFC 9369 #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 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 // Like fail_packet for TCP
int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen); int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen);

View File

@ -26,6 +26,9 @@ struct tls_verdict {
#define TLS_MESSAGE_ANALYZE_FOUND 0 #define TLS_MESSAGE_ANALYZE_FOUND 0
#define TLS_MESSAGE_ANALYZE_GOTO_NEXT 1 #define TLS_MESSAGE_ANALYZE_GOTO_NEXT 1
/**
* Analyzes each TLS Client Hello message (inside TLS Record or QUIC CRYPTO FRAME)
*/
int analyze_tls_message( int analyze_tls_message(
const struct section_config_t *section, const struct section_config_t *section,
const uint8_t *message_data, const uint8_t *message_data,

View File

@ -58,6 +58,10 @@ TEST(QuicTest, Test_decrypts)
// tag is left // tag is left
TEST_ASSERT_EQUAL(16, decrypted_payload + decrypted_payload_len - curptr); 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) TEST(QuicTest, Test_crypto_parser_valid)
@ -81,7 +85,9 @@ TEST(QuicTest, Test_crypto_parser_tls)
struct tls_verdict tlsv; struct tls_verdict tlsv;
fret = quic_parse_crypto(&fr_cr, (const uint8_t *)quic_decrypted_crypto, sizeof(quic_decrypted_crypto)); 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); 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); 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_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) TEST_GROUP_RUNNER(QuicTest)
{ {
RUN_TEST_CASE(QuicTest, Test_decrypts); 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_tls);
RUN_TEST_CASE(QuicTest, Test_crypto_parser_invalid); RUN_TEST_CASE(QuicTest, Test_crypto_parser_invalid);
RUN_TEST_CASE(QuicTest, Test_varlength_parser); RUN_TEST_CASE(QuicTest, Test_varlength_parser);
RUN_TEST_CASE(QuicTest, Test_parse_quic_decrypted)
} }