]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/dynlist.c
ITS#5340,ITS#6423
[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-2010 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                 e_flags &= ~REP_ENTRY_MUSTRELEASE;
469         } else {
470                 e = rs->sr_entry;
471         }
472
473         dlc.dlc_e = e;
474         dlc.dlc_dli = dli;
475         cb.sc_private = &dlc;
476         cb.sc_response = dynlist_sc_update;
477         cb.sc_cleanup = NULL;
478         cb.sc_next = NULL;
479
480         o.o_callback = &cb;
481         o.ors_deref = LDAP_DEREF_NEVER;
482         o.ors_limit = NULL;
483         o.ors_tlimit = SLAP_NO_LIMIT;
484         o.ors_slimit = SLAP_NO_LIMIT;
485
486         for ( url = a->a_nvals; !BER_BVISNULL( url ); url++ ) {
487                 LDAPURLDesc     *lud = NULL;
488                 int             i, j;
489                 struct berval   dn;
490                 int             rc;
491
492                 BER_BVZERO( &o.o_req_dn );
493                 BER_BVZERO( &o.o_req_ndn );
494                 o.ors_filter = NULL;
495                 o.ors_attrs = NULL;
496                 BER_BVZERO( &o.ors_filterstr );
497
498                 if ( ldap_url_parse( url->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
499                         /* FIXME: error? */
500                         continue;
501                 }
502
503                 if ( lud->lud_host != NULL ) {
504                         /* FIXME: host not allowed; reject as illegal? */
505                         Debug( LDAP_DEBUG_ANY, "dynlist_prepare_entry(\"%s\"): "
506                                 "illegal URI \"%s\"\n",
507                                 e->e_name.bv_val, url->bv_val, 0 );
508                         goto cleanup;
509                 }
510
511                 if ( lud->lud_dn == NULL ) {
512                         /* note that an empty base is not honored in terms
513                          * of defaultSearchBase, because select_backend()
514                          * is not aware of the defaultSearchBase option;
515                          * this can be useful in case of a database serving
516                          * the empty suffix */
517                         BER_BVSTR( &dn, "" );
518
519                 } else {
520                         ber_str2bv( lud->lud_dn, 0, 0, &dn );
521                 }
522                 rc = dnPrettyNormal( NULL, &dn, &o.o_req_dn, &o.o_req_ndn, op->o_tmpmemctx );
523                 if ( rc != LDAP_SUCCESS ) {
524                         /* FIXME: error? */
525                         goto cleanup;
526                 }
527                 o.ors_scope = lud->lud_scope;
528
529                 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
530                         if ( dlm->dlm_mapped_ad != NULL ) {
531                                 break;
532                         }
533                 }
534
535                 if ( dli->dli_dlm && !dlm ) {
536                         /* if ( lud->lud_attrs != NULL ),
537                          * the URL should be ignored */
538                         o.ors_attrs = slap_anlist_no_attrs;
539
540                 } else if ( lud->lud_attrs == NULL ) {
541                         o.ors_attrs = rs->sr_attrs;
542
543                 } else {
544                         for ( i = 0; lud->lud_attrs[i]; i++)
545                                 /* just count */ ;
546
547                         o.ors_attrs = op->o_tmpcalloc( i + 1, sizeof( AttributeName ), op->o_tmpmemctx );
548                         for ( i = 0, j = 0; lud->lud_attrs[i]; i++) {
549                                 const char      *text = NULL;
550         
551                                 ber_str2bv( lud->lud_attrs[i], 0, 0, &o.ors_attrs[j].an_name );
552                                 o.ors_attrs[j].an_desc = NULL;
553                                 (void)slap_bv2ad( &o.ors_attrs[j].an_name, &o.ors_attrs[j].an_desc, &text );
554                                 /* FIXME: ignore errors... */
555
556                                 if ( rs->sr_attrs == NULL ) {
557                                         if ( o.ors_attrs[j].an_desc != NULL &&
558                                                         is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
559                                         {
560                                                 continue;
561                                         }
562
563                                 } else {
564                                         if ( o.ors_attrs[j].an_desc != NULL &&
565                                                         is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
566                                         {
567                                                 if ( !opattrs ) {
568                                                         continue;
569                                                 }
570
571                                                 if ( !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) ) {
572                                                         /* lookup if mapped -- linear search,
573                                                          * not very efficient unless list
574                                                          * is very short */
575                                                         for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
576                                                                 if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
577                                                                         break;
578                                                                 }
579                                                         }
580
581                                                         if ( dlm == NULL ) {
582                                                                 continue;
583                                                         }
584                                                 }
585
586                                         } else {
587                                                 if ( !userattrs && 
588                                                                 o.ors_attrs[j].an_desc != NULL &&
589                                                                 !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
590                                                 {
591                                                         /* lookup if mapped -- linear search,
592                                                          * not very efficient unless list
593                                                          * is very short */
594                                                         for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
595                                                                 if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
596                                                                         break;
597                                                                 }
598                                                         }
599
600                                                         if ( dlm == NULL ) {
601                                                                 continue;
602                                                         }
603                                                 }
604                                         }
605                                 }
606
607                                 j++;
608                         }
609
610                         if ( j == 0 ) {
611                                 goto cleanup;
612                         }
613                 
614                         BER_BVZERO( &o.ors_attrs[j].an_name );
615                 }
616
617                 if ( lud->lud_filter == NULL ) {
618                         ber_dupbv_x( &o.ors_filterstr,
619                                         &dli->dli_default_filter, op->o_tmpmemctx );
620
621                 } else {
622                         struct berval   flt;
623                         ber_str2bv( lud->lud_filter, 0, 0, &flt );
624                         if ( dynlist_make_filter( op, rs->sr_entry, url->bv_val, &flt, &o.ors_filterstr ) ) {
625                                 /* error */
626                                 goto cleanup;
627                         }
628                 }
629                 o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
630                 if ( o.ors_filter == NULL ) {
631                         goto cleanup;
632                 }
633                 
634                 o.o_bd = select_backend( &o.o_req_ndn, 1 );
635                 if ( o.o_bd && o.o_bd->be_search ) {
636 #ifdef SLAP_OPATTRS
637                         r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
638 #endif /* SLAP_OPATTRS */
639                         (void)o.o_bd->be_search( &o, &r );
640                 }
641
642 cleanup:;
643                 if ( id ) {
644                         slap_op_groups_free( &o );
645                 }
646                 if ( o.ors_filter ) {
647                         filter_free_x( &o, o.ors_filter, 1 );
648                 }
649                 if ( o.ors_attrs && o.ors_attrs != rs->sr_attrs
650                                 && o.ors_attrs != slap_anlist_no_attrs )
651                 {
652                         op->o_tmpfree( o.ors_attrs, op->o_tmpmemctx );
653                 }
654                 if ( !BER_BVISNULL( &o.o_req_dn ) ) {
655                         op->o_tmpfree( o.o_req_dn.bv_val, op->o_tmpmemctx );
656                 }
657                 if ( !BER_BVISNULL( &o.o_req_ndn ) ) {
658                         op->o_tmpfree( o.o_req_ndn.bv_val, op->o_tmpmemctx );
659                 }
660                 assert( BER_BVISNULL( &o.ors_filterstr )
661                         || o.ors_filterstr.bv_val != lud->lud_filter );
662                 op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
663                 ldap_free_urldesc( lud );
664         }
665
666         rs->sr_entry = e;
667         rs->sr_flags = e_flags;
668
669         return SLAP_CB_CONTINUE;
670 }
671
672 static int
673 dynlist_sc_save_entry( Operation *op, SlapReply *rs )
674 {
675         /* save the entry in the private field of the callback,
676          * so it doesn't get freed (it's temporary!) */
677         if ( rs->sr_entry != NULL ) {
678                 dynlist_sc_t    *dlc = (dynlist_sc_t *)op->o_callback->sc_private;
679                 dlc->dlc_e = rs->sr_entry;
680                 rs->sr_entry = NULL;
681         }
682
683         return 0;
684 }
685
686 static int
687 dynlist_compare( Operation *op, SlapReply *rs )
688 {
689         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
690         dynlist_info_t  *dli = (dynlist_info_t *)on->on_bi.bi_private;
691         Operation o = *op;
692         Entry *e = NULL;
693         dynlist_map_t *dlm;
694
695         for ( ; dli != NULL; dli = dli->dli_next ) {
696                 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next )
697                         if ( op->oq_compare.rs_ava->aa_desc == dlm->dlm_member_ad )
698                                 break;
699
700                 if ( dli->dli_dlm && dlm ) {
701                         /* This compare is for one of the attributes we're
702                          * interested in. We'll use slapd's existing dyngroup
703                          * evaluator to get the answer we want.
704                          */
705                         BerVarray id = NULL, authz = NULL;
706
707                         o.o_do_not_cache = 1;
708
709                         if ( ad_dgIdentity && backend_attribute( &o, NULL, &o.o_req_ndn,
710                                 ad_dgIdentity, &id, ACL_READ ) == LDAP_SUCCESS )
711                         {
712                                 /* if not rootdn and dgAuthz is present,
713                                  * check if user can be authorized as dgIdentity */
714                                 if ( ad_dgAuthz && !BER_BVISEMPTY( id ) && !be_isroot( op )
715                                         && backend_attribute( &o, NULL, &o.o_req_ndn,
716                                                 ad_dgAuthz, &authz, ACL_READ ) == LDAP_SUCCESS )
717                                 {
718                                         
719                                         rs->sr_err = slap_sasl_matches( op, authz,
720                                                 &o.o_ndn, &o.o_ndn );
721                                         ber_bvarray_free_x( authz, op->o_tmpmemctx );
722                                         if ( rs->sr_err != LDAP_SUCCESS ) {
723                                                 goto done;
724                                         }
725                                 }
726
727                                 o.o_dn = *id;
728                                 o.o_ndn = *id;
729                                 o.o_groups = NULL; /* authz changed, invalidate cached groups */
730                         }
731
732                         rs->sr_err = backend_group( &o, NULL, &o.o_req_ndn,
733                                 &o.oq_compare.rs_ava->aa_value, dli->dli_oc, dli->dli_ad );
734                         switch ( rs->sr_err ) {
735                         case LDAP_SUCCESS:
736                                 rs->sr_err = LDAP_COMPARE_TRUE;
737                                 break;
738
739                         case LDAP_NO_SUCH_OBJECT:
740                                 /* NOTE: backend_group() returns noSuchObject
741                                  * if op_ndn does not exist; however, since
742                                  * dynamic list expansion means that the
743                                  * member attribute is virtually present, the
744                                  * non-existence of the asserted value implies
745                                  * the assertion is FALSE rather than
746                                  * UNDEFINED */
747                                 rs->sr_err = LDAP_COMPARE_FALSE;
748                                 break;
749                         }
750
751 done:;
752                         if ( id ) ber_bvarray_free_x( id, o.o_tmpmemctx );
753
754                         return SLAP_CB_CONTINUE;
755                 }
756         }
757
758         if ( overlay_entry_get_ov( &o, &o.o_req_ndn, NULL, NULL, 0, &e, on ) !=
759                 LDAP_SUCCESS || e == NULL )
760         {
761                 return SLAP_CB_CONTINUE;
762         }
763
764         if ( ad_dgIdentity ) {
765                 Attribute *id = attrs_find( e->e_attrs, ad_dgIdentity );
766                 if ( id ) {
767                         Attribute *authz;
768
769                         /* if not rootdn and dgAuthz is present,
770                          * check if user can be authorized as dgIdentity */
771                         if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op )
772                                 && ( authz = attrs_find( e->e_attrs, ad_dgAuthz ) ) )
773                         {
774                                 if ( slap_sasl_matches( op, authz->a_nvals,
775                                         &o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS )
776                                 {
777                                         goto release;
778                                 }
779                         }
780
781                         o.o_dn = id->a_vals[0];
782                         o.o_ndn = id->a_nvals[0];
783                         o.o_groups = NULL;
784                 }
785         }
786
787         dli = (dynlist_info_t *)on->on_bi.bi_private;
788         for ( ; dli != NULL && rs->sr_err != LDAP_COMPARE_TRUE; dli = dli->dli_next ) {
789                 Attribute       *a;
790                 slap_callback   cb;
791                 SlapReply       r = { REP_SEARCH };
792                 AttributeName   an[2];
793                 int             rc;
794                 dynlist_sc_t    dlc = { 0 };
795
796                 if ( !is_entry_objectclass_or_sub( e, dli->dli_oc ))
797                         continue;
798
799                 /* if the entry has the right objectClass, generate
800                  * the dynamic list and compare */
801                 dlc.dlc_dli = dli;
802                 cb.sc_private = &dlc;
803                 cb.sc_response = dynlist_sc_save_entry;
804                 cb.sc_cleanup = NULL;
805                 cb.sc_next = NULL;
806                 o.o_callback = &cb;
807
808                 o.o_tag = LDAP_REQ_SEARCH;
809                 o.ors_limit = NULL;
810                 o.ors_tlimit = SLAP_NO_LIMIT;
811                 o.ors_slimit = SLAP_NO_LIMIT;
812
813                 o.o_bd = select_backend( &o.o_req_ndn, 1 );
814                 if ( !o.o_bd || !o.o_bd->be_search ) {
815                         goto release;
816                 }
817
818                 o.ors_filterstr = *slap_filterstr_objectClass_pres;
819                 o.ors_filter = (Filter *) slap_filter_objectClass_pres;
820
821                 o.ors_scope = LDAP_SCOPE_BASE;
822                 o.ors_deref = LDAP_DEREF_NEVER;
823                 an[0].an_name = op->orc_ava->aa_desc->ad_cname;
824                 an[0].an_desc = op->orc_ava->aa_desc;
825                 BER_BVZERO( &an[1].an_name );
826                 o.ors_attrs = an;
827                 o.ors_attrsonly = 0;
828
829                 o.o_acl_priv = ACL_COMPARE;
830
831                 rc = o.o_bd->be_search( &o, &r );
832
833                 if ( o.o_dn.bv_val != op->o_dn.bv_val ) {
834                         slap_op_groups_free( &o );
835                 }
836
837                 if ( rc != 0 ) {
838                         goto release;
839                 }
840
841                 if ( dlc.dlc_e != NULL ) {
842                         r.sr_entry = dlc.dlc_e;
843                 }
844
845                 if ( r.sr_err != LDAP_SUCCESS || r.sr_entry == NULL ) {
846                         /* error? */
847                         goto release;
848                 }
849
850                 for ( a = attrs_find( r.sr_entry->e_attrs, op->orc_ava->aa_desc );
851                         a != NULL;
852                         a = attrs_find( a->a_next, op->orc_ava->aa_desc ) )
853                 {
854                         /* if we're here, we got a match... */
855                         rs->sr_err = LDAP_COMPARE_FALSE;
856
857                         if ( attr_valfind( a,
858                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
859                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
860                                 &op->orc_ava->aa_value, NULL, op->o_tmpmemctx ) == 0 )
861                         {
862                                 rs->sr_err = LDAP_COMPARE_TRUE;
863                                 break;
864                         }
865                 }
866
867                 if ( r.sr_flags & REP_ENTRY_MUSTBEFREED ) {
868                         entry_free( r.sr_entry );
869                         r.sr_entry = NULL;
870                         r.sr_flags ^= REP_ENTRY_MUSTBEFREED;
871                 }
872         }
873
874 release:;
875         if ( e != NULL ) {
876                 overlay_entry_release_ov( &o, e, 0, on );
877         }
878
879         return SLAP_CB_CONTINUE;
880 }
881
882 static int
883 dynlist_response( Operation *op, SlapReply *rs )
884 {
885         dynlist_info_t  *dli;
886
887         switch ( op->o_tag ) {
888         case LDAP_REQ_SEARCH:
889                 if ( rs->sr_type == REP_SEARCH && !get_manageDSAit( op ) )
890                 {
891                         int     rc = LDAP_OTHER;
892
893                         for ( dli = dynlist_is_dynlist_next( op, rs, NULL );
894                                 dli;
895                                 dli = dynlist_is_dynlist_next( op, rs, dli ) )
896                         {
897                                 rc = dynlist_prepare_entry( op, rs, dli );
898                         }
899
900                         if ( rc != LDAP_OTHER ) {
901                                 return rc;
902                         }
903                 }
904                 break;
905
906         case LDAP_REQ_COMPARE:
907                 switch ( rs->sr_err ) {
908                 /* NOTE: we waste a few cycles running the dynamic list
909                  * also when the result is FALSE, which occurs if the
910                  * dynamic entry itself contains the AVA attribute  */
911                 /* FIXME: this approach is less than optimal; a dedicated
912                  * compare op should be implemented, that fetches the
913                  * entry, checks if it has the appropriate objectClass
914                  * and, in case, runs a compare thru all the URIs,
915                  * stopping at the first positive occurrence; see ITS#3756 */
916                 case LDAP_COMPARE_FALSE:
917                 case LDAP_NO_SUCH_ATTRIBUTE:
918                         return dynlist_compare( op, rs );
919                 }
920                 break;
921
922         default:
923                 break;
924         }
925
926         return SLAP_CB_CONTINUE;
927 }
928
929 static int
930 dynlist_build_def_filter( dynlist_info_t *dli )
931 {
932         char    *ptr;
933
934         dli->dli_default_filter.bv_len = STRLENOF( "(!(objectClass=" "))" )
935                 + dli->dli_oc->soc_cname.bv_len;
936         dli->dli_default_filter.bv_val = ch_malloc( dli->dli_default_filter.bv_len + 1 );
937         if ( dli->dli_default_filter.bv_val == NULL ) {
938                 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: malloc failed.\n",
939                         0, 0, 0 );
940                 return -1;
941         }
942
943         ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" );
944         ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
945         ptr = lutil_strcopy( ptr, "))" );
946
947         assert( ptr == &dli->dli_default_filter.bv_val[dli->dli_default_filter.bv_len] );
948
949         return 0;
950 }
951
952 #ifdef OL_2_2_COMPAT
953 static int
954 dynlist_db_config(
955         BackendDB       *be,
956         const char      *fname,
957         int             lineno,
958         int             argc,
959         char            **argv )
960 {
961         slap_overinst   *on = (slap_overinst *)be->bd_info;
962
963         int             rc = 0;
964
965         if ( strcasecmp( argv[0], "dynlist-attrset" ) == 0 ) {
966                 dynlist_info_t          **dlip;
967                 ObjectClass             *oc;
968                 AttributeDescription    *ad = NULL,
969                                         *member_ad = NULL;
970                 dynlist_map_t           *dlm = NULL, *dlml = NULL;
971                 const char              *text;
972
973                 if ( argc < 3 ) {
974                         Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
975                                 "invalid arg number #%d.\n",
976                                 fname, lineno, argc );
977                         return 1;
978                 }
979
980                 oc = oc_find( argv[1] );
981                 if ( oc == NULL ) {
982                         Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
983                                 "unable to find ObjectClass \"%s\"\n",
984                                 fname, lineno, argv[ 1 ] );
985                         return 1;
986                 }
987
988                 rc = slap_str2ad( argv[2], &ad, &text );
989                 if ( rc != LDAP_SUCCESS ) {
990                         Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
991                                 "unable to find AttributeDescription \"%s\"\n",
992                                 fname, lineno, argv[2] );
993                         return 1;
994                 }
995
996                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
997                         Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
998                                 "AttributeDescription \"%s\" "
999                                 "must be a subtype of \"labeledURI\"\n",
1000                                 fname, lineno, argv[2] );
1001                         return 1;
1002                 }
1003
1004                 for ( i = 3; i < argc; i++ ) {
1005                         char *arg; 
1006                         char *cp;
1007                         AttributeDescription *member_ad = NULL;
1008                         AttributeDescription *mapped_ad = NULL;
1009                         dynlist_map_t *dlmp;
1010
1011
1012                         /*
1013                          * If no mapped attribute is given, dn is used 
1014                          * for backward compatibility.
1015                          */
1016                         arg = argv[i];
1017                         if ( cp = strchr( arg, (int)':' ) != NULL ) {
1018                                 struct berval bv;
1019                                 ber_str2bv( arg, cp - arg, 0, &bv );
1020                                 rc = slap_bv2ad( &bv, &mapped_ad, &text );
1021                                 if ( rc != LDAP_SUCCESS ) {
1022                                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1023                                                 DYNLIST_USAGE
1024                                                 "unable to find mapped AttributeDescription \"%s\"\n",
1025                                                 fname, lineno, arg );
1026                                         return 1;
1027                                 }
1028                                 
1029                                 arg = cp + 1;
1030                         }
1031
1032                         rc = slap_str2ad( arg, &member_ad, &text );
1033                         if ( rc != LDAP_SUCCESS ) {
1034                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1035                                         DYNLIST_USAGE
1036                                         "unable to find AttributeDescription \"%s\"\n",
1037                                         fname, lineno, arg );
1038                                 return 1;
1039                         }
1040
1041                         dlmp = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
1042                         if ( dlm == NULL ) {
1043                                 dlm = dlmp;
1044                         }
1045                         dlmp->dlm_member_ad = member_ad;
1046                         dlmp->dlm_mapped_ad = mapped_ad;
1047                         dlmp->dlm_next = NULL;
1048                 
1049                         if ( dlml != NULL )
1050                                 dlml->dlm_next = dlmp;
1051                         dlml = dlmp;
1052                 }
1053
1054                 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1055                         *dlip; dlip = &(*dlip)->dli_next )
1056                 {
1057                         /* 
1058                          * The same URL attribute / member attribute pair
1059                          * cannot be repeated, but we enforce this only 
1060                          * when the member attribute is unique. Performing
1061                          * the check for multiple values would require
1062                          * sorting and comparing the lists, which is left
1063                          * as a future improvement
1064                          */
1065                         if ( (*dlip)->dli_ad == ad &&
1066                              (*dlip)->dli_dlm->dlm_next == NULL &&
1067                              dlm->dlm_next == NULL &&
1068                              dlm->dlm_member_ad == (*dlip)->dli_dlm->dlm_member_ad &&
1069                              dlm->dlm_mapped_ad == (*dlip)->dli_dlm->dlm_mapped_ad ) {
1070                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1071                                         DYNLIST_USAGE
1072                                         "URL attributeDescription \"%s\" already mapped.\n",
1073                                         fname, lineno, ad->ad_cname.bv_val );
1074 #if 0
1075                                 /* make it a warning... */
1076                                 return 1;
1077 #endif
1078                         }
1079                 }
1080
1081                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1082                 (*dlip)->dli_oc = oc;
1083                 (*dlip)->dli_ad = ad;
1084                 (*dlip)->dli_dlm = dlm;
1085
1086                 if ( dynlist_build_def_filter( *dlip ) ) {
1087                         dynlist_map_t *dlm = (*dlip)->ldi_dlm;
1088                         dynlist_map_t *dlm_next;
1089
1090                         while ( dlm != NULL ) {
1091                                 dlm_next = dlm->dlm_next;
1092                                 ch_free( dlm );
1093                                 dlm = dlm_next;
1094                         }
1095
1096                         ch_free( *dlip );
1097                         *dlip = NULL;
1098                         return 1;
1099                 }
1100
1101         /* allow dyngroup syntax */
1102         } else if ( strcasecmp( argv[0], "dynlist-attrpair" ) == 0 ) {
1103                 dynlist_info_t          **dlip;
1104                 ObjectClass             *oc;
1105                 AttributeDescription    *ad = NULL,
1106                                         *member_ad = NULL;
1107                 const char              *text;
1108
1109                 if ( argc != 3 ) {
1110                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1111                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1112                                 "invalid arg number #%d.\n",
1113                                 fname, lineno, argc );
1114                         return 1;
1115                 }
1116
1117                 oc = oc_find( "groupOfURLs" );
1118                 if ( oc == NULL ) {
1119                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1120                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1121                                 "unable to find default ObjectClass \"groupOfURLs\"\n",
1122                                 fname, lineno, 0 );
1123                         return 1;
1124                 }
1125
1126                 rc = slap_str2ad( argv[1], &member_ad, &text );
1127                 if ( rc != LDAP_SUCCESS ) {
1128                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1129                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1130                                 "unable to find AttributeDescription \"%s\"\n",
1131                                 fname, lineno, argv[1] );
1132                         return 1;
1133                 }
1134
1135                 rc = slap_str2ad( argv[2], &ad, &text );
1136                 if ( rc != LDAP_SUCCESS ) {
1137                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1138                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1139                                 "unable to find AttributeDescription \"%s\"\n",
1140                                 fname, lineno, argv[2] );
1141                         return 1;
1142                 }
1143
1144                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1145                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1146                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1147                                 "AttributeDescription \"%s\" "
1148                                 "must be a subtype of \"labeledURI\"\n",
1149                                 fname, lineno, argv[2] );
1150                         return 1;
1151                 }
1152
1153                 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1154                         *dlip; dlip = &(*dlip)->dli_next )
1155                 {
1156                         /* 
1157                          * The same URL attribute / member attribute pair
1158                          * cannot be repeated, but we enforce this only 
1159                          * when the member attribute is unique. Performing
1160                          * the check for multiple values would require
1161                          * sorting and comparing the lists, which is left
1162                          * as a future improvement
1163                          */
1164                         if ( (*dlip)->dli_ad == ad &&
1165                              (*dlip)->dli_dlm->dlm_next == NULL &&
1166                              member_ad == (*dlip)->dli_dlm->dlm_member_ad ) {
1167                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1168                                         "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1169                                         "URL attributeDescription \"%s\" already mapped.\n",
1170                                         fname, lineno, ad->ad_cname.bv_val );
1171 #if 0
1172                                 /* make it a warning... */
1173                                 return 1;
1174 #endif
1175                         }
1176                 }
1177
1178                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1179                 (*dlip)->dli_oc = oc;
1180                 (*dlip)->dli_ad = ad;
1181                 (*dlip)->dli_dlm = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
1182                 (*dlip)->dli_dlm->dlm_member_ad = member_ad;
1183                 (*dlip)->dli_dlm->dlm_mapped_ad = NULL;
1184
1185                 if ( dynlist_build_def_filter( *dlip ) ) {
1186                         ch_free( (*dlip)->dli_dlm );
1187                         ch_free( *dlip );
1188                         *dlip = NULL;
1189                         return 1;
1190                 }
1191
1192         } else {
1193                 rc = SLAP_CONF_UNKNOWN;
1194         }
1195
1196         return rc;
1197 }
1198
1199 #else
1200 enum {
1201         DL_ATTRSET = 1,
1202         DL_ATTRPAIR,
1203         DL_ATTRPAIR_COMPAT,
1204         DL_LAST
1205 };
1206
1207 static ConfigDriver     dl_cfgen;
1208
1209 /* XXXmanu 255 is the maximum arguments we allow. Can we go beyond? */
1210 static ConfigTable dlcfg[] = {
1211         { "dynlist-attrset", "group-oc> [uri] <URL-ad> <[mapped:]member-ad> [...]",
1212                 3, 0, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen,
1213                 "( OLcfgOvAt:8.1 NAME 'olcDlAttrSet' "
1214                         "DESC 'Dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1215                         "EQUALITY caseIgnoreMatch "
1216                         "SYNTAX OMsDirectoryString "
1217                         "X-ORDERED 'VALUES' )",
1218                         NULL, NULL },
1219         { "dynlist-attrpair", "member-ad> <URL-ad",
1220                 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR, dl_cfgen,
1221                         NULL, NULL, NULL },
1222 #ifdef TAKEOVER_DYNGROUP
1223         { "attrpair", "member-ad> <URL-ad",
1224                 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR_COMPAT, dl_cfgen,
1225                         NULL, NULL, NULL },
1226 #endif
1227         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1228 };
1229
1230 static ConfigOCs dlocs[] = {
1231         { "( OLcfgOvOc:8.1 "
1232                 "NAME 'olcDynamicList' "
1233                 "DESC 'Dynamic list configuration' "
1234                 "SUP olcOverlayConfig "
1235                 "MAY olcDLattrSet )",
1236                 Cft_Overlay, dlcfg, NULL, NULL },
1237         { NULL, 0, NULL }
1238 };
1239
1240 static int
1241 dl_cfgen( ConfigArgs *c )
1242 {
1243         slap_overinst   *on = (slap_overinst *)c->bi;
1244         dynlist_info_t  *dli = (dynlist_info_t *)on->on_bi.bi_private;
1245
1246         int             rc = 0, i;
1247
1248         if ( c->op == SLAP_CONFIG_EMIT ) {
1249                 switch( c->type ) {
1250                 case DL_ATTRSET:
1251                         for ( i = 0; dli; i++, dli = dli->dli_next ) {
1252                                 struct berval   bv;
1253                                 char            *ptr = c->cr_msg;
1254                                 dynlist_map_t   *dlm;
1255
1256                                 assert( dli->dli_oc != NULL );
1257                                 assert( dli->dli_ad != NULL );
1258
1259                                 /* FIXME: check buffer overflow! */
1260                                 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1261                                         SLAP_X_ORDERED_FMT "%s", i,
1262                                         dli->dli_oc->soc_cname.bv_val );
1263
1264                                 if ( !BER_BVISNULL( &dli->dli_uri ) ) {
1265                                         *ptr++ = ' ';
1266                                         *ptr++ = '"';
1267                                         ptr = lutil_strncopy( ptr, dli->dli_uri.bv_val,
1268                                                 dli->dli_uri.bv_len );
1269                                         *ptr++ = '"';
1270                                 }
1271
1272                                 *ptr++ = ' ';
1273                                 ptr = lutil_strncopy( ptr, dli->dli_ad->ad_cname.bv_val,
1274                                         dli->dli_ad->ad_cname.bv_len );
1275
1276                                 for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
1277                                         ptr[ 0 ] = ' ';
1278                                         ptr++;
1279                                         if ( dlm->dlm_mapped_ad ) {
1280                                                 ptr = lutil_strcopy( ptr, dlm->dlm_mapped_ad->ad_cname.bv_val );
1281                                                 ptr[ 0 ] = ':';
1282                                                 ptr++;
1283                                         }
1284                                                 
1285                                         ptr = lutil_strcopy( ptr, dlm->dlm_member_ad->ad_cname.bv_val );
1286                                 }
1287
1288                                 bv.bv_val = c->cr_msg;
1289                                 bv.bv_len = ptr - bv.bv_val;
1290                                 value_add_one( &c->rvalue_vals, &bv );
1291                         }
1292                         break;
1293
1294                 case DL_ATTRPAIR_COMPAT:
1295                 case DL_ATTRPAIR:
1296                         rc = 1;
1297                         break;
1298
1299                 default:
1300                         rc = 1;
1301                         break;
1302                 }
1303
1304                 return rc;
1305
1306         } else if ( c->op == LDAP_MOD_DELETE ) {
1307                 switch( c->type ) {
1308                 case DL_ATTRSET:
1309                         if ( c->valx < 0 ) {
1310                                 dynlist_info_t  *dli_next;
1311
1312                                 for ( dli_next = dli; dli_next; dli = dli_next ) {
1313                                         dynlist_map_t *dlm = dli->dli_dlm;
1314                                         dynlist_map_t *dlm_next;
1315
1316                                         dli_next = dli->dli_next;
1317
1318                                         if ( !BER_BVISNULL( &dli->dli_uri ) ) {
1319                                                 ch_free( dli->dli_uri.bv_val );
1320                                         }
1321
1322                                         if ( dli->dli_lud != NULL ) {
1323                                                 ldap_free_urldesc( dli->dli_lud );
1324                                         }
1325
1326                                         if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
1327                                                 ber_memfree( dli->dli_uri_nbase.bv_val );
1328                                         }
1329
1330                                         if ( dli->dli_uri_filter != NULL ) {
1331                                                 filter_free( dli->dli_uri_filter );
1332                                         }
1333
1334                                         ch_free( dli->dli_default_filter.bv_val );
1335
1336                                         while ( dlm != NULL ) {
1337                                                 dlm_next = dlm->dlm_next;
1338                                                 ch_free( dlm );
1339                                                 dlm = dlm_next;
1340                                         }
1341                                         ch_free( dli );
1342                                 }
1343
1344                                 on->on_bi.bi_private = NULL;
1345
1346                         } else {
1347                                 dynlist_info_t  **dlip;
1348                                 dynlist_map_t *dlm;
1349                                 dynlist_map_t *dlm_next;
1350
1351                                 for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1352                                         i < c->valx; i++ )
1353                                 {
1354                                         if ( *dlip == NULL ) {
1355                                                 return 1;
1356                                         }
1357                                         dlip = &(*dlip)->dli_next;
1358                                 }
1359
1360                                 dli = *dlip;
1361                                 *dlip = dli->dli_next;
1362
1363                                 if ( !BER_BVISNULL( &dli->dli_uri ) ) {
1364                                         ch_free( dli->dli_uri.bv_val );
1365                                 }
1366
1367                                 if ( dli->dli_lud != NULL ) {
1368                                         ldap_free_urldesc( dli->dli_lud );
1369                                 }
1370
1371                                 if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
1372                                         ber_memfree( dli->dli_uri_nbase.bv_val );
1373                                 }
1374
1375                                 if ( dli->dli_uri_filter != NULL ) {
1376                                         filter_free( dli->dli_uri_filter );
1377                                 }
1378
1379                                 ch_free( dli->dli_default_filter.bv_val );
1380
1381                                 dlm = dli->dli_dlm;
1382                                 while ( dlm != NULL ) {
1383                                         dlm_next = dlm->dlm_next;
1384                                         ch_free( dlm );
1385                                         dlm = dlm_next;
1386                                 }
1387                                 ch_free( dli );
1388
1389                                 dli = (dynlist_info_t *)on->on_bi.bi_private;
1390                         }
1391                         break;
1392
1393                 case DL_ATTRPAIR_COMPAT:
1394                 case DL_ATTRPAIR:
1395                         rc = 1;
1396                         break;
1397
1398                 default:
1399                         rc = 1;
1400                         break;
1401                 }
1402
1403                 return rc;
1404         }
1405
1406         switch( c->type ) {
1407         case DL_ATTRSET: {
1408                 dynlist_info_t          **dlip,
1409                                         *dli_next = NULL;
1410                 ObjectClass             *oc = NULL;
1411                 AttributeDescription    *ad = NULL;
1412                 int                     attridx = 2;
1413                 LDAPURLDesc             *lud = NULL;
1414                 struct berval           nbase = BER_BVNULL;
1415                 Filter                  *filter = NULL;
1416                 struct berval           uri = BER_BVNULL;
1417                 dynlist_map_t           *dlm = NULL, *dlml = NULL;
1418                 const char              *text;
1419
1420                 oc = oc_find( c->argv[ 1 ] );
1421                 if ( oc == NULL ) {
1422                         snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1423                                 "unable to find ObjectClass \"%s\"",
1424                                 c->argv[ 1 ] );
1425                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1426                                 c->log, c->cr_msg, 0 );
1427                         return 1;
1428                 }
1429
1430                 if ( strncasecmp( c->argv[ attridx ], "ldap://", STRLENOF("ldap://") ) == 0 ) {
1431                         if ( ldap_url_parse( c->argv[ attridx ], &lud ) != LDAP_URL_SUCCESS ) {
1432                                 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1433                                         "unable to parse URI \"%s\"",
1434                                         c->argv[ attridx ] );
1435                                 rc = 1;
1436                                 goto done_uri;
1437                         }
1438
1439                         if ( lud->lud_host != NULL ) {
1440                                 if ( lud->lud_host[0] == '\0' ) {
1441                                         ch_free( lud->lud_host );
1442                                         lud->lud_host = NULL;
1443
1444                                 } else {
1445                                         snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1446                                                 "host not allowed in URI \"%s\"",
1447                                                 c->argv[ attridx ] );
1448                                         rc = 1;
1449                                         goto done_uri;
1450                                 }
1451                         }
1452
1453                         if ( lud->lud_attrs != NULL ) {
1454                                 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1455                                         "attrs not allowed in URI \"%s\"",
1456                                         c->argv[ attridx ] );
1457                                 rc = 1;
1458                                 goto done_uri;
1459                         }
1460
1461                         if ( lud->lud_exts != NULL ) {
1462                                 snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1463                                         "extensions not allowed in URI \"%s\"",
1464                                         c->argv[ attridx ] );
1465                                 rc = 1;
1466                                 goto done_uri;
1467                         }
1468
1469                         if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' ) {
1470                                 struct berval dn;
1471                                 ber_str2bv( lud->lud_dn, 0, 0, &dn );
1472                                 rc = dnNormalize( 0, NULL, NULL, &dn, &nbase, NULL );
1473                                 if ( rc != LDAP_SUCCESS ) {
1474                                         snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1475                                                 "DN normalization failed in URI \"%s\"",
1476                                                 c->argv[ attridx ] );
1477                                         goto done_uri;
1478                                 }
1479                         }
1480
1481                         if ( lud->lud_filter != NULL && lud->lud_filter[ 0 ] != '\0' ) {
1482                                 filter = str2filter( lud->lud_filter );
1483                                 if ( filter == NULL ) {
1484                                         snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1485                                                 "filter parsing failed in URI \"%s\"",
1486                                                 c->argv[ attridx ] );
1487                                         rc = 1;
1488                                         goto done_uri;
1489                                 }
1490                         }
1491
1492                         ber_str2bv( c->argv[ attridx ], 0, 1, &uri );
1493
1494 done_uri:;
1495                         if ( rc ) {
1496                                 if ( lud ) {
1497                                         ldap_free_urldesc( lud );
1498                                 }
1499
1500                                 if ( !BER_BVISNULL( &nbase ) ) {
1501                                         ber_memfree( nbase.bv_val );
1502                                 }
1503
1504                                 if ( filter != NULL ) {
1505                                         filter_free( filter );
1506                                 }
1507
1508                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1509                                         c->log, c->cr_msg, 0 );
1510
1511                                 return rc;
1512                         }
1513
1514                         attridx++;
1515                 }
1516
1517                 rc = slap_str2ad( c->argv[ attridx ], &ad, &text );
1518                 if ( rc != LDAP_SUCCESS ) {
1519                         snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1520                                 "unable to find AttributeDescription \"%s\"",
1521                                 c->argv[ attridx ] );
1522                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1523                                 c->log, c->cr_msg, 0 );
1524                         return 1;
1525                 }
1526
1527                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1528                         snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
1529                                 "AttributeDescription \"%s\" "
1530                                 "must be a subtype of \"labeledURI\"",
1531                                 c->argv[ attridx ] );
1532                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1533                                 c->log, c->cr_msg, 0 );
1534                         return 1;
1535                 }
1536
1537                 attridx++;
1538
1539                 for ( i = attridx; i < c->argc; i++ ) {
1540                         char *arg; 
1541                         char *cp;
1542                         AttributeDescription *member_ad = NULL;
1543                         AttributeDescription *mapped_ad = NULL;
1544                         dynlist_map_t *dlmp;
1545
1546
1547                         /*
1548                          * If no mapped attribute is given, dn is used 
1549                          * for backward compatibility.
1550                          */
1551                         arg = c->argv[i];
1552                         if ( ( cp = strchr( arg, ':' ) ) != NULL ) {
1553                                 struct berval bv;
1554                                 ber_str2bv( arg, cp - arg, 0, &bv );
1555                                 rc = slap_bv2ad( &bv, &mapped_ad, &text );
1556                                 if ( rc != LDAP_SUCCESS ) {
1557                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1558                                                 DYNLIST_USAGE
1559                                                 "unable to find mapped AttributeDescription #%d \"%s\"\n",
1560                                                 i - 3, c->argv[ i ] );
1561                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1562                                                 c->log, c->cr_msg, 0 );
1563                                         return 1;
1564                                 }
1565                                 arg = cp + 1;
1566                         }
1567
1568                         rc = slap_str2ad( arg, &member_ad, &text );
1569                         if ( rc != LDAP_SUCCESS ) {
1570                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1571                                         DYNLIST_USAGE
1572                                         "unable to find AttributeDescription #%d \"%s\"\n",
1573                                         i - 3, c->argv[ i ] );
1574                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1575                                         c->log, c->cr_msg, 0 );
1576                                 return 1;
1577                         }
1578
1579                         dlmp = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
1580                         if ( dlm == NULL ) {
1581                                 dlm = dlmp;
1582                         }
1583                         dlmp->dlm_member_ad = member_ad;
1584                         dlmp->dlm_mapped_ad = mapped_ad;
1585                         dlmp->dlm_next = NULL;
1586                 
1587                         if ( dlml != NULL ) 
1588                                 dlml->dlm_next = dlmp;
1589                         dlml = dlmp;
1590                 }
1591
1592                 if ( c->valx > 0 ) {
1593                         int     i;
1594
1595                         for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1596                                 i < c->valx; i++ )
1597                         {
1598                                 if ( *dlip == NULL ) {
1599                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1600                                                 DYNLIST_USAGE
1601                                                 "invalid index {%d}\n",
1602                                                 c->valx );
1603                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1604                                                 c->log, c->cr_msg, 0 );
1605                                         return 1;
1606                                 }
1607                                 dlip = &(*dlip)->dli_next;
1608                         }
1609                         dli_next = *dlip;
1610
1611                 } else {
1612                         for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1613                                 *dlip; dlip = &(*dlip)->dli_next )
1614                                 /* goto last */;
1615                 }
1616
1617                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1618
1619                 (*dlip)->dli_oc = oc;
1620                 (*dlip)->dli_ad = ad;
1621                 (*dlip)->dli_dlm = dlm;
1622                 (*dlip)->dli_next = dli_next;
1623
1624                 (*dlip)->dli_lud = lud;
1625                 (*dlip)->dli_uri_nbase = nbase;
1626                 (*dlip)->dli_uri_filter = filter;
1627                 (*dlip)->dli_uri = uri;
1628
1629                 rc = dynlist_build_def_filter( *dlip );
1630
1631                 } break;
1632
1633         case DL_ATTRPAIR_COMPAT:
1634                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1635                         "warning: \"attrpair\" only supported for limited "
1636                         "backward compatibility with overlay \"dyngroup\"" );
1637                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1638                 /* fallthru */
1639
1640         case DL_ATTRPAIR: {
1641                 dynlist_info_t          **dlip;
1642                 ObjectClass             *oc = NULL;
1643                 AttributeDescription    *ad = NULL,
1644                                         *member_ad = NULL;
1645                 const char              *text;
1646
1647                 oc = oc_find( "groupOfURLs" );
1648                 if ( oc == NULL ) {
1649                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1650                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1651                                 "unable to find default ObjectClass \"groupOfURLs\"" );
1652                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1653                                 c->log, c->cr_msg, 0 );
1654                         return 1;
1655                 }
1656
1657                 rc = slap_str2ad( c->argv[ 1 ], &member_ad, &text );
1658                 if ( rc != LDAP_SUCCESS ) {
1659                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1660                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1661                                 "unable to find AttributeDescription \"%s\"",
1662                                 c->argv[ 1 ] );
1663                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1664                                 c->log, c->cr_msg, 0 );
1665                         return 1;
1666                 }
1667
1668                 rc = slap_str2ad( c->argv[ 2 ], &ad, &text );
1669                 if ( rc != LDAP_SUCCESS ) {
1670                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1671                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1672                                 "unable to find AttributeDescription \"%s\"\n",
1673                                 c->argv[ 2 ] );
1674                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1675                                 c->log, c->cr_msg, 0 );
1676                         return 1;
1677                 }
1678
1679                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1680                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1681                                 DYNLIST_USAGE
1682                                 "AttributeDescription \"%s\" "
1683                                 "must be a subtype of \"labeledURI\"",
1684                                 c->argv[ 2 ] );
1685                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1686                                 c->log, c->cr_msg, 0 );
1687                         return 1;
1688                 }
1689
1690                 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1691                         *dlip; dlip = &(*dlip)->dli_next )
1692                 {
1693                         /* 
1694                          * The same URL attribute / member attribute pair
1695                          * cannot be repeated, but we enforce this only 
1696                          * when the member attribute is unique. Performing
1697                          * the check for multiple values would require
1698                          * sorting and comparing the lists, which is left
1699                          * as a future improvement
1700                          */
1701                         if ( (*dlip)->dli_ad == ad &&
1702                              (*dlip)->dli_dlm->dlm_next == NULL &&
1703                              member_ad == (*dlip)->dli_dlm->dlm_member_ad ) {
1704                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1705                                         "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1706                                         "URL attributeDescription \"%s\" already mapped.\n",
1707                                         ad->ad_cname.bv_val );
1708                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1709                                         c->log, c->cr_msg, 0 );
1710 #if 0
1711                                 /* make it a warning... */
1712                                 return 1;
1713 #endif
1714                         }
1715                 }
1716
1717                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1718
1719                 (*dlip)->dli_oc = oc;
1720                 (*dlip)->dli_ad = ad;
1721                 (*dlip)->dli_dlm = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
1722                 (*dlip)->dli_dlm->dlm_member_ad = member_ad;
1723                 (*dlip)->dli_dlm->dlm_mapped_ad = NULL;
1724
1725                 rc = dynlist_build_def_filter( *dlip );
1726
1727                 } break;
1728
1729         default:
1730                 rc = 1;
1731                 break;
1732         }
1733
1734         return rc;
1735 }
1736 #endif
1737
1738 static int
1739 dynlist_db_open(
1740         BackendDB       *be,
1741         ConfigReply     *cr )
1742 {
1743         slap_overinst           *on = (slap_overinst *) be->bd_info;
1744         dynlist_info_t          *dli = (dynlist_info_t *)on->on_bi.bi_private;
1745         ObjectClass             *oc = NULL;
1746         AttributeDescription    *ad = NULL;
1747         const char      *text;
1748         int rc;
1749
1750         if ( dli == NULL ) {
1751                 dli = ch_calloc( 1, sizeof( dynlist_info_t ) );
1752                 on->on_bi.bi_private = (void *)dli;
1753         }
1754
1755         for ( ; dli; dli = dli->dli_next ) {
1756                 if ( dli->dli_oc == NULL ) {
1757                         if ( oc == NULL ) {
1758                                 oc = oc_find( "groupOfURLs" );
1759                                 if ( oc == NULL ) {
1760                                         snprintf( cr->msg, sizeof( cr->msg),
1761                                                 "unable to fetch objectClass \"groupOfURLs\"" );
1762                                         Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg, 0, 0 );
1763                                         return 1;
1764                                 }
1765                         }
1766
1767                         dli->dli_oc = oc;
1768                 }
1769
1770                 if ( dli->dli_ad == NULL ) {
1771                         if ( ad == NULL ) {
1772                                 rc = slap_str2ad( "memberURL", &ad, &text );
1773                                 if ( rc != LDAP_SUCCESS ) {
1774                                         snprintf( cr->msg, sizeof( cr->msg),
1775                                                 "unable to fetch attributeDescription \"memberURL\": %d (%s)",
1776                                                 rc, text );
1777                                         Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg, 0, 0 );
1778                                         return 1;
1779                                 }
1780                         }
1781                 
1782                         dli->dli_ad = ad;                       
1783                 }
1784
1785                 if ( BER_BVISNULL( &dli->dli_default_filter ) ) {
1786                         rc = dynlist_build_def_filter( dli );
1787                         if ( rc != 0 ) {
1788                                 return rc;
1789                         }
1790                 }
1791         }
1792
1793         if ( ad_dgIdentity == NULL ) {
1794                 rc = slap_str2ad( "dgIdentity", &ad_dgIdentity, &text );
1795                 if ( rc != LDAP_SUCCESS ) {
1796                         snprintf( cr->msg, sizeof( cr->msg),
1797                                 "unable to fetch attributeDescription \"dgIdentity\": %d (%s)",
1798                                 rc, text );
1799                         Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg, 0, 0 );
1800                         /* Just a warning */
1801                 }
1802         }
1803
1804         if ( ad_dgAuthz == NULL ) {
1805                 rc = slap_str2ad( "dgAuthz", &ad_dgAuthz, &text );
1806                 if ( rc != LDAP_SUCCESS ) {
1807                         snprintf( cr->msg, sizeof( cr->msg),
1808                                 "unable to fetch attributeDescription \"dgAuthz\": %d (%s)",
1809                                 rc, text );
1810                         Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg, 0, 0 );
1811                         /* Just a warning */
1812                 }
1813         }
1814
1815         return 0;
1816 }
1817
1818 static int
1819 dynlist_db_destroy(
1820         BackendDB       *be,
1821         ConfigReply     *cr )
1822 {
1823         slap_overinst   *on = (slap_overinst *) be->bd_info;
1824
1825         if ( on->on_bi.bi_private ) {
1826                 dynlist_info_t  *dli = (dynlist_info_t *)on->on_bi.bi_private,
1827                                 *dli_next;
1828
1829                 for ( dli_next = dli; dli_next; dli = dli_next ) {
1830                         dynlist_map_t *dlm;
1831                         dynlist_map_t *dlm_next;
1832
1833                         dli_next = dli->dli_next;
1834
1835                         if ( !BER_BVISNULL( &dli->dli_uri ) ) {
1836                                 ch_free( dli->dli_uri.bv_val );
1837                         }
1838
1839                         if ( dli->dli_lud != NULL ) {
1840                                 ldap_free_urldesc( dli->dli_lud );
1841                         }
1842
1843                         if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
1844                                 ber_memfree( dli->dli_uri_nbase.bv_val );
1845                         }
1846
1847                         if ( dli->dli_uri_filter != NULL ) {
1848                                 filter_free( dli->dli_uri_filter );
1849                         }
1850
1851                         ch_free( dli->dli_default_filter.bv_val );
1852
1853                         dlm = dli->dli_dlm;
1854                         while ( dlm != NULL ) {
1855                                 dlm_next = dlm->dlm_next;
1856                                 ch_free( dlm );
1857                                 dlm = dlm_next;
1858                         }
1859                         ch_free( dli );
1860                 }
1861         }
1862
1863         return 0;
1864 }
1865
1866 static slap_overinst    dynlist = { { NULL } };
1867 #ifdef TAKEOVER_DYNGROUP
1868 static char             *obsolete_names[] = {
1869         "dyngroup",
1870         NULL
1871 };
1872 #endif
1873
1874 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
1875 static
1876 #endif /* SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC */
1877 int
1878 dynlist_initialize(void)
1879 {
1880 #ifndef OL_2_2_COMPAT
1881         int     rc = 0;
1882 #endif
1883
1884         dynlist.on_bi.bi_type = "dynlist";
1885
1886 #ifdef TAKEOVER_DYNGROUP
1887         /* makes dynlist incompatible with dyngroup */
1888         dynlist.on_bi.bi_obsolete_names = obsolete_names;
1889 #endif
1890
1891 #ifdef OL_2_2_COMPAT
1892         dynlist.on_bi.bi_db_config = dynlist_db_config;
1893 #else
1894         dynlist.on_bi.bi_db_config = config_generic_wrapper;
1895 #endif
1896         dynlist.on_bi.bi_db_open = dynlist_db_open;
1897         dynlist.on_bi.bi_db_destroy = dynlist_db_destroy;
1898
1899         dynlist.on_response = dynlist_response;
1900
1901 #ifndef OL_2_2_COMPAT
1902         dynlist.on_bi.bi_cf_ocs = dlocs;
1903
1904         rc = config_register_schema( dlcfg, dlocs );
1905         if ( rc ) {
1906                 return rc;
1907         }
1908 #endif
1909
1910         return overlay_register( &dynlist );
1911 }
1912
1913 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
1914 int
1915 init_module( int argc, char *argv[] )
1916 {
1917         return dynlist_initialize();
1918 }
1919 #endif
1920
1921 #endif /* SLAPD_OVER_DYNLIST */