r23679: invocationID is a GUID too.
[abartlet/samba.git/.git] / source4 / dsdb / samdb / ldb_modules / entryUUID.c
1 /* 
2    ldb database module
3
4    LDAP semantics mapping module
5
6    Copyright (C) Jelmer Vernooij 2005
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 /* 
25    This module relies on ldb_map to do all the real work, but performs
26    some of the trivial mappings between AD semantics and that provided
27    by OpenLDAP and similar servers.
28 */
29
30 #include "includes.h"
31 #include "ldb/include/ldb.h"
32 #include "ldb/include/ldb_private.h"
33 #include "ldb/include/ldb_errors.h"
34 #include "ldb/ldb_map/ldb_map.h"
35
36 #include "librpc/gen_ndr/ndr_misc.h"
37 #include "librpc/ndr/libndr.h"
38
39 struct entryUUID_private {
40         struct ldb_result *objectclass_res;     
41         struct ldb_dn **base_dns;
42 };
43
44 static struct ldb_val encode_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
45 {
46         struct GUID guid;
47         NTSTATUS status = GUID_from_string((char *)val->data, &guid);
48         struct ldb_val out = data_blob(NULL, 0);
49
50         if (!NT_STATUS_IS_OK(status)) {
51                 return out;
52         }
53         status = ndr_push_struct_blob(&out, ctx, &guid, 
54                                       (ndr_push_flags_fn_t)ndr_push_GUID);
55         if (!NT_STATUS_IS_OK(status)) {
56                 return out;
57         }
58
59         return out;
60 }
61
62 static struct ldb_val guid_always_string(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
63 {
64         struct GUID *guid;
65         NTSTATUS status;
66         struct ldb_val out = data_blob(NULL, 0);
67         if (val->length >= 32 && val->data[val->length] == '\0') {
68                 ldb_handler_copy(module->ldb, ctx, val, &out);
69         } else {
70                 guid = talloc(ctx, struct GUID);
71                 if (guid == NULL) {
72                         return out;
73                 }
74                 status = ndr_pull_struct_blob(val, guid, guid, 
75                                               (ndr_pull_flags_fn_t)ndr_pull_GUID);
76                 if (!NT_STATUS_IS_OK(status)) {
77                         talloc_free(guid);
78                         return out;
79                 }
80                 out = data_blob_string_const(GUID_string(ctx, guid));
81                 talloc_free(guid);
82         }
83         return out;
84 }
85
86 static struct ldb_val encode_ns_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
87 {
88         struct GUID guid;
89         NTSTATUS status = NS_GUID_from_string((char *)val->data, &guid);
90         struct ldb_val out = data_blob(NULL, 0);
91
92         if (!NT_STATUS_IS_OK(status)) {
93                 return out;
94         }
95         status = ndr_push_struct_blob(&out, ctx, &guid, 
96                                       (ndr_push_flags_fn_t)ndr_push_GUID);
97         if (!NT_STATUS_IS_OK(status)) {
98                 return out;
99         }
100
101         return out;
102 }
103
104 static struct ldb_val guid_ns_string(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
105 {
106         NTSTATUS status;
107         struct ldb_val out = data_blob(NULL, 0);
108         if (val->length >= 32 && val->data[val->length] == '\0') {
109                 struct GUID guid;
110                 GUID_from_string((char *)val->data, &guid);
111                 out = data_blob_string_const(NS_GUID_string(ctx, &guid));
112         } else {
113                 struct GUID *guid_p;
114                 guid_p = talloc(ctx, struct GUID);
115                 if (guid_p == NULL) {
116                         return out;
117                 }
118                 status = ndr_pull_struct_blob(val, guid_p, guid_p, 
119                                               (ndr_pull_flags_fn_t)ndr_pull_GUID);
120                 if (!NT_STATUS_IS_OK(status)) {
121                         talloc_free(guid_p);
122                         return out;
123                 }
124                 out = data_blob_string_const(NS_GUID_string(ctx, guid_p));
125                 talloc_free(guid_p);
126         }
127         return out;
128 }
129
130 /* The backend holds binary sids, so just copy them back */
131 static struct ldb_val val_copy(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
132 {
133         struct ldb_val out = data_blob(NULL, 0);
134         ldb_handler_copy(module->ldb, ctx, val, &out);
135
136         return out;
137 }
138
139 /* Ensure we always convert sids into binary, so the backend doesn't have to know about both forms */
140 static struct ldb_val sid_always_binary(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
141 {
142         struct ldb_val out = data_blob(NULL, 0);
143         const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(module->ldb, "objectSid");
144
145         if (a->syntax->canonicalise_fn(module->ldb, ctx, val, &out) != LDB_SUCCESS) {
146                 return data_blob(NULL, 0);
147         }
148
149         return out;
150 }
151
152 static struct ldb_val objectCategory_always_dn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
153 {
154         int i;
155         struct map_private *map_private;
156         struct entryUUID_private *entryUUID_private;
157         struct ldb_result *list;
158
159         if (ldb_dn_validate(ldb_dn_new(ctx, module->ldb, (const char *)val->data))) {
160                 return *val;
161         }
162         map_private = talloc_get_type(module->private_data, struct map_private);
163
164         entryUUID_private = talloc_get_type(map_private->caller_private, struct entryUUID_private);
165         list = entryUUID_private->objectclass_res;
166
167         for (i=0; list && (i < list->count); i++) {
168                 if (ldb_attr_cmp((const char *)val->data, ldb_msg_find_attr_as_string(list->msgs[i], "lDAPDisplayName", NULL)) == 0) {
169                         char *dn = ldb_dn_alloc_linearized(ctx, list->msgs[i]->dn);
170                         return data_blob_string_const(dn);
171                 }
172         }
173         return *val;
174 }
175
176 static struct ldb_val class_to_oid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
177 {
178         int i;
179         struct map_private *map_private;
180         struct entryUUID_private *entryUUID_private;
181         struct ldb_result *list;
182
183         map_private = talloc_get_type(module->private_data, struct map_private);
184
185         entryUUID_private = talloc_get_type(map_private->caller_private, struct entryUUID_private);
186         list = entryUUID_private->objectclass_res;
187
188         for (i=0; list && (i < list->count); i++) {
189                 if (ldb_attr_cmp((const char *)val->data, ldb_msg_find_attr_as_string(list->msgs[i], "lDAPDisplayName", NULL)) == 0) {
190                         const char *oid = ldb_msg_find_attr_as_string(list->msgs[i], "governsID", NULL);
191                         return data_blob_string_const(oid);
192                 }
193         }
194         return *val;
195 }
196
197 static struct ldb_val class_from_oid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
198 {
199         int i;
200         struct map_private *map_private;
201         struct entryUUID_private *entryUUID_private;
202         struct ldb_result *list;
203
204         map_private = talloc_get_type(module->private_data, struct map_private);
205
206         entryUUID_private = talloc_get_type(map_private->caller_private, struct entryUUID_private);
207         list = entryUUID_private->objectclass_res;
208
209         for (i=0; list && (i < list->count); i++) {
210                 if (ldb_attr_cmp((const char *)val->data, ldb_msg_find_attr_as_string(list->msgs[i], "governsID", NULL)) == 0) {
211                         const char *oc = ldb_msg_find_attr_as_string(list->msgs[i], "lDAPDisplayName", NULL);
212                         return data_blob_string_const(oc);
213                 }
214         }
215         return *val;
216 }
217
218
219 static struct ldb_val normalise_to_signed32(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
220 {
221         long long int signed_ll = strtoll((const char *)val->data, NULL, 10);
222         if (signed_ll >= 0x80000000LL) {
223                 union {
224                         int32_t signed_int;
225                         uint32_t unsigned_int;
226                 } u = {
227                         .unsigned_int = strtoul((const char *)val->data, NULL, 10)
228                 };
229
230                 struct ldb_val out = data_blob_string_const(talloc_asprintf(ctx, "%d", u.signed_int));
231                 return out;
232         }
233         return val_copy(module, ctx, val);
234 }
235
236 static struct ldb_val usn_to_entryCSN(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
237 {
238         struct ldb_val out;
239         unsigned long long usn = strtoull((const char *)val->data, NULL, 10);
240         time_t t = (usn >> 24);
241         out = data_blob_string_const(talloc_asprintf(ctx, "%s#%06x#00#000000", ldb_timestring(ctx, t), (unsigned int)(usn & 0xFFFFFF)));
242         return out;
243 }
244
245 static unsigned long long entryCSN_to_usn_int(TALLOC_CTX *ctx, const struct ldb_val *val) 
246 {
247         char *entryCSN = talloc_strdup(ctx, (const char *)val->data);
248         char *mod_per_sec;
249         time_t t;
250         unsigned long long usn;
251         char *p;
252         if (!entryCSN) {
253                 return 0;
254         }
255         p = strchr(entryCSN, '#');
256         if (!p) {
257                 return 0;
258         }
259         p[0] = '\0';
260         p++;
261         mod_per_sec = p;
262
263         p = strchr(p, '#');
264         if (!p) {
265                 return 0;
266         }
267         p[0] = '\0';
268         p++;
269
270         usn = strtol(mod_per_sec, NULL, 16);
271
272         t = ldb_string_to_time(entryCSN);
273         
274         usn = usn | ((unsigned long long)t <<24);
275         return usn;
276 }
277
278 static struct ldb_val entryCSN_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
279 {
280         struct ldb_val out;
281         unsigned long long usn = entryCSN_to_usn_int(ctx, val);
282         out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
283         return out;
284 }
285
286 static struct ldb_val usn_to_timestamp(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
287 {
288         struct ldb_val out;
289         unsigned long long usn = strtoull((const char *)val->data, NULL, 10);
290         time_t t = (usn >> 24);
291         out = data_blob_string_const(ldb_timestring(ctx, t));
292         return out;
293 }
294
295 static struct ldb_val timestamp_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
296 {
297         struct ldb_val out;
298         time_t t;
299         unsigned long long usn;
300
301         t = ldb_string_to_time((const char *)val->data);
302         
303         usn = ((unsigned long long)t <<24);
304
305         out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
306         return out;
307 }
308
309
310 const struct ldb_map_attribute entryUUID_attributes[] = 
311 {
312         /* objectGUID */
313         {
314                 .local_name = "objectGUID",
315                 .type = MAP_CONVERT,
316                 .u = {
317                         .convert = {
318                                 .remote_name = "entryUUID", 
319                                 .convert_local = guid_always_string,
320                                 .convert_remote = encode_guid,
321                         },
322                 },
323         },
324         /* invocationId */
325         {
326                 .local_name = "invocationId",
327                 .type = MAP_CONVERT,
328                 .u = {
329                         .convert = {
330                                 .remote_name = "invocationId", 
331                                 .convert_local = guid_always_string,
332                                 .convert_remote = encode_guid,
333                         },
334                 },
335         },
336         /* objectSid */
337         {
338                 .local_name = "objectSid",
339                 .type = MAP_CONVERT,
340                 .u = {
341                         .convert = {
342                                 .remote_name = "objectSid", 
343                                 .convert_local = sid_always_binary,
344                                 .convert_remote = val_copy,
345                         },
346                 },
347         },
348         {
349                 .local_name = "whenCreated",
350                 .type = MAP_RENAME,
351                 .u = {
352                         .rename = {
353                                  .remote_name = "createTimestamp"
354                          }
355                 }
356         },
357         {
358                 .local_name = "whenChanged",
359                 .type = MAP_RENAME,
360                 .u = {
361                         .rename = {
362                                  .remote_name = "modifyTimestamp"
363                          }
364                 }
365         },
366         {
367                 .local_name = "objectClasses",
368                 .type = MAP_RENAME,
369                 .u = {
370                         .rename = {
371                                  .remote_name = "samba4ObjectClasses"
372                          }
373                 }
374         },
375         {
376                 .local_name = "dITContentRules",
377                 .type = MAP_RENAME,
378                 .u = {
379                         .rename = {
380                                  .remote_name = "samba4DITContentRules"
381                          }
382                 }
383         },
384         {
385                 .local_name = "attributeTypes",
386                 .type = MAP_RENAME,
387                 .u = {
388                         .rename = {
389                                  .remote_name = "samba4AttributeTypes"
390                          }
391                 }
392         },
393         {
394                 .local_name = "sambaPassword",
395                 .type = MAP_RENAME,
396                 .u = {
397                         .rename = {
398                                  .remote_name = "userPassword"
399                          }
400                 }
401         },
402 #if 0
403         {
404                 .local_name = "allowedChildClassesEffective",
405                 .type = MAP_CONVERT,
406                 .u = {
407                         .convert = {
408                                 .remote_name = "allowedChildClassesEffective", 
409                                 .convert_local = class_to_oid,
410                                 .convert_remote = class_from_oid,
411                         },
412                 },
413         },
414 #endif
415         {
416                 .local_name = "objectCategory",
417                 .type = MAP_CONVERT,
418                 .u = {
419                         .convert = {
420                                 .remote_name = "objectCategory", 
421                                 .convert_local = objectCategory_always_dn,
422                                 .convert_remote = val_copy,
423                         },
424                 },
425         },
426         {
427                 .local_name = "distinguishedName",
428                 .type = MAP_RENAME,
429                 .u = {
430                         .rename = {
431                                  .remote_name = "entryDN"
432                          }
433                 }
434         },
435         {
436                 .local_name = "groupType",
437                 .type = MAP_CONVERT,
438                 .u = {
439                         .convert = {
440                                  .remote_name = "groupType",
441                                  .convert_local = normalise_to_signed32,
442                                  .convert_remote = val_copy,
443                          },
444                 }
445         },
446         {
447                 .local_name = "sAMAccountType",
448                 .type = MAP_CONVERT,
449                 .u = {
450                         .convert = {
451                                  .remote_name = "sAMAccountType",
452                                  .convert_local = normalise_to_signed32,
453                                  .convert_remote = val_copy,
454                          },
455                 }
456         },
457         {
458                 .local_name = "usnChanged",
459                 .type = MAP_CONVERT,
460                 .u = {
461                         .convert = {
462                                  .remote_name = "entryCSN",
463                                  .convert_local = usn_to_entryCSN,
464                                  .convert_remote = entryCSN_to_usn
465                          },
466                 },
467         },
468         {
469                 .local_name = "usnCreated",
470                 .type = MAP_CONVERT,
471                 .u = {
472                         .convert = {
473                                  .remote_name = "createTimestamp",
474                                  .convert_local = usn_to_timestamp,
475                                  .convert_remote = timestamp_to_usn,
476                          },
477                 },
478         },
479         {
480                 .local_name = "*",
481                 .type = MAP_KEEP,
482         },
483         {
484                 .local_name = NULL,
485         }
486 };
487
488 /* This objectClass conflicts with builtin classes on OpenLDAP */
489 const struct ldb_map_objectclass entryUUID_objectclasses[] =
490 {
491         {
492                 .local_name = "subSchema",
493                 .remote_name = "samba4SubSchema"
494         },
495         {
496                 .local_name = NULL
497         }
498 };
499
500 /* These things do not show up in wildcard searches in OpenLDAP, but
501  * we need them to show up in the AD-like view */
502 const char * const entryUUID_wildcard_attributes[] = {
503         "objectGUID", 
504         "whenCreated", 
505         "whenChanged",
506         "usnCreated",
507         "usnChanged",
508         NULL
509 };
510
511 const struct ldb_map_attribute nsuniqueid_attributes[] = 
512 {
513         /* objectGUID */
514         {
515                 .local_name = "objectGUID",
516                 .type = MAP_CONVERT,
517                 .u = {
518                         .convert = {
519                                 .remote_name = "nsuniqueid", 
520                                 .convert_local = guid_ns_string,
521                                 .convert_remote = encode_ns_guid,
522                         },
523                 },
524         },
525         /* objectSid */ 
526         {
527                 .local_name = "objectSid",
528                 .type = MAP_CONVERT,
529                 .u = {
530                         .convert = {
531                                 .remote_name = "objectSid", 
532                                 .convert_local = sid_always_binary,
533                                 .convert_remote = val_copy,
534                         },
535                 },
536         },
537         {
538                 .local_name = "whenCreated",
539                 .type = MAP_RENAME,
540                 .u = {
541                         .rename = {
542                                  .remote_name = "createTimestamp"
543                          }
544                 }
545         },
546         {
547                 .local_name = "whenChanged",
548                 .type = MAP_RENAME,
549                 .u = {
550                         .rename = {
551                                  .remote_name = "modifyTimestamp"
552                          }
553                 }
554         },
555         {
556                 .local_name = "sambaPassword",
557                 .type = MAP_RENAME,
558                 .u = {
559                         .rename = {
560                                  .remote_name = "userPassword"
561                          }
562                 }
563         },
564 #if 0
565         {
566                 .local_name = "allowedChildClassesEffective",
567                 .type = MAP_CONVERT,
568                 .u = {
569                         .convert = {
570                                 .remote_name = "allowedChildClassesEffective", 
571                                 .convert_local = class_to_oid,
572                                 .convert_remote = class_from_oid,
573                         },
574                 },
575         },
576 #endif
577         {
578                 .local_name = "objectCategory",
579                 .type = MAP_CONVERT,
580                 .u = {
581                         .convert = {
582                                 .remote_name = "objectCategory", 
583                                 .convert_local = objectCategory_always_dn,
584                                 .convert_remote = val_copy,
585                         },
586                 },
587         },
588         {
589                 .local_name = "distinguishedName",
590                 .type = MAP_RENAME,
591                 .u = {
592                         .rename = {
593                                  .remote_name = "entryDN"
594                          }
595                 }
596         },
597         {
598                 .local_name = "groupType",
599                 .type = MAP_CONVERT,
600                 .u = {
601                         .convert = {
602                                  .remote_name = "groupType",
603                                  .convert_local = normalise_to_signed32,
604                                  .convert_remote = val_copy,
605                          },
606                 }
607         },
608         {
609                 .local_name = "sAMAccountType",
610                 .type = MAP_CONVERT,
611                 .u = {
612                         .convert = {
613                                  .remote_name = "sAMAccountType",
614                                  .convert_local = normalise_to_signed32,
615                                  .convert_remote = val_copy,
616                          },
617                 }
618         },
619         {
620                 .local_name = "usnChanged",
621                 .type = MAP_CONVERT,
622                 .u = {
623                         .convert = {
624                                  .remote_name = "modifyTimestamp",
625                                  .convert_local = usn_to_timestamp,
626                                  .convert_remote = timestamp_to_usn,
627                          },
628                 },
629         },
630         {
631                 .local_name = "usnCreated",
632                 .type = MAP_CONVERT,
633                 .u = {
634                         .convert = {
635                                  .remote_name = "createTimestamp",
636                                  .convert_local = usn_to_timestamp,
637                                  .convert_remote = timestamp_to_usn,
638                          },
639                 },
640         },
641         {
642                 .local_name = "*",
643                 .type = MAP_KEEP,
644         },
645         {
646                 .local_name = NULL,
647         }
648 };
649
650 /* These things do not show up in wildcard searches in OpenLDAP, but
651  * we need them to show up in the AD-like view */
652 const char * const nsuniqueid_wildcard_attributes[] = {
653         "objectGUID", 
654         "whenCreated", 
655         "whenChanged",
656         "usnCreated",
657         "usnChanged",
658         NULL
659 };
660
661 static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx) 
662 {
663         const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
664         struct ldb_dn *schema_dn;
665         struct ldb_dn *basedn = ldb_dn_new(mem_ctx, ldb, NULL);
666         struct ldb_result *rootdse_res;
667         int ldb_ret;
668         if (!basedn) {
669                 return NULL;
670         }
671         
672         /* Search for rootdse */
673         ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, rootdse_attrs, &rootdse_res);
674         if (ldb_ret != LDB_SUCCESS) {
675                 return NULL;
676         }
677         
678         talloc_steal(mem_ctx, rootdse_res);
679
680         if (rootdse_res->count != 1) {
681                 ldb_asprintf_errstring(ldb, "Failed to find rootDSE: count %d", rootdse_res->count);
682                 return NULL;
683         }
684         
685         /* Locate schema */
686         schema_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
687         if (!schema_dn) {
688                 return NULL;
689         }
690
691         talloc_free(rootdse_res);
692         return schema_dn;
693 }
694
695 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
696                               TALLOC_CTX *mem_ctx, 
697                               struct ldb_result **objectclass_res)
698 {
699         TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
700         int ret;
701         const char *attrs[] = {
702                 "lDAPDisplayName",
703                 "governsID",
704                 NULL
705         };
706
707         if (!local_ctx) {
708                 return LDB_ERR_OPERATIONS_ERROR;
709         }
710         
711         /* Downlaod schema */
712         ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE, 
713                          "objectClass=classSchema", 
714                          attrs, objectclass_res);
715         if (ret != LDB_SUCCESS) {
716                 return ret;
717         }
718
719         talloc_steal(mem_ctx, objectclass_res);
720
721         return ret;
722 }
723
724
725 static int get_remote_rootdse(struct ldb_context *ldb, void *context, 
726                        struct ldb_reply *ares) 
727 {
728         struct entryUUID_private *entryUUID_private;
729         entryUUID_private = talloc_get_type(context,
730                                             struct entryUUID_private);
731         if (ares->type == LDB_REPLY_ENTRY) {
732                 int i;
733                 struct ldb_message_element *el = ldb_msg_find_element(ares->message, "namingContexts");
734                 entryUUID_private->base_dns = talloc_realloc(entryUUID_private, entryUUID_private->base_dns, struct ldb_dn *, 
735                                                              el->num_values + 1);
736                 for (i=0; i < el->num_values; i++) {
737                         if (!entryUUID_private->base_dns) {
738                                 return LDB_ERR_OPERATIONS_ERROR;
739                         }
740                         entryUUID_private->base_dns[i] = ldb_dn_new(entryUUID_private->base_dns, ldb, (const char *)el->values[i].data);
741                         if ( ! ldb_dn_validate(entryUUID_private->base_dns[i])) {
742                                 return LDB_ERR_OPERATIONS_ERROR;
743                         }
744                 }
745                 entryUUID_private->base_dns[i] = NULL;
746         }
747
748         return LDB_SUCCESS;
749 }
750
751 static int find_base_dns(struct ldb_module *module, 
752                           struct entryUUID_private *entryUUID_private) 
753 {
754         int ret;
755         struct ldb_request *req;
756         const char *naming_context_attr[] = {
757                 "namingContexts",
758                 NULL
759         };
760         req = talloc(entryUUID_private, struct ldb_request);
761         if (req == NULL) {
762                 ldb_set_errstring(module->ldb, "Out of Memory");
763                 return LDB_ERR_OPERATIONS_ERROR;
764         }
765
766         req->operation = LDB_SEARCH;
767         req->op.search.base = ldb_dn_new(req, module->ldb, NULL);
768         req->op.search.scope = LDB_SCOPE_BASE;
769
770         req->op.search.tree = ldb_parse_tree(req, "objectClass=*");
771         if (req->op.search.tree == NULL) {
772                 ldb_set_errstring(module->ldb, "Unable to parse search expression");
773                 talloc_free(req);
774                 return LDB_ERR_OPERATIONS_ERROR;
775         }
776
777         req->op.search.attrs = naming_context_attr;
778         req->controls = NULL;
779         req->context = entryUUID_private;
780         req->callback = get_remote_rootdse;
781         ldb_set_timeout(module->ldb, req, 0); /* use default timeout */
782
783         ret = ldb_next_request(module, req);
784         
785         if (ret == LDB_SUCCESS) {
786                 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
787         }
788         
789         talloc_free(req);
790         if (ret != LDB_SUCCESS) {
791                 return ret;
792         }
793
794         return LDB_SUCCESS;
795 }
796
797 /* the context init function */
798 static int entryUUID_init(struct ldb_module *module)
799 {
800         int ret;
801         struct map_private *map_private;
802         struct entryUUID_private *entryUUID_private;
803         struct ldb_dn *schema_dn;
804
805         ret = ldb_map_init(module, entryUUID_attributes, entryUUID_objectclasses, entryUUID_wildcard_attributes, NULL);
806         if (ret != LDB_SUCCESS)
807                 return ret;
808
809         map_private = talloc_get_type(module->private_data, struct map_private);
810
811         entryUUID_private = talloc_zero(map_private, struct entryUUID_private);
812         map_private->caller_private = entryUUID_private;
813
814         schema_dn = find_schema_dn(module->ldb, map_private);
815         if (!schema_dn) {
816                 /* Perhaps no schema yet */
817                 return LDB_SUCCESS;
818         }
819         
820         ret = fetch_objectclass_schema(module->ldb, schema_dn, entryUUID_private, 
821                                        &entryUUID_private->objectclass_res);
822         if (ret != LDB_SUCCESS) {
823                 /* Perhaps no schema yet */
824                 return LDB_SUCCESS;
825         }       
826
827         ret = find_base_dns(module, entryUUID_private);
828
829         return ldb_next_init(module);
830 }
831
832 /* the context init function */
833 static int nsuniqueid_init(struct ldb_module *module)
834 {
835         int ret;
836         struct map_private *map_private;
837         struct entryUUID_private *entryUUID_private;
838         struct ldb_dn *schema_dn;
839
840         ret = ldb_map_init(module, nsuniqueid_attributes, NULL, nsuniqueid_wildcard_attributes, NULL);
841         if (ret != LDB_SUCCESS)
842                 return ret;
843
844         map_private = talloc_get_type(module->private_data, struct map_private);
845
846         entryUUID_private = talloc_zero(map_private, struct entryUUID_private);
847         map_private->caller_private = entryUUID_private;
848
849         schema_dn = find_schema_dn(module->ldb, map_private);
850         if (!schema_dn) {
851                 /* Perhaps no schema yet */
852                 return LDB_SUCCESS;
853         }
854         
855         ret = fetch_objectclass_schema(module->ldb, schema_dn, entryUUID_private, 
856                                        &entryUUID_private->objectclass_res);
857         if (ret != LDB_SUCCESS) {
858                 /* Perhaps no schema yet */
859                 return LDB_SUCCESS;
860         }       
861
862         ret = find_base_dns(module, entryUUID_private);
863
864         return ldb_next_init(module);
865 }
866
867 static int get_seq(struct ldb_context *ldb, void *context, 
868                    struct ldb_reply *ares) 
869 {
870         unsigned long long *max_seq = context;
871         unsigned long long seq;
872         if (ares->type == LDB_REPLY_ENTRY) {
873                 struct ldb_message_element *el = ldb_msg_find_element(ares->message, "contextCSN");
874                 if (el) {
875                         seq = entryCSN_to_usn_int(ares, &el->values[0]);
876                         *max_seq = MAX(seq, *max_seq);
877                 }
878         }
879
880         return LDB_SUCCESS;
881 }
882
883 static int entryUUID_sequence_number(struct ldb_module *module, struct ldb_request *req)
884 {
885         int i, ret;
886         struct map_private *map_private;
887         struct entryUUID_private *entryUUID_private;
888         unsigned long long max_seq = 0;
889         struct ldb_request *search_req;
890         map_private = talloc_get_type(module->private_data, struct map_private);
891
892         entryUUID_private = talloc_get_type(map_private->caller_private, struct entryUUID_private);
893
894         /* Search the baseDNs for a sequence number */
895         for (i=0; entryUUID_private && 
896                      entryUUID_private->base_dns && 
897                      entryUUID_private->base_dns[i];
898                 i++) {
899                 static const char *contextCSN_attr[] = {
900                         "contextCSN", NULL
901                 };
902                 search_req = talloc(req, struct ldb_request);
903                 if (search_req == NULL) {
904                         ldb_set_errstring(module->ldb, "Out of Memory");
905                         return LDB_ERR_OPERATIONS_ERROR;
906                 }
907                 
908                 search_req->operation = LDB_SEARCH;
909                 search_req->op.search.base = entryUUID_private->base_dns[i];
910                 search_req->op.search.scope = LDB_SCOPE_BASE;
911                 
912                 search_req->op.search.tree = ldb_parse_tree(search_req, "objectClass=*");
913                 if (search_req->op.search.tree == NULL) {
914                         ldb_set_errstring(module->ldb, "Unable to parse search expression");
915                         talloc_free(search_req);
916                         return LDB_ERR_OPERATIONS_ERROR;
917                 }
918                 
919                 search_req->op.search.attrs = contextCSN_attr;
920                 search_req->controls = NULL;
921                 search_req->context = &max_seq;
922                 search_req->callback = get_seq;
923                 ldb_set_timeout(module->ldb, search_req, 0); /* use default timeout */
924                 
925                 ret = ldb_next_request(module, search_req);
926                 
927                 if (ret == LDB_SUCCESS) {
928                         ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
929                 }
930                 
931                 talloc_free(search_req);
932                 if (ret != LDB_SUCCESS) {
933                         return ret;
934                 }
935         }
936
937         switch (req->op.seq_num.type) {
938         case LDB_SEQ_HIGHEST_SEQ:
939                 req->op.seq_num.seq_num = max_seq;
940                 break;
941         case LDB_SEQ_NEXT:
942                 req->op.seq_num.seq_num = max_seq;
943                 req->op.seq_num.seq_num++;
944                 break;
945         case LDB_SEQ_HIGHEST_TIMESTAMP:
946         {
947                 req->op.seq_num.seq_num = (max_seq >> 24);
948                 break;
949         }
950         }
951         req->op.seq_num.flags = 0;
952         req->op.seq_num.flags |= LDB_SEQ_TIMESTAMP_SEQUENCE;
953         req->op.seq_num.flags |= LDB_SEQ_GLOBAL_SEQUENCE;
954         return LDB_SUCCESS;
955 }
956
957 static struct ldb_module_ops entryUUID_ops = {
958         .name              = "entryUUID",
959         .init_context      = entryUUID_init,
960         .sequence_number   = entryUUID_sequence_number
961 };
962
963 static struct ldb_module_ops nsuniqueid_ops = {
964         .name              = "nsuniqueid",
965         .init_context      = nsuniqueid_init,
966         .sequence_number   = entryUUID_sequence_number
967 };
968
969 /* the init function */
970 int ldb_entryUUID_module_init(void)
971 {
972         int ret;
973         struct ldb_module_ops ops = ldb_map_get_ops();
974         entryUUID_ops.add       = ops.add;
975         entryUUID_ops.modify    = ops.modify;
976         entryUUID_ops.del       = ops.del;
977         entryUUID_ops.rename    = ops.rename;
978         entryUUID_ops.search    = ops.search;
979         entryUUID_ops.wait      = ops.wait;
980         ret = ldb_register_module(&entryUUID_ops);
981
982         if (ret) {
983                 return ret;
984         }
985
986         nsuniqueid_ops.add      = ops.add;
987         nsuniqueid_ops.modify   = ops.modify;
988         nsuniqueid_ops.del      = ops.del;
989         nsuniqueid_ops.rename   = ops.rename;
990         nsuniqueid_ops.search   = ops.search;
991         nsuniqueid_ops.wait     = ops.wait;
992         ret = ldb_register_module(&nsuniqueid_ops);
993
994         return ret;
995 }