r8663: Since simo constructed the samdb module, he and tridge have worked on
[vlendec/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / samldb.c
1 /* 
2    SAM ldb module
3
4    Copyright (C) Simo Sorce  2004
5
6    * NOTICE: this module is NOT released under the GNU LGPL license as
7    * other ldb code. This module is release under the GNU GPL v2 or
8    * later license.
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb samldb module
29  *
30  *  Description: add embedded user/group creation functionality
31  *
32  *  Author: Simo Sorce
33  */
34
35 #include "includes.h"
36 #include "lib/ldb/include/ldb.h"
37 #include "lib/ldb/include/ldb_private.h"
38 #include "system/time.h"
39 #include "librpc/gen_ndr/ndr_security.h"
40
41 #define SAM_ACCOUNT_NAME_BASE "$000000-000000000000"
42
43 struct private_data {
44         const char *error_string;
45 };
46
47 static int samldb_search(struct ldb_module *module, const char *base,
48                                   enum ldb_scope scope, const char *expression,
49                                   const char * const *attrs, struct ldb_message ***res)
50 {
51         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_search\n");
52         return ldb_next_search(module, base, scope, expression, attrs, res);
53 }
54
55 static int samldb_search_bytree(struct ldb_module *module, const char *base,
56                                 enum ldb_scope scope, struct ldb_parse_tree *tree,
57                                 const char * const *attrs, struct ldb_message ***res)
58 {
59         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_search\n");
60         return ldb_next_search_bytree(module, base, scope, tree, attrs, res);
61 }
62
63 /*
64   allocate a new id, attempting to do it atomically
65   return 0 on failure, the id on success
66 */
67 static int samldb_allocate_next_rid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
68                                    const char *dn, uint32_t *id)
69 {
70         const char * const attrs[2] = { "nextRid", NULL };
71         struct ldb_message **res = NULL;
72         struct ldb_message msg;
73         int ret;
74         const char *str;
75         struct ldb_val vals[2];
76         struct ldb_message_element els[2];
77
78         ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, "nextRid=*", attrs, &res);
79         if (ret != 1) {
80                 if (res) talloc_free(res);
81                 return -1;
82         }
83         str = ldb_msg_find_string(res[0], "nextRid", NULL);
84         if (str == NULL) {
85                 ldb_debug(ldb, LDB_DEBUG_FATAL, "attribute nextRid not found in %s\n", dn);
86                 talloc_free(res);
87                 return -1;
88         }
89
90         *id = strtol(str, NULL, 0);
91         if ((*id)+1 == 0) {
92                 /* out of IDs ! */
93                 ldb_debug(ldb, LDB_DEBUG_FATAL, "Are we out of valid IDs ?\n");
94                 talloc_free(res);
95                 return -1;
96         }
97         talloc_free(res);
98
99         /* we do a delete and add as a single operation. That prevents
100            a race */
101         ZERO_STRUCT(msg);
102         msg.dn = talloc_strdup(mem_ctx, dn);
103         if (!msg.dn) {
104                 return -1;
105         }
106         msg.num_elements = 2;
107         msg.elements = els;
108
109         els[0].num_values = 1;
110         els[0].values = &vals[0];
111         els[0].flags = LDB_FLAG_MOD_DELETE;
112         els[0].name = talloc_strdup(mem_ctx, "nextRid");
113         if (!els[0].name) {
114                 return -1;
115         }
116
117         els[1].num_values = 1;
118         els[1].values = &vals[1];
119         els[1].flags = LDB_FLAG_MOD_ADD;
120         els[1].name = els[0].name;
121
122         vals[0].data = talloc_asprintf(mem_ctx, "%u", *id);
123         if (!vals[0].data) {
124                 return -1;
125         }
126         vals[0].length = strlen(vals[0].data);
127
128         vals[1].data = talloc_asprintf(mem_ctx, "%u", (*id)+1);
129         if (!vals[1].data) {
130                 return -1;
131         }
132         vals[1].length = strlen(vals[1].data);
133
134         ret = ldb_modify(ldb, &msg);
135         if (ret != 0) {
136                 return 1;
137         }
138
139         (*id)++;
140
141         return 0;
142 }
143
144 static char *samldb_search_domain(struct ldb_module *module, TALLOC_CTX *mem_ctx, const char *dn)
145 {
146         const char *sdn;
147         struct ldb_message **res = NULL;
148         int ret = 0;
149
150         sdn = dn;
151         while ((sdn = strchr(sdn, ',')) != NULL) {
152
153                 sdn++;
154
155                 ret = ldb_search(module->ldb, sdn, LDB_SCOPE_BASE, "objectClass=domain", NULL, &res);
156                 talloc_free(res);
157
158                 if (ret == 1)
159                         break;
160         }
161
162         if (ret != 1) {
163                 return NULL;
164         }
165
166         return talloc_strdup(mem_ctx, sdn);
167 }
168
169 /* search the domain related to the provided dn
170    allocate a new RID for the domain
171    return the new sid string
172 */
173 static struct dom_sid *samldb_get_new_sid(struct ldb_module *module, 
174                                           TALLOC_CTX *mem_ctx, const char *obj_dn)
175 {
176         const char * const attrs[2] = { "objectSid", NULL };
177         struct ldb_message **res = NULL;
178         const char *dom_dn;
179         uint32_t rid;
180         int ret, tries = 10;
181         struct dom_sid *dom_sid, *obj_sid;
182
183         /* get the domain component part of the provided dn */
184
185         /* FIXME: quick search here, I think we should use something like
186            ldap_parse_dn here to be 100% sure we get the right domain dn */
187
188         /* FIXME: "dc=" is probably not utf8 safe either,
189            we need a multibyte safe substring search function here */
190         
191         dom_dn = samldb_search_domain(module, mem_ctx, obj_dn);
192         if (dom_dn == NULL) {
193                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Invalid dn (%s) not child of a domain object!\n", obj_dn);
194                 return NULL;
195         }
196
197         /* find the domain sid */
198
199         ret = ldb_search(module->ldb, dom_dn, LDB_SCOPE_BASE, "objectSid=*", attrs, &res);
200         if (ret != 1) {
201                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_get_new_sid: error retrieving domain sid!\n");
202                 talloc_free(res);
203                 return NULL;
204         }
205
206         dom_sid = samdb_result_dom_sid(res, res[0], "objectSid");
207         if (dom_sid == NULL) {
208                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_get_new_sid: error retrieving domain sid!\n");
209                 talloc_free(res);
210                 return NULL;
211         }
212
213         /* allocate a new Rid for the domain */
214
215         /* we need to try multiple times to cope with two account
216            creations at the same time */
217         while (tries--) {
218                 ret = samldb_allocate_next_rid(module->ldb, mem_ctx, dom_dn, &rid);
219                 if (ret != 1) {
220                         break;
221                 }
222         }
223         if (ret != 0) {
224                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Failed to increment nextRid of %s\n", dom_dn);
225                 talloc_free(res);
226                 return NULL;
227         }
228
229         /* return the new object sid */
230         obj_sid = dom_sid_add_rid(mem_ctx, dom_sid, rid);
231
232         talloc_free(res);
233
234         return obj_sid;
235 }
236
237 static char *samldb_generate_samAccountName(const void *mem_ctx) {
238         char *name;
239
240         name = talloc_strdup(mem_ctx, SAM_ACCOUNT_NAME_BASE);
241         /* TODO: randomize name */      
242
243         return name;
244 }
245
246 static BOOL samldb_get_rdn_and_basedn(void *mem_ctx, const char *dn, struct ldb_dn_component **rdn, char **base_dn)
247 {
248         struct ldb_dn *dn_exploded = ldb_dn_explode(mem_ctx, dn);
249         struct ldb_dn base_dn_exploded;
250
251         if (!dn_exploded) {
252                 return False;
253         }
254         
255         if (dn_exploded->comp_num < 1) {
256                 return False;
257         }
258         
259         if (dn_exploded->comp_num < 2) {
260                 *base_dn = NULL;
261         } else {
262                 base_dn_exploded.comp_num = dn_exploded->comp_num - 1;
263                 base_dn_exploded.components = &dn_exploded->components[1];
264                 
265                 *base_dn = ldb_dn_linearize(mem_ctx, &base_dn_exploded);
266         }
267
268         *rdn = &dn_exploded->components[0];
269         return True;
270 }
271
272 /* if value is not null also check for attribute to have exactly that value */
273 static struct ldb_message_element *samldb_find_attribute(const struct ldb_message *msg, const char *name, const char *value)
274 {
275         int i, j;
276
277         for (i = 0; i < msg->num_elements; i++) {
278                 if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
279                         if (!value) {
280                                 return &msg->elements[i];
281                         }
282                         for (j = 0; j < msg->elements[i].num_values; j++) {
283                                 if (strcasecmp(value, msg->elements[i].values[j].data) == 0) {
284                                         return &msg->elements[i];
285                                 }
286                         }
287                 }
288         }
289
290         return NULL;
291 }
292
293 static BOOL samldb_msg_add_string(struct ldb_module *module, struct ldb_message *msg, const char *name, const char *value)
294 {
295         char *aname = talloc_strdup(msg, name);
296         char *aval = talloc_strdup(msg, value);
297
298         if (aname == NULL || aval == NULL) {
299                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_msg_add_string: talloc_strdup failed!\n");
300                 return False;
301         }
302
303         if (ldb_msg_add_string(module->ldb, msg, aname, aval) != 0) {
304                 return False;
305         }
306
307         return True;
308 }
309
310 static BOOL samldb_msg_add_sid(struct ldb_module *module, struct ldb_message *msg, const char *name, const struct dom_sid *sid)
311 {
312         struct ldb_val v;
313         NTSTATUS status;
314         status = ndr_push_struct_blob(&v, msg, sid, 
315                                       (ndr_push_flags_fn_t)ndr_push_dom_sid);
316         if (!NT_STATUS_IS_OK(status)) {
317                 return -1;
318         }
319         return (ldb_msg_add_value(module->ldb, msg, name, &v) == 0);
320 }
321
322 static BOOL samldb_find_or_add_attribute(struct ldb_module *module, struct ldb_message *msg, const char *name, const char *value, const char *set_value)
323 {
324         if (samldb_find_attribute(msg, name, value) == NULL) {
325                 return samldb_msg_add_string(module, msg, name, set_value);
326         }
327         return True;
328 }
329
330 static int samldb_copy_template(struct ldb_module *module, struct ldb_message *msg, const char *filter)
331 {
332         struct ldb_message **res, *t;
333         int ret, i, j;
334         
335
336         /* pull the template record */
337         ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &res);
338         if (ret != 1) {
339                 ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb: ERROR: template '%s' matched %d records\n", filter, ret);
340                 return -1;
341         }
342         t = res[0];
343
344         for (i = 0; i < t->num_elements; i++) {
345                 struct ldb_message_element *el = &t->elements[i];
346                 /* some elements should not be copied from the template */
347                 if (strcasecmp(el->name, "cn") == 0 ||
348                     strcasecmp(el->name, "name") == 0 ||
349                     strcasecmp(el->name, "sAMAccountName") == 0 ||
350                     strcasecmp(el->name, "objectGUID") == 0) {
351                         continue;
352                 }
353                 for (j = 0; j < el->num_values; j++) {
354                         if (strcasecmp(el->name, "objectClass") == 0 &&
355                             (strcasecmp((char *)el->values[j].data, "Template") == 0 ||
356                              strcasecmp((char *)el->values[j].data, "userTemplate") == 0 ||
357                              strcasecmp((char *)el->values[j].data, "groupTemplate") == 0 ||
358                              strcasecmp((char *)el->values[j].data, "foreignSecurityTemplate") == 0 ||
359                              strcasecmp((char *)el->values[j].data, "aliasTemplate") == 0 || 
360                              strcasecmp((char *)el->values[j].data, "trustedDomainTemplate") == 0 || 
361                              strcasecmp((char *)el->values[j].data, "secretTemplate") == 0)) {
362                                 continue;
363                         }
364                         if ( ! samldb_find_or_add_attribute(module, msg, el->name, 
365                                                             NULL,
366                                                             (char *)el->values[j].data)) {
367                                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Attribute adding failed...\n");
368                                 talloc_free(res);
369                                 return -1;
370                         }
371                 }
372         }
373
374         talloc_free(res);
375
376         return 0;
377 }
378
379 static struct ldb_message *samldb_fill_group_object(struct ldb_module *module, const struct ldb_message *msg)
380 {
381         struct ldb_message *msg2;
382         struct ldb_message_element *attribute;
383         struct ldb_dn_component *rdn;
384         char *basedn;
385
386         if (samldb_find_attribute(msg, "objectclass", "group") == NULL) {
387                 return NULL;
388         }
389
390         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_fill_group_object\n");
391
392         /* build the new msg */
393         msg2 = ldb_msg_copy(module->ldb, msg);
394         if (!msg2) {
395                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: ldb_msg_copy failed!\n");
396                 return NULL;
397         }
398
399         if (samldb_copy_template(module, msg2, "(&(CN=TemplateGroup)(objectclass=groupTemplate))") != 0) {
400                 ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb_fill_group_object: Error copying template!\n");
401                 return NULL;
402         }
403
404         if ( ! samldb_get_rdn_and_basedn(msg2, msg2->dn, &rdn, &basedn)) {
405                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad DN (%s)!\n", msg2->dn);
406                 return NULL;
407         }
408         if (strcasecmp(rdn->name, "cn") != 0) {
409                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad RDN (%s) for group!\n", rdn->name);
410                 return NULL;
411         }
412
413         if ((attribute = samldb_find_attribute(msg2, "cn", NULL)) != NULL) {
414                 if (strcasecmp(rdn->value.data, attribute->values[0].data) != 0) {
415                         ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad Attribute Syntax for CN\n");
416                         return NULL;
417                 }
418         } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "cn" attribute */
419                 if ( ! ldb_msg_add_value(module->ldb, msg2, "cn", &rdn->value)) {
420                         return NULL;
421                 }
422         }
423
424         if ((attribute = samldb_find_attribute(msg2, "name", NULL)) != NULL) {
425                 if (strcasecmp(rdn->value.data, attribute->values[0].data) != 0) {
426
427                         return NULL;
428                 }
429         } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "name" attribute */
430                 if ( !ldb_msg_add_value(module->ldb, msg2, "name", &rdn->value)) {
431                         return NULL;
432                 }
433         }
434
435         if ((attribute = samldb_find_attribute(msg2, "objectSid", NULL)) == NULL ) {
436                 struct dom_sid *sid = samldb_get_new_sid(module, msg2, msg2->dn);
437                 if (sid == NULL) {
438                         ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: internal error! Can't generate new sid\n");
439                         return NULL;
440                 }
441
442                 if (!samldb_msg_add_sid(module, msg2, "objectSid", sid)) {
443                         talloc_free(sid);
444                         return NULL;
445                 }
446                 talloc_free(sid);
447         }
448
449         if ( ! samldb_find_or_add_attribute(module, msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
450                 return NULL;
451         }
452
453         talloc_steal(msg, msg2);
454
455         return msg2;
456 }
457
458 static struct ldb_message *samldb_fill_user_or_computer_object(struct ldb_module *module, const struct ldb_message *msg)
459 {
460         struct ldb_message *msg2;
461         struct ldb_message_element *attribute;
462         struct ldb_dn_component *rdn;
463         char *basedn;
464
465         if ((samldb_find_attribute(msg, "objectclass", "user") == NULL) && 
466             (samldb_find_attribute(msg, "objectclass", "computer") == NULL)) {
467                 return NULL;
468         }
469
470         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_fill_user_or_computer_object\n");
471
472         /* build the new msg */
473         msg2 = ldb_msg_copy(module->ldb, msg);
474         if (!msg2) {
475                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: ldb_msg_copy failed!\n");
476                 return NULL;
477         }
478
479         if (samldb_find_attribute(msg, "objectclass", "computer") != NULL) {
480                 if (samldb_copy_template(module, msg2, "(&(CN=TemplateMemberServer)(objectclass=userTemplate))") != 0) {
481                         ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb_fill_user_or_computer_object: Error copying computer template!\n");
482                         return NULL;
483                 }
484         } else {
485                 if (samldb_copy_template(module, msg2, "(&(CN=TemplateUser)(objectclass=userTemplate))") != 0) {
486                         ldb_debug(module->ldb, LDB_DEBUG_WARNING, "samldb_fill_user_or_computer_object: Error copying user template!\n");
487                         return NULL;
488                 }
489         }
490
491         if ( ! samldb_get_rdn_and_basedn(msg2, msg2->dn, &rdn, &basedn)) {
492                 return NULL;
493         }
494         if (strcasecmp(rdn->name, "cn") != 0) {
495                 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: Bad RDN (%s) for group!\n", rdn->name);
496                 return NULL;
497         }
498
499         /* if the only attribute was: "objectclass: computer", then make sure we also add "user" objectclass */
500         if ( ! samldb_find_or_add_attribute(module, msg2, "objectclass", "user", "user")) {
501                 return NULL;
502         }
503
504         if ((attribute = samldb_find_attribute(msg2, "cn", NULL)) != NULL) {
505                 if (strcasecmp(rdn->value.data, attribute->values[0].data) != 0) {
506                         ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_group_object: Bad Attribute Syntax for CN\n");
507                         return NULL;
508                 }
509         } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "cn" attribute */
510                 if ( !ldb_msg_add_value(module->ldb, msg2, "cn", &rdn->value)) {
511                         return NULL;
512                 }
513         }
514
515         if ((attribute = samldb_find_attribute(msg2, "name", NULL)) != NULL) {
516                 if (strcasecmp(rdn->value.data, attribute->values[0].data) != 0) {
517                         ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: Bad Attribute Syntax for name\n");
518                         return NULL;
519                 }
520         } else { /* FIXME: remove this if ldb supports natively aliasing between the rdn and the "name" attribute */
521                 if ( !ldb_msg_add_value(module->ldb, msg2, "name", &rdn->value)) {
522                         return NULL;
523                 }
524         }
525
526         if ((attribute = samldb_find_attribute(msg2, "objectSid", NULL)) == NULL ) {
527                 struct dom_sid *sid;
528                 sid = samldb_get_new_sid(module, msg2, msg2->dn);
529                 if (sid == NULL) {
530                         ldb_debug(module->ldb, LDB_DEBUG_FATAL, "samldb_fill_user_or_computer_object: internal error! Can't generate new sid\n");
531                         return NULL;
532                 }
533
534                 if ( ! samldb_msg_add_sid(module, msg2, "objectSid", sid)) {
535                         talloc_free(sid);
536                         return NULL;
537                 }
538                 talloc_free(sid);
539         }
540
541         if ( ! samldb_find_or_add_attribute(module, msg2, "sAMAccountName", NULL, samldb_generate_samAccountName(msg2))) {
542                 return NULL;
543         }
544
545         /* TODO: objectCategory, userAccountControl, badPwdCount, codePage, countryCode, badPasswordTime, lastLogoff, lastLogon, pwdLastSet, primaryGroupID, accountExpires, logonCount */
546
547         return msg2;
548 }
549
550 /* add_record */
551 static int samldb_add_record(struct ldb_module *module, const struct ldb_message *msg)
552 {
553         struct ldb_message *msg2 = NULL;
554         int ret;
555
556         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_add_record\n");
557
558         if (msg->dn[0] == '@') { /* do not manipulate our control entries */
559                 return ldb_next_add_record(module, msg);
560         }
561
562         /* is user or computer?  add all relevant missing objects */
563         msg2 = samldb_fill_user_or_computer_object(module, msg);
564
565         /* is group? add all relevant missing objects */
566         if ( ! msg2 ) {
567                 msg2 = samldb_fill_group_object(module, msg);
568         }
569
570         if (msg2) {
571                 ret = ldb_next_add_record(module, msg2);
572         } else {
573                 ret = ldb_next_add_record(module, msg);
574         }
575
576         return ret;
577 }
578
579 /* modify_record: change modifyTimestamp as well */
580 static int samldb_modify_record(struct ldb_module *module, const struct ldb_message *msg)
581 {
582         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_modify_record\n");
583         return ldb_next_modify_record(module, msg);
584 }
585
586 static int samldb_delete_record(struct ldb_module *module, const char *dn)
587 {
588         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_delete_record\n");
589         return ldb_next_delete_record(module, dn);
590 }
591
592 static int samldb_rename_record(struct ldb_module *module, const char *olddn, const char *newdn)
593 {
594         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_rename_record\n");
595         return ldb_next_rename_record(module, olddn, newdn);
596 }
597
598 static int samldb_lock(struct ldb_module *module, const char *lockname)
599 {
600         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_lock\n");
601         return ldb_next_named_lock(module, lockname);
602 }
603
604 static int samldb_unlock(struct ldb_module *module, const char *lockname)
605 {
606         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_unlock\n");
607         return ldb_next_named_unlock(module, lockname);
608 }
609
610 /* return extended error information */
611 static const char *samldb_errstring(struct ldb_module *module)
612 {
613         struct private_data *data = (struct private_data *)module->private_data;
614
615         ldb_debug(module->ldb, LDB_DEBUG_TRACE, "samldb_errstring\n");
616         if (data->error_string) {
617                 const char *error;
618
619                 error = data->error_string;
620                 data->error_string = NULL;
621                 return error;
622         }
623
624         return ldb_next_errstring(module);
625 }
626
627 static int samldb_destructor(void *module_ctx)
628 {
629         /* struct ldb_module *ctx = module_ctx; */
630         /* put your clean-up functions here */
631         return 0;
632 }
633
634 static const struct ldb_module_ops samldb_ops = {
635         .name          = "samldb",
636         .search        = samldb_search,
637         .search_bytree = samldb_search_bytree,
638         .add_record    = samldb_add_record,
639         .modify_record = samldb_modify_record,
640         .delete_record = samldb_delete_record,
641         .rename_record = samldb_rename_record,
642         .named_lock    = samldb_lock,
643         .named_unlock  = samldb_unlock,
644         .errstring     = samldb_errstring
645 };
646
647
648 /* the init function */
649 #ifdef HAVE_DLOPEN_DISABLED
650  struct ldb_module *init_module(struct ldb_context *ldb, const char *options[])
651 #else
652 struct ldb_module *samldb_module_init(struct ldb_context *ldb, const char *options[])
653 #endif
654 {
655         struct ldb_module *ctx;
656         struct private_data *data;
657
658         ctx = talloc(ldb, struct ldb_module);
659         if (!ctx)
660                 return NULL;
661
662         data = talloc(ctx, struct private_data);
663         if (!data) {
664                 talloc_free(ctx);
665                 return NULL;
666         }
667
668         data->error_string = NULL;
669         ctx->private_data = data;
670         ctx->ldb = ldb;
671         ctx->prev = ctx->next = NULL;
672         ctx->ops = &samldb_ops;
673
674         talloc_set_destructor(ctx, samldb_destructor);
675
676         return ctx;
677 }