dynlist.c 46.4 KB
Newer Older
1
/* dynlist.c - dynamic list overlay */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
2
/* $OpenLDAP$ */
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
5
 * Copyright 2003-2009 The OpenLDAP Foundation.
6
 * Portions Copyright 2004-2005 Pierangelo Masarati.
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
7
 * Portions Copyright 2008 Emmanuel Dreyfus.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Pierangelo Masarati
 * for SysNet s.n.c., for inclusion in OpenLDAP Software.
 */

#include "portable.h"

#ifdef SLAPD_OVER_DYNLIST

27
#if LDAP_VENDOR_VERSION_MINOR == X || LDAP_VENDOR_VERSION_MINOR > 3
28
#if SLAPD_OVER_DYNGROUP != SLAPD_MOD_STATIC
29
#define TAKEOVER_DYNGROUP
30
31
32
#endif
#else
#if LDAP_VENDOR_VERSION_MINOR < 3
33
#define OL_2_2_COMPAT
34
#endif
35
#endif
36

37
38
39
40
41
#include <stdio.h>

#include <ac/string.h>

#include "slap.h"
42
43
44
#ifndef OL_2_2_COMPAT
#include "config.h"
#endif
45
46
47
48
49
#include "lutil.h"

/* FIXME: the code differs if SLAP_OPATTRS is defined or not;
 * SLAP_OPATTRS is not defined in 2.2 yet, while this overlay
 * expects HEAD code at least later than August 6, 2004. */
50
51
52
/* FIXME: slap_anlist_no_attrs was introduced in 2.3; here it
 * is anticipated to allow using this overlay with 2.2. */

53
#ifdef OL_2_2_COMPAT
54
55
56
57
58
59
60
static AttributeName anlist_no_attrs[] = {
	{ BER_BVC( LDAP_NO_ATTRS ), NULL, 0, NULL },
	{ BER_BVNULL, NULL, 0, NULL }
};

static AttributeName *slap_anlist_no_attrs = anlist_no_attrs;
#endif
61

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
62
static AttributeDescription *ad_dgIdentity, *ad_dgAuthz;
63

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
64
typedef struct dynlist_map_t {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
65
66
67
	AttributeDescription	*dlm_member_ad;
	AttributeDescription	*dlm_mapped_ad;
	struct dynlist_map_t	*dlm_next;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
68
69
} dynlist_map_t;

70
typedef struct dynlist_info_t {
71
72
	ObjectClass		*dli_oc;
	AttributeDescription	*dli_ad;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
73
	struct dynlist_map_t	*dli_dlm;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
74
75
76
77
	struct berval		dli_uri;
	LDAPURLDesc		*dli_lud;
	struct berval		dli_uri_nbase;
	Filter			*dli_uri_filter;
78
	struct berval		dli_default_filter;
79
80
	struct dynlist_info_t	*dli_next;
} dynlist_info_t;
81

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
82
#define DYNLIST_USAGE \
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
83
	"\"dynlist-attrset <oc> [uri] <URL-ad> [[<mapped-ad>:]<member-ad> ...]\": "
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
84

85
static dynlist_info_t *
86
dynlist_is_dynlist_next( Operation *op, SlapReply *rs, dynlist_info_t *old_dli )
87
88
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
89
	dynlist_info_t	*dli;
90
91
92

	Attribute	*a;

93
94
95
96
97
98
99
	if ( old_dli == NULL ) {
		dli = (dynlist_info_t *)on->on_bi.bi_private;

	} else {
		dli = old_dli->dli_next;
	}

100
101
102
103
104
	a = attrs_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
	if ( a == NULL ) {
		/* FIXME: objectClass must be present; for non-storage
		 * backends, like back-ldap, it needs to be added
		 * to the requested attributes */
105
		return NULL;
106
107
	}

