Skip to content

Raw binary in LDIF format when using the WinLDAP backend #21926

Description

@oreadvanthink

I did this

I am using curl_easy_setopt and curl_easy_perform to query a jpegPhoto attribute from an LDAP server. My code is equivalent to this CLI command:

curl "ldap://ldap.example.com/uid=jdoe,ou=people,dc=example,dc=com?jpegPhoto?base"

On Linux with the OpenLDAP backend, this query returns an LDIF text response, with the jpegPhoto value as base64.

DN: uid=jdoe,ou=people,dc=example,dc=com
        jpegPhoto:: /9j/4AAQSkZJRgABAQEASABIAAD//gBbUGh<more base64 here>

On Windows with the WinLDAP backend, it returns the attribute value as raw binary data.

DN: uid=jdoe,ou=people,dc=example,dc=com
        jpegPhoto: <raw binary data here>

The above is what the callback I set with CURLOPT_WRITEFUNCTION receives. When using ther CLI instead, it stops with a warning as it detects raw binary.

DN: uid=jdoe,ou=people,dc=example,dc=com
        jpegPhoto: Warning: Binary output can mess up your terminal. Use "--output -" to tell curl to output it to your terminal anyway,
Warning: or consider "--output <FILE>" to save to a file.

LDIF is a pure ASCII format, that uses spaces, tabs and newlines as delimiters. Curl chose to return the response as LDIF, but with the WinLDAP backend this LDIF response is malformed due to the injection of raw binary data. It makes the response unparseable.

Looking at the code, I see that lib/openldap.c is the OpenLDAP backend, and lib/ldap.c is the WinLDAP backend, and their logic to encode the values as base64 diverge.

curl/lib/openldap.c

Lines 1177 to 1212 in feb609f

binary = bv.bv_len > 7 &&
curl_strnequal(bv.bv_val + bv.bv_len - 7, ";binary", 7);
for(i = 0; bvals[i].bv_val != NULL; i++) {
bool binval = FALSE;
result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
STRCONST(":"));
if(result)
break;
if(!binary) {
/* check for leading or trailing whitespace */
if(bvals[i].bv_len &&
(ISBLANK(bvals[i].bv_val[0]) ||
ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1])))
binval = TRUE;
else {
/* check for unprintable characters */
unsigned int j;
for(j = 0; j < bvals[i].bv_len; j++)
if(!ISPRINT(bvals[i].bv_val[j])) {
binval = TRUE;
break;
}
}
}
if(binary || binval) {
char *val_b64 = NULL;
size_t val_b64_sz = 0;
/* Binary value, encode to base64. */
if(bvals[i].bv_len)
result = curlx_base64_encode((uint8_t *)bvals[i].bv_val,
bvals[i].bv_len,
&val_b64, &val_b64_sz);

curl/lib/ldap.c

Lines 516 to 522 in feb609f

if((attr_len > 7) &&
curl_strequal(";binary", attr + (attr_len - 7))) {
/* Binary attribute, encode to base64. */
if(vals[i]->bv_len) {
result = curlx_base64_encode((uint8_t *)vals[i]->bv_val,
vals[i]->bv_len,
&val_b64, &val_b64_sz);

Both check for the ;binary option, but only the OpenLDAP backend checks for leading and trailing whitespaces and for unprintable characters.

The ldap.c code should use the same logic than openldap.c (and possibly using shared code).

I don't know any workaround. I tried adding the ;binary option to the attribute in the query to force the base64 encoding, but it returns no result. It seems this option is reserved for certificate attributes only.

I expected the following

I expect LDAP responses to be in a valid LDIF format.

curl/libcurl version

curl 8.19.0

operating system

Windows 11 Pro

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions