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