108
	for ( ; dli; dli = dli->dli_next ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
		if ( dli->dli_lud != NULL ) {
			/* check base and scope */
			if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
				int d = rs->sr_entry->e_nname.bv_len - dli->dli_uri_nbase.bv_len;

				if ( d < 0 ) {
					continue;
				}

				if ( !dnIsSuffix( &rs->sr_entry->e_nname, &dli->dli_uri_nbase ) ) {
					continue;
				}

				switch ( dli->dli_lud->lud_scope ) {
				case LDAP_SCOPE_BASE:
					if ( d != 0 ) {
						continue;
					}
					break;

				case LDAP_SCOPE_ONELEVEL: {
					struct berval pdn;

					dnParent( &rs->sr_entry->e_nname, &pdn );
					if ( pdn.bv_len != dli->dli_uri_nbase.bv_len ) {
						continue;
					}
					} break;

				case LDAP_SCOPE_SUBORDINATE:
					if ( d == 0 ) {
						continue;
					}
					break;

				case LDAP_SCOPE_SUBTREE:
				case LDAP_SCOPE_DEFAULT:
					break;

				default:
					continue;
				}
			}

			/* check filter */
			if ( dli->dli_uri_filter && test_filter( op, rs->sr_entry, dli->dli_uri_filter ) != LDAP_COMPARE_TRUE ) {
				continue;
			}
		}

159
		if ( attr_valfind( a,
160
161
				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
162
				&dli->dli_oc->soc_cname, NULL,
163
164
165
166
				op->o_tmpmemctx ) == 0 )
		{
			return dli;
		}
167
168
	}

169
	return NULL;
170
171
172
173
174
175
}

static int
dynlist_make_filter( Operation *op, struct berval *oldf, struct berval *newf )
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
176
	dynlist_info_t	*dli = (dynlist_info_t *)on->on_bi.bi_private;
177
178
179

	char		*ptr;

180
181
	assert( oldf != NULL );
	assert( newf != NULL );
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
	assert( !BER_BVISNULL( oldf ) );
	assert( !BER_BVISEMPTY( oldf ) );

	newf->bv_len = STRLENOF( "(&(!(objectClass=" "))" ")" )
		+ dli->dli_oc->soc_cname.bv_len + oldf->bv_len;
	newf->bv_val = op->o_tmpalloc( newf->bv_len + 1, op->o_tmpmemctx );
	if ( newf->bv_val == NULL ) {
		return -1;
	}
	ptr = lutil_strcopy( newf->bv_val, "(&(!(objectClass=" );
	ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
	ptr = lutil_strcopy( ptr, "))" );
	ptr = lutil_strcopy( ptr, oldf->bv_val );
	ptr = lutil_strcopy( ptr, ")" );
	newf->bv_len = ptr - newf->bv_val;

	return 0;
}

typedef struct dynlist_sc_t {
202
	dynlist_info_t    *dlc_dli;
203
204
205
206
207
208
209
210
211
212
213
214
215
	Entry		*dlc_e;
} dynlist_sc_t;

static int
dynlist_sc_update( Operation *op, SlapReply *rs )
{
	Entry			*e;
	Attribute		*a;
	int			opattrs,
				userattrs;
	AccessControlState	acl_state = ACL_STATE_INIT;

	dynlist_sc_t		*dlc;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
216
	dynlist_map_t		*dlm;
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234

	if ( rs->sr_type != REP_SEARCH ) {
		return 0;
	}

	dlc = (dynlist_sc_t *)op->o_callback->sc_private;
	e = dlc->dlc_e;

	assert( e != NULL );
	assert( rs->sr_entry != NULL );

	/* test access to entry */
	if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
				NULL, ACL_READ, NULL ) )
	{
		goto done;
	}

235
236
237
238
	/* if there is only one member_ad, and it's not mapped,
	 * consider it as old-style member listing */
	dlm = dlc->dlc_dli->dli_dlm;
	if ( dlm && dlm->dlm_mapped_ad == NULL && dlm->dlm_next == NULL ) {
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
		/* if access allowed, try to add values, emulating permissive
		 * control to silently ignore duplicates */
		if ( access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
					NULL, ACL_READ, NULL ) )
		{
			Modification	mod;
			const char	*text = NULL;
			char		textbuf[1024];
			struct berval	vals[ 2 ], nvals[ 2 ];

			vals[ 0 ] = rs->sr_entry->e_name;
			BER_BVZERO( &vals[ 1 ] );
			nvals[ 0 ] = rs->sr_entry->e_nname;
			BER_BVZERO( &nvals[ 1 ] );

			mod.sm_op = LDAP_MOD_ADD;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
255
256
			mod.sm_desc = dlm->dlm_member_ad;
			mod.sm_type = dlm->dlm_member_ad->ad_cname;
257
258
			mod.sm_values = vals;
			mod.sm_nvalues = nvals;
259
			mod.sm_numvals = 1;
260
261
262
263
264
265
266
267
268

			(void)modify_add_values( e, &mod, /* permissive */ 1,
					&text, textbuf, sizeof( textbuf ) );
		}

		goto done;
	}

