1/* 2 ldb database library 3 4 Copyright (C) Simo Sorce 2005-2008 5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007-2008 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. 19*/ 20 21/* 22 * Name: ldb 23 * 24 * Component: ldb extended dn control module 25 * 26 * Description: this module builds a special dn for returned search 27 * results, and fixes some other aspects of the result (returned case issues) 28 * values. 29 * 30 * Authors: Simo Sorce 31 * Andrew Bartlett 32 */ 33 34#include "includes.h" 35#include "ldb/include/ldb.h" 36#include "ldb/include/ldb_errors.h" 37#include "ldb/include/ldb_module.h" 38#include "librpc/gen_ndr/ndr_misc.h" 39#include "librpc/ndr/libndr.h" 40#include "dsdb/samdb/samdb.h" 41 42struct extended_dn_out_private { 43 bool dereference; 44 bool normalise; 45 struct dsdb_openldap_dereference_control *dereference_control; 46}; 47 48static bool is_attr_in_list(const char * const * attrs, const char *attr) 49{ 50 int i; 51 52 for (i = 0; attrs[i]; i++) { 53 if (ldb_attr_cmp(attrs[i], attr) == 0) 54 return true; 55 } 56 57 return false; 58} 59 60static char **copy_attrs(void *mem_ctx, const char * const * attrs) 61{ 62 char **nattrs; 63 int i, num; 64 65 for (num = 0; attrs[num]; num++); 66 67 nattrs = talloc_array(mem_ctx, char *, num + 1); 68 if (!nattrs) return NULL; 69 70 for(i = 0; i < num; i++) { 71 nattrs[i] = talloc_strdup(nattrs, attrs[i]); 72 if (!nattrs[i]) { 73 talloc_free(nattrs); 74 return NULL; 75 } 76 } 77 nattrs[i] = NULL; 78 79 return nattrs; 80} 81 82static bool add_attrs(void *mem_ctx, char ***attrs, const char *attr) 83{ 84 char **nattrs; 85 int num; 86 87 for (num = 0; (*attrs)[num]; num++); 88 89 nattrs = talloc_realloc(mem_ctx, *attrs, char *, num + 2); 90 if (!nattrs) return false; 91 92 *attrs = nattrs; 93 94 nattrs[num] = talloc_strdup(nattrs, attr); 95 if (!nattrs[num]) return false; 96 97 nattrs[num + 1] = NULL; 98 99 return true; 100} 101 102/* Fix the DN so that the relative attribute names are in upper case so that the DN: 103 cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com becomes 104 CN=Adminstrator,CN=users,DC=samba,DC=example,DC=com 105*/ 106 107 108static int fix_dn(struct ldb_dn *dn) 109{ 110 int i, ret; 111 char *upper_rdn_attr; 112 113 for (i=0; i < ldb_dn_get_comp_num(dn); i++) { 114 /* We need the attribute name in upper case */ 115 upper_rdn_attr = strupper_talloc(dn, 116 ldb_dn_get_component_name(dn, i)); 117 if (!upper_rdn_attr) { 118 return LDB_ERR_OPERATIONS_ERROR; 119 } 120 121 /* And replace it with CN=foo (we need the attribute in upper case */ 122 ret = ldb_dn_set_component(dn, i, upper_rdn_attr, 123 *ldb_dn_get_component_val(dn, i)); 124 talloc_free(upper_rdn_attr); 125 if (ret != LDB_SUCCESS) { 126 return ret; 127 } 128 } 129 return LDB_SUCCESS; 130} 131 132/* Inject the extended DN components, so the DN cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com becomes 133 <GUID=541203ae-f7d6-47ef-8390-bfcf019f9583>;<SID=S-1-5-21-4177067393-1453636373-93818737-500>;cn=Adminstrator,cn=users,dc=samba,dc=example,dc=com */ 134 135static int inject_extended_dn_out(struct ldb_reply *ares, 136 struct ldb_context *ldb, 137 int type, 138 bool remove_guid, 139 bool remove_sid) 140{ 141 int ret; 142 const DATA_BLOB *guid_blob; 143 const DATA_BLOB *sid_blob; 144 145 guid_blob = ldb_msg_find_ldb_val(ares->message, "objectGUID"); 146 sid_blob = ldb_msg_find_ldb_val(ares->message, "objectSID"); 147 148 if (!guid_blob) { 149 ldb_set_errstring(ldb, "Did not find objectGUID to inject into extended DN"); 150 return LDB_ERR_OPERATIONS_ERROR; 151 } 152 153 ret = ldb_dn_set_extended_component(ares->message->dn, "GUID", guid_blob); 154 if (ret != LDB_SUCCESS) { 155 return ret; 156 } 157 if (sid_blob) { 158 ret = ldb_dn_set_extended_component(ares->message->dn, "SID", sid_blob); 159 if (ret != LDB_SUCCESS) { 160 return ret; 161 } 162 } 163 164 if (remove_guid) { 165 ldb_msg_remove_attr(ares->message, "objectGUID"); 166 } 167 168 if (sid_blob && remove_sid) { 169 ldb_msg_remove_attr(ares->message, "objectSID"); 170 } 171 172 return LDB_SUCCESS; 173} 174 175static int handle_dereference(struct ldb_dn *dn, 176 struct dsdb_openldap_dereference_result **dereference_attrs, 177 const char *attr, const DATA_BLOB *val) 178{ 179 const struct ldb_val *entryUUIDblob, *sid_blob; 180 struct ldb_message fake_msg; /* easier to use routines that expect an ldb_message */ 181 int j; 182 183 fake_msg.num_elements = 0; 184 185 /* Look for this attribute in the returned control */ 186 for (j = 0; dereference_attrs && dereference_attrs[j]; j++) { 187 struct ldb_val source_dn = data_blob_string_const(dereference_attrs[j]->dereferenced_dn); 188 if (ldb_attr_cmp(dereference_attrs[j]->source_attribute, attr) == 0 189 && data_blob_cmp(&source_dn, val) == 0) { 190 fake_msg.num_elements = dereference_attrs[j]->num_attributes; 191 fake_msg.elements = dereference_attrs[j]->attributes; 192 break; 193 } 194 } 195 if (!fake_msg.num_elements) { 196 return LDB_SUCCESS; 197 } 198 /* Look for an OpenLDAP entryUUID */ 199 200 entryUUIDblob = ldb_msg_find_ldb_val(&fake_msg, "entryUUID"); 201 if (entryUUIDblob) { 202 NTSTATUS status; 203 enum ndr_err_code ndr_err; 204 205 struct ldb_val guid_blob; 206 struct GUID guid; 207 208 status = GUID_from_data_blob(entryUUIDblob, &guid); 209 210 if (!NT_STATUS_IS_OK(status)) { 211 return LDB_ERR_INVALID_DN_SYNTAX; 212 } 213 ndr_err = ndr_push_struct_blob(&guid_blob, NULL, NULL, &guid, 214 (ndr_push_flags_fn_t)ndr_push_GUID); 215 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 216 return LDB_ERR_INVALID_DN_SYNTAX; 217 } 218 219 ldb_dn_set_extended_component(dn, "GUID", &guid_blob); 220 } 221 222 sid_blob = ldb_msg_find_ldb_val(&fake_msg, "objectSID"); 223 224 /* Look for the objectSID */ 225 if (sid_blob) { 226 ldb_dn_set_extended_component(dn, "SID", sid_blob); 227 } 228 return LDB_SUCCESS; 229} 230 231/* search */ 232struct extended_search_context { 233 struct ldb_module *module; 234 const struct dsdb_schema *schema; 235 struct ldb_request *req; 236 bool inject; 237 bool remove_guid; 238 bool remove_sid; 239 int extended_type; 240}; 241 242static int extended_callback(struct ldb_request *req, struct ldb_reply *ares) 243{ 244 struct extended_search_context *ac; 245 struct ldb_control *control; 246 struct dsdb_openldap_dereference_result_control *dereference_control = NULL; 247 int ret, i, j; 248 struct ldb_message *msg = ares->message; 249 struct extended_dn_out_private *p; 250 251 ac = talloc_get_type(req->context, struct extended_search_context); 252 p = talloc_get_type(ldb_module_get_private(ac->module), struct extended_dn_out_private); 253 254 if (!ares) { 255 return ldb_module_done(ac->req, NULL, NULL, 256 LDB_ERR_OPERATIONS_ERROR); 257 } 258 if (ares->error != LDB_SUCCESS) { 259 return ldb_module_done(ac->req, ares->controls, 260 ares->response, ares->error); 261 } 262 263 switch (ares->type) { 264 case LDB_REPLY_REFERRAL: 265 return ldb_module_send_referral(ac->req, ares->referral); 266 267 case LDB_REPLY_DONE: 268 return ldb_module_done(ac->req, ares->controls, 269 ares->response, LDB_SUCCESS); 270 case LDB_REPLY_ENTRY: 271 break; 272 } 273 274 if (p && p->normalise) { 275 ret = fix_dn(ares->message->dn); 276 if (ret != LDB_SUCCESS) { 277 return ldb_module_done(ac->req, NULL, NULL, ret); 278 } 279 } 280 281 if (ac->inject) { 282 /* for each record returned post-process to add any derived 283 attributes that have been asked for */ 284 ret = inject_extended_dn_out(ares, ldb_module_get_ctx(ac->module), 285 ac->extended_type, ac->remove_guid, 286 ac->remove_sid); 287 if (ret != LDB_SUCCESS) { 288 return ldb_module_done(ac->req, NULL, NULL, ret); 289 } 290 } 291 292 if ((p && p->normalise) || ac->inject) { 293 const struct ldb_val *val = ldb_msg_find_ldb_val(ares->message, "distinguishedName"); 294 if (val) { 295 ldb_msg_remove_attr(ares->message, "distinguishedName"); 296 if (ac->inject) { 297 ret = ldb_msg_add_steal_string(ares->message, "distinguishedName", 298 ldb_dn_get_extended_linearized(ares->message, ares->message->dn, ac->extended_type)); 299 } else { 300 ret = ldb_msg_add_string(ares->message, "distinguishedName", 301 ldb_dn_get_linearized(ares->message->dn)); 302 } 303 if (ret != LDB_SUCCESS) { 304 ldb_oom(ldb_module_get_ctx(ac->module)); 305 return LDB_ERR_OPERATIONS_ERROR; 306 } 307 } 308 } 309 310 if (p && p->dereference) { 311 control = ldb_reply_get_control(ares, DSDB_OPENLDAP_DEREFERENCE_CONTROL); 312 313 if (control && control->data) { 314 dereference_control = talloc_get_type(control->data, struct dsdb_openldap_dereference_result_control); 315 } 316 } 317 318 /* Walk the retruned elements (but only if we have a schema to interpret the list with) */ 319 for (i = 0; ac->schema && i < msg->num_elements; i++) { 320 const struct dsdb_attribute *attribute; 321 attribute = dsdb_attribute_by_lDAPDisplayName(ac->schema, msg->elements[i].name); 322 if (!attribute) { 323 continue; 324 } 325 326 if (p->normalise) { 327 /* If we are also in 'normalise' mode, then 328 * fix the attribute names to be in the 329 * correct case */ 330 msg->elements[i].name = talloc_strdup(msg->elements, attribute->lDAPDisplayName); 331 if (!msg->elements[i].name) { 332 ldb_oom(ldb_module_get_ctx(ac->module)); 333 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); 334 } 335 } 336 337 /* distinguishedName has been dealt with above */ 338 if (ldb_attr_cmp(msg->elements[i].name, "distinguishedName") == 0) { 339 continue; 340 } 341 342 /* Look to see if this attributeSyntax is a DN */ 343 if (strcmp(attribute->attributeSyntax_oid, "2.5.5.1") != 0) { 344 continue; 345 } 346 347 for (j = 0; j < msg->elements[i].num_values; j++) { 348 const char *dn_str; 349 struct ldb_dn *dn = ldb_dn_from_ldb_val(ac, ldb_module_get_ctx(ac->module), &msg->elements[i].values[j]); 350 if (!dn || !ldb_dn_validate(dn)) { 351 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_INVALID_DN_SYNTAX); 352 } 353 354 if (p->normalise) { 355 ret = fix_dn(dn); 356 if (ret != LDB_SUCCESS) { 357 return ldb_module_done(ac->req, NULL, NULL, ret); 358 } 359 } 360 361 /* If we are running in dereference mode (such 362 * as against OpenLDAP) then the DN in the msg 363 * above does not contain the extended values, 364 * and we need to look in the dereference 365 * result */ 366 367 /* Look for this value in the attribute */ 368 369 if (dereference_control) { 370 ret = handle_dereference(dn, 371 dereference_control->attributes, 372 msg->elements[i].name, 373 &msg->elements[i].values[j]); 374 if (ret != LDB_SUCCESS) { 375 return ldb_module_done(ac->req, NULL, NULL, ret); 376 } 377 } 378 379 if (!ac->inject) { 380 dn_str = talloc_steal(msg->elements[i].values, 381 ldb_dn_get_linearized(dn)); 382 } else { 383 dn_str = talloc_steal(msg->elements[i].values, 384 ldb_dn_get_extended_linearized(msg->elements[i].values, 385 dn, ac->extended_type)); 386 } 387 if (!dn_str) { 388 ldb_oom(ldb_module_get_ctx(ac->module)); 389 return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); 390 } 391 msg->elements[i].values[j] = data_blob_string_const(dn_str); 392 talloc_free(dn); 393 } 394 } 395 return ldb_module_send_entry(ac->req, msg, ares->controls); 396} 397 398 399static int extended_dn_out_search(struct ldb_module *module, struct ldb_request *req) 400{ 401 struct ldb_control *control; 402 struct ldb_control *storage_format_control; 403 struct ldb_extended_dn_control *extended_ctrl = NULL; 404 struct ldb_control **saved_controls; 405 struct extended_search_context *ac; 406 struct ldb_request *down_req; 407 char **new_attrs; 408 const char * const *const_attrs; 409 int ret; 410 411 struct extended_dn_out_private *p = talloc_get_type(ldb_module_get_private(module), struct extended_dn_out_private); 412 413 /* check if there's an extended dn control */ 414 control = ldb_request_get_control(req, LDB_CONTROL_EXTENDED_DN_OID); 415 if (control && control->data) { 416 extended_ctrl = talloc_get_type(control->data, struct ldb_extended_dn_control); 417 if (!extended_ctrl) { 418 return LDB_ERR_PROTOCOL_ERROR; 419 } 420 } 421 422 /* Look to see if, as we are in 'store DN+GUID+SID' mode, the 423 * client is after the storage format (to fill in linked 424 * attributes) */ 425 storage_format_control = ldb_request_get_control(req, DSDB_CONTROL_DN_STORAGE_FORMAT_OID); 426 if (!control && storage_format_control && storage_format_control->data) { 427 extended_ctrl = talloc_get_type(storage_format_control->data, struct ldb_extended_dn_control); 428 if (!extended_ctrl) { 429 ldb_set_errstring(ldb_module_get_ctx(module), "extended_dn_out: extended_ctrl was of the wrong data type"); 430 return LDB_ERR_PROTOCOL_ERROR; 431 } 432 } 433 434 ac = talloc_zero(req, struct extended_search_context); 435 if (ac == NULL) { 436 ldb_oom(ldb_module_get_ctx(module)); 437 return LDB_ERR_OPERATIONS_ERROR; 438 } 439 440 ac->module = module; 441 ac->schema = dsdb_get_schema(ldb_module_get_ctx(module)); 442 ac->req = req; 443 ac->inject = false; 444 ac->remove_guid = false; 445 ac->remove_sid = false; 446 447 const_attrs = req->op.search.attrs; 448 449 /* We only need to do special processing if we were asked for 450 * the extended DN, or we are 'store DN+GUID+SID' 451 * (!dereference) mode. (This is the normal mode for LDB on 452 * tdb). */ 453 if (control || (storage_format_control && p && !p->dereference)) { 454 ac->inject = true; 455 if (extended_ctrl) { 456 ac->extended_type = extended_ctrl->type; 457 } else { 458 ac->extended_type = 0; 459 } 460 461 /* check if attrs only is specified, in that case check wether we need to modify them */ 462 if (req->op.search.attrs && !is_attr_in_list(req->op.search.attrs, "*")) { 463 if (! is_attr_in_list(req->op.search.attrs, "objectGUID")) { 464 ac->remove_guid = true; 465 } 466 if (! is_attr_in_list(req->op.search.attrs, "objectSID")) { 467 ac->remove_sid = true; 468 } 469 if (ac->remove_guid || ac->remove_sid) { 470 new_attrs = copy_attrs(ac, req->op.search.attrs); 471 if (new_attrs == NULL) { 472 ldb_oom(ldb_module_get_ctx(module)); 473 return LDB_ERR_OPERATIONS_ERROR; 474 } 475 476 if (ac->remove_guid) { 477 if (!add_attrs(ac, &new_attrs, "objectGUID")) 478 return LDB_ERR_OPERATIONS_ERROR; 479 } 480 if (ac->remove_sid) { 481 if (!add_attrs(ac, &new_attrs, "objectSID")) 482 return LDB_ERR_OPERATIONS_ERROR; 483 } 484 const_attrs = (const char * const *)new_attrs; 485 } 486 } 487 } 488 489 ret = ldb_build_search_req_ex(&down_req, 490 ldb_module_get_ctx(module), ac, 491 req->op.search.base, 492 req->op.search.scope, 493 req->op.search.tree, 494 const_attrs, 495 req->controls, 496 ac, extended_callback, 497 req); 498 if (ret != LDB_SUCCESS) { 499 return ret; 500 } 501 502 /* Remove extended DN and storage format controls */ 503 504 if (control) { 505 /* save it locally and remove it from the list */ 506 /* we do not need to replace them later as we 507 * are keeping the original req intact */ 508 if (!save_controls(control, down_req, &saved_controls)) { 509 return LDB_ERR_OPERATIONS_ERROR; 510 } 511 } 512 513 if (storage_format_control) { 514 /* save it locally and remove it from the list */ 515 /* we do not need to replace them later as we 516 * are keeping the original req intact */ 517 if (!save_controls(storage_format_control, down_req, &saved_controls)) { 518 return LDB_ERR_OPERATIONS_ERROR; 519 } 520 } 521 522 /* Add in dereference control, if we were asked to, we are 523 * using the 'dereference' mode (such as with an OpenLDAP 524 * backend) and have the control prepared */ 525 if (control && p && p->dereference && p->dereference_control) { 526 ret = ldb_request_add_control(down_req, 527 DSDB_OPENLDAP_DEREFERENCE_CONTROL, 528 false, p->dereference_control); 529 if (ret != LDB_SUCCESS) { 530 return ret; 531 } 532 } 533 534 /* perform the search */ 535 return ldb_next_request(module, down_req); 536} 537 538static int extended_dn_out_ldb_init(struct ldb_module *module) 539{ 540 int ret; 541 542 struct extended_dn_out_private *p = talloc(module, struct extended_dn_out_private); 543 544 ldb_module_set_private(module, p); 545 546 if (!p) { 547 ldb_oom(ldb_module_get_ctx(module)); 548 return LDB_ERR_OPERATIONS_ERROR; 549 } 550 551 p->dereference = false; 552 p->normalise = false; 553 554 ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID); 555 if (ret != LDB_SUCCESS) { 556 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR, 557 "extended_dn_out: Unable to register control with rootdse!\n"); 558 return LDB_ERR_OPERATIONS_ERROR; 559 } 560 561 return ldb_next_init(module); 562} 563 564static int extended_dn_out_dereference_init(struct ldb_module *module) 565{ 566 int ret, i = 0; 567 struct extended_dn_out_private *p = talloc_zero(module, struct extended_dn_out_private); 568 struct dsdb_openldap_dereference_control *dereference_control; 569 struct dsdb_attribute *cur; 570 571 struct dsdb_schema *schema; 572 573 ldb_module_set_private(module, p); 574 575 if (!p) { 576 ldb_oom(ldb_module_get_ctx(module)); 577 return LDB_ERR_OPERATIONS_ERROR; 578 } 579 580 p->dereference = true; 581 582 /* At the moment, servers that need dereference also need the 583 * DN and attribute names to be normalised */ 584 p->normalise = true; 585 586 ret = ldb_mod_register_control(module, LDB_CONTROL_EXTENDED_DN_OID); 587 if (ret != LDB_SUCCESS) { 588 ldb_debug(ldb_module_get_ctx(module), LDB_DEBUG_ERROR, 589 "extended_dn_out: Unable to register control with rootdse!\n"); 590 return LDB_ERR_OPERATIONS_ERROR; 591 } 592 593 ret = ldb_next_init(module); 594 595 if (ret != LDB_SUCCESS) { 596 return ret; 597 } 598 599 schema = dsdb_get_schema(ldb_module_get_ctx(module)); 600 if (!schema) { 601 /* No schema on this DB (yet) */ 602 return LDB_SUCCESS; 603 } 604 605 p->dereference_control = dereference_control 606 = talloc_zero(p, struct dsdb_openldap_dereference_control); 607 608 if (!p->dereference_control) { 609 ldb_oom(ldb_module_get_ctx(module)); 610 return LDB_ERR_OPERATIONS_ERROR; 611 } 612 613 for (cur = schema->attributes; cur; cur = cur->next) { 614 static const char *attrs[] = { 615 "entryUUID", 616 "objectSID", 617 NULL 618 }; 619 620 if (strcmp(cur->syntax->attributeSyntax_oid, "2.5.5.1") != 0) { 621 continue; 622 } 623 dereference_control->dereference 624 = talloc_realloc(p, dereference_control->dereference, 625 struct dsdb_openldap_dereference *, i + 2); 626 if (!dereference_control) { 627 ldb_oom(ldb_module_get_ctx(module)); 628 return LDB_ERR_OPERATIONS_ERROR; 629 } 630 dereference_control->dereference[i] = talloc(dereference_control->dereference, 631 struct dsdb_openldap_dereference); 632 if (!dereference_control->dereference[i]) { 633 ldb_oom(ldb_module_get_ctx(module)); 634 return LDB_ERR_OPERATIONS_ERROR; 635 } 636 dereference_control->dereference[i]->source_attribute = cur->lDAPDisplayName; 637 dereference_control->dereference[i]->dereference_attribute = attrs; 638 i++; 639 dereference_control->dereference[i] = NULL; 640 } 641 return LDB_SUCCESS; 642} 643 644_PUBLIC_ const struct ldb_module_ops ldb_extended_dn_out_ldb_module_ops = { 645 .name = "extended_dn_out_ldb", 646 .search = extended_dn_out_search, 647 .init_context = extended_dn_out_ldb_init, 648}; 649 650 651_PUBLIC_ const struct ldb_module_ops ldb_extended_dn_out_dereference_module_ops = { 652 .name = "extended_dn_out_dereference", 653 .search = extended_dn_out_search, 654 .init_context = extended_dn_out_dereference_init, 655}; 656