memberof.c 52.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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
/* memberof.c - back-reference for group membership */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 * Copyright 2005-2007 Pierangelo Masarati <ando@sys-net.it>
 * 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>.
 */
/* ACKNOWLEDGMENTS:
 * This work was initially developed by Pierangelo Masarati for inclusion
 * in OpenLDAP Software, sponsored by SysNet s.r.l.
 */

#include "portable.h"

#ifdef SLAPD_OVER_MEMBEROF

#include <stdio.h>

#include "ac/string.h"
#include "ac/socket.h"

#include "slap.h"
#include "config.h"
#include "lutil.h"

/*
 *	Glossary:
 *
 *		GROUP		a group object (an entry with GROUP_OC
 *				objectClass)
 *		MEMBER		a member object (an entry whose DN is
 *				listed as MEMBER_AT value of a GROUP)
 *		GROUP_OC	the objectClass of the group object
 *				(default: groupOfNames)
 *		MEMBER_AT	the membership attribute, DN-valued;
 *				note: nameAndOptionalUID is tolerated
 *				as soon as the optionalUID is absent
 *				(default: member)
 *		MEMBER_OF	reverse membership attribute
 *				(default: memberOf)
 *
 * 	- add:
 *		- if the entry that is being added is a GROUP,
 *		  the MEMBER_AT defined as values of the add operation
 *		  get the MEMBER_OF value directly from the request.
 *
 *		  if configured to do so, the MEMBER objects do not exist,
 *		  and no relax control is issued, either:
 *			- fail
 *			- drop non-existing members
 *		  (by default: don't muck with values)
 *
 *		- if (configured to do so,) the referenced GROUP exists,
 *		  the relax control is set and the user has
 *		  "manage" privileges, allow to add MEMBER_OF values to
 *		  generic entries.
 *
 *	- modify:
 *		- if the entry being modified is a GROUP_OC and the 
 *		  MEMBER_AT attribute is modified, the MEMBER_OF value
 *		  of the (existing) MEMBER_AT entries that are affected
 *		  is modified according to the request:
 *			- if a MEMBER is removed from the group,
 *			  delete the corresponding MEMBER_OF
 *			- if a MEMBER is added to a group,
 *			  add the corresponding MEMBER_OF
 *
 *		  We need to determine, from the database, if it is
 *		  a GROUP_OC, and we need to check, from the
 *		  modification list, if the MEMBER_AT attribute is being
 *		  affected, and what MEMBER_AT values are affected.
 *
 *		  if configured to do so, the entries corresponding to
 *		  the MEMBER_AT values do not exist, and no relax control
 *		  is issued, either:
 *			- fail
 *			- drop non-existing members
 *		  (by default: don't muck with values)
 *
 *		- if configured to do so, the referenced GROUP exists,
 *		  (the relax control is set) and the user has
 *		  "manage" privileges, allow to add MEMBER_OF values to
 *		  generic entries; the change is NOT automatically reflected
 *		  in the MEMBER attribute of the GROUP referenced
 *		  by the value of MEMBER_OF; a separate modification,
 *		  with or without relax control, needs to be performed.
 *
 *	- modrdn:
 *		- if the entry being renamed is a GROUP, the MEMBER_OF
 *		  value of the (existing) MEMBER objects is modified
 *		  accordingly based on the newDN of the GROUP.
 *
 *		  We need to determine, from the database, if it is
 *		  a GROUP; the list of MEMBER objects is obtained from
 *		  the database.
 *
 *		  Non-existing MEMBER objects are ignored, since the
 *		  MEMBER_AT is not being addressed by the operation.
 *
 *		- if the entry being renamed has the MEMBER_OF attribute,
 *		  the corresponding MEMBER value must be modified in the
 *		  respective group entries.
 *		
 *
 *	- delete:
 *		- if the entry being deleted is a GROUP, the (existing)
 *		  MEMBER objects are modified accordingly; a copy of the 
 *		  values of the MEMBER_AT is saved and, if the delete 
 *		  succeeds, the MEMBER_OF value of the (existing) MEMBER
 *		  objects is deleted.
 *
 *		  We need to determine, from the database, if it is
 *		  a GROUP.
 *
 *		  Non-existing MEMBER objects are ignored, since the entry
 *		  is being deleted.
 *
 *		- if the entry being deleted has the MEMBER_OF attribute,
 *		  the corresponding value of the MEMBER_AT must be deleted
 *		  from the respective GROUP entries.
 */

