• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src/router/samba-3.5.8/source4/libcli/ldap/
1/*
2   Unix SMB/CIFS mplementation.
3   LDAP protocol helper functions for SAMBA
4
5   Copyright (C) Andrew Tridgell  2004
6   Copyright (C) Volker Lendecke 2004
7   Copyright (C) Stefan Metzmacher 2004
8   Copyright (C) Simo Sorce 2004
9
10   This program is free software; you can redistribute it and/or modify
11   it under the terms of the GNU General Public License as published by
12   the Free Software Foundation; either version 3 of the License, or
13   (at your option) any later version.
14
15   This program is distributed in the hope that it will be useful,
16   but WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18   GNU General Public License for more details.
19
20   You should have received a copy of the GNU General Public License
21   along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
23*/
24
25#include "includes.h"
26#include <tevent.h>
27#include "lib/socket/socket.h"
28#include "../lib/util/asn1.h"
29#include "../lib/util/dlinklist.h"
30#include "libcli/ldap/ldap.h"
31#include "libcli/ldap/ldap_proto.h"
32#include "libcli/ldap/ldap_client.h"
33#include "libcli/composite/composite.h"
34#include "lib/stream/packet.h"
35#include "lib/tls/tls.h"
36#include "auth/gensec/gensec.h"
37#include "system/time.h"
38#include "param/param.h"
39#include "libcli/resolve/resolve.h"
40
41/**
42  create a new ldap_connection stucture. The event context is optional
43*/
44_PUBLIC_ struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx,
45					     struct loadparm_context *lp_ctx,
46					     struct tevent_context *ev)
47{
48	struct ldap_connection *conn;
49
50	if (ev == NULL) {
51		return NULL;
52	}
53
54	conn = talloc_zero(mem_ctx, struct ldap_connection);
55	if (conn == NULL) {
56		return NULL;
57	}
58
59	conn->next_messageid  = 1;
60	conn->event.event_ctx = ev;
61
62	conn->lp_ctx = lp_ctx;
63
64	/* set a reasonable request timeout */
65	conn->timeout = 60;
66
67	/* explicitly avoid reconnections by default */
68	conn->reconnect.max_retries = 0;
69
70	return conn;
71}
72
73/*
74  the connection is dead
75*/
76static void ldap_connection_dead(struct ldap_connection *conn)
77{
78	struct ldap_request *req;
79
80	talloc_free(conn->sock);  /* this will also free event.fde */
81	talloc_free(conn->packet);
82	conn->sock = NULL;
83	conn->event.fde = NULL;
84	conn->packet = NULL;
85
86	/* return an error for any pending request ... */
87	while (conn->pending) {
88		req = conn->pending;
89		DLIST_REMOVE(req->conn->pending, req);
90		req->state = LDAP_REQUEST_DONE;
91		req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
92		if (req->async.fn) {
93			req->async.fn(req);
94		}
95	}
96}
97
98static void ldap_reconnect(struct ldap_connection *conn);
99
100/*
101  handle packet errors
102*/
103static void ldap_error_handler(void *private_data, NTSTATUS status)
104{
105	struct ldap_connection *conn = talloc_get_type(private_data,
106						       struct ldap_connection);
107	ldap_connection_dead(conn);
108
109	/* but try to reconnect so that the ldb client can go on */
110	ldap_reconnect(conn);
111}
112
113
114/*
115  match up with a pending message, adding to the replies list
116*/
117static void ldap_match_message(struct ldap_connection *conn, struct ldap_message *msg)
118{
119	struct ldap_request *req;
120	int i;
121
122	for (req=conn->pending; req; req=req->next) {
123		if (req->messageid == msg->messageid) break;
124	}
125	/* match a zero message id to the last request sent.
126	   It seems that servers send 0 if unable to parse */
127	if (req == NULL && msg->messageid == 0) {
128		req = conn->pending;
129	}
130	if (req == NULL) {
131		DEBUG(0,("ldap: no matching message id for %u\n",
132			 msg->messageid));
133		talloc_free(msg);
134		return;
135	}
136
137	/* Check for undecoded critical extensions */
138	for (i=0; msg->controls && msg->controls[i]; i++) {
139		if (!msg->controls_decoded[i] &&
140		    msg->controls[i]->critical) {
141			req->status = NT_STATUS_LDAP(LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
142			req->state = LDAP_REQUEST_DONE;
143			DLIST_REMOVE(conn->pending, req);
144			if (req->async.fn) {
145				req->async.fn(req);
146			}
147			return;
148		}
149	}
150
151	/* add to the list of replies received */
152	talloc_steal(req, msg);
153	req->replies = talloc_realloc(req, req->replies,
154				      struct ldap_message *, req->num_replies+1);
155	if (req->replies == NULL) {
156		req->status = NT_STATUS_NO_MEMORY;
157		req->state = LDAP_REQUEST_DONE;
158		DLIST_REMOVE(conn->pending, req);
159		if (req->async.fn) {
160			req->async.fn(req);
161		}
162		return;
163	}
164
165	req->replies[req->num_replies] = talloc_steal(req->replies, msg);
166	req->num_replies++;
167
168	if (msg->type != LDAP_TAG_SearchResultEntry &&
169	    msg->type != LDAP_TAG_SearchResultReference) {
170		/* currently only search results expect multiple
171		   replies */
172		req->state = LDAP_REQUEST_DONE;
173		DLIST_REMOVE(conn->pending, req);
174	}
175
176	if (req->async.fn) {
177		req->async.fn(req);
178	}
179}
180
181
182/*
183  decode/process LDAP data
184*/
185static NTSTATUS ldap_recv_handler(void *private_data, DATA_BLOB blob)
186{
187	NTSTATUS status;
188	struct ldap_connection *conn = talloc_get_type(private_data,
189						       struct ldap_connection);
190	struct ldap_message *msg = talloc(conn, struct ldap_message);
191	struct asn1_data *asn1 = asn1_init(conn);
192
193	if (asn1 == NULL || msg == NULL) {
194		return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
195	}
196
197	if (!asn1_load(asn1, blob)) {
198		talloc_free(msg);
199		talloc_free(asn1);
200		return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
201	}
202
203	status = ldap_decode(asn1, samba_ldap_control_handlers(), msg);
204	if (!NT_STATUS_IS_OK(status)) {
205		asn1_free(asn1);
206		return status;
207	}
208
209	ldap_match_message(conn, msg);
210
211	data_blob_free(&blob);
212	asn1_free(asn1);
213	return NT_STATUS_OK;
214}
215
216/* Handle read events, from the GENSEC socket callback, or real events */
217void ldap_read_io_handler(void *private_data, uint16_t flags)
218{
219	struct ldap_connection *conn = talloc_get_type(private_data,
220						       struct ldap_connection);
221	packet_recv(conn->packet);
222}
223
224/*
225  handle ldap socket events
226*/
227static void ldap_io_handler(struct tevent_context *ev, struct tevent_fd *fde,
228			    uint16_t flags, void *private_data)
229{
230	struct ldap_connection *conn = talloc_get_type(private_data,
231						       struct ldap_connection);
232	if (flags & TEVENT_FD_WRITE) {
233		packet_queue_run(conn->packet);
234		if (!tls_enabled(conn->sock)) return;
235	}
236	if (flags & TEVENT_FD_READ) {
237		ldap_read_io_handler(private_data, flags);
238	}
239}
240
241/*
242  parse a ldap URL
243*/
244static NTSTATUS ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url,
245				     char **host, uint16_t *port, bool *ldaps)
246{
247	int tmp_port = 0;
248	char protocol[11];
249	char tmp_host[1025];
250	int ret;
251
252	/* Paranoia check */
253	SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254);
254
255	ret = sscanf(url, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port);
256	if (ret < 2) {
257		return NT_STATUS_INVALID_PARAMETER;
258	}
259
260	if (strequal(protocol, "ldap")) {
261		*port = 389;
262		*ldaps = false;
263	} else if (strequal(protocol, "ldaps")) {
264		*port = 636;
265		*ldaps = true;
266	} else {
267		DEBUG(0, ("unrecognised ldap protocol (%s)!\n", protocol));
268		return NT_STATUS_PROTOCOL_UNREACHABLE;
269	}
270
271	if (tmp_port != 0)
272		*port = tmp_port;
273
274	*host = talloc_strdup(mem_ctx, tmp_host);
275	NT_STATUS_HAVE_NO_MEMORY(*host);
276
277	return NT_STATUS_OK;
278}
279
280/*
281  connect to a ldap server
282*/
283
284struct ldap_connect_state {
285	struct composite_context *ctx;
286	struct ldap_connection *conn;
287};
288
289static void ldap_connect_recv_unix_conn(struct composite_context *ctx);
290static void ldap_connect_recv_tcp_conn(struct composite_context *ctx);
291
292_PUBLIC_ struct composite_context *ldap_connect_send(struct ldap_connection *conn,
293					    const char *url)
294{
295	struct composite_context *result, *ctx;
296	struct ldap_connect_state *state;
297	char protocol[11];
298	int ret;
299
300	result = talloc_zero(conn, struct composite_context);
301	if (result == NULL) goto failed;
302	result->state = COMPOSITE_STATE_IN_PROGRESS;
303	result->async.fn = NULL;
304	result->event_ctx = conn->event.event_ctx;
305
306	state = talloc(result, struct ldap_connect_state);
307	if (state == NULL) goto failed;
308	state->ctx = result;
309	result->private_data = state;
310
311	state->conn = conn;
312
313	if (conn->reconnect.url == NULL) {
314		conn->reconnect.url = talloc_strdup(conn, url);
315		if (conn->reconnect.url == NULL) goto failed;
316	}
317
318	/* Paranoia check */
319	SMB_ASSERT(sizeof(protocol)>10);
320
321	ret = sscanf(url, "%10[^:]://", protocol);
322	if (ret < 1) {
323		return NULL;
324	}
325
326	if (strequal(protocol, "ldapi")) {
327		struct socket_address *unix_addr;
328		char path[1025];
329
330		NTSTATUS status = socket_create("unix", SOCKET_TYPE_STREAM, &conn->sock, 0);
331		if (!NT_STATUS_IS_OK(status)) {
332			return NULL;
333		}
334		talloc_steal(conn, conn->sock);
335		SMB_ASSERT(sizeof(protocol)>10);
336		SMB_ASSERT(sizeof(path)>1024);
337
338		/* LDAPI connections are to localhost, so give the
339		 * local host name as the target for gensec's
340		 * DIGEST-MD5 mechanism */
341		conn->host = talloc_asprintf(conn, "%s.%s", lp_netbios_name(conn->lp_ctx),  lp_realm(conn->lp_ctx));
342		if (composite_nomem(conn->host, state->ctx)) {
343			return result;
344		}
345
346		/* The %c specifier doesn't null terminate :-( */
347		ZERO_STRUCT(path);
348		ret = sscanf(url, "%10[^:]://%1025c", protocol, path);
349		if (ret < 2) {
350			composite_error(state->ctx, NT_STATUS_INVALID_PARAMETER);
351			return result;
352		}
353
354		rfc1738_unescape(path);
355
356		unix_addr = socket_address_from_strings(conn, conn->sock->backend_name,
357							path, 0);
358		if (!unix_addr) {
359			return NULL;
360		}
361
362		ctx = socket_connect_send(conn->sock, NULL, unix_addr,
363					  0, conn->event.event_ctx);
364		ctx->async.fn = ldap_connect_recv_unix_conn;
365		ctx->async.private_data = state;
366		return result;
367	} else {
368		NTSTATUS status = ldap_parse_basic_url(conn, url, &conn->host,
369							  &conn->port, &conn->ldaps);
370		if (!NT_STATUS_IS_OK(state->ctx->status)) {
371			composite_error(state->ctx, status);
372			return result;
373		}
374
375		ctx = socket_connect_multi_send(state, conn->host, 1, &conn->port,
376						lp_resolve_context(conn->lp_ctx), conn->event.event_ctx);
377		if (ctx == NULL) goto failed;
378
379		ctx->async.fn = ldap_connect_recv_tcp_conn;
380		ctx->async.private_data = state;
381		return result;
382	}
383 failed:
384	talloc_free(result);
385	return NULL;
386}
387
388static void ldap_connect_got_sock(struct composite_context *ctx,
389				  struct ldap_connection *conn)
390{
391	/* setup a handler for events on this socket */
392	conn->event.fde = tevent_add_fd(conn->event.event_ctx, conn->sock,
393					socket_get_fd(conn->sock),
394					TEVENT_FD_READ, ldap_io_handler, conn);
395	if (conn->event.fde == NULL) {
396		composite_error(ctx, NT_STATUS_INTERNAL_ERROR);
397		return;
398	}
399
400	tevent_fd_set_close_fn(conn->event.fde, socket_tevent_fd_close_fn);
401	socket_set_flags(conn->sock, SOCKET_FLAG_NOCLOSE);
402
403	talloc_steal(conn, conn->sock);
404	if (conn->ldaps) {
405		struct socket_context *tls_socket;
406		struct socket_context *tmp_socket;
407		char *cafile = lp_tls_cafile(conn->sock, conn->lp_ctx);
408
409		if (!cafile || !*cafile) {
410			talloc_free(conn->sock);
411			return;
412		}
413
414		tls_socket = tls_init_client(conn->sock, conn->event.fde, cafile);
415		talloc_free(cafile);
416
417		if (tls_socket == NULL) {
418			talloc_free(conn->sock);
419			return;
420		}
421
422		conn->sock = talloc_steal(conn, tls_socket);
423	}
424
425	conn->packet = packet_init(conn);
426	if (conn->packet == NULL) {
427		talloc_free(conn->sock);
428		return;
429	}
430
431	packet_set_private(conn->packet, conn);
432	packet_set_socket(conn->packet, conn->sock);
433	packet_set_callback(conn->packet, ldap_recv_handler);
434	packet_set_full_request(conn->packet, ldap_full_packet);
435	packet_set_error_handler(conn->packet, ldap_error_handler);
436	packet_set_event_context(conn->packet, conn->event.event_ctx);
437	packet_set_fde(conn->packet, conn->event.fde);
438/*	packet_set_serialise(conn->packet); */
439
440	if (conn->ldaps) {
441		packet_set_unreliable_select(conn->packet);
442	}
443
444	composite_done(ctx);
445}
446
447static void ldap_connect_recv_tcp_conn(struct composite_context *ctx)
448{
449	struct ldap_connect_state *state =
450		talloc_get_type(ctx->async.private_data,
451				struct ldap_connect_state);
452	struct ldap_connection *conn = state->conn;
453	uint16_t port;
454	NTSTATUS status = socket_connect_multi_recv(ctx, state, &conn->sock,
455						       &port);
456	if (!NT_STATUS_IS_OK(status)) {
457		composite_error(state->ctx, status);
458		return;
459	}
460
461	ldap_connect_got_sock(state->ctx, conn);
462}
463
464static void ldap_connect_recv_unix_conn(struct composite_context *ctx)
465{
466	struct ldap_connect_state *state =
467		talloc_get_type(ctx->async.private_data,
468				struct ldap_connect_state);
469	struct ldap_connection *conn = state->conn;
470
471	NTSTATUS status = socket_connect_recv(ctx);
472
473	if (!NT_STATUS_IS_OK(state->ctx->status)) {
474		composite_error(state->ctx, status);
475		return;
476	}
477
478	ldap_connect_got_sock(state->ctx, conn);
479}
480
481_PUBLIC_ NTSTATUS ldap_connect_recv(struct composite_context *ctx)
482{
483	NTSTATUS status = composite_wait(ctx);
484	talloc_free(ctx);
485	return status;
486}
487
488_PUBLIC_ NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url)
489{
490	struct composite_context *ctx = ldap_connect_send(conn, url);
491	return ldap_connect_recv(ctx);
492}
493
494/* set reconnect parameters */
495
496_PUBLIC_ void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries)
497{
498	if (conn) {
499		conn->reconnect.max_retries = max_retries;
500		conn->reconnect.retries = 0;
501		conn->reconnect.previous = time(NULL);
502	}
503}
504
505/* Actually this function is NOT ASYNC safe, FIXME? */
506static void ldap_reconnect(struct ldap_connection *conn)
507{
508	NTSTATUS status;
509	time_t now = time(NULL);
510
511	/* do we have set up reconnect ? */
512	if (conn->reconnect.max_retries == 0) return;
513
514	/* is the retry time expired ? */
515	if (now > conn->reconnect.previous + 30) {
516		conn->reconnect.retries = 0;
517		conn->reconnect.previous = now;
518	}
519
520	/* are we reconnectind too often and too fast? */
521	if (conn->reconnect.retries > conn->reconnect.max_retries) return;
522
523	/* keep track of the number of reconnections */
524	conn->reconnect.retries++;
525
526	/* reconnect */
527	status = ldap_connect(conn, conn->reconnect.url);
528	if ( ! NT_STATUS_IS_OK(status)) {
529		return;
530	}
531
532	/* rebind */
533	status = ldap_rebind(conn);
534	if ( ! NT_STATUS_IS_OK(status)) {
535		ldap_connection_dead(conn);
536	}
537}
538
539/* destroy an open ldap request */
540static int ldap_request_destructor(struct ldap_request *req)
541{
542	if (req->state == LDAP_REQUEST_PENDING) {
543		DLIST_REMOVE(req->conn->pending, req);
544	}
545	return 0;
546}
547
548/*
549  called on timeout of a ldap request
550*/
551static void ldap_request_timeout(struct tevent_context *ev, struct tevent_timer *te,
552				      struct timeval t, void *private_data)
553{
554	struct ldap_request *req = talloc_get_type(private_data, struct ldap_request);
555	req->status = NT_STATUS_IO_TIMEOUT;
556	if (req->state == LDAP_REQUEST_PENDING) {
557		DLIST_REMOVE(req->conn->pending, req);
558	}
559	req->state = LDAP_REQUEST_DONE;
560	if (req->async.fn) {
561		req->async.fn(req);
562	}
563}
564
565
566/*
567  called on completion of a one-way ldap request
568*/
569static void ldap_request_complete(struct tevent_context *ev, struct tevent_timer *te,
570				  struct timeval t, void *private_data)
571{
572	struct ldap_request *req = talloc_get_type(private_data, struct ldap_request);
573	if (req->async.fn) {
574		req->async.fn(req);
575	}
576}
577
578/*
579  send a ldap message - async interface
580*/
581_PUBLIC_ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
582				       struct ldap_message *msg)
583{
584	struct ldap_request *req;
585	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
586
587	req = talloc_zero(conn, struct ldap_request);
588	if (req == NULL) return NULL;
589
590	if (conn->sock == NULL) {
591		status = NT_STATUS_INVALID_CONNECTION;
592		goto failed;
593	}
594
595	req->state       = LDAP_REQUEST_SEND;
596	req->conn        = conn;
597	req->messageid   = conn->next_messageid++;
598	if (conn->next_messageid == 0) {
599		conn->next_messageid = 1;
600	}
601	req->type        = msg->type;
602	if (req->messageid == -1) {
603		goto failed;
604	}
605
606	talloc_set_destructor(req, ldap_request_destructor);
607
608	msg->messageid = req->messageid;
609
610	if (!ldap_encode(msg, samba_ldap_control_handlers(), &req->data, req)) {
611		status = NT_STATUS_INTERNAL_ERROR;
612		goto failed;
613	}
614
615	status = packet_send(conn->packet, req->data);
616	if (!NT_STATUS_IS_OK(status)) {
617		goto failed;
618	}
619
620	/* some requests don't expect a reply, so don't add those to the
621	   pending queue */
622	if (req->type == LDAP_TAG_AbandonRequest ||
623	    req->type == LDAP_TAG_UnbindRequest) {
624		req->status = NT_STATUS_OK;
625		req->state = LDAP_REQUEST_DONE;
626		/* we can't call the async callback now, as it isn't setup, so
627		   call it as next event */
628		tevent_add_timer(conn->event.event_ctx, req, timeval_zero(),
629				 ldap_request_complete, req);
630		return req;
631	}
632
633	req->state = LDAP_REQUEST_PENDING;
634	DLIST_ADD(conn->pending, req);
635
636	/* put a timeout on the request */
637	req->time_event = tevent_add_timer(conn->event.event_ctx, req,
638					   timeval_current_ofs(conn->timeout, 0),
639					   ldap_request_timeout, req);
640
641	return req;
642
643failed:
644	req->status = status;
645	req->state = LDAP_REQUEST_ERROR;
646	tevent_add_timer(conn->event.event_ctx, req, timeval_zero(),
647			 ldap_request_complete, req);
648
649	return req;
650}
651
652
653/*
654  wait for a request to complete
655  note that this does not destroy the request
656*/
657_PUBLIC_ NTSTATUS ldap_request_wait(struct ldap_request *req)
658{
659	while (req->state < LDAP_REQUEST_DONE) {
660		if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
661			req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
662			break;
663		}
664	}
665	return req->status;
666}
667
668
669/*
670  a mapping of ldap response code to strings
671*/
672static const struct {
673	enum ldap_result_code code;
674	const char *str;
675} ldap_code_map[] = {
676#define _LDAP_MAP_CODE(c) { c, #c }
677	_LDAP_MAP_CODE(LDAP_SUCCESS),
678	_LDAP_MAP_CODE(LDAP_OPERATIONS_ERROR),
679	_LDAP_MAP_CODE(LDAP_PROTOCOL_ERROR),
680	_LDAP_MAP_CODE(LDAP_TIME_LIMIT_EXCEEDED),
681	_LDAP_MAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED),
682	_LDAP_MAP_CODE(LDAP_COMPARE_FALSE),
683	_LDAP_MAP_CODE(LDAP_COMPARE_TRUE),
684	_LDAP_MAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED),
685	_LDAP_MAP_CODE(LDAP_STRONG_AUTH_REQUIRED),
686	_LDAP_MAP_CODE(LDAP_REFERRAL),
687	_LDAP_MAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED),
688	_LDAP_MAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION),
689	_LDAP_MAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED),
690	_LDAP_MAP_CODE(LDAP_SASL_BIND_IN_PROGRESS),
691	_LDAP_MAP_CODE(LDAP_NO_SUCH_ATTRIBUTE),
692	_LDAP_MAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE),
693	_LDAP_MAP_CODE(LDAP_INAPPROPRIATE_MATCHING),
694	_LDAP_MAP_CODE(LDAP_CONSTRAINT_VIOLATION),
695	_LDAP_MAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS),
696	_LDAP_MAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX),
697	_LDAP_MAP_CODE(LDAP_NO_SUCH_OBJECT),
698	_LDAP_MAP_CODE(LDAP_ALIAS_PROBLEM),
699	_LDAP_MAP_CODE(LDAP_INVALID_DN_SYNTAX),
700	_LDAP_MAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM),
701	_LDAP_MAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION),
702	_LDAP_MAP_CODE(LDAP_INVALID_CREDENTIALS),
703	_LDAP_MAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTS),
704	_LDAP_MAP_CODE(LDAP_BUSY),
705	_LDAP_MAP_CODE(LDAP_UNAVAILABLE),
706	_LDAP_MAP_CODE(LDAP_UNWILLING_TO_PERFORM),
707	_LDAP_MAP_CODE(LDAP_LOOP_DETECT),
708	_LDAP_MAP_CODE(LDAP_NAMING_VIOLATION),
709	_LDAP_MAP_CODE(LDAP_OBJECT_CLASS_VIOLATION),
710	_LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF),
711	_LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_RDN),
712	_LDAP_MAP_CODE(LDAP_ENTRY_ALREADY_EXISTS),
713	_LDAP_MAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED),
714	_LDAP_MAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS),
715	_LDAP_MAP_CODE(LDAP_OTHER)
716};
717
718/*
719  used to setup the status code from a ldap response
720*/
721_PUBLIC_ NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r)
722{
723	int i;
724	const char *codename = "unknown";
725
726	if (r->resultcode == LDAP_SUCCESS) {
727		return NT_STATUS_OK;
728	}
729
730	if (conn->last_error) {
731		talloc_free(conn->last_error);
732	}
733
734	for (i=0;i<ARRAY_SIZE(ldap_code_map);i++) {
735		if (r->resultcode == ldap_code_map[i].code) {
736			codename = ldap_code_map[i].str;
737			break;
738		}
739	}
740
741	conn->last_error = talloc_asprintf(conn, "LDAP error %u %s - %s <%s> <%s>",
742					   r->resultcode,
743					   codename,
744					   r->dn?r->dn:"(NULL)",
745					   r->errormessage?r->errormessage:"",
746					   r->referral?r->referral:"");
747
748	return NT_STATUS_LDAP(r->resultcode);
749}
750
751/*
752  return error string representing the last error
753*/
754_PUBLIC_ const char *ldap_errstr(struct ldap_connection *conn,
755			TALLOC_CTX *mem_ctx,
756			NTSTATUS status)
757{
758	if (NT_STATUS_IS_LDAP(status) && conn->last_error != NULL) {
759		return talloc_strdup(mem_ctx, conn->last_error);
760	}
761	return talloc_asprintf(mem_ctx, "LDAP client internal error: %s", nt_errstr(status));
762}
763
764
765/*
766  return the Nth result message, waiting if necessary
767*/
768_PUBLIC_ NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg)
769{
770	*msg = NULL;
771
772	NT_STATUS_HAVE_NO_MEMORY(req);
773
774	while (req->state < LDAP_REQUEST_DONE && n >= req->num_replies) {
775		if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
776			return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
777		}
778	}
779
780	if (n < req->num_replies) {
781		*msg = req->replies[n];
782		return NT_STATUS_OK;
783	}
784
785	if (!NT_STATUS_IS_OK(req->status)) {
786		return req->status;
787	}
788
789	return NT_STATUS_NO_MORE_ENTRIES;
790}
791
792
793/*
794  return a single result message, checking if it is of the expected LDAP type
795*/
796_PUBLIC_ NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type)
797{
798	NTSTATUS status;
799	status = ldap_result_n(req, 0, msg);
800	if (!NT_STATUS_IS_OK(status)) {
801		return status;
802	}
803	if ((*msg)->type != type) {
804		*msg = NULL;
805		return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
806	}
807	return status;
808}
809
810/*
811  a simple ldap transaction, for single result requests that only need a status code
812  this relies on single valued requests having the response type == request type + 1
813*/
814_PUBLIC_ NTSTATUS ldap_transaction(struct ldap_connection *conn, struct ldap_message *msg)
815{
816	struct ldap_request *req = ldap_request_send(conn, msg);
817	struct ldap_message *res;
818	NTSTATUS status;
819	status = ldap_result_n(req, 0, &res);
820	if (!NT_STATUS_IS_OK(status)) {
821		talloc_free(req);
822		return status;
823	}
824	if (res->type != msg->type + 1) {
825		talloc_free(req);
826		return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
827	}
828	status = ldap_check_response(conn, &res->r.GeneralResult);
829	talloc_free(req);
830	return status;
831}
832