1 /* $NetBSD: variant.c,v 1.2 2021/08/14 16:14:54 christos Exp $ */ 2 3 /* variant.c - variant overlay */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 2016-2021 Symas Corporation. 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 developed in 2016-2017 by Ondřej Kuzník for Symas Corp. 19 */ 20 21 #include <sys/cdefs.h> 22 __RCSID("$NetBSD: variant.c,v 1.2 2021/08/14 16:14:54 christos Exp $"); 23 24 #include "portable.h" 25 26 #ifdef SLAPD_OVER_VARIANT 27 28 #include "slap.h" 29 #include "slap-config.h" 30 #include "ldap_queue.h" 31 32 typedef enum variant_type_t { 33 VARIANT_INFO_PLAIN = 1 << 0, 34 VARIANT_INFO_REGEX = 1 << 1, 35 36 VARIANT_INFO_ALL = ~0 37 } variant_type_t; 38 39 typedef struct variant_info_t { 40 int passReplication; 41 LDAP_STAILQ_HEAD(variant_list, variantEntry_info) variants, regex_variants; 42 } variant_info_t; 43 44 typedef struct variantEntry_info { 45 variant_info_t *ov; 46 struct berval dn; 47 variant_type_t type; 48 regex_t *regex; 49 LDAP_SLIST_HEAD(attribute_list, variantAttr_info) attributes; 50 LDAP_STAILQ_ENTRY(variantEntry_info) next; 51 } variantEntry_info; 52 53 typedef struct variantAttr_info { 54 variantEntry_info *variant; 55 struct berval dn; 56 AttributeDescription *attr, *alternative; 57 LDAP_SLIST_ENTRY(variantAttr_info) next; 58 } variantAttr_info; 59 60 static int 61 variant_build_dn( 62 Operation *op, 63 variantAttr_info *vai, 64 int nmatch, 65 regmatch_t *pmatch, 66 struct berval *out ) 67 { 68 struct berval dn, *ndn = &op->o_req_ndn; 69 char *dest, *p, *prev, *end = vai->dn.bv_val + vai->dn.bv_len; 70 size_t len = vai->dn.bv_len; 71 int rc; 72 73 p = vai->dn.bv_val; 74 while ( (p = memchr( p, '$', end - p )) != NULL ) { 75 len -= 1; 76 p += 1; 77 78 if ( ( *p >= '0' ) && ( *p <= '9' ) ) { 79 int i = *p - '0'; 80 81 len += ( pmatch[i].rm_eo - pmatch[i].rm_so ); 82 } else if ( *p != '$' ) { 83 /* Should have been checked at configuration time */ 84 assert(0); 85 } 86 len -= 1; 87 p += 1; 88 } 89 90 dest = dn.bv_val = ch_realloc( out->bv_val, len + 1 ); 91 dn.bv_len = len; 92 93 prev = vai->dn.bv_val; 94 while ( (p = memchr( prev, '$', end - prev )) != NULL ) { 95 len = p - prev; 96 AC_MEMCPY( dest, prev, len ); 97 dest += len; 98 p += 1; 99 100 if ( ( *p >= '0' ) && ( *p <= '9' ) ) { 101 int i = *p - '0'; 102 len = pmatch[i].rm_eo - pmatch[i].rm_so; 103 104 AC_MEMCPY( dest, ndn->bv_val + pmatch[i].rm_so, len ); 105 dest += len; 106 } else if ( *p == '$' ) { 107 *dest++ = *p; 108 } 109 prev = p + 1; 110 } 111 len = end - prev; 112 AC_MEMCPY( dest, prev, len ); 113 dest += len; 114 *dest = '\0'; 115 116 rc = dnNormalize( 0, NULL, NULL, &dn, out, NULL ); 117 ch_free( dn.bv_val ); 118 119 return rc; 120 } 121 122 static int 123 variant_build_entry( 124 Operation *op, 125 variantEntry_info *vei, 126 struct berval *dn, 127 Entry **ep, 128 int nmatch, 129 regmatch_t *pmatch ) 130 { 131 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 132 BackendDB *be_orig = op->o_bd, *db; 133 struct berval ndn = BER_BVNULL; 134 variantAttr_info *vai; 135 Attribute *a; 136 BerVarray nvals; 137 Entry *e; 138 unsigned int i; 139 int rc; 140 141 assert( ep ); 142 assert( !*ep ); 143 144 rc = overlay_entry_get_ov( op, dn, NULL, NULL, 0, &e, on ); 145 if ( rc == LDAP_SUCCESS && is_entry_referral( e ) ) { 146 overlay_entry_release_ov( op, e, 0, on ); 147 rc = LDAP_REFERRAL; 148 } 149 150 if ( rc != LDAP_SUCCESS ) { 151 goto done; 152 } 153 154 *ep = entry_dup( e ); 155 overlay_entry_release_ov( op, e, 0, on ); 156 157 LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) { 158 if ( vei->type == VARIANT_INFO_REGEX ) { 159 rc = variant_build_dn( op, vai, nmatch, pmatch, &ndn ); 160 if ( rc != LDAP_SUCCESS ) { 161 goto done; 162 } 163 } else { 164 ndn = vai->dn; 165 } 166 167 (void)attr_delete( &(*ep)->e_attrs, vai->attr ); 168 op->o_bd = be_orig; 169 170 /* only select backend if not served by ours, would retrace all 171 * overlays again */ 172 db = select_backend( &ndn, 0 ); 173 if ( db && db != be_orig->bd_self ) { 174 op->o_bd = db; 175 rc = be_entry_get_rw( op, &ndn, NULL, vai->alternative, 0, &e ); 176 } else { 177 rc = overlay_entry_get_ov( 178 op, &ndn, NULL, vai->alternative, 0, &e, on ); 179 } 180 181 switch ( rc ) { 182 case LDAP_SUCCESS: 183 break; 184 case LDAP_INSUFFICIENT_ACCESS: 185 case LDAP_NO_SUCH_ATTRIBUTE: 186 case LDAP_NO_SUCH_OBJECT: 187 rc = LDAP_SUCCESS; 188 continue; 189 break; 190 default: 191 goto done; 192 break; 193 } 194 195 a = attr_find( e->e_attrs, vai->alternative ); 196 197 /* back-ldif doesn't check the attribute exists in the entry before 198 * returning it */ 199 if ( a ) { 200 if ( a->a_nvals ) { 201 nvals = a->a_nvals; 202 } else { 203 nvals = a->a_vals; 204 } 205 206 for ( i = 0; i < a->a_numvals; i++ ) { 207 if ( backend_access( op, e, &ndn, vai->alternative, &nvals[i], 208 ACL_READ, NULL ) != LDAP_SUCCESS ) { 209 continue; 210 } 211 212 rc = attr_merge_one( *ep, vai->attr, &a->a_vals[i], &nvals[i] ); 213 if ( rc != LDAP_SUCCESS ) { 214 break; 215 } 216 } 217 } 218 219 if ( db && db != be_orig->bd_self ) { 220 be_entry_release_rw( op, e, 0 ); 221 } else { 222 overlay_entry_release_ov( op, e, 0, on ); 223 } 224 if ( rc != LDAP_SUCCESS ) { 225 goto done; 226 } 227 } 228 229 done: 230 op->o_bd = be_orig; 231 if ( rc != LDAP_SUCCESS && *ep ) { 232 entry_free( *ep ); 233 *ep = NULL; 234 } 235 if ( vei->type == VARIANT_INFO_REGEX ) { 236 ch_free( ndn.bv_val ); 237 } 238 239 return rc; 240 } 241 242 static int 243 variant_find_config( 244 Operation *op, 245 variant_info_t *ov, 246 struct berval *ndn, 247 int which, 248 variantEntry_info **veip, 249 size_t nmatch, 250 regmatch_t *pmatch ) 251 { 252 variantEntry_info *vei; 253 254 assert( veip ); 255 256 if ( which & VARIANT_INFO_PLAIN ) { 257 int diff; 258 259 LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) { 260 dnMatch( &diff, 0, NULL, NULL, ndn, &vei->dn ); 261 if ( diff ) continue; 262 263 *veip = vei; 264 return LDAP_SUCCESS; 265 } 266 } 267 268 if ( which & VARIANT_INFO_REGEX ) { 269 LDAP_STAILQ_FOREACH( vei, &ov->regex_variants, next ) { 270 if ( regexec( vei->regex, ndn->bv_val, nmatch, pmatch, 0 ) ) { 271 continue; 272 } 273 274 *veip = vei; 275 return LDAP_SUCCESS; 276 } 277 } 278 279 return SLAP_CB_CONTINUE; 280 } 281 282 static int 283 variant_op_add( Operation *op, SlapReply *rs ) 284 { 285 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 286 variant_info_t *ov = on->on_bi.bi_private; 287 variantEntry_info *vei; 288 int rc; 289 290 /* Replication always uses the rootdn */ 291 if ( ov->passReplication && SLAPD_SYNC_IS_SYNCCONN(op->o_connid) && 292 be_isroot( op ) ) { 293 return SLAP_CB_CONTINUE; 294 } 295 296 Debug( LDAP_DEBUG_TRACE, "variant_op_add: " 297 "dn=%s\n", op->o_req_ndn.bv_val ); 298 299 rc = variant_find_config( 300 op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, 0, NULL ); 301 if ( rc == LDAP_SUCCESS ) { 302 variantAttr_info *vai; 303 304 LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) { 305 Attribute *a; 306 for ( a = op->ora_e->e_attrs; a; a = a->a_next ) { 307 if ( a->a_desc == vai->attr ) { 308 rc = LDAP_CONSTRAINT_VIOLATION; 309 send_ldap_error( op, rs, rc, 310 "variant: trying to add variant attributes" ); 311 goto done; 312 } 313 } 314 } 315 } 316 rc = SLAP_CB_CONTINUE; 317 318 done: 319 Debug( LDAP_DEBUG_TRACE, "variant_op_add: " 320 "finished with %d\n", 321 rc ); 322 return rc; 323 } 324 325 static int 326 variant_op_compare( Operation *op, SlapReply *rs ) 327 { 328 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 329 variant_info_t *ov = on->on_bi.bi_private; 330 variantEntry_info *vei; 331 regmatch_t pmatch[10]; 332 int rc, nmatch = sizeof(pmatch) / sizeof(regmatch_t); 333 334 Debug( LDAP_DEBUG_TRACE, "variant_op_compare: " 335 "dn=%s\n", op->o_req_ndn.bv_val ); 336 337 rc = variant_find_config( 338 op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, nmatch, pmatch ); 339 if ( rc == LDAP_SUCCESS ) { 340 Entry *e = NULL; 341 342 rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch ); 343 /* in case of error, just let the backend deal with the mod and the 344 * client should get a meaningful error back */ 345 if ( rc != LDAP_SUCCESS ) { 346 rc = SLAP_CB_CONTINUE; 347 } else { 348 rc = slap_compare_entry( op, e, op->orc_ava ); 349 350 entry_free( e ); 351 e = NULL; 352 } 353 } 354 355 if ( rc != SLAP_CB_CONTINUE ) { 356 rs->sr_err = rc; 357 send_ldap_result( op, rs ); 358 } 359 360 Debug( LDAP_DEBUG_TRACE, "variant_op_compare: " 361 "finished with %d\n", rc ); 362 return rc; 363 } 364 365 static int 366 variant_cmp_op( const void *l, const void *r ) 367 { 368 const Operation *left = l, *right = r; 369 int diff; 370 371 dnMatch( &diff, 0, NULL, NULL, (struct berval *)&left->o_req_ndn, 372 (void *)&right->o_req_ndn ); 373 374 return diff; 375 } 376 377 static int 378 variant_run_mod( void *nop, void *arg ) 379 { 380 SlapReply nrs = { REP_RESULT }; 381 slap_callback cb = { 0 }; 382 Operation *op = nop; 383 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 384 int *rc = arg; 385 386 cb.sc_response = slap_null_cb; 387 op->o_callback = &cb; 388 389 Debug( LDAP_DEBUG_TRACE, "variant_run_mod: " 390 "running mod on dn=%s\n", 391 op->o_req_ndn.bv_val ); 392 *rc = on->on_info->oi_orig->bi_op_modify( op, &nrs ); 393 Debug( LDAP_DEBUG_TRACE, "variant_run_mod: " 394 "finished with %d\n", *rc ); 395 396 return ( *rc != LDAP_SUCCESS ); 397 } 398 399 /** Move the Modifications back to the original Op so that they can be disposed 400 * of by the original creator 401 */ 402 static int 403 variant_reassign_mods( void *nop, void *arg ) 404 { 405 Operation *op = nop, *orig_op = arg; 406 Modifications *mod; 407 408 assert( op->orm_modlist ); 409 410 for ( mod = op->orm_modlist; mod->sml_next; mod = mod->sml_next ) 411 /* get the tail mod */; 412 413 mod->sml_next = orig_op->orm_modlist; 414 orig_op->orm_modlist = op->orm_modlist; 415 416 return LDAP_SUCCESS; 417 } 418 419 void 420 variant_free_op( void *op ) 421 { 422 ch_free( ((Operation *)op)->o_req_ndn.bv_val ); 423 ch_free( op ); 424 } 425 426 static int 427 variant_op_mod( Operation *op, SlapReply *rs ) 428 { 429 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 430 variant_info_t *ov = on->on_bi.bi_private; 431 variantEntry_info *vei; 432 variantAttr_info *vai; 433 Avlnode *ops = NULL; 434 Entry *e = NULL; 435 Modifications *mod, *nextmod; 436 regmatch_t pmatch[10]; 437 int rc, nmatch = sizeof(pmatch) / sizeof(regmatch_t); 438 439 /* Replication always uses the rootdn */ 440 if ( ov->passReplication && SLAPD_SYNC_IS_SYNCCONN(op->o_connid) && 441 be_isroot( op ) ) { 442 return SLAP_CB_CONTINUE; 443 } 444 445 Debug( LDAP_DEBUG_TRACE, "variant_op_mod: " 446 "dn=%s\n", op->o_req_ndn.bv_val ); 447 448 rc = variant_find_config( 449 op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, nmatch, pmatch ); 450 if ( rc != LDAP_SUCCESS ) { 451 Debug( LDAP_DEBUG_TRACE, "variant_op_mod: " 452 "not a variant\n" ); 453 rc = SLAP_CB_CONTINUE; 454 goto done; 455 } 456 457 rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch ); 458 /* in case of error, just let the backend deal with the mod and the client 459 * should get a meaningful error back */ 460 if ( rc != LDAP_SUCCESS ) { 461 Debug( LDAP_DEBUG_TRACE, "variant_op_mod: " 462 "failed to retrieve entry\n" ); 463 rc = SLAP_CB_CONTINUE; 464 goto done; 465 } 466 467 rc = acl_check_modlist( op, e, op->orm_modlist ); 468 entry_free( e ); 469 470 if ( !rc ) { 471 rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 472 send_ldap_error( op, rs, rc, "" ); 473 return rc; 474 } 475 476 for ( mod = op->orm_modlist; mod; mod = nextmod ) { 477 Operation needle = { .o_req_ndn = BER_BVNULL }, *nop; 478 479 nextmod = mod->sml_next; 480 481 LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) { 482 if ( vai->attr == mod->sml_desc ) { 483 break; 484 } 485 } 486 487 if ( vai ) { 488 if ( vei->type == VARIANT_INFO_REGEX ) { 489 rc = variant_build_dn( 490 op, vai, nmatch, pmatch, &needle.o_req_ndn ); 491 if ( rc != LDAP_SUCCESS ) { 492 continue; 493 } 494 } else { 495 needle.o_req_ndn = vai->dn; 496 } 497 498 nop = ldap_avl_find( ops, &needle, variant_cmp_op ); 499 if ( nop == NULL ) { 500 nop = ch_calloc( 1, sizeof(Operation) ); 501 *nop = *op; 502 503 ber_dupbv( &nop->o_req_ndn, &needle.o_req_ndn ); 504 nop->o_req_dn = nop->o_req_ndn; 505 nop->orm_modlist = NULL; 506 507 rc = ldap_avl_insert( &ops, nop, variant_cmp_op, ldap_avl_dup_error ); 508 assert( rc == 0 ); 509 } 510 mod->sml_desc = vai->alternative; 511 512 op->orm_modlist = nextmod; 513 mod->sml_next = nop->orm_modlist; 514 nop->orm_modlist = mod; 515 516 if ( vei->type == VARIANT_INFO_REGEX ) { 517 ch_free( needle.o_req_ndn.bv_val ); 518 } 519 } 520 } 521 522 if ( !ops ) { 523 Debug( LDAP_DEBUG_TRACE, "variant_op_mod: " 524 "no variant attributes in mod\n" ); 525 return SLAP_CB_CONTINUE; 526 } 527 528 /* 529 * First run original Operation 530 * This will take care of making sure the entry exists as well. 531 * 532 * FIXME? 533 * Since we cannot make the subsequent Ops atomic wrt. this one, we just 534 * let it send the response as well. After all, the changes on the main DN 535 * have finished by then 536 */ 537 rc = on->on_info->oi_orig->bi_op_modify( op, rs ); 538 if ( rc == LDAP_SUCCESS ) { 539 /* FIXME: if a mod fails, should we attempt to apply the rest? */ 540 ldap_avl_apply( ops, variant_run_mod, &rc, -1, AVL_INORDER ); 541 } 542 543 ldap_avl_apply( ops, variant_reassign_mods, op, -1, AVL_INORDER ); 544 ldap_avl_free( ops, variant_free_op ); 545 546 done: 547 Debug( LDAP_DEBUG_TRACE, "variant_op_mod: " 548 "finished with %d\n", rc ); 549 return rc; 550 } 551 552 static int 553 variant_search_response( Operation *op, SlapReply *rs ) 554 { 555 slap_overinst *on = op->o_callback->sc_private; 556 variant_info_t *ov = on->on_bi.bi_private; 557 variantEntry_info *vei; 558 int rc; 559 560 if ( rs->sr_type == REP_RESULT ) { 561 ch_free( op->o_callback ); 562 op->o_callback = NULL; 563 } 564 565 if ( rs->sr_type != REP_SEARCH ) { 566 return SLAP_CB_CONTINUE; 567 } 568 569 rc = variant_find_config( 570 op, ov, &rs->sr_entry->e_nname, VARIANT_INFO_PLAIN, &vei, 0, NULL ); 571 if ( rc == LDAP_SUCCESS ) { 572 rs->sr_nentries--; 573 return rc; 574 } 575 576 return SLAP_CB_CONTINUE; 577 } 578 579 static int 580 variant_op_search( Operation *op, SlapReply *rs ) 581 { 582 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 583 variant_info_t *ov = on->on_bi.bi_private; 584 variantEntry_info *vei; 585 slap_callback *cb; 586 Entry *e = NULL; 587 regmatch_t pmatch[10]; 588 int variantInScope = 0, rc = SLAP_CB_CONTINUE, 589 nmatch = sizeof(pmatch) / sizeof(regmatch_t); 590 591 if ( ov->passReplication && ( op->o_sync > SLAP_CONTROL_IGNORED ) ) { 592 return SLAP_CB_CONTINUE; 593 } 594 595 Debug( LDAP_DEBUG_TRACE, "variant_op_search: " 596 "dn=%s, scope=%d\n", 597 op->o_req_ndn.bv_val, op->ors_scope ); 598 599 LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) { 600 if ( !dnIsSuffixScope( &vei->dn, &op->o_req_ndn, op->ors_scope ) ) 601 continue; 602 603 variantInScope = 1; 604 605 rc = variant_build_entry( op, vei, &vei->dn, &e, 0, NULL ); 606 if ( rc == LDAP_NO_SUCH_OBJECT || rc == LDAP_REFERRAL ) { 607 rc = SLAP_CB_CONTINUE; 608 continue; 609 } else if ( rc != LDAP_SUCCESS ) { 610 Debug( LDAP_DEBUG_TRACE, "variant_op_search: " 611 "failed to retrieve entry: dn=%s\n", 612 vei->dn.bv_val ); 613 goto done; 614 } 615 616 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) { 617 Debug( LDAP_DEBUG_TRACE, "variant_op_search: " 618 "entry matched: dn=%s\n", 619 vei->dn.bv_val ); 620 rs->sr_entry = e; 621 rs->sr_attrs = op->ors_attrs; 622 rc = send_search_entry( op, rs ); 623 } 624 entry_free( e ); 625 e = NULL; 626 } 627 628 /* Three options: 629 * - the entry has been handled above, in that case vei->type is VARIANT_INFO_PLAIN 630 * - the entry matches a regex, use the first one and we're finished 631 * - no configuration matches entry - do nothing 632 */ 633 if ( op->ors_scope == LDAP_SCOPE_BASE && 634 variant_find_config( op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, 635 nmatch, pmatch ) == LDAP_SUCCESS && 636 vei->type == VARIANT_INFO_REGEX ) { 637 rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch ); 638 if ( rc == LDAP_NO_SUCH_OBJECT || rc == LDAP_REFERRAL ) { 639 rc = SLAP_CB_CONTINUE; 640 } else if ( rc != LDAP_SUCCESS ) { 641 Debug( LDAP_DEBUG_TRACE, "variant_op_search: " 642 "failed to retrieve entry: dn=%s\n", 643 vei->dn.bv_val ); 644 goto done; 645 } else { 646 if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) { 647 Debug( LDAP_DEBUG_TRACE, "variant_op_search: " 648 "entry matched: dn=%s\n", 649 vei->dn.bv_val ); 650 rs->sr_entry = e; 651 rs->sr_attrs = op->ors_attrs; 652 rc = send_search_entry( op, rs ); 653 } 654 entry_free( e ); 655 e = NULL; 656 goto done; 657 } 658 } 659 rc = SLAP_CB_CONTINUE; 660 661 if ( variantInScope ) { 662 cb = ch_calloc( 1, sizeof(slap_callback) ); 663 cb->sc_private = on; 664 cb->sc_response = variant_search_response; 665 cb->sc_next = op->o_callback; 666 667 op->o_callback = cb; 668 } 669 670 done: 671 if ( rc != SLAP_CB_CONTINUE ) { 672 rs->sr_err = (rc == LDAP_SUCCESS) ? rc : LDAP_OTHER; 673 send_ldap_result( op, rs ); 674 } 675 Debug( LDAP_DEBUG_TRACE, "variant_op_search: " 676 "finished with %d\n", rc ); 677 return rc; 678 } 679 680 /* Configuration */ 681 682 static ConfigLDAPadd variant_ldadd; 683 static ConfigLDAPadd variant_regex_ldadd; 684 static ConfigLDAPadd variant_attr_ldadd; 685 686 static ConfigDriver variant_set_dn; 687 static ConfigDriver variant_set_regex; 688 static ConfigDriver variant_set_alt_dn; 689 static ConfigDriver variant_set_alt_pattern; 690 static ConfigDriver variant_set_attribute; 691 static ConfigDriver variant_add_alt_attr; 692 static ConfigDriver variant_add_alt_attr_regex; 693 694 static ConfigCfAdd variant_cfadd; 695 696 enum 697 { 698 VARIANT_ATTR = 1, 699 VARIANT_ATTR_ALT, 700 701 VARIANT_LAST, 702 }; 703 704 static ConfigTable variant_cfg[] = { 705 { "passReplication", "on|off", 2, 2, 0, 706 ARG_ON_OFF|ARG_OFFSET, 707 (void *)offsetof( variant_info_t, passReplication ), 708 "( OLcfgOvAt:FIXME.1 NAME 'olcVariantPassReplication' " 709 "DESC 'Whether to let searches with replication control " 710 "pass unmodified' " 711 "SYNTAX OMsBoolean " 712 "SINGLE-VALUE )", 713 NULL, NULL 714 }, 715 { "variantDN", "dn", 2, 2, 0, 716 ARG_DN|ARG_QUOTE|ARG_MAGIC, 717 variant_set_dn, 718 "( OLcfgOvAt:FIXME.2 NAME 'olcVariantEntry' " 719 "DESC 'DN of the variant entry' " 720 "EQUALITY distinguishedNameMatch " 721 "SYNTAX OMsDN " 722 "SINGLE-VALUE )", 723 NULL, NULL 724 }, 725 { "variantRegex", "regex", 2, 2, 0, 726 ARG_BERVAL|ARG_QUOTE|ARG_MAGIC, 727 variant_set_regex, 728 "( OLcfgOvAt:FIXME.6 NAME 'olcVariantEntryRegex' " 729 "DESC 'Pattern for the variant entry' " 730 "EQUALITY caseExactMatch " 731 "SYNTAX OMsDirectoryString " 732 "SINGLE-VALUE )", 733 NULL, NULL 734 }, 735 /* These have no equivalent in slapd.conf */ 736 { "", NULL, 2, 2, 0, 737 ARG_STRING|ARG_MAGIC|VARIANT_ATTR, 738 variant_set_attribute, 739 "( OLcfgOvAt:FIXME.3 NAME 'olcVariantVariantAttribute' " 740 "DESC 'Attribute to fill in the entry' " 741 "EQUALITY caseIgnoreMatch " 742 "SYNTAX OMsDirectoryString " 743 "SINGLE-VALUE )", 744 NULL, NULL 745 }, 746 { "", NULL, 2, 2, 0, 747 ARG_STRING|ARG_MAGIC|VARIANT_ATTR_ALT, 748 variant_set_attribute, 749 "( OLcfgOvAt:FIXME.4 NAME 'olcVariantAlternativeAttribute' " 750 "DESC 'Attribute to take from the alternative entry' " 751 "EQUALITY caseIgnoreMatch " 752 "SYNTAX OMsDirectoryString " 753 "SINGLE-VALUE )", 754 NULL, NULL 755 }, 756 { "", NULL, 2, 2, 0, 757 ARG_DN|ARG_QUOTE|ARG_MAGIC, 758 variant_set_alt_dn, 759 "( OLcfgOvAt:FIXME.5 NAME 'olcVariantAlternativeEntry' " 760 "DESC 'DN of the alternative entry' " 761 "EQUALITY distinguishedNameMatch " 762 "SYNTAX OMsDN " 763 "SINGLE-VALUE )", 764 NULL, NULL 765 }, 766 { "", NULL, 2, 2, 0, 767 ARG_BERVAL|ARG_QUOTE|ARG_MAGIC, 768 variant_set_alt_pattern, 769 "( OLcfgOvAt:FIXME.7 NAME 'olcVariantAlternativeEntryPattern' " 770 "DESC 'Replacement pattern to locate the alternative entry' " 771 "EQUALITY caseExactMatch " 772 "SYNTAX OMsDirectoryString " 773 "SINGLE-VALUE )", 774 NULL, NULL 775 }, 776 /* slapd.conf alternatives for the four above */ 777 { "variantSpec", "attr attr2 dn", 4, 4, 0, 778 ARG_QUOTE|ARG_MAGIC, 779 variant_add_alt_attr, 780 NULL, NULL, NULL 781 }, 782 { "variantRegexSpec", "attr attr2 pattern", 4, 4, 0, 783 ARG_QUOTE|ARG_MAGIC, 784 variant_add_alt_attr_regex, 785 NULL, NULL, NULL 786 }, 787 788 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 789 }; 790 791 static ConfigOCs variant_ocs[] = { 792 { "( OLcfgOvOc:FIXME.1 " 793 "NAME 'olcVariantConfig' " 794 "DESC 'Variant overlay configuration' " 795 "SUP olcOverlayConfig " 796 "MAY ( olcVariantPassReplication ) )", 797 Cft_Overlay, variant_cfg, NULL, variant_cfadd }, 798 { "( OLcfgOvOc:FIXME.2 " 799 "NAME 'olcVariantVariant' " 800 "DESC 'Variant configuration' " 801 "MUST ( olcVariantEntry ) " 802 "MAY ( name ) " 803 "SUP top " 804 "STRUCTURAL )", 805 Cft_Misc, variant_cfg, variant_ldadd }, 806 { "( OLcfgOvOc:FIXME.3 " 807 "NAME 'olcVariantAttribute' " 808 "DESC 'Variant attribute description' " 809 "MUST ( olcVariantVariantAttribute $ " 810 "olcVariantAlternativeAttribute $ " 811 "olcVariantAlternativeEntry " 812 ") " 813 "MAY name " 814 "SUP top " 815 "STRUCTURAL )", 816 Cft_Misc, variant_cfg, variant_attr_ldadd }, 817 { "( OLcfgOvOc:FIXME.4 " 818 "NAME 'olcVariantRegex' " 819 "DESC 'Variant configuration' " 820 "MUST ( olcVariantEntryRegex ) " 821 "MAY ( name ) " 822 "SUP top " 823 "STRUCTURAL )", 824 Cft_Misc, variant_cfg, variant_regex_ldadd }, 825 { "( OLcfgOvOc:FIXME.5 " 826 "NAME 'olcVariantAttributePattern' " 827 "DESC 'Variant attribute description' " 828 "MUST ( olcVariantVariantAttribute $ " 829 "olcVariantAlternativeAttribute $ " 830 "olcVariantAlternativeEntryPattern " 831 ") " 832 "MAY name " 833 "SUP top " 834 "STRUCTURAL )", 835 Cft_Misc, variant_cfg, variant_attr_ldadd }, 836 837 { NULL, 0, NULL } 838 }; 839 840 static int 841 variant_set_dn( ConfigArgs *ca ) 842 { 843 variantEntry_info *vei2, *vei = ca->ca_private; 844 slap_overinst *on = (slap_overinst *)ca->bi; 845 variant_info_t *ov = on->on_bi.bi_private; 846 int diff; 847 848 if ( ca->op == SLAP_CONFIG_EMIT ) { 849 value_add_one( &ca->rvalue_vals, &vei->dn ); 850 return LDAP_SUCCESS; 851 } else if ( ca->op == LDAP_MOD_DELETE ) { 852 ber_memfree( vei->dn.bv_val ); 853 BER_BVZERO( &vei->dn ); 854 return LDAP_SUCCESS; 855 } 856 857 if ( !vei ) { 858 vei = ch_calloc( 1, sizeof(variantEntry_info) ); 859 vei->ov = ov; 860 vei->type = VARIANT_INFO_PLAIN; 861 LDAP_SLIST_INIT(&vei->attributes); 862 LDAP_STAILQ_ENTRY_INIT(vei, next); 863 LDAP_STAILQ_INSERT_TAIL(&ov->variants, vei, next); 864 865 ca->ca_private = vei; 866 } 867 vei->dn = ca->value_ndn; 868 ber_memfree( ca->value_dn.bv_val ); 869 870 /* Each DN should only be listed once */ 871 LDAP_STAILQ_FOREACH( vei2, &vei->ov->variants, next ) { 872 if ( vei == vei2 ) continue; 873 874 dnMatch( &diff, 0, NULL, NULL, &vei->dn, &vei2->dn ); 875 if ( !diff ) { 876 ca->reply.err = LDAP_CONSTRAINT_VIOLATION; 877 return ca->reply.err; 878 } 879 } 880 881 return LDAP_SUCCESS; 882 } 883 884 static int 885 variant_set_regex( ConfigArgs *ca ) 886 { 887 variantEntry_info *vei2, *vei = ca->ca_private; 888 slap_overinst *on = (slap_overinst *)ca->bi; 889 variant_info_t *ov = on->on_bi.bi_private; 890 891 if ( ca->op == SLAP_CONFIG_EMIT ) { 892 ca->value_bv = vei->dn; 893 return LDAP_SUCCESS; 894 } else if ( ca->op == LDAP_MOD_DELETE ) { 895 ber_memfree( vei->dn.bv_val ); 896 BER_BVZERO( &vei->dn ); 897 regfree( vei->regex ); 898 return LDAP_SUCCESS; 899 } 900 901 if ( !vei ) { 902 vei = ch_calloc( 1, sizeof(variantEntry_info) ); 903 vei->ov = ov; 904 vei->type = VARIANT_INFO_REGEX; 905 LDAP_SLIST_INIT(&vei->attributes); 906 LDAP_STAILQ_ENTRY_INIT(vei, next); 907 LDAP_STAILQ_INSERT_TAIL(&ov->regex_variants, vei, next); 908 909 ca->ca_private = vei; 910 } 911 vei->dn = ca->value_bv; 912 913 /* Each regex should only be listed once */ 914 LDAP_STAILQ_FOREACH( vei2, &vei->ov->regex_variants, next ) { 915 if ( vei == vei2 ) continue; 916 917 if ( !ber_bvcmp( &ca->value_bv, &vei2->dn ) ) { 918 ch_free( vei ); 919 ca->ca_private = NULL; 920 ca->reply.err = LDAP_CONSTRAINT_VIOLATION; 921 return ca->reply.err; 922 } 923 } 924 925 vei->regex = ch_calloc( 1, sizeof(regex_t) ); 926 if ( regcomp( vei->regex, vei->dn.bv_val, REG_EXTENDED ) ) { 927 ch_free( vei->regex ); 928 ca->reply.err = LDAP_CONSTRAINT_VIOLATION; 929 return ca->reply.err; 930 } 931 932 return LDAP_SUCCESS; 933 } 934 935 static int 936 variant_set_alt_dn( ConfigArgs *ca ) 937 { 938 variantAttr_info *vai = ca->ca_private; 939 940 if ( ca->op == SLAP_CONFIG_EMIT ) { 941 value_add_one( &ca->rvalue_vals, &vai->dn ); 942 return LDAP_SUCCESS; 943 } else if ( ca->op == LDAP_MOD_DELETE ) { 944 ber_memfree( vai->dn.bv_val ); 945 BER_BVZERO( &vai->dn ); 946 return LDAP_SUCCESS; 947 } 948 949 vai->dn = ca->value_ndn; 950 ber_memfree( ca->value_dn.bv_val ); 951 952 return LDAP_SUCCESS; 953 } 954 955 static int 956 variant_set_alt_pattern( ConfigArgs *ca ) 957 { 958 variantAttr_info *vai = ca->ca_private; 959 char *p = ca->value_bv.bv_val, 960 *end = ca->value_bv.bv_val + ca->value_bv.bv_len; 961 962 if ( ca->op == SLAP_CONFIG_EMIT ) { 963 ca->value_bv = vai->dn; 964 return LDAP_SUCCESS; 965 } else if ( ca->op == LDAP_MOD_DELETE ) { 966 ber_memfree( vai->dn.bv_val ); 967 BER_BVZERO( &vai->dn ); 968 return LDAP_SUCCESS; 969 } 970 971 while ( (p = memchr( p, '$', end - p )) != NULL ) { 972 p += 1; 973 974 if ( ( ( *p >= '0' ) && ( *p <= '9' ) ) || ( *p == '$' ) ) { 975 p += 1; 976 } else { 977 Debug( LDAP_DEBUG_ANY, "variant_set_alt_pattern: " 978 "invalid replacement pattern supplied '%s'\n", 979 ca->value_bv.bv_val ); 980 ca->reply.err = LDAP_CONSTRAINT_VIOLATION; 981 return ca->reply.err; 982 } 983 } 984 985 vai->dn = ca->value_bv; 986 987 return LDAP_SUCCESS; 988 } 989 990 static int 991 variant_set_attribute( ConfigArgs *ca ) 992 { 993 variantAttr_info *vai2, *vai = ca->ca_private; 994 char *s = ca->value_string; 995 const char *text; 996 AttributeDescription **ad; 997 int rc; 998 999 if ( ca->type == VARIANT_ATTR ) { 1000 ad = &vai->attr; 1001 } else { 1002 ad = &vai->alternative; 1003 } 1004 1005 if ( ca->op == SLAP_CONFIG_EMIT ) { 1006 ca->value_string = ch_strdup( (*ad)->ad_cname.bv_val ); 1007 return LDAP_SUCCESS; 1008 } else if ( ca->op == LDAP_MOD_DELETE ) { 1009 *ad = NULL; 1010 return LDAP_SUCCESS; 1011 } 1012 1013 if ( *s == '{' ) { 1014 s = strchr( s, '}' ); 1015 if ( !s ) { 1016 ca->reply.err = LDAP_UNDEFINED_TYPE; 1017 return ca->reply.err; 1018 } 1019 s += 1; 1020 } 1021 1022 rc = slap_str2ad( s, ad, &text ); 1023 ber_memfree( ca->value_string ); 1024 if ( rc ) { 1025 return rc; 1026 } 1027 1028 /* Both attributes have to share the same syntax */ 1029 if ( vai->attr && vai->alternative && 1030 vai->attr->ad_type->sat_syntax != 1031 vai->alternative->ad_type->sat_syntax ) { 1032 ca->reply.err = LDAP_CONSTRAINT_VIOLATION; 1033 return ca->reply.err; 1034 } 1035 1036 if ( ca->type == VARIANT_ATTR ) { 1037 /* Each attribute should only be listed once */ 1038 LDAP_SLIST_FOREACH( vai2, &vai->variant->attributes, next ) { 1039 if ( vai == vai2 ) continue; 1040 if ( vai->attr == vai2->attr ) { 1041 ca->reply.err = LDAP_CONSTRAINT_VIOLATION; 1042 return ca->reply.err; 1043 } 1044 } 1045 } 1046 1047 return LDAP_SUCCESS; 1048 } 1049 1050 static int 1051 variant_add_alt_attr( ConfigArgs *ca ) 1052 { 1053 slap_overinst *on = (slap_overinst *)ca->bi; 1054 variant_info_t *ov = on->on_bi.bi_private; 1055 variantEntry_info *vei = 1056 LDAP_STAILQ_LAST( &ov->variants, variantEntry_info, next ); 1057 variantAttr_info *vai; 1058 struct berval dn, ndn; 1059 int rc; 1060 1061 vai = ch_calloc( 1, sizeof(variantAttr_info) ); 1062 vai->variant = vei; 1063 LDAP_SLIST_ENTRY_INIT( vai, next ); 1064 ca->ca_private = vai; 1065 1066 ca->value_string = ch_strdup( ca->argv[1] ); 1067 ca->type = VARIANT_ATTR; 1068 rc = variant_set_attribute( ca ); 1069 if ( rc != LDAP_SUCCESS ) { 1070 goto done; 1071 } 1072 1073 ca->value_string = ch_strdup( ca->argv[2] ); 1074 ca->type = VARIANT_ATTR_ALT; 1075 rc = variant_set_attribute( ca ); 1076 if ( rc != LDAP_SUCCESS ) { 1077 goto done; 1078 } 1079 1080 dn.bv_val = ca->argv[3]; 1081 dn.bv_len = strlen( dn.bv_val ); 1082 rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ); 1083 if ( rc != LDAP_SUCCESS ) { 1084 goto done; 1085 } 1086 1087 ca->type = 0; 1088 BER_BVZERO( &ca->value_dn ); 1089 ca->value_ndn = ndn; 1090 rc = variant_set_alt_dn( ca ); 1091 if ( rc != LDAP_SUCCESS ) { 1092 ch_free( ndn.bv_val ); 1093 goto done; 1094 } 1095 1096 done: 1097 if ( rc == LDAP_SUCCESS ) { 1098 LDAP_SLIST_INSERT_HEAD( &vei->attributes, vai, next ); 1099 } else { 1100 ca->reply.err = rc; 1101 } 1102 1103 return rc; 1104 } 1105 1106 static int 1107 variant_add_alt_attr_regex( ConfigArgs *ca ) 1108 { 1109 slap_overinst *on = (slap_overinst *)ca->bi; 1110 variant_info_t *ov = on->on_bi.bi_private; 1111 variantEntry_info *vei = 1112 LDAP_STAILQ_LAST( &ov->regex_variants, variantEntry_info, next ); 1113 variantAttr_info *vai; 1114 int rc; 1115 1116 vai = ch_calloc( 1, sizeof(variantAttr_info) ); 1117 vai->variant = vei; 1118 LDAP_SLIST_ENTRY_INIT( vai, next ); 1119 ca->ca_private = vai; 1120 1121 ca->value_string = ch_strdup( ca->argv[1] ); 1122 ca->type = VARIANT_ATTR; 1123 rc = variant_set_attribute( ca ); 1124 if ( rc != LDAP_SUCCESS ) { 1125 goto done; 1126 } 1127 1128 ca->value_string = ch_strdup( ca->argv[2] ); 1129 ca->type = VARIANT_ATTR_ALT; 1130 rc = variant_set_attribute( ca ); 1131 if ( rc != LDAP_SUCCESS ) { 1132 goto done; 1133 } 1134 1135 ca->type = 0; 1136 ber_str2bv( ca->argv[3], 0, 1, &ca->value_bv ); 1137 rc = variant_set_alt_pattern( ca ); 1138 if ( rc != LDAP_SUCCESS ) { 1139 goto done; 1140 } 1141 1142 done: 1143 if ( rc == LDAP_SUCCESS ) { 1144 LDAP_SLIST_INSERT_HEAD( &vei->attributes, vai, next ); 1145 } else { 1146 ca->reply.err = rc; 1147 } 1148 1149 return rc; 1150 } 1151 1152 static int 1153 variant_ldadd_cleanup( ConfigArgs *ca ) 1154 { 1155 variantEntry_info *vei = ca->ca_private; 1156 slap_overinst *on = (slap_overinst *)ca->bi; 1157 variant_info_t *ov = on->on_bi.bi_private; 1158 1159 if ( ca->reply.err != LDAP_SUCCESS ) { 1160 assert( LDAP_SLIST_EMPTY(&vei->attributes) ); 1161 ch_free( vei ); 1162 return LDAP_SUCCESS; 1163 } 1164 1165 if ( vei->type == VARIANT_INFO_PLAIN ) { 1166 LDAP_STAILQ_INSERT_TAIL(&ov->variants, vei, next); 1167 } else { 1168 LDAP_STAILQ_INSERT_TAIL(&ov->regex_variants, vei, next); 1169 } 1170 1171 return LDAP_SUCCESS; 1172 } 1173 1174 static int 1175 variant_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca ) 1176 { 1177 slap_overinst *on; 1178 variant_info_t *ov; 1179 variantEntry_info *vei; 1180 1181 if ( cei->ce_type != Cft_Overlay || !cei->ce_bi || 1182 cei->ce_bi->bi_cf_ocs != variant_ocs ) 1183 return LDAP_CONSTRAINT_VIOLATION; 1184 1185 on = (slap_overinst *)cei->ce_bi; 1186 ov = on->on_bi.bi_private; 1187 1188 vei = ch_calloc( 1, sizeof(variantEntry_info) ); 1189 vei->ov = ov; 1190 vei->type = VARIANT_INFO_PLAIN; 1191 LDAP_SLIST_INIT(&vei->attributes); 1192 LDAP_STAILQ_ENTRY_INIT(vei, next); 1193 1194 ca->bi = cei->ce_bi; 1195 ca->ca_private = vei; 1196 config_push_cleanup( ca, variant_ldadd_cleanup ); 1197 /* config_push_cleanup is only run in the case of online config but we use it to 1198 * save the new config when done with the entry */ 1199 ca->lineno = 0; 1200 1201 return LDAP_SUCCESS; 1202 } 1203 1204 static int 1205 variant_regex_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca ) 1206 { 1207 slap_overinst *on; 1208 variant_info_t *ov; 1209 variantEntry_info *vei; 1210 1211 if ( cei->ce_type != Cft_Overlay || !cei->ce_bi || 1212 cei->ce_bi->bi_cf_ocs != variant_ocs ) 1213 return LDAP_CONSTRAINT_VIOLATION; 1214 1215 on = (slap_overinst *)cei->ce_bi; 1216 ov = on->on_bi.bi_private; 1217 1218 vei = ch_calloc( 1, sizeof(variantEntry_info) ); 1219 vei->ov = ov; 1220 vei->type = VARIANT_INFO_REGEX; 1221 LDAP_SLIST_INIT(&vei->attributes); 1222 LDAP_STAILQ_ENTRY_INIT(vei, next); 1223 1224 ca->bi = cei->ce_bi; 1225 ca->ca_private = vei; 1226 config_push_cleanup( ca, variant_ldadd_cleanup ); 1227 /* config_push_cleanup is only run in the case of online config but we use it to 1228 * save the new config when done with the entry */ 1229 ca->lineno = 0; 1230 1231 return LDAP_SUCCESS; 1232 } 1233 1234 static int 1235 variant_attr_ldadd_cleanup( ConfigArgs *ca ) 1236 { 1237 variantAttr_info *vai = ca->ca_private; 1238 variantEntry_info *vei = vai->variant; 1239 1240 if ( ca->reply.err != LDAP_SUCCESS ) { 1241 ch_free( vai ); 1242 return LDAP_SUCCESS; 1243 } 1244 1245 LDAP_SLIST_INSERT_HEAD(&vei->attributes, vai, next); 1246 1247 return LDAP_SUCCESS; 1248 } 1249 1250 static int 1251 variant_attr_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca ) 1252 { 1253 variantEntry_info *vei; 1254 variantAttr_info *vai; 1255 CfEntryInfo *parent = cei->ce_parent; 1256 1257 if ( cei->ce_type != Cft_Misc || !parent || !parent->ce_bi || 1258 parent->ce_bi->bi_cf_ocs != variant_ocs ) 1259 return LDAP_CONSTRAINT_VIOLATION; 1260 1261 vei = (variantEntry_info *)cei->ce_private; 1262 1263 vai = ch_calloc( 1, sizeof(variantAttr_info) ); 1264 vai->variant = vei; 1265 LDAP_SLIST_ENTRY_INIT(vai, next); 1266 1267 ca->ca_private = vai; 1268 config_push_cleanup( ca, variant_attr_ldadd_cleanup ); 1269 /* config_push_cleanup is only run in the case of online config but we use it to 1270 * save the new config when done with the entry */ 1271 ca->lineno = 0; 1272 1273 return LDAP_SUCCESS; 1274 } 1275 1276 static int 1277 variant_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca ) 1278 { 1279 slap_overinst *on = (slap_overinst *)ca->bi; 1280 variant_info_t *ov = on->on_bi.bi_private; 1281 variantEntry_info *vei; 1282 variantAttr_info *vai; 1283 Entry *e; 1284 struct berval rdn; 1285 int i = 0; 1286 1287 LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) { 1288 int j = 0; 1289 rdn.bv_len = snprintf( 1290 ca->cr_msg, sizeof(ca->cr_msg), "name={%d}variant", i++ ); 1291 rdn.bv_val = ca->cr_msg; 1292 1293 ca->ca_private = vei; 1294 e = config_build_entry( 1295 op, rs, p->e_private, ca, &rdn, &variant_ocs[1], NULL ); 1296 assert( e ); 1297 1298 LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) { 1299 rdn.bv_len = snprintf( ca->cr_msg, sizeof(ca->cr_msg), 1300 "olcVariantVariantAttribute={%d}%s", j++, 1301 vai->attr->ad_cname.bv_val ); 1302 rdn.bv_val = ca->cr_msg; 1303 1304 ca->ca_private = vai; 1305 config_build_entry( 1306 op, rs, e->e_private, ca, &rdn, &variant_ocs[2], NULL ); 1307 } 1308 } 1309 1310 LDAP_STAILQ_FOREACH( vei, &ov->regex_variants, next ) { 1311 int j = 0; 1312 rdn.bv_len = snprintf( 1313 ca->cr_msg, sizeof(ca->cr_msg), "name={%d}regex", i++ ); 1314 rdn.bv_val = ca->cr_msg; 1315 1316 ca->ca_private = vei; 1317 e = config_build_entry( 1318 op, rs, p->e_private, ca, &rdn, &variant_ocs[3], NULL ); 1319 assert( e ); 1320 1321 LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) { 1322 rdn.bv_len = snprintf( ca->cr_msg, sizeof(ca->cr_msg), 1323 "olcVariantVariantAttribute={%d}%s", j++, 1324 vai->attr->ad_cname.bv_val ); 1325 rdn.bv_val = ca->cr_msg; 1326 1327 ca->ca_private = vai; 1328 config_build_entry( 1329 op, rs, e->e_private, ca, &rdn, &variant_ocs[4], NULL ); 1330 } 1331 } 1332 return LDAP_SUCCESS; 1333 } 1334 1335 static slap_overinst variant; 1336 1337 static int 1338 variant_db_init( BackendDB *be, ConfigReply *cr ) 1339 { 1340 slap_overinst *on = (slap_overinst *)be->bd_info; 1341 variant_info_t *ov; 1342 1343 if ( SLAP_ISGLOBALOVERLAY(be) ) { 1344 Debug( LDAP_DEBUG_ANY, "variant overlay must be instantiated within " 1345 "a database.\n" ); 1346 return 1; 1347 } 1348 1349 ov = ch_calloc( 1, sizeof(variant_info_t) ); 1350 LDAP_STAILQ_INIT(&ov->variants); 1351 LDAP_STAILQ_INIT(&ov->regex_variants); 1352 1353 on->on_bi.bi_private = ov; 1354 1355 return LDAP_SUCCESS; 1356 } 1357 1358 static int 1359 variant_db_destroy( BackendDB *be, ConfigReply *cr ) 1360 { 1361 slap_overinst *on = (slap_overinst *)be->bd_info; 1362 variant_info_t *ov = on->on_bi.bi_private; 1363 1364 if ( ov ) { 1365 while ( !LDAP_STAILQ_EMPTY( &ov->variants ) ) { 1366 variantEntry_info *vei = LDAP_STAILQ_FIRST( &ov->variants ); 1367 LDAP_STAILQ_REMOVE_HEAD( &ov->variants, next ); 1368 1369 while ( !LDAP_SLIST_EMPTY( &vei->attributes ) ) { 1370 variantAttr_info *vai = LDAP_SLIST_FIRST( &vei->attributes ); 1371 LDAP_SLIST_REMOVE_HEAD( &vei->attributes, next ); 1372 1373 ber_memfree( vai->dn.bv_val ); 1374 ch_free( vai ); 1375 } 1376 ber_memfree( vei->dn.bv_val ); 1377 ch_free( vei ); 1378 } 1379 while ( !LDAP_STAILQ_EMPTY( &ov->regex_variants ) ) { 1380 variantEntry_info *vei = LDAP_STAILQ_FIRST( &ov->regex_variants ); 1381 LDAP_STAILQ_REMOVE_HEAD( &ov->regex_variants, next ); 1382 1383 while ( !LDAP_SLIST_EMPTY( &vei->attributes ) ) { 1384 variantAttr_info *vai = LDAP_SLIST_FIRST( &vei->attributes ); 1385 LDAP_SLIST_REMOVE_HEAD( &vei->attributes, next ); 1386 1387 ber_memfree( vai->dn.bv_val ); 1388 ch_free( vai ); 1389 } 1390 ber_memfree( vei->dn.bv_val ); 1391 ch_free( vei ); 1392 } 1393 ch_free( ov ); 1394 } 1395 1396 return LDAP_SUCCESS; 1397 } 1398 1399 int 1400 variant_initialize() 1401 { 1402 int rc; 1403 1404 variant.on_bi.bi_type = "variant"; 1405 variant.on_bi.bi_db_init = variant_db_init; 1406 variant.on_bi.bi_db_destroy = variant_db_destroy; 1407 1408 variant.on_bi.bi_op_add = variant_op_add; 1409 variant.on_bi.bi_op_compare = variant_op_compare; 1410 variant.on_bi.bi_op_modify = variant_op_mod; 1411 variant.on_bi.bi_op_search = variant_op_search; 1412 1413 variant.on_bi.bi_cf_ocs = variant_ocs; 1414 1415 rc = config_register_schema( variant_cfg, variant_ocs ); 1416 if ( rc ) return rc; 1417 1418 return overlay_register( &variant ); 1419 } 1420 1421 #if SLAPD_OVER_VARIANT == SLAPD_MOD_DYNAMIC 1422 int 1423 init_module( int argc, char *argv[] ) 1424 { 1425 return variant_initialize(); 1426 } 1427 #endif 1428 1429 #endif /* SLAPD_OVER_VARIANT */ 1430