#define	SLAPD_MEMBEROF_ATTR	"memberOf"

static slap_overinst		memberof;

typedef struct memberof_t {
	struct berval		mo_dn;
	struct berval		mo_ndn;

	ObjectClass		*mo_oc_group;
	AttributeDescription	*mo_ad_member;
	AttributeDescription	*mo_ad_memberof;
	
	struct berval		mo_groupFilterstr;
	AttributeAssertion	mo_groupAVA;
	Filter			mo_groupFilter;

	struct berval		mo_memberFilterstr;
	Filter			mo_memberFilter;

	unsigned		mo_flags;
#define	MEMBEROF_NONE		0x00U
#define	MEMBEROF_FDANGLING_DROP	0x01U
#define	MEMBEROF_FDANGLING_ERROR	0x02U
#define	MEMBEROF_FDANGLING_MASK	(MEMBEROF_FDANGLING_DROP|MEMBEROF_FDANGLING_ERROR)
#define	MEMBEROF_FREFINT	0x04U
#define	MEMBEROF_FREVERSE	0x08U

158
159
	ber_int_t		mo_dangling_err;

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#define MEMBEROF_CHK(mo,f) \
	(((mo)->mo_flags & (f)) == (f))
#define MEMBEROF_DANGLING_CHECK(mo) \
	((mo)->mo_flags & MEMBEROF_FDANGLING_MASK)
#define MEMBEROF_DANGLING_DROP(mo) \
	MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_DROP)
#define MEMBEROF_DANGLING_ERROR(mo) \
	MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_ERROR)
#define MEMBEROF_REFINT(mo) \
	MEMBEROF_CHK((mo),MEMBEROF_FREFINT)
#define MEMBEROF_REVERSE(mo) \
	MEMBEROF_CHK((mo),MEMBEROF_FREVERSE)
} memberof_t;

typedef enum memberof_is_t {
	MEMBEROF_IS_NONE = 0x00,
	MEMBEROF_IS_GROUP = 0x01,
	MEMBEROF_IS_MEMBER = 0x02,
	MEMBEROF_IS_BOTH = (MEMBEROF_IS_GROUP|MEMBEROF_IS_MEMBER)
} memberof_is_t;

typedef struct memberof_cookie_t {
	AttributeDescription	*ad;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
183
	BerVarray		vals;
184
185
186
	int			foundit;
} memberof_cookie_t;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
187
188
189
190
typedef struct memberof_cbinfo_t {
	slap_overinst *on;
	BerVarray member;
	BerVarray memberof;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
191
	memberof_is_t what;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
192
193
} memberof_cbinfo_t;
	
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
static int
memberof_isGroupOrMember_cb( Operation *op, SlapReply *rs )
{
	if ( rs->sr_type == REP_SEARCH ) {
		memberof_cookie_t	*mc;

		mc = (memberof_cookie_t *)op->o_callback->sc_private;
		mc->foundit = 1;
	}

	return 0;
}

/*
 * callback for internal search that saves the member attribute values
 * of groups being deleted.
 */
static int
memberof_saveMember_cb( Operation *op, SlapReply *rs )
{
	if ( rs->sr_type == REP_SEARCH ) {
		memberof_cookie_t	*mc;
		Attribute		*a;

		mc = (memberof_cookie_t *)op->o_callback->sc_private;
		mc->foundit = 1;

221
222
		assert( rs->sr_entry != NULL );
		assert( rs->sr_entry->e_attrs != NULL );
223
224

		a = attr_find( rs->sr_entry->e_attrs, mc->ad );
225
		if ( a != NULL ) {
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
226
			ber_bvarray_dup_x( &mc->vals, a->a_nvals, op->o_tmpmemctx );
227

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
228
			assert( attr_find( a->a_next, mc->ad ) == NULL );
229
230
231
232
233
234
235
236
237
238
239
		}
	}

	return 0;
}

/*
 * the delete hook performs an internal search that saves the member
 * attribute values of groups being deleted.
 */
