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