varnish-cache/bin/varnishd/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_varnishd.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 2620
h2h_assert_ready(const struct h2h_decode *d)
43
{
44
45 2620
        CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
46 2620
        AN(d->out);
47 2620
        assert(d->namelen >= 2); /* 2 chars from the ": " that we added */
48 2620
        assert(d->namelen <= d->out_u);
49 2620
        assert(d->out[d->namelen - 2] == ':');
50 2620
        assert(d->out[d->namelen - 1] == ' ');
51 2620
}
52
53
// rfc9113,l,2493,2528
54
static h2_error
55 2620
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 2620
        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 2620
        l = vmin_t(int, Tlen(nm) + 2 + Tlen(val), 20);
75 2620
        state = FLD_NAME_FIRST;
76 19890
        Tforeach(p, nm) {
77 17290
                switch(state) {
78
                case FLD_NAME_FIRST:
79 2620
                        state = FLD_NAME;
80 2620
                        if (*p == ':')
81 2040
                                break;
82
                        /* FALLTHROUGH */
83
                case FLD_NAME:
84 15250
                        if (isupper(*p)) {
85 10
                                VSLb(vsl, SLT_BogoHeader,
86
                                    "Illegal field header name (upper-case): %.*s",
87 5
                                    l, nm.b);
88 5
                                return (H2SE_PROTOCOL_ERROR);
89
                        }
90 15245
                        if (!vct_istchar(*p) || *p == ':') {
91 30
                                VSLb(vsl, SLT_BogoHeader,
92
                                    "Illegal field header name (non-token): %.*s",
93 15
                                    l, nm.b);
94 15
                                return (H2SE_PROTOCOL_ERROR);
95
                        }
96 15230
                        break;
97
                default:
98 0
                        WRONG("http2 field name validation state");
99 0
                }
100 17270
        }
101
102 2600
        state = FLD_VALUE_FIRST;
103 158195
        Tforeach(p, val) {
104 155645
                switch(state) {
105
                case FLD_VALUE_FIRST:
106 2565
                        if (vct_issp(*p)) {
107 90
                                VSLb(vsl, SLT_BogoHeader,
108 45
                                    "Illegal field value start %.*s", l, nm.b);
109 45
                                return (H2SE_PROTOCOL_ERROR);
110
                        }
111 2520
                        state = FLD_VALUE;
112
                        /* FALLTHROUGH */
113
                case FLD_VALUE:
114 155600
                        if (!vct_ishdrval(*p)) {
115 10
                                VSLb(vsl, SLT_BogoHeader,
116 5
                                    "Illegal field value %.*s", l, nm.b);
117 5
                                return (H2SE_PROTOCOL_ERROR);
118
                        }
119 155595
                        break;
120
                default:
121 0
                        WRONG("http2 field value validation state");
122 0
                }
123 155595
        }
124 2550
        if (state == FLD_VALUE && vct_issp(val.e[-1])) {
125 20
                VSLb(vsl, SLT_BogoHeader,
126 10
                    "Illegal field value (end) %.*s", l, nm.b);
127 10
                return (H2SE_PROTOCOL_ERROR);
128
        }
129 2540
        return (0);