#ifndef SLAP_OPATTRS
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
269
270
	opattrs = ( rs->sr_attrs == NULL ) ? 0 : an_find( rs->sr_attrs, slap_bv_operational_attrs );
	userattrs = ( rs->sr_attrs == NULL ) ? 1 : an_find( rs->sr_attrs, slap_bv_user_attrs );
271
272
273
274
275
276
277
#else /* SLAP_OPATTRS */
	opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
	userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
#endif /* SLAP_OPATTRS */

	for ( a = rs->sr_entry->e_attrs; a != NULL; a = a->a_next ) {
		BerVarray	vals, nvals = NULL;
278
279
		int		i, j,
				is_oc = a->a_desc == slap_schema.si_ad_objectClass;
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318

		/* if attribute is not requested, skip it */
		if ( rs->sr_attrs == NULL ) {
			if ( is_at_operational( a->a_desc->ad_type ) ) {
				continue;
			}

		} else {
			if ( is_at_operational( a->a_desc->ad_type ) ) {
				if ( !opattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
				{
					continue;
				}

			} else {
				if ( !userattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
				{
					continue;
				}
			}
		}

		/* test access to attribute */
		if ( op->ors_attrsonly ) {
			if ( !access_allowed( op, rs->sr_entry, a->a_desc, NULL,
						ACL_READ, &acl_state ) )
			{
				continue;
			}
		}

		/* single-value check: keep first only */
		if ( is_at_single_value( a->a_desc->ad_type ) ) {
			if ( attr_find( e->e_attrs, a->a_desc ) != NULL ) {
				continue;
			}
		}

		/* test access to attribute */
319
		i = a->a_numvals;
320
321
322
323
324
325
326

		vals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
		if ( a->a_nvals != a->a_vals ) {
			nvals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
		}

		for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
327
328
329
330
331
332
333
334
			if ( is_oc ) {
				ObjectClass	*soc = oc_bvfind( &a->a_vals[i] );

				if ( soc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
					continue;
				}
			}

335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
			if ( access_allowed( op, rs->sr_entry, a->a_desc,
						&a->a_nvals[i], ACL_READ, &acl_state ) )
			{
				vals[j] = a->a_vals[i];
				if ( nvals ) {
					nvals[j] = a->a_nvals[i];
				}
				j++;
			}
		}

		/* if access allowed, try to add values, emulating permissive
		 * control to silently ignore duplicates */
		if ( j != 0 ) {
			Modification	mod;
			const char	*text = NULL;
			char		textbuf[1024];
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
352
353
			dynlist_map_t	*dlm;
			AttributeDescription *ad;
354
355
356
357
358
359

			BER_BVZERO( &vals[j] );
			if ( nvals ) {
				BER_BVZERO( &nvals[j] );
			}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
360
361
362
			ad = a->a_desc;
			for ( dlm = dlc->dlc_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
				if ( dlm->dlm_member_ad == a->a_desc ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
363
364
365
					if ( dlm->dlm_mapped_ad ) {
						ad = dlm->dlm_mapped_ad;
					}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
366
367
368
369
					break;
				}
			}

370
			mod.sm_op = LDAP_MOD_ADD;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
371
372
			mod.sm_desc = ad;
			mod.sm_type = ad->ad_cname;
373
374
			mod.sm_values = vals;
			mod.sm_nvalues = nvals;
375
			mod.sm_numvals = j;
376
377
378
379
380
381
382
383
384
385
386
387
388
389

			(void)modify_add_values( e, &mod, /* permissive */ 1,
					&text, textbuf, sizeof( textbuf ) );
		}

		op->o_tmpfree( vals, op->o_tmpmemctx );
		if ( nvals ) {
			op->o_tmpfree( nvals, op->o_tmpmemctx );
		}
	}

done:;
	if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
		entry_free( rs->sr_entry );
390
391
		rs->sr_entry = NULL;
		rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
392
393
394
395
396
397
	}

	return 0;
}
	
