autogroup.c revision 1.1.1.1 1 /* autogroup.c - automatic group overlay */
2 /* $OpenLDAP: pkg/ldap/contrib/slapd-modules/autogroup/autogroup.c,v 1.2.2.1 2008/02/08 23:00:43 quanah Exp $ */
3 /*
4 * Copyright 2007 Micha Szulczyski.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
9 * Public License.
10 *
11 * A copy of this license is available in the file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
14 */
15
16 #include "portable.h"
17
18 #include <stdio.h>
19
20 #include <ac/string.h>
21
22 #include "slap.h"
23 #include "config.h"
24 #include "lutil.h"
25
26 /* Filter represents the memberURL of a group. */
27 typedef struct autogroup_filter_t {
28 struct berval agf_dn; /* The base DN in memberURL */
29 struct berval agf_ndn;
30 struct berval agf_filterstr;
31 Filter *agf_filter;
32 int agf_scope;
33 struct autogroup_filter_t *agf_next;
34 } autogroup_filter_t;
35
36 /* Description of group attributes. */
37 typedef struct autogroup_def_t {
38 ObjectClass *agd_oc;
39 AttributeDescription *agd_member_url_ad;
40 AttributeDescription *agd_member_ad;
41 struct autogroup_def_t *agd_next;
42 } autogroup_def_t;
43
44 /* Represents the group entry. */
45 typedef struct autogroup_entry_t {
46 BerValue age_dn;
47 BerValue age_ndn;
48 autogroup_filter_t *age_filter; /* List of filters made from memberURLs */
49 autogroup_def_t *age_def; /* Attribute definition */
50 ldap_pvt_thread_mutex_t age_mutex;
51 struct autogroup_entry_t *age_next;
52 } autogroup_entry_t;
53
54 /* Holds pointers to attribute definitions and groups. */
55 typedef struct autogroup_info_t {
56 autogroup_def_t *agi_def; /* Group attributes definitions. */
57 autogroup_entry_t *agi_entry; /* Group entries. */
58 ldap_pvt_thread_mutex_t agi_mutex;
59 } autogroup_info_t;
60
61 /* Search callback for adding groups initially. */
62 typedef struct autogroup_sc_t {
63 autogroup_info_t *ags_info; /* Group definitions and entries. */
64 autogroup_def_t *ags_def; /* Attributes definition of the group being added. */
65 } autogroup_sc_t;
66
67 /* Used for adding members, found when searching, to a group. */
68 typedef struct autogroup_ga_t {
69 autogroup_entry_t *agg_group; /* The group to which the members will be added. */
70 Entry *agg_entry; /* Used in autogroup_member_search_cb to modify
71 this entry with the search results. */
72
73 Modifications *agg_mod; /* Used in autogroup_member_search_modify_cb to hold the
74 search results which will be added to the group. */
75
76 Modifications *agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't
77 have to search for the last mod added. */
78 } autogroup_ga_t;
79
80
81 /*
82 ** dn, ndn - the DN of the member to add
83 ** age - the group to which the member DN will be added
84 */
85 static int
86 autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
87 {
88 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
89 Modifications modlist;
90 SlapReply sreply = {REP_RESULT};
91 BerValue vals[ 2 ], nvals[ 2 ];
92 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
93 Operation o = *op;
94
95 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
96 dn->bv_val, age->age_dn.bv_val, 0);
97
98 assert( dn != NULL );
99 assert( ndn != NULL );
100
101 vals[ 0 ] = *dn;
102 BER_BVZERO( &vals[ 1 ] );
103 nvals[ 0 ] = *ndn;
104 BER_BVZERO( &nvals[ 1 ] );
105
106 modlist.sml_op = LDAP_MOD_ADD;
107 modlist.sml_desc = age->age_def->agd_member_ad;
108 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
109 modlist.sml_values = vals;
110 modlist.sml_nvalues = nvals;
111 modlist.sml_numvals = 1;
112 modlist.sml_flags = SLAP_MOD_INTERNAL;
113 modlist.sml_next = NULL;
114
115 o.o_tag = LDAP_REQ_MODIFY;
116 o.o_callback = &cb;
117 o.orm_modlist = &modlist;
118 o.o_req_dn = age->age_dn;
119 o.o_req_ndn = age->age_ndn;
120 o.o_permissive_modify = 1;
121 o.o_managedsait = SLAP_CONTROL_CRITICAL;
122 o.o_relax = SLAP_CONTROL_CRITICAL;
123
124 o.o_bd->bd_info = (BackendInfo *)on->on_info;
125 (void)op->o_bd->be_modify( &o, &sreply );
126 o.o_bd->bd_info = (BackendInfo *)on;
127
128 return sreply.sr_err;
129 }
130
131 /*
132 ** dn,ndn - the DN to be deleted
133 ** age - the group from which the DN will be deleted
134 ** If we pass a NULL dn and ndn, all members are deleted from the group.
135 */
136 static int
137 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
138 {
139 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
140 Modifications modlist;
141 SlapReply sreply = {REP_RESULT};
142 BerValue vals[ 2 ], nvals[ 2 ];
143 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
144 Operation o = *op;
145
146 if ( dn == NULL || ndn == NULL ) {
147 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
148 age->age_dn.bv_val, 0 ,0);
149
150 modlist.sml_values = NULL;
151 modlist.sml_nvalues = NULL;
152 modlist.sml_numvals = 0;
153 } else {
154 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
155 dn->bv_val, age->age_dn.bv_val, 0);
156
157 vals[ 0 ] = *dn;
158 BER_BVZERO( &vals[ 1 ] );
159 nvals[ 0 ] = *ndn;
160 BER_BVZERO( &nvals[ 1 ] );
161
162 modlist.sml_values = vals;
163 modlist.sml_nvalues = nvals;
164 modlist.sml_numvals = 1;
165 }
166
167
168 modlist.sml_op = LDAP_MOD_DELETE;
169 modlist.sml_desc = age->age_def->agd_member_ad;
170 modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
171 modlist.sml_flags = SLAP_MOD_INTERNAL;
172 modlist.sml_next = NULL;
173
174 o.o_callback = &cb;
175 o.o_tag = LDAP_REQ_MODIFY;
176 o.orm_modlist = &modlist;
177 o.o_req_dn = age->age_dn;
178 o.o_req_ndn = age->age_ndn;
179 o.o_relax = SLAP_CONTROL_CRITICAL;
180 o.o_managedsait = SLAP_CONTROL_CRITICAL;
181 o.o_permissive_modify = 1;
182
183 o.o_bd->bd_info = (BackendInfo *)on->on_info;
184 (void)op->o_bd->be_modify( &o, &sreply );
185 o.o_bd->bd_info = (BackendInfo *)on;
186
187 return sreply.sr_err;
188 }
189
190 /*
191 ** Callback used to add entries to a group,
192 ** which are going to be written in the database
193 ** (used in bi_op_add)
194 ** The group is passed in autogroup_ga_t->agg_group
195 */
196 static int
197 autogroup_member_search_cb( Operation *op, SlapReply *rs )
198 {
199 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
200
201 assert( op->o_tag == LDAP_REQ_SEARCH );
202
203 if ( rs->sr_type == REP_SEARCH ) {
204 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
205 autogroup_entry_t *age = agg->agg_group;
206 Modification mod;
207 const char *text = NULL;
208 char textbuf[1024];
209 struct berval vals[ 2 ], nvals[ 2 ];
210
211 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
212 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
213
214 vals[ 0 ] = rs->sr_entry->e_name;
215 BER_BVZERO( &vals[ 1 ] );
216 nvals[ 0 ] = rs->sr_entry->e_nname;
217 BER_BVZERO( &nvals[ 1 ] );
218
219 mod.sm_op = LDAP_MOD_ADD;
220 mod.sm_desc = age->age_def->agd_member_ad;
221 mod.sm_type = age->age_def->agd_member_ad->ad_cname;
222 mod.sm_values = vals;
223 mod.sm_nvalues = nvals;
224 mod.sm_numvals = 1;
225
226 modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
227 }
228
229 return 0;
230 }
231
232 /*
233 ** Callback used to add entries to a group, which is already in the database.
234 ** (used in on_response)
235 ** The group is passed in autogroup_ga_t->agg_group
236 ** NOTE: Very slow.
237 */
238 static int
239 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
240 {
241 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
242
243 assert( op->o_tag == LDAP_REQ_SEARCH );
244
245 if ( rs->sr_type == REP_SEARCH ) {
246 autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
247 autogroup_entry_t *age = agg->agg_group;
248 Operation o = *op;
249 Modifications *modlist;
250 SlapReply sreply = {REP_RESULT};
251 const char *text = NULL;
252 char textbuf[1024];
253 struct berval vals[ 2 ], nvals[ 2 ];
254 slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
255
256 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
257 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
258
259 vals[ 0 ] = rs->sr_entry->e_name;
260 BER_BVZERO( &vals[ 1 ] );
261 nvals[ 0 ] = rs->sr_entry->e_nname;
262 BER_BVZERO( &nvals[ 1 ] );
263
264 modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
265
266 modlist->sml_op = LDAP_MOD_ADD;
267 modlist->sml_desc = age->age_def->agd_member_ad;
268 modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
269
270 ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
271 ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
272 modlist->sml_numvals = 1;
273
274 modlist->sml_flags = SLAP_MOD_INTERNAL;
275 modlist->sml_next = NULL;
276
277 if ( agg->agg_mod == NULL ) {
278 agg->agg_mod = modlist;
279 agg->agg_mod_last = modlist;
280 } else {
281 agg->agg_mod_last->sml_next = modlist;
282 agg->agg_mod_last = modlist;
283 }
284
285 }
286
287 return 0;
288 }
289
290
291 /*
292 ** Adds all entries matching the passed filter to the specified group.
293 ** If modify == 1, then we modify the group's entry in the database using be_modify.
294 ** If modify == 0, then, we must supply a rw entry for the group,
295 ** because we only modify the entry, without calling be_modify.
296 ** e - the group entry, to which the members will be added
297 ** age - the group
298 ** agf - the filter
299 */
300 static int
301 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
302 {
303 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
304 Operation o = *op;
305 SlapReply rs = { REP_SEARCH };
306 slap_callback cb = { 0 };
307 slap_callback null_cb = { NULL, slap_null_cb, NULL, NULL };
308 autogroup_ga_t agg;
309
310 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
311 age->age_dn.bv_val, 0, 0);
312
313 o.ors_attrsonly = 0;
314 o.o_tag = LDAP_REQ_SEARCH;
315
316 o.o_req_dn = agf->agf_dn;
317 o.o_req_ndn = agf->agf_ndn;
318
319 o.ors_filterstr = agf->agf_filterstr;
320 o.ors_filter = agf->agf_filter;
321
322 o.ors_scope = agf->agf_scope;
323 o.ors_deref = LDAP_DEREF_NEVER;
324 o.ors_limit = NULL;
325 o.ors_tlimit = SLAP_NO_LIMIT;
326 o.ors_slimit = SLAP_NO_LIMIT;
327 o.ors_attrs = slap_anlist_no_attrs;
328
329 agg.agg_group = age;
330 agg.agg_mod = NULL;
331 agg.agg_mod_last = NULL;
332 agg.agg_entry = e;
333 cb.sc_private = &agg;
334
335 if ( modify == 1 ) {
336 cb.sc_response = autogroup_member_search_modify_cb;
337 } else {
338 cb.sc_response = autogroup_member_search_cb;
339 }
340
341 cb.sc_cleanup = NULL;
342 cb.sc_next = NULL;
343
344 o.o_callback = &cb;
345
346 o.o_bd->bd_info = (BackendInfo *)on->on_info;
347 op->o_bd->be_search( &o, &rs );
348 o.o_bd->bd_info = (BackendInfo *)on;
349
350 if ( modify == 1 ) {
351 o = *op;
352 o.o_callback = &null_cb;
353 o.o_tag = LDAP_REQ_MODIFY;
354 o.orm_modlist = agg.agg_mod;
355 o.o_req_dn = age->age_dn;
356 o.o_req_ndn = age->age_ndn;
357 o.o_relax = SLAP_CONTROL_CRITICAL;
358 o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
359 o.o_permissive_modify = 1;
360
361 o.o_bd->bd_info = (BackendInfo *)on->on_info;
362 (void)op->o_bd->be_modify( &o, &rs );
363 o.o_bd->bd_info = (BackendInfo *)on;
364
365 slap_mods_free(agg.agg_mod, 1);
366 }
367
368 return 0;
369 }
370
371 /*
372 ** Adds a group to the internal list from the passed entry.
373 ** scan specifies whether to add all maching members to the group.
374 ** modify specifies whether to modify the given group entry (when modify == 0),
375 ** or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
376 ** agi - pointer to the groups and the attribute definitions
377 ** agd - the attribute definition of the added group
378 ** e - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
379 ** ndn - the DN of the group, can be NULL if we give a non-NULL e
380 */
381 static int
382 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
383 {
384 autogroup_entry_t **agep = &agi->agi_entry;
385 autogroup_filter_t *agf, *agf_prev = NULL;
386 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
387 LDAPURLDesc *lud = NULL;
388 Attribute *a;
389 BerValue *bv, dn;
390 int rc = 0, match = 1, null_entry = 0;
391
392 if ( e == NULL ) {
393 if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
394 LDAP_SUCCESS || e == NULL ) {
395 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
396 return 1;
397 }
398
399 null_entry = 1;
400 }
401
402 Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
403 e->e_name.bv_val, 0, 0);
404
405 if ( agi->agi_entry != NULL ) {
406 for ( ; *agep ; agep = &(*agep)->age_next ) {
407 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
408 if ( match == 0 ) {
409 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
410 return 1;
411 }
412 /* goto last */;
413 }
414 }
415
416
417 *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
418 ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
419 (*agep)->age_def = agd;
420 (*agep)->age_filter = NULL;
421
422 ber_dupbv( &(*agep)->age_dn, &e->e_name );
423 ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
424
425 a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
426
427 if ( null_entry == 1 ) {
428 a = attrs_dup( a );
429 overlay_entry_release_ov( op, e, 0, on );
430 }
431
432 if( a == NULL ) {
433 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
434 } else {
435 for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
436
437 agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
438
439 if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
440 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
441 /* FIXME: error? */
442 ch_free( agf );
443 continue;
444 }
445
446 agf->agf_scope = lud->lud_scope;
447
448 if ( lud->lud_dn == NULL ) {
449 BER_BVSTR( &dn, "" );
450 } else {
451 ber_str2bv( lud->lud_dn, 0, 0, &dn );
452 }
453
454 rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
455 if ( rc != LDAP_SUCCESS ) {
456 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
457 /* FIXME: error? */
458 goto cleanup;
459 }
460
461 if ( lud->lud_filter != NULL ) {
462 ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
463 agf->agf_filter = str2filter( lud->lud_filter );
464 }
465
466 agf->agf_next = NULL;
467
468
469 if( (*agep)->age_filter == NULL ) {
470 (*agep)->age_filter = agf;
471 }
472
473 if( agf_prev != NULL ) {
474 agf_prev->agf_next = agf;
475 }
476
477 agf_prev = agf;
478
479 if ( scan == 1 ){
480 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
481 }
482
483 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
484 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
485
486 ldap_free_urldesc( lud );
487
488 continue;
489
490
491 cleanup:;
492
493 ldap_free_urldesc( lud );
494 ch_free( agf );
495 }
496 }
497
498 if ( null_entry == 1 ) {
499 attrs_free( a );
500 }
501 return rc;
502 }
503
504 /*
505 ** Used when opening the database to add all existing
506 ** groups from the database to our internal list.
507 */
508 static int
509 autogroup_group_add_cb( Operation *op, SlapReply *rs )
510 {
511 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
512
513 assert( op->o_tag == LDAP_REQ_SEARCH );
514
515
516 if ( rs->sr_type == REP_SEARCH ) {
517 autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
518
519 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
520 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
521
522 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
523 }
524
525 return 0;
526 }
527
528
529 /*
530 ** When adding a group, we first strip any existing members,
531 ** and add all which match the filters ourselfs.
532 */
533 static int
534 autogroup_add_entry( Operation *op, SlapReply *rs)
535 {
536 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
537 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
538 autogroup_def_t *agd = agi->agi_def;
539 autogroup_entry_t *age = agi->agi_entry;
540 autogroup_filter_t *agf;
541 Attribute *a;
542 int rc = 0;
543
544 Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
545 op->ora_e->e_name.bv_val, 0, 0);
546
547 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
548
549 /* Check if it's a group. */
550 for ( ; agd ; agd = agd->agd_next ) {
551 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
552 Modification mod;
553 const char *text = NULL;
554 char textbuf[1024];
555
556 mod.sm_op = LDAP_MOD_DELETE;
557 mod.sm_desc = agd->agd_member_ad;
558 mod.sm_type = agd->agd_member_ad->ad_cname;
559 mod.sm_values = NULL;
560 mod.sm_nvalues = NULL;
561
562 /* We don't want any member attributes added by the user. */
563 modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
564
565 autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
566 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
567 return SLAP_CB_CONTINUE;
568 }
569 }
570
571 for ( ; age ; age = age->age_next ) {
572 ldap_pvt_thread_mutex_lock( &age->age_mutex );
573
574 /* Check if any of the filters are the suffix to the entry DN.
575 If yes, we can test that filter against the entry. */
576
577 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
578 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
579 rc = test_filter( op, op->ora_e, agf->agf_filter );
580 if ( rc == LDAP_COMPARE_TRUE ) {
581 autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
582 break;
583 }
584 }
585 }
586 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
587 }
588
589 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
590
591 return SLAP_CB_CONTINUE;
592 }
593
594 /*
595 ** agi - internal group and attribute definitions list
596 ** e - the group to remove from the internal list
597 */
598 static int
599 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
600 {
601 autogroup_entry_t *age = agi->agi_entry,
602 *age_prev = NULL,
603 *age_next;
604 int rc = 1;
605
606 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
607 age->age_dn.bv_val, 0, 0);
608
609 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
610 age_next = age->age_next;
611
612 if ( age == e ) {
613 autogroup_filter_t *agf = age->age_filter,
614 *agf_next;
615
616 if ( age_prev != NULL ) {
617 age_prev->age_next = age_next;
618 } else {
619 agi->agi_entry = NULL;
620 }
621
622 ch_free( age->age_dn.bv_val );
623 ch_free( age->age_ndn.bv_val );
624
625 for( agf_next = agf ; agf_next ; agf = agf_next ){
626 agf_next = agf->agf_next;
627
628 filter_free( agf->agf_filter );
629 ch_free( agf->agf_filterstr.bv_val );
630 ch_free( agf->agf_dn.bv_val );
631 ch_free( agf->agf_ndn.bv_val );
632 }
633
634 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
635 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
636 ch_free( age );
637
638 rc = 0;
639 return rc;
640
641 }
642 }
643
644 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
645
646 return rc;
647
648 }
649
650 static int
651 autogroup_delete_entry( Operation *op, SlapReply *rs)
652 {
653 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
654 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
655 autogroup_def_t *agd = agi->agi_def;
656 autogroup_entry_t *age = agi->agi_entry,
657 *age_prev, *age_next;
658 autogroup_filter_t *agf;
659 Entry *e;
660 int matched_group = 0, rc = 0;
661
662 Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
663
664 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
665
666 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
667 LDAP_SUCCESS || e == NULL ) {
668 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
669 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
670 return SLAP_CB_CONTINUE;
671 }
672
673 /* Check if the entry to be deleted is one of our groups. */
674 for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
675 ldap_pvt_thread_mutex_lock( &age->age_mutex );
676 age_next = age->age_next;
677
678 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
679 int match = 1;
680
681 matched_group = 1;
682
683 dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
684
685 if ( match == 0 ) {
686 autogroup_filter_t *agf = age->age_filter,
687 *agf_next;
688
689 autogroup_delete_group( agi, age );
690 break;
691 }
692 }
693
694 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
695 }
696
697 if ( matched_group == 1 ) {
698 overlay_entry_release_ov( op, e, 0, on );
699 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
700 return SLAP_CB_CONTINUE;
701 }
702
703 /* Check if the entry matches any of the groups.
704 If yes, we can delete the entry from that group. */
705
706 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
707 ldap_pvt_thread_mutex_lock( &age->age_mutex );
708
709 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
710 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
711 rc = test_filter( op, e, agf->agf_filter );
712 if ( rc == LDAP_COMPARE_TRUE ) {
713 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
714 break;
715 }
716 }
717 }
718 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
719 }
720
721 overlay_entry_release_ov( op, e, 0, on );
722 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
723
724 return SLAP_CB_CONTINUE;
725 }
726
727 static int
728 autogroup_response( Operation *op, SlapReply *rs )
729 {
730 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
731 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
732 autogroup_def_t *agd = agi->agi_def;
733 autogroup_entry_t *age = agi->agi_entry;
734 autogroup_filter_t *agf;
735 BerValue new_dn, new_ndn, pdn;
736 Entry *e, *group;
737 Attribute *a;
738 int is_olddn, is_newdn, dn_equal;
739
740 if ( op->o_tag == LDAP_REQ_MODRDN ) {
741 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
742
743 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
744
745 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
746
747 if ( op->oq_modrdn.rs_newSup ) {
748 pdn = *op->oq_modrdn.rs_newSup;
749 } else {
750 dnParent( &op->o_req_dn, &pdn );
751 }
752 build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
753
754 if ( op->oq_modrdn.rs_nnewSup ) {
755 pdn = *op->oq_modrdn.rs_nnewSup;
756 } else {
757 dnParent( &op->o_req_ndn, &pdn );
758 }
759 build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
760
761 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
762
763 dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
764
765 if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
766 LDAP_SUCCESS || e == NULL ) {
767 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
768 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
769 return SLAP_CB_CONTINUE;
770 }
771
772 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
773
774
775 if ( a == NULL ) {
776 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
777 overlay_entry_release_ov( op, e, 0, on );
778 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
779 return SLAP_CB_CONTINUE;
780 }
781
782
783 /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
784 for ( ; agd; agd = agd->agd_next ) {
785
786 if ( value_find_ex( slap_schema.si_ad_objectClass,
787 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
788 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
789 a->a_nvals, &agd->agd_oc->soc_cname,
790 op->o_tmpmemctx ) == 0 )
791 {
792 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
793 int match = 1;
794
795 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
796 if ( match == 0 ) {
797 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
798 ber_dupbv( &age->age_dn, &new_dn );
799 ber_dupbv( &age->age_ndn, &new_ndn );
800
801 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
802 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
803 overlay_entry_release_ov( op, e, 0, on );
804 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
805 return SLAP_CB_CONTINUE;
806 }
807 }
808
809 }
810 }
811
812 overlay_entry_release_ov( op, e, 0, on );
813
814 /* For each group:
815 1. check if the orginal entry's DN is in the group.
816 2. chceck if the any of the group filter's base DN is a suffix of the new DN
817
818 If 1 and 2 are both false, we do nothing.
819 If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
820 If 1 is false, and 2 is true, we check the entry against the group's filters,
821 and add it's DN to the group.
822 If 1 is true, and 2 is false, we delete the entry's DN from the group.
823 */
824 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
825 is_olddn = 0;
826 is_newdn = 0;
827
828
829 ldap_pvt_thread_mutex_lock( &age->age_mutex );
830
831 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
832 LDAP_SUCCESS || group == NULL ) {
833 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
834
835 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
836 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
837
838 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
839 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
840 return SLAP_CB_CONTINUE;
841 }
842
843 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
844
845 if ( a != NULL ) {
846 if ( value_find_ex( age->age_def->agd_member_ad,
847 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
848 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
849 a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
850 {
851 is_olddn = 1;
852 }
853
854 }
855
856 overlay_entry_release_ov( op, group, 0, on );
857
858 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
859 if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
860 is_newdn = 1;
861 break;
862 }
863 }
864
865
866 if ( is_olddn == 1 && is_newdn == 0 ) {
867 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
868 } else
869 if ( is_olddn == 0 && is_newdn == 1 ) {
870 for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
871 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
872 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
873 break;
874 }
875 }
876 } else
877 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
878 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
879 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
880 }
881
882 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
883 }
884
885 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
886 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
887
888 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
889 }
890 }
891
892 if ( op->o_tag == LDAP_REQ_MODIFY ) {
893 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
894 Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
895
896 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
897
898 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
899 LDAP_SUCCESS || e == NULL ) {
900 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
901 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
902 return SLAP_CB_CONTINUE;
903 }
904
905 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
906
907
908 if ( a == NULL ) {
909 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
910 overlay_entry_release_ov( op, e, 0, on );
911 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
912 return SLAP_CB_CONTINUE;
913 }
914
915
916 /* If we modify a group's memberURL, we have to delete all of it's members,
917 and add them anew, because we cannot tell from which memberURL a member was added. */
918 for ( ; agd; agd = agd->agd_next ) {
919
920 if ( value_find_ex( slap_schema.si_ad_objectClass,
921 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
922 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
923 a->a_nvals, &agd->agd_oc->soc_cname,
924 op->o_tmpmemctx ) == 0 )
925 {
926 Modifications *m;
927 int match = 1;
928
929 m = op->orm_modlist;
930
931 for ( ; age ; age = age->age_next ) {
932 ldap_pvt_thread_mutex_lock( &age->age_mutex );
933
934 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
935
936 if ( match == 0 ) {
937 for ( ; m ; m = m->sml_next ) {
938 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
939 autogroup_def_t *group_agd = age->age_def;
940 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
941 op->o_req_dn.bv_val, 0, 0);
942
943 overlay_entry_release_ov( op, e, 0, on );
944
945 autogroup_delete_member_from_group( op, NULL, NULL, age );
946 autogroup_delete_group( agi, age );
947
948 autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
949
950 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
951 return SLAP_CB_CONTINUE;
952 }
953 }
954
955 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
956 break;
957 }
958
959 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
960 }
961
962 overlay_entry_release_ov( op, e, 0, on );
963 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
964 return SLAP_CB_CONTINUE;
965 }
966 }
967
968 overlay_entry_release_ov( op, e, 0, on );
969
970 /* When modifing any of the attributes of an entry, we must
971 check if the entry is in any of our groups, and if
972 the modified entry maches any of the filters of that group.
973
974 If the entry exists in a group, but the modified attributes do
975 not match any of the group's filters, we delete the entry from that group.
976 If the entry doesn't exist in a group, but matches a filter,
977 we add it to that group.
978 */
979 for ( age = agi->agi_entry ; age ; age = age->age_next ) {
980 is_olddn = 0;
981 is_newdn = 0;
982
983
984 ldap_pvt_thread_mutex_lock( &age->age_mutex );
985
986 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
987 LDAP_SUCCESS || group == NULL ) {
988 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
989 age->age_dn.bv_val, 0, 0);
990
991 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
992 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
993 return SLAP_CB_CONTINUE;
994 }
995
996 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
997
998 if ( a != NULL ) {
999 if ( value_find_ex( age->age_def->agd_member_ad,
1000 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1001 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1002 a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
1003 {
1004 is_olddn = 1;
1005 }
1006
1007 }
1008
1009 overlay_entry_release_ov( op, group, 0, on );
1010
1011 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1012 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1013 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1014 is_newdn = 1;
1015 break;
1016 }
1017 }
1018 }
1019
1020 if ( is_olddn == 1 && is_newdn == 0 ) {
1021 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1022 } else
1023 if ( is_olddn == 0 && is_newdn == 1 ) {
1024 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1025 }
1026
1027 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1028 }
1029
1030 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1031 }
1032 }
1033
1034 return SLAP_CB_CONTINUE;
1035 }
1036
1037 /*
1038 ** When modifing a group, we must deny any modifications to the member attribute,
1039 ** because the group would be inconsistent.
1040 */
1041 static int
1042 autogroup_modify_entry( Operation *op, SlapReply *rs)
1043 {
1044 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1045 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1046 autogroup_def_t *agd = agi->agi_def;
1047 autogroup_entry_t *age = agi->agi_entry;
1048 Entry *e;
1049 Attribute *a;
1050
1051 if ( get_manageDSAit( op ) ) {
1052 return SLAP_CB_CONTINUE;
1053 }
1054
1055 Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1056 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1057
1058 if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1059 LDAP_SUCCESS || e == NULL ) {
1060 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1061 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1062 return SLAP_CB_CONTINUE;
1063 }
1064
1065 a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1066
1067 if ( a == NULL ) {
1068 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1069 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1070 return SLAP_CB_CONTINUE;
1071 }
1072
1073
1074 for ( ; agd; agd = agd->agd_next ) {
1075
1076 if ( value_find_ex( slap_schema.si_ad_objectClass,
1077 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1078 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1079 a->a_nvals, &agd->agd_oc->soc_cname,
1080 op->o_tmpmemctx ) == 0 )
1081 {
1082 Modifications *m;
1083 int match = 1;
1084
1085 m = op->orm_modlist;
1086
1087 for ( ; age ; age = age->age_next ) {
1088 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1089
1090 if ( match == 0 ) {
1091 for ( ; m ; m = m->sml_next ) {
1092 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1093 overlay_entry_release_ov( op, e, 0, on );
1094 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1095 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1096 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1097 return LDAP_CONSTRAINT_VIOLATION;
1098 }
1099 }
1100 break;
1101 }
1102 }
1103
1104 overlay_entry_release_ov( op, e, 0, on );
1105 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1106 return SLAP_CB_CONTINUE;
1107 }
1108 }
1109
1110 overlay_entry_release_ov( op, e, 0, on );
1111 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1112 return SLAP_CB_CONTINUE;
1113 }
1114
1115 /*
1116 ** Builds a filter for searching for the
1117 ** group entries, according to the objectClass.
1118 */
1119 static int
1120 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1121 {
1122 char *ptr;
1123
1124 Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1125
1126 op->ors_filterstr.bv_len = STRLENOF( "(=)" )
1127 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1128 + agd->agd_oc->soc_cname.bv_len;
1129 ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1130 *ptr++ = '(';
1131 ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1132 *ptr++ = '=';
1133 ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1134 *ptr++ = ')';
1135 *ptr = '\0';
1136
1137 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1138
1139 assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1140
1141 return 0;
1142 }
1143
1144 enum {
1145 AG_ATTRSET = 1,
1146 AG_LAST
1147 };
1148
1149 static ConfigDriver ag_cfgen;
1150
1151 static ConfigTable agcfg[] = {
1152 { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1153 3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1154 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1155 "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1156 "EQUALITY caseIgnoreMatch "
1157 "SYNTAX OMsDirectoryString "
1158 "X-ORDERED 'VALUES' )",
1159 NULL, NULL },
1160 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1161 };
1162
1163 static ConfigOCs agocs[] = {
1164 { "( OLcfgCtOc:2.1 "
1165 "NAME 'olcAutomaticGroups' "
1166 "DESC 'Automatic groups configuration' "
1167 "SUP olcOverlayConfig "
1168 "MAY olcAGattrSet )",
1169 Cft_Overlay, agcfg, NULL, NULL },
1170 { NULL, 0, NULL }
1171 };
1172
1173
1174 static int
1175 ag_cfgen( ConfigArgs *c )
1176 {
1177 slap_overinst *on = (slap_overinst *)c->bi;
1178 autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
1179 autogroup_def_t *agd;
1180 autogroup_entry_t *age;
1181
1182 int rc = 0, i;
1183
1184 Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1185
1186 if( agi == NULL ) {
1187 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1188 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1189 agi->agi_def = NULL;
1190 agi->agi_entry = NULL;
1191 on->on_bi.bi_private = (void *)agi;
1192 }
1193
1194 agd = agi->agi_def;
1195 age = agi->agi_entry;
1196
1197 if ( c->op == SLAP_CONFIG_EMIT ) {
1198
1199 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1200
1201 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1202 struct berval bv;
1203 char *ptr = c->cr_msg;
1204
1205 assert(agd->agd_oc != NULL);
1206 assert(agd->agd_member_url_ad != NULL);
1207 assert(agd->agd_member_ad != NULL);
1208
1209 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1210 SLAP_X_ORDERED_FMT "%s %s %s", i,
1211 agd->agd_oc->soc_cname.bv_val,
1212 agd->agd_member_url_ad->ad_cname.bv_val,
1213 agd->agd_member_ad->ad_cname.bv_val );
1214
1215 bv.bv_val = c->cr_msg;
1216 bv.bv_len = ptr - bv.bv_val;
1217 value_add_one ( &c->rvalue_vals, &bv );
1218
1219 }
1220 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1221
1222 return rc;
1223
1224 }else if ( c->op == LDAP_MOD_DELETE ) {
1225 if ( c->valx < 0) {
1226 autogroup_def_t *agd_next;
1227 autogroup_entry_t *age_next;
1228 autogroup_filter_t *agf = age->age_filter,
1229 *agf_next;
1230
1231 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1232
1233 for ( agd_next = agd; agd_next; agd = agd_next ) {
1234 agd_next = agd->agd_next;
1235
1236 ch_free( agd );
1237 }
1238
1239 for ( age_next = age ; age_next ; age = age_next ) {
1240 age_next = age->age_next;
1241
1242 ch_free( age->age_dn.bv_val );
1243 ch_free( age->age_ndn.bv_val );
1244
1245 for( agf_next = agf ; agf_next ; agf = agf_next ){
1246 agf_next = agf->agf_next;
1247
1248 filter_free( agf->agf_filter );
1249 ch_free( agf->agf_filterstr.bv_val );
1250 ch_free( agf->agf_dn.bv_val );
1251 ch_free( agf->agf_ndn.bv_val );
1252 }
1253
1254 ldap_pvt_thread_mutex_init( &age->age_mutex );
1255 ch_free( age );
1256 }
1257
1258 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1259
1260 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
1261 ch_free( agi );
1262 on->on_bi.bi_private = NULL;
1263
1264 } else {
1265 autogroup_def_t **agdp;
1266 autogroup_entry_t *age_next, *age_prev;
1267 autogroup_filter_t *agf,
1268 *agf_next;
1269 struct berval *bv;
1270
1271 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1272
1273 for ( i = 0, agdp = &agi->agi_def;
1274 i < c->valx; i++ )
1275 {
1276 if ( *agdp == NULL) {
1277 return 1;
1278 }
1279 agdp = &(*agdp)->agd_next;
1280 }
1281
1282 agd = *agdp;
1283 *agdp = agd->agd_next;
1284
1285 for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1286 age_next = age->age_next;
1287
1288 if( age->age_def == agd ) {
1289 agf = age->age_filter;
1290
1291 ch_free( age->age_dn.bv_val );
1292 ch_free( age->age_ndn.bv_val );
1293
1294 for ( agf_next = agf; agf_next ; agf = agf_next ) {
1295 agf_next = agf->agf_next;
1296 filter_free( agf->agf_filter );
1297 ch_free( agf->agf_filterstr.bv_val );
1298 ch_free( agf->agf_dn.bv_val );
1299 ch_free( agf->agf_ndn.bv_val );
1300 }
1301
1302 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1303 ch_free( age );
1304
1305 age = age_prev;
1306
1307 if( age_prev != NULL ) {
1308 age_prev->age_next = age_next;
1309 }
1310 }
1311 }
1312
1313 ch_free( agd );
1314 agd = agi->agi_def;
1315 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1316
1317 }
1318
1319 return rc;
1320 }
1321
1322 switch(c->type){
1323 case AG_ATTRSET: {
1324 autogroup_def_t **agdp,
1325 *agd_next = NULL;
1326 ObjectClass *oc = NULL;
1327 AttributeDescription *member_url_ad = NULL,
1328 *member_ad = NULL;
1329 const char *text;
1330
1331
1332 oc = oc_find( c->argv[ 1 ] );
1333 if( oc == NULL ){
1334 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1335 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1336 "unable to find ObjectClass \"%s\"",
1337 c->argv[ 1 ] );
1338 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1339 c->log, c->cr_msg, 0 );
1340 return 1;
1341 }
1342
1343
1344 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1345 if( rc != LDAP_SUCCESS ) {
1346 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1347 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1348 "unable to find AttributeDescription \"%s\"",
1349 c->argv[ 2 ] );
1350 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1351 c->log, c->cr_msg, 0 );
1352 return 1;
1353 }
1354
1355 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1356 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1357 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1358 "AttributeDescription \"%s\" ",
1359 "must be of a subtype \"labeledURI\"",
1360 c->argv[ 2 ] );
1361 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1362 c->log, c->cr_msg, 0 );
1363 return 1;
1364 }
1365
1366 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1367 if( rc != LDAP_SUCCESS ) {
1368 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1369 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1370 "unable to find AttributeDescription \"%s\"",
1371 c->argv[ 3 ] );
1372 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1373 c->log, c->cr_msg, 0 );
1374 return 1;
1375 }
1376
1377 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1378
1379 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1380 /* The same URL attribute / member attribute pair
1381 * cannot be repeated */
1382
1383 if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1384 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1385 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1386 "URL attributeDescription \"%s\" already mapped",
1387 member_ad->ad_cname.bv_val );
1388 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1389 c->log, c->cr_msg, 0 );
1390 /* return 1; //warning*/
1391 }
1392 }
1393
1394 if ( c->valx > 0 ) {
1395 int i;
1396
1397 for ( i = 0, agdp = &agi->agi_def ;
1398 i < c->valx; i++ )
1399 {
1400 if ( *agdp == NULL ) {
1401 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1402 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1403 "invalid index {%d}",
1404 c->valx );
1405 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1406 c->log, c->cr_msg, 0 );
1407
1408 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1409 return 1;
1410 }
1411 agdp = &(*agdp)->agd_next;
1412 }
1413 agd_next = *agdp;
1414
1415 } else {
1416 for ( agdp = &agi->agi_def; *agdp;
1417 agdp = &(*agdp)->agd_next )
1418 /* goto last */;
1419 }
1420
1421 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1422
1423 (*agdp)->agd_oc = oc;
1424 (*agdp)->agd_member_url_ad = member_url_ad;
1425 (*agdp)->agd_member_ad = member_ad;
1426 (*agdp)->agd_next = agd_next;
1427
1428 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1429
1430 } break;
1431
1432 default:
1433 rc = 1;
1434 break;
1435 }
1436
1437 return rc;
1438 }
1439
1440 /*
1441 ** Do a search for all the groups in the
1442 ** database, and add them to out internal list.
1443 */
1444 static int
1445 autogroup_db_open(
1446 BackendDB *be,
1447 ConfigReply *cr )
1448 {
1449 slap_overinst *on = (slap_overinst *) be->bd_info,
1450 *on_bd;
1451 autogroup_info_t *agi = on->on_bi.bi_private;
1452 autogroup_def_t *agd;
1453 autogroup_sc_t ags;
1454 Operation *op;
1455 SlapReply rs = { REP_RESULT };
1456 slap_callback cb = { 0 };
1457
1458 void *thrctx = ldap_pvt_thread_pool_context();
1459 Connection conn = { 0 };
1460 OperationBuffer opbuf;
1461 BerValue bv;
1462 char *ptr;
1463 int rc = 0;
1464
1465 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1466
1467 connection_fake_init( &conn, &opbuf, thrctx );
1468 op = &opbuf.ob_op;
1469
1470 op->ors_attrsonly = 0;
1471 op->o_tag = LDAP_REQ_SEARCH;
1472 op->o_dn = be->be_rootdn;
1473 op->o_ndn = be->be_rootndn;
1474
1475 op->o_req_dn = be->be_suffix[0];
1476 op->o_req_ndn = be->be_nsuffix[0];
1477
1478 op->ors_scope = LDAP_SCOPE_SUBTREE;
1479 op->ors_deref = LDAP_DEREF_NEVER;
1480 op->ors_limit = NULL;
1481 op->ors_tlimit = SLAP_NO_LIMIT;
1482 op->ors_slimit = SLAP_NO_LIMIT;
1483 op->ors_attrs = slap_anlist_no_attrs;
1484
1485 op->o_bd = select_backend(&op->o_req_ndn, 0);
1486
1487 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1488 for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1489
1490 autogroup_build_def_filter(agd, op);
1491
1492
1493 ags.ags_info = agi;
1494 ags.ags_def = agd;
1495 cb.sc_private = &ags;
1496 cb.sc_response = autogroup_group_add_cb;
1497 cb.sc_cleanup = NULL;
1498 cb.sc_next = NULL;
1499
1500 op->o_callback = &cb;
1501
1502 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1503 op->o_bd->be_search( op, &rs );
1504 op->o_bd->bd_info = (BackendInfo *)on;
1505
1506 filter_free_x( op, op->ors_filter );
1507 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1508 }
1509 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1510
1511 return 0;
1512 }
1513
1514 static int
1515 autogroup_db_close(
1516 BackendDB *be,
1517 ConfigReply *cr )
1518 {
1519 slap_overinst *on = (slap_overinst *) be->bd_info;
1520
1521 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1522
1523 if ( on->on_bi.bi_private ) {
1524 autogroup_info_t *agi = on->on_bi.bi_private;
1525 autogroup_entry_t *age = agi->agi_entry,
1526 *age_next;
1527 autogroup_filter_t *agf, *agf_next;
1528
1529 for ( age_next = age; age_next; age = age_next ) {
1530 age_next = age->age_next;
1531
1532 ch_free( age->age_dn.bv_val );
1533 ch_free( age->age_ndn.bv_val );
1534
1535 agf = age->age_filter;
1536
1537 for ( agf_next = agf; agf_next; agf = agf_next ) {
1538 agf_next = agf->agf_next;
1539
1540 filter_free( agf->agf_filter );
1541 ch_free( agf->agf_filterstr.bv_val );
1542 ch_free( agf->agf_dn.bv_val );
1543 ch_free( agf->agf_ndn.bv_val );
1544 ch_free( agf );
1545 }
1546
1547 ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1548 ch_free( age );
1549 }
1550 }
1551
1552 return 0;
1553 }
1554
1555 static int
1556 autogroup_db_destroy(
1557 BackendDB *be,
1558 ConfigReply *cr )
1559 {
1560 slap_overinst *on = (slap_overinst *) be->bd_info;
1561
1562 Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
1563
1564 if ( on->on_bi.bi_private ) {
1565 autogroup_info_t *agi = on->on_bi.bi_private;
1566 autogroup_def_t *agd = agi->agi_def,
1567 *agd_next;
1568
1569 for ( agd_next = agd; agd_next; agd = agd_next ) {
1570 agd_next = agd->agd_next;
1571
1572 ch_free( agd );
1573 }
1574
1575 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
1576 ch_free( agi );
1577 }
1578
1579 return 0;
1580 }
1581
1582 static slap_overinst autogroup = { { NULL } };
1583
1584 static
1585 int
1586 autogroup_initialize(void)
1587 {
1588 int rc = 0;
1589 autogroup.on_bi.bi_type = "autogroup";
1590
1591 autogroup.on_bi.bi_db_open = autogroup_db_open;
1592 autogroup.on_bi.bi_db_close = autogroup_db_close;
1593 autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
1594
1595 autogroup.on_bi.bi_op_add = autogroup_add_entry;
1596 autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
1597 autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
1598
1599 autogroup.on_response = autogroup_response;
1600
1601 autogroup.on_bi.bi_cf_ocs = agocs;
1602
1603 rc = config_register_schema( agcfg, agocs );
1604 if ( rc ) {
1605 return rc;
1606 }
1607
1608 return overlay_register( &autogroup );
1609 }
1610
1611 int
1612 init_module( int argc, char *argv[] )
1613 {
1614 return autogroup_initialize();
1615 }
1616