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