From 4f5791ebd03eaec1c7da0865a383175b05102712 Mon Sep 17 00:00:00 2001
From: Daniel Baumann <daniel.baumann@progress-linux.org>
Date: Sun, 5 May 2024 19:47:29 +0200
Subject: Adding upstream version 2:4.17.12+dfsg.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
---
 libcli/cldap/cldap.c       | 1224 ++++++++++++++++++++++++++++++++++++++++++++
 libcli/cldap/cldap.h       |  134 +++++
 libcli/cldap/wscript_build |   10 +
 3 files changed, 1368 insertions(+)
 create mode 100644 libcli/cldap/cldap.c
 create mode 100644 libcli/cldap/cldap.h
 create mode 100644 libcli/cldap/wscript_build

(limited to 'libcli/cldap')

diff --git a/libcli/cldap/cldap.c b/libcli/cldap/cldap.c
new file mode 100644
index 0000000..7de72b5
--- /dev/null
+++ b/libcli/cldap/cldap.c
@@ -0,0 +1,1224 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   cldap client library
+
+   Copyright (C) Andrew Tridgell 2005
+   Copyright (C) Stefan Metzmacher 2009
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+  see RFC1798 for details of CLDAP
+
+  basic properties
+    - carried over UDP on port 389
+    - request and response matched by message ID
+    - request consists of only a single searchRequest element
+    - response can be in one of two forms
+       - a single searchResponse, followed by a searchResult
+       - a single searchResult
+*/
+
+#include "includes.h"
+#include <tevent.h>
+#include "../lib/util/dlinklist.h"
+#include "../libcli/ldap/ldap_message.h"
+#include "../libcli/ldap/ldap_ndr.h"
+#include "../libcli/cldap/cldap.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../libcli/security/dom_sid.h"
+#include "../librpc/gen_ndr/ndr_nbt.h"
+#include "../lib/util/asn1.h"
+#include "../lib/util/tevent_ntstatus.h"
+
+#undef strcasecmp
+
+/*
+  context structure for operations on cldap packets
+*/
+struct cldap_socket {
+	/* the low level socket */
+	struct tdgram_context *sock;
+
+	/*
+	 * Are we in connected mode, which means
+	 * we get ICMP errors back instead of timing
+	 * out requests. And we can only send requests
+	 * to the connected peer.
+	 */
+	bool connected;
+
+	/* the queue for outgoing dgrams */
+	struct tevent_queue *send_queue;
+
+	/* do we have an async tsocket_recvfrom request pending */
+	struct tevent_req *recv_subreq;
+
+	struct {
+		/* a queue of pending search requests */
+		struct cldap_search_state *list;
+
+		/* mapping from message_id to pending request */
+		struct idr_context *idr;
+	} searches;
+
+	/* what to do with incoming request packets */
+	struct {
+		struct tevent_context *ev;
+		void (*handler)(struct cldap_socket *,
+				void *private_data,
+				struct cldap_incoming *);
+		void *private_data;
+	} incoming;
+};
+
+struct cldap_search_state {
+	struct cldap_search_state *prev, *next;
+
+	struct {
+		struct tevent_context *ev;
+		struct cldap_socket *cldap;
+	} caller;
+
+	int message_id;
+
+	struct {
+		uint32_t idx;
+		uint32_t delay;
+		uint32_t count;
+		struct tsocket_address *dest;
+		DATA_BLOB blob;
+	} request;
+
+	struct {
+		struct cldap_incoming *in;
+		struct asn1_data *asn1;
+	} response;
+
+	struct tevent_req *req;
+};
+
+/*
+ * For CLDAP we limit the maximum search request size to 4kb
+ */
+#define MAX_SEARCH_REQUEST 4096
+
+static int cldap_socket_destructor(struct cldap_socket *c)
+{
+	while (c->searches.list) {
+		struct cldap_search_state *s = c->searches.list;
+		DLIST_REMOVE(c->searches.list, s);
+		ZERO_STRUCT(s->caller);
+	}
+
+	talloc_free(c->recv_subreq);
+	talloc_free(c->send_queue);
+	talloc_free(c->sock);
+	return 0;
+}
+
+static void cldap_recvfrom_done(struct tevent_req *subreq);
+
+static bool cldap_recvfrom_setup(struct cldap_socket *c)
+{
+	struct tevent_context *ev;
+
+	if (c->recv_subreq) {
+		return true;
+	}
+
+	if (!c->searches.list && !c->incoming.handler) {
+		return true;
+	}
+
+	ev = c->incoming.ev;
+	if (ev == NULL) {
+		/* this shouldn't happen but should be protected against */
+		if (c->searches.list == NULL) {
+			return false;
+		}
+		ev = c->searches.list->caller.ev;
+	}
+
+	c->recv_subreq = tdgram_recvfrom_send(c, ev, c->sock);
+	if (!c->recv_subreq) {
+		return false;
+	}
+	tevent_req_set_callback(c->recv_subreq, cldap_recvfrom_done, c);
+
+	return true;
+}
+
+static void cldap_recvfrom_stop(struct cldap_socket *c)
+{
+	if (!c->recv_subreq) {
+		return;
+	}
+
+	if (c->searches.list || c->incoming.handler) {
+		return;
+	}
+
+	talloc_free(c->recv_subreq);
+	c->recv_subreq = NULL;
+}
+
+static bool cldap_socket_recv_dgram(struct cldap_socket *c,
+				    struct cldap_incoming *in);
+
+static void cldap_recvfrom_done(struct tevent_req *subreq)
+{
+	struct cldap_socket *c = tevent_req_callback_data(subreq,
+				 struct cldap_socket);
+	struct cldap_incoming *in = NULL;
+	ssize_t ret;
+	bool setup_done;
+
+	c->recv_subreq = NULL;
+
+	in = talloc_zero(c, struct cldap_incoming);
+	if (!in) {
+		goto nomem;
+	}
+
+	ret = tdgram_recvfrom_recv(subreq,
+				   &in->recv_errno,
+				   in,
+				   &in->buf,
+				   &in->src);
+	talloc_free(subreq);
+	subreq = NULL;
+	if (ret >= 0) {
+		in->len = ret;
+	}
+	if (ret == -1 && in->recv_errno == 0) {
+		in->recv_errno = EIO;
+	}
+
+	/* this function should free or steal 'in' */
+	setup_done = cldap_socket_recv_dgram(c, in);
+	in = NULL;
+
+	if (!setup_done && !cldap_recvfrom_setup(c)) {
+		goto nomem;
+	}
+
+	return;
+
+nomem:
+	talloc_free(subreq);
+	talloc_free(in);
+}
+
+/*
+  handle recv events on a cldap socket
+*/
+static bool cldap_socket_recv_dgram(struct cldap_socket *c,
+				    struct cldap_incoming *in)
+{
+	struct asn1_data *asn1;
+	void *p;
+	struct cldap_search_state *search;
+	NTSTATUS status;
+	struct ldap_request_limits limits = {
+		.max_search_size = MAX_SEARCH_REQUEST
+	};
+
+	if (in->recv_errno != 0) {
+		goto error;
+	}
+
+	asn1 = asn1_init(in, ASN1_MAX_TREE_DEPTH);
+	if (!asn1) {
+		goto nomem;
+	}
+
+	asn1_load_nocopy(asn1, in->buf, in->len);
+
+	in->ldap_msg = talloc(in, struct ldap_message);
+	if (in->ldap_msg == NULL) {
+		goto nomem;
+	}
+
+	/* this initial decode is used to find the message id */
+	status = ldap_decode(asn1, &limits, NULL, in->ldap_msg);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto nterror;
+	}
+
+	/* find the pending request */
+	p = idr_find(c->searches.idr, in->ldap_msg->messageid);
+	if (p == NULL) {
+		if (!c->incoming.handler) {
+			TALLOC_FREE(in);
+			return true;
+		}
+
+		/* this function should free or steal 'in' */
+		c->incoming.handler(c, c->incoming.private_data, in);
+		return false;
+	}
+
+	search = talloc_get_type_abort(p, struct cldap_search_state);
+	search->response.in = talloc_move(search, &in);
+
+	search->response.asn1 = asn1;
+
+	asn1_load_nocopy(search->response.asn1,
+			 search->response.in->buf, search->response.in->len);
+
+	DLIST_REMOVE(c->searches.list, search);
+
+	if (cldap_recvfrom_setup(c)) {
+		tevent_req_done(search->req);
+		return true;
+	}
+
+	/*
+	 * This request was ok, just defer the notify of the caller
+	 * and then just fail the next request if needed
+	 */
+	tevent_req_defer_callback(search->req, search->caller.ev);
+	tevent_req_done(search->req);
+
+	status = NT_STATUS_NO_MEMORY;
+	/* in is NULL it this point */
+	goto nterror;
+nomem:
+	in->recv_errno = ENOMEM;
+error:
+	status = map_nt_error_from_unix_common(in->recv_errno);
+nterror:
+	TALLOC_FREE(in);
+	/* in connected mode the first pending search gets the error */
+	if (!c->connected) {
+		/* otherwise we just ignore the error */
+		return false;
+	}
+	if (!c->searches.list) {
+		return false;
+	}
+	/*
+	 * We might called tevent_req_done() for a successful
+	 * search before, so we better deliver the failure
+	 * after the success, that is why we better also
+	 * use tevent_req_defer_callback() here.
+	 */
+	tevent_req_defer_callback(c->searches.list->req,
+				  c->searches.list->caller.ev);
+	tevent_req_nterror(c->searches.list->req, status);
+	return false;
+}
+
+/*
+  initialise a cldap_sock
+*/
+NTSTATUS cldap_socket_init(TALLOC_CTX *mem_ctx,
+			   const struct tsocket_address *local_addr,
+			   const struct tsocket_address *remote_addr,
+			   struct cldap_socket **_cldap)
+{
+	struct cldap_socket *c = NULL;
+	struct tsocket_address *any = NULL;
+	NTSTATUS status;
+	int ret;
+	const char *fam = NULL;
+
+	if (local_addr == NULL && remote_addr == NULL) {
+		return NT_STATUS_INVALID_PARAMETER_MIX;
+	}
+
+	if (remote_addr) {
+		bool is_ipv4;
+		bool is_ipv6;
+
+		is_ipv4 = tsocket_address_is_inet(remote_addr, "ipv4");
+		is_ipv6 = tsocket_address_is_inet(remote_addr, "ipv6");
+
+		if (is_ipv4) {
+			fam = "ipv4";
+		} else if (is_ipv6) {
+			fam = "ipv6";
+		} else {
+			return NT_STATUS_INVALID_ADDRESS;
+		}
+	}
+
+	c = talloc_zero(mem_ctx, struct cldap_socket);
+	if (!c) {
+		goto nomem;
+	}
+
+	if (!local_addr) {
+		/*
+		 * Here we know the address family of the remote address.
+		 */
+		if (fam == NULL) {
+			return NT_STATUS_INVALID_PARAMETER_MIX;
+		}
+
+		ret = tsocket_address_inet_from_strings(c, fam,
+							NULL, 0,
+							&any);
+		if (ret != 0) {
+			status = map_nt_error_from_unix_common(errno);
+			goto nterror;
+		}
+		local_addr = any;
+	}
+
+	c->searches.idr = idr_init(c);
+	if (!c->searches.idr) {
+		goto nomem;
+	}
+
+	ret = tdgram_inet_udp_socket(local_addr, remote_addr,
+				     c, &c->sock);
+	if (ret != 0) {
+		status = map_nt_error_from_unix_common(errno);
+		goto nterror;
+	}
+	talloc_free(any);
+
+	if (remote_addr) {
+		c->connected = true;
+	}
+
+	c->send_queue = tevent_queue_create(c, "cldap_send_queue");
+	if (!c->send_queue) {
+		goto nomem;
+	}
+
+	talloc_set_destructor(c, cldap_socket_destructor);
+
+	*_cldap = c;
+	return NT_STATUS_OK;
+
+nomem:
+	status = NT_STATUS_NO_MEMORY;
+nterror:
+	talloc_free(c);
+	return status;
+}
+
+/*
+  setup a handler for incoming requests
+*/
+NTSTATUS cldap_set_incoming_handler(struct cldap_socket *c,
+				    struct tevent_context *ev,
+				    void (*handler)(struct cldap_socket *,
+						    void *private_data,
+						    struct cldap_incoming *),
+				    void *private_data)
+{
+	if (c->connected) {
+		return NT_STATUS_PIPE_CONNECTED;
+	}
+
+	c->incoming.ev = ev;
+	c->incoming.handler = handler;
+	c->incoming.private_data = private_data;
+
+	if (!cldap_recvfrom_setup(c)) {
+		ZERO_STRUCT(c->incoming);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	return NT_STATUS_OK;
+}
+
+struct cldap_reply_state {
+	struct tsocket_address *dest;
+	DATA_BLOB blob;
+};
+
+static void cldap_reply_state_destroy(struct tevent_req *subreq);
+
+/*
+  queue a cldap reply for send
+*/
+NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io)
+{
+	struct cldap_reply_state *state = NULL;
+	struct ldap_message *msg;
+	DATA_BLOB blob1, blob2;
+	NTSTATUS status;
+	struct tevent_req *subreq;
+
+	if (cldap->connected) {
+		return NT_STATUS_PIPE_CONNECTED;
+	}
+
+	if (cldap->incoming.ev == NULL) {
+		return NT_STATUS_INVALID_PIPE_STATE;
+	}
+
+	if (!io->dest) {
+		return NT_STATUS_INVALID_ADDRESS;
+	}
+
+	state = talloc(cldap, struct cldap_reply_state);
+	NT_STATUS_HAVE_NO_MEMORY(state);
+
+	state->dest = tsocket_address_copy(io->dest, state);
+	if (!state->dest) {
+		goto nomem;
+	}
+
+	msg = talloc(state, struct ldap_message);
+	if (!msg) {
+		goto nomem;
+	}
+
+	msg->messageid       = io->messageid;
+	msg->controls        = NULL;
+
+	if (io->response) {
+		msg->type = LDAP_TAG_SearchResultEntry;
+		msg->r.SearchResultEntry = *io->response;
+
+		if (!ldap_encode(msg, NULL, &blob1, state)) {
+			status = NT_STATUS_INVALID_PARAMETER;
+			goto failed;
+		}
+	} else {
+		blob1 = data_blob(NULL, 0);
+	}
+
+	msg->type = LDAP_TAG_SearchResultDone;
+	msg->r.SearchResultDone = *io->result;
+
+	if (!ldap_encode(msg, NULL, &blob2, state)) {
+		status = NT_STATUS_INVALID_PARAMETER;
+		goto failed;
+	}
+	talloc_free(msg);
+
+	state->blob = data_blob_talloc(state, NULL, blob1.length + blob2.length);
+	if (!state->blob.data) {
+		goto nomem;
+	}
+
+	memcpy(state->blob.data, blob1.data, blob1.length);
+	memcpy(state->blob.data+blob1.length, blob2.data, blob2.length);
+	data_blob_free(&blob1);
+	data_blob_free(&blob2);
+
+	subreq = tdgram_sendto_queue_send(state,
+					  cldap->incoming.ev,
+					  cldap->sock,
+					  cldap->send_queue,
+					  state->blob.data,
+					  state->blob.length,
+					  state->dest);
+	if (!subreq) {
+		goto nomem;
+	}
+	/* the callback will just free the state, as we don't need a result */
+	tevent_req_set_callback(subreq, cldap_reply_state_destroy, state);
+
+	return NT_STATUS_OK;
+
+nomem:
+	status = NT_STATUS_NO_MEMORY;
+failed:
+	talloc_free(state);
+	return status;
+}
+
+static void cldap_reply_state_destroy(struct tevent_req *subreq)
+{
+	struct cldap_reply_state *state = tevent_req_callback_data(subreq,
+					  struct cldap_reply_state);
+
+	/* we don't want to know the result here, we just free the state */
+	talloc_free(subreq);
+	talloc_free(state);
+}
+
+static int cldap_search_state_destructor(struct cldap_search_state *s)
+{
+	if (s->caller.cldap) {
+		if (s->message_id != -1) {
+			idr_remove(s->caller.cldap->searches.idr, s->message_id);
+			s->message_id = -1;
+		}
+		DLIST_REMOVE(s->caller.cldap->searches.list, s);
+		cldap_recvfrom_stop(s->caller.cldap);
+		ZERO_STRUCT(s->caller);
+	}
+
+	return 0;
+}
+
+static void cldap_search_state_queue_done(struct tevent_req *subreq);
+static void cldap_search_state_wakeup_done(struct tevent_req *subreq);
+
+/*
+  queue a cldap reply for send
+*/
+struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
+				     struct tevent_context *ev,
+				     struct cldap_socket *cldap,
+				     const struct cldap_search *io)
+{
+	struct tevent_req *req, *subreq;
+	struct cldap_search_state *state = NULL;
+	struct ldap_message *msg;
+	struct ldap_SearchRequest *search;
+	struct timeval now;
+	struct timeval end;
+	uint32_t i;
+	int ret;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct cldap_search_state);
+	if (!req) {
+		return NULL;
+	}
+	state->caller.ev = ev;
+	state->req = req;
+	state->caller.cldap = cldap;
+	state->message_id = -1;
+
+	talloc_set_destructor(state, cldap_search_state_destructor);
+
+	if (state->caller.cldap == NULL) {
+		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+		goto post;
+	}
+
+	if (io->in.dest_address) {
+		if (cldap->connected) {
+			tevent_req_nterror(req, NT_STATUS_PIPE_CONNECTED);
+			goto post;
+		}
+		ret = tsocket_address_inet_from_strings(state,
+						        "ip",
+						        io->in.dest_address,
+						        io->in.dest_port,
+							&state->request.dest);
+		if (ret != 0) {
+			tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+			goto post;
+		}
+	} else {
+		if (!cldap->connected) {
+			tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS);
+			goto post;
+		}
+		state->request.dest = NULL;
+	}
+
+	state->message_id = idr_get_new_random(cldap->searches.idr,
+					       state, UINT16_MAX);
+	if (state->message_id == -1) {
+		tevent_req_nterror(req, NT_STATUS_INSUFFICIENT_RESOURCES);
+		goto post;
+	}
+
+	msg = talloc(state, struct ldap_message);
+	if (tevent_req_nomem(msg, req)) {
+		goto post;
+	}
+
+	msg->messageid	= state->message_id;
+	msg->type	= LDAP_TAG_SearchRequest;
+	msg->controls	= NULL;
+	search = &msg->r.SearchRequest;
+
+	search->basedn		= "";
+	search->scope		= LDAP_SEARCH_SCOPE_BASE;
+	search->deref		= LDAP_DEREFERENCE_NEVER;
+	search->timelimit	= 0;
+	search->sizelimit	= 0;
+	search->attributesonly	= false;
+	search->num_attributes	= str_list_length(io->in.attributes);
+	search->attributes	= io->in.attributes;
+	search->tree		= ldb_parse_tree(msg, io->in.filter);
+	if (tevent_req_nomem(search->tree, req)) {
+		goto post;
+	}
+
+	if (!ldap_encode(msg, NULL, &state->request.blob, state)) {
+		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+		goto post;
+	}
+	talloc_free(msg);
+
+	state->request.idx = 0;
+	state->request.delay = 10*1000*1000;
+	state->request.count = 3;
+	if (io->in.timeout > 0) {
+		state->request.delay = io->in.timeout * 1000 * 1000;
+		state->request.count = io->in.retries + 1;
+	}
+
+	now = tevent_timeval_current();
+	end = now;
+	for (i = 0; i < state->request.count; i++) {
+		end = tevent_timeval_add(&end, state->request.delay / 1000000,
+					 state->request.delay % 1000000);
+	}
+
+	if (!tevent_req_set_endtime(req, state->caller.ev, end)) {
+		goto post;
+	}
+
+	subreq = tdgram_sendto_queue_send(state,
+					  state->caller.ev,
+					  state->caller.cldap->sock,
+					  state->caller.cldap->send_queue,
+					  state->request.blob.data,
+					  state->request.blob.length,
+					  state->request.dest);
+	if (tevent_req_nomem(subreq, req)) {
+		goto post;
+	}
+	tevent_req_set_callback(subreq, cldap_search_state_queue_done, req);
+
+	DLIST_ADD_END(cldap->searches.list, state);
+
+	return req;
+
+ post:
+	return tevent_req_post(req, state->caller.ev);
+}
+
+static void cldap_search_state_queue_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(subreq,
+				 struct tevent_req);
+	struct cldap_search_state *state = tevent_req_data(req,
+					   struct cldap_search_state);
+	ssize_t ret;
+	int sys_errno = 0;
+	struct timeval next;
+
+	ret = tdgram_sendto_queue_recv(subreq, &sys_errno);
+	talloc_free(subreq);
+	if (ret == -1) {
+		NTSTATUS status;
+		status = map_nt_error_from_unix_common(sys_errno);
+		DLIST_REMOVE(state->caller.cldap->searches.list, state);
+		ZERO_STRUCT(state->caller.cldap);
+		tevent_req_nterror(req, status);
+		return;
+	}
+
+	state->request.idx++;
+
+	/* wait for incoming traffic */
+	if (!cldap_recvfrom_setup(state->caller.cldap)) {
+		tevent_req_oom(req);
+		return;
+	}
+
+	if (state->request.idx > state->request.count) {
+		/* we just wait for the response or a timeout */
+		return;
+	}
+
+	next = tevent_timeval_current_ofs(state->request.delay / 1000000,
+					  state->request.delay % 1000000);
+	subreq = tevent_wakeup_send(state,
+				    state->caller.ev,
+				    next);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, cldap_search_state_wakeup_done, req);
+}
+
+static void cldap_search_state_wakeup_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(subreq,
+				 struct tevent_req);
+	struct cldap_search_state *state = tevent_req_data(req,
+					   struct cldap_search_state);
+	bool ok;
+
+	ok = tevent_wakeup_recv(subreq);
+	talloc_free(subreq);
+	if (!ok) {
+		tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+		return;
+	}
+
+	subreq = tdgram_sendto_queue_send(state,
+					  state->caller.ev,
+					  state->caller.cldap->sock,
+					  state->caller.cldap->send_queue,
+					  state->request.blob.data,
+					  state->request.blob.length,
+					  state->request.dest);
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, cldap_search_state_queue_done, req);
+}
+
+/*
+  receive a cldap reply
+*/
+NTSTATUS cldap_search_recv(struct tevent_req *req,
+			   TALLOC_CTX *mem_ctx,
+			   struct cldap_search *io)
+{
+	struct cldap_search_state *state = tevent_req_data(req,
+					   struct cldap_search_state);
+	struct ldap_message *ldap_msg;
+	NTSTATUS status;
+	struct ldap_request_limits limits = {
+		.max_search_size = MAX_SEARCH_REQUEST
+	};
+
+	if (tevent_req_is_nterror(req, &status)) {
+		goto failed;
+	}
+
+	ldap_msg = talloc(mem_ctx, struct ldap_message);
+	if (!ldap_msg) {
+		goto nomem;
+	}
+
+	status = ldap_decode(state->response.asn1, &limits, NULL, ldap_msg);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto failed;
+	}
+
+	ZERO_STRUCT(io->out);
+
+	/* the first possible form has a search result in first place */
+	if (ldap_msg->type == LDAP_TAG_SearchResultEntry) {
+		io->out.response = talloc(mem_ctx, struct ldap_SearchResEntry);
+		if (!io->out.response) {
+			goto nomem;
+		}
+		*io->out.response = ldap_msg->r.SearchResultEntry;
+
+		/* decode the 2nd part */
+		status = ldap_decode(
+			state->response.asn1, &limits, NULL, ldap_msg);
+		if (!NT_STATUS_IS_OK(status)) {
+			goto failed;
+		}
+	}
+
+	if (ldap_msg->type != LDAP_TAG_SearchResultDone) {
+		status = NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+		goto failed;
+	}
+
+	io->out.result = talloc(mem_ctx, struct ldap_Result);
+	if (!io->out.result) {
+		goto nomem;
+	}
+	*io->out.result = ldap_msg->r.SearchResultDone;
+
+	if (io->out.result->resultcode != LDAP_SUCCESS) {
+		status = NT_STATUS_LDAP(io->out.result->resultcode);
+		goto failed;
+	}
+
+	tevent_req_received(req);
+	return NT_STATUS_OK;
+
+nomem:
+	status = NT_STATUS_NO_MEMORY;
+failed:
+	tevent_req_received(req);
+	return status;
+}
+
+
+/*
+  synchronous cldap search
+*/
+NTSTATUS cldap_search(struct cldap_socket *cldap,
+		      TALLOC_CTX *mem_ctx,
+		      struct cldap_search *io)
+{
+	TALLOC_CTX *frame;
+	struct tevent_req *req;
+	struct tevent_context *ev;
+	NTSTATUS status;
+
+	if (cldap->searches.list) {
+		return NT_STATUS_PIPE_BUSY;
+	}
+
+	if (cldap->incoming.handler) {
+		return NT_STATUS_INVALID_PIPE_STATE;
+	}
+
+	frame = talloc_stackframe();
+
+	ev = samba_tevent_context_init(frame);
+	if (ev == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	req = cldap_search_send(mem_ctx, ev, cldap, io);
+	if (req == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	if (!tevent_req_poll(req, ev)) {
+		status = map_nt_error_from_unix_common(errno);
+		TALLOC_FREE(frame);
+		return status;
+	}
+
+	status = cldap_search_recv(req, mem_ctx, io);
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(frame);
+		return status;
+	}
+
+	TALLOC_FREE(frame);
+	return NT_STATUS_OK;
+}
+
+struct cldap_netlogon_state {
+	struct cldap_search search;
+};
+
+char *cldap_netlogon_create_filter(TALLOC_CTX *mem_ctx,
+				   const struct cldap_netlogon *io)
+{
+	char *filter;
+
+	filter = talloc_asprintf(mem_ctx, "(&(NtVer=%s)",
+				 ldap_encode_ndr_uint32(mem_ctx, io->in.version));
+	if (filter == NULL)
+		return NULL;
+
+	if (io->in.user) {
+		filter = talloc_asprintf_append_buffer(filter, "(User=%s)", io->in.user);
+		if (filter == NULL) {
+			return NULL;
+		}
+	}
+	if (io->in.host) {
+		filter = talloc_asprintf_append_buffer(filter, "(Host=%s)", io->in.host);
+		if (filter == NULL) {
+			return NULL;
+		}
+	}
+	if (io->in.realm) {
+		filter = talloc_asprintf_append_buffer(filter, "(DnsDomain=%s)", io->in.realm);
+		if (filter == NULL) {
+			return NULL;
+		}
+	}
+	if (io->in.acct_control != -1) {
+		filter = talloc_asprintf_append_buffer(filter, "(AAC=%s)", 
+						ldap_encode_ndr_uint32(mem_ctx, io->in.acct_control));
+		if (filter == NULL) {
+			return NULL;
+		}
+	}
+	if (io->in.domain_sid) {
+		struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, io->in.domain_sid);
+
+		filter = talloc_asprintf_append_buffer(filter, "(domainSid=%s)",
+						ldap_encode_ndr_dom_sid(mem_ctx, sid));
+		if (filter == NULL) {
+			return NULL;
+		}
+	}
+	if (io->in.domain_guid) {
+		struct GUID guid;
+		GUID_from_string(io->in.domain_guid, &guid);
+
+		filter = talloc_asprintf_append_buffer(filter, "(DomainGuid=%s)",
+						ldap_encode_ndr_GUID(mem_ctx, &guid));
+		if (filter == NULL) {
+			return NULL;
+		}
+	}
+	filter = talloc_asprintf_append_buffer(filter, ")");
+
+	return filter;
+}
+
+static void cldap_netlogon_state_done(struct tevent_req *subreq);
+/*
+  queue a cldap netlogon for send
+*/
+struct tevent_req *cldap_netlogon_send(TALLOC_CTX *mem_ctx,
+				       struct tevent_context *ev,
+				       struct cldap_socket *cldap,
+				       const struct cldap_netlogon *io)
+{
+	struct tevent_req *req, *subreq;
+	struct cldap_netlogon_state *state;
+	char *filter;
+	static const char * const attr[] = { "NetLogon", NULL };
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct cldap_netlogon_state);
+	if (!req) {
+		return NULL;
+	}
+
+	filter = cldap_netlogon_create_filter(state, io);
+	if (tevent_req_nomem(filter, req)) {
+		goto post;
+	}
+
+	if (io->in.dest_address) {
+		state->search.in.dest_address = talloc_strdup(state,
+						io->in.dest_address);
+		if (tevent_req_nomem(state->search.in.dest_address, req)) {
+			goto post;
+		}
+		state->search.in.dest_port = io->in.dest_port;
+	} else {
+		state->search.in.dest_address	= NULL;
+		state->search.in.dest_port	= 0;
+	}
+	state->search.in.filter		= filter;
+	state->search.in.attributes	= attr;
+	state->search.in.timeout	= 2;
+	state->search.in.retries	= 2;
+
+	subreq = cldap_search_send(state, ev, cldap, &state->search);
+	if (tevent_req_nomem(subreq, req)) {
+		goto post;
+	}
+	tevent_req_set_callback(subreq, cldap_netlogon_state_done, req);
+
+	return req;
+post:
+	return tevent_req_post(req, ev);
+}
+
+static void cldap_netlogon_state_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(subreq,
+				 struct tevent_req);
+	struct cldap_netlogon_state *state = tevent_req_data(req,
+					     struct cldap_netlogon_state);
+	NTSTATUS status;
+
+	status = cldap_search_recv(subreq, state, &state->search);
+	talloc_free(subreq);
+
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	tevent_req_done(req);
+}
+
+/*
+  receive a cldap netlogon reply
+*/
+NTSTATUS cldap_netlogon_recv(struct tevent_req *req,
+			     TALLOC_CTX *mem_ctx,
+			     struct cldap_netlogon *io)
+{
+	struct cldap_netlogon_state *state = tevent_req_data(req,
+					     struct cldap_netlogon_state);
+	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+	DATA_BLOB *data;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		goto failed;
+	}
+
+	if (state->search.out.response == NULL) {
+		status = NT_STATUS_NOT_FOUND;
+		goto failed;
+	}
+
+	if (state->search.out.response->num_attributes != 1 ||
+	    strcasecmp(state->search.out.response->attributes[0].name, "netlogon") != 0 ||
+	    state->search.out.response->attributes[0].num_values != 1 ||
+	    state->search.out.response->attributes[0].values->length < 2) {
+		status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+		goto failed;
+	}
+	data = state->search.out.response->attributes[0].values;
+
+	status = pull_netlogon_samlogon_response(data, mem_ctx,
+						 &io->out.netlogon);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto failed;
+	}
+
+	if (io->in.map_response) {
+		map_netlogon_samlogon_response(&io->out.netlogon);
+	}
+
+	status =  NT_STATUS_OK;
+failed:
+	tevent_req_received(req);
+	return status;
+}
+
+/*
+  sync cldap netlogon search
+*/
+NTSTATUS cldap_netlogon(struct cldap_socket *cldap,
+			TALLOC_CTX *mem_ctx,
+			struct cldap_netlogon *io)
+{
+	TALLOC_CTX *frame;
+	struct tevent_req *req;
+	struct tevent_context *ev;
+	NTSTATUS status;
+
+	if (cldap->searches.list) {
+		return NT_STATUS_PIPE_BUSY;
+	}
+
+	if (cldap->incoming.handler) {
+		return NT_STATUS_INVALID_PIPE_STATE;
+	}
+
+	frame = talloc_stackframe();
+
+	ev = samba_tevent_context_init(frame);
+	if (ev == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	req = cldap_netlogon_send(mem_ctx, ev, cldap, io);
+	if (req == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	if (!tevent_req_poll(req, ev)) {
+		status = map_nt_error_from_unix_common(errno);
+		TALLOC_FREE(frame);
+		return status;
+	}
+
+	status = cldap_netlogon_recv(req, mem_ctx, io);
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(frame);
+		return status;
+	}
+
+	TALLOC_FREE(frame);
+	return NT_STATUS_OK;
+}
+
+
+/*
+  send an empty reply (used on any error, so the client doesn't keep waiting
+  or send the bad request again)
+*/
+NTSTATUS cldap_empty_reply(struct cldap_socket *cldap,
+			   uint32_t message_id,
+			   struct tsocket_address *dest)
+{
+	NTSTATUS status;
+	struct cldap_reply reply;
+	struct ldap_Result result;
+
+	reply.messageid    = message_id;
+	reply.dest         = dest;
+	reply.response     = NULL;
+	reply.result       = &result;
+
+	ZERO_STRUCT(result);
+
+	status = cldap_reply_send(cldap, &reply);
+
+	return status;
+}
+
+/*
+  send an error reply (used on any error, so the client doesn't keep waiting
+  or send the bad request again)
+*/
+NTSTATUS cldap_error_reply(struct cldap_socket *cldap,
+			   uint32_t message_id,
+			   struct tsocket_address *dest,
+			   int resultcode,
+			   const char *errormessage)
+{
+	NTSTATUS status;
+	struct cldap_reply reply;
+	struct ldap_Result result;
+
+	reply.messageid    = message_id;
+	reply.dest         = dest;
+	reply.response     = NULL;
+	reply.result       = &result;
+
+	ZERO_STRUCT(result);
+	result.resultcode	= resultcode;
+	result.errormessage	= errormessage;
+
+	status = cldap_reply_send(cldap, &reply);
+
+	return status;
+}
+
+
+/*
+  send a netlogon reply 
+*/
+NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap,
+			      uint32_t message_id,
+			      struct tsocket_address *dest,
+			      uint32_t version,
+			      struct netlogon_samlogon_response *netlogon)
+{
+	NTSTATUS status;
+	struct cldap_reply reply;
+	struct ldap_SearchResEntry response;
+	struct ldap_Result result;
+	TALLOC_CTX *tmp_ctx = talloc_new(cldap);
+	DATA_BLOB blob;
+
+	status = push_netlogon_samlogon_response(&blob, tmp_ctx,
+						 netlogon);
+	if (!NT_STATUS_IS_OK(status)) {
+		talloc_free(tmp_ctx);
+		return status;
+	}
+	reply.messageid    = message_id;
+	reply.dest         = dest;
+	reply.response     = &response;
+	reply.result       = &result;
+
+	ZERO_STRUCT(result);
+
+	response.dn = "";
+	response.num_attributes = 1;
+	response.attributes = talloc(tmp_ctx, struct ldb_message_element);
+	NT_STATUS_HAVE_NO_MEMORY(response.attributes);
+	response.attributes->name = "netlogon";
+	response.attributes->num_values = 1;
+	response.attributes->values = &blob;
+
+	status = cldap_reply_send(cldap, &reply);
+
+	talloc_free(tmp_ctx);
+
+	return status;
+}
+
diff --git a/libcli/cldap/cldap.h b/libcli/cldap/cldap.h
new file mode 100644
index 0000000..cd76fee
--- /dev/null
+++ b/libcli/cldap/cldap.h
@@ -0,0 +1,134 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   a async CLDAP library
+
+   Copyright (C) Andrew Tridgell 2005
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "../libcli/netlogon/netlogon.h"
+
+struct ldap_message;
+struct tsocket_address;
+struct cldap_socket;
+
+struct cldap_incoming {
+	int recv_errno;
+	uint8_t *buf;
+	size_t len;
+	struct tsocket_address *src;
+	struct ldap_message *ldap_msg;
+};
+
+/*
+ a general cldap search request
+*/
+struct cldap_search {
+	struct {
+		const char *dest_address;
+		uint16_t dest_port;
+		const char *filter;
+		const char * const *attributes;
+		int timeout;
+		int retries;
+	} in;
+	struct {
+		struct ldap_SearchResEntry *response;
+		struct ldap_Result         *result;
+	} out;
+};
+
+NTSTATUS cldap_socket_init(TALLOC_CTX *mem_ctx,
+			   const struct tsocket_address *local_addr,
+			   const struct tsocket_address *remote_addr,
+			   struct cldap_socket **_cldap);
+
+NTSTATUS cldap_set_incoming_handler(struct cldap_socket *cldap,
+				    struct tevent_context *ev,
+				    void (*handler)(struct cldap_socket *,
+						    void *private_data,
+						    struct cldap_incoming *),
+				    void *private_data);
+struct tevent_req *cldap_search_send(TALLOC_CTX *mem_ctx,
+				     struct tevent_context *ev,
+				     struct cldap_socket *cldap,
+				     const struct cldap_search *io);
+NTSTATUS cldap_search_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+			   struct cldap_search *io);
+NTSTATUS cldap_search(struct cldap_socket *cldap, TALLOC_CTX *mem_ctx,
+		      struct cldap_search *io);
+
+/*
+  a general cldap reply
+*/
+struct cldap_reply {
+	uint32_t messageid;
+	struct tsocket_address *dest;
+	struct ldap_SearchResEntry *response;
+	struct ldap_Result         *result;
+};
+
+NTSTATUS cldap_reply_send(struct cldap_socket *cldap, struct cldap_reply *io);
+
+NTSTATUS cldap_empty_reply(struct cldap_socket *cldap,
+			   uint32_t message_id,
+			   struct tsocket_address *dst);
+NTSTATUS cldap_error_reply(struct cldap_socket *cldap,
+			   uint32_t message_id,
+			   struct tsocket_address *dst,
+			   int resultcode,
+			   const char *errormessage);
+
+/*
+  a netlogon cldap request  
+*/
+struct cldap_netlogon {
+	struct {
+		const char *dest_address;
+		uint16_t dest_port;
+		const char *realm;
+		const char *host;
+		const char *user;
+		const char *domain_guid;
+		const char *domain_sid;
+		int acct_control;
+		uint32_t version;
+		bool map_response;
+	} in;
+	struct {
+		struct netlogon_samlogon_response netlogon;
+	} out;
+};
+
+struct tevent_req *cldap_netlogon_send(TALLOC_CTX *mem_ctx,
+				       struct tevent_context *ev,
+				       struct cldap_socket *cldap,
+				       const struct cldap_netlogon *io);
+NTSTATUS cldap_netlogon_recv(struct tevent_req *req,
+			     TALLOC_CTX *mem_ctx,
+			     struct cldap_netlogon *io);
+NTSTATUS cldap_netlogon(struct cldap_socket *cldap,
+			TALLOC_CTX *mem_ctx,
+			struct cldap_netlogon *io);
+char *cldap_netlogon_create_filter(TALLOC_CTX *mem_ctx,
+				   const struct cldap_netlogon *io);
+
+NTSTATUS cldap_netlogon_reply(struct cldap_socket *cldap,
+			      uint32_t message_id,
+			      struct tsocket_address *dst,
+			      uint32_t version,
+			      struct netlogon_samlogon_response *netlogon);
+
diff --git a/libcli/cldap/wscript_build b/libcli/cldap/wscript_build
new file mode 100644
index 0000000..889c0af
--- /dev/null
+++ b/libcli/cldap/wscript_build
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+
+bld.SAMBA_LIBRARY('cli_cldap',
+                  source='cldap.c',
+                  public_deps='cli-ldap',
+                  deps='LIBTSOCKET samba-util tevent-util ldb LIBCLI_NETLOGON',
+                  private_library=True
+                  )
+
-- 
cgit v1.2.3