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