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