r9391: Convert all the code to use struct ldb_dn to ohandle ldap like distinguished...
[kai/samba-autobuild/.git] / source4 / ldap_server / ldap_simple_ldb.c
1 /* 
2    Unix SMB/CIFS implementation.
3    LDAP server SIMPLE LDB implementation
4    Copyright (C) Stefan Metzmacher 2004
5    Copyright (C) Simo Sorce 2004
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "ldap_server/ldap_server.h"
24 #include "lib/ldb/include/ldb.h"
25 #include "db_wrap.h"
26
27 #define VALID_DN_SYNTAX(dn,i) do {\
28         if (!(dn)) {\
29                 return NT_STATUS_NO_MEMORY;\
30         } else if ((dn)->comp_num < (i)) {\
31                 result = LDAP_INVALID_DN_SYNTAX;\
32                 errstr = "Invalid DN (" #i " components needed for '" #dn "')";\
33                 goto reply;\
34         }\
35 } while(0)
36
37 static NTSTATUS sldb_Search(struct ldapsrv_partition *partition, struct ldapsrv_call *call,
38                                      struct ldap_SearchRequest *r)
39 {
40         void *local_ctx;
41         struct ldb_dn *basedn;
42         struct ldap_Result *done;
43         struct ldap_SearchResEntry *ent;
44         struct ldapsrv_reply *ent_r, *done_r;
45         int result = LDAP_SUCCESS;
46         struct ldb_context *samdb;
47         struct ldb_message **res = NULL;
48         int i, j, y, count = 0;
49         int success_limit = 1;
50         enum ldb_scope scope = LDB_SCOPE_DEFAULT;
51         const char **attrs = NULL;
52         const char *errstr = NULL;
53
54         local_ctx = talloc_named(call, 0, "sldb_Search local memory context");
55         NT_STATUS_HAVE_NO_MEMORY(local_ctx);
56
57         samdb = ldapsrv_sam_connect(call);
58         NT_STATUS_HAVE_NO_MEMORY(samdb);
59
60         basedn = ldb_dn_explode(local_ctx, r->basedn);
61         VALID_DN_SYNTAX(basedn, 0);
62
63         DEBUG(10, ("sldb_Search: basedn: [%s]\n", r->basedn));
64         DEBUG(10, ("sldb_Search: filter: [%s]\n", ldb_filter_from_tree(call, r->tree)));
65
66         switch (r->scope) {
67                 case LDAP_SEARCH_SCOPE_BASE:
68                         DEBUG(10,("sldb_Search: scope: [BASE]\n"));
69                         scope = LDB_SCOPE_BASE;
70                         success_limit = 0;
71                         break;
72                 case LDAP_SEARCH_SCOPE_SINGLE:
73                         DEBUG(10,("sldb_Search: scope: [ONE]\n"));
74                         scope = LDB_SCOPE_ONELEVEL;
75                         success_limit = 0;
76                         break;
77                 case LDAP_SEARCH_SCOPE_SUB:
78                         DEBUG(10,("sldb_Search: scope: [SUB]\n"));
79                         scope = LDB_SCOPE_SUBTREE;
80                         success_limit = 0;
81                         break;
82         }
83
84         if (r->num_attributes >= 1) {
85                 attrs = talloc_array(samdb, const char *, r->num_attributes+1);
86                 NT_STATUS_HAVE_NO_MEMORY(attrs);
87
88                 for (i=0; i < r->num_attributes; i++) {
89                         DEBUG(10,("sldb_Search: attrs: [%s]\n",r->attributes[i]));
90                         attrs[i] = r->attributes[i];
91                 }
92                 attrs[i] = NULL;
93         }
94
95         DEBUG(5,("ldb_search_bytree dn=%s filter=%s\n", 
96                  r->basedn, ldb_filter_from_tree(call, r->tree)));
97
98         count = ldb_search_bytree(samdb, basedn, scope, r->tree, attrs, &res);
99         talloc_steal(samdb, res);
100
101         for (i=0; i < count; i++) {
102                 ent_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultEntry);
103                 NT_STATUS_HAVE_NO_MEMORY(ent_r);
104
105                 ent = &ent_r->msg->r.SearchResultEntry;
106                 ent->dn = ldb_dn_linearize(ent_r, res[i]->dn);
107                 ent->num_attributes = 0;
108                 ent->attributes = NULL;
109                 if (res[i]->num_elements == 0) {
110                         goto queue_reply;
111                 }
112                 ent->num_attributes = res[i]->num_elements;
113                 ent->attributes = talloc_array(ent_r, struct ldb_message_element, ent->num_attributes);
114                 NT_STATUS_HAVE_NO_MEMORY(ent->attributes);
115                 for (j=0; j < ent->num_attributes; j++) {
116                         ent->attributes[j].name = talloc_steal(ent->attributes, res[i]->elements[j].name);
117                         ent->attributes[j].num_values = 0;
118                         ent->attributes[j].values = NULL;
119                         if (r->attributesonly && (res[i]->elements[j].num_values == 0)) {
120                                 continue;
121                         }
122                         ent->attributes[j].num_values = res[i]->elements[j].num_values;
123                         ent->attributes[j].values = talloc_array(ent->attributes,
124                                                         DATA_BLOB, ent->attributes[j].num_values);
125                         NT_STATUS_HAVE_NO_MEMORY(ent->attributes[j].values);
126                         for (y=0; y < ent->attributes[j].num_values; y++) {
127                                 ent->attributes[j].values[y].length = res[i]->elements[j].values[y].length;
128                                 ent->attributes[j].values[y].data = talloc_steal(ent->attributes[j].values,
129                                                                         res[i]->elements[j].values[y].data);
130                         }
131                 }
132 queue_reply:
133                 ldapsrv_queue_reply(call, ent_r);
134         }
135
136 reply:
137         done_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultDone);
138         NT_STATUS_HAVE_NO_MEMORY(done_r);
139
140         if (result == LDAP_SUCCESS) {
141                 if (count >= success_limit) {
142                         DEBUG(10,("sldb_Search: results: [%d]\n",count));
143                         result = LDAP_SUCCESS;
144                         errstr = NULL;
145                 } else if (count == 0) {
146                         DEBUG(10,("sldb_Search: no results\n"));
147                         result = LDAP_NO_SUCH_OBJECT;
148                         errstr = ldb_errstring(samdb);
149                 } else if (count == -1) {
150                         DEBUG(10,("sldb_Search: error\n"));
151                         result = LDAP_OTHER;
152                         errstr = ldb_errstring(samdb);
153                 }
154         }
155
156         done = &done_r->msg->r.SearchResultDone;
157         done->dn = NULL;
158         done->resultcode = result;
159         done->errormessage = (errstr?talloc_strdup(done_r,errstr):NULL);
160         done->referral = NULL;
161
162         talloc_free(local_ctx);
163
164         ldapsrv_queue_reply(call, done_r);
165         return NT_STATUS_OK;
166 }
167
168 static NTSTATUS sldb_Add(struct ldapsrv_partition *partition, struct ldapsrv_call *call,
169                                      struct ldap_AddRequest *r)
170 {
171         void *local_ctx;
172         struct ldb_dn *dn;
173         struct ldap_Result *add_result;
174         struct ldapsrv_reply *add_reply;
175         int ldb_ret;
176         struct ldb_context *samdb;
177         struct ldb_message *msg = NULL;
178         int result = LDAP_SUCCESS;
179         const char *errstr = NULL;
180         int i,j;
181
182         local_ctx = talloc_named(call, 0, "sldb_Add local memory context");
183         NT_STATUS_HAVE_NO_MEMORY(local_ctx);
184
185         samdb = ldapsrv_sam_connect(call);
186         NT_STATUS_HAVE_NO_MEMORY(samdb);
187
188         dn = ldb_dn_explode(local_ctx, r->dn);
189         VALID_DN_SYNTAX(dn,1);
190
191         DEBUG(10, ("sldb_add: dn: [%s]\n", r->dn));
192
193         msg = talloc(local_ctx, struct ldb_message);
194         NT_STATUS_HAVE_NO_MEMORY(msg);
195
196         msg->dn = dn;
197         msg->private_data = NULL;
198         msg->num_elements = 0;
199         msg->elements = NULL;
200
201         if (r->num_attributes > 0) {
202                 msg->num_elements = r->num_attributes;
203                 msg->elements = talloc_array(msg, struct ldb_message_element, msg->num_elements);
204                 NT_STATUS_HAVE_NO_MEMORY(msg->elements);
205
206                 for (i=0; i < msg->num_elements; i++) {
207                         msg->elements[i].name = discard_const_p(char, r->attributes[i].name);
208                         msg->elements[i].flags = 0;
209                         msg->elements[i].num_values = 0;
210                         msg->elements[i].values = NULL;
211                         
212                         if (r->attributes[i].num_values > 0) {
213                                 msg->elements[i].num_values = r->attributes[i].num_values;
214                                 msg->elements[i].values = talloc_array(msg, struct ldb_val, msg->elements[i].num_values);
215                                 NT_STATUS_HAVE_NO_MEMORY(msg->elements[i].values);
216
217                                 for (j=0; j < msg->elements[i].num_values; j++) {
218                                         if (!(r->attributes[i].values[j].length > 0)) {
219                                                 result = LDAP_OTHER;
220                                                 errstr = "Empty attribute values are not allowed";
221                                                 goto reply;
222                                         }
223                                         msg->elements[i].values[j].length = r->attributes[i].values[j].length;
224                                         msg->elements[i].values[j].data = r->attributes[i].values[j].data;                      
225                                 }
226                         } else {
227                                 result = LDAP_OTHER;
228                                 errstr = "No attribute values are not allowed";
229                                 goto reply;
230                         }
231                 }
232         } else {
233                 result = LDAP_OTHER;
234                 errstr = "No attributes are not allowed";
235                 goto reply;
236         }
237
238 reply:
239         add_reply = ldapsrv_init_reply(call, LDAP_TAG_AddResponse);
240         NT_STATUS_HAVE_NO_MEMORY(add_reply);
241
242         if (result == LDAP_SUCCESS) {
243                 ldb_ret = ldb_add(samdb, msg);
244                 if (ldb_ret == 0) {
245                         result = LDAP_SUCCESS;
246                         errstr = NULL;
247                 } else {
248                         /* currently we have no way to tell if there was an internal ldb error
249                          * or if the object was not found, return the most probable error
250                          */
251                         result = LDAP_OPERATIONS_ERROR;
252                         errstr = ldb_errstring(samdb);
253                 }
254         }
255
256         add_result = &add_reply->msg->r.AddResponse;
257         add_result->dn = NULL;
258         add_result->resultcode = result;
259         add_result->errormessage = (errstr?talloc_strdup(add_reply,errstr):NULL);
260         add_result->referral = NULL;
261
262         talloc_free(local_ctx);
263
264         ldapsrv_queue_reply(call, add_reply);
265         return NT_STATUS_OK;
266 }
267
268 static NTSTATUS sldb_Del(struct ldapsrv_partition *partition, struct ldapsrv_call *call,
269                                      struct ldap_DelRequest *r)
270 {
271         void *local_ctx;
272         struct ldb_dn *dn;
273         struct ldap_Result *del_result;
274         struct ldapsrv_reply *del_reply;
275         int ldb_ret;
276         struct ldb_context *samdb;
277         const char *errstr = NULL;
278         int result = LDAP_SUCCESS;
279
280         local_ctx = talloc_named(call, 0, "sldb_Del local memory context");
281         NT_STATUS_HAVE_NO_MEMORY(local_ctx);
282
283         samdb = ldapsrv_sam_connect(call);
284         NT_STATUS_HAVE_NO_MEMORY(samdb);
285
286         dn = ldb_dn_explode(local_ctx, r->dn);
287         VALID_DN_SYNTAX(dn,1);
288
289         DEBUG(10, ("sldb_Del: dn: [%s]\n", r->dn));
290
291 reply:
292         del_reply = ldapsrv_init_reply(call, LDAP_TAG_DelResponse);
293         NT_STATUS_HAVE_NO_MEMORY(del_reply);
294
295         if (result == LDAP_SUCCESS) {
296                 ldb_ret = ldb_delete(samdb, dn);
297                 if (ldb_ret == 0) {
298                         result = LDAP_SUCCESS;
299                         errstr = NULL;
300                 } else {
301                         /* currently we have no way to tell if there was an internal ldb error
302                          * or if the object was not found, return the most probable error
303                          */
304                         result = LDAP_NO_SUCH_OBJECT;
305                         errstr = ldb_errstring(samdb);
306                 }
307         }
308
309         del_result = &del_reply->msg->r.DelResponse;
310         del_result->dn = NULL;
311         del_result->resultcode = result;
312         del_result->errormessage = (errstr?talloc_strdup(del_reply,errstr):NULL);
313         del_result->referral = NULL;
314
315         talloc_free(local_ctx);
316
317         ldapsrv_queue_reply(call, del_reply);
318         return NT_STATUS_OK;
319 }
320
321 static NTSTATUS sldb_Modify(struct ldapsrv_partition *partition, struct ldapsrv_call *call,
322                                      struct ldap_ModifyRequest *r)
323 {
324         void *local_ctx;
325         struct ldb_dn *dn;
326         struct ldap_Result *modify_result;
327         struct ldapsrv_reply *modify_reply;
328         int ldb_ret;
329         struct ldb_context *samdb;
330         struct ldb_message *msg = NULL;
331         int result = LDAP_SUCCESS;
332         const char *errstr = NULL;
333         int i,j;
334
335         local_ctx = talloc_named(call, 0, "sldb_Modify local memory context");
336         NT_STATUS_HAVE_NO_MEMORY(local_ctx);
337
338         samdb = ldapsrv_sam_connect(call);
339         NT_STATUS_HAVE_NO_MEMORY(samdb);
340
341         dn = ldb_dn_explode(local_ctx, r->dn);
342         VALID_DN_SYNTAX(dn, 1);
343
344         DEBUG(10, ("sldb_modify: dn: [%s]\n", r->dn));
345
346         msg = talloc(local_ctx, struct ldb_message);
347         NT_STATUS_HAVE_NO_MEMORY(msg);
348
349         msg->dn = dn;
350         msg->private_data = NULL;
351         msg->num_elements = 0;
352         msg->elements = NULL;
353
354         if (r->num_mods > 0) {
355                 msg->num_elements = r->num_mods;
356                 msg->elements = talloc_array(msg, struct ldb_message_element, r->num_mods);
357                 NT_STATUS_HAVE_NO_MEMORY(msg->elements);
358
359                 for (i=0; i < msg->num_elements; i++) {
360                         msg->elements[i].name = discard_const_p(char, r->mods[i].attrib.name);
361                         msg->elements[i].num_values = 0;
362                         msg->elements[i].values = NULL;
363
364                         switch (r->mods[i].type) {
365                         default:
366                                 result = LDAP_PROTOCOL_ERROR;
367                                 errstr = "Invalid LDAP_MODIFY_* type";
368                                 goto reply;
369                         case LDAP_MODIFY_ADD:
370                                 msg->elements[i].flags = LDB_FLAG_MOD_ADD;
371                                 break;
372                         case LDAP_MODIFY_DELETE:
373                                 msg->elements[i].flags = LDB_FLAG_MOD_DELETE;
374                                 break;
375                         case LDAP_MODIFY_REPLACE:
376                                 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
377                                 break;
378                         }
379
380                         msg->elements[i].num_values = r->mods[i].attrib.num_values;
381                         if (msg->elements[i].num_values > 0) {
382                                 msg->elements[i].values = talloc_array(msg, struct ldb_val, msg->elements[i].num_values);
383                                 NT_STATUS_HAVE_NO_MEMORY(msg->elements[i].values);
384
385                                 for (j=0; j < msg->elements[i].num_values; j++) {
386                                         if (!(r->mods[i].attrib.values[j].length > 0)) {
387                                                 result = LDAP_OTHER;
388                                                 errstr = "Empty attribute values are not allowed";
389                                                 goto reply;
390                                         }
391                                         msg->elements[i].values[j].length = r->mods[i].attrib.values[j].length;
392                                         msg->elements[i].values[j].data = r->mods[i].attrib.values[j].data;                     
393                                 }
394                         }
395                 }
396         } else {
397                 result = LDAP_OTHER;
398                 errstr = "No mods are not allowed";
399                 goto reply;
400         }
401
402 reply:
403         modify_reply = ldapsrv_init_reply(call, LDAP_TAG_ModifyResponse);
404         NT_STATUS_HAVE_NO_MEMORY(modify_reply);
405
406         if (result == LDAP_SUCCESS) {
407                 ldb_ret = ldb_modify(samdb, msg);
408                 if (ldb_ret == 0) {
409                         result = LDAP_SUCCESS;
410                         errstr = NULL;
411                 } else {
412                         /* currently we have no way to tell if there was an internal ldb error
413                          * or if the object was not found, return the most probable error
414                          */
415                         result = LDAP_OPERATIONS_ERROR;
416                         errstr = ldb_errstring(samdb);
417                 }
418         }
419
420         modify_result = &modify_reply->msg->r.AddResponse;
421         modify_result->dn = NULL;
422         modify_result->resultcode = result;
423         modify_result->errormessage = (errstr?talloc_strdup(modify_reply,errstr):NULL);
424         modify_result->referral = NULL;
425
426         talloc_free(local_ctx);
427
428         ldapsrv_queue_reply(call, modify_reply);
429         return NT_STATUS_OK;
430 }
431
432 static NTSTATUS sldb_Compare(struct ldapsrv_partition *partition, struct ldapsrv_call *call,
433                                      struct ldap_CompareRequest *r)
434 {
435         void *local_ctx;
436         struct ldb_dn *dn;
437         struct ldap_Result *compare;
438         struct ldapsrv_reply *compare_r;
439         int result = LDAP_SUCCESS;
440         struct ldb_context *samdb;
441         struct ldb_message **res = NULL;
442         const char *attrs[1];
443         const char *errstr = NULL;
444         const char *filter = NULL;
445         int count;
446
447         local_ctx = talloc_named(call, 0, "sldb_Compare local_memory_context");
448         NT_STATUS_HAVE_NO_MEMORY(local_ctx);
449
450         samdb = ldapsrv_sam_connect(call);
451         NT_STATUS_HAVE_NO_MEMORY(samdb);
452
453         dn = ldb_dn_explode(local_ctx, r->dn);
454         VALID_DN_SYNTAX(dn, 1);
455
456         DEBUG(10, ("sldb_Compare: dn: [%s]\n", r->dn));
457         filter = talloc_asprintf(local_ctx, "(%s=%*s)", r->attribute, 
458                                  (int)r->value.length, r->value.data);
459         NT_STATUS_HAVE_NO_MEMORY(filter);
460
461         DEBUGADD(10, ("sldb_Compare: attribute: [%s]\n", filter));
462
463         attrs[0] = NULL;
464
465 reply:
466         compare_r = ldapsrv_init_reply(call, LDAP_TAG_CompareResponse);
467         NT_STATUS_HAVE_NO_MEMORY(compare_r);
468
469         if (result == LDAP_SUCCESS) {
470                 count = ldb_search(samdb, dn, LDB_SCOPE_BASE, filter, attrs, &res);
471                 talloc_steal(samdb, res);
472                 if (count == 1) {
473                         DEBUG(10,("sldb_Compare: matched\n"));
474                         result = LDAP_COMPARE_TRUE;
475                         errstr = NULL;
476                 } else if (count == 0) {
477                         DEBUG(10,("sldb_Compare: doesn't matched\n"));
478                         result = LDAP_COMPARE_FALSE;
479                         errstr = NULL;
480                 } else if (count > 1) {
481                         result = LDAP_OTHER;
482                         errstr = "too many objects match";
483                         DEBUG(10,("sldb_Compare: %d results: %s\n", count, errstr));
484                 } else if (count == -1) {
485                         result = LDAP_OTHER;
486                         errstr = ldb_errstring(samdb);
487                         DEBUG(10,("sldb_Compare: error: %s\n", errstr));
488                 }
489         }
490
491         compare = &compare_r->msg->r.CompareResponse;
492         compare->dn = NULL;
493         compare->resultcode = result;
494         compare->errormessage = (errstr?talloc_strdup(compare_r,errstr):NULL);
495         compare->referral = NULL;
496
497         talloc_free(local_ctx);
498
499         ldapsrv_queue_reply(call, compare_r);
500         return NT_STATUS_OK;
501 }
502
503 static NTSTATUS sldb_ModifyDN(struct ldapsrv_partition *partition, struct ldapsrv_call *call, struct ldap_ModifyDNRequest *r)
504 {
505         void *local_ctx;
506         struct ldb_dn *olddn, *newdn, *newrdn;
507         struct ldb_dn *parentdn = NULL;
508         struct ldap_Result *modifydn;
509         struct ldapsrv_reply *modifydn_r;
510         int ldb_ret;
511         struct ldb_context *samdb;
512         const char *errstr = NULL;
513         int result = LDAP_SUCCESS;
514
515         local_ctx = talloc_named(call, 0, "sldb_ModifyDN local memory context");
516         NT_STATUS_HAVE_NO_MEMORY(local_ctx);
517
518         samdb = ldapsrv_sam_connect(call);
519         NT_STATUS_HAVE_NO_MEMORY(samdb);
520
521         olddn = ldb_dn_explode(local_ctx, r->dn);
522         VALID_DN_SYNTAX(olddn, 2);
523
524         newrdn = ldb_dn_explode(local_ctx, r->newrdn);
525         VALID_DN_SYNTAX(newrdn, 1);
526
527         DEBUG(10, ("sldb_ModifyDN: olddn: [%s]\n", r->dn));
528         DEBUG(10, ("sldb_ModifyDN: newrdn: [%s]\n", r->newrdn));
529
530         /* we can't handle the rename if we should not remove the old dn */
531         if (!r->deleteolddn) {
532                 result = LDAP_UNWILLING_TO_PERFORM;
533                 errstr = "Old RDN must be deleted";
534                 goto reply;
535         }
536
537         if (newrdn->comp_num > 1) {
538                 result = LDAP_NAMING_VIOLATION;
539                 errstr = "Error new RDN invalid";
540                 goto reply;
541         }
542
543         if (r->newsuperior) {
544                 parentdn = ldb_dn_explode(local_ctx, r->newsuperior);
545                 VALID_DN_SYNTAX(parentdn, 0);
546                 DEBUG(10, ("sldb_ModifyDN: newsuperior: [%s]\n", r->newsuperior));
547                 
548                 if (parentdn->comp_num < 1) {
549                         result = LDAP_AFFECTS_MULTIPLE_DSAS;
550                         errstr = "Error new Superior DN invalid";
551                         goto reply;
552                 }
553         }
554
555         if (!parentdn) {
556                 parentdn = ldb_dn_get_parent(local_ctx, olddn);
557                 NT_STATUS_HAVE_NO_MEMORY(parentdn);
558         }
559
560         newdn = ldb_dn_make_child(local_ctx, ldb_dn_get_rdn(local_ctx, newrdn), parentdn);
561         NT_STATUS_HAVE_NO_MEMORY(newdn);
562
563 reply:
564         modifydn_r = ldapsrv_init_reply(call, LDAP_TAG_ModifyDNResponse);
565         NT_STATUS_HAVE_NO_MEMORY(modifydn_r);
566
567         if (result == LDAP_SUCCESS) {
568                 ldb_ret = ldb_rename(samdb, olddn, newdn);
569                 if (ldb_ret == 0) {
570                         result = LDAP_SUCCESS;
571                         errstr = NULL;
572                 } else {
573                         /* currently we have no way to tell if there was an internal ldb error
574                          * or if the object was not found, return the most probable error
575                          */
576                         result = LDAP_NO_SUCH_OBJECT;
577                         errstr = ldb_errstring(samdb);
578                 }
579         }
580
581         modifydn = &modifydn_r->msg->r.ModifyDNResponse;
582         modifydn->dn = NULL;
583         modifydn->resultcode = result;
584         modifydn->errormessage = (errstr?talloc_strdup(modifydn_r,errstr):NULL);
585         modifydn->referral = NULL;
586
587         talloc_free(local_ctx);
588
589         ldapsrv_queue_reply(call, modifydn_r);
590         return NT_STATUS_OK;
591 }
592
593 static const struct ldapsrv_partition_ops sldb_ops = {
594         .Search         = sldb_Search,
595         .Add            = sldb_Add,
596         .Del            = sldb_Del,
597         .Modify         = sldb_Modify,
598         .Compare        = sldb_Compare,
599         .ModifyDN       = sldb_ModifyDN
600 };
601
602 const struct ldapsrv_partition_ops *ldapsrv_get_sldb_partition_ops(void)
603 {
604         return &sldb_ops;
605 }