litespeed-quic/src/liblsquic/lsquic_hspack_valid.c

166 lines
5.2 KiB
C

/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_hspack_valid.c -- Handshake packet validator.
*
* We want to eliminate invalid packets as soon as we read them in and not
* feed them to lsquic engine if we can avoid it. The handshake packet
* possesses several characteristics which make it possible to detect
* garbage packets.
*/
#include <assert.h>
#include <string.h>
#include <sys/queue.h>
#include "lsquic.h"
#include "lsquic_types.h"
#include "lsquic_int_types.h"
#include "lsquic_packet_common.h"
#include "lsquic_packet_gquic.h"
#include "lsquic_packet_ietf.h"
#include "lsquic_mm.h"
#include "lsquic_engine_public.h"
#include "lsquic_version.h"
#include "lsquic_parse_common.h"
#define SMALLEST_GQUIC_OVERHEAD \
1 /* Type */ \
+ GQUIC_CID_LEN \
+ sizeof(lsquic_ver_tag_t) \
+ 1 /* Packet number */ \
+ 1 /* Stream frame */ \
+ 1 /* Stream ID */ \
+ 2 /* Data length */ \
+ 12 /* IV */
/* Note that we ignore nonce: even if the flag is set, we know that Chrome
* does not actually include the 32-byte nonce.
*/
static int
is_valid_gquic_hs_packet (const unsigned char *buf, size_t bufsz,
lsquic_ver_tag_t *tag)
{
if (bufsz > GQUIC_MAX_PACKET_SZ ||
/* Data: HPACKed :method GET :path / is 2 bytes */
bufsz < SMALLEST_GQUIC_OVERHEAD + 2 ||
/* Check maximum packet number: */
buf[1 + GQUIC_CID_LEN + sizeof(lsquic_ver_tag_t)] > 64 ||
/* From [draft-hamilton-quic-transport-protocol-01]:
* 0x80 is currently unused, and must be set to 0.
* 0x40 = MULTIPATH. This bit is reserved for multipath use.
*
* 0x30 = Packet number length. We expect these bits to be
* unset.
*
* The reference implementation checks that two high bits are not
* set if version flag is not set or if the version is the same.
* For our purposes, all GQUIC version we support so far have these
* bits set to zero.
*
* Incoming handshake packets must have both connection ID and
* version bits set.
*
* Nonce flag is ignored: Chrome sets it erronesously, but it may
* not be true (a) in the future or (b) in other clients.
*/
((buf[0] ^ (
/* These should be unset: */
(~(0x80|0x40|0x30|PACKET_PUBLIC_FLAGS_RST))
&
/* While these should be set: */
(PACKET_PUBLIC_FLAGS_VERSION|
PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID)
)) & /* Ignore this bit: */ ~PACKET_PUBLIC_FLAGS_NONCE)
)
{
return 0;
}
memcpy(tag, buf + 1 + 8, sizeof(*tag));
return 1;
}
int
lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
const unsigned char *buf, size_t bufsz)
{
lsquic_ver_tag_t tag;
int is_valid;
if (bufsz < 1)
return 0;
switch (buf[0] & 0xF8)
{
/* Xs vary, Gs are iGnored: */
/* 1X11 XGGG: Q046 long header */
case 0x80|0x40|0x20|0x10|0x08:
case 0x80|0x00|0x20|0x10|0x08:
case 0x80|0x40|0x20|0x10|0x00:
case 0x80|0x00|0x20|0x10|0x00:
is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ
&& lsquic_is_valid_iquic_hs_packet(buf, bufsz, &tag);
break;
/* 1X00 XGGG: ID-22 long header */
case 0x80|0x40|0x00|0x00|0x08:
case 0x80|0x00|0x00|0x00|0x08:
case 0x80|0x40|0x00|0x00|0x00:
case 0x80|0x00|0x00|0x00|0x00:
/* 1X01 XGGG: ID-22 long header */
case 0x80|0x40|0x00|0x10|0x08:
case 0x80|0x00|0x00|0x10|0x08:
case 0x80|0x40|0x00|0x10|0x00:
case 0x80|0x00|0x00|0x10|0x00:
/* 1X10 XGGG: ID-22 long header */
case 0x80|0x40|0x20|0x00|0x08:
case 0x80|0x00|0x20|0x00|0x08:
case 0x80|0x40|0x20|0x00|0x00:
case 0x80|0x00|0x20|0x00|0x00:
is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ
&& lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet(buf, bufsz, &tag);
break;
/* 01XX XGGG: ID-22 short header */
case 0x00|0x40|0x00|0x00|0x00:
case 0x00|0x40|0x00|0x00|0x08:
case 0x00|0x40|0x00|0x10|0x00:
case 0x00|0x40|0x00|0x10|0x08:
case 0x00|0x40|0x20|0x00|0x00:
case 0x00|0x40|0x20|0x00|0x08:
case 0x00|0x40|0x20|0x10|0x00:
case 0x00|0x40|0x20|0x10|0x08:
is_valid = 0;
break;
/* 00XX 0GGG: Q046 short header */
case 0x00|0x00|0x00|0x00|0x00:
case 0x00|0x00|0x00|0x10|0x00:
case 0x00|0x00|0x20|0x00|0x00:
case 0x00|0x00|0x20|0x10|0x00:
is_valid = 0;
break;
/* 00XX 1GGG: GQUIC */
case 0x00|0x00|0x00|0x00|0x08:
case 0x00|0x00|0x00|0x10|0x08:
case 0x00|0x00|0x20|0x00|0x08:
case 0x00|0x00|0x20|0x10|0x08:
is_valid = is_valid_gquic_hs_packet(buf, bufsz, &tag);
break;
default: /* gcc thinks this is possible?! */
assert(0);
is_valid = 0;
break;
}
if (is_valid)
{
return 1;
}
else
return 0;
}