← Commit list

228f3a5fac

Bobbeh Rhino
Bobbeh Rhino
2026-09-17
SUCCESS Duration: 433s

Subject

ldap: base64-encode a non-SAFE DN and reject a control-byte type name

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

lib/ldap.c+47 -20
@@ -230,27 +230,30 @@ static ULONG ldap_win_bind(struct Curl_easy *data, LDAP *server,
}
#endif /* USE_WIN32_LDAP */
+/* RFC 2849: a string that is not a SAFE-STRING (leading ':' or '<', leading or
+ trailing space, or any non-printable byte such as CR/LF) cannot go into LDIF
+ as-is. DNs and values get base64-encoded instead; an attribute type has no
+ base64 form in LDIF and is rejected by the caller. */
+static bool ldap_str_needs_base64(const char *s, size_t len)
+{
+ size_t i;
+ if(!len)
+ return FALSE;
+ if((s[0] == ':') || (s[0] == '<') || ISBLANK(s[0]) || ISBLANK(s[len - 1]))
+ return TRUE;
+ for(i = 0; i < len; i++)
+ if(!ISPRINT(s[i]))
+ return TRUE;
+ return FALSE;
+}
+
static bool ldap_value_needs_base64(const char *attr, size_t attr_len,
const BerValue *val)
{
- ber_len_t j;
-
if((attr_len > 7) && curl_strequal(";binary", attr + attr_len - 7))
return TRUE;
- /* check for a leading ':' or '<' (not a SAFE-INIT-CHAR per RFC 2849) or
- leading or trailing whitespace */
- if(val->bv_len && ((val->bv_val[0] == ':') || (val->bv_val[0] == '<') ||
- ISBLANK(val->bv_val[0]) ||
- ISBLANK(val->bv_val[val->bv_len - 1])))
- return TRUE;
-
- /* check for unprintable characters */
- for(j = 0; j < val->bv_len; j++)
- if(!ISPRINT(val->bv_val[j]))
- return TRUE;
-
- return FALSE;
+ return ldap_str_needs_base64(val->bv_val, val->bv_len);
}
#ifdef USE_WIN32_LDAP
@@ -271,6 +274,14 @@ static CURLcode show_vals(struct Curl_easy *data, BerValue **vals,
int i;
size_t attr_len = strlen(attr);
+ /* An attribute type has no base64 form in LDIF, so a type carrying control
+ bytes (CR/LF etc.) would let a hostile server inject lines. A conformant
+ type name never contains them, so reject it. */
+ if(ldap_str_needs_base64(attr, attr_len)) {
+ failf(data, "LDAP local: attribute type contains illegal bytes");
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
+
for(i = 0; vals[i] && !result; i++) {
result = Curl_client_write(data, CLIENTWRITE_BODY, "\t", 1);
if(!result)
@@ -534,12 +545,28 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
result = dn ? CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT;
else {
name_len = strlen(name);
- result = Curl_client_write(data, CLIENTWRITE_BODY, "DN: ", 4);
+ if(ldap_str_needs_base64(name, name_len)) {
+ char *dn_b64 = NULL;
+ size_t dn_b64_sz = 0;
+
+ /* A DN with e.g. CR/LF would inject LDIF lines, so encode it */
+ result = curlx_base64_encode((uint8_t *)name, name_len,
+ &dn_b64, &dn_b64_sz);
+ if(!result)
+ result = Curl_client_write(data, CLIENTWRITE_BODY, "DN:: ", 5);
+ if(!result)
+ result = Curl_client_write(data, CLIENTWRITE_BODY, dn_b64,
+ dn_b64_sz);
+ curlx_free(dn_b64);
+ }
+ else {
+ result = Curl_client_write(data, CLIENTWRITE_BODY, "DN: ", 4);
+ if(!result)
+ result = Curl_client_write(data, CLIENTWRITE_BODY, name, name_len);
+ }
+ if(!result)
+ result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
}
- if(!result)
- result = Curl_client_write(data, CLIENTWRITE_BODY, name, name_len);
- if(!result)
- result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
FREE_ON_WINLDAP(name);
ldap_memfree(dn);
if(result)
lib/openldap.c+45 -20
@@ -1100,6 +1100,23 @@ static CURLcode client_write(struct Curl_easy *data,
return result;
}
+/* RFC 2849: a string that is not a SAFE-STRING (leading ':' or '<', leading or
+ trailing space, or any non-printable byte such as CR/LF) cannot go into LDIF
+ as-is. DNs and values get base64-encoded instead; an attribute type has no
+ base64 form in LDIF and is rejected by the caller. */
+static bool oldap_str_needs_base64(const char *s, size_t len)
+{
+ size_t i;
+ if(!len)
+ return FALSE;
+ if((s[0] == ':') || (s[0] == '<') || ISBLANK(s[0]) || ISBLANK(s[len - 1]))
+ return TRUE;
+ for(i = 0; i < len; i++)
+ if(!ISPRINT(s[i]))
+ return TRUE;
+ return FALSE;
+}
+
static CURLcode oldap_recv(struct Curl_easy *data, int8_t sockindex, char *buf,
size_t len, size_t *pnread)
{
@@ -1172,8 +1189,21 @@ static CURLcode oldap_recv(struct Curl_easy *data, int8_t sockindex, char *buf,
break;
}
- result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
- STRCONST("\n"));
+ if(oldap_str_needs_base64(bv.bv_val, bv.bv_len)) {
+ char *dn_b64 = NULL;
+ size_t dn_b64_sz = 0;
+
+ /* A DN with e.g. CR/LF would inject LDIF lines, so base64-encode it. */
+ result = curlx_base64_encode((uint8_t *)bv.bv_val, bv.bv_len,
+ &dn_b64, &dn_b64_sz);
+ if(!result)
+ result = client_write(data, STRCONST("DN:: "), dn_b64, dn_b64_sz,
+ STRCONST("\n"));
+ curlx_free(dn_b64);
+ }
+ else
+ result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
+ STRCONST("\n"));
if(result)
break;
@@ -1186,6 +1216,17 @@ static CURLcode oldap_recv(struct Curl_easy *data, int8_t sockindex, char *buf,
if(!bv.bv_val)
break;
+ /* An attribute type has no base64 form in LDIF, so a type carrying
+ control bytes (CR/LF etc.) would let a hostile server inject lines.
+ A conformant type name never contains them, so reject it. */
+ if(oldap_str_needs_base64(bv.bv_val, bv.bv_len)) {
+ failf(data, "LDAP local: attribute type contains illegal bytes");
+ if(bvals)
+ ber_memfree(bvals);
+ result = CURLE_WEIRD_SERVER_REPLY;
+ break;
+ }
+
if(!bvals) {
result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
STRCONST(":\n"));
@@ -1205,24 +1246,8 @@ static CURLcode oldap_recv(struct Curl_easy *data, int8_t sockindex, char *buf,
if(result)
break;
- if(!binary) {
- /* check for a leading ':' or '<' (not a SAFE-INIT-CHAR per RFC
- 2849) or leading or trailing whitespace */
- if(bvals[i].bv_len &&
- ((bvals[i].bv_val[0] == ':') || (bvals[i].bv_val[0] == '<') ||
- 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 = oldap_str_needs_base64(bvals[i].bv_val, bvals[i].bv_len);
if(binary || binval) {
char *val_b64 = NULL;
size_t val_b64_sz = 0;