From 34b62bd0c47c915a12ff1b81f52b123fc3eb4a69 Mon Sep 17 00:00:00 2001 From: Xavier Del Campo Romero Date: Thu, 22 Aug 2024 01:56:20 +0200 Subject: http.c: Fix ending boundaries not followed by CRLF According to RFC 2046, section 5.1.1, end boundaries might not be followed by CRLF. However, so far libweb naively relied on this behaviour as major implementations, such as cURL, Chromium or Gecko always add the optional CRLF, whereas Dillo does not. --- http.c | 125 ++++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 41 deletions(-) diff --git a/http.c b/http.c index bc269a7..e9b50ac 100644 --- a/http.c +++ b/http.c @@ -63,6 +63,7 @@ struct http_ctx MF_START_BOUNDARY, MF_HEADER_CR_LINE, MF_BODY_BOUNDARY_LINE, + MF_END_BOUNDARY, MF_END_BOUNDARY_CR_LINE } state; @@ -1590,46 +1591,32 @@ static int mf_header_cr_line(struct http_ctx *const h) return 0; } -static int end_boundary_line(struct http_ctx *const h) +static int end_boundary(struct http_ctx *const h) { - const char *const line = h->line; + /* Found end boundary. */ + struct ctx *const c = &h->ctx; + struct multiform *const m = &c->u.mf; - if (!*line) + const struct http_payload p = { - h->ctx.u.mf.state = MF_HEADER_CR_LINE; - return 0; - } - else if (!strcmp(line, "--")) - { - /* Found end boundary. */ - struct ctx *const c = &h->ctx; - struct multiform *const m = &c->u.mf; - - const struct http_payload p = + .cookie = { - .cookie = - { - .field = c->field, - .value = c->value - }, - - .op = c->op, - .resource = c->resource, - .u.post = - { - .files = m->files, - .pairs = m->pairs, - .nfiles = m->nfiles, - .npairs = m->npairs - } - }; + .field = c->field, + .value = c->value + }, - return send_payload(h, &p); - } + .op = c->op, + .resource = c->resource, + .u.post = + { + .files = m->files, + .pairs = m->pairs, + .nfiles = m->nfiles, + .npairs = m->npairs + } + }; - fprintf(stderr, "%s: unexpected line after boundary: %s\n", - __func__, line); - return 1; + return send_payload(h, &p); } static int process_mf_line(struct http_ctx *const h) @@ -1637,8 +1624,7 @@ static int process_mf_line(struct http_ctx *const h) static int (*const state[])(struct http_ctx *) = { [MF_START_BOUNDARY] = start_boundary_line, - [MF_HEADER_CR_LINE] = mf_header_cr_line, - [MF_END_BOUNDARY_CR_LINE] = end_boundary_line + [MF_HEADER_CR_LINE] = mf_header_cr_line }; h->ctx.payload.read += strlen(h->line) + strlen("\r\n"); @@ -1827,7 +1813,7 @@ static bool name_exists(const struct multiform *const m, if (!strcmp(f->name, p->name)) { fprintf(stderr, "%s: \"%s\" defined more than once\n", - f->name, __func__); + __func__, f->name); return true; } } @@ -1893,7 +1879,7 @@ static int read_mf_body_boundary_byte(struct http_ctx *const h, const char b, memset(m->boundary, '\0', clen); m->blen = 0; - m->state = MF_END_BOUNDARY_CR_LINE; + m->state = MF_END_BOUNDARY; m->written = 0; return ret; } @@ -1991,6 +1977,53 @@ static int read_mf_body_boundary(struct http_ctx *const h, return 0; } +static int read_mf_end_boundary(struct http_ctx *const h, + const char *const buf, size_t *const n) +{ + struct ctx *const c = &h->ctx; + struct multiform *const m = &c->u.mf; + static const char end[] = "--", crlf[] = "\r\n"; + const bool check = c->len >= strlen(end) || c->len >= strlen(crlf); + + h->line[c->len++] = *buf; + (*n)--; + + if (c->len >= strlen(end) && !strncmp(h->line, end, strlen(end))) + { + c->len = 0; + + /* Found end boundary. */ + if (!*n) + return end_boundary(h); + else + m->state = MF_END_BOUNDARY_CR_LINE; + } + else if (c->len >= strlen(crlf) && !strncmp(h->line, crlf, strlen(crlf))) + { + c->len = 0; + m->state = MF_HEADER_CR_LINE; + } + else if (check) + { + fprintf(stderr, "%s: unexpected boundary delimiter: %.*s\n", + __func__, (int)c->len, h->line); + return 1; + } + + return 0; +} + +static int process_mf_end_crlf(struct http_ctx *const h) +{ + if (*h->line) + { + fprintf(stderr, "%s: expected empty line\n", __func__); + return 1; + } + + return end_boundary(h); +} + static int read_multiform(struct http_ctx *const h, const char *const buf, size_t *const sz) { @@ -2002,9 +2035,7 @@ static int read_multiform(struct http_ctx *const h, const char *const buf, case MF_START_BOUNDARY: /* Fall through. */ case MF_HEADER_CR_LINE: - /* Fall through. */ - case MF_END_BOUNDARY_CR_LINE: - if ((res = update_lstate(h, close, process_mf_line, buf, sz))) + if ((res = update_lstate(h, process_mf_line, buf, sz))) return res; break; @@ -2014,6 +2045,18 @@ static int read_multiform(struct http_ctx *const h, const char *const buf, return res; break; + + case MF_END_BOUNDARY: + if ((res = read_mf_end_boundary(h, buf, sz))) + return res; + + break; + + case MF_END_BOUNDARY_CR_LINE: + if ((res = update_lstate(h, process_mf_end_crlf, buf, sz))) + return res; + + break; } return 0; -- cgit v1.2.3