aboutsummaryrefslogtreecommitdiff
path: root/http.c
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2024-08-21 23:33:00 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2024-08-22 02:27:34 +0200
commit3a25e79f269aa171f4e5646d52eb2f90d275cb3c (patch)
tree8f39b7241373ab302a3e0eafa128289f7916624f /http.c
parent700ae79d57b1d78551d984ce2dc886a310cc6be8 (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.
Diffstat (limited to 'http.c')
-rw-r--r--http.c73
1 files changed, 66 insertions, 7 deletions
diff --git a/http.c b/http.c
index f6c5d5f..bc269a7 100644
--- a/http.c
+++ b/http.c
@@ -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;