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