dynlist.c 38.3 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/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 2003-2014 The OpenLDAP Foundation.
6
 * Portions Copyright 2004-2005 Pierangelo Masarati.
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 SLAPD_OVER_DYNGROUP != SLAPD_MOD_STATIC
28
#define TAKEOVER_DYNGROUP
29
#endif
30

31
32
33
34
35
#include <stdio.h>

#include <ac/string.h>

#include "slap.h"
36
#include "config.h"
37
38
#include "lutil.h"

39
static AttributeDescription *ad_dgIdentity, *ad_dgAuthz;
Howard Chu's avatar
Howard Chu committed
40

41
typedef struct dynlist_map_t {
42
43
44
	AttributeDescription	*dlm_member_ad;
	AttributeDescription	*dlm_mapped_ad;
	struct dynlist_map_t	*dlm_next;
45
46
} dynlist_map_t;

47
typedef struct dynlist_info_t {
48
49
	ObjectClass		*dli_oc;
	AttributeDescription	*dli_ad;
50
	struct dynlist_map_t	*dli_dlm;
51
52
53
54
	struct berval		dli_uri;
	LDAPURLDesc		*dli_lud;
	struct berval		dli_uri_nbase;
	Filter			*dli_uri_filter;
55
	struct berval		dli_default_filter;
56
57
	struct dynlist_info_t	*dli_next;
} dynlist_info_t;
58

59
#define DYNLIST_USAGE \
60
	"\"dynlist-attrset <oc> [uri] <URL-ad> [[<mapped-ad>:]<member-ad> ...]\": "
61

62
static dynlist_info_t *
63
dynlist_is_dynlist_next( Operation *op, SlapReply *rs, dynlist_info_t *old_dli )
64
65
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
66
	dynlist_info_t	*dli;
67
68
69

	Attribute	*a;

70
71
72
73
74
75
76
	if ( old_dli == NULL ) {
		dli = (dynlist_info_t *)on->on_bi.bi_private;

	} else {
		dli = old_dli->dli_next;
	}

77
78
79
80
81
	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 */
82
		return NULL;
83
84
	}

85
	for ( ; dli; dli = dli->dli_next ) {
86
87
		if ( dli->dli_lud != NULL ) {
			/* check base and scope */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
88
89
90
91
92
93
			if ( !BER_BVISNULL( &dli->dli_uri_nbase )
				&& !dnIsSuffixScope( &rs->sr_entry->e_nname,
					&dli->dli_uri_nbase,
					dli->dli_lud->lud_scope ) )
			{
				continue;
94
95
96
97
98
99
100
101
			}

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

102
		if ( attr_valfind( a,
103
104
				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
105
				&dli->dli_oc->soc_cname, NULL,
106
107
108
109
				op->o_tmpmemctx ) == 0 )
		{
			return dli;
		}
110
111
	}

112
	return NULL;
113
114
115
}

static int
116
dynlist_make_filter( Operation *op, Entry *e, const char *url, struct berval *oldf, struct berval *newf )
117
118
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
119
	dynlist_info_t	*dli = (dynlist_info_t *)on->on_bi.bi_private;
120
121

	char		*ptr;
122
	int		needBrackets = 0;
123

124
125
	assert( oldf != NULL );
	assert( newf != NULL );
126
127
128
	assert( !BER_BVISNULL( oldf ) );
	assert( !BER_BVISEMPTY( oldf ) );

129
	if ( oldf->bv_val[0] != '(' ) {
130
131
		Debug( LDAP_DEBUG_ANY, "%s: dynlist, DN=\"%s\": missing brackets in URI=\"%s\" filter\n",
			op->o_log_prefix, e->e_name.bv_val, url );
132
133
134
		needBrackets = 2;
	}

135
	newf->bv_len = STRLENOF( "(&(!(objectClass=" "))" ")" )
136
		+ dli->dli_oc->soc_cname.bv_len + oldf->bv_len + needBrackets;
137
138
139
140
141
142
143
	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, "))" );
144
	if ( needBrackets ) *ptr++ = '(';
