diff options
Diffstat (limited to 'source4/libcli/ldap/ldap_client.c')
-rw-r--r-- | source4/libcli/ldap/ldap_client.c | 1275 |
1 files changed, 419 insertions, 856 deletions
diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c index 6ff8db85a5..f3a7f104d4 100644 --- a/source4/libcli/ldap/ldap_client.c +++ b/source4/libcli/ldap/ldap_client.c @@ -24,1024 +24,587 @@ */ #include "includes.h" -#include "system/network.h" -#include "system/filesys.h" -#include "auth/auth.h" #include "asn_1.h" #include "dlinklist.h" +#include "lib/events/events.h" +#include "lib/socket/socket.h" #include "libcli/ldap/ldap.h" +#include "libcli/ldap/ldap_client.h" - -/**************************************************************************** - Check the timeout. -****************************************************************************/ -static BOOL timeout_until(struct timeval *timeout, - const struct timeval *endtime) -{ - struct timeval now; - - GetTimeOfDay(&now); - - if ((now.tv_sec > endtime->tv_sec) || - ((now.tv_sec == endtime->tv_sec) && - (now.tv_usec > endtime->tv_usec))) - return False; - - timeout->tv_sec = endtime->tv_sec - now.tv_sec; - timeout->tv_usec = endtime->tv_usec - now.tv_usec; - return True; -} - - -/**************************************************************************** - Read data from the client, reading exactly N bytes, with timeout. -****************************************************************************/ -static ssize_t read_data_until(int fd,char *buffer,size_t N, - const struct timeval *endtime) +/* + create a new ldap_connection stucture. The event context is optional +*/ +struct ldap_connection *ldap_new_connection(TALLOC_CTX *mem_ctx, + struct event_context *ev) { - ssize_t ret; - size_t total=0; - - while (total < N) { - - if (endtime != NULL) { - fd_set r_fds; - struct timeval timeout; - int res; - - FD_ZERO(&r_fds); - FD_SET(fd, &r_fds); - - if (!timeout_until(&timeout, endtime)) - return -1; - - res = sys_select(fd+1, &r_fds, NULL, NULL, &timeout); - if (res <= 0) - return -1; - } - - ret = sys_read(fd,buffer + total,N - total); - - if (ret == 0) { - DEBUG(10,("read_data: read of %d returned 0. Error = %s\n", (int)(N - total), strerror(errno) )); - return 0; - } + struct ldap_connection *conn; - if (ret == -1) { - DEBUG(0,("read_data: read failure for %d. Error = %s\n", (int)(N - total), strerror(errno) )); - return -1; - } - total += ret; + conn = talloc_zero(mem_ctx, struct ldap_connection); + if (conn == NULL) { + return NULL; } - return (ssize_t)total; -} - -/**************************************************************************** - Write data to a fd with timeout. -****************************************************************************/ -static ssize_t write_data_until(int fd,char *buffer,size_t N, - const struct timeval *endtime) -{ - size_t total=0; - ssize_t ret; - - while (total < N) { - - if (endtime != NULL) { - fd_set w_fds; - struct timeval timeout; - int res; - - FD_ZERO(&w_fds); - FD_SET(fd, &w_fds); - - if (!timeout_until(&timeout, endtime)) - return -1; - - res = sys_select(fd+1, NULL, &w_fds, NULL, &timeout); - if (res <= 0) - return -1; - } - - ret = sys_write(fd,buffer + total,N - total); - - if (ret == -1) { - DEBUG(0,("write_data: write failure. Error = %s\n", strerror(errno) )); - return -1; + if (ev == NULL) { + ev = event_context_init(conn); + if (ev == NULL) { + talloc_free(conn); + return NULL; } - if (ret == 0) - return total; - - total += ret; } - return (ssize_t)total; -} + conn->next_messageid = 1; + conn->event.event_ctx = ev; + /* set a reasonable request timeout */ + conn->timeout = 60; -static BOOL read_one_uint8(int sock, uint8_t *result, struct asn1_data *data, - const struct timeval *endtime) -{ - if (read_data_until(sock, result, 1, endtime) != 1) - return False; - - return asn1_write(data, result, 1); + return conn; } -/* Read a complete ASN sequence (ie LDAP result) from a socket */ -static BOOL asn1_read_sequence_until(int sock, struct asn1_data *data, - const struct timeval *endtime) -{ - uint8_t b; - size_t len; - char *buf; - - ZERO_STRUCTP(data); - - if (!read_one_uint8(sock, &b, data, endtime)) - return False; - if (b != 0x30) { - data->has_error = True; - return False; - } +/* + the connection is dead +*/ +static void ldap_connection_dead(struct ldap_connection *conn) +{ + struct ldap_request *req; - if (!read_one_uint8(sock, &b, data, endtime)) - return False; - - if (b & 0x80) { - int n = b & 0x7f; - if (!read_one_uint8(sock, &b, data, endtime)) - return False; - len = b; - while (n > 1) { - if (!read_one_uint8(sock, &b, data, endtime)) - return False; - len = (len<<8) | b; - n--; + while (conn->pending) { + req = conn->pending; + DLIST_REMOVE(req->conn->pending, req); + req->state = LDAP_REQUEST_DONE; + req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + 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); } - } else { - len = b; } - buf = talloc_size(NULL, len); - if (buf == NULL) - return False; - - if (read_data_until(sock, buf, len, endtime) != len) - return False; - - if (!asn1_write(data, buf, len)) - return False; - - talloc_free(buf); - - data->ofs = 0; - - return True; + talloc_free(conn->sock); + conn->sock = NULL; } - -/**************************************************************************** - create an outgoing socket. timeout is in milliseconds. - **************************************************************************/ -static int open_socket_out(int type, struct ipv4_addr *addr, int port, int timeout) -{ - struct sockaddr_in sock_out; - int res,ret; - int connect_loop = 250; /* 250 milliseconds */ - int loops = (timeout) / connect_loop; - - /* create a socket to write to */ - res = socket(PF_INET, type, 0); - if (res == -1) - { DEBUG(0,("socket error\n")); return -1; } - - if (type != SOCK_STREAM) return(res); - - memset((char *)&sock_out,'\0',sizeof(sock_out)); - sock_out.sin_addr.s_addr = addr->addr; - - sock_out.sin_port = htons( port ); - sock_out.sin_family = PF_INET; - - /* set it non-blocking */ - set_blocking(res,False); - - DEBUG(3,("Connecting to %s at port %d\n", sys_inet_ntoa(*addr),port)); - - /* and connect it to the destination */ -connect_again: - ret = connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out)); - - /* Some systems return EAGAIN when they mean EINPROGRESS */ - if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY || - errno == EAGAIN) && loops--) { - msleep(connect_loop); - goto connect_again; - } - - if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY || - errno == EAGAIN)) { - DEBUG(1,("timeout connecting to %s:%d\n", sys_inet_ntoa(*addr),port)); - close(res); - return -1; - } - -#ifdef EISCONN - if (ret < 0 && errno == EISCONN) { - errno = 0; - ret = 0; - } -#endif - - if (ret < 0) { - DEBUG(2,("error connecting to %s:%d (%s)\n", - sys_inet_ntoa(*addr),port,strerror(errno))); - close(res); - return -1; - } - - /* set it blocking again */ - set_blocking(res,True); - - return res; -} - -#if 0 -static struct ldap_message *new_ldap_search_message(struct ldap_connection *conn, - const char *base, - enum ldap_scope scope, - char *filter, - int num_attributes, - const char **attributes) +/* + match up with a pending message, adding to the replies list +*/ +static void ldap_match_message(struct ldap_connection *conn, struct ldap_message *msg) { - struct ldap_message *res; + struct ldap_request *req; - res = new_ldap_message(conn); - if (!res) { - return NULL; + for (req=conn->pending; req; req=req->next) { + if (req->messageid == msg->messageid) break; } - - res->type = LDAP_TAG_SearchRequest; - res->r.SearchRequest.basedn = base; - res->r.SearchRequest.scope = scope; - res->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER; - res->r.SearchRequest.timelimit = 0; - res->r.SearchRequest.sizelimit = 0; - res->r.SearchRequest.attributesonly = False; - res->r.SearchRequest.filter = filter; - res->r.SearchRequest.num_attributes = num_attributes; - res->r.SearchRequest.attributes = attributes; - - return res; -} -#endif - -static struct ldap_message *new_ldap_simple_bind_msg(struct ldap_connection *conn, const char *dn, const char *pw) -{ - struct ldap_message *res; - - res = new_ldap_message(conn); - if (!res) { - return NULL; + if (req == NULL) { + DEBUG(0,("ldap: no matching message id for %u\n", + msg->messageid)); + talloc_free(msg); + return; } - res->type = LDAP_TAG_BindRequest; - res->r.BindRequest.version = 3; - res->r.BindRequest.dn = talloc_strdup(res, dn); - res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SIMPLE; - res->r.BindRequest.creds.password = talloc_strdup(res, pw); - - return res; -} - -static struct ldap_message *new_ldap_sasl_bind_msg(struct ldap_connection *conn, const char *sasl_mechanism, DATA_BLOB *secblob) -{ - struct ldap_message *res; - - res = new_ldap_message(conn); - if (!res) { - return NULL; + /* add to the list of replies received */ + talloc_steal(req, msg); + req->replies = talloc_realloc(req, req->replies, + struct ldap_message *, req->num_replies+1); + if (req->replies == NULL) { + req->status = NT_STATUS_NO_MEMORY; + req->state = LDAP_REQUEST_DONE; + DLIST_REMOVE(conn->pending, req); + if (req->async.fn) { + req->async.fn(req); + } + return; } - res->type = LDAP_TAG_BindRequest; - res->r.BindRequest.version = 3; - res->r.BindRequest.dn = ""; - res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SASL; - res->r.BindRequest.creds.SASL.mechanism = talloc_strdup(res, sasl_mechanism); - res->r.BindRequest.creds.SASL.secblob = *secblob; - - return res; -} - -static struct ldap_connection *new_ldap_connection(TALLOC_CTX *mem_ctx) -{ - struct ldap_connection *result; + req->replies[req->num_replies] = talloc_steal(req->replies, msg); + req->num_replies++; - result = talloc(mem_ctx, struct ldap_connection); - - if (!result) { - return NULL; + if (msg->type != LDAP_TAG_SearchResultEntry) { + /* currently only search results expect multiple + replies */ + req->state = LDAP_REQUEST_DONE; + DLIST_REMOVE(conn->pending, req); } - result->next_msgid = 1; - result->outstanding = NULL; - result->searchid = 0; - result->search_entries = NULL; - result->auth_dn = NULL; - result->simple_pw = NULL; - result->gensec = NULL; - - return result; + if (req->async.fn) { + req->async.fn(req); + } } -struct ldap_connection *ldap_connect(TALLOC_CTX *mem_ctx, const char *url) +/* + try and decode/process plain data +*/ +static void ldap_try_decode_plain(struct ldap_connection *conn) { - struct hostent *hp; - struct ipv4_addr ip; - struct ldap_connection *conn; - BOOL ret; + struct asn1_data asn1; - conn = new_ldap_connection(mem_ctx); - if (!conn) { - return NULL; + if (!asn1_load(&asn1, conn->partial)) { + ldap_connection_dead(conn); + return; } - ret = ldap_parse_basic_url(conn, url, &conn->host, - &conn->port, &conn->ldaps); - if (!ret) { - talloc_free(conn); - return NULL; - } + /* 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); + if (msg == NULL) { + ldap_connection_dead(conn); + return; + } - hp = sys_gethostbyname(conn->host); - if (!hp || !hp->h_addr) { - talloc_free(conn); - return NULL; + if (ldap_decode(&asn1, msg)) { + ldap_match_message(conn, msg); + } else { + talloc_free(msg); + break; + } } - memcpy((char *)&ip, (char *)hp->h_addr, 4); - - conn->sock = open_socket_out(SOCK_STREAM, &ip, conn->port, LDAP_CONNECTION_TIMEOUT); - if (conn->sock < 0) { - talloc_free(conn); - return NULL; + /* keep any remaining data in conn->partial */ + data_blob_free(&conn->partial); + if (asn1.ofs != conn->partial.length) { + conn->partial = data_blob_talloc(conn, + asn1.data + asn1.ofs, + asn1.length - asn1.ofs); } - - return conn; -} - -struct ldap_message *new_ldap_message(TALLOC_CTX *mem_ctx) -{ - return talloc(mem_ctx, struct ldap_message); -} - -BOOL ldap_send_msg(struct ldap_connection *conn, struct ldap_message *msg, - const struct timeval *endtime) -{ - DATA_BLOB request; - BOOL result; - struct ldap_queue_entry *entry; - - msg->messageid = conn->next_msgid++; - - if (!ldap_encode(msg, &request)) - return False; - - result = (write_data_until(conn->sock, request.data, request.length, - endtime) == request.length); - - data_blob_free(&request); - - if (!result) - return result; - - /* abandon and unbind don't expect results */ - - if ((msg->type == LDAP_TAG_AbandonRequest) || - (msg->type == LDAP_TAG_UnbindRequest)) - return True; - - entry = malloc_p(struct ldap_queue_entry); - - if (entry == NULL) - return False; - - entry->msgid = msg->messageid; - entry->msg = NULL; - DLIST_ADD(conn->outstanding, entry); - - return True; -} - -BOOL ldap_receive_msg(struct ldap_connection *conn, struct ldap_message *msg, - const struct timeval *endtime) -{ - struct asn1_data data; - BOOL result; - - if (!asn1_read_sequence_until(conn->sock, &data, endtime)) - return False; - - result = ldap_decode(&data, msg); - - asn1_free(&data); - return result; + asn1_free(&asn1); } -static struct ldap_message *recv_from_queue(struct ldap_connection *conn, - int msgid) +/* + try and decode/process wrapped data +*/ +static void ldap_try_decode_wrapped(struct ldap_connection *conn) { - struct ldap_queue_entry *e; + uint32_t len; - for (e = conn->outstanding; e != NULL; e = e->next) { + /* 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 (e->msgid == msgid) { - struct ldap_message *result = e->msg; - DLIST_REMOVE(conn->outstanding, e); - SAFE_FREE(e); - return result; + if (msg == NULL) { + ldap_connection_dead(conn); + return; } - } - return NULL; -} - -static void add_search_entry(struct ldap_connection *conn, - struct ldap_message *msg) -{ - struct ldap_queue_entry *e = malloc_p(struct ldap_queue_entry); - - if (e == NULL) - return; + wrapped.data = conn->partial.data+4; + wrapped.length = len; - e->msg = msg; - DLIST_ADD_END(conn->search_entries, e, struct ldap_queue_entry *); - return; -} - -static void fill_outstanding_request(struct ldap_connection *conn, - struct ldap_message *msg) -{ - struct ldap_queue_entry *e; - - for (e = conn->outstanding; e != NULL; e = e->next) { - if (e->msgid == msg->messageid) { - e->msg = msg; + status = gensec_unwrap(conn->gensec, msg, &wrapped, &unwrapped); + if (!NT_STATUS_IS_OK(status)) { + ldap_connection_dead(conn); return; } - } - - /* This reply has not been expected, destroy the incoming msg */ - talloc_free(msg); - return; -} - -struct ldap_message *ldap_receive(struct ldap_connection *conn, int msgid, - const struct timeval *endtime) -{ - struct ldap_message *result = recv_from_queue(conn, msgid); - - if (result != NULL) - return result; - while (True) { - struct asn1_data data; - BOOL res; - - result = new_ldap_message(conn); - - if (!asn1_read_sequence_until(conn->sock, &data, endtime)) - return NULL; - - res = ldap_decode(&data, result); - asn1_free(&data); - - if (!res) - return NULL; + if (!asn1_load(&asn1, unwrapped)) { + ldap_connection_dead(conn); + return; + } - if (result->messageid == msgid) - return result; + if (ldap_decode(&asn1, msg)) { + ldap_match_message(conn, msg); + } else { + talloc_free(msg); + } + + asn1_free(&asn1); - if (result->type == LDAP_TAG_SearchResultEntry) { - add_search_entry(conn, result); + if (conn->partial.length == len + 4) { + data_blob_free(&conn->partial); } else { - fill_outstanding_request(conn, result); + memmove(conn->partial.data, conn->partial.data+len+4, + conn->partial.length - (len+4)); + conn->partial.length -= len + 4; } } - - return NULL; } + /* - Write data to a fd + handle ldap recv events */ -static ssize_t write_data(int fd, char *buffer, size_t N) +static void ldap_recv_handler(struct ldap_connection *conn) { - size_t total=0; - ssize_t ret; + NTSTATUS status; + size_t npending=0, nread; - while (total < N) { - ret = sys_write(fd,buffer + total,N - total); + /* work out how much data is pending */ + status = socket_pending(conn->sock, &npending); + if (!NT_STATUS_IS_OK(status) || npending == 0) { + DEBUG(0,("ldap_recv_handler - pending=%d - %s\n", + (int)npending, nt_errstr(status))); + return; + } - if (ret == -1) { - DEBUG(0,("write_data: write failure. Error = %s\n", strerror(errno) )); - return -1; - } - if (ret == 0) - return total; + conn->partial.data = talloc_realloc_size(conn, conn->partial.data, + conn->partial.length + npending); + if (conn->partial.data == NULL) { + ldap_connection_dead(conn); + return; + } - total += ret; + /* receive the pending data */ + status = socket_recv(conn->sock, conn->partial.data + conn->partial.length, + npending, &nread, 0); + if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + return; } + if (!NT_STATUS_IS_OK(status)) { + ldap_connection_dead(conn); + return; + } + conn->partial.length += nread; - return (ssize_t)total; + /* see if we can decode what we have */ + if (conn->enable_wrap) { + ldap_try_decode_wrapped(conn); + } else { + ldap_try_decode_plain(conn); + } } /* - Read data from the client, reading exactly N bytes + handle ldap send events */ -static ssize_t read_data(int fd, char *buffer, size_t N) +static void ldap_send_handler(struct ldap_connection *conn) { - ssize_t ret; - size_t total=0; - - while (total < N) { + while (conn->send_queue) { + struct ldap_request *req = conn->send_queue; + size_t nsent; + NTSTATUS status; - ret = sys_read(fd,buffer + total,N - total); - - if (ret == 0) { - DEBUG(10,("read_data: read of %d returned 0. Error = %s\n", - (int)(N - total), strerror(errno) )); - return 0; + status = socket_send(conn->sock, &req->data, &nsent, 0); + if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + break; } - - if (ret == -1) { - DEBUG(0,("read_data: read failure for %d. Error = %s\n", - (int)(N - total), strerror(errno) )); - return -1; + if (!NT_STATUS_IS_OK(status)) { + ldap_connection_dead(conn); + return; } - total += ret; - } - - return (ssize_t)total; -} - -static struct ldap_message *ldap_transaction_sasl(struct ldap_connection *conn, struct ldap_message *req) -{ - NTSTATUS status; - DATA_BLOB request; - BOOL result; - DATA_BLOB wrapped; - int len; - char length[4]; - struct asn1_data asn1; - struct ldap_message *rep; - - req->messageid = conn->next_msgid++; - - if (!ldap_encode(req, &request)) - return NULL; - - status = gensec_wrap(conn->gensec, - req, - &request, - &wrapped); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("gensec_wrap: %s\n",nt_errstr(status))); - return NULL; - } - - RSIVAL(length, 0, wrapped.length); - result = (write_data(conn->sock, length, 4) == 4); - if (!result) - return NULL; - - result = (write_data(conn->sock, wrapped.data, wrapped.length) == wrapped.length); - if (!result) - return NULL; - - wrapped = data_blob(NULL, 0x4000); - data_blob_clear(&wrapped); - - result = (read_data(conn->sock, length, 4) == 4); - if (!result) - return NULL; - - len = RIVAL(length,0); - - result = (read_data(conn->sock, wrapped.data, MIN(wrapped.length,len)) == len); - if (!result) - return NULL; - - wrapped.length = len; - - status = gensec_unwrap(conn->gensec, - req, - &wrapped, - &request); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("gensec_unwrap: %s\n",nt_errstr(status))); - return NULL; + 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); + } + } } - - rep = new_ldap_message(req); - - asn1_load(&asn1, request); - if (!ldap_decode(&asn1, rep)) { - return NULL; + if (conn->send_queue == NULL) { + EVENT_FD_NOT_WRITEABLE(conn->event.fde); } - - return rep; } -struct ldap_message *ldap_transaction(struct ldap_connection *conn, - struct ldap_message *request) + +/* + handle ldap socket events +*/ +static void ldap_io_handler(struct event_context *ev, struct fd_event *fde, + uint16_t flags, void *private) { - if ((request->type != LDAP_TAG_BindRequest) && conn->gensec && - (gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN) || - gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN))) { - return ldap_transaction_sasl(conn, request); + struct ldap_connection *conn = talloc_get_type(private, struct ldap_connection); + if (flags & EVENT_FD_WRITE) { + ldap_send_handler(conn); + if (conn->sock == NULL) return; + } + if (flags & EVENT_FD_READ) { + ldap_recv_handler(conn); } - - if (!ldap_send_msg(conn, request, NULL)) - return False; - - return ldap_receive(conn, request->messageid, NULL); } -int ldap_bind_simple(struct ldap_connection *conn, const char *userdn, const char *password) +/* + parse a ldap URL +*/ +static NTSTATUS ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url, + char **host, uint16_t *port, BOOL *ldaps) { - struct ldap_message *response; - struct ldap_message *msg; - const char *dn, *pw; - int result = LDAP_OTHER; + int tmp_port = 0; + char protocol[11]; + char tmp_host[255]; + const char *p = url; + int ret; - if (conn == NULL) - return result; + /* skip leading "URL:" (if any) */ + if (strncasecmp(p, "URL:", 4) == 0) { + p += 4; + } - if (userdn) { - dn = userdn; - } else { - if (conn->auth_dn) { - dn = conn->auth_dn; - } else { - dn = ""; - } + /* Paranoia check */ + SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254); + + ret = sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port); + if (ret < 2) { + return NT_STATUS_INVALID_PARAMETER; } - if (password) { - pw = password; + if (strequal(protocol, "ldap")) { + *port = 389; + *ldaps = False; + } else if (strequal(protocol, "ldaps")) { + *port = 636; + *ldaps = True; } else { - if (conn->simple_pw) { - pw = conn->simple_pw; - } else { - pw = ""; - } + DEBUG(0, ("unrecognised ldap protocol (%s)!\n", protocol)); + return NT_STATUS_PROTOCOL_UNREACHABLE; } - msg = new_ldap_simple_bind_msg(conn, dn, pw); - if (!msg) - return result; + if (tmp_port != 0) + *port = tmp_port; - response = ldap_transaction(conn, msg); - if (!response) { - talloc_free(msg); - return result; - } - - result = response->r.BindResponse.response.resultcode; - - talloc_free(msg); - talloc_free(response); + *host = talloc_strdup(mem_ctx, tmp_host); + NT_STATUS_HAVE_NO_MEMORY(*host); - return result; + return NT_STATUS_OK; } -int ldap_bind_sasl(struct ldap_connection *conn, struct cli_credentials *creds) +/* + connect to a ldap server +*/ +NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url) { NTSTATUS status; - TALLOC_CTX *mem_ctx = NULL; - struct ldap_message *response; - struct ldap_message *msg; - DATA_BLOB input = data_blob(NULL, 0); - DATA_BLOB output = data_blob(NULL, 0); - int result = LDAP_OTHER; - if (conn == NULL) - return result; + status = ldap_parse_basic_url(conn, url, &conn->host, + &conn->port, &conn->ldaps); + NT_STATUS_NOT_OK_RETURN(status); - status = gensec_client_start(conn, &conn->gensec); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status))); - return result; - } - - gensec_want_feature(conn->gensec, 0 | GENSEC_FEATURE_SIGN | GENSEC_FEATURE_SEAL); - - status = gensec_set_credentials(conn->gensec, creds); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC creds: %s\n", - nt_errstr(status))); - goto done; - } + status = socket_create("ipv4", SOCKET_TYPE_STREAM, &conn->sock, 0); + NT_STATUS_NOT_OK_RETURN(status); - status = gensec_set_target_hostname(conn->gensec, conn->host); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", - nt_errstr(status))); - goto done; - } + talloc_steal(conn, conn->sock); - status = gensec_set_target_service(conn->gensec, "ldap"); + /* 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)) { - DEBUG(1, ("Failed to start set GENSEC target service: %s\n", - nt_errstr(status))); - goto done; + talloc_free(conn->sock); + return status; } - status = gensec_start_mech_by_sasl_name(conn->gensec, "NTLM"); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism: %s\n", - nt_errstr(status))); - goto done; + /* 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; } - mem_ctx = talloc_init("ldap_bind_sasl"); - if (!mem_ctx) - goto done; - - status = gensec_update(conn->gensec, mem_ctx, - input, - &output); - - while(1) { - if (NT_STATUS_IS_OK(status) && output.length == 0) { - break; - } - if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) { - break; - } - - msg = new_ldap_sasl_bind_msg(conn, "GSS-SPNEGO", &output); - if (!msg) - goto done; - - response = ldap_transaction(conn, msg); - talloc_free(msg); - - if (!response) { - goto done; - } - - result = response->r.BindResponse.response.resultcode; - - if (result != LDAP_SUCCESS && result != LDAP_SASL_BIND_IN_PROGRESS) { - break; - } - - if (!NT_STATUS_IS_OK(status)) { - status = gensec_update(conn->gensec, mem_ctx, - response->r.BindResponse.SASL.secblob, - &output); - } else { - output.length = 0; - } - - talloc_free(response); - } - -done: - talloc_free(mem_ctx); - - return result; + return NT_STATUS_OK; } -struct ldap_connection *ldap_setup_connection(TALLOC_CTX *mem_ctx, const char *url, - const char *userdn, const char *password) +/* destroy an open ldap request */ +static int ldap_request_destructor(void *ptr) { - struct ldap_connection *conn; - int result; - - conn =ldap_connect(mem_ctx, url); - if (!conn) { - return NULL; + struct ldap_request *req = talloc_get_type(ptr, struct ldap_request); + if (req->state == LDAP_REQUEST_SEND) { + DLIST_REMOVE(req->conn->send_queue, req); } - - result = ldap_bind_simple(conn, userdn, password); - if (result != LDAP_SUCCESS) { - talloc_free(conn); - return NULL; + if (req->state == LDAP_REQUEST_PENDING) { + DLIST_REMOVE(req->conn->pending, req); } - - return conn; + return 0; } -struct ldap_connection *ldap_setup_connection_with_sasl(TALLOC_CTX *mem_ctx, - const char *url, - struct cli_credentials *creds) +/* + 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 ldap_connection *conn; - int result; - - conn =ldap_connect(mem_ctx, url); - if (!conn) { - return NULL; + struct ldap_request *req = talloc_get_type(private, struct ldap_request); + req->status = NT_STATUS_IO_TIMEOUT; + if (req->state == LDAP_REQUEST_SEND) { + DLIST_REMOVE(req->conn->send_queue, req); } - - result = ldap_bind_sasl(conn, creds); - if (result != LDAP_SUCCESS) { - talloc_free(conn); - return NULL; + if (req->state == LDAP_REQUEST_PENDING) { + DLIST_REMOVE(req->conn->pending, req); + } + req->state = LDAP_REQUEST_DONE; + if (req->async.fn) { + req->async.fn(req); } - - return conn; } -BOOL ldap_abandon_message(struct ldap_connection *conn, int msgid, - const struct timeval *endtime) +/* + send a ldap message - async interface +*/ +struct ldap_request *ldap_request_send(struct ldap_connection *conn, + struct ldap_message *msg) { - struct ldap_message *msg = new_ldap_message(conn); - BOOL result; + struct ldap_request *req; - if (msg == NULL) - return False; + if (conn->sock == NULL) { + return NULL; + } - msg->type = LDAP_TAG_AbandonRequest; - msg->r.AbandonRequest.messageid = msgid; + req = talloc_zero(conn, struct ldap_request); + if (req == NULL) goto failed; - result = ldap_send_msg(conn, msg, endtime); - talloc_free(msg); - return result; -} + req->state = LDAP_REQUEST_SEND; + req->conn = conn; + req->messageid = conn->next_messageid++; + req->type = msg->type; + if (req->messageid == -1) { + goto failed; + } -BOOL ldap_setsearchent(struct ldap_connection *conn, struct ldap_message *msg, - const struct timeval *endtime) -{ - if ((conn->searchid != 0) && - (!ldap_abandon_message(conn, conn->searchid, endtime))) - return False; + talloc_set_destructor(req, ldap_request_destructor); - conn->searchid = conn->next_msgid; - return ldap_send_msg(conn, msg, endtime); -} + msg->messageid = req->messageid; -struct ldap_message *ldap_getsearchent(struct ldap_connection *conn, - const struct timeval *endtime) -{ - struct ldap_message *result; + if (!ldap_encode(msg, &req->data)) { + goto failed; + } - if (conn->search_entries != NULL) { - struct ldap_queue_entry *e = conn->search_entries; + /* possibly encrypt/sign the request */ + if (conn->enable_wrap) { + DATA_BLOB wrapped; + NTSTATUS status; - result = e->msg; - DLIST_REMOVE(conn->search_entries, e); - SAFE_FREE(e); - return result; + 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); } - result = ldap_receive(conn, conn->searchid, endtime); - if (!result) { - return NULL; + + if (conn->send_queue == NULL) { + EVENT_FD_WRITEABLE(conn->event.fde); } + DLIST_ADD_END(conn->send_queue, req, struct ldap_request *); - if (result->type == LDAP_TAG_SearchResultEntry) - return result; + /* put a timeout on the request */ + event_add_timed(conn->event.event_ctx, req, + timeval_current_ofs(conn->timeout, 0), + ldap_request_timeout, req); - if (result->type == LDAP_TAG_SearchResultDone) { - /* TODO: Handle Paged Results */ - talloc_free(result); - return NULL; - } + return req; - /* TODO: Handle Search References here */ +failed: + talloc_free(req); return NULL; } -void ldap_endsearchent(struct ldap_connection *conn, - const struct timeval *endtime) -{ - struct ldap_queue_entry *e; - - e = conn->search_entries; - while (e != NULL) { - struct ldap_queue_entry *next = e->next; - DLIST_REMOVE(conn->search_entries, e); - SAFE_FREE(e); - e = next; +/* + wait for a request to complete + note that this does not destroy the request +*/ +NTSTATUS ldap_request_wait(struct ldap_request *req) +{ + while (req->state != LDAP_REQUEST_DONE) { + if (event_loop_once(req->conn->event.event_ctx) != 0) { + req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR; + break; + } } + return req->status; } -struct ldap_message *ldap_searchone(struct ldap_connection *conn, - struct ldap_message *msg, - const struct timeval *endtime) -{ - struct ldap_message *res1, *res2 = NULL; - if (!ldap_setsearchent(conn, msg, endtime)) - return NULL; - - res1 = ldap_getsearchent(conn, endtime); - - if (res1 != NULL) - res2 = ldap_getsearchent(conn, endtime); - - ldap_endsearchent(conn, endtime); - - if (res1 == NULL) - return NULL; - if (res2 != NULL) { - /* More than one entry */ - talloc_free(res1); - talloc_free(res2); - return NULL; +/* + used to setup the status code from a ldap response +*/ +NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r) +{ + if (r->resultcode == LDAP_SUCCESS) { + return NT_STATUS_OK; } - return res1; + if (conn->last_error) { + talloc_free(conn->last_error); + } + conn->last_error = talloc_asprintf(conn, "LDAP error %u - %s <%s> <%s>", + r->resultcode, + r->dn, r->errormessage, r->referral); + + return NT_STATUS_LDAP(r->resultcode); } -BOOL ldap_find_single_value(struct ldap_message *msg, const char *attr, - DATA_BLOB *value) +/* + return error string representing the last error +*/ +const char *ldap_errstr(struct ldap_connection *conn, NTSTATUS status) { - int i; - struct ldap_SearchResEntry *r = &msg->r.SearchResultEntry; - - if (msg->type != LDAP_TAG_SearchResultEntry) - return False; - - for (i=0; i<r->num_attributes; i++) { - if (strequal(attr, r->attributes[i].name)) { - if (r->attributes[i].num_values != 1) - return False; - - *value = r->attributes[i].values[0]; - return True; - } + if (NT_STATUS_IS_LDAP(status) && conn->last_error != NULL) { + return conn->last_error; } - return False; + return nt_errstr(status); } -BOOL ldap_find_single_string(struct ldap_message *msg, const char *attr, - TALLOC_CTX *mem_ctx, char **value) -{ - DATA_BLOB blob; - - if (!ldap_find_single_value(msg, attr, &blob)) - return False; - - *value = talloc_size(mem_ctx, blob.length+1); - - if (*value == NULL) - return False; - memcpy(*value, blob.data, blob.length); - (*value)[blob.length] = '\0'; - return True; -} - -BOOL ldap_find_single_int(struct ldap_message *msg, const char *attr, - int *value) +/* + return the Nth result message, waiting if necessary +*/ +NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg) { - DATA_BLOB blob; - char *val; - int errno_save; - BOOL res; - - if (!ldap_find_single_value(msg, attr, &blob)) - return False; - - val = malloc(blob.length+1); - if (val == NULL) - return False; + *msg = NULL; - memcpy(val, blob.data, blob.length); - val[blob.length] = '\0'; - - errno_save = errno; - errno = 0; - - *value = strtol(val, NULL, 10); + 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; + } + } - res = (errno == 0); + if (n < req->num_replies) { + *msg = req->replies[n]; + return NT_STATUS_OK; + } - free(val); - errno = errno_save; + if (!NT_STATUS_IS_OK(req->status)) { + return req->status; + } - return res; + return NT_STATUS_NO_MORE_ENTRIES; } -int ldap_error(struct ldap_connection *conn) -{ - return 0; -} -NTSTATUS ldap2nterror(int ldaperror) +/* + return a single result message, checking if it is of the expected LDAP type +*/ +NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type) { - return NT_STATUS_OK; + NTSTATUS status; + status = ldap_result_n(req, 0, msg); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if ((*msg)->type != type) { + *msg = NULL; + return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + } + return status; } |