static int
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
240
memberof_isGroupOrMember( Operation *op, memberof_cbinfo_t *mci )
241
{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
242
	slap_overinst		*on = mci->on;
243
244
245
246
247
	memberof_t		*mo = (memberof_t *)on->on_bi.bi_private;

	Operation		op2 = *op;
	SlapReply		rs2 = { REP_RESULT };
	slap_callback		cb = { 0 };
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
248
	BackendInfo	*bi = op->o_bd->bd_info;
249
250
251
	AttributeName		an[ 2 ];

	memberof_is_t		iswhat = MEMBEROF_IS_NONE;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
252
	memberof_cookie_t	mc;
253

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
254
	assert( mci->what != MEMBEROF_IS_NONE );
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

	cb.sc_private = &mc;
	if ( op->o_tag == LDAP_REQ_DELETE ) {
		cb.sc_response = memberof_saveMember_cb;

	} else {
		cb.sc_response = memberof_isGroupOrMember_cb;
	}

	op2.o_tag = LDAP_REQ_SEARCH;
	op2.o_callback = &cb;
	op2.o_dn = op->o_bd->be_rootdn;
	op2.o_ndn = op->o_bd->be_rootndn;

	op2.ors_scope = LDAP_SCOPE_BASE;
	op2.ors_deref = LDAP_DEREF_NEVER;
	BER_BVZERO( &an[ 1 ].an_name );
	op2.ors_attrs = an;
	op2.ors_attrsonly = 0;
	op2.ors_limit = NULL;
	op2.ors_slimit = 1;
	op2.ors_tlimit = SLAP_NO_LIMIT;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
278
	if ( mci->what & MEMBEROF_IS_GROUP ) {
279
280
		mc.ad = mo->mo_ad_member;
		mc.foundit = 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
281
		mc.vals = NULL;
282
283
284
285
286
287
288
		an[ 0 ].an_desc = mo->mo_ad_member;
		an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
		op2.ors_filterstr = mo->mo_groupFilterstr;
		op2.ors_filter = &mo->mo_groupFilter;

		op2.o_bd->bd_info = (BackendInfo *)on->on_info;
		(void)op->o_bd->be_search( &op2, &rs2 );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
289
		op2.o_bd->bd_info = bi;
290
291
292

		if ( mc.foundit ) {
			iswhat |= MEMBEROF_IS_GROUP;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
293
			if ( mc.vals ) mci->member = mc.vals;
294
295
296
297

		}
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
298
	if ( mci->what & MEMBEROF_IS_MEMBER ) {
299
300
		mc.ad = mo->mo_ad_memberof;
		mc.foundit = 0;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
301
		mc.vals = NULL;
302
303
304
305
306
307
308
		an[ 0 ].an_desc = mo->mo_ad_memberof;
		an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
		op2.ors_filterstr = mo->mo_memberFilterstr;
		op2.ors_filter = &mo->mo_memberFilter;

		op2.o_bd->bd_info = (BackendInfo *)on->on_info;
		(void)op->o_bd->be_search( &op2, &rs2 );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
309
		op2.o_bd->bd_info = bi;
310
311
312

		if ( mc.foundit ) {
			iswhat |= MEMBEROF_IS_MEMBER;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
313
			if ( mc.vals ) mci->memberof = mc.vals;
314
315
316
317

		}
	}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
318
	mci->what = iswhat;
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336

	return LDAP_SUCCESS;
}

/*
 * response callback that adds memberof values when a group is modified.
 */
static int
memberof_value_modify(
	Operation		*op,
	SlapReply		*rs,
	struct berval		*ndn,
	AttributeDescription	*ad,
	struct berval		*old_dn,
	struct berval		*old_ndn,
	struct berval		*new_dn,
	struct berval		*new_ndn )
{
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
337
338
	memberof_cbinfo_t *mci = op->o_callback->sc_private;
	slap_overinst	*on = mci->on;
339
340
341
342
343
344
345
	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;

	Operation	op2 = *op;
	SlapReply	rs2 = { REP_RESULT };
	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
	Modifications	mod[ 2 ] = { { { 0 } } }, *ml;
	struct berval	values[ 4 ], nvalues[ 4 ];
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
346
	int		mcnt = 0;
347
348
349
350
351
352
353
354
355

	op2.o_tag = LDAP_REQ_MODIFY;

	op2.o_req_dn = *ndn;
	op2.o_req_ndn = *ndn;

	op2.o_callback = &cb;
	op2.o_dn = op->o_bd->be_rootdn;
	op2.o_ndn = op->o_bd->be_rootndn;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
	op2.orm_modlist = NULL;

	if ( !BER_BVISNULL( &mo->mo_ndn ) ) {
		ml = &mod[ mcnt ];
		ml->sml_numvals = 1;
		ml->sml_values = &values[ 0 ];
		ml->sml_values[ 0 ] = mo->mo_dn;
		BER_BVZERO( &ml->sml_values[ 1 ] );
		ml->sml_nvalues = &nvalues[ 0 ];
		ml->sml_nvalues[ 0 ] = mo->mo_ndn;
		BER_BVZERO( &ml->sml_nvalues[ 1 ] );
		ml->sml_desc = slap_schema.si_ad_modifiersName;
		ml->sml_type = ml->sml_desc->ad_cname;
		ml->sml_op = LDAP_MOD_REPLACE;
		ml->sml_flags = SLAP_MOD_INTERNAL;
		ml->sml_next = op2.orm_modlist;
		op2.orm_modlist = ml;

		mcnt++;
	}
376

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
377
	ml = &mod[ mcnt ];
378
	ml->sml_numvals = 1;
379
380
381
382
383
384
385
	ml->sml_values = &values[ 2 ];
	BER_BVZERO( &ml->sml_values[ 1 ] );
	ml->sml_nvalues = &nvalues[ 2 ];
	BER_BVZERO( &ml->sml_nvalues[ 1 ] );
	ml->sml_desc = ad;
	ml->sml_type = ml->sml_desc->ad_cname;
	ml->sml_flags = SLAP_MOD_INTERNAL;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
386
387
	ml->sml_next = op2.orm_modlist;
	op2.orm_modlist = ml;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
388
	op2.orm_no_opattrs = 0;
389
390
391
392
393

	if ( new_ndn != NULL ) {
		assert( !BER_BVISNULL( new_dn ) );
		assert( !BER_BVISNULL( new_ndn ) );

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
394
		ml = &mod[ mcnt ];
395
396
397
398
399
400
		ml->sml_op = LDAP_MOD_ADD;

		ml->sml_values[ 0 ] = *new_dn;
		ml->sml_nvalues[ 0 ] = *new_ndn;

		(void)op->o_bd->be_modify( &op2, &rs2 );
401
402
403
		if ( rs2.sr_err != LDAP_SUCCESS ) {
			char buf[ SLAP_TEXT_BUFLEN ];
			snprintf( buf, sizeof( buf ),
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
404
405
				"memberof_value_modify %s=\"%s\" failed err=%d",
				ad->ad_cname.bv_val, new_dn->bv_val, rs2.sr_err );
406
407
			Debug( LDAP_DEBUG_ANY, "%s: %s\n",
				op->o_log_prefix, buf, 0 );
408
		}
409

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
410
411
412
413
414
415
416
		assert( op2.orm_modlist == &mod[ mcnt ] );
		assert( mcnt == 0 || op2.orm_modlist->sml_next == &mod[ 0 ] );
		ml = op2.orm_modlist->sml_next;
		if ( mcnt == 1 ) {
			assert( ml == &mod[ 0 ] );
			ml = ml->sml_next;
		}
417
418
419
		if ( ml != NULL ) {
			slap_mods_free( ml, 1 );
		}
420
421

		mod[ 0 ].sml_next = NULL;
422
423
424
425
426
427
	}

	if ( old_ndn != NULL ) {
		assert( !BER_BVISNULL( old_dn ) );
		assert( !BER_BVISNULL( old_ndn ) );

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
428
		ml = &mod[ mcnt ];
429
430
431
432
433
434
		ml->sml_op = LDAP_MOD_DELETE;

		ml->sml_values[ 0 ] = *old_dn;
		ml->sml_nvalues[ 0 ] = *old_ndn;

		(void)op->o_bd->be_modify( &op2, &rs2 );
435
436
437
		if ( rs2.sr_err != LDAP_SUCCESS ) {
			char buf[ SLAP_TEXT_BUFLEN ];
			snprintf( buf, sizeof( buf ),
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
438
439
				"memberof_value_modify %s=\"%s\" failed err=%d",
				ad->ad_cname.bv_val, old_dn->bv_val, rs2.sr_err );
440
441
			Debug( LDAP_DEBUG_ANY, "%s: %s\n",
				op->o_log_prefix, buf, 0 );
442
		}
443

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
444
445
446
447
448
449
		assert( op2.orm_modlist == &mod[ mcnt ] );
		ml = op2.orm_modlist->sml_next;
		if ( mcnt == 1 ) {
			assert( ml == &mod[ 0 ] );
			ml = ml->sml_next;
		}
450
451
452
453
454
455
456
457
458
459
460
461
462
		if ( ml != NULL ) {
			slap_mods_free( ml, 1 );
		}
	}

	/* FIXME: if old_group_ndn doesn't exist, both delete __and__
	 * add will fail; better split in two operations, although
	 * not optimal in terms of performance.  At least it would
	 * move towards self-repairing capabilities. */

	return rs2.sr_err;
}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
static int
memberof_cleanup( Operation *op, SlapReply *rs )
{
	slap_callback *sc = op->o_callback;
	memberof_cbinfo_t *mci = sc->sc_private;

	op->o_callback = sc->sc_next;
	if ( mci->memberof )
		ber_bvarray_free_x( mci->memberof, op->o_tmpmemctx );
	if ( mci->member )
		ber_bvarray_free_x( mci->member, op->o_tmpmemctx );
	op->o_tmpfree( sc, op->o_tmpmemctx );
	return 0;
}

static int memberof_res_add( Operation *op, SlapReply *rs );
static int memberof_res_delete( Operation *op, SlapReply *rs );
static int memberof_res_modify( Operation *op, SlapReply *rs );
static int memberof_res_modrdn( Operation *op, SlapReply *rs );

483
484
485
486
487
488
489
490
491
492
static int
memberof_op_add( Operation *op, SlapReply *rs )
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;

	Attribute	**ap, **map = NULL;
	int		rc = SLAP_CB_CONTINUE;
	int		i;
	struct berval	save_dn, save_ndn;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
493
494
	slap_callback *sc;
	memberof_cbinfo_t *mci;
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520

	if ( op->ora_e->e_attrs == NULL ) {
		/* FIXME: global overlay; need to deal with */
		Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
			"consistency checks not implemented when overlay "
			"is instantiated as global.\n",
			op->o_log_prefix, op->o_req_dn.bv_val, 0 );
		return SLAP_CB_CONTINUE;
	}

	if ( MEMBEROF_REVERSE( mo ) ) {
		for ( ap = &op->ora_e->e_attrs; *ap; ap = &(*ap)->a_next ) {
			Attribute	*a = *ap;

			if ( a->a_desc == mo->mo_ad_memberof ) {
				map = ap;
				break;
			}
		}
	}

	save_dn = op->o_dn;
	save_ndn = op->o_ndn;

	if ( MEMBEROF_DANGLING_CHECK( mo )
			&& !get_relax( op )
521
			&& is_entry_objectclass_or_sub( op->ora_e, mo->mo_oc_group ) )
