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