1/*	$NetBSD: autogroup.c,v 1.3 2021/08/14 16:14:51 christos Exp $	*/
2
3/* autogroup.c - automatic group overlay */
4/* $OpenLDAP$ */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2007-2021 The OpenLDAP Foundation.
8 * Portions Copyright 2007 Micha�� Szulczy��ski.
9 * Portions Copyright 2009 Howard Chu.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted only as authorized by the OpenLDAP
14 * Public License.
15 *
16 * A copy of this license is available in the file LICENSE in the
17 * top-level directory of the distribution or, alternatively, at
18 * <http://www.OpenLDAP.org/license.html>.
19 */
20/* ACKNOWLEDGEMENTS:
21 * This work was initially developed by Micha�� Szulczy��ski for inclusion in
22 * OpenLDAP Software.  Additional significant contributors include:
23 *   Howard Chu
24 *   Raphael Ouazana
25 *   Norbert Pueschel
26 *   Christian Manal
27 */
28
29#include <sys/cdefs.h>
30__RCSID("$NetBSD: autogroup.c,v 1.3 2021/08/14 16:14:51 christos Exp $");
31
32#include "portable.h"
33
34#include <stdio.h>
35
36#include <ac/string.h>
37
38#include "slap.h"
39#include "slap-config.h"
40#include "lutil.h"
41
42#ifndef SLAPD_MEMBEROF_ATTR
43#define	SLAPD_MEMBEROF_ATTR	"memberOf"
44#endif
45
46static slap_overinst	autogroup;
47
48/* Filter represents the memberURL of a group. */
49typedef struct autogroup_filter_t {
50	struct berval			agf_dn;	/* The base DN in memberURL */
51	struct berval			agf_ndn;
52	struct berval			agf_filterstr;
53	Filter				*agf_filter;
54	int				agf_scope;
55	AttributeName			*agf_anlist;
56	struct autogroup_filter_t	*agf_next;
57} autogroup_filter_t;
58
59/* Description of group attributes. */
60typedef struct autogroup_def_t {
61	ObjectClass		*agd_oc;
62	AttributeDescription	*agd_member_url_ad;
63	AttributeDescription	*agd_member_ad;
64	struct autogroup_def_t	*agd_next;
65} autogroup_def_t;
66
67/* Represents the group entry. */
68typedef struct autogroup_entry_t {
69	BerValue		age_dn;
70	BerValue		age_ndn;
71	autogroup_filter_t	*age_filter; /* List of filters made from memberURLs */
72	autogroup_def_t		*age_def; /* Attribute definition */
73	ldap_pvt_thread_mutex_t age_mutex;
74	int			age_mustrefresh; /* Defined in request to refresh in response */
75	int			age_modrdn_olddnmodified; /* Defined in request to refresh in response */
76	struct autogroup_entry_t	*age_next;
77} autogroup_entry_t;
78
79/* Holds pointers to attribute definitions and groups. */
80typedef struct autogroup_info_t {
81	autogroup_def_t		*agi_def;	/* Group attributes definitions. */
82	autogroup_entry_t	*agi_entry;	/* Group entries.  */
83	AttributeDescription	*agi_memberof_ad;	/* memberOf attribute description  */
84	ldap_pvt_thread_mutex_t agi_mutex;
85} autogroup_info_t;
86
87/* Search callback for adding groups initially. */
88typedef struct autogroup_sc_t {
89	autogroup_info_t		*ags_info;	/* Group definitions and entries.  */
90	autogroup_def_t		*ags_def;	/* Attributes definition of the group being added. */
91} autogroup_sc_t;
92
93/* Used for adding members, found when searching, to a group. */
94typedef struct autogroup_ga_t {
95	autogroup_entry_t	*agg_group;	/* The group to which the members will be added. */
96	autogroup_filter_t	*agg_filter;	/* Current filter */
97	Entry			*agg_entry;	/* Used in autogroup_member_search_cb to modify
98						this entry with the search results. */
99
100	Modifications		*agg_mod;	/* Used in autogroup_member_search_modify_cb to hold the
101						search results which will be added to the group. */
102
103	Modifications		*agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't
104						have to search for the last mod added. */
105} autogroup_ga_t;
106
107
108/*
109**	dn, ndn	- the DN of the member to add
110**	age	- the group to which the member DN will be added
111*/
112static int
113autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
114{
115	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
116	Modifications	*modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
117	SlapReply	sreply = {REP_RESULT};
118	BerValue	*vals, *nvals;
119	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
120	Operation	o = *op;
121	unsigned long opid = op->o_opid;
122	OpExtra oex;
123
124	assert( dn != NULL );
125	assert( ndn != NULL );
126	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
127		dn->bv_val, age->age_dn.bv_val );
128
129	vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
130	nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
131	ber_dupbv( vals, dn );
132	BER_BVZERO( &vals[ 1 ] );
133	ber_dupbv( nvals, ndn );
134	BER_BVZERO( &nvals[ 1 ] );
135
136	modlist->sml_op = LDAP_MOD_ADD;
137	modlist->sml_desc = age->age_def->agd_member_ad;
138	modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
139	modlist->sml_values = vals;
140	modlist->sml_nvalues = nvals;
141	modlist->sml_numvals = 1;
142	modlist->sml_flags = SLAP_MOD_INTERNAL;
143	modlist->sml_next = NULL;
144
145	o.o_opid = 0;	/* shared with op, saved above */
146	o.o_tag = LDAP_REQ_MODIFY;
147	o.o_callback = &cb;
148	o.orm_modlist = modlist;
149	o.o_dn = op->o_bd->be_rootdn;
150	o.o_ndn = op->o_bd->be_rootndn;
151	o.o_req_dn = age->age_dn;
152	o.o_req_ndn = age->age_ndn;
153	o.o_permissive_modify = 1;
154	o.o_dont_replicate = 1;
155	o.orm_no_opattrs = 1;
156	o.o_managedsait = SLAP_CONTROL_CRITICAL;
157	o.o_relax = SLAP_CONTROL_CRITICAL;
158
159	oex.oe_key = (void *)&autogroup;
160	LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
161
162	o.o_bd->bd_info = (BackendInfo *)on->on_info;
163	(void)op->o_bd->be_modify( &o, &sreply );
164	o.o_bd->bd_info = (BackendInfo *)on;
165
166	LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
167
168	slap_mods_free( modlist, 1 );
169	op->o_opid = opid;
170
171	return sreply.sr_err;
172}
173
174/*
175**	e	- the entry where to get the attribute values
176**	age	- the group to which the values will be added
177*/
178static int
179autogroup_add_member_values_to_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
180{
181	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
182	Modifications	modlist;
183	SlapReply	sreply = {REP_RESULT};
184	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
185	Operation	o = *op;
186	unsigned long opid = op->o_opid;
187	OpExtra oex;
188
189	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
190		dn->bv_val, age->age_dn.bv_val );
191
192	modlist.sml_op = LDAP_MOD_ADD;
193	modlist.sml_desc = age->age_def->agd_member_ad;
194	modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
195	modlist.sml_values = attr->a_vals;
196	modlist.sml_nvalues = attr->a_nvals;
197	modlist.sml_numvals = attr->a_numvals;
198	modlist.sml_flags = SLAP_MOD_INTERNAL;
199	modlist.sml_next = NULL;
200
201	o.o_opid = 0;
202	o.o_tag = LDAP_REQ_MODIFY;
203	o.o_callback = &cb;
204	o.orm_modlist = &modlist;
205	o.o_dn = op->o_bd->be_rootdn;
206	o.o_ndn = op->o_bd->be_rootndn;
207	o.o_req_dn = age->age_dn;
208	o.o_req_ndn = age->age_ndn;
209	o.o_permissive_modify = 1;
210	o.o_dont_replicate = 1;
211	o.orm_no_opattrs = 1;
212	o.o_managedsait = SLAP_CONTROL_CRITICAL;
213	o.o_relax = SLAP_CONTROL_CRITICAL;
214
215	oex.oe_key = (void *)&autogroup;
216	LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
217
218	o.o_bd->bd_info = (BackendInfo *)on->on_info;
219	(void)op->o_bd->be_modify( &o, &sreply );
220	o.o_bd->bd_info = (BackendInfo *)on;
221	op->o_opid = opid;
222	LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
223
224	return sreply.sr_err;
225}
226
227/*
228** dn,ndn	- the DN to be deleted
229** age		- the group from which the DN will be deleted
230** If we pass a NULL dn and ndn, all members are deleted from the group.
231*/
232static int
233autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
234{
235	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
236	Modifications	*modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
237	SlapReply	sreply = {REP_RESULT};
238	BerValue	*vals, *nvals;
239	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
240	Operation	o = *op;
241	unsigned long opid = op->o_opid;
242	OpExtra oex;
243
244	if ( dn == NULL || ndn == NULL ) {
245		Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
246			age->age_dn.bv_val );
247
248		modlist->sml_values = NULL;
249		modlist->sml_nvalues = NULL;
250		modlist->sml_numvals = 0;
251	} else {
252		Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
253			dn->bv_val, age->age_dn.bv_val );
254
255		vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
256		nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
257		ber_dupbv( vals, dn );
258		BER_BVZERO( &vals[ 1 ] );
259		ber_dupbv( nvals, ndn );
260		BER_BVZERO( &nvals[ 1 ] );
261
262		modlist->sml_values = vals;
263		modlist->sml_nvalues = nvals;
264		modlist->sml_numvals = 1;
265	}
266
267
268	modlist->sml_op = LDAP_MOD_DELETE;
269	modlist->sml_desc = age->age_def->agd_member_ad;
270	modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
271	modlist->sml_flags = SLAP_MOD_INTERNAL;
272	modlist->sml_next = NULL;
273
274	o.o_opid = 0;
275	o.o_callback = &cb;
276	o.o_tag = LDAP_REQ_MODIFY;
277	o.orm_modlist = modlist;
278	o.o_dn = op->o_bd->be_rootdn;
279	o.o_ndn = op->o_bd->be_rootndn;
280	o.o_req_dn = age->age_dn;
281	o.o_req_ndn = age->age_ndn;
282	o.o_relax = SLAP_CONTROL_CRITICAL;
283	o.o_managedsait = SLAP_CONTROL_CRITICAL;
284	o.o_permissive_modify = 1;
285	o.o_dont_replicate = 1;
286	o.orm_no_opattrs = 1;
287
288	oex.oe_key = (void *)&autogroup;
289	LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
290
291	o.o_bd->bd_info = (BackendInfo *)on->on_info;
292	(void)op->o_bd->be_modify( &o, &sreply );
293	o.o_bd->bd_info = (BackendInfo *)on;
294
295	LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
296
297	slap_mods_free( modlist, 1 );
298
299	op->o_opid = opid;
300	return sreply.sr_err;
301}
302
303/*
304**      e       - the entry where to get the attribute values
305**      age     - the group from which the values will be deleted
306*/
307static int
308autogroup_delete_member_values_from_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
309{
310        slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
311        Modifications   modlist;
312        SlapReply       sreply = {REP_RESULT};
313        slap_callback   cb = { NULL, slap_null_cb, NULL, NULL };
314        Operation       o = *op;
315	unsigned long opid = op->o_opid;
316	OpExtra oex;
317
318        Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
319			dn->bv_val, age->age_dn.bv_val );
320
321        modlist.sml_op = LDAP_MOD_DELETE;
322        modlist.sml_desc = age->age_def->agd_member_ad;
323        modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
324        modlist.sml_values = attr->a_vals;
325        modlist.sml_nvalues = attr->a_nvals;
326        modlist.sml_numvals = attr->a_numvals;
327        modlist.sml_flags = SLAP_MOD_INTERNAL;
328        modlist.sml_next = NULL;
329
330	o.o_opid = 0;
331        o.o_tag = LDAP_REQ_MODIFY;
332        o.o_callback = &cb;
333        o.orm_modlist = &modlist;
334		o.o_dn = op->o_bd->be_rootdn;
335		o.o_ndn = op->o_bd->be_rootndn;
336        o.o_req_dn = age->age_dn;
337        o.o_req_ndn = age->age_ndn;
338        o.o_permissive_modify = 1;
339	o.o_dont_replicate = 1;
340	o.orm_no_opattrs = 1;
341        o.o_managedsait = SLAP_CONTROL_CRITICAL;
342        o.o_relax = SLAP_CONTROL_CRITICAL;
343
344	oex.oe_key = (void *)&autogroup;
345	LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
346
347        o.o_bd->bd_info = (BackendInfo *)on->on_info;
348        (void)op->o_bd->be_modify( &o, &sreply );
349        o.o_bd->bd_info = (BackendInfo *)on;
350	op->o_opid = opid;
351
352	LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
353
354        return sreply.sr_err;
355}
356
357/*
358** Callback used to add entries to a group,
359** which are going to be written in the database
360** (used in bi_op_add)
361** The group is passed in autogroup_ga_t->agg_group
362*/
363static int
364autogroup_member_search_cb( Operation *op, SlapReply *rs )
365{
366	assert( op->o_tag == LDAP_REQ_SEARCH );
367
368	if ( rs->sr_type == REP_SEARCH ) {
369		autogroup_ga_t		*agg = (autogroup_ga_t *)op->o_callback->sc_private;
370		autogroup_entry_t	*age = agg->agg_group;
371		autogroup_filter_t	*agf = agg->agg_filter;
372		Modification		mod;
373		const char		*text = NULL;
374		char			textbuf[1024];
375		struct berval		*vals, *nvals;
376		struct berval		lvals[ 2 ], lnvals[ 2 ];
377		int			numvals;
378
379		Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
380			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
381
382		if ( agf->agf_anlist ) {
383			Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
384			if (attr) {
385				vals = attr->a_vals;
386				nvals = attr->a_nvals;
387				numvals = attr->a_numvals;
388			} else {
389				// Nothing to add
390				return 0;
391			}
392		} else {
393			lvals[ 0 ] = rs->sr_entry->e_name;
394			BER_BVZERO( &lvals[ 1 ] );
395			lnvals[ 0 ] = rs->sr_entry->e_nname;
396			BER_BVZERO( &lnvals[ 1 ] );
397			vals = lvals;
398			nvals = lnvals;
399			numvals = 1;
400		}
401
402		mod.sm_op = LDAP_MOD_ADD;
403		mod.sm_desc = age->age_def->agd_member_ad;
404		mod.sm_type = age->age_def->agd_member_ad->ad_cname;
405		mod.sm_values = vals;
406		mod.sm_nvalues = nvals;
407		mod.sm_numvals = numvals;
408
409		modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
410	}
411
412	return 0;
413}
414
415/*
416** Callback used to add entries to a group, which is already in the database.
417** (used in on_response)
418** The group is passed in autogroup_ga_t->agg_group
419** NOTE: Very slow.
420*/
421static int
422autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
423{
424	assert( op->o_tag == LDAP_REQ_SEARCH );
425
426	if ( rs->sr_type == REP_SEARCH ) {
427		autogroup_ga_t		*agg = (autogroup_ga_t *)op->o_callback->sc_private;
428		autogroup_entry_t	*age = agg->agg_group;
429		autogroup_filter_t	*agf = agg->agg_filter;
430		Modifications		*modlist;
431		struct berval		*vals, *nvals;
432		struct berval		lvals[ 2 ], lnvals[ 2 ];
433		int			numvals;
434
435		Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
436			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
437
438		if ( agf->agf_anlist ) {
439			Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
440			if (attr) {
441				vals = attr->a_vals;
442				nvals = attr->a_nvals;
443				numvals = attr->a_numvals;
444			} else {
445				// Nothing to add
446				return 0;
447			}
448		} else {
449			lvals[ 0 ] = rs->sr_entry->e_name;
450			BER_BVZERO( &lvals[ 1 ] );
451			lnvals[ 0 ] = rs->sr_entry->e_nname;
452			BER_BVZERO( &lnvals[ 1 ] );
453			vals = lvals;
454			nvals = lnvals;
455			numvals = 1;
456		}
457
458		if ( numvals ) {
459			modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
460
461			modlist->sml_op = LDAP_MOD_ADD;
462			modlist->sml_desc = age->age_def->agd_member_ad;
463			modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
464
465			ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
466			ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
467			modlist->sml_numvals = numvals;
468
469			modlist->sml_flags = SLAP_MOD_INTERNAL;
470			modlist->sml_next = NULL;
471
472			if ( agg->agg_mod == NULL ) {
473				agg->agg_mod = modlist;
474				agg->agg_mod_last = modlist;
475			} else {
476				agg->agg_mod_last->sml_next = modlist;
477				agg->agg_mod_last = modlist;
478			}
479		}
480
481	}
482
483	return 0;
484}
485
486
487/*
488** Adds all entries matching the passed filter to the specified group.
489** If modify == 1, then we modify the group's entry in the database using be_modify.
490** If modify == 0, then, we must supply a rw entry for the group,
491**	because we only modify the entry, without calling be_modify.
492** e	- the group entry, to which the members will be added
493** age	- the group
494** agf	- the filter
495*/
496static int
497autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
498{
499	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
500	Operation		o = *op;
501	SlapReply		rs = { REP_SEARCH };
502	slap_callback		cb = { 0 };
503	slap_callback		null_cb = { NULL, slap_null_cb, NULL, NULL };
504	autogroup_ga_t		agg;
505	OpExtra oex;
506
507	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
508		age->age_dn.bv_val );
509
510	o.ors_attrsonly = 0;
511	o.o_tag = LDAP_REQ_SEARCH;
512
513	o.o_dn = op->o_bd->be_rootdn;
514	o.o_ndn = op->o_bd->be_rootndn;
515	o.o_req_dn = agf->agf_dn;
516	o.o_req_ndn = agf->agf_ndn;
517
518	o.ors_filterstr = agf->agf_filterstr;
519	o.ors_filter = agf->agf_filter;
520
521	o.ors_scope = agf->agf_scope;
522	o.ors_deref = LDAP_DEREF_NEVER;
523	o.ors_limit = NULL;
524	o.ors_tlimit = SLAP_NO_LIMIT;
525	o.ors_slimit = SLAP_NO_LIMIT;
526	o.ors_attrs =  agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
527	o.o_do_not_cache = 1;
528
529	agg.agg_group = age;
530	agg.agg_filter = agf;
531	agg.agg_mod = NULL;
532	agg.agg_mod_last = NULL;
533	agg.agg_entry = e;
534	cb.sc_private = &agg;
535
536	if ( modify == 1 ) {
537		cb.sc_response = autogroup_member_search_modify_cb;
538	} else {
539		cb.sc_response = autogroup_member_search_cb;
540	}
541
542	cb.sc_cleanup = NULL;
543	cb.sc_next = NULL;
544
545	o.o_callback = &cb;
546
547	o.o_bd->bd_info = (BackendInfo *)on->on_info;
548	op->o_bd->be_search( &o, &rs );
549	o.o_bd->bd_info = (BackendInfo *)on;
550
551	if ( modify == 1 && agg.agg_mod ) {
552		unsigned long opid = op->o_opid;
553
554		rs_reinit( &rs, REP_RESULT );
555
556		o = *op;
557		o.o_opid = 0;
558		o.o_callback = &null_cb;
559		o.o_tag = LDAP_REQ_MODIFY;
560		o.orm_modlist = agg.agg_mod;
561		o.o_dn = op->o_bd->be_rootdn;
562		o.o_ndn = op->o_bd->be_rootndn;
563		o.o_req_dn = age->age_dn;
564		o.o_req_ndn = age->age_ndn;
565		o.o_relax = SLAP_CONTROL_CRITICAL;
566		o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
567		o.o_permissive_modify = 1;
568		o.o_dont_replicate = 1;
569		o.orm_no_opattrs = 1;
570
571	oex.oe_key = (void *)&autogroup;
572	LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
573
574		o.o_bd->bd_info = (BackendInfo *)on->on_info;
575		(void)op->o_bd->be_modify( &o, &rs );
576		o.o_bd->bd_info = (BackendInfo *)on;
577
578	LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
579
580		slap_mods_free(agg.agg_mod, 1);
581		op->o_opid = opid;
582	}
583
584	return 0;
585}
586
587/*
588** Adds a group to the internal list from the passed entry.
589** scan specifies whether to add all matching members to the group.
590** modify specifies whether to modify the given group entry (when modify == 0),
591**	or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
592** agi	- pointer to the groups and the attribute definitions
593** agd - the attribute definition of the added group
594** e	- the entry representing the group, can be NULL if the ndn is specified, and modify == 1
595** ndn	- the DN of the group, can be NULL if we give a non-NULL e
596*/
597static int
598autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
599{
600	autogroup_entry_t	**agep = &agi->agi_entry;
601	autogroup_filter_t	*agf, *agf_prev = NULL;
602	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
603	LDAPURLDesc		*lud = NULL;
604	Attribute		*a;
605	BerValue		*bv, dn;
606	int			rc = 0, match = 1, null_entry = 0;
607
608	if ( e == NULL ) {
609		if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
610			LDAP_SUCCESS || e == NULL ) {
611			Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val );
612			return 1;
613		}
614
615		null_entry = 1;
616	}
617
618	Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
619		e->e_name.bv_val );
620
621	if ( agi->agi_entry != NULL ) {
622		for ( ; *agep ; agep = &(*agep)->age_next ) {
623			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
624			if ( match == 0 ) {
625				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val );
626				return 1;
627			}
628			/* goto last */;
629		}
630	}
631
632
633	*agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
634	ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
635	(*agep)->age_def = agd;
636	(*agep)->age_filter = NULL;
637	(*agep)->age_mustrefresh = 0;
638	(*agep)->age_modrdn_olddnmodified = 0;
639
640	ber_dupbv( &(*agep)->age_dn, &e->e_name );
641	ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
642
643	a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
644
645	if ( null_entry == 1 ) {
646		a = attrs_dup( a );
647		overlay_entry_release_ov( op, e, 0, on );
648	}
649
650	if( a == NULL ) {
651		Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n" );
652	} else {
653		for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
654
655			agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
656
657			if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
658				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val );
659				/* FIXME: error? */
660				ch_free( agf );
661				continue;
662			}
663
664			agf->agf_scope = lud->lud_scope;
665
666			if ( lud->lud_dn == NULL ) {
667				BER_BVSTR( &dn, "" );
668			} else {
669				ber_str2bv( lud->lud_dn, 0, 0, &dn );
670			}
671
672			rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
673			if ( rc != LDAP_SUCCESS ) {
674				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val );
675				/* FIXME: error? */
676				goto cleanup;
677			}
678
679			if ( lud->lud_filter != NULL ) {
680				ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
681				agf->agf_filter = str2filter( lud->lud_filter );
682			} else {
683				Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: URL filter is missing <%s>\n", bv->bv_val );
684				/* FIXME: error? */
685				goto cleanup;
686			}
687
688			if ( lud->lud_attrs != NULL ) {
689				int i;
690
691				for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
692					/* Just counting */;
693				}
694
695				if ( i > 1 ) {
696					Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
697						bv->bv_val );
698					/* FIXME: error? */
699					filter_free( agf->agf_filter );
700					ch_free( agf->agf_filterstr.bv_val );
701					ch_free( agf->agf_dn.bv_val );
702					ch_free( agf->agf_ndn.bv_val );
703					ldap_free_urldesc( lud );
704					ch_free( agf );
705					continue;
706				}
707
708				agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
709
710				if ( agf->agf_anlist == NULL ) {
711					Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
712						lud->lud_attrs[0] );
713					/* FIXME: error? */
714					filter_free( agf->agf_filter );
715					ch_free( agf->agf_filterstr.bv_val );
716					ch_free( agf->agf_dn.bv_val );
717					ch_free( agf->agf_ndn.bv_val );
718					ldap_free_urldesc( lud );
719					ch_free( agf );
720					continue;
721				}
722			}
723
724			agf->agf_next = NULL;
725
726			if( (*agep)->age_filter == NULL ) {
727				(*agep)->age_filter = agf;
728			}
729
730			if( agf_prev != NULL ) {
731				agf_prev->agf_next = agf;
732			}
733
734			agf_prev = agf;
735
736			if ( scan == 1 ){
737				autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
738			}
739
740			Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
741				agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val );
742
743			ldap_free_urldesc( lud );
744
745			continue;
746
747
748cleanup:;
749
750			ch_free( agf->agf_ndn.bv_val );
751			ch_free( agf->agf_dn.bv_val );
752			ldap_free_urldesc( lud );
753			ch_free( agf );
754		}
755	}
756
757	if ( null_entry == 1 ) {
758		attrs_free( a );
759	}
760	return rc;
761}
762
763/*
764** Used when opening the database to add all existing
765** groups from the database to our internal list.
766*/
767static int
768autogroup_group_add_cb( Operation *op, SlapReply *rs )
769{
770	assert( op->o_tag == LDAP_REQ_SEARCH );
771
772	if ( rs->sr_type == REP_SEARCH ) {
773		autogroup_sc_t		*ags = (autogroup_sc_t *)op->o_callback->sc_private;
774
775		Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
776			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
777
778		autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
779	}
780
781	return 0;
782}
783
784typedef struct ag_addinfo {
785	slap_overinst *on;
786	Entry *e;
787	autogroup_def_t		*agd;
788} ag_addinfo;
789
790static int
791autogroup_add_entry_cb( Operation *op, SlapReply *rs )
792{
793	slap_callback *sc = op->o_callback;
794	ag_addinfo *aa = sc->sc_private;
795	slap_overinst *on = aa->on;
796	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
797	BackendInfo *bi = op->o_bd->bd_info;
798
799	if ( rs->sr_err != LDAP_SUCCESS )
800		goto done;
801
802	op->o_bd->bd_info = (BackendInfo *)on;
803	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
804	if ( aa->agd ) {
805		autogroup_add_group( op, agi, aa->agd, aa->e, NULL, 1 , 0);
806	} else {
807		autogroup_entry_t	*age;
808		autogroup_filter_t	*agf;
809		struct berval odn, ondn;
810		int rc;
811
812		/* must use rootdn when calling test_filter */
813		odn = op->o_dn;
814		ondn = op->o_ndn;
815		op->o_dn = op->o_bd->be_rootdn;
816		op->o_ndn = op->o_bd->be_rootndn;
817
818		for ( age = agi->agi_entry; age ; age = age->age_next ) {
819			ldap_pvt_thread_mutex_lock( &age->age_mutex );
820
821			/* Check if any of the filters are the suffix to the entry DN.
822			   If yes, we can test that filter against the entry. */
823
824			for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
825				if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
826					rc = test_filter( op, aa->e, agf->agf_filter );
827					if ( rc == LDAP_COMPARE_TRUE ) {
828						if ( agf->agf_anlist ) {
829							Attribute *a = attr_find( aa->e->e_attrs, agf->agf_anlist[0].an_desc );
830							if ( a )
831								autogroup_add_member_values_to_group( op, &op->o_req_dn, age, a );
832						} else {
833							autogroup_add_member_to_group( op, &aa->e->e_name, &aa->e->e_nname, age );
834						}
835						break;
836					}
837				}
838			}
839			ldap_pvt_thread_mutex_unlock( &age->age_mutex );
840		}
841		op->o_dn = odn;
842		op->o_ndn = ondn;
843	}
844	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
845
846	op->o_bd->bd_info = bi;
847
848done:
849	op->o_callback = sc->sc_next;
850	op->o_tmpfree( sc, op->o_tmpmemctx );
851
852	return SLAP_CB_CONTINUE;
853}
854
855/*
856** When adding a group, we first strip any existing members,
857** and add all which match the filters ourselves.
858*/
859static int
860autogroup_add_entry( Operation *op, SlapReply *rs)
861{
862	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
863	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
864	autogroup_def_t		*agd = agi->agi_def;
865	slap_callback	*sc = NULL;
866	ag_addinfo	*aa = NULL;
867
868	Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
869		op->ora_e->e_name.bv_val );
870
871	sc = op->o_tmpcalloc( sizeof(slap_callback) + sizeof(ag_addinfo), 1, op->o_tmpmemctx );
872	sc->sc_private = (sc+1);
873	sc->sc_response = autogroup_add_entry_cb;
874	aa = sc->sc_private;
875	aa->on = on;
876	aa->e = op->ora_e;
877	sc->sc_next = op->o_callback;
878	op->o_callback = sc;
879
880	/* Check if it's a group. */
881	for ( ; agd ; agd = agd->agd_next ) {
882		if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
883			Modification		mod;
884			const char		*text = NULL;
885			char			textbuf[1024];
886
887			mod.sm_op = LDAP_MOD_DELETE;
888			mod.sm_desc = agd->agd_member_ad;
889			mod.sm_type = agd->agd_member_ad->ad_cname;
890			mod.sm_values = NULL;
891			mod.sm_nvalues = NULL;
892
893			/* We don't want any member attributes added by the user. */
894			modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
895
896			aa->agd = agd;
897
898			break;
899		}
900	}
901
902	return SLAP_CB_CONTINUE;
903}
904
905/*
906** agi	- internal group and attribute definitions list
907** e	- the group to remove from the internal list
908*/
909static int
910autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
911{
912	autogroup_entry_t	*age = agi->agi_entry,
913				*age_prev = NULL,
914				*age_next;
915	int			rc = 1;
916
917	Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
918		age->age_dn.bv_val );
919
920	for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
921		age_next = age->age_next;
922
923		if ( age == e ) {
924			autogroup_filter_t	*agf = age->age_filter,
925							*agf_next;
926
927			if ( age_prev != NULL ) {
928				age_prev->age_next = age_next;
929			} else {
930				agi->agi_entry = NULL;
931			}
932
933			ch_free( age->age_dn.bv_val );
934			ch_free( age->age_ndn.bv_val );
935
936			for( agf_next = agf ; agf_next ; agf = agf_next ){
937				agf_next = agf->agf_next;
938
939				filter_free( agf->agf_filter );
940				ch_free( agf->agf_filterstr.bv_val );
941				ch_free( agf->agf_dn.bv_val );
942				ch_free( agf->agf_ndn.bv_val );
943				anlist_free( agf->agf_anlist, 1, NULL );
944				ch_free( agf );
945			}
946
947			ldap_pvt_thread_mutex_unlock( &age->age_mutex );
948			ldap_pvt_thread_mutex_destroy( &age->age_mutex );
949			ch_free( age );
950
951			rc = 0;
952			return rc;
953
954		}
955	}
956
957	Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val );
958
959	return rc;
960
961}
962
963static int
964autogroup_delete_entry( Operation *op, SlapReply *rs)
965{
966	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
967	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
968	autogroup_entry_t	*age, *age_prev, *age_next;
969	autogroup_filter_t	*agf;
970	Entry			*e;
971	int			matched_group = 0, rc = 0;
972	struct berval odn, ondn;
973	OpExtra *oex;
974
975	LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
976		if ( oex->oe_key == (void *)&autogroup )
977			return SLAP_CB_CONTINUE;
978	}
979
980	Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val );
981
982	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
983
984	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
985		LDAP_SUCCESS || e == NULL ) {
986		Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val );
987		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
988		return SLAP_CB_CONTINUE;
989	}
990
991	/* Check if the entry to be deleted is one of our groups. */
992	for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
993		age = age_next;
994		ldap_pvt_thread_mutex_lock( &age->age_mutex );
995		age_next = age->age_next;
996
997		if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
998			int match = 1;
999
1000			matched_group = 1;
1001
1002			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
1003
1004			if ( match == 0 ) {
1005				autogroup_delete_group( agi, age );
1006				break;
1007			}
1008		}
1009
1010		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1011	}
1012
1013	if ( matched_group == 1 ) {
1014		overlay_entry_release_ov( op, e, 0, on );
1015		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1016		return SLAP_CB_CONTINUE;
1017	}
1018
1019	/* Check if the entry matches any of the groups.
1020	   If yes, we can delete the entry from that group. */
1021
1022	odn = op->o_dn;
1023	ondn = op->o_ndn;
1024	op->o_dn = op->o_bd->be_rootdn;
1025	op->o_ndn = op->o_bd->be_rootndn;
1026
1027	for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1028		ldap_pvt_thread_mutex_lock( &age->age_mutex );
1029
1030		for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
1031			if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1032				rc = test_filter( op, e, agf->agf_filter );
1033				if ( rc == LDAP_COMPARE_TRUE ) {
1034					/* If the attribute is retrieved from the entry, we don't know what to delete
1035					** So the group must be entirely refreshed
1036					** But the refresh can't be done now because the entry is not deleted
1037					** So the group is marked as mustrefresh
1038					*/
1039					if ( agf->agf_anlist ) {
1040						age->age_mustrefresh = 1;
1041					} else {
1042						autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
1043					}
1044					break;
1045				}
1046			}
1047		}
1048		ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1049	}
1050	op->o_dn = odn;
1051	op->o_ndn = ondn;
1052
1053	overlay_entry_release_ov( op, e, 0, on );
1054	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1055
1056	return SLAP_CB_CONTINUE;
1057}
1058
1059static int
1060autogroup_response( Operation *op, SlapReply *rs )
1061{
1062	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
1063	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
1064	autogroup_def_t		*agd = agi->agi_def;
1065	autogroup_entry_t	*age;
1066	autogroup_filter_t	*agf;
1067	BerValue		new_dn, new_ndn, pdn;
1068	Entry			*e, *group;
1069	Attribute		*a, *ea, *attrs;
1070	int			is_olddn, is_newdn, is_value_refresh, dn_equal;
1071	OpExtra *oex;
1072
1073	LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
1074		if ( oex->oe_key == (void *)&autogroup )
1075			break;
1076	}
1077
1078	/* Handle all cases where a refresh of the group is needed */
1079	if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
1080		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !oex ) {
1081
1082			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1083
1084			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1085				/* Request detected that the group must be refreshed */
1086
1087				ldap_pvt_thread_mutex_lock( &age->age_mutex );
1088
1089				if ( age->age_mustrefresh ) {
1090					autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1091
1092					for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1093						autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1094					}
1095				}
1096
1097				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1098			}
1099
1100			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1101		}
1102	} else if ( op->o_tag == LDAP_REQ_MODRDN ) {
1103		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !oex ) {
1104
1105			Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val );
1106
1107			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1108
1109			if ( op->oq_modrdn.rs_newSup ) {
1110				pdn = *op->oq_modrdn.rs_newSup;
1111			} else {
1112				dnParent( &op->o_req_dn, &pdn );
1113			}
1114			build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
1115
1116			if ( op->oq_modrdn.rs_nnewSup ) {
1117				pdn = *op->oq_modrdn.rs_nnewSup;
1118			} else {
1119				dnParent( &op->o_req_ndn, &pdn );
1120			}
1121			build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
1122
1123			Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val );
1124
1125			dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
1126
1127			if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
1128				LDAP_SUCCESS || e == NULL ) {
1129				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val );
1130				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1131				return SLAP_CB_CONTINUE;
1132			}
1133
1134			a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1135
1136
1137			if ( a == NULL ) {
1138				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val );
1139				overlay_entry_release_ov( op, e, 0, on );
1140				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1141				return SLAP_CB_CONTINUE;
1142			}
1143
1144
1145			/* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1146			for ( ; agd; agd = agd->agd_next ) {
1147
1148				if ( value_find_ex( slap_schema.si_ad_objectClass,
1149						SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1150						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1151						a->a_nvals, &agd->agd_oc->soc_cname,
1152						op->o_tmpmemctx ) == 0 )
1153				{
1154					for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1155						int match = 1;
1156
1157						dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1158						if ( match == 0 ) {
1159							Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val );
1160							ber_dupbv( &age->age_dn, &new_dn );
1161							ber_dupbv( &age->age_ndn, &new_ndn );
1162
1163							op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx  );
1164							op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1165							overlay_entry_release_ov( op, e, 0, on );
1166							ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1167							return SLAP_CB_CONTINUE;
1168						}
1169					}
1170
1171				}
1172			}
1173
1174			/* For each group:
1175			   1. check if the original entry's DN is in the group.
1176			   2. check if the any of the group filter's base DN is a suffix of the new DN
1177
1178			   If 1 and 2 are both false, we do nothing.
1179			   If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1180			   If 1 is false, and 2 is true, we check the entry against the group's filters,
1181				and add it's DN to the group.
1182			   If 1 is true, and 2 is false, we delete the entry's DN from the group.
1183			*/
1184			attrs = attrs_dup( e->e_attrs );
1185			overlay_entry_release_ov( op, e, 0, on );
1186			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1187				is_olddn = 0;
1188				is_newdn = 0;
1189				is_value_refresh = 0;
1190
1191				ldap_pvt_thread_mutex_lock( &age->age_mutex );
1192
1193				if ( age->age_filter && age->age_filter->agf_anlist ) {
1194					ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1195				}
1196				else {
1197					ea = NULL;
1198				}
1199
1200				if ( age->age_modrdn_olddnmodified ) {
1201					/* Request already marked this group to be updated */
1202					is_olddn = 1;
1203					is_value_refresh = 1;
1204					age->age_modrdn_olddnmodified = 0;
1205				} else {
1206
1207					if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1208						LDAP_SUCCESS || group == NULL ) {
1209						Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val );
1210
1211						op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1212						op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1213
1214						attrs_free( attrs );
1215						ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1216						ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1217						return SLAP_CB_CONTINUE;
1218					}
1219
1220					a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1221
1222					if ( a != NULL ) {
1223						if ( value_find_ex( age->age_def->agd_member_ad,
1224								SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1225								SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1226								a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1227						{
1228							is_olddn = 1;
1229						}
1230
1231					}
1232
1233					overlay_entry_release_ov( op, group, 0, on );
1234
1235				}
1236
1237				for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1238					if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1239						/* TODO: should retest filter as it could imply conditions on the dn */
1240						is_newdn = 1;
1241						break;
1242					}
1243				}
1244
1245
1246				if ( is_value_refresh ) {
1247					if ( is_olddn != is_newdn ) {
1248						/* group refresh */
1249						autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1250
1251						for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1252							autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1253						}
1254					}
1255					ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1256					continue;
1257				}
1258				if ( is_olddn == 1 && is_newdn == 0 ) {
1259					if ( ea )
1260						autogroup_delete_member_values_from_group( op, &new_dn, age, ea );
1261					else
1262						autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1263				} else
1264				if ( is_olddn == 0 && is_newdn == 1 ) {
1265					Entry etmp;
1266					struct berval odn, ondn;
1267					etmp.e_name = op->o_req_dn;
1268					etmp.e_nname = op->o_req_ndn;
1269					etmp.e_attrs = attrs;
1270					odn = op->o_dn;
1271					ondn = op->o_ndn;
1272					op->o_dn = op->o_bd->be_rootdn;
1273					op->o_ndn = op->o_bd->be_rootndn;
1274
1275					for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1276						if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1277							if ( ea ) {
1278								autogroup_add_member_values_to_group( op, &new_dn, age, ea );
1279							} else
1280								autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1281							break;
1282						}
1283					}
1284					op->o_dn = odn;
1285					op->o_ndn = ondn;
1286				} else
1287				if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1288					if ( ea ) {
1289						/* group refresh */
1290						autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1291
1292						for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1293							autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1294						}
1295					}
1296					else {
1297						autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1298						autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1299					}
1300				}
1301
1302				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1303			}
1304
1305			op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1306			op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1307
1308			attrs_free( attrs );
1309
1310			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1311		}
1312	}
1313
1314	if ( op->o_tag == LDAP_REQ_MODIFY ) {
1315		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS  && !oex ) {
1316			Entry etmp;
1317			struct berval odn, ondn;
1318			Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val );
1319
1320			ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1321
1322			if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1323				LDAP_SUCCESS || e == NULL ) {
1324				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val );
1325				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1326				return SLAP_CB_CONTINUE;
1327			}
1328
1329			a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1330
1331
1332			if ( a == NULL ) {
1333				Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val );
1334				overlay_entry_release_ov( op, e, 0, on );
1335				ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1336				return SLAP_CB_CONTINUE;
1337			}
1338
1339			/* If we modify a group's memberURL, we have to delete all of it's members,
1340			   and add them anew, because we cannot tell from which memberURL a member was added. */
1341			for ( ; agd; agd = agd->agd_next ) {
1342
1343				if ( value_find_ex( slap_schema.si_ad_objectClass,
1344						SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1345						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1346						a->a_nvals, &agd->agd_oc->soc_cname,
1347						op->o_tmpmemctx ) == 0 )
1348				{
1349					Modifications	*m;
1350					int		match = 1;
1351
1352					m = op->orm_modlist;
1353
1354					for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1355						ldap_pvt_thread_mutex_lock( &age->age_mutex );
1356
1357						dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1358
1359						if ( match == 0 ) {
1360							for ( ; m ; m = m->sml_next ) {
1361								if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1362									autogroup_def_t	*group_agd = age->age_def;
1363									Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1364										op->o_req_dn.bv_val );
1365
1366									overlay_entry_release_ov( op, e, 0, on );
1367
1368									autogroup_delete_member_from_group( op, NULL, NULL, age );
1369									autogroup_delete_group( agi, age );
1370
1371									autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1372
1373									ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1374									return SLAP_CB_CONTINUE;
1375								}
1376							}
1377
1378							ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1379							break;
1380						}
1381
1382						ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1383					}
1384
1385					overlay_entry_release_ov( op, e, 0, on );
1386					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1387					return SLAP_CB_CONTINUE;
1388				}
1389			}
1390
1391			/* When modifying any of the attributes of an entry, we must
1392			   check if the entry is in any of our groups, and if
1393			   the modified entry matches any of the filters of that group.
1394
1395			   If the entry exists in a group, but the modified attributes do
1396				not match any of the group's filters, we delete the entry from that group.
1397			   If the entry doesn't exist in a group, but matches a filter,
1398				we add it to that group.
1399			*/
1400			attrs = attrs_dup( e->e_attrs );
1401			overlay_entry_release_ov( op, e, 0, on );
1402			etmp.e_name = op->o_req_dn;
1403			etmp.e_nname = op->o_req_ndn;
1404			etmp.e_attrs = attrs;
1405			odn = op->o_dn;
1406			ondn = op->o_ndn;
1407			op->o_dn = op->o_bd->be_rootdn;
1408			op->o_ndn = op->o_bd->be_rootndn;
1409
1410			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1411				is_olddn = 0;
1412				is_newdn = 0;
1413
1414				ldap_pvt_thread_mutex_lock( &age->age_mutex );
1415
1416				if ( age->age_filter && age->age_filter->agf_anlist ) {
1417					ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1418				}
1419				else {
1420					ea = NULL;
1421				}
1422
1423				if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1424					LDAP_SUCCESS || group == NULL ) {
1425					Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1426						age->age_dn.bv_val );
1427
1428					attrs_free( attrs );
1429					ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1430					ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1431					op->o_dn = odn;
1432					op->o_ndn = ondn;
1433					return SLAP_CB_CONTINUE;
1434				}
1435
1436				a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1437
1438				if ( a != NULL ) {
1439					if ( value_find_ex( age->age_def->agd_member_ad,
1440							SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1441							SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1442							a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1443					{
1444						is_olddn = 1;
1445					}
1446
1447				}
1448
1449				overlay_entry_release_ov( op, group, 0, on );
1450
1451				for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1452					if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1453						if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1454							is_newdn = 1;
1455							break;
1456						}
1457					}
1458				}
1459
1460				if ( is_olddn == 1 && is_newdn == 0 ) {
1461					if(ea)
1462						autogroup_delete_member_values_from_group( op, &op->o_req_dn, age, ea );
1463					else
1464						autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1465				} else
1466				if ( is_olddn == 0 && is_newdn == 1 ) {
1467					if(ea)
1468						autogroup_add_member_values_to_group( op, &op->o_req_dn, age, ea );
1469					else
1470						autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1471				}
1472
1473				ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1474			}
1475
1476			op->o_dn = odn;
1477			op->o_ndn = ondn;
1478			attrs_free( attrs );
1479
1480			ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1481		}
1482	}
1483
1484	return SLAP_CB_CONTINUE;
1485}
1486
1487/*
1488** Detect if filter contains a memberOf check for dn
1489*/
1490static int
1491autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1492{
1493	int result = 0;
1494	if ( f == NULL ) return 0;
1495
1496  	switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1497		case LDAP_FILTER_AND:
1498		case LDAP_FILTER_OR:
1499		case LDAP_FILTER_NOT:
1500			for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1501				result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1502			}
1503			break;
1504		case LDAP_FILTER_EQUALITY:
1505			result = ( f->f_ava->aa_desc == memberof_ad &&
1506			           ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1507			break;
1508		default:
1509			break;
1510	}
1511
1512	return result;
1513}
1514
1515/*
1516** When modifying a group, we must deny any modifications to the member attribute,
1517** because the group would be inconsistent.
1518*/
1519static int
1520autogroup_modify_entry( Operation *op, SlapReply *rs)
1521{
1522	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
1523	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
1524	autogroup_def_t		*agd = agi->agi_def;
1525	autogroup_entry_t	*age;
1526	Entry			*e;
1527	Attribute		*a;
1528	struct berval odn, ondn;
1529	OpExtra *oex;
1530
1531	LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
1532		if ( oex->oe_key == (void *)&autogroup )
1533			return SLAP_CB_CONTINUE;
1534	}
1535
1536	Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val );
1537	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1538
1539	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1540		LDAP_SUCCESS || e == NULL ) {
1541		Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val );
1542		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1543		return SLAP_CB_CONTINUE;
1544	}
1545
1546	odn = op->o_dn;
1547	ondn = op->o_ndn;
1548	op->o_dn = op->o_bd->be_rootdn;
1549	op->o_ndn = op->o_bd->be_rootndn;
1550
1551	/* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1552	for ( age = agi->agi_entry; age ; age = age->age_next ) {
1553		autogroup_filter_t	*agf;
1554		for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1555			if ( agf->agf_anlist ) {
1556				Modifications	*m;
1557				for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1558					if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1559						if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1560							int rc = test_filter( op, e, agf->agf_filter );
1561							if ( rc == LDAP_COMPARE_TRUE ) {
1562								age->age_mustrefresh = 1;
1563							}
1564						}
1565					}
1566				}
1567			}
1568
1569			if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1570				age->age_mustrefresh = 1;
1571			}
1572		}
1573	}
1574	op->o_dn = odn;
1575	op->o_ndn = ondn;
1576
1577	a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1578
1579	if ( a == NULL ) {
1580		Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val );
1581		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1582		return SLAP_CB_CONTINUE;
1583	}
1584
1585
1586	for ( ; agd; agd = agd->agd_next ) {
1587
1588		if ( value_find_ex( slap_schema.si_ad_objectClass,
1589				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1590				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1591				a->a_nvals, &agd->agd_oc->soc_cname,
1592				op->o_tmpmemctx ) == 0 )
1593		{
1594			Modifications	*m;
1595			int		match = 1;
1596
1597			m = op->orm_modlist;
1598
1599			for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1600				dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1601
1602				if ( match == 0 ) {
1603					for ( ; m ; m = m->sml_next ) {
1604						if ( m->sml_desc == age->age_def->agd_member_ad ) {
1605							overlay_entry_release_ov( op, e, 0, on );
1606							ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1607							Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val );
1608							send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1609							return LDAP_CONSTRAINT_VIOLATION;
1610						}
1611					}
1612					break;
1613				}
1614			}
1615
1616			/* an entry may only have one dynamic group class */
1617			break;
1618		}
1619	}
1620
1621	overlay_entry_release_ov( op, e, 0, on );
1622	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1623	return SLAP_CB_CONTINUE;
1624}
1625
1626/*
1627** Detect if the olddn is part of a group and so if the group should be refreshed
1628*/
1629static int
1630autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1631{
1632	slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
1633	autogroup_info_t	*agi = (autogroup_info_t *)on->on_bi.bi_private;
1634	autogroup_entry_t	*age;
1635	Entry			*e;
1636	struct berval odn, ondn;
1637	OpExtra *oex;
1638
1639	LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
1640		if ( oex->oe_key == (void *)&autogroup )
1641			return SLAP_CB_CONTINUE;
1642	}
1643
1644	Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val );
1645	ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1646
1647	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1648		LDAP_SUCCESS || e == NULL ) {
1649		Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val );
1650		ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1651		return SLAP_CB_CONTINUE;
1652	}
1653
1654	odn = op->o_dn;
1655	ondn = op->o_ndn;
1656	op->o_dn = op->o_bd->be_rootdn;
1657	op->o_ndn = op->o_bd->be_rootndn;
1658
1659	/* Must check if a dn is modified */
1660	for ( age = agi->agi_entry; age ; age = age->age_next ) {
1661		autogroup_filter_t	*agf;
1662		for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1663			if ( agf->agf_anlist ) {
1664				if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1665					int rc = test_filter( op, e, agf->agf_filter );
1666					if ( rc == LDAP_COMPARE_TRUE ) {
1667						age->age_modrdn_olddnmodified = 1;
1668					}
1669				}
1670			}
1671		}
1672	}
1673	op->o_dn = odn;
1674	op->o_ndn = ondn;
1675
1676	overlay_entry_release_ov( op, e, 0, on );
1677	ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1678	return SLAP_CB_CONTINUE;
1679}
1680
1681/*
1682** Builds a filter for searching for the
1683** group entries, according to the objectClass.
1684*/
1685static int
1686autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1687{
1688	char	*ptr;
1689
1690	Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n" );
1691
1692	op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1693			+ slap_schema.si_ad_objectClass->ad_cname.bv_len
1694			+ agd->agd_oc->soc_cname.bv_len;
1695	ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1696	*ptr++ = '(';
1697	ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1698	*ptr++ = '=';
1699	ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1700	*ptr++ = ')';
1701	*ptr = '\0';
1702
1703	op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1704
1705	assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1706
1707	return 0;
1708}
1709
1710enum {
1711	AG_ATTRSET = 1,
1712	AG_MEMBER_OF_AD,
1713	AG_LAST
1714};
1715
1716static ConfigDriver	ag_cfgen;
1717
1718static ConfigTable agcfg[] = {
1719	{ "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1720		4, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1721		"( OLcfgCtAt:2.1 NAME ( 'olcAutoGroupAttrSet' 'olcAGattrSet' ) "
1722			"DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1723			"EQUALITY caseIgnoreMatch "
1724			"SYNTAX OMsDirectoryString "
1725			"X-ORDERED 'VALUES' )",
1726			NULL, NULL },
1727
1728	{ "autogroup-memberof-ad", "memberOf attribute",
1729		2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1730		"( OLcfgCtAt:2.2 NAME ( 'olcAutoGroupMemberOfAd' 'olcAGmemberOfAd' ) "
1731			"DESC 'memberOf attribute' "
1732			"EQUALITY caseIgnoreMatch "
1733			"SYNTAX OMsDirectoryString SINGLE-VALUE )",
1734			NULL, NULL },
1735
1736	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1737};
1738
1739static ConfigOCs agocs[] = {
1740	{ "( OLcfgCtOc:2.1 "
1741		"NAME 'olcAutoGroupConfig' "
1742		"DESC 'Automatic groups configuration' "
1743		"SUP olcOverlayConfig "
1744		"MAY ( "
1745			"olcAutoGroupAttrSet "
1746			"$ olcAutoGroupMemberOfAd "
1747		    ")"
1748	  ")",
1749		Cft_Overlay, agcfg, NULL, NULL },
1750	{ NULL, 0, NULL }
1751};
1752
1753
1754static int
1755ag_cfgen( ConfigArgs *c )
1756{
1757	slap_overinst		*on = (slap_overinst *)c->bi;
1758	autogroup_info_t		*agi = (autogroup_info_t *)on->on_bi.bi_private;
1759	autogroup_def_t		*agd;
1760	autogroup_entry_t	*age;
1761
1762	int rc = 0, i;
1763
1764	Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n" );
1765
1766	if( agi == NULL ) {
1767		agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1768		ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1769		agi->agi_def = NULL;
1770		agi->agi_entry = NULL;
1771		on->on_bi.bi_private = (void *)agi;
1772	}
1773
1774	agd = agi->agi_def;
1775	age = agi->agi_entry;
1776
1777	if ( c->op == SLAP_CONFIG_EMIT ) {
1778
1779		switch( c->type ){
1780		case AG_ATTRSET:
1781			for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1782				struct berval	bv;
1783				char		*ptr = c->cr_msg;
1784
1785				assert(agd->agd_oc != NULL);
1786				assert(agd->agd_member_url_ad != NULL);
1787				assert(agd->agd_member_ad != NULL);
1788
1789				ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1790					SLAP_X_ORDERED_FMT "%s %s %s", i,
1791					agd->agd_oc->soc_cname.bv_val,
1792					agd->agd_member_url_ad->ad_cname.bv_val,
1793					agd->agd_member_ad->ad_cname.bv_val );
1794
1795				bv.bv_val = c->cr_msg;
1796				bv.bv_len = ptr - bv.bv_val;
1797				value_add_one ( &c->rvalue_vals, &bv );
1798
1799			}
1800			break;
1801
1802		case AG_MEMBER_OF_AD:
1803			if ( agi->agi_memberof_ad != NULL ){
1804				value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1805			}
1806			break;
1807
1808		default:
1809			assert( 0 );
1810			return 1;
1811      }
1812
1813		return rc;
1814
1815	}else if ( c->op == LDAP_MOD_DELETE ) {
1816		if ( c->valx < 0) {
1817			autogroup_def_t 		*agd_next;
1818			autogroup_entry_t	*age_next;
1819			autogroup_filter_t	*agf = age->age_filter,
1820						*agf_next;
1821
1822			for ( agd_next = agd; agd_next; agd = agd_next ) {
1823				agd_next = agd->agd_next;
1824
1825				ch_free( agd );
1826			}
1827
1828			for ( age_next = age ; age_next ; age = age_next ) {
1829				age_next = age->age_next;
1830
1831				ch_free( age->age_dn.bv_val );
1832				ch_free( age->age_ndn.bv_val );
1833
1834				for( agf_next = agf ; agf_next ; agf = agf_next ){
1835					agf_next = agf->agf_next;
1836
1837					filter_free( agf->agf_filter );
1838					ch_free( agf->agf_filterstr.bv_val );
1839					ch_free( agf->agf_dn.bv_val );
1840					ch_free( agf->agf_ndn.bv_val );
1841					anlist_free( agf->agf_anlist, 1, NULL );
1842					ch_free( agf );
1843				}
1844
1845				ldap_pvt_thread_mutex_init( &age->age_mutex );
1846				ch_free( age );
1847			}
1848
1849			ch_free( agi );
1850			on->on_bi.bi_private = NULL;
1851
1852		} else {
1853			autogroup_def_t		**agdp;
1854			autogroup_entry_t	*age_next, *age_prev;
1855			autogroup_filter_t	*agf,
1856						*agf_next;
1857
1858			for ( i = 0, agdp = &agi->agi_def;
1859				i < c->valx; i++ )
1860			{
1861				if ( *agdp == NULL) {
1862					return 1;
1863				}
1864				agdp = &(*agdp)->agd_next;
1865			}
1866
1867			agd = *agdp;
1868			*agdp = agd->agd_next;
1869
1870			for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1871				age_next = age->age_next;
1872
1873				if( age->age_def == agd ) {
1874					agf = age->age_filter;
1875
1876					ch_free( age->age_dn.bv_val );
1877					ch_free( age->age_ndn.bv_val );
1878
1879					for ( agf_next = agf; agf_next ; agf = agf_next ) {
1880						agf_next = agf->agf_next;
1881						filter_free( agf->agf_filter );
1882						ch_free( agf->agf_filterstr.bv_val );
1883						ch_free( agf->agf_dn.bv_val );
1884						ch_free( agf->agf_ndn.bv_val );
1885						anlist_free( agf->agf_anlist, 1, NULL );
1886						ch_free( agf );
1887					}
1888
1889					ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1890					ch_free( age );
1891
1892					age = age_prev;
1893
1894					if( age_prev != NULL ) {
1895						age_prev->age_next = age_next;
1896					}
1897				}
1898			}
1899
1900			ch_free( agd );
1901			agd = agi->agi_def;
1902
1903		}
1904
1905		return rc;
1906	}
1907
1908	switch(c->type){
1909	case AG_ATTRSET: {
1910		autogroup_def_t		**agdp,
1911					*agd_next = NULL;
1912		ObjectClass		*oc = NULL;
1913		AttributeDescription	*member_url_ad = NULL,
1914					*member_ad = NULL;
1915		const char		*text;
1916
1917
1918		oc = oc_find( c->argv[ 1 ] );
1919		if( oc == NULL ){
1920			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1921				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1922				"unable to find ObjectClass \"%s\"",
1923				c->argv[ 1 ] );
1924			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1925				c->log, c->cr_msg );
1926			return 1;
1927		}
1928
1929
1930		rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1931		if( rc != LDAP_SUCCESS ) {
1932			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1933				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1934				"unable to find AttributeDescription \"%s\"",
1935				c->argv[ 2 ] );
1936			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1937				c->log, c->cr_msg );
1938			return 1;
1939		}
1940
1941		if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1942			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1943				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1944				"AttributeDescription \"%s\" ",
1945				"must be of a subtype \"labeledURI\"",
1946				c->argv[ 2 ] );
1947			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1948				c->log, c->cr_msg );
1949			return 1;
1950		}
1951
1952		rc = slap_str2ad( c->argv[3], &member_ad, &text );
1953		if( rc != LDAP_SUCCESS ) {
1954			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1955				"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1956				"unable to find AttributeDescription \"%s\"",
1957				c->argv[ 3 ] );
1958			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1959				c->log, c->cr_msg );
1960			return 1;
1961		}
1962
1963		for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1964			/* The same URL attribute / member attribute pair
1965			* cannot be repeated */
1966
1967			if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1968				snprintf( c->cr_msg, sizeof( c->cr_msg ),
1969					"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1970					"URL attributeDescription \"%s\" already mapped",
1971					member_ad->ad_cname.bv_val );
1972				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1973					c->log, c->cr_msg );
1974/*				return 1; //warning*/
1975			}
1976		}
1977
1978		if ( c->valx > 0 ) {
1979			int	i;
1980
1981			for ( i = 0, agdp = &agi->agi_def ;
1982				i < c->valx; i++ )
1983			{
1984				if ( *agdp == NULL ) {
1985					snprintf( c->cr_msg, sizeof( c->cr_msg ),
1986						"\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1987						"invalid index {%d}",
1988						c->valx );
1989					Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1990						c->log, c->cr_msg );
1991
1992					return 1;
1993				}
1994				agdp = &(*agdp)->agd_next;
1995			}
1996			agd_next = *agdp;
1997
1998		} else {
1999			for ( agdp = &agi->agi_def; *agdp;
2000				agdp = &(*agdp)->agd_next )
2001				/* goto last */;
2002		}
2003
2004		*agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
2005
2006		(*agdp)->agd_oc = oc;
2007		(*agdp)->agd_member_url_ad = member_url_ad;
2008		(*agdp)->agd_member_ad = member_ad;
2009		(*agdp)->agd_next = agd_next;
2010
2011		} break;
2012
2013	case AG_MEMBER_OF_AD: {
2014		AttributeDescription *memberof_ad = NULL;
2015		const char     *text;
2016
2017		rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
2018		if( rc != LDAP_SUCCESS ) {
2019			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2020				"\"autogroup-memberof-ad <memberof-ad>\": "
2021				"unable to find AttributeDescription \"%s\"",
2022				c->argv[ 1 ] );
2023			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2024				c->log, c->cr_msg );
2025			return 1;
2026		}
2027
2028		if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX )    /* e.g. "member" */
2029		     && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) )  /* e.g. "uniqueMember" */
2030		{
2031			snprintf( c->cr_msg, sizeof( c->cr_msg ),
2032				"memberof attribute=\"%s\" must either "
2033				"have DN (%s) or nameUID (%s) syntax",
2034				c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
2035			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
2036				c->log, c->cr_msg );
2037			return 1;
2038		}
2039
2040		agi->agi_memberof_ad = memberof_ad;
2041
2042		} break;
2043
2044	default:
2045		rc = 1;
2046		break;
2047	}
2048
2049	return rc;
2050}
2051
2052extern int slapMode;
2053
2054/*
2055** Do a search for all the groups in the
2056** database, and add them to out internal list.
2057*/
2058static int
2059autogroup_db_open(
2060	BackendDB	*be,
2061	ConfigReply	*cr )
2062{
2063	slap_overinst			*on = (slap_overinst *) be->bd_info;
2064	autogroup_info_t		*agi = on->on_bi.bi_private;
2065	autogroup_def_t		*agd;
2066	autogroup_sc_t		ags;
2067	Operation		*op;
2068	slap_callback		cb = { 0 };
2069
2070	void				*thrctx = ldap_pvt_thread_pool_context();
2071	Connection			conn = { 0 };
2072	OperationBuffer 	opbuf;
2073
2074	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n" );
2075
2076	if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
2077		return 0;
2078	}
2079
2080	connection_fake_init2( &conn, &opbuf, thrctx, 0 );
2081	op = &opbuf.ob_op;
2082
2083	op->ors_attrsonly = 0;
2084	op->o_tag = LDAP_REQ_SEARCH;
2085	op->o_dn = be->be_rootdn;
2086	op->o_ndn = be->be_rootndn;
2087
2088	op->o_req_dn = be->be_suffix[0];
2089	op->o_req_ndn = be->be_nsuffix[0];
2090
2091	op->ors_scope = LDAP_SCOPE_SUBTREE;
2092	op->ors_deref = LDAP_DEREF_NEVER;
2093	op->ors_limit = NULL;
2094	op->ors_tlimit = SLAP_NO_LIMIT;
2095	op->ors_slimit = SLAP_NO_LIMIT;
2096	op->ors_attrs =  slap_anlist_no_attrs;
2097	op->o_do_not_cache = 1;
2098
2099	op->o_bd = be;
2100	op->o_bd->bd_info = (BackendInfo *)on->on_info;
2101
2102	ags.ags_info = agi;
2103	cb.sc_private = &ags;
2104	cb.sc_response = autogroup_group_add_cb;
2105	cb.sc_cleanup = NULL;
2106	cb.sc_next = NULL;
2107
2108	op->o_callback = &cb;
2109
2110	for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
2111		SlapReply	rs = { REP_RESULT };
2112
2113		autogroup_build_def_filter(agd, op);
2114
2115		ags.ags_def = agd;
2116
2117		op->o_bd->be_search( op, &rs );
2118
2119		filter_free_x( op, op->ors_filter, 1 );
2120		op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
2121	}
2122
2123	if( ! agi->agi_memberof_ad ){
2124		int			rc;
2125		const char		*text = NULL;
2126
2127		rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
2128		if ( rc != LDAP_SUCCESS ) {
2129			Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
2130			"unable to find attribute=\"%s\": %s (%d)\n",
2131			SLAPD_MEMBEROF_ATTR, text, rc );
2132			return rc;
2133		}
2134	}
2135
2136	return 0;
2137}
2138
2139static int
2140autogroup_db_close(
2141	BackendDB	*be,
2142	ConfigReply	*cr )
2143{
2144	slap_overinst			*on = (slap_overinst *) be->bd_info;
2145
2146	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n" );
2147
2148	if ( on->on_bi.bi_private ) {
2149		autogroup_info_t		*agi = on->on_bi.bi_private;
2150		autogroup_entry_t	*age = agi->agi_entry,
2151					*age_next;
2152		autogroup_filter_t	*agf, *agf_next;
2153
2154		for ( age_next = age; age_next; age = age_next ) {
2155			age_next = age->age_next;
2156
2157			ch_free( age->age_dn.bv_val );
2158			ch_free( age->age_ndn.bv_val );
2159
2160			agf = age->age_filter;
2161
2162			for ( agf_next = agf; agf_next; agf = agf_next ) {
2163				agf_next = agf->agf_next;
2164
2165				filter_free( agf->agf_filter );
2166				ch_free( agf->agf_filterstr.bv_val );
2167				ch_free( agf->agf_dn.bv_val );
2168				ch_free( agf->agf_ndn.bv_val );
2169				anlist_free( agf->agf_anlist, 1, NULL );
2170				ch_free( agf );
2171			}
2172
2173			ldap_pvt_thread_mutex_destroy( &age->age_mutex );
2174			ch_free( age );
2175		}
2176	}
2177
2178	return 0;
2179}
2180
2181static int
2182autogroup_db_destroy(
2183	BackendDB	*be,
2184	ConfigReply	*cr )
2185{
2186	slap_overinst			*on = (slap_overinst *) be->bd_info;
2187
2188	Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n" );
2189
2190	if ( on->on_bi.bi_private ) {
2191		autogroup_info_t		*agi = on->on_bi.bi_private;
2192		autogroup_def_t		*agd = agi->agi_def,
2193					*agd_next;
2194
2195		for ( agd_next = agd; agd_next; agd = agd_next ) {
2196			agd_next = agd->agd_next;
2197
2198			ch_free( agd );
2199		}
2200
2201		ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2202		ch_free( agi );
2203	}
2204
2205	return 0;
2206}
2207
2208static
2209int
2210autogroup_initialize(void)
2211{
2212	int		rc = 0;
2213	autogroup.on_bi.bi_type = "autogroup";
2214
2215	autogroup.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
2216	autogroup.on_bi.bi_db_open = autogroup_db_open;
2217	autogroup.on_bi.bi_db_close = autogroup_db_close;
2218	autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2219
2220	autogroup.on_bi.bi_op_add = autogroup_add_entry;
2221	autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2222	autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2223	autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2224
2225	autogroup.on_response = autogroup_response;
2226
2227	autogroup.on_bi.bi_cf_ocs = agocs;
2228
2229	rc = config_register_schema( agcfg, agocs );
2230	if ( rc ) {
2231		return rc;
2232	}
2233
2234	return overlay_register( &autogroup );
2235}
2236
2237int
2238init_module( int argc, char *argv[] )
2239{
2240	return autogroup_initialize();
2241}
2242