1 /* $NetBSD: dynlist.c,v 1.1.1.8 2019/08/08 13:31:41 christos Exp $ */ 2 3 /* dynlist.c - dynamic list overlay */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2003-2019 The OpenLDAP Foundation. 8 * Portions Copyright 2004-2005 Pierangelo Masarati. 9 * Portions Copyright 2008 Emmanuel Dreyfus. 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted only as authorized by the OpenLDAP 14 * Public License. 15 * 16 * A copy of this license is available in the file LICENSE in the 17 * top-level directory of the distribution or, alternatively, at 18 * <http://www.OpenLDAP.org/license.html>. 19 */ 20 /* ACKNOWLEDGEMENTS: 21 * This work was initially developed by Pierangelo Masarati 22 * for SysNet s.n.c., for inclusion in OpenLDAP Software. 23 */ 24 25 #include <sys/cdefs.h> 26 __RCSID("$NetBSD: dynlist.c,v 1.1.1.8 2019/08/08 13:31:41 christos Exp $"); 27 28 #include "portable.h" 29 30 #ifdef SLAPD_OVER_DYNLIST 31 32 #if SLAPD_OVER_DYNGROUP != SLAPD_MOD_STATIC 33 #define TAKEOVER_DYNGROUP 34 #endif 35 36 #include <stdio.h> 37 38 #include <ac/string.h> 39 40 #include "slap.h" 41 #include "config.h" 42 #include "lutil.h" 43 44 static AttributeDescription *ad_dgIdentity, *ad_dgAuthz; 45 46 typedef struct dynlist_map_t { 47 AttributeDescription *dlm_member_ad; 48 AttributeDescription *dlm_mapped_ad; 49 struct dynlist_map_t *dlm_next; 50 } dynlist_map_t; 51 52 typedef struct dynlist_info_t { 53 ObjectClass *dli_oc; 54 AttributeDescription *dli_ad; 55 struct dynlist_map_t *dli_dlm; 56 struct berval dli_uri; 57 LDAPURLDesc *dli_lud; 58 struct berval dli_uri_nbase; 59 Filter *dli_uri_filter; 60 struct berval dli_default_filter; 61 struct dynlist_info_t *dli_next; 62 } dynlist_info_t; 63 64 #define DYNLIST_USAGE \ 65 "\"dynlist-attrset <oc> [uri] <URL-ad> [[<mapped-ad>:]<member-ad> ...]\": " 66 67 static dynlist_info_t * 68 dynlist_is_dynlist_next( Operation *op, SlapReply *rs, dynlist_info_t *old_dli ) 69 { 70 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 71 dynlist_info_t *dli; 72 73 Attribute *a; 74 75 if ( old_dli == NULL ) { 76 dli = (dynlist_info_t *)on->on_bi.bi_private; 77 78 } else { 79 dli = old_dli->dli_next; 80 } 81 82 a = attrs_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass ); 83 if ( a == NULL ) { 84 /* FIXME: objectClass must be present; for non-storage 85 * backends, like back-ldap, it needs to be added 86 * to the requested attributes */ 87 return NULL; 88 } 89 90 for ( ; dli; dli = dli->dli_next ) { 91 if ( dli->dli_lud != NULL ) { 92 /* check base and scope */ 93 if ( !BER_BVISNULL( &dli->dli_uri_nbase ) 94 && !dnIsSuffixScope( &rs->sr_entry->e_nname, 95 &dli->dli_uri_nbase, 96 dli->dli_lud->lud_scope ) ) 97 { 98 continue; 99 } 100 101 /* check filter */ 102 if ( dli->dli_uri_filter && test_filter( op, rs->sr_entry, dli->dli_uri_filter ) != LDAP_COMPARE_TRUE ) { 103 continue; 104 } 105 } 106 107 if ( attr_valfind( a, 108 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 109 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, 110 &dli->dli_oc->soc_cname, NULL, 111 op->o_tmpmemctx ) == 0 ) 112 { 113 return dli; 114 } 115 } 116 117 return NULL; 118 } 119 120 static int 121 dynlist_make_filter( Operation *op, Entry *e, const char *url, struct berval *oldf, struct berval *newf ) 122 { 123 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 124 dynlist_info_t *dli = (dynlist_info_t *)on->on_bi.bi_private; 125 126 char *ptr; 127 int needBrackets = 0; 128 129 assert( oldf != NULL ); 130 assert( newf != NULL ); 131 assert( !BER_BVISNULL( oldf ) ); 132 assert( !BER_BVISEMPTY( oldf ) ); 133 134 if ( oldf->bv_val[0] != '(' ) { 135 Debug( LDAP_DEBUG_ANY, "%s: dynlist, DN=\"%s\": missing brackets in URI=\"%s\" filter\n", 136 op->o_log_prefix, e->e_name.bv_val, url ); 137 needBrackets = 2; 138 } 139 140 newf->bv_len = STRLENOF( "(&(!(objectClass=" "))" ")" ) 141 + dli->dli_oc->soc_cname.bv_len + oldf->bv_len + needBrackets; 142 newf->bv_val = op->o_tmpalloc( newf->bv_len + 1, op->o_tmpmemctx ); 143 if ( newf->bv_val == NULL ) { 144 return -1; 145 } 146 ptr = lutil_strcopy( newf->bv_val, "(&(!(objectClass=" ); 147 ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val ); 148 ptr = lutil_strcopy( ptr, "))" ); 149 if ( needBrackets ) *ptr++ = '('; 150 ptr = lutil_strcopy( ptr, oldf->bv_val ); 151 if ( needBrackets ) *ptr++ = ')'; 152 ptr = lutil_strcopy( ptr, ")" ); 153 newf->bv_len = ptr - newf->bv_val; 154 155 return 0; 156 } 157 158 /* dynlist_sc_update() callback info set by dynlist_prepare_entry() */ 159 typedef struct dynlist_sc_t { 160 dynlist_info_t *dlc_dli; 161 Entry *dlc_e; 162 } dynlist_sc_t; 163 164 static int 165 dynlist_sc_update( Operation *op, SlapReply *rs ) 166 { 167 Entry *e; 168 Attribute *a; 169 int opattrs, 170 userattrs; 171 AccessControlState acl_state = ACL_STATE_INIT; 172 173 dynlist_sc_t *dlc; 174 dynlist_map_t *dlm; 175 176 if ( rs->sr_type != REP_SEARCH ) { 177 return 0; 178 } 179 180 dlc = (dynlist_sc_t *)op->o_callback->sc_private; 181 e = dlc->dlc_e; 182 183 assert( e != NULL ); 184 assert( rs->sr_entry != NULL ); 185 186 /* test access to entry */ 187 if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry, 188 NULL, ACL_READ, NULL ) ) 189 { 190 goto done; 191 } 192 193 /* if there is only one member_ad, and it's not mapped, 194 * consider it as old-style member listing */ 195 dlm = dlc->dlc_dli->dli_dlm; 196 if ( dlm && dlm->dlm_mapped_ad == NULL && dlm->dlm_next == NULL ) { 197 /* if access allowed, try to add values, emulating permissive 198 * control to silently ignore duplicates */ 199 if ( access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry, 200 NULL, ACL_READ, NULL ) ) 201 { 202 Modification mod; 203 const char *text = NULL; 204 char textbuf[1024]; 205 struct berval vals[ 2 ], nvals[ 2 ]; 206 207 vals[ 0 ] = rs->sr_entry->e_name; 208 BER_BVZERO( &vals[ 1 ] ); 209 nvals[ 0 ] = rs->sr_entry->e_nname; 210 BER_BVZERO( &nvals[ 1 ] ); 211 212 mod.sm_op = LDAP_MOD_ADD; 213 mod.sm_desc = dlm->dlm_member_ad; 214 mod.sm_type = dlm->dlm_member_ad->ad_cname; 215 mod.sm_values = vals; 216 mod.sm_nvalues = nvals; 217 mod.sm_numvals = 1; 218 219 (void)modify_add_values( e, &mod, /* permissive */ 1, 220 &text, textbuf, sizeof( textbuf ) ); 221 } 222 223 goto done; 224 } 225 226 opattrs = SLAP_OPATTRS( rs->sr_attr_flags ); 227 userattrs = SLAP_USERATTRS( rs->sr_attr_flags ); 228 229 for ( a = rs->sr_entry->e_attrs; a != NULL; a = a->a_next ) { 230 BerVarray vals, nvals = NULL; 231 int i, j, 232 is_oc = a->a_desc == slap_schema.si_ad_objectClass; 233 234 /* if attribute is not requested, skip it */ 235 if ( rs->sr_attrs == NULL ) { 236 if ( is_at_operational( a->a_desc->ad_type ) ) { 237 continue; 238 } 239 240 } else { 241 if ( is_at_operational( a->a_desc->ad_type ) ) { 242 if ( !opattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) ) 243 { 244 continue; 245 } 246 247 } else { 248 if ( !userattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) ) 249 { 250 continue; 251 } 252 } 253 } 254 255 /* test access to attribute */ 256 if ( op->ors_attrsonly ) { 257 if ( !access_allowed( op, rs->sr_entry, a->a_desc, NULL, 258 ACL_READ, &acl_state ) ) 259 { 260 continue; 261 } 262 } 263 264 /* single-value check: keep first only */ 265 if ( is_at_single_value( a->a_desc->ad_type ) ) { 266 if ( attr_find( e->e_attrs, a->a_desc ) != NULL ) { 267 continue; 268 } 269 } 270 271 /* test access to attribute */ 272 i = a->a_numvals; 273 274 vals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx ); 275 if ( a->a_nvals != a->a_vals ) { 276 nvals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx ); 277 } 278 279 for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) { 280 if ( is_oc ) { 281 ObjectClass *soc = oc_bvfind( &a->a_vals[i] ); 282 283 if ( soc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) { 284 continue; 285 } 286 } 287 288 if ( access_allowed( op, rs->sr_entry, a->a_desc, 289 &a->a_nvals[i], ACL_READ, &acl_state ) ) 290 { 291 vals[j] = a->a_vals[i]; 292 if ( nvals ) { 293 nvals[j] = a->a_nvals[i]; 294 } 295 j++; 296 } 297 } 298 299 /* if access allowed, try to add values, emulating permissive 300 * control to silently ignore duplicates */ 301 if ( j != 0 ) { 302 Modification mod; 303 const char *text = NULL; 304 char textbuf[1024]; 305 dynlist_map_t *dlm; 306 AttributeDescription *ad; 307 308 BER_BVZERO( &vals[j] ); 309 if ( nvals ) { 310 BER_BVZERO( &nvals[j] ); 311 } 312 313 ad = a->a_desc; 314 for ( dlm = dlc->dlc_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 315 if ( dlm->dlm_member_ad == a->a_desc ) { 316 if ( dlm->dlm_mapped_ad ) { 317 ad = dlm->dlm_mapped_ad; 318 } 319 break; 320 } 321 } 322 323 mod.sm_op = LDAP_MOD_ADD; 324 mod.sm_desc = ad; 325 mod.sm_type = ad->ad_cname; 326 mod.sm_values = vals; 327 mod.sm_nvalues = nvals; 328 mod.sm_numvals = j; 329 330 (void)modify_add_values( e, &mod, /* permissive */ 1, 331 &text, textbuf, sizeof( textbuf ) ); 332 } 333 334 op->o_tmpfree( vals, op->o_tmpmemctx ); 335 if ( nvals ) { 336 op->o_tmpfree( nvals, op->o_tmpmemctx ); 337 } 338 } 339 340 done:; 341 if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) { 342 entry_free( rs->sr_entry ); 343 rs->sr_entry = NULL; 344 rs->sr_flags &= ~REP_ENTRY_MASK; 345 } 346 347 return 0; 348 } 349 350 static int 351 dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli ) 352 { 353 Attribute *a, *id = NULL; 354 slap_callback cb = { 0 }; 355 Operation o = *op; 356 struct berval *url; 357 Entry *e; 358 int opattrs, 359 userattrs; 360 dynlist_sc_t dlc = { 0 }; 361 dynlist_map_t *dlm; 362 363 a = attrs_find( rs->sr_entry->e_attrs, dli->dli_ad ); 364 if ( a == NULL ) { 365 /* FIXME: error? */ 366 return SLAP_CB_CONTINUE; 367 } 368 369 opattrs = SLAP_OPATTRS( rs->sr_attr_flags ); 370 userattrs = SLAP_USERATTRS( rs->sr_attr_flags ); 371 372 /* Don't generate member list if it wasn't requested */ 373 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 374 AttributeDescription *ad = dlm->dlm_mapped_ad ? dlm->dlm_mapped_ad : dlm->dlm_member_ad; 375 if ( userattrs || ad_inlist( ad, rs->sr_attrs ) ) 376 break; 377 } 378 if ( dli->dli_dlm && !dlm ) 379 return SLAP_CB_CONTINUE; 380 381 if ( ad_dgIdentity && ( id = attrs_find( rs->sr_entry->e_attrs, ad_dgIdentity ))) { 382 Attribute *authz = NULL; 383 384 /* if not rootdn and dgAuthz is present, 385 * check if user can be authorized as dgIdentity */ 386 if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op ) 387 && ( authz = attrs_find( rs->sr_entry->e_attrs, ad_dgAuthz ) ) ) 388 { 389 if ( slap_sasl_matches( op, authz->a_nvals, 390 &o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS ) 391 { 392 return SLAP_CB_CONTINUE; 393 } 394 } 395 396 o.o_dn = id->a_vals[0]; 397 o.o_ndn = id->a_nvals[0]; 398 o.o_groups = NULL; 399 } 400 401 e = rs->sr_entry; 402 /* ensure e is modifiable, but do not replace 403 * sr_entry yet since we have pointers into it */ 404 if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) { 405 e = entry_dup( rs->sr_entry ); 406 } 407 408 dlc.dlc_e = e; 409 dlc.dlc_dli = dli; 410 cb.sc_private = &dlc; 411 cb.sc_response = dynlist_sc_update; 412 413 o.o_callback = &cb; 414 o.ors_deref = LDAP_DEREF_NEVER; 415 o.ors_limit = NULL; 416 o.ors_tlimit = SLAP_NO_LIMIT; 417 o.ors_slimit = SLAP_NO_LIMIT; 418 419 for ( url = a->a_nvals; !BER_BVISNULL( url ); url++ ) { 420 LDAPURLDesc *lud = NULL; 421 int i, j; 422 struct berval dn; 423 int rc; 424 425 BER_BVZERO( &o.o_req_dn ); 426 BER_BVZERO( &o.o_req_ndn ); 427 o.ors_filter = NULL; 428 o.ors_attrs = NULL; 429 BER_BVZERO( &o.ors_filterstr ); 430 431 if ( ldap_url_parse( url->bv_val, &lud ) != LDAP_URL_SUCCESS ) { 432 /* FIXME: error? */ 433 continue; 434 } 435 436 if ( lud->lud_host != NULL ) { 437 /* FIXME: host not allowed; reject as illegal? */ 438 Debug( LDAP_DEBUG_ANY, "dynlist_prepare_entry(\"%s\"): " 439 "illegal URI \"%s\"\n", 440 e->e_name.bv_val, url->bv_val, 0 ); 441 goto cleanup; 442 } 443 444 if ( lud->lud_dn == NULL ) { 445 /* note that an empty base is not honored in terms 446 * of defaultSearchBase, because select_backend() 447 * is not aware of the defaultSearchBase option; 448 * this can be useful in case of a database serving 449 * the empty suffix */ 450 BER_BVSTR( &dn, "" ); 451 452 } else { 453 ber_str2bv( lud->lud_dn, 0, 0, &dn ); 454 } 455 rc = dnPrettyNormal( NULL, &dn, &o.o_req_dn, &o.o_req_ndn, op->o_tmpmemctx ); 456 if ( rc != LDAP_SUCCESS ) { 457 /* FIXME: error? */ 458 goto cleanup; 459 } 460 o.ors_scope = lud->lud_scope; 461 462 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 463 if ( dlm->dlm_mapped_ad != NULL ) { 464 break; 465 } 466 } 467 468 if ( dli->dli_dlm && !dlm ) { 469 /* if ( lud->lud_attrs != NULL ), 470 * the URL should be ignored */ 471 o.ors_attrs = slap_anlist_no_attrs; 472 473 } else if ( lud->lud_attrs == NULL ) { 474 o.ors_attrs = rs->sr_attrs; 475 476 } else { 477 for ( i = 0; lud->lud_attrs[i]; i++) 478 /* just count */ ; 479 480 o.ors_attrs = op->o_tmpcalloc( i + 1, sizeof( AttributeName ), op->o_tmpmemctx ); 481 for ( i = 0, j = 0; lud->lud_attrs[i]; i++) { 482 const char *text = NULL; 483 484 ber_str2bv( lud->lud_attrs[i], 0, 0, &o.ors_attrs[j].an_name ); 485 o.ors_attrs[j].an_desc = NULL; 486 (void)slap_bv2ad( &o.ors_attrs[j].an_name, &o.ors_attrs[j].an_desc, &text ); 487 /* FIXME: ignore errors... */ 488 489 if ( rs->sr_attrs == NULL ) { 490 if ( o.ors_attrs[j].an_desc != NULL && 491 is_at_operational( o.ors_attrs[j].an_desc->ad_type ) ) 492 { 493 continue; 494 } 495 496 } else { 497 if ( o.ors_attrs[j].an_desc != NULL && 498 is_at_operational( o.ors_attrs[j].an_desc->ad_type ) ) 499 { 500 if ( !opattrs ) { 501 continue; 502 } 503 504 if ( !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) ) { 505 /* lookup if mapped -- linear search, 506 * not very efficient unless list 507 * is very short */ 508 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 509 if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) { 510 break; 511 } 512 } 513 514 if ( dlm == NULL ) { 515 continue; 516 } 517 } 518 519 } else { 520 if ( !userattrs && 521 o.ors_attrs[j].an_desc != NULL && 522 !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) ) 523 { 524 /* lookup if mapped -- linear search, 525 * not very efficient unless list 526 * is very short */ 527 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 528 if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) { 529 break; 530 } 531 } 532 533 if ( dlm == NULL ) { 534 continue; 535 } 536 } 537 } 538 } 539 540 j++; 541 } 542 543 if ( j == 0 ) { 544 goto cleanup; 545 } 546 547 BER_BVZERO( &o.ors_attrs[j].an_name ); 548 } 549 550 if ( lud->lud_filter == NULL ) { 551 ber_dupbv_x( &o.ors_filterstr, 552 &dli->dli_default_filter, op->o_tmpmemctx ); 553 554 } else { 555 struct berval flt; 556 ber_str2bv( lud->lud_filter, 0, 0, &flt ); 557 if ( dynlist_make_filter( op, rs->sr_entry, url->bv_val, &flt, &o.ors_filterstr ) ) { 558 /* error */ 559 goto cleanup; 560 } 561 } 562 o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val ); 563 if ( o.ors_filter == NULL ) { 564 goto cleanup; 565 } 566 567 o.o_bd = select_backend( &o.o_req_ndn, 1 ); 568 if ( o.o_bd && o.o_bd->be_search ) { 569 SlapReply r = { REP_SEARCH }; 570 r.sr_attr_flags = slap_attr_flags( o.ors_attrs ); 571 (void)o.o_bd->be_search( &o, &r ); 572 } 573 574 cleanup:; 575 if ( id ) { 576 slap_op_groups_free( &o ); 577 } 578 if ( o.ors_filter ) { 579 filter_free_x( &o, o.ors_filter, 1 ); 580 } 581 if ( o.ors_attrs && o.ors_attrs != rs->sr_attrs 582 && o.ors_attrs != slap_anlist_no_attrs ) 583 { 584 op->o_tmpfree( o.ors_attrs, op->o_tmpmemctx ); 585 } 586 if ( !BER_BVISNULL( &o.o_req_dn ) ) { 587 op->o_tmpfree( o.o_req_dn.bv_val, op->o_tmpmemctx ); 588 } 589 if ( !BER_BVISNULL( &o.o_req_ndn ) ) { 590 op->o_tmpfree( o.o_req_ndn.bv_val, op->o_tmpmemctx ); 591 } 592 assert( BER_BVISNULL( &o.ors_filterstr ) 593 || o.ors_filterstr.bv_val != lud->lud_filter ); 594 op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx ); 595 ldap_free_urldesc( lud ); 596 } 597 598 if ( e != rs->sr_entry ) { 599 rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e ); 600 rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED; 601 } 602 603 return SLAP_CB_CONTINUE; 604 } 605 606 /* dynlist_sc_compare_entry() callback set by dynlist_compare() */ 607 typedef struct dynlist_cc_t { 608 slap_callback dc_cb; 609 # define dc_ava dc_cb.sc_private /* attr:val to compare with */ 610 int *dc_res; 611 } dynlist_cc_t; 612 613 static int 614 dynlist_sc_compare_entry( Operation *op, SlapReply *rs ) 615 { 616 if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) { 617 dynlist_cc_t *dc = (dynlist_cc_t *)op->o_callback; 618 AttributeAssertion *ava = dc->dc_ava; 619 Attribute *a = attrs_find( rs->sr_entry->e_attrs, ava->aa_desc ); 620 621 if ( a != NULL ) { 622 while ( LDAP_SUCCESS != attr_valfind( a, 623 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 624 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, 625 &ava->aa_value, NULL, op->o_tmpmemctx ) 626 && (a = attrs_find( a->a_next, ava->aa_desc )) != NULL ) 627 ; 628 *dc->dc_res = a ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE; 629 } 630 } 631 632 return 0; 633 } 634 635 static int 636 dynlist_compare( Operation *op, SlapReply *rs ) 637 { 638 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 639 dynlist_info_t *dli = (dynlist_info_t *)on->on_bi.bi_private; 640 Operation o = *op; 641 Entry *e = NULL; 642 dynlist_map_t *dlm; 643 BackendDB *be; 644 645 for ( ; dli != NULL; dli = dli->dli_next ) { 646 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) 647 if ( op->oq_compare.rs_ava->aa_desc == dlm->dlm_member_ad ) 648 break; 649 650 if ( dlm ) { 651 /* This compare is for one of the attributes we're 652 * interested in. We'll use slapd's existing dyngroup 653 * evaluator to get the answer we want. 654 */ 655 BerVarray id = NULL, authz = NULL; 656 657 o.o_do_not_cache = 1; 658 659 if ( ad_dgIdentity && backend_attribute( &o, NULL, &o.o_req_ndn, 660 ad_dgIdentity, &id, ACL_READ ) == LDAP_SUCCESS ) 661 { 662 /* if not rootdn and dgAuthz is present, 663 * check if user can be authorized as dgIdentity */ 664 if ( ad_dgAuthz && !BER_BVISEMPTY( id ) && !be_isroot( op ) 665 && backend_attribute( &o, NULL, &o.o_req_ndn, 666 ad_dgAuthz, &authz, ACL_READ ) == LDAP_SUCCESS ) 667 { 668 669 rs->sr_err = slap_sasl_matches( op, authz, 670 &o.o_ndn, &o.o_ndn ); 671 ber_bvarray_free_x( authz, op->o_tmpmemctx ); 672 if ( rs->sr_err != LDAP_SUCCESS ) { 673 goto done; 674 } 675 } 676 677 o.o_dn = *id; 678 o.o_ndn = *id; 679 o.o_groups = NULL; /* authz changed, invalidate cached groups */ 680 } 681 682 rs->sr_err = backend_group( &o, NULL, &o.o_req_ndn, 683 &o.oq_compare.rs_ava->aa_value, dli->dli_oc, dli->dli_ad ); 684 switch ( rs->sr_err ) { 685 case LDAP_SUCCESS: 686 rs->sr_err = LDAP_COMPARE_TRUE; 687 break; 688 689 case LDAP_NO_SUCH_OBJECT: 690 /* NOTE: backend_group() returns noSuchObject 691 * if op_ndn does not exist; however, since 692 * dynamic list expansion means that the 693 * member attribute is virtually present, the 694 * non-existence of the asserted value implies 695 * the assertion is FALSE rather than 696 * UNDEFINED */ 697 rs->sr_err = LDAP_COMPARE_FALSE; 698 break; 699 } 700 701 done:; 702 if ( id ) ber_bvarray_free_x( id, o.o_tmpmemctx ); 703 704 return SLAP_CB_CONTINUE; 705 } 706 } 707 708 be = select_backend( &o.o_req_ndn, 1 ); 709 if ( !be || !be->be_search ) { 710 return SLAP_CB_CONTINUE; 711 } 712 713 if ( overlay_entry_get_ov( &o, &o.o_req_ndn, NULL, NULL, 0, &e, on ) != 714 LDAP_SUCCESS || e == NULL ) 715 { 716 return SLAP_CB_CONTINUE; 717 } 718 719 /* check for dynlist objectClass; done if not found */ 720 dli = (dynlist_info_t *)on->on_bi.bi_private; 721 while ( dli != NULL && !is_entry_objectclass_or_sub( e, dli->dli_oc ) ) { 722 dli = dli->dli_next; 723 } 724 if ( dli == NULL ) { 725 goto release; 726 } 727 728 if ( ad_dgIdentity ) { 729 Attribute *id = attrs_find( e->e_attrs, ad_dgIdentity ); 730 if ( id ) { 731 Attribute *authz; 732 733 /* if not rootdn and dgAuthz is present, 734 * check if user can be authorized as dgIdentity */ 735 if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op ) 736 && ( authz = attrs_find( e->e_attrs, ad_dgAuthz ) ) ) 737 { 738 if ( slap_sasl_matches( op, authz->a_nvals, 739 &o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS ) 740 { 741 goto release; 742 } 743 } 744 745 o.o_dn = id->a_vals[0]; 746 o.o_ndn = id->a_nvals[0]; 747 o.o_groups = NULL; 748 } 749 } 750 751 /* generate dynamic list with dynlist_response() and compare */ 752 { 753 SlapReply r = { REP_SEARCH }; 754 dynlist_cc_t dc = { { 0, dynlist_sc_compare_entry, 0, 0 }, 0 }; 755 AttributeName an[2]; 756 757 dc.dc_ava = op->orc_ava; 758 dc.dc_res = &rs->sr_err; 759 o.o_callback = (slap_callback *) &dc; 760 761 o.o_tag = LDAP_REQ_SEARCH; 762 o.ors_limit = NULL; 763 o.ors_tlimit = SLAP_NO_LIMIT; 764 o.ors_slimit = SLAP_NO_LIMIT; 765 766 o.ors_filterstr = *slap_filterstr_objectClass_pres; 767 o.ors_filter = (Filter *) slap_filter_objectClass_pres; 768 769 o.ors_scope = LDAP_SCOPE_BASE; 770 o.ors_deref = LDAP_DEREF_NEVER; 771 an[0].an_name = op->orc_ava->aa_desc->ad_cname; 772 an[0].an_desc = op->orc_ava->aa_desc; 773 BER_BVZERO( &an[1].an_name ); 774 o.ors_attrs = an; 775 o.ors_attrsonly = 0; 776 777 o.o_acl_priv = ACL_COMPARE; 778 779 o.o_bd = be; 780 (void)be->be_search( &o, &r ); 781 782 if ( o.o_dn.bv_val != op->o_dn.bv_val ) { 783 slap_op_groups_free( &o ); 784 } 785 } 786 787 release:; 788 if ( e != NULL ) { 789 overlay_entry_release_ov( &o, e, 0, on ); 790 } 791 792 return SLAP_CB_CONTINUE; 793 } 794 795 static int 796 dynlist_response( Operation *op, SlapReply *rs ) 797 { 798 switch ( op->o_tag ) { 799 case LDAP_REQ_SEARCH: 800 if ( rs->sr_type == REP_SEARCH && !get_manageDSAit( op ) ) 801 { 802 int rc = SLAP_CB_CONTINUE; 803 dynlist_info_t *dli = NULL; 804 805 while ( (dli = dynlist_is_dynlist_next( op, rs, dli )) != NULL ) { 806 rc = dynlist_prepare_entry( op, rs, dli ); 807 } 808 809 return rc; 810 } 811 break; 812 813 case LDAP_REQ_COMPARE: 814 switch ( rs->sr_err ) { 815 /* NOTE: we waste a few cycles running the dynamic list 816 * also when the result is FALSE, which occurs if the 817 * dynamic entry itself contains the AVA attribute */ 818 /* FIXME: this approach is less than optimal; a dedicated 819 * compare op should be implemented, that fetches the 820 * entry, checks if it has the appropriate objectClass 821 * and, in case, runs a compare thru all the URIs, 822 * stopping at the first positive occurrence; see ITS#3756 */ 823 case LDAP_COMPARE_FALSE: 824 case LDAP_NO_SUCH_ATTRIBUTE: 825 return dynlist_compare( op, rs ); 826 } 827 break; 828 } 829 830 return SLAP_CB_CONTINUE; 831 } 832 833 static int 834 dynlist_build_def_filter( dynlist_info_t *dli ) 835 { 836 char *ptr; 837 838 dli->dli_default_filter.bv_len = STRLENOF( "(!(objectClass=" "))" ) 839 + dli->dli_oc->soc_cname.bv_len; 840 dli->dli_default_filter.bv_val = ch_malloc( dli->dli_default_filter.bv_len + 1 ); 841 if ( dli->dli_default_filter.bv_val == NULL ) { 842 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: malloc failed.\n", 843 0, 0, 0 ); 844 return -1; 845 } 846 847 ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" ); 848 ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val ); 849 ptr = lutil_strcopy( ptr, "))" ); 850 851 assert( ptr == &dli->dli_default_filter.bv_val[dli->dli_default_filter.bv_len] ); 852 853 return 0; 854 } 855 856 enum { 857 DL_ATTRSET = 1, 858 DL_ATTRPAIR, 859 DL_ATTRPAIR_COMPAT, 860 DL_LAST 861 }; 862 863 static ConfigDriver dl_cfgen; 864 865 /* XXXmanu 255 is the maximum arguments we allow. Can we go beyond? */ 866 static ConfigTable dlcfg[] = { 867 { "dynlist-attrset", "group-oc> [uri] <URL-ad> <[mapped:]member-ad> [...]", 868 3, 0, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen, 869 "( OLcfgOvAt:8.1 NAME 'olcDlAttrSet' " 870 "DESC 'Dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' " 871 "EQUALITY caseIgnoreMatch " 872 "SYNTAX OMsDirectoryString " 873 "X-ORDERED 'VALUES' )", 874 NULL, NULL }, 875 { "dynlist-attrpair", "member-ad> <URL-ad", 876 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR, dl_cfgen, 877 NULL, NULL, NULL }, 878 #ifdef TAKEOVER_DYNGROUP 879 { "attrpair", "member-ad> <URL-ad", 880 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR_COMPAT, dl_cfgen, 881 NULL, NULL, NULL }, 882 #endif 883 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 884 }; 885 886 static ConfigOCs dlocs[] = { 887 { "( OLcfgOvOc:8.1 " 888 "NAME 'olcDynamicList' " 889 "DESC 'Dynamic list configuration' " 890 "SUP olcOverlayConfig " 891 "MAY olcDLattrSet )", 892 Cft_Overlay, dlcfg, NULL, NULL }, 893 { NULL, 0, NULL } 894 }; 895 896 static int 897 dl_cfgen( ConfigArgs *c ) 898 { 899 slap_overinst *on = (slap_overinst *)c->bi; 900 dynlist_info_t *dli = (dynlist_info_t *)on->on_bi.bi_private; 901 902 int rc = 0, i; 903 904 if ( c->op == SLAP_CONFIG_EMIT ) { 905 switch( c->type ) { 906 case DL_ATTRSET: 907 for ( i = 0; dli; i++, dli = dli->dli_next ) { 908 struct berval bv; 909 char *ptr = c->cr_msg; 910 dynlist_map_t *dlm; 911 912 assert( dli->dli_oc != NULL ); 913 assert( dli->dli_ad != NULL ); 914 915 /* FIXME: check buffer overflow! */ 916 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ), 917 SLAP_X_ORDERED_FMT "%s", i, 918 dli->dli_oc->soc_cname.bv_val ); 919 920 if ( !BER_BVISNULL( &dli->dli_uri ) ) { 921 *ptr++ = ' '; 922 *ptr++ = '"'; 923 ptr = lutil_strncopy( ptr, dli->dli_uri.bv_val, 924 dli->dli_uri.bv_len ); 925 *ptr++ = '"'; 926 } 927 928 *ptr++ = ' '; 929 ptr = lutil_strncopy( ptr, dli->dli_ad->ad_cname.bv_val, 930 dli->dli_ad->ad_cname.bv_len ); 931 932 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 933 ptr[ 0 ] = ' '; 934 ptr++; 935 if ( dlm->dlm_mapped_ad ) { 936 ptr = lutil_strcopy( ptr, dlm->dlm_mapped_ad->ad_cname.bv_val ); 937 ptr[ 0 ] = ':'; 938 ptr++; 939 } 940 941 ptr = lutil_strcopy( ptr, dlm->dlm_member_ad->ad_cname.bv_val ); 942 } 943 944 bv.bv_val = c->cr_msg; 945 bv.bv_len = ptr - bv.bv_val; 946 value_add_one( &c->rvalue_vals, &bv ); 947 } 948 break; 949 950 case DL_ATTRPAIR_COMPAT: 951 case DL_ATTRPAIR: 952 rc = 1; 953 break; 954 955 default: 956 rc = 1; 957 break; 958 } 959 960 return rc; 961 962 } else if ( c->op == LDAP_MOD_DELETE ) { 963 switch( c->type ) { 964 case DL_ATTRSET: 965 if ( c->valx < 0 ) { 966 dynlist_info_t *dli_next; 967 968 for ( dli_next = dli; dli_next; dli = dli_next ) { 969 dynlist_map_t *dlm = dli->dli_dlm; 970 dynlist_map_t *dlm_next; 971 972 dli_next = dli->dli_next; 973 974 if ( !BER_BVISNULL( &dli->dli_uri ) ) { 975 ch_free( dli->dli_uri.bv_val ); 976 } 977 978 if ( dli->dli_lud != NULL ) { 979 ldap_free_urldesc( dli->dli_lud ); 980 } 981 982 if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) { 983 ber_memfree( dli->dli_uri_nbase.bv_val ); 984 } 985 986 if ( dli->dli_uri_filter != NULL ) { 987 filter_free( dli->dli_uri_filter ); 988 } 989 990 ch_free( dli->dli_default_filter.bv_val ); 991 992 while ( dlm != NULL ) { 993 dlm_next = dlm->dlm_next; 994 ch_free( dlm ); 995 dlm = dlm_next; 996 } 997 ch_free( dli ); 998 } 999 1000 on->on_bi.bi_private = NULL; 1001 1002 } else { 1003 dynlist_info_t **dlip; 1004 dynlist_map_t *dlm; 1005 dynlist_map_t *dlm_next; 1006 1007 for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private; 1008 i < c->valx; i++ ) 1009 { 1010 if ( *dlip == NULL ) { 1011 return 1; 1012 } 1013 dlip = &(*dlip)->dli_next; 1014 } 1015 1016 dli = *dlip; 1017 *dlip = dli->dli_next; 1018 1019 if ( !BER_BVISNULL( &dli->dli_uri ) ) { 1020 ch_free( dli->dli_uri.bv_val ); 1021 } 1022 1023 if ( dli->dli_lud != NULL ) { 1024 ldap_free_urldesc( dli->dli_lud ); 1025 } 1026 1027 if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) { 1028 ber_memfree( dli->dli_uri_nbase.bv_val ); 1029 } 1030 1031 if ( dli->dli_uri_filter != NULL ) { 1032 filter_free( dli->dli_uri_filter ); 1033 } 1034 1035 ch_free( dli->dli_default_filter.bv_val ); 1036 1037 dlm = dli->dli_dlm; 1038 while ( dlm != NULL ) { 1039 dlm_next = dlm->dlm_next; 1040 ch_free( dlm ); 1041 dlm = dlm_next; 1042 } 1043 ch_free( dli ); 1044 1045 dli = (dynlist_info_t *)on->on_bi.bi_private; 1046 } 1047 break; 1048 1049 case DL_ATTRPAIR_COMPAT: 1050 case DL_ATTRPAIR: 1051 rc = 1; 1052 break; 1053 1054 default: 1055 rc = 1; 1056 break; 1057 } 1058 1059 return rc; 1060 } 1061 1062 switch( c->type ) { 1063 case DL_ATTRSET: { 1064 dynlist_info_t **dlip, 1065 *dli_next = NULL; 1066 ObjectClass *oc = NULL; 1067 AttributeDescription *ad = NULL; 1068 int attridx = 2; 1069 LDAPURLDesc *lud = NULL; 1070 struct berval nbase = BER_BVNULL; 1071 Filter *filter = NULL; 1072 struct berval uri = BER_BVNULL; 1073 dynlist_map_t *dlm = NULL, *dlml = NULL; 1074 const char *text; 1075 1076 oc = oc_find( c->argv[ 1 ] ); 1077 if ( oc == NULL ) { 1078 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 1079 "unable to find ObjectClass \"%s\"", 1080 c->argv[ 1 ] ); 1081 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1082 c->log, c->cr_msg, 0 ); 1083 return 1; 1084 } 1085 1086 if ( strncasecmp( c->argv[ attridx ], "ldap://", STRLENOF("ldap://") ) == 0 ) { 1087 if ( ldap_url_parse( c->argv[ attridx ], &lud ) != LDAP_URL_SUCCESS ) { 1088 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 1089 "unable to parse URI \"%s\"", 1090 c->argv[ attridx ] ); 1091 rc = 1; 1092 goto done_uri; 1093 } 1094 1095 if ( lud->lud_host != NULL ) { 1096 if ( lud->lud_host[0] == '\0' ) { 1097 ch_free( lud->lud_host ); 1098 lud->lud_host = NULL; 1099 1100 } else { 1101 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 1102 "host not allowed in URI \"%s\"", 1103 c->argv[ attridx ] ); 1104 rc = 1; 1105 goto done_uri; 1106 } 1107 } 1108 1109 if ( lud->lud_attrs != NULL ) { 1110 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 1111 "attrs not allowed in URI \"%s\"", 1112 c->argv[ attridx ] ); 1113 rc = 1; 1114 goto done_uri; 1115 } 1116 1117 if ( lud->lud_exts != NULL ) { 1118 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 1119 "extensions not allowed in URI \"%s\"", 1120 c->argv[ attridx ] ); 1121 rc = 1; 1122 goto done_uri; 1123 } 1124 1125 if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' ) { 1126 struct berval dn; 1127 ber_str2bv( lud->lud_dn, 0, 0, &dn ); 1128 rc = dnNormalize( 0, NULL, NULL, &dn, &nbase, NULL ); 1129 if ( rc != LDAP_SUCCESS ) { 1130 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 1131 "DN normalization failed in URI \"%s\"", 1132 c->argv[ attridx ] ); 1133 goto done_uri; 1134 } 1135 } 1136 1137 if ( lud->lud_filter != NULL && lud->lud_filter[ 0 ] != '\0' ) { 1138 filter = str2filter( lud->lud_filter ); 1139 if ( filter == NULL ) { 1140 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 1141 "filter parsing failed in URI \"%s\"", 1142 c->argv[ attridx ] ); 1143 rc = 1; 1144 goto done_uri; 1145 } 1146 } 1147 1148 ber_str2bv( c->argv[ attridx ], 0, 1, &uri ); 1149 1150 done_uri:; 1151 if ( rc ) { 1152 if ( lud ) { 1153 ldap_free_urldesc( lud ); 1154 } 1155 1156 if ( !BER_BVISNULL( &nbase ) ) { 1157 ber_memfree( nbase.bv_val ); 1158 } 1159 1160 if ( filter != NULL ) { 1161 filter_free( filter ); 1162 } 1163 1164 while ( dlm != NULL ) { 1165 dlml = dlm; 1166 dlm = dlm->dlm_next; 1167 ch_free( dlml ); 1168 } 1169 1170 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1171 c->log, c->cr_msg, 0 ); 1172 1173 return rc; 1174 } 1175 1176 attridx++; 1177 } 1178 1179 rc = slap_str2ad( c->argv[ attridx ], &ad, &text ); 1180 if ( rc != LDAP_SUCCESS ) { 1181 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 1182 "unable to find AttributeDescription \"%s\"", 1183 c->argv[ attridx ] ); 1184 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1185 c->log, c->cr_msg, 0 ); 1186 rc = 1; 1187 goto done_uri; 1188 } 1189 1190 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) { 1191 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 1192 "AttributeDescription \"%s\" " 1193 "must be a subtype of \"labeledURI\"", 1194 c->argv[ attridx ] ); 1195 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1196 c->log, c->cr_msg, 0 ); 1197 rc = 1; 1198 goto done_uri; 1199 } 1200 1201 attridx++; 1202 1203 for ( i = attridx; i < c->argc; i++ ) { 1204 char *arg; 1205 char *cp; 1206 AttributeDescription *member_ad = NULL; 1207 AttributeDescription *mapped_ad = NULL; 1208 dynlist_map_t *dlmp; 1209 1210 1211 /* 1212 * If no mapped attribute is given, dn is used 1213 * for backward compatibility. 1214 */ 1215 arg = c->argv[i]; 1216 if ( ( cp = strchr( arg, ':' ) ) != NULL ) { 1217 struct berval bv; 1218 ber_str2bv( arg, cp - arg, 0, &bv ); 1219 rc = slap_bv2ad( &bv, &mapped_ad, &text ); 1220 if ( rc != LDAP_SUCCESS ) { 1221 snprintf( c->cr_msg, sizeof( c->cr_msg ), 1222 DYNLIST_USAGE 1223 "unable to find mapped AttributeDescription #%d \"%s\"\n", 1224 i - 3, c->argv[ i ] ); 1225 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1226 c->log, c->cr_msg, 0 ); 1227 rc = 1; 1228 goto done_uri; 1229 } 1230 arg = cp + 1; 1231 } 1232 1233 rc = slap_str2ad( arg, &member_ad, &text ); 1234 if ( rc != LDAP_SUCCESS ) { 1235 snprintf( c->cr_msg, sizeof( c->cr_msg ), 1236 DYNLIST_USAGE 1237 "unable to find AttributeDescription #%d \"%s\"\n", 1238 i - 3, c->argv[ i ] ); 1239 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1240 c->log, c->cr_msg, 0 ); 1241 rc = 1; 1242 goto done_uri; 1243 } 1244 1245 dlmp = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) ); 1246 if ( dlm == NULL ) { 1247 dlm = dlmp; 1248 } 1249 dlmp->dlm_member_ad = member_ad; 1250 dlmp->dlm_mapped_ad = mapped_ad; 1251 dlmp->dlm_next = NULL; 1252 1253 if ( dlml != NULL ) 1254 dlml->dlm_next = dlmp; 1255 dlml = dlmp; 1256 } 1257 1258 if ( c->valx > 0 ) { 1259 int i; 1260 1261 for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private; 1262 i < c->valx; i++ ) 1263 { 1264 if ( *dlip == NULL ) { 1265 snprintf( c->cr_msg, sizeof( c->cr_msg ), 1266 DYNLIST_USAGE 1267 "invalid index {%d}\n", 1268 c->valx ); 1269 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1270 c->log, c->cr_msg, 0 ); 1271 rc = 1; 1272 goto done_uri; 1273 } 1274 dlip = &(*dlip)->dli_next; 1275 } 1276 dli_next = *dlip; 1277 1278 } else { 1279 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private; 1280 *dlip; dlip = &(*dlip)->dli_next ) 1281 /* goto last */; 1282 } 1283 1284 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) ); 1285 1286 (*dlip)->dli_oc = oc; 1287 (*dlip)->dli_ad = ad; 1288 (*dlip)->dli_dlm = dlm; 1289 (*dlip)->dli_next = dli_next; 1290 1291 (*dlip)->dli_lud = lud; 1292 (*dlip)->dli_uri_nbase = nbase; 1293 (*dlip)->dli_uri_filter = filter; 1294 (*dlip)->dli_uri = uri; 1295 1296 rc = dynlist_build_def_filter( *dlip ); 1297 1298 } break; 1299 1300 case DL_ATTRPAIR_COMPAT: 1301 snprintf( c->cr_msg, sizeof( c->cr_msg ), 1302 "warning: \"attrpair\" only supported for limited " 1303 "backward compatibility with overlay \"dyngroup\"" ); 1304 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 ); 1305 /* fallthru */ 1306 1307 case DL_ATTRPAIR: { 1308 dynlist_info_t **dlip; 1309 ObjectClass *oc = NULL; 1310 AttributeDescription *ad = NULL, 1311 *member_ad = NULL; 1312 const char *text; 1313 1314 oc = oc_find( "groupOfURLs" ); 1315 if ( oc == NULL ) { 1316 snprintf( c->cr_msg, sizeof( c->cr_msg ), 1317 "\"dynlist-attrpair <member-ad> <URL-ad>\": " 1318 "unable to find default ObjectClass \"groupOfURLs\"" ); 1319 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1320 c->log, c->cr_msg, 0 ); 1321 return 1; 1322 } 1323 1324 rc = slap_str2ad( c->argv[ 1 ], &member_ad, &text ); 1325 if ( rc != LDAP_SUCCESS ) { 1326 snprintf( c->cr_msg, sizeof( c->cr_msg ), 1327 "\"dynlist-attrpair <member-ad> <URL-ad>\": " 1328 "unable to find AttributeDescription \"%s\"", 1329 c->argv[ 1 ] ); 1330 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1331 c->log, c->cr_msg, 0 ); 1332 return 1; 1333 } 1334 1335 rc = slap_str2ad( c->argv[ 2 ], &ad, &text ); 1336 if ( rc != LDAP_SUCCESS ) { 1337 snprintf( c->cr_msg, sizeof( c->cr_msg ), 1338 "\"dynlist-attrpair <member-ad> <URL-ad>\": " 1339 "unable to find AttributeDescription \"%s\"\n", 1340 c->argv[ 2 ] ); 1341 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1342 c->log, c->cr_msg, 0 ); 1343 return 1; 1344 } 1345 1346 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) { 1347 snprintf( c->cr_msg, sizeof( c->cr_msg ), 1348 DYNLIST_USAGE 1349 "AttributeDescription \"%s\" " 1350 "must be a subtype of \"labeledURI\"", 1351 c->argv[ 2 ] ); 1352 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1353 c->log, c->cr_msg, 0 ); 1354 return 1; 1355 } 1356 1357 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private; 1358 *dlip; dlip = &(*dlip)->dli_next ) 1359 { 1360 /* 1361 * The same URL attribute / member attribute pair 1362 * cannot be repeated, but we enforce this only 1363 * when the member attribute is unique. Performing 1364 * the check for multiple values would require 1365 * sorting and comparing the lists, which is left 1366 * as a future improvement 1367 */ 1368 if ( (*dlip)->dli_ad == ad && 1369 (*dlip)->dli_dlm->dlm_next == NULL && 1370 member_ad == (*dlip)->dli_dlm->dlm_member_ad ) { 1371 snprintf( c->cr_msg, sizeof( c->cr_msg ), 1372 "\"dynlist-attrpair <member-ad> <URL-ad>\": " 1373 "URL attributeDescription \"%s\" already mapped.\n", 1374 ad->ad_cname.bv_val ); 1375 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 1376 c->log, c->cr_msg, 0 ); 1377 #if 0 1378 /* make it a warning... */ 1379 return 1; 1380 #endif 1381 } 1382 } 1383 1384 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) ); 1385 1386 (*dlip)->dli_oc = oc; 1387 (*dlip)->dli_ad = ad; 1388 (*dlip)->dli_dlm = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) ); 1389 (*dlip)->dli_dlm->dlm_member_ad = member_ad; 1390 (*dlip)->dli_dlm->dlm_mapped_ad = NULL; 1391 1392 rc = dynlist_build_def_filter( *dlip ); 1393 1394 } break; 1395 1396 default: 1397 rc = 1; 1398 break; 1399 } 1400 1401 return rc; 1402 } 1403 1404 static int 1405 dynlist_db_open( 1406 BackendDB *be, 1407 ConfigReply *cr ) 1408 { 1409 slap_overinst *on = (slap_overinst *) be->bd_info; 1410 dynlist_info_t *dli = (dynlist_info_t *)on->on_bi.bi_private; 1411 ObjectClass *oc = NULL; 1412 AttributeDescription *ad = NULL; 1413 const char *text; 1414 int rc; 1415 1416 if ( dli == NULL ) { 1417 dli = ch_calloc( 1, sizeof( dynlist_info_t ) ); 1418 on->on_bi.bi_private = (void *)dli; 1419 } 1420 1421 for ( ; dli; dli = dli->dli_next ) { 1422 if ( dli->dli_oc == NULL ) { 1423 if ( oc == NULL ) { 1424 oc = oc_find( "groupOfURLs" ); 1425 if ( oc == NULL ) { 1426 snprintf( cr->msg, sizeof( cr->msg), 1427 "unable to fetch objectClass \"groupOfURLs\"" ); 1428 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg, 0, 0 ); 1429 return 1; 1430 } 1431 } 1432 1433 dli->dli_oc = oc; 1434 } 1435 1436 if ( dli->dli_ad == NULL ) { 1437 if ( ad == NULL ) { 1438 rc = slap_str2ad( "memberURL", &ad, &text ); 1439 if ( rc != LDAP_SUCCESS ) { 1440 snprintf( cr->msg, sizeof( cr->msg), 1441 "unable to fetch attributeDescription \"memberURL\": %d (%s)", 1442 rc, text ); 1443 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg, 0, 0 ); 1444 return 1; 1445 } 1446 } 1447 1448 dli->dli_ad = ad; 1449 } 1450 1451 if ( BER_BVISNULL( &dli->dli_default_filter ) ) { 1452 rc = dynlist_build_def_filter( dli ); 1453 if ( rc != 0 ) { 1454 return rc; 1455 } 1456 } 1457 } 1458 1459 if ( ad_dgIdentity == NULL ) { 1460 rc = slap_str2ad( "dgIdentity", &ad_dgIdentity, &text ); 1461 if ( rc != LDAP_SUCCESS ) { 1462 snprintf( cr->msg, sizeof( cr->msg), 1463 "unable to fetch attributeDescription \"dgIdentity\": %d (%s)", 1464 rc, text ); 1465 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg, 0, 0 ); 1466 /* Just a warning */ 1467 } 1468 } 1469 1470 if ( ad_dgAuthz == NULL ) { 1471 rc = slap_str2ad( "dgAuthz", &ad_dgAuthz, &text ); 1472 if ( rc != LDAP_SUCCESS ) { 1473 snprintf( cr->msg, sizeof( cr->msg), 1474 "unable to fetch attributeDescription \"dgAuthz\": %d (%s)", 1475 rc, text ); 1476 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg, 0, 0 ); 1477 /* Just a warning */ 1478 } 1479 } 1480 1481 return 0; 1482 } 1483 1484 static int 1485 dynlist_db_destroy( 1486 BackendDB *be, 1487 ConfigReply *cr ) 1488 { 1489 slap_overinst *on = (slap_overinst *) be->bd_info; 1490 1491 if ( on->on_bi.bi_private ) { 1492 dynlist_info_t *dli = (dynlist_info_t *)on->on_bi.bi_private, 1493 *dli_next; 1494 1495 for ( dli_next = dli; dli_next; dli = dli_next ) { 1496 dynlist_map_t *dlm; 1497 dynlist_map_t *dlm_next; 1498 1499 dli_next = dli->dli_next; 1500 1501 if ( !BER_BVISNULL( &dli->dli_uri ) ) { 1502 ch_free( dli->dli_uri.bv_val ); 1503 } 1504 1505 if ( dli->dli_lud != NULL ) { 1506 ldap_free_urldesc( dli->dli_lud ); 1507 } 1508 1509 if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) { 1510 ber_memfree( dli->dli_uri_nbase.bv_val ); 1511 } 1512 1513 if ( dli->dli_uri_filter != NULL ) { 1514 filter_free( dli->dli_uri_filter ); 1515 } 1516 1517 ch_free( dli->dli_default_filter.bv_val ); 1518 1519 dlm = dli->dli_dlm; 1520 while ( dlm != NULL ) { 1521 dlm_next = dlm->dlm_next; 1522 ch_free( dlm ); 1523 dlm = dlm_next; 1524 } 1525 ch_free( dli ); 1526 } 1527 } 1528 1529 return 0; 1530 } 1531 1532 static slap_overinst dynlist = { { NULL } }; 1533 #ifdef TAKEOVER_DYNGROUP 1534 static char *obsolete_names[] = { 1535 "dyngroup", 1536 NULL 1537 }; 1538 #endif 1539 1540 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC 1541 static 1542 #endif /* SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC */ 1543 int 1544 dynlist_initialize(void) 1545 { 1546 int rc = 0; 1547 1548 dynlist.on_bi.bi_type = "dynlist"; 1549 1550 #ifdef TAKEOVER_DYNGROUP 1551 /* makes dynlist incompatible with dyngroup */ 1552 dynlist.on_bi.bi_obsolete_names = obsolete_names; 1553 #endif 1554 1555 dynlist.on_bi.bi_db_config = config_generic_wrapper; 1556 dynlist.on_bi.bi_db_open = dynlist_db_open; 1557 dynlist.on_bi.bi_db_destroy = dynlist_db_destroy; 1558 1559 dynlist.on_response = dynlist_response; 1560 1561 dynlist.on_bi.bi_cf_ocs = dlocs; 1562 1563 rc = config_register_schema( dlcfg, dlocs ); 1564 if ( rc ) { 1565 return rc; 1566 } 1567 1568 return overlay_register( &dynlist ); 1569 } 1570 1571 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC 1572 int 1573 init_module( int argc, char *argv[] ) 1574 { 1575 return dynlist_initialize(); 1576 } 1577 #endif 1578 1579 #endif /* SLAPD_OVER_DYNLIST */ 1580