static int
398
dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli )
399
{
400
	Attribute	*a, *id = NULL;
401
402
	slap_callback	cb;
	Operation	o = *op;
403
	SlapReply	r = { REP_SEARCH };
404
405
	struct berval	*url;
	Entry		*e;
406
	slap_mask_t	e_flags;
407
408
409
	int		opattrs,
			userattrs;
	dynlist_sc_t	dlc = { 0 };
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
410
	dynlist_map_t	*dlm;
411
412
413
414
415
416
417

	a = attrs_find( rs->sr_entry->e_attrs, dli->dli_ad );
	if ( a == NULL ) {
		/* FIXME: error? */
		return SLAP_CB_CONTINUE;
	}

Howard Chu's avatar
Howard Chu committed
418
#ifndef SLAP_OPATTRS
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
419
420
	opattrs = ( rs->sr_attrs == NULL ) ? 0 : an_find( rs->sr_attrs, slap_bv_operational_attrs );
	userattrs = ( rs->sr_attrs == NULL ) ? 1 : an_find( rs->sr_attrs, slap_bv_user_attrs );
Howard Chu's avatar
Howard Chu committed
421
422
423
424
425
426
#else /* SLAP_OPATTRS */
	opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
	userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
#endif /* SLAP_OPATTRS */

	/* Don't generate member list if it wasn't requested */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
427
	for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
428
429
		AttributeDescription *ad = dlm->dlm_mapped_ad ? dlm->dlm_mapped_ad : dlm->dlm_member_ad;
		if ( userattrs || ad_inlist( ad, rs->sr_attrs ) ) 
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
430
			break;
Howard Chu's avatar
Howard Chu committed
431
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
432
433
	if ( dli->dli_dlm && !dlm )
		return SLAP_CB_CONTINUE;
Howard Chu's avatar
Howard Chu committed
434

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
	if ( ad_dgIdentity && ( id = attrs_find( rs->sr_entry->e_attrs, ad_dgIdentity ))) {
		Attribute *authz = NULL;

		/* if not rootdn and dgAuthz is present,
		 * check if user can be authorized as dgIdentity */
		if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op )
			&& ( authz = attrs_find( rs->sr_entry->e_attrs, ad_dgAuthz ) ) )
		{
			if ( slap_sasl_matches( op, authz->a_nvals,
				&o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS )
			{
				return SLAP_CB_CONTINUE;
			}
		}

		o.o_dn = id->a_vals[0];
		o.o_ndn = id->a_nvals[0];
		o.o_groups = NULL;
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
455
	e_flags = rs->sr_flags;
456
457
	if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
		e = entry_dup( rs->sr_entry );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
458
		e_flags |= ( REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED );
459
460
461
	} else {
		e = rs->sr_entry;
	}
462
463
464
465
466
467
468
469
470

	dlc.dlc_e = e;
	dlc.dlc_dli = dli;
	cb.sc_private = &dlc;
	cb.sc_response = dynlist_sc_update;
	cb.sc_cleanup = NULL;
	cb.sc_next = NULL;

	o.o_callback = &cb;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
