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