vinyl-cache/bin/vinyld/http2/cache_http2_hpack.c
0
/*-
1
 * Copyright (c) 2016 Varnish Software AS
2
 * All rights reserved.
3
 *
4
 * Author: Martin Blix Grydeland <martin@varnish-software.com>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 */
30
31
#include "config.h"
32
33
#include "cache/cache_int.h"
34
35
#include <ctype.h>
36
#include <stdio.h>
37
38
#include "http2/cache_http2.h"
39
#include "vct.h"
40
41
static void
42 13104
h2h_assert_ready(const struct h2h_decode *d)
43
{
44
45 13104
        CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
46 13104
        AN(d->out);
47 13104
        assert(d->namelen >= 2); /* 2 chars from the ": " that we added */
48 13104
        assert(d->namelen <= d->out_u);
49 13104
        assert(d->out[d->namelen - 2] == ':');
50 13104
        assert(d->out[d->namelen - 1] == ' ');
51 13104
}
52
53
// rfc9113,l,2493,2528
54
static h2_error
55 13104
h2h_checkhdr(struct vsl_log *vsl, txt nm, txt val)
56
{
57
        const char *p;
58
        int l;
59
        enum {
60
                FLD_NAME_FIRST,
61
                FLD_NAME,
62
                FLD_VALUE_FIRST,
63
                FLD_VALUE
64
        } state;
65
66 13104
        if (Tlen(nm) == 0) {
67 0
                VSLb(vsl, SLT_BogoHeader, "Empty name");
68 0
                return (H2SE_PROTOCOL_ERROR);
69
        }
70
71
        // VSLb(vsl, SLT_Debug, "CHDR [%.*s] [%.*s]",
72
        //     (int)Tlen(nm), nm.b, (int)Tlen(val), val.b);
73
74 13104
        l = vmin_t(int, Tlen(nm) + 2 + Tlen(val), 20);
75 13104
        state = FLD_NAME_FIRST;
76 98427
        Tforeach(p, nm) {
77 85407
                switch(state) {
78
                case FLD_NAME_FIRST:
79 13104
                        state = FLD_NAME;
80 13104
                        if (*p == ':')
81 10332
                                break;
82
                        /* FALLTHROUGH */
83
                case FLD_NAME:
84 75075
                        if (isupper(*p)) {
85 42
                                VSLb(vsl, SLT_BogoHeader,
86
                                    "Illegal field header name (upper-case): %.*s",
87 21
                                    l, nm.b);
88 21
                                return (H2SE_PROTOCOL_ERROR);
89
                        }
90 75054
                        if (!vct_istchar(*p) || *p == ':') {
91 126
                                VSLb(vsl, SLT_BogoHeader,
92
                                    "Illegal field header name (non-token): %.*s",
93 63
                                    l, nm.b);
94 63
                                return (H2SE_PROTOCOL_ERROR);
95
                        }
96 74991
                        break;
97
                default:
98 0
                        WRONG("http2 field name validation state");
99 0
                }
100 85323
        }
101
102 13020
        state = FLD_VALUE_FIRST;
103 680106
        Tforeach(p, val) {
104 667296
                switch(state) {
105
                case FLD_VALUE_FIRST:
106 12873
                        if (vct_issp(*p)) {
107 378
                                VSLb(vsl, SLT_BogoHeader,
108
                                    "Illegal field value 0x%02x start %.*s",
109 189
                                    *p, l, nm.b);
110 189
                                return (H2SE_PROTOCOL_ERROR);
111
                        }
112 12684
                        state = FLD_VALUE;
113
                        /* FALLTHROUGH */
114
                case FLD_VALUE:
115 667107
                        if (!vct_ishdrval(*p)) {
116 42
                                VSLb(vsl, SLT_BogoHeader,
117
                                    "Illegal field value 0x%02x %.*s",
118 21
                                    *p, l, nm.b);
119 21
                                return (H2SE_PROTOCOL_ERROR);
120
                        }
121 667086
                        break;
122
                default:
123 0
                        WRONG("http2 field value validation state");
124 0
                }
125 667086
        }
126 12810
        if (state == FLD_VALUE && vct_issp(val.e[-1])) {
127 84
                VSLb(vsl, SLT_BogoHeader,
128
                    "Illegal field value 0x%02x (end) at %.*s",
129 42
                    val.e[-1], l, nm.b);
130 42
                return (H2SE_PROTOCOL_ERROR);
131
        }
132 12768
        return (0);
133 13104
}
134
135
static h2_error
136 13104
h2h_addhdr(struct http *hp, struct h2h_decode *d)
137
{
138
        /* XXX: This might belong in cache/cache_http.c */
139
        txt hdr, nm, val;
140
        int disallow_empty;
141
        const char *p;
142
        unsigned n, has_dup;
143
        h2_error err;
144
145 13104
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
146 13104
        h2h_assert_ready(d);
147
148
        /* Assume hdr is by default a regular header from what we decoded. */
149 13104
        hdr.b = d->out;
150 13104
        hdr.e = hdr.b + d->out_u;
151 13104
        n = hp->nhd;
152
153
        /* nm and val are separated by ": " */
154 13104
        nm.b = hdr.b;
155 13104
        nm.e = nm.b + d->namelen - 2;
156 13104
        val.b = nm.e + 2;
157 13104
        val.e = hdr.e;
158
159 13104
        err = h2h_checkhdr(hp->vsl, nm, val);
160 13104
        if (err != NULL)
161 336
                return (err);
162
163 12768
        disallow_empty = 0;
164 12768
        has_dup = 0;
165
166 12768
        if (Tlen(hdr) > cache_param->http_req_hdr_len) {
167 21
                VSLb(hp->vsl, SLT_BogoHeader, "Header too large: %.20s", hdr.b);
168 21
                return (H2SE_ENHANCE_YOUR_CALM);
169
        }
170
171
        /* Match H/2 pseudo headers */
172
        /* XXX: Should probably have some include tbl for pseudo-headers */
173 12747
        if (Tstreq(nm, ":method")) {
174 3192
                hdr.b = val.b;
175 3192
                n = HTTP_HDR_METHOD;
176 3192
                disallow_empty = 1;
177
178
                /* Check HTTP token */
179 13398
                Tforeach(p, hdr) {
180 10206
                        if (!vct_istchar(*p))
181 0
                                return (H2SE_PROTOCOL_ERROR);
182 10206
                }
183 12747
        } else if (Tstreq(nm, ":path")) {
184 3339
                hdr.b = val.b;
185 3339
                n = HTTP_HDR_URL;
186 3339
                disallow_empty = 1;
187
188
                // rfc9113,l,2693,2705
189 3339
                if (Tlen(val) > 0 && val.b[0] != '/' && !Tstreq(val, "*")) {
190 84
                        VSLb(hp->vsl, SLT_BogoHeader,
191
                            "Illegal :path pseudo-header %.*s",
192 42
                            (int)Tlen(val), val.b);
193 42
                        return (H2SE_PROTOCOL_ERROR);
194
                }
195
196
                /* Path cannot contain LWS or CTL */
197 11025
                Tforeach(p, hdr) {
198 7728
                        if (vct_islws(*p) || vct_isctl(*p))
199 0
                                return (H2SE_PROTOCOL_ERROR);
200 7728
                }
201 9513
        } else if (Tstreq(nm, ":scheme")) {
202
                /* XXX: What to do about this one? (typically
203
                   "http" or "https"). For now set it as a normal
204
                   header, stripping the first ':'. */
205 3213
                hdr.b++;
206 3213
                has_dup = d->has_scheme;
207 3213
                d->has_scheme = 1;
208 3213
                disallow_empty = 1;
209
210
                /* Check HTTP token */
211 15918
                Tforeach(p, val) {
212 12705
                        if (!vct_istchar(*p))
213 0
                                return (H2SE_PROTOCOL_ERROR);
214 12705
                }
215 6216
        } else if (Tstreq(nm, ":authority")) {
216
                /* NB: we inject "host" in place of "rity" for
217
                 * the ":authority" pseudo-header.
218
                 */
219 378
                vmemcpy(d->out + 6, "host", 4);
220 378
                hdr.b += 6;
221 378
                nm = Tstr(":authority"); /* preserve original */
222 378
                has_dup = d->has_authority;
223 378
                d->has_authority = 1;
224 3003
        } else if (nm.b[0] == ':') {
225 126
                VSLb(hp->vsl, SLT_BogoHeader,
226
                    "Unknown pseudo-header: %.*s",
227 63
                    vmin_t(int, Tlen(hdr), 20), hdr.b);
228 63
                return (H2SE_PROTOCOL_ERROR);   // rfc7540,l,2990,2992
229
        }
230
231 12642
        if (disallow_empty && Tlen(val) == 0) {
232 252
                VSLb(hp->vsl, SLT_BogoHeader,
233
                    "Empty pseudo-header %.*s",
234 126
                    (int)Tlen(nm), nm.b);
235 126
                return (H2SE_PROTOCOL_ERROR);
236
        }
237
238 12516
        if (n >= HTTP_HDR_FIRST) {
239
                /* Check for space in struct http */
240 6111
                if (n >= hp->shd) {
241 42
                        VSLb(hp->vsl, SLT_LostHeader,
242
                            "Too many headers: %.*s",
243 21
                            vmin_t(int, Tlen(hdr), 20), hdr.b);
244 21
                        return (H2SE_ENHANCE_YOUR_CALM);
245
                }
246 6090
                hp->nhd++;
247 6090
                AZ(hp->hd[n].b);
248 6090
        }
249
250 12495
        if (has_dup || hp->hd[n].b != NULL) {
251 105
                assert(nm.b[0] == ':');
252 210
                VSLb(hp->vsl, SLT_BogoHeader,
253
                    "Duplicate pseudo-header %.*s",
254 105
                    (int)Tlen(nm), nm.b);
255 105
                return (H2SE_PROTOCOL_ERROR);   // rfc7540,l,3158,3162
256
        }
257
258 12390
        hp->hd[n] = hdr;
259 12390
        return (0);
260 13104
}
261
262
static void
263 3969
h2h_decode_init(const struct h2_sess *h2, struct ws *ws)
264
{
265
        struct h2h_decode *d;
266
267 3969
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
268 3969
        CHECK_OBJ_NOTNULL(ws, WS_MAGIC);
269
270 3969
        AN(h2->decode);
271 3969
        d = h2->decode;
272 3969
        INIT_OBJ(d, H2H_DECODE_MAGIC);
273 3969
        VHD_Init(d->vhd);
274 3969
        d->out_l = WS_ReserveSize(ws, cache_param->http_req_size);
275
        /*
276
         * Can't do any work without any buffer
277
         * space. Require non-zero size.
278
         */
279 3969
        XXXAN(d->out_l);
280 3969
        d->out = WS_Reservation(ws);
281
282 3969
        if (cache_param->h2_max_header_list_size == 0)
283 3885
                d->limit =
284 3885
                    (long)h2->local_settings.max_header_list_size * 3 / 2;
285
        else
286 84
                d->limit = cache_param->h2_max_header_list_size;
287
288
        //  d->limit < cache_param->http_req_size
289 3969
        if (d->limit < h2->local_settings.max_header_list_size)
290 0
                d->limit = INT64_MAX;
291
292 3969
        d->ws = ws;
293 3969
}
294
295
void
296 3969
h2h_decode_hdr_init(const struct h2_sess *h2)
297
{
298
299 3969
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
300 3969
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
301 3969
        CHECK_OBJ_NOTNULL(h2->new_req->http, HTTP_MAGIC);
302 3969
        h2h_decode_init(h2, h2->new_req->ws);
303 3969
}
304
305
/* Possible error returns:
306
 *
307
 * H2E_COMPRESSION_ERROR: Lost compression state due to incomplete header
308
 * block. This is a connection level error.
309
 *
310
 * H2E_ENHANCE_YOUR_CALM: Ran out of workspace or http header space. This
311
 * is a stream level error.
312
 */
