summaryrefslogtreecommitdiff
path: root/libcli/cldap/cldap.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcli/cldap/cldap.c')
-rw-r--r--libcli/cldap/cldap.c1125
1 files changed, 1125 insertions, 0 deletions
diff --git a/libcli/cldap/cldap.c b/libcli/cldap/cldap.c
new file mode 100644
index 0000000000..561ae8037c
--- /dev/null
+++ b/libcli/cldap/cldap.c
@@ -0,0 +1,1125 @@
+/*
+ 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 tsocket_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;
+
+ /*
+ * we allow sync requests only, if the caller
+ * did not pass an event context to cldap_socket_init()
+ */
+ struct {
+ bool allow_poll;
+ struct tevent_context *ctx;
+ } event;
+
+ /* 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 {
+ 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 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;
+};
+
+static int cldap_socket_destructor(struct cldap_socket *c)
+{
+ tsocket_disconnect(c->sock);
+
+ 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)
+{
+ if (c->recv_subreq) {
+ return true;
+ }
+
+ if (!c->searches.list && !c->incoming.handler) {
+ return true;
+ }
+
+ c->recv_subreq = tsocket_recvfrom_send(c->sock, c);
+ 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 void 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;
+
+ c->recv_subreq = NULL;
+
+ in = talloc_zero(c, struct cldap_incoming);
+ if (!in) {
+ goto nomem;
+ }
+
+ ret = tsocket_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' */
+ cldap_socket_recv_dgram(c, in);
+ in = NULL;
+
+ if (!cldap_recvfrom_setup(c)) {
+ goto nomem;
+ }
+
+ return;
+
+nomem:
+ talloc_free(subreq);
+ talloc_free(in);
+ /*TODO: call a dead socket handler */
+ return;
+}
+
+/*
+ handle recv events on a cldap socket
+*/
+static void cldap_socket_recv_dgram(struct cldap_socket *c,
+ struct cldap_incoming *in)
+{
+ DATA_BLOB blob;
+ struct asn1_data *asn1;
+ void *p;
+ struct cldap_search_state *search;
+ NTSTATUS status;
+
+ if (in->recv_errno != 0) {
+ goto error;
+ }
+
+ blob = data_blob_const(in->buf, in->len);
+
+ asn1 = asn1_init(in);
+ if (!asn1) {
+ goto nomem;
+ }
+
+ if (!asn1_load(asn1, blob)) {
+ goto nomem;
+ }
+
+ 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, 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) {
+ goto done;
+ }
+
+ /* this function should free or steal 'in' */
+ c->incoming.handler(c, c->incoming.private_data, in);
+ return;
+ }
+
+ search = talloc_get_type(p, struct cldap_search_state);
+ search->response.in = talloc_move(search, &in);
+ search->response.asn1 = asn1;
+ search->response.asn1->ofs = 0;
+
+ tevent_req_done(search->req);
+ goto done;
+
+nomem:
+ in->recv_errno = ENOMEM;
+error:
+ status = map_nt_error_from_unix(in->recv_errno);
+nterror:
+ /* in connected mode the first pending search gets the error */
+ if (!c->connected) {
+ /* otherwise we just ignore the error */
+ goto done;
+ }
+ if (!c->searches.list) {
+ goto done;
+ }
+ tevent_req_nterror(c->searches.list->req, status);
+done:
+ talloc_free(in);
+}
+
+/*
+ initialise a cldap_sock
+*/
+NTSTATUS cldap_socket_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ 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;
+
+ c = talloc_zero(mem_ctx, struct cldap_socket);
+ if (!c) {
+ goto nomem;
+ }
+
+ if (!ev) {
+ ev = tevent_context_init(c);
+ if (!ev) {
+ goto nomem;
+ }
+ c->event.allow_poll = true;
+ }
+ c->event.ctx = ev;
+
+ if (!local_addr) {
+ ret = tsocket_address_inet_from_strings(c, "ipv4",
+ NULL, 0,
+ &any);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
+ goto nterror;
+ }
+ local_addr = any;
+ }
+
+ c->searches.idr = idr_init(c);
+ if (!c->searches.idr) {
+ goto nomem;
+ }
+
+ ret = tsocket_address_create_socket(local_addr,
+ TSOCKET_TYPE_DGRAM,
+ c, &c->sock);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
+ goto nterror;
+ }
+ talloc_free(any);
+
+ tsocket_set_event_context(c->sock, c->event.ctx);
+
+ if (remote_addr) {
+ ret = tsocket_connect(c->sock, remote_addr);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
+ goto nterror;
+ }
+ 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,
+ void (*handler)(struct cldap_socket *,
+ void *private_data,
+ struct cldap_incoming *),
+ void *private_data)
+{
+ if (c->connected) {
+ return NT_STATUS_PIPE_CONNECTED;
+ }
+
+ /* if sync requests are allowed, we don't allow an incoming handler */
+ if (c->event.allow_poll) {
+ return NT_STATUS_INVALID_PIPE_STATE;
+ }
+
+ 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 *req);
+
+/*
+ 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 *req;
+
+ if (cldap->connected) {
+ return NT_STATUS_PIPE_CONNECTED;
+ }
+
+ 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);
+
+ req = tsocket_sendto_queue_send(state,
+ cldap->sock,
+ cldap->send_queue,
+ state->blob.data,
+ state->blob.length,
+ state->dest);
+ if (!req) {
+ goto nomem;
+ }
+ /* the callback will just free the state, as we don't need a result */
+ tevent_req_set_callback(req, 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 *req)
+{
+ struct cldap_reply_state *state = tevent_req_callback_data(req,
+ struct cldap_reply_state);
+
+ /* we don't want to know the result here, we just free the state */
+ talloc_free(req);
+ talloc_free(state);
+}
+
+static int cldap_search_state_destructor(struct cldap_search_state *s)
+{
+ if (s->caller.cldap) {
+ 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 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->req = req;
+ state->caller.cldap = cldap;
+
+ 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,
+ "ipv4",
+ 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, 0, state->request.delay);
+ }
+
+ if (!tevent_req_set_endtime(req, state->caller.cldap->event.ctx, end)) {
+ tevent_req_nomem(NULL, req);
+ goto post;
+ }
+
+ subreq = tsocket_sendto_queue_send(state,
+ 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, struct cldap_search_state *);
+ talloc_set_destructor(state, cldap_search_state_destructor);
+
+ return req;
+
+ post:
+ return tevent_req_post(req, cldap->event.ctx);
+}
+
+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 = tsocket_sendto_queue_recv(subreq, &sys_errno);
+ talloc_free(subreq);
+ if (ret == -1) {
+ NTSTATUS status;
+ status = map_nt_error_from_unix(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_nomem(NULL, req);
+ return;
+ }
+
+ if (state->request.idx > state->request.count) {
+ /* we just wait for the response or a timeout */
+ return;
+ }
+
+ next = tevent_timeval_current_ofs(0, state->request.delay);
+ subreq = tevent_wakeup_send(state,
+ state->caller.cldap->event.ctx,
+ 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 = tsocket_sendto_queue_send(state,
+ 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;
+
+ 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, 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, 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)
+{
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ if (!cldap->event.allow_poll) {
+ return NT_STATUS_INVALID_PIPE_STATE;
+ }
+
+ if (cldap->searches.list) {
+ return NT_STATUS_PIPE_BUSY;
+ }
+
+ req = cldap_search_send(mem_ctx, cldap, io);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ if (!tevent_req_poll(req, cldap->event.ctx)) {
+ talloc_free(req);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = cldap_search_recv(req, mem_ctx, io);
+ talloc_free(req);
+
+ return status;
+}
+
+struct cldap_netlogon_state {
+ struct cldap_search search;
+};
+
+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 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 = talloc_asprintf(state, "(&(NtVer=%s)",
+ ldap_encode_ndr_uint32(state, io->in.version));
+ if (tevent_req_nomem(filter, req)) {
+ goto post;
+ }
+ if (io->in.user) {
+ filter = talloc_asprintf_append_buffer(filter, "(User=%s)", io->in.user);
+ if (tevent_req_nomem(filter, req)) {
+ goto post;
+ }
+ }
+ if (io->in.host) {
+ filter = talloc_asprintf_append_buffer(filter, "(Host=%s)", io->in.host);
+ if (tevent_req_nomem(filter, req)) {
+ goto post;
+ }
+ }
+ if (io->in.realm) {
+ filter = talloc_asprintf_append_buffer(filter, "(DnsDomain=%s)", io->in.realm);
+ if (tevent_req_nomem(filter, req)) {
+ goto post;
+ }
+ }
+ if (io->in.acct_control != -1) {
+ filter = talloc_asprintf_append_buffer(filter, "(AAC=%s)",
+ ldap_encode_ndr_uint32(state, io->in.acct_control));
+ if (tevent_req_nomem(filter, req)) {
+ goto post;
+ }
+ }
+ if (io->in.domain_sid) {
+ struct dom_sid *sid = dom_sid_parse_talloc(state, io->in.domain_sid);
+ if (tevent_req_nomem(sid, req)) {
+ goto post;
+ }
+ filter = talloc_asprintf_append_buffer(filter, "(domainSid=%s)",
+ ldap_encode_ndr_dom_sid(state, sid));
+ if (tevent_req_nomem(filter, req)) {
+ goto post;
+ }
+ }
+ if (io->in.domain_guid) {
+ struct GUID guid;
+ NTSTATUS status;
+ status = GUID_from_string(io->in.domain_guid, &guid);
+ if (tevent_req_nterror(req, status)) {
+ goto post;
+ }
+ filter = talloc_asprintf_append_buffer(filter, "(DomainGuid=%s)",
+ ldap_encode_ndr_GUID(state, &guid));
+ if (tevent_req_nomem(filter, req)) {
+ goto post;
+ }
+ }
+ filter = talloc_asprintf_append_buffer(filter, ")");
+ 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, 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, cldap->event.ctx);
+}
+
+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,
+ struct smb_iconv_convenience *iconv_convenience,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_netlogon *io)
+{
+ struct cldap_netlogon_state *state = tevent_req_data(req,
+ struct cldap_netlogon_state);
+ NTSTATUS status;
+ 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,
+ iconv_convenience,
+ &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,
+ struct smb_iconv_convenience *iconv_convenience,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_netlogon *io)
+{
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ if (!cldap->event.allow_poll) {
+ return NT_STATUS_INVALID_PIPE_STATE;
+ }
+
+ if (cldap->searches.list) {
+ return NT_STATUS_PIPE_BUSY;
+ }
+
+ req = cldap_netlogon_send(mem_ctx, cldap, io);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ if (!tevent_req_poll(req, cldap->event.ctx)) {
+ talloc_free(req);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = cldap_netlogon_recv(req, iconv_convenience, mem_ctx, io);
+ talloc_free(req);
+
+ return status;
+}
+
+
+/*
+ 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,
+ struct smb_iconv_convenience *iconv_convenience,
+ 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,
+ iconv_convenience,
+ 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;
+}
+