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