openldap: several minor improvements

- Early check proper LDAP URL syntax. Reject URLs with a userinfo part.
- Use dynamic memory for ldap_init_fd() URL rather than a
  stack-allocated buffer.
- Never chase referrals: supporting it would require additional parallel
  connections and alternate authentication credentials.
- Do not wait 1 microsecond while polling/reading query response data.
- Store last received server code for retrieval with CURLINFO_RESPONSE_CODE.

Closes #8140
This commit is contained in:
Patrick Monnerat 2021-12-14 15:42:47 +01:00 committed by Daniel Stenberg
parent 39406280bb
commit cdc1da9120
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
2 changed files with 108 additions and 87 deletions

View file

@ -30,15 +30,14 @@ CURLINFO_RESPONSE_CODE \- get the last response code
CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_RESPONSE_CODE, long *codep); CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_RESPONSE_CODE, long *codep);
.fi .fi
.SH DESCRIPTION .SH DESCRIPTION
Pass a pointer to a long to receive the last received HTTP, FTP or SMTP Pass a pointer to a long to receive the last received HTTP, FTP, SMTP or
response code. This option was previously known as CURLINFO_HTTP_CODE in LDAP (openldap only) response code. This option was previously known as
libcurl 7.10.7 and earlier. The stored value will be zero if no server CURLINFO_HTTP_CODE in libcurl 7.10.7 and earlier.
response code has been received. Note that a proxy's CONNECT response should The stored value will be zero if no server response code has been received.
Note that a proxy's CONNECT response should
be read with \fICURLINFO_HTTP_CONNECTCODE(3)\fP and not this. be read with \fICURLINFO_HTTP_CONNECTCODE(3)\fP and not this.
Support for SMTP responses added in 7.25.0.
.SH PROTOCOLS .SH PROTOCOLS
HTTP, FTP and SMTP HTTP, FTP, SMTP and LDAP
.SH EXAMPLE .SH EXAMPLE
.nf .nf
CURL *curl = curl_easy_init(); CURL *curl = curl_easy_init();
@ -55,6 +54,7 @@ if(curl) {
.fi .fi
.SH AVAILABILITY .SH AVAILABILITY
Added in 7.10.8. CURLINFO_HTTP_CODE was added in 7.4.1. Added in 7.10.8. CURLINFO_HTTP_CODE was added in 7.4.1.
Support for SMTP responses added in 7.25.0, for OpenLDAP in 7.81.0.
.SH RETURN VALUE .SH RETURN VALUE
Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not. Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
.SH "SEE ALSO" .SH "SEE ALSO"

View file

@ -154,20 +154,6 @@ const struct Curl_handler Curl_handler_ldaps = {
}; };
#endif #endif
static const char *url_errs[] = {
"success",
"out of memory",
"bad parameter",
"unrecognized scheme",
"unbalanced delimiter",
"bad URL",
"bad host or port",
"bad or missing attributes",
"bad or missing scope",
"bad or missing filter",
"bad or missing extensions"
};
struct ldapconninfo { struct ldapconninfo {
LDAP *ld; /* Openldap connection handle. */ LDAP *ld; /* Openldap connection handle. */
Curl_recv *recv; /* For stacking SSL handler */ Curl_recv *recv; /* For stacking SSL handler */
@ -231,40 +217,66 @@ static CURLcode oldap_map_error(int rc, CURLcode result)
return result; return result;
} }
static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
{
CURLcode result = CURLE_OK;
int rc = LDAP_URL_ERR_BADURL;
static const char * const url_errs[] = {
"success",
"out of memory",
"bad parameter",
"unrecognized scheme",
"unbalanced delimiter",
"bad URL",
"bad host or port",
"bad or missing attributes",
"bad or missing scope",
"bad or missing filter",
"bad or missing extensions"
};
*ludp = NULL;
if(!data->state.up.user && !data->state.up.password &&
!data->state.up.options)
rc = ldap_url_parse(data->state.url, ludp);
if(rc != LDAP_URL_SUCCESS) {
const char *msg = "url parsing problem";
result = rc == LDAP_URL_ERR_MEM? CURLE_OUT_OF_MEMORY: CURLE_URL_MALFORMAT;
rc -= LDAP_URL_SUCCESS;
if((size_t) rc < sizeof(url_errs) / sizeof(url_errs[0]))
msg = url_errs[rc];
failf(data, "LDAP local: %s", msg);
}
return result;
}
static CURLcode oldap_setup_connection(struct Curl_easy *data, static CURLcode oldap_setup_connection(struct Curl_easy *data,
struct connectdata *conn) struct connectdata *conn)
{ {
struct ldapconninfo *li; CURLcode result;
LDAPURLDesc *lud; LDAPURLDesc *lud;
int rc, proto; struct ldapconninfo *li;
CURLcode status;
rc = ldap_url_parse(data->state.url, &lud); /* Early URL syntax check. */
if(rc != LDAP_URL_SUCCESS) { result = oldap_url_parse(data, &lud);
const char *msg = "url parsing problem";
status = CURLE_URL_MALFORMAT;
if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
if(rc == LDAP_URL_ERR_MEM)
status = CURLE_OUT_OF_MEMORY;
msg = url_errs[rc];
}
failf(data, "LDAP local: %s", msg);
return status;
}
proto = ldap_pvt_url_scheme2proto(lud->lud_scheme);
ldap_free_urldesc(lud); ldap_free_urldesc(lud);
li = calloc(1, sizeof(struct ldapconninfo)); if(!result) {
if(!li) li = calloc(1, sizeof(struct ldapconninfo));
return CURLE_OUT_OF_MEMORY; if(!li)
li->proto = proto; result = CURLE_OUT_OF_MEMORY;
conn->proto.ldapc = li; else {
connkeep(conn, "OpenLDAP default"); li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
conn->proto.ldapc = li;
connkeep(conn, "OpenLDAP default");
/* Clear the TLS upgraded flag */ /* Clear the TLS upgraded flag */
conn->bits.tls_upgraded = FALSE; conn->bits.tls_upgraded = FALSE;
}
}
return CURLE_OK; return result;
} }
/* Starts LDAP simple bind. */ /* Starts LDAP simple bind. */
@ -350,29 +362,31 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
{ {
struct connectdata *conn = data->conn; struct connectdata *conn = data->conn;
struct ldapconninfo *li = conn->proto.ldapc; struct ldapconninfo *li = conn->proto.ldapc;
int rc, proto = LDAP_VERSION3; static const int version = LDAP_VERSION3;
char hosturl[1024]; int rc;
char *ptr; char *hosturl;
#ifdef CURL_OPENLDAP_DEBUG #ifdef CURL_OPENLDAP_DEBUG
static int do_trace = -1; static int do_trace = -1;
#endif #endif
(void)done; (void)done;
strcpy(hosturl, "ldap"); hosturl = aprintf("ldap%s://%s:%d",
ptr = hosturl + 4; conn->handler->flags & PROTOPT_SSL? "s": "",
if(conn->handler->flags & PROTOPT_SSL) conn->host.name, conn->remote_port);
*ptr++ = 's'; if(!hosturl)
msnprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d", return CURLE_OUT_OF_MEMORY;
conn->host.name, conn->remote_port);
rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld); rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
if(rc) { if(rc) {
failf(data, "LDAP local: Cannot connect to %s, %s", failf(data, "LDAP local: Cannot connect to %s, %s",
hosturl, ldap_err2string(rc)); hosturl, ldap_err2string(rc));
free(hosturl);
return CURLE_COULDNT_CONNECT; return CURLE_COULDNT_CONNECT;
} }
free(hosturl);
#ifdef CURL_OPENLDAP_DEBUG #ifdef CURL_OPENLDAP_DEBUG
if(do_trace < 0) { if(do_trace < 0) {
const char *env = getenv("CURL_OPENLDAP_TRACE"); const char *env = getenv("CURL_OPENLDAP_TRACE");
@ -382,7 +396,11 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace); ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
#endif #endif
ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto); /* Try version 3 first. */
ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
/* Do not chase referrals. */
ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
#ifdef USE_SSL #ifdef USE_SSL
if(conn->handler->flags & PROTOPT_SSL) if(conn->handler->flags & PROTOPT_SSL)
@ -453,6 +471,10 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0); rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
if(rc) if(rc)
code = rc; code = rc;
else {
/* store the latest code for later retrieval */
data->info.httpcode = code;
}
/* If protocol version 3 is not supported, fallback to version 2. */ /* If protocol version 3 is not supported, fallback to version 2. */
if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2
@ -551,44 +573,40 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done)
struct connectdata *conn = data->conn; struct connectdata *conn = data->conn;
struct ldapconninfo *li = conn->proto.ldapc; struct ldapconninfo *li = conn->proto.ldapc;
struct ldapreqinfo *lr; struct ldapreqinfo *lr;
CURLcode status = CURLE_OK; CURLcode result;
int rc = 0; int rc;
LDAPURLDesc *ludp = NULL; LDAPURLDesc *lud;
int msgid; int msgid;
connkeep(conn, "OpenLDAP do"); connkeep(conn, "OpenLDAP do");
infof(data, "LDAP local: %s", data->state.url); infof(data, "LDAP local: %s", data->state.url);
rc = ldap_url_parse(data->state.url, &ludp); result = oldap_url_parse(data, &lud);
if(rc != LDAP_URL_SUCCESS) { if(!result) {
const char *msg = "url parsing problem"; rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
status = CURLE_URL_MALFORMAT; lud->lud_filter, lud->lud_attrs, 0,
if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) { NULL, NULL, NULL, 0, &msgid);
if(rc == LDAP_URL_ERR_MEM) ldap_free_urldesc(lud);
status = CURLE_OUT_OF_MEMORY; if(rc != LDAP_SUCCESS) {
msg = url_errs[rc]; failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
result = CURLE_LDAP_SEARCH_FAILED;
}
else {
lr = calloc(1, sizeof(struct ldapreqinfo));
if(!lr) {
ldap_abandon_ext(li->ld, msgid, NULL, NULL);
result = CURLE_OUT_OF_MEMORY;
}
else {
lr->msgid = msgid;
data->req.p.ldap = lr;
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
*done = TRUE;
}
} }
failf(data, "LDAP local: %s", msg);
return status;
} }
return result;
rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
ludp->lud_filter, ludp->lud_attrs, 0,
NULL, NULL, NULL, 0, &msgid);
ldap_free_urldesc(ludp);
if(rc != LDAP_SUCCESS) {
failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
return CURLE_LDAP_SEARCH_FAILED;
}
lr = calloc(1, sizeof(struct ldapreqinfo));
if(!lr)
return CURLE_OUT_OF_MEMORY;
lr->msgid = msgid;
data->req.p.ldap = lr;
Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
*done = TRUE;
return CURLE_OK;
} }
static CURLcode oldap_done(struct Curl_easy *data, CURLcode res, static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
@ -653,7 +671,7 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
int rc; int rc;
LDAPMessage *msg = NULL; LDAPMessage *msg = NULL;
BerElement *ber = NULL; BerElement *ber = NULL;
struct timeval tv = {0, 1}; struct timeval tv = {0, 0};
struct berval bv, *bvals; struct berval bv, *bvals;
int binary = 0; int binary = 0;
CURLcode result = CURLE_AGAIN; CURLcode result = CURLE_AGAIN;
@ -689,6 +707,9 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
break; break;
} }
/* store the latest code for later retrieval */
data->info.httpcode = code;
switch(code) { switch(code) {
case LDAP_SIZELIMIT_EXCEEDED: case LDAP_SIZELIMIT_EXCEEDED:
infof(data, "There are more than %d entries", lr->nument); infof(data, "There are more than %d entries", lr->nument);