Commit 66fda2c3 authored by bol-van's avatar bol-van
Browse files

nfqws: support QUIC multi packet CRYPTO fragmentation

parent 77df43b9
Loading
Loading
Loading
Loading
+64 −32
Original line number Diff line number Diff line
@@ -1953,10 +1953,12 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
						return verdict; // cannot be first packet
					}
				}

				uint8_t defrag[16384];
				size_t hello_offset, hello_len, defrag_len = sizeof(defrag);
				if (QUICDefragCrypto(pclean,clean_len,defrag,&defrag_len))
				bool bFull;
				if (QUICDefragCrypto(pclean,clean_len,defrag,&defrag_len,&bFull))
				{
					if (bFull)
					{
						bool bIsHello = IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len);
						bool bReqFull = bIsHello ? IsTLSHandshakeFull(defrag+hello_offset,hello_len) : false;
@@ -2012,6 +2014,36 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
						}
					}
					else
					{
						DLOG("QUIC initial contains CRYPTO without full fragment coverage\n");
						if (ctrack)
						{
							if (ReasmIsEmpty(&ctrack->reasm_orig))
							{
								// preallocate max buffer to avoid reallocs that cause memory copy
								if (!reasm_orig_start(ctrack,IPPROTO_UDP,16384,16384,clean,clean_len))
								{
									reasm_orig_cancel(ctrack);
									return verdict;
								}
							}
							verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6);
							if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload))
							{
								DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed));
							}
							else
							{
								DLOG_ERR("rawpacket_queue failed !\n");
								reasm_orig_cancel(ctrack);
								return verdict;
							}
							return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt);
						}
						if (!quic_reasm_cancel(ctrack,"QUIC initial fragmented CRYPTO")) return verdict;
					}
				}
				else
				{
					// defrag failed
					if (!quic_reasm_cancel(ctrack,"QUIC initial defrag CRYPTO failed")) return verdict;
+36 −3
Original line number Diff line number Diff line
@@ -844,7 +844,16 @@ bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, si
	return !memcmp(data + pn_offset + pkn_len + cryptlen, atag, 16);
}

bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len)
struct range64
{
	uint64_t offset,len;
};
#define MAX_DEFRAG_PIECES	128
static int cmp_range64(const void * a, const void * b)
{
	return (((struct range64*)a)->offset < ((struct range64*)b)->offset) ? -1 : (((struct range64*)a)->offset > ((struct range64*)b)->offset) ? 1 : 0;
}
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull)
{
	// Crypto frame can be split into multiple chunks
	// chromium randomly splits it and pads with zero/one bytes to force support the standard
@@ -853,13 +862,15 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
	if (*defrag_len<10) return false;
	uint8_t *defrag_data = defrag+10;
	size_t defrag_data_len = *defrag_len-10;

	uint8_t ft;
	uint64_t offset,sz,szmax=0,zeropos=0,pos=0;
	bool found=false;
	struct range64 ranges[MAX_DEFRAG_PIECES];
	int i,range=0;

	while(pos<clean_len)
	{
		// frame type
		ft = clean[pos];
		pos++;
		if (ft>1) // 00 - padding, 01 - ping
@@ -867,6 +878,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
			if (ft!=6) return false; // dont want to know all possible frame type formats

			if (pos>=clean_len) return false;
			if (range>=MAX_DEFRAG_PIECES) return false;

			if ((pos+tvb_get_size(clean[pos])>=clean_len)) return false;
			pos += tvb_get_varint(clean+pos, &offset);
@@ -875,7 +887,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
			pos += tvb_get_varint(clean+pos, &sz);
			if ((pos+sz)>clean_len) return false;

			if ((offset+sz)>defrag_data_len) return false;
			if ((offset+sz)>defrag_data_len) return false; // defrag buf overflow
			if (zeropos < offset)
				// make sure no uninitialized gaps exist in case of not full fragment coverage
				memset(defrag_data+zeropos,0,offset-zeropos);
@@ -886,6 +898,10 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz

			found=true;
			pos+=sz;

			ranges[range].offset = offset;
			ranges[range].len = sz;
			range++;
		}
	}
	if (found)
@@ -897,6 +913,23 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
		phton64(defrag+2,szmax);
		defrag[2] |= 0xC0; // 64 bit value
		*defrag_len = (size_t)(szmax+10);

		qsort(ranges, range, sizeof(*ranges), cmp_range64);

		for(i=0 ; i<range ; i++)
			printf("RANGE %zu len %zu\n",ranges[i].offset,ranges[i].len);

		for(i=0,offset=0,*bFull=true ; i<range ; i++)
		{
			if (ranges[i].offset!=offset)
			{
				*bFull = false;
				break;
			}
			offset += ranges[i].len;
		}

printf("bFull=%d\n",*bFull);
	}
	return found;
}
+2 −1
Original line number Diff line number Diff line
@@ -87,5 +87,6 @@ uint8_t QUICDraftVersion(uint32_t version);
bool QUICExtractDCID(const uint8_t *data, size_t len, quic_cid_t *cid);

bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, size_t *clean_len);
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len);
// returns true if crypto frames were found . bFull = true if crypto frame fragments have full coverage
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull);
//bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);