313
h2_error
314 3969
h2h_decode_hdr_fini(const struct h2_sess *h2)
315
{
316
        h2_error ret;
317
        struct h2h_decode *d;
318
319 3969
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
320 3969
        d = h2->decode;
321 3969
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
322 3969
        CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
323 3969
        WS_ReleaseP(d->ws, d->out);
324 3969
        if (d->vhd_ret != VHD_OK) {
325
                /* HPACK header block didn't finish at an instruction
326
                   boundary */
327 1554
                VSLb(h2->new_req->http->vsl, SLT_BogoHeader,
328 777
                    "HPACK compression error/fini (%s)", VHD_Error(d->vhd_ret));
329 777
                ret = H2CE_COMPRESSION_ERROR;
330 3969
        } else if (d->error == NULL && !d->has_scheme) {
331 84
                H2S_Lock_VSLb(h2, SLT_Debug, "Missing :scheme");
332 84
                ret = H2SE_MISSING_SCHEME; //rfc7540,l,3087,3090
333 84
        } else
334 3108
                ret = d->error;
335 3969
        FINI_OBJ(d);
336 3969
        if (ret == H2SE_REQ_SIZE) {
337 21
                VSLb(h2->new_req->http->vsl, SLT_LostHeader,
338
                    "Header list too large");
339 21
        }
340 3969
        return (ret);
341
}
342
343
/* Possible error returns:
344
 *
345
 * H2E_COMPRESSION_ERROR: Lost compression state due to invalid header
346
 * block. This is a connection level error.
347
 *
348
 * H2E_PROTOCOL_ERROR: Malformed header or duplicate pseudo-header.
349
 *                     Violation of field name/value charsets
350
 */