471
	o.ors_deref = LDAP_DEREF_NEVER;
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
	o.ors_limit = NULL;
	o.ors_tlimit = SLAP_NO_LIMIT;
	o.ors_slimit = SLAP_NO_LIMIT;

	for ( url = a->a_nvals; !BER_BVISNULL( url ); url++ ) {
		LDAPURLDesc	*lud = NULL;
		int		i, j;
		struct berval	dn;
		int		rc;

		BER_BVZERO( &o.o_req_dn );
		BER_BVZERO( &o.o_req_ndn );
		o.ors_filter = NULL;
		o.ors_attrs = NULL;
		BER_BVZERO( &o.ors_filterstr );

		if ( ldap_url_parse( url->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
			/* FIXME: error? */
			continue;
		}

493
		if ( lud->lud_host != NULL ) {
494
			/* FIXME: host not allowed; reject as illegal? */
495
			Debug( LDAP_DEBUG_ANY, "dynlist_prepare_entry(\"%s\"): "
496
497
498
499
500
501
502
503
504
505
506
507
				"illegal URI \"%s\"\n",
				e->e_name.bv_val, url->bv_val, 0 );
			goto cleanup;
		}

		if ( lud->lud_dn == NULL ) {
			/* note that an empty base is not honored in terms
			 * of defaultSearchBase, because select_backend()
			 * is not aware of the defaultSearchBase option;
			 * this can be useful in case of a database serving
			 * the empty suffix */
			BER_BVSTR( &dn, "" );
508

509
510
511
512
513
514
515
516
517
518
		} else {
			ber_str2bv( lud->lud_dn, 0, 0, &dn );
		}
		rc = dnPrettyNormal( NULL, &dn, &o.o_req_dn, &o.o_req_ndn, op->o_tmpmemctx );
		if ( rc != LDAP_SUCCESS ) {
			/* FIXME: error? */
			goto cleanup;
		}
		o.ors_scope = lud->lud_scope;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
519
520
521
522
523
524
525
		for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
			if ( dlm->dlm_mapped_ad != NULL ) {
				break;
			}
		}

		if ( dli->dli_dlm && !dlm ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
526
527
			/* if ( lud->lud_attrs != NULL ),
			 * the URL should be ignored */
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
			o.ors_attrs = slap_anlist_no_attrs;

		} else if ( lud->lud_attrs == NULL ) {
			o.ors_attrs = rs->sr_attrs;

		} else {
			for ( i = 0; lud->lud_attrs[i]; i++)
				/* just count */ ;

			o.ors_attrs = op->o_tmpcalloc( i + 1, sizeof( AttributeName ), op->o_tmpmemctx );
			for ( i = 0, j = 0; lud->lud_attrs[i]; i++) {
				const char	*text = NULL;
	
				ber_str2bv( lud->lud_attrs[i], 0, 0, &o.ors_attrs[j].an_name );
				o.ors_attrs[j].an_desc = NULL;
				(void)slap_bv2ad( &o.ors_attrs[j].an_name, &o.ors_attrs[j].an_desc, &text );
				/* FIXME: ignore errors... */

				if ( rs->sr_attrs == NULL ) {
					if ( o.ors_attrs[j].an_desc != NULL &&
							is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
					{
						continue;
					}

				} else {
					if ( o.ors_attrs[j].an_desc != NULL &&
							is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
					{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
557
						if ( !opattrs ) {
558
559
560
							continue;
						}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
						if ( !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) ) {
							/* lookup if mapped -- linear search,
							 * not very efficient unless list
							 * is very short */
							for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
								if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
									break;
								}
							}

							if ( dlm == NULL ) {
								continue;
							}
						}

576
577
578
579
580
					} else {
						if ( !userattrs && 
								o.ors_attrs[j].an_desc != NULL &&
								!ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
						{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
581
582
583
584
585
586
587
588
589
590
591
592
							/* lookup if mapped -- linear search,
							 * not very efficient unless list
							 * is very short */
							for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
								if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
									break;
								}
							}

							if ( dlm == NULL ) {
								continue;
							}
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
						}
					}
				}

				j++;
			}

			if ( j == 0 ) {
				goto cleanup;
			}
		
			BER_BVZERO( &o.ors_attrs[j].an_name );
		}

		if ( lud->lud_filter == NULL ) {
			ber_dupbv_x( &o.ors_filterstr,
					&dli->dli_default_filter, op->o_tmpmemctx );
610

611
612
613
614
615
616
617
618
619
620
621
622
623
		} else {
			struct berval	flt;
			ber_str2bv( lud->lud_filter, 0, 0, &flt );
			if ( dynlist_make_filter( op, &flt, &o.ors_filterstr ) ) {
				/* error */
				goto cleanup;
			}
		}
		o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
		if ( o.ors_filter == NULL ) {
			goto cleanup;
		}
		
624
		o.o_bd = select_backend( &o.o_req_ndn, 1 );
625
626
627
628
629
630
631
632
		if ( o.o_bd && o.o_bd->be_search ) {
#ifdef SLAP_OPATTRS
			r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
#endif /* SLAP_OPATTRS */
			(void)o.o_bd->be_search( &o, &r );
		}

cleanup:;
633
634
635
		if ( id ) {
			slap_op_groups_free( &o );
		}
636
		if ( o.ors_filter ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
637
			filter_free_x( &o, o.ors_filter, 1 );
638
639
640
641
642
643
644
645
646
647
648
649
		}
		if ( o.ors_attrs && o.ors_attrs != rs->sr_attrs
				&& o.ors_attrs != slap_anlist_no_attrs )
		{
			op->o_tmpfree( o.ors_attrs, op->o_tmpmemctx );
		}
		if ( !BER_BVISNULL( &o.o_req_dn ) ) {
			op->o_tmpfree( o.o_req_dn.bv_val, op->o_tmpmemctx );
		}
		if ( !BER_BVISNULL( &o.o_req_ndn ) ) {
			op->o_tmpfree( o.o_req_ndn.bv_val, op->o_tmpmemctx );
		}
