Use a custom init function for samba4 that sets a samba4
[ira/wip.git] / source4 / lib / ldb / ldb_ildap / ldb_ildap.c
1 /* 
2    ldb database library - ildap backend
3
4    Copyright (C) Andrew Tridgell  2005
5    Copyright (C) Simo Sorce       2006
6
7      ** NOTE! The following LGPL license applies to the ldb
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10    
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 3 of the License, or (at your option) any later version.
15
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Lesser General Public License for more details.
20
21    You should have received a copy of the GNU Lesser General Public
22    License along with this library; if not, see <http://www.gnu.org/licenses/>.
23 */
24
25 /*
26  *  Name: ldb_ildap
27  *
28  *  Component: ldb ildap backend
29  *
30  *  Description: This is a ldb backend for the internal ldap
31  *  client library in Samba4. By using this backend we are
32  *  independent of a system ldap library
33  *
34  *  Author: Andrew Tridgell
35  *
36  *  Modifications:
37  *
38  *  - description: make the module use asyncronous calls
39  *    date: Feb 2006
40  *    author: Simo Sorce
41  */
42
43
44 #include "includes.h"
45 #include "ldb_includes.h"
46
47 #include "lib/events/events.h"
48 #include "libcli/ldap/ldap.h"
49 #include "libcli/ldap/ldap_client.h"
50 #include "auth/auth.h"
51 #include "auth/credentials/credentials.h"
52 #include "param/param.h"
53
54 struct ildb_private {
55         struct ldap_connection *ldap;
56         struct ldb_module *module;
57 };
58
59 struct ildb_context {
60         struct ildb_private *ildb;
61         struct ldb_handle *handle;
62         struct ldap_request *req;
63         void *context;
64         int (*callback)(struct ldb_context *, void *, struct ldb_reply *);
65 };
66
67 /*
68   convert a ldb_message structure to a list of ldap_mod structures
69   ready for ildap_add() or ildap_modify()
70 */
71 static struct ldap_mod **ildb_msg_to_mods(void *mem_ctx, int *num_mods,
72                                           const struct ldb_message *msg, int use_flags)
73 {
74         struct ldap_mod **mods;
75         unsigned int i;
76         int n = 0;
77
78         /* allocate maximum number of elements needed */
79         mods = talloc_array(mem_ctx, struct ldap_mod *, msg->num_elements+1);
80         if (!mods) {
81                 errno = ENOMEM;
82                 return NULL;
83         }
84         mods[0] = NULL;
85
86         for (i = 0; i < msg->num_elements; i++) {
87                 const struct ldb_message_element *el = &msg->elements[i];
88
89                 mods[n] = talloc(mods, struct ldap_mod);
90                 if (!mods[n]) {
91                         goto failed;
92                 }
93                 mods[n + 1] = NULL;
94                 mods[n]->type = 0;
95                 mods[n]->attrib = *el;
96                 if (use_flags) {
97                         switch (el->flags & LDB_FLAG_MOD_MASK) {
98                         case LDB_FLAG_MOD_ADD:
99                                 mods[n]->type = LDAP_MODIFY_ADD;
100                                 break;
101                         case LDB_FLAG_MOD_DELETE:
102                                 mods[n]->type = LDAP_MODIFY_DELETE;
103                                 break;
104                         case LDB_FLAG_MOD_REPLACE:
105                                 mods[n]->type = LDAP_MODIFY_REPLACE;
106                                 break;
107                         }
108                 }
109                 n++;
110         }
111
112         *num_mods = n;
113         return mods;
114
115 failed:
116         talloc_free(mods);
117         return NULL;
118 }
119
120
121 /*
122   map an ildap NTSTATUS to a ldb error code
123 */
124 static int ildb_map_error(struct ildb_private *ildb, NTSTATUS status)
125 {
126         TALLOC_CTX *mem_ctx = talloc_new(ildb);
127         if (NT_STATUS_IS_OK(status)) {
128                 return LDB_SUCCESS;
129         }
130         if (!mem_ctx) {
131                 ldb_oom(ildb->module->ldb);
132                 return LDB_ERR_OPERATIONS_ERROR;
133         }
134         ldb_set_errstring(ildb->module->ldb, ldap_errstr(ildb->ldap, mem_ctx, status));
135         talloc_free(mem_ctx);
136         if (NT_STATUS_IS_LDAP(status)) {
137                 return NT_STATUS_LDAP_CODE(status);
138         }
139         return LDB_ERR_OPERATIONS_ERROR;
140 }
141
142 static void ildb_request_timeout(struct event_context *ev, struct timed_event *te,
143                                  struct timeval t, void *private_data)
144 {
145         struct ildb_context *ac = talloc_get_type(private_data, struct ildb_context);
146         struct ldb_handle *handle = ac->handle;
147
148         if (ac->req->state == LDAP_REQUEST_PENDING) {
149                 DLIST_REMOVE(ac->req->conn->pending, ac->req);
150         }
151
152         handle->status = LDB_ERR_TIME_LIMIT_EXCEEDED;
153
154         return;
155 }
156
157 static void ildb_callback(struct ldap_request *req)
158 {
159         struct ildb_context *ac = talloc_get_type(req->async.private_data, struct ildb_context);
160         struct ldb_handle *handle = ac->handle;
161         struct ildb_private *ildb = ac->ildb;
162         NTSTATUS status;
163         int i;
164
165         handle->status = LDB_SUCCESS;
166
167         if (!NT_STATUS_IS_OK(req->status)) {
168                 handle->status = ildb_map_error(ildb, req->status);
169                 return;
170         }
171
172         if (req->num_replies < 1) {
173                 handle->status = LDB_ERR_OPERATIONS_ERROR;
174                 return;
175         } 
176                 
177         switch (req->type) {
178
179         case LDAP_TAG_ModifyRequest:
180                 if (req->replies[0]->type != LDAP_TAG_ModifyResponse) {
181                         handle->status = LDB_ERR_PROTOCOL_ERROR;
182                         return;
183                 }
184                 status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
185                 handle->status = ildb_map_error(ildb, status);
186                 if (ac->callback && handle->status == LDB_SUCCESS) {
187                         /* FIXME: build a corresponding ares to pass on */
188                         handle->status = ac->callback(ac->ildb->module->ldb, ac->context, NULL);
189                 }
190                 handle->state = LDB_ASYNC_DONE;
191                 break;
192
193         case LDAP_TAG_AddRequest:
194                 if (req->replies[0]->type != LDAP_TAG_AddResponse) {
195                         handle->status = LDB_ERR_PROTOCOL_ERROR;
196                         return;
197                 }
198                 status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
199                 handle->status = ildb_map_error(ildb, status);
200                 if (ac->callback && handle->status == LDB_SUCCESS) {
201                         /* FIXME: build a corresponding ares to pass on */
202                         handle->status = ac->callback(ac->ildb->module->ldb, ac->context, NULL);
203                 }
204                 handle->state = LDB_ASYNC_DONE;
205                 break;
206
207         case LDAP_TAG_DelRequest:
208                 if (req->replies[0]->type != LDAP_TAG_DelResponse) {
209                         handle->status = LDB_ERR_PROTOCOL_ERROR;
210                         return;
211                 }
212                 status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
213                 handle->status = ildb_map_error(ildb, status);
214                 if (ac->callback && handle->status == LDB_SUCCESS) {
215                         /* FIXME: build a corresponding ares to pass on */
216                         handle->status = ac->callback(ac->ildb->module->ldb, ac->context, NULL);
217                 }
218                 handle->state = LDB_ASYNC_DONE;
219                 break;
220
221         case LDAP_TAG_ModifyDNRequest:
222                 if (req->replies[0]->type != LDAP_TAG_ModifyDNResponse) {
223                         handle->status = LDB_ERR_PROTOCOL_ERROR;
224                         return;
225                 }
226                 status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
227                 handle->status = ildb_map_error(ildb, status);
228                 if (ac->callback && handle->status == LDB_SUCCESS) {
229                         /* FIXME: build a corresponding ares to pass on */
230                         handle->status = ac->callback(ac->ildb->module->ldb, ac->context, NULL);
231                 }
232                 handle->state = LDB_ASYNC_DONE;
233                 break;
234
235         case LDAP_TAG_SearchRequest:
236                 /* loop over all messages */
237                 for (i = 0; i < req->num_replies; i++) {
238                         struct ldap_SearchResEntry *search;
239                         struct ldb_reply *ares = NULL;
240                         struct ldap_message *msg;
241                         int ret;
242
243                         ares = talloc_zero(ac, struct ldb_reply);
244                         if (!ares) {
245                                 handle->status = LDB_ERR_OPERATIONS_ERROR;
246                                 return;
247                         }
248
249                         msg = req->replies[i];
250                         switch (msg->type) {
251
252                         case LDAP_TAG_SearchResultDone:
253
254                                 status = ldap_check_response(req->conn, &msg->r.GeneralResult);
255                                 if (!NT_STATUS_IS_OK(status)) {
256                                         handle->status = ildb_map_error(ildb, status);
257                                         return;
258                                 }
259                                 
260                                 ares->controls = talloc_move(ares, &msg->controls);
261                                 if (msg->r.SearchResultDone.resultcode) {
262                                         if (msg->r.SearchResultDone.errormessage) {
263                                                 ldb_set_errstring(ac->ildb->module->ldb, msg->r.SearchResultDone.errormessage);
264                                         }
265                                 }
266
267                                 handle->status = msg->r.SearchResultDone.resultcode;
268                                 handle->state = LDB_ASYNC_DONE;
269                                 ares->type = LDB_REPLY_DONE;
270                                 break;
271
272                         case LDAP_TAG_SearchResultEntry:
273
274
275                                 ares->message = ldb_msg_new(ares);
276                                 if (!ares->message) {
277                                         handle->status = LDB_ERR_OPERATIONS_ERROR;
278                                         return;
279                                 }
280
281                                 search = &(msg->r.SearchResultEntry);
282                 
283                                 ares->message->dn = ldb_dn_new(ares->message, ac->ildb->module->ldb, search->dn);
284                                 if ( ! ldb_dn_validate(ares->message->dn)) {
285                                         handle->status = LDB_ERR_OPERATIONS_ERROR;
286                                         return;
287                                 }
288                                 ares->message->num_elements = search->num_attributes;
289                                 ares->message->elements = talloc_move(ares->message,
290                                                                       &search->attributes);
291
292                                 handle->status = LDB_SUCCESS;
293                                 handle->state = LDB_ASYNC_PENDING;
294                                 ares->type = LDB_REPLY_ENTRY;
295                                 break;
296
297                         case LDAP_TAG_SearchResultReference:
298
299                                 ares->referral = talloc_strdup(ares, msg->r.SearchResultReference.referral);
300                                 
301                                 handle->status = LDB_SUCCESS;
302                                 handle->state = LDB_ASYNC_PENDING;
303                                 ares->type = LDB_REPLY_REFERRAL;
304                                 break;
305
306                         default:
307                                 /* TAG not handled, fail ! */
308                                 handle->status = LDB_ERR_PROTOCOL_ERROR;
309                                 return;
310                         }
311
312                         ret = ac->callback(ac->ildb->module->ldb, ac->context, ares);
313                         if (ret) {
314                                 handle->status = ret;
315                         }
316                 }
317
318                 talloc_free(req->replies);
319                 req->replies = NULL;
320                 req->num_replies = 0;
321
322                 break;
323                 
324         default:
325                 handle->status = LDB_ERR_PROTOCOL_ERROR;
326                 return;
327         }
328 }
329
330 static struct ildb_context *init_ildb_handle(struct ildb_private *ildb,
331                                              struct ldb_request *req)
332 {
333         struct ildb_context *ildb_ac;
334         struct ldb_handle *h;
335
336         h = talloc_zero(req, struct ldb_handle);
337         if (h == NULL) {
338                 ldb_set_errstring(ildb->module->ldb, "Out of Memory");
339                 return NULL;
340         }
341
342         h->module = ildb->module;
343
344         ildb_ac = talloc(h, struct ildb_context);
345         if (ildb_ac == NULL) {
346                 ldb_set_errstring(ildb->module->ldb, "Out of Memory");
347                 talloc_free(h);
348                 return NULL;
349         }
350
351         h->private_data = ildb_ac;
352
353         h->state = LDB_ASYNC_INIT;
354         h->status = LDB_SUCCESS;
355
356         ildb_ac->ildb = ildb;
357         ildb_ac->handle = h;
358         ildb_ac->context = req->context;
359         ildb_ac->callback = req->callback;
360
361         req->handle = h;
362         return ildb_ac;
363 }
364
365 static int ildb_request_send(struct ildb_private *ildb, struct ldap_message *msg, struct ldb_request *r)
366 {
367         struct ildb_context *ildb_ac = init_ildb_handle(ildb, r);
368         struct ldap_request *req;
369
370         if (!ildb_ac) {
371                 return LDB_ERR_OPERATIONS_ERROR;                
372         }
373
374         req = ldap_request_send(ildb->ldap, msg);
375         if (req == NULL) {
376                 ldb_set_errstring(ildb->module->ldb, "async send request failed");
377                 return LDB_ERR_OPERATIONS_ERROR;
378         }
379         ildb_ac->req = talloc_steal(ildb_ac, req);
380
381         if (!req->conn) {
382                 ldb_set_errstring(ildb->module->ldb, "connection to remote LDAP server dropped?");
383                 return LDB_ERR_OPERATIONS_ERROR;
384         }
385
386         talloc_free(req->time_event);
387         req->time_event = NULL;
388         if (r->timeout) {
389                 req->time_event = event_add_timed(req->conn->event.event_ctx, ildb_ac, 
390                                                   timeval_current_ofs(r->timeout, 0),
391                                                   ildb_request_timeout, ildb_ac);
392         }
393
394         req->async.fn = ildb_callback;
395         req->async.private_data = ildb_ac;
396
397         return LDB_SUCCESS;
398 }
399
400 static int ildb_request_noop(struct ildb_private *ildb, struct ldb_request *req) 
401 {
402         struct ildb_context *ildb_ac = init_ildb_handle(ildb, req);
403         int ret = LDB_SUCCESS;
404
405         if (!ildb_ac) {
406                 return LDB_ERR_OPERATIONS_ERROR;                
407         }
408
409         if (ildb_ac->callback) {
410                 ret = ildb_ac->callback(ildb->module->ldb, ildb_ac->context, NULL);
411         }
412         ildb_ac->handle->state = LDB_ASYNC_DONE;
413         return ret;
414 }
415
416 /*
417   search for matching records using an asynchronous function
418  */
419 static int ildb_search(struct ldb_module *module, struct ldb_request *req)
420 {
421         struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
422         struct ldap_message *msg;
423         int n;
424
425         req->handle = NULL;
426
427         if (!req->callback || !req->context) {
428                 ldb_set_errstring(module->ldb, "Async interface called with NULL callback function or NULL context");
429                 return LDB_ERR_OPERATIONS_ERROR;
430         }
431         
432         if (req->op.search.tree == NULL) {
433                 ldb_set_errstring(module->ldb, "Invalid expression parse tree");
434                 return LDB_ERR_OPERATIONS_ERROR;
435         }
436
437         msg = new_ldap_message(req);
438         if (msg == NULL) {
439                 ldb_set_errstring(module->ldb, "Out of Memory");
440                 return LDB_ERR_OPERATIONS_ERROR;
441         }
442
443         msg->type = LDAP_TAG_SearchRequest;
444
445         if (req->op.search.base == NULL) {
446                 msg->r.SearchRequest.basedn = talloc_strdup(msg, "");
447         } else {
448                 msg->r.SearchRequest.basedn  = ldb_dn_alloc_linearized(msg, req->op.search.base);
449         }
450         if (msg->r.SearchRequest.basedn == NULL) {
451                 ldb_set_errstring(module->ldb, "Unable to determine baseDN");
452                 talloc_free(msg);
453                 return LDB_ERR_OPERATIONS_ERROR;
454         }
455
456         if (req->op.search.scope == LDB_SCOPE_DEFAULT) {
457                 msg->r.SearchRequest.scope = LDB_SCOPE_SUBTREE;
458         } else {
459                 msg->r.SearchRequest.scope = req->op.search.scope;
460         }
461         
462         msg->r.SearchRequest.deref  = LDAP_DEREFERENCE_NEVER;
463         msg->r.SearchRequest.timelimit = 0;
464         msg->r.SearchRequest.sizelimit = 0;
465         msg->r.SearchRequest.attributesonly = 0;
466         msg->r.SearchRequest.tree = discard_const(req->op.search.tree);
467         
468         for (n = 0; req->op.search.attrs && req->op.search.attrs[n]; n++) /* noop */ ;
469         msg->r.SearchRequest.num_attributes = n;
470         msg->r.SearchRequest.attributes = discard_const(req->op.search.attrs);
471         msg->controls = req->controls;
472
473         return ildb_request_send(ildb, msg, req);
474 }
475
476 /*
477   add a record
478 */
479 static int ildb_add(struct ldb_module *module, struct ldb_request *req)
480 {
481         struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
482         struct ldap_message *msg;
483         struct ldap_mod **mods;
484         int i,n;
485
486         req->handle = NULL;
487
488         /* ignore ltdb specials */
489         if (ldb_dn_is_special(req->op.add.message->dn)) {
490                 return ildb_request_noop(ildb, req);
491         }
492
493         msg = new_ldap_message(req);
494         if (msg == NULL) {
495                 return LDB_ERR_OPERATIONS_ERROR;
496         }
497
498         msg->type = LDAP_TAG_AddRequest;
499
500         msg->r.AddRequest.dn = ldb_dn_alloc_linearized(msg, req->op.add.message->dn);
501         if (msg->r.AddRequest.dn == NULL) {
502                 talloc_free(msg);
503                 return LDB_ERR_INVALID_DN_SYNTAX;
504         }
505
506         mods = ildb_msg_to_mods(msg, &n, req->op.add.message, 0);
507         if (mods == NULL) {
508                 talloc_free(msg);
509                 return LDB_ERR_OPERATIONS_ERROR;
510         }
511
512         msg->r.AddRequest.num_attributes = n;
513         msg->r.AddRequest.attributes = talloc_array(msg, struct ldb_message_element, n);
514         if (msg->r.AddRequest.attributes == NULL) {
515                 talloc_free(msg);
516                 return LDB_ERR_OPERATIONS_ERROR;
517         }
518
519         for (i = 0; i < n; i++) {
520                 msg->r.AddRequest.attributes[i] = mods[i]->attrib;
521         }
522
523         return ildb_request_send(ildb, msg, req);
524 }
525
526 /*
527   modify a record
528 */
529 static int ildb_modify(struct ldb_module *module, struct ldb_request *req)
530 {
531         struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
532         struct ldap_message *msg;
533         struct ldap_mod **mods;
534         int i,n;
535
536         req->handle = NULL;
537
538         /* ignore ltdb specials */
539         if (ldb_dn_is_special(req->op.mod.message->dn)) {
540                 return ildb_request_noop(ildb, req);
541         }
542
543         msg = new_ldap_message(req);
544         if (msg == NULL) {
545                 return LDB_ERR_OPERATIONS_ERROR;
546         }
547
548         msg->type = LDAP_TAG_ModifyRequest;
549
550         msg->r.ModifyRequest.dn = ldb_dn_alloc_linearized(msg, req->op.mod.message->dn);
551         if (msg->r.ModifyRequest.dn == NULL) {
552                 talloc_free(msg);
553                 return LDB_ERR_INVALID_DN_SYNTAX;
554         }
555
556         mods = ildb_msg_to_mods(msg, &n, req->op.mod.message, 1);
557         if (mods == NULL) {
558                 talloc_free(msg);
559                 return LDB_ERR_OPERATIONS_ERROR;
560         }
561
562         msg->r.ModifyRequest.num_mods = n;
563         msg->r.ModifyRequest.mods = talloc_array(msg, struct ldap_mod, n);
564         if (msg->r.ModifyRequest.mods == NULL) {
565                 talloc_free(msg);
566                 return LDB_ERR_OPERATIONS_ERROR;
567         }
568
569         for (i = 0; i < n; i++) {
570                 msg->r.ModifyRequest.mods[i] = *mods[i];
571         }
572
573         return ildb_request_send(ildb, msg, req);
574 }
575
576 /*
577   delete a record
578 */
579 static int ildb_delete(struct ldb_module *module, struct ldb_request *req)
580 {
581         struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
582         struct ldap_message *msg;
583
584         req->handle = NULL;
585
586         /* ignore ltdb specials */
587         if (ldb_dn_is_special(req->op.del.dn)) {
588                 return ildb_request_noop(ildb, req);
589         }
590
591         msg = new_ldap_message(req);
592         if (msg == NULL) {
593                 return LDB_ERR_OPERATIONS_ERROR;
594         }
595
596         msg->type = LDAP_TAG_DelRequest;
597         
598         msg->r.DelRequest.dn = ldb_dn_alloc_linearized(msg, req->op.del.dn);
599         if (msg->r.DelRequest.dn == NULL) {
600                 talloc_free(msg);
601                 return LDB_ERR_INVALID_DN_SYNTAX;
602         }
603
604         return ildb_request_send(ildb, msg, req);
605 }
606
607 /*
608   rename a record
609 */
610 static int ildb_rename(struct ldb_module *module, struct ldb_request *req)
611 {
612         struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
613         struct ldap_message *msg;
614
615         req->handle = NULL;
616
617         /* ignore ltdb specials */
618         if (ldb_dn_is_special(req->op.rename.olddn) || ldb_dn_is_special(req->op.rename.newdn)) {
619                 return ildb_request_noop(ildb, req);
620         }
621
622         msg = new_ldap_message(req);
623         if (msg == NULL) {
624                 return LDB_ERR_OPERATIONS_ERROR;
625         }
626
627         msg->type = LDAP_TAG_ModifyDNRequest;
628         msg->r.ModifyDNRequest.dn = ldb_dn_alloc_linearized(msg, req->op.rename.olddn);
629         if (msg->r.ModifyDNRequest.dn == NULL) {
630                 talloc_free(msg);
631                 return LDB_ERR_INVALID_DN_SYNTAX;
632         }
633
634         msg->r.ModifyDNRequest.newrdn = 
635                 talloc_asprintf(msg, "%s=%s",
636                                 ldb_dn_get_rdn_name(req->op.rename.newdn),
637                                 ldb_dn_escape_value(msg, *ldb_dn_get_rdn_val(req->op.rename.newdn)));
638         if (msg->r.ModifyDNRequest.newrdn == NULL) {
639                 talloc_free(msg);
640                 return LDB_ERR_OPERATIONS_ERROR;
641         }
642
643         msg->r.ModifyDNRequest.newsuperior =
644                 ldb_dn_alloc_linearized(msg, ldb_dn_get_parent(msg, req->op.rename.newdn));
645         if (msg->r.ModifyDNRequest.newsuperior == NULL) {
646                 talloc_free(msg);
647                 return LDB_ERR_INVALID_DN_SYNTAX;
648         }
649
650         msg->r.ModifyDNRequest.deleteolddn = true;
651
652         return ildb_request_send(ildb, msg, req);
653 }
654
655 static int ildb_start_trans(struct ldb_module *module)
656 {
657         /* TODO implement a local locking mechanism here */
658
659         return LDB_SUCCESS;
660 }
661
662 static int ildb_end_trans(struct ldb_module *module)
663 {
664         /* TODO implement a local transaction mechanism here */
665
666         return LDB_SUCCESS;
667 }
668
669 static int ildb_del_trans(struct ldb_module *module)
670 {
671         /* TODO implement a local locking mechanism here */
672
673         return LDB_SUCCESS;
674 }
675
676 static int ildb_request(struct ldb_module *module, struct ldb_request *req)
677 {
678         return LDB_ERR_OPERATIONS_ERROR;
679 }
680
681 static int ildb_wait(struct ldb_handle *handle, enum ldb_wait_type type)
682 {
683         struct ildb_context *ac = talloc_get_type(handle->private_data, struct ildb_context);
684
685         if (handle->state == LDB_ASYNC_DONE) {
686                 return handle->status;
687         }
688
689         if (!ac) {
690                 return LDB_ERR_OPERATIONS_ERROR;
691         }
692
693         handle->state = LDB_ASYNC_INIT;
694
695         switch(type) {
696         case LDB_WAIT_NONE:
697                 if (event_loop_once(ac->req->conn->event.event_ctx) != 0) {
698                         return LDB_ERR_OTHER;
699                 }
700                 break;
701         case LDB_WAIT_ALL:
702                 while (handle->status == LDB_SUCCESS && handle->state != LDB_ASYNC_DONE) {
703                         if (event_loop_once(ac->req->conn->event.event_ctx) != 0) {
704                                 return LDB_ERR_OTHER;
705                         }
706                 }
707                 break;
708         default:
709                 return LDB_ERR_OPERATIONS_ERROR;
710         }
711         
712         return handle->status;
713 }
714
715 static const struct ldb_module_ops ildb_ops = {
716         .name              = "ldap",
717         .search            = ildb_search,
718         .add               = ildb_add,
719         .modify            = ildb_modify,
720         .del               = ildb_delete,
721         .rename            = ildb_rename,
722         .request           = ildb_request,
723         .start_transaction = ildb_start_trans,
724         .end_transaction   = ildb_end_trans,
725         .del_transaction   = ildb_del_trans,
726         .wait              = ildb_wait
727 };
728
729 /*
730   connect to the database
731 */
732 static int ildb_connect(struct ldb_context *ldb, const char *url, 
733                         unsigned int flags, const char *options[],
734                         struct ldb_module **_module)
735 {
736         struct ldb_module *module;
737         struct ildb_private *ildb;
738         NTSTATUS status;
739         struct cli_credentials *creds;
740         struct event_context *event_ctx;
741
742         module = talloc(ldb, struct ldb_module);
743         if (!module) {
744                 ldb_oom(ldb);
745                 return -1;
746         }
747         talloc_set_name_const(module, "ldb_ildap backend");
748         module->ldb             = ldb;
749         module->prev            = module->next = NULL;
750         module->private_data    = NULL;
751         module->ops             = &ildb_ops;
752
753         ildb = talloc(module, struct ildb_private);
754         if (!ildb) {
755                 ldb_oom(ldb);
756                 goto failed;
757         }
758         module->private_data    = ildb;
759         ildb->module            = module;
760
761         event_ctx = ldb_get_event_context(ldb);
762
763         /* FIXME: We must make the event context an explicit parameter, but we
764          * need to build the events library separately first. Hack a new event
765          * context so that CMD line utilities work until we have them all
766          * converted */
767         if (event_ctx == NULL) {
768                 event_ctx = event_context_init(NULL);
769         }
770
771         ildb->ldap = ldap4_new_connection(ildb, ldb_get_opaque(ldb, "loadparm"),
772                                           event_ctx);
773         if (!ildb->ldap) {
774                 ldb_oom(ldb);
775                 goto failed;
776         }
777
778         if (flags & LDB_FLG_RECONNECT) {
779                 ldap_set_reconn_params(ildb->ldap, 10);
780         }
781
782         status = ldap_connect(ildb->ldap, url);
783         if (!NT_STATUS_IS_OK(status)) {
784                 ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to connect to ldap URL '%s' - %s\n",
785                           url, ldap_errstr(ildb->ldap, module, status));
786                 goto failed;
787         }
788
789         /* caller can optionally setup credentials using the opaque token 'credentials' */
790         creds = talloc_get_type(ldb_get_opaque(ldb, "credentials"), struct cli_credentials);
791         if (creds == NULL) {
792                 struct auth_session_info *session_info = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"), struct auth_session_info);
793                 if (session_info) {
794                         creds = session_info->credentials;
795                 }
796         }
797
798         if (creds != NULL && cli_credentials_authentication_requested(creds)) {
799                 const char *bind_dn = cli_credentials_get_bind_dn(creds);
800                 if (bind_dn) {
801                         const char *password = cli_credentials_get_password(creds);
802                         status = ldap_bind_simple(ildb->ldap, bind_dn, password);
803                         if (!NT_STATUS_IS_OK(status)) {
804                                 ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n",
805                                           ldap_errstr(ildb->ldap, module, status));
806                                 goto failed;
807                         }
808                 } else {
809                         status = ldap_bind_sasl(ildb->ldap, creds, ldb_get_opaque(ldb, "loadparm"));
810                         if (!NT_STATUS_IS_OK(status)) {
811                                 ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n",
812                                           ldap_errstr(ildb->ldap, module, status));
813                                 goto failed;
814                         }
815                 }
816         }
817
818         *_module = module;
819         return 0;
820
821 failed:
822         talloc_free(module);
823         return -1;
824 }
825
826 _PUBLIC_ const struct ldb_backend_ops ldb_ldap_backend_ops = {
827         .name = "ldap",
828         .connect_fn = ildb_connect
829 };
830
831 _PUBLIC_ const struct ldb_backend_ops ldb_ldapi_backend_ops = {
832         .name = "ldapi",
833         .connect_fn = ildb_connect
834 };
835
836 _PUBLIC_ const struct ldb_backend_ops ldb_ldaps_backend_ops = {
837         .name = "ldaps",
838         .connect_fn = ildb_connect
839 };
840