351
h2_error
352 4263
h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l)
353
{
354
        struct http *hp;
355
        struct h2h_decode *d;
356 4263
        size_t in_u = 0;
357
        const char *r, *e;
358
359 4263
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
360 4263
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
361 4263
        hp = h2->new_req->http;
362 4263
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
363 4263
        d = h2->decode;
364 4263
        CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
365 4263
        CHECK_OBJ_NOTNULL(d->ws, WS_MAGIC);
366 4263
        r = WS_Reservation(d->ws);
367 4263
        AN(r);
368 4263
        e = r + WS_ReservationSize(d->ws);
369
370
        /* Only H2E_ENHANCE_YOUR_CALM indicates that we should continue
371
           processing. Other errors should have been returned and handled
372
           by the caller. */
373 4263
        if (d->error != NULL)
374 84
                assert(H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM));
375
376 30513
        while (d->limit >= 0) {
377 30471
                AN(d->out);
378 30471
                assert(d->out_u <= d->out_l);
379 60942
                d->vhd_ret = VHD_Decode(d->vhd, h2->dectbl, in, in_l, &in_u,
380 30471
                    d->out, d->out_l, &d->out_u);
381
382 30471
                if (d->vhd_ret < 0) {
383 84
                        H2S_Lock_VSLb(h2, SLT_BogoHeader,
384
                            "HPACK compression error (%s)",
385 42
                            VHD_Error(d->vhd_ret));
386 42
                        d->error = H2CE_COMPRESSION_ERROR;
387 42
                        break;
388 30429
                } else if (d->vhd_ret == VHD_OK || d->vhd_ret == VHD_MORE) {
389 3507
                        assert(in_u == in_l);
390 3507
                        break;
391
                }
392
393 26922
                if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) {
394 588
                        d->limit -= d->out_u;
395 588
                        d->out_u = 0;
396 588
                        assert(d->out_u < d->out_l);
397 588
                        continue;
398
                }