522
523
	{
		op->o_dn = op->o_bd->be_rootdn;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
524
		op->o_ndn = op->o_bd->be_rootndn;
525
526
527
528
529
530
531
532
533
534
535
536
537
		op->o_bd->bd_info = (BackendInfo *)on->on_info;

		for ( ap = &op->ora_e->e_attrs; *ap; ) {
			Attribute	*a = *ap;

			if ( !is_ad_subtype( a->a_desc, mo->mo_ad_member ) ) {
				ap = &a->a_next;
				continue;
			}

			assert( a->a_nvals != NULL );

			for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
538
				Entry		*e = NULL;
539

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
540
541
542
543
				/* ITS#6670 Ignore member pointing to this entry */
				if ( dn_match( &a->a_nvals[i], &save_ndn ))
					continue;

544
545
546
547
548
549
550
551
				rc = be_entry_get_rw( op, &a->a_nvals[ i ],
						NULL, NULL, 0, &e );
				if ( rc == LDAP_SUCCESS ) {
					be_entry_release_r( op, e );
					continue;
				}

				if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
552
					rc = rs->sr_err = mo->mo_dangling_err;
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
					rs->sr_text = "adding non-existing object "
						"as group member";
					send_ldap_result( op, rs );
					goto done;
				}

				if ( MEMBEROF_DANGLING_DROP( mo ) ) {
					int	j;
	
					Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
						"member=\"%s\" does not exist (stripping...)\n",
						op->o_log_prefix, op->ora_e->e_name.bv_val,
						a->a_vals[ i ].bv_val );
	
					for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
					ber_memfree( a->a_vals[ i ].bv_val );
					BER_BVZERO( &a->a_vals[ i ] );
					if ( a->a_nvals != a->a_vals ) {
						ber_memfree( a->a_nvals[ i ].bv_val );
						BER_BVZERO( &a->a_nvals[ i ] );
					}
					if ( j - i == 1 ) {
						break;
					}
		
					AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
						sizeof( struct berval ) * ( j - i ) );
					if ( a->a_nvals != a->a_vals ) {
						AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
							sizeof( struct berval ) * ( j - i ) );
					}
					i--;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