130 2620
}
131
132
static h2_error
133 2620
h2h_addhdr(struct http *hp, struct h2h_decode *d)
134
{
135
        /* XXX: This might belong in cache/cache_http.c */
136
        txt hdr, nm, val;
137
        int disallow_empty;
138
        const char *p;
139
        unsigned n, has_dup;
140
        h2_error err;
141
142 2620
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
143 2620
        h2h_assert_ready(d);
144
145
        /* Assume hdr is by default a regular header from what we decoded. */
146 2620
        hdr.b = d->out;
147 2620
        hdr.e = hdr.b + d->out_u;
148 2620
        n = hp->nhd;
149
150
        /* nm and val are separated by ": " */
151 2620
        nm.b = hdr.b;
152 2620
        nm.e = nm.b + d->namelen - 2;
153 2620
        val.b = nm.e + 2;
154 2620
        val.e = hdr.e;
155
156 2620
        err = h2h_checkhdr(hp->vsl, nm, val);
157 2620
        if (err != NULL)
158 80
                return (err);
159
160 2540
        disallow_empty = 0;
161 2540
        has_dup = 0;
162
163 2540
        if (Tlen(hdr) > cache_param->http_req_hdr_len) {
164 5
                VSLb(hp->vsl, SLT_BogoHeader, "Header too large: %.20s", hdr.b);
165 5
                return (H2SE_ENHANCE_YOUR_CALM);
166
        }
167
168
        /* Match H/2 pseudo headers */
169
        /* XXX: Should probably have some include tbl for pseudo-headers */
170 2535
        if (!Tstrcmp(nm, ":method")) {
171 625
                hdr.b = val.b;
172 625
                n = HTTP_HDR_METHOD;
173 625
                disallow_empty = 1;
174
175
                /* Check HTTP token */
176 2650
                Tforeach(p, hdr) {
177 2025
                        if (!vct_istchar(*p))
178 0
                                return (H2SE_PROTOCOL_ERROR);
179 2025
                }
180 2535
        } else if (!Tstrcmp(nm, ":path")) {
181 655
                hdr.b = val.b;
182 655
                n = HTTP_HDR_URL;
183 655
                disallow_empty = 1;
184
185
                // rfc9113,l,2693,2705
186 655
                if (Tlen(val) > 0 && val.b[0] != '/' && Tstrcmp(val, "*")) {
187 20
                        VSLb(hp->vsl, SLT_BogoHeader,
188
                            "Illegal :path pseudo-header %.*s",
189 10
                            (int)Tlen(val), val.b);
190 10
                        return (H2SE_PROTOCOL_ERROR);
191
                }
192
193
                /* Path cannot contain LWS or CTL */
194 2165
                Tforeach(p, hdr) {
195 1520
                        if (vct_islws(*p) || vct_isctl(*p))
196 0
                                return (H2SE_PROTOCOL_ERROR);
197 1520
                }
198 1900
        } else if (!Tstrcmp(nm, ":scheme")) {
199
                /* XXX: What to do about this one? (typically
200
                   "http" or "https"). For now set it as a normal
201
                   header, stripping the first ':'. */
202 630
                hdr.b++;
203 630
                has_dup = d->has_scheme;
204 630
                d->has_scheme = 1;
205 630
                disallow_empty = 1;
206
207
                /* Check HTTP token */
208 3115
                Tforeach(p, val) {
209 2485
                        if (!vct_istchar(*p))
210 0
                                return (H2SE_PROTOCOL_ERROR);
211 2485
                }
212 1255
        } else if (!Tstrcmp(nm, ":authority")) {
213
                /* NB: we inject "host" in place of "rity" for
214
                 * the ":authority" pseudo-header.
215
                 */
216 85
                memcpy(d->out + 6, "host", 4);
217 85
                hdr.b += 6;
218 85
                nm = Tstr(":authority"); /* preserve original */
219 85
                has_dup = d->has_authority;
220 85
                d->has_authority = 1;
221 625
        } else if (nm.b[0] == ':') {
222 20
                VSLb(hp->vsl, SLT_BogoHeader,
223
                    "Unknown pseudo-header: %.*s",
224 10
                    vmin_t(int, Tlen(hdr), 20), hdr.b);
225 10
                return (H2SE_PROTOCOL_ERROR);   // rfc7540,l,2990,2992
226
        }
227
228 2515
        if (disallow_empty && Tlen(val) == 0) {
229 60
                VSLb(hp->vsl, SLT_BogoHeader,
230
                    "Empty pseudo-header %.*s",
231 30
                    (int)Tlen(nm), nm.b);
232 30
                return (H2SE_PROTOCOL_ERROR);
233
        }
234
235 2485
        if (n >= HTTP_HDR_FIRST) {
236
                /* Check for space in struct http */
237 1235
                if (n >= hp->shd) {
238 10
                        VSLb(hp->vsl, SLT_LostHeader,
239
                            "Too many headers: %.*s",
240 5
                            vmin_t(int, Tlen(hdr), 20), hdr.b);
241 5
                        return (H2SE_ENHANCE_YOUR_CALM);
242
                }
243 1230
                hp->nhd++;
244 1230
                AZ(hp->hd[n].b);
245 1230
        }
246
247 2480
        if (has_dup || hp->hd[n].b != NULL) {
248 25
                assert(nm.b[0] == ':');
249 50
                VSLb(hp->vsl, SLT_BogoHeader,
250
                    "Duplicate pseudo-header %.*s",
251 25
                    (int)Tlen(nm), nm.b);
252 25
                return (H2SE_PROTOCOL_ERROR);   // rfc7540,l,3158,3162
253
        }
254
255 2455
        hp->hd[n] = hdr;
256 2455
        return (0);
257 2620
}
258
259
static void
260 800
h2h_decode_init(const struct h2_sess *h2, struct ws *ws)
261
{
262
        struct h2h_decode *d;
263
264 800
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
265 800
        CHECK_OBJ_NOTNULL(ws, WS_MAGIC);
266
267 800
        AN(h2->decode);
268 800
        d = h2->decode;
269 800
        INIT_OBJ(d, H2H_DECODE_MAGIC);
270 800
        VHD_Init(d->vhd);
271 800
        d->out_l = WS_ReserveSize(ws, cache_param->http_req_size);
272
        /*
273
         * Can't do any work without any buffer
274
         * space. Require non-zero size.
275
         */
276 800
        XXXAN(d->out_l);
277 800
        d->out = WS_Reservation(ws);
278
279 800
        if (cache_param->h2_max_header_list_size == 0)
280 800
                d->limit =
281 800
                    (long)(h2->local_settings.max_header_list_size * 1.5);
282
        else
283 0
                d->limit = cache_param->h2_max_header_list_size;
284
285 800
        if (d->limit < h2->local_settings.max_header_list_size)
286 0
                d->limit = INT64_MAX;
287
288 800
        d->ws = ws;
289 800
}
290
291
void
292 800
h2h_decode_hdr_init(const struct h2_sess *h2)
293
{
294
295 800
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
296 800
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
297 800
        CHECK_OBJ_NOTNULL(h2->new_req->http, HTTP_MAGIC);
298 800
        h2h_decode_init(h2, h2->new_req->ws);
299 800
}
300
301
/* Possible error returns:
302
 *
303
 * H2E_COMPRESSION_ERROR: Lost compression state due to incomplete header
304
 * block. This is a connection level error.
305
 *
306
 * H2E_ENHANCE_YOUR_CALM: Ran out of workspace or http header space. This
307
 * is a stream level error.
308
 */
