diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-08-21 23:33:00 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-08-22 02:27:34 +0200 |
| commit | 3a25e79f269aa171f4e5646d52eb2f90d275cb3c (patch) | |
| tree | 8f39b7241373ab302a3e0eafa128289f7916624f | |
| parent | 700ae79d57b1d78551d984ce2dc886a310cc6be8 (diff) | |
http.c: Accept double quotes on boundaries
"multipart/form-data"-encoded POST requests might use double quotes for
their boundaries. While this is required when invalid characters are
otherwise used (e.g.: ':'), some web clients always insert double
quotes.
Additionally, according to RFC 2046 section 5.1.1, the boundary
parameter consists of 1 to 70 characters, but libweb was not imposing
such restrictions.
| -rw-r--r-- | http.c | 73 |
1 files changed, 66 insertions, 7 deletions
@@ -923,6 +923,64 @@ static int set_length(struct http_ctx *const h, const char *const len) return 0; } +static int get_boundary(const char *bnd, char *const buf, const size_t n) +{ + size_t len = strlen(bnd); + bool quotes = false; + + if (*bnd == '"') + { + if (bnd[len - 1] != '"') + { + fprintf(stderr, "%s: boundary starts with, but does not end with, " + "double quotes\n", __func__); + return 1; + } + + len -= strlen("\"\""); + quotes = true; + bnd++; + } + + int res = snprintf(buf, n, "%.*s", (int)len, bnd); + + if (res < 0) + { + fprintf(stderr, "%s: snprintf(3) failed with %d\n", __func__, res); + return -1; + } + else if (res >= n) + { + fprintf(stderr, "%s: boundary exceeds maximum length (%d/%zu)\n", + __func__, res, n); + return 1; + } + else if (isspace(buf[res - 1])) + { + fprintf(stderr, "%s: boundary ends with whitespace character: %s\n", + __func__, buf); + return 1; + } + +#define BSET "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "0123456789'()+_,-./=?" + + static const char bset[] = BSET, extset[] = BSET ": "; +#undef BSET + + const char *const set = quotes ? extset : bset; + + if (strspn(buf, set) != res) + { + fprintf(stderr, "%s: boundary contains invalid characters: %s\n", + __func__, buf); + return 1; + } + + return 0; +} + static int set_content_type(struct http_ctx *const h, const char *const type) { const char *const sep = strchr(type, ';'); @@ -975,14 +1033,15 @@ static int set_content_type(struct http_ctx *const h, const char *const type) } const char *val = eq + 1; + /* According to RFC 2046, section 5.1.1, null character included. */ + enum {MAXLEN = 71}; + char buf[MAXLEN]; + const int ret = get_boundary(val, buf, sizeof buf); - while (*val == ' ') - val++; - - if (!*val) + if (ret) { - fprintf(stderr, "%s: expected value after boundary\n", __func__); - return 1; + fprintf(stderr, "%s: get_boundary failed\n", __func__); + return ret; } struct ctx *const c = &h->ctx; @@ -990,7 +1049,7 @@ static int set_content_type(struct http_ctx *const h, const char *const type) dynstr_init(&b); - if (dynstr_append(&b, "\r\n--%s", val)) + if (dynstr_append(&b, "\r\n--%s", buf)) { fprintf(stderr, "%s: dynstr_append failed\n", __func__); return -1; |
