Back to index

openldap  2.4.31
dynlist.c
Go to the documentation of this file.
00001 /* dynlist.c - dynamic list overlay */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2003-2012 The OpenLDAP Foundation.
00006  * Portions Copyright 2004-2005 Pierangelo Masarati.
00007  * Portions Copyright 2008 Emmanuel Dreyfus.
00008  * All rights reserved.
00009  *
00010  * Redistribution and use in source and binary forms, with or without
00011  * modification, are permitted only as authorized by the OpenLDAP
00012  * Public License.
00013  *
00014  * A copy of this license is available in the file LICENSE in the
00015  * top-level directory of the distribution or, alternatively, at
00016  * <http://www.OpenLDAP.org/license.html>.
00017  */
00018 /* ACKNOWLEDGEMENTS:
00019  * This work was initially developed by Pierangelo Masarati
00020  * for SysNet s.n.c., for inclusion in OpenLDAP Software.
00021  */
00022 
00023 #include "portable.h"
00024 
00025 #ifdef SLAPD_OVER_DYNLIST
00026 
00027 #if SLAPD_OVER_DYNGROUP != SLAPD_MOD_STATIC
00028 #define TAKEOVER_DYNGROUP
00029 #endif
00030 
00031 #include <stdio.h>
00032 
00033 #include <ac/string.h>
00034 
00035 #include "slap.h"
00036 #include "config.h"
00037 #include "lutil.h"
00038 
00039 static AttributeDescription *ad_dgIdentity, *ad_dgAuthz;
00040 
00041 typedef struct dynlist_map_t {
00042        AttributeDescription *dlm_member_ad;
00043        AttributeDescription *dlm_mapped_ad;
00044        struct dynlist_map_t *dlm_next;
00045 } dynlist_map_t;
00046 
00047 typedef struct dynlist_info_t {
00048        ObjectClass          *dli_oc;
00049        AttributeDescription *dli_ad;
00050        struct dynlist_map_t *dli_dlm;
00051        struct berval        dli_uri;
00052        LDAPURLDesc          *dli_lud;
00053        struct berval        dli_uri_nbase;
00054        Filter               *dli_uri_filter;
00055        struct berval        dli_default_filter;
00056        struct dynlist_info_t       *dli_next;
00057 } dynlist_info_t;
00058 
00059 #define DYNLIST_USAGE \
00060        "\"dynlist-attrset <oc> [uri] <URL-ad> [[<mapped-ad>:]<member-ad> ...]\": "
00061 
00062 static dynlist_info_t *
00063 dynlist_is_dynlist_next( Operation *op, SlapReply *rs, dynlist_info_t *old_dli )
00064 {
00065        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00066        dynlist_info_t       *dli;
00067 
00068        Attribute     *a;
00069 
00070        if ( old_dli == NULL ) {
00071               dli = (dynlist_info_t *)on->on_bi.bi_private;
00072 
00073        } else {
00074               dli = old_dli->dli_next;
00075        }
00076 
00077        a = attrs_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
00078        if ( a == NULL ) {
00079               /* FIXME: objectClass must be present; for non-storage
00080                * backends, like back-ldap, it needs to be added
00081                * to the requested attributes */
00082               return NULL;
00083        }
00084 
00085        for ( ; dli; dli = dli->dli_next ) {
00086               if ( dli->dli_lud != NULL ) {
00087                      /* check base and scope */
00088                      if ( !BER_BVISNULL( &dli->dli_uri_nbase )
00089                             && !dnIsSuffixScope( &rs->sr_entry->e_nname,
00090                                    &dli->dli_uri_nbase,
00091                                    dli->dli_lud->lud_scope ) )
00092                      {
00093                             continue;
00094                      }
00095 
00096                      /* check filter */
00097                      if ( dli->dli_uri_filter && test_filter( op, rs->sr_entry, dli->dli_uri_filter ) != LDAP_COMPARE_TRUE ) {
00098                             continue;
00099                      }
00100               }
00101 
00102               if ( attr_valfind( a,
00103                             SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
00104                             SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
00105                             &dli->dli_oc->soc_cname, NULL,
00106                             op->o_tmpmemctx ) == 0 )
00107               {
00108                      return dli;
00109               }
00110        }
00111 
00112        return NULL;
00113 }
00114 
00115 static int
00116 dynlist_make_filter( Operation *op, Entry *e, const char *url, struct berval *oldf, struct berval *newf )
00117 {
00118        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00119        dynlist_info_t       *dli = (dynlist_info_t *)on->on_bi.bi_private;
00120 
00121        char          *ptr;
00122        int           needBrackets = 0;
00123 
00124        assert( oldf != NULL );
00125        assert( newf != NULL );
00126        assert( !BER_BVISNULL( oldf ) );
00127        assert( !BER_BVISEMPTY( oldf ) );
00128 
00129        if ( oldf->bv_val[0] != '(' ) {
00130               Debug( LDAP_DEBUG_ANY, "%s: dynlist, DN=\"%s\": missing brackets in URI=\"%s\" filter\n",
00131                      op->o_log_prefix, e->e_name.bv_val, url );
00132               needBrackets = 2;
00133        }
00134 
00135        newf->bv_len = STRLENOF( "(&(!(objectClass=" "))" ")" )
00136               + dli->dli_oc->soc_cname.bv_len + oldf->bv_len + needBrackets;
00137        newf->bv_val = op->o_tmpalloc( newf->bv_len + 1, op->o_tmpmemctx );
00138        if ( newf->bv_val == NULL ) {
00139               return -1;
00140        }
00141        ptr = lutil_strcopy( newf->bv_val, "(&(!(objectClass=" );
00142        ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
00143        ptr = lutil_strcopy( ptr, "))" );
00144        if ( needBrackets ) *ptr++ = '(';
00145        ptr = lutil_strcopy( ptr, oldf->bv_val );
00146        if ( needBrackets ) *ptr++ = ')';
00147        ptr = lutil_strcopy( ptr, ")" );
00148        newf->bv_len = ptr - newf->bv_val;
00149 
00150        return 0;
00151 }
00152 
00153 /* dynlist_sc_update() callback info set by dynlist_prepare_entry() */
00154 typedef struct dynlist_sc_t {
00155        dynlist_info_t    *dlc_dli;
00156        Entry         *dlc_e;
00157 } dynlist_sc_t;
00158 
00159 static int
00160 dynlist_sc_update( Operation *op, SlapReply *rs )
00161 {
00162        Entry                *e;
00163        Attribute            *a;
00164        int                  opattrs,
00165                             userattrs;
00166        AccessControlState   acl_state = ACL_STATE_INIT;
00167 
00168        dynlist_sc_t         *dlc;
00169        dynlist_map_t        *dlm;
00170 
00171        if ( rs->sr_type != REP_SEARCH ) {
00172               return 0;
00173        }
00174 
00175        dlc = (dynlist_sc_t *)op->o_callback->sc_private;
00176        e = dlc->dlc_e;
00177 
00178        assert( e != NULL );
00179        assert( rs->sr_entry != NULL );
00180 
00181        /* test access to entry */
00182        if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
00183                             NULL, ACL_READ, NULL ) )
00184        {
00185               goto done;
00186        }
00187 
00188        /* if there is only one member_ad, and it's not mapped,
00189         * consider it as old-style member listing */
00190        dlm = dlc->dlc_dli->dli_dlm;
00191        if ( dlm && dlm->dlm_mapped_ad == NULL && dlm->dlm_next == NULL ) {
00192               /* if access allowed, try to add values, emulating permissive
00193                * control to silently ignore duplicates */
00194               if ( access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
00195                                    NULL, ACL_READ, NULL ) )
00196               {
00197                      Modification  mod;
00198                      const char    *text = NULL;
00199                      char          textbuf[1024];
00200                      struct berval vals[ 2 ], nvals[ 2 ];
00201 
00202                      vals[ 0 ] = rs->sr_entry->e_name;
00203                      BER_BVZERO( &vals[ 1 ] );
00204                      nvals[ 0 ] = rs->sr_entry->e_nname;
00205                      BER_BVZERO( &nvals[ 1 ] );
00206 
00207                      mod.sm_op = LDAP_MOD_ADD;
00208                      mod.sm_desc = dlm->dlm_member_ad;
00209                      mod.sm_type = dlm->dlm_member_ad->ad_cname;
00210                      mod.sm_values = vals;
00211                      mod.sm_nvalues = nvals;
00212                      mod.sm_numvals = 1;
00213 
00214                      (void)modify_add_values( e, &mod, /* permissive */ 1,
00215                                    &text, textbuf, sizeof( textbuf ) );
00216               }
00217 
00218               goto done;
00219        }
00220 
00221        opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
00222        userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
00223 
00224        for ( a = rs->sr_entry->e_attrs; a != NULL; a = a->a_next ) {
00225               BerVarray     vals, nvals = NULL;
00226               int           i, j,
00227                             is_oc = a->a_desc == slap_schema.si_ad_objectClass;
00228 
00229               /* if attribute is not requested, skip it */
00230               if ( rs->sr_attrs == NULL ) {
00231                      if ( is_at_operational( a->a_desc->ad_type ) ) {
00232                             continue;
00233                      }
00234 
00235               } else {
00236                      if ( is_at_operational( a->a_desc->ad_type ) ) {
00237                             if ( !opattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
00238                             {
00239                                    continue;
00240                             }
00241 
00242                      } else {
00243                             if ( !userattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
00244                             {
00245                                    continue;
00246                             }
00247                      }
00248               }
00249 
00250               /* test access to attribute */
00251               if ( op->ors_attrsonly ) {
00252                      if ( !access_allowed( op, rs->sr_entry, a->a_desc, NULL,
00253                                           ACL_READ, &acl_state ) )
00254                      {
00255                             continue;
00256                      }
00257               }
00258 
00259               /* single-value check: keep first only */
00260               if ( is_at_single_value( a->a_desc->ad_type ) ) {
00261                      if ( attr_find( e->e_attrs, a->a_desc ) != NULL ) {
00262                             continue;
00263                      }
00264               }
00265 
00266               /* test access to attribute */
00267               i = a->a_numvals;
00268 
00269               vals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
00270               if ( a->a_nvals != a->a_vals ) {
00271                      nvals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
00272               }
00273 
00274               for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
00275                      if ( is_oc ) {
00276                             ObjectClass   *soc = oc_bvfind( &a->a_vals[i] );
00277 
00278                             if ( soc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
00279                                    continue;
00280                             }
00281                      }
00282 
00283                      if ( access_allowed( op, rs->sr_entry, a->a_desc,
00284                                           &a->a_nvals[i], ACL_READ, &acl_state ) )
00285                      {
00286                             vals[j] = a->a_vals[i];
00287                             if ( nvals ) {
00288                                    nvals[j] = a->a_nvals[i];
00289                             }
00290                             j++;
00291                      }
00292               }
00293 
00294               /* if access allowed, try to add values, emulating permissive
00295                * control to silently ignore duplicates */
00296               if ( j != 0 ) {
00297                      Modification  mod;
00298                      const char    *text = NULL;
00299                      char          textbuf[1024];
00300                      dynlist_map_t *dlm;
00301                      AttributeDescription *ad;
00302 
00303                      BER_BVZERO( &vals[j] );
00304                      if ( nvals ) {
00305                             BER_BVZERO( &nvals[j] );
00306                      }
00307 
00308                      ad = a->a_desc;
00309                      for ( dlm = dlc->dlc_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
00310                             if ( dlm->dlm_member_ad == a->a_desc ) {
00311                                    if ( dlm->dlm_mapped_ad ) {
00312                                           ad = dlm->dlm_mapped_ad;
00313                                    }
00314                                    break;
00315                             }
00316                      }
00317 
00318                      mod.sm_op = LDAP_MOD_ADD;
00319                      mod.sm_desc = ad;
00320                      mod.sm_type = ad->ad_cname;
00321                      mod.sm_values = vals;
00322                      mod.sm_nvalues = nvals;
00323                      mod.sm_numvals = j;
00324 
00325                      (void)modify_add_values( e, &mod, /* permissive */ 1,
00326                                    &text, textbuf, sizeof( textbuf ) );
00327               }
00328 
00329               op->o_tmpfree( vals, op->o_tmpmemctx );
00330               if ( nvals ) {
00331                      op->o_tmpfree( nvals, op->o_tmpmemctx );
00332               }
00333        }
00334 
00335 done:;
00336        if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
00337               entry_free( rs->sr_entry );
00338               rs->sr_entry = NULL;
00339               rs->sr_flags &= ~REP_ENTRY_MASK;
00340        }
00341 
00342        return 0;
00343 }
00344        
00345 static int
00346 dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli )
00347 {
00348        Attribute     *a, *id = NULL;
00349        slap_callback cb;
00350        Operation     o = *op;
00351        struct berval *url;
00352        Entry         *e;
00353        int           opattrs,
00354                      userattrs;
00355        dynlist_sc_t  dlc = { 0 };
00356        dynlist_map_t *dlm;
00357 
00358        a = attrs_find( rs->sr_entry->e_attrs, dli->dli_ad );
00359        if ( a == NULL ) {
00360               /* FIXME: error? */
00361               return SLAP_CB_CONTINUE;
00362        }
00363 
00364        opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
00365        userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
00366 
00367        /* Don't generate member list if it wasn't requested */
00368        for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
00369               AttributeDescription *ad = dlm->dlm_mapped_ad ? dlm->dlm_mapped_ad : dlm->dlm_member_ad;
00370               if ( userattrs || ad_inlist( ad, rs->sr_attrs ) ) 
00371                      break;
00372        }
00373        if ( dli->dli_dlm && !dlm )
00374               return SLAP_CB_CONTINUE;
00375 
00376        if ( ad_dgIdentity && ( id = attrs_find( rs->sr_entry->e_attrs, ad_dgIdentity ))) {
00377               Attribute *authz = NULL;
00378 
00379               /* if not rootdn and dgAuthz is present,
00380                * check if user can be authorized as dgIdentity */
00381               if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op )
00382                      && ( authz = attrs_find( rs->sr_entry->e_attrs, ad_dgAuthz ) ) )
00383               {
00384                      if ( slap_sasl_matches( op, authz->a_nvals,
00385                             &o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS )
00386                      {
00387                             return SLAP_CB_CONTINUE;
00388                      }
00389               }
00390 
00391               o.o_dn = id->a_vals[0];
00392               o.o_ndn = id->a_nvals[0];
00393               o.o_groups = NULL;
00394        }
00395 
00396        e = rs->sr_entry;
00397        /* ensure e is modifiable, but do not replace
00398         * sr_entry yet since we have pointers into it */
00399        if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
00400               e = entry_dup( rs->sr_entry );
00401        }
00402 
00403        dlc.dlc_e = e;
00404        dlc.dlc_dli = dli;
00405        cb.sc_private = &dlc;
00406        cb.sc_response = dynlist_sc_update;
00407        cb.sc_cleanup = NULL;
00408        cb.sc_next = NULL;
00409 
00410        o.o_callback = &cb;
00411        o.ors_deref = LDAP_DEREF_NEVER;
00412        o.ors_limit = NULL;
00413        o.ors_tlimit = SLAP_NO_LIMIT;
00414        o.ors_slimit = SLAP_NO_LIMIT;
00415 
00416        for ( url = a->a_nvals; !BER_BVISNULL( url ); url++ ) {
00417               LDAPURLDesc   *lud = NULL;
00418               int           i, j;
00419               struct berval dn;
00420               int           rc;
00421 
00422               BER_BVZERO( &o.o_req_dn );
00423               BER_BVZERO( &o.o_req_ndn );
00424               o.ors_filter = NULL;
00425               o.ors_attrs = NULL;
00426               BER_BVZERO( &o.ors_filterstr );
00427 
00428               if ( ldap_url_parse( url->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
00429                      /* FIXME: error? */
00430                      continue;
00431               }
00432 
00433               if ( lud->lud_host != NULL ) {
00434                      /* FIXME: host not allowed; reject as illegal? */
00435                      Debug( LDAP_DEBUG_ANY, "dynlist_prepare_entry(\"%s\"): "
00436                             "illegal URI \"%s\"\n",
00437                             e->e_name.bv_val, url->bv_val, 0 );
00438                      goto cleanup;
00439               }
00440 
00441               if ( lud->lud_dn == NULL ) {
00442                      /* note that an empty base is not honored in terms
00443                       * of defaultSearchBase, because select_backend()
00444                       * is not aware of the defaultSearchBase option;
00445                       * this can be useful in case of a database serving
00446                       * the empty suffix */
00447                      BER_BVSTR( &dn, "" );
00448 
00449               } else {
00450                      ber_str2bv( lud->lud_dn, 0, 0, &dn );
00451               }
00452               rc = dnPrettyNormal( NULL, &dn, &o.o_req_dn, &o.o_req_ndn, op->o_tmpmemctx );
00453               if ( rc != LDAP_SUCCESS ) {
00454                      /* FIXME: error? */
00455                      goto cleanup;
00456               }
00457               o.ors_scope = lud->lud_scope;
00458 
00459               for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
00460                      if ( dlm->dlm_mapped_ad != NULL ) {
00461                             break;
00462                      }
00463               }
00464 
00465               if ( dli->dli_dlm && !dlm ) {
00466                      /* if ( lud->lud_attrs != NULL ),
00467                       * the URL should be ignored */
00468                      o.ors_attrs = slap_anlist_no_attrs;
00469 
00470               } else if ( lud->lud_attrs == NULL ) {
00471                      o.ors_attrs = rs->sr_attrs;
00472 
00473               } else {
00474                      for ( i = 0; lud->lud_attrs[i]; i++)
00475                             /* just count */ ;
00476 
00477                      o.ors_attrs = op->o_tmpcalloc( i + 1, sizeof( AttributeName ), op->o_tmpmemctx );
00478                      for ( i = 0, j = 0; lud->lud_attrs[i]; i++) {
00479                             const char    *text = NULL;
00480        
00481                             ber_str2bv( lud->lud_attrs[i], 0, 0, &o.ors_attrs[j].an_name );
00482                             o.ors_attrs[j].an_desc = NULL;
00483                             (void)slap_bv2ad( &o.ors_attrs[j].an_name, &o.ors_attrs[j].an_desc, &text );
00484                             /* FIXME: ignore errors... */
00485 
00486                             if ( rs->sr_attrs == NULL ) {
00487                                    if ( o.ors_attrs[j].an_desc != NULL &&
00488                                                  is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
00489                                    {
00490                                           continue;
00491                                    }
00492 
00493                             } else {
00494                                    if ( o.ors_attrs[j].an_desc != NULL &&
00495                                                  is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
00496                                    {
00497                                           if ( !opattrs ) {
00498                                                  continue;
00499                                           }
00500 
00501                                           if ( !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) ) {
00502                                                  /* lookup if mapped -- linear search,
00503                                                   * not very efficient unless list
00504                                                   * is very short */
00505                                                  for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
00506                                                         if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
00507                                                                break;
00508                                                         }
00509                                                  }
00510 
00511                                                  if ( dlm == NULL ) {
00512                                                         continue;
00513                                                  }
00514                                           }
00515 
00516                                    } else {
00517                                           if ( !userattrs && 
00518                                                         o.ors_attrs[j].an_desc != NULL &&
00519                                                         !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
00520                                           {
00521                                                  /* lookup if mapped -- linear search,
00522                                                   * not very efficient unless list
00523                                                   * is very short */
00524                                                  for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
00525                                                         if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
00526                                                                break;
00527                                                         }
00528                                                  }
00529 
00530                                                  if ( dlm == NULL ) {
00531                                                         continue;
00532                                                  }
00533                                           }
00534                                    }
00535                             }
00536 
00537                             j++;
00538                      }
00539 
00540                      if ( j == 0 ) {
00541                             goto cleanup;
00542                      }
00543               
00544                      BER_BVZERO( &o.ors_attrs[j].an_name );
00545               }
00546 
00547               if ( lud->lud_filter == NULL ) {
00548                      ber_dupbv_x( &o.ors_filterstr,
00549                                    &dli->dli_default_filter, op->o_tmpmemctx );
00550 
00551               } else {
00552                      struct berval flt;
00553                      ber_str2bv( lud->lud_filter, 0, 0, &flt );
00554                      if ( dynlist_make_filter( op, rs->sr_entry, url->bv_val, &flt, &o.ors_filterstr ) ) {
00555                             /* error */
00556                             goto cleanup;
00557                      }
00558               }
00559               o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
00560               if ( o.ors_filter == NULL ) {
00561                      goto cleanup;
00562               }
00563               
00564               o.o_bd = select_backend( &o.o_req_ndn, 1 );
00565               if ( o.o_bd && o.o_bd->be_search ) {
00566                      SlapReply     r = { REP_SEARCH };
00567                      r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
00568                      (void)o.o_bd->be_search( &o, &r );
00569               }
00570 
00571 cleanup:;
00572               if ( id ) {
00573                      slap_op_groups_free( &o );
00574               }
00575               if ( o.ors_filter ) {
00576                      filter_free_x( &o, o.ors_filter, 1 );
00577               }
00578               if ( o.ors_attrs && o.ors_attrs != rs->sr_attrs
00579                             && o.ors_attrs != slap_anlist_no_attrs )
00580               {
00581                      op->o_tmpfree( o.ors_attrs, op->o_tmpmemctx );
00582               }
00583               if ( !BER_BVISNULL( &o.o_req_dn ) ) {
00584                      op->o_tmpfree( o.o_req_dn.bv_val, op->o_tmpmemctx );
00585               }
00586               if ( !BER_BVISNULL( &o.o_req_ndn ) ) {
00587                      op->o_tmpfree( o.o_req_ndn.bv_val, op->o_tmpmemctx );
00588               }
00589               assert( BER_BVISNULL( &o.ors_filterstr )
00590                      || o.ors_filterstr.bv_val != lud->lud_filter );
00591               op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
00592               ldap_free_urldesc( lud );
00593        }
00594 
00595        if ( e != rs->sr_entry ) {
00596               rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e );
00597               rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
00598        }
00599 
00600        return SLAP_CB_CONTINUE;
00601 }
00602 
00603 /* dynlist_sc_compare_entry() callback set by dynlist_compare() */
00604 typedef struct dynlist_cc_t {
00605        slap_callback dc_cb;
00606 #      define dc_ava dc_cb.sc_private /* attr:val to compare with */
00607        int *dc_res;
00608 } dynlist_cc_t;
00609 
00610 static int
00611 dynlist_sc_compare_entry( Operation *op, SlapReply *rs )
00612 {
00613        if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) {
00614               dynlist_cc_t *dc = (dynlist_cc_t *)op->o_callback;
00615               AttributeAssertion *ava = dc->dc_ava;
00616               Attribute *a = attrs_find( rs->sr_entry->e_attrs, ava->aa_desc );
00617 
00618               if ( a != NULL ) {
00619                      while ( LDAP_SUCCESS != attr_valfind( a,
00620                                    SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
00621                                           SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
00622                                    &ava->aa_value, NULL, op->o_tmpmemctx )
00623                             && (a = attrs_find( a->a_next, ava->aa_desc )) != NULL )
00624                             ;
00625                      *dc->dc_res = a ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
00626               }
00627        }
00628 
00629        return 0;
00630 }
00631 
00632 static int
00633 dynlist_compare( Operation *op, SlapReply *rs )
00634 {
00635        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00636        dynlist_info_t       *dli = (dynlist_info_t *)on->on_bi.bi_private;
00637        Operation o = *op;
00638        Entry *e = NULL;
00639        dynlist_map_t *dlm;
00640        BackendDB *be;
00641 
00642        for ( ; dli != NULL; dli = dli->dli_next ) {
00643               for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next )
00644                      if ( op->oq_compare.rs_ava->aa_desc == dlm->dlm_member_ad )
00645                             break;
00646 
00647               if ( dlm ) {
00648                      /* This compare is for one of the attributes we're
00649                       * interested in. We'll use slapd's existing dyngroup
00650                       * evaluator to get the answer we want.
00651                       */
00652                      BerVarray id = NULL, authz = NULL;
00653 
00654                      o.o_do_not_cache = 1;
00655 
00656                      if ( ad_dgIdentity && backend_attribute( &o, NULL, &o.o_req_ndn,
00657                             ad_dgIdentity, &id, ACL_READ ) == LDAP_SUCCESS )
00658                      {
00659                             /* if not rootdn and dgAuthz is present,
00660                              * check if user can be authorized as dgIdentity */
00661                             if ( ad_dgAuthz && !BER_BVISEMPTY( id ) && !be_isroot( op )
00662                                    && backend_attribute( &o, NULL, &o.o_req_ndn,
00663                                           ad_dgAuthz, &authz, ACL_READ ) == LDAP_SUCCESS )
00664                             {
00665                                    
00666                                    rs->sr_err = slap_sasl_matches( op, authz,
00667                                           &o.o_ndn, &o.o_ndn );
00668                                    ber_bvarray_free_x( authz, op->o_tmpmemctx );
00669                                    if ( rs->sr_err != LDAP_SUCCESS ) {
00670                                           goto done;
00671                                    }
00672                             }
00673 
00674                             o.o_dn = *id;
00675                             o.o_ndn = *id;
00676                             o.o_groups = NULL; /* authz changed, invalidate cached groups */
00677                      }
00678 
00679                      rs->sr_err = backend_group( &o, NULL, &o.o_req_ndn,
00680                             &o.oq_compare.rs_ava->aa_value, dli->dli_oc, dli->dli_ad );
00681                      switch ( rs->sr_err ) {
00682                      case LDAP_SUCCESS:
00683                             rs->sr_err = LDAP_COMPARE_TRUE;
00684                             break;
00685 
00686                      case LDAP_NO_SUCH_OBJECT:
00687                             /* NOTE: backend_group() returns noSuchObject
00688                              * if op_ndn does not exist; however, since
00689                              * dynamic list expansion means that the
00690                              * member attribute is virtually present, the
00691                              * non-existence of the asserted value implies
00692                              * the assertion is FALSE rather than
00693                              * UNDEFINED */
00694                             rs->sr_err = LDAP_COMPARE_FALSE;
00695                             break;
00696                      }
00697 
00698 done:;
00699                      if ( id ) ber_bvarray_free_x( id, o.o_tmpmemctx );
00700 
00701                      return SLAP_CB_CONTINUE;
00702               }
00703        }
00704 
00705        be = select_backend( &o.o_req_ndn, 1 );
00706        if ( !be || !be->be_search ) {
00707               return SLAP_CB_CONTINUE;
00708        }
00709 
00710        if ( overlay_entry_get_ov( &o, &o.o_req_ndn, NULL, NULL, 0, &e, on ) !=
00711               LDAP_SUCCESS || e == NULL )
00712        {
00713               return SLAP_CB_CONTINUE;
00714        }
00715 
00716        /* check for dynlist objectClass; done if not found */
00717        dli = (dynlist_info_t *)on->on_bi.bi_private;
00718        while ( dli != NULL && !is_entry_objectclass_or_sub( e, dli->dli_oc ) ) {
00719               dli = dli->dli_next;
00720        }
00721        if ( dli == NULL ) {
00722               goto release;
00723        }
00724 
00725        if ( ad_dgIdentity ) {
00726               Attribute *id = attrs_find( e->e_attrs, ad_dgIdentity );
00727               if ( id ) {
00728                      Attribute *authz;
00729 
00730                      /* if not rootdn and dgAuthz is present,
00731                       * check if user can be authorized as dgIdentity */
00732                      if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op )
00733                             && ( authz = attrs_find( e->e_attrs, ad_dgAuthz ) ) )
00734                      {
00735                             if ( slap_sasl_matches( op, authz->a_nvals,
00736                                    &o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS )
00737                             {
00738                                    goto release;
00739                             }
00740                      }
00741 
00742                      o.o_dn = id->a_vals[0];
00743                      o.o_ndn = id->a_nvals[0];
00744                      o.o_groups = NULL;
00745               }
00746        }
00747 
00748        /* generate dynamic list with dynlist_response() and compare */
00749        {
00750               SlapReply     r = { REP_SEARCH };
00751               dynlist_cc_t  dc = { { 0, dynlist_sc_compare_entry, 0, 0 }, 0 };
00752               AttributeName an[2];
00753 
00754               dc.dc_ava = op->orc_ava;
00755               dc.dc_res = &rs->sr_err;
00756               o.o_callback = (slap_callback *) &dc;
00757 
00758               o.o_tag = LDAP_REQ_SEARCH;
00759               o.ors_limit = NULL;
00760               o.ors_tlimit = SLAP_NO_LIMIT;
00761               o.ors_slimit = SLAP_NO_LIMIT;
00762 
00763               o.ors_filterstr = *slap_filterstr_objectClass_pres;
00764               o.ors_filter = (Filter *) slap_filter_objectClass_pres;
00765 
00766               o.ors_scope = LDAP_SCOPE_BASE;
00767               o.ors_deref = LDAP_DEREF_NEVER;
00768               an[0].an_name = op->orc_ava->aa_desc->ad_cname;
00769               an[0].an_desc = op->orc_ava->aa_desc;
00770               BER_BVZERO( &an[1].an_name );
00771               o.ors_attrs = an;
00772               o.ors_attrsonly = 0;
00773 
00774               o.o_acl_priv = ACL_COMPARE;
00775 
00776               o.o_bd = be;
00777               (void)be->be_search( &o, &r );
00778 
00779               if ( o.o_dn.bv_val != op->o_dn.bv_val ) {
00780                      slap_op_groups_free( &o );
00781               }
00782        }
00783 
00784 release:;
00785        if ( e != NULL ) {
00786               overlay_entry_release_ov( &o, e, 0, on );
00787        }
00788 
00789        return SLAP_CB_CONTINUE;
00790 }
00791 
00792 static int
00793 dynlist_response( Operation *op, SlapReply *rs )
00794 {
00795        switch ( op->o_tag ) {
00796        case LDAP_REQ_SEARCH:
00797               if ( rs->sr_type == REP_SEARCH && !get_manageDSAit( op ) )
00798               {
00799                      int    rc = SLAP_CB_CONTINUE;
00800                      dynlist_info_t       *dli = NULL;
00801 
00802                      while ( (dli = dynlist_is_dynlist_next( op, rs, dli )) != NULL ) {
00803                             rc = dynlist_prepare_entry( op, rs, dli );
00804                      }
00805 
00806                      return rc;
00807               }
00808               break;
00809 
00810        case LDAP_REQ_COMPARE:
00811               switch ( rs->sr_err ) {
00812               /* NOTE: we waste a few cycles running the dynamic list
00813                * also when the result is FALSE, which occurs if the
00814                * dynamic entry itself contains the AVA attribute  */
00815               /* FIXME: this approach is less than optimal; a dedicated
00816                * compare op should be implemented, that fetches the
00817                * entry, checks if it has the appropriate objectClass
00818                * and, in case, runs a compare thru all the URIs,
00819                * stopping at the first positive occurrence; see ITS#3756 */
00820               case LDAP_COMPARE_FALSE:
00821               case LDAP_NO_SUCH_ATTRIBUTE:
00822                      return dynlist_compare( op, rs );
00823               }
00824               break;
00825        }
00826 
00827        return SLAP_CB_CONTINUE;
00828 }
00829 
00830 static int
00831 dynlist_build_def_filter( dynlist_info_t *dli )
00832 {
00833        char   *ptr;
00834 
00835        dli->dli_default_filter.bv_len = STRLENOF( "(!(objectClass=" "))" )
00836               + dli->dli_oc->soc_cname.bv_len;
00837        dli->dli_default_filter.bv_val = ch_malloc( dli->dli_default_filter.bv_len + 1 );
00838        if ( dli->dli_default_filter.bv_val == NULL ) {
00839               Debug( LDAP_DEBUG_ANY, "dynlist_db_open: malloc failed.\n",
00840                      0, 0, 0 );
00841               return -1;
00842        }
00843 
00844        ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" );
00845        ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
00846        ptr = lutil_strcopy( ptr, "))" );
00847 
00848        assert( ptr == &dli->dli_default_filter.bv_val[dli->dli_default_filter.bv_len] );
00849 
00850        return 0;
00851 }
00852 
00853 enum {
00854        DL_ATTRSET = 1,
00855        DL_ATTRPAIR,
00856        DL_ATTRPAIR_COMPAT,
00857        DL_LAST
00858 };
00859 
00860 static ConfigDriver  dl_cfgen;
00861 
00862 /* XXXmanu 255 is the maximum arguments we allow. Can we go beyond? */
00863 static ConfigTable dlcfg[] = {
00864        { "dynlist-attrset", "group-oc> [uri] <URL-ad> <[mapped:]member-ad> [...]",
00865               3, 0, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen,
00866               "( OLcfgOvAt:8.1 NAME 'olcDlAttrSet' "
00867                      "DESC 'Dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
00868                      "EQUALITY caseIgnoreMatch "
00869                      "SYNTAX OMsDirectoryString "
00870                      "X-ORDERED 'VALUES' )",
00871                      NULL, NULL },
00872        { "dynlist-attrpair", "member-ad> <URL-ad",
00873               3, 3, 0, ARG_MAGIC|DL_ATTRPAIR, dl_cfgen,
00874                      NULL, NULL, NULL },
00875 #ifdef TAKEOVER_DYNGROUP
00876        { "attrpair", "member-ad> <URL-ad",
00877               3, 3, 0, ARG_MAGIC|DL_ATTRPAIR_COMPAT, dl_cfgen,
00878                      NULL, NULL, NULL },
00879 #endif
00880        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
00881 };
00882 
00883 static ConfigOCs dlocs[] = {
00884        { "( OLcfgOvOc:8.1 "
00885               "NAME 'olcDynamicList' "
00886               "DESC 'Dynamic list configuration' "
00887               "SUP olcOverlayConfig "
00888               "MAY olcDLattrSet )",
00889               Cft_Overlay, dlcfg, NULL, NULL },
00890        { NULL, 0, NULL }
00891 };
00892 
00893 static int
00894 dl_cfgen( ConfigArgs *c )
00895 {
00896        slap_overinst *on = (slap_overinst *)c->bi;
00897        dynlist_info_t       *dli = (dynlist_info_t *)on->on_bi.bi_private;
00898 
00899        int           rc = 0, i;
00900 
00901        if ( c->op == SLAP_CONFIG_EMIT ) {
00902               switch( c->type ) {
00903               case DL_ATTRSET:
00904                      for ( i = 0; dli; i++, dli = dli->dli_next ) {
00905                             struct berval bv;
00906                             char          *ptr = c->cr_msg;
00907                             dynlist_map_t *dlm;
00908 
00909                             assert( dli->dli_oc != NULL );
00910                             assert( dli->dli_ad != NULL );
00911 
00912                             /* FIXME: check buffer overflow! */
00913                             ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
00914                                    SLAP_X_ORDERED_FMT "%s", i,
00915                                    dli->dli_oc->soc_cname.bv_val );
00916 
00917                             if ( !BER_BVISNULL( &dli->dli_uri ) ) {
00918                                    *ptr++ = ' ';
00919                                    *ptr++ = '"';
00920                                    ptr = lutil_strncopy( ptr, dli->dli_uri.bv_val,
00921                                           dli->dli_uri.bv_len );
00922                                    *ptr++ = '"';
00923                             }
00924 
00925                             *ptr++ = ' ';
00926                             ptr = lutil_strncopy( ptr, dli->dli_ad->ad_cname.bv_val,
00927                                    dli->dli_ad->ad_cname.bv_len );
00928 
00929                             for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
00930                                    ptr[ 0 ] = ' ';
00931                                    ptr++;
00932                                    if ( dlm->dlm_mapped_ad ) {
00933                                           ptr = lutil_strcopy( ptr, dlm->dlm_mapped_ad->ad_cname.bv_val );
00934                                           ptr[ 0 ] = ':';
00935                                           ptr++;
00936                                    }
00937                                           
00938                                    ptr = lutil_strcopy( ptr, dlm->dlm_member_ad->ad_cname.bv_val );
00939                             }
00940 
00941                             bv.bv_val = c->cr_msg;
00942                             bv.bv_len = ptr - bv.bv_val;
00943                             value_add_one( &c->rvalue_vals, &bv );
00944                      }
00945                      break;
00946 
00947               case DL_ATTRPAIR_COMPAT:
00948               case DL_ATTRPAIR:
00949                      rc = 1;
00950                      break;
00951 
00952               default:
00953                      rc = 1;
00954                      break;
00955               }
00956 
00957               return rc;
00958 
00959        } else if ( c->op == LDAP_MOD_DELETE ) {
00960               switch( c->type ) {
00961               case DL_ATTRSET:
00962                      if ( c->valx < 0 ) {
00963                             dynlist_info_t       *dli_next;
00964 
00965                             for ( dli_next = dli; dli_next; dli = dli_next ) {
00966                                    dynlist_map_t *dlm = dli->dli_dlm;
00967                                    dynlist_map_t *dlm_next;
00968 
00969                                    dli_next = dli->dli_next;
00970 
00971                                    if ( !BER_BVISNULL( &dli->dli_uri ) ) {
00972                                           ch_free( dli->dli_uri.bv_val );
00973                                    }
00974 
00975                                    if ( dli->dli_lud != NULL ) {
00976                                           ldap_free_urldesc( dli->dli_lud );
00977                                    }
00978 
00979                                    if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
00980                                           ber_memfree( dli->dli_uri_nbase.bv_val );
00981                                    }
00982 
00983                                    if ( dli->dli_uri_filter != NULL ) {
00984                                           filter_free( dli->dli_uri_filter );
00985                                    }
00986 
00987                                    ch_free( dli->dli_default_filter.bv_val );
00988 
00989                                    while ( dlm != NULL ) {
00990                                           dlm_next = dlm->dlm_next;
00991                                           ch_free( dlm );
00992                                           dlm = dlm_next;
00993                                    }
00994                                    ch_free( dli );
00995                             }
00996 
00997                             on->on_bi.bi_private = NULL;
00998 
00999                      } else {
01000                             dynlist_info_t       **dlip;
01001                             dynlist_map_t *dlm;
01002                             dynlist_map_t *dlm_next;
01003 
01004                             for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private;
01005                                    i < c->valx; i++ )
01006                             {
01007                                    if ( *dlip == NULL ) {
01008                                           return 1;
01009                                    }
01010                                    dlip = &(*dlip)->dli_next;
01011                             }
01012 
01013                             dli = *dlip;
01014                             *dlip = dli->dli_next;
01015 
01016                             if ( !BER_BVISNULL( &dli->dli_uri ) ) {
01017                                    ch_free( dli->dli_uri.bv_val );
01018                             }
01019 
01020                             if ( dli->dli_lud != NULL ) {
01021                                    ldap_free_urldesc( dli->dli_lud );
01022                             }
01023 
01024                             if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
01025                                    ber_memfree( dli->dli_uri_nbase.bv_val );
01026                             }
01027 
01028                             if ( dli->dli_uri_filter != NULL ) {
01029                                    filter_free( dli->dli_uri_filter );
01030                             }
01031 
01032                             ch_free( dli->dli_default_filter.bv_val );
01033 
01034                             dlm = dli->dli_dlm;
01035                             while ( dlm != NULL ) {
01036                                    dlm_next = dlm->dlm_next;
01037                                    ch_free( dlm );
01038                                    dlm = dlm_next;
01039                             }
01040                             ch_free( dli );
01041 
01042                             dli = (dynlist_info_t *)on->on_bi.bi_private;
01043                      }
01044                      break;
01045 
01046               case DL_ATTRPAIR_COMPAT:
01047               case DL_ATTRPAIR:
01048                      rc = 1;
01049                      break;
01050 
01051               default:
01052                      rc = 1;
01053                      break;
01054               }
01055 
01056               return rc;
01057        }
01058 
01059        switch( c->type ) {
01060        case DL_ATTRSET: {
01061               dynlist_info_t              **dlip,
01062                                    *dli_next = NULL;
01063               ObjectClass          *oc = NULL;
01064               AttributeDescription *ad = NULL;
01065               int                  attridx = 2;
01066               LDAPURLDesc          *lud = NULL;
01067               struct berval        nbase = BER_BVNULL;
01068               Filter               *filter = NULL;
01069               struct berval        uri = BER_BVNULL;
01070               dynlist_map_t           *dlm = NULL, *dlml = NULL;
01071               const char           *text;
01072 
01073               oc = oc_find( c->argv[ 1 ] );
01074               if ( oc == NULL ) {
01075                      snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
01076                             "unable to find ObjectClass \"%s\"",
01077                             c->argv[ 1 ] );
01078                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01079                             c->log, c->cr_msg, 0 );
01080                      return 1;
01081               }
01082 
01083               if ( strncasecmp( c->argv[ attridx ], "ldap://", STRLENOF("ldap://") ) == 0 ) {
01084                      if ( ldap_url_parse( c->argv[ attridx ], &lud ) != LDAP_URL_SUCCESS ) {
01085                             snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
01086                                    "unable to parse URI \"%s\"",
01087                                    c->argv[ attridx ] );
01088                             rc = 1;
01089                             goto done_uri;
01090                      }
01091 
01092                      if ( lud->lud_host != NULL ) {
01093                             if ( lud->lud_host[0] == '\0' ) {
01094                                    ch_free( lud->lud_host );
01095                                    lud->lud_host = NULL;
01096 
01097                             } else {
01098                                    snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
01099                                           "host not allowed in URI \"%s\"",
01100                                           c->argv[ attridx ] );
01101                                    rc = 1;
01102                                    goto done_uri;
01103                             }
01104                      }
01105 
01106                      if ( lud->lud_attrs != NULL ) {
01107                             snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
01108                                    "attrs not allowed in URI \"%s\"",
01109                                    c->argv[ attridx ] );
01110                             rc = 1;
01111                             goto done_uri;
01112                      }
01113 
01114                      if ( lud->lud_exts != NULL ) {
01115                             snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
01116                                    "extensions not allowed in URI \"%s\"",
01117                                    c->argv[ attridx ] );
01118                             rc = 1;
01119                             goto done_uri;
01120                      }
01121 
01122                      if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' ) {
01123                             struct berval dn;
01124                             ber_str2bv( lud->lud_dn, 0, 0, &dn );
01125                             rc = dnNormalize( 0, NULL, NULL, &dn, &nbase, NULL );
01126                             if ( rc != LDAP_SUCCESS ) {
01127                                    snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
01128                                           "DN normalization failed in URI \"%s\"",
01129                                           c->argv[ attridx ] );
01130                                    goto done_uri;
01131                             }
01132                      }
01133 
01134                      if ( lud->lud_filter != NULL && lud->lud_filter[ 0 ] != '\0' ) {
01135                             filter = str2filter( lud->lud_filter );
01136                             if ( filter == NULL ) {
01137                                    snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
01138                                           "filter parsing failed in URI \"%s\"",
01139                                           c->argv[ attridx ] );
01140                                    rc = 1;
01141                                    goto done_uri;
01142                             }
01143                      }
01144 
01145                      ber_str2bv( c->argv[ attridx ], 0, 1, &uri );
01146 
01147 done_uri:;
01148                      if ( rc ) {
01149                             if ( lud ) {
01150                                    ldap_free_urldesc( lud );
01151                             }
01152 
01153                             if ( !BER_BVISNULL( &nbase ) ) {
01154                                    ber_memfree( nbase.bv_val );
01155                             }
01156 
01157                             if ( filter != NULL ) {
01158                                    filter_free( filter );
01159                             }
01160 
01161                             Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01162                                    c->log, c->cr_msg, 0 );
01163 
01164                             return rc;
01165                      }
01166 
01167                      attridx++;
01168               }
01169 
01170               rc = slap_str2ad( c->argv[ attridx ], &ad, &text );
01171               if ( rc != LDAP_SUCCESS ) {
01172                      snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
01173                             "unable to find AttributeDescription \"%s\"",
01174                             c->argv[ attridx ] );
01175                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01176                             c->log, c->cr_msg, 0 );
01177                      return 1;
01178               }
01179 
01180               if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
01181                      snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
01182                             "AttributeDescription \"%s\" "
01183                             "must be a subtype of \"labeledURI\"",
01184                             c->argv[ attridx ] );
01185                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01186                             c->log, c->cr_msg, 0 );
01187                      return 1;
01188               }
01189 
01190               attridx++;
01191 
01192               for ( i = attridx; i < c->argc; i++ ) {
01193                      char *arg; 
01194                      char *cp;
01195                      AttributeDescription *member_ad = NULL;
01196                      AttributeDescription *mapped_ad = NULL;
01197                      dynlist_map_t *dlmp;
01198 
01199 
01200                      /*
01201                       * If no mapped attribute is given, dn is used 
01202                       * for backward compatibility.
01203                       */
01204                      arg = c->argv[i];
01205                      if ( ( cp = strchr( arg, ':' ) ) != NULL ) {
01206                             struct berval bv;
01207                             ber_str2bv( arg, cp - arg, 0, &bv );
01208                             rc = slap_bv2ad( &bv, &mapped_ad, &text );
01209                             if ( rc != LDAP_SUCCESS ) {
01210                                    snprintf( c->cr_msg, sizeof( c->cr_msg ),
01211                                           DYNLIST_USAGE
01212                                           "unable to find mapped AttributeDescription #%d \"%s\"\n",
01213                                           i - 3, c->argv[ i ] );
01214                                    Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01215                                           c->log, c->cr_msg, 0 );
01216                                    return 1;
01217                             }
01218                             arg = cp + 1;
01219                      }
01220 
01221                      rc = slap_str2ad( arg, &member_ad, &text );
01222                      if ( rc != LDAP_SUCCESS ) {
01223                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
01224                                    DYNLIST_USAGE
01225                                    "unable to find AttributeDescription #%d \"%s\"\n",
01226                                    i - 3, c->argv[ i ] );
01227                             Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01228                                    c->log, c->cr_msg, 0 );
01229                             return 1;
01230                      }
01231 
01232                      dlmp = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
01233                      if ( dlm == NULL ) {
01234                             dlm = dlmp;
01235                      }
01236                      dlmp->dlm_member_ad = member_ad;
01237                      dlmp->dlm_mapped_ad = mapped_ad;
01238                      dlmp->dlm_next = NULL;
01239               
01240                      if ( dlml != NULL ) 
01241                             dlml->dlm_next = dlmp;
01242                      dlml = dlmp;
01243               }
01244 
01245               if ( c->valx > 0 ) {
01246                      int    i;
01247 
01248                      for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private;
01249                             i < c->valx; i++ )
01250                      {
01251                             if ( *dlip == NULL ) {
01252                                    snprintf( c->cr_msg, sizeof( c->cr_msg ),
01253                                           DYNLIST_USAGE
01254                                           "invalid index {%d}\n",
01255                                           c->valx );
01256                                    Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01257                                           c->log, c->cr_msg, 0 );
01258                                    return 1;
01259                             }
01260                             dlip = &(*dlip)->dli_next;
01261                      }
01262                      dli_next = *dlip;
01263 
01264               } else {
01265                      for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
01266                             *dlip; dlip = &(*dlip)->dli_next )
01267                             /* goto last */;
01268               }
01269 
01270               *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
01271 
01272               (*dlip)->dli_oc = oc;
01273               (*dlip)->dli_ad = ad;
01274               (*dlip)->dli_dlm = dlm;
01275               (*dlip)->dli_next = dli_next;
01276 
01277               (*dlip)->dli_lud = lud;
01278               (*dlip)->dli_uri_nbase = nbase;
01279               (*dlip)->dli_uri_filter = filter;
01280               (*dlip)->dli_uri = uri;
01281 
01282               rc = dynlist_build_def_filter( *dlip );
01283 
01284               } break;
01285 
01286        case DL_ATTRPAIR_COMPAT:
01287               snprintf( c->cr_msg, sizeof( c->cr_msg ),
01288                      "warning: \"attrpair\" only supported for limited "
01289                      "backward compatibility with overlay \"dyngroup\"" );
01290               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
01291               /* fallthru */
01292 
01293        case DL_ATTRPAIR: {
01294               dynlist_info_t              **dlip;
01295               ObjectClass          *oc = NULL;
01296               AttributeDescription *ad = NULL,
01297                                    *member_ad = NULL;
01298               const char           *text;
01299 
01300               oc = oc_find( "groupOfURLs" );
01301               if ( oc == NULL ) {
01302                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
01303                             "\"dynlist-attrpair <member-ad> <URL-ad>\": "
01304                             "unable to find default ObjectClass \"groupOfURLs\"" );
01305                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01306                             c->log, c->cr_msg, 0 );
01307                      return 1;
01308               }
01309 
01310               rc = slap_str2ad( c->argv[ 1 ], &member_ad, &text );
01311               if ( rc != LDAP_SUCCESS ) {
01312                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
01313                             "\"dynlist-attrpair <member-ad> <URL-ad>\": "
01314                             "unable to find AttributeDescription \"%s\"",
01315                             c->argv[ 1 ] );
01316                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01317                             c->log, c->cr_msg, 0 );
01318                      return 1;
01319               }
01320 
01321               rc = slap_str2ad( c->argv[ 2 ], &ad, &text );
01322               if ( rc != LDAP_SUCCESS ) {
01323                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
01324                             "\"dynlist-attrpair <member-ad> <URL-ad>\": "
01325                             "unable to find AttributeDescription \"%s\"\n",
01326                             c->argv[ 2 ] );
01327                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01328                             c->log, c->cr_msg, 0 );
01329                      return 1;
01330               }
01331 
01332               if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
01333                      snprintf( c->cr_msg, sizeof( c->cr_msg ),
01334                             DYNLIST_USAGE
01335                             "AttributeDescription \"%s\" "
01336                             "must be a subtype of \"labeledURI\"",
01337                             c->argv[ 2 ] );
01338                      Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01339                             c->log, c->cr_msg, 0 );
01340                      return 1;
01341               }
01342 
01343               for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
01344                      *dlip; dlip = &(*dlip)->dli_next )
01345               {
01346                      /* 
01347                       * The same URL attribute / member attribute pair
01348                       * cannot be repeated, but we enforce this only 
01349                       * when the member attribute is unique. Performing
01350                       * the check for multiple values would require
01351                       * sorting and comparing the lists, which is left
01352                       * as a future improvement
01353                       */
01354                      if ( (*dlip)->dli_ad == ad &&
01355                           (*dlip)->dli_dlm->dlm_next == NULL &&
01356                           member_ad == (*dlip)->dli_dlm->dlm_member_ad ) {
01357                             snprintf( c->cr_msg, sizeof( c->cr_msg ),
01358                                    "\"dynlist-attrpair <member-ad> <URL-ad>\": "
01359                                    "URL attributeDescription \"%s\" already mapped.\n",
01360                                    ad->ad_cname.bv_val );
01361                             Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
01362                                    c->log, c->cr_msg, 0 );
01363 #if 0
01364                             /* make it a warning... */
01365                             return 1;
01366 #endif
01367                      }
01368               }
01369 
01370               *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
01371 
01372               (*dlip)->dli_oc = oc;
01373               (*dlip)->dli_ad = ad;
01374               (*dlip)->dli_dlm = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
01375               (*dlip)->dli_dlm->dlm_member_ad = member_ad;
01376               (*dlip)->dli_dlm->dlm_mapped_ad = NULL;
01377 
01378               rc = dynlist_build_def_filter( *dlip );
01379 
01380               } break;
01381 
01382        default:
01383               rc = 1;
01384               break;
01385        }
01386 
01387        return rc;
01388 }
01389 
01390 static int
01391 dynlist_db_open(
01392        BackendDB     *be,
01393        ConfigReply   *cr )
01394 {
01395        slap_overinst        *on = (slap_overinst *) be->bd_info;
01396        dynlist_info_t              *dli = (dynlist_info_t *)on->on_bi.bi_private;
01397        ObjectClass          *oc = NULL;
01398        AttributeDescription *ad = NULL;
01399        const char    *text;
01400        int rc;
01401 
01402        if ( dli == NULL ) {
01403               dli = ch_calloc( 1, sizeof( dynlist_info_t ) );
01404               on->on_bi.bi_private = (void *)dli;
01405        }
01406 
01407        for ( ; dli; dli = dli->dli_next ) {
01408               if ( dli->dli_oc == NULL ) {
01409                      if ( oc == NULL ) {
01410                             oc = oc_find( "groupOfURLs" );
01411                             if ( oc == NULL ) {
01412                                    snprintf( cr->msg, sizeof( cr->msg),
01413                                           "unable to fetch objectClass \"groupOfURLs\"" );
01414                                    Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg, 0, 0 );
01415                                    return 1;
01416                             }
01417                      }
01418 
01419                      dli->dli_oc = oc;
01420               }
01421 
01422               if ( dli->dli_ad == NULL ) {
01423                      if ( ad == NULL ) {
01424                             rc = slap_str2ad( "memberURL", &ad, &text );
01425                             if ( rc != LDAP_SUCCESS ) {
01426                                    snprintf( cr->msg, sizeof( cr->msg),
01427                                           "unable to fetch attributeDescription \"memberURL\": %d (%s)",
01428                                           rc, text );
01429                                    Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg, 0, 0 );
01430                                    return 1;
01431                             }
01432                      }
01433               
01434                      dli->dli_ad = ad;                  
01435               }
01436 
01437               if ( BER_BVISNULL( &dli->dli_default_filter ) ) {
01438                      rc = dynlist_build_def_filter( dli );
01439                      if ( rc != 0 ) {
01440                             return rc;
01441                      }
01442               }
01443        }
01444 
01445        if ( ad_dgIdentity == NULL ) {
01446               rc = slap_str2ad( "dgIdentity", &ad_dgIdentity, &text );
01447               if ( rc != LDAP_SUCCESS ) {
01448                      snprintf( cr->msg, sizeof( cr->msg),
01449                             "unable to fetch attributeDescription \"dgIdentity\": %d (%s)",
01450                             rc, text );
01451                      Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg, 0, 0 );
01452                      /* Just a warning */
01453               }
01454        }
01455 
01456        if ( ad_dgAuthz == NULL ) {
01457               rc = slap_str2ad( "dgAuthz", &ad_dgAuthz, &text );
01458               if ( rc != LDAP_SUCCESS ) {
01459                      snprintf( cr->msg, sizeof( cr->msg),
01460                             "unable to fetch attributeDescription \"dgAuthz\": %d (%s)",
01461                             rc, text );
01462                      Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg, 0, 0 );
01463                      /* Just a warning */
01464               }
01465        }
01466 
01467        return 0;
01468 }
01469 
01470 static int
01471 dynlist_db_destroy(
01472        BackendDB     *be,
01473        ConfigReply   *cr )
01474 {
01475        slap_overinst *on = (slap_overinst *) be->bd_info;
01476 
01477        if ( on->on_bi.bi_private ) {
01478               dynlist_info_t       *dli = (dynlist_info_t *)on->on_bi.bi_private,
01479                             *dli_next;
01480 
01481               for ( dli_next = dli; dli_next; dli = dli_next ) {
01482                      dynlist_map_t *dlm;
01483                      dynlist_map_t *dlm_next;
01484 
01485                      dli_next = dli->dli_next;
01486 
01487                      if ( !BER_BVISNULL( &dli->dli_uri ) ) {
01488                             ch_free( dli->dli_uri.bv_val );
01489                      }
01490 
01491                      if ( dli->dli_lud != NULL ) {
01492                             ldap_free_urldesc( dli->dli_lud );
01493                      }
01494 
01495                      if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
01496                             ber_memfree( dli->dli_uri_nbase.bv_val );
01497                      }
01498 
01499                      if ( dli->dli_uri_filter != NULL ) {
01500                             filter_free( dli->dli_uri_filter );
01501                      }
01502 
01503                      ch_free( dli->dli_default_filter.bv_val );
01504 
01505                      dlm = dli->dli_dlm;
01506                      while ( dlm != NULL ) {
01507                             dlm_next = dlm->dlm_next;
01508                             ch_free( dlm );
01509                             dlm = dlm_next;
01510                      }
01511                      ch_free( dli );
01512               }
01513        }
01514 
01515        return 0;
01516 }
01517 
01518 static slap_overinst dynlist = { { NULL } };
01519 #ifdef TAKEOVER_DYNGROUP
01520 static char          *obsolete_names[] = {
01521        "dyngroup",
01522        NULL
01523 };
01524 #endif
01525 
01526 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
01527 static
01528 #endif /* SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC */
01529 int
01530 dynlist_initialize(void)
01531 {
01532        int    rc = 0;
01533 
01534        dynlist.on_bi.bi_type = "dynlist";
01535 
01536 #ifdef TAKEOVER_DYNGROUP
01537        /* makes dynlist incompatible with dyngroup */
01538        dynlist.on_bi.bi_obsolete_names = obsolete_names;
01539 #endif
01540 
01541        dynlist.on_bi.bi_db_config = config_generic_wrapper;
01542        dynlist.on_bi.bi_db_open = dynlist_db_open;
01543        dynlist.on_bi.bi_db_destroy = dynlist_db_destroy;
01544 
01545        dynlist.on_response = dynlist_response;
01546 
01547        dynlist.on_bi.bi_cf_ocs = dlocs;
01548 
01549        rc = config_register_schema( dlcfg, dlocs );
01550        if ( rc ) {
01551               return rc;
01552        }
01553 
01554        return overlay_register( &dynlist );
01555 }
01556 
01557 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
01558 int
01559 init_module( int argc, char *argv[] )
01560 {
01561        return dynlist_initialize();
01562 }
01563 #endif
01564 
01565 #endif /* SLAPD_OVER_DYNLIST */