309
h2_error
310 800
h2h_decode_hdr_fini(const struct h2_sess *h2)
311
{
312
        h2_error ret;
313
        struct h2h_decode *d;
314
315 800
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
316 800
        d = h2->decode;
317 800
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
318 800
        CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
319 800
        WS_ReleaseP(d->ws, d->out);
320 800
        if (d->vhd_ret != VHD_OK) {
321
                /* HPACK header block didn't finish at an instruction
322
                   boundary */
323 350
                VSLb(h2->new_req->http->vsl, SLT_BogoHeader,
324 175
                    "HPACK compression error/fini (%s)", VHD_Error(d->vhd_ret));
325 175
                ret = H2CE_COMPRESSION_ERROR;
326 800
        } else if (d->error == NULL && !d->has_scheme) {
327 20
                H2S_Lock_VSLb(h2, SLT_Debug, "Missing :scheme");
328 20
                ret = H2SE_MISSING_SCHEME; //rfc7540,l,3087,3090
329 20
        } else
330 605
                ret = d->error;
331 800
        FINI_OBJ(d);
332 800
        if (ret == H2SE_REQ_SIZE) {
333 5
                VSLb(h2->new_req->http->vsl, SLT_LostHeader,
334
                    "Header list too large");
335 5
        }
336 800
        return (ret);
337
}
338
339
/* Possible error returns:
340
 *
341
 * H2E_COMPRESSION_ERROR: Lost compression state due to invalid header
342
 * block. This is a connection level error.
343
 *
344
 * H2E_PROTOCOL_ERROR: Malformed header or duplicate pseudo-header.
345
 *                     Violation of field name/value charsets
346
 */
