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