lsquic_hspack_valid.c revision 7d09751d
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * lsquic_hspack_valid.c -- Handshake packet validator.
4 *
5 * We want to eliminate invalid packets as soon as we read them in and not
6 * feed them to lsquic engine if we can avoid it.  The handshake packet
7 * possesses several characteristics which make it possible to detect
8 * garbage packets.
9 */
10
11
12#include <assert.h>
13#include <string.h>
14#include <sys/queue.h>
15
16#include "lsquic.h"
17#include "lsquic_types.h"
18#include "lsquic_int_types.h"
19#include "lsquic_packet_common.h"
20#include "lsquic_packet_gquic.h"
21#include "lsquic_packet_ietf.h"
22#include "lsquic_mm.h"
23#include "lsquic_engine_public.h"
24#include "lsquic_version.h"
25#include "lsquic_parse_common.h"
26
27
28#define SMALLEST_GQUIC_OVERHEAD     \
29    1 /* Type */                    \
30    + GQUIC_CID_LEN                 \
31    + sizeof(lsquic_ver_tag_t)      \
32    + 1 /* Packet number */         \
33    + 1 /* Stream frame */          \
34    + 1 /* Stream ID */             \
35    + 2 /* Data length */           \
36    + 12 /* IV */
37
38
39
40/* Note that we ignore nonce: even if the flag is set, we know that Chrome
41 * does not actually include the 32-byte nonce.
42 */
43static int
44is_valid_gquic_hs_packet (const unsigned char *buf, size_t bufsz,
45                                                        lsquic_ver_tag_t *tag)
46{
47    if (bufsz > GQUIC_MAX_PACKET_SZ                                     ||
48                    /* Data: HPACKed :method GET :path / is 2 bytes */
49        bufsz < SMALLEST_GQUIC_OVERHEAD + 2                            ||
50        /* Check maximum packet number: */
51        buf[1 + GQUIC_CID_LEN + sizeof(lsquic_ver_tag_t)] > 64  ||
52        /* From [draft-hamilton-quic-transport-protocol-01]:
53         *    0x80 is currently unused, and must be set to 0.
54         *    0x40 = MULTIPATH. This bit is reserved for multipath use.
55         *
56         *    0x30 = Packet number length.  We expect these bits to be
57         *            unset.
58         *
59         * The reference implementation checks that two high bits are not
60         * set if version flag is not set or if the version is the same.
61         * For our purposes, all GQUIC version we support so far have these
62         * bits set to zero.
63         *
64         * Incoming handshake packets must have both connection ID and
65         * version bits set.
66         *
67         * Nonce flag is ignored: Chrome sets it erronesously, but it may
68         * not be true (a) in the future or (b) in other clients.
69         */
70        ((buf[0] ^ (
71                    /* These should be unset: */
72                    (~(0x80|0x40|0x30|PACKET_PUBLIC_FLAGS_RST))
73                    &
74                    /* While these should be set: */
75                    (PACKET_PUBLIC_FLAGS_VERSION|
76                        PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID)
77                   )) & /* Ignore this bit: */ ~PACKET_PUBLIC_FLAGS_NONCE)
78        )
79    {
80        return 0;
81    }
82
83    memcpy(tag, buf + 1 + 8, sizeof(*tag));
84
85    return 1;
86}
87
88
89int
90lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
91                                    const unsigned char *buf, size_t bufsz)
92{
93    lsquic_ver_tag_t tag;
94    int is_valid;
95
96    if (bufsz < 1)
97        return 0;
98
99    switch (buf[0] & 0xF8)
100    {
101    /* Xs vary, Gs are iGnored: */
102    /* 1X11 XGGG: Q046 long header */
103    case 0x80|0x40|0x20|0x10|0x08:
104    case 0x80|0x00|0x20|0x10|0x08:
105    case 0x80|0x40|0x20|0x10|0x00:
106    case 0x80|0x00|0x20|0x10|0x00:
107        is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ
108            && lsquic_is_valid_iquic_hs_packet(buf, bufsz, &tag);
109        break;
110    /* 1X00 XGGG: ID-22 long header */
111    case 0x80|0x40|0x00|0x00|0x08:
112    case 0x80|0x00|0x00|0x00|0x08:
113    case 0x80|0x40|0x00|0x00|0x00:
114    case 0x80|0x00|0x00|0x00|0x00:
115    /* 1X01 XGGG: ID-22 long header */
116    case 0x80|0x40|0x00|0x10|0x08:
117    case 0x80|0x00|0x00|0x10|0x08:
118    case 0x80|0x40|0x00|0x10|0x00:
119    case 0x80|0x00|0x00|0x10|0x00:
120    /* 1X10 XGGG: ID-22 long header */
121    case 0x80|0x40|0x20|0x00|0x08:
122    case 0x80|0x00|0x20|0x00|0x08:
123    case 0x80|0x40|0x20|0x00|0x00:
124    case 0x80|0x00|0x20|0x00|0x00:
125        is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ
126            && lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet(buf, bufsz, &tag);
127        break;
128    /* 01XX XGGG: ID-22 short header */
129    case 0x00|0x40|0x00|0x00|0x00:
130    case 0x00|0x40|0x00|0x00|0x08:
131    case 0x00|0x40|0x00|0x10|0x00:
132    case 0x00|0x40|0x00|0x10|0x08:
133    case 0x00|0x40|0x20|0x00|0x00:
134    case 0x00|0x40|0x20|0x00|0x08:
135    case 0x00|0x40|0x20|0x10|0x00:
136    case 0x00|0x40|0x20|0x10|0x08:
137        is_valid = 0;
138        break;
139    /* 00XX 0GGG: Q046 short header */
140    case 0x00|0x00|0x00|0x00|0x00:
141    case 0x00|0x00|0x00|0x10|0x00:
142    case 0x00|0x00|0x20|0x00|0x00:
143    case 0x00|0x00|0x20|0x10|0x00:
144        is_valid = 0;
145        break;
146    /* 00XX 1GGG: GQUIC */
147    case 0x00|0x00|0x00|0x00|0x08:
148    case 0x00|0x00|0x00|0x10|0x08:
149    case 0x00|0x00|0x20|0x00|0x08:
150    case 0x00|0x00|0x20|0x10|0x08:
151        is_valid = is_valid_gquic_hs_packet(buf, bufsz, &tag);
152        break;
153    default:    /* gcc thinks this is possible?! */
154        assert(0);
155        is_valid = 0;
156        break;
157    }
158
159    if (is_valid)
160    {
161        return 1;
162    }
163    else
164        return 0;
165}
166