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