347
h2_error
348 870
h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l)
349
{
350
        struct http *hp;
351
        struct h2h_decode *d;
352 870
        size_t in_u = 0;
353
        const char *r, *e;
354
355 870
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
356 870
        CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
357 870
        hp = h2->new_req->http;
358 870
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
359 870
        d = h2->decode;
360 870
        CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
361 870
        CHECK_OBJ_NOTNULL(d->ws, WS_MAGIC);
362 870
        r = WS_Reservation(d->ws);
363 870
        AN(r);
364 870
        e = r + WS_ReservationSize(d->ws);
365
366
        /* Only H2E_ENHANCE_YOUR_CALM indicates that we should continue
367
           processing. Other errors should have been returned and handled
368
           by the caller. */
369 870
        if (d->error != NULL)
370 20
                assert(H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM));
371
372 6105
        while (d->limit >= 0) {
373 6100
                AN(d->out);
374 6100
                assert(d->out_u <= d->out_l);
375 12200
                d->vhd_ret = VHD_Decode(d->vhd, h2->dectbl, in, in_l, &in_u,
376 6100
                    d->out, d->out_l, &d->out_u);
377
378 6100
                if (d->vhd_ret < 0) {
379 20
                        H2S_Lock_VSLb(h2, SLT_BogoHeader,
380
                            "HPACK compression error (%s)",
381 10
                            VHD_Error(d->vhd_ret));
382 10
                        d->error = H2CE_COMPRESSION_ERROR;
383 10
                        break;
384 6090
                } else if (d->vhd_ret == VHD_OK || d->vhd_ret == VHD_MORE) {
385 700
                        assert(in_u == in_l);
386 700
                        break;
387
                }
388
389 5390
                if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) {
390 130
                        d->limit -= d->out_u;
391 130
                        d->out_u = 0;
392 130
                        assert(d->out_u < d->out_l);
393 130
                        continue;
394
                }
395
396 5260
                switch (d->vhd_ret) {
397
                case VHD_NAME_SEC:
398
                        /* XXX: header flag for never-indexed header */
399
                case VHD_NAME:
400 2635
                        assert(d->namelen == 0);
401 2635
                        if (d->out_l - d->out_u < 2) {
402 5
                                d->error = H2SE_REQ_SIZE;
403 5
                                break;
404
                        }
405 2630
                        d->out[d->out_u++] = ':';
406 2630
                        d->out[d->out_u++] = ' ';
407 2630
                        d->namelen = d->out_u;
408 2630
                        break;
409
410
                case VHD_VALUE_SEC:
411
                        /* XXX: header flag for never-indexed header */
412
                case VHD_VALUE:
413 2620
                        assert(d->namelen > 0);
414 2620
                        if (d->out_l - d->out_u < 1) {
415 0
                                d->error = H2SE_REQ_SIZE;
416 0
                                break;
417
                        }
418 2620
                        d->error = h2h_addhdr(hp, d);
419 2620
                        if (d->error)
420 165
                                break;
421 2455
                        d->out[d->out_u++] = '\0'; /* Zero guard */
422 2455
                        d->out += d->out_u;
423 2455
                        d->out_l -= d->out_u;
424 2455
                        d->limit -= d->out_u;
425 2455
                        d->out_u = 0;
426 2455
                        d->namelen = 0;
427 2455
                        break;
428
429
                case VHD_BUF:
430 5
                        d->error = H2SE_REQ_SIZE;
431 5
                        break;
432
433
                default:
434 0
                        WRONG("Unhandled return value");
435 0
                        break;
436
                }
437
438 5260
                if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) {
439 20
                        d->out = WS_Reservation(d->ws);
440 20
                        d->out_l = e - d->out;
441 20
                        d->limit -= d->out_u;
442 20
                        d->out_u = 0;
443 20
                        assert(d->out_l > 0);
444 5260
                } else if (d->error)
445 155
                        break;
446
        }
447
448 870
        if (d->limit < 0) {
449
                /* Fatal error, the client exceeded both http_req_size
450
                 * and h2_max_header_list_size. */
451 5
                H2S_Lock_VSLb(h2, SLT_SessError, "Header list too large");
452 5
                return (H2CE_ENHANCE_YOUR_CALM);
453
        }
454
455 865
        if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) {
456
                /* Stream error, delay reporting until h2h_decode_hdr_fini so
457
                 * that we can process the complete header block. */
458 35
                return (NULL);
459
        }
460
461 830
        return (d->error);
462 870
}