399
400 26334
                switch (d->vhd_ret) {
401
                case VHD_NAME_SEC:
402
                        /* XXX: header flag for never-indexed header */
403
                case VHD_NAME:
404 13167
                        assert(d->namelen == 0);
405 13167
                        if (d->out_l - d->out_u < 2) {
406 0
                                d->error = H2SE_REQ_SIZE;
407 0
                                break;
408
                        }
409 13167
                        d->out[d->out_u++] = ':';
410 13167
                        d->out[d->out_u++] = ' ';
411 13167
                        d->namelen = d->out_u;
412 13167
                        break;
413
414
                case VHD_VALUE_SEC:
415
                        /* XXX: header flag for never-indexed header */
416
                case VHD_VALUE:
417 13104
                        assert(d->namelen > 0);
418 13104
                        if (d->out_l - d->out_u < 1) {
419 0
                                d->error = H2SE_REQ_SIZE;
420 0
                                break;
421
                        }
422 13104
                        d->error = h2h_addhdr(hp, d);
423 13104
                        if (d->error)
424 714
                                break;
425 12390
                        d->out[d->out_u++] = '\0'; /* Zero guard */
426 12390
                        d->out += d->out_u;
427 12390
                        d->out_l -= d->out_u;
428 12390
                        d->limit -= d->out_u;
429 12390
                        d->out_u = 0;
430 12390
                        d->namelen = 0;
431 12390
                        break;
432
433
                case VHD_BUF:
434 63
                        d->error = H2SE_REQ_SIZE;
435 63
                        break;
436
437
                default:
438 0
                        WRONG("Unhandled return value");
439 0
                        break;
440
                }
441
442 26334
                if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) {
443 105
                        d->out = WS_Reservation(d->ws);
444 105
                        d->out_l = e - d->out;
445 105
                        d->limit -= d->out_u;
446 105
                        d->out_u = 0;
447 105
                        assert(d->out_l > 0);
448 26334
                } else if (d->error)
449 672
                        break;
450
        }
451
452 4263
        if (d->limit < 0) {
453
                /* Fatal error, the client exceeded both http_req_size
454
                 * and h2_max_header_list_size. */
455 42
                H2S_Lock_VSLb(h2, SLT_SessError, "Header list too large");
456 42
                return (H2CE_ENHANCE_YOUR_CALM);
457
        }
458
459 4221
        if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) {
460
                /* Stream error, delay reporting until h2h_decode_hdr_fini so
461
                 * that we can process the complete header block. */
462 147
                return (NULL);
463
        }
464
465 4074
        return (d->error);
466 4263
}