r22531: Fix up OpenLDAP schema map to almost pass 'make test'.
[Samba.git] / source / dsdb / samdb / ldb_modules / entryUUID.c
blob397642c48468d3045b843ec75887b3b5c625b044
1 /*
2 ldb database module
4 LDAP semantics mapping module
6 Copyright (C) Jelmer Vernooij 2005
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
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.
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.
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.
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.
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/modules/ldb_map.h"
36 #include "librpc/gen_ndr/ndr_misc.h"
37 #include "librpc/ndr/libndr.h"
39 struct entryUUID_private {
40 struct ldb_result *objectclass_res;
41 struct ldb_dn **base_dns;
44 static struct ldb_val encode_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
46 struct GUID guid;
47 NTSTATUS status = GUID_from_string((char *)val->data, &guid);
48 struct ldb_val out = data_blob(NULL, 0);
50 if (!NT_STATUS_IS_OK(status)) {
51 return out;
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;
59 return out;
62 static struct ldb_val guid_always_string(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
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;
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;
80 out = data_blob_string_const(GUID_string(ctx, guid));
81 talloc_free(guid);
83 return out;
86 static struct ldb_val encode_ns_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
88 struct GUID guid;
89 NTSTATUS status = NS_GUID_from_string((char *)val->data, &guid);
90 struct ldb_val out = data_blob(NULL, 0);
92 if (!NT_STATUS_IS_OK(status)) {
93 return out;
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;
101 return out;
104 static struct ldb_val guid_ns_string(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
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;
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;
124 out = data_blob_string_const(NS_GUID_string(ctx, guid_p));
125 talloc_free(guid_p);
127 return out;
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)
133 struct ldb_val out = data_blob(NULL, 0);
134 ldb_handler_copy(module->ldb, ctx, val, &out);
136 return out;
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)
142 struct ldb_val out = data_blob(NULL, 0);
143 const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(module->ldb, "objectSid");
145 if (a->syntax->canonicalise_fn(module->ldb, ctx, val, &out) != LDB_SUCCESS) {
146 return data_blob(NULL, 0);
149 return out;
152 static struct ldb_val objectCategory_always_dn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
154 int i;
155 struct map_private *map_private;
156 struct entryUUID_private *entryUUID_private;
157 struct ldb_result *list;
159 if (ldb_dn_validate(ldb_dn_new(ctx, module->ldb, (const char *)val->data))) {
160 return *val;
162 map_private = talloc_get_type(module->private_data, struct map_private);
164 entryUUID_private = talloc_get_type(map_private->caller_private, struct entryUUID_private);
165 list = entryUUID_private->objectclass_res;
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);
173 return *val;
176 static struct ldb_val class_to_oid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
178 int i;
179 struct map_private *map_private;
180 struct entryUUID_private *entryUUID_private;
181 struct ldb_result *list;
183 map_private = talloc_get_type(module->private_data, struct map_private);
185 entryUUID_private = talloc_get_type(map_private->caller_private, struct entryUUID_private);
186 list = entryUUID_private->objectclass_res;
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);
194 return *val;
197 static struct ldb_val class_from_oid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
199 int i;
200 struct map_private *map_private;
201 struct entryUUID_private *entryUUID_private;
202 struct ldb_result *list;
204 map_private = talloc_get_type(module->private_data, struct map_private);
206 entryUUID_private = talloc_get_type(map_private->caller_private, struct entryUUID_private);
207 list = entryUUID_private->objectclass_res;
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);
215 return *val;
219 static struct ldb_val normalise_to_signed32(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
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)
230 struct ldb_val out = data_blob_string_const(talloc_asprintf(ctx, "%d", u.signed_int));
231 return out;
233 return val_copy(module, ctx, val);
236 static struct ldb_val usn_to_entryCSN(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
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;
245 static unsigned long long entryCSN_to_usn_int(TALLOC_CTX *ctx, const struct ldb_val *val)
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;
255 p = strchr(entryCSN, '#');
256 if (!p) {
257 return 0;
259 p[0] = '\0';
260 p++;
261 mod_per_sec = p;
263 p = strchr(p, '#');
264 if (!p) {
265 return 0;
267 p[0] = '\0';
268 p++;
270 usn = strtol(mod_per_sec, NULL, 16);
272 t = ldb_string_to_time(entryCSN);
274 usn = usn | ((unsigned long long)t <<24);
275 return usn;
278 static struct ldb_val entryCSN_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
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;
286 static struct ldb_val usn_to_timestamp(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
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;
295 static struct ldb_val timestamp_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
297 struct ldb_val out;
298 time_t t;
299 unsigned long long usn;
301 t = ldb_string_to_time((const char *)val->data);
303 usn = ((unsigned long long)t <<24);
305 out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
306 return out;
310 const struct ldb_map_attribute entryUUID_attributes[] =
312 /* objectGUID */
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,
324 /* objectSid */
326 .local_name = "objectSid",
327 .type = MAP_CONVERT,
328 .u = {
329 .convert = {
330 .remote_name = "objectSid",
331 .convert_local = sid_always_binary,
332 .convert_remote = val_copy,
337 .local_name = "whenCreated",
338 .type = MAP_RENAME,
339 .u = {
340 .rename = {
341 .remote_name = "createTimestamp"
346 .local_name = "whenChanged",
347 .type = MAP_RENAME,
348 .u = {
349 .rename = {
350 .remote_name = "modifyTimestamp"
355 .local_name = "objectClasses",
356 .type = MAP_RENAME,
357 .u = {
358 .rename = {
359 .remote_name = "samba4ObjectClasses"
364 .local_name = "dITContentRules",
365 .type = MAP_RENAME,
366 .u = {
367 .rename = {
368 .remote_name = "samba4DITContentRules"
373 .local_name = "attributeTypes",
374 .type = MAP_RENAME,
375 .u = {
376 .rename = {
377 .remote_name = "samba4AttributeTypes"
382 .local_name = "sambaPassword",
383 .type = MAP_RENAME,
384 .u = {
385 .rename = {
386 .remote_name = "userPassword"
390 #if 0
392 .local_name = "allowedChildClassesEffective",
393 .type = MAP_CONVERT,
394 .u = {
395 .convert = {
396 .remote_name = "allowedChildClassesEffective",
397 .convert_local = class_to_oid,
398 .convert_remote = class_from_oid,
402 #endif
404 .local_name = "objectCategory",
405 .type = MAP_CONVERT,
406 .u = {
407 .convert = {
408 .remote_name = "objectCategory",
409 .convert_local = objectCategory_always_dn,
410 .convert_remote = val_copy,
415 .local_name = "distinguishedName",
416 .type = MAP_RENAME,
417 .u = {
418 .rename = {
419 .remote_name = "entryDN"
424 .local_name = "groupType",
425 .type = MAP_CONVERT,
426 .u = {
427 .convert = {
428 .remote_name = "groupType",
429 .convert_local = normalise_to_signed32,
430 .convert_remote = val_copy,
435 .local_name = "sAMAccountType",
436 .type = MAP_CONVERT,
437 .u = {
438 .convert = {
439 .remote_name = "sAMAccountType",
440 .convert_local = normalise_to_signed32,
441 .convert_remote = val_copy,
446 .local_name = "usnChanged",
447 .type = MAP_CONVERT,
448 .u = {
449 .convert = {
450 .remote_name = "entryCSN",
451 .convert_local = usn_to_entryCSN,
452 .convert_remote = entryCSN_to_usn
457 .local_name = "usnCreated",
458 .type = MAP_CONVERT,
459 .u = {
460 .convert = {
461 .remote_name = "createTimestamp",
462 .convert_local = usn_to_timestamp,
463 .convert_remote = timestamp_to_usn,
468 .local_name = "*",
469 .type = MAP_KEEP,
472 .local_name = NULL,
476 /* This objectClass conflicts with builtin classes on OpenLDAP */
477 const struct ldb_map_objectclass entryUUID_objectclasses[] =
480 .local_name = "subSchema",
481 .remote_name = "samba4SubSchema"
484 .local_name = NULL
488 /* These things do not show up in wildcard searches in OpenLDAP, but
489 * we need them to show up in the AD-like view */
490 const char * const entryUUID_wildcard_attributes[] = {
491 "objectGUID",
492 "whenCreated",
493 "whenChanged",
494 "usnCreated",
495 "usnChanged",
496 NULL
499 const struct ldb_map_attribute nsuniqueid_attributes[] =
501 /* objectGUID */
503 .local_name = "objectGUID",
504 .type = MAP_CONVERT,
505 .u = {
506 .convert = {
507 .remote_name = "nsuniqueid",
508 .convert_local = guid_ns_string,
509 .convert_remote = encode_ns_guid,
513 /* objectSid */
515 .local_name = "objectSid",
516 .type = MAP_CONVERT,
517 .u = {
518 .convert = {
519 .remote_name = "objectSid",
520 .convert_local = sid_always_binary,
521 .convert_remote = val_copy,
526 .local_name = "whenCreated",
527 .type = MAP_RENAME,
528 .u = {
529 .rename = {
530 .remote_name = "createTimestamp"
535 .local_name = "whenChanged",
536 .type = MAP_RENAME,
537 .u = {
538 .rename = {
539 .remote_name = "modifyTimestamp"
544 .local_name = "sambaPassword",
545 .type = MAP_RENAME,
546 .u = {
547 .rename = {
548 .remote_name = "userPassword"
552 #if 0
554 .local_name = "allowedChildClassesEffective",
555 .type = MAP_CONVERT,
556 .u = {
557 .convert = {
558 .remote_name = "allowedChildClassesEffective",
559 .convert_local = class_to_oid,
560 .convert_remote = class_from_oid,
564 #endif
566 .local_name = "objectCategory",
567 .type = MAP_CONVERT,
568 .u = {
569 .convert = {
570 .remote_name = "objectCategory",
571 .convert_local = objectCategory_always_dn,
572 .convert_remote = val_copy,
577 .local_name = "distinguishedName",
578 .type = MAP_RENAME,
579 .u = {
580 .rename = {
581 .remote_name = "entryDN"
586 .local_name = "groupType",
587 .type = MAP_CONVERT,
588 .u = {
589 .convert = {
590 .remote_name = "groupType",
591 .convert_local = normalise_to_signed32,
592 .convert_remote = val_copy,
597 .local_name = "sAMAccountType",
598 .type = MAP_CONVERT,
599 .u = {
600 .convert = {
601 .remote_name = "sAMAccountType",
602 .convert_local = normalise_to_signed32,
603 .convert_remote = val_copy,
608 .local_name = "usnChanged",
609 .type = MAP_CONVERT,
610 .u = {
611 .convert = {
612 .remote_name = "modifyTimestamp",
613 .convert_local = usn_to_timestamp,
614 .convert_remote = timestamp_to_usn,
619 .local_name = "usnCreated",
620 .type = MAP_CONVERT,
621 .u = {
622 .convert = {
623 .remote_name = "createTimestamp",
624 .convert_local = usn_to_timestamp,
625 .convert_remote = timestamp_to_usn,
630 .local_name = "*",
631 .type = MAP_KEEP,
634 .local_name = NULL,
638 /* These things do not show up in wildcard searches in OpenLDAP, but
639 * we need them to show up in the AD-like view */
640 const char * const nsuniqueid_wildcard_attributes[] = {
641 "objectGUID",
642 "whenCreated",
643 "whenChanged",
644 "usnCreated",
645 "usnChanged",
646 NULL
649 static struct ldb_dn *find_schema_dn(struct ldb_context *ldb, TALLOC_CTX *mem_ctx)
651 const char *rootdse_attrs[] = {"schemaNamingContext", NULL};
652 struct ldb_dn *schema_dn;
653 struct ldb_dn *basedn = ldb_dn_new(mem_ctx, ldb, NULL);
654 struct ldb_result *rootdse_res;
655 int ldb_ret;
656 if (!basedn) {
657 return NULL;
660 /* Search for rootdse */
661 ldb_ret = ldb_search(ldb, basedn, LDB_SCOPE_BASE, NULL, rootdse_attrs, &rootdse_res);
662 if (ldb_ret != LDB_SUCCESS) {
663 return NULL;
666 talloc_steal(mem_ctx, rootdse_res);
668 if (rootdse_res->count != 1) {
669 ldb_asprintf_errstring(ldb, "Failed to find rootDSE: count %d", rootdse_res->count);
670 return NULL;
673 /* Locate schema */
674 schema_dn = ldb_msg_find_attr_as_dn(ldb, mem_ctx, rootdse_res->msgs[0], "schemaNamingContext");
675 if (!schema_dn) {
676 return NULL;
679 talloc_free(rootdse_res);
680 return schema_dn;
683 static int fetch_objectclass_schema(struct ldb_context *ldb, struct ldb_dn *schemadn,
684 TALLOC_CTX *mem_ctx,
685 struct ldb_result **objectclass_res)
687 TALLOC_CTX *local_ctx = talloc_new(mem_ctx);
688 int ret;
689 const char *attrs[] = {
690 "lDAPDisplayName",
691 "governsID",
692 NULL
695 if (!local_ctx) {
696 return LDB_ERR_OPERATIONS_ERROR;
699 /* Downlaod schema */
700 ret = ldb_search(ldb, schemadn, LDB_SCOPE_SUBTREE,
701 "objectClass=classSchema",
702 attrs, objectclass_res);
703 if (ret != LDB_SUCCESS) {
704 return ret;
707 talloc_steal(mem_ctx, objectclass_res);
709 return ret;
713 static int get_remote_rootdse(struct ldb_context *ldb, void *context,
714 struct ldb_reply *ares)
716 struct entryUUID_private *entryUUID_private;
717 entryUUID_private = talloc_get_type(context,
718 struct entryUUID_private);
719 if (ares->type == LDB_REPLY_ENTRY) {
720 int i;
721 struct ldb_message_element *el = ldb_msg_find_element(ares->message, "namingContexts");
722 entryUUID_private->base_dns = talloc_realloc(entryUUID_private, entryUUID_private->base_dns, struct ldb_dn *,
723 el->num_values + 1);
724 for (i=0; i < el->num_values; i++) {
725 if (!entryUUID_private->base_dns) {
726 return LDB_ERR_OPERATIONS_ERROR;
728 entryUUID_private->base_dns[i] = ldb_dn_new(entryUUID_private->base_dns, ldb, (const char *)el->values[i].data);
729 if ( ! ldb_dn_validate(entryUUID_private->base_dns[i])) {
730 return LDB_ERR_OPERATIONS_ERROR;
733 entryUUID_private->base_dns[i] = NULL;
736 return LDB_SUCCESS;
739 static int find_base_dns(struct ldb_module *module,
740 struct entryUUID_private *entryUUID_private)
742 int ret;
743 struct ldb_request *req;
744 const char *naming_context_attr[] = {
745 "namingContexts",
746 NULL
748 req = talloc(entryUUID_private, struct ldb_request);
749 if (req == NULL) {
750 ldb_set_errstring(module->ldb, "Out of Memory");
751 return LDB_ERR_OPERATIONS_ERROR;
754 req->operation = LDB_SEARCH;
755 req->op.search.base = ldb_dn_new(req, module->ldb, NULL);
756 req->op.search.scope = LDB_SCOPE_BASE;
758 req->op.search.tree = ldb_parse_tree(req, "objectClass=*");
759 if (req->op.search.tree == NULL) {
760 ldb_set_errstring(module->ldb, "Unable to parse search expression");
761 talloc_free(req);
762 return LDB_ERR_OPERATIONS_ERROR;
765 req->op.search.attrs = naming_context_attr;
766 req->controls = NULL;
767 req->context = entryUUID_private;
768 req->callback = get_remote_rootdse;
769 ldb_set_timeout(module->ldb, req, 0); /* use default timeout */
771 ret = ldb_next_request(module, req);
773 if (ret == LDB_SUCCESS) {
774 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
777 talloc_free(req);
778 if (ret != LDB_SUCCESS) {
779 return ret;
782 return LDB_SUCCESS;
785 /* the context init function */
786 static int entryUUID_init(struct ldb_module *module)
788 int ret;
789 struct map_private *map_private;
790 struct entryUUID_private *entryUUID_private;
791 struct ldb_dn *schema_dn;
793 ret = ldb_map_init(module, entryUUID_attributes, entryUUID_objectclasses, entryUUID_wildcard_attributes, NULL);
794 if (ret != LDB_SUCCESS)
795 return ret;
797 map_private = talloc_get_type(module->private_data, struct map_private);
799 entryUUID_private = talloc_zero(map_private, struct entryUUID_private);
800 map_private->caller_private = entryUUID_private;
802 schema_dn = find_schema_dn(module->ldb, map_private);
803 if (!schema_dn) {
804 /* Perhaps no schema yet */
805 return LDB_SUCCESS;
808 ret = fetch_objectclass_schema(module->ldb, schema_dn, entryUUID_private,
809 &entryUUID_private->objectclass_res);
810 if (ret != LDB_SUCCESS) {
811 /* Perhaps no schema yet */
812 return LDB_SUCCESS;
815 ret = find_base_dns(module, entryUUID_private);
817 return ldb_next_init(module);
820 /* the context init function */
821 static int nsuniqueid_init(struct ldb_module *module)
823 int ret;
824 struct map_private *map_private;
825 struct entryUUID_private *entryUUID_private;
826 struct ldb_dn *schema_dn;
828 ret = ldb_map_init(module, nsuniqueid_attributes, NULL, nsuniqueid_wildcard_attributes, NULL);
829 if (ret != LDB_SUCCESS)
830 return ret;
832 map_private = talloc_get_type(module->private_data, struct map_private);
834 entryUUID_private = talloc_zero(map_private, struct entryUUID_private);
835 map_private->caller_private = entryUUID_private;
837 schema_dn = find_schema_dn(module->ldb, map_private);
838 if (!schema_dn) {
839 /* Perhaps no schema yet */
840 return LDB_SUCCESS;
843 ret = fetch_objectclass_schema(module->ldb, schema_dn, entryUUID_private,
844 &entryUUID_private->objectclass_res);
845 if (ret != LDB_SUCCESS) {
846 /* Perhaps no schema yet */
847 return LDB_SUCCESS;
850 ret = find_base_dns(module, entryUUID_private);
852 return ldb_next_init(module);
855 static int get_seq(struct ldb_context *ldb, void *context,
856 struct ldb_reply *ares)
858 unsigned long long *max_seq = context;
859 unsigned long long seq;
860 if (ares->type == LDB_REPLY_ENTRY) {
861 struct ldb_message_element *el = ldb_msg_find_element(ares->message, "contextCSN");
862 if (el) {
863 seq = entryCSN_to_usn_int(ares, &el->values[0]);
864 *max_seq = MAX(seq, *max_seq);
868 return LDB_SUCCESS;
871 static int entryUUID_sequence_number(struct ldb_module *module, struct ldb_request *req)
873 int i, ret;
874 struct map_private *map_private;
875 struct entryUUID_private *entryUUID_private;
876 unsigned long long max_seq = 0;
877 struct ldb_request *search_req;
878 map_private = talloc_get_type(module->private_data, struct map_private);
880 entryUUID_private = talloc_get_type(map_private->caller_private, struct entryUUID_private);
882 /* Search the baseDNs for a sequence number */
883 for (i=0; entryUUID_private &&
884 entryUUID_private->base_dns &&
885 entryUUID_private->base_dns[i];
886 i++) {
887 static const char *contextCSN_attr[] = {
888 "contextCSN", NULL
890 search_req = talloc(req, struct ldb_request);
891 if (search_req == NULL) {
892 ldb_set_errstring(module->ldb, "Out of Memory");
893 return LDB_ERR_OPERATIONS_ERROR;
896 search_req->operation = LDB_SEARCH;
897 search_req->op.search.base = entryUUID_private->base_dns[i];
898 search_req->op.search.scope = LDB_SCOPE_BASE;
900 search_req->op.search.tree = ldb_parse_tree(search_req, "objectClass=*");
901 if (search_req->op.search.tree == NULL) {
902 ldb_set_errstring(module->ldb, "Unable to parse search expression");
903 talloc_free(search_req);
904 return LDB_ERR_OPERATIONS_ERROR;
907 search_req->op.search.attrs = contextCSN_attr;
908 search_req->controls = NULL;
909 search_req->context = &max_seq;
910 search_req->callback = get_seq;
911 ldb_set_timeout(module->ldb, search_req, 0); /* use default timeout */
913 ret = ldb_next_request(module, search_req);
915 if (ret == LDB_SUCCESS) {
916 ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
919 talloc_free(search_req);
920 if (ret != LDB_SUCCESS) {
921 return ret;
925 switch (req->op.seq_num.type) {
926 case LDB_SEQ_HIGHEST_SEQ:
927 req->op.seq_num.seq_num = max_seq;
928 break;
929 case LDB_SEQ_NEXT:
930 req->op.seq_num.seq_num = max_seq;
931 req->op.seq_num.seq_num++;
932 break;
933 case LDB_SEQ_HIGHEST_TIMESTAMP:
935 req->op.seq_num.seq_num = (max_seq >> 24);
936 break;
939 req->op.seq_num.flags = 0;
940 req->op.seq_num.flags |= LDB_SEQ_TIMESTAMP_SEQUENCE;
941 req->op.seq_num.flags |= LDB_SEQ_GLOBAL_SEQUENCE;
942 return LDB_SUCCESS;
945 static struct ldb_module_ops entryUUID_ops = {
946 .name = "entryUUID",
947 .init_context = entryUUID_init,
948 .sequence_number = entryUUID_sequence_number
951 static struct ldb_module_ops nsuniqueid_ops = {
952 .name = "nsuniqueid",
953 .init_context = nsuniqueid_init,
954 .sequence_number = entryUUID_sequence_number
957 /* the init function */
958 int ldb_entryUUID_module_init(void)
960 int ret;
961 struct ldb_module_ops ops = ldb_map_get_ops();
962 entryUUID_ops.add = ops.add;
963 entryUUID_ops.modify = ops.modify;
964 entryUUID_ops.del = ops.del;
965 entryUUID_ops.rename = ops.rename;
966 entryUUID_ops.search = ops.search;
967 entryUUID_ops.wait = ops.wait;
968 ret = ldb_register_module(&entryUUID_ops);
970 if (ret) {
971 return ret;
974 nsuniqueid_ops.add = ops.add;
975 nsuniqueid_ops.modify = ops.modify;
976 nsuniqueid_ops.del = ops.del;
977 nsuniqueid_ops.rename = ops.rename;
978 nsuniqueid_ops.search = ops.search;
979 nsuniqueid_ops.wait = ops.wait;
980 ret = ldb_register_module(&nsuniqueid_ops);
982 return ret;