1/* $NetBSD: distproc.c,v 1.3 2021/08/14 16:14:59 christos Exp $ */ 2 3/* distproc.c - implement distributed procedures */ 4/* $OpenLDAP$ */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2005-2021 The OpenLDAP Foundation. 8 * Portions Copyright 2003 Howard Chu. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted only as authorized by the OpenLDAP 13 * Public License. 14 * 15 * A copy of this license is available in the file LICENSE in the 16 * top-level directory of the distribution or, alternatively, at 17 * <http://www.OpenLDAP.org/license.html>. 18 */ 19/* ACKNOWLEDGEMENTS: 20 * This work was initially developed by Pierangelo Masarati for inclusion 21 * in OpenLDAP Software. 22 * Based on back-ldap and slapo-chain, developed by Howard Chu 23 */ 24 25#include <sys/cdefs.h> 26__RCSID("$NetBSD: distproc.c,v 1.3 2021/08/14 16:14:59 christos Exp $"); 27 28#include "portable.h" 29 30#include <stdio.h> 31 32#include <ac/string.h> 33#include <ac/socket.h> 34 35#include "slap.h" 36 37#ifdef SLAP_DISTPROC 38 39#include "back-ldap.h" 40 41#include "slap-config.h" 42 43/* 44 * From <draft-sermersheim-ldap-distproc> 45 * 46 47 ContinuationReference ::= SET { 48 referralURI [0] SET SIZE (1..MAX) OF URI, 49 localReference [2] LDAPDN, 50 referenceType [3] ReferenceType, 51 remainingName [4] RelativeLDAPDN OPTIONAL, 52 searchScope [5] SearchScope OPTIONAL, 53 searchedSubtrees [6] SearchedSubtrees OPTIONAL, 54 failedName [7] LDAPDN OPTIONAL, 55 ... } 56 57 ReferenceType ::= ENUMERATED { 58 superior (0), 59 subordinate (1), 60 cross (2), 61 nonSpecificSubordinate (3), 62 supplier (4), 63 master (5), 64 immediateSuperior (6), 65 self (7), 66 ... } 67 68 SearchScope ::= ENUMERATED { 69 baseObject (0), 70 singleLevel (1), 71 wholeSubtree (2), 72 subordinateSubtree (3), 73 ... } 74 75 SearchedSubtrees ::= SET OF RelativeLDAPDN 76 77 LDAPDN, RelativeLDAPDN, and LDAPString, are defined in [RFC2251]. 78 79 */ 80 81typedef enum ReferenceType_t { 82 LDAP_DP_RT_UNKNOWN = -1, 83 LDAP_DP_RT_SUPERIOR = 0, 84 LDAP_DP_RT_SUBORDINATE = 1, 85 LDAP_DP_RT_CROSS = 2, 86 LDAP_DP_RT_NONSPECIFICSUBORDINATE = 3, 87 LDAP_DP_RT_SUPPLIER = 4, 88 LDAP_DP_RT_MASTER = 5, 89 LDAP_DP_RT_IMMEDIATESUPERIOR = 6, 90 LDAP_DP_RT_SELF = 7, 91 LDAP_DP_RT_LAST 92} ReferenceType_t; 93 94typedef enum SearchScope_t { 95 LDAP_DP_SS_UNKNOWN = -1, 96 LDAP_DP_SS_BASEOBJECT = 0, 97 LDAP_DP_SS_SINGLELEVEL = 1, 98 LDAP_DP_SS_WHOLESUBTREE = 2, 99 LDAP_DP_SS_SUBORDINATESUBTREE = 3, 100 LDAP_DP_SS_LAST 101} SearchScope_t; 102 103typedef struct ContinuationReference_t { 104 BerVarray cr_referralURI; 105 /* ? [1] ? */ 106 struct berval cr_localReference; 107 ReferenceType_t cr_referenceType; 108 struct berval cr_remainingName; 109 SearchScope_t cr_searchScope; 110 BerVarray cr_searchedSubtrees; 111 struct berval cr_failedName; 112} ContinuationReference_t; 113#define CR_INIT { NULL, BER_BVNULL, LDAP_DP_RT_UNKNOWN, BER_BVNULL, LDAP_DP_SS_UNKNOWN, NULL, BER_BVNULL } 114 115#ifdef unused 116static struct berval bv2rt[] = { 117 BER_BVC( "superior" ), 118 BER_BVC( "subordinate" ), 119 BER_BVC( "cross" ), 120 BER_BVC( "nonSpecificSubordinate" ), 121 BER_BVC( "supplier" ), 122 BER_BVC( "master" ), 123 BER_BVC( "immediateSuperior" ), 124 BER_BVC( "self" ), 125 BER_BVNULL 126}; 127 128static struct berval bv2ss[] = { 129 BER_BVC( "baseObject" ), 130 BER_BVC( "singleLevel" ), 131 BER_BVC( "wholeSubtree" ), 132 BER_BVC( "subordinateSubtree" ), 133 BER_BVNULL 134}; 135 136static struct berval * 137ldap_distproc_rt2bv( ReferenceType_t rt ) 138{ 139 return &bv2rt[ rt ]; 140} 141 142static const char * 143ldap_distproc_rt2str( ReferenceType_t rt ) 144{ 145 return bv2rt[ rt ].bv_val; 146} 147 148static ReferenceType_t 149ldap_distproc_bv2rt( struct berval *bv ) 150{ 151 ReferenceType_t rt; 152 153 for ( rt = 0; !BER_BVISNULL( &bv2rt[ rt ] ); rt++ ) { 154 if ( ber_bvstrcasecmp( bv, &bv2rt[ rt ] ) == 0 ) { 155 return rt; 156 } 157 } 158 159 return LDAP_DP_RT_UNKNOWN; 160} 161 162static ReferenceType_t 163ldap_distproc_str2rt( const char *s ) 164{ 165 struct berval bv; 166 167 ber_str2bv( s, 0, 0, &bv ); 168 return ldap_distproc_bv2rt( &bv ); 169} 170 171static struct berval * 172ldap_distproc_ss2bv( SearchScope_t ss ) 173{ 174 return &bv2ss[ ss ]; 175} 176 177static const char * 178ldap_distproc_ss2str( SearchScope_t ss ) 179{ 180 return bv2ss[ ss ].bv_val; 181} 182 183static SearchScope_t 184ldap_distproc_bv2ss( struct berval *bv ) 185{ 186 ReferenceType_t ss; 187 188 for ( ss = 0; !BER_BVISNULL( &bv2ss[ ss ] ); ss++ ) { 189 if ( ber_bvstrcasecmp( bv, &bv2ss[ ss ] ) == 0 ) { 190 return ss; 191 } 192 } 193 194 return LDAP_DP_SS_UNKNOWN; 195} 196 197static SearchScope_t 198ldap_distproc_str2ss( const char *s ) 199{ 200 struct berval bv; 201 202 ber_str2bv( s, 0, 0, &bv ); 203 return ldap_distproc_bv2ss( &bv ); 204} 205#endif /* unused */ 206 207/* 208 * NOTE: this overlay assumes that the chainingBehavior control 209 * is registered by the chain overlay; it may move here some time. 210 * This overlay provides support for that control as well. 211 */ 212 213 214static int sc_returnContRef; 215#define o_returnContRef o_ctrlflag[sc_returnContRef] 216#define get_returnContRef(op) ((op)->o_returnContRef & SLAP_CONTROL_MASK) 217 218static struct berval slap_EXOP_CHAINEDREQUEST = BER_BVC( LDAP_EXOP_X_CHAINEDREQUEST ); 219static struct berval slap_FEATURE_CANCHAINOPS = BER_BVC( LDAP_FEATURE_X_CANCHAINOPS ); 220 221static BackendInfo *lback; 222 223typedef struct ldap_distproc_t { 224 /* "common" configuration info (anything occurring before an "uri") */ 225 ldapinfo_t *lc_common_li; 226 227 /* current configuration info */ 228 ldapinfo_t *lc_cfg_li; 229 230 /* tree of configured[/generated?] "uri" info */ 231 ldap_avl_info_t lc_lai; 232 233 unsigned lc_flags; 234#define LDAP_DISTPROC_F_NONE (0x00U) 235#define LDAP_DISTPROC_F_CHAINING (0x01U) 236#define LDAP_DISTPROC_F_CACHE_URI (0x10U) 237 238#define LDAP_DISTPROC_CHAINING( lc ) ( ( (lc)->lc_flags & LDAP_DISTPROC_F_CHAINING ) == LDAP_DISTPROC_F_CHAINING ) 239#define LDAP_DISTPROC_CACHE_URI( lc ) ( ( (lc)->lc_flags & LDAP_DISTPROC_F_CACHE_URI ) == LDAP_DISTPROC_F_CACHE_URI ) 240 241} ldap_distproc_t; 242 243static int ldap_distproc_db_init_common( BackendDB *be ); 244static int ldap_distproc_db_init_one( BackendDB *be ); 245#define ldap_distproc_db_open_one(be) (lback)->bi_db_open( (be) ) 246#define ldap_distproc_db_close_one(be) (0) 247#define ldap_distproc_db_destroy_one(be, ca) (lback)->bi_db_destroy( (be), (ca) ) 248 249static int 250ldap_distproc_uri_cmp( const void *c1, const void *c2 ) 251{ 252 const ldapinfo_t *li1 = (const ldapinfo_t *)c1; 253 const ldapinfo_t *li2 = (const ldapinfo_t *)c2; 254 255 assert( li1->li_bvuri != NULL ); 256 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) ); 257 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) ); 258 259 assert( li2->li_bvuri != NULL ); 260 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) ); 261 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) ); 262 263 /* If local DNs don't match, it is definitely not a match */ 264 return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ); 265} 266 267static int 268ldap_distproc_uri_dup( void *c1, void *c2 ) 269{ 270 ldapinfo_t *li1 = (ldapinfo_t *)c1; 271 ldapinfo_t *li2 = (ldapinfo_t *)c2; 272 273 assert( li1->li_bvuri != NULL ); 274 assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) ); 275 assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) ); 276 277 assert( li2->li_bvuri != NULL ); 278 assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) ); 279 assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) ); 280 281 /* Cannot have more than one shared session with same DN */ 282 if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) { 283 return -1; 284 } 285 286 return 0; 287} 288 289static int 290ldap_distproc_operational( Operation *op, SlapReply *rs ) 291{ 292 /* Trap entries generated by back-ldap. 293 * 294 * FIXME: we need a better way to recognize them; a cleaner 295 * solution would be to be able to intercept the response 296 * of be_operational(), so that we can divert only those 297 * calls that fail because operational attributes were 298 * requested for entries that do not belong to the underlying 299 * database. This fix is likely to intercept also entries 300 * generated by back-perl and so. */ 301 if ( rs->sr_entry->e_private == NULL ) { 302 return LDAP_SUCCESS; 303 } 304 305 return SLAP_CB_CONTINUE; 306} 307 308static int 309ldap_distproc_response( Operation *op, SlapReply *rs ) 310{ 311 return SLAP_CB_CONTINUE; 312} 313 314/* 315 * configuration... 316 */ 317 318enum { 319 /* NOTE: the chaining behavior control is registered 320 * by the chain overlay; it may move here some time */ 321 DP_CHAINING = 1, 322 DP_CACHE_URI, 323 324 DP_LAST 325}; 326 327static ConfigDriver distproc_cfgen; 328static ConfigCfAdd distproc_cfadd; 329static ConfigLDAPadd distproc_ldadd; 330 331static ConfigTable distproc_cfg[] = { 332 { "distproc-chaining", "args", 333 2, 4, 0, ARG_MAGIC|ARG_BERVAL|DP_CHAINING, distproc_cfgen, 334 /* NOTE: using the same attributeTypes defined 335 * for the "chain" overlay */ 336 "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' " 337 "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' " 338 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL }, 339 { "distproc-cache-uri", "TRUE/FALSE", 340 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DP_CACHE_URI, distproc_cfgen, 341 "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' " 342 "DESC 'Enables caching of URIs not present in configuration' " 343 "SYNTAX OMsBoolean " 344 "SINGLE-VALUE )", NULL, NULL }, 345 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 346}; 347 348static ConfigOCs distproc_ocs[] = { 349 { "( OLcfgOvOc:7.1 " 350 "NAME 'olcDistProcConfig' " 351 "DESC 'Distributed procedures <draft-sermersheim-ldap-distproc> configuration' " 352 "SUP olcOverlayConfig " 353 "MAY ( " 354 "olcChainingBehavior $ " 355 "olcChainCacheURI " 356 ") )", 357 Cft_Overlay, distproc_cfg, NULL, distproc_cfadd }, 358 { "( OLcfgOvOc:7.2 " 359 "NAME 'olcDistProcDatabase' " 360 "DESC 'Distributed procedure remote server configuration' " 361 "AUXILIARY )", 362 Cft_Misc, distproc_cfg, distproc_ldadd }, 363 { NULL, 0, NULL } 364}; 365 366static int 367distproc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca ) 368{ 369 slap_overinst *on; 370 ldap_distproc_t *lc; 371 372 ldapinfo_t *li; 373 374 AttributeDescription *ad = NULL; 375 Attribute *at; 376 const char *text; 377 378 int rc; 379 380 if ( p->ce_type != Cft_Overlay 381 || !p->ce_bi 382 || p->ce_bi->bi_cf_ocs != distproc_ocs ) 383 { 384 return LDAP_CONSTRAINT_VIOLATION; 385 } 386 387 on = (slap_overinst *)p->ce_bi; 388 lc = (ldap_distproc_t *)on->on_bi.bi_private; 389 390 assert( ca->be == NULL ); 391 ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) ); 392 393 ca->be->bd_info = (BackendInfo *)on; 394 395 rc = slap_str2ad( "olcDbURI", &ad, &text ); 396 assert( rc == LDAP_SUCCESS ); 397 398 at = attr_find( e->e_attrs, ad ); 399 if ( lc->lc_common_li == NULL && at != NULL ) { 400 /* FIXME: we should generate an empty default entry 401 * if none is supplied */ 402 Debug( LDAP_DEBUG_ANY, "slapd-distproc: " 403 "first underlying database \"%s\" " 404 "cannot contain attribute \"%s\".\n", 405 e->e_name.bv_val, ad->ad_cname.bv_val ); 406 rc = LDAP_CONSTRAINT_VIOLATION; 407 goto done; 408 409 } else if ( lc->lc_common_li != NULL && at == NULL ) { 410 /* FIXME: we should generate an empty default entry 411 * if none is supplied */ 412 Debug( LDAP_DEBUG_ANY, "slapd-distproc: " 413 "subsequent underlying database \"%s\" " 414 "must contain attribute \"%s\".\n", 415 e->e_name.bv_val, ad->ad_cname.bv_val ); 416 rc = LDAP_CONSTRAINT_VIOLATION; 417 goto done; 418 } 419 420 if ( lc->lc_common_li == NULL ) { 421 rc = ldap_distproc_db_init_common( ca->be ); 422 423 } else { 424 rc = ldap_distproc_db_init_one( ca->be ); 425 } 426 427 if ( rc != 0 ) { 428 Debug( LDAP_DEBUG_ANY, "slapd-distproc: " 429 "unable to init %sunderlying database \"%s\".\n", 430 lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val ); 431 rc = LDAP_CONSTRAINT_VIOLATION; 432 goto done; 433 } 434 435 li = ca->be->be_private; 436 437 if ( lc->lc_common_li == NULL ) { 438 lc->lc_common_li = li; 439 440 } else if ( ldap_tavl_insert( &lc->lc_lai.lai_tree, (caddr_t)li, 441 ldap_distproc_uri_cmp, ldap_distproc_uri_dup ) ) 442 { 443 Debug( LDAP_DEBUG_ANY, "slapd-distproc: " 444 "database \"%s\" insert failed.\n", 445 e->e_name.bv_val ); 446 rc = LDAP_CONSTRAINT_VIOLATION; 447 goto done; 448 } 449 450done:; 451 if ( rc != LDAP_SUCCESS ) { 452 (void)ldap_distproc_db_destroy_one( ca->be, NULL ); 453 ch_free( ca->be ); 454 ca->be = NULL; 455 } 456 457 return rc; 458} 459 460typedef struct ldap_distproc_cfadd_apply_t { 461 Operation *op; 462 SlapReply *rs; 463 Entry *p; 464 ConfigArgs *ca; 465 int count; 466} ldap_distproc_cfadd_apply_t; 467 468static void 469ldap_distproc_cfadd_apply( 470 ldapinfo_t *li, 471 Operation *op, 472 SlapReply *rs, 473 Entry *p, 474 ConfigArgs *ca, 475 int count ) 476{ 477 struct berval bv; 478 479 /* FIXME: should not hardcode "olcDatabase" here */ 480 bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ), 481 "olcDatabase={%d}%s", count, lback->bi_type ); 482 bv.bv_val = ca->cr_msg; 483 484 ca->be->be_private = (void *)li; 485 config_build_entry( op, rs, p->e_private, ca, 486 &bv, lback->bi_cf_ocs, &distproc_ocs[ 1 ] ); 487 488 return; 489} 490 491static int 492distproc_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca ) 493{ 494 CfEntryInfo *pe = p->e_private; 495 slap_overinst *on = (slap_overinst *)pe->ce_bi; 496 ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private; 497 void *priv = (void *)ca->be->be_private; 498 TAvlnode *edge; 499 int count = 0; 500 501 if ( lback->bi_cf_ocs ) { 502 ldap_distproc_cfadd_apply_t lca = { 0 }; 503 504 lca.op = op; 505 lca.rs = rs; 506 lca.p = p; 507 lca.ca = ca; 508 lca.count = 0; 509 510 ldap_distproc_cfadd_apply( lc->lc_common_li, op, rs, p, ca, count++ ); 511 512 edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT ); 513 while ( edge ) { 514 TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT ); 515 ldapinfo_t *li = (ldapinfo_t *)edge->avl_data; 516 ldap_distproc_cfadd_apply( li, op, rs, p, ca, count++ ); 517 edge = next; 518 } 519 520 ca->be->be_private = priv; 521 } 522 523 return 0; 524} 525 526static int 527distproc_cfgen( ConfigArgs *c ) 528{ 529 slap_overinst *on = (slap_overinst *)c->bi; 530 ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private; 531 532 int rc = 0; 533 534 if ( c->op == SLAP_CONFIG_EMIT ) { 535 switch( c->type ) { 536 case DP_CACHE_URI: 537 c->value_int = LDAP_DISTPROC_CACHE_URI( lc ); 538 break; 539 540 default: 541 assert( 0 ); 542 rc = 1; 543 } 544 return rc; 545 546 } else if ( c->op == LDAP_MOD_DELETE ) { 547 switch( c->type ) { 548 case DP_CHAINING: 549 return 1; 550 551 case DP_CACHE_URI: 552 lc->lc_flags &= ~LDAP_DISTPROC_F_CACHE_URI; 553 break; 554 555 default: 556 return 1; 557 } 558 return rc; 559 } 560 561 switch( c->type ) { 562 case DP_CACHE_URI: 563 if ( c->value_int ) { 564 lc->lc_flags |= LDAP_DISTPROC_F_CACHE_URI; 565 } else { 566 lc->lc_flags &= ~LDAP_DISTPROC_F_CACHE_URI; 567 } 568 break; 569 570 default: 571 assert( 0 ); 572 return 1; 573 } 574 575 return rc; 576} 577 578static int 579ldap_distproc_db_init( 580 BackendDB *be, 581 ConfigReply *cr ) 582{ 583 slap_overinst *on = (slap_overinst *)be->bd_info; 584 ldap_distproc_t *lc = NULL; 585 586 if ( lback == NULL ) { 587 lback = backend_info( "ldap" ); 588 589 if ( lback == NULL ) { 590 return 1; 591 } 592 } 593 594 lc = ch_malloc( sizeof( ldap_distproc_t ) ); 595 if ( lc == NULL ) { 596 return 1; 597 } 598 memset( lc, 0, sizeof( ldap_distproc_t ) ); 599 ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex ); 600 601 on->on_bi.bi_private = (void *)lc; 602 603 return 0; 604} 605 606static int 607ldap_distproc_db_config( 608 BackendDB *be, 609 const char *fname, 610 int lineno, 611 int argc, 612 char **argv ) 613{ 614 slap_overinst *on = (slap_overinst *)be->bd_info; 615 ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private; 616 617 int rc = SLAP_CONF_UNKNOWN; 618 619 if ( lc->lc_common_li == NULL ) { 620 void *be_private = be->be_private; 621 ldap_distproc_db_init_common( be ); 622 lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private; 623 be->be_private = be_private; 624 } 625 626 /* Something for the distproc database? */ 627 if ( strncasecmp( argv[ 0 ], "distproc-", STRLENOF( "distproc-" ) ) == 0 ) { 628 char *save_argv0 = argv[ 0 ]; 629 BackendInfo *bd_info = be->bd_info; 630 void *be_private = be->be_private; 631 ConfigOCs *be_cf_ocs = be->be_cf_ocs; 632 int is_uri = 0; 633 634 argv[ 0 ] += STRLENOF( "distproc-" ); 635 636 if ( strcasecmp( argv[ 0 ], "uri" ) == 0 ) { 637 rc = ldap_distproc_db_init_one( be ); 638 if ( rc != 0 ) { 639 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 640 "underlying slapd-ldap initialization failed.\n.", 641 fname, lineno ); 642 return 1; 643 } 644 lc->lc_cfg_li = be->be_private; 645 is_uri = 1; 646 } 647 648 /* TODO: add checks on what other slapd-ldap(5) args 649 * should be put in the template; this is not quite 650 * harmful, because attributes that shouldn't don't 651 * get actually used, but the user should at least 652 * be warned. 653 */ 654 655 be->bd_info = lback; 656 be->be_private = (void *)lc->lc_cfg_li; 657 be->be_cf_ocs = lback->bi_cf_ocs; 658 659 rc = config_generic_wrapper( be, fname, lineno, argc, argv ); 660 661 argv[ 0 ] = save_argv0; 662 be->be_cf_ocs = be_cf_ocs; 663 be->be_private = be_private; 664 be->bd_info = bd_info; 665 666 if ( is_uri ) { 667private_destroy:; 668 if ( rc != 0 ) { 669 BackendDB db = *be; 670 671 db.bd_info = lback; 672 db.be_private = (void *)lc->lc_cfg_li; 673 ldap_distproc_db_destroy_one( &db, NULL ); 674 lc->lc_cfg_li = NULL; 675 676 } else { 677 if ( lc->lc_cfg_li->li_bvuri == NULL 678 || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] ) 679 || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) ) 680 { 681 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 682 "no URI list allowed in slapo-distproc.\n", 683 fname, lineno ); 684 rc = 1; 685 goto private_destroy; 686 } 687 688 if ( ldap_tavl_insert( &lc->lc_lai.lai_tree, 689 (caddr_t)lc->lc_cfg_li, 690 ldap_distproc_uri_cmp, ldap_distproc_uri_dup ) ) 691 { 692 Debug( LDAP_DEBUG_ANY, "%s: line %d: " 693 "duplicate URI in slapo-distproc.\n", 694 fname, lineno ); 695 rc = 1; 696 goto private_destroy; 697 } 698 } 699 } 700 } 701 702 return rc; 703} 704 705enum db_which { 706 db_open = 0, 707 db_close, 708 db_destroy, 709 710 db_last 711}; 712 713static int 714ldap_distproc_db_func( 715 BackendDB *be, 716 enum db_which which 717) 718{ 719 slap_overinst *on = (slap_overinst *)be->bd_info; 720 ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private; 721 722 int rc = 0; 723 724 if ( lc ) { 725 BI_db_func *func = (&lback->bi_db_open)[ which ]; 726 727 if ( func != NULL && lc->lc_common_li != NULL ) { 728 BackendDB db = *be; 729 730 db.bd_info = lback; 731 db.be_private = lc->lc_common_li; 732 733 rc = func( &db, NULL ); 734 735 if ( rc != 0 ) { 736 return rc; 737 } 738 739 if ( lc->lc_lai.lai_tree != NULL ) { 740 TAvlnode *edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT ); 741 while ( edge ) { 742 TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT ); 743 ldapinfo_t *li = (ldapinfo_t *)edge->avl_data; 744 be->be_private = (void *)li; 745 rc = func( &db, NULL ); 746 if ( rc == 1 ) { 747 break; 748 } 749 edge = next; 750 } 751 } 752 } 753 } 754 755 return rc; 756} 757 758static int 759ldap_distproc_db_open( 760 BackendDB *be, 761 ConfigReply *cr ) 762{ 763 return ldap_distproc_db_func( be, db_open ); 764} 765 766static int 767ldap_distproc_db_close( 768 BackendDB *be, 769 ConfigReply *cr ) 770{ 771 return ldap_distproc_db_func( be, db_close ); 772} 773 774static int 775ldap_distproc_db_destroy( 776 BackendDB *be, 777 ConfigReply *cr ) 778{ 779 slap_overinst *on = (slap_overinst *) be->bd_info; 780 ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private; 781 782 int rc; 783 784 rc = ldap_distproc_db_func( be, db_destroy ); 785 786 if ( lc ) { 787 ldap_tavl_free( lc->lc_lai.lai_tree, NULL ); 788 ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex ); 789 ch_free( lc ); 790 } 791 792 return rc; 793} 794 795/* 796 * inits one instance of the slapd-ldap backend, and stores 797 * the private info in be_private of the arg 798 */ 799static int 800ldap_distproc_db_init_common( 801 BackendDB *be ) 802{ 803 BackendInfo *bi = be->bd_info; 804 int t; 805 806 be->bd_info = lback; 807 be->be_private = NULL; 808 t = lback->bi_db_init( be, NULL ); 809 if ( t != 0 ) { 810 return t; 811 } 812 be->bd_info = bi; 813 814 return 0; 815} 816 817/* 818 * inits one instance of the slapd-ldap backend, stores 819 * the private info in be_private of the arg and fills 820 * selected fields with data from the template. 821 * 822 * NOTE: add checks about the other fields of the template, 823 * which are ignored and SHOULD NOT be configured by the user. 824 */ 825static int 826ldap_distproc_db_init_one( 827 BackendDB *be ) 828{ 829 slap_overinst *on = (slap_overinst *)be->bd_info; 830 ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private; 831 832 BackendInfo *bi = be->bd_info; 833 ldapinfo_t *li; 834 835 slap_op_t t; 836 837 be->bd_info = lback; 838 be->be_private = NULL; 839 t = lback->bi_db_init( be, NULL ); 840 if ( t != 0 ) { 841 return t; 842 } 843 li = (ldapinfo_t *)be->be_private; 844 845 /* copy common data */ 846 li->li_nretries = lc->lc_common_li->li_nretries; 847 li->li_flags = lc->lc_common_li->li_flags; 848 li->li_version = lc->lc_common_li->li_version; 849 for ( t = 0; t < SLAP_OP_LAST; t++ ) { 850 li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ]; 851 } 852 be->bd_info = bi; 853 854 return 0; 855} 856 857static int 858ldap_distproc_connection_destroy( 859 BackendDB *be, 860 Connection *conn 861) 862{ 863 slap_overinst *on = (slap_overinst *) be->bd_info; 864 ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private; 865 void *private = be->be_private; 866 int rc; 867 TAvlnode *edge; 868 869 be->be_private = NULL; 870 ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex ); 871 edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT ); 872 while ( edge ) { 873 TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT ); 874 ldapinfo_t *li = (ldapinfo_t *)edge->avl_data; 875 be->be_private = (void *)li; 876 rc = lback->bi_connection_destroy( be, conn ); 877 if ( rc == 1 ) { 878 break; 879 } 880 edge = next; 881 } 882 ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex ); 883 be->be_private = private; 884 885 return rc; 886} 887 888static int 889ldap_distproc_parse_returnContRef_ctrl( 890 Operation *op, 891 SlapReply *rs, 892 LDAPControl *ctrl ) 893{ 894 if ( get_returnContRef( op ) != SLAP_CONTROL_NONE ) { 895 rs->sr_text = "returnContinuationReference control specified multiple times"; 896 return LDAP_PROTOCOL_ERROR; 897 } 898 899 if ( op->o_pagedresults != SLAP_CONTROL_NONE ) { 900 rs->sr_text = "returnContinuationReference control specified with pagedResults control"; 901 return LDAP_PROTOCOL_ERROR; 902 } 903 904 if ( !BER_BVISEMPTY( &ctrl->ldctl_value ) ) { 905 rs->sr_text = "returnContinuationReference control: value must be NULL"; 906 return LDAP_PROTOCOL_ERROR; 907 } 908 909 op->o_returnContRef = ctrl->ldctl_iscritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL; 910 911 return LDAP_SUCCESS; 912} 913 914static int 915ldap_exop_chained_request( 916 Operation *op, 917 SlapReply *rs ) 918{ 919 Debug( LDAP_DEBUG_STATS, "%s CHAINED REQUEST\n", 920 op->o_log_prefix ); 921 922 rs->sr_err = backend_check_restrictions( op, rs, 923 (struct berval *)&slap_EXOP_CHAINEDREQUEST ); 924 if ( rs->sr_err != LDAP_SUCCESS ) { 925 return rs->sr_err; 926 } 927 928 /* by now, just reject requests */ 929 rs->sr_text = "under development"; 930 return LDAP_UNWILLING_TO_PERFORM; 931} 932 933 934static slap_overinst distproc; 935 936int 937distproc_initialize( void ) 938{ 939 int rc; 940 941 /* Make sure we don't exceed the bits reserved for userland */ 942 config_check_userland( DP_LAST ); 943 944 rc = load_extop( (struct berval *)&slap_EXOP_CHAINEDREQUEST, 945 SLAP_EXOP_HIDE, ldap_exop_chained_request ); 946 if ( rc != LDAP_SUCCESS ) { 947 Debug( LDAP_DEBUG_ANY, "slapd-distproc: " 948 "unable to register chainedRequest exop: %d.\n", 949 rc ); 950 return rc; 951 } 952 953 rc = supported_feature_load( &slap_FEATURE_CANCHAINOPS ); 954 if ( rc != LDAP_SUCCESS ) { 955 Debug( LDAP_DEBUG_ANY, "slapd-distproc: " 956 "unable to register canChainOperations supported feature: %d.\n", 957 rc ); 958 return rc; 959 } 960 961 rc = register_supported_control( LDAP_CONTROL_X_RETURNCONTREF, 962 SLAP_CTRL_GLOBAL|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL, 963 ldap_distproc_parse_returnContRef_ctrl, &sc_returnContRef ); 964 if ( rc != LDAP_SUCCESS ) { 965 Debug( LDAP_DEBUG_ANY, "slapd-distproc: " 966 "unable to register returnContinuationReference control: %d.\n", 967 rc ); 968 return rc; 969 } 970 971 distproc.on_bi.bi_type = "distproc"; 972 distproc.on_bi.bi_db_init = ldap_distproc_db_init; 973 distproc.on_bi.bi_db_config = ldap_distproc_db_config; 974 distproc.on_bi.bi_db_open = ldap_distproc_db_open; 975 distproc.on_bi.bi_db_close = ldap_distproc_db_close; 976 distproc.on_bi.bi_db_destroy = ldap_distproc_db_destroy; 977 978 /* ... otherwise the underlying backend's function would be called, 979 * likely passing an invalid entry; on the contrary, the requested 980 * operational attributes should have been returned while chasing 981 * the referrals. This all in all is a bit messy, because part 982 * of the operational attributes are generated by the backend; 983 * part by the frontend; back-ldap should receive all the available 984 * ones from the remote server, but then, on its own, it strips those 985 * it assumes will be (re)generated by the frontend (e.g. 986 * subschemaSubentry, entryDN, ...) */ 987 distproc.on_bi.bi_operational = ldap_distproc_operational; 988 989 distproc.on_bi.bi_connection_destroy = ldap_distproc_connection_destroy; 990 991 distproc.on_response = ldap_distproc_response; 992 993 distproc.on_bi.bi_cf_ocs = distproc_ocs; 994 995 rc = config_register_schema( distproc_cfg, distproc_ocs ); 996 if ( rc ) { 997 return rc; 998 } 999 1000 return overlay_register( &distproc ); 1001} 1002 1003#endif /* SLAP_DISTPROC */ 1004