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