585
					a->a_numvals--;
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
				}
			}

			/* If all values have been removed,
			 * remove the attribute itself. */
			if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
				*ap = a->a_next;
				attr_free( a );
	
			} else {
				ap = &a->a_next;
			}
		}
		op->o_dn = save_dn;
		op->o_ndn = save_ndn;
		op->o_bd->bd_info = (BackendInfo *)on;
	}

	if ( map != NULL ) {
		Attribute		*a = *map;
		AccessControlState	acl_state = ACL_STATE_INIT;

		for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
			Entry		*e;

			op->o_bd->bd_info = (BackendInfo *)on->on_info;
			/* access is checked with the original identity */
			rc = access_allowed( op, op->ora_e, mo->mo_ad_memberof,
					&a->a_nvals[ i ], ACL_WADD,
					&acl_state );
			if ( rc == 0 ) {
				rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
				rs->sr_text = NULL;
				send_ldap_result( op, rs );
				goto done;
			}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
622
623
624
625
			/* ITS#6670 Ignore member pointing to this entry */
			if ( dn_match( &a->a_nvals[i], &save_ndn ))
				continue;

626
627
628
629
630
631
632
633
634
			rc = be_entry_get_rw( op, &a->a_nvals[ i ],
					NULL, NULL, 0, &e );
			op->o_bd->bd_info = (BackendInfo *)on;
			if ( rc != LDAP_SUCCESS ) {
				if ( get_relax( op ) ) {
					continue;
				}

				if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
635
					rc = rs->sr_err = mo->mo_dangling_err;
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
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
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
					rs->sr_text = "adding non-existing object "
						"as memberof";
					send_ldap_result( op, rs );
					goto done;
				}

				if ( MEMBEROF_DANGLING_DROP( mo ) ) {
					int	j;
	
					Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
						"memberof=\"%s\" does not exist (stripping...)\n",
						op->o_log_prefix, op->ora_e->e_name.bv_val,
						a->a_nvals[ i ].bv_val );
	
					for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
					ber_memfree( a->a_vals[ i ].bv_val );
					BER_BVZERO( &a->a_vals[ i ] );
					if ( a->a_nvals != a->a_vals ) {
						ber_memfree( a->a_nvals[ i ].bv_val );
						BER_BVZERO( &a->a_nvals[ i ] );
					}
					if ( j - i == 1 ) {
						break;
					}
		
					AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
						sizeof( struct berval ) * ( j - i ) );
					if ( a->a_nvals != a->a_vals ) {
						AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
							sizeof( struct berval ) * ( j - i ) );
					}
					i--;
				}
				
				continue;
			}

			/* access is checked with the original identity */
			op->o_bd->bd_info = (BackendInfo *)on->on_info;
			rc = access_allowed( op, e, mo->mo_ad_member,
					&op->o_req_ndn, ACL_WADD, NULL );
			be_entry_release_r( op, e );
			op->o_bd->bd_info = (BackendInfo *)on;

			if ( !rc ) {
				rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
				rs->sr_text = "insufficient access to object referenced by memberof";
				send_ldap_result( op, rs );
				goto done;
			}
		}

		if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
			*map = a->a_next;
			attr_free( a );
		}
	}

	rc = SLAP_CB_CONTINUE;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