145
	ptr = lutil_strcopy( ptr, oldf->bv_val );
146
	if ( needBrackets ) *ptr++ = ')';
147
148
149
150
151
152
	ptr = lutil_strcopy( ptr, ")" );
	newf->bv_len = ptr - newf->bv_val;

	return 0;
}

153
/* dynlist_sc_update() callback info set by dynlist_prepare_entry() */
154
typedef struct dynlist_sc_t {
155
	dynlist_info_t    *dlc_dli;
156
157
158
159
160
161
162
163
164
165
166
167
168
	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;
169
	dynlist_map_t		*dlm;
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

	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;
	}

188
189
190
191
	/* 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 ) {
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
		/* 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;
208
209
			mod.sm_desc = dlm->dlm_member_ad;
			mod.sm_type = dlm->dlm_member_ad->ad_cname;
210
211
			mod.sm_values = vals;
			mod.sm_nvalues = nvals;
Howard Chu's avatar
Howard Chu committed
212
			mod.sm_numvals = 1;
213
214
215
216
217
218
219
220
221
222
223
224
225

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

		goto done;
	}

	opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
	userattrs = SLAP_USERATTRS( rs->sr_attr_flags );

	for ( a = rs->sr_entry->e_attrs; a != NULL; a = a->a_next ) {
		BerVarray	vals, nvals = NULL;
226
227
		int		i, j,
				is_oc = a->a_desc == slap_schema.si_ad_objectClass;
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266

		/* 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 */
Howard Chu's avatar
Howard Chu committed
267
		i = a->a_numvals;
268
269
270
271
272
273
274

		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++ ) {
275
276
277
278
279
280
281
282
			if ( is_oc ) {
				ObjectClass	*soc = oc_bvfind( &a->a_vals[i] );

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

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
			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];
300
301
			dynlist_map_t	*dlm;
			AttributeDescription *ad;
302
303
304
305
306
307

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

308
309
310
			ad = a->a_desc;
			for ( dlm = dlc->dlc_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
				if ( dlm->dlm_member_ad == a->a_desc ) {
311
312
313
					if ( dlm->dlm_mapped_ad ) {
						ad = dlm->dlm_mapped_ad;
					}
314
315
316
317
					break;
				}
			}

318
			mod.sm_op = LDAP_MOD_ADD;
319
320
			mod.sm_desc = ad;
			mod.sm_type = ad->ad_cname;
321
322
			mod.sm_values = vals;
			mod.sm_nvalues = nvals;
Howard Chu's avatar
Howard Chu committed
323
			mod.sm_numvals = j;
324
325
326
327
328
329
330
331
332
333
334
335
336
337

			(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 );
338
		rs->sr_entry = NULL;
339
		rs->sr_flags &= ~REP_ENTRY_MASK;
340
341
342
343
344
345
	}

	return 0;
}
	
static int
346
dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli )
347
{
Howard Chu's avatar
Howard Chu committed
348
	Attribute	*a, *id = NULL;
349
350
351
352
353
354
355
	slap_callback	cb;
	Operation	o = *op;
	struct berval	*url;
	Entry		*e;
	int		opattrs,
			userattrs;
	dynlist_sc_t	dlc = { 0 };
356
	dynlist_map_t	*dlm;
357
358
359
360
361
362
363

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

364
365
366
367
	opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
	userattrs = SLAP_USERATTRS( rs->sr_attr_flags );

	/* Don't generate member list if it wasn't requested */
368
	for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
369
370
		AttributeDescription *ad = dlm->dlm_mapped_ad ? dlm->dlm_mapped_ad : dlm->dlm_member_ad;
		if ( userattrs || ad_inlist( ad, rs->sr_attrs ) ) 
371
			break;
372
	}
373
374
	if ( dli->dli_dlm && !dlm )
		return SLAP_CB_CONTINUE;
375

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
	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;
	}

396
397
398
	e = rs->sr_entry;
	/* ensure e is modifiable, but do not replace
	 * sr_entry yet since we have pointers into it */
