X-Git-Url: http://git.samba.org/samba.git/?p=bbaumbach%2Fsamba-autobuild%2F.git;a=blobdiff_plain;f=source4%2Flibcli%2Fldap%2Fldap_client.c;h=4fa8cc01464fe1121f276f5acfc944f4afcc2ab5;hp=a8463f78724202289c0c71580ee4190d2370b977;hb=22ced36791da7de99afb799deb30b934ccbd902e;hpb=bec00581247c9062ea0acce6037ef75ab188548c diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c index a8463f78724..4fa8cc01464 100644 --- a/source4/libcli/ldap/ldap_client.c +++ b/source4/libcli/ldap/ldap_client.c @@ -24,13 +24,17 @@ */ #include "includes.h" -#include "asn_1.h" -#include "dlinklist.h" +#include "libcli/util/asn_1.h" +#include "lib/util/dlinklist.h" #include "lib/events/events.h" #include "lib/socket/socket.h" -#include "lib/tls/tls.h" #include "libcli/ldap/ldap.h" #include "libcli/ldap/ldap_client.h" +#include "libcli/composite/composite.h" +#include "lib/stream/packet.h" +#include "lib/tls/tls.h" +#include "auth/gensec/gensec.h" +#include "system/time.h" /* @@ -60,10 +64,12 @@ struct ldap_connection *ldap_new_connection(TALLOC_CTX *mem_ctx, /* set a reasonable request timeout */ conn->timeout = 60; + /* explicitly avoid reconnections by default */ + conn->reconnect.max_retries = 0; + return conn; } - /* the connection is dead */ @@ -71,6 +77,7 @@ static void ldap_connection_dead(struct ldap_connection *conn) { struct ldap_request *req; + /* return an error for any pending request ... */ while (conn->pending) { req = conn->pending; DLIST_REMOVE(req->conn->pending, req); @@ -79,20 +86,28 @@ static void ldap_connection_dead(struct ldap_connection *conn) if (req->async.fn) { req->async.fn(req); } - } - - while (conn->send_queue) { - req = conn->send_queue; - DLIST_REMOVE(req->conn->send_queue, req); - req->state = LDAP_REQUEST_DONE; - req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; - if (req->async.fn) { - req->async.fn(req); - } } - talloc_free(conn->tls); - conn->tls = NULL; + talloc_free(conn->sock); /* this will also free event.fde */ + talloc_free(conn->packet); + conn->sock = NULL; + conn->event.fde = NULL; + conn->packet = NULL; +} + +static void ldap_reconnect(struct ldap_connection *conn); + +/* + handle packet errors +*/ +static void ldap_error_handler(void *private_data, NTSTATUS status) +{ + struct ldap_connection *conn = talloc_get_type(private_data, + struct ldap_connection); + ldap_connection_dead(conn); + + /* but try to reconnect so that the ldb client can go on */ + ldap_reconnect(conn); } @@ -106,6 +121,11 @@ static void ldap_match_message(struct ldap_connection *conn, struct ldap_message for (req=conn->pending; req; req=req->next) { if (req->messageid == msg->messageid) break; } + /* match a zero message id to the last request sent. + It seems that servers send 0 if unable to parse */ + if (req == NULL && msg->messageid == 0) { + req = conn->pending; + } if (req == NULL) { DEBUG(0,("ldap: no matching message id for %u\n", msg->messageid)); @@ -143,200 +163,58 @@ static void ldap_match_message(struct ldap_connection *conn, struct ldap_message } } -/* - try and decode/process plain data -*/ -static void ldap_try_decode_plain(struct ldap_connection *conn) -{ - struct asn1_data asn1; - - if (!asn1_load(&asn1, conn->partial)) { - ldap_connection_dead(conn); - return; - } - - /* try and decode - this will fail if we don't have a full packet yet */ - while (asn1.ofs < asn1.length) { - struct ldap_message *msg = talloc(conn, struct ldap_message); - off_t saved_ofs = asn1.ofs; - - if (msg == NULL) { - ldap_connection_dead(conn); - return; - } - - if (ldap_decode(&asn1, msg)) { - ldap_match_message(conn, msg); - } else { - asn1.ofs = saved_ofs; - talloc_free(msg); - break; - } - } - - /* keep any remaining data in conn->partial */ - data_blob_free(&conn->partial); - if (asn1.ofs != asn1.length) { - conn->partial = data_blob_talloc(conn, - asn1.data + asn1.ofs, - asn1.length - asn1.ofs); - } - asn1_free(&asn1); -} /* - try and decode/process wrapped data + decode/process LDAP data */ -static void ldap_try_decode_wrapped(struct ldap_connection *conn) +static NTSTATUS ldap_recv_handler(void *private_data, DATA_BLOB blob) { - uint32_t len; - - /* keep decoding while we have a full wrapped packet */ - while (conn->partial.length >= 4 && - (len=RIVAL(conn->partial.data, 0)) <= conn->partial.length-4) { - DATA_BLOB wrapped, unwrapped; - struct asn1_data asn1; - struct ldap_message *msg = talloc(conn, struct ldap_message); - NTSTATUS status; - - if (msg == NULL) { - ldap_connection_dead(conn); - return; - } - - wrapped.data = conn->partial.data+4; - wrapped.length = len; - - status = gensec_unwrap(conn->gensec, msg, &wrapped, &unwrapped); - if (!NT_STATUS_IS_OK(status)) { - ldap_connection_dead(conn); - return; - } - - if (!asn1_load(&asn1, unwrapped)) { - ldap_connection_dead(conn); - return; - } + struct asn1_data asn1; + struct ldap_connection *conn = talloc_get_type(private_data, + struct ldap_connection); + struct ldap_message *msg = talloc(conn, struct ldap_message); - while (ldap_decode(&asn1, msg)) { - ldap_match_message(conn, msg); - msg = talloc(conn, struct ldap_message); - } - - talloc_free(msg); - asn1_free(&asn1); - - if (conn->partial.length == len + 4) { - data_blob_free(&conn->partial); - } else { - memmove(conn->partial.data, conn->partial.data+len+4, - conn->partial.length - (len+4)); - conn->partial.length -= len + 4; - } + if (msg == NULL) { + return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR); } -} - -/* - handle ldap recv events -*/ -static void ldap_recv_handler(struct ldap_connection *conn) -{ - NTSTATUS status; - size_t npending=0, nread; - - /* work out how much data is pending */ - status = tls_socket_pending(conn->tls, &npending); - if (!NT_STATUS_IS_OK(status) || npending == 0) { - ldap_connection_dead(conn); - return; + if (!asn1_load(&asn1, blob)) { + return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR); } - - conn->partial.data = talloc_realloc_size(conn, conn->partial.data, - conn->partial.length + npending); - if (conn->partial.data == NULL) { - ldap_connection_dead(conn); - return; + + if (!ldap_decode(&asn1, msg)) { + return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR); } - /* receive the pending data */ - status = tls_socket_recv(conn->tls, conn->partial.data + conn->partial.length, - npending, &nread); - if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { - return; - } - if (!NT_STATUS_IS_OK(status)) { - ldap_connection_dead(conn); - return; - } - conn->partial.length += nread; + ldap_match_message(conn, msg); - /* see if we can decode what we have */ - if (conn->enable_wrap) { - ldap_try_decode_wrapped(conn); - } else { - ldap_try_decode_plain(conn); - } + data_blob_free(&blob); + asn1_free(&asn1); + return NT_STATUS_OK; } - -/* - handle ldap send events -*/ -static void ldap_send_handler(struct ldap_connection *conn) +/* Handle read events, from the GENSEC socket callback, or real events */ +void ldap_read_io_handler(void *private_data, uint16_t flags) { - while (conn->send_queue) { - struct ldap_request *req = conn->send_queue; - size_t nsent; - NTSTATUS status; - - status = tls_socket_send(conn->tls, &req->data, &nsent); - if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { - break; - } - if (!NT_STATUS_IS_OK(status)) { - ldap_connection_dead(conn); - return; - } - - req->data.data += nsent; - req->data.length -= nsent; - if (req->data.length == 0) { - req->state = LDAP_REQUEST_PENDING; - DLIST_REMOVE(conn->send_queue, req); - - /* some types of requests don't expect a reply */ - if (req->type == LDAP_TAG_AbandonRequest || - req->type == LDAP_TAG_UnbindRequest) { - req->status = NT_STATUS_OK; - req->state = LDAP_REQUEST_DONE; - if (req->async.fn) { - req->async.fn(req); - } - } else { - DLIST_ADD(conn->pending, req); - } - } - } - if (conn->send_queue == NULL) { - EVENT_FD_NOT_WRITEABLE(conn->event.fde); - } + struct ldap_connection *conn = talloc_get_type(private_data, + struct ldap_connection); + packet_recv(conn->packet); } - /* handle ldap socket events */ static void ldap_io_handler(struct event_context *ev, struct fd_event *fde, - uint16_t flags, void *private) + uint16_t flags, void *private_data) { - struct ldap_connection *conn = talloc_get_type(private, struct ldap_connection); + struct ldap_connection *conn = talloc_get_type(private_data, + struct ldap_connection); if (flags & EVENT_FD_WRITE) { - ldap_send_handler(conn); - if (conn->tls == NULL) return; + packet_queue_run(conn->packet); + if (!tls_enabled(conn->sock)) return; } if (flags & EVENT_FD_READ) { - ldap_recv_handler(conn); + ldap_read_io_handler(private_data, flags); } } @@ -348,19 +226,13 @@ static NTSTATUS ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url, { int tmp_port = 0; char protocol[11]; - char tmp_host[255]; - const char *p = url; + char tmp_host[1025]; int ret; - /* skip leading "URL:" (if any) */ - if (strncasecmp(p, "URL:", 4) == 0) { - p += 4; - } - /* Paranoia check */ SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254); - ret = sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port); + ret = sscanf(url, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port); if (ret < 2) { return NT_STATUS_INVALID_PARAMETER; } @@ -388,54 +260,237 @@ static NTSTATUS ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url, /* connect to a ldap server */ -NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url) + +struct ldap_connect_state { + struct composite_context *ctx; + struct ldap_connection *conn; +}; + +static void ldap_connect_recv_unix_conn(struct composite_context *ctx); +static void ldap_connect_recv_tcp_conn(struct composite_context *ctx); + +struct composite_context *ldap_connect_send(struct ldap_connection *conn, + const char *url) { - NTSTATUS status; + struct composite_context *result, *ctx; + struct ldap_connect_state *state; + char protocol[11]; + int ret; - status = ldap_parse_basic_url(conn, url, &conn->host, - &conn->port, &conn->ldaps); - NT_STATUS_NOT_OK_RETURN(status); + result = talloc_zero(NULL, struct composite_context); + if (result == NULL) goto failed; + result->state = COMPOSITE_STATE_IN_PROGRESS; + result->async.fn = NULL; + result->event_ctx = conn->event.event_ctx; - status = socket_create("ipv4", SOCKET_TYPE_STREAM, &conn->sock, 0); - NT_STATUS_NOT_OK_RETURN(status); + state = talloc(result, struct ldap_connect_state); + if (state == NULL) goto failed; + state->ctx = result; + result->private_data = state; - talloc_steal(conn, conn->sock); + state->conn = conn; - /* connect in a event friendly way */ - status = socket_connect_ev(conn->sock, NULL, 0, conn->host, conn->port, 0, - conn->event.event_ctx); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(conn->sock); - return status; + if (conn->reconnect.url == NULL) { + conn->reconnect.url = talloc_strdup(conn, url); + if (conn->reconnect.url == NULL) goto failed; + } + + /* Paranoia check */ + SMB_ASSERT(sizeof(protocol)>10); + + ret = sscanf(url, "%10[^:]://", protocol); + if (ret < 1) { + return NULL; } + if (strequal(protocol, "ldapi")) { + struct socket_address *unix_addr; + char path[1025]; + + NTSTATUS status = socket_create("unix", SOCKET_TYPE_STREAM, &conn->sock, 0); + if (!NT_STATUS_IS_OK(status)) { + return NULL; + } + SMB_ASSERT(sizeof(protocol)>10); + SMB_ASSERT(sizeof(path)>1024); + + ret = sscanf(url, "%10[^:]://%1025c", protocol, path); + if (ret < 2) { + composite_error(state->ctx, NT_STATUS_INVALID_PARAMETER); + return result; + } + + rfc1738_unescape(path); + + unix_addr = socket_address_from_strings(conn, conn->sock->backend_name, + path, 0); + if (!unix_addr) { + return NULL; + } + + ctx = socket_connect_send(conn->sock, NULL, unix_addr, + 0, conn->event.event_ctx); + ctx->async.fn = ldap_connect_recv_unix_conn; + ctx->async.private_data = state; + return result; + } else { + NTSTATUS status = ldap_parse_basic_url(conn, url, &conn->host, + &conn->port, &conn->ldaps); + if (!NT_STATUS_IS_OK(state->ctx->status)) { + composite_error(state->ctx, status); + return result; + } + + ctx = socket_connect_multi_send(state, conn->host, 1, &conn->port, + conn->event.event_ctx); + if (ctx == NULL) goto failed; + + ctx->async.fn = ldap_connect_recv_tcp_conn; + ctx->async.private_data = state; + return result; + } + failed: + talloc_free(result); + return NULL; +} + +static void ldap_connect_got_sock(struct composite_context *ctx, struct ldap_connection *conn) +{ /* setup a handler for events on this socket */ conn->event.fde = event_add_fd(conn->event.event_ctx, conn->sock, socket_get_fd(conn->sock), EVENT_FD_READ, ldap_io_handler, conn); if (conn->event.fde == NULL) { - talloc_free(conn->sock); - return NT_STATUS_INTERNAL_ERROR; + composite_error(ctx, NT_STATUS_INTERNAL_ERROR); + return; } - conn->tls = tls_init_client(conn->sock, conn->event.fde, conn->ldaps); - if (conn->tls == NULL) { + talloc_steal(conn, conn->sock); + if (conn->ldaps) { + struct socket_context *tls_socket = tls_init_client(conn->sock, conn->event.fde); + if (tls_socket == NULL) { + talloc_free(conn->sock); + return; + } + talloc_unlink(conn, conn->sock); + conn->sock = tls_socket; + talloc_steal(conn, conn->sock); + } + + conn->packet = packet_init(conn); + if (conn->packet == NULL) { talloc_free(conn->sock); - return NT_STATUS_INTERNAL_ERROR; + return; } - talloc_steal(conn, conn->tls); - talloc_steal(conn->tls, conn->sock); - return NT_STATUS_OK; + packet_set_private(conn->packet, conn); + packet_set_socket(conn->packet, conn->sock); + packet_set_callback(conn->packet, ldap_recv_handler); + packet_set_full_request(conn->packet, ldap_full_packet); + packet_set_error_handler(conn->packet, ldap_error_handler); + packet_set_event_context(conn->packet, conn->event.event_ctx); + packet_set_fde(conn->packet, conn->event.fde); + packet_set_serialise(conn->packet); + + composite_done(ctx); } -/* destroy an open ldap request */ -static int ldap_request_destructor(void *ptr) +static void ldap_connect_recv_tcp_conn(struct composite_context *ctx) +{ + struct ldap_connect_state *state = + talloc_get_type(ctx->async.private_data, + struct ldap_connect_state); + struct ldap_connection *conn = state->conn; + uint16_t port; + + NTSTATUS status = socket_connect_multi_recv(ctx, state, &conn->sock, + &port); + if (!NT_STATUS_IS_OK(state->ctx->status)) { + composite_error(state->ctx, status); + return; + } + + ldap_connect_got_sock(state->ctx, conn); +} + +static void ldap_connect_recv_unix_conn(struct composite_context *ctx) +{ + struct ldap_connect_state *state = + talloc_get_type(ctx->async.private_data, + struct ldap_connect_state); + struct ldap_connection *conn = state->conn; + + NTSTATUS status = socket_connect_recv(ctx); + + if (!NT_STATUS_IS_OK(state->ctx->status)) { + composite_error(state->ctx, status); + return; + } + + ldap_connect_got_sock(state->ctx, conn); +} + +NTSTATUS ldap_connect_recv(struct composite_context *ctx) +{ + NTSTATUS status = composite_wait(ctx); + talloc_free(ctx); + return status; +} + +NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url) +{ + struct composite_context *ctx = ldap_connect_send(conn, url); + return ldap_connect_recv(ctx); +} + +/* set reconnect parameters */ + +void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries) +{ + if (conn) { + conn->reconnect.max_retries = max_retries; + conn->reconnect.retries = 0; + conn->reconnect.previous = time(NULL); + } +} + +/* Actually this function is NOT ASYNC safe, FIXME? */ +static void ldap_reconnect(struct ldap_connection *conn) { - struct ldap_request *req = talloc_get_type(ptr, struct ldap_request); - if (req->state == LDAP_REQUEST_SEND) { - DLIST_REMOVE(req->conn->send_queue, req); + NTSTATUS status; + time_t now = time(NULL); + + /* do we have set up reconnect ? */ + if (conn->reconnect.max_retries == 0) return; + + /* is the retry time expired ? */ + if (now > conn->reconnect.previous + 30) { + conn->reconnect.retries = 0; + conn->reconnect.previous = now; + } + + /* are we reconnectind too often and too fast? */ + if (conn->reconnect.retries > conn->reconnect.max_retries) return; + + /* keep track of the number of reconnections */ + conn->reconnect.retries++; + + /* reconnect */ + status = ldap_connect(conn, conn->reconnect.url); + if ( ! NT_STATUS_IS_OK(status)) { + return; } + + /* rebind */ + status = ldap_rebind(conn); + if ( ! NT_STATUS_IS_OK(status)) { + ldap_connection_dead(conn); + } +} + +/* destroy an open ldap request */ +static int ldap_request_destructor(struct ldap_request *req) +{ if (req->state == LDAP_REQUEST_PENDING) { DLIST_REMOVE(req->conn->pending, req); } @@ -446,13 +501,10 @@ static int ldap_request_destructor(void *ptr) called on timeout of a ldap request */ static void ldap_request_timeout(struct event_context *ev, struct timed_event *te, - struct timeval t, void *private) + struct timeval t, void *private_data) { - struct ldap_request *req = talloc_get_type(private, struct ldap_request); + struct ldap_request *req = talloc_get_type(private_data, struct ldap_request); req->status = NT_STATUS_IO_TIMEOUT; - if (req->state == LDAP_REQUEST_SEND) { - DLIST_REMOVE(req->conn->send_queue, req); - } if (req->state == LDAP_REQUEST_PENDING) { DLIST_REMOVE(req->conn->pending, req); } @@ -462,6 +514,19 @@ static void ldap_request_timeout(struct event_context *ev, struct timed_event *t } } + +/* + called on completion of a one-way ldap request +*/ +static void ldap_request_complete(struct event_context *ev, struct timed_event *te, + struct timeval t, void *private_data) +{ + struct ldap_request *req = talloc_get_type(private_data, struct ldap_request); + if (req->async.fn) { + req->async.fn(req); + } +} + /* send a ldap message - async interface */ @@ -469,17 +534,22 @@ struct ldap_request *ldap_request_send(struct ldap_connection *conn, struct ldap_message *msg) { struct ldap_request *req; - - if (conn->tls == NULL) { - return NULL; - } + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; req = talloc_zero(conn, struct ldap_request); - if (req == NULL) goto failed; + if (req == NULL) return NULL; + + if (conn->sock == NULL) { + status = NT_STATUS_INVALID_CONNECTION; + goto failed; + } req->state = LDAP_REQUEST_SEND; req->conn = conn; req->messageid = conn->next_messageid++; + if (conn->next_messageid == 0) { + conn->next_messageid = 1; + } req->type = msg->type; if (req->messageid == -1) { goto failed; @@ -489,45 +559,45 @@ struct ldap_request *ldap_request_send(struct ldap_connection *conn, msg->messageid = req->messageid; - if (!ldap_encode(msg, &req->data)) { + if (!ldap_encode(msg, &req->data, req)) { goto failed; } - /* possibly encrypt/sign the request */ - if (conn->enable_wrap) { - DATA_BLOB wrapped; - NTSTATUS status; - - status = gensec_wrap(conn->gensec, req, &req->data, &wrapped); - if (!NT_STATUS_IS_OK(status)) { - goto failed; - } - data_blob_free(&req->data); - req->data = data_blob_talloc(req, NULL, wrapped.length + 4); - if (req->data.data == NULL) { - goto failed; - } - RSIVAL(req->data.data, 0, wrapped.length); - memcpy(req->data.data+4, wrapped.data, wrapped.length); - data_blob_free(&wrapped); + status = packet_send(conn->packet, req->data); + if (!NT_STATUS_IS_OK(status)) { + goto failed; } - - if (conn->send_queue == NULL) { - EVENT_FD_WRITEABLE(conn->event.fde); + /* some requests don't expect a reply, so don't add those to the + pending queue */ + if (req->type == LDAP_TAG_AbandonRequest || + req->type == LDAP_TAG_UnbindRequest) { + req->status = NT_STATUS_OK; + req->state = LDAP_REQUEST_DONE; + /* we can't call the async callback now, as it isn't setup, so + call it as next event */ + event_add_timed(conn->event.event_ctx, req, timeval_zero(), + ldap_request_complete, req); + return req; } - DLIST_ADD_END(conn->send_queue, req, struct ldap_request *); + + req->state = LDAP_REQUEST_PENDING; + DLIST_ADD(conn->pending, req); /* put a timeout on the request */ - event_add_timed(conn->event.event_ctx, req, - timeval_current_ofs(conn->timeout, 0), - ldap_request_timeout, req); + req->time_event = event_add_timed(conn->event.event_ctx, req, + timeval_current_ofs(conn->timeout, 0), + ldap_request_timeout, req); return req; failed: - talloc_free(req); - return NULL; + req->status = status; + req->state = LDAP_REQUEST_ERROR; + event_add_timed(conn->event.event_ctx, req, timeval_zero(), + ldap_request_complete, req); + + return req; } @@ -537,7 +607,7 @@ failed: */ NTSTATUS ldap_request_wait(struct ldap_request *req) { - while (req->state != LDAP_REQUEST_DONE) { + while (req->state < LDAP_REQUEST_DONE) { if (event_loop_once(req->conn->event.event_ctx) != 0) { req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; break; @@ -547,11 +617,63 @@ NTSTATUS ldap_request_wait(struct ldap_request *req) } +/* + a mapping of ldap response code to strings +*/ +static const struct { + enum ldap_result_code code; + const char *str; +} ldap_code_map[] = { +#define _LDAP_MAP_CODE(c) { c, #c } + _LDAP_MAP_CODE(LDAP_SUCCESS), + _LDAP_MAP_CODE(LDAP_OPERATIONS_ERROR), + _LDAP_MAP_CODE(LDAP_PROTOCOL_ERROR), + _LDAP_MAP_CODE(LDAP_TIME_LIMIT_EXCEEDED), + _LDAP_MAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED), + _LDAP_MAP_CODE(LDAP_COMPARE_FALSE), + _LDAP_MAP_CODE(LDAP_COMPARE_TRUE), + _LDAP_MAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED), + _LDAP_MAP_CODE(LDAP_STRONG_AUTH_REQUIRED), + _LDAP_MAP_CODE(LDAP_REFERRAL), + _LDAP_MAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED), + _LDAP_MAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION), + _LDAP_MAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED), + _LDAP_MAP_CODE(LDAP_SASL_BIND_IN_PROGRESS), + _LDAP_MAP_CODE(LDAP_NO_SUCH_ATTRIBUTE), + _LDAP_MAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE), + _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_MATCHING), + _LDAP_MAP_CODE(LDAP_CONSTRAINT_VIOLATION), + _LDAP_MAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS), + _LDAP_MAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX), + _LDAP_MAP_CODE(LDAP_NO_SUCH_OBJECT), + _LDAP_MAP_CODE(LDAP_ALIAS_PROBLEM), + _LDAP_MAP_CODE(LDAP_INVALID_DN_SYNTAX), + _LDAP_MAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM), + _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION), + _LDAP_MAP_CODE(LDAP_INVALID_CREDENTIALS), + _LDAP_MAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTs), + _LDAP_MAP_CODE(LDAP_BUSY), + _LDAP_MAP_CODE(LDAP_UNAVAILABLE), + _LDAP_MAP_CODE(LDAP_UNWILLING_TO_PERFORM), + _LDAP_MAP_CODE(LDAP_LOOP_DETECT), + _LDAP_MAP_CODE(LDAP_NAMING_VIOLATION), + _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_VIOLATION), + _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF), + _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_RDN), + _LDAP_MAP_CODE(LDAP_ENTRY_ALREADY_EXISTS), + _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED), + _LDAP_MAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS), + _LDAP_MAP_CODE(LDAP_OTHER) +}; + /* used to setup the status code from a ldap response */ NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r) { + int i; + const char *codename = "unknown"; + if (r->resultcode == LDAP_SUCCESS) { return NT_STATUS_OK; } @@ -559,8 +681,17 @@ NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r if (conn->last_error) { talloc_free(conn->last_error); } - conn->last_error = talloc_asprintf(conn, "LDAP error %u - %s <%s> <%s>", + + for (i=0;iresultcode == ldap_code_map[i].code) { + codename = ldap_code_map[i].str; + break; + } + } + + conn->last_error = talloc_asprintf(conn, "LDAP error %u %s - %s <%s> <%s>", r->resultcode, + codename, r->dn?r->dn:"(NULL)", r->errormessage?r->errormessage:"", r->referral?r->referral:""); @@ -589,7 +720,7 @@ NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **ms NT_STATUS_HAVE_NO_MEMORY(req); - while (req->state != LDAP_REQUEST_DONE && n >= req->num_replies) { + while (req->state < LDAP_REQUEST_DONE && n >= req->num_replies) { if (event_loop_once(req->conn->event.event_ctx) != 0) { return NT_STATUS_UNEXPECTED_NETWORK_ERROR; }