166 lines
5.2 KiB
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;
|
|
}
|