#include "curl_setup.h"
#if !defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)
#include <ldap.h>
#include "urldata.h"
#include "url.h"
#include <curl/curl.h>
#include "sendf.h"
#include "vtls/vtls.h"
#include "transfer.h"
#include "curl_ldap.h"
#include "curlx/base64.h"
#include "cfilters.h"
#include "connect.h"
#include "curl_sasl.h"
#include "strcase.h"
#include "curl_memory.h"
#include "memdebug.h"
typedef enum {
OLDAP_STOP,
OLDAP_SSL,
OLDAP_STARTTLS,
OLDAP_TLS,
OLDAP_MECHS,
OLDAP_SASL,
OLDAP_BIND,
OLDAP_BINDV2,
OLDAP_LAST
} ldapstate;
#ifndef _LDAP_PVT_H
extern int ldap_pvt_url_scheme2proto(const char *);
extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
LDAP **ld);
#endif
static CURLcode oldap_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
static CURLcode oldap_do(struct Curl_easy *data, bool *done);
static CURLcode oldap_done(struct Curl_easy *data, CURLcode, bool);
static CURLcode oldap_connect(struct Curl_easy *data, bool *done);
static CURLcode oldap_connecting(struct Curl_easy *data, bool *done);
static CURLcode oldap_disconnect(struct Curl_easy *data,
struct connectdata *conn, bool dead);
static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
const struct bufref *initresp);
static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
const struct bufref *resp);
static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech);
static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out);
static Curl_recv oldap_recv;
const struct Curl_handler Curl_handler_ldap = {
"ldap",
oldap_setup_connection,
oldap_do,
oldap_done,
ZERO_NULL,
oldap_connect,
oldap_connecting,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
oldap_disconnect,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
PORT_LDAP,
CURLPROTO_LDAP,
CURLPROTO_LDAP,
PROTOPT_SSL_REUSE
};
#ifdef USE_SSL
const struct Curl_handler Curl_handler_ldaps = {
"ldaps",
oldap_setup_connection,
oldap_do,
oldap_done,
ZERO_NULL,
oldap_connect,
oldap_connecting,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
oldap_disconnect,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
ZERO_NULL,
PORT_LDAPS,
CURLPROTO_LDAPS,
CURLPROTO_LDAP,
PROTOPT_SSL
};
#endif
static const struct SASLproto saslldap = {
"ldap",
oldap_perform_auth,
oldap_continue_auth,
oldap_cancel_auth,
oldap_get_message,
0,
LDAP_SASL_BIND_IN_PROGRESS,
LDAP_SUCCESS,
SASL_AUTH_NONE,
0
};
struct ldapconninfo {
struct SASL sasl;
LDAP *ld;
Curl_recv *recv;
Curl_send *send;
struct berval *servercred;
ldapstate state;
int proto;
int msgid;
};
struct ldapreqinfo {
int msgid;
int nument;
};
#define CURL_META_LDAP_EASY "meta:proto:ldap:easy"
#define CURL_META_LDAP_CONN "meta:proto:ldap:conn"
static void oldap_state(struct Curl_easy *data, struct ldapconninfo *li,
ldapstate newstate)
{
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
static const char * const names[] = {
"STOP",
"SSL",
"STARTTLS",
"TLS",
"MECHS",
"SASL",
"BIND",
"BINDV2",
};
if(li->state != newstate)
infof(data, "LDAP %p state change from %s to %s",
(void *)li, names[li->state], names[newstate]);
#endif
(void)data;
li->state = newstate;
}
static CURLcode oldap_map_error(int rc, CURLcode result)
{
switch(rc) {
case LDAP_NO_MEMORY:
return CURLE_OUT_OF_MEMORY;
case LDAP_INVALID_CREDENTIALS:
return CURLE_LOGIN_DENIED;
case LDAP_PROTOCOL_ERROR:
return CURLE_UNSUPPORTED_PROTOCOL;
case LDAP_INSUFFICIENT_ACCESS:
return CURLE_REMOTE_ACCESS_DENIED;
}
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 < CURL_ARRAYSIZE(url_errs))
msg = url_errs[rc];
failf(data, "LDAP local: %s", msg);
}
return result;
}
static CURLcode oldap_parse_login_options(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
const char *ptr = conn->options;
DEBUGASSERT(li);
if(!li)
return CURLE_FAILED_INIT;
while(!result && ptr && *ptr) {
const char *key = ptr;
const char *value;
while(*ptr && *ptr != '=')
ptr++;
value = ptr + 1;
while(*ptr && *ptr != ';')
ptr++;
if(checkprefix("AUTH=", key))
result = Curl_sasl_parse_url_auth_option(&li->sasl, value, ptr - value);
else
result = CURLE_SETOPT_OPTION_SYNTAX;
if(*ptr == ';')
ptr++;
}
return result == CURLE_URL_MALFORMAT ? CURLE_SETOPT_OPTION_SYNTAX : result;
}
static CURLcode oldap_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
CURLcode result;
LDAPURLDesc *lud;
(void)conn;
result = oldap_url_parse(data, &lud);
ldap_free_urldesc(lud);
return result;
}
static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out)
{
struct ldapconninfo *li =
Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
struct berval *servercred = li ? li->servercred : NULL;
DEBUGASSERT(li);
if(!li)
return CURLE_FAILED_INIT;
if(!servercred || !servercred->bv_val)
return CURLE_WEIRD_SERVER_REPLY;
Curl_bufref_set(out, servercred->bv_val, servercred->bv_len, NULL);
return CURLE_OK;
}
static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
const struct bufref *initresp)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
struct berval cred;
struct berval *pcred = &cred;
int rc;
DEBUGASSERT(li);
if(!li)
return CURLE_FAILED_INIT;
cred.bv_val = (char *)CURL_UNCONST(Curl_bufref_ptr(initresp));
cred.bv_len = Curl_bufref_len(initresp);
if(!cred.bv_val)
pcred = NULL;
rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
if(rc != LDAP_SUCCESS)
return oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
return CURLE_OK;
}
static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
const struct bufref *resp)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
struct berval cred;
struct berval *pcred = &cred;
int rc;
if(!li)
return CURLE_FAILED_INIT;
cred.bv_val = (char *)CURL_UNCONST(Curl_bufref_ptr(resp));
cred.bv_len = Curl_bufref_len(resp);
if(!cred.bv_val)
pcred = NULL;
rc = ldap_sasl_bind(li->ld, NULL, mech, pcred, NULL, NULL, &li->msgid);
if(rc != LDAP_SUCCESS)
return oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
return CURLE_OK;
}
static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech)
{
struct ldapconninfo *li =
Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
int rc;
(void)mech;
if(!li)
return CURLE_FAILED_INIT;
rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL,
&li->msgid);
if(rc != LDAP_SUCCESS)
return oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
return CURLE_OK;
}
static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
char *binddn = NULL;
struct berval passwd;
int rc;
if(!li)
return CURLE_FAILED_INIT;
passwd.bv_val = NULL;
passwd.bv_len = 0;
if(data->state.aptr.user) {
binddn = conn->user;
passwd.bv_val = conn->passwd;
passwd.bv_len = strlen(passwd.bv_val);
}
rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
NULL, NULL, &li->msgid);
if(rc != LDAP_SUCCESS)
return oldap_map_error(rc,
data->state.aptr.user ?
CURLE_LOGIN_DENIED : CURLE_LDAP_CANNOT_BIND);
oldap_state(data, li, newstate);
return CURLE_OK;
}
static CURLcode oldap_perform_mechs(struct Curl_easy *data)
{
struct ldapconninfo *li =
Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
int rc;
static const char * const supportedSASLMechanisms[] = {
"supportedSASLMechanisms",
NULL
};
if(!li)
return CURLE_FAILED_INIT;
rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
(char **)CURL_UNCONST(supportedSASLMechanisms), 0,
NULL, NULL, NULL, 0, &li->msgid);
if(rc != LDAP_SUCCESS)
return oldap_map_error(rc, CURLE_LOGIN_DENIED);
oldap_state(data, li, OLDAP_MECHS);
return CURLE_OK;
}
static CURLcode oldap_perform_sasl(struct Curl_easy *data)
{
struct ldapconninfo *li =
Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
saslprogress progress = SASL_IDLE;
CURLcode result;
if(!li)
return CURLE_FAILED_INIT;
result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
oldap_state(data, li, OLDAP_SASL);
if(!result && progress != SASL_INPROGRESS)
result = Curl_sasl_is_blocked(&li->sasl, data);
return result;
}
#ifdef USE_SSL
static int ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg);
static int ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod);
static int ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg);
static ber_slen_t ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf,
ber_len_t len);
static ber_slen_t ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf,
ber_len_t len);
static int ldapsb_tls_close(Sockbuf_IO_Desc *sbiod);
static Sockbuf_IO ldapsb_tls =
{
ldapsb_tls_setup,
ldapsb_tls_remove,
ldapsb_tls_ctrl,
ldapsb_tls_read,
ldapsb_tls_write,
ldapsb_tls_close
};
static bool ssl_installed(struct connectdata *conn)
{
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
return li && li->recv != NULL;
}
static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
bool ssldone = FALSE;
CURLcode result;
if(!li)
return CURLE_FAILED_INIT;
result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
if(result)
return result;
oldap_state(data, li, newstate);
if(ssldone) {
Sockbuf *sb;
if((ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb) != LDAP_OPT_SUCCESS) ||
ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data))
return CURLE_FAILED_INIT;
li->recv = conn->recv[FIRSTSOCKET];
li->send = conn->send[FIRSTSOCKET];
}
return result;
}
static CURLcode oldap_perform_starttls(struct Curl_easy *data)
{
struct ldapconninfo *li =
Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
int rc;
if(!li)
return CURLE_FAILED_INIT;
rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
if(rc != LDAP_SUCCESS)
return oldap_map_error(rc, CURLE_USE_SSL_FAILED);
oldap_state(data, li, OLDAP_STARTTLS);
return CURLE_OK;
}
#endif
static void oldap_easy_dtor(void *key, size_t klen, void *entry)
{
struct ldapreqinfo *lr = entry;
(void)key;
(void)klen;
free(lr);
}
static void oldap_conn_dtor(void *key, size_t klen, void *entry)
{
struct ldapconninfo *li = entry;
(void)key;
(void)klen;
if(li->ld) {
ldap_unbind_ext(li->ld, NULL, NULL);
li->ld = NULL;
}
free(li);
}
static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li;
static const int version = LDAP_VERSION3;
char *hosturl = NULL;
CURLcode result;
int rc;
#ifdef CURL_OPENLDAP_DEBUG
static int do_trace = -1;
#endif
(void)done;
li = calloc(1, sizeof(struct ldapconninfo));
if(!li) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
result = Curl_conn_meta_set(conn, CURL_META_LDAP_CONN, li, oldap_conn_dtor);
if(result)
goto out;
li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
Curl_sasl_init(&li->sasl, data, &saslldap);
result = oldap_parse_login_options(conn);
if(result)
goto out;
hosturl = curl_maprintf("%s://%s%s%s:%d",
conn->handler->scheme,
conn->bits.ipv6_ip ? "[" : "",
conn->host.name,
conn->bits.ipv6_ip ? "]" : "",
conn->remote_port);
if(!hosturl) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
if(rc) {
failf(data, "LDAP local: Cannot connect to %s, %s",
hosturl, ldap_err2string(rc));
result = CURLE_COULDNT_CONNECT;
goto out;
}
#ifdef CURL_OPENLDAP_DEBUG
if(do_trace < 0) {
const char *env = getenv("CURL_OPENLDAP_TRACE");
curl_off_t e = 0;
if(!curlx_str_number(&env, &e, INT_MAX))
do_trace = e > 0;
}
if(do_trace)
ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
#endif
ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
{
ber_len_t max = 256*1024;
Sockbuf *sb;
if((ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb) != LDAP_OPT_SUCCESS) ||
!ber_sockbuf_ctrl(sb, LBER_SB_OPT_SET_MAX_INCOMING, &max)) {
result = CURLE_FAILED_INIT;
goto out;
}
}
#ifdef USE_SSL
if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = oldap_ssl_connect(data, OLDAP_SSL);
goto out;
}
if(data->set.use_ssl) {
result = oldap_perform_starttls(data);
if(!result || data->set.use_ssl != CURLUSESSL_TRY)
goto out;
}
#endif
if(li->sasl.prefmech != SASL_AUTH_NONE) {
result = oldap_perform_mechs(data);
goto out;
}
result = oldap_perform_bind(data, OLDAP_BIND);
out:
free(hosturl);
return result;
}
static CURLcode oldap_state_mechs_resp(struct Curl_easy *data,
LDAPMessage *msg, int code)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
int rc;
BerElement *ber = NULL;
CURLcode result = CURLE_OK;
struct berval bv, *bvals;
if(!li)
return CURLE_FAILED_INIT;
switch(ldap_msgtype(msg)) {
case LDAP_RES_SEARCH_ENTRY:
if(code != LDAP_SUCCESS && code != LDAP_NO_RESULTS_RETURNED)
return CURLE_LOGIN_DENIED;
rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
if(rc < 0)
return oldap_map_error(rc, CURLE_BAD_CONTENT_ENCODING);
for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
rc == LDAP_SUCCESS;
rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
int i;
if(!bv.bv_val)
break;
if(bvals) {
for(i = 0; bvals[i].bv_val; i++) {
size_t llen;
unsigned short mech = Curl_sasl_decode_mech((char *) bvals[i].bv_val,
bvals[i].bv_len, &llen);
if(bvals[i].bv_len == llen)
li->sasl.authmechs |= mech;
}
ber_memfree(bvals);
}
}
ber_free(ber, 0);
break;
case LDAP_RES_SEARCH_RESULT:
switch(code) {
case LDAP_SIZELIMIT_EXCEEDED:
infof(data, "Too many authentication mechanisms");
FALLTHROUGH();
case LDAP_SUCCESS:
case LDAP_NO_RESULTS_RETURNED:
if(Curl_sasl_can_authenticate(&li->sasl, data))
result = oldap_perform_sasl(data);
else
result = CURLE_LOGIN_DENIED;
break;
default:
result = oldap_map_error(code, CURLE_LOGIN_DENIED);
break;
}
break;
default:
break;
}
return result;
}
static CURLcode oldap_state_sasl_resp(struct Curl_easy *data,
LDAPMessage *msg, int code)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
CURLcode result = CURLE_OK;
saslprogress progress;
int rc;
if(!li)
return CURLE_FAILED_INIT;
li->servercred = NULL;
rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0);
if(rc != LDAP_SUCCESS) {
failf(data, "LDAP local: sasl ldap_parse_result %s", ldap_err2string(rc));
result = oldap_map_error(rc, CURLE_LOGIN_DENIED);
}
else {
result = Curl_sasl_continue(&li->sasl, data, code, &progress);
if(!result && progress != SASL_INPROGRESS)
oldap_state(data, li, OLDAP_STOP);
}
if(li->servercred)
ber_bvfree(li->servercred);
return result;
}
static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg,
int code)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
CURLcode result = CURLE_OK;
struct berval *bv = NULL;
int rc;
if(!li)
return CURLE_FAILED_INIT;
if(code != LDAP_SUCCESS)
return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0);
if(rc != LDAP_SUCCESS) {
failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s",
ldap_err2string(rc));
result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
}
else
oldap_state(data, li, OLDAP_STOP);
if(bv)
ber_bvfree(bv);
return result;
}
static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
LDAPMessage *msg = NULL;
struct timeval tv = {0, 0};
int code = LDAP_SUCCESS;
int rc;
if(!li)
return CURLE_FAILED_INIT;
if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) {
rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
switch(rc) {
case 0:
return CURLE_OK;
case LDAP_RES_SEARCH_ENTRY:
case LDAP_RES_SEARCH_REFERENCE:
break;
default:
li->msgid = 0;
if(rc < 0) {
failf(data, "LDAP local: connecting ldap_result %s",
ldap_err2string(rc));
return oldap_map_error(rc, CURLE_COULDNT_CONNECT);
}
break;
}
rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
if(rc)
code = rc;
else {
data->info.httpcode = code;
}
if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2 &&
#ifdef USE_SSL
(ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY) &&
#endif
li->sasl.prefmech == SASL_AUTH_NONE) {
static const int version = LDAP_VERSION2;
ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
ldap_msgfree(msg);
return oldap_perform_bind(data, OLDAP_BINDV2);
}
}
switch(li->state) {
#ifdef USE_SSL
case OLDAP_SSL:
result = oldap_ssl_connect(data, OLDAP_SSL);
if(!result && ssl_installed(conn)) {
if(li->sasl.prefmech != SASL_AUTH_NONE)
result = oldap_perform_mechs(data);
else
result = oldap_perform_bind(data, OLDAP_BIND);
}
break;
case OLDAP_STARTTLS:
if(code != LDAP_SUCCESS) {
if(data->set.use_ssl != CURLUSESSL_TRY)
result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
else if(li->sasl.prefmech != SASL_AUTH_NONE)
result = oldap_perform_mechs(data);
else
result = oldap_perform_bind(data, OLDAP_BIND);
break;
}
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
break;
FALLTHROUGH();
case OLDAP_TLS:
result = oldap_ssl_connect(data, OLDAP_TLS);
if(result)
result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
else if(ssl_installed(conn)) {
if(li->sasl.prefmech != SASL_AUTH_NONE)
result = oldap_perform_mechs(data);
else if(data->state.aptr.user)
result = oldap_perform_bind(data, OLDAP_BIND);
else {
oldap_state(data, li, OLDAP_STOP);
result = CURLE_OK;
}
}
break;
#endif
case OLDAP_MECHS:
result = oldap_state_mechs_resp(data, msg, code);
break;
case OLDAP_SASL:
result = oldap_state_sasl_resp(data, msg, code);
break;
case OLDAP_BIND:
case OLDAP_BINDV2:
result = oldap_state_bind_resp(data, msg, code);
break;
default:
result = CURLE_COULDNT_CONNECT;
break;
}
ldap_msgfree(msg);
*done = li->state == OLDAP_STOP;
if(*done)
conn->recv[FIRSTSOCKET] = oldap_recv;
if(result && li->msgid) {
ldap_abandon_ext(li->ld, li->msgid, NULL, NULL);
li->msgid = 0;
}
return result;
}
static CURLcode oldap_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead_connection)
{
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
(void)dead_connection;
#ifndef USE_SSL
(void)data;
#endif
if(li && li->ld) {
#ifdef USE_SSL
if(ssl_installed(conn)) {
Sockbuf *sb;
if((ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb) != LDAP_OPT_SUCCESS)
||
ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data))
return CURLE_FAILED_INIT;
}
#endif
ldap_unbind_ext(li->ld, NULL, NULL);
li->ld = NULL;
}
return CURLE_OK;
}
static CURLcode oldap_do(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
struct ldapreqinfo *lr;
CURLcode result;
int rc;
LDAPURLDesc *lud;
int msgid;
if(!li)
return CURLE_FAILED_INIT;
connkeep(conn, "OpenLDAP do");
infof(data, "LDAP local: %s", data->state.url);
result = oldap_url_parse(data, &lud);
if(result)
goto out;
#ifdef USE_SSL
if(ssl_installed(conn)) {
Sockbuf *sb;
if((ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb) != LDAP_OPT_SUCCESS) ||
ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data)) {
ldap_free_urldesc(lud);
return CURLE_FAILED_INIT;
}
}
#endif
rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
lud->lud_filter, lud->lud_attrs, 0,
NULL, NULL, NULL, 0, &msgid);
ldap_free_urldesc(lud);
if(rc != LDAP_SUCCESS) {
failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
result = CURLE_LDAP_SEARCH_FAILED;
goto out;
}
lr = calloc(1, sizeof(struct ldapreqinfo));
if(!lr ||
Curl_meta_set(data, CURL_META_LDAP_EASY, lr, oldap_easy_dtor)) {
ldap_abandon_ext(li->ld, msgid, NULL, NULL);
result = CURLE_OUT_OF_MEMORY;
goto out;
}
lr->msgid = msgid;
Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
*done = TRUE;
out:
return result;
}
static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
bool premature)
{
struct connectdata *conn = data->conn;
struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
(void)res;
(void)premature;
if(lr) {
if(lr->msgid) {
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
if(li && li->ld) {
ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
}
lr->msgid = 0;
}
Curl_meta_remove(data, CURL_META_LDAP_EASY);
}
return CURLE_OK;
}
static CURLcode client_write(struct Curl_easy *data,
const char *prefix, size_t plen,
const char *value, size_t len,
const char *suffix, size_t slen)
{
CURLcode result = CURLE_OK;
if(prefix) {
if(!len && plen && prefix[plen - 1] == ' ')
plen--;
result = Curl_client_write(data, CLIENTWRITE_BODY, prefix, plen);
}
if(!result && value) {
result = Curl_client_write(data, CLIENTWRITE_BODY, value, len);
}
if(!result && suffix) {
result = Curl_client_write(data, CLIENTWRITE_BODY, suffix, slen);
}
return result;
}
static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
size_t len, size_t *pnread)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
int rc;
LDAPMessage *msg = NULL;
BerElement *ber = NULL;
struct timeval tv = {0, 0};
struct berval bv, *bvals;
CURLcode result = CURLE_AGAIN;
int code;
char *info = NULL;
(void)len;
(void)buf;
(void)sockindex;
*pnread = 0;
if(!li || !lr)
return CURLE_FAILED_INIT;
rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
if(rc < 0) {
failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
result = CURLE_RECV_ERROR;
}
if(!msg)
return result;
result = CURLE_OK;
switch(ldap_msgtype(msg)) {
case LDAP_RES_SEARCH_RESULT:
lr->msgid = 0;
rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
if(rc) {
failf(data, "LDAP local: search ldap_parse_result %s",
ldap_err2string(rc));
result = CURLE_LDAP_SEARCH_FAILED;
break;
}
data->info.httpcode = code;
switch(code) {
case LDAP_SIZELIMIT_EXCEEDED:
infof(data, "There are more than %d entries", lr->nument);
FALLTHROUGH();
case LDAP_SUCCESS:
data->req.size = data->req.bytecount;
break;
default:
failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
info ? info : "");
result = CURLE_LDAP_SEARCH_FAILED;
break;
}
if(info)
ldap_memfree(info);
break;
case LDAP_RES_SEARCH_ENTRY:
lr->nument++;
rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
if(rc < 0) {
result = CURLE_RECV_ERROR;
break;
}
result = client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len,
STRCONST("\n"));
if(result)
break;
for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
rc == LDAP_SUCCESS;
rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
int i;
bool binary;
if(!bv.bv_val)
break;
if(!bvals) {
result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len,
STRCONST(":\n"));
if(result)
break;
continue;
}
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) {
if(bvals[i].bv_len &&
(ISBLANK(bvals[i].bv_val[0]) ||
ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1])))
binval = TRUE;
else {
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;
if(bvals[i].bv_len)
result = curlx_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
&val_b64, &val_b64_sz);
if(!result)
result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
STRCONST("\n"));
free(val_b64);
}
else
result = client_write(data, STRCONST(" "),
bvals[i].bv_val, bvals[i].bv_len,
STRCONST("\n"));
if(result)
break;
}
ber_memfree(bvals);
bvals = NULL;
if(!result)
result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
if(result)
break;
}
if(!result)
result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0);
if(!result)
result = CURLE_AGAIN;
break;
}
ber_free(ber, 0);
ldap_msgfree(msg);
return result;
}
#ifdef USE_SSL
static int
ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg)
{
sbiod->sbiod_pvt = arg;
return 0;
}
static int
ldapsb_tls_remove(Sockbuf_IO_Desc *sbiod)
{
sbiod->sbiod_pvt = NULL;
return 0;
}
static int
ldapsb_tls_close(Sockbuf_IO_Desc *sbiod)
{
(void)sbiod;
return 0;
}
static int
ldapsb_tls_ctrl(Sockbuf_IO_Desc *sbiod, int opt, void *arg)
{
(void)arg;
if(opt == LBER_SB_OPT_DATA_READY) {
struct Curl_easy *data = sbiod->sbiod_pvt;
return Curl_conn_data_pending(data, FIRSTSOCKET);
}
return 0;
}
static ber_slen_t
ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
{
struct Curl_easy *data = sbiod->sbiod_pvt;
ber_slen_t ret = 0;
if(data) {
struct connectdata *conn = data->conn;
if(conn) {
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
CURLcode err = CURLE_RECV_ERROR;
size_t nread;
if(!li) {
SET_SOCKERRNO(SOCKEINVAL);
return -1;
}
err = (li->recv)(data, FIRSTSOCKET, buf, len, &nread);
if(err == CURLE_AGAIN) {
SET_SOCKERRNO(SOCKEWOULDBLOCK);
}
ret = err ? -1 : (ber_slen_t)nread;
}
}
return ret;
}
static ber_slen_t
ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
{
struct Curl_easy *data = sbiod->sbiod_pvt;
ber_slen_t ret = 0;
if(data) {
struct connectdata *conn = data->conn;
if(conn) {
struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
CURLcode err = CURLE_SEND_ERROR;
size_t nwritten;
if(!li) {
SET_SOCKERRNO(SOCKEINVAL);
return -1;
}
err = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &nwritten);
if(err == CURLE_AGAIN) {
SET_SOCKERRNO(SOCKEWOULDBLOCK);
}
ret = err ? -1 : (ber_slen_t)nwritten;
}
}
return ret;
}
#endif
#endif