399
400
401
	if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
		e = entry_dup( rs->sr_entry );
	}
402
403
404
405
406
407
408
409
410

	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
411
	o.ors_deref = LDAP_DEREF_NEVER;
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
	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;
		}

433
		if ( lud->lud_host != NULL ) {
434
			/* FIXME: host not allowed; reject as illegal? */
435
			Debug( LDAP_DEBUG_ANY, "dynlist_prepare_entry(\"%s\"): "
436
437
438
439
440
441
442
443
444
445
446
447
				"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, "" );
448

449
450
451
452
453
454
455
456
457
458
		} 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;

Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
459
460
		for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
			if ( dlm->dlm_mapped_ad != NULL ) {
461
				break;
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
462
463
464
			}
		}

465
		if ( dli->dli_dlm && !dlm ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
466
467
			/* if ( lud->lud_attrs != NULL ),
			 * the URL should be ignored */
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
			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 ) )
					{
Pierangelo Masarati's avatar
Pierangelo Masarati committed
497
						if ( !opattrs ) {
498
499
500
							continue;
						}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
						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;
							}
						}

516
517
518
519
520
					} else {
						if ( !userattrs && 
								o.ors_attrs[j].an_desc != NULL &&
								!ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
						{
Pierangelo Masarati's avatar
Pierangelo Masarati committed
521
522
523
524
525
526
527
528
529
530
531
532
							/* 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;
							}
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
						}
					}
				}

				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 );
550

551
552
553
		} else {
			struct berval	flt;
			ber_str2bv( lud->lud_filter, 0, 0, &flt );
554
			if ( dynlist_make_filter( op, rs->sr_entry, url->bv_val, &flt, &o.ors_filterstr ) ) {
555
556
557
558
559
560
561
562
563
				/* error */
				goto cleanup;
			}
		}
		o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
		if ( o.ors_filter == NULL ) {
			goto cleanup;
		}
		
564
		o.o_bd = select_backend( &o.o_req_ndn, 1 );
565
		if ( o.o_bd && o.o_bd->be_search ) {
566
			SlapReply	r = { REP_SEARCH };
567
568
569
570
571
			r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
			(void)o.o_bd->be_search( &o, &r );
		}

cleanup:;
Howard Chu's avatar
Howard Chu committed
572
573
574
		if ( id ) {
			slap_op_groups_free( &o );
		}
575
		if ( o.ors_filter ) {
576
			filter_free_x( &o, o.ors_filter, 1 );
577
578
579
580
581
582
583
584
585
586
587
588
		}
		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 );
		}
589
590
		assert( BER_BVISNULL( &o.ors_filterstr )
			|| o.ors_filterstr.bv_val != lud->lud_filter );
591
		op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
592
		ldap_free_urldesc( lud );
593
594
	}

595
596
597
598
	if ( e != rs->sr_entry ) {
		rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e );
		rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
	}
599
600
601
602

	return SLAP_CB_CONTINUE;
}

603
604
605
606
607
608
609
/* dynlist_sc_compare_entry() callback set by dynlist_compare() */
typedef struct dynlist_cc_t {
	slap_callback dc_cb;
#	define dc_ava	dc_cb.sc_private /* attr:val to compare with */
	int *dc_res;
} dynlist_cc_t;

610
static int
611
dynlist_sc_compare_entry( Operation *op, SlapReply *rs )
612
{
613
614
615
616
617
618
619
620
621
622
623
624
625
626
	if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) {
		dynlist_cc_t *dc = (dynlist_cc_t *)op->o_callback;
		AttributeAssertion *ava = dc->dc_ava;
		Attribute *a = attrs_find( rs->sr_entry->e_attrs, ava->aa_desc );

		if ( a != NULL ) {
			while ( LDAP_SUCCESS != attr_valfind( a,
					SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
					&ava->aa_value, NULL, op->o_tmpmemctx )
				&& (a = attrs_find( a->a_next, ava->aa_desc )) != NULL )
				;
			*dc->dc_res = a ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
		}
627
628
629
630
631
632
633
634
635
	}

	return 0;
}

