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