1/* chain.c - chain LDAP operations */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2003-2011 The OpenLDAP Foundation. 6 * Portions Copyright 2003 Howard Chu. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17/* ACKNOWLEDGEMENTS: 18 * This work was initially developed by the Howard Chu for inclusion 19 * in OpenLDAP Software. 20 * This work was subsequently modified by Pierangelo Masarati. 21 */ 22 23#include "portable.h" 24 25#include <stdio.h> 26 27#include <ac/string.h> 28#include <ac/socket.h> 29 30#include "lutil.h" 31#include "slap.h" 32#include "back-ldap.h" 33#include "config.h" 34 35#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 36#define SLAP_CHAINING_DEFAULT LDAP_CHAINING_PREFERRED 37#define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT 38#define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT) 39#define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT) 40#define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT) 41#define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT) 42#define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT) 43#define SLAP_CH_RESOLVE_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT) 44#define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2) 45#define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT) 46#define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT) 47#define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT) 48#define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT) 49#define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT) 50#define SLAP_CH_CONTINUATION_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT) 51 52#define o_chaining o_ctrlflag[sc_chainingBehavior] 53#define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK) 54#define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK)) 55#define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK) 56#define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK) 57 58static int sc_chainingBehavior; 59#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 60 61typedef enum { 62 LDAP_CH_NONE = 0, 63 LDAP_CH_RES, 64 LDAP_CH_ERR 65} ldap_chain_status_t; 66 67static BackendInfo *lback; 68 69typedef struct ldap_chain_t { 70 /* 71 * A "template" ldapinfo_t gets all common configuration items; 72 * then, for each configured URI, an entry is created in the tree; 73 * all the specific configuration items get in the current URI 74 * structure. 75 * 76 * Then, for each referral, extract the URI and lookup the 77 * related structure. If configured to do so, allow URIs 78 * not found in the structure to create a temporary one 79 * that chains anonymously; maybe it can also be added to 80 * the tree? Should be all configurable. 81 */ 82 83 /* "common" configuration info (anything occurring before an "uri") */ 84 ldapinfo_t *lc_common_li; 85 86 /* current configuration info */ 87 ldapinfo_t *lc_cfg_li; 88 89 /* tree of configured[/generated?] "uri" info */ 90 ldap_avl_info_t lc_lai; 91 92 /* max depth in nested referrals chaining */ 93 int lc_max_depth; 94 95 unsigned lc_flags; 96#define LDAP_CHAIN_F_NONE (0x00U) 97#define LDAP_CHAIN_F_CHAINING (0x01U) 98#define LDAP_CHAIN_F_CACHE_URI (0x02U) 99#define LDAP_CHAIN_F_RETURN_ERR (0x04U) 100 101#define LDAP_CHAIN_ISSET(lc, f) ( ( (lc)->lc_flags & (f) ) == (f) ) 102#define LDAP_CHAIN_CHAINING( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING ) 103#define LDAP_CHAIN_CACHE_URI( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI ) 104#define LDAP_CHAIN_RETURN_ERR( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR ) 105 106#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 107 LDAPControl lc_chaining_ctrl; 108 char lc_chaining_ctrlflag; 109#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 110} ldap_chain_t; 111 112static int ldap_chain_db_init_common( BackendDB *be ); 113static int ldap_chain_db_init_one( BackendDB *be ); 114static int ldap_chain_db_open_one( BackendDB *be ); 115#define ldap_chain_db_close_one(be) (0) 116#define ldap_chain_db_destroy_one(be, rs) (lback)->bi_db_destroy( (be), (rs) ) 117 118typedef struct ldap_chain_cb_t { 119 ldap_chain_status_t lb_status; 120 ldap_chain_t *lb_lc; 121 BI_op_func *lb_op_f; 122 int lb_depth; 123} ldap_chain_cb_t; 124 125static int 126ldap_chain_op( 127 Operation *op, 128 SlapReply *rs, 129 BI_op_func *op_f, 130 BerVarray ref, 131 int depth ); 132 133static int 134ldap_chain_search( 135 Operation *op, 136 SlapReply *rs, 137 BerVarray ref, 138 int depth ); 139 140static slap_overinst ldapchain; 141 142#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 143static int 144chaining_control_add( 145 ldap_chain_t *lc, 146 Operation *op, 147 LDAPControl ***oldctrlsp ) 148{ 149 LDAPControl **ctrls = NULL; 150 int c = 0; 151 152 *oldctrlsp = op->o_ctrls; 153 154 /* default chaining control not defined */ 155 if ( !LDAP_CHAIN_CHAINING( lc ) ) { 156 return 0; 157 } 158 159 /* already present */ 160 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) { 161 return 0; 162 } 163 164 /* FIXME: check other incompatibilities */ 165 166 /* add to other controls */ 167 if ( op->o_ctrls ) { 168 for ( c = 0; op->o_ctrls[ c ]; c++ ) 169 /* count them */ ; 170 } 171 172 ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 ); 173 ctrls[ 0 ] = &lc->lc_chaining_ctrl; 174 if ( op->o_ctrls ) { 175 for ( c = 0; op->o_ctrls[ c ]; c++ ) { 176 ctrls[ c + 1 ] = op->o_ctrls[ c ]; 177 } 178 } 179 ctrls[ c + 1 ] = NULL; 180 181 op->o_ctrls = ctrls; 182 183 op->o_chaining = lc->lc_chaining_ctrlflag; 184 185 return 0; 186} 187 188static int 189chaining_control_remove( 190 Operation *op, 191 LDAPControl ***oldctrlsp ) 192{ 193 LDAPControl **oldctrls = *oldctrlsp; 194 195 /* we assume that the first control is the chaining control 196 * added by the chain overlay, so it's the only one we explicitly 197 * free */ 198 if ( op->o_ctrls != oldctrls ) { 199 assert( op->o_ctrls != NULL ); 200 assert( op->o_ctrls[ 0 ] != NULL ); 201 202 free( op->o_ctrls ); 203 204 op->o_chaining = 0; 205 op->o_ctrls = oldctrls; 206 } 207 208 *oldctrlsp = NULL; 209 210 return 0; 211} 212#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 213 214static int 215ldap_chain_uri_cmp( const void *c1, const void *c2 ) 216{ 217 const ldapinfo_t *li1 = (const ldapinfo_t *)c1; 218 const ldapinfo_t *li2 = (const ldapinfo_t *)c2; 219 220 assert( li1->li_bvuri != NULL ); 221 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) ); 222 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) ); 223 224 assert( li2->li_bvuri != NULL ); 225 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) ); 226 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) ); 227 228 return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ); 229} 230 231static int 232ldap_chain_uri_dup( void *c1, void *c2 ) 233{ 234 ldapinfo_t *li1 = (ldapinfo_t *)c1; 235 ldapinfo_t *li2 = (ldapinfo_t *)c2; 236 237 assert( li1->li_bvuri != NULL ); 238 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) ); 239 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) ); 240 241 assert( li2->li_bvuri != NULL ); 242 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) ); 243 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) ); 244 245 if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) { 246 return -1; 247 } 248 249 return 0; 250} 251 252/* 253 * Search specific response that strips entryDN from entries 254 */ 255static int 256ldap_chain_cb_search_response( Operation *op, SlapReply *rs ) 257{ 258 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private; 259 260 assert( op->o_tag == LDAP_REQ_SEARCH ); 261 262 /* if in error, don't proceed any further */ 263 if ( lb->lb_status == LDAP_CH_ERR ) { 264 return 0; 265 } 266 267 if ( rs->sr_type == REP_SEARCH ) { 268 Attribute **ap = &rs->sr_entry->e_attrs; 269 270 for ( ; *ap != NULL; ap = &(*ap)->a_next ) { 271 /* will be generated later by frontend 272 * (a cleaner solution would be that 273 * the frontend checks if it already exists */ 274 if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 ) 275 { 276 Attribute *a = *ap; 277 278 *ap = (*ap)->a_next; 279 attr_free( a ); 280 281 /* there SHOULD be one only! */ 282 break; 283 } 284 } 285 286 /* tell the frontend not to add generated 287 * operational attributes */ 288 rs->sr_flags |= REP_NO_OPERATIONALS; 289 290 return SLAP_CB_CONTINUE; 291 292 } else if ( rs->sr_type == REP_SEARCHREF ) { 293 /* if we get it here, it means the library was unable 294 * to chase the referral... */ 295 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) { 296 rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth ); 297 } 298 299#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 300 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) { 301 switch ( get_continuationBehavior( op ) ) { 302 case SLAP_CH_RESOLVE_CHAINING_REQUIRED: 303 lb->lb_status = LDAP_CH_ERR; 304 return rs->sr_err = LDAP_X_CANNOT_CHAIN; 305 306 default: 307 break; 308 } 309 } 310#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 311 return SLAP_CB_CONTINUE; 312 313 } else if ( rs->sr_type == REP_RESULT ) { 314 if ( rs->sr_err == LDAP_REFERRAL 315 && lb->lb_depth < lb->lb_lc->lc_max_depth 316 && rs->sr_ref != NULL ) 317 { 318 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth ); 319 } 320 321 /* back-ldap tried to send result */ 322 lb->lb_status = LDAP_CH_RES; 323 } 324 325 return 0; 326} 327 328/* 329 * Dummy response that simply traces if back-ldap tried to send 330 * anything to the client 331 */ 332static int 333ldap_chain_cb_response( Operation *op, SlapReply *rs ) 334{ 335 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private; 336 337 /* if in error, don't proceed any further */ 338 if ( lb->lb_status == LDAP_CH_ERR ) { 339 return 0; 340 } 341 342 if ( rs->sr_type == REP_RESULT ) { 343retry:; 344 switch ( rs->sr_err ) { 345 case LDAP_COMPARE_TRUE: 346 case LDAP_COMPARE_FALSE: 347 if ( op->o_tag != LDAP_REQ_COMPARE ) { 348 return rs->sr_err; 349 } 350 /* fallthru */ 351 352 case LDAP_SUCCESS: 353 lb->lb_status = LDAP_CH_RES; 354 break; 355 356 case LDAP_REFERRAL: 357 if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) { 358 rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth ); 359 goto retry; 360 } 361 362#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 363 if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) { 364 switch ( get_continuationBehavior( op ) ) { 365 case SLAP_CH_RESOLVE_CHAINING_REQUIRED: 366 lb->lb_status = LDAP_CH_ERR; 367 return rs->sr_err = LDAP_X_CANNOT_CHAIN; 368 369 default: 370 break; 371 } 372 } 373#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 374 break; 375 376 default: 377 return rs->sr_err; 378 } 379 380 } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH ) 381 { 382 /* strip the entryDN attribute, but keep returning results */ 383 (void)ldap_chain_cb_search_response( op, rs ); 384 } 385 386 return SLAP_CB_CONTINUE; 387} 388 389static int 390ldap_chain_op( 391 Operation *op, 392 SlapReply *rs, 393 BI_op_func *op_f, 394 BerVarray ref, 395 int depth ) 396{ 397 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 398 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private; 399 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 400 struct berval odn = op->o_req_dn, 401 ondn = op->o_req_ndn; 402 ldapinfo_t li = { 0 }, *lip = NULL; 403 struct berval bvuri[ 2 ] = { { 0 } }; 404 405 /* NOTE: returned if ref is empty... */ 406 int rc = LDAP_OTHER, 407 first_rc; 408 409#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 410 LDAPControl **ctrls = NULL; 411 412 (void)chaining_control_add( lc, op, &ctrls ); 413#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 414 415 li.li_bvuri = bvuri; 416 first_rc = -1; 417 for ( ; !BER_BVISNULL( ref ); ref++ ) { 418 SlapReply rs2 = { 0 }; 419 LDAPURLDesc *srv = NULL; 420 req_search_s save_oq_search = op->oq_search, 421 tmp_oq_search = { 0 }; 422 struct berval dn = BER_BVNULL, 423 pdn = odn, 424 ndn = ondn; 425 char *filter = NULL; 426 int temporary = 0; 427 int free_dn = 0; 428 429 /* We're setting the URI of the first referral; 430 * what if there are more? 431 432Document: RFC 4511 433 4344.1.10. Referral 435 ... 436 If the client wishes to progress the operation, it MUST follow the 437 referral by contacting one of the supported services. If multiple 438 URIs are present, the client assumes that any supported URI may be 439 used to progress the operation. 440 441 * so we actually need to follow exactly one, 442 * and we can assume any is fine. 443 */ 444 445 /* parse reference and use 446 * proto://[host][:port]/ only */ 447 rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE ); 448 if ( rc != LDAP_URL_SUCCESS ) { 449 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n", 450 op->o_log_prefix, ref->bv_val, 0 ); 451 452 /* try next */ 453 rc = LDAP_OTHER; 454 continue; 455 } 456 457 if ( op->o_tag == LDAP_REQ_SEARCH ) { 458 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) { 459 /* RFC 4511: if scope is present, use it */ 460 tmp_oq_search.rs_scope = srv->lud_scope; 461 462 } else { 463 /* RFC 4511: if scope is absent, use original */ 464 tmp_oq_search.rs_scope = op->ors_scope; 465 } 466 } 467 468 rc = LDAP_SUCCESS; 469 srv->lud_scope = LDAP_SCOPE_DEFAULT; 470 dn.bv_val = srv->lud_dn; 471 filter = srv->lud_filter; 472 473 /* normalize DN */ 474 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) { 475 if ( srv->lud_dn == NULL ) { 476 srv->lud_dn = ""; 477 } 478 479 } else { 480 ber_str2bv( srv->lud_dn, 0, 0, &dn ); 481 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx ); 482 if ( rc == LDAP_SUCCESS ) { 483 /* remove DN essentially because later on 484 * ldap_initialize() will parse the URL 485 * as a comma-separated URL list */ 486 srv->lud_dn = ""; 487 free_dn = 1; 488 } 489 } 490 491 /* prepare filter */ 492 if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) { 493 /* filter */ 494 if ( srv->lud_filter != NULL 495 && srv->lud_filter[0] != '\0' 496 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 ) 497 { 498 /* RFC 4511: if filter is present, use it; 499 * otherwise, use original */ 500 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter ); 501 if ( tmp_oq_search.rs_filter != NULL ) { 502 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr ); 503 504 } else { 505 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n", 506 op->o_log_prefix, ref->bv_val, srv->lud_filter ); 507 rc = LDAP_OTHER; 508 } 509 } 510 } 511 srv->lud_filter = NULL; 512 513 if ( rc == LDAP_SUCCESS ) { 514 li.li_uri = ldap_url_desc2str( srv ); 515 } 516 517 srv->lud_dn = dn.bv_val; 518 srv->lud_filter = filter; 519 ldap_free_urldesc( srv ); 520 521 if ( rc != LDAP_SUCCESS ) { 522 /* try next */ 523 rc = LDAP_OTHER; 524 continue; 525 } 526 527 if ( li.li_uri == NULL ) { 528 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n", 529 op->o_log_prefix, ref->bv_val, 0 ); 530 531 /* try next */ 532 rc = LDAP_OTHER; 533 goto further_cleanup; 534 } 535 536 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n", 537 op->o_log_prefix, ref->bv_val, li.li_uri ); 538 539 op->o_req_dn = pdn; 540 op->o_req_ndn = ndn; 541 542 if ( op->o_tag == LDAP_REQ_SEARCH ) { 543 op->ors_scope = tmp_oq_search.rs_scope; 544 if ( tmp_oq_search.rs_filter != NULL ) { 545 op->ors_filter = tmp_oq_search.rs_filter; 546 op->ors_filterstr = tmp_oq_search.rs_filterstr; 547 } 548 } 549 550 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] ); 551 552 /* Searches for a ldapinfo in the avl tree */ 553 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); 554 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 555 (caddr_t)&li, ldap_chain_uri_cmp ); 556 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); 557 558 if ( lip != NULL ) { 559 op->o_bd->be_private = (void *)lip; 560 561 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n", 562 op->o_log_prefix, ref->bv_val, li.li_uri ); 563 564 } else { 565 rc = ldap_chain_db_init_one( op->o_bd ); 566 if ( rc != 0 ) { 567 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n", 568 op->o_log_prefix, ref->bv_val, li.li_uri ); 569 goto cleanup; 570 } 571 lip = (ldapinfo_t *)op->o_bd->be_private; 572 lip->li_uri = li.li_uri; 573 lip->li_bvuri = bvuri; 574 rc = ldap_chain_db_open_one( op->o_bd ); 575 if ( rc != 0 ) { 576 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n", 577 op->o_log_prefix, ref->bv_val, li.li_uri ); 578 lip->li_uri = NULL; 579 lip->li_bvuri = NULL; 580 (void)ldap_chain_db_destroy_one( op->o_bd, NULL); 581 goto cleanup; 582 } 583 584 if ( LDAP_CHAIN_CACHE_URI( lc ) ) { 585 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); 586 if ( avl_insert( &lc->lc_lai.lai_tree, 587 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) ) 588 { 589 /* someone just inserted another; 590 * don't bother, use this and then 591 * just free it */ 592 temporary = 1; 593 } 594 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); 595 596 } else { 597 temporary = 1; 598 } 599 600 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n", 601 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" ); 602 } 603 604 lb->lb_op_f = op_f; 605 lb->lb_depth = depth + 1; 606 607 rc = op_f( op, &rs2 ); 608 609 /* note the first error */ 610 if ( first_rc == -1 ) { 611 first_rc = rc; 612 } 613 614cleanup:; 615 ldap_memfree( li.li_uri ); 616 li.li_uri = NULL; 617 618 if ( temporary ) { 619 lip->li_uri = NULL; 620 lip->li_bvuri = NULL; 621 (void)ldap_chain_db_close_one( op->o_bd ); 622 (void)ldap_chain_db_destroy_one( op->o_bd, NULL ); 623 } 624 625further_cleanup:; 626 if ( free_dn ) { 627 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx ); 628 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); 629 } 630 631 if ( op->o_tag == LDAP_REQ_SEARCH ) { 632 if ( tmp_oq_search.rs_filter != NULL ) { 633 filter_free_x( op, tmp_oq_search.rs_filter, 1 ); 634 } 635 636 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) { 637 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx ); 638 } 639 640 op->oq_search = save_oq_search; 641 } 642 643 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) { 644 *rs = rs2; 645 break; 646 } 647 648 rc = rs2.sr_err; 649 } 650 651 op->o_req_dn = odn; 652 op->o_req_ndn = ondn; 653 654#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 655 (void)chaining_control_remove( op, &ctrls ); 656#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 657 658 if ( rc != LDAP_SUCCESS && first_rc > 0 ) { 659 rc = first_rc; 660 } 661 662 return rc; 663} 664 665static int 666ldap_chain_search( 667 Operation *op, 668 SlapReply *rs, 669 BerVarray ref, 670 int depth ) 671 672{ 673 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 674 ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private; 675 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 676 ldapinfo_t li = { 0 }, *lip = NULL; 677 struct berval bvuri[ 2 ] = { { 0 } }; 678 679 struct berval odn = op->o_req_dn, 680 ondn = op->o_req_ndn; 681 Entry *save_entry = rs->sr_entry; 682 slap_mask_t save_flags = rs->sr_flags; 683 684 int rc = LDAP_OTHER, 685 first_rc = -1; 686 687#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 688 LDAPControl **ctrls = NULL; 689 690 (void)chaining_control_add( lc, op, &ctrls ); 691#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 692 693 assert( rs->sr_type == REP_SEARCHREF ); 694 695 rs->sr_type = REP_SEARCH; 696 697 /* if we parse the URI then by no means 698 * we can cache stuff or reuse connections, 699 * because in back-ldap there's no caching 700 * based on the URI value, which is supposed 701 * to be set once for all (correct?) */ 702 li.li_bvuri = bvuri; 703 for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) { 704 SlapReply rs2 = { REP_RESULT }; 705 LDAPURLDesc *srv; 706 req_search_s save_oq_search = op->oq_search, 707 tmp_oq_search = { 0 }; 708 struct berval dn, 709 pdn = op->o_req_dn, 710 ndn = op->o_req_ndn; 711 char *filter = NULL; 712 int temporary = 0; 713 int free_dn = 0; 714 715 /* parse reference and use 716 * proto://[host][:port]/ only */ 717 rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE ); 718 if ( rc != LDAP_URL_SUCCESS ) { 719 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n", 720 op->o_log_prefix, ref->bv_val, 0 ); 721 722 /* try next */ 723 rs->sr_err = LDAP_OTHER; 724 continue; 725 } 726 727 if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) { 728 /* RFC 4511: if scope is present, use it */ 729 tmp_oq_search.rs_scope = srv->lud_scope; 730 731 } else { 732 /* RFC 4511: if scope is absent, use original */ 733 /* Section 4.5.3: if scope is onelevel, use base */ 734 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) 735 tmp_oq_search.rs_scope = LDAP_SCOPE_BASE; 736 else 737 tmp_oq_search.rs_scope = op->ors_scope; 738 } 739 740 rc = LDAP_SUCCESS; 741 srv->lud_scope = LDAP_SCOPE_DEFAULT; 742 dn.bv_val = srv->lud_dn; 743 filter = srv->lud_filter; 744 745 /* normalize DN */ 746 if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) { 747 if ( srv->lud_dn == NULL ) { 748 srv->lud_dn = ""; 749 } 750 751 if ( save_entry != NULL ) { 752 /* use the "right" DN, if available */ 753 pdn = save_entry->e_name; 754 ndn = save_entry->e_nname; 755 } /* else leave the original req DN in place, if any RFC 4511 */ 756 757 } else { 758 /* RFC 4511: if DN is present, use it */ 759 ber_str2bv( srv->lud_dn, 0, 0, &dn ); 760 rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx ); 761 if ( rc == LDAP_SUCCESS ) { 762 /* remove DN essentially because later on 763 * ldap_initialize() will parse the URL 764 * as a comma-separated URL list */ 765 srv->lud_dn = ""; 766 free_dn = 1; 767 } 768 } 769 770 /* prepare filter */ 771 if ( rc == LDAP_SUCCESS ) { 772 /* filter */ 773 if ( srv->lud_filter != NULL 774 && srv->lud_filter[0] != '\0' 775 && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 ) 776 { 777 /* RFC 4511: if filter is present, use it; 778 * otherwise, use original */ 779 tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter ); 780 if ( tmp_oq_search.rs_filter != NULL ) { 781 filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr ); 782 783 } else { 784 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n", 785 op->o_log_prefix, ref->bv_val, srv->lud_filter ); 786 rc = LDAP_OTHER; 787 } 788 } 789 } 790 srv->lud_filter = NULL; 791 792 if ( rc == LDAP_SUCCESS ) { 793 li.li_uri = ldap_url_desc2str( srv ); 794 } 795 796 srv->lud_dn = dn.bv_val; 797 srv->lud_filter = filter; 798 ldap_free_urldesc( srv ); 799 800 if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) { 801 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n", 802 op->o_log_prefix, ref->bv_val, 0 ); 803 804 /* try next */ 805 rc = LDAP_OTHER; 806 goto further_cleanup; 807 } 808 809 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n", 810 op->o_log_prefix, ref->bv_val, li.li_uri ); 811 812 op->o_req_dn = pdn; 813 op->o_req_ndn = ndn; 814 op->ors_scope = tmp_oq_search.rs_scope; 815 if ( tmp_oq_search.rs_filter != NULL ) { 816 op->ors_filter = tmp_oq_search.rs_filter; 817 op->ors_filterstr = tmp_oq_search.rs_filterstr; 818 } 819 820 ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] ); 821 822 /* Searches for a ldapinfo in the avl tree */ 823 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); 824 lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 825 (caddr_t)&li, ldap_chain_uri_cmp ); 826 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); 827 828 if ( lip != NULL ) { 829 op->o_bd->be_private = (void *)lip; 830 831 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n", 832 op->o_log_prefix, ref->bv_val, li.li_uri ); 833 834 } else { 835 /* if none is found, create a temporary... */ 836 rc = ldap_chain_db_init_one( op->o_bd ); 837 if ( rc != 0 ) { 838 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n", 839 op->o_log_prefix, ref->bv_val, li.li_uri ); 840 goto cleanup; 841 } 842 lip = (ldapinfo_t *)op->o_bd->be_private; 843 lip->li_uri = li.li_uri; 844 lip->li_bvuri = bvuri; 845 rc = ldap_chain_db_open_one( op->o_bd ); 846 if ( rc != 0 ) { 847 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n", 848 op->o_log_prefix, ref->bv_val, li.li_uri ); 849 lip->li_uri = NULL; 850 lip->li_bvuri = NULL; 851 (void)ldap_chain_db_destroy_one( op->o_bd, NULL ); 852 goto cleanup; 853 } 854 855 if ( LDAP_CHAIN_CACHE_URI( lc ) ) { 856 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); 857 if ( avl_insert( &lc->lc_lai.lai_tree, 858 (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) ) 859 { 860 /* someone just inserted another; 861 * don't bother, use this and then 862 * just free it */ 863 temporary = 1; 864 } 865 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); 866 867 } else { 868 temporary = 1; 869 } 870 871 Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n", 872 op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" ); 873 } 874 875 lb->lb_op_f = lback->bi_op_search; 876 lb->lb_depth = depth + 1; 877 878 /* FIXME: should we also copy filter and scope? 879 * according to RFC3296, no */ 880 rc = lback->bi_op_search( op, &rs2 ); 881 if ( first_rc == -1 ) { 882 first_rc = rc; 883 } 884 885cleanup:; 886 ldap_memfree( li.li_uri ); 887 li.li_uri = NULL; 888 889 if ( temporary ) { 890 lip->li_uri = NULL; 891 lip->li_bvuri = NULL; 892 (void)ldap_chain_db_close_one( op->o_bd ); 893 (void)ldap_chain_db_destroy_one( op->o_bd, NULL ); 894 } 895 896further_cleanup:; 897 if ( free_dn ) { 898 op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx ); 899 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); 900 } 901 902 op->o_req_dn = odn; 903 op->o_req_ndn = ondn; 904 905 if ( tmp_oq_search.rs_filter != NULL ) { 906 filter_free_x( op, tmp_oq_search.rs_filter, 1 ); 907 } 908 909 if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) { 910 slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx ); 911 } 912 913 op->oq_search = save_oq_search; 914 915 if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) { 916 *rs = rs2; 917 break; 918 } 919 920 rc = rs2.sr_err; 921 } 922 923#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 924 (void)chaining_control_remove( op, &ctrls ); 925#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 926 927 op->o_req_dn = odn; 928 op->o_req_ndn = ondn; 929 rs->sr_type = REP_SEARCHREF; 930 rs->sr_entry = save_entry; 931 rs->sr_flags = save_flags; 932 933 if ( rc != LDAP_SUCCESS ) { 934 /* couldn't chase any of the referrals */ 935 if ( first_rc != -1 ) { 936 rc = first_rc; 937 938 } else { 939 rc = SLAP_CB_CONTINUE; 940 } 941 } 942 943 return rc; 944} 945 946static int 947ldap_chain_response( Operation *op, SlapReply *rs ) 948{ 949 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 950 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 951 BackendDB db, *bd = op->o_bd; 952 ldap_chain_cb_t lb = { 0 }; 953 slap_callback *sc = op->o_callback, 954 sc2 = { 0 }; 955 int rc = 0; 956 const char *text = NULL; 957 const char *matched; 958 BerVarray ref; 959 struct berval ndn = op->o_ndn; 960 961 int sr_err = rs->sr_err; 962 slap_reply_t sr_type = rs->sr_type; 963#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 964 slap_mask_t chain_mask = 0; 965 ber_len_t chain_shift = 0; 966#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 967 968 if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) { 969 return SLAP_CB_CONTINUE; 970 } 971 972#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 973 if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) { 974 switch ( get_resolveBehavior( op ) ) { 975 case SLAP_CH_RESOLVE_REFERRALS_PREFERRED: 976 case SLAP_CH_RESOLVE_REFERRALS_REQUIRED: 977 return SLAP_CB_CONTINUE; 978 979 default: 980 chain_mask = SLAP_CH_RESOLVE_MASK; 981 chain_shift = SLAP_CH_RESOLVE_SHIFT; 982 break; 983 } 984 985 } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) { 986 switch ( get_continuationBehavior( op ) ) { 987 case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED: 988 case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED: 989 return SLAP_CB_CONTINUE; 990 991 default: 992 chain_mask = SLAP_CH_CONTINUATION_MASK; 993 chain_shift = SLAP_CH_CONTINUATION_SHIFT; 994 break; 995 } 996 } 997#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 998 999 /* 1000 * TODO: add checks on who/when chain operations; e.g.: 1001 * a) what identities are authorized 1002 * b) what request DN (e.g. only chain requests rooted at <DN>) 1003 * c) what referral URIs 1004 * d) what protocol scheme (e.g. only ldaps://) 1005 * e) what ssf 1006 */ 1007 1008 db = *op->o_bd; 1009 SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING; 1010 op->o_bd = &db; 1011 1012 text = rs->sr_text; 1013 rs->sr_text = NULL; 1014 matched = rs->sr_matched; 1015 rs->sr_matched = NULL; 1016 ref = rs->sr_ref; 1017 rs->sr_ref = NULL; 1018 1019 /* we need this to know if back-ldap returned any result */ 1020 lb.lb_lc = lc; 1021 sc2.sc_next = sc->sc_next; 1022 sc2.sc_private = &lb; 1023 sc2.sc_response = ldap_chain_cb_response; 1024 op->o_callback = &sc2; 1025 1026 /* Chaining can be performed by a privileged user on behalf 1027 * of normal users, using the ProxyAuthz control, by exploiting 1028 * the identity assertion feature of back-ldap; see idassert-* 1029 * directives in slapd-ldap(5). 1030 * 1031 * FIXME: the idassert-authcDN is one, will it be fine regardless 1032 * of the URI we obtain from the referral? 1033 */ 1034 1035 switch ( op->o_tag ) { 1036 case LDAP_REQ_BIND: { 1037 struct berval rndn = op->o_req_ndn; 1038 Connection *conn = op->o_conn; 1039 1040 /* FIXME: can we really get a referral for binds? */ 1041 op->o_req_ndn = slap_empty_bv; 1042 op->o_conn = NULL; 1043 rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 ); 1044 op->o_req_ndn = rndn; 1045 op->o_conn = conn; 1046 } 1047 break; 1048 1049 case LDAP_REQ_ADD: 1050 rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 ); 1051 break; 1052 1053 case LDAP_REQ_DELETE: 1054 rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 ); 1055 break; 1056 1057 case LDAP_REQ_MODRDN: 1058 rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 ); 1059 break; 1060 1061 case LDAP_REQ_MODIFY: 1062 rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 ); 1063 break; 1064 1065 case LDAP_REQ_COMPARE: 1066 rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 ); 1067 if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) { 1068 rc = LDAP_SUCCESS; 1069 } 1070 break; 1071 1072 case LDAP_REQ_SEARCH: 1073 if ( rs->sr_type == REP_SEARCHREF ) { 1074 sc2.sc_response = ldap_chain_cb_search_response; 1075 rc = ldap_chain_search( op, rs, ref, 0 ); 1076 1077 } else { 1078 /* we might get here before any database actually 1079 * performed a search; in those cases, we need 1080 * to check limits, to make sure safe defaults 1081 * are in place */ 1082 if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) { 1083 rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 ); 1084 1085 } else { 1086 rc = SLAP_CB_CONTINUE; 1087 } 1088 } 1089 break; 1090 1091 case LDAP_REQ_EXTENDED: 1092 rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 ); 1093 /* FIXME: ldap_back_extended() by design 1094 * doesn't send result; frontend is expected 1095 * to send it... */ 1096 /* FIXME: what about chaining? */ 1097 if ( rc != SLAPD_ABANDON ) { 1098 rs->sr_err = rc; 1099 send_ldap_extended( op, rs ); 1100 rc = LDAP_SUCCESS; 1101 } 1102 lb.lb_status = LDAP_CH_RES; 1103 break; 1104 1105 default: 1106 rc = SLAP_CB_CONTINUE; 1107 break; 1108 } 1109 1110 switch ( rc ) { 1111 case SLAPD_ABANDON: 1112 goto dont_chain; 1113 1114 case LDAP_SUCCESS: 1115 case LDAP_REFERRAL: 1116 sr_err = rs->sr_err; 1117 /* slapd-ldap sent response */ 1118 if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) { 1119 /* FIXME: should we send response? */ 1120 Debug( LDAP_DEBUG_ANY, 1121 "%s: ldap_chain_response: " 1122 "overlay should have sent result.\n", 1123 op->o_log_prefix, 0, 0 ); 1124 } 1125 break; 1126 1127 default: 1128#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 1129 if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) { 1130 goto cannot_chain; 1131 } 1132 1133 switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) { 1134 case LDAP_CHAINING_REQUIRED: 1135cannot_chain:; 1136 op->o_callback = NULL; 1137 send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN, 1138 "operation cannot be completed without chaining" ); 1139 goto dont_chain; 1140 1141 default: 1142#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 1143 if ( LDAP_CHAIN_RETURN_ERR( lc ) ) { 1144 sr_err = rs->sr_err = rc; 1145 rs->sr_type = sr_type; 1146 1147 } else { 1148 rc = SLAP_CB_CONTINUE; 1149 rs->sr_err = sr_err; 1150 rs->sr_type = sr_type; 1151 rs->sr_text = text; 1152 rs->sr_matched = matched; 1153 rs->sr_ref = ref; 1154 } 1155#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 1156 break; 1157 } 1158#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 1159 } 1160 1161 if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) { 1162 /* give the remaining callbacks a chance */ 1163 op->o_callback = sc->sc_next; 1164 rc = rs->sr_err = slap_map_api2result( rs ); 1165 send_ldap_result( op, rs ); 1166 } 1167 1168dont_chain:; 1169 rs->sr_err = sr_err; 1170 rs->sr_type = sr_type; 1171 rs->sr_text = text; 1172 rs->sr_matched = matched; 1173 rs->sr_ref = ref; 1174 op->o_bd = bd; 1175 op->o_callback = sc; 1176 op->o_ndn = ndn; 1177 1178 return rc; 1179} 1180 1181#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 1182static int 1183ldap_chain_parse_ctrl( 1184 Operation *op, 1185 SlapReply *rs, 1186 LDAPControl *ctrl ); 1187 1188static int 1189str2chain( const char *s ) 1190{ 1191 if ( strcasecmp( s, "chainingPreferred" ) == 0 ) { 1192 return LDAP_CHAINING_PREFERRED; 1193 1194 } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) { 1195 return LDAP_CHAINING_REQUIRED; 1196 1197 } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) { 1198 return LDAP_REFERRALS_PREFERRED; 1199 1200 } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) { 1201 return LDAP_REFERRALS_REQUIRED; 1202 } 1203 1204 return -1; 1205} 1206#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 1207 1208/* 1209 * configuration... 1210 */ 1211 1212enum { 1213 CH_CHAINING = 1, 1214 CH_CACHE_URI, 1215 CH_MAX_DEPTH, 1216 CH_RETURN_ERR, 1217 1218 CH_LAST 1219}; 1220 1221static ConfigDriver chain_cf_gen; 1222static ConfigCfAdd chain_cfadd; 1223static ConfigLDAPadd chain_ldadd; 1224#ifdef SLAP_CONFIG_DELETE 1225static ConfigLDAPdel chain_lddel; 1226#endif 1227 1228static ConfigTable chaincfg[] = { 1229#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 1230 { "chain-chaining", "args", 1231 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen, 1232 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' " 1233 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' " 1234 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL }, 1235#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 1236 { "chain-cache-uri", "TRUE/FALSE", 1237 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen, 1238 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' " 1239 "DESC 'Enables caching of URIs not present in configuration' " 1240 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, 1241 { "chain-max-depth", "args", 1242 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen, 1243 "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' " 1244 "DESC 'max referral depth' " 1245 "SYNTAX OMsInteger " 1246 "EQUALITY integerMatch " 1247 "SINGLE-VALUE )", NULL, NULL }, 1248 { "chain-return-error", "TRUE/FALSE", 1249 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen, 1250 "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' " 1251 "DESC 'Errors are returned instead of the original referral' " 1252 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, 1253 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 1254}; 1255 1256static ConfigOCs chainocs[] = { 1257 { "( OLcfgOvOc:3.1 " 1258 "NAME 'olcChainConfig' " 1259 "DESC 'Chain configuration' " 1260 "SUP olcOverlayConfig " 1261 "MAY ( " 1262#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 1263 "olcChainingBehavior $ " 1264#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 1265 "olcChainCacheURI $ " 1266 "olcChainMaxReferralDepth $ " 1267 "olcChainReturnError " 1268 ") )", 1269 Cft_Overlay, chaincfg, NULL, chain_cfadd }, 1270 { "( OLcfgOvOc:3.2 " 1271 "NAME 'olcChainDatabase' " 1272 "DESC 'Chain remote server configuration' " 1273 "AUXILIARY )", 1274 Cft_Misc, olcDatabaseDummy, chain_ldadd 1275#ifdef SLAP_CONFIG_DELETE 1276 , NULL, chain_lddel 1277#endif 1278 }, 1279 { NULL, 0, NULL } 1280}; 1281 1282static int 1283chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca ) 1284{ 1285 slap_overinst *on; 1286 ldap_chain_t *lc; 1287 1288 ldapinfo_t *li; 1289 1290 AttributeDescription *ad = NULL; 1291 Attribute *at; 1292 const char *text; 1293 1294 int rc; 1295 1296 if ( p->ce_type != Cft_Overlay 1297 || !p->ce_bi 1298 || p->ce_bi->bi_cf_ocs != chainocs ) 1299 { 1300 return LDAP_CONSTRAINT_VIOLATION; 1301 } 1302 1303 on = (slap_overinst *)p->ce_bi; 1304 lc = (ldap_chain_t *)on->on_bi.bi_private; 1305 1306 assert( ca->be == NULL ); 1307 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) ); 1308 1309 ca->be->bd_info = (BackendInfo *)on; 1310 1311 rc = slap_str2ad( "olcDbURI", &ad, &text ); 1312 assert( rc == LDAP_SUCCESS ); 1313 1314 at = attr_find( e->e_attrs, ad ); 1315#if 0 1316 if ( lc->lc_common_li == NULL && at != NULL ) { 1317 /* FIXME: we should generate an empty default entry 1318 * if none is supplied */ 1319 Debug( LDAP_DEBUG_ANY, "slapd-chain: " 1320 "first underlying database \"%s\" " 1321 "cannot contain attribute \"%s\".\n", 1322 e->e_name.bv_val, ad->ad_cname.bv_val, 0 ); 1323 rc = LDAP_CONSTRAINT_VIOLATION; 1324 goto done; 1325 1326 } else 1327#endif 1328 if ( lc->lc_common_li != NULL && at == NULL ) { 1329 /* FIXME: we should generate an empty default entry 1330 * if none is supplied */ 1331 Debug( LDAP_DEBUG_ANY, "slapd-chain: " 1332 "subsequent underlying database \"%s\" " 1333 "must contain attribute \"%s\".\n", 1334 e->e_name.bv_val, ad->ad_cname.bv_val, 0 ); 1335 rc = LDAP_CONSTRAINT_VIOLATION; 1336 goto done; 1337 } 1338 1339 if ( lc->lc_common_li == NULL ) { 1340 rc = ldap_chain_db_init_common( ca->be ); 1341 1342 } else { 1343 rc = ldap_chain_db_init_one( ca->be ); 1344 } 1345 1346 if ( rc != 0 ) { 1347 Debug( LDAP_DEBUG_ANY, "slapd-chain: " 1348 "unable to init %sunderlying database \"%s\".\n", 1349 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 ); 1350 return LDAP_CONSTRAINT_VIOLATION; 1351 } 1352 1353 li = ca->be->be_private; 1354 1355 if ( lc->lc_common_li == NULL ) { 1356 lc->lc_common_li = li; 1357 1358 } else { 1359 li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val ); 1360 value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] ); 1361 if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li, 1362 ldap_chain_uri_cmp, ldap_chain_uri_dup ) ) 1363 { 1364 Debug( LDAP_DEBUG_ANY, "slapd-chain: " 1365 "database \"%s\" insert failed.\n", 1366 e->e_name.bv_val, 0, 0 ); 1367 rc = LDAP_CONSTRAINT_VIOLATION; 1368 goto done; 1369 } 1370 } 1371 1372 ca->ca_private = on; 1373 1374done:; 1375 if ( rc != LDAP_SUCCESS ) { 1376 (void)ldap_chain_db_destroy_one( ca->be, NULL ); 1377 ch_free( ca->be ); 1378 ca->be = NULL; 1379 } 1380 1381 return rc; 1382} 1383 1384typedef struct ldap_chain_cfadd_apply_t { 1385 Operation *op; 1386 SlapReply *rs; 1387 Entry *p; 1388 ConfigArgs *ca; 1389 int count; 1390} ldap_chain_cfadd_apply_t; 1391 1392static int 1393ldap_chain_cfadd_apply( void *datum, void *arg ) 1394{ 1395 ldapinfo_t *li = (ldapinfo_t *)datum; 1396 ldap_chain_cfadd_apply_t *lca = (ldap_chain_cfadd_apply_t *)arg; 1397 1398 struct berval bv; 1399 1400 /* FIXME: should not hardcode "olcDatabase" here */ 1401 bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ), 1402 "olcDatabase={%d}%s", lca->count, lback->bi_type ); 1403 bv.bv_val = lca->ca->cr_msg; 1404 1405 lca->ca->be->be_private = (void *)li; 1406 config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca, 1407 &bv, lback->bi_cf_ocs, &chainocs[1] ); 1408 1409 lca->count++; 1410 1411 return 0; 1412} 1413 1414static int 1415chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca ) 1416{ 1417 CfEntryInfo *pe = p->e_private; 1418 slap_overinst *on = (slap_overinst *)pe->ce_bi; 1419 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 1420 void *priv = (void *)ca->be->be_private; 1421 1422 if ( lback->bi_cf_ocs ) { 1423 ldap_chain_cfadd_apply_t lca = { 0 }; 1424 1425 lca.op = op; 1426 lca.rs = rs; 1427 lca.p = p; 1428 lca.ca = ca; 1429 lca.count = 0; 1430 1431 (void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca ); 1432 1433 (void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply, 1434 &lca, 1, AVL_INORDER ); 1435 1436 ca->be->be_private = priv; 1437 } 1438 1439 return 0; 1440} 1441 1442#ifdef SLAP_CONFIG_DELETE 1443static int 1444chain_lddel( CfEntryInfo *ce, Operation *op ) 1445{ 1446 CfEntryInfo *pe = ce->ce_parent; 1447 slap_overinst *on = (slap_overinst *)pe->ce_bi; 1448 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 1449 ldapinfo_t *li = (ldapinfo_t *) ce->ce_be->be_private; 1450 1451 if ( li != lc->lc_common_li ) { 1452 if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) { 1453 Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. " 1454 "\"%s\" not found.\n", li->li_uri, 0, 0 ); 1455 return -1; 1456 } 1457 } else if ( lc->lc_lai.lai_tree ) { 1458 Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying " 1459 "LDAP database when other databases are still present.\n", 0, 0, 0 ); 1460 return -1; 1461 } else { 1462 lc->lc_common_li = NULL; 1463 } 1464 1465 ce->ce_be->bd_info = lback; 1466 1467 if ( ce->ce_be->bd_info->bi_db_close ) { 1468 ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL ); 1469 } 1470 if ( ce->ce_be->bd_info->bi_db_destroy ) { 1471 ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL ); 1472 } 1473 1474 ch_free(ce->ce_be); 1475 ce->ce_be = NULL; 1476 1477 return LDAP_SUCCESS; 1478} 1479#endif /* SLAP_CONFIG_DELETE */ 1480 1481#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 1482static slap_verbmasks chaining_mode[] = { 1483 { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED }, 1484 { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED }, 1485 { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED }, 1486 { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED }, 1487 { BER_BVNULL, 0 } 1488}; 1489#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 1490 1491static int 1492chain_cf_gen( ConfigArgs *c ) 1493{ 1494 slap_overinst *on = (slap_overinst *)c->bi; 1495 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 1496 1497 int rc = 0; 1498 1499 if ( c->op == SLAP_CONFIG_EMIT ) { 1500 switch( c->type ) { 1501#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 1502 case CH_CHAINING: { 1503 struct berval resolve = BER_BVNULL, 1504 continuation = BER_BVNULL; 1505 1506 if ( !LDAP_CHAIN_CHAINING( lc ) ) { 1507 return 1; 1508 } 1509 1510 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve ); 1511 enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation ); 1512 1513 c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len 1514 + STRLENOF( " " ) 1515 + STRLENOF( "continuation=" ) + continuation.bv_len; 1516 c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 ); 1517 snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1, 1518 "resolve=%s continuation=%s", 1519 resolve.bv_val, continuation.bv_val ); 1520 1521 if ( lc->lc_chaining_ctrl.ldctl_iscritical ) { 1522 c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val, 1523 c->value_bv.bv_len + STRLENOF( " critical" ) + 1 ); 1524 AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ], 1525 " critical", STRLENOF( " critical" ) + 1 ); 1526 c->value_bv.bv_len += STRLENOF( " critical" ); 1527 } 1528 1529 break; 1530 } 1531#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 1532 1533 case CH_CACHE_URI: 1534 c->value_int = LDAP_CHAIN_CACHE_URI( lc ); 1535 break; 1536 1537 case CH_MAX_DEPTH: 1538 c->value_int = lc->lc_max_depth; 1539 break; 1540 1541 case CH_RETURN_ERR: 1542 c->value_int = LDAP_CHAIN_RETURN_ERR( lc ); 1543 break; 1544 1545 default: 1546 assert( 0 ); 1547 rc = 1; 1548 } 1549 return rc; 1550 1551 } else if ( c->op == LDAP_MOD_DELETE ) { 1552 switch( c->type ) { 1553 case CH_CHAINING: 1554 return 1; 1555 1556 case CH_CACHE_URI: 1557 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI; 1558 break; 1559 1560 case CH_MAX_DEPTH: 1561 c->value_int = 0; 1562 break; 1563 1564 case CH_RETURN_ERR: 1565 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR; 1566 break; 1567 1568 default: 1569 return 1; 1570 } 1571 return rc; 1572 } 1573 1574 switch( c->type ) { 1575 case CH_CHAINING: { 1576#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 1577 char **argv = c->argv; 1578 int argc = c->argc; 1579 BerElementBuffer berbuf; 1580 BerElement *ber = (BerElement *)&berbuf; 1581 int resolve = -1, 1582 continuation = -1, 1583 iscritical = 0; 1584 Operation op = { 0 }; 1585 SlapReply rs = { 0 }; 1586 1587 lc->lc_chaining_ctrlflag = 0; 1588 1589 for ( argc--, argv++; argc > 0; argc--, argv++ ) { 1590 if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) { 1591 resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) ); 1592 if ( resolve == -1 ) { 1593 Debug( LDAP_DEBUG_ANY, "%s: " 1594 "illegal <resolve> value %s " 1595 "in \"chain-chaining>\".\n", 1596 c->log, argv[ 0 ], 0 ); 1597 return 1; 1598 } 1599 1600 } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) { 1601 continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) ); 1602 if ( continuation == -1 ) { 1603 Debug( LDAP_DEBUG_ANY, "%s: " 1604 "illegal <continuation> value %s " 1605 "in \"chain-chaining\".\n", 1606 c->log, argv[ 0 ], 0 ); 1607 return 1; 1608 } 1609 1610 } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) { 1611 iscritical = 1; 1612 1613 } else { 1614 Debug( LDAP_DEBUG_ANY, "%s: " 1615 "unknown option in \"chain-chaining\".\n", 1616 c->log, 0, 0 ); 1617 return 1; 1618 } 1619 } 1620 1621 if ( resolve != -1 || continuation != -1 ) { 1622 int err; 1623 1624 if ( resolve == -1 ) { 1625 /* default */ 1626 resolve = SLAP_CHAINING_DEFAULT; 1627 } 1628 1629 ber_init2( ber, NULL, LBER_USE_DER ); 1630 1631 err = ber_printf( ber, "{e" /* } */, resolve ); 1632 if ( err == -1 ) { 1633 ber_free( ber, 1 ); 1634 Debug( LDAP_DEBUG_ANY, "%s: " 1635 "chaining behavior control encoding error!\n", 1636 c->log, 0, 0 ); 1637 return 1; 1638 } 1639 1640 if ( continuation > -1 ) { 1641 err = ber_printf( ber, "e", continuation ); 1642 if ( err == -1 ) { 1643 ber_free( ber, 1 ); 1644 Debug( LDAP_DEBUG_ANY, "%s: " 1645 "chaining behavior control encoding error!\n", 1646 c->log, 0, 0 ); 1647 return 1; 1648 } 1649 } 1650 1651 err = ber_printf( ber, /* { */ "N}" ); 1652 if ( err == -1 ) { 1653 ber_free( ber, 1 ); 1654 Debug( LDAP_DEBUG_ANY, "%s: " 1655 "chaining behavior control encoding error!\n", 1656 c->log, 0, 0 ); 1657 return 1; 1658 } 1659 1660 if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) { 1661 exit( EXIT_FAILURE ); 1662 } 1663 1664 } else { 1665 BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value ); 1666 } 1667 1668 lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR; 1669 lc->lc_chaining_ctrl.ldctl_iscritical = iscritical; 1670 1671 if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS ) 1672 { 1673 Debug( LDAP_DEBUG_ANY, "%s: " 1674 "unable to parse chaining control%s%s.\n", 1675 c->log, rs.sr_text ? ": " : "", 1676 rs.sr_text ? rs.sr_text : "" ); 1677 return 1; 1678 } 1679 1680 lc->lc_chaining_ctrlflag = op.o_chaining; 1681 1682 lc->lc_flags |= LDAP_CHAIN_F_CHAINING; 1683 1684 rc = 0; 1685#else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 1686 Debug( LDAP_DEBUG_ANY, "%s: " 1687 "\"chaining\" control unsupported (ignored).\n", 1688 c->log, 0, 0 ); 1689#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 1690 } break; 1691 1692 case CH_CACHE_URI: 1693 if ( c->value_int ) { 1694 lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI; 1695 } else { 1696 lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI; 1697 } 1698 break; 1699 1700 case CH_MAX_DEPTH: 1701 if ( c->value_int < 0 ) { 1702 snprintf( c->cr_msg, sizeof( c->cr_msg ), 1703 "<%s> invalid max referral depth %d", 1704 c->argv[0], c->value_int ); 1705 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1706 c->log, c->cr_msg, 0 ); 1707 rc = 1; 1708 break; 1709 } 1710 lc->lc_max_depth = c->value_int; 1711 1712 case CH_RETURN_ERR: 1713 if ( c->value_int ) { 1714 lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR; 1715 } else { 1716 lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR; 1717 } 1718 break; 1719 1720 default: 1721 assert( 0 ); 1722 return 1; 1723 } 1724 return rc; 1725} 1726 1727static int 1728ldap_chain_db_init( 1729 BackendDB *be, 1730 ConfigReply *cr ) 1731{ 1732 slap_overinst *on = (slap_overinst *)be->bd_info; 1733 ldap_chain_t *lc = NULL; 1734 1735 if ( lback == NULL ) { 1736 lback = backend_info( "ldap" ); 1737 1738 if ( lback == NULL ) { 1739 return 1; 1740 } 1741 } 1742 1743 lc = ch_malloc( sizeof( ldap_chain_t ) ); 1744 if ( lc == NULL ) { 1745 return 1; 1746 } 1747 memset( lc, 0, sizeof( ldap_chain_t ) ); 1748 lc->lc_max_depth = 1; 1749 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex ); 1750 1751 on->on_bi.bi_private = (void *)lc; 1752 1753 return 0; 1754} 1755 1756static int 1757ldap_chain_db_config( 1758 BackendDB *be, 1759 const char *fname, 1760 int lineno, 1761 int argc, 1762 char **argv ) 1763{ 1764 slap_overinst *on = (slap_overinst *)be->bd_info; 1765 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 1766 1767 int rc = SLAP_CONF_UNKNOWN; 1768 1769 if ( lc->lc_common_li == NULL ) { 1770 BackendDB db = *be; 1771 ldap_chain_db_init_common( &db ); 1772 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private; 1773 } 1774 1775 /* Something for the chain database? */ 1776 if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) { 1777 char *save_argv0 = argv[ 0 ]; 1778 BackendDB db = *be; 1779 static char *allowed_argv[] = { 1780 /* special: put URI here, so in the meanwhile 1781 * it detects whether a new URI is being provided */ 1782 "uri", 1783 "nretries", 1784 "timeout", 1785 /* flags */ 1786 "tls", 1787 /* FIXME: maybe rebind-as-user should be allowed 1788 * only within known URIs... */ 1789 "rebind-as-user", 1790 "chase-referrals", 1791 "t-f-support", 1792 "proxy-whoami", 1793 NULL 1794 }; 1795 int which_argv = -1; 1796 1797 argv[ 0 ] += STRLENOF( "chain-" ); 1798 1799 for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) { 1800 if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) { 1801 break; 1802 } 1803 } 1804 1805 if ( allowed_argv[ which_argv ] == NULL ) { 1806 which_argv = -1; 1807 1808 if ( lc->lc_cfg_li == lc->lc_common_li ) { 1809 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1810 "\"%s\" only allowed within a URI directive.\n.", 1811 fname, lineno, argv[ 0 ] ); 1812 return 1; 1813 } 1814 } 1815 1816 if ( which_argv == 0 ) { 1817 rc = ldap_chain_db_init_one( &db ); 1818 if ( rc != 0 ) { 1819 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1820 "underlying slapd-ldap initialization failed.\n.", 1821 fname, lineno, 0 ); 1822 return 1; 1823 } 1824 lc->lc_cfg_li = db.be_private; 1825 } 1826 1827 /* TODO: add checks on what other slapd-ldap(5) args 1828 * should be put in the template; this is not quite 1829 * harmful, because attributes that shouldn't don't 1830 * get actually used, but the user should at least 1831 * be warned. 1832 */ 1833 1834 db.bd_info = lback; 1835 db.be_private = (void *)lc->lc_cfg_li; 1836 db.be_cf_ocs = lback->bi_cf_ocs; 1837 1838 rc = config_generic_wrapper( &db, fname, lineno, argc, argv ); 1839 1840 argv[ 0 ] = save_argv0; 1841 1842 if ( which_argv == 0 ) { 1843private_destroy:; 1844 if ( rc != 0 ) { 1845 db.bd_info = lback; 1846 db.be_private = (void *)lc->lc_cfg_li; 1847 ldap_chain_db_destroy_one( &db, NULL ); 1848 lc->lc_cfg_li = NULL; 1849 } else { 1850 if ( lc->lc_cfg_li->li_bvuri == NULL 1851 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] ) 1852 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) ) 1853 { 1854 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1855 "no URI list allowed in slapo-chain.\n", 1856 fname, lineno, 0 ); 1857 rc = 1; 1858 goto private_destroy; 1859 } 1860 1861 if ( avl_insert( &lc->lc_lai.lai_tree, 1862 (caddr_t)lc->lc_cfg_li, 1863 ldap_chain_uri_cmp, ldap_chain_uri_dup ) ) 1864 { 1865 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 1866 "duplicate URI in slapo-chain.\n", 1867 fname, lineno, 0 ); 1868 rc = 1; 1869 goto private_destroy; 1870 } 1871 } 1872 } 1873 } 1874 1875 return rc; 1876} 1877 1878enum db_which { 1879 db_open = 0, 1880 db_close, 1881 db_destroy, 1882 1883 db_last 1884}; 1885 1886typedef struct ldap_chain_db_apply_t { 1887 BackendDB *be; 1888 BI_db_func *func; 1889} ldap_chain_db_apply_t; 1890 1891static int 1892ldap_chain_db_apply( void *datum, void *arg ) 1893{ 1894 ldapinfo_t *li = (ldapinfo_t *)datum; 1895 ldap_chain_db_apply_t *lca = (ldap_chain_db_apply_t *)arg; 1896 1897 lca->be->be_private = (void *)li; 1898 1899 return lca->func( lca->be, NULL ); 1900} 1901 1902static int 1903ldap_chain_db_func( 1904 BackendDB *be, 1905 enum db_which which 1906) 1907{ 1908 slap_overinst *on = (slap_overinst *)be->bd_info; 1909 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 1910 1911 int rc = 0; 1912 1913 if ( lc ) { 1914 BI_db_func *func = (&lback->bi_db_open)[ which ]; 1915 1916 if ( func != NULL && lc->lc_common_li != NULL ) { 1917 BackendDB db = *be; 1918 1919 db.bd_info = lback; 1920 db.be_private = lc->lc_common_li; 1921 1922 rc = func( &db, NULL ); 1923 1924 if ( rc != 0 ) { 1925 return rc; 1926 } 1927 1928 if ( lc->lc_lai.lai_tree != NULL ) { 1929 ldap_chain_db_apply_t lca; 1930 1931 lca.be = &db; 1932 lca.func = func; 1933 1934 rc = avl_apply( lc->lc_lai.lai_tree, 1935 ldap_chain_db_apply, (void *)&lca, 1936 1, AVL_INORDER ) != AVL_NOMORE; 1937 } 1938 } 1939 } 1940 1941 return rc; 1942} 1943 1944static int 1945ldap_chain_db_open( 1946 BackendDB *be, 1947 ConfigReply *cr ) 1948{ 1949 slap_overinst *on = (slap_overinst *) be->bd_info; 1950 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 1951 slap_mask_t monitoring; 1952 int rc = 0; 1953 1954#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 1955 rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR ); 1956 if ( rc != 0 ) { 1957 return rc; 1958 } 1959#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 1960 1961 if ( lc->lc_common_li == NULL ) { 1962 void *be_private = be->be_private; 1963 ldap_chain_db_init_common( be ); 1964 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private; 1965 be->be_private = be_private; 1966 } 1967 1968 /* filter out and restore monitoring */ 1969 monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING ); 1970 SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING; 1971 rc = ldap_chain_db_func( be, db_open ); 1972 SLAP_DBFLAGS( be ) |= monitoring; 1973 1974 return rc; 1975} 1976 1977static int 1978ldap_chain_db_close( 1979 BackendDB *be, 1980 ConfigReply *cr ) 1981{ 1982#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 1983#ifdef SLAP_CONFIG_DELETE 1984 overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR ); 1985#endif /* SLAP_CONFIG_DELETE */ 1986#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 1987 return ldap_chain_db_func( be, db_close ); 1988} 1989 1990static int 1991ldap_chain_db_destroy( 1992 BackendDB *be, 1993 ConfigReply *cr ) 1994{ 1995 slap_overinst *on = (slap_overinst *) be->bd_info; 1996 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 1997 1998 int rc; 1999 2000 rc = ldap_chain_db_func( be, db_destroy ); 2001 2002 if ( lc ) { 2003 avl_free( lc->lc_lai.lai_tree, NULL ); 2004 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex ); 2005 ch_free( lc ); 2006 } 2007 2008 return rc; 2009} 2010 2011/* 2012 * inits one instance of the slapd-ldap backend, and stores 2013 * the private info in be_private of the arg 2014 */ 2015static int 2016ldap_chain_db_init_common( 2017 BackendDB *be ) 2018{ 2019 BackendInfo *bi = be->bd_info; 2020 ldapinfo_t *li; 2021 int rc; 2022 2023 be->bd_info = lback; 2024 be->be_private = NULL; 2025 rc = lback->bi_db_init( be, NULL ); 2026 if ( rc != 0 ) { 2027 return rc; 2028 } 2029 li = (ldapinfo_t *)be->be_private; 2030 li->li_urllist_f = NULL; 2031 li->li_urllist_p = NULL; 2032 2033 be->bd_info = bi; 2034 2035 return 0; 2036} 2037 2038/* 2039 * inits one instance of the slapd-ldap backend, stores 2040 * the private info in be_private of the arg and fills 2041 * selected fields with data from the template. 2042 * 2043 * NOTE: add checks about the other fields of the template, 2044 * which are ignored and SHOULD NOT be configured by the user. 2045 */ 2046static int 2047ldap_chain_db_init_one( 2048 BackendDB *be ) 2049{ 2050 slap_overinst *on = (slap_overinst *)be->bd_info; 2051 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 2052 2053 BackendInfo *bi = be->bd_info; 2054 ldapinfo_t *li; 2055 2056 slap_op_t t; 2057 2058 be->bd_info = lback; 2059 be->be_private = NULL; 2060 t = lback->bi_db_init( be, NULL ); 2061 if ( t != 0 ) { 2062 return t; 2063 } 2064 li = (ldapinfo_t *)be->be_private; 2065 li->li_urllist_f = NULL; 2066 li->li_urllist_p = NULL; 2067 2068 /* copy common data */ 2069 li->li_nretries = lc->lc_common_li->li_nretries; 2070 li->li_flags = lc->lc_common_li->li_flags; 2071 li->li_version = lc->lc_common_li->li_version; 2072 for ( t = 0; t < SLAP_OP_LAST; t++ ) { 2073 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ]; 2074 } 2075 be->bd_info = bi; 2076 2077 return 0; 2078} 2079 2080static int 2081ldap_chain_db_open_one( 2082 BackendDB *be ) 2083{ 2084 if ( SLAP_DBMONITORING( be ) ) { 2085 ldapinfo_t *li = (ldapinfo_t *)be->be_private; 2086 2087 if ( li->li_uri == NULL ) { 2088 ber_str2bv( "cn=Common Connections", 0, 1, 2089 &li->li_monitor_info.lmi_rdn ); 2090 2091 } else { 2092 char *ptr; 2093 2094 li->li_monitor_info.lmi_rdn.bv_len 2095 = STRLENOF( "cn=" ) + strlen( li->li_uri ); 2096 ptr = li->li_monitor_info.lmi_rdn.bv_val 2097 = ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 ); 2098 ptr = lutil_strcopy( ptr, "cn=" ); 2099 ptr = lutil_strcopy( ptr, li->li_uri ); 2100 ptr[ 0 ] = '\0'; 2101 } 2102 } 2103 2104 return lback->bi_db_open( be, NULL ); 2105} 2106 2107typedef struct ldap_chain_conn_apply_t { 2108 BackendDB *be; 2109 Connection *conn; 2110} ldap_chain_conn_apply_t; 2111 2112static int 2113ldap_chain_conn_apply( void *datum, void *arg ) 2114{ 2115 ldapinfo_t *li = (ldapinfo_t *)datum; 2116 ldap_chain_conn_apply_t *lca = (ldap_chain_conn_apply_t *)arg; 2117 2118 lca->be->be_private = (void *)li; 2119 2120 return lback->bi_connection_destroy( lca->be, lca->conn ); 2121} 2122 2123static int 2124ldap_chain_connection_destroy( 2125 BackendDB *be, 2126 Connection *conn 2127) 2128{ 2129 slap_overinst *on = (slap_overinst *) be->bd_info; 2130 ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private; 2131 void *private = be->be_private; 2132 ldap_chain_conn_apply_t lca; 2133 int rc; 2134 2135 be->be_private = NULL; 2136 lca.be = be; 2137 lca.conn = conn; 2138 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); 2139 rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply, 2140 (void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE; 2141 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); 2142 be->be_private = private; 2143 2144 return rc; 2145} 2146 2147#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 2148static int 2149ldap_chain_parse_ctrl( 2150 Operation *op, 2151 SlapReply *rs, 2152 LDAPControl *ctrl ) 2153{ 2154 ber_tag_t tag; 2155 BerElement *ber; 2156 ber_int_t mode, 2157 behavior; 2158 2159 if ( get_chaining( op ) != SLAP_CONTROL_NONE ) { 2160 rs->sr_text = "Chaining behavior control specified multiple times"; 2161 return LDAP_PROTOCOL_ERROR; 2162 } 2163 2164 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) { 2165 rs->sr_text = "Chaining behavior control specified with pagedResults control"; 2166 return LDAP_PROTOCOL_ERROR; 2167 } 2168 2169 if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) { 2170 mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT); 2171 2172 } else { 2173 ber_len_t len; 2174 2175 /* Parse the control value 2176 * ChainingBehavior ::= SEQUENCE { 2177 * resolveBehavior Behavior OPTIONAL, 2178 * continuationBehavior Behavior OPTIONAL } 2179 * 2180 * Behavior :: = ENUMERATED { 2181 * chainingPreferred (0), 2182 * chainingRequired (1), 2183 * referralsPreferred (2), 2184 * referralsRequired (3) } 2185 */ 2186 2187 ber = ber_init( &ctrl->ldctl_value ); 2188 if( ber == NULL ) { 2189 rs->sr_text = "internal error"; 2190 return LDAP_OTHER; 2191 } 2192 2193 tag = ber_scanf( ber, "{e" /* } */, &behavior ); 2194 /* FIXME: since the whole SEQUENCE is optional, 2195 * should we accept no enumerations at all? */ 2196 if ( tag != LBER_ENUMERATED ) { 2197 rs->sr_text = "Chaining behavior control: resolveBehavior decoding error"; 2198 return LDAP_PROTOCOL_ERROR; 2199 } 2200 2201 switch ( behavior ) { 2202 case LDAP_CHAINING_PREFERRED: 2203 mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED; 2204 break; 2205 2206 case LDAP_CHAINING_REQUIRED: 2207 mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED; 2208 break; 2209 2210 case LDAP_REFERRALS_PREFERRED: 2211 mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED; 2212 break; 2213 2214 case LDAP_REFERRALS_REQUIRED: 2215 mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED; 2216 break; 2217 2218 default: 2219 rs->sr_text = "Chaining behavior control: unknown resolveBehavior"; 2220 return LDAP_PROTOCOL_ERROR; 2221 } 2222 2223 tag = ber_peek_tag( ber, &len ); 2224 if ( tag == LBER_ENUMERATED ) { 2225 tag = ber_scanf( ber, "e", &behavior ); 2226 if ( tag == LBER_ERROR ) { 2227 rs->sr_text = "Chaining behavior control: continuationBehavior decoding error"; 2228 return LDAP_PROTOCOL_ERROR; 2229 } 2230 } 2231 2232 if ( tag == LBER_DEFAULT ) { 2233 mode |= SLAP_CH_CONTINUATION_DEFAULT; 2234 2235 } else { 2236 switch ( behavior ) { 2237 case LDAP_CHAINING_PREFERRED: 2238 mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED; 2239 break; 2240 2241 case LDAP_CHAINING_REQUIRED: 2242 mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED; 2243 break; 2244 2245 case LDAP_REFERRALS_PREFERRED: 2246 mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED; 2247 break; 2248 2249 case LDAP_REFERRALS_REQUIRED: 2250 mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED; 2251 break; 2252 2253 default: 2254 rs->sr_text = "Chaining behavior control: unknown continuationBehavior"; 2255 return LDAP_PROTOCOL_ERROR; 2256 } 2257 } 2258 2259 if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) { 2260 rs->sr_text = "Chaining behavior control: decoding error"; 2261 return LDAP_PROTOCOL_ERROR; 2262 } 2263 2264 (void) ber_free( ber, 1 ); 2265 } 2266 2267 op->o_chaining = mode | ( ctrl->ldctl_iscritical 2268 ? SLAP_CONTROL_CRITICAL 2269 : SLAP_CONTROL_NONCRITICAL ); 2270 2271 return LDAP_SUCCESS; 2272} 2273#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 2274 2275int 2276chain_initialize( void ) 2277{ 2278 int rc; 2279 2280 /* Make sure we don't exceed the bits reserved for userland */ 2281 config_check_userland( CH_LAST ); 2282 2283#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 2284 rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR, 2285 /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL, 2286 ldap_chain_parse_ctrl, &sc_chainingBehavior ); 2287 if ( rc != LDAP_SUCCESS ) { 2288 Debug( LDAP_DEBUG_ANY, "slapd-chain: " 2289 "unable to register chaining behavior control: %d.\n", 2290 rc, 0, 0 ); 2291 return rc; 2292 } 2293#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 2294 2295 ldapchain.on_bi.bi_type = "chain"; 2296 ldapchain.on_bi.bi_db_init = ldap_chain_db_init; 2297 ldapchain.on_bi.bi_db_config = ldap_chain_db_config; 2298 ldapchain.on_bi.bi_db_open = ldap_chain_db_open; 2299 ldapchain.on_bi.bi_db_close = ldap_chain_db_close; 2300 ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy; 2301 2302 ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy; 2303 2304 ldapchain.on_response = ldap_chain_response; 2305 2306 ldapchain.on_bi.bi_cf_ocs = chainocs; 2307 2308 rc = config_register_schema( chaincfg, chainocs ); 2309 if ( rc ) { 2310 return rc; 2311 } 2312 2313 return overlay_register( &ldapchain ); 2314} 2315 2316