695
696
697
698
699
700
701
702
703
704
705
706

	sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
	sc->sc_private = sc+1;
	sc->sc_response = memberof_res_add;
	sc->sc_cleanup = memberof_cleanup;
	mci = sc->sc_private;
	mci->on = on;
	mci->member = NULL;
	mci->memberof = NULL;
	sc->sc_next = op->o_callback;
	op->o_callback = sc;

707
708
709
710
711
712
713
714
715
716
717
718
719
720
done:;
	op->o_dn = save_dn;
	op->o_ndn = save_ndn;
	op->o_bd->bd_info = (BackendInfo *)on;

	return rc;
}

static int
memberof_op_delete( Operation *op, SlapReply *rs )
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
721
722
	slap_callback *sc;
	memberof_cbinfo_t *mci;
723
724


Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
725
726
727
728
729
730
731
732
	sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
	sc->sc_private = sc+1;
	sc->sc_response = memberof_res_delete;
	sc->sc_cleanup = memberof_cleanup;
	mci = sc->sc_private;
	mci->on = on;
	mci->member = NULL;
	mci->memberof = NULL;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
733
734
735
736
	mci->what = MEMBEROF_IS_GROUP;
	if ( MEMBEROF_REFINT( mo ) ) {
		mci->what = MEMBEROF_IS_BOTH;
	}
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
737

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
738
	memberof_isGroupOrMember( op, mci );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