650
651
		assert( BER_BVISNULL( &o.ors_filterstr )
			|| o.ors_filterstr.bv_val != lud->lud_filter );
652
		op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
653
		ldap_free_urldesc( lud );
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
	}

	rs->sr_entry = e;
	rs->sr_flags = e_flags;

	return SLAP_CB_CONTINUE;
}

static int
dynlist_sc_save_entry( Operation *op, SlapReply *rs )
{
	/* save the entry in the private field of the callback,
	 * so it doesn't get freed (it's temporary!) */
	if ( rs->sr_entry != NULL ) {
		dynlist_sc_t	*dlc = (dynlist_sc_t *)op->o_callback->sc_private;
		dlc->dlc_e = rs->sr_entry;
		rs->sr_entry = NULL;
	}

	return 0;
}

static int
dynlist_compare( Operation *op, SlapReply *rs )
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
680
	dynlist_info_t	*dli = (dynlist_info_t *)on->on_bi.bi_private;
681
	Operation o = *op;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
682
	Entry *e = NULL;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
683
	dynlist_map_t *dlm;
684
685

	for ( ; dli != NULL; dli = dli->dli_next ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
686
687
688
689
690
		for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next )
			if ( op->oq_compare.rs_ava->aa_desc == dlm->dlm_member_ad )
				break;

		if ( dli->dli_dlm && dlm ) {
691
692
693
694
			/* This compare is for one of the attributes we're
			 * interested in. We'll use slapd's existing dyngroup
			 * evaluator to get the answer we want.
			 */
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
695
			BerVarray id = NULL, authz = NULL;
696
697
698
699

			o.o_do_not_cache = 1;

			if ( ad_dgIdentity && backend_attribute( &o, NULL, &o.o_req_ndn,
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
				ad_dgIdentity, &id, ACL_READ ) == LDAP_SUCCESS )
			{
				/* if not rootdn and dgAuthz is present,
				 * check if user can be authorized as dgIdentity */
				if ( ad_dgAuthz && !BER_BVISEMPTY( id ) && !be_isroot( op )
					&& backend_attribute( &o, NULL, &o.o_req_ndn,
						ad_dgAuthz, &authz, ACL_READ ) == LDAP_SUCCESS )
				{
					
					rs->sr_err = slap_sasl_matches( op, authz,
						&o.o_ndn, &o.o_ndn );
					ber_bvarray_free_x( authz, op->o_tmpmemctx );
					if ( rs->sr_err != LDAP_SUCCESS ) {
						goto done;
					}
				}

717
718
719
720
				o.o_dn = *id;
				o.o_ndn = *id;
				o.o_groups = NULL; /* authz changed, invalidate cached groups */
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
721

722
723
			rs->sr_err = backend_group( &o, NULL, &o.o_req_ndn,
				&o.oq_compare.rs_ava->aa_value, dli->dli_oc, dli->dli_ad );
724
725
726
727
728
729
730
731
732
733
734
735
736
737
			switch ( rs->sr_err ) {
			case LDAP_SUCCESS:
				rs->sr_err = LDAP_COMPARE_TRUE;
				break;

			case LDAP_NO_SUCH_OBJECT:
				/* NOTE: backend_group() returns noSuchObject
				 * if op_ndn does not exist; however, since
				 * dynamic list expansion means that the
				 * member attribute is virtually present, the
				 * non-existence of the asserted value implies
				 * the assertion is FALSE rather than
				 * UNDEFINED */
				rs->sr_err = LDAP_COMPARE_FALSE;
738
				break;
739
			}
740

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
741
done:;
742
743
			if ( id ) ber_bvarray_free_x( id, o.o_tmpmemctx );

744
745
746
747
			return SLAP_CB_CONTINUE;
		}
	}

