/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- encode_guid
- guid_always_string
- encode_ns_guid
- guid_ns_string
- val_copy
- sid_always_binary
- objectCategory_always_dn
- normalise_to_signed32
- usn_to_entryCSN
- entryCSN_to_usn_int
- entryCSN_to_usn
- usn_to_timestamp
- timestamp_to_usn
- entryuuid_init
- nsuniqueid_init
- get_seq_callback
- entryuuid_sequence_number
- entryuuid_extended
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
148 {
149 long long int signed_ll = strtoll((const char *)val->data, NULL, 10);
150 if (signed_ll >= 0x80000000LL) {
151 union {
152 int32_t signed_int;
153 uint32_t unsigned_int;
154 } u = {
155 .unsigned_int = strtoul((const char *)val->data, NULL, 10)
156 };
157
158 struct ldb_val out = data_blob_string_const(talloc_asprintf(ctx, "%d", u.signed_int));
159 return out;
160 }
161 return val_copy(module, ctx, val);
162 }
163
164 static struct ldb_val usn_to_entryCSN(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
/* [<][>][^][v][top][bottom][index][help] */
165 {
166 struct ldb_val out;
167 unsigned long long usn = strtoull((const char *)val->data, NULL, 10);
168 time_t t = (usn >> 24);
169 out = data_blob_string_const(talloc_asprintf(ctx, "%s#%06x#00#000000", ldb_timestring(ctx, t), (unsigned int)(usn & 0xFFFFFF)));
170 return out;
171 }
172
173 static unsigned long long entryCSN_to_usn_int(TALLOC_CTX *ctx, const struct ldb_val *val)
/* [<][>][^][v][top][bottom][index][help] */
174 {
175 char *entryCSN = talloc_strdup(ctx, (const char *)val->data);
176 char *mod_per_sec;
177 time_t t;
178 unsigned long long usn;
179 char *p;
180 if (!entryCSN) {
181 return 0;
182 }
183 p = strchr(entryCSN, '#');
184 if (!p) {
185 return 0;
186 }
187 p[0] = '\0';
188 p++;
189 mod_per_sec = p;
190
191 p = strchr(p, '#');
192 if (!p) {
193 return 0;
194 }
195 p[0] = '\0';
196 p++;
197
198 usn = strtol(mod_per_sec, NULL, 16);
199
200 t = ldb_string_to_time(entryCSN);
201
202 usn = usn | ((unsigned long long)t <<24);
203 return usn;
204 }
205
206 static struct ldb_val entryCSN_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
/* [<][>][^][v][top][bottom][index][help] */
207 {
208 struct ldb_val out;
209 unsigned long long usn = entryCSN_to_usn_int(ctx, val);
210 out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
211 return out;
212 }
213
214 static struct ldb_val usn_to_timestamp(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
/* [<][>][^][v][top][bottom][index][help] */
215 {
216 struct ldb_val out;
217 unsigned long long usn = strtoull((const char *)val->data, NULL, 10);
218 time_t t = (usn >> 24);
219 out = data_blob_string_const(ldb_timestring(ctx, t));
220 return out;
221 }
222
223 static struct ldb_val timestamp_to_usn(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
/* [<][>][^][v][top][bottom][index][help] */
224 {
225 struct ldb_val out;
226 time_t t;
227 unsigned long long usn;
228
229 t = ldb_string_to_time((const char *)val->data);
230
231 usn = ((unsigned long long)t <<24);
232
233 out = data_blob_string_const(talloc_asprintf(ctx, "%lld", usn));
234 return out;
235 }
236
237
238 static const struct ldb_map_attribute entryuuid_attributes[] =
239 {
240 /* objectGUID */
241 {
242 .local_name = "objectGUID",
243 .type = MAP_CONVERT,
244 .u = {
245 .convert = {
246 .remote_name = "entryUUID",
247 .convert_local = guid_always_string,
248 .convert_remote = encode_guid,
249 },
250 },
251 },
252 /* invocationId */
253 {
254 .local_name = "invocationId",
255 .type = MAP_CONVERT,
256 .u = {
257 .convert = {
258 .remote_name = "invocationId",
259 .convert_local = guid_always_string,
260 .convert_remote = encode_guid,
261 },
262 },
263 },
264 /* objectSid */
265 {
266 .local_name = "objectSid",
267 .type = MAP_CONVERT,
268 .u = {
269 .convert = {
270 .remote_name = "objectSid",
271 .convert_local = sid_always_binary,
272 .convert_remote = val_copy,
273 },
274 },
275 },
276 {
277 .local_name = "name",
278 .type = MAP_RENAME,
279 .u = {
280 .rename = {
281 .remote_name = "samba4RDN"
282 }
283 }
284 },
285 {
286 .local_name = "whenCreated",
287 .type = MAP_RENAME,
288 .u = {
289 .rename = {
290 .remote_name = "createTimestamp"
291 }
292 }
293 },
294 {
295 .local_name = "whenChanged",
296 .type = MAP_RENAME,
297 .u = {
298 .rename = {
299 .remote_name = "modifyTimestamp"
300 }
301 }
302 },
303 {
304 .local_name = "objectClasses",
305 .type = MAP_RENAME,
306 .u = {
307 .rename = {
308 .remote_name = "samba4ObjectClasses"
309 }
310 }
311 },
312 {
313 .local_name = "dITContentRules",
314 .type = MAP_RENAME,
315 .u = {
316 .rename = {
317 .remote_name = "samba4DITContentRules"
318 }
319 }
320 },
321 {
322 .local_name = "attributeTypes",
323 .type = MAP_RENAME,
324 .u = {
325 .rename = {
326 .remote_name = "samba4AttributeTypes"
327 }
328 }
329 },
330 {
331 .local_name = "objectCategory",
332 .type = MAP_CONVERT,
333 .u = {
334 .convert = {
335 .remote_name = "objectCategory",
336 .convert_local = objectCategory_always_dn,
337 .convert_remote = val_copy,
338 },
339 },
340 },
341 {
342 .local_name = "distinguishedName",
343 .type = MAP_RENAME,
344 .u = {
345 .rename = {
346 .remote_name = "entryDN"
347 }
348 }
349 },
350 {
351 .local_name = "groupType",
352 .type = MAP_CONVERT,
353 .u = {
354 .convert = {
355 .remote_name = "groupType",
356 .convert_local = normalise_to_signed32,
357 .convert_remote = val_copy,
358 },
359 }
360 },
361 {
362 .local_name = "sAMAccountType",
363 .type = MAP_CONVERT,
364 .u = {
365 .convert = {
366 .remote_name = "sAMAccountType",
367 .convert_local = normalise_to_signed32,
368 .convert_remote = val_copy,
369 },
370 }
371 },
372 {
373 .local_name = "usnChanged",
374 .type = MAP_CONVERT,
375 .u = {
376 .convert = {
377 .remote_name = "entryCSN",
378 .convert_local = usn_to_entryCSN,
379 .convert_remote = entryCSN_to_usn
380 },
381 },
382 },
383 {
384 .local_name = "usnCreated",
385 .type = MAP_CONVERT,
386 .u = {
387 .convert = {
388 .remote_name = "createTimestamp",
389 .convert_local = usn_to_timestamp,
390 .convert_remote = timestamp_to_usn,
391 },
392 },
393 },
394 {
395 .local_name = "*",
396 .type = MAP_KEEP,
397 },
398 {
399 .local_name = NULL,
400 }
401 };
402
403 /* This objectClass conflicts with builtin classes on OpenLDAP */
404 const struct ldb_map_objectclass entryuuid_objectclasses[] =
405 {
406 {
407 .local_name = "subSchema",
408 .remote_name = "samba4SubSchema"
409 },
410 {
411 .local_name = NULL
412 }
413 };
414
415 /* These things do not show up in wildcard searches in OpenLDAP, but
416 * we need them to show up in the AD-like view */
417 static const char * const entryuuid_wildcard_attributes[] = {
418 "objectGUID",
419 "whenCreated",
420 "whenChanged",
421 "usnCreated",
422 "usnChanged",
423 "memberOf",
424 NULL
425 };
426
427 static const struct ldb_map_attribute nsuniqueid_attributes[] =
428 {
429 /* objectGUID */
430 {
431 .local_name = "objectGUID",
432 .type = MAP_CONVERT,
433 .u = {
434 .convert = {
435 .remote_name = "nsuniqueid",
436 .convert_local = guid_ns_string,
437 .convert_remote = encode_ns_guid,
438 },
439 },
440 },
441 /* objectSid */
442 {
443 .local_name = "objectSid",
444 .type = MAP_CONVERT,
445 .u = {
446 .convert = {
447 .remote_name = "objectSid",
448 .convert_local = sid_always_binary,
449 .convert_remote = val_copy,
450 },
451 },
452 },
453 {
454 .local_name = "whenCreated",
455 .type = MAP_RENAME,
456 .u = {
457 .rename = {
458 .remote_name = "createTimestamp"
459 }
460 }
461 },
462 {
463 .local_name = "whenChanged",
464 .type = MAP_RENAME,
465 .u = {
466 .rename = {
467 .remote_name = "modifyTimestamp"
468 }
469 }
470 },
471 {
472 .local_name = "objectCategory",
473 .type = MAP_CONVERT,
474 .u = {
475 .convert = {
476 .remote_name = "objectCategory",
477 .convert_local = objectCategory_always_dn,
478 .convert_remote = val_copy,
479 },
480 },
481 },
482 {
483 .local_name = "distinguishedName",
484 .type = MAP_RENAME,
485 .u = {
486 .rename = {
487 .remote_name = "entryDN"
488 }
489 }
490 },
491 {
492 .local_name = "groupType",
493 .type = MAP_CONVERT,
494 .u = {
495 .convert = {
496 .remote_name = "groupType",
497 .convert_local = normalise_to_signed32,
498 .convert_remote = val_copy,
499 },
500 }
501 },
502 {
503 .local_name = "sAMAccountType",
504 .type = MAP_CONVERT,
505 .u = {
506 .convert = {
507 .remote_name = "sAMAccountType",
508 .convert_local = normalise_to_signed32,
509 .convert_remote = val_copy,
510 },
511 }
512 },
513 {
514 .local_name = "usnChanged",
515 .type = MAP_CONVERT,
516 .u = {
517 .convert = {
518 .remote_name = "modifyTimestamp",
519 .convert_local = usn_to_timestamp,
520 .convert_remote = timestamp_to_usn,
521 },
522 },
523 },
524 {
525 .local_name = "usnCreated",
526 .type = MAP_CONVERT,
527 .u = {
528 .convert = {
529 .remote_name = "createTimestamp",
530 .convert_local = usn_to_timestamp,
531 .convert_remote = timestamp_to_usn,
532 },
533 },
534 },
535 {
536 .local_name = "*",
537 .type = MAP_KEEP,
538 },
539 {
540 .local_name = NULL,
541 }
542 };
543
544 /* These things do not show up in wildcard searches in OpenLDAP, but
545 * we need them to show up in the AD-like view */
546 static const char * const nsuniqueid_wildcard_attributes[] = {
547 "objectGUID",
548 "whenCreated",
549 "whenChanged",
550 "usnCreated",
551 "usnChanged",
552 NULL
553 };
554
555 /* the context init function */
556 static int entryuuid_init(struct ldb_module *module)
/* [<][>][^][v][top][bottom][index][help] */
557 {
558 int ret;
559 ret = ldb_map_init(module, entryuuid_attributes, entryuuid_objectclasses, entryuuid_wildcard_attributes, "samba4Top", NULL);
560 if (ret != LDB_SUCCESS)
561 return ret;
562
563 return ldb_next_init(module);
564 }
565
566 /* the context init function */
567 static int nsuniqueid_init(struct ldb_module *module)
/* [<][>][^][v][top][bottom][index][help] */
568 {
569 int ret;
570 ret = ldb_map_init(module, nsuniqueid_attributes, NULL, nsuniqueid_wildcard_attributes, "extensibleObject", NULL);
571 if (ret != LDB_SUCCESS)
572 return ret;
573
574 return ldb_next_init(module);
575 }
576
577 static int get_seq_callback(struct ldb_request *req,
/* [<][>][^][v][top][bottom][index][help] */
578 struct ldb_reply *ares)
579 {
580 unsigned long long *seq = (unsigned long long *)req->context;
581
582 if (!ares) {
583 return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
584 }
585 if (ares->error != LDB_SUCCESS) {
586 return ldb_request_done(req, ares->error);
587 }
588
589 if (ares->type == LDB_REPLY_ENTRY) {
590 struct ldb_message_element *el = ldb_msg_find_element(ares->message, "contextCSN");
591 if (el) {
592 *seq = entryCSN_to_usn_int(ares, &el->values[0]);
593 }
594 }
595
596 if (ares->type == LDB_REPLY_DONE) {
597 return ldb_request_done(req, LDB_SUCCESS);
598 }
599
600 talloc_free(ares);
601 return LDB_SUCCESS;
602 }
603
604 static int entryuuid_sequence_number(struct ldb_module *module, struct ldb_request *req)
/* [<][>][^][v][top][bottom][index][help] */
605 {
606 struct ldb_context *ldb;
607 int ret;
608 struct map_private *map_private;
609 struct entryuuid_private *entryuuid_private;
610 unsigned long long seq_num = 0;
611 struct ldb_request *search_req;
612
613 const struct ldb_control *partition_ctrl;
614 const struct dsdb_control_current_partition *partition;
615
616 static const char *contextCSN_attr[] = {
617 "contextCSN", NULL
618 };
619
620 struct ldb_seqnum_request *seq;
621 struct ldb_seqnum_result *seqr;
622 struct ldb_extended *ext;
623
624 ldb = ldb_module_get_ctx(module);
625
626 seq = talloc_get_type(req->op.extended.data, struct ldb_seqnum_request);
627
628 map_private = talloc_get_type(ldb_module_get_private(module), struct map_private);
629
630 entryuuid_private = talloc_get_type(map_private->caller_private, struct entryuuid_private);
631
632 /* All this to get the DN of the parition, so we can search the right thing */
633 partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
634 if (!partition_ctrl) {
635 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
636 "entryuuid_sequence_number: no current partition control found");
637 return LDB_ERR_CONSTRAINT_VIOLATION;
638 }
639
640 partition = talloc_get_type(partition_ctrl->data,
641 struct dsdb_control_current_partition);
642 SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
643
644 ret = ldb_build_search_req(&search_req, ldb, req,
645 partition->dn, LDB_SCOPE_BASE,
646 NULL, contextCSN_attr, NULL,
647 &seq_num, get_seq_callback,
648 NULL);
649 if (ret != LDB_SUCCESS) {
650 return ret;
651 }
652
653 ret = ldb_next_request(module, search_req);
654
655 if (ret == LDB_SUCCESS) {
656 ret = ldb_wait(search_req->handle, LDB_WAIT_ALL);
657 }
658
659 talloc_free(search_req);
660 if (ret != LDB_SUCCESS) {
661 return ret;
662 }
663
664 ext = talloc_zero(req, struct ldb_extended);
665 if (!ext) {
666 return LDB_ERR_OPERATIONS_ERROR;
667 }
668 seqr = talloc_zero(req, struct ldb_seqnum_result);
669 if (seqr == NULL) {
670 talloc_free(ext);
671 return LDB_ERR_OPERATIONS_ERROR;
672 }
673 ext->oid = LDB_EXTENDED_SEQUENCE_NUMBER;
674 ext->data = seqr;
675
676 switch (seq->type) {
677 case LDB_SEQ_HIGHEST_SEQ:
678 seqr->seq_num = seq_num;
679 break;
680 case LDB_SEQ_NEXT:
681 seqr->seq_num = seq_num;
682 seqr->seq_num++;
683 break;
684 case LDB_SEQ_HIGHEST_TIMESTAMP:
685 {
686 seqr->seq_num = (seq_num >> 24);
687 break;
688 }
689 }
690 seqr->flags = 0;
691 seqr->flags |= LDB_SEQ_TIMESTAMP_SEQUENCE;
692 seqr->flags |= LDB_SEQ_GLOBAL_SEQUENCE;
693
694 /* send request done */
695 return ldb_module_done(req, NULL, ext, LDB_SUCCESS);
696 }
697
698 static int entryuuid_extended(struct ldb_module *module, struct ldb_request *req)
/* [<][>][^][v][top][bottom][index][help] */
699 {
700 if (strcmp(req->op.extended.oid, LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
701 return entryuuid_sequence_number(module, req);
702 }
703
704 return ldb_next_request(module, req);
705 }
706
707 _PUBLIC_ const struct ldb_module_ops ldb_entryuuid_module_ops = {
708 .name = "entryuuid",
709 .init_context = entryuuid_init,
710 .extended = entryuuid_extended,
711 LDB_MAP_OPS
712 };
713
714 _PUBLIC_ const struct ldb_module_ops ldb_nsuniqueid_module_ops = {
715 .name = "nsuniqueid",
716 .init_context = nsuniqueid_init,
717 .extended = entryuuid_extended,
718 LDB_MAP_OPS
719 };