View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0010354 | libmicrohttpd2 | General | public | 2025-09-03 17:56 | 2025-09-07 01:01 |
Reporter | arthurscchan | Assigned To | Karlson2k | ||
Priority | normal | Severity | minor | Reproducibility | always |
Status | resolved | Resolution | fixed | ||
Fixed in Version | Git master | ||||
Summary | 0010354: A conditional check bug in auth scheme parsing found in request_auth_get.c | ||||
Description | The function [`mhd_request_get_auth_header_value(...)`](https://git.gnunet.org/libmicrohttpd2.git/tree/src/mhd2/request_auth_get.c#n48) compares an auth **prefix** (e.g., `"Basic"`, `"Digest"`) against the **header value**, there exist some guards intended to ensure enough bytes are available, but one of them is checking a **wrong variable length**: ```c if (hdr_name.len != f->field.nv.name.len) continue; if (MHD_VK_HEADER != f->field.kind) continue; if (prefix_len > f->field.nv.name.len) continue; if (! mhd_str_equal_caseless_bin_n (hdr_name.cstr, f->field.nv.name.cstr, hdr_name.len)) continue; if (! mhd_str_equal_caseless_bin_n (prefix_str, f->field.nv.value.cstr, prefix_len)) continue; ``` There are two calls to the function [`mhd_str_equal_caseless_bin_n(...)`](https://git.gnunet.org/libmicrohttpd2.git/tree/src/mhd2/mhd_str.c#n1000), which requires three parameters. The function loops through the 1st and 2nd parameters character-by-character **N** times, where **N** is determined by the 3rd parameter. If the 3rd parameter is too large, it will cause a buffer over-read. ```c MHD_INTERNAL bool mhd_str_equal_caseless_bin_n (const char *str1, const char *str2, size_t len) { for (size_t i = 0; i < len; ++i) { const char c1 = str1[i]; const char c2 = str2[i]; if (charsequalcaseless (c1, c2)) continue; else return 0; } return !0; } ``` The first call to `mhd_str_equal_caseless_bin_n(...)` is OK because `hdr_name.len` must equal `f->field.nv.name.len`, and thus it **guarantees** the loop won’t go past the range of either `hdr_name.cstr` or `f->field.nv.name.cstr`, stopping just before the NUL terminator. This is guarded by the preceding check, which skips the call and moves to the next iteration when the lengths are not equal. However, the guard for the second `mhd_str_equal_caseless_bin_n(...)` call checks the **wrong** variable. It compares `prefix_len` against `f->field.nv.name.len`, then passes `f->field.nv.value.cstr` to the function. This incorrect check means that if `f->field.nv.value.len` is less than `prefix_len`, the guard still lets it pass, causing a buffer over-read in `mhd_str_equal_caseless_bin_n(...)`. Here is an example: assume `prefix_str` is `"ABC"`, `f->field.nv.name` is `"ABCDEF"` and `f->field.nv.value` is `"A"`. Then `prefix_len` will be `3` and `f->field.nv.name.len` will be `6`, so the check passes. The three parameters passed to `mhd_str_equal_caseless_bin_n(...)` will be `"ABC"`, `"A"` and `3`. This results in three iterations of the loop and causes a buffer over-read for `f->field.nv.value.cstr` since it only has 2 bytes (including the NUL terminator). ## Consequence This mismatch between the guard and the actual call to `mhd_str_equal_caseless_bin_n(...)` using different string lengths can cause a buffer over-read. That may lead to illegal memory access and wrong parsing of the authorisation headers, because the code fails to correctly catch data underflow. | ||||
Additional Information | Found during the ongoing security audit carried out by Ada Logics and facilitated by OSTIF in the libmicrohttpd2 project. | ||||
Tags | No tags attached. | ||||
|
It is not a security issue, but it is a performance issue. The check indeed must be (prefix_len > f->field.nv.value.len), not (prefix_len > f->field.nv.name.len). However, it does not trigger any buffer over-read. All strings that are processed, are guaranteed to be NUL-terminated C strings. The length of the string does not include NUL (zero) termination and the size of allocated memory is guaranteed to be at least length + 1. The 'prefix_str' is defined in this function and does not contain binary zeros. If 'f->field.nv.value.cstr' is shorter than prefix_len then the function mhd_str_equal_caseless_bin_n (prefix_str, f->field.nv.value.cstr, prefix_len) may compare 'f->field.nv.value.cstr' until NUL-termination is found in 'f->field.nv.value.cstr' where the function is stopped (as the char in 'prefix_str' does not match) and returned 'false'. Or, taking your example: `prefix_str` is actually `"ABC\0"` `f->field.nv.name` is actually `"ABCDEF\0"` `f->field.nv.value` is actually `"A\0"` `prefix_len` is `3` `mhd_str_equal_caseless_bin_n()` would stop at the second char (at index 1). This character is in correctly allocated memory. |
|
Fixed by 4f008e59e8d99d2024cd5b0a39f9335f8dc7b2f3 |
Date Modified | Username | Field | Change |
---|---|---|---|
2025-09-03 17:56 | arthurscchan | New Issue | |
2025-09-06 16:24 | Karlson2k | Note Added: 0025838 | |
2025-09-06 16:42 | Karlson2k | Assigned To | => Karlson2k |
2025-09-06 16:42 | Karlson2k | Status | new => confirmed |
2025-09-06 16:54 | Karlson2k | Status | confirmed => resolved |
2025-09-06 16:54 | Karlson2k | Resolution | open => fixed |
2025-09-06 16:54 | Karlson2k | Fixed in Version | => Git master |
2025-09-06 16:54 | Karlson2k | Note Added: 0025844 | |
2025-09-07 01:01 | root | Project | libmicrohttpd => libmicrohttpd2 |
2025-09-07 01:01 | root | Category | digest authentication (HTTP) => General |