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