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