1 /* dynlist.c - dynamic list overlay */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 2003-2005 The OpenLDAP Foundation.
5 * Portions Copyright 2004-2005 Pierangelo Masarati.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
17 * This work was initially developed by Pierangelo Masarati
18 * for SysNet s.n.c., for inclusion in OpenLDAP Software.
23 #ifdef SLAPD_OVER_DYNLIST
27 #include <ac/string.h>
32 /* FIXME: the code differs if SLAP_OPATTRS is defined or not;
33 * SLAP_OPATTRS is not defined in 2.2 yet, while this overlay
34 * expects HEAD code at least later than August 6, 2004. */
35 /* FIXME: slap_anlist_no_attrs was introduced in 2.3; here it
36 * is anticipated to allow using this overlay with 2.2. */
38 #if LDAP_VENDOR_VERSION_MINOR != X && LDAP_VENDOR_VERSION_MINOR < 3
39 static AttributeName anlist_no_attrs[] = {
40 { BER_BVC( LDAP_NO_ATTRS ), NULL, 0, NULL },
41 { BER_BVNULL, NULL, 0, NULL }
44 static AttributeName *slap_anlist_no_attrs = anlist_no_attrs;
47 typedef struct dynlist_info {
49 AttributeDescription *dli_ad;
50 AttributeDescription *dli_member_ad;
51 struct berval dli_default_filter;
55 dynlist_is_dynlist( Operation *op, SlapReply *rs )
57 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
58 dynlist_info *dli = (dynlist_info *)on->on_bi.bi_private;
62 a = attrs_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
64 /* FIXME: objectClass must be present; for non-storage
65 * backends, like back-ldap, it needs to be added
66 * to the requested attributes */
70 if ( value_find_ex( slap_schema.si_ad_objectClass,
71 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
72 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
73 a->a_nvals, &dli->dli_oc->soc_cname,
74 op->o_tmpmemctx ) == 0 )
83 dynlist_make_filter( Operation *op, struct berval *oldf, struct berval *newf )
85 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
86 dynlist_info *dli = (dynlist_info *)on->on_bi.bi_private;
92 assert( !BER_BVISNULL( oldf ) );
93 assert( !BER_BVISEMPTY( oldf ) );
95 newf->bv_len = STRLENOF( "(&(!(objectClass=" "))" ")" )
96 + dli->dli_oc->soc_cname.bv_len + oldf->bv_len;
97 newf->bv_val = op->o_tmpalloc( newf->bv_len + 1, op->o_tmpmemctx );
98 if ( newf->bv_val == NULL ) {
101 ptr = lutil_strcopy( newf->bv_val, "(&(!(objectClass=" );
102 ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
103 ptr = lutil_strcopy( ptr, "))" );
104 ptr = lutil_strcopy( ptr, oldf->bv_val );
105 ptr = lutil_strcopy( ptr, ")" );
106 newf->bv_len = ptr - newf->bv_val;
111 typedef struct dynlist_sc_t {
112 dynlist_info *dlc_dli;
117 dynlist_sc_update( Operation *op, SlapReply *rs )
123 AccessControlState acl_state = ACL_STATE_INIT;
127 if ( rs->sr_type != REP_SEARCH ) {
131 dlc = (dynlist_sc_t *)op->o_callback->sc_private;
135 assert( rs->sr_entry != NULL );
137 /* test access to entry */
138 if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
139 NULL, ACL_READ, NULL ) )
144 if ( dlc->dlc_dli->dli_member_ad ) {
146 /* if access allowed, try to add values, emulating permissive
147 * control to silently ignore duplicates */
148 if ( access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
149 NULL, ACL_READ, NULL ) )
152 const char *text = NULL;
154 struct berval vals[ 2 ], nvals[ 2 ];
156 vals[ 0 ] = rs->sr_entry->e_name;
157 BER_BVZERO( &vals[ 1 ] );
158 nvals[ 0 ] = rs->sr_entry->e_nname;
159 BER_BVZERO( &nvals[ 1 ] );
161 mod.sm_op = LDAP_MOD_ADD;
162 mod.sm_desc = dlc->dlc_dli->dli_member_ad;
163 mod.sm_type = dlc->dlc_dli->dli_member_ad->ad_cname;
164 mod.sm_values = vals;
165 mod.sm_nvalues = nvals;
167 (void)modify_add_values( e, &mod, /* permissive */ 1,
168 &text, textbuf, sizeof( textbuf ) );
175 opattrs = ( rs->sr_attrs == NULL ) ? 0 : an_find( rs->sr_attrs, &AllOper );
176 userattrs = ( rs->sr_attrs == NULL ) ? 1 : an_find( rs->sr_attrs, &AllUser );
177 #else /* SLAP_OPATTRS */
178 opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
179 userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
180 #endif /* SLAP_OPATTRS */
182 for ( a = rs->sr_entry->e_attrs; a != NULL; a = a->a_next ) {
183 BerVarray vals, nvals = NULL;
186 /* if attribute is not requested, skip it */
187 if ( rs->sr_attrs == NULL ) {
188 if ( is_at_operational( a->a_desc->ad_type ) ) {
193 if ( is_at_operational( a->a_desc->ad_type ) ) {
194 if ( !opattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
200 if ( !userattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
207 /* test access to attribute */
208 if ( op->ors_attrsonly ) {
209 if ( !access_allowed( op, rs->sr_entry, a->a_desc, NULL,
210 ACL_READ, &acl_state ) )
216 /* single-value check: keep first only */
217 if ( is_at_single_value( a->a_desc->ad_type ) ) {
218 if ( attr_find( e->e_attrs, a->a_desc ) != NULL ) {
223 /* test access to attribute */
224 for ( i = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ )
227 vals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
228 if ( a->a_nvals != a->a_vals ) {
229 nvals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
232 for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
233 if ( access_allowed( op, rs->sr_entry, a->a_desc,
234 &a->a_nvals[i], ACL_READ, &acl_state ) )
236 vals[j] = a->a_vals[i];
238 nvals[j] = a->a_nvals[i];
244 /* if access allowed, try to add values, emulating permissive
245 * control to silently ignore duplicates */
248 const char *text = NULL;
251 BER_BVZERO( &vals[j] );
253 BER_BVZERO( &nvals[j] );
256 mod.sm_op = LDAP_MOD_ADD;
257 mod.sm_desc = a->a_desc;
258 mod.sm_type = a->a_desc->ad_cname;
259 mod.sm_values = vals;
260 mod.sm_nvalues = nvals;
262 (void)modify_add_values( e, &mod, /* permissive */ 1,
263 &text, textbuf, sizeof( textbuf ) );
266 op->o_tmpfree( vals, op->o_tmpmemctx );
268 op->o_tmpfree( nvals, op->o_tmpmemctx );
273 if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
274 entry_free( rs->sr_entry );
281 dynlist_send_entry( Operation *op, SlapReply *rs )
283 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
284 dynlist_info *dli = (dynlist_info *)on->on_bi.bi_private;
289 SlapReply r = { REP_SEARCH };
295 dynlist_sc_t dlc = { 0 };
297 a = attrs_find( rs->sr_entry->e_attrs, dli->dli_ad );
300 return SLAP_CB_CONTINUE;
303 e = entry_dup( rs->sr_entry );
304 e_flags = rs->sr_flags | ( REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED );
308 cb.sc_private = &dlc;
309 cb.sc_response = dynlist_sc_update;
310 cb.sc_cleanup = NULL;
314 o.ors_deref = LDAP_DEREF_NEVER;
316 o.ors_tlimit = SLAP_NO_LIMIT;
317 o.ors_slimit = SLAP_NO_LIMIT;
320 opattrs = ( rs->sr_attrs == NULL ) ? 0 : an_find( rs->sr_attrs, &AllOper );
321 userattrs = ( rs->sr_attrs == NULL ) ? 1 : an_find( rs->sr_attrs, &AllUser );
322 #else /* SLAP_OPATTRS */
323 opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
324 userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
325 #endif /* SLAP_OPATTRS */
327 for ( url = a->a_nvals; !BER_BVISNULL( url ); url++ ) {
328 LDAPURLDesc *lud = NULL;
333 BER_BVZERO( &o.o_req_dn );
334 BER_BVZERO( &o.o_req_ndn );
337 BER_BVZERO( &o.ors_filterstr );
339 if ( ldap_url_parse( url->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
344 if ( lud->lud_host ) {
345 /* FIXME: host not allowed; reject as illegal? */
346 Debug( LDAP_DEBUG_ANY, "dynlist_send_entry(\"%s\"): "
347 "illegal URI \"%s\"\n",
348 e->e_name.bv_val, url->bv_val, 0 );
352 if ( lud->lud_dn == NULL ) {
353 /* note that an empty base is not honored in terms
354 * of defaultSearchBase, because select_backend()
355 * is not aware of the defaultSearchBase option;
356 * this can be useful in case of a database serving
357 * the empty suffix */
358 BER_BVSTR( &dn, "" );
360 ber_str2bv( lud->lud_dn, 0, 0, &dn );
362 rc = dnPrettyNormal( NULL, &dn, &o.o_req_dn, &o.o_req_ndn, op->o_tmpmemctx );
363 if ( rc != LDAP_SUCCESS ) {
367 o.ors_scope = lud->lud_scope;
369 if ( dli->dli_member_ad != NULL ) {
370 o.ors_attrs = slap_anlist_no_attrs;
372 } else if ( lud->lud_attrs == NULL ) {
373 o.ors_attrs = rs->sr_attrs;
376 for ( i = 0; lud->lud_attrs[i]; i++)
379 o.ors_attrs = op->o_tmpcalloc( i + 1, sizeof( AttributeName ), op->o_tmpmemctx );
380 for ( i = 0, j = 0; lud->lud_attrs[i]; i++) {
381 const char *text = NULL;
383 ber_str2bv( lud->lud_attrs[i], 0, 0, &o.ors_attrs[j].an_name );
384 o.ors_attrs[j].an_desc = NULL;
385 (void)slap_bv2ad( &o.ors_attrs[j].an_name, &o.ors_attrs[j].an_desc, &text );
386 /* FIXME: ignore errors... */
388 if ( rs->sr_attrs == NULL ) {
389 if ( o.ors_attrs[j].an_desc != NULL &&
390 is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
396 if ( o.ors_attrs[j].an_desc != NULL &&
397 is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
399 if ( !opattrs && !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
406 o.ors_attrs[j].an_desc != NULL &&
407 !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
421 BER_BVZERO( &o.ors_attrs[j].an_name );
424 if ( lud->lud_filter == NULL ) {
425 ber_dupbv_x( &o.ors_filterstr,
426 &dli->dli_default_filter, op->o_tmpmemctx );
429 ber_str2bv( lud->lud_filter, 0, 0, &flt );
430 if ( dynlist_make_filter( op, &flt, &o.ors_filterstr ) ) {
435 o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
436 if ( o.ors_filter == NULL ) {
440 o.o_bd = select_backend( &o.o_req_ndn, 0, 1 );
441 if ( o.o_bd && o.o_bd->be_search ) {
443 r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
444 #endif /* SLAP_OPATTRS */
445 (void)o.o_bd->be_search( &o, &r );
449 if ( o.ors_filter ) {
450 filter_free_x( &o, o.ors_filter );
452 if ( o.ors_attrs && o.ors_attrs != rs->sr_attrs
453 && o.ors_attrs != slap_anlist_no_attrs )
455 op->o_tmpfree( o.ors_attrs, op->o_tmpmemctx );
457 if ( !BER_BVISNULL( &o.o_req_dn ) ) {
458 op->o_tmpfree( o.o_req_dn.bv_val, op->o_tmpmemctx );
460 if ( !BER_BVISNULL( &o.o_req_ndn ) ) {
461 op->o_tmpfree( o.o_req_ndn.bv_val, op->o_tmpmemctx );
463 if ( o.ors_filterstr.bv_val != lud->lud_filter ) {
464 op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
465 lud->lud_filter = NULL;
468 ldap_free_urldesc( lud );
473 rs->sr_flags = e_flags;
475 return SLAP_CB_CONTINUE;
479 dynlist_sc_save_entry( Operation *op, SlapReply *rs )
481 /* save the entry in the private field of the callback,
482 * so it doesn't get freed (it's temporary!) */
483 if ( rs->sr_entry != NULL ) {
484 dynlist_sc_t *dlc = (dynlist_sc_t *)op->o_callback->sc_private;
485 dlc->dlc_e = rs->sr_entry;
493 dynlist_compare( Operation *op, SlapReply *rs )
495 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
496 dynlist_info *dli = (dynlist_info *)on->on_bi.bi_private;
501 SlapReply r = { REP_SEARCH };
504 dynlist_sc_t dlc = { 0 };
507 cb.sc_private = &dlc;
508 cb.sc_response = dynlist_sc_save_entry;
509 cb.sc_cleanup = NULL;
513 o.o_tag = LDAP_REQ_SEARCH;
515 o.ors_tlimit = SLAP_NO_LIMIT;
516 o.ors_slimit = SLAP_NO_LIMIT;
518 o.o_bd = select_backend( &o.o_req_ndn, 0, 1 );
519 if ( !o.o_bd || !o.o_bd->be_search ) {
520 return SLAP_CB_CONTINUE;
523 BER_BVSTR( &o.ors_filterstr, "(objectClass=*)" );
524 o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
525 if ( o.ors_filter == NULL ) {
527 return SLAP_CB_CONTINUE;
530 o.ors_scope = LDAP_SCOPE_BASE;
531 o.ors_deref = LDAP_DEREF_NEVER;
532 an[0].an_name = op->orc_ava->aa_desc->ad_cname;
533 an[0].an_desc = op->orc_ava->aa_desc;
534 BER_BVZERO( &an[1].an_name );
538 rc = o.o_bd->be_search( &o, &r );
539 filter_free_x( &o, o.ors_filter );
545 if ( dlc.dlc_e != NULL ) {
546 r.sr_entry = dlc.dlc_e;
549 if ( r.sr_err != LDAP_SUCCESS || r.sr_entry == NULL ) {
551 return SLAP_CB_CONTINUE;
554 /* if we're here, we got a match... */
555 rs->sr_err = LDAP_COMPARE_FALSE;
556 for ( a = attrs_find( r.sr_entry->e_attrs, op->orc_ava->aa_desc );
558 a = attrs_find( a->a_next, op->orc_ava->aa_desc ) )
560 if ( value_find_ex( op->orc_ava->aa_desc,
561 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
562 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
563 a->a_nvals, &op->orc_ava->aa_value, op->o_tmpmemctx ) == 0 )
565 rs->sr_err = LDAP_COMPARE_TRUE;
570 if ( r.sr_flags & REP_ENTRY_MUSTBEFREED ) {
571 entry_free( r.sr_entry );
574 return SLAP_CB_CONTINUE;
578 dynlist_response( Operation *op, SlapReply *rs )
580 switch ( op->o_tag ) {
581 case LDAP_REQ_SEARCH:
582 if ( rs->sr_type == REP_SEARCH && !get_manageDSAit( op ) )
584 if ( dynlist_is_dynlist( op, rs ) ) {
585 return dynlist_send_entry( op, rs );
590 case LDAP_REQ_COMPARE:
591 if ( rs->sr_err == LDAP_NO_SUCH_ATTRIBUTE ) {
592 return dynlist_compare( op, rs );
600 return SLAP_CB_CONTINUE;
612 slap_overinst *on = (slap_overinst *)be->bd_info;
613 dynlist_info *dli = (dynlist_info *)on->on_bi.bi_private;
617 if ( strcasecmp( argv[0], "dynlist-oc" ) == 0 ) {
619 fprintf( stderr, "dynlist-oc <oc>\n" );
622 dli->dli_oc = oc_find( argv[1] );
623 if ( dli->dli_oc == NULL ) {
624 fprintf( stderr, "dynlist-oc <oc>: "
625 "unable to find ObjectClass "
626 "\"%s\"\n", argv[1] );
630 } else if ( strcasecmp( argv[0], "dynlist-ad" ) == 0 ) {
634 fprintf( stderr, "dynlist-ad <ad>\n" );
638 rc = slap_str2ad( argv[1], &dli->dli_ad, &text );
639 if ( rc != LDAP_SUCCESS ) {
640 fprintf( stderr, "dynlist-ad <ad>: "
641 "unable to find AttributeDescription "
642 "\"%s\"\n", argv[1] );
646 } else if ( strcasecmp( argv[0], "dynlist-member-ad" ) == 0 ) {
650 fprintf( stderr, "dynlist-member-ad <ad>\n" );
653 dli->dli_member_ad = NULL;
654 rc = slap_str2ad( argv[1], &dli->dli_member_ad, &text );
655 if ( rc != LDAP_SUCCESS ) {
656 fprintf( stderr, "dynlist-member-ad <ad>: "
657 "unable to find AttributeDescription "
658 "\"%s\"\n", argv[1] );
663 rc = SLAP_CONF_UNKNOWN;
674 slap_overinst *on = (slap_overinst *) be->bd_info;
677 dli = (dynlist_info *)ch_malloc( sizeof( dynlist_info ) );
678 memset( dli, 0, sizeof( dynlist_info ) );
680 on->on_bi.bi_private = (void *)dli;
690 slap_overinst *on = (slap_overinst *) be->bd_info;
691 dynlist_info *dli = (dynlist_info *)on->on_bi.bi_private;
695 if ( dli->dli_oc == NULL ) {
696 fprintf( stderr, "dynlist_db_open(): missing \"dynlist-oc <ObjectClass>\"\n" );
700 if ( dli->dli_ad == NULL ) {
701 fprintf( stderr, "dynlist_db_open(): missing \"dynlist-ad <AttributeDescription>\"\n" );
705 len = STRLENOF( "(!(objectClass=" "))" )
706 + dli->dli_oc->soc_cname.bv_len;
707 dli->dli_default_filter.bv_val = SLAP_MALLOC( len + 1 );
708 if ( dli->dli_default_filter.bv_val == NULL ) {
709 fprintf( stderr, "dynlist_db_open(): malloc failed\n" );
712 ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" );
713 ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
714 ptr = lutil_strcopy( ptr, "))" );
715 dli->dli_default_filter.bv_len = ptr - dli->dli_default_filter.bv_val;
725 slap_overinst *on = (slap_overinst *) be->bd_info;
728 if ( on->on_bi.bi_private ) {
729 dynlist_info *dli = (dynlist_info *)on->on_bi.bi_private;
740 static slap_overinst dynlist = { { NULL } };
745 dynlist.on_bi.bi_type = "dynlist";
746 dynlist.on_bi.bi_db_init = dynlist_db_init;
747 dynlist.on_bi.bi_db_config = dynlist_db_config;
748 dynlist.on_bi.bi_db_open = dynlist_db_open;
749 dynlist.on_bi.bi_db_destroy = dynlist_db_destroy;
751 dynlist.on_response = dynlist_response;
753 return overlay_register( &dynlist );
756 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
758 init_module( int argc, char *argv[] )
760 return dynlist_init();
764 #endif /* SLAPD_OVER_DYNLIST */