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