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