Correct handling of 32-bit integer attributes in SAMBA 4
[samba.git] / source4 / dsdb / samdb / ldb_modules / simple_ldap_map.c
1 /* 
2    ldb database module
3
4    LDAP semantics mapping module
5
6    Copyright (C) Jelmer Vernooij 2005
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /* 
24    This module relies on ldb_map to do all the real work, but performs
25    some of the trivial mappings between AD semantics and that provided
26    by OpenLDAP and similar servers.
27 */
28
29 #include "includes.h"
30 #include "ldb/include/ldb_module.h"
31 #include "ldb/ldb_map/ldb_map.h"
32
33 #include "librpc/gen_ndr/ndr_misc.h"
34 #include "librpc/ndr/libndr.h"
35 #include "dsdb/samdb/samdb.h"
36
37 struct entryuuid_private {
38         struct ldb_context *ldb;
39         struct ldb_dn **base_dns;
40 };
41
42 static struct ldb_val encode_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
43 {
44         struct GUID guid;
45         NTSTATUS status = GUID_from_data_blob(val, &guid);
46         enum ndr_err_code ndr_err;
47         struct ldb_val out = data_blob(NULL, 0);
48
49         if (!NT_STATUS_IS_OK(status)) {
50                 return out;
51         }
52         ndr_err = ndr_push_struct_blob(&out, ctx, NULL, &guid,
53                                        (ndr_push_flags_fn_t)ndr_push_GUID);
54         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
55                 return out;
56         }
57
58         return out;
59 }
60
61 static struct ldb_val guid_always_string(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
62 {
63         struct ldb_val out = data_blob(NULL, 0);
64         struct GUID guid;
65         NTSTATUS status = GUID_from_data_blob(val, &guid);
66         if (!NT_STATUS_IS_OK(status)) {
67                 return out;
68         }
69         return data_blob_string_const(GUID_string(ctx, &guid));
70 }
71
72 static struct ldb_val encode_ns_guid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
73 {
74         struct GUID guid;
75         NTSTATUS status = NS_GUID_from_string((char *)val->data, &guid);
76         enum ndr_err_code ndr_err;
77         struct ldb_val out = data_blob(NULL, 0);
78
79         if (!NT_STATUS_IS_OK(status)) {
80                 return out;
81         }
82         ndr_err = ndr_push_struct_blob(&out, ctx, NULL, &guid,
83                                        (ndr_push_flags_fn_t)ndr_push_GUID);
84         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
85                 return out;
86         }
87
88         return out;
89 }
90
91 static struct ldb_val guid_ns_string(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
92 {
93         struct ldb_val out = data_blob(NULL, 0);
94         struct GUID guid;
95         NTSTATUS status = GUID_from_data_blob(val, &guid);
96         if (!NT_STATUS_IS_OK(status)) {
97                 return out;
98         }
99         return data_blob_string_const(NS_GUID_string(ctx, &guid));
100 }
101
102 /* The backend holds binary sids, so just copy them back */
103 static struct ldb_val val_copy(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
104 {
105         struct ldb_val out = data_blob(NULL, 0);
106         out = ldb_val_dup(ctx, val);
107
108         return out;
109 }
110
111 /* Ensure we always convert sids into binary, so the backend doesn't have to know about both forms */
112 static struct ldb_val sid_always_binary(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
113 {
114         struct ldb_context *ldb = ldb_module_get_ctx(module);
115         struct ldb_val out = data_blob(NULL, 0);
116         const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, "objectSid");
117
118         if (a->syntax->canonicalise_fn(ldb, ctx, val, &out) != LDB_SUCCESS) {
119                 return data_blob(NULL, 0);
120         }
121
122         return out;
123 }
124
125 /* Ensure we always convert objectCategory into a DN */
126 static struct ldb_val objectCategory_always_dn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
127 {
128         struct ldb_context *ldb = ldb_module_get_ctx(module);
129         struct ldb_dn *dn;
130         struct ldb_val out = data_blob(NULL, 0);
131         const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, "objectCategory");
132
133         dn = ldb_dn_from_ldb_val(ctx, ldb, val);
134         if (dn && ldb_dn_validate(dn)) {
135                 talloc_free(dn);
136                 return val_copy(module, ctx, val);
137         }
138         talloc_free(dn);
139
140         if (a->syntax->canonicalise_fn(ldb, ctx, val, &out) != LDB_SUCCESS) {
141                 return data_blob(NULL, 0);
142         }
143
144         return out;
145 }
146
147 static struct ldb_val normalise_to_signed32(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
148 {
149         struct ldb_val out;
150         int32_t i = (int32_t) strtol((char *)val->data, NULL, 0);
151         out = data_blob_string_const(talloc_asprintf(ctx, "%d", i));
152         return out;
153 }
154
155 static struct ldb_val usn_to_entryCSN(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
156 {
157         struct ldb_val out;
158         unsigned long long usn = strtoull((const char *)val->data, NULL, 10);
159         time_t t = (usn >> 24);
160         out = data_blob_string_const(talloc_asprintf(ctx, "%s#%06x#00#000000", ldb_timestring(ctx, t), (unsigned int)(usn & 0xFFFFFF)));
161         return out;
162 }
163
164 static unsigned long long entryCSN_to_usn_int(TALLOC_CTX *ctx, const struct ldb_val *val) 
165 {
166         char *entryCSN = talloc_strdup(ctx, (const char *)val->data);
167         char *mod_per_sec;
168         time_t t;
169         unsigned long long usn;
170         char *p;
171         if (!entryCSN) {
172                 return 0;
173         }
174         p = strchr(entryCSN, '#');
175         if (!p) {
176                 return 0;
177         }
178         p[0] = '\0';
179         p++;
180         mod_per_sec = p;
181
182         p = strchr(p, '#');
183         if (!p) {
184                 return 0;
185         }
186         p[0] = '\0';
187         p++;
188
189         usn = strtol(mod_per_sec, NULL, 16);
190
191         t = ldb_string_to_time(entryCSN);
192         
193         usn = usn | ((unsigned long long)t <<24);
194         return usn;
195 }
196
197 static struct ldb_val entryCSN_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
198 {
199         struct ldb_val out;
200         unsigned long long usn = entryCSN_to_usn_int(ctx, val);
201         out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
202         return out;
203 }
204
205 static struct ldb_val usn_to_timestamp(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
206 {
207         struct ldb_val out;
208         unsigned long long usn = strtoull((const char *)val->data, NULL, 10);
209         time_t t = (usn >> 24);
210         out = data_blob_string_const(ldb_timestring(ctx, t));
211         return out;
212 }
213
214 static struct ldb_val timestamp_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
215 {
216         struct ldb_val out;
217         time_t t;
218         unsigned long long usn;
219
220         t = ldb_string_to_time((const char *)val->data);
221         
222         usn = ((unsigned long long)t <<24);
223
224         out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
225         return out;
226 }
227
228
229 static const struct ldb_map_attribute entryuuid_attributes[] = 
230 {
231         /* objectGUID */
232         {
233                 .local_name = "objectGUID",
234                 .type = MAP_CONVERT,
235                 .u = {
236                         .convert = {
237                                 .remote_name = "entryUUID", 
238                                 .convert_local = guid_always_string,
239                                 .convert_remote = encode_guid,
240                         },
241                 },
242         },
243         /* invocationId */
244         {
245                 .local_name = "invocationId",
246                 .type = MAP_CONVERT,
247                 .u = {
248                         .convert = {
249                                 .remote_name = "invocationId", 
250                                 .convert_local = guid_always_string,
251                                 .convert_remote = encode_guid,
252                         },
253                 },
254         },
255         /* objectSid */
256         {
257                 .local_name = "objectSid",
258                 .type = MAP_CONVERT,
259                 .u = {
260                         .convert = {
261                                 .remote_name = "objectSid", 
262                                 .convert_local = sid_always_binary,
263                                 .convert_remote = val_copy,
264                         },
265                 },
266         },
267         {
268                 .local_name = "name",
269                 .type = MAP_RENAME,
270                 .u = {
271                         .rename = {
272                                  .remote_name = "samba4RDN"
273                          }
274                 }
275         },
276         {
277                 .local_name = "whenCreated",
278                 .type = MAP_RENAME,
279                 .u = {
280                         .rename = {
281                                  .remote_name = "createTimestamp"
282                          }
283                 }
284         },
285         {
286                 .local_name = "whenChanged",
287                 .type = MAP_RENAME,
288                 .u = {
289                         .rename = {
290                                  .remote_name = "modifyTimestamp"
291                          }
292                 }
293         },
294         {
295                 .local_name = "objectClasses",
296                 .type = MAP_RENAME,
297                 .u = {
298                         .rename = {
299                                  .remote_name = "samba4ObjectClasses"
300                          }
301                 }
302         },
303         {
304                 .local_name = "dITContentRules",
305                 .type = MAP_RENAME,
306                 .u = {
307                         .rename = {
308                                  .remote_name = "samba4DITContentRules"
309                          }
310                 }
311         },
312         {
313                 .local_name = "attributeTypes",
314                 .type = MAP_RENAME,
315                 .u = {
316                         .rename = {
317                                  .remote_name = "samba4AttributeTypes"
318                          }
319                 }
320         },
321         {
322                 .local_name = "objectCategory",
323                 .type = MAP_CONVERT,
324                 .u = {
325                         .convert = {
326                                 .remote_name = "objectCategory", 
327                                 .convert_local = objectCategory_always_dn,
328                                 .convert_remote = val_copy,
329                         },
330                 },
331         },
332         {
333                 .local_name = "distinguishedName",
334                 .type = MAP_RENAME,
335                 .u = {
336                         .rename = {
337                                  .remote_name = "entryDN"
338                          }
339                 }
340         },
341         {
342                 .local_name = "groupType",
343                 .type = MAP_CONVERT,
344                 .u = {
345                         .convert = {
346                                  .remote_name = "groupType",
347                                  .convert_local = normalise_to_signed32,
348                                  .convert_remote = val_copy,
349                          },
350                 }
351         },
352         {
353                 .local_name = "userAccountControl",
354                 .type = MAP_CONVERT,
355                 .u = {
356                         .convert = {
357                                  .remote_name = "userAccountControl",
358                                  .convert_local = normalise_to_signed32,
359                                  .convert_remote = val_copy,
360                          },
361                 }
362         },
363         {
364                 .local_name = "sAMAccountType",
365                 .type = MAP_CONVERT,
366                 .u = {
367                         .convert = {
368                                  .remote_name = "sAMAccountType",
369                                  .convert_local = normalise_to_signed32,
370                                  .convert_remote = val_copy,
371                          },
372                 }
373         },
374         {
375                 .local_name = "usnChanged",
376                 .type = MAP_CONVERT,
377                 .u = {
378                         .convert = {
379                                  .remote_name = "entryCSN",
380                                  .convert_local = usn_to_entryCSN,
381                                  .convert_remote = entryCSN_to_usn
382                          },
383                 },
384         },
385         {
386                 .local_name = "usnCreated",
387                 .type = MAP_CONVERT,
388                 .u = {
389                         .convert = {
390                                  .remote_name = "createTimestamp",
391                                  .convert_local = usn_to_timestamp,
392                                  .convert_remote = timestamp_to_usn,
393                          },
394                 },
395         },
396         {
397                 .local_name = "*",
398                 .type = MAP_KEEP,
399         },
400         {
401                 .local_name = NULL,
402         }
403 };
404
405 /* This objectClass conflicts with builtin classes on OpenLDAP */
406 const struct ldb_map_objectclass entryuuid_objectclasses[] =
407 {
408         {
409                 .local_name = "subSchema",
410                 .remote_name = "samba4SubSchema"
411         },
412         {
413                 .local_name = NULL
414         }
415 };
416
417 /* These things do not show up in wildcard searches in OpenLDAP, but
418  * we need them to show up in the AD-like view */
419 static const char * const entryuuid_wildcard_attributes[] = {
420         "objectGUID", 
421         "whenCreated", 
422         "whenChanged",
423         "usnCreated",
424         "usnChanged",
425         "memberOf",
426         NULL
427 };
428
429 static const struct ldb_map_attribute nsuniqueid_attributes[] = 
430 {
431         /* objectGUID */
432         {
433                 .local_name = "objectGUID",
434                 .type = MAP_CONVERT,
435                 .u = {
436                         .convert = {
437                                 .remote_name = "nsuniqueid", 
438                                 .convert_local = guid_ns_string,
439                                 .convert_remote = encode_ns_guid,
440                         },
441                 },
442         },
443         /* objectSid */ 
444         {
445                 .local_name = "objectSid",
446                 .type = MAP_CONVERT,
447                 .u = {
448                         .convert = {
449                                 .remote_name = "objectSid", 
450                                 .convert_local = sid_always_binary,
451                                 .convert_remote = val_copy,
452                         },
453                 },
454         },
455         {
456                 .local_name = "whenCreated",
457                 .type = MAP_RENAME,
458                 .u = {
459                         .rename = {
460                                  .remote_name = "createTimestamp"
461                          }
462                 }
463         },
464         {
465                 .local_name = "whenChanged",
466                 .type = MAP_RENAME,
467                 .u = {
468                         .rename = {
469                                  .remote_name = "modifyTimestamp"
470                          }
471                 }
472         },
473         {
474                 .local_name = "objectCategory",
475                 .type = MAP_CONVERT,
476                 .u = {
477                         .convert = {
478                                 .remote_name = "objectCategory", 
479                                 .convert_local = objectCategory_always_dn,
480                                 .convert_remote = val_copy,
481                         },
482                 },
483         },
484         {
485                 .local_name = "distinguishedName",
486                 .type = MAP_RENAME,
487                 .u = {
488                         .rename = {
489                                  .remote_name = "entryDN"
490                          }
491                 }
492         },
493         {
494                 .local_name = "groupType",
495                 .type = MAP_CONVERT,
496                 .u = {
497                         .convert = {
498                                  .remote_name = "groupType",
499                                  .convert_local = normalise_to_signed32,
500                                  .convert_remote = val_copy,
501                          },
502                 }
503         },
504         {
505                 .local_name = "userAccountControl",
506                 .type = MAP_CONVERT,
507                 .u = {
508                         .convert = {
509                                  .remote_name = "userAccountControl",
510                                  .convert_local = normalise_to_signed32,
511                                  .convert_remote = val_copy,
512                          },
513                 }
514         },
515         {
516                 .local_name = "sAMAccountType",
517                 .type = MAP_CONVERT,
518                 .u = {
519                         .convert = {
520                                  .remote_name = "sAMAccountType",
521                                  .convert_local = normalise_to_signed32,
522                                  .convert_remote = val_copy,
523                          },
524                 }
525         },
526         {
527                 .local_name = "usnChanged",
528                 .type = MAP_CONVERT,
529                 .u = {
530                         .convert = {
531                                  .remote_name = "modifyTimestamp",
532                                  .convert_local = usn_to_timestamp,
533                                  .convert_remote = timestamp_to_usn,
534                          },
535                 },
536         },
537         {
538                 .local_name = "usnCreated",
539                 .type = MAP_CONVERT,
540                 .u = {
541                         .convert = {
542                                  .remote_name = "createTimestamp",
543                                  .convert_local = usn_to_timestamp,
544                                  .convert_remote = timestamp_to_usn,
545                          },
546                 },
547         },
548         {
549                 .local_name = "*",
550                 .type = MAP_KEEP,
551         },
552         {
553                 .local_name = NULL,
554         }
555 };
556
557 /* These things do not show up in wildcard searches in OpenLDAP, but
558  * we need them to show up in the AD-like view */
559 static const char * const nsuniqueid_wildcard_attributes[] = {
560         "objectGUID", 
561         "whenCreated", 
562         "whenChanged",
563         "usnCreated",
564         "usnChanged",
565         NULL
566 };
567
568 /* the context init function */
569 static int entryuuid_init(struct ldb_module *module)
570 {
571         int ret;
572         ret = ldb_map_init(module, entryuuid_attributes, entryuuid_objectclasses, entryuuid_wildcard_attributes, "samba4Top", NULL);
573         if (ret != LDB_SUCCESS)
574                 return ret;
575
576         return ldb_next_init(module);
577 }
578
579 /* the context init function */
580 static int nsuniqueid_init(struct ldb_module *module)
581 {
582         int ret;
583         ret = ldb_map_init(module, nsuniqueid_attributes, NULL, nsuniqueid_wildcard_attributes, "extensibleObject", NULL);
584         if (ret != LDB_SUCCESS)
585                 return ret;
586
587         return ldb_next_init(module);
588 }
589
590 static int get_seq_callback(struct ldb_request *req,
591                             struct ldb_reply *ares)
592 {
593         unsigned long long *seq = (unsigned long long *)req->context;
594
595         if (!ares) {
596                 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
597         }
598         if (ares->error != LDB_SUCCESS) {
599                 return ldb_request_done(req, ares->error);
600         }
601
602         if (ares->type == LDB_REPLY_ENTRY) {
603                 struct ldb_message_element *el = ldb_msg_find_element(ares->message, "contextCSN");
604                 if (el) {
605                         *seq = entryCSN_to_usn_int(ares, &el->values[0]);
606                 }
607         }
608
609         if (ares->type == LDB_REPLY_DONE) {
610                 return ldb_request_done(req, LDB_SUCCESS);
611         }
612
613         talloc_free(ares);
614         return LDB_SUCCESS;
615 }
616
617 static int entryuuid_sequence_number(struct ldb_module *module, struct ldb_request *req)
618 {
619         struct ldb_context *ldb;
620         int ret;
621         struct map_private *map_private;
622         struct entryuuid_private *entryuuid_private;
623         unsigned long long seq_num = 0;
624         struct ldb_request *search_req;
625
626         const struct ldb_control *partition_ctrl;
627         const struct dsdb_control_current_partition *partition;
628  
629         static const char *contextCSN_attr[] = {
630                 "contextCSN", NULL
631         };
632
633         struct ldb_seqnum_request *seq;
634         struct ldb_seqnum_result *seqr;
635         struct ldb_extended *ext;
636
637         ldb = ldb_module_get_ctx(module);
638
639         seq = talloc_get_type(req->op.extended.data, struct ldb_seqnum_request);
640
641         map_private = talloc_get_type(ldb_module_get_private(module), struct map_private);
642
643         entryuuid_private = talloc_get_type(map_private->caller_private, struct entryuuid_private);
644
645         /* All this to get the DN of the parition, so we can search the right thing */
646         partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
647         if (!partition_ctrl) {
648                 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
649                               "entryuuid_sequence_number: no current partition control found");
650                 return LDB_ERR_CONSTRAINT_VIOLATION;
651         }
652
653         partition = talloc_get_type(partition_ctrl->data,
654                                     struct dsdb_control_current_partition);
655         SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
656
657         ret = ldb_build_search_req(&search_req, ldb, req,
658                                    partition->dn, LDB_SCOPE_BASE,
659                                    NULL, contextCSN_attr, NULL,
660                                    &seq_num, get_seq_callback,
661                                    NULL);
662         if (ret != LDB_SUCCESS) {
663                 return ret;
664         }
665
666         ret = ldb_next_request(module, search_req);
667
668         if (ret == LDB_SUCCESS) {
669                 ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
670         }
671
672         talloc_free(search_req);
673         if (ret != LDB_SUCCESS) {
674                 return ret;
675         }
676
677         ext = talloc_zero(req, struct ldb_extended);
678         if (!ext) {
679                 return LDB_ERR_OPERATIONS_ERROR;
680         }
681         seqr = talloc_zero(req, struct ldb_seqnum_result);
682         if (seqr == NULL) {
683                 talloc_free(ext);
684                 return LDB_ERR_OPERATIONS_ERROR;
685         }
686         ext->oid = LDB_EXTENDED_SEQUENCE_NUMBER;
687         ext->data = seqr;
688
689         switch (seq->type) {
690         case LDB_SEQ_HIGHEST_SEQ:
691                 seqr->seq_num = seq_num;
692                 break;
693         case LDB_SEQ_NEXT:
694                 seqr->seq_num = seq_num;
695                 seqr->seq_num++;
696                 break;
697         case LDB_SEQ_HIGHEST_TIMESTAMP:
698         {
699                 seqr->seq_num = (seq_num >> 24);
700                 break;
701         }
702         }
703         seqr->flags = 0;
704         seqr->flags |= LDB_SEQ_TIMESTAMP_SEQUENCE;
705         seqr->flags |= LDB_SEQ_GLOBAL_SEQUENCE;
706
707         /* send request done */
708         return ldb_module_done(req, NULL, ext, LDB_SUCCESS);
709 }
710
711 static int entryuuid_extended(struct ldb_module *module, struct ldb_request *req)
712 {
713         if (strcmp(req->op.extended.oid, LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
714                 return entryuuid_sequence_number(module, req);
715         }
716
717         return ldb_next_request(module, req);
718 }
719
720 _PUBLIC_ const struct ldb_module_ops ldb_entryuuid_module_ops = {
721         .name              = "entryuuid",
722         .init_context      = entryuuid_init,
723         .extended          = entryuuid_extended,
724         LDB_MAP_OPS
725 };
726
727 _PUBLIC_ const struct ldb_module_ops ldb_nsuniqueid_module_ops = {
728         .name              = "nsuniqueid",
729         .init_context      = nsuniqueid_init,
730         .extended          = entryuuid_extended,
731         LDB_MAP_OPS
732 };