Merge branch 'master' of ssh://git.samba.org/data/git/samba into master-devel
[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.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_callback(struct ldb_request *req,
607                             struct ldb_reply *ares)
608 {
609         unsigned long long *seq = (unsigned long long *)req->context;
610
611         if (!ares) {
612                 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
613         }
614         if (ares->error != LDB_SUCCESS) {
615                 return ldb_request_done(req, ares->error);
616         }
617
618         if (ares->type == LDB_REPLY_ENTRY) {
619                 struct ldb_message_element *el = ldb_msg_find_element(ares->message, "contextCSN");
620                 if (el) {
621                         *seq = entryCSN_to_usn_int(ares, &el->values[0]);
622                 }
623         }
624
625         if (ares->type == LDB_REPLY_DONE) {
626                 return ldb_request_done(req, LDB_SUCCESS);
627         }
628
629         talloc_free(ares);
630         return LDB_SUCCESS;
631 }
632
633 static int entryuuid_sequence_number(struct ldb_module *module, struct ldb_request *req)
634 {
635         int ret;
636         struct map_private *map_private;
637         struct entryuuid_private *entryuuid_private;
638         unsigned long long seq_num = 0;
639         struct ldb_request *search_req;
640
641         const struct ldb_control *partition_ctrl;
642         const struct dsdb_control_current_partition *partition;
643  
644         static const char *contextCSN_attr[] = {
645                 "contextCSN", NULL
646         };
647
648         struct ldb_seqnum_request *seq;
649         struct ldb_seqnum_result *seqr;
650         struct ldb_extended *ext;
651
652         seq = talloc_get_type(req->op.extended.data, struct ldb_seqnum_request);
653
654         map_private = talloc_get_type(module->private_data, struct map_private);
655
656         entryuuid_private = talloc_get_type(map_private->caller_private, struct entryuuid_private);
657
658         /* All this to get the DN of the parition, so we can search the right thing */
659         partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
660         if (!partition_ctrl) {
661                 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
662                               "entryuuid_sequence_number: no current partition control found");
663                 return LDB_ERR_CONSTRAINT_VIOLATION;
664         }
665
666         partition = talloc_get_type(partition_ctrl->data,
667                                     struct dsdb_control_current_partition);
668         SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
669
670         ret = ldb_build_search_req(&search_req, module->ldb, req,
671                                    partition->dn, LDB_SCOPE_BASE,
672                                    NULL, contextCSN_attr, NULL,
673                                    &seq_num, get_seq_callback,
674                                    NULL);
675         if (ret != LDB_SUCCESS) {
676                 return ret;
677         }
678
679         ret = ldb_next_request(module, search_req);
680
681         if (ret == LDB_SUCCESS) {
682                 ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
683         }
684
685         talloc_free(search_req);
686         if (ret != LDB_SUCCESS) {
687                 return ret;
688         }
689
690         ext = talloc_zero(req, struct ldb_extended);
691         if (!ext) {
692                 return LDB_ERR_OPERATIONS_ERROR;
693         }
694         seqr = talloc_zero(req, struct ldb_seqnum_result);
695         if (seqr == NULL) {
696                 talloc_free(ext);
697                 return LDB_ERR_OPERATIONS_ERROR;
698         }
699         ext->oid = LDB_EXTENDED_SEQUENCE_NUMBER;
700         ext->data = seqr;
701
702         switch (seq->type) {
703         case LDB_SEQ_HIGHEST_SEQ:
704                 seqr->seq_num = seq_num;
705                 break;
706         case LDB_SEQ_NEXT:
707                 seqr->seq_num = seq_num;
708                 seqr->seq_num++;
709                 break;
710         case LDB_SEQ_HIGHEST_TIMESTAMP:
711         {
712                 seqr->seq_num = (seq_num >> 24);
713                 break;
714         }
715         }
716         seqr->flags = 0;
717         seqr->flags |= LDB_SEQ_TIMESTAMP_SEQUENCE;
718         seqr->flags |= LDB_SEQ_GLOBAL_SEQUENCE;
719
720         /* send request done */
721         return ldb_module_done(req, NULL, ext, LDB_SUCCESS);
722 }
723
724 static int entryuuid_extended(struct ldb_module *module, struct ldb_request *req)
725 {
726         if (strcmp(req->op.extended.oid, LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
727                 return entryuuid_sequence_number(module, req);
728         }
729
730         return ldb_next_request(module, req);
731 }
732
733 _PUBLIC_ const struct ldb_module_ops ldb_entryuuid_module_ops = {
734         .name              = "entryuuid",
735         .init_context      = entryuuid_init,
736         .extended          = entryuuid_extended,
737         LDB_MAP_OPS
738 };
739
740 _PUBLIC_ const struct ldb_module_ops ldb_nsuniqueid_module_ops = {
741         .name              = "nsuniqueid",
742         .init_context      = nsuniqueid_init,
743         .extended          = entryuuid_extended,
744         LDB_MAP_OPS
745 };