748
	if ( overlay_entry_get_ov( &o, &o.o_req_ndn, NULL, NULL, 0, &e, on ) !=
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
749
750
		LDAP_SUCCESS || e == NULL )
	{
751
752
		return SLAP_CB_CONTINUE;
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
753

754
755
756
	if ( ad_dgIdentity ) {
		Attribute *id = attrs_find( e->e_attrs, ad_dgIdentity );
		if ( id ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
757
758
759
760
761
762
763
764
765
766
767
768
769
770
			Attribute *authz;

			/* if not rootdn and dgAuthz is present,
			 * check if user can be authorized as dgIdentity */
			if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op )
				&& ( authz = attrs_find( e->e_attrs, ad_dgAuthz ) ) )
			{
				if ( slap_sasl_matches( op, authz->a_nvals,
					&o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS )
				{
					goto release;
				}
			}

771
772
773
774
775
			o.o_dn = id->a_vals[0];
			o.o_ndn = id->a_nvals[0];
			o.o_groups = NULL;
		}
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
776

777
778
779
780
781
782
783
784
	dli = (dynlist_info_t *)on->on_bi.bi_private;
	for ( ; dli != NULL && rs->sr_err != LDAP_COMPARE_TRUE; dli = dli->dli_next ) {
		Attribute	*a;
		slap_callback	cb;
		SlapReply	r = { REP_SEARCH };
		AttributeName	an[2];
		int		rc;
		dynlist_sc_t	dlc = { 0 };
785
786

		if ( !is_entry_objectclass_or_sub( e, dli->dli_oc ))
787
			continue;
788

789
790
791
792
793
794
795
796
		/* if the entry has the right objectClass, generate
		 * the dynamic list and compare */
		dlc.dlc_dli = dli;
		cb.sc_private = &dlc;
		cb.sc_response = dynlist_sc_save_entry;
		cb.sc_cleanup = NULL;
		cb.sc_next = NULL;
		o.o_callback = &cb;
797

798
799
800
801
		o.o_tag = LDAP_REQ_SEARCH;
		o.ors_limit = NULL;
		o.ors_tlimit = SLAP_NO_LIMIT;
		o.ors_slimit = SLAP_NO_LIMIT;
802

803
		o.o_bd = select_backend( &o.o_req_ndn, 1 );
804
		if ( !o.o_bd || !o.o_bd->be_search ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
805
			goto release;
806
		}
807

Ralf Haferkamp's avatar
Ralf Haferkamp committed
808
		o.ors_filterstr = *slap_filterstr_objectClass_pres;
809
		o.ors_filter = (Filter *) slap_filter_objectClass_pres;
810

811
812
813
814
815
816
817
		o.ors_scope = LDAP_SCOPE_BASE;
		o.ors_deref = LDAP_DEREF_NEVER;
		an[0].an_name = op->orc_ava->aa_desc->ad_cname;
		an[0].an_desc = op->orc_ava->aa_desc;
		BER_BVZERO( &an[1].an_name );
		o.ors_attrs = an;
		o.ors_attrsonly = 0;
818

819
820
		o.o_acl_priv = ACL_COMPARE;

821
		rc = o.o_bd->be_search( &o, &r );
822

823
824
825
826
		if ( o.o_dn.bv_val != op->o_dn.bv_val ) {
			slap_op_groups_free( &o );
		}

827
		if ( rc != 0 ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
828
			goto release;
829
		}
830

831
832
833
		if ( dlc.dlc_e != NULL ) {
			r.sr_entry = dlc.dlc_e;
		}
834

835
836
		if ( r.sr_err != LDAP_SUCCESS || r.sr_entry == NULL ) {
			/* error? */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
837
			goto release;
838
		}
839

840
841
842
		for ( a = attrs_find( r.sr_entry->e_attrs, op->orc_ava->aa_desc );
			a != NULL;
			a = attrs_find( a->a_next, op->orc_ava->aa_desc ) )
843
		{
844
845
846
			/* if we're here, we got a match... */
			rs->sr_err = LDAP_COMPARE_FALSE;

847
			if ( attr_valfind( a,
848
849
				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
					SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
850
				&op->orc_ava->aa_value, NULL, op->o_tmpmemctx ) == 0 )
851
852
853
854
			{
				rs->sr_err = LDAP_COMPARE_TRUE;
				break;
			}
855
856
		}

857
858
		if ( r.sr_flags & REP_ENTRY_MUSTBEFREED ) {
			entry_free( r.sr_entry );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
859
860
			r.sr_entry = NULL;
			r.sr_flags ^= REP_ENTRY_MUSTBEFREED;
861
		}
862
863
	}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
864
865
release:;
	if ( e != NULL ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
866
		overlay_entry_release_ov( op, e, 0, on );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
867
868
	}

869
870
871
872
873
874
	return SLAP_CB_CONTINUE;
}

