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