xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/memberof.c (revision 5c46dd73a9bcb28b2994504ea090f64066b17a77)
1 /*	$NetBSD: memberof.c,v 1.1.1.3 2010/03/08 02:14:20 lukem Exp $	*/
2 
3 /* memberof.c - back-reference for group membership */
4 /* OpenLDAP: pkg/ldap/servers/slapd/overlays/memberof.c,v 1.2.2.20 2009/11/17 17:37:39 quanah Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2005-2007 Pierangelo Masarati <ando@sys-net.it>
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGMENTS:
19  * This work was initially developed by Pierangelo Masarati for inclusion
20  * in OpenLDAP Software, sponsored by SysNet s.r.l.
21  */
22 
23 #include "portable.h"
24 
25 #ifdef SLAPD_OVER_MEMBEROF
26 
27 #include <stdio.h>
28 
29 #include "ac/string.h"
30 #include "ac/socket.h"
31 
32 #include "slap.h"
33 #include "config.h"
34 #include "lutil.h"
35 
36 /*
37  *	Glossary:
38  *
39  *		GROUP		a group object (an entry with GROUP_OC
40  *				objectClass)
41  *		MEMBER		a member object (an entry whose DN is
42  *				listed as MEMBER_AT value of a GROUP)
43  *		GROUP_OC	the objectClass of the group object
44  *				(default: groupOfNames)
45  *		MEMBER_AT	the membership attribute, DN-valued;
46  *				note: nameAndOptionalUID is tolerated
47  *				as soon as the optionalUID is absent
48  *				(default: member)
49  *		MEMBER_OF	reverse membership attribute
50  *				(default: memberOf)
51  *
52  * 	- add:
53  *		- if the entry that is being added is a GROUP,
54  *		  the MEMBER_AT defined as values of the add operation
55  *		  get the MEMBER_OF value directly from the request.
56  *
57  *		  if configured to do so, the MEMBER objects do not exist,
58  *		  and no relax control is issued, either:
59  *			- fail
60  *			- drop non-existing members
61  *		  (by default: don't muck with values)
62  *
63  *		- if (configured to do so,) the referenced GROUP exists,
64  *		  the relax control is set and the user has
65  *		  "manage" privileges, allow to add MEMBER_OF values to
66  *		  generic entries.
67  *
68  *	- modify:
69  *		- if the entry being modified is a GROUP_OC and the
70  *		  MEMBER_AT attribute is modified, the MEMBER_OF value
71  *		  of the (existing) MEMBER_AT entries that are affected
72  *		  is modified according to the request:
73  *			- if a MEMBER is removed from the group,
74  *			  delete the corresponding MEMBER_OF
75  *			- if a MEMBER is added to a group,
76  *			  add the corresponding MEMBER_OF
77  *
78  *		  We need to determine, from the database, if it is
79  *		  a GROUP_OC, and we need to check, from the
80  *		  modification list, if the MEMBER_AT attribute is being
81  *		  affected, and what MEMBER_AT values are affected.
82  *
83  *		  if configured to do so, the entries corresponding to
84  *		  the MEMBER_AT values do not exist, and no relax control
85  *		  is issued, either:
86  *			- fail
87  *			- drop non-existing members
88  *		  (by default: don't muck with values)
89  *
90  *		- if configured to do so, the referenced GROUP exists,
91  *		  (the relax control is set) and the user has
92  *		  "manage" privileges, allow to add MEMBER_OF values to
93  *		  generic entries; the change is NOT automatically reflected
94  *		  in the MEMBER attribute of the GROUP referenced
95  *		  by the value of MEMBER_OF; a separate modification,
96  *		  with or without relax control, needs to be performed.
97  *
98  *	- modrdn:
99  *		- if the entry being renamed is a GROUP, the MEMBER_OF
100  *		  value of the (existing) MEMBER objects is modified
101  *		  accordingly based on the newDN of the GROUP.
102  *
103  *		  We need to determine, from the database, if it is
104  *		  a GROUP; the list of MEMBER objects is obtained from
105  *		  the database.
106  *
107  *		  Non-existing MEMBER objects are ignored, since the
108  *		  MEMBER_AT is not being addressed by the operation.
109  *
110  *		- if the entry being renamed has the MEMBER_OF attribute,
111  *		  the corresponding MEMBER value must be modified in the
112  *		  respective group entries.
113  *
114  *
115  *	- delete:
116  *		- if the entry being deleted is a GROUP, the (existing)
117  *		  MEMBER objects are modified accordingly; a copy of the
118  *		  values of the MEMBER_AT is saved and, if the delete
119  *		  succeeds, the MEMBER_OF value of the (existing) MEMBER
120  *		  objects is deleted.
121  *
122  *		  We need to determine, from the database, if it is
123  *		  a GROUP.
124  *
125  *		  Non-existing MEMBER objects are ignored, since the entry
126  *		  is being deleted.
127  *
128  *		- if the entry being deleted has the MEMBER_OF attribute,
129  *		  the corresponding value of the MEMBER_AT must be deleted
130  *		  from the respective GROUP entries.
131  */
132 
133 #define	SLAPD_MEMBEROF_ATTR	"memberOf"
134 
135 static slap_overinst		memberof;
136 
137 typedef struct memberof_t {
138 	struct berval		mo_dn;
139 	struct berval		mo_ndn;
140 
141 	ObjectClass		*mo_oc_group;
142 	AttributeDescription	*mo_ad_member;
143 	AttributeDescription	*mo_ad_memberof;
144 
145 	struct berval		mo_groupFilterstr;
146 	AttributeAssertion	mo_groupAVA;
147 	Filter			mo_groupFilter;
148 
149 	struct berval		mo_memberFilterstr;
150 	Filter			mo_memberFilter;
151 
152 	unsigned		mo_flags;
153 #define	MEMBEROF_NONE		0x00U
154 #define	MEMBEROF_FDANGLING_DROP	0x01U
155 #define	MEMBEROF_FDANGLING_ERROR	0x02U
156 #define	MEMBEROF_FDANGLING_MASK	(MEMBEROF_FDANGLING_DROP|MEMBEROF_FDANGLING_ERROR)
157 #define	MEMBEROF_FREFINT	0x04U
158 #define	MEMBEROF_FREVERSE	0x08U
159 
160 	ber_int_t		mo_dangling_err;
161 
162 #define MEMBEROF_CHK(mo,f) \
163 	(((mo)->mo_flags & (f)) == (f))
164 #define MEMBEROF_DANGLING_CHECK(mo) \
165 	((mo)->mo_flags & MEMBEROF_FDANGLING_MASK)
166 #define MEMBEROF_DANGLING_DROP(mo) \
167 	MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_DROP)
168 #define MEMBEROF_DANGLING_ERROR(mo) \
169 	MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_ERROR)
170 #define MEMBEROF_REFINT(mo) \
171 	MEMBEROF_CHK((mo),MEMBEROF_FREFINT)
172 #define MEMBEROF_REVERSE(mo) \
173 	MEMBEROF_CHK((mo),MEMBEROF_FREVERSE)
174 } memberof_t;
175 
176 typedef enum memberof_is_t {
177 	MEMBEROF_IS_NONE = 0x00,
178 	MEMBEROF_IS_GROUP = 0x01,
179 	MEMBEROF_IS_MEMBER = 0x02,
180 	MEMBEROF_IS_BOTH = (MEMBEROF_IS_GROUP|MEMBEROF_IS_MEMBER)
181 } memberof_is_t;
182 
183 typedef struct memberof_cookie_t {
184 	AttributeDescription	*ad;
185 	BerVarray		vals;
186 	int			foundit;
187 } memberof_cookie_t;
188 
189 typedef struct memberof_cbinfo_t {
190 	slap_overinst *on;
191 	BerVarray member;
192 	BerVarray memberof;
193 	memberof_is_t what;
194 } memberof_cbinfo_t;
195 
196 static int
197 memberof_isGroupOrMember_cb( Operation *op, SlapReply *rs )
198 {
199 	if ( rs->sr_type == REP_SEARCH ) {
200 		memberof_cookie_t	*mc;
201 
202 		mc = (memberof_cookie_t *)op->o_callback->sc_private;
203 		mc->foundit = 1;
204 	}
205 
206 	return 0;
207 }
208 
209 /*
210  * callback for internal search that saves the member attribute values
211  * of groups being deleted.
212  */
213 static int
214 memberof_saveMember_cb( Operation *op, SlapReply *rs )
215 {
216 	if ( rs->sr_type == REP_SEARCH ) {
217 		memberof_cookie_t	*mc;
218 		Attribute		*a;
219 
220 		mc = (memberof_cookie_t *)op->o_callback->sc_private;
221 		mc->foundit = 1;
222 
223 		assert( rs->sr_entry != NULL );
224 		assert( rs->sr_entry->e_attrs != NULL );
225 
226 		a = attr_find( rs->sr_entry->e_attrs, mc->ad );
227 		if ( a != NULL ) {
228 			ber_bvarray_dup_x( &mc->vals, a->a_nvals, op->o_tmpmemctx );
229 
230 			assert( attr_find( a->a_next, mc->ad ) == NULL );
231 		}
232 	}
233 
234 	return 0;
235 }
236 
237 /*
238  * the delete hook performs an internal search that saves the member
239  * attribute values of groups being deleted.
240  */
241 static int
242 memberof_isGroupOrMember( Operation *op, memberof_cbinfo_t *mci )
243 {
244 	slap_overinst		*on = mci->on;
245 	memberof_t		*mo = (memberof_t *)on->on_bi.bi_private;
246 
247 	Operation		op2 = *op;
248 	SlapReply		rs2 = { REP_RESULT };
249 	slap_callback		cb = { 0 };
250 	BackendInfo	*bi = op->o_bd->bd_info;
251 	AttributeName		an[ 2 ];
252 
253 	memberof_is_t		iswhat = MEMBEROF_IS_NONE;
254 	memberof_cookie_t	mc;
255 
256 	assert( mci->what != MEMBEROF_IS_NONE );
257 
258 	cb.sc_private = &mc;
259 	if ( op->o_tag == LDAP_REQ_DELETE ) {
260 		cb.sc_response = memberof_saveMember_cb;
261 
262 	} else {
263 		cb.sc_response = memberof_isGroupOrMember_cb;
264 	}
265 
266 	op2.o_tag = LDAP_REQ_SEARCH;
267 	op2.o_callback = &cb;
268 	op2.o_dn = op->o_bd->be_rootdn;
269 	op2.o_ndn = op->o_bd->be_rootndn;
270 
271 	op2.ors_scope = LDAP_SCOPE_BASE;
272 	op2.ors_deref = LDAP_DEREF_NEVER;
273 	BER_BVZERO( &an[ 1 ].an_name );
274 	op2.ors_attrs = an;
275 	op2.ors_attrsonly = 0;
276 	op2.ors_limit = NULL;
277 	op2.ors_slimit = 1;
278 	op2.ors_tlimit = SLAP_NO_LIMIT;
279 
280 	if ( mci->what & MEMBEROF_IS_GROUP ) {
281 		mc.ad = mo->mo_ad_member;
282 		mc.foundit = 0;
283 		mc.vals = NULL;
284 		an[ 0 ].an_desc = mo->mo_ad_member;
285 		an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
286 		op2.ors_filterstr = mo->mo_groupFilterstr;
287 		op2.ors_filter = &mo->mo_groupFilter;
288 
289 		op2.o_bd->bd_info = (BackendInfo *)on->on_info;
290 		(void)op->o_bd->be_search( &op2, &rs2 );
291 		op2.o_bd->bd_info = bi;
292 
293 		if ( mc.foundit ) {
294 			iswhat |= MEMBEROF_IS_GROUP;
295 			if ( mc.vals ) mci->member = mc.vals;
296 
297 		}
298 	}
299 
300 	if ( mci->what & MEMBEROF_IS_MEMBER ) {
301 		mc.ad = mo->mo_ad_memberof;
302 		mc.foundit = 0;
303 		mc.vals = NULL;
304 		an[ 0 ].an_desc = mo->mo_ad_memberof;
305 		an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
306 		op2.ors_filterstr = mo->mo_memberFilterstr;
307 		op2.ors_filter = &mo->mo_memberFilter;
308 
309 		op2.o_bd->bd_info = (BackendInfo *)on->on_info;
310 		(void)op->o_bd->be_search( &op2, &rs2 );
311 		op2.o_bd->bd_info = bi;
312 
313 		if ( mc.foundit ) {
314 			iswhat |= MEMBEROF_IS_MEMBER;
315 			if ( mc.vals ) mci->memberof = mc.vals;
316 
317 		}
318 	}
319 
320 	mci->what = iswhat;
321 
322 	return LDAP_SUCCESS;
323 }
324 
325 /*
326  * response callback that adds memberof values when a group is modified.
327  */
328 static int
329 memberof_value_modify(
330 	Operation		*op,
331 	SlapReply		*rs,
332 	struct berval		*ndn,
333 	AttributeDescription	*ad,
334 	struct berval		*old_dn,
335 	struct berval		*old_ndn,
336 	struct berval		*new_dn,
337 	struct berval		*new_ndn )
338 {
339 	memberof_cbinfo_t *mci = op->o_callback->sc_private;
340 	slap_overinst	*on = mci->on;
341 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
342 
343 	Operation	op2 = *op;
344 	SlapReply	rs2 = { REP_RESULT };
345 	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
346 	Modifications	mod[ 2 ] = { { { 0 } } }, *ml;
347 	struct berval	values[ 4 ], nvalues[ 4 ];
348 	int		mcnt = 0;
349 
350 	op2.o_tag = LDAP_REQ_MODIFY;
351 
352 	op2.o_req_dn = *ndn;
353 	op2.o_req_ndn = *ndn;
354 
355 	op2.o_callback = &cb;
356 	op2.o_dn = op->o_bd->be_rootdn;
357 	op2.o_ndn = op->o_bd->be_rootndn;
358 	op2.orm_modlist = NULL;
359 
360 	if ( !BER_BVISNULL( &mo->mo_ndn ) ) {
361 		ml = &mod[ mcnt ];
362 		ml->sml_numvals = 1;
363 		ml->sml_values = &values[ 0 ];
364 		ml->sml_values[ 0 ] = mo->mo_dn;
365 		BER_BVZERO( &ml->sml_values[ 1 ] );
366 		ml->sml_nvalues = &nvalues[ 0 ];
367 		ml->sml_nvalues[ 0 ] = mo->mo_ndn;
368 		BER_BVZERO( &ml->sml_nvalues[ 1 ] );
369 		ml->sml_desc = slap_schema.si_ad_modifiersName;
370 		ml->sml_type = ml->sml_desc->ad_cname;
371 		ml->sml_op = LDAP_MOD_REPLACE;
372 		ml->sml_flags = SLAP_MOD_INTERNAL;
373 		ml->sml_next = op2.orm_modlist;
374 		op2.orm_modlist = ml;
375 
376 		mcnt++;
377 	}
378 
379 	ml = &mod[ mcnt ];
380 	ml->sml_numvals = 1;
381 	ml->sml_values = &values[ 2 ];
382 	BER_BVZERO( &ml->sml_values[ 1 ] );
383 	ml->sml_nvalues = &nvalues[ 2 ];
384 	BER_BVZERO( &ml->sml_nvalues[ 1 ] );
385 	ml->sml_desc = ad;
386 	ml->sml_type = ml->sml_desc->ad_cname;
387 	ml->sml_flags = SLAP_MOD_INTERNAL;
388 	ml->sml_next = op2.orm_modlist;
389 	op2.orm_modlist = ml;
390 	op2.orm_no_opattrs = 0;
391 
392 	if ( new_ndn != NULL ) {
393 		assert( !BER_BVISNULL( new_dn ) );
394 		assert( !BER_BVISNULL( new_ndn ) );
395 
396 		ml = &mod[ mcnt ];
397 		ml->sml_op = LDAP_MOD_ADD;
398 
399 		ml->sml_values[ 0 ] = *new_dn;
400 		ml->sml_nvalues[ 0 ] = *new_ndn;
401 
402 		(void)op->o_bd->be_modify( &op2, &rs2 );
403 		if ( rs2.sr_err != LDAP_SUCCESS ) {
404 			char buf[ SLAP_TEXT_BUFLEN ];
405 			snprintf( buf, sizeof( buf ),
406 				"memberof_value_modify %s=\"%s\" failed err=%d text=%s",
407 				ad->ad_cname.bv_val, new_dn->bv_val, rs2.sr_err,
408 				rs2.sr_text ? rs2.sr_text : "" );
409 			Debug( LDAP_DEBUG_ANY, "%s: %s\n",
410 				op->o_log_prefix, buf, 0 );
411 		}
412 
413 		assert( op2.orm_modlist == &mod[ mcnt ] );
414 		assert( mcnt == 0 || op2.orm_modlist->sml_next == &mod[ 0 ] );
415 		ml = op2.orm_modlist->sml_next;
416 		if ( mcnt == 1 ) {
417 			assert( ml == &mod[ 0 ] );
418 			ml = ml->sml_next;
419 		}
420 		if ( ml != NULL ) {
421 			slap_mods_free( ml, 1 );
422 		}
423 
424 		mod[ 0 ].sml_next = NULL;
425 	}
426 
427 	if ( old_ndn != NULL ) {
428 		assert( !BER_BVISNULL( old_dn ) );
429 		assert( !BER_BVISNULL( old_ndn ) );
430 
431 		ml = &mod[ mcnt ];
432 		ml->sml_op = LDAP_MOD_DELETE;
433 
434 		ml->sml_values[ 0 ] = *old_dn;
435 		ml->sml_nvalues[ 0 ] = *old_ndn;
436 
437 		(void)op->o_bd->be_modify( &op2, &rs2 );
438 		if ( rs2.sr_err != LDAP_SUCCESS ) {
439 			char buf[ SLAP_TEXT_BUFLEN ];
440 			snprintf( buf, sizeof( buf ),
441 				"memberof_value_modify %s=\"%s\" failed err=%d text=%s",
442 				ad->ad_cname.bv_val, old_dn->bv_val, rs2.sr_err,
443 				rs2.sr_text ? rs2.sr_text : "" );
444 			Debug( LDAP_DEBUG_ANY, "%s: %s\n",
445 				op->o_log_prefix, buf, 0 );
446 		}
447 
448 		assert( op2.orm_modlist == &mod[ mcnt ] );
449 		ml = op2.orm_modlist->sml_next;
450 		if ( mcnt == 1 ) {
451 			assert( ml == &mod[ 0 ] );
452 			ml = ml->sml_next;
453 		}
454 		if ( ml != NULL ) {
455 			slap_mods_free( ml, 1 );
456 		}
457 	}
458 
459 	/* FIXME: if old_group_ndn doesn't exist, both delete __and__
460 	 * add will fail; better split in two operations, although
461 	 * not optimal in terms of performance.  At least it would
462 	 * move towards self-repairing capabilities. */
463 
464 	return rs2.sr_err;
465 }
466 
467 static int
468 memberof_cleanup( Operation *op, SlapReply *rs )
469 {
470 	slap_callback *sc = op->o_callback;
471 	memberof_cbinfo_t *mci = sc->sc_private;
472 
473 	op->o_callback = sc->sc_next;
474 	if ( mci->memberof )
475 		ber_bvarray_free_x( mci->memberof, op->o_tmpmemctx );
476 	if ( mci->member )
477 		ber_bvarray_free_x( mci->member, op->o_tmpmemctx );
478 	op->o_tmpfree( sc, op->o_tmpmemctx );
479 	return 0;
480 }
481 
482 static int memberof_res_add( Operation *op, SlapReply *rs );
483 static int memberof_res_delete( Operation *op, SlapReply *rs );
484 static int memberof_res_modify( Operation *op, SlapReply *rs );
485 static int memberof_res_modrdn( Operation *op, SlapReply *rs );
486 
487 static int
488 memberof_op_add( Operation *op, SlapReply *rs )
489 {
490 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
491 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
492 
493 	Attribute	**ap, **map = NULL;
494 	int		rc = SLAP_CB_CONTINUE;
495 	int		i;
496 	struct berval	save_dn, save_ndn;
497 	slap_callback *sc;
498 	memberof_cbinfo_t *mci;
499 
500 	if ( op->ora_e->e_attrs == NULL ) {
501 		/* FIXME: global overlay; need to deal with */
502 		Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
503 			"consistency checks not implemented when overlay "
504 			"is instantiated as global.\n",
505 			op->o_log_prefix, op->o_req_dn.bv_val, 0 );
506 		return SLAP_CB_CONTINUE;
507 	}
508 
509 	if ( MEMBEROF_REVERSE( mo ) ) {
510 		for ( ap = &op->ora_e->e_attrs; *ap; ap = &(*ap)->a_next ) {
511 			Attribute	*a = *ap;
512 
513 			if ( a->a_desc == mo->mo_ad_memberof ) {
514 				map = ap;
515 				break;
516 			}
517 		}
518 	}
519 
520 	save_dn = op->o_dn;
521 	save_ndn = op->o_ndn;
522 
523 	if ( MEMBEROF_DANGLING_CHECK( mo )
524 			&& !get_relax( op )
525 			&& is_entry_objectclass_or_sub( op->ora_e, mo->mo_oc_group ) )
526 	{
527 		op->o_dn = op->o_bd->be_rootdn;
528 		op->o_ndn = op->o_bd->be_rootndn;
529 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
530 
531 		for ( ap = &op->ora_e->e_attrs; *ap; ) {
532 			Attribute	*a = *ap;
533 
534 			if ( !is_ad_subtype( a->a_desc, mo->mo_ad_member ) ) {
535 				ap = &a->a_next;
536 				continue;
537 			}
538 
539 			assert( a->a_nvals != NULL );
540 
541 			for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
542 				Entry		*e = NULL;
543 
544 				rc = be_entry_get_rw( op, &a->a_nvals[ i ],
545 						NULL, NULL, 0, &e );
546 				if ( rc == LDAP_SUCCESS ) {
547 					be_entry_release_r( op, e );
548 					continue;
549 				}
550 
551 				if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
552 					rc = rs->sr_err = mo->mo_dangling_err;
553 					rs->sr_text = "adding non-existing object "
554 						"as group member";
555 					send_ldap_result( op, rs );
556 					goto done;
557 				}
558 
559 				if ( MEMBEROF_DANGLING_DROP( mo ) ) {
560 					int	j;
561 
562 					Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
563 						"member=\"%s\" does not exist (stripping...)\n",
564 						op->o_log_prefix, op->ora_e->e_name.bv_val,
565 						a->a_vals[ i ].bv_val );
566 
567 					for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
568 					ber_memfree( a->a_vals[ i ].bv_val );
569 					BER_BVZERO( &a->a_vals[ i ] );
570 					if ( a->a_nvals != a->a_vals ) {
571 						ber_memfree( a->a_nvals[ i ].bv_val );
572 						BER_BVZERO( &a->a_nvals[ i ] );
573 					}
574 					if ( j - i == 1 ) {
575 						break;
576 					}
577 
578 					AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
579 						sizeof( struct berval ) * ( j - i ) );
580 					if ( a->a_nvals != a->a_vals ) {
581 						AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
582 							sizeof( struct berval ) * ( j - i ) );
583 					}
584 					i--;
585 				}
586 			}
587 
588 			/* If all values have been removed,
589 			 * remove the attribute itself. */
590 			if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
591 				*ap = a->a_next;
592 				attr_free( a );
593 
594 			} else {
595 				ap = &a->a_next;
596 			}
597 		}
598 		op->o_dn = save_dn;
599 		op->o_ndn = save_ndn;
600 		op->o_bd->bd_info = (BackendInfo *)on;
601 	}
602 
603 	if ( map != NULL ) {
604 		Attribute		*a = *map;
605 		AccessControlState	acl_state = ACL_STATE_INIT;
606 
607 		for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
608 			Entry		*e;
609 
610 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
611 			/* access is checked with the original identity */
612 			rc = access_allowed( op, op->ora_e, mo->mo_ad_memberof,
613 					&a->a_nvals[ i ], ACL_WADD,
614 					&acl_state );
615 			if ( rc == 0 ) {
616 				rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
617 				rs->sr_text = NULL;
618 				send_ldap_result( op, rs );
619 				goto done;
620 			}
621 			rc = be_entry_get_rw( op, &a->a_nvals[ i ],
622 					NULL, NULL, 0, &e );
623 			op->o_bd->bd_info = (BackendInfo *)on;
624 			if ( rc != LDAP_SUCCESS ) {
625 				if ( get_relax( op ) ) {
626 					continue;
627 				}
628 
629 				if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
630 					rc = rs->sr_err = mo->mo_dangling_err;
631 					rs->sr_text = "adding non-existing object "
632 						"as memberof";
633 					send_ldap_result( op, rs );
634 					goto done;
635 				}
636 
637 				if ( MEMBEROF_DANGLING_DROP( mo ) ) {
638 					int	j;
639 
640 					Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
641 						"memberof=\"%s\" does not exist (stripping...)\n",
642 						op->o_log_prefix, op->ora_e->e_name.bv_val,
643 						a->a_nvals[ i ].bv_val );
644 
645 					for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
646 					ber_memfree( a->a_vals[ i ].bv_val );
647 					BER_BVZERO( &a->a_vals[ i ] );
648 					if ( a->a_nvals != a->a_vals ) {
649 						ber_memfree( a->a_nvals[ i ].bv_val );
650 						BER_BVZERO( &a->a_nvals[ i ] );
651 					}
652 					if ( j - i == 1 ) {
653 						break;
654 					}
655 
656 					AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
657 						sizeof( struct berval ) * ( j - i ) );
658 					if ( a->a_nvals != a->a_vals ) {
659 						AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
660 							sizeof( struct berval ) * ( j - i ) );
661 					}
662 					i--;
663 				}
664 
665 				continue;
666 			}
667 
668 			/* access is checked with the original identity */
669 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
670 			rc = access_allowed( op, e, mo->mo_ad_member,
671 					&op->o_req_ndn, ACL_WADD, NULL );
672 			be_entry_release_r( op, e );
673 			op->o_bd->bd_info = (BackendInfo *)on;
674 
675 			if ( !rc ) {
676 				rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
677 				rs->sr_text = "insufficient access to object referenced by memberof";
678 				send_ldap_result( op, rs );
679 				goto done;
680 			}
681 		}
682 
683 		if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
684 			*map = a->a_next;
685 			attr_free( a );
686 		}
687 	}
688 
689 	rc = SLAP_CB_CONTINUE;
690 
691 	sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
692 	sc->sc_private = sc+1;
693 	sc->sc_response = memberof_res_add;
694 	sc->sc_cleanup = memberof_cleanup;
695 	mci = sc->sc_private;
696 	mci->on = on;
697 	mci->member = NULL;
698 	mci->memberof = NULL;
699 	sc->sc_next = op->o_callback;
700 	op->o_callback = sc;
701 
702 done:;
703 	op->o_dn = save_dn;
704 	op->o_ndn = save_ndn;
705 	op->o_bd->bd_info = (BackendInfo *)on;
706 
707 	return rc;
708 }
709 
710 static int
711 memberof_op_delete( Operation *op, SlapReply *rs )
712 {
713 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
714 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
715 
716 	slap_callback *sc;
717 	memberof_cbinfo_t *mci;
718 
719 
720 	sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
721 	sc->sc_private = sc+1;
722 	sc->sc_response = memberof_res_delete;
723 	sc->sc_cleanup = memberof_cleanup;
724 	mci = sc->sc_private;
725 	mci->on = on;
726 	mci->member = NULL;
727 	mci->memberof = NULL;
728 	mci->what = MEMBEROF_IS_GROUP;
729 	if ( MEMBEROF_REFINT( mo ) ) {
730 		mci->what = MEMBEROF_IS_BOTH;
731 	}
732 
733 	memberof_isGroupOrMember( op, mci );
734 
735 	sc->sc_next = op->o_callback;
736 	op->o_callback = sc;
737 
738 	return SLAP_CB_CONTINUE;
739 }
740 
741 static int
742 memberof_op_modify( Operation *op, SlapReply *rs )
743 {
744 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
745 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
746 
747 	Modifications	**mlp, **mmlp = NULL;
748 	int		rc = SLAP_CB_CONTINUE, save_member = 0;
749 	struct berval	save_dn, save_ndn;
750 	slap_callback *sc;
751 	memberof_cbinfo_t *mci, mcis;
752 
753 	if ( MEMBEROF_REVERSE( mo ) ) {
754 		for ( mlp = &op->orm_modlist; *mlp; mlp = &(*mlp)->sml_next ) {
755 			Modifications	*ml = *mlp;
756 
757 			if ( ml->sml_desc == mo->mo_ad_memberof ) {
758 				mmlp = mlp;
759 				break;
760 			}
761 		}
762 	}
763 
764 	save_dn = op->o_dn;
765 	save_ndn = op->o_ndn;
766 	mcis.on = on;
767 	mcis.what = MEMBEROF_IS_GROUP;
768 
769 	if ( memberof_isGroupOrMember( op, &mcis ) == LDAP_SUCCESS
770 		&& ( mcis.what & MEMBEROF_IS_GROUP ) )
771 	{
772 		Modifications *ml;
773 
774 		for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
775 			if ( ml->sml_desc == mo->mo_ad_member ) {
776 				switch ( ml->sml_op ) {
777 				case LDAP_MOD_DELETE:
778 				case LDAP_MOD_REPLACE:
779 					save_member = 1;
780 					break;
781 				}
782 			}
783 		}
784 
785 
786 		if ( MEMBEROF_DANGLING_CHECK( mo )
787 				&& !get_relax( op ) )
788 		{
789 			op->o_dn = op->o_bd->be_rootdn;
790 			op->o_ndn = op->o_bd->be_rootndn;
791 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
792 
793 			assert( op->orm_modlist != NULL );
794 
795 			for ( mlp = &op->orm_modlist; *mlp; ) {
796 				Modifications	*ml = *mlp;
797 				int		i;
798 
799 				if ( !is_ad_subtype( ml->sml_desc, mo->mo_ad_member ) ) {
800 					mlp = &ml->sml_next;
801 					continue;
802 				}
803 
804 				switch ( ml->sml_op ) {
805 				case LDAP_MOD_DELETE:
806 					/* we don't care about cancellations: if the value
807 					 * exists, fine; if it doesn't, we let the underlying
808 					 * database fail as appropriate; */
809 					mlp = &ml->sml_next;
810 					break;
811 
812 				case LDAP_MOD_REPLACE:
813  					/* Handle this just like a delete (see above) */
814  					if ( !ml->sml_values ) {
815  						mlp = &ml->sml_next;
816  						break;
817  					}
818 
819 				case LDAP_MOD_ADD:
820 					/* NOTE: right now, the attributeType we use
821 					 * for member must have a normalized value */
822 					assert( ml->sml_nvalues != NULL );
823 
824 					for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
825 						int		rc;
826 						Entry		*e;
827 
828 						if ( be_entry_get_rw( op, &ml->sml_nvalues[ i ],
829 								NULL, NULL, 0, &e ) == LDAP_SUCCESS )
830 						{
831 							be_entry_release_r( op, e );
832 							continue;
833 						}
834 
835 						if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
836 							rc = rs->sr_err = mo->mo_dangling_err;
837 							rs->sr_text = "adding non-existing object "
838 								"as group member";
839 							send_ldap_result( op, rs );
840 							goto done;
841 						}
842 
843 						if ( MEMBEROF_DANGLING_DROP( mo ) ) {
844 							int	j;
845 
846 							Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
847 								"member=\"%s\" does not exist (stripping...)\n",
848 								op->o_log_prefix, op->o_req_dn.bv_val,
849 								ml->sml_nvalues[ i ].bv_val );
850 
851 							for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
852 							ber_memfree( ml->sml_values[ i ].bv_val );
853 							BER_BVZERO( &ml->sml_values[ i ] );
854 							ber_memfree( ml->sml_nvalues[ i ].bv_val );
855 							BER_BVZERO( &ml->sml_nvalues[ i ] );
856 							ml->sml_numvals--;
857 							if ( j - i == 1 ) {
858 								break;
859 							}
860 
861 							AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
862 								sizeof( struct berval ) * ( j - i ) );
863 							AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
864 								sizeof( struct berval ) * ( j - i ) );
865 							i--;
866 						}
867 					}
868 
869 					if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
870 						*mlp = ml->sml_next;
871 						slap_mod_free( &ml->sml_mod, 0 );
872 						free( ml );
873 
874 					} else {
875 						mlp = &ml->sml_next;
876 					}
877 
878 					break;
879 
880 				default:
881 					assert( 0 );
882 				}
883 			}
884 		}
885 	}
886 
887 	if ( mmlp != NULL ) {
888 		Modifications	*ml = *mmlp;
889 		int		i;
890 		Entry		*target;
891 
892 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
893 		rc = be_entry_get_rw( op, &op->o_req_ndn,
894 				NULL, NULL, 0, &target );
895 		op->o_bd->bd_info = (BackendInfo *)on;
896 		if ( rc != LDAP_SUCCESS ) {
897 			rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
898 			send_ldap_result( op, rs );
899 			goto done;
900 		}
901 
902 		switch ( ml->sml_op ) {
903 		case LDAP_MOD_DELETE:
904 			if ( ml->sml_nvalues != NULL ) {
905 				AccessControlState	acl_state = ACL_STATE_INIT;
906 
907 				for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
908 					Entry		*e;
909 
910 					op->o_bd->bd_info = (BackendInfo *)on->on_info;
911 					/* access is checked with the original identity */
912 					rc = access_allowed( op, target,
913 							mo->mo_ad_memberof,
914 							&ml->sml_nvalues[ i ],
915 							ACL_WDEL,
916 							&acl_state );
917 					if ( rc == 0 ) {
918 						rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
919 						rs->sr_text = NULL;
920 						send_ldap_result( op, rs );
921 						goto done2;
922 					}
923 
924 					rc = be_entry_get_rw( op, &ml->sml_nvalues[ i ],
925 							NULL, NULL, 0, &e );
926 					op->o_bd->bd_info = (BackendInfo *)on;
927 					if ( rc != LDAP_SUCCESS ) {
928 						if ( get_relax( op ) ) {
929 							continue;
930 						}
931 
932 						if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
933 							rc = rs->sr_err = mo->mo_dangling_err;
934 							rs->sr_text = "deleting non-existing object "
935 								"as memberof";
936 							send_ldap_result( op, rs );
937 							goto done2;
938 						}
939 
940 						if ( MEMBEROF_DANGLING_DROP( mo ) ) {
941 							int	j;
942 
943 							Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
944 								"memberof=\"%s\" does not exist (stripping...)\n",
945 								op->o_log_prefix, op->o_req_ndn.bv_val,
946 								ml->sml_nvalues[ i ].bv_val );
947 
948 							for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
949 							ber_memfree( ml->sml_values[ i ].bv_val );
950 							BER_BVZERO( &ml->sml_values[ i ] );
951 							if ( ml->sml_nvalues != ml->sml_values ) {
952 								ber_memfree( ml->sml_nvalues[ i ].bv_val );
953 								BER_BVZERO( &ml->sml_nvalues[ i ] );
954 							}
955 							ml->sml_numvals--;
956 							if ( j - i == 1 ) {
957 								break;
958 							}
959 
960 							AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
961 								sizeof( struct berval ) * ( j - i ) );
962 							if ( ml->sml_nvalues != ml->sml_values ) {
963 								AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
964 									sizeof( struct berval ) * ( j - i ) );
965 							}
966 							i--;
967 						}
968 
969 						continue;
970 					}
971 
972 					/* access is checked with the original identity */
973 					op->o_bd->bd_info = (BackendInfo *)on->on_info;
974 					rc = access_allowed( op, e, mo->mo_ad_member,
975 							&op->o_req_ndn,
976 							ACL_WDEL, NULL );
977 					be_entry_release_r( op, e );
978 					op->o_bd->bd_info = (BackendInfo *)on;
979 
980 					if ( !rc ) {
981 						rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
982 						rs->sr_text = "insufficient access to object referenced by memberof";
983 						send_ldap_result( op, rs );
984 						goto done;
985 					}
986 				}
987 
988 				if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
989 					*mmlp = ml->sml_next;
990 					slap_mod_free( &ml->sml_mod, 0 );
991 					free( ml );
992 				}
993 
994 				break;
995 			}
996 			/* fall thru */
997 
998 		case LDAP_MOD_REPLACE:
999 
1000 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
1001 			/* access is checked with the original identity */
1002 			rc = access_allowed( op, target,
1003 					mo->mo_ad_memberof,
1004 					NULL,
1005 					ACL_WDEL, NULL );
1006 			op->o_bd->bd_info = (BackendInfo *)on;
1007 			if ( rc == 0 ) {
1008 				rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1009 				rs->sr_text = NULL;
1010 				send_ldap_result( op, rs );
1011 				goto done2;
1012 			}
1013 
1014 			if ( ml->sml_op == LDAP_MOD_DELETE || !ml->sml_values ) {
1015 				break;
1016 			}
1017 			/* fall thru */
1018 
1019 		case LDAP_MOD_ADD: {
1020 			AccessControlState	acl_state = ACL_STATE_INIT;
1021 
1022 			for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
1023 				Entry		*e;
1024 
1025 				op->o_bd->bd_info = (BackendInfo *)on->on_info;
1026 				/* access is checked with the original identity */
1027 				rc = access_allowed( op, target,
1028 						mo->mo_ad_memberof,
1029 						&ml->sml_nvalues[ i ],
1030 						ACL_WADD,
1031 						&acl_state );
1032 				if ( rc == 0 ) {
1033 					rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1034 					rs->sr_text = NULL;
1035 					send_ldap_result( op, rs );
1036 					goto done2;
1037 				}
1038 
1039 				rc = be_entry_get_rw( op, &ml->sml_nvalues[ i ],
1040 						NULL, NULL, 0, &e );
1041 				op->o_bd->bd_info = (BackendInfo *)on;
1042 				if ( rc != LDAP_SUCCESS ) {
1043 					if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
1044 						rc = rs->sr_err = mo->mo_dangling_err;
1045 						rs->sr_text = "adding non-existing object "
1046 							"as memberof";
1047 						send_ldap_result( op, rs );
1048 						goto done2;
1049 					}
1050 
1051 					if ( MEMBEROF_DANGLING_DROP( mo ) ) {
1052 						int	j;
1053 
1054 						Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
1055 							"memberof=\"%s\" does not exist (stripping...)\n",
1056 							op->o_log_prefix, op->o_req_ndn.bv_val,
1057 							ml->sml_nvalues[ i ].bv_val );
1058 
1059 						for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
1060 						ber_memfree( ml->sml_values[ i ].bv_val );
1061 						BER_BVZERO( &ml->sml_values[ i ] );
1062 						if ( ml->sml_nvalues != ml->sml_values ) {
1063 							ber_memfree( ml->sml_nvalues[ i ].bv_val );
1064 							BER_BVZERO( &ml->sml_nvalues[ i ] );
1065 						}
1066 						ml->sml_numvals--;
1067 						if ( j - i == 1 ) {
1068 							break;
1069 						}
1070 
1071 						AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
1072 							sizeof( struct berval ) * ( j - i ) );
1073 						if ( ml->sml_nvalues != ml->sml_values ) {
1074 							AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
1075 								sizeof( struct berval ) * ( j - i ) );
1076 						}
1077 						i--;
1078 					}
1079 
1080 					continue;
1081 				}
1082 
1083 				/* access is checked with the original identity */
1084 				op->o_bd->bd_info = (BackendInfo *)on->on_info;
1085 				rc = access_allowed( op, e, mo->mo_ad_member,
1086 						&op->o_req_ndn,
1087 						ACL_WDEL, NULL );
1088 				be_entry_release_r( op, e );
1089 				op->o_bd->bd_info = (BackendInfo *)on;
1090 
1091 				if ( !rc ) {
1092 					rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1093 					rs->sr_text = "insufficient access to object referenced by memberof";
1094 					send_ldap_result( op, rs );
1095 					goto done;
1096 				}
1097 			}
1098 
1099 			if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
1100 				*mmlp = ml->sml_next;
1101 				slap_mod_free( &ml->sml_mod, 0 );
1102 				free( ml );
1103 			}
1104 
1105 			} break;
1106 
1107 		default:
1108 			assert( 0 );
1109 		}
1110 
1111 done2:;
1112 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1113 		be_entry_release_r( op, target );
1114 		op->o_bd->bd_info = (BackendInfo *)on;
1115 	}
1116 
1117 	sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
1118 	sc->sc_private = sc+1;
1119 	sc->sc_response = memberof_res_modify;
1120 	sc->sc_cleanup = memberof_cleanup;
1121 	mci = sc->sc_private;
1122 	mci->on = on;
1123 	mci->member = NULL;
1124 	mci->memberof = NULL;
1125 	mci->what = mcis.what;
1126 
1127 	if ( save_member ) {
1128 		op->o_dn = op->o_bd->be_rootdn;
1129 		op->o_ndn = op->o_bd->be_rootndn;
1130 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1131 		rc = backend_attribute( op, NULL, &op->o_req_ndn,
1132 				mo->mo_ad_member, &mci->member, ACL_READ );
1133 		op->o_bd->bd_info = (BackendInfo *)on;
1134 	}
1135 
1136 	sc->sc_next = op->o_callback;
1137 	op->o_callback = sc;
1138 
1139 	rc = SLAP_CB_CONTINUE;
1140 
1141 done:;
1142 	op->o_dn = save_dn;
1143 	op->o_ndn = save_ndn;
1144 	op->o_bd->bd_info = (BackendInfo *)on;
1145 
1146 	return rc;
1147 }
1148 
1149 static int
1150 memberof_op_modrdn( Operation *op, SlapReply *rs )
1151 {
1152 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
1153 	slap_callback *sc;
1154 	memberof_cbinfo_t *mci;
1155 
1156 	sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
1157 	sc->sc_private = sc+1;
1158 	sc->sc_response = memberof_res_modrdn;
1159 	sc->sc_cleanup = memberof_cleanup;
1160 	mci = sc->sc_private;
1161 	mci->on = on;
1162 	mci->member = NULL;
1163 	mci->memberof = NULL;
1164 
1165 	sc->sc_next = op->o_callback;
1166 	op->o_callback = sc;
1167 
1168 	return SLAP_CB_CONTINUE;
1169 }
1170 
1171 /*
1172  * response callback that adds memberof values when a group is added.
1173  */
1174 static int
1175 memberof_res_add( Operation *op, SlapReply *rs )
1176 {
1177 	memberof_cbinfo_t *mci = op->o_callback->sc_private;
1178 	slap_overinst	*on = mci->on;
1179 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1180 
1181 	int		i;
1182 
1183 	if ( rs->sr_err != LDAP_SUCCESS ) {
1184 		return SLAP_CB_CONTINUE;
1185 	}
1186 
1187 	if ( MEMBEROF_REVERSE( mo ) ) {
1188 		Attribute	*ma;
1189 
1190 		ma = attr_find( op->ora_e->e_attrs, mo->mo_ad_memberof );
1191 		if ( ma != NULL ) {
1192 			char relax = op->o_relax;
1193 
1194 			/* relax is required to allow to add
1195 			 * a non-existing member */
1196 			op->o_relax = SLAP_CONTROL_CRITICAL;
1197 
1198 			for ( i = 0; !BER_BVISNULL( &ma->a_nvals[ i ] ); i++ ) {
1199 
1200 				/* the modification is attempted
1201 				 * with the original identity */
1202 				(void)memberof_value_modify( op, rs,
1203 					&ma->a_nvals[ i ], mo->mo_ad_member,
1204 					NULL, NULL, &op->o_req_dn, &op->o_req_ndn );
1205 			}
1206 		}
1207 	}
1208 
1209 	if ( is_entry_objectclass_or_sub( op->ora_e, mo->mo_oc_group ) ) {
1210 		Attribute	*a;
1211 
1212 		for ( a = attrs_find( op->ora_e->e_attrs, mo->mo_ad_member );
1213 				a != NULL;
1214 				a = attrs_find( a->a_next, mo->mo_ad_member ) )
1215 		{
1216 			for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
1217 				(void)memberof_value_modify( op, rs,
1218 						&a->a_nvals[ i ],
1219 						mo->mo_ad_memberof,
1220 						NULL, NULL,
1221 						&op->o_req_dn,
1222 						&op->o_req_ndn );
1223 			}
1224 		}
1225 	}
1226 
1227 	return SLAP_CB_CONTINUE;
1228 }
1229 
1230 /*
1231  * response callback that deletes memberof values when a group is deleted.
1232  */
1233 static int
1234 memberof_res_delete( Operation *op, SlapReply *rs )
1235 {
1236 	memberof_cbinfo_t *mci = op->o_callback->sc_private;
1237 	slap_overinst	*on = mci->on;
1238 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1239 
1240  	BerVarray	vals;
1241 	int		i;
1242 
1243 	if ( rs->sr_err != LDAP_SUCCESS ) {
1244 		return SLAP_CB_CONTINUE;
1245 	}
1246 
1247 	vals = mci->member;
1248 	if ( vals != NULL ) {
1249 		for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1250 			(void)memberof_value_modify( op, rs,
1251 					&vals[ i ], mo->mo_ad_memberof,
1252 					&op->o_req_dn, &op->o_req_ndn,
1253 					NULL, NULL );
1254 		}
1255 	}
1256 
1257 	if ( MEMBEROF_REFINT( mo ) ) {
1258 		vals = mci->memberof;
1259 		if ( vals != NULL ) {
1260 			for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1261 				(void)memberof_value_modify( op, rs,
1262 						&vals[ i ], mo->mo_ad_member,
1263 						&op->o_req_dn, &op->o_req_ndn,
1264 						NULL, NULL );
1265 			}
1266 		}
1267 	}
1268 
1269 	return SLAP_CB_CONTINUE;
1270 }
1271 
1272 /*
1273  * response callback that adds/deletes memberof values when a group
1274  * is modified.
1275  */
1276 static int
1277 memberof_res_modify( Operation *op, SlapReply *rs )
1278 {
1279 	memberof_cbinfo_t *mci = op->o_callback->sc_private;
1280 	slap_overinst	*on = mci->on;
1281 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1282 
1283 	int		i, rc;
1284 	Modifications	*ml, *mml = NULL;
1285 	BerVarray	vals;
1286 
1287 	if ( rs->sr_err != LDAP_SUCCESS ) {
1288 		return SLAP_CB_CONTINUE;
1289 	}
1290 
1291 	if ( MEMBEROF_REVERSE( mo ) ) {
1292 		for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
1293 			if ( ml->sml_desc == mo->mo_ad_memberof ) {
1294 				mml = ml;
1295 				break;
1296 			}
1297 		}
1298 	}
1299 
1300 	if ( mml != NULL ) {
1301 		BerVarray	vals = mml->sml_nvalues;
1302 
1303 		switch ( mml->sml_op ) {
1304 		case LDAP_MOD_DELETE:
1305 			if ( vals != NULL ) {
1306 				for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1307 					memberof_value_modify( op, rs,
1308 							&vals[ i ], mo->mo_ad_member,
1309 							&op->o_req_dn, &op->o_req_ndn,
1310 							NULL, NULL );
1311 				}
1312 				break;
1313 			}
1314 			/* fall thru */
1315 
1316 		case LDAP_MOD_REPLACE:
1317 			/* delete all ... */
1318 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
1319 			rc = backend_attribute( op, NULL, &op->o_req_ndn,
1320 					mo->mo_ad_memberof, &vals, ACL_READ );
1321 			op->o_bd->bd_info = (BackendInfo *)on;
1322 			if ( rc == LDAP_SUCCESS ) {
1323 				for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1324 					(void)memberof_value_modify( op, rs,
1325 							&vals[ i ], mo->mo_ad_member,
1326 							&op->o_req_dn, &op->o_req_ndn,
1327 							NULL, NULL );
1328 				}
1329 				ber_bvarray_free_x( vals, op->o_tmpmemctx );
1330 			}
1331 
1332 			if ( ml->sml_op == LDAP_MOD_DELETE || !mml->sml_values ) {
1333 				break;
1334 			}
1335 			/* fall thru */
1336 
1337 		case LDAP_MOD_ADD:
1338 			assert( vals != NULL );
1339 
1340 			for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1341 				memberof_value_modify( op, rs,
1342 						&vals[ i ], mo->mo_ad_member,
1343 						NULL, NULL,
1344 						&op->o_req_dn, &op->o_req_ndn );
1345 			}
1346 			break;
1347 
1348 		default:
1349 			assert( 0 );
1350 		}
1351 	}
1352 
1353 	if ( mci->what & MEMBEROF_IS_GROUP )
1354 	{
1355 		for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
1356 			if ( ml->sml_desc != mo->mo_ad_member ) {
1357 				continue;
1358 			}
1359 
1360 			switch ( ml->sml_op ) {
1361 			case LDAP_MOD_DELETE:
1362 				vals = ml->sml_nvalues;
1363 				if ( vals != NULL ) {
1364 					for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1365 						memberof_value_modify( op, rs,
1366 								&vals[ i ], mo->mo_ad_memberof,
1367 								&op->o_req_dn, &op->o_req_ndn,
1368 								NULL, NULL );
1369 					}
1370 					break;
1371 				}
1372 				/* fall thru */
1373 
1374 			case LDAP_MOD_REPLACE:
1375 				vals = mci->member;
1376 
1377 				/* delete all ... */
1378 				if ( vals != NULL ) {
1379 					for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1380 						(void)memberof_value_modify( op, rs,
1381 								&vals[ i ], mo->mo_ad_memberof,
1382 								&op->o_req_dn, &op->o_req_ndn,
1383 								NULL, NULL );
1384 					}
1385 				}
1386 
1387 				if ( ml->sml_op == LDAP_MOD_DELETE || !ml->sml_values ) {
1388 					break;
1389 				}
1390 				/* fall thru */
1391 
1392 			case LDAP_MOD_ADD:
1393 				assert( ml->sml_nvalues != NULL );
1394 				vals = ml->sml_nvalues;
1395 				for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1396 					memberof_value_modify( op, rs,
1397 							&vals[ i ], mo->mo_ad_memberof,
1398 							NULL, NULL,
1399 							&op->o_req_dn, &op->o_req_ndn );
1400 				}
1401 				break;
1402 
1403 			default:
1404 				assert( 0 );
1405 			}
1406 		}
1407 	}
1408 
1409 	return SLAP_CB_CONTINUE;
1410 }
1411 
1412 /*
1413  * response callback that adds/deletes member values when a group member
1414  * is renamed.
1415  */
1416 static int
1417 memberof_res_modrdn( Operation *op, SlapReply *rs )
1418 {
1419 	memberof_cbinfo_t *mci = op->o_callback->sc_private;
1420 	slap_overinst	*on = mci->on;
1421 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1422 
1423 	struct berval	newPDN, newDN = BER_BVNULL, newPNDN, newNDN;
1424 	int		i, rc;
1425 	BerVarray	vals;
1426 
1427 	struct berval	save_dn, save_ndn;
1428 
1429 	if ( rs->sr_err != LDAP_SUCCESS ) {
1430 		return SLAP_CB_CONTINUE;
1431 	}
1432 
1433 	mci->what = MEMBEROF_IS_GROUP;
1434 	if ( MEMBEROF_REFINT( mo ) ) {
1435 		mci->what |= MEMBEROF_IS_MEMBER;
1436 	}
1437 
1438 	if ( op->orr_nnewSup ) {
1439 		newPNDN = *op->orr_nnewSup;
1440 
1441 	} else {
1442 		dnParent( &op->o_req_ndn, &newPNDN );
1443 	}
1444 
1445 	build_new_dn( &newNDN, &newPNDN, &op->orr_nnewrdn, op->o_tmpmemctx );
1446 
1447 	save_dn = op->o_req_dn;
1448 	save_ndn = op->o_req_ndn;
1449 
1450 	op->o_req_dn = newNDN;
1451 	op->o_req_ndn = newNDN;
1452 	rc = memberof_isGroupOrMember( op, mci );
1453 	op->o_req_dn = save_dn;
1454 	op->o_req_ndn = save_ndn;
1455 
1456 	if ( rc != LDAP_SUCCESS || mci->what == MEMBEROF_IS_NONE ) {
1457 		goto done;
1458 	}
1459 
1460 	if ( op->orr_newSup ) {
1461 		newPDN = *op->orr_newSup;
1462 
1463 	} else {
1464 		dnParent( &op->o_req_dn, &newPDN );
1465 	}
1466 
1467 	build_new_dn( &newDN, &newPDN, &op->orr_newrdn, op->o_tmpmemctx );
1468 
1469 	if ( mci->what & MEMBEROF_IS_GROUP ) {
1470 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1471 		rc = backend_attribute( op, NULL, &newNDN,
1472 				mo->mo_ad_member, &vals, ACL_READ );
1473 		op->o_bd->bd_info = (BackendInfo *)on;
1474 
1475 		if ( rc == LDAP_SUCCESS ) {
1476 			for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1477 				(void)memberof_value_modify( op, rs,
1478 						&vals[ i ], mo->mo_ad_memberof,
1479 						&op->o_req_dn, &op->o_req_ndn,
1480 						&newDN, &newNDN );
1481 			}
1482 			ber_bvarray_free_x( vals, op->o_tmpmemctx );
1483 		}
1484 	}
1485 
1486 	if ( MEMBEROF_REFINT( mo ) && ( mci->what & MEMBEROF_IS_MEMBER ) ) {
1487 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1488 		rc = backend_attribute( op, NULL, &newNDN,
1489 				mo->mo_ad_memberof, &vals, ACL_READ );
1490 		op->o_bd->bd_info = (BackendInfo *)on;
1491 
1492 		if ( rc == LDAP_SUCCESS ) {
1493 			for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1494 				(void)memberof_value_modify( op, rs,
1495 						&vals[ i ], mo->mo_ad_member,
1496 						&op->o_req_dn, &op->o_req_ndn,
1497 						&newDN, &newNDN );
1498 			}
1499 			ber_bvarray_free_x( vals, op->o_tmpmemctx );
1500 		}
1501 	}
1502 
1503 done:;
1504 	if ( !BER_BVISNULL( &newDN ) ) {
1505 		op->o_tmpfree( newDN.bv_val, op->o_tmpmemctx );
1506 	}
1507 	op->o_tmpfree( newNDN.bv_val, op->o_tmpmemctx );
1508 
1509 	return SLAP_CB_CONTINUE;
1510 }
1511 
1512 
1513 static int
1514 memberof_db_init(
1515 	BackendDB	*be,
1516 	ConfigReply	*cr )
1517 {
1518 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1519 	memberof_t		*mo;
1520 
1521 	mo = (memberof_t *)ch_calloc( 1, sizeof( memberof_t ) );
1522 
1523 	/* safe default */
1524 	mo->mo_dangling_err = LDAP_CONSTRAINT_VIOLATION;
1525 
1526 	on->on_bi.bi_private = (void *)mo;
1527 
1528 	return 0;
1529 }
1530 
1531 enum {
1532 	MO_DN = 1,
1533 	MO_DANGLING,
1534 	MO_REFINT,
1535 	MO_GROUP_OC,
1536 	MO_MEMBER_AD,
1537 	MO_MEMBER_OF_AD,
1538 #if 0
1539 	MO_REVERSE,
1540 #endif
1541 
1542 	MO_DANGLING_ERROR,
1543 
1544 	MO_LAST
1545 };
1546 
1547 static ConfigDriver mo_cf_gen;
1548 
1549 #define OID		"1.3.6.1.4.1.7136.2.666.4"
1550 #define	OIDAT		OID ".1.1"
1551 #define	OIDCFGAT	OID ".1.2"
1552 #define	OIDOC		OID ".2.1"
1553 #define	OIDCFGOC	OID ".2.2"
1554 
1555 
1556 static ConfigTable mo_cfg[] = {
1557 	{ "memberof-dn", "modifiersName",
1558 		2, 2, 0, ARG_MAGIC|ARG_DN|MO_DN, mo_cf_gen,
1559 		"( OLcfgOvAt:18.0 NAME 'olcMemberOfDN' "
1560 			"DESC 'DN to be used as modifiersName' "
1561 			"SYNTAX OMsDN SINGLE-VALUE )",
1562 		NULL, NULL },
1563 
1564 	{ "memberof-dangling", "ignore|drop|error",
1565 		2, 2, 0, ARG_MAGIC|MO_DANGLING, mo_cf_gen,
1566 		"( OLcfgOvAt:18.1 NAME 'olcMemberOfDangling' "
1567 			"DESC 'Behavior with respect to dangling members, "
1568 				"constrained to ignore, drop, error' "
1569 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1570 		NULL, NULL },
1571 
1572 	{ "memberof-refint", "true|FALSE",
1573 		2, 2, 0, ARG_MAGIC|ARG_ON_OFF|MO_REFINT, mo_cf_gen,
1574 		"( OLcfgOvAt:18.2 NAME 'olcMemberOfRefInt' "
1575 			"DESC 'Take care of referential integrity' "
1576 			"SYNTAX OMsBoolean SINGLE-VALUE )",
1577 		NULL, NULL },
1578 
1579 	{ "memberof-group-oc", "objectClass",
1580 		2, 2, 0, ARG_MAGIC|MO_GROUP_OC, mo_cf_gen,
1581 		"( OLcfgOvAt:18.3 NAME 'olcMemberOfGroupOC' "
1582 			"DESC 'Group objectClass' "
1583 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1584 		NULL, NULL },
1585 
1586 	{ "memberof-member-ad", "member attribute",
1587 		2, 2, 0, ARG_MAGIC|MO_MEMBER_AD, mo_cf_gen,
1588 		"( OLcfgOvAt:18.4 NAME 'olcMemberOfMemberAD' "
1589 			"DESC 'member attribute' "
1590 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1591 		NULL, NULL },
1592 
1593 	{ "memberof-memberof-ad", "memberOf attribute",
1594 		2, 2, 0, ARG_MAGIC|MO_MEMBER_OF_AD, mo_cf_gen,
1595 		"( OLcfgOvAt:18.5 NAME 'olcMemberOfMemberOfAD' "
1596 			"DESC 'memberOf attribute' "
1597 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1598 		NULL, NULL },
1599 
1600 #if 0
1601 	{ "memberof-reverse", "true|FALSE",
1602 		2, 2, 0, ARG_MAGIC|ARG_ON_OFF|MO_REVERSE, mo_cf_gen,
1603 		"( OLcfgOvAt:18.6 NAME 'olcMemberOfReverse' "
1604 			"DESC 'Take care of referential integrity "
1605 				"also when directly modifying memberOf' "
1606 			"SYNTAX OMsBoolean SINGLE-VALUE )",
1607 		NULL, NULL },
1608 #endif
1609 
1610 	{ "memberof-dangling-error", "error code",
1611 		2, 2, 0, ARG_MAGIC|MO_DANGLING_ERROR, mo_cf_gen,
1612 		"( OLcfgOvAt:18.7 NAME 'olcMemberOfDanglingError' "
1613 			"DESC 'Error code returned in case of dangling back reference' "
1614 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1615 		NULL, NULL },
1616 
1617 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1618 };
1619 
1620 static ConfigOCs mo_ocs[] = {
1621 	{ "( OLcfgOvOc:18.1 "
1622 		"NAME 'olcMemberOf' "
1623 		"DESC 'Member-of configuration' "
1624 		"SUP olcOverlayConfig "
1625 		"MAY ( "
1626 			"olcMemberOfDN "
1627 			"$ olcMemberOfDangling "
1628 			"$ olcMemberOfDanglingError"
1629 			"$ olcMemberOfRefInt "
1630 			"$ olcMemberOfGroupOC "
1631 			"$ olcMemberOfMemberAD "
1632 			"$ olcMemberOfMemberOfAD "
1633 #if 0
1634 			"$ olcMemberOfReverse "
1635 #endif
1636 			") "
1637 		")",
1638 		Cft_Overlay, mo_cfg, NULL, NULL },
1639 	{ NULL, 0, NULL }
1640 };
1641 
1642 static slap_verbmasks dangling_mode[] = {
1643 	{ BER_BVC( "ignore" ),		MEMBEROF_NONE },
1644 	{ BER_BVC( "drop" ),		MEMBEROF_FDANGLING_DROP },
1645 	{ BER_BVC( "error" ),		MEMBEROF_FDANGLING_ERROR },
1646 	{ BER_BVNULL,			0 }
1647 };
1648 
1649 static int
1650 memberof_make_group_filter( memberof_t *mo )
1651 {
1652 	char		*ptr;
1653 
1654 	if ( !BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
1655 		ch_free( mo->mo_groupFilterstr.bv_val );
1656 	}
1657 
1658 	mo->mo_groupFilter.f_choice = LDAP_FILTER_EQUALITY;
1659 	mo->mo_groupFilter.f_ava = &mo->mo_groupAVA;
1660 
1661 	mo->mo_groupFilter.f_av_desc = slap_schema.si_ad_objectClass;
1662 	mo->mo_groupFilter.f_av_value = mo->mo_oc_group->soc_cname;
1663 
1664 	mo->mo_groupFilterstr.bv_len = STRLENOF( "(=)" )
1665 		+ slap_schema.si_ad_objectClass->ad_cname.bv_len
1666 		+ mo->mo_oc_group->soc_cname.bv_len;
1667 	ptr = mo->mo_groupFilterstr.bv_val = ch_malloc( mo->mo_groupFilterstr.bv_len + 1 );
1668 	*ptr++ = '(';
1669 	ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1670 	*ptr++ = '=';
1671 	ptr = lutil_strcopy( ptr, mo->mo_oc_group->soc_cname.bv_val );
1672 	*ptr++ = ')';
1673 	*ptr = '\0';
1674 
1675 	return 0;
1676 }
1677 
1678 static int
1679 memberof_make_member_filter( memberof_t *mo )
1680 {
1681 	char		*ptr;
1682 
1683 	if ( !BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
1684 		ch_free( mo->mo_memberFilterstr.bv_val );
1685 	}
1686 
1687 	mo->mo_memberFilter.f_choice = LDAP_FILTER_PRESENT;
1688 	mo->mo_memberFilter.f_desc = mo->mo_ad_memberof;
1689 
1690 	mo->mo_memberFilterstr.bv_len = STRLENOF( "(=*)" )
1691 		+ mo->mo_ad_memberof->ad_cname.bv_len;
1692 	ptr = mo->mo_memberFilterstr.bv_val = ch_malloc( mo->mo_memberFilterstr.bv_len + 1 );
1693 	*ptr++ = '(';
1694 	ptr = lutil_strcopy( ptr, mo->mo_ad_memberof->ad_cname.bv_val );
1695 	ptr = lutil_strcopy( ptr, "=*)" );
1696 
1697 	return 0;
1698 }
1699 
1700 static int
1701 mo_cf_gen( ConfigArgs *c )
1702 {
1703 	slap_overinst	*on = (slap_overinst *)c->bi;
1704 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1705 
1706 	int		i, rc = 0;
1707 
1708 	if ( c->op == SLAP_CONFIG_EMIT ) {
1709 		struct berval bv = BER_BVNULL;
1710 
1711 		switch( c->type ) {
1712 		case MO_DN:
1713 			if ( mo->mo_dn.bv_val != NULL) {
1714 				value_add_one( &c->rvalue_vals, &mo->mo_dn );
1715 				value_add_one( &c->rvalue_nvals, &mo->mo_ndn );
1716 			}
1717 			break;
1718 
1719 		case MO_DANGLING:
1720 			enum_to_verb( dangling_mode, (mo->mo_flags & MEMBEROF_FDANGLING_MASK), &bv );
1721 			if ( BER_BVISNULL( &bv ) ) {
1722 				/* there's something wrong... */
1723 				assert( 0 );
1724 				rc = 1;
1725 
1726 			} else {
1727 				value_add_one( &c->rvalue_vals, &bv );
1728 			}
1729 			break;
1730 
1731 		case MO_DANGLING_ERROR:
1732 			if ( mo->mo_flags & MEMBEROF_FDANGLING_ERROR ) {
1733 				char buf[ SLAP_TEXT_BUFLEN ];
1734 				enum_to_verb( slap_ldap_response_code, mo->mo_dangling_err, &bv );
1735 				if ( BER_BVISNULL( &bv ) ) {
1736 					bv.bv_len = snprintf( buf, sizeof( buf ), "0x%x", mo->mo_dangling_err );
1737 					if ( bv.bv_len < sizeof( buf ) ) {
1738 						bv.bv_val = buf;
1739 					} else {
1740 						rc = 1;
1741 						break;
1742 					}
1743 				}
1744 				value_add_one( &c->rvalue_vals, &bv );
1745 			} else {
1746 				rc = 1;
1747 			}
1748 			break;
1749 
1750 		case MO_REFINT:
1751 			c->value_int = MEMBEROF_REFINT( mo );
1752 			break;
1753 
1754 #if 0
1755 		case MO_REVERSE:
1756 			c->value_int = MEMBEROF_REVERSE( mo );
1757 			break;
1758 #endif
1759 
1760 		case MO_GROUP_OC:
1761 			if ( mo->mo_oc_group != NULL ){
1762 				value_add_one( &c->rvalue_vals, &mo->mo_oc_group->soc_cname );
1763 			}
1764 			break;
1765 
1766 		case MO_MEMBER_AD:
1767 			if ( mo->mo_ad_member != NULL ){
1768 				value_add_one( &c->rvalue_vals, &mo->mo_ad_member->ad_cname );
1769 			}
1770 			break;
1771 
1772 		case MO_MEMBER_OF_AD:
1773 			if ( mo->mo_ad_memberof != NULL ){
1774 				value_add_one( &c->rvalue_vals, &mo->mo_ad_memberof->ad_cname );
1775 			}
1776 			break;
1777 
1778 		default:
1779 			assert( 0 );
1780 			return 1;
1781 		}
1782 
1783 		return rc;
1784 
1785 	} else if ( c->op == LDAP_MOD_DELETE ) {
1786 		return 1;	/* FIXME */
1787 
1788 	} else {
1789 		switch( c->type ) {
1790 		case MO_DN:
1791 			if ( !BER_BVISNULL( &mo->mo_dn ) ) {
1792 				ber_memfree( mo->mo_dn.bv_val );
1793 				ber_memfree( mo->mo_ndn.bv_val );
1794 			}
1795 			mo->mo_dn = c->value_dn;
1796 			mo->mo_ndn = c->value_ndn;
1797 			break;
1798 
1799 		case MO_DANGLING:
1800 			i = verb_to_mask( c->argv[ 1 ], dangling_mode );
1801 			if ( BER_BVISNULL( &dangling_mode[ i ].word ) ) {
1802 				return 1;
1803 			}
1804 
1805 			mo->mo_flags &= ~MEMBEROF_FDANGLING_MASK;
1806 			mo->mo_flags |= dangling_mode[ i ].mask;
1807 			break;
1808 
1809 		case MO_DANGLING_ERROR:
1810 			i = verb_to_mask( c->argv[ 1 ], slap_ldap_response_code );
1811 			if ( !BER_BVISNULL( &slap_ldap_response_code[ i ].word ) ) {
1812 				mo->mo_dangling_err = slap_ldap_response_code[ i ].mask;
1813 			} else if ( lutil_atoix( &mo->mo_dangling_err, c->argv[ 1 ], 0 ) ) {
1814 				return 1;
1815 			}
1816 			break;
1817 
1818 		case MO_REFINT:
1819 			if ( c->value_int ) {
1820 				mo->mo_flags |= MEMBEROF_FREFINT;
1821 
1822 			} else {
1823 				mo->mo_flags &= ~MEMBEROF_FREFINT;
1824 			}
1825 			break;
1826 
1827 #if 0
1828 		case MO_REVERSE:
1829 			if ( c->value_int ) {
1830 				mo->mo_flags |= MEMBEROF_FREVERSE;
1831 
1832 			} else {
1833 				mo->mo_flags &= ~MEMBEROF_FREVERSE;
1834 			}
1835 			break;
1836 #endif
1837 
1838 		case MO_GROUP_OC: {
1839 			ObjectClass	*oc = NULL;
1840 
1841 			oc = oc_find( c->argv[ 1 ] );
1842 			if ( oc == NULL ) {
1843 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1844 					"unable to find group objectClass=\"%s\"",
1845 					c->argv[ 1 ] );
1846 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
1847 					c->log, c->cr_msg, 0 );
1848 				return 1;
1849 			}
1850 
1851 			mo->mo_oc_group = oc;
1852 			memberof_make_group_filter( mo );
1853 			} break;
1854 
1855 		case MO_MEMBER_AD: {
1856 			AttributeDescription	*ad = NULL;
1857 			const char		*text = NULL;
1858 
1859 
1860 			rc = slap_str2ad( c->argv[ 1 ], &ad, &text );
1861 			if ( rc != LDAP_SUCCESS ) {
1862 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1863 					"unable to find member attribute=\"%s\": %s (%d)",
1864 					c->argv[ 1 ], text, rc );
1865 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
1866 					c->log, c->cr_msg, 0 );
1867 				return 1;
1868 			}
1869 
1870 			if ( !is_at_syntax( ad->ad_type, SLAPD_DN_SYNTAX )		/* e.g. "member" */
1871 				&& !is_at_syntax( ad->ad_type, SLAPD_NAMEUID_SYNTAX ) )	/* e.g. "uniqueMember" */
1872 			{
1873 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1874 					"member attribute=\"%s\" must either "
1875 					"have DN (%s) or nameUID (%s) syntax",
1876 					c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1877 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
1878 					c->log, c->cr_msg, 0 );
1879 				return 1;
1880 			}
1881 
1882 			mo->mo_ad_member = ad;
1883 			} break;
1884 
1885 		case MO_MEMBER_OF_AD: {
1886 			AttributeDescription	*ad = NULL;
1887 			const char		*text = NULL;
1888 
1889 
1890 			rc = slap_str2ad( c->argv[ 1 ], &ad, &text );
1891 			if ( rc != LDAP_SUCCESS ) {
1892 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1893 					"unable to find memberof attribute=\"%s\": %s (%d)",
1894 					c->argv[ 1 ], text, rc );
1895 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
1896 					c->log, c->cr_msg, 0 );
1897 				return 1;
1898 			}
1899 
1900 			if ( !is_at_syntax( ad->ad_type, SLAPD_DN_SYNTAX )		/* e.g. "member" */
1901 				&& !is_at_syntax( ad->ad_type, SLAPD_NAMEUID_SYNTAX ) )	/* e.g. "uniqueMember" */
1902 			{
1903 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1904 					"memberof attribute=\"%s\" must either "
1905 					"have DN (%s) or nameUID (%s) syntax",
1906 					c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1907 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
1908 					c->log, c->cr_msg, 0 );
1909 				return 1;
1910 			}
1911 
1912 			mo->mo_ad_memberof = ad;
1913 			memberof_make_member_filter( mo );
1914 			} break;
1915 
1916 		default:
1917 			assert( 0 );
1918 			return 1;
1919 		}
1920 	}
1921 
1922 	return 0;
1923 }
1924 
1925 static int
1926 memberof_db_open(
1927 	BackendDB	*be,
1928 	ConfigReply	*cr )
1929 {
1930 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1931 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1932 
1933 	int		rc;
1934 	const char	*text = NULL;
1935 
1936 	if( ! mo->mo_ad_memberof ){
1937 		rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &mo->mo_ad_memberof, &text );
1938 		if ( rc != LDAP_SUCCESS ) {
1939 			Debug( LDAP_DEBUG_ANY, "memberof_db_open: "
1940 					"unable to find attribute=\"%s\": %s (%d)\n",
1941 					SLAPD_MEMBEROF_ATTR, text, rc );
1942 			return rc;
1943 		}
1944 	}
1945 
1946 	if( ! mo->mo_ad_member ){
1947 		rc = slap_str2ad( SLAPD_GROUP_ATTR, &mo->mo_ad_member, &text );
1948 		if ( rc != LDAP_SUCCESS ) {
1949 			Debug( LDAP_DEBUG_ANY, "memberof_db_open: "
1950 					"unable to find attribute=\"%s\": %s (%d)\n",
1951 					SLAPD_GROUP_ATTR, text, rc );
1952 			return rc;
1953 		}
1954 	}
1955 
1956 	if( ! mo->mo_oc_group ){
1957 		mo->mo_oc_group = oc_find( SLAPD_GROUP_CLASS );
1958 		if ( mo->mo_oc_group == NULL ) {
1959 			Debug( LDAP_DEBUG_ANY,
1960 					"memberof_db_open: "
1961 					"unable to find objectClass=\"%s\"\n",
1962 					SLAPD_GROUP_CLASS, 0, 0 );
1963 			return 1;
1964 		}
1965 	}
1966 
1967 	if ( BER_BVISNULL( &mo->mo_dn ) && !BER_BVISNULL( &be->be_rootdn ) ) {
1968 		ber_dupbv( &mo->mo_dn, &be->be_rootdn );
1969 		ber_dupbv( &mo->mo_ndn, &be->be_rootndn );
1970 	}
1971 
1972 	if ( BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
1973 		memberof_make_group_filter( mo );
1974 	}
1975 
1976 	if ( BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
1977 		memberof_make_member_filter( mo );
1978 	}
1979 
1980 	return 0;
1981 }
1982 
1983 static int
1984 memberof_db_destroy(
1985 	BackendDB	*be,
1986 	ConfigReply	*cr )
1987 {
1988 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1989 	memberof_t	*mo = (memberof_t *)on->on_bi.bi_private;
1990 
1991 	if ( mo ) {
1992 		if ( !BER_BVISNULL( &mo->mo_dn ) ) {
1993 			ber_memfree( mo->mo_dn.bv_val );
1994 			ber_memfree( mo->mo_ndn.bv_val );
1995 		}
1996 
1997 		if ( !BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
1998 			ber_memfree( mo->mo_groupFilterstr.bv_val );
1999 		}
2000 
2001 		if ( !BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
2002 			ber_memfree( mo->mo_memberFilterstr.bv_val );
2003 		}
2004 
2005 		ber_memfree( mo );
2006 	}
2007 
2008 	return 0;
2009 }
2010 
2011 /* unused */
2012 static AttributeDescription	*ad_memberOf;
2013 
2014 static struct {
2015 	char	*desc;
2016 	AttributeDescription **adp;
2017 } as[] = {
2018 	{ "( 1.2.840.113556.1.2.102 "
2019 		"NAME 'memberOf' "
2020 		"DESC 'Group that the entry belongs to' "
2021 		"SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' "
2022 		"EQUALITY distinguishedNameMatch "	/* added */
2023 		"USAGE dSAOperation "			/* added; questioned */
2024 		/* "NO-USER-MODIFICATION " */		/* add? */
2025 		"X-ORIGIN 'iPlanet Delegated Administrator' )",
2026 		&ad_memberOf },
2027 	{ NULL }
2028 };
2029 
2030 #if SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC
2031 static
2032 #endif /* SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC */
2033 int
2034 memberof_initialize( void )
2035 {
2036 	int			code, i;
2037 
2038 	for ( i = 0; as[ i ].desc != NULL; i++ ) {
2039 		code = register_at( as[ i ].desc, as[ i ].adp, 0 );
2040 		if ( code ) {
2041 			Debug( LDAP_DEBUG_ANY,
2042 				"memberof_initialize: register_at #%d failed\n",
2043 				i, 0, 0 );
2044 			return code;
2045 		}
2046 	}
2047 
2048 	memberof.on_bi.bi_type = "memberof";
2049 
2050 	memberof.on_bi.bi_db_init = memberof_db_init;
2051 	memberof.on_bi.bi_db_open = memberof_db_open;
2052 	memberof.on_bi.bi_db_destroy = memberof_db_destroy;
2053 
2054 	memberof.on_bi.bi_op_add = memberof_op_add;
2055 	memberof.on_bi.bi_op_delete = memberof_op_delete;
2056 	memberof.on_bi.bi_op_modify = memberof_op_modify;
2057 	memberof.on_bi.bi_op_modrdn = memberof_op_modrdn;
2058 
2059 	memberof.on_bi.bi_cf_ocs = mo_ocs;
2060 
2061 	code = config_register_schema( mo_cfg, mo_ocs );
2062 	if ( code ) return code;
2063 
2064 	return overlay_register( &memberof );
2065 }
2066 
2067 #if SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC
2068 int
2069 init_module( int argc, char *argv[] )
2070 {
2071 	return memberof_initialize();
2072 }
2073 #endif /* SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC */
2074 
2075 #endif /* SLAPD_OVER_MEMBEROF */
2076