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