1 /* autogroup.c - automatic group overlay */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2007-2015 The OpenLDAP Foundation.
6 * Portions Copyright 2007 Michał Szulczyński.
7 * Portions Copyright 2009 Howard Chu.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
19 * This work was initially developed by Michał Szulczyński for inclusion in
20 * OpenLDAP Software. Additional significant contributors include:
31 #include <ac/string.h>
37 #ifndef SLAPD_MEMBEROF_ATTR
38 #define SLAPD_MEMBEROF_ATTR "memberOf"
41 /* Filter represents the memberURL of a group. */
42 typedef struct autogroup_filter_t {
43 struct berval agf_dn; /* The base DN in memberURL */
44 struct berval agf_ndn;
45 struct berval agf_filterstr;
48 AttributeName *agf_anlist;
49 struct autogroup_filter_t *agf_next;
52 /* Description of group attributes. */
53 typedef struct autogroup_def_t {
55 AttributeDescription *agd_member_url_ad;
56 AttributeDescription *agd_member_ad;
57 struct autogroup_def_t *agd_next;
60 /* Represents the group entry. */
61 typedef struct autogroup_entry_t {
64 autogroup_filter_t *age_filter; /* List of filters made from memberURLs */
65 autogroup_def_t *age_def; /* Attribute definition */
66 ldap_pvt_thread_mutex_t age_mutex;
67 int age_mustrefresh; /* Defined in request to refresh in response */
68 int age_modrdn_olddnmodified; /* Defined in request to refresh in response */
69 struct autogroup_entry_t *age_next;
72 /* Holds pointers to attribute definitions and groups. */
73 typedef struct autogroup_info_t {
74 autogroup_def_t *agi_def; /* Group attributes definitions. */
75 autogroup_entry_t *agi_entry; /* Group entries. */
76 AttributeDescription *agi_memberof_ad; /* memberOf attribute description */
77 ldap_pvt_thread_mutex_t agi_mutex;
80 /* Search callback for adding groups initially. */
81 typedef struct autogroup_sc_t {
82 autogroup_info_t *ags_info; /* Group definitions and entries. */
83 autogroup_def_t *ags_def; /* Attributes definition of the group being added. */
86 /* Used for adding members, found when searching, to a group. */
87 typedef struct autogroup_ga_t {
88 autogroup_entry_t *agg_group; /* The group to which the members will be added. */
89 autogroup_filter_t *agg_filter; /* Current filter */
90 Entry *agg_entry; /* Used in autogroup_member_search_cb to modify
91 this entry with the search results. */
93 Modifications *agg_mod; /* Used in autogroup_member_search_modify_cb to hold the
94 search results which will be added to the group. */
96 Modifications *agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't
97 have to search for the last mod added. */
102 ** dn, ndn - the DN of the member to add
103 ** age - the group to which the member DN will be added
106 autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
108 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
109 Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
110 SlapReply sreply = {REP_RESULT};
111 BerValue *vals, *nvals;
112 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
114 unsigned long opid = op->o_opid;
116 assert( dn != NULL );
117 assert( ndn != NULL );
118 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
119 dn->bv_val, age->age_dn.bv_val, 0);
121 vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
122 nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
123 ber_dupbv( vals, dn );
124 BER_BVZERO( &vals[ 1 ] );
125 ber_dupbv( nvals, ndn );
126 BER_BVZERO( &nvals[ 1 ] );
128 modlist->sml_op = LDAP_MOD_ADD;
129 modlist->sml_desc = age->age_def->agd_member_ad;
130 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
131 modlist->sml_values = vals;
132 modlist->sml_nvalues = nvals;
133 modlist->sml_numvals = 1;
134 modlist->sml_flags = SLAP_MOD_INTERNAL;
135 modlist->sml_next = NULL;
137 o.o_opid = 0; /* shared with op, saved above */
138 o.o_tag = LDAP_REQ_MODIFY;
140 o.orm_modlist = modlist;
141 o.o_dn = op->o_bd->be_rootdn;
142 o.o_ndn = op->o_bd->be_rootndn;
143 o.o_req_dn = age->age_dn;
144 o.o_req_ndn = age->age_ndn;
145 o.o_permissive_modify = 1;
146 o.o_dont_replicate = 1;
147 o.orm_no_opattrs = 1;
148 o.o_managedsait = SLAP_CONTROL_CRITICAL;
149 o.o_relax = SLAP_CONTROL_CRITICAL;
151 o.o_bd->bd_info = (BackendInfo *)on->on_info;
152 (void)op->o_bd->be_modify( &o, &sreply );
153 o.o_bd->bd_info = (BackendInfo *)on;
155 slap_mods_free( modlist, 1 );
158 return sreply.sr_err;
162 ** e - the entry where to get the attribute values
163 ** age - the group to which the values will be added
166 autogroup_add_member_values_to_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
168 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
169 Modifications modlist;
170 SlapReply sreply = {REP_RESULT};
171 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
173 unsigned long opid = op->o_opid;
175 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
176 dn->bv_val, age->age_dn.bv_val, 0);
178 modlist.sml_op = LDAP_MOD_ADD;
179 modlist.sml_desc = age->age_def->agd_member_ad;
180 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
181 modlist.sml_values = attr->a_vals;
182 modlist.sml_nvalues = attr->a_nvals;
183 modlist.sml_numvals = attr->a_numvals;
184 modlist.sml_flags = SLAP_MOD_INTERNAL;
185 modlist.sml_next = NULL;
188 o.o_tag = LDAP_REQ_MODIFY;
190 o.orm_modlist = &modlist;
191 o.o_dn = op->o_bd->be_rootdn;
192 o.o_ndn = op->o_bd->be_rootndn;
193 o.o_req_dn = age->age_dn;
194 o.o_req_ndn = age->age_ndn;
195 o.o_permissive_modify = 1;
196 o.o_dont_replicate = 1;
197 o.orm_no_opattrs = 1;
198 o.o_managedsait = SLAP_CONTROL_CRITICAL;
199 o.o_relax = SLAP_CONTROL_CRITICAL;
201 o.o_bd->bd_info = (BackendInfo *)on->on_info;
202 (void)op->o_bd->be_modify( &o, &sreply );
203 o.o_bd->bd_info = (BackendInfo *)on;
206 return sreply.sr_err;
210 ** dn,ndn - the DN to be deleted
211 ** age - the group from which the DN will be deleted
212 ** If we pass a NULL dn and ndn, all members are deleted from the group.
215 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
217 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
218 Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
219 SlapReply sreply = {REP_RESULT};
220 BerValue *vals, *nvals;
221 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
223 unsigned long opid = op->o_opid;
225 if ( dn == NULL || ndn == NULL ) {
226 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
227 age->age_dn.bv_val, 0 ,0);
229 modlist->sml_values = NULL;
230 modlist->sml_nvalues = NULL;
231 modlist->sml_numvals = 0;
233 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
234 dn->bv_val, age->age_dn.bv_val, 0);
236 vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
237 nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
238 ber_dupbv( vals, dn );
239 BER_BVZERO( &vals[ 1 ] );
240 ber_dupbv( nvals, ndn );
241 BER_BVZERO( &nvals[ 1 ] );
243 modlist->sml_values = vals;
244 modlist->sml_nvalues = nvals;
245 modlist->sml_numvals = 1;
249 modlist->sml_op = LDAP_MOD_DELETE;
250 modlist->sml_desc = age->age_def->agd_member_ad;
251 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
252 modlist->sml_flags = SLAP_MOD_INTERNAL;
253 modlist->sml_next = NULL;
257 o.o_tag = LDAP_REQ_MODIFY;
258 o.orm_modlist = modlist;
259 o.o_dn = op->o_bd->be_rootdn;
260 o.o_ndn = op->o_bd->be_rootndn;
261 o.o_req_dn = age->age_dn;
262 o.o_req_ndn = age->age_ndn;
263 o.o_relax = SLAP_CONTROL_CRITICAL;
264 o.o_managedsait = SLAP_CONTROL_CRITICAL;
265 o.o_permissive_modify = 1;
266 o.o_dont_replicate = 1;
267 o.orm_no_opattrs = 1;
269 o.o_bd->bd_info = (BackendInfo *)on->on_info;
270 (void)op->o_bd->be_modify( &o, &sreply );
271 o.o_bd->bd_info = (BackendInfo *)on;
273 slap_mods_free( modlist, 1 );
276 return sreply.sr_err;
280 ** e - the entry where to get the attribute values
281 ** age - the group from which the values will be deleted
284 autogroup_delete_member_values_from_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
286 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
287 Modifications modlist;
288 SlapReply sreply = {REP_RESULT};
289 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
291 unsigned long opid = op->o_opid;
293 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
294 dn->bv_val, age->age_dn.bv_val, 0);
296 modlist.sml_op = LDAP_MOD_DELETE;
297 modlist.sml_desc = age->age_def->agd_member_ad;
298 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
299 modlist.sml_values = attr->a_vals;
300 modlist.sml_nvalues = attr->a_nvals;
301 modlist.sml_numvals = attr->a_numvals;
302 modlist.sml_flags = SLAP_MOD_INTERNAL;
303 modlist.sml_next = NULL;
306 o.o_tag = LDAP_REQ_MODIFY;
308 o.orm_modlist = &modlist;
309 o.o_dn = op->o_bd->be_rootdn;
310 o.o_ndn = op->o_bd->be_rootndn;
311 o.o_req_dn = age->age_dn;
312 o.o_req_ndn = age->age_ndn;
313 o.o_permissive_modify = 1;
314 o.o_dont_replicate = 1;
315 o.orm_no_opattrs = 1;
316 o.o_managedsait = SLAP_CONTROL_CRITICAL;
317 o.o_relax = SLAP_CONTROL_CRITICAL;
319 o.o_bd->bd_info = (BackendInfo *)on->on_info;
320 (void)op->o_bd->be_modify( &o, &sreply );
321 o.o_bd->bd_info = (BackendInfo *)on;
324 return sreply.sr_err;
328 ** Callback used to add entries to a group,
329 ** which are going to be written in the database
330 ** (used in bi_op_add)
331 ** The group is passed in autogroup_ga_t->agg_group
334 autogroup_member_search_cb( Operation *op, SlapReply *rs )
336 assert( op->o_tag == LDAP_REQ_SEARCH );
338 if ( rs->sr_type == REP_SEARCH ) {
339 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
340 autogroup_entry_t *age = agg->agg_group;
341 autogroup_filter_t *agf = agg->agg_filter;
343 const char *text = NULL;
345 struct berval *vals, *nvals;
346 struct berval lvals[ 2 ], lnvals[ 2 ];
349 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
350 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
352 if ( agf->agf_anlist ) {
353 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
356 nvals = attr->a_nvals;
357 numvals = attr->a_numvals;
363 lvals[ 0 ] = rs->sr_entry->e_name;
364 BER_BVZERO( &lvals[ 1 ] );
365 lnvals[ 0 ] = rs->sr_entry->e_nname;
366 BER_BVZERO( &lnvals[ 1 ] );
372 mod.sm_op = LDAP_MOD_ADD;
373 mod.sm_desc = age->age_def->agd_member_ad;
374 mod.sm_type = age->age_def->agd_member_ad->ad_cname;
375 mod.sm_values = vals;
376 mod.sm_nvalues = nvals;
377 mod.sm_numvals = numvals;
379 modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
386 ** Callback used to add entries to a group, which is already in the database.
387 ** (used in on_response)
388 ** The group is passed in autogroup_ga_t->agg_group
392 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
394 assert( op->o_tag == LDAP_REQ_SEARCH );
396 if ( rs->sr_type == REP_SEARCH ) {
397 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
398 autogroup_entry_t *age = agg->agg_group;
399 autogroup_filter_t *agf = agg->agg_filter;
400 Modifications *modlist;
401 struct berval *vals, *nvals;
402 struct berval lvals[ 2 ], lnvals[ 2 ];
405 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
406 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
408 if ( agf->agf_anlist ) {
409 Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
412 nvals = attr->a_nvals;
413 numvals = attr->a_numvals;
419 lvals[ 0 ] = rs->sr_entry->e_name;
420 BER_BVZERO( &lvals[ 1 ] );
421 lnvals[ 0 ] = rs->sr_entry->e_nname;
422 BER_BVZERO( &lnvals[ 1 ] );
429 modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
431 modlist->sml_op = LDAP_MOD_ADD;
432 modlist->sml_desc = age->age_def->agd_member_ad;
433 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
435 ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
436 ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
437 modlist->sml_numvals = numvals;
439 modlist->sml_flags = SLAP_MOD_INTERNAL;
440 modlist->sml_next = NULL;
442 if ( agg->agg_mod == NULL ) {
443 agg->agg_mod = modlist;
444 agg->agg_mod_last = modlist;
446 agg->agg_mod_last->sml_next = modlist;
447 agg->agg_mod_last = modlist;
458 ** Adds all entries matching the passed filter to the specified group.
459 ** If modify == 1, then we modify the group's entry in the database using be_modify.
460 ** If modify == 0, then, we must supply a rw entry for the group,
461 ** because we only modify the entry, without calling be_modify.
462 ** e - the group entry, to which the members will be added
467 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
469 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
471 SlapReply rs = { REP_SEARCH };
472 slap_callback cb = { 0 };
473 slap_callback null_cb = { NULL, slap_null_cb, NULL, NULL };
476 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
477 age->age_dn.bv_val, 0, 0);
480 o.o_tag = LDAP_REQ_SEARCH;
482 o.o_dn = op->o_bd->be_rootdn;
483 o.o_ndn = op->o_bd->be_rootndn;
484 o.o_req_dn = agf->agf_dn;
485 o.o_req_ndn = agf->agf_ndn;
487 o.ors_filterstr = agf->agf_filterstr;
488 o.ors_filter = agf->agf_filter;
490 o.ors_scope = agf->agf_scope;
491 o.ors_deref = LDAP_DEREF_NEVER;
493 o.ors_tlimit = SLAP_NO_LIMIT;
494 o.ors_slimit = SLAP_NO_LIMIT;
495 o.ors_attrs = agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
496 o.o_do_not_cache = 1;
499 agg.agg_filter = agf;
501 agg.agg_mod_last = NULL;
503 cb.sc_private = &agg;
506 cb.sc_response = autogroup_member_search_modify_cb;
508 cb.sc_response = autogroup_member_search_cb;
511 cb.sc_cleanup = NULL;
516 o.o_bd->bd_info = (BackendInfo *)on->on_info;
517 op->o_bd->be_search( &o, &rs );
518 o.o_bd->bd_info = (BackendInfo *)on;
520 if ( modify == 1 && agg.agg_mod ) {
521 unsigned long opid = op->o_opid;
523 rs_reinit( &rs, REP_RESULT );
527 o.o_callback = &null_cb;
528 o.o_tag = LDAP_REQ_MODIFY;
529 o.orm_modlist = agg.agg_mod;
530 o.o_dn = op->o_bd->be_rootdn;
531 o.o_ndn = op->o_bd->be_rootndn;
532 o.o_req_dn = age->age_dn;
533 o.o_req_ndn = age->age_ndn;
534 o.o_relax = SLAP_CONTROL_CRITICAL;
535 o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
536 o.o_permissive_modify = 1;
537 o.o_dont_replicate = 1;
538 o.orm_no_opattrs = 1;
540 o.o_bd->bd_info = (BackendInfo *)on->on_info;
541 (void)op->o_bd->be_modify( &o, &rs );
542 o.o_bd->bd_info = (BackendInfo *)on;
544 slap_mods_free(agg.agg_mod, 1);
552 ** Adds a group to the internal list from the passed entry.
553 ** scan specifies whether to add all maching members to the group.
554 ** modify specifies whether to modify the given group entry (when modify == 0),
555 ** or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
556 ** agi - pointer to the groups and the attribute definitions
557 ** agd - the attribute definition of the added group
558 ** e - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
559 ** ndn - the DN of the group, can be NULL if we give a non-NULL e
562 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
564 autogroup_entry_t **agep = &agi->agi_entry;
565 autogroup_filter_t *agf, *agf_prev = NULL;
566 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
567 LDAPURLDesc *lud = NULL;
570 int rc = 0, match = 1, null_entry = 0;
573 if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
574 LDAP_SUCCESS || e == NULL ) {
575 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
582 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
583 e->e_name.bv_val, 0, 0);
585 if ( agi->agi_entry != NULL ) {
586 for ( ; *agep ; agep = &(*agep)->age_next ) {
587 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
589 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
597 *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
598 ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
599 (*agep)->age_def = agd;
600 (*agep)->age_filter = NULL;
601 (*agep)->age_mustrefresh = 0;
602 (*agep)->age_modrdn_olddnmodified = 0;
604 ber_dupbv( &(*agep)->age_dn, &e->e_name );
605 ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
607 a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
609 if ( null_entry == 1 ) {
611 overlay_entry_release_ov( op, e, 0, on );
615 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
617 for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
619 agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
621 if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
622 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
628 agf->agf_scope = lud->lud_scope;
630 if ( lud->lud_dn == NULL ) {
631 BER_BVSTR( &dn, "" );
633 ber_str2bv( lud->lud_dn, 0, 0, &dn );
636 rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
637 if ( rc != LDAP_SUCCESS ) {
638 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
643 if ( lud->lud_filter != NULL ) {
644 ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
645 agf->agf_filter = str2filter( lud->lud_filter );
647 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: URL filter is missing <%s>\n", bv->bv_val,0,0);
652 if ( lud->lud_attrs != NULL ) {
655 for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
660 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
663 filter_free( agf->agf_filter );
664 ch_free( agf->agf_filterstr.bv_val );
665 ch_free( agf->agf_dn.bv_val );
666 ch_free( agf->agf_ndn.bv_val );
667 ldap_free_urldesc( lud );
672 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
674 if ( agf->agf_anlist == NULL ) {
675 Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
676 lud->lud_attrs[0], 0, 0 );
678 filter_free( agf->agf_filter );
679 ch_free( agf->agf_filterstr.bv_val );
680 ch_free( agf->agf_dn.bv_val );
681 ch_free( agf->agf_ndn.bv_val );
682 ldap_free_urldesc( lud );
688 agf->agf_next = NULL;
690 if( (*agep)->age_filter == NULL ) {
691 (*agep)->age_filter = agf;
694 if( agf_prev != NULL ) {
695 agf_prev->agf_next = agf;
701 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
704 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
705 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
707 ldap_free_urldesc( lud );
714 ch_free( agf->agf_ndn.bv_val );
715 ch_free( agf->agf_dn.bv_val );
716 ldap_free_urldesc( lud );
721 if ( null_entry == 1 ) {
728 ** Used when opening the database to add all existing
729 ** groups from the database to our internal list.
732 autogroup_group_add_cb( Operation *op, SlapReply *rs )
734 assert( op->o_tag == LDAP_REQ_SEARCH );
736 if ( rs->sr_type == REP_SEARCH ) {
737 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
739 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
740 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
742 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
748 typedef struct ag_addinfo {
751 autogroup_def_t *agd;
755 autogroup_add_entry_cb( Operation *op, SlapReply *rs )
757 slap_callback *sc = op->o_callback;
758 ag_addinfo *aa = sc->sc_private;
759 slap_overinst *on = aa->on;
760 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
761 BackendInfo *bi = op->o_bd->bd_info;
763 if ( rs->sr_err != LDAP_SUCCESS )
766 op->o_bd->bd_info = (BackendInfo *)on;
767 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
769 autogroup_add_group( op, agi, aa->agd, aa->e, NULL, 1 , 0);
771 autogroup_entry_t *age;
772 autogroup_filter_t *agf;
774 for ( age = agi->agi_entry; age ; age = age->age_next ) {
775 ldap_pvt_thread_mutex_lock( &age->age_mutex );
777 /* Check if any of the filters are the suffix to the entry DN.
778 If yes, we can test that filter against the entry. */
780 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
781 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
782 rc = test_filter( op, aa->e, agf->agf_filter );
783 if ( rc == LDAP_COMPARE_TRUE ) {
784 if ( agf->agf_anlist ) {
785 Attribute *a = attr_find( aa->e->e_attrs, agf->agf_anlist[0].an_desc );
787 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, a );
789 autogroup_add_member_to_group( op, &aa->e->e_name, &aa->e->e_nname, age );
795 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
798 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
800 op->o_bd->bd_info = bi;
803 op->o_callback = sc->sc_next;
804 op->o_tmpfree( sc, op->o_tmpmemctx );
806 return SLAP_CB_CONTINUE;
810 ** When adding a group, we first strip any existing members,
811 ** and add all which match the filters ourselfs.
814 autogroup_add_entry( Operation *op, SlapReply *rs)
816 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
817 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
818 autogroup_def_t *agd = agi->agi_def;
819 slap_callback *sc = NULL;
820 ag_addinfo *aa = NULL;
823 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
824 op->ora_e->e_name.bv_val, 0, 0);
826 sc = op->o_tmpcalloc( sizeof(slap_callback) + sizeof(ag_addinfo), 1, op->o_tmpmemctx );
827 sc->sc_private = (sc+1);
828 sc->sc_response = autogroup_add_entry_cb;
832 sc->sc_next = op->o_callback;
835 /* Check if it's a group. */
836 for ( ; agd ; agd = agd->agd_next ) {
837 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
839 const char *text = NULL;
842 mod.sm_op = LDAP_MOD_DELETE;
843 mod.sm_desc = agd->agd_member_ad;
844 mod.sm_type = agd->agd_member_ad->ad_cname;
845 mod.sm_values = NULL;
846 mod.sm_nvalues = NULL;
848 /* We don't want any member attributes added by the user. */
849 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
857 return SLAP_CB_CONTINUE;
861 ** agi - internal group and attribute definitions list
862 ** e - the group to remove from the internal list
865 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
867 autogroup_entry_t *age = agi->agi_entry,
872 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
873 age->age_dn.bv_val, 0, 0);
875 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
876 age_next = age->age_next;
879 autogroup_filter_t *agf = age->age_filter,
882 if ( age_prev != NULL ) {
883 age_prev->age_next = age_next;
885 agi->agi_entry = NULL;
888 ch_free( age->age_dn.bv_val );
889 ch_free( age->age_ndn.bv_val );
891 for( agf_next = agf ; agf_next ; agf = agf_next ){
892 agf_next = agf->agf_next;
894 filter_free( agf->agf_filter );
895 ch_free( agf->agf_filterstr.bv_val );
896 ch_free( agf->agf_dn.bv_val );
897 ch_free( agf->agf_ndn.bv_val );
898 anlist_free( agf->agf_anlist, 1, NULL );
902 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
903 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
912 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
919 autogroup_delete_entry( Operation *op, SlapReply *rs)
921 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
922 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
923 autogroup_entry_t *age, *age_prev, *age_next;
924 autogroup_filter_t *agf;
926 int matched_group = 0, rc = 0;
928 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
930 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
932 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
933 LDAP_SUCCESS || e == NULL ) {
934 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
935 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
936 return SLAP_CB_CONTINUE;
939 /* Check if the entry to be deleted is one of our groups. */
940 for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
942 ldap_pvt_thread_mutex_lock( &age->age_mutex );
943 age_next = age->age_next;
945 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
950 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
953 autogroup_delete_group( agi, age );
958 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
961 if ( matched_group == 1 ) {
962 overlay_entry_release_ov( op, e, 0, on );
963 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
964 return SLAP_CB_CONTINUE;
967 /* Check if the entry matches any of the groups.
968 If yes, we can delete the entry from that group. */
970 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
971 ldap_pvt_thread_mutex_lock( &age->age_mutex );
973 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
974 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
975 rc = test_filter( op, e, agf->agf_filter );
976 if ( rc == LDAP_COMPARE_TRUE ) {
977 /* If the attribute is retrieved from the entry, we don't know what to delete
978 ** So the group must be entirely refreshed
979 ** But the refresh can't be done now because the entry is not deleted
980 ** So the group is marked as mustrefresh
982 if ( agf->agf_anlist ) {
983 age->age_mustrefresh = 1;
985 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
991 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
994 overlay_entry_release_ov( op, e, 0, on );
995 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
997 return SLAP_CB_CONTINUE;
1001 autogroup_response( Operation *op, SlapReply *rs )
1003 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1004 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1005 autogroup_def_t *agd = agi->agi_def;
1006 autogroup_entry_t *age;
1007 autogroup_filter_t *agf;
1008 BerValue new_dn, new_ndn, pdn;
1010 Attribute *a, *ea, *attrs;
1011 int is_olddn, is_newdn, is_value_refresh, dn_equal;
1013 /* Handle all cases where a refresh of the group is needed */
1014 if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
1015 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1017 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1019 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1020 /* Request detected that the group must be refreshed */
1022 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1024 if ( age->age_mustrefresh ) {
1025 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1027 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1028 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1032 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1035 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1037 } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
1038 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
1040 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
1042 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1044 if ( op->oq_modrdn.rs_newSup ) {
1045 pdn = *op->oq_modrdn.rs_newSup;
1047 dnParent( &op->o_req_dn, &pdn );
1049 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
1051 if ( op->oq_modrdn.rs_nnewSup ) {
1052 pdn = *op->oq_modrdn.rs_nnewSup;
1054 dnParent( &op->o_req_ndn, &pdn );
1056 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
1058 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
1060 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
1062 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
1063 LDAP_SUCCESS || e == NULL ) {
1064 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
1065 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1066 return SLAP_CB_CONTINUE;
1069 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1073 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
1074 overlay_entry_release_ov( op, e, 0, on );
1075 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1076 return SLAP_CB_CONTINUE;
1080 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1081 for ( ; agd; agd = agd->agd_next ) {
1083 if ( value_find_ex( slap_schema.si_ad_objectClass,
1084 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1085 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1086 a->a_nvals, &agd->agd_oc->soc_cname,
1087 op->o_tmpmemctx ) == 0 )
1089 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1092 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1094 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1095 ber_dupbv( &age->age_dn, &new_dn );
1096 ber_dupbv( &age->age_ndn, &new_ndn );
1098 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1099 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1100 overlay_entry_release_ov( op, e, 0, on );
1101 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1102 return SLAP_CB_CONTINUE;
1110 1. check if the orginal entry's DN is in the group.
1111 2. chceck if the any of the group filter's base DN is a suffix of the new DN
1113 If 1 and 2 are both false, we do nothing.
1114 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1115 If 1 is false, and 2 is true, we check the entry against the group's filters,
1116 and add it's DN to the group.
1117 If 1 is true, and 2 is false, we delete the entry's DN from the group.
1119 attrs = attrs_dup( e->e_attrs );
1120 overlay_entry_release_ov( op, e, 0, on );
1121 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1124 is_value_refresh = 0;
1126 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1128 if ( age->age_filter && age->age_filter->agf_anlist ) {
1129 ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1135 if ( age->age_modrdn_olddnmodified ) {
1136 /* Resquest already marked this group to be updated */
1138 is_value_refresh = 1;
1139 age->age_modrdn_olddnmodified = 0;
1142 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1143 LDAP_SUCCESS || group == NULL ) {
1144 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1146 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1147 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1149 attrs_free( attrs );
1150 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1151 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1152 return SLAP_CB_CONTINUE;
1155 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1158 if ( value_find_ex( age->age_def->agd_member_ad,
1159 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1160 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1161 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1168 overlay_entry_release_ov( op, group, 0, on );
1172 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1173 if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1174 /* TODO: should retest filter as it could imply conditions on the dn */
1181 if ( is_value_refresh ) {
1182 if ( is_olddn != is_newdn ) {
1184 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1186 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1187 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1190 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1193 if ( is_olddn == 1 && is_newdn == 0 ) {
1195 autogroup_delete_member_values_from_group( op, &new_dn, age, ea );
1197 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1199 if ( is_olddn == 0 && is_newdn == 1 ) {
1201 etmp.e_name = op->o_req_dn;
1202 etmp.e_nname = op->o_req_ndn;
1203 etmp.e_attrs = attrs;
1204 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1205 if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1207 autogroup_add_member_values_to_group( op, &new_dn, age, ea );
1209 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1214 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1217 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1219 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1220 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1224 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1225 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1229 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1232 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1233 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1235 attrs_free( attrs );
1237 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1241 if ( op->o_tag == LDAP_REQ_MODIFY ) {
1242 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1244 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1246 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1248 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1249 LDAP_SUCCESS || e == NULL ) {
1250 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1251 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1252 return SLAP_CB_CONTINUE;
1255 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1259 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1260 overlay_entry_release_ov( op, e, 0, on );
1261 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1262 return SLAP_CB_CONTINUE;
1265 /* If we modify a group's memberURL, we have to delete all of it's members,
1266 and add them anew, because we cannot tell from which memberURL a member was added. */
1267 for ( ; agd; agd = agd->agd_next ) {
1269 if ( value_find_ex( slap_schema.si_ad_objectClass,
1270 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1271 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1272 a->a_nvals, &agd->agd_oc->soc_cname,
1273 op->o_tmpmemctx ) == 0 )
1278 m = op->orm_modlist;
1280 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1281 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1283 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1286 for ( ; m ; m = m->sml_next ) {
1287 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1288 autogroup_def_t *group_agd = age->age_def;
1289 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
1290 op->o_req_dn.bv_val, 0, 0);
1292 overlay_entry_release_ov( op, e, 0, on );
1294 autogroup_delete_member_from_group( op, NULL, NULL, age );
1295 autogroup_delete_group( agi, age );
1297 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1299 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1300 return SLAP_CB_CONTINUE;
1304 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1308 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1311 overlay_entry_release_ov( op, e, 0, on );
1312 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1313 return SLAP_CB_CONTINUE;
1317 /* When modifying any of the attributes of an entry, we must
1318 check if the entry is in any of our groups, and if
1319 the modified entry maches any of the filters of that group.
1321 If the entry exists in a group, but the modified attributes do
1322 not match any of the group's filters, we delete the entry from that group.
1323 If the entry doesn't exist in a group, but matches a filter,
1324 we add it to that group.
1326 attrs = attrs_dup( e->e_attrs );
1327 overlay_entry_release_ov( op, e, 0, on );
1328 etmp.e_name = op->o_req_dn;
1329 etmp.e_nname = op->o_req_ndn;
1330 etmp.e_attrs = attrs;
1331 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1336 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1338 if ( age->age_filter && age->age_filter->agf_anlist ) {
1339 ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1345 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1346 LDAP_SUCCESS || group == NULL ) {
1347 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
1348 age->age_dn.bv_val, 0, 0);
1350 attrs_free( attrs );
1351 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1352 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1353 return SLAP_CB_CONTINUE;
1356 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1359 if ( value_find_ex( age->age_def->agd_member_ad,
1360 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1361 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1362 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1369 overlay_entry_release_ov( op, group, 0, on );
1371 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1372 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1373 if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1380 if ( is_olddn == 1 && is_newdn == 0 ) {
1382 autogroup_delete_member_values_from_group( op, &op->o_req_dn, age, ea );
1384 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1386 if ( is_olddn == 0 && is_newdn == 1 ) {
1388 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, ea );
1390 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1393 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1396 attrs_free( attrs );
1398 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1402 return SLAP_CB_CONTINUE;
1406 ** Detect if filter contains a memberOf check for dn
1409 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1412 if ( f == NULL ) return 0;
1414 switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1415 case LDAP_FILTER_AND:
1416 case LDAP_FILTER_OR:
1417 case LDAP_FILTER_NOT:
1418 for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1419 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1422 case LDAP_FILTER_EQUALITY:
1423 result = ( f->f_ava->aa_desc == memberof_ad &&
1424 ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1434 ** When modifing a group, we must deny any modifications to the member attribute,
1435 ** because the group would be inconsistent.
1438 autogroup_modify_entry( Operation *op, SlapReply *rs)
1440 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1441 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1442 autogroup_def_t *agd = agi->agi_def;
1443 autogroup_entry_t *age;
1447 if ( get_manageDSAit( op ) ) {
1448 return SLAP_CB_CONTINUE;
1451 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1452 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1454 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1455 LDAP_SUCCESS || e == NULL ) {
1456 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1457 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1458 return SLAP_CB_CONTINUE;
1461 /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1462 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1463 autogroup_filter_t *agf;
1464 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1465 if ( agf->agf_anlist ) {
1467 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1468 if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1469 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1470 int rc = test_filter( op, e, agf->agf_filter );
1471 if ( rc == LDAP_COMPARE_TRUE ) {
1472 age->age_mustrefresh = 1;
1479 if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1480 age->age_mustrefresh = 1;
1485 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1488 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1489 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1490 return SLAP_CB_CONTINUE;
1494 for ( ; agd; agd = agd->agd_next ) {
1496 if ( value_find_ex( slap_schema.si_ad_objectClass,
1497 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1498 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1499 a->a_nvals, &agd->agd_oc->soc_cname,
1500 op->o_tmpmemctx ) == 0 )
1505 m = op->orm_modlist;
1507 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1508 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1511 for ( ; m ; m = m->sml_next ) {
1512 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1513 overlay_entry_release_ov( op, e, 0, on );
1514 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1515 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1516 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1517 return LDAP_CONSTRAINT_VIOLATION;
1524 overlay_entry_release_ov( op, e, 0, on );
1525 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1526 return SLAP_CB_CONTINUE;
1530 overlay_entry_release_ov( op, e, 0, on );
1531 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1532 return SLAP_CB_CONTINUE;
1536 ** Detect if the olddn is part of a group and so if the group should be refreshed
1539 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1541 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1542 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1543 autogroup_entry_t *age;
1546 if ( get_manageDSAit( op ) ) {
1547 return SLAP_CB_CONTINUE;
1550 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1551 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1553 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1554 LDAP_SUCCESS || e == NULL ) {
1555 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1556 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1557 return SLAP_CB_CONTINUE;
1560 /* Must check if a dn is modified */
1561 for ( age = agi->agi_entry; age ; age = age->age_next ) {
1562 autogroup_filter_t *agf;
1563 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1564 if ( agf->agf_anlist ) {
1565 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1566 int rc = test_filter( op, e, agf->agf_filter );
1567 if ( rc == LDAP_COMPARE_TRUE ) {
1568 age->age_modrdn_olddnmodified = 1;
1575 overlay_entry_release_ov( op, e, 0, on );
1576 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1577 return SLAP_CB_CONTINUE;
1581 ** Builds a filter for searching for the
1582 ** group entries, according to the objectClass.
1585 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1589 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1591 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1592 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1593 + agd->agd_oc->soc_cname.bv_len;
1594 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1596 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1598 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1602 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1604 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1615 static ConfigDriver ag_cfgen;
1617 static ConfigTable agcfg[] = {
1618 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1619 4, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1620 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1621 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1622 "EQUALITY caseIgnoreMatch "
1623 "SYNTAX OMsDirectoryString "
1624 "X-ORDERED 'VALUES' )",
1627 { "autogroup-memberof-ad", "memberOf attribute",
1628 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1629 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1630 "DESC 'memberOf attribute' "
1631 "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1634 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1637 static ConfigOCs agocs[] = {
1638 { "( OLcfgCtOc:2.1 "
1639 "NAME 'olcAutomaticGroups' "
1640 "DESC 'Automatic groups configuration' "
1641 "SUP olcOverlayConfig "
1644 "$ olcAGmemberOfAd "
1647 Cft_Overlay, agcfg, NULL, NULL },
1653 ag_cfgen( ConfigArgs *c )
1655 slap_overinst *on = (slap_overinst *)c->bi;
1656 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1657 autogroup_def_t *agd;
1658 autogroup_entry_t *age;
1662 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1665 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1666 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1667 agi->agi_def = NULL;
1668 agi->agi_entry = NULL;
1669 on->on_bi.bi_private = (void *)agi;
1673 age = agi->agi_entry;
1675 if ( c->op == SLAP_CONFIG_EMIT ) {
1679 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1681 char *ptr = c->cr_msg;
1683 assert(agd->agd_oc != NULL);
1684 assert(agd->agd_member_url_ad != NULL);
1685 assert(agd->agd_member_ad != NULL);
1687 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1688 SLAP_X_ORDERED_FMT "%s %s %s", i,
1689 agd->agd_oc->soc_cname.bv_val,
1690 agd->agd_member_url_ad->ad_cname.bv_val,
1691 agd->agd_member_ad->ad_cname.bv_val );
1693 bv.bv_val = c->cr_msg;
1694 bv.bv_len = ptr - bv.bv_val;
1695 value_add_one ( &c->rvalue_vals, &bv );
1700 case AG_MEMBER_OF_AD:
1701 if ( agi->agi_memberof_ad != NULL ){
1702 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1713 }else if ( c->op == LDAP_MOD_DELETE ) {
1715 autogroup_def_t *agd_next;
1716 autogroup_entry_t *age_next;
1717 autogroup_filter_t *agf = age->age_filter,
1720 for ( agd_next = agd; agd_next; agd = agd_next ) {
1721 agd_next = agd->agd_next;
1726 for ( age_next = age ; age_next ; age = age_next ) {
1727 age_next = age->age_next;
1729 ch_free( age->age_dn.bv_val );
1730 ch_free( age->age_ndn.bv_val );
1732 for( agf_next = agf ; agf_next ; agf = agf_next ){
1733 agf_next = agf->agf_next;
1735 filter_free( agf->agf_filter );
1736 ch_free( agf->agf_filterstr.bv_val );
1737 ch_free( agf->agf_dn.bv_val );
1738 ch_free( agf->agf_ndn.bv_val );
1739 anlist_free( agf->agf_anlist, 1, NULL );
1743 ldap_pvt_thread_mutex_init( &age->age_mutex );
1748 on->on_bi.bi_private = NULL;
1751 autogroup_def_t **agdp;
1752 autogroup_entry_t *age_next, *age_prev;
1753 autogroup_filter_t *agf,
1756 for ( i = 0, agdp = &agi->agi_def;
1759 if ( *agdp == NULL) {
1762 agdp = &(*agdp)->agd_next;
1766 *agdp = agd->agd_next;
1768 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1769 age_next = age->age_next;
1771 if( age->age_def == agd ) {
1772 agf = age->age_filter;
1774 ch_free( age->age_dn.bv_val );
1775 ch_free( age->age_ndn.bv_val );
1777 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1778 agf_next = agf->agf_next;
1779 filter_free( agf->agf_filter );
1780 ch_free( agf->agf_filterstr.bv_val );
1781 ch_free( agf->agf_dn.bv_val );
1782 ch_free( agf->agf_ndn.bv_val );
1783 anlist_free( agf->agf_anlist, 1, NULL );
1787 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1792 if( age_prev != NULL ) {
1793 age_prev->age_next = age_next;
1808 autogroup_def_t **agdp,
1810 ObjectClass *oc = NULL;
1811 AttributeDescription *member_url_ad = NULL,
1816 oc = oc_find( c->argv[ 1 ] );
1818 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1819 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1820 "unable to find ObjectClass \"%s\"",
1822 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1823 c->log, c->cr_msg, 0 );
1828 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1829 if( rc != LDAP_SUCCESS ) {
1830 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1831 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1832 "unable to find AttributeDescription \"%s\"",
1834 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1835 c->log, c->cr_msg, 0 );
1839 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1840 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1841 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1842 "AttributeDescription \"%s\" ",
1843 "must be of a subtype \"labeledURI\"",
1845 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1846 c->log, c->cr_msg, 0 );
1850 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1851 if( rc != LDAP_SUCCESS ) {
1852 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1853 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1854 "unable to find AttributeDescription \"%s\"",
1856 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1857 c->log, c->cr_msg, 0 );
1861 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1862 /* The same URL attribute / member attribute pair
1863 * cannot be repeated */
1865 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1866 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1867 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1868 "URL attributeDescription \"%s\" already mapped",
1869 member_ad->ad_cname.bv_val );
1870 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1871 c->log, c->cr_msg, 0 );
1872 /* return 1; //warning*/
1876 if ( c->valx > 0 ) {
1879 for ( i = 0, agdp = &agi->agi_def ;
1882 if ( *agdp == NULL ) {
1883 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1884 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1885 "invalid index {%d}",
1887 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1888 c->log, c->cr_msg, 0 );
1892 agdp = &(*agdp)->agd_next;
1897 for ( agdp = &agi->agi_def; *agdp;
1898 agdp = &(*agdp)->agd_next )
1902 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1904 (*agdp)->agd_oc = oc;
1905 (*agdp)->agd_member_url_ad = member_url_ad;
1906 (*agdp)->agd_member_ad = member_ad;
1907 (*agdp)->agd_next = agd_next;
1911 case AG_MEMBER_OF_AD: {
1912 AttributeDescription *memberof_ad = NULL;
1915 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1916 if( rc != LDAP_SUCCESS ) {
1917 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1918 "\"autogroup-memberof-ad <memberof-ad>\": "
1919 "unable to find AttributeDescription \"%s\"",
1921 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1922 c->log, c->cr_msg, 0 );
1926 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
1927 && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
1929 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1930 "memberof attribute=\"%s\" must either "
1931 "have DN (%s) or nameUID (%s) syntax",
1932 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1933 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1934 c->log, c->cr_msg, 0 );
1938 agi->agi_memberof_ad = memberof_ad;
1950 extern int slapMode;
1953 ** Do a search for all the groups in the
1954 ** database, and add them to out internal list.
1961 slap_overinst *on = (slap_overinst *) be->bd_info;
1962 autogroup_info_t *agi = on->on_bi.bi_private;
1963 autogroup_def_t *agd;
1966 slap_callback cb = { 0 };
1968 void *thrctx = ldap_pvt_thread_pool_context();
1969 Connection conn = { 0 };
1970 OperationBuffer opbuf;
1972 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1974 if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1978 connection_fake_init( &conn, &opbuf, thrctx );
1981 op->ors_attrsonly = 0;
1982 op->o_tag = LDAP_REQ_SEARCH;
1983 op->o_dn = be->be_rootdn;
1984 op->o_ndn = be->be_rootndn;
1986 op->o_req_dn = be->be_suffix[0];
1987 op->o_req_ndn = be->be_nsuffix[0];
1989 op->ors_scope = LDAP_SCOPE_SUBTREE;
1990 op->ors_deref = LDAP_DEREF_NEVER;
1991 op->ors_limit = NULL;
1992 op->ors_tlimit = SLAP_NO_LIMIT;
1993 op->ors_slimit = SLAP_NO_LIMIT;
1994 op->ors_attrs = slap_anlist_no_attrs;
1995 op->o_do_not_cache = 1;
1998 op->o_bd->bd_info = (BackendInfo *)on->on_info;
2001 cb.sc_private = &ags;
2002 cb.sc_response = autogroup_group_add_cb;
2003 cb.sc_cleanup = NULL;
2006 op->o_callback = &cb;
2008 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
2009 SlapReply rs = { REP_RESULT };
2011 autogroup_build_def_filter(agd, op);
2015 op->o_bd->be_search( op, &rs );
2017 filter_free_x( op, op->ors_filter, 1 );
2018 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
2021 if( ! agi->agi_memberof_ad ){
2023 const char *text = NULL;
2025 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
2026 if ( rc != LDAP_SUCCESS ) {
2027 Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
2028 "unable to find attribute=\"%s\": %s (%d)\n",
2029 SLAPD_MEMBEROF_ATTR, text, rc );
2042 slap_overinst *on = (slap_overinst *) be->bd_info;
2044 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
2046 if ( on->on_bi.bi_private ) {
2047 autogroup_info_t *agi = on->on_bi.bi_private;
2048 autogroup_entry_t *age = agi->agi_entry,
2050 autogroup_filter_t *agf, *agf_next;
2052 for ( age_next = age; age_next; age = age_next ) {
2053 age_next = age->age_next;
2055 ch_free( age->age_dn.bv_val );
2056 ch_free( age->age_ndn.bv_val );
2058 agf = age->age_filter;
2060 for ( agf_next = agf; agf_next; agf = agf_next ) {
2061 agf_next = agf->agf_next;
2063 filter_free( agf->agf_filter );
2064 ch_free( agf->agf_filterstr.bv_val );
2065 ch_free( agf->agf_dn.bv_val );
2066 ch_free( agf->agf_ndn.bv_val );
2067 anlist_free( agf->agf_anlist, 1, NULL );
2071 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
2080 autogroup_db_destroy(
2084 slap_overinst *on = (slap_overinst *) be->bd_info;
2086 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2088 if ( on->on_bi.bi_private ) {
2089 autogroup_info_t *agi = on->on_bi.bi_private;
2090 autogroup_def_t *agd = agi->agi_def,
2093 for ( agd_next = agd; agd_next; agd = agd_next ) {
2094 agd_next = agd->agd_next;
2099 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2106 static slap_overinst autogroup = { { NULL } };
2110 autogroup_initialize(void)
2113 autogroup.on_bi.bi_type = "autogroup";
2115 autogroup.on_bi.bi_db_open = autogroup_db_open;
2116 autogroup.on_bi.bi_db_close = autogroup_db_close;
2117 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2119 autogroup.on_bi.bi_op_add = autogroup_add_entry;
2120 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2121 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2122 autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2124 autogroup.on_response = autogroup_response;
2126 autogroup.on_bi.bi_cf_ocs = agocs;
2128 rc = config_register_schema( agcfg, agocs );
2133 return overlay_register( &autogroup );
2137 init_module( int argc, char *argv[] )
2139 return autogroup_initialize();