1/* $NetBSD: dynlist.c,v 1.3 2021/08/14 16:15:02 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-2021 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.3 2021/08/14 16:15:02 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 "slap-config.h" 42#include "lutil.h" 43 44static AttributeDescription *ad_dgIdentity, *ad_dgAuthz; 45static AttributeDescription *ad_memberOf; 46 47typedef struct dynlist_map_t { 48 AttributeDescription *dlm_member_ad; 49 AttributeDescription *dlm_mapped_ad; 50 AttributeDescription *dlm_memberOf_ad; 51 ObjectClass *dlm_static_oc; 52 int dlm_memberOf_nested; 53 int dlm_member_oper; 54 int dlm_memberOf_oper; 55 struct dynlist_map_t *dlm_next; 56} dynlist_map_t; 57 58typedef struct dynlist_info_t { 59 ObjectClass *dli_oc; 60 AttributeDescription *dli_ad; 61 struct dynlist_map_t *dli_dlm; 62 struct berval dli_uri; 63 LDAPURLDesc *dli_lud; 64 struct berval dli_uri_nbase; 65 Filter *dli_uri_filter; 66 struct berval dli_default_filter; 67 struct dynlist_info_t *dli_next; 68} dynlist_info_t; 69 70typedef struct dynlist_gen_t { 71 dynlist_info_t *dlg_dli; 72 int dlg_memberOf; 73} dynlist_gen_t; 74 75#define DYNLIST_USAGE \ 76 "\"dynlist-attrset <oc> [uri] <URL-ad> [[<mapped-ad>:]<member-ad>[+<memberOf-ad>[@<static-oc>[*]] ...]\": " 77 78static int 79ad_infilter( AttributeDescription *ad, Filter *f ) 80{ 81 if ( !f ) 82 return 0; 83 84 switch( f->f_choice & SLAPD_FILTER_MASK ) { 85 case SLAPD_FILTER_COMPUTED: 86 return 0; 87 case LDAP_FILTER_PRESENT: 88 return f->f_desc == ad; 89 case LDAP_FILTER_EQUALITY: 90 case LDAP_FILTER_GE: 91 case LDAP_FILTER_LE: 92 case LDAP_FILTER_APPROX: 93 case LDAP_FILTER_SUBSTRINGS: 94 case LDAP_FILTER_EXT: 95 return f->f_av_desc == ad; 96 case LDAP_FILTER_AND: 97 case LDAP_FILTER_OR: 98 case LDAP_FILTER_NOT: { 99 for ( f = f->f_list; f; f = f->f_next ) 100 if ( ad_infilter( ad, f )) 101 return 1; 102 } 103 } 104 return 0; 105} 106 107static int 108dynlist_make_filter( Operation *op, Entry *e, dynlist_info_t *dli, const char *url, struct berval *oldf, struct berval *newf ) 109{ 110 char *ptr; 111 int needBrackets = 0; 112 113 assert( oldf != NULL ); 114 assert( newf != NULL ); 115 assert( !BER_BVISNULL( oldf ) ); 116 assert( !BER_BVISEMPTY( oldf ) ); 117 118 if ( oldf->bv_val[0] != '(' ) { 119 Debug( LDAP_DEBUG_ANY, "%s: dynlist, DN=\"%s\": missing parentheses in URI=\"%s\" filter\n", 120 op->o_log_prefix, e->e_name.bv_val, url ); 121 needBrackets = 2; 122 } 123 124 newf->bv_len = STRLENOF( "(&(!(objectClass=" "))" ")" ) 125 + dli->dli_oc->soc_cname.bv_len + oldf->bv_len + needBrackets; 126 newf->bv_val = op->o_tmpalloc( newf->bv_len + 1, op->o_tmpmemctx ); 127 if ( newf->bv_val == NULL ) { 128 return -1; 129 } 130 ptr = lutil_strcopy( newf->bv_val, "(&(!(objectClass=" ); 131 ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val ); 132 ptr = lutil_strcopy( ptr, "))" ); 133 if ( needBrackets ) *ptr++ = '('; 134 ptr = lutil_strcopy( ptr, oldf->bv_val ); 135 if ( needBrackets ) *ptr++ = ')'; 136 ptr = lutil_strcopy( ptr, ")" ); 137 newf->bv_len = ptr - newf->bv_val; 138 139 return 0; 140} 141 142/* dynlist_sc_update() callback info set by dynlist_prepare_entry() */ 143typedef struct dynlist_sc_t { 144 dynlist_info_t *dlc_dli; 145 Entry *dlc_e; 146 char **dlc_attrs; 147} dynlist_sc_t; 148 149static int 150dynlist_sc_update( Operation *op, SlapReply *rs ) 151{ 152 Entry *e; 153 Attribute *a; 154 int opattrs, 155 userattrs; 156 AccessControlState acl_state = ACL_STATE_INIT; 157 158 dynlist_sc_t *dlc; 159 dynlist_map_t *dlm; 160 161 if ( rs->sr_type != REP_SEARCH ) { 162 return 0; 163 } 164 165 dlc = (dynlist_sc_t *)op->o_callback->sc_private; 166 e = dlc->dlc_e; 167 168 assert( e != NULL ); 169 assert( rs->sr_entry != NULL ); 170 171 /* test access to entry */ 172 if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry, 173 NULL, ACL_READ, NULL ) ) 174 { 175 goto done; 176 } 177 178 /* if there is only one member_ad, and it's not mapped, 179 * consider it as old-style member listing */ 180 dlm = dlc->dlc_dli->dli_dlm; 181 if ( dlm && dlm->dlm_mapped_ad == NULL && dlm->dlm_next == NULL && dlc->dlc_attrs == NULL ) { 182 /* if access allowed, try to add values, emulating permissive 183 * control to silently ignore duplicates */ 184 if ( access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry, 185 NULL, ACL_READ, NULL ) ) 186 { 187 Modification mod; 188 const char *text = NULL; 189 char textbuf[1024]; 190 struct berval vals[ 2 ], nvals[ 2 ]; 191 192 vals[ 0 ] = rs->sr_entry->e_name; 193 BER_BVZERO( &vals[ 1 ] ); 194 nvals[ 0 ] = rs->sr_entry->e_nname; 195 BER_BVZERO( &nvals[ 1 ] ); 196 197 mod.sm_op = LDAP_MOD_ADD; 198 mod.sm_desc = dlm->dlm_member_ad; 199 mod.sm_type = dlm->dlm_member_ad->ad_cname; 200 mod.sm_values = vals; 201 mod.sm_nvalues = nvals; 202 mod.sm_numvals = 1; 203 204 (void)modify_add_values( e, &mod, /* permissive */ 1, 205 &text, textbuf, sizeof( textbuf ) ); 206 } 207 208 goto done; 209 } 210 211 opattrs = SLAP_OPATTRS( rs->sr_attr_flags ); 212 userattrs = SLAP_USERATTRS( rs->sr_attr_flags ); 213 214 for ( a = rs->sr_entry->e_attrs; a != NULL; a = a->a_next ) { 215 BerVarray vals, nvals = NULL; 216 int i, j, 217 is_oc = a->a_desc == slap_schema.si_ad_objectClass; 218 219 /* if attribute is not requested, skip it */ 220 if ( rs->sr_attrs == NULL ) { 221 if ( is_at_operational( a->a_desc->ad_type ) ) { 222 continue; 223 } 224 225 } else { 226 if ( is_at_operational( a->a_desc->ad_type ) ) { 227 if ( !opattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) ) 228 { 229 continue; 230 } 231 232 } else { 233 if ( !userattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) ) 234 { 235 continue; 236 } 237 } 238 } 239 240 /* test access to attribute */ 241 if ( op->ors_attrsonly ) { 242 if ( !access_allowed( op, rs->sr_entry, a->a_desc, NULL, 243 ACL_READ, &acl_state ) ) 244 { 245 continue; 246 } 247 } 248 249 /* single-value check: keep first only */ 250 if ( is_at_single_value( a->a_desc->ad_type ) ) { 251 if ( attr_find( e->e_attrs, a->a_desc ) != NULL ) { 252 continue; 253 } 254 } 255 256 /* test access to attribute */ 257 i = a->a_numvals; 258 259 vals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx ); 260 if ( a->a_nvals != a->a_vals ) { 261 nvals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx ); 262 } 263 264 for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) { 265 if ( is_oc ) { 266 ObjectClass *soc = oc_bvfind( &a->a_vals[i] ); 267 268 if ( soc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) { 269 continue; 270 } 271 } 272 273 if ( access_allowed( op, rs->sr_entry, a->a_desc, 274 &a->a_nvals[i], ACL_READ, &acl_state ) ) 275 { 276 vals[j] = a->a_vals[i]; 277 if ( nvals ) { 278 nvals[j] = a->a_nvals[i]; 279 } 280 j++; 281 } 282 } 283 284 /* if access allowed, try to add values, emulating permissive 285 * control to silently ignore duplicates */ 286 if ( j != 0 ) { 287 Modification mod; 288 const char *text = NULL; 289 char textbuf[1024]; 290 dynlist_map_t *dlm; 291 AttributeDescription *ad; 292 293 BER_BVZERO( &vals[j] ); 294 if ( nvals ) { 295 BER_BVZERO( &nvals[j] ); 296 } 297 298 ad = a->a_desc; 299 for ( dlm = dlc->dlc_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 300 if ( dlm->dlm_member_ad == a->a_desc ) { 301 if ( dlm->dlm_mapped_ad ) { 302 ad = dlm->dlm_mapped_ad; 303 } 304 break; 305 } 306 } 307 308 mod.sm_op = LDAP_MOD_ADD; 309 mod.sm_desc = ad; 310 mod.sm_type = ad->ad_cname; 311 mod.sm_values = vals; 312 mod.sm_nvalues = nvals; 313 mod.sm_numvals = j; 314 315 (void)modify_add_values( e, &mod, /* permissive */ 1, 316 &text, textbuf, sizeof( textbuf ) ); 317 } 318 319 op->o_tmpfree( vals, op->o_tmpmemctx ); 320 if ( nvals ) { 321 op->o_tmpfree( nvals, op->o_tmpmemctx ); 322 } 323 } 324 325done:; 326 if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) { 327 entry_free( rs->sr_entry ); 328 rs->sr_entry = NULL; 329 rs->sr_flags &= ~REP_ENTRY_MASK; 330 } 331 332 return 0; 333} 334 335typedef struct dynlist_name_t { 336 struct berval dy_name; 337 dynlist_info_t *dy_dli; 338 AttributeDescription *dy_staticmember; 339 int dy_seen; 340 int dy_numuris; 341 TAvlnode *dy_subs; 342 TAvlnode *dy_sups; 343 LDAPURLDesc *dy_uris[]; 344} dynlist_name_t; 345 346static void 347dynlist_urlmembers( Operation *op, dynlist_name_t *dyn, slap_callback *sc ) 348{ 349 Operation o = *op; 350 LDAPURLDesc *ludp; 351 int i; 352 353 o.ors_deref = LDAP_DEREF_NEVER; 354 o.ors_limit = NULL; 355 o.ors_tlimit = SLAP_NO_LIMIT; 356 o.ors_slimit = SLAP_NO_LIMIT; 357 o.ors_attrs = NULL; 358 memset( o.o_ctrlflag, 0, sizeof( o.o_ctrlflag )); 359 o.o_callback = sc; 360 361 for (i=0; i<dyn->dy_numuris; i++) { 362 ludp = dyn->dy_uris[i]; 363 if ( ludp->lud_attrs ) 364 continue; 365 o.o_req_dn.bv_val = ludp->lud_dn; 366 o.o_req_dn.bv_len = ludp->lud_port; 367 o.o_req_ndn = o.o_req_dn; 368 o.ors_scope = ludp->lud_scope; 369 o.ors_filter = (Filter *)ludp->lud_filter; 370 filter2bv_x( op, o.ors_filter, &o.ors_filterstr ); 371 o.o_bd = select_backend( &o.o_req_ndn, 1 ); 372 if ( o.o_bd && o.o_bd->be_search ) { 373 SlapReply r = { REP_SEARCH }; 374 r.sr_attr_flags = slap_attr_flags( o.ors_attrs ); 375 o.o_managedsait = SLAP_CONTROL_CRITICAL; 376 (void)o.o_bd->be_search( &o, &r ); 377 } 378 op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx ); 379 } 380} 381 382static void 383dynlist_nested_memberOf( Entry *e, AttributeDescription *ad, TAvlnode *sups ) 384{ 385 TAvlnode *ptr; 386 dynlist_name_t *dyn; 387 Attribute *a; 388 389 a = attr_find( e->e_attrs, ad ); 390 for ( ptr = ldap_tavl_end( sups, TAVL_DIR_LEFT ); ptr; 391 ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) { 392 dyn = ptr->avl_data; 393 if ( a ) { 394 unsigned slot; 395 if ( attr_valfind( a, SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX | 396 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH | 397 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, 398 &dyn->dy_name, &slot, NULL ) == LDAP_SUCCESS ) 399 continue; 400 } 401 attr_merge_one( e, ad, &dyn->dy_name, &dyn->dy_name ); 402 if ( !a ) 403 a = attr_find( e->e_attrs, ad ); 404 if ( dyn->dy_sups ) 405 dynlist_nested_memberOf( e, ad, dyn->dy_sups ); 406 } 407} 408 409typedef struct dynlist_member_t { 410 Entry *dm_e; 411 AttributeDescription *dm_ad; 412 Modification dm_mod; 413 TAvlnode *dm_groups; 414 struct berval dm_bv[2]; 415 struct berval dm_nbv[2]; 416 const char *dm_text; 417 char dm_textbuf[1024]; 418} dynlist_member_t; 419 420static int 421dynlist_ptr_cmp( const void *c1, const void *c2 ) 422{ 423 return ( c1 < c2 ) ? -1 : c1 > c2; 424} 425 426static int 427dynlist_nested_member_dg( Operation *op, SlapReply *rs ) 428{ 429 dynlist_member_t *dm = op->o_callback->sc_private; 430 431 if ( rs->sr_type != REP_SEARCH ) 432 return LDAP_SUCCESS; 433 434 dm->dm_bv[0] = rs->sr_entry->e_name; 435 dm->dm_nbv[0] = rs->sr_entry->e_nname; 436 modify_add_values( dm->dm_e, &dm->dm_mod, /* permissive */ 1, 437 &dm->dm_text, dm->dm_textbuf, sizeof( dm->dm_textbuf )); 438 439 return LDAP_SUCCESS; 440} 441 442static void 443dynlist_nested_member( Operation *op, dynlist_member_t *dm, TAvlnode *subs ) 444{ 445 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 446 TAvlnode *ptr; 447 dynlist_name_t *dyn; 448 Entry *ne; 449 Attribute *a, *b; 450 451 a = attr_find( dm->dm_e->e_attrs, dm->dm_ad ); 452 if ( !a ) 453 return; 454 455 for ( ptr = ldap_tavl_end( subs, TAVL_DIR_LEFT ); ptr; 456 ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) { 457 dyn = ptr->avl_data; 458 if ( ldap_tavl_insert( &dm->dm_groups, dyn, dynlist_ptr_cmp, ldap_avl_dup_error )) 459 continue; 460 if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &ne, on ) != LDAP_SUCCESS || ne == NULL ) 461 continue; 462 b = attr_find( ne->e_attrs, dm->dm_ad ); 463 if ( b ) { 464 dm->dm_mod.sm_values = b->a_vals; 465 dm->dm_mod.sm_nvalues = b->a_nvals; 466 dm->dm_mod.sm_numvals = b->a_numvals; 467 modify_add_values( dm->dm_e, &dm->dm_mod, /* permissive */ 1, 468 &dm->dm_text, dm->dm_textbuf, sizeof( dm->dm_textbuf )); 469 } 470 overlay_entry_release_ov( op, ne, 0, on ); 471 if ( dyn->dy_numuris ) { 472 slap_callback cb = { 0 }; 473 cb.sc_private = dm; 474 BER_BVZERO( &dm->dm_bv[1] ); 475 BER_BVZERO( &dm->dm_nbv[1] ); 476 dm->dm_mod.sm_values = dm->dm_bv; 477 dm->dm_mod.sm_nvalues = dm->dm_nbv; 478 dm->dm_mod.sm_numvals = 1; 479 cb.sc_response = dynlist_nested_member_dg; 480 dynlist_urlmembers( op, dyn, &cb ); 481 } 482 if ( dyn->dy_subs ) 483 dynlist_nested_member( op, dm, dyn->dy_subs ); 484 } 485} 486 487static int 488dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli, dynlist_name_t *dyn ) 489{ 490 Attribute *a, *id = NULL; 491 slap_callback cb = { 0 }; 492 Operation o = *op; 493 struct berval *url; 494 Entry *e; 495 int opattrs, 496 userattrs; 497 dynlist_sc_t dlc = { 0 }; 498 dynlist_map_t *dlm; 499 500 e = rs->sr_entry; 501 a = attrs_find( rs->sr_entry->e_attrs, dli->dli_ad ); 502 if ( a == NULL ) { 503 /* FIXME: error? */ 504 goto checkdyn; 505 } 506 507 opattrs = SLAP_OPATTRS( rs->sr_attr_flags ); 508 userattrs = SLAP_USERATTRS( rs->sr_attr_flags ); 509 510 /* Don't generate member list if it wasn't requested */ 511 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 512 AttributeDescription *ad = dlm->dlm_mapped_ad ? dlm->dlm_mapped_ad : dlm->dlm_member_ad; 513 if ( userattrs || ad_inlist( ad, rs->sr_attrs ) 514 || ad_infilter( ad, op->ors_filter )) 515 break; 516 } 517 if ( dli->dli_dlm && !dlm ) 518 goto checkdyn; 519 520 if ( ad_dgIdentity && ( id = attrs_find( rs->sr_entry->e_attrs, ad_dgIdentity ))) { 521 Attribute *authz = NULL; 522 523 /* if not rootdn and dgAuthz is present, 524 * check if user can be authorized as dgIdentity */ 525 if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op ) 526 && ( authz = attrs_find( rs->sr_entry->e_attrs, ad_dgAuthz ) ) ) 527 { 528 if ( slap_sasl_matches( op, authz->a_nvals, 529 &o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS ) 530 { 531 goto checkdyn; 532 } 533 } 534 535 o.o_dn = id->a_vals[0]; 536 o.o_ndn = id->a_nvals[0]; 537 o.o_groups = NULL; 538 } 539 540 /* ensure e is modifiable, but do not replace 541 * sr_entry yet since we have pointers into it */ 542 if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) { 543 e = entry_dup( rs->sr_entry ); 544 } 545 546 dlc.dlc_e = e; 547 dlc.dlc_dli = dli; 548 cb.sc_private = &dlc; 549 cb.sc_response = dynlist_sc_update; 550 551 o.o_callback = &cb; 552 o.ors_deref = LDAP_DEREF_NEVER; 553 o.ors_limit = NULL; 554 o.ors_tlimit = SLAP_NO_LIMIT; 555 o.ors_slimit = SLAP_NO_LIMIT; 556 memset( o.o_ctrlflag, 0, sizeof( o.o_ctrlflag )); 557 558 for ( url = a->a_nvals; !BER_BVISNULL( url ); url++ ) { 559 LDAPURLDesc *lud = NULL; 560 int i, j; 561 struct berval dn; 562 int rc; 563 564 BER_BVZERO( &o.o_req_dn ); 565 BER_BVZERO( &o.o_req_ndn ); 566 o.ors_filter = NULL; 567 o.ors_attrs = NULL; 568 BER_BVZERO( &o.ors_filterstr ); 569 570 if ( ldap_url_parse( url->bv_val, &lud ) != LDAP_URL_SUCCESS ) { 571 /* FIXME: error? */ 572 continue; 573 } 574 575 if ( lud->lud_host != NULL ) { 576 /* FIXME: host not allowed; reject as illegal? */ 577 Debug( LDAP_DEBUG_ANY, "dynlist_prepare_entry(\"%s\"): " 578 "illegal URI \"%s\"\n", 579 e->e_name.bv_val, url->bv_val ); 580 goto cleanup; 581 } 582 583 if ( lud->lud_dn == NULL ) { 584 /* note that an empty base is not honored in terms 585 * of defaultSearchBase, because select_backend() 586 * is not aware of the defaultSearchBase option; 587 * this can be useful in case of a database serving 588 * the empty suffix */ 589 BER_BVSTR( &dn, "" ); 590 591 } else { 592 ber_str2bv( lud->lud_dn, 0, 0, &dn ); 593 } 594 rc = dnPrettyNormal( NULL, &dn, &o.o_req_dn, &o.o_req_ndn, op->o_tmpmemctx ); 595 if ( rc != LDAP_SUCCESS ) { 596 /* FIXME: error? */ 597 goto cleanup; 598 } 599 o.ors_scope = lud->lud_scope; 600 601 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 602 if ( dlm->dlm_mapped_ad != NULL ) { 603 break; 604 } 605 } 606 607 if ( dli->dli_dlm && !dlm ) { 608 /* if ( lud->lud_attrs != NULL ), 609 * the URL should be ignored */ 610 o.ors_attrs = slap_anlist_no_attrs; 611 612 } else if ( lud->lud_attrs == NULL ) { 613 o.ors_attrs = rs->sr_attrs; 614 615 } else { 616 for ( i = 0; lud->lud_attrs[i]; i++) 617 /* just count */ ; 618 619 o.ors_attrs = op->o_tmpcalloc( i + 1, sizeof( AttributeName ), op->o_tmpmemctx ); 620 for ( i = 0, j = 0; lud->lud_attrs[i]; i++) { 621 const char *text = NULL; 622 623 ber_str2bv( lud->lud_attrs[i], 0, 0, &o.ors_attrs[j].an_name ); 624 o.ors_attrs[j].an_desc = NULL; 625 (void)slap_bv2ad( &o.ors_attrs[j].an_name, &o.ors_attrs[j].an_desc, &text ); 626 /* FIXME: ignore errors... */ 627 628 if ( ad_infilter( o.ors_attrs[j].an_desc, op->ors_filter )) { 629 /* if referenced in filter, must retrieve */ 630 } else if ( rs->sr_attrs == NULL ) { 631 if ( o.ors_attrs[j].an_desc != NULL && 632 is_at_operational( o.ors_attrs[j].an_desc->ad_type ) ) 633 { 634 continue; 635 } 636 637 } else { 638 if ( o.ors_attrs[j].an_desc != NULL && 639 is_at_operational( o.ors_attrs[j].an_desc->ad_type ) ) 640 { 641 if ( !opattrs ) { 642 continue; 643 } 644 645 if ( !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) ) { 646 /* lookup if mapped -- linear search, 647 * not very efficient unless list 648 * is very short */ 649 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 650 if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) { 651 break; 652 } 653 } 654 655 if ( dlm == NULL ) { 656 continue; 657 } 658 } 659 660 } else { 661 if ( !userattrs && 662 o.ors_attrs[j].an_desc != NULL && 663 !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) ) 664 { 665 /* lookup if mapped -- linear search, 666 * not very efficient unless list 667 * is very short */ 668 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 669 if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) { 670 break; 671 } 672 } 673 674 if ( dlm == NULL ) { 675 continue; 676 } 677 } 678 } 679 } 680 681 j++; 682 } 683 684 if ( j == 0 ) { 685 goto cleanup; 686 } 687 688 BER_BVZERO( &o.ors_attrs[j].an_name ); 689 } 690 dlc.dlc_attrs = lud->lud_attrs; 691 692 if ( lud->lud_filter == NULL ) { 693 ber_dupbv_x( &o.ors_filterstr, 694 &dli->dli_default_filter, op->o_tmpmemctx ); 695 696 } else { 697 /* don't allow recursion in lists */ 698 if ( lud->lud_attrs ) { 699 struct berval flt; 700 ber_str2bv( lud->lud_filter, 0, 0, &flt ); 701 if ( dynlist_make_filter( op, rs->sr_entry, dli, url->bv_val, &flt, &o.ors_filterstr ) ) { 702 /* error */ 703 goto cleanup; 704 } 705 } else { 706 ber_str2bv( lud->lud_filter, 0, 0, &o.ors_filterstr ); 707 } 708 } 709 o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val ); 710 if ( o.ors_filter == NULL ) { 711 goto cleanup; 712 } 713 714 o.o_bd = select_backend( &o.o_req_ndn, 1 ); 715 if ( o.o_bd && o.o_bd->be_search ) { 716 SlapReply r = { REP_SEARCH }; 717 r.sr_attr_flags = slap_attr_flags( o.ors_attrs ); 718 o.o_managedsait = SLAP_CONTROL_CRITICAL; 719 (void)o.o_bd->be_search( &o, &r ); 720 } 721 722cleanup:; 723 if ( id ) { 724 slap_op_groups_free( &o ); 725 } 726 if ( o.ors_filter ) { 727 filter_free_x( &o, o.ors_filter, 1 ); 728 } 729 if ( o.ors_attrs && o.ors_attrs != rs->sr_attrs 730 && o.ors_attrs != slap_anlist_no_attrs ) 731 { 732 op->o_tmpfree( o.ors_attrs, op->o_tmpmemctx ); 733 } 734 if ( !BER_BVISNULL( &o.o_req_dn ) ) { 735 op->o_tmpfree( o.o_req_dn.bv_val, op->o_tmpmemctx ); 736 } 737 if ( !BER_BVISNULL( &o.o_req_ndn ) ) { 738 op->o_tmpfree( o.o_req_ndn.bv_val, op->o_tmpmemctx ); 739 } 740 if ( lud->lud_attrs ) { 741 assert( BER_BVISNULL( &o.ors_filterstr ) 742 || o.ors_filterstr.bv_val != lud->lud_filter ); 743 op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx ); 744 } else { 745 if ( o.ors_filterstr.bv_val != lud->lud_filter ) 746 op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx ); 747 } 748 ldap_free_urldesc( lud ); 749 } 750 751checkdyn: 752 /* handle nested groups */ 753 if ( dyn && ( dyn->dy_sups || dyn->dy_subs )) { 754 /* ensure e is modifiable */ 755 if ( e == rs->sr_entry && !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) { 756 e = entry_dup( rs->sr_entry ); 757 rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e ); 758 rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED; 759 } 760 if ( dyn->dy_subs ) { 761 for ( dlm = dyn->dy_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 762 if ( dlm->dlm_member_ad ) { 763 dynlist_member_t dm; 764 dm.dm_groups = NULL; 765 dm.dm_mod.sm_op = LDAP_MOD_ADD; 766 dm.dm_mod.sm_desc = dlm->dlm_member_ad; 767 dm.dm_mod.sm_type = dlm->dlm_member_ad->ad_cname; 768 dm.dm_e = e; 769 dm.dm_ad = dlm->dlm_member_ad; 770 dynlist_nested_member( op, &dm, dyn->dy_subs ); 771 if ( dm.dm_groups ) 772 ldap_tavl_free( dm.dm_groups, NULL ); 773 } 774 } 775 } 776 if ( dyn->dy_sups ) { 777 for ( dlm = dyn->dy_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 778 if ( dlm->dlm_memberOf_ad ) { 779 dynlist_nested_memberOf( e, dlm->dlm_memberOf_ad, dyn->dy_sups ); 780 } 781 } 782 } 783 } 784 785 if ( e != rs->sr_entry ) { 786 rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e ); 787 rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED; 788 } 789 790 return SLAP_CB_CONTINUE; 791} 792 793/* dynlist_sc_compare_entry() callback set by dynlist_compare() */ 794typedef struct dynlist_cc_t { 795 slap_callback dc_cb; 796# define dc_ava dc_cb.sc_private /* attr:val to compare with */ 797 int *dc_res; 798} dynlist_cc_t; 799 800static int 801dynlist_sc_compare_entry( Operation *op, SlapReply *rs ) 802{ 803 if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) { 804 dynlist_cc_t *dc = (dynlist_cc_t *)op->o_callback; 805 AttributeAssertion *ava = dc->dc_ava; 806 Attribute *a = attrs_find( rs->sr_entry->e_attrs, ava->aa_desc ); 807 808 if ( a != NULL ) { 809 while ( LDAP_SUCCESS != attr_valfind( a, 810 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 811 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, 812 &ava->aa_value, NULL, op->o_tmpmemctx ) 813 && (a = attrs_find( a->a_next, ava->aa_desc )) != NULL ) 814 ; 815 *dc->dc_res = a ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE; 816 } 817 } 818 819 return 0; 820} 821 822static int 823dynlist_compare( Operation *op, SlapReply *rs ) 824{ 825 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 826 dynlist_gen_t *dlg = (dynlist_gen_t *)on->on_bi.bi_private; 827 dynlist_info_t *dli = dlg->dlg_dli; 828 Operation o = *op; 829 Entry *e = NULL; 830 dynlist_map_t *dlm; 831 BackendDB *be; 832 int ret = SLAP_CB_CONTINUE; 833 834 if ( get_manageDSAit( op ) ) 835 return SLAP_CB_CONTINUE; 836 837 for ( ; dli != NULL; dli = dli->dli_next ) { 838 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 839 AttributeDescription *ad = dlm->dlm_mapped_ad ? dlm->dlm_mapped_ad : dlm->dlm_member_ad; 840 /* builtin dyngroup evaluator only works for DNs */ 841 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) 842 continue; 843 if ( op->oq_compare.rs_ava->aa_desc == ad ) 844 break; 845 } 846 847 if ( dlm ) { 848 /* This compare is for one of the attributes we're 849 * interested in. We'll use slapd's existing dyngroup 850 * evaluator to get the answer we want. 851 */ 852 BerVarray id = NULL, authz = NULL; 853 854 o.o_do_not_cache = 1; 855 856 if ( ad_dgIdentity && backend_attribute( &o, NULL, &o.o_req_ndn, 857 ad_dgIdentity, &id, ACL_READ ) == LDAP_SUCCESS ) 858 { 859 /* if not rootdn and dgAuthz is present, 860 * check if user can be authorized as dgIdentity */ 861 if ( ad_dgAuthz && !BER_BVISEMPTY( id ) && !be_isroot( op ) 862 && backend_attribute( &o, NULL, &o.o_req_ndn, 863 ad_dgAuthz, &authz, ACL_READ ) == LDAP_SUCCESS ) 864 { 865 866 rs->sr_err = slap_sasl_matches( op, authz, 867 &o.o_ndn, &o.o_ndn ); 868 ber_bvarray_free_x( authz, op->o_tmpmemctx ); 869 if ( rs->sr_err != LDAP_SUCCESS ) { 870 goto done; 871 } 872 } 873 874 o.o_dn = *id; 875 o.o_ndn = *id; 876 o.o_groups = NULL; /* authz changed, invalidate cached groups */ 877 } 878 879 rs->sr_err = backend_group( &o, NULL, &o.o_req_ndn, 880 &o.oq_compare.rs_ava->aa_value, dli->dli_oc, dli->dli_ad ); 881 switch ( rs->sr_err ) { 882 case LDAP_SUCCESS: 883 rs->sr_err = LDAP_COMPARE_TRUE; 884 break; 885 886 case LDAP_NO_SUCH_OBJECT: 887 /* NOTE: backend_group() returns noSuchObject 888 * if op_ndn does not exist; however, since 889 * dynamic list expansion means that the 890 * member attribute is virtually present, the 891 * non-existence of the asserted value implies 892 * the assertion is FALSE rather than 893 * UNDEFINED */ 894 rs->sr_err = LDAP_COMPARE_FALSE; 895 break; 896 } 897 898done:; 899 if ( id ) ber_bvarray_free_x( id, o.o_tmpmemctx ); 900 901 send_ldap_result( op, rs ); 902 return rs->sr_err; 903 } 904 } 905 906 be = select_backend( &o.o_req_ndn, 1 ); 907 if ( !be || !be->be_search ) { 908 return SLAP_CB_CONTINUE; 909 } 910 911 if ( overlay_entry_get_ov( &o, &o.o_req_ndn, NULL, NULL, 0, &e, on ) != 912 LDAP_SUCCESS || e == NULL ) 913 { 914 return SLAP_CB_CONTINUE; 915 } 916 917 /* check for dynlist objectClass; done if not found */ 918 dli = (dynlist_info_t *)dlg->dlg_dli; 919 while ( dli != NULL && !is_entry_objectclass_or_sub( e, dli->dli_oc ) ) { 920 dli = dli->dli_next; 921 } 922 if ( dli == NULL ) { 923 goto release; 924 } 925 926 if ( ad_dgIdentity ) { 927 Attribute *id = attrs_find( e->e_attrs, ad_dgIdentity ); 928 if ( id ) { 929 Attribute *authz; 930 931 /* if not rootdn and dgAuthz is present, 932 * check if user can be authorized as dgIdentity */ 933 if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op ) 934 && ( authz = attrs_find( e->e_attrs, ad_dgAuthz ) ) ) 935 { 936 if ( slap_sasl_matches( op, authz->a_nvals, 937 &o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS ) 938 { 939 goto release; 940 } 941 } 942 943 o.o_dn = id->a_vals[0]; 944 o.o_ndn = id->a_nvals[0]; 945 o.o_groups = NULL; 946 } 947 } 948 949 /* generate dynamic list with dynlist_response() and compare */ 950 { 951 SlapReply r = { REP_SEARCH }; 952 Attribute *a; 953 AttributeName an[2]; 954 955 o.o_tag = LDAP_REQ_SEARCH; 956 o.ors_limit = NULL; 957 o.ors_tlimit = SLAP_NO_LIMIT; 958 o.ors_slimit = SLAP_NO_LIMIT; 959 960 o.ors_filterstr = *slap_filterstr_objectClass_pres; 961 o.ors_filter = (Filter *) slap_filter_objectClass_pres; 962 963 o.ors_scope = LDAP_SCOPE_BASE; 964 o.ors_deref = LDAP_DEREF_NEVER; 965 an[0].an_name = op->orc_ava->aa_desc->ad_cname; 966 an[0].an_desc = op->orc_ava->aa_desc; 967 BER_BVZERO( &an[1].an_name ); 968 o.ors_attrs = an; 969 o.ors_attrsonly = 0; 970 r.sr_entry = e; 971 r.sr_attrs = an; 972 973 o.o_acl_priv = ACL_COMPARE; 974 dynlist_prepare_entry( &o, &r, dli, NULL ); 975 a = attrs_find( r.sr_entry->e_attrs, op->orc_ava->aa_desc ); 976 977 ret = LDAP_NO_SUCH_ATTRIBUTE; 978 for ( ; a ; a = attrs_find( a->a_next, op->orc_ava->aa_desc )) { 979 ret = LDAP_COMPARE_FALSE; 980 if ( attr_valfind( a, 981 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 982 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, 983 &op->orc_ava->aa_value, NULL, op->o_tmpmemctx ) == LDAP_SUCCESS ) { 984 ret = LDAP_COMPARE_TRUE; 985 break; 986 } 987 } 988 rs->sr_err = ret; 989 990 if ( r.sr_entry != e ) 991 entry_free( r.sr_entry ); 992 send_ldap_result( op, rs ); 993 } 994 995release:; 996 if ( e != NULL ) { 997 overlay_entry_release_ov( &o, e, 0, on ); 998 } 999 1000 return ret; 1001} 1002 1003#define WANT_MEMBEROF 1 1004#define WANT_MEMBER 2 1005 1006typedef struct dynlist_search_t { 1007 TAvlnode *ds_names; 1008 TAvlnode *ds_fnodes; 1009 dynlist_info_t *ds_dli; 1010 dynlist_map_t *ds_dlm; 1011 Filter *ds_origfilter; 1012 struct berval ds_origfilterbv; 1013 int ds_want; 1014 int ds_found; 1015} dynlist_search_t; 1016 1017static int 1018dynlist_avl_cmp( const void *c1, const void *c2 ) 1019{ 1020 const dynlist_name_t *n1, *n2; 1021 int rc; 1022 n1 = c1; n2 = c2; 1023 1024 rc = n1->dy_name.bv_len - n2->dy_name.bv_len; 1025 if ( rc ) return rc; 1026 return ber_bvcmp( &n1->dy_name, &n2->dy_name ); 1027} 1028 1029/* build a list of dynamic entries */ 1030static int 1031dynlist_search1resp( Operation *op, SlapReply *rs ) 1032{ 1033 if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) { 1034 dynlist_search_t *ds = op->o_callback->sc_private; 1035 Attribute *a, *b = NULL; 1036 1037 if ( ds->ds_dlm && ds->ds_dlm->dlm_static_oc && is_entry_objectclass( rs->sr_entry, ds->ds_dlm->dlm_static_oc, 0 )) 1038 b = attr_find( rs->sr_entry->e_attrs, ds->ds_dlm->dlm_member_ad ); 1039 a = attr_find( rs->sr_entry->e_attrs, ds->ds_dli->dli_ad ); 1040 if ( a || b ) { 1041 unsigned len; 1042 dynlist_name_t *dyn; 1043 struct berval bv, nbase; 1044 LDAPURLDesc *ludp; 1045 int i, j = 0; 1046 1047 if ( a ) 1048 len = a->a_numvals * sizeof(LDAPURLDesc *); 1049 else 1050 len = 0; 1051 1052 dyn = ch_calloc(1, sizeof(dynlist_name_t)+rs->sr_entry->e_nname.bv_len + 1 + len); 1053 dyn->dy_name.bv_val = ((char *)(dyn+1)) + len; 1054 dyn->dy_dli = ds->ds_dli; 1055 dyn->dy_name.bv_len = rs->sr_entry->e_nname.bv_len; 1056 if ( a ) { 1057 Filter *f; 1058 /* parse and validate the URIs */ 1059 for (i=0; i<a->a_numvals; i++) { 1060 if (ldap_url_parse( a->a_vals[i].bv_val, &ludp ) != LDAP_URL_SUCCESS ) 1061 continue; 1062 if (( ludp->lud_host && *ludp->lud_host) 1063 || ludp->lud_exts ) { 1064 skipit: 1065 ldap_free_urldesc( ludp ); 1066 continue; 1067 } 1068 ber_str2bv( ludp->lud_dn, 0, 0, &bv ); 1069 if ( dnNormalize( 0, NULL, NULL, &bv, &nbase, op->o_tmpmemctx ) != LDAP_SUCCESS ) 1070 goto skipit; 1071 ldap_memfree( ludp->lud_dn ); 1072 ludp->lud_dn = ldap_strdup( nbase.bv_val ); 1073 op->o_tmpfree( nbase.bv_val, op->o_tmpmemctx ); 1074 /* cheat here, reuse fields */ 1075 ludp->lud_port = nbase.bv_len; 1076 if ( ludp->lud_filter && *ludp->lud_filter ) { 1077 f = str2filter( ludp->lud_filter ); 1078 if ( f == NULL ) 1079 goto skipit; 1080 ldap_memfree( ludp->lud_filter ); 1081 } else { 1082 f = ch_malloc( sizeof( Filter )); 1083 f->f_choice = SLAPD_FILTER_COMPUTED; 1084 f->f_result = LDAP_COMPARE_TRUE; 1085 f->f_next = NULL; 1086 } 1087 ludp->lud_filter = (char *)f; 1088 dyn->dy_uris[j] = ludp; 1089 j++; 1090 } 1091 } 1092 dyn->dy_numuris = j; 1093 memcpy(dyn->dy_name.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len ); 1094 if ( b ) 1095 dyn->dy_staticmember = ds->ds_dlm->dlm_member_ad; 1096 1097 if ( ldap_tavl_insert( &ds->ds_names, dyn, dynlist_avl_cmp, ldap_avl_dup_error )) { 1098 for (i=dyn->dy_numuris-1; i>=0; i--) { 1099 ludp = dyn->dy_uris[i]; 1100 if ( ludp->lud_filter ) { 1101 filter_free( (Filter *)ludp->lud_filter ); 1102 ludp->lud_filter = NULL; 1103 } 1104 ldap_free_urldesc( ludp ); 1105 } 1106 ch_free( dyn ); 1107 } else { 1108 ds->ds_found++; 1109 } 1110 } 1111 } 1112 return 0; 1113} 1114 1115/* replace a filter clause (memberOf=<groupDN>) with an expansion 1116 * of its dynamic members 1117 * using (&(entryDN=<groupURIbase>)<groupURIfilter>) 1118 */ 1119static int 1120dynlist_filter_dyngroup( Operation *op, Filter *n, Attribute *a ) 1121{ 1122 Filter *andf = NULL, *dnf, *urif, *orf = NULL; 1123 LDAPURLDesc *ludp; 1124 struct berval bv, nbase; 1125 int i; 1126 1127 for (i=0; i<a->a_numvals; i++) { 1128 if ( ldap_url_parse( a->a_vals[i].bv_val, &ludp ) != LDAP_URL_SUCCESS ) 1129 continue; 1130 if (( ludp->lud_host && *ludp->lud_host ) 1131 || ludp->lud_attrs 1132 || ludp->lud_exts ) { 1133 skip: 1134 ldap_free_urldesc( ludp ); 1135 continue; 1136 } 1137 ber_str2bv( ludp->lud_dn, 0, 0, &bv ); 1138 if ( dnNormalize( 0, NULL, NULL, &bv, &nbase, op->o_tmpmemctx ) != LDAP_SUCCESS ) 1139 goto skip; 1140 if ( ludp->lud_filter && *ludp->lud_filter ) { 1141 urif = str2filter_x( op, ludp->lud_filter ); 1142 if ( urif == NULL ) { 1143 op->o_tmpfree( nbase.bv_val, op->o_tmpmemctx ); 1144 goto skip; 1145 } 1146 } else { 1147 urif = NULL; 1148 } 1149 if ( !andf && n->f_choice == SLAPD_FILTER_COMPUTED ) { 1150 andf = n; 1151 andf->f_next = NULL; 1152 } else { 1153 orf = n; 1154 if ( n->f_choice != LDAP_FILTER_OR ) { 1155 andf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); 1156 *andf = *n; 1157 orf->f_choice = LDAP_FILTER_OR; 1158 orf->f_next = NULL; 1159 orf->f_list = andf; 1160 } 1161 andf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); 1162 andf->f_next = orf->f_list; 1163 orf->f_list = andf; 1164 } 1165 dnf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); 1166 andf->f_choice = LDAP_FILTER_AND; 1167 andf->f_list = dnf; 1168 dnf->f_next = urif; 1169 if ( ludp->lud_scope == LDAP_SCOPE_BASE ) { 1170 dnf->f_choice = LDAP_FILTER_EQUALITY; 1171 dnf->f_ava = op->o_tmpcalloc( 1, sizeof(AttributeAssertion), op->o_tmpmemctx ); 1172 dnf->f_av_desc = slap_schema.si_ad_entryDN; 1173 dnf->f_av_value = nbase; 1174 } else { 1175 dnf->f_choice = LDAP_FILTER_EXT; 1176 dnf->f_mra = op->o_tmpcalloc( 1, sizeof(MatchingRuleAssertion), op->o_tmpmemctx ); 1177 dnf->f_mr_desc = slap_schema.si_ad_entryDN; 1178 dnf->f_mr_value = nbase; 1179 switch ( ludp->lud_scope ) { 1180 case LDAP_SCOPE_ONELEVEL: 1181 dnf->f_mr_rule = slap_schema.si_mr_dnOneLevelMatch; 1182 break; 1183 case LDAP_SCOPE_SUBTREE: 1184 dnf->f_mr_rule = slap_schema.si_mr_dnSubtreeMatch; 1185 break; 1186 case LDAP_SCOPE_SUBORDINATE: 1187 dnf->f_mr_rule = slap_schema.si_mr_dnSubordinateMatch; 1188 break; 1189 } 1190 ber_str2bv( dnf->f_mr_rule->smr_names[0], 0, 0, &dnf->f_mr_rule_text ); 1191 } 1192 ldap_free_urldesc( ludp ); 1193 } 1194 if ( !andf ) 1195 return -1; 1196 return 0; 1197} 1198 1199/* replace a filter clause (memberOf=<groupDN>) with an expansion 1200 * of its static members 1201 * using (|(entryDN=<memberN>)[...]) 1202 */ 1203static int 1204dynlist_filter_stgroup( Operation *op, Filter *n, Attribute *a ) 1205{ 1206 Filter *dnf, *orf = NULL; 1207 int i; 1208 1209 if ( a->a_numvals == 1 && n->f_choice == SLAPD_FILTER_COMPUTED ) { 1210 dnf = n; 1211 dnf->f_next = NULL; 1212 } else { 1213 orf = n; 1214 if ( n->f_choice != LDAP_FILTER_OR ) { 1215 dnf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); 1216 *dnf = *n; 1217 orf->f_choice = LDAP_FILTER_OR; 1218 orf->f_next = NULL; 1219 orf->f_list = dnf; 1220 } 1221 dnf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); 1222 dnf->f_next = orf->f_list; 1223 orf->f_list = dnf; 1224 } 1225 1226 for (i=0; i<a->a_numvals; i++) { 1227 if ( i ) { 1228 dnf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); 1229 dnf->f_next = orf->f_list; 1230 orf->f_list = dnf; 1231 } 1232 dnf->f_choice = LDAP_FILTER_EQUALITY; 1233 dnf->f_ava = op->o_tmpcalloc( 1, sizeof(AttributeAssertion), op->o_tmpmemctx ); 1234 dnf->f_av_desc = slap_schema.si_ad_entryDN; 1235 ber_dupbv_x( &dnf->f_av_value, &a->a_nvals[i], op->o_tmpmemctx ); 1236 } 1237 return 0; 1238} 1239 1240/* replace a filter clause (memberOf=<groupDN>) with an expansion of 1241 * its members. 1242 */ 1243static int 1244dynlist_filter_group( Operation *op, dynlist_name_t *dyn, Filter *n, dynlist_search_t *ds ) 1245{ 1246 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1247 Entry *e; 1248 Attribute *a; 1249 int rc = -1; 1250 1251 if ( ldap_tavl_insert( &ds->ds_fnodes, dyn, dynlist_ptr_cmp, ldap_avl_dup_error )) 1252 return 0; 1253 1254 if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &e, on ) != 1255 LDAP_SUCCESS || e == NULL ) { 1256 return -1; 1257 } 1258 if ( ds->ds_dlm->dlm_static_oc && is_entry_objectclass( e, ds->ds_dlm->dlm_static_oc, 0 )) { 1259 a = attr_find( e->e_attrs, ds->ds_dlm->dlm_member_ad ); 1260 if ( a ) { 1261 rc = dynlist_filter_stgroup( op, n, a ); 1262 } 1263 } else { 1264 a = attr_find( e->e_attrs, ds->ds_dli->dli_ad ); 1265 if ( a ) { 1266 rc = dynlist_filter_dyngroup( op, n, a ); 1267 } 1268 } 1269 overlay_entry_release_ov( op, e, 0, on ); 1270 if ( dyn->dy_subs && !rc ) { 1271 TAvlnode *ptr; 1272 for ( ptr = ldap_tavl_end( dyn->dy_subs, TAVL_DIR_LEFT ); ptr; 1273 ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) { 1274 dyn = ptr->avl_data; 1275 rc = dynlist_filter_group( op, dyn, n, ds ); 1276 if ( rc ) 1277 break; 1278 } 1279 } 1280 return rc; 1281} 1282 1283/* Dup the filter, replacing any references to given ad with group evaluation */ 1284static Filter * 1285dynlist_filter_dup( Operation *op, Filter *f, AttributeDescription *ad, dynlist_search_t *ds ) 1286{ 1287 Filter *n = NULL; 1288 1289 if ( !f ) 1290 return NULL; 1291 1292 n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); 1293 n->f_next = NULL; 1294 switch( f->f_choice & SLAPD_FILTER_MASK ) { 1295 case SLAPD_FILTER_COMPUTED: 1296 n->f_choice = f->f_choice; 1297 n->f_result = f->f_result; 1298 break; 1299 1300 case LDAP_FILTER_PRESENT: 1301 n->f_choice = f->f_choice; 1302 n->f_desc = f->f_desc; 1303 break; 1304 1305 case LDAP_FILTER_EQUALITY: 1306 n->f_choice = SLAPD_FILTER_COMPUTED; 1307 if ( f->f_av_desc == ad ) { 1308 dynlist_name_t *dyn = ldap_tavl_find( ds->ds_names, &f->f_av_value, dynlist_avl_cmp ); 1309 if ( dyn && !dynlist_filter_group( op, dyn, n, ds )) 1310 break; 1311 } 1312 /* FALLTHRU */ 1313 case LDAP_FILTER_GE: 1314 case LDAP_FILTER_LE: 1315 case LDAP_FILTER_APPROX: 1316 n->f_choice = f->f_choice; 1317 n->f_ava = f->f_ava; 1318 break; 1319 1320 case LDAP_FILTER_SUBSTRINGS: 1321 n->f_choice = f->f_choice; 1322 n->f_sub = f->f_sub; 1323 break; 1324 1325 case LDAP_FILTER_EXT: 1326 n->f_choice = f->f_choice; 1327 n->f_mra = f->f_mra; 1328 break; 1329 1330 case LDAP_FILTER_NOT: 1331 case LDAP_FILTER_AND: 1332 case LDAP_FILTER_OR: { 1333 Filter **p; 1334 1335 n->f_choice = f->f_choice; 1336 1337 for ( p = &n->f_list, f = f->f_list; f; f = f->f_next ) { 1338 *p = dynlist_filter_dup( op, f, ad, ds ); 1339 if ( !*p ) 1340 continue; 1341 p = &(*p)->f_next; 1342 } 1343 } 1344 break; 1345 } 1346 return n; 1347} 1348 1349static void 1350dynlist_filter_free( Operation *op, Filter *f ) 1351{ 1352 Filter *p, *next; 1353 1354 if ( f == NULL ) 1355 return; 1356 1357 f->f_choice &= SLAPD_FILTER_MASK; 1358 switch( f->f_choice ) { 1359 case LDAP_FILTER_AND: 1360 case LDAP_FILTER_OR: 1361 case LDAP_FILTER_NOT: 1362 for ( p = f->f_list; p; p = next ) { 1363 next = p->f_next; 1364 op->o_tmpfree( p, op->o_tmpmemctx ); 1365 } 1366 break; 1367 default: 1368 op->o_tmpfree( f, op->o_tmpmemctx ); 1369 } 1370} 1371 1372static void 1373dynlist_search_free( void *ptr ) 1374{ 1375 dynlist_name_t *dyn = (dynlist_name_t *)ptr; 1376 LDAPURLDesc *ludp; 1377 int i; 1378 1379 for (i=dyn->dy_numuris-1; i>=0; i--) { 1380 ludp = dyn->dy_uris[i]; 1381 if ( ludp->lud_filter ) { 1382 filter_free( (Filter *)ludp->lud_filter ); 1383 ludp->lud_filter = NULL; 1384 } 1385 ldap_free_urldesc( ludp ); 1386 } 1387 if ( dyn->dy_subs ) 1388 ldap_tavl_free( dyn->dy_subs, NULL ); 1389 if ( dyn->dy_sups ) 1390 ldap_tavl_free( dyn->dy_sups, NULL ); 1391 ch_free( ptr ); 1392} 1393 1394static int 1395dynlist_search_cleanup( Operation *op, SlapReply *rs ) 1396{ 1397 if ( rs->sr_type == REP_RESULT || op->o_abandon || 1398 rs->sr_err == SLAPD_ABANDON ) { 1399 slap_callback *sc = op->o_callback; 1400 dynlist_search_t *ds = op->o_callback->sc_private; 1401 ldap_tavl_free( ds->ds_names, dynlist_search_free ); 1402 if ( ds->ds_fnodes ) 1403 ldap_tavl_free( ds->ds_fnodes, NULL ); 1404 if ( ds->ds_origfilter ) { 1405 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 1406 dynlist_filter_free( op, op->ors_filter ); 1407 op->ors_filter = ds->ds_origfilter; 1408 op->ors_filterstr = ds->ds_origfilterbv; 1409 } 1410 op->o_callback = sc->sc_next; 1411 op->o_tmpfree( sc, op->o_tmpmemctx ); 1412 1413 } 1414 return 0; 1415} 1416 1417static int 1418dynlist_test_membership(Operation *op, dynlist_name_t *dyn, Entry *e) 1419{ 1420 LDAPURLDesc *ludp; 1421 struct berval nbase, bv; 1422 int i, rc = LDAP_COMPARE_FALSE; 1423 if ( dyn->dy_staticmember ) { 1424 Entry *grp; 1425 if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &grp, (slap_overinst *)op->o_bd->bd_info ) == LDAP_SUCCESS && grp ) { 1426 Attribute *a = attr_find( grp->e_attrs, dyn->dy_staticmember ); 1427 if ( a ) { 1428 i = value_find_ex( dyn->dy_staticmember, SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 1429 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, a->a_nvals, &e->e_nname, op->o_tmpmemctx ); 1430 } 1431 overlay_entry_release_ov( op, grp, 0, (slap_overinst *)op->o_bd->bd_info ); 1432 return i == LDAP_SUCCESS ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE; 1433 } 1434 } 1435 for (i=0; i<dyn->dy_numuris; i++) { 1436 ludp = dyn->dy_uris[i]; 1437 nbase.bv_val = ludp->lud_dn; 1438 nbase.bv_len = ludp->lud_port; 1439 if ( ludp->lud_attrs ) 1440 continue; 1441 switch( ludp->lud_scope ) { 1442 case LDAP_SCOPE_BASE: 1443 if ( !dn_match( &nbase, &e->e_nname )) 1444 continue; 1445 break; 1446 case LDAP_SCOPE_ONELEVEL: 1447 dnParent( &e->e_nname, &bv ); 1448 if ( !dn_match( &nbase, &bv )) 1449 continue; 1450 break; 1451 case LDAP_SCOPE_SUBTREE: 1452 if ( !dnIsSuffix( &e->e_nname, &nbase )) 1453 continue; 1454 break; 1455 case LDAP_SCOPE_SUBORDINATE: 1456 if ( dn_match( &nbase, &e->e_nname ) || 1457 !dnIsSuffix( &e->e_nname, &nbase )) 1458 continue; 1459 break; 1460 } 1461 if ( !ludp->lud_filter ) /* there really should always be a filter */ 1462 rc = LDAP_COMPARE_TRUE; 1463 else 1464 rc = test_filter( op, e, (Filter *)ludp->lud_filter ); 1465 if ( rc == LDAP_COMPARE_TRUE ) 1466 break; 1467 } 1468 return rc; 1469} 1470 1471static void 1472dynlist_add_memberOf(Operation *op, SlapReply *rs, dynlist_search_t *ds) 1473{ 1474 TAvlnode *ptr; 1475 Entry *e = rs->sr_entry; 1476 dynlist_name_t *dyn; 1477 Attribute *a; 1478 1479 /* See if there are any memberOf values to attach to this entry */ 1480 for ( ptr = ldap_tavl_end( ds->ds_names, TAVL_DIR_LEFT ); ptr; 1481 ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) { 1482 dynlist_map_t *dlm; 1483 dyn = ptr->avl_data; 1484 for ( dlm = dyn->dy_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 1485 if ( dlm->dlm_memberOf_ad ) { 1486 if ( dynlist_test_membership( op, dyn, e ) == LDAP_COMPARE_TRUE ) { 1487 /* ensure e is modifiable, but do not replace 1488 * sr_entry yet since we have pointers into it */ 1489 if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) && e == rs->sr_entry ) { 1490 e = entry_dup( rs->sr_entry ); 1491 } 1492 a = attr_find( e->e_attrs, dlm->dlm_memberOf_ad ); 1493 if ( a ) { 1494 unsigned slot; 1495 if ( attr_valfind( a, SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX | 1496 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH | 1497 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, 1498 &dyn->dy_name, &slot, NULL ) != LDAP_SUCCESS ) 1499 a = NULL; 1500 } 1501 if ( !a ) 1502 attr_merge_one( e, dlm->dlm_memberOf_ad, &dyn->dy_name, &dyn->dy_name ); 1503 if ( dyn->dy_sups ) { 1504 dynlist_nested_memberOf( e, dlm->dlm_memberOf_ad, dyn->dy_sups ); 1505 } 1506 break; 1507 } 1508 } 1509 } 1510 } 1511 if ( e != rs->sr_entry ) { 1512 rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e ); 1513 rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED; 1514 } 1515} 1516 1517/* process the search responses */ 1518static int 1519dynlist_search2resp( Operation *op, SlapReply *rs ) 1520{ 1521 dynlist_search_t *ds = op->o_callback->sc_private; 1522 dynlist_name_t *dyn; 1523 int rc; 1524 1525 if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) { 1526 rc = SLAP_CB_CONTINUE; 1527 /* See if this is one of our dynamic entries */ 1528 dyn = ldap_tavl_find( ds->ds_names, &rs->sr_entry->e_nname, dynlist_avl_cmp ); 1529 if ( dyn ) { 1530 dyn->dy_seen = 1; 1531 rc = dynlist_prepare_entry( op, rs, dyn->dy_dli, dyn ); 1532 } else if ( ds->ds_want ) 1533 dynlist_add_memberOf( op, rs, ds ); 1534 if ( ds->ds_origfilter && test_filter( op, rs->sr_entry, ds->ds_origfilter ) != LDAP_COMPARE_TRUE ) { 1535 rs_flush_entry( op, rs, NULL ); 1536 return LDAP_SUCCESS; 1537 } 1538 return rc; 1539 } else if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS ) { 1540 TAvlnode *ptr; 1541 SlapReply r = *rs; 1542 Filter *f = ds->ds_origfilter ? ds->ds_origfilter : op->ors_filter; 1543 1544 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) 1545 return SLAP_CB_CONTINUE; 1546 1547 /* Check for any unexpanded dynamic group entries that weren't picked up 1548 * by the original search filter. 1549 */ 1550 for ( ptr = ldap_tavl_end( ds->ds_names, TAVL_DIR_LEFT ); ptr; 1551 ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) { 1552 dyn = ptr->avl_data; 1553 if ( dyn->dy_seen ) 1554 continue; 1555 if ( !dnIsSuffixScope( &dyn->dy_name, &op->o_req_ndn, op->ors_scope )) 1556 continue; 1557 if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &r.sr_entry, (slap_overinst *)op->o_bd->bd_info ) != LDAP_SUCCESS || 1558 r.sr_entry == NULL ) 1559 continue; 1560 r.sr_flags = REP_ENTRY_MUSTRELEASE; 1561 dynlist_prepare_entry( op, &r, dyn->dy_dli, dyn ); 1562 if ( test_filter( op, r.sr_entry, f ) == LDAP_COMPARE_TRUE ) { 1563 r.sr_attrs = op->ors_attrs; 1564 rs->sr_err = send_search_entry( op, &r ); 1565 if ( rs->sr_err != LDAP_SUCCESS ) 1566 break; 1567 } else { 1568 rs_flush_entry( op, &r, NULL ); 1569 } 1570 } 1571 rs->sr_nentries = r.sr_nentries; 1572 } 1573 return SLAP_CB_CONTINUE; 1574} 1575 1576static void 1577dynlist_fix_filter( Operation *op, AttributeDescription *ad, dynlist_search_t *ds ) 1578{ 1579 Filter *f; 1580 f = dynlist_filter_dup( op, op->ors_filter, ad, ds ); 1581 if ( ds->ds_origfilter ) { 1582 dynlist_filter_free( op, op->ors_filter ); 1583 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 1584 } else { 1585 ds->ds_origfilter = op->ors_filter; 1586 ds->ds_origfilterbv = op->ors_filterstr; 1587 } 1588 op->ors_filter = f; 1589 filter2bv_x( op, f, &op->ors_filterstr ); 1590} 1591 1592typedef struct dynlist_link_t { 1593 dynlist_search_t *dl_ds; 1594 dynlist_name_t *dl_sup; 1595} dynlist_link_t; 1596 1597static int 1598dynlist_nestlink_dg( Operation *op, SlapReply *rs ) 1599{ 1600 dynlist_link_t *dll = op->o_callback->sc_private; 1601 dynlist_search_t *ds = dll->dl_ds; 1602 dynlist_name_t *di = dll->dl_sup, *dj; 1603 1604 if ( rs->sr_type != REP_SEARCH ) 1605 return LDAP_SUCCESS; 1606 1607 dj = ldap_tavl_find( dll->dl_ds->ds_names, &rs->sr_entry->e_nname, dynlist_avl_cmp ); 1608 if ( dj ) { 1609 if ( ds->ds_want & WANT_MEMBEROF ) { 1610 ldap_tavl_insert( &dj->dy_sups, di, dynlist_ptr_cmp, ldap_avl_dup_error ); 1611 } 1612 if ( ds->ds_want & WANT_MEMBER ) { 1613 ldap_tavl_insert( &di->dy_subs, dj, dynlist_ptr_cmp, ldap_avl_dup_error ); 1614 } 1615 } 1616 return LDAP_SUCCESS; 1617} 1618 1619/* Connect all nested groups to their parents/children */ 1620static void 1621dynlist_nestlink( Operation *op, dynlist_search_t *ds ) 1622{ 1623 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1624 dynlist_name_t *di, *dj; 1625 TAvlnode *ptr; 1626 Entry *e; 1627 Attribute *a; 1628 int i; 1629 1630 for ( ptr = ldap_tavl_end( ds->ds_names, TAVL_DIR_LEFT ); ptr; 1631 ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) { 1632 di = ptr->avl_data; 1633 if ( ds->ds_dlm ) { 1634 if ( overlay_entry_get_ov( op, &di->dy_name, NULL, NULL, 0, &e, on ) != LDAP_SUCCESS || e == NULL ) 1635 continue; 1636 a = attr_find( e->e_attrs, ds->ds_dlm->dlm_member_ad ); 1637 if ( a ) { 1638 for ( i=0; i < a->a_numvals; i++ ) { 1639 dj = ldap_tavl_find( ds->ds_names, &a->a_nvals[i], dynlist_avl_cmp ); 1640 if ( dj ) { 1641 if ( ds->ds_want & WANT_MEMBEROF ) { 1642 ldap_tavl_insert( &dj->dy_sups, di, dynlist_ptr_cmp, ldap_avl_dup_error ); 1643 } 1644 if ( ds->ds_want & WANT_MEMBER ) { 1645 ldap_tavl_insert( &di->dy_subs, dj, dynlist_ptr_cmp, ldap_avl_dup_error ); 1646 } 1647 } 1648 } 1649 } 1650 overlay_entry_release_ov( op, e, 0, on ); 1651 } 1652 1653 if ( di->dy_numuris ) { 1654 slap_callback cb = { 0 }; 1655 dynlist_link_t dll; 1656 dll.dl_ds = ds; 1657 dll.dl_sup = di; 1658 cb.sc_private = &dll; 1659 cb.sc_response = dynlist_nestlink_dg; 1660 dynlist_urlmembers( op, di, &cb ); 1661 } 1662 } 1663} 1664 1665static int 1666dynlist_search( Operation *op, SlapReply *rs ) 1667{ 1668 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 1669 dynlist_gen_t *dlg = (dynlist_gen_t *)on->on_bi.bi_private; 1670 dynlist_info_t *dli; 1671 Operation o = *op; 1672 dynlist_map_t *dlm; 1673 Filter f[3]; 1674 AttributeAssertion ava[2]; 1675 AttributeName an[2] = {0}; 1676 1677 slap_callback *sc; 1678 dynlist_search_t *ds; 1679 ObjectClass *static_oc; 1680 int nested, found, tmpwant; 1681 int opattrs, userattrs; 1682 1683 if ( get_manageDSAit( op ) ) 1684 return SLAP_CB_CONTINUE; 1685 1686 sc = op->o_tmpcalloc( 1, sizeof(slap_callback)+sizeof(dynlist_search_t), op->o_tmpmemctx ); 1687 sc->sc_private = (void *)(sc+1); 1688 ds = sc->sc_private; 1689 1690 memset( o.o_ctrlflag, 0, sizeof( o.o_ctrlflag )); 1691 o.o_managedsait = SLAP_CONTROL_CRITICAL; 1692 1693 /* Are we using memberOf, and does it affect this request? */ 1694 if ( dlg->dlg_memberOf ) { 1695 int attrflags = slap_attr_flags( op->ors_attrs ); 1696 opattrs = SLAP_OPATTRS( attrflags ); 1697 userattrs = SLAP_USERATTRS( attrflags ); 1698 } 1699 1700 /* Find all groups in scope. For group expansion 1701 * we only need the groups within the search scope, but 1702 * for memberOf populating, we need all dyngroups. 1703 */ 1704 for ( dli = dlg->dlg_dli; dli; dli = dli->dli_next ) { 1705 static_oc = NULL; 1706 nested = 0; 1707 tmpwant = 0; 1708 if ( dlg->dlg_memberOf ) { 1709 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 1710 if ( dlm->dlm_memberOf_ad ) { 1711 int want = 0; 1712 1713 /* is attribute in filter? */ 1714 if ( ad_infilter( dlm->dlm_memberOf_ad, op->ors_filter )) { 1715 want |= WANT_MEMBEROF; 1716 /* with nesting, filter attributes also require nestlink */ 1717 if ( dlm->dlm_memberOf_nested ) { 1718 /* WANT_ flags have inverted meaning here: 1719 * to satisfy (memberOf=) filter, we need to also 1720 * find all subordinate groups. No special 1721 * treatment is needed for (member=) since we 1722 * already search all group entries. 1723 */ 1724 want |= WANT_MEMBER; 1725 } 1726 } 1727 1728 /* if attribute is not requested, skip it */ 1729 if ( op->ors_attrs == NULL ) { 1730 if ( !dlm->dlm_memberOf_oper ) { 1731 want |= WANT_MEMBEROF; 1732 if ( dlm->dlm_memberOf_nested && !dlm->dlm_member_oper ) 1733 want |= WANT_MEMBER; 1734 } 1735 } else { 1736 if ( ad_inlist( dlm->dlm_memberOf_ad, op->ors_attrs )) { 1737 want |= WANT_MEMBEROF; 1738 if ( dlm->dlm_memberOf_nested && ad_inlist( dlm->dlm_member_ad, op->ors_attrs )) 1739 want |= WANT_MEMBER; 1740 } else { 1741 if ( opattrs ) { 1742 if ( dlm->dlm_memberOf_oper ) { 1743 want |= WANT_MEMBEROF; 1744 if ( dlm->dlm_memberOf_nested && dlm->dlm_member_oper ) 1745 want |= WANT_MEMBER; 1746 } 1747 } 1748 if ( userattrs ) { 1749 if ( !dlm->dlm_memberOf_oper ) { 1750 want |= WANT_MEMBEROF; 1751 if ( dlm->dlm_memberOf_nested && !dlm->dlm_member_oper ) 1752 want |= WANT_MEMBER; 1753 } 1754 } 1755 } 1756 } 1757 if ( want ) { 1758 nested = dlm->dlm_memberOf_nested; 1759 ds->ds_want = tmpwant = want; 1760 if ( dlm->dlm_static_oc ) { 1761 static_oc = dlm->dlm_static_oc; 1762 ds->ds_dlm = dlm; 1763 } 1764 } 1765 } 1766 } 1767 } 1768 1769 if ( static_oc ) { 1770 f[0].f_choice = LDAP_FILTER_OR; 1771 f[0].f_list = &f[1]; 1772 f[0].f_next = NULL; 1773 f[1].f_choice = LDAP_FILTER_EQUALITY; 1774 f[1].f_next = &f[2]; 1775 f[1].f_ava = &ava[0]; 1776 f[1].f_av_desc = slap_schema.si_ad_objectClass; 1777 f[1].f_av_value = dli->dli_oc->soc_cname; 1778 f[2].f_choice = LDAP_FILTER_EQUALITY; 1779 f[2].f_ava = &ava[1]; 1780 f[2].f_av_desc = slap_schema.si_ad_objectClass; 1781 f[2].f_av_value = static_oc->soc_cname; 1782 f[2].f_next = NULL; 1783 } else { 1784 f[0].f_choice = LDAP_FILTER_EQUALITY; 1785 f[0].f_ava = ava; 1786 f[0].f_av_desc = slap_schema.si_ad_objectClass; 1787 f[0].f_av_value = dli->dli_oc->soc_cname; 1788 f[0].f_next = NULL; 1789 } 1790 1791 if ( o.o_callback != sc ) { 1792 o.o_callback = sc; 1793 o.ors_filter = f; 1794 if ( tmpwant ) { 1795 o.o_req_dn = op->o_bd->be_suffix[0]; 1796 o.o_req_ndn = op->o_bd->be_nsuffix[0]; 1797 o.ors_scope = LDAP_SCOPE_SUBTREE; 1798 } else { 1799 o.o_req_dn = op->o_req_dn; 1800 o.o_req_ndn = op->o_req_ndn; 1801 o.ors_scope = op->ors_scope; 1802 } 1803 o.ors_attrsonly = 0; 1804 o.ors_attrs = an; 1805 o.o_bd = select_backend( op->o_bd->be_nsuffix, 1 ); 1806 BER_BVZERO( &o.ors_filterstr ); 1807 sc->sc_response = dynlist_search1resp; 1808 } 1809 1810 ds->ds_dli = dli; 1811 if ( o.ors_filterstr.bv_val ) 1812 o.o_tmpfree( o.ors_filterstr.bv_val, o.o_tmpmemctx ); 1813 filter2bv_x( &o, f, &o.ors_filterstr ); 1814 an[0].an_desc = dli->dli_ad; 1815 an[0].an_name = dli->dli_ad->ad_cname; 1816 found = ds->ds_found; 1817 { 1818 SlapReply r = { REP_SEARCH }; 1819 (void)o.o_bd->be_search( &o, &r ); 1820 } 1821 if ( found != ds->ds_found && nested ) 1822 dynlist_nestlink( op, ds ); 1823 } 1824 1825 if ( ds->ds_names != NULL ) { 1826 sc->sc_response = dynlist_search2resp; 1827 sc->sc_cleanup = dynlist_search_cleanup; 1828 sc->sc_next = op->o_callback; 1829 op->o_callback = sc; 1830 1831 /* see if filter needs fixing */ 1832 if ( dlg->dlg_memberOf ) { 1833 for ( dli = dlg->dlg_dli; dli; dli = dli->dli_next ) { 1834 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 1835 if ( dlm->dlm_memberOf_ad ) { 1836 1837 /* if attribute is in filter, fix it */ 1838 if ( ad_infilter( dlm->dlm_memberOf_ad, op->ors_filter )) { 1839 ds->ds_dli = dli; 1840 ds->ds_dlm = dlm; 1841 dynlist_fix_filter( op, dlm->dlm_memberOf_ad, ds ); 1842 } 1843 } 1844 } 1845 } 1846 } 1847 1848 } else { 1849 op->o_tmpfree( sc, op->o_tmpmemctx ); 1850 } 1851 return SLAP_CB_CONTINUE; 1852} 1853 1854static int 1855dynlist_build_def_filter( dynlist_info_t *dli ) 1856{ 1857 char *ptr; 1858 1859 dli->dli_default_filter.bv_len = STRLENOF( "(!(objectClass=" "))" ) 1860 + dli->dli_oc->soc_cname.bv_len; 1861 dli->dli_default_filter.bv_val = ch_malloc( dli->dli_default_filter.bv_len + 1 ); 1862 if ( dli->dli_default_filter.bv_val == NULL ) { 1863 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: malloc failed.\n" ); 1864 return -1; 1865 } 1866 1867 ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" ); 1868 ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val ); 1869 ptr = lutil_strcopy( ptr, "))" ); 1870 1871 assert( ptr == &dli->dli_default_filter.bv_val[dli->dli_default_filter.bv_len] ); 1872 1873 return 0; 1874} 1875 1876enum { 1877 DL_ATTRSET = 1, 1878 DL_ATTRPAIR, 1879 DL_ATTRPAIR_COMPAT, 1880 DL_LAST 1881}; 1882 1883static ConfigDriver dl_cfgen; 1884 1885/* XXXmanu 255 is the maximum arguments we allow. Can we go beyond? */ 1886static ConfigTable dlcfg[] = { 1887 { "dynlist-attrset", "group-oc> [uri] <URL-ad> <[mapped:]member-ad> [...]", 1888 3, 0, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen, 1889 "( OLcfgOvAt:8.1 NAME ( 'olcDynListAttrSet' 'olcDlAttrSet' ) " 1890 "DESC 'Dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' " 1891 "EQUALITY caseIgnoreMatch " 1892 "SYNTAX OMsDirectoryString " 1893 "X-ORDERED 'VALUES' )", 1894 NULL, NULL }, 1895 { "dynlist-attrpair", "member-ad> <URL-ad", 1896 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR, dl_cfgen, 1897 NULL, NULL, NULL }, 1898#ifdef TAKEOVER_DYNGROUP 1899 { "attrpair", "member-ad> <URL-ad", 1900 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR_COMPAT, dl_cfgen, 1901 NULL, NULL, NULL }, 1902#endif 1903 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 1904}; 1905 1906static ConfigOCs dlocs[] = { 1907 { "( OLcfgOvOc:8.1 " 1908 "NAME ( 'olcDynListConfig' 'olcDynamicList' ) " 1909 "DESC 'Dynamic list configuration' " 1910 "SUP olcOverlayConfig " 1911 "MAY olcDynListAttrSet )", 1912 Cft_Overlay, dlcfg, NULL, NULL }, 1913 { NULL, 0, NULL } 1914}; 1915 1916static int 1917dl_cfgen( ConfigArgs *c ) 1918{ 1919 slap_overinst *on = (slap_overinst *)c->bi; 1920 dynlist_gen_t *dlg = (dynlist_gen_t *)on->on_bi.bi_private; 1921 dynlist_info_t *dli = dlg->dlg_dli; 1922 1923 int rc = 0, i; 1924 1925 if ( c->op == SLAP_CONFIG_EMIT ) { 1926 switch( c->type ) { 1927 case DL_ATTRSET: 1928 for ( i = 0; dli; i++, dli = dli->dli_next ) { 1929 struct berval bv; 1930 char *ptr = c->cr_msg; 1931 dynlist_map_t *dlm; 1932 1933 assert( dli->dli_oc != NULL ); 1934 assert( dli->dli_ad != NULL ); 1935 1936 /* FIXME: check buffer overflow! */ 1937 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ), 1938 SLAP_X_ORDERED_FMT "%s", i, 1939 dli->dli_oc->soc_cname.bv_val ); 1940 1941 if ( !BER_BVISNULL( &dli->dli_uri ) ) { 1942 *ptr++ = ' '; 1943 *ptr++ = '"'; 1944 ptr = lutil_strncopy( ptr, dli->dli_uri.bv_val, 1945 dli->dli_uri.bv_len ); 1946 *ptr++ = '"'; 1947 } 1948 1949 *ptr++ = ' '; 1950 ptr = lutil_strncopy( ptr, dli->dli_ad->ad_cname.bv_val, 1951 dli->dli_ad->ad_cname.bv_len ); 1952 1953 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) { 1954 ptr[ 0 ] = ' '; 1955 ptr++; 1956 if ( dlm->dlm_mapped_ad ) { 1957 ptr = lutil_strcopy( ptr, dlm->dlm_mapped_ad->ad_cname.bv_val ); 1958 ptr[ 0 ] = ':'; 1959 ptr++; 1960 } 1961 1962 ptr = lutil_strcopy( ptr, dlm->dlm_member_ad->ad_cname.bv_val ); 1963 1964 if ( dlm->dlm_memberOf_ad ) { 1965 *ptr++ = '+'; 1966 ptr = lutil_strcopy( ptr, dlm->dlm_memberOf_ad->ad_cname.bv_val ); 1967 if ( dlm->dlm_static_oc ) { 1968 *ptr++ = '@'; 1969 ptr = lutil_strcopy( ptr, dlm->dlm_static_oc->soc_cname.bv_val ); 1970 } 1971 if ( dlm->dlm_memberOf_nested ) { 1972 *ptr++ = '*'; 1973 } 1974 } 1975 } 1976 1977 bv.bv_val = c->cr_msg; 1978 bv.bv_len = ptr - bv.bv_val; 1979 value_add_one( &c->rvalue_vals, &bv ); 1980 } 1981 break; 1982 1983 case DL_ATTRPAIR_COMPAT: 1984 case DL_ATTRPAIR: 1985 rc = 1; 1986 break; 1987 1988 default: 1989 rc = 1; 1990 break; 1991 } 1992 1993 return rc; 1994 1995 } else if ( c->op == LDAP_MOD_DELETE ) { 1996 switch( c->type ) { 1997 case DL_ATTRSET: 1998 if ( c->valx < 0 ) { 1999 dynlist_info_t *dli_next; 2000 2001 for ( dli_next = dli; dli_next; dli = dli_next ) { 2002 dynlist_map_t *dlm = dli->dli_dlm; 2003 dynlist_map_t *dlm_next; 2004 2005 dli_next = dli->dli_next; 2006 2007 if ( !BER_BVISNULL( &dli->dli_uri ) ) { 2008 ch_free( dli->dli_uri.bv_val ); 2009 } 2010 2011 if ( dli->dli_lud != NULL ) { 2012 ldap_free_urldesc( dli->dli_lud ); 2013 } 2014 2015 if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) { 2016 ber_memfree( dli->dli_uri_nbase.bv_val ); 2017 } 2018 2019 if ( dli->dli_uri_filter != NULL ) { 2020 filter_free( dli->dli_uri_filter ); 2021 } 2022 2023 ch_free( dli->dli_default_filter.bv_val ); 2024 2025 while ( dlm != NULL ) { 2026 dlm_next = dlm->dlm_next; 2027 ch_free( dlm ); 2028 dlm = dlm_next; 2029 } 2030 ch_free( dli ); 2031 } 2032 2033 dlg->dlg_dli = NULL; 2034 dlg->dlg_memberOf = 0; 2035 2036 } else { 2037 dynlist_info_t **dlip; 2038 dynlist_map_t *dlm; 2039 dynlist_map_t *dlm_next; 2040 2041 for ( i = 0, dlip = (dynlist_info_t **)&dlg->dlg_dli; 2042 i < c->valx; i++ ) 2043 { 2044 if ( *dlip == NULL ) { 2045 return 1; 2046 } 2047 dlip = &(*dlip)->dli_next; 2048 } 2049 2050 dli = *dlip; 2051 *dlip = dli->dli_next; 2052 2053 if ( !BER_BVISNULL( &dli->dli_uri ) ) { 2054 ch_free( dli->dli_uri.bv_val ); 2055 } 2056 2057 if ( dli->dli_lud != NULL ) { 2058 ldap_free_urldesc( dli->dli_lud ); 2059 } 2060 2061 if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) { 2062 ber_memfree( dli->dli_uri_nbase.bv_val ); 2063 } 2064 2065 if ( dli->dli_uri_filter != NULL ) { 2066 filter_free( dli->dli_uri_filter ); 2067 } 2068 2069 ch_free( dli->dli_default_filter.bv_val ); 2070 2071 dlm = dli->dli_dlm; 2072 while ( dlm != NULL ) { 2073 dlm_next = dlm->dlm_next; 2074 if ( dlm->dlm_memberOf_ad ) 2075 dlg->dlg_memberOf--; 2076 ch_free( dlm ); 2077 dlm = dlm_next; 2078 } 2079 ch_free( dli ); 2080 2081 dli = (dynlist_info_t *)dlg->dlg_dli; 2082 } 2083 break; 2084 2085 case DL_ATTRPAIR_COMPAT: 2086 case DL_ATTRPAIR: 2087 rc = 1; 2088 break; 2089 2090 default: 2091 rc = 1; 2092 break; 2093 } 2094 2095 return rc; 2096 } 2097 2098 switch( c->type ) { 2099 case DL_ATTRSET: { 2100 dynlist_info_t **dlip, 2101 *dli_next = NULL; 2102 ObjectClass *oc = NULL; 2103 AttributeDescription *ad = NULL; 2104 int attridx = 2; 2105 LDAPURLDesc *lud = NULL; 2106 struct berval nbase = BER_BVNULL; 2107 Filter *filter = NULL; 2108 struct berval uri = BER_BVNULL; 2109 dynlist_map_t *dlm = NULL, *dlml = NULL; 2110 const char *text; 2111 2112 oc = oc_find( c->argv[ 1 ] ); 2113 if ( oc == NULL ) { 2114 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 2115 "unable to find ObjectClass \"%s\"", 2116 c->argv[ 1 ] ); 2117 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2118 c->log, c->cr_msg ); 2119 return 1; 2120 } 2121 2122 if ( strncasecmp( c->argv[ attridx ], "ldap://", STRLENOF("ldap://") ) == 0 ) { 2123 if ( ldap_url_parse( c->argv[ attridx ], &lud ) != LDAP_URL_SUCCESS ) { 2124 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 2125 "unable to parse URI \"%s\"", 2126 c->argv[ attridx ] ); 2127 rc = 1; 2128 goto done_uri; 2129 } 2130 2131 if ( lud->lud_host != NULL ) { 2132 if ( lud->lud_host[0] == '\0' ) { 2133 ch_free( lud->lud_host ); 2134 lud->lud_host = NULL; 2135 2136 } else { 2137 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 2138 "host not allowed in URI \"%s\"", 2139 c->argv[ attridx ] ); 2140 rc = 1; 2141 goto done_uri; 2142 } 2143 } 2144 2145 if ( lud->lud_attrs != NULL ) { 2146 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 2147 "attrs not allowed in URI \"%s\"", 2148 c->argv[ attridx ] ); 2149 rc = 1; 2150 goto done_uri; 2151 } 2152 2153 if ( lud->lud_exts != NULL ) { 2154 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 2155 "extensions not allowed in URI \"%s\"", 2156 c->argv[ attridx ] ); 2157 rc = 1; 2158 goto done_uri; 2159 } 2160 2161 if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' ) { 2162 struct berval dn; 2163 ber_str2bv( lud->lud_dn, 0, 0, &dn ); 2164 rc = dnNormalize( 0, NULL, NULL, &dn, &nbase, NULL ); 2165 if ( rc != LDAP_SUCCESS ) { 2166 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 2167 "DN normalization failed in URI \"%s\"", 2168 c->argv[ attridx ] ); 2169 goto done_uri; 2170 } 2171 } 2172 2173 if ( lud->lud_filter != NULL && lud->lud_filter[ 0 ] != '\0' ) { 2174 filter = str2filter( lud->lud_filter ); 2175 if ( filter == NULL ) { 2176 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 2177 "filter parsing failed in URI \"%s\"", 2178 c->argv[ attridx ] ); 2179 rc = 1; 2180 goto done_uri; 2181 } 2182 } 2183 2184 ber_str2bv( c->argv[ attridx ], 0, 1, &uri ); 2185 2186done_uri:; 2187 if ( rc ) { 2188 if ( lud ) { 2189 ldap_free_urldesc( lud ); 2190 } 2191 2192 if ( !BER_BVISNULL( &nbase ) ) { 2193 ber_memfree( nbase.bv_val ); 2194 } 2195 2196 if ( filter != NULL ) { 2197 filter_free( filter ); 2198 } 2199 2200 while ( dlm != NULL ) { 2201 dlml = dlm; 2202 dlm = dlm->dlm_next; 2203 ch_free( dlml ); 2204 } 2205 2206 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2207 c->log, c->cr_msg ); 2208 2209 return rc; 2210 } 2211 2212 attridx++; 2213 } 2214 2215 rc = slap_str2ad( c->argv[ attridx ], &ad, &text ); 2216 if ( rc != LDAP_SUCCESS ) { 2217 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 2218 "unable to find AttributeDescription \"%s\"", 2219 c->argv[ attridx ] ); 2220 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2221 c->log, c->cr_msg ); 2222 rc = 1; 2223 goto done_uri; 2224 } 2225 2226 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) { 2227 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE 2228 "AttributeDescription \"%s\" " 2229 "must be a subtype of \"labeledURI\"", 2230 c->argv[ attridx ] ); 2231 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2232 c->log, c->cr_msg ); 2233 rc = 1; 2234 goto done_uri; 2235 } 2236 2237 attridx++; 2238 2239 for ( i = attridx; i < c->argc; i++ ) { 2240 char *arg; 2241 char *cp; 2242 AttributeDescription *member_ad = NULL; 2243 AttributeDescription *mapped_ad = NULL; 2244 AttributeDescription *memberOf_ad = NULL; 2245 ObjectClass *static_oc = NULL; 2246 int nested = 0; 2247 dynlist_map_t *dlmp; 2248 2249 2250 /* 2251 * If no mapped attribute is given, dn is used 2252 * for backward compatibility. 2253 */ 2254 arg = c->argv[i]; 2255 if ( ( cp = strchr( arg, ':' ) ) != NULL ) { 2256 struct berval bv; 2257 ber_str2bv( arg, cp - arg, 0, &bv ); 2258 rc = slap_bv2ad( &bv, &mapped_ad, &text ); 2259 if ( rc != LDAP_SUCCESS ) { 2260 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2261 DYNLIST_USAGE 2262 "unable to find mapped AttributeDescription #%d \"%s\"\n", 2263 i - 3, c->argv[ i ] ); 2264 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2265 c->log, c->cr_msg ); 2266 rc = 1; 2267 goto done_uri; 2268 } 2269 arg = cp + 1; 2270 } 2271 if ( ( cp = strchr( arg, '+' ) ) != NULL ) { 2272 struct berval bv; 2273 char *ocp, *np; 2274 np = strrchr( cp+1, '*' ); 2275 if ( np ) { 2276 nested = 1; 2277 *np = '\0'; 2278 } 2279 ocp = strchr( cp+1, '@' ); 2280 if ( ocp ) { 2281 static_oc = oc_find( ocp+1 ); 2282 if ( !static_oc ) { 2283 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2284 DYNLIST_USAGE 2285 "unable to find static-oc ObjectClass #%d \"%s\"\n", 2286 i - 3, c->argv[ i ] ); 2287 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2288 c->log, c->cr_msg ); 2289 rc = 1; 2290 goto done_uri; 2291 } 2292 *ocp = '\0'; 2293 } 2294 ber_str2bv( cp+1, 0, 0, &bv ); 2295 rc = slap_bv2ad( &bv, &memberOf_ad, &text ); 2296 if ( rc != LDAP_SUCCESS ) { 2297 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2298 DYNLIST_USAGE 2299 "unable to find memberOf AttributeDescription #%d \"%s\"\n", 2300 i - 3, c->argv[ i ] ); 2301 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2302 c->log, c->cr_msg ); 2303 rc = 1; 2304 goto done_uri; 2305 } 2306 dlg->dlg_memberOf++; 2307 *cp = '\0'; 2308 } 2309 2310 rc = slap_str2ad( arg, &member_ad, &text ); 2311 if ( rc != LDAP_SUCCESS ) { 2312 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2313 DYNLIST_USAGE 2314 "unable to find AttributeDescription #%d \"%s\"\n", 2315 i - 3, c->argv[ i ] ); 2316 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2317 c->log, c->cr_msg ); 2318 rc = 1; 2319 goto done_uri; 2320 } 2321 2322 dlmp = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) ); 2323 if ( dlm == NULL ) { 2324 dlm = dlmp; 2325 } 2326 dlmp->dlm_member_ad = member_ad; 2327 dlmp->dlm_mapped_ad = mapped_ad; 2328 dlmp->dlm_memberOf_ad = memberOf_ad; 2329 dlmp->dlm_static_oc = static_oc; 2330 dlmp->dlm_memberOf_nested = nested; 2331 dlmp->dlm_member_oper = is_at_operational( member_ad->ad_type ); 2332 if ( memberOf_ad ) { 2333 dlmp->dlm_memberOf_oper = is_at_operational( memberOf_ad->ad_type ); 2334 } else { 2335 dlmp->dlm_memberOf_oper = 0; 2336 } 2337 dlmp->dlm_next = NULL; 2338 2339 if ( dlml != NULL ) 2340 dlml->dlm_next = dlmp; 2341 dlml = dlmp; 2342 } 2343 2344 if ( c->valx > 0 ) { 2345 int i; 2346 2347 for ( i = 0, dlip = (dynlist_info_t **)&dlg->dlg_dli; 2348 i < c->valx; i++ ) 2349 { 2350 if ( *dlip == NULL ) { 2351 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2352 DYNLIST_USAGE 2353 "invalid index {%d}\n", 2354 c->valx ); 2355 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2356 c->log, c->cr_msg ); 2357 rc = 1; 2358 goto done_uri; 2359 } 2360 dlip = &(*dlip)->dli_next; 2361 } 2362 dli_next = *dlip; 2363 2364 } else { 2365 for ( dlip = (dynlist_info_t **)&dlg->dlg_dli; 2366 *dlip; dlip = &(*dlip)->dli_next ) 2367 /* goto last */; 2368 } 2369 2370 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) ); 2371 2372 (*dlip)->dli_oc = oc; 2373 (*dlip)->dli_ad = ad; 2374 (*dlip)->dli_dlm = dlm; 2375 (*dlip)->dli_next = dli_next; 2376 2377 (*dlip)->dli_lud = lud; 2378 (*dlip)->dli_uri_nbase = nbase; 2379 (*dlip)->dli_uri_filter = filter; 2380 (*dlip)->dli_uri = uri; 2381 2382 rc = dynlist_build_def_filter( *dlip ); 2383 2384 } break; 2385 2386 case DL_ATTRPAIR_COMPAT: 2387 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2388 "warning: \"attrpair\" only supported for limited " 2389 "backward compatibility with overlay \"dyngroup\"" ); 2390 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg ); 2391 /* fallthru */ 2392 2393 case DL_ATTRPAIR: { 2394 dynlist_info_t **dlip; 2395 ObjectClass *oc = NULL; 2396 AttributeDescription *ad = NULL, 2397 *member_ad = NULL; 2398 const char *text; 2399 2400 oc = oc_find( "groupOfURLs" ); 2401 if ( oc == NULL ) { 2402 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2403 "\"dynlist-attrpair <member-ad> <URL-ad>\": " 2404 "unable to find default ObjectClass \"groupOfURLs\"" ); 2405 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2406 c->log, c->cr_msg ); 2407 return 1; 2408 } 2409 2410 rc = slap_str2ad( c->argv[ 1 ], &member_ad, &text ); 2411 if ( rc != LDAP_SUCCESS ) { 2412 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2413 "\"dynlist-attrpair <member-ad> <URL-ad>\": " 2414 "unable to find AttributeDescription \"%s\"", 2415 c->argv[ 1 ] ); 2416 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2417 c->log, c->cr_msg ); 2418 return 1; 2419 } 2420 2421 rc = slap_str2ad( c->argv[ 2 ], &ad, &text ); 2422 if ( rc != LDAP_SUCCESS ) { 2423 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2424 "\"dynlist-attrpair <member-ad> <URL-ad>\": " 2425 "unable to find AttributeDescription \"%s\"\n", 2426 c->argv[ 2 ] ); 2427 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2428 c->log, c->cr_msg ); 2429 return 1; 2430 } 2431 2432 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) { 2433 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2434 DYNLIST_USAGE 2435 "AttributeDescription \"%s\" " 2436 "must be a subtype of \"labeledURI\"", 2437 c->argv[ 2 ] ); 2438 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2439 c->log, c->cr_msg ); 2440 return 1; 2441 } 2442 2443 for ( dlip = (dynlist_info_t **)&dlg->dlg_dli; 2444 *dlip; dlip = &(*dlip)->dli_next ) 2445 { 2446 /* 2447 * The same URL attribute / member attribute pair 2448 * cannot be repeated, but we enforce this only 2449 * when the member attribute is unique. Performing 2450 * the check for multiple values would require 2451 * sorting and comparing the lists, which is left 2452 * as a future improvement 2453 */ 2454 if ( (*dlip)->dli_ad == ad && 2455 (*dlip)->dli_dlm->dlm_next == NULL && 2456 member_ad == (*dlip)->dli_dlm->dlm_member_ad ) { 2457 snprintf( c->cr_msg, sizeof( c->cr_msg ), 2458 "\"dynlist-attrpair <member-ad> <URL-ad>\": " 2459 "URL attributeDescription \"%s\" already mapped.\n", 2460 ad->ad_cname.bv_val ); 2461 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", 2462 c->log, c->cr_msg ); 2463#if 0 2464 /* make it a warning... */ 2465 return 1; 2466#endif 2467 } 2468 } 2469 2470 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) ); 2471 2472 (*dlip)->dli_oc = oc; 2473 (*dlip)->dli_ad = ad; 2474 (*dlip)->dli_dlm = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) ); 2475 (*dlip)->dli_dlm->dlm_member_ad = member_ad; 2476 (*dlip)->dli_dlm->dlm_mapped_ad = NULL; 2477 2478 rc = dynlist_build_def_filter( *dlip ); 2479 2480 } break; 2481 2482 default: 2483 rc = 1; 2484 break; 2485 } 2486 2487 return rc; 2488} 2489 2490static int 2491dynlist_db_init( 2492 BackendDB *be, 2493 ConfigReply *cr) 2494{ 2495 slap_overinst *on = (slap_overinst *)be->bd_info; 2496 dynlist_gen_t *dlg; 2497 2498 dlg = (dynlist_gen_t *)ch_malloc( sizeof( *dlg )); 2499 on->on_bi.bi_private = dlg; 2500 dlg->dlg_dli = NULL; 2501 dlg->dlg_memberOf = 0; 2502 2503 return 0; 2504} 2505 2506static int 2507dynlist_db_open( 2508 BackendDB *be, 2509 ConfigReply *cr ) 2510{ 2511 slap_overinst *on = (slap_overinst *) be->bd_info; 2512 dynlist_gen_t *dlg = (dynlist_gen_t *)on->on_bi.bi_private; 2513 dynlist_info_t *dli = dlg->dlg_dli; 2514 ObjectClass *oc = NULL; 2515 AttributeDescription *ad = NULL; 2516 const char *text; 2517 int rc; 2518 2519 if ( dli == NULL ) { 2520 dli = ch_calloc( 1, sizeof( dynlist_info_t ) ); 2521 dlg->dlg_dli = dli; 2522 } 2523 2524 for ( ; dli; dli = dli->dli_next ) { 2525 if ( dli->dli_oc == NULL ) { 2526 if ( oc == NULL ) { 2527 oc = oc_find( "groupOfURLs" ); 2528 if ( oc == NULL ) { 2529 snprintf( cr->msg, sizeof( cr->msg), 2530 "unable to fetch objectClass \"groupOfURLs\"" ); 2531 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg ); 2532 return 1; 2533 } 2534 } 2535 2536 dli->dli_oc = oc; 2537 } 2538 2539 if ( dli->dli_ad == NULL ) { 2540 if ( ad == NULL ) { 2541 rc = slap_str2ad( "memberURL", &ad, &text ); 2542 if ( rc != LDAP_SUCCESS ) { 2543 snprintf( cr->msg, sizeof( cr->msg), 2544 "unable to fetch attributeDescription \"memberURL\": %d (%s)", 2545 rc, text ); 2546 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg ); 2547 return 1; 2548 } 2549 } 2550 2551 dli->dli_ad = ad; 2552 } 2553 2554 if ( BER_BVISNULL( &dli->dli_default_filter ) ) { 2555 rc = dynlist_build_def_filter( dli ); 2556 if ( rc != 0 ) { 2557 return rc; 2558 } 2559 } 2560 } 2561 2562 if ( ad_dgIdentity == NULL ) { 2563 rc = slap_str2ad( "dgIdentity", &ad_dgIdentity, &text ); 2564 if ( rc != LDAP_SUCCESS ) { 2565 snprintf( cr->msg, sizeof( cr->msg), 2566 "unable to fetch attributeDescription \"dgIdentity\": %d (%s)", 2567 rc, text ); 2568 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg ); 2569 /* Just a warning */ 2570 } 2571 } 2572 2573 if ( ad_dgAuthz == NULL ) { 2574 rc = slap_str2ad( "dgAuthz", &ad_dgAuthz, &text ); 2575 if ( rc != LDAP_SUCCESS ) { 2576 snprintf( cr->msg, sizeof( cr->msg), 2577 "unable to fetch attributeDescription \"dgAuthz\": %d (%s)", 2578 rc, text ); 2579 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg ); 2580 /* Just a warning */ 2581 } 2582 } 2583 2584 return 0; 2585} 2586 2587static int 2588dynlist_db_destroy( 2589 BackendDB *be, 2590 ConfigReply *cr ) 2591{ 2592 slap_overinst *on = (slap_overinst *) be->bd_info; 2593 2594 if ( on->on_bi.bi_private ) { 2595 dynlist_gen_t *dlg = (dynlist_gen_t *)on->on_bi.bi_private; 2596 dynlist_info_t *dli = dlg->dlg_dli, 2597 *dli_next; 2598 2599 for ( dli_next = dli; dli_next; dli = dli_next ) { 2600 dynlist_map_t *dlm; 2601 dynlist_map_t *dlm_next; 2602 2603 dli_next = dli->dli_next; 2604 2605 if ( !BER_BVISNULL( &dli->dli_uri ) ) { 2606 ch_free( dli->dli_uri.bv_val ); 2607 } 2608 2609 if ( dli->dli_lud != NULL ) { 2610 ldap_free_urldesc( dli->dli_lud ); 2611 } 2612 2613 if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) { 2614 ber_memfree( dli->dli_uri_nbase.bv_val ); 2615 } 2616 2617 if ( dli->dli_uri_filter != NULL ) { 2618 filter_free( dli->dli_uri_filter ); 2619 } 2620 2621 ch_free( dli->dli_default_filter.bv_val ); 2622 2623 dlm = dli->dli_dlm; 2624 while ( dlm != NULL ) { 2625 dlm_next = dlm->dlm_next; 2626 ch_free( dlm ); 2627 dlm = dlm_next; 2628 } 2629 ch_free( dli ); 2630 } 2631 ch_free( dlg ); 2632 } 2633 2634 return 0; 2635} 2636 2637static slap_overinst dynlist = { { NULL } }; 2638#ifdef TAKEOVER_DYNGROUP 2639static char *obsolete_names[] = { 2640 "dyngroup", 2641 NULL 2642}; 2643#endif 2644 2645#if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC 2646static 2647#endif /* SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC */ 2648int 2649dynlist_initialize(void) 2650{ 2651 const char *text; 2652 int rc = 0; 2653 2654 /* See if we need to define memberOf opattr */ 2655 rc = slap_str2ad( "memberOf", &ad_memberOf, &text ); 2656 if ( rc ) { 2657 rc = register_at( 2658 "( 1.2.840.113556.1.2.102 " 2659 "NAME 'memberOf' " 2660 "DESC 'Group that the entry belongs to' " 2661 "SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' " 2662 "EQUALITY distinguishedNameMatch " /* added */ 2663 "USAGE dSAOperation " /* added; questioned */ 2664 "NO-USER-MODIFICATION " /* added */ 2665 "X-ORIGIN 'iPlanet Delegated Administrator' )", 2666 &ad_memberOf, 0 ); 2667 if ( rc ) { 2668 Debug( LDAP_DEBUG_ANY, 2669 "dynlist_initialize: register_at (memberOf) failed\n" ); 2670 return rc; 2671 } 2672 } 2673 2674 dynlist.on_bi.bi_type = "dynlist"; 2675 2676#ifdef TAKEOVER_DYNGROUP 2677 /* makes dynlist incompatible with dyngroup */ 2678 dynlist.on_bi.bi_obsolete_names = obsolete_names; 2679#endif 2680 2681 dynlist.on_bi.bi_flags = SLAPO_BFLAG_SINGLE; 2682 dynlist.on_bi.bi_db_init = dynlist_db_init; 2683 dynlist.on_bi.bi_db_config = config_generic_wrapper; 2684 dynlist.on_bi.bi_db_open = dynlist_db_open; 2685 dynlist.on_bi.bi_db_destroy = dynlist_db_destroy; 2686 2687 dynlist.on_bi.bi_op_search = dynlist_search; 2688 dynlist.on_bi.bi_op_compare = dynlist_compare; 2689 2690 dynlist.on_bi.bi_cf_ocs = dlocs; 2691 2692 rc = config_register_schema( dlcfg, dlocs ); 2693 if ( rc ) { 2694 return rc; 2695 } 2696 2697 return overlay_register( &dynlist ); 2698} 2699 2700#if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC 2701int 2702init_module( int argc, char *argv[] ) 2703{ 2704 return dynlist_initialize(); 2705} 2706#endif 2707 2708#endif /* SLAPD_OVER_DYNLIST */ 2709