static int
dynlist_compare( Operation *op, SlapReply *rs )
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
636
	dynlist_info_t	*dli = (dynlist_info_t *)on->on_bi.bi_private;
Howard Chu's avatar
Howard Chu committed
637
	Operation o = *op;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
638
	Entry *e = NULL;
639
	dynlist_map_t *dlm;
640
	BackendDB *be;
641
642

	for ( ; dli != NULL; dli = dli->dli_next ) {
643
644
645
646
		for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next )
			if ( op->oq_compare.rs_ava->aa_desc == dlm->dlm_member_ad )
				break;

647
		if ( dlm ) {
648
649
650
651
			/* 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.
			 */
652
			BerVarray id = NULL, authz = NULL;
Howard Chu's avatar
Howard Chu committed
653
654
655
656

			o.o_do_not_cache = 1;

			if ( ad_dgIdentity && backend_attribute( &o, NULL, &o.o_req_ndn,
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
				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;
					}
				}

Howard Chu's avatar
Howard Chu committed
674
675
676
677
				o.o_dn = *id;
				o.o_ndn = *id;
				o.o_groups = NULL; /* authz changed, invalidate cached groups */
			}
678

Howard Chu's avatar
Howard Chu committed
679
680
			rs->sr_err = backend_group( &o, NULL, &o.o_req_ndn,
				&o.oq_compare.rs_ava->aa_value, dli->dli_oc, dli->dli_ad );
681
682
683
684
685
686
687
688
689
690
691
692
693
694
			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;
695
				break;
696
			}
697

698
done:;
Howard Chu's avatar
Howard Chu committed
699
700
			if ( id ) ber_bvarray_free_x( id, o.o_tmpmemctx );

701
702
703
704
			return SLAP_CB_CONTINUE;
		}
	}

705
706
707
708
709
	be = select_backend( &o.o_req_ndn, 1 );
	if ( !be || !be->be_search ) {
		return SLAP_CB_CONTINUE;
	}

Howard Chu's avatar
Howard Chu committed
710
	if ( overlay_entry_get_ov( &o, &o.o_req_ndn, NULL, NULL, 0, &e, on ) !=
711
712
		LDAP_SUCCESS || e == NULL )
	{
Howard Chu's avatar
Howard Chu committed
713
714
		return SLAP_CB_CONTINUE;
	}
715

716
717
718
719
720
721
722
723
724
	/* check for dynlist objectClass; done if not found */
	dli = (dynlist_info_t *)on->on_bi.bi_private;
	while ( dli != NULL && !is_entry_objectclass_or_sub( e, dli->dli_oc ) ) {
		dli = dli->dli_next;
	}
	if ( dli == NULL ) {
		goto release;
	}

Howard Chu's avatar
Howard Chu committed
725
726
727
	if ( ad_dgIdentity ) {
		Attribute *id = attrs_find( e->e_attrs, ad_dgIdentity );
		if ( id ) {
728
729
730
731
732
733
734
735
736
737
738
739
740
741
			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;
				}
			}

Howard Chu's avatar
Howard Chu committed
742
743
744
745
746
			o.o_dn = id->a_vals[0];
			o.o_ndn = id->a_nvals[0];
			o.o_groups = NULL;
		}
	}
747

748
749
	/* generate dynamic list with dynlist_response() and compare */
	{
750
		SlapReply	r = { REP_SEARCH };
751
		dynlist_cc_t	dc = { { 0, dynlist_sc_compare_entry, 0, 0 }, 0 };
752
		AttributeName	an[2];
Howard Chu's avatar
Howard Chu committed
753

754
755
756
		dc.dc_ava = op->orc_ava;
		dc.dc_res = &rs->sr_err;
		o.o_callback = (slap_callback *) &dc;
757

758
759
760
761
		o.o_tag = LDAP_REQ_SEARCH;
		o.ors_limit = NULL;
		o.ors_tlimit = SLAP_NO_LIMIT;
		o.ors_slimit = SLAP_NO_LIMIT;
762

763
		o.ors_filterstr = *slap_filterstr_objectClass_pres;
Hallvard Furuseth's avatar
Hallvard Furuseth committed
764
		o.ors_filter = (Filter *) slap_filter_objectClass_pres;
765

766
767
768
769
770
771
772
		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;
773

774
775
		o.o_acl_priv = ACL_COMPARE;

776
		o.o_bd = be;
777
		(void)be->be_search( &o, &r );
778

Howard Chu's avatar
Howard Chu committed
779
780
781
		if ( o.o_dn.bv_val != op->o_dn.bv_val ) {
			slap_op_groups_free( &o );
		}
782
783
	}

