View Issue Details

IDProjectCategoryView StatusLast Update
0010354libmicrohttpd2Generalpublic2025-09-07 01:01
Reporterarthurscchan Assigned ToKarlson2k  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Fixed in VersionGit master 
Summary0010354: A conditional check bug in auth scheme parsing found in request_auth_get.c
DescriptionThe 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 InformationFound during the ongoing security audit carried out by Ada Logics and facilitated by OSTIF in the libmicrohttpd2 project.
TagsNo tags attached.

Activities

Karlson2k

2025-09-06 16:24

manager   ~0025838

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.

Karlson2k

2025-09-06 16:54

manager   ~0025844

Fixed by 4f008e59e8d99d2024cd5b0a39f9335f8dc7b2f3

Issue History

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