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