Pierangelo Masarati's avatar
Pierangelo Masarati committed
784
785
release:;
	if ( e != NULL ) {
786
		overlay_entry_release_ov( &o, e, 0, on );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
787
788
	}

789
790
791
792
793
794
795
796
797
798
	return SLAP_CB_CONTINUE;
}

static int
dynlist_response( Operation *op, SlapReply *rs )
{
	switch ( op->o_tag ) {
	case LDAP_REQ_SEARCH:
		if ( rs->sr_type == REP_SEARCH && !get_manageDSAit( op ) )
		{
799
800
			int	rc = SLAP_CB_CONTINUE;
			dynlist_info_t	*dli = NULL;
801

802
			while ( (dli = dynlist_is_dynlist_next( op, rs, dli )) != NULL ) {
803
804
805
				rc = dynlist_prepare_entry( op, rs, dli );
			}

806
			return rc;
807
808
809
810
		}
		break;

	case LDAP_REQ_COMPARE:
811
812
813
814
		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  */
815
816
817
818
819
		/* 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 */
820
821
		case LDAP_COMPARE_FALSE:
		case LDAP_NO_SUCH_ATTRIBUTE:
822
823
824
825
826
827
828
829
			return dynlist_compare( op, rs );
		}
		break;
	}

	return SLAP_CB_CONTINUE;
}

830
831
832
833
834
835
836
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;
837
	dli->dli_default_filter.bv_val = ch_malloc( dli->dli_default_filter.bv_len + 1 );
838
839
840
841
842
843
844
845
846
847
	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, "))" );

848
	assert( ptr == &dli->dli_default_filter.bv_val[dli->dli_default_filter.bv_len] );
849
850
851
852
853
854
855

	return 0;
}

enum {
	DL_ATTRSET = 1,
	DL_ATTRPAIR,
856
	DL_ATTRPAIR_COMPAT,
857
858
859
860
861
	DL_LAST
};

static ConfigDriver	dl_cfgen;

862
/* XXXmanu 255 is the maximum arguments we allow. Can we go beyond? */
863
static ConfigTable dlcfg[] = {
864
865
866
	{ "dynlist-attrset", "group-oc> [uri] <URL-ad> <[mapped:]member-ad> [...]",
		3, 0, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen,
		"( OLcfgOvAt:8.1 NAME 'olcDlAttrSet' "
867
868
869
870
871
872
873
874
			"DESC 'Dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
			"EQUALITY caseIgnoreMatch "
			"SYNTAX OMsDirectoryString "
			"X-ORDERED 'VALUES' )",
			NULL, NULL },
	{ "dynlist-attrpair", "member-ad> <URL-ad",
		3, 3, 0, ARG_MAGIC|DL_ATTRPAIR, dl_cfgen,
			NULL, NULL, NULL },
875
876
877
878
879
#ifdef TAKEOVER_DYNGROUP
	{ "attrpair", "member-ad> <URL-ad",
		3, 3, 0, ARG_MAGIC|DL_ATTRPAIR_COMPAT, dl_cfgen,
			NULL, NULL, NULL },
#endif
880
881
882
883
884
885
886
887
888
889
890
891
892
	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};

static ConfigOCs dlocs[] = {
	{ "( OLcfgOvOc:8.1 "
		"NAME 'olcDynamicList' "
		"DESC 'Dynamic list configuration' "
		"SUP olcOverlayConfig "
		"MAY olcDLattrSet )",
		Cft_Overlay, dlcfg, NULL, NULL },
	{ NULL, 0, NULL }
};

