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