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