static int
dynlist_response( Operation *op, SlapReply *rs )
{
875
876
	dynlist_info_t	*dli;

877
878
879
880
	switch ( op->o_tag ) {
	case LDAP_REQ_SEARCH:
		if ( rs->sr_type == REP_SEARCH && !get_manageDSAit( op ) )
		{
881
882
883
884
885
886
887
888
889
890
891
			int	rc = LDAP_OTHER;

			for ( dli = dynlist_is_dynlist_next( op, rs, NULL );
				dli;
				dli = dynlist_is_dynlist_next( op, rs, dli ) )
			{
				rc = dynlist_prepare_entry( op, rs, dli );
			}

			if ( rc != LDAP_OTHER ) {
				return rc;
892
893
894
895
896
			}
		}
		break;

	case LDAP_REQ_COMPARE:
897
898
899
900
		switch ( rs->sr_err ) {
		/* NOTE: we waste a few cycles running the dynamic list
		 * also when the result is FALSE, which occurs if the
		 * dynamic entry itself contains the AVA attribute  */
901
902
903
904
905
		/* FIXME: this approach is less than optimal; a dedicated
		 * compare op should be implemented, that fetches the
		 * entry, checks if it has the appropriate objectClass
		 * and, in case, runs a compare thru all the URIs,
		 * stopping at the first positive occurrence; see ITS#3756 */
906
907
		case LDAP_COMPARE_FALSE:
		case LDAP_NO_SUCH_ATTRIBUTE:
908
909
910
911
912
913
914
915
916
917
918
			return dynlist_compare( op, rs );
		}
		break;

	default:
		break;
	}

	return SLAP_CB_CONTINUE;
}

919
920
921
922
923
924
925
static int
dynlist_build_def_filter( dynlist_info_t *dli )
{
	char	*ptr;

	dli->dli_default_filter.bv_len = STRLENOF( "(!(objectClass=" "))" )
		+ dli->dli_oc->soc_cname.bv_len;
926
	dli->dli_default_filter.bv_val = ch_malloc( dli->dli_default_filter.bv_len + 1 );
927
928
929
930
931
932
933
934
935
936
	if ( dli->dli_default_filter.bv_val == NULL ) {
		Debug( LDAP_DEBUG_ANY, "dynlist_db_open: malloc failed.\n",
			0, 0, 0 );
		return -1;
	}

	ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" );
	ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
	ptr = lutil_strcopy( ptr, "))" );

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
937
	assert( ptr == &dli->dli_default_filter.bv_val[dli->dli_default_filter.bv_len] );
938
939
940
941
942

	return 0;
}

#ifdef OL_2_2_COMPAT
943
944
static int
dynlist_db_config(
945
946
947
948
949
	BackendDB	*be,
	const char	*fname,
	int		lineno,
	int		argc,
	char		**argv )
950
951
952
953
954
{
	slap_overinst	*on = (slap_overinst *)be->bd_info;

	int		rc = 0;

955
956
957
958
959
	if ( strcasecmp( argv[0], "dynlist-attrset" ) == 0 ) {
		dynlist_info_t		**dlip;
		ObjectClass		*oc;
		AttributeDescription	*ad = NULL,
					*member_ad = NULL;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
960
		dynlist_map_t		*dlm = NULL;
961
962
		const char		*text;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
963
964
		if ( argc < 3 ) {
			Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
965
966
				"invalid arg number #%d.\n",
				fname, lineno, argc );
967
968
			return 1;
		}
969
970
971

		oc = oc_find( argv[1] );
		if ( oc == NULL ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
972
			Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
973
974
				"unable to find ObjectClass \"%s\"\n",
				fname, lineno, argv[ 1 ] );
975
976
977
			return 1;
		}

978
979
		rc = slap_str2ad( argv[2], &ad, &text );
		if ( rc != LDAP_SUCCESS ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
980
			Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
981
982
983
984
				"unable to find AttributeDescription \"%s\"\n",
				fname, lineno, argv[2] );
			return 1;
		}
985

986
		if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
987
			Debug( LDAP_DEBUG_ANY, "%s: line %d: " DYNLIST_USAGE
988
989
990
				"AttributeDescription \"%s\" "
				"must be a subtype of \"labeledURI\"\n",
				fname, lineno, argv[2] );
991
992
			return 1;
		}
993

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
994
995
996
997
998
999
1000
		for ( i = 3; i < argc; i++ ) {
			char *arg; 
			char *cp;
			AttributeDescription *member_ad = NULL;
			AttributeDescription *mapped_ad = NULL;
			dynlist_map_t *dlmp;
			dynlist_map_t *dlml;