893
static int
894
dl_cfgen( ConfigArgs *c )
895
{
896
897
898
899
900
901
902
903
904
905
	slap_overinst	*on = (slap_overinst *)c->bi;
	dynlist_info_t	*dli = (dynlist_info_t *)on->on_bi.bi_private;

	int		rc = 0, i;

	if ( c->op == SLAP_CONFIG_EMIT ) {
		switch( c->type ) {
		case DL_ATTRSET:
			for ( i = 0; dli; i++, dli = dli->dli_next ) {
				struct berval	bv;
906
				char		*ptr = c->cr_msg;
907
				dynlist_map_t	*dlm;
908
909
910
911

				assert( dli->dli_oc != NULL );
				assert( dli->dli_ad != NULL );

912
				/* FIXME: check buffer overflow! */
913
				ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
914
915
916
917
918
919
920
921
922
923
924
925
					SLAP_X_ORDERED_FMT "%s", i,
					dli->dli_oc->soc_cname.bv_val );

				if ( !BER_BVISNULL( &dli->dli_uri ) ) {
					*ptr++ = ' ';
					*ptr++ = '"';
					ptr = lutil_strncopy( ptr, dli->dli_uri.bv_val,
						dli->dli_uri.bv_len );
					*ptr++ = '"';
				}

				*ptr++ = ' ';
Howard Chu's avatar
Howard Chu committed
926
927
				ptr = lutil_strncopy( ptr, dli->dli_ad->ad_cname.bv_val,
					dli->dli_ad->ad_cname.bv_len );
928

929
				for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
930
931
					ptr[ 0 ] = ' ';
					ptr++;
932
933
934
935
936
937
938
					if ( dlm->dlm_mapped_ad ) {
						ptr = lutil_strcopy( ptr, dlm->dlm_mapped_ad->ad_cname.bv_val );
						ptr[ 0 ] = ':';
						ptr++;
					}
						
					ptr = lutil_strcopy( ptr, dlm->dlm_member_ad->ad_cname.bv_val );
939
				}
940

941
				bv.bv_val = c->cr_msg;
942
943
944
945
				bv.bv_len = ptr - bv.bv_val;
				value_add_one( &c->rvalue_vals, &bv );
			}
			break;
946

947
		case DL_ATTRPAIR_COMPAT:
948
949
950
		case DL_ATTRPAIR:
			rc = 1;
			break;
951

952
953
954
955
956
957
958
959
960
961
962
963
964
965
		default:
			rc = 1;
			break;
		}

		return rc;

	} else if ( c->op == LDAP_MOD_DELETE ) {
		switch( c->type ) {
		case DL_ATTRSET:
			if ( c->valx < 0 ) {
				dynlist_info_t	*dli_next;

				for ( dli_next = dli; dli_next; dli = dli_next ) {
966
967
968
					dynlist_map_t *dlm = dli->dli_dlm;
					dynlist_map_t *dlm_next;

969
970
					dli_next = dli->dli_next;

971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
					if ( !BER_BVISNULL( &dli->dli_uri ) ) {
						ch_free( dli->dli_uri.bv_val );
					}

					if ( dli->dli_lud != NULL ) {
						ldap_free_urldesc( dli->dli_lud );
					}

					if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
						ber_memfree( dli->dli_uri_nbase.bv_val );
					}

					if ( dli->dli_uri_filter != NULL ) {
						filter_free( dli->dli_uri_filter );
					}

987
					ch_free( dli->dli_default_filter.bv_val );
988
989
990
991
992
993

					while ( dlm != NULL ) {
						dlm_next = dlm->dlm_next;
						ch_free( dlm );
						dlm = dlm_next;
					}
994
995
996
997
998
999
1000
					ch_free( dli );
				}

				on->on_bi.bi_private = NULL;

			} else {
				dynlist_info_t	**dlip;
For faster browsing, not all history is shown. View entire blame