739
740
741

	sc->sc_next = op->o_callback;
	op->o_callback = sc;
742
743
744
745
746
747
748
749
750
751
752

	return SLAP_CB_CONTINUE;
}

static int
memberof_op_modify( Operation *op, SlapReply *rs )
{
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;

	Modifications	**mlp, **mmlp = NULL;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
753
	int		rc = SLAP_CB_CONTINUE, save_member = 0;
754
	struct berval	save_dn, save_ndn;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
755
756
	slap_callback *sc;
	memberof_cbinfo_t *mci, mcis;
757
758
759
760
761
762
763
764
765
766
767
768
769
770

	if ( MEMBEROF_REVERSE( mo ) ) {
		for ( mlp = &op->orm_modlist; *mlp; mlp = &(*mlp)->sml_next ) {
			Modifications	*ml = *mlp;

			if ( ml->sml_desc == mo->mo_ad_memberof ) {
				mmlp = mlp;
				break;
			}
		}
	}

	save_dn = op->o_dn;
	save_ndn = op->o_ndn;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
771
	mcis.on = on;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
772
	mcis.what = MEMBEROF_IS_GROUP;
773

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
774
775
	if ( memberof_isGroupOrMember( op, &mcis ) == LDAP_SUCCESS
		&& ( mcis.what & MEMBEROF_IS_GROUP ) )
776
	{
777
778
779
780
781
782
783
784
785
786
		Modifications *ml;

		for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
			if ( ml->sml_desc == mo->mo_ad_member ) {
				switch ( ml->sml_op ) {
				case LDAP_MOD_DELETE:
				case LDAP_MOD_REPLACE:
					save_member = 1;
					break;
				}
787
			}
788
789
790
791
792
793
794
		}


		if ( MEMBEROF_DANGLING_CHECK( mo )
				&& !get_relax( op ) )
		{
			op->o_dn = op->o_bd->be_rootdn;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
795
			op->o_ndn = op->o_bd->be_rootndn;
796
797
798
799
800
801
802
803
804
805
806
			op->o_bd->bd_info = (BackendInfo *)on->on_info;
		
			assert( op->orm_modlist != NULL );
		
			for ( mlp = &op->orm_modlist; *mlp; ) {
				Modifications	*ml = *mlp;
				int		i;
		
				if ( !is_ad_subtype( ml->sml_desc, mo->mo_ad_member ) ) {
					mlp = &ml->sml_next;
					continue;
807
				}
808
809
810
811
812
813
		
				switch ( ml->sml_op ) {
				case LDAP_MOD_DELETE:
					/* we don't care about cancellations: if the value
					 * exists, fine; if it doesn't, we let the underlying
					 * database fail as appropriate; */
814
					mlp = &ml->sml_next;
815
816
817
					break;
		
				case LDAP_MOD_REPLACE:
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
818
819
820
821
822
823
 					/* Handle this just like a delete (see above) */
 					if ( !ml->sml_values ) {
 						mlp = &ml->sml_next;
 						break;
 					}
 
824
825
826
827
828
829
830
831
832
				case LDAP_MOD_ADD:
					/* NOTE: right now, the attributeType we use
					 * for member must have a normalized value */
					assert( ml->sml_nvalues != NULL );
		
					for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
						int		rc;
						Entry		*e;
		
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
833
834
835
836
						/* ITS#6670 Ignore member pointing to this entry */
						if ( dn_match( &ml->sml_nvalues[i], &save_ndn ))
							continue;

837
838
839
840
841
842
843
844
						if ( be_entry_get_rw( op, &ml->sml_nvalues[ i ],
								NULL, NULL, 0, &e ) == LDAP_SUCCESS )
						{
							be_entry_release_r( op, e );
							continue;
						}
		
						if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
845
							rc = rs->sr_err = mo->mo_dangling_err;
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
							rs->sr_text = "adding non-existing object "
								"as group member";
							send_ldap_result( op, rs );
							goto done;
						}
		
						if ( MEMBEROF_DANGLING_DROP( mo ) ) {
							int	j;
		
							Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
								"member=\"%s\" does not exist (stripping...)\n",
								op->o_log_prefix, op->o_req_dn.bv_val,
								ml->sml_nvalues[ i ].bv_val );
		
							for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
							ber_memfree( ml->sml_values[ i ].bv_val );
							BER_BVZERO( &ml->sml_values[ i ] );
							ber_memfree( ml->sml_nvalues[ i ].bv_val );
							BER_BVZERO( &ml->sml_nvalues[ i ] );
							ml->sml_numvals--;
							if ( j - i == 1 ) {
								break;
							}
		
							AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
								sizeof( struct berval ) * ( j - i ) );
							AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
								sizeof( struct berval ) * ( j - i ) );
							i--;
						}
					}
		
					if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
						*mlp = ml->sml_next;
						slap_mod_free( &ml->sml_mod, 0 );
						free( ml );
		
					} else {
						mlp = &ml->sml_next;
					}
		
					break;
		
				default:
					assert( 0 );
891
892
893
894
				}
			}
		}
	}
895
	
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
	if ( mmlp != NULL ) {
		Modifications	*ml = *mmlp;
		int		i;
		Entry		*target;

		op->o_bd->bd_info = (BackendInfo *)on->on_info;
		rc = be_entry_get_rw( op, &op->o_req_ndn,
				NULL, NULL, 0, &target );
		op->o_bd->bd_info = (BackendInfo *)on;
		if ( rc != LDAP_SUCCESS ) {
			rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
			send_ldap_result( op, rs );
			goto done;
		}

		switch ( ml->sml_op ) {
		case LDAP_MOD_DELETE:
			if ( ml->sml_nvalues != NULL ) {
				AccessControlState	acl_state = ACL_STATE_INIT;

				for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
					Entry		*e;

					op->o_bd->bd_info = (BackendInfo *)on->on_info;
					/* access is checked with the original identity */
					rc = access_allowed( op, target,
							mo->mo_ad_memberof,
							&ml->sml_nvalues[ i ],
							ACL_WDEL,
							&acl_state );
					if ( rc == 0 ) {
						rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
						rs->sr_text = NULL;
						send_ldap_result( op, rs );
						goto done2;
					}

					rc = be_entry_get_rw( op, &ml->sml_nvalues[ i ],
							NULL, NULL, 0, &e );
					op->o_bd->bd_info = (BackendInfo *)on;
					if ( rc != LDAP_SUCCESS ) {
						if ( get_relax( op ) ) {
							continue;
						}

						if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
942
							rc = rs->sr_err = mo->mo_dangling_err;
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
							rs->sr_text = "deleting non-existing object "
								"as memberof";
							send_ldap_result( op, rs );
							goto done2;
						}

						if ( MEMBEROF_DANGLING_DROP( mo ) ) {
							int	j;
	
							Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
								"memberof=\"%s\" does not exist (stripping...)\n",
								op->o_log_prefix, op->o_req_ndn.bv_val,
								ml->sml_nvalues[ i ].bv_val );
	
							for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
							ber_memfree( ml->sml_values[ i ].bv_val );
							BER_BVZERO( &ml->sml_values[ i ] );
							if ( ml->sml_nvalues != ml->sml_values ) {
								ber_memfree( ml->sml_nvalues[ i ].bv_val );
								BER_BVZERO( &ml->sml_nvalues[ i ] );
							}
964
							ml->sml_numvals--;
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
							if ( j - i == 1 ) {
								break;
							}
		
							AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
								sizeof( struct berval ) * ( j - i ) );
							if ( ml->sml_nvalues != ml->sml_values ) {
								AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
									sizeof( struct berval ) * ( j - i ) );
							}
							i--;
						}

						continue;
					}

					/* access is checked with the original identity */
					op->o_bd->bd_info = (BackendInfo *)on->on_info;
					rc = access_allowed( op, e, mo->mo_ad_member,
							&op->o_req_ndn,
							ACL_WDEL, NULL );
					be_entry_release_r( op, e );
					op->o_bd->bd_info = (BackendInfo *)on;

					if ( !rc ) {
						rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
						rs->sr_text = "insufficient access to object referenced by memberof";
						send_ldap_result( op, rs );
						goto done;
					}
				}

				if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
					*mmlp = ml->sml_next;
					slap_mod_free( &ml->sml_mod, 0 );
					free( ml );