s4: Make the int32 problem more clear - and fix another error
[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/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 = 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 = 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 = 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 = MAP_RENAME,
272                 .u = {
273                         .rename = {
274                                  .remote_name = "samba4RDN"
275                          }
276                 }
277         },
278         {
279                 .local_name = "whenCreated",
280                 .type = MAP_RENAME,
281                 .u = {
282                         .rename = {
283                                  .remote_name = "createTimestamp"
284                          }
285                 }
286         },
287         {
288                 .local_name = "whenChanged",
289                 .type = MAP_RENAME,
290                 .u = {
291                         .rename = {
292                                  .remote_name = "modifyTimestamp"
293                          }
294                 }
295         },
296         {
297                 .local_name = "objectClasses",
298                 .type = MAP_RENAME,
299                 .u = {
300                         .rename = {
301                                  .remote_name = "samba4ObjectClasses"
302                          }
303                 }
304         },
305         {
306                 .local_name = "dITContentRules",
307                 .type = MAP_RENAME,
308                 .u = {
309                         .rename = {
310                                  .remote_name = "samba4DITContentRules"
311                          }
312                 }
313         },
314         {
315                 .local_name = "attributeTypes",
316                 .type = MAP_RENAME,
317                 .u = {
318                         .rename = {
319                                  .remote_name = "samba4AttributeTypes"
320                          }
321                 }
322         },
323         {
324                 .local_name = "objectCategory",
325                 .type = 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 = MAP_RENAME,
337                 .u = {
338                         .rename = {
339                                  .remote_name = "entryDN"
340                          }
341                 }
342         },
343         {
344                 .local_name = "groupType",
345                 .type = MAP_CONVERT,
346                 .u = {
347                         .convert = {
348                                  .remote_name = "groupType",
349                                  .convert_local = normalise_to_signed32,
350                                  .convert_remote = val_copy,
351                          },
352                 }
353         },
354         {
355                 .local_name = "userAccountControl",
356                 .type = MAP_CONVERT,
357                 .u = {
358                         .convert = {
359                                  .remote_name = "userAccountControl",
360                                  .convert_local = normalise_to_signed32,
361                                  .convert_remote = val_copy,
362                          },
363                 }
364         },
365         {
366                 .local_name = "sAMAccountType",
367                 .type = MAP_CONVERT,
368                 .u = {
369                         .convert = {
370                                  .remote_name = "sAMAccountType",
371                                  .convert_local = normalise_to_signed32,
372                                  .convert_remote = val_copy,
373                          },
374                 }
375         },
376         {
377                 .local_name = "systemFlags",
378                 .type = MAP_CONVERT,
379                 .u = {
380                         .convert = {
381                                  .remote_name = "systemFlags",
382                                  .convert_local = normalise_to_signed32,
383                                  .convert_remote = val_copy,
384                          },
385                 }
386         },
387         {
388                 .local_name = "usnChanged",
389                 .type = MAP_CONVERT,
390                 .u = {
391                         .convert = {
392                                  .remote_name = "entryCSN",
393                                  .convert_local = usn_to_entryCSN,
394                                  .convert_remote = entryCSN_to_usn
395                          },
396                 },
397         },
398         {
399                 .local_name = "usnCreated",
400                 .type = MAP_CONVERT,
401                 .u = {
402                         .convert = {
403                                  .remote_name = "createTimestamp",
404                                  .convert_local = usn_to_timestamp,
405                                  .convert_remote = timestamp_to_usn,
406                          },
407                 },
408         },
409         {
410                 .local_name = "*",
411                 .type = MAP_KEEP,
412         },
413         {
414                 .local_name = NULL,
415         }
416 };
417
418 /* This objectClass conflicts with builtin classes on OpenLDAP */
419 const struct ldb_map_objectclass entryuuid_objectclasses[] =
420 {
421         {
422                 .local_name = "subSchema",
423                 .remote_name = "samba4SubSchema"
424         },
425         {
426                 .local_name = NULL
427         }
428 };
429
430 /* These things do not show up in wildcard searches in OpenLDAP, but
431  * we need them to show up in the AD-like view */
432 static const char * const entryuuid_wildcard_attributes[] = {
433         "objectGUID", 
434         "whenCreated", 
435         "whenChanged",
436         "usnCreated",
437         "usnChanged",
438         "memberOf",
439         NULL
440 };
441
442 static const struct ldb_map_attribute nsuniqueid_attributes[] = 
443 {
444         /* objectGUID */
445         {
446                 .local_name = "objectGUID",
447                 .type = MAP_CONVERT,
448                 .u = {
449                         .convert = {
450                                 .remote_name = "nsuniqueid", 
451                                 .convert_local = guid_ns_string,
452                                 .convert_remote = encode_ns_guid,
453                         },
454                 },
455         },
456         /* objectSid */ 
457         {
458                 .local_name = "objectSid",
459                 .type = MAP_CONVERT,
460                 .u = {
461                         .convert = {
462                                 .remote_name = "objectSid", 
463                                 .convert_local = sid_always_binary,
464                                 .convert_remote = val_copy,
465                         },
466                 },
467         },
468         {
469                 .local_name = "whenCreated",
470                 .type = MAP_RENAME,
471                 .u = {
472                         .rename = {
473                                  .remote_name = "createTimestamp"
474                          }
475                 }
476         },
477         {
478                 .local_name = "whenChanged",
479                 .type = MAP_RENAME,
480                 .u = {
481                         .rename = {
482                                  .remote_name = "modifyTimestamp"
483                          }
484                 }
485         },
486         {
487                 .local_name = "objectCategory",
488                 .type = MAP_CONVERT,
489                 .u = {
490                         .convert = {
491                                 .remote_name = "objectCategory", 
492                                 .convert_local = objectCategory_always_dn,
493                                 .convert_remote = val_copy,
494                         },
495                 },
496         },
497         {
498                 .local_name = "distinguishedName",
499                 .type = MAP_RENAME,
500                 .u = {
501                         .rename = {
502                                  .remote_name = "entryDN"
503                          }
504                 }
505         },
506         {
507                 .local_name = "groupType",
508                 .type = MAP_CONVERT,
509                 .u = {
510                         .convert = {
511                                  .remote_name = "groupType",
512                                  .convert_local = normalise_to_signed32,
513                                  .convert_remote = val_copy,
514                          },
515                 }
516         },
517         {
518                 .local_name = "userAccountControl",
519                 .type = MAP_CONVERT,
520                 .u = {
521                         .convert = {
522                                  .remote_name = "userAccountControl",
523                                  .convert_local = normalise_to_signed32,
524                                  .convert_remote = val_copy,
525                          },
526                 }
527         },
528         {
529                 .local_name = "sAMAccountType",
530                 .type = MAP_CONVERT,
531                 .u = {
532                         .convert = {
533                                  .remote_name = "sAMAccountType",
534                                  .convert_local = normalise_to_signed32,
535                                  .convert_remote = val_copy,
536                          },
537                 }
538         },
539         {
540                 .local_name = "systemFlags",
541                 .type = MAP_CONVERT,
542                 .u = {
543                         .convert = {
544                                  .remote_name = "systemFlags",
545                                  .convert_local = normalise_to_signed32,
546                                  .convert_remote = val_copy,
547                          },
548                 }
549         },
550         {
551                 .local_name = "usnChanged",
552                 .type = MAP_CONVERT,
553                 .u = {
554                         .convert = {
555                                  .remote_name = "modifyTimestamp",
556                                  .convert_local = usn_to_timestamp,
557                                  .convert_remote = timestamp_to_usn,
558                          },
559                 },
560         },
561         {
562                 .local_name = "usnCreated",
563                 .type = MAP_CONVERT,
564                 .u = {
565                         .convert = {
566                                  .remote_name = "createTimestamp",
567                                  .convert_local = usn_to_timestamp,
568                                  .convert_remote = timestamp_to_usn,
569                          },
570                 },
571         },
572         {
573                 .local_name = "*",
574                 .type = MAP_KEEP,
575         },
576         {
577                 .local_name = NULL,
578         }
579 };
580
581 /* These things do not show up in wildcard searches in OpenLDAP, but
582  * we need them to show up in the AD-like view */
583 static const char * const nsuniqueid_wildcard_attributes[] = {
584         "objectGUID", 
585         "whenCreated", 
586         "whenChanged",
587         "usnCreated",
588         "usnChanged",
589         NULL
590 };
591
592 /* the context init function */
593 static int entryuuid_init(struct ldb_module *module)
594 {
595         int ret;
596         ret = ldb_map_init(module, entryuuid_attributes, entryuuid_objectclasses, entryuuid_wildcard_attributes, "samba4Top", NULL);
597         if (ret != LDB_SUCCESS)
598                 return ret;
599
600         return ldb_next_init(module);
601 }
602
603 /* the context init function */
604 static int nsuniqueid_init(struct ldb_module *module)
605 {
606         int ret;
607         ret = ldb_map_init(module, nsuniqueid_attributes, NULL, nsuniqueid_wildcard_attributes, "extensibleObject", NULL);
608         if (ret != LDB_SUCCESS)
609                 return ret;
610
611         return ldb_next_init(module);
612 }
613
614 static int get_seq_callback(struct ldb_request *req,
615                             struct ldb_reply *ares)
616 {
617         unsigned long long *seq = (unsigned long long *)req->context;
618
619         if (!ares) {
620                 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
621         }
622         if (ares->error != LDB_SUCCESS) {
623                 return ldb_request_done(req, ares->error);
624         }
625
626         if (ares->type == LDB_REPLY_ENTRY) {
627                 struct ldb_message_element *el = ldb_msg_find_element(ares->message, "contextCSN");
628                 if (el) {
629                         *seq = entryCSN_to_usn_int(ares, &el->values[0]);
630                 }
631         }
632
633         if (ares->type == LDB_REPLY_DONE) {
634                 return ldb_request_done(req, LDB_SUCCESS);
635         }
636
637         talloc_free(ares);
638         return LDB_SUCCESS;
639 }
640
641 static int entryuuid_sequence_number(struct ldb_module *module, struct ldb_request *req)
642 {
643         struct ldb_context *ldb;
644         int ret;
645         struct map_private *map_private;
646         struct entryuuid_private *entryuuid_private;
647         unsigned long long seq_num = 0;
648         struct ldb_request *search_req;
649
650         const struct ldb_control *partition_ctrl;
651         const struct dsdb_control_current_partition *partition;
652  
653         static const char *contextCSN_attr[] = {
654                 "contextCSN", NULL
655         };
656
657         struct ldb_seqnum_request *seq;
658         struct ldb_seqnum_result *seqr;
659         struct ldb_extended *ext;
660
661         ldb = ldb_module_get_ctx(module);
662
663         seq = talloc_get_type(req->op.extended.data, struct ldb_seqnum_request);
664
665         map_private = talloc_get_type(ldb_module_get_private(module), struct map_private);
666
667         entryuuid_private = talloc_get_type(map_private->caller_private, struct entryuuid_private);
668
669         /* All this to get the DN of the parition, so we can search the right thing */
670         partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
671         if (!partition_ctrl) {
672                 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
673                               "entryuuid_sequence_number: no current partition control found");
674                 return LDB_ERR_CONSTRAINT_VIOLATION;
675         }
676
677         partition = talloc_get_type(partition_ctrl->data,
678                                     struct dsdb_control_current_partition);
679         SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
680
681         ret = ldb_build_search_req(&search_req, ldb, req,
682                                    partition->dn, LDB_SCOPE_BASE,
683                                    NULL, contextCSN_attr, NULL,
684                                    &seq_num, get_seq_callback,
685                                    NULL);
686         if (ret != LDB_SUCCESS) {
687                 return ret;
688         }
689
690         ret = ldb_next_request(module, search_req);
691
692         if (ret == LDB_SUCCESS) {
693                 ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
694         }
695
696         talloc_free(search_req);
697         if (ret != LDB_SUCCESS) {
698                 return ret;
699         }
700
701         ext = talloc_zero(req, struct ldb_extended);
702         if (!ext) {
703                 return LDB_ERR_OPERATIONS_ERROR;
704         }
705         seqr = talloc_zero(req, struct ldb_seqnum_result);
706         if (seqr == NULL) {
707                 talloc_free(ext);
708                 return LDB_ERR_OPERATIONS_ERROR;
709         }
710         ext->oid = LDB_EXTENDED_SEQUENCE_NUMBER;
711         ext->data = seqr;
712
713         switch (seq->type) {
714         case LDB_SEQ_HIGHEST_SEQ:
715                 seqr->seq_num = seq_num;
716                 break;
717         case LDB_SEQ_NEXT:
718                 seqr->seq_num = seq_num;
719                 seqr->seq_num++;
720                 break;
721         case LDB_SEQ_HIGHEST_TIMESTAMP:
722         {
723                 seqr->seq_num = (seq_num >> 24);
724                 break;
725         }
726         }
727         seqr->flags = 0;
728         seqr->flags |= LDB_SEQ_TIMESTAMP_SEQUENCE;
729         seqr->flags |= LDB_SEQ_GLOBAL_SEQUENCE;
730
731         /* send request done */
732         return ldb_module_done(req, NULL, ext, LDB_SUCCESS);
733 }
734
735 static int entryuuid_extended(struct ldb_module *module, struct ldb_request *req)
736 {
737         if (strcmp(req->op.extended.oid, LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
738                 return entryuuid_sequence_number(module, req);
739         }
740
741         return ldb_next_request(module, req);
742 }
743
744 _PUBLIC_ const struct ldb_module_ops ldb_entryuuid_module_ops = {
745         .name              = "entryuuid",
746         .init_context      = entryuuid_init,
747         .extended          = entryuuid_extended,
748         LDB_MAP_OPS
749 };
750
751 _PUBLIC_ const struct ldb_module_ops ldb_nsuniqueid_module_ops = {
752         .name              = "nsuniqueid",
753         .init_context      = nsuniqueid_init,
754         .extended          = entryuuid_extended,
755         LDB_MAP_OPS
756 };