s4 - Mapped AD schema to existing FDS schema.
[samba.git] / source4 / dsdb / samdb / ldb_modules / simple_ldap_map.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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 /* 
24    This module relies on ldb_map to do all the real work, but performs
25    some of the trivial mappings between AD semantics and that provided
26    by OpenLDAP and similar servers.
27 */
28
29 #include "includes.h"
30 #include "ldb/include/ldb_module.h"
31 #include "ldb/ldb_map/ldb_map.h"
32
33 #include "librpc/gen_ndr/ndr_misc.h"
34 #include "librpc/ndr/libndr.h"
35 #include "dsdb/samdb/samdb.h"
36
37 struct entryuuid_private {
38         struct ldb_context *ldb;
39         struct ldb_dn **base_dns;
40 };
41
42 static struct ldb_val encode_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
43 {
44         struct GUID guid;
45         NTSTATUS status = GUID_from_data_blob(val, &guid);
46         enum ndr_err_code ndr_err;
47         struct ldb_val out = data_blob(NULL, 0);
48
49         if (!NT_STATUS_IS_OK(status)) {
50                 return out;
51         }
52         ndr_err = ndr_push_struct_blob(&out, ctx, NULL, &guid,
53                                        (ndr_push_flags_fn_t)ndr_push_GUID);
54         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
55                 return out;
56         }
57
58         return out;
59 }
60
61 static struct ldb_val guid_always_string(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
62 {
63         struct ldb_val out = data_blob(NULL, 0);
64         struct GUID guid;
65         NTSTATUS status = GUID_from_data_blob(val, &guid);
66         if (!NT_STATUS_IS_OK(status)) {
67                 return out;
68         }
69         return data_blob_string_const(GUID_string(ctx, &guid));
70 }
71
72 static struct ldb_val encode_ns_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
73 {
74         struct GUID guid;
75         NTSTATUS status = NS_GUID_from_string((char *)val->data, &guid);
76         enum ndr_err_code ndr_err;
77         struct ldb_val out = data_blob(NULL, 0);
78
79         if (!NT_STATUS_IS_OK(status)) {
80                 return out;
81         }
82         ndr_err = ndr_push_struct_blob(&out, ctx, NULL, &guid,
83                                        (ndr_push_flags_fn_t)ndr_push_GUID);
84         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
85                 return out;
86         }
87
88         return out;
89 }
90
91 static struct ldb_val guid_ns_string(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
92 {
93         struct ldb_val out = data_blob(NULL, 0);
94         struct GUID guid;
95         NTSTATUS status = GUID_from_data_blob(val, &guid);
96         if (!NT_STATUS_IS_OK(status)) {
97                 return out;
98         }
99         return data_blob_string_const(NS_GUID_string(ctx, &guid));
100 }
101
102 /* The backend holds binary sids, so just copy them back */
103 static struct ldb_val val_copy(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
104 {
105         struct ldb_val out = data_blob(NULL, 0);
106         out = ldb_val_dup(ctx, val);
107
108         return out;
109 }
110
111 /* Ensure we always convert sids into binary, so the backend doesn't have to know about both forms */
112 static struct ldb_val sid_always_binary(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
113 {
114         struct ldb_context *ldb = ldb_module_get_ctx(module);
115         struct ldb_val out = data_blob(NULL, 0);
116         const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, "objectSid");
117
118         if (a->syntax->canonicalise_fn(ldb, ctx, val, &out) != LDB_SUCCESS) {
119                 return data_blob(NULL, 0);
120         }
121
122         return out;
123 }
124
125 /* Ensure we always convert objectCategory into a DN */
126 static struct ldb_val objectCategory_always_dn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
127 {
128         struct ldb_context *ldb = ldb_module_get_ctx(module);
129         struct ldb_dn *dn;
130         struct ldb_val out = data_blob(NULL, 0);
131         const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, "objectCategory");
132
133         dn = ldb_dn_from_ldb_val(ctx, ldb, val);
134         if (dn && ldb_dn_validate(dn)) {
135                 talloc_free(dn);
136                 return val_copy(module, ctx, val);
137         }
138         talloc_free(dn);
139
140         if (a->syntax->canonicalise_fn(ldb, ctx, val, &out) != LDB_SUCCESS) {
141                 return data_blob(NULL, 0);
142         }
143
144         return out;
145 }
146
147 static struct ldb_val normalise_to_signed32(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
148 {
149         struct ldb_val out;
150         /* We've to use "strtoll" here to have the intended overflows.
151          * Otherwise we may get "LONG_MAX" and the conversion is wrong. */
152         int32_t i = (int32_t) strtoll((char *)val->data, NULL, 0);
153         out = data_blob_string_const(talloc_asprintf(ctx, "%d", i));
154         return out;
155 }
156
157 static struct ldb_val usn_to_entryCSN(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
158 {
159         struct ldb_val out;
160         unsigned long long usn = strtoull((const char *)val->data, NULL, 10);
161         time_t t = (usn >> 24);
162         out = data_blob_string_const(talloc_asprintf(ctx, "%s#%06x#00#000000", ldb_timestring(ctx, t), (unsigned int)(usn & 0xFFFFFF)));
163         return out;
164 }
165
166 static unsigned long long entryCSN_to_usn_int(TALLOC_CTX *ctx, const struct ldb_val *val) 
167 {
168         char *entryCSN = talloc_strdup(ctx, (const char *)val->data);
169         char *mod_per_sec;
170         time_t t;
171         unsigned long long usn;
172         char *p;
173         if (!entryCSN) {
174                 return 0;
175         }
176         p = strchr(entryCSN, '#');
177         if (!p) {
178                 return 0;
179         }
180         p[0] = '\0';
181         p++;
182         mod_per_sec = p;
183
184         p = strchr(p, '#');
185         if (!p) {
186                 return 0;
187         }
188         p[0] = '\0';
189         p++;
190
191         usn = strtol(mod_per_sec, NULL, 16);
192
193         t = ldb_string_to_time(entryCSN);
194         
195         usn = usn | ((unsigned long long)t <<24);
196         return usn;
197 }
198
199 static struct ldb_val entryCSN_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
200 {
201         struct ldb_val out;
202         unsigned long long usn = entryCSN_to_usn_int(ctx, val);
203         out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
204         return out;
205 }
206
207 static struct ldb_val usn_to_timestamp(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
208 {
209         struct ldb_val out;
210         unsigned long long usn = strtoull((const char *)val->data, NULL, 10);
211         time_t t = (usn >> 24);
212         out = data_blob_string_const(ldb_timestring(ctx, t));
213         return out;
214 }
215
216 static struct ldb_val timestamp_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
217 {
218         struct ldb_val out;
219         time_t t;
220         unsigned long long usn;
221
222         t = ldb_string_to_time((const char *)val->data);
223         
224         usn = ((unsigned long long)t <<24);
225
226         out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
227         return out;
228 }
229
230
231 static const struct ldb_map_attribute entryuuid_attributes[] = 
232 {
233         /* objectGUID */
234         {
235                 .local_name = "objectGUID",
236                 .type = LDB_MAP_CONVERT,
237                 .u = {
238                         .convert = {
239                                 .remote_name = "entryUUID", 
240                                 .convert_local = guid_always_string,
241                                 .convert_remote = encode_guid,
242                         },
243                 },
244         },
245         /* invocationId */
246         {
247                 .local_name = "invocationId",
248                 .type = LDB_MAP_CONVERT,
249                 .u = {
250                         .convert = {
251                                 .remote_name = "invocationId", 
252                                 .convert_local = guid_always_string,
253                                 .convert_remote = encode_guid,
254                         },
255                 },
256         },
257         /* objectSid */
258         {
259                 .local_name = "objectSid",
260                 .type = LDB_MAP_CONVERT,
261                 .u = {
262                         .convert = {
263                                 .remote_name = "objectSid", 
264                                 .convert_local = sid_always_binary,
265                                 .convert_remote = val_copy,
266                         },
267                 },
268         },
269         {
270                 .local_name = "name",
271                 .type = LDB_MAP_RENAME,
272                 .u = {
273                         .rename = {
274                                  .remote_name = "samba4RDN"
275                          }
276                 }
277         },
278         {
279                 .local_name = "whenCreated",
280                 .type = LDB_MAP_RENAME,
281                 .u = {
282                         .rename = {
283                                  .remote_name = "createTimestamp"
284                          }
285                 }
286         },
287         {
288                 .local_name = "whenChanged",
289                 .type = LDB_MAP_RENAME,
290                 .u = {
291                         .rename = {
292                                  .remote_name = "modifyTimestamp"
293                          }
294                 }
295         },
296         {
297                 .local_name = "objectClasses",
298                 .type = LDB_MAP_RENAME,
299                 .u = {
300                         .rename = {
301                                  .remote_name = "samba4ObjectClasses"
302                          }
303                 }
304         },
305         {
306                 .local_name = "dITContentRules",
307                 .type = LDB_MAP_RENAME,
308                 .u = {
309                         .rename = {
310                                  .remote_name = "samba4DITContentRules"
311                          }
312                 }
313         },
314         {
315                 .local_name = "attributeTypes",
316                 .type = LDB_MAP_RENAME,
317                 .u = {
318                         .rename = {
319                                  .remote_name = "samba4AttributeTypes"
320                          }
321                 }
322         },
323         {
324                 .local_name = "objectCategory",
325                 .type = LDB_MAP_CONVERT,
326                 .u = {
327                         .convert = {
328                                 .remote_name = "objectCategory", 
329                                 .convert_local = objectCategory_always_dn,
330                                 .convert_remote = val_copy,
331                         },
332                 },
333         },
334         {
335                 .local_name = "distinguishedName",
336                 .type = LDB_MAP_RENAME,
337                 .u = {
338                         .rename = {
339                                  .remote_name = "entryDN"
340                          }
341                 }
342         },
343         {
344                 .local_name = "primaryGroupID",
345                 .type = LDB_MAP_CONVERT,
346                 .u = {
347                         .convert = {
348                                  .remote_name = "primaryGroupID",
349                                  .convert_local = normalise_to_signed32,
350                                  .convert_remote = val_copy,
351                         }
352                 }
353         },
354         {
355                 .local_name = "groupType",
356                 .type = LDB_MAP_CONVERT,
357                 .u = {
358                         .convert = {
359                                  .remote_name = "groupType",
360                                  .convert_local = normalise_to_signed32,
361                                  .convert_remote = val_copy,
362                          }
363                 }
364         },
365         {
366                 .local_name = "userAccountControl",
367                 .type = LDB_MAP_CONVERT,
368                 .u = {
369                         .convert = {
370                                  .remote_name = "userAccountControl",
371                                  .convert_local = normalise_to_signed32,
372                                  .convert_remote = val_copy,
373                          }
374                 }
375         },
376         {
377                 .local_name = "sAMAccountType",
378                 .type = LDB_MAP_CONVERT,
379                 .u = {
380                         .convert = {
381                                  .remote_name = "sAMAccountType",
382                                  .convert_local = normalise_to_signed32,
383                                  .convert_remote = val_copy,
384                          }
385                 }
386         },
387         {
388                 .local_name = "systemFlags",
389                 .type = LDB_MAP_CONVERT,
390                 .u = {
391                         .convert = {
392                                  .remote_name = "systemFlags",
393                                  .convert_local = normalise_to_signed32,
394                                  .convert_remote = val_copy,
395                          }
396                 }
397         },
398         {
399                 .local_name = "usnChanged",
400                 .type = LDB_MAP_CONVERT,
401                 .u = {
402                         .convert = {
403                                  .remote_name = "entryCSN",
404                                  .convert_local = usn_to_entryCSN,
405                                  .convert_remote = entryCSN_to_usn
406                          },
407                 },
408         },
409         {
410                 .local_name = "usnCreated",
411                 .type = LDB_MAP_CONVERT,
412                 .u = {
413                         .convert = {
414                                  .remote_name = "createTimestamp",
415                                  .convert_local = usn_to_timestamp,
416                                  .convert_remote = timestamp_to_usn,
417                          },
418                 },
419         },
420         {
421                 .local_name = "*",
422                 .type = LDB_MAP_KEEP,
423         },
424         {
425                 .local_name = NULL,
426         }
427 };
428
429 /* This objectClass conflicts with builtin classes on OpenLDAP */
430 const struct ldb_map_objectclass entryuuid_objectclasses[] =
431 {
432         {
433                 .local_name = "subSchema",
434                 .remote_name = "samba4SubSchema"
435         },
436         {
437                 .local_name = NULL
438         }
439 };
440
441 /* These things do not show up in wildcard searches in OpenLDAP, but
442  * we need them to show up in the AD-like view */
443 static const char * const entryuuid_wildcard_attributes[] = {
444         "objectGUID", 
445         "whenCreated", 
446         "whenChanged",
447         "usnCreated",
448         "usnChanged",
449         "memberOf",
450         NULL
451 };
452
453 static const struct ldb_map_attribute nsuniqueid_attributes[] = 
454 {
455         /* objectGUID */
456         {
457                 .local_name = "objectGUID",
458                 .type = LDB_MAP_CONVERT,
459                 .u = {
460                         .convert = {
461                                 .remote_name = "nsuniqueid", 
462                                 .convert_local = guid_ns_string,
463                                 .convert_remote = encode_ns_guid,
464                         }
465                 }
466         },
467         /* objectSid */ 
468         {
469                 .local_name = "objectSid",
470                 .type = LDB_MAP_CONVERT,
471                 .u = {
472                         .convert = {
473                                 .remote_name = "objectSid", 
474                                 .convert_local = sid_always_binary,
475                                 .convert_remote = val_copy,
476                         }
477                 }
478         },
479         {
480                 .local_name = "whenCreated",
481                 .type = LDB_MAP_RENAME,
482                 .u = {
483                         .rename = {
484                                  .remote_name = "createTimestamp"
485                          }
486                 }
487         },
488         {
489                 .local_name = "whenChanged",
490                 .type = LDB_MAP_RENAME,
491                 .u = {
492                         .rename = {
493                                  .remote_name = "modifyTimestamp"
494                          }
495                 }
496         },
497         {
498                 .local_name = "objectCategory",
499                 .type = LDB_MAP_CONVERT,
500                 .u = {
501                         .convert = {
502                                 .remote_name = "objectCategory", 
503                                 .convert_local = objectCategory_always_dn,
504                                 .convert_remote = val_copy,
505                         }
506                 }
507         },
508         {
509                 .local_name = "distinguishedName",
510                 .type = LDB_MAP_RENAME,
511                 .u = {
512                         .rename = {
513                                  .remote_name = "entryDN"
514                          }
515                 }
516         },
517         {
518                 .local_name = "primaryGroupID",
519                 .type = LDB_MAP_CONVERT,
520                 .u = {
521                         .convert = {
522                                  .remote_name = "primaryGroupID",
523                                  .convert_local = normalise_to_signed32,
524                                  .convert_remote = val_copy,
525                         }
526                 }
527         },
528         {
529                 .local_name = "groupType",
530                 .type = LDB_MAP_CONVERT,
531                 .u = {
532                         .convert = {
533                                  .remote_name = "sambaGroupType",
534                                  .convert_local = normalise_to_signed32,
535                                  .convert_remote = val_copy,
536                          }
537                 }
538         },
539         {
540                 .local_name = "userAccountControl",
541                 .type = LDB_MAP_CONVERT,
542                 .u = {
543                         .convert = {
544                                  .remote_name = "userAccountControl",
545                                  .convert_local = normalise_to_signed32,
546                                  .convert_remote = val_copy,
547                          }
548                 }
549         },
550         {
551                 .local_name = "sAMAccountType",
552                 .type = LDB_MAP_CONVERT,
553                 .u = {
554                         .convert = {
555                                  .remote_name = "sAMAccountType",
556                                  .convert_local = normalise_to_signed32,
557                                  .convert_remote = val_copy,
558                          }
559                 }
560         },
561         {
562                 .local_name = "systemFlags",
563                 .type = LDB_MAP_CONVERT,
564                 .u = {
565                         .convert = {
566                                  .remote_name = "systemFlags",
567                                  .convert_local = normalise_to_signed32,
568                                  .convert_remote = val_copy,
569                          }
570                 }
571         },
572         {
573                 .local_name = "usnChanged",
574                 .type = LDB_MAP_CONVERT,
575                 .u = {
576                         .convert = {
577                                  .remote_name = "modifyTimestamp",
578                                  .convert_local = usn_to_timestamp,
579                                  .convert_remote = timestamp_to_usn,
580                          }
581                 }
582         },
583         {
584                 .local_name = "usnCreated",
585                 .type = LDB_MAP_CONVERT,
586                 .u = {
587                         .convert = {
588                                  .remote_name = "createTimestamp",
589                                  .convert_local = usn_to_timestamp,
590                                  .convert_remote = timestamp_to_usn,
591                          }
592                 }
593         },
594         {
595                 .local_name = "unixHomeDirectory",
596                 .type = MAP_RENAME,
597                 .u = {
598                         .rename = {
599                                  .remote_name = "homeDirectory"
600                          }
601                 }
602         },
603         {
604                 .local_name = "pwdLastSet",
605                 .type = MAP_RENAME,
606                 .u = {
607                         .rename = {
608                                  .remote_name = "sambaPwdLastSet"
609                          }
610                 }
611         },
612         {
613                 .local_name = "lastLogon",
614                 .type = MAP_RENAME,
615                 .u = {
616                         .rename = {
617                                  .remote_name = "sambaLogonTime"
618                          }
619                 }
620         },
621         {
622                 .local_name = "lastLogoff",
623                 .type = MAP_RENAME,
624                 .u = {
625                         .rename = {
626                                  .remote_name = "sambaLogoffTime"
627                          }
628                 }
629         },
630         {
631                 .local_name = "badPwdCount",
632                 .type = MAP_RENAME,
633                 .u = {
634                         .rename = {
635                                  .remote_name = "sambaBadPasswordCount"
636                          }
637                 }
638         },
639         {
640                 .local_name = "logonHours",
641                 .type = MAP_RENAME,
642                 .u = {
643                         .rename = {
644                                  .remote_name = "sambaLogonHours"
645                          }
646                 }
647         },
648         {
649                 .local_name = "homeDrive",
650                 .type = MAP_RENAME,
651                 .u = {
652                         .rename = {
653                                  .remote_name = "sambaHomeDrive"
654                          }
655                 }
656         },
657         {
658                 .local_name = "scriptPath",
659                 .type = MAP_RENAME,
660                 .u = {
661                         .rename = {
662                                  .remote_name = "sambaLogonScript"
663                          }
664                 }
665         },
666         {
667                 .local_name = "profilePath",
668                 .type = MAP_RENAME,
669                 .u = {
670                         .rename = {
671                                  .remote_name = "sambaProfilePath"
672                          }
673                 }
674         },
675         {
676                 .local_name = "userWorkstations",
677                 .type = MAP_RENAME,
678                 .u = {
679                         .rename = {
680                                  .remote_name = "sambaUserWorkstations"
681                          }
682                 }
683         },
684         {
685                 .local_name = "homeDirectory",
686                 .type = MAP_RENAME,
687                 .u = {
688                         .rename = {
689                                  .remote_name = "sambaHomePath"
690                          }
691                 }
692         },
693         {
694                 .local_name = "nextRid",
695                 .type = MAP_RENAME,
696                 .u = {
697                         .rename = {
698                                  .remote_name = "sambaNextRid"
699                          }
700                 }
701         },
702         {
703                 .local_name = "privilegeDisplayName",
704                 .type = MAP_RENAME,
705                 .u = {
706                         .rename = {
707                                  .remote_name = "sambaPrivName"
708                          }
709                 }
710         },
711         {
712                 .local_name = "*",
713                 .type = LDB_MAP_KEEP,
714         },
715         {
716                 .local_name = NULL,
717         }
718 };
719
720 /* This objectClass conflicts with builtin classes on FDS */
721 const struct ldb_map_objectclass nsuniqueid_objectclasses[] =
722 {
723         {
724                 .local_name = "domain",
725                 .remote_name = "samba4Domain"
726         },
727         {
728                 .local_name = "rFC822LocalPart",
729                 .remote_name = "samba4RFC822LocalPart"
730         },
731         {
732                 .local_name = "mailRecipient",
733                 .remote_name = "samba4MailRecipient"
734         },
735         {
736                 .local_name = "nisMap",
737                 .remote_name = "samba4NisMap"
738         },
739         {
740                 .local_name = "person",
741                 .remote_name = "samba4Person"
742         },
743         {
744                 .local_name = "organizationalPerson",
745                 .remote_name = "samba4OrganizationalPerson"
746         },
747         {
748                 .local_name = "residentialPerson",
749                 .remote_name = "samba4ResidentialPerson"
750         },
751         {
752                 .local_name = "inetOrgPerson",
753                 .remote_name = "samba4InetOrgPerson"
754         },
755         {
756                 .local_name = NULL
757         }
758 };
759
760 /* These things do not show up in wildcard searches in OpenLDAP, but
761  * we need them to show up in the AD-like view */
762 static const char * const nsuniqueid_wildcard_attributes[] = {
763         "objectGUID", 
764         "whenCreated", 
765         "whenChanged",
766         "usnCreated",
767         "usnChanged",
768         NULL
769 };
770
771 /* the context init function */
772 static int entryuuid_init(struct ldb_module *module)
773 {
774         int ret;
775         ret = ldb_map_init(module, entryuuid_attributes, entryuuid_objectclasses, entryuuid_wildcard_attributes, "samba4Top", NULL);
776         if (ret != LDB_SUCCESS)
777                 return ret;
778
779         return ldb_next_init(module);
780 }
781
782 /* the context init function */
783 static int nsuniqueid_init(struct ldb_module *module)
784 {
785         int ret;
786         ret = ldb_map_init(module, nsuniqueid_attributes, nsuniqueid_objectclasses, nsuniqueid_wildcard_attributes, "extensibleObject", NULL);
787         if (ret != LDB_SUCCESS)
788                 return ret;
789
790         return ldb_next_init(module);
791 }
792
793 static int get_seq_callback(struct ldb_request *req,
794                             struct ldb_reply *ares)
795 {
796         unsigned long long *seq = (unsigned long long *)req->context;
797
798         if (!ares) {
799                 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
800         }
801         if (ares->error != LDB_SUCCESS) {
802                 return ldb_request_done(req, ares->error);
803         }
804
805         if (ares->type == LDB_REPLY_ENTRY) {
806                 struct ldb_message_element *el = ldb_msg_find_element(ares->message, "contextCSN");
807                 if (el) {
808                         *seq = entryCSN_to_usn_int(ares, &el->values[0]);
809                 }
810         }
811
812         if (ares->type == LDB_REPLY_DONE) {
813                 return ldb_request_done(req, LDB_SUCCESS);
814         }
815
816         talloc_free(ares);
817         return LDB_SUCCESS;
818 }
819
820 static int entryuuid_sequence_number(struct ldb_module *module, struct ldb_request *req)
821 {
822         struct ldb_context *ldb;
823         int ret;
824         struct map_private *map_private;
825         struct entryuuid_private *entryuuid_private;
826         unsigned long long seq_num = 0;
827         struct ldb_request *search_req;
828
829         const struct ldb_control *partition_ctrl;
830         const struct dsdb_control_current_partition *partition;
831  
832         static const char *contextCSN_attr[] = {
833                 "contextCSN", NULL
834         };
835
836         struct ldb_seqnum_request *seq;
837         struct ldb_seqnum_result *seqr;
838         struct ldb_extended *ext;
839
840         ldb = ldb_module_get_ctx(module);
841
842         seq = talloc_get_type(req->op.extended.data, struct ldb_seqnum_request);
843
844         map_private = talloc_get_type(ldb_module_get_private(module), struct map_private);
845
846         entryuuid_private = talloc_get_type(map_private->caller_private, struct entryuuid_private);
847
848         /* All this to get the DN of the parition, so we can search the right thing */
849         partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
850         if (!partition_ctrl) {
851                 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
852                               "entryuuid_sequence_number: no current partition control found");
853                 return LDB_ERR_CONSTRAINT_VIOLATION;
854         }
855
856         partition = talloc_get_type(partition_ctrl->data,
857                                     struct dsdb_control_current_partition);
858         SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
859
860         ret = ldb_build_search_req(&search_req, ldb, req,
861                                    partition->dn, LDB_SCOPE_BASE,
862                                    NULL, contextCSN_attr, NULL,
863                                    &seq_num, get_seq_callback,
864                                    NULL);
865         if (ret != LDB_SUCCESS) {
866                 return ret;
867         }
868
869         ret = ldb_next_request(module, search_req);
870
871         if (ret == LDB_SUCCESS) {
872                 ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
873         }
874
875         talloc_free(search_req);
876         if (ret != LDB_SUCCESS) {
877                 return ret;
878         }
879
880         ext = talloc_zero(req, struct ldb_extended);
881         if (!ext) {
882                 return LDB_ERR_OPERATIONS_ERROR;
883         }
884         seqr = talloc_zero(req, struct ldb_seqnum_result);
885         if (seqr == NULL) {
886                 talloc_free(ext);
887                 return LDB_ERR_OPERATIONS_ERROR;
888         }
889         ext->oid = LDB_EXTENDED_SEQUENCE_NUMBER;
890         ext->data = seqr;
891
892         switch (seq->type) {
893         case LDB_SEQ_HIGHEST_SEQ:
894                 seqr->seq_num = seq_num;
895                 break;
896         case LDB_SEQ_NEXT:
897                 seqr->seq_num = seq_num;
898                 seqr->seq_num++;
899                 break;
900         case LDB_SEQ_HIGHEST_TIMESTAMP:
901         {
902                 seqr->seq_num = (seq_num >> 24);
903                 break;
904         }
905         }
906         seqr->flags = 0;
907         seqr->flags |= LDB_SEQ_TIMESTAMP_SEQUENCE;
908         seqr->flags |= LDB_SEQ_GLOBAL_SEQUENCE;
909
910         /* send request done */
911         return ldb_module_done(req, NULL, ext, LDB_SUCCESS);
912 }
913
914 static int entryuuid_extended(struct ldb_module *module, struct ldb_request *req)
915 {
916         if (strcmp(req->op.extended.oid, LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
917                 return entryuuid_sequence_number(module, req);
918         }
919
920         return ldb_next_request(module, req);
921 }
922
923 _PUBLIC_ const struct ldb_module_ops ldb_entryuuid_module_ops = {
924         .name              = "entryuuid",
925         .init_context      = entryuuid_init,
926         .extended          = entryuuid_extended,
927         LDB_MAP_OPS
928 };
929
930 _PUBLIC_ const struct ldb_module_ops ldb_nsuniqueid_module_ops = {
931         .name              = "nsuniqueid",
932         .init_context      = nsuniqueid_init,
933         .extended          = entryuuid_extended,
934         LDB_MAP_OPS
935 };