1 /* $NetBSD: refint.c,v 1.1.1.5 2014/05/28 09:58:52 tron Exp $ */ 2 3 /* refint.c - referential integrity module */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2004-2014 The OpenLDAP Foundation. 8 * Portions Copyright 2004 Symas Corporation. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted only as authorized by the OpenLDAP 13 * Public License. 14 * 15 * A copy of this license is available in the file LICENSE in the 16 * top-level directory of the distribution or, alternatively, at 17 * <http://www.OpenLDAP.org/license.html>. 18 */ 19 /* ACKNOWLEDGEMENTS: 20 * This work was initially developed by Symas Corp. for inclusion in 21 * OpenLDAP Software. This work was sponsored by Hewlett-Packard. 22 */ 23 24 #include "portable.h" 25 26 /* This module maintains referential integrity for a set of 27 * DN-valued attributes by searching for all references to a given 28 * DN whenever the DN is changed or its entry is deleted, and making 29 * the appropriate update. 30 * 31 * Updates are performed using the database rootdn in a separate task 32 * to allow the original operation to complete immediately. 33 */ 34 35 #ifdef SLAPD_OVER_REFINT 36 37 #include <stdio.h> 38 39 #include <ac/string.h> 40 #include <ac/socket.h> 41 42 #include "slap.h" 43 #include "config.h" 44 #include "ldap_rq.h" 45 46 static slap_overinst refint; 47 48 /* The DN to use in the ModifiersName for all refint updates */ 49 static BerValue refint_dn = BER_BVC("cn=Referential Integrity Overlay"); 50 static BerValue refint_ndn = BER_BVC("cn=referential integrity overlay"); 51 52 typedef struct refint_attrs_s { 53 struct refint_attrs_s *next; 54 AttributeDescription *attr; 55 BerVarray old_vals; 56 BerVarray old_nvals; 57 BerVarray new_vals; 58 BerVarray new_nvals; 59 int ra_numvals; 60 int dont_empty; 61 } refint_attrs; 62 63 typedef struct dependents_s { 64 struct dependents_s *next; 65 BerValue dn; /* target dn */ 66 BerValue ndn; 67 refint_attrs *attrs; 68 } dependent_data; 69 70 typedef struct refint_q { 71 struct refint_q *next; 72 struct refint_data_s *rdata; 73 dependent_data *attrs; /* entries and attrs returned from callback */ 74 BackendDB *db; 75 BerValue olddn; 76 BerValue oldndn; 77 BerValue newdn; 78 BerValue newndn; 79 } refint_q; 80 81 typedef struct refint_data_s { 82 struct refint_attrs_s *attrs; /* list of known attrs */ 83 BerValue dn; /* basedn in parent, */ 84 BerValue nothing; /* the nothing value, if needed */ 85 BerValue nnothing; /* normalized nothingness */ 86 BerValue refint_dn; /* modifier's name */ 87 BerValue refint_ndn; /* normalized modifier's name */ 88 struct re_s *qtask; 89 refint_q *qhead; 90 refint_q *qtail; 91 ldap_pvt_thread_mutex_t qmutex; 92 } refint_data; 93 94 #define RUNQ_INTERVAL 36000 /* a long time */ 95 96 static MatchingRule *mr_dnSubtreeMatch; 97 98 enum { 99 REFINT_ATTRS = 1, 100 REFINT_NOTHING, 101 REFINT_MODIFIERSNAME 102 }; 103 104 static ConfigDriver refint_cf_gen; 105 106 static ConfigTable refintcfg[] = { 107 { "refint_attributes", "attribute...", 2, 0, 0, 108 ARG_MAGIC|REFINT_ATTRS, refint_cf_gen, 109 "( OLcfgOvAt:11.1 NAME 'olcRefintAttribute' " 110 "DESC 'Attributes for referential integrity' " 111 "EQUALITY caseIgnoreMatch " 112 "SYNTAX OMsDirectoryString )", NULL, NULL }, 113 { "refint_nothing", "string", 2, 2, 0, 114 ARG_DN|ARG_MAGIC|REFINT_NOTHING, refint_cf_gen, 115 "( OLcfgOvAt:11.2 NAME 'olcRefintNothing' " 116 "DESC 'Replacement DN to supply when needed' " 117 "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL }, 118 { "refint_modifiersName", "DN", 2, 2, 0, 119 ARG_DN|ARG_MAGIC|REFINT_MODIFIERSNAME, refint_cf_gen, 120 "( OLcfgOvAt:11.3 NAME 'olcRefintModifiersName' " 121 "DESC 'The DN to use as modifiersName' " 122 "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL }, 123 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 124 }; 125 126 static ConfigOCs refintocs[] = { 127 { "( OLcfgOvOc:11.1 " 128 "NAME 'olcRefintConfig' " 129 "DESC 'Referential integrity configuration' " 130 "SUP olcOverlayConfig " 131 "MAY ( olcRefintAttribute " 132 "$ olcRefintNothing " 133 "$ olcRefintModifiersName " 134 ") )", 135 Cft_Overlay, refintcfg }, 136 { NULL, 0, NULL } 137 }; 138 139 static int 140 refint_cf_gen(ConfigArgs *c) 141 { 142 slap_overinst *on = (slap_overinst *)c->bi; 143 refint_data *dd = (refint_data *)on->on_bi.bi_private; 144 refint_attrs *ip, *pip, **pipp = NULL; 145 AttributeDescription *ad; 146 const char *text; 147 int rc = ARG_BAD_CONF; 148 int i; 149 150 switch ( c->op ) { 151 case SLAP_CONFIG_EMIT: 152 switch ( c->type ) { 153 case REFINT_ATTRS: 154 ip = dd->attrs; 155 while ( ip ) { 156 value_add_one( &c->rvalue_vals, 157 &ip->attr->ad_cname ); 158 ip = ip->next; 159 } 160 rc = 0; 161 break; 162 case REFINT_NOTHING: 163 if ( !BER_BVISEMPTY( &dd->nothing )) { 164 rc = value_add_one( &c->rvalue_vals, 165 &dd->nothing ); 166 if ( rc ) return rc; 167 rc = value_add_one( &c->rvalue_nvals, 168 &dd->nnothing ); 169 return rc; 170 } 171 rc = 0; 172 break; 173 case REFINT_MODIFIERSNAME: 174 if ( !BER_BVISEMPTY( &dd->refint_dn )) { 175 rc = value_add_one( &c->rvalue_vals, 176 &dd->refint_dn ); 177 if ( rc ) return rc; 178 rc = value_add_one( &c->rvalue_nvals, 179 &dd->refint_ndn ); 180 return rc; 181 } 182 rc = 0; 183 break; 184 default: 185 abort (); 186 } 187 break; 188 case LDAP_MOD_DELETE: 189 switch ( c->type ) { 190 case REFINT_ATTRS: 191 pipp = &dd->attrs; 192 if ( c->valx < 0 ) { 193 ip = *pipp; 194 *pipp = NULL; 195 while ( ip ) { 196 pip = ip; 197 ip = ip->next; 198 ch_free ( pip ); 199 } 200 } else { 201 /* delete from linked list */ 202 for ( i=0; i < c->valx; ++i ) { 203 pipp = &(*pipp)->next; 204 } 205 ip = *pipp; 206 *pipp = (*pipp)->next; 207 208 /* AttributeDescriptions are global so 209 * shouldn't be freed here... */ 210 ch_free ( ip ); 211 } 212 rc = 0; 213 break; 214 case REFINT_NOTHING: 215 ch_free( dd->nothing.bv_val ); 216 ch_free( dd->nnothing.bv_val ); 217 BER_BVZERO( &dd->nothing ); 218 BER_BVZERO( &dd->nnothing ); 219 rc = 0; 220 break; 221 case REFINT_MODIFIERSNAME: 222 ch_free( dd->refint_dn.bv_val ); 223 ch_free( dd->refint_ndn.bv_val ); 224 BER_BVZERO( &dd->refint_dn ); 225 BER_BVZERO( &dd->refint_ndn ); 226 rc = 0; 227 break; 228 default: 229 abort (); 230 } 231 break; 232 case SLAP_CONFIG_ADD: 233 /* fallthrough to LDAP_MOD_ADD */ 234 case LDAP_MOD_ADD: 235 switch ( c->type ) { 236 case REFINT_ATTRS: 237 rc = 0; 238 for ( i=1; i < c->argc; ++i ) { 239 ad = NULL; 240 if ( slap_str2ad ( c->argv[i], &ad, &text ) 241 == LDAP_SUCCESS) { 242 ip = ch_malloc ( 243 sizeof ( refint_attrs ) ); 244 ip->attr = ad; 245 ip->next = dd->attrs; 246 dd->attrs = ip; 247 } else { 248 snprintf( c->cr_msg, sizeof( c->cr_msg ), 249 "%s <%s>: %s", c->argv[0], c->argv[i], text ); 250 Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 251 "%s: %s\n", c->log, c->cr_msg, 0 ); 252 rc = ARG_BAD_CONF; 253 } 254 } 255 break; 256 case REFINT_NOTHING: 257 if ( !BER_BVISNULL( &c->value_ndn )) { 258 ch_free ( dd->nothing.bv_val ); 259 ch_free ( dd->nnothing.bv_val ); 260 dd->nothing = c->value_dn; 261 dd->nnothing = c->value_ndn; 262 rc = 0; 263 } else { 264 rc = ARG_BAD_CONF; 265 } 266 break; 267 case REFINT_MODIFIERSNAME: 268 if ( !BER_BVISNULL( &c->value_ndn )) { 269 ch_free( dd->refint_dn.bv_val ); 270 ch_free( dd->refint_ndn.bv_val ); 271 dd->refint_dn = c->value_dn; 272 dd->refint_ndn = c->value_ndn; 273 rc = 0; 274 } else { 275 rc = ARG_BAD_CONF; 276 } 277 break; 278 default: 279 abort (); 280 } 281 break; 282 default: 283 abort (); 284 } 285 286 return rc; 287 } 288 289 /* 290 ** allocate new refint_data; 291 ** store in on_bi.bi_private; 292 ** 293 */ 294 295 static int 296 refint_db_init( 297 BackendDB *be, 298 ConfigReply *cr 299 ) 300 { 301 slap_overinst *on = (slap_overinst *)be->bd_info; 302 refint_data *id = ch_calloc(1,sizeof(refint_data)); 303 304 on->on_bi.bi_private = id; 305 ldap_pvt_thread_mutex_init( &id->qmutex ); 306 return(0); 307 } 308 309 static int 310 refint_db_destroy( 311 BackendDB *be, 312 ConfigReply *cr 313 ) 314 { 315 slap_overinst *on = (slap_overinst *)be->bd_info; 316 317 if ( on->on_bi.bi_private ) { 318 refint_data *id = on->on_bi.bi_private; 319 on->on_bi.bi_private = NULL; 320 ldap_pvt_thread_mutex_destroy( &id->qmutex ); 321 ch_free( id ); 322 } 323 return(0); 324 } 325 326 /* 327 ** initialize, copy basedn if not already set 328 ** 329 */ 330 331 static int 332 refint_open( 333 BackendDB *be, 334 ConfigReply *cr 335 ) 336 { 337 slap_overinst *on = (slap_overinst *)be->bd_info; 338 refint_data *id = on->on_bi.bi_private; 339 340 if ( BER_BVISNULL( &id->dn )) { 341 if ( BER_BVISNULL( &be->be_nsuffix[0] )) 342 return -1; 343 ber_dupbv( &id->dn, &be->be_nsuffix[0] ); 344 } 345 if ( BER_BVISNULL( &id->refint_dn ) ) { 346 ber_dupbv( &id->refint_dn, &refint_dn ); 347 ber_dupbv( &id->refint_ndn, &refint_ndn ); 348 } 349 return(0); 350 } 351 352 353 /* 354 ** foreach configured attribute: 355 ** free it; 356 ** free our basedn; 357 ** reset on_bi.bi_private; 358 ** free our config data; 359 ** 360 */ 361 362 static int 363 refint_close( 364 BackendDB *be, 365 ConfigReply *cr 366 ) 367 { 368 slap_overinst *on = (slap_overinst *) be->bd_info; 369 refint_data *id = on->on_bi.bi_private; 370 refint_attrs *ii, *ij; 371 372 for(ii = id->attrs; ii; ii = ij) { 373 ij = ii->next; 374 ch_free(ii); 375 } 376 id->attrs = NULL; 377 378 ch_free( id->dn.bv_val ); 379 BER_BVZERO( &id->dn ); 380 ch_free( id->nothing.bv_val ); 381 BER_BVZERO( &id->nothing ); 382 ch_free( id->nnothing.bv_val ); 383 BER_BVZERO( &id->nnothing ); 384 ch_free( id->refint_dn.bv_val ); 385 BER_BVZERO( &id->refint_dn ); 386 ch_free( id->refint_ndn.bv_val ); 387 BER_BVZERO( &id->refint_ndn ); 388 389 return(0); 390 } 391 392 /* 393 ** search callback 394 ** generates a list of Attributes from search results 395 */ 396 397 static int 398 refint_search_cb( 399 Operation *op, 400 SlapReply *rs 401 ) 402 { 403 Attribute *a; 404 BerVarray b = NULL; 405 refint_q *rq = op->o_callback->sc_private; 406 refint_data *dd = rq->rdata; 407 refint_attrs *ia, *da = dd->attrs, *na; 408 dependent_data *ip; 409 int i; 410 411 Debug(LDAP_DEBUG_TRACE, "refint_search_cb <%s>\n", 412 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0); 413 414 if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0); 415 416 /* 417 ** foreach configured attribute type: 418 ** if this attr exists in the search result, 419 ** and it has a value matching the target: 420 ** allocate an attr; 421 ** save/build DNs of any subordinate matches; 422 ** handle special case: found exact + subordinate match; 423 ** handle olcRefintNothing; 424 ** 425 */ 426 427 ip = op->o_tmpalloc(sizeof(dependent_data), op->o_tmpmemctx ); 428 ber_dupbv_x( &ip->dn, &rs->sr_entry->e_name, op->o_tmpmemctx ); 429 ber_dupbv_x( &ip->ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx ); 430 ip->next = rq->attrs; 431 rq->attrs = ip; 432 ip->attrs = NULL; 433 for(ia = da; ia; ia = ia->next) { 434 if ( (a = attr_find(rs->sr_entry->e_attrs, ia->attr) ) ) { 435 int exact = -1, is_exact; 436 437 na = NULL; 438 439 for(i = 0, b = a->a_nvals; b[i].bv_val; i++) { 440 if(dnIsSuffix(&b[i], &rq->oldndn)) { 441 is_exact = b[i].bv_len == rq->oldndn.bv_len; 442 443 /* Paranoia: skip buggy duplicate exact match, 444 * it would break ra_numvals 445 */ 446 if ( is_exact && exact >= 0 ) 447 continue; 448 449 /* first match? create structure */ 450 if ( na == NULL ) { 451 na = op->o_tmpcalloc( 1, 452 sizeof( refint_attrs ), 453 op->o_tmpmemctx ); 454 na->next = ip->attrs; 455 ip->attrs = na; 456 na->attr = ia->attr; 457 } 458 459 na->ra_numvals++; 460 461 if ( is_exact ) { 462 /* Exact match: refint_repair will deduce the DNs */ 463 exact = i; 464 465 } else { 466 /* Subordinate match */ 467 struct berval newsub, newdn, olddn, oldndn; 468 469 /* Save old DN */ 470 ber_dupbv_x( &olddn, &a->a_vals[i], op->o_tmpmemctx ); 471 ber_bvarray_add_x( &na->old_vals, &olddn, op->o_tmpmemctx ); 472 473 ber_dupbv_x( &oldndn, &a->a_nvals[i], op->o_tmpmemctx ); 474 ber_bvarray_add_x( &na->old_nvals, &oldndn, op->o_tmpmemctx ); 475 476 if ( BER_BVISEMPTY( &rq->newdn ) ) 477 continue; 478 479 /* Rename subordinate match: Build new DN */ 480 newsub = a->a_vals[i]; 481 newsub.bv_len -= rq->olddn.bv_len + 1; 482 build_new_dn( &newdn, &rq->newdn, &newsub, op->o_tmpmemctx ); 483 ber_bvarray_add_x( &na->new_vals, &newdn, op->o_tmpmemctx ); 484 485 newsub = a->a_nvals[i]; 486 newsub.bv_len -= rq->oldndn.bv_len + 1; 487 build_new_dn( &newdn, &rq->newndn, &newsub, op->o_tmpmemctx ); 488 ber_bvarray_add_x( &na->new_nvals, &newdn, op->o_tmpmemctx ); 489 } 490 } 491 } 492 493 /* If we got both subordinate and exact match, 494 * refint_repair won't special-case the exact match */ 495 if ( exact >= 0 && na->old_vals ) { 496 struct berval dn; 497 498 ber_dupbv_x( &dn, &a->a_vals[exact], op->o_tmpmemctx ); 499 ber_bvarray_add_x( &na->old_vals, &dn, op->o_tmpmemctx ); 500 ber_dupbv_x( &dn, &a->a_nvals[exact], op->o_tmpmemctx ); 501 ber_bvarray_add_x( &na->old_nvals, &dn, op->o_tmpmemctx ); 502 503 if ( !BER_BVISEMPTY( &rq->newdn ) ) { 504 ber_dupbv_x( &dn, &rq->newdn, op->o_tmpmemctx ); 505 ber_bvarray_add_x( &na->new_vals, &dn, op->o_tmpmemctx ); 506 ber_dupbv_x( &dn, &rq->newndn, op->o_tmpmemctx ); 507 ber_bvarray_add_x( &na->new_nvals, &dn, op->o_tmpmemctx ); 508 } 509 } 510 511 /* Deleting/replacing all values and a nothing DN is configured? */ 512 if ( na && na->ra_numvals == i && !BER_BVISNULL(&dd->nothing) ) 513 na->dont_empty = 1; 514 515 Debug( LDAP_DEBUG_TRACE, "refint_search_cb: %s: %s (#%d)\n", 516 a->a_desc->ad_cname.bv_val, rq->olddn.bv_val, i ); 517 } 518 } 519 520 return(0); 521 } 522 523 static int 524 refint_repair( 525 Operation *op, 526 refint_data *id, 527 refint_q *rq ) 528 { 529 dependent_data *dp; 530 SlapReply rs = {REP_RESULT}; 531 Operation op2; 532 unsigned long opid; 533 int rc; 534 535 op->o_callback->sc_response = refint_search_cb; 536 op->o_req_dn = op->o_bd->be_suffix[ 0 ]; 537 op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ]; 538 op->o_dn = op->o_bd->be_rootdn; 539 op->o_ndn = op->o_bd->be_rootndn; 540 541 /* search */ 542 rc = op->o_bd->be_search( op, &rs ); 543 544 if ( rc != LDAP_SUCCESS ) { 545 Debug( LDAP_DEBUG_TRACE, 546 "refint_repair: search failed: %d\n", 547 rc, 0, 0 ); 548 return 0; 549 } 550 551 /* safety? paranoid just in case */ 552 if ( op->o_callback->sc_private == NULL ) { 553 Debug( LDAP_DEBUG_TRACE, 554 "refint_repair: callback wiped out sc_private?!\n", 555 0, 0, 0 ); 556 return 0; 557 } 558 559 /* Set up the Modify requests */ 560 op->o_callback->sc_response = &slap_null_cb; 561 562 /* 563 * [our search callback builds a list of attrs] 564 * foreach attr: 565 * make sure its dn has a backend; 566 * build Modification* chain; 567 * call the backend modify function; 568 * 569 */ 570 571 opid = op->o_opid; 572 op2 = *op; 573 for ( dp = rq->attrs; dp; dp = dp->next ) { 574 SlapReply rs2 = {REP_RESULT}; 575 refint_attrs *ra; 576 Modifications *m; 577 578 if ( dp->attrs == NULL ) continue; /* TODO: Is this needed? */ 579 580 op2.o_bd = select_backend( &dp->ndn, 1 ); 581 if ( !op2.o_bd ) { 582 Debug( LDAP_DEBUG_TRACE, 583 "refint_repair: no backend for DN %s!\n", 584 dp->dn.bv_val, 0, 0 ); 585 continue; 586 } 587 op2.o_tag = LDAP_REQ_MODIFY; 588 op2.orm_modlist = NULL; 589 op2.o_req_dn = dp->dn; 590 op2.o_req_ndn = dp->ndn; 591 /* Internal ops, never replicate these */ 592 op2.orm_no_opattrs = 1; 593 op2.o_dont_replicate = 1; 594 op2.o_opid = 0; 595 596 /* Set our ModifiersName */ 597 if ( SLAP_LASTMOD( op->o_bd ) ) { 598 m = op2.o_tmpalloc( sizeof(Modifications) + 599 4*sizeof(BerValue), op2.o_tmpmemctx ); 600 m->sml_next = op2.orm_modlist; 601 op2.orm_modlist = m; 602 m->sml_op = LDAP_MOD_REPLACE; 603 m->sml_flags = SLAP_MOD_INTERNAL; 604 m->sml_desc = slap_schema.si_ad_modifiersName; 605 m->sml_type = m->sml_desc->ad_cname; 606 m->sml_numvals = 1; 607 m->sml_values = (BerVarray)(m+1); 608 m->sml_nvalues = m->sml_values+2; 609 BER_BVZERO( &m->sml_values[1] ); 610 BER_BVZERO( &m->sml_nvalues[1] ); 611 m->sml_values[0] = id->refint_dn; 612 m->sml_nvalues[0] = id->refint_ndn; 613 } 614 615 for ( ra = dp->attrs; ra; ra = ra->next ) { 616 size_t len; 617 618 /* Add values */ 619 if ( ra->dont_empty || !BER_BVISEMPTY( &rq->newdn ) ) { 620 len = sizeof(Modifications); 621 622 if ( ra->new_vals == NULL ) { 623 len += 4*sizeof(BerValue); 624 } 625 626 m = op2.o_tmpalloc( len, op2.o_tmpmemctx ); 627 m->sml_next = op2.orm_modlist; 628 op2.orm_modlist = m; 629 m->sml_op = LDAP_MOD_ADD; 630 m->sml_flags = 0; 631 m->sml_desc = ra->attr; 632 m->sml_type = ra->attr->ad_cname; 633 if ( ra->new_vals == NULL ) { 634 m->sml_values = (BerVarray)(m+1); 635 m->sml_nvalues = m->sml_values+2; 636 BER_BVZERO( &m->sml_values[1] ); 637 BER_BVZERO( &m->sml_nvalues[1] ); 638 m->sml_numvals = 1; 639 if ( BER_BVISEMPTY( &rq->newdn ) ) { 640 m->sml_values[0] = id->nothing; 641 m->sml_nvalues[0] = id->nnothing; 642 } else { 643 m->sml_values[0] = rq->newdn; 644 m->sml_nvalues[0] = rq->newndn; 645 } 646 } else { 647 m->sml_values = ra->new_vals; 648 m->sml_nvalues = ra->new_nvals; 649 m->sml_numvals = ra->ra_numvals; 650 } 651 } 652 653 /* Delete values */ 654 len = sizeof(Modifications); 655 if ( ra->old_vals == NULL ) { 656 len += 4*sizeof(BerValue); 657 } 658 m = op2.o_tmpalloc( len, op2.o_tmpmemctx ); 659 m->sml_next = op2.orm_modlist; 660 op2.orm_modlist = m; 661 m->sml_op = LDAP_MOD_DELETE; 662 m->sml_flags = 0; 663 m->sml_desc = ra->attr; 664 m->sml_type = ra->attr->ad_cname; 665 if ( ra->old_vals == NULL ) { 666 m->sml_numvals = 1; 667 m->sml_values = (BerVarray)(m+1); 668 m->sml_nvalues = m->sml_values+2; 669 m->sml_values[0] = rq->olddn; 670 m->sml_nvalues[0] = rq->oldndn; 671 BER_BVZERO( &m->sml_values[1] ); 672 BER_BVZERO( &m->sml_nvalues[1] ); 673 } else { 674 m->sml_values = ra->old_vals; 675 m->sml_nvalues = ra->old_nvals; 676 m->sml_numvals = ra->ra_numvals; 677 } 678 } 679 680 op2.o_dn = op2.o_bd->be_rootdn; 681 op2.o_ndn = op2.o_bd->be_rootndn; 682 rc = op2.o_bd->be_modify( &op2, &rs2 ); 683 if ( rc != LDAP_SUCCESS ) { 684 Debug( LDAP_DEBUG_TRACE, 685 "refint_repair: dependent modify failed: %d\n", 686 rs2.sr_err, 0, 0 ); 687 } 688 689 while ( ( m = op2.orm_modlist ) ) { 690 op2.orm_modlist = m->sml_next; 691 op2.o_tmpfree( m, op2.o_tmpmemctx ); 692 } 693 } 694 op2.o_opid = opid; 695 696 return 0; 697 } 698 699 static void * 700 refint_qtask( void *ctx, void *arg ) 701 { 702 struct re_s *rtask = arg; 703 refint_data *id = rtask->arg; 704 Connection conn = {0}; 705 OperationBuffer opbuf; 706 Operation *op; 707 slap_callback cb = { NULL, NULL, NULL, NULL }; 708 Filter ftop, *fptr; 709 refint_q *rq; 710 refint_attrs *ip; 711 712 connection_fake_init( &conn, &opbuf, ctx ); 713 op = &opbuf.ob_op; 714 715 /* 716 ** build a search filter for all configured attributes; 717 ** populate our Operation; 718 ** pass our data (attr list, dn) to backend via sc_private; 719 ** call the backend search function; 720 ** nb: (|(one=thing)) is valid, but do smart formatting anyway; 721 ** nb: 16 is arbitrarily a dozen or so extra bytes; 722 ** 723 */ 724 725 ftop.f_choice = LDAP_FILTER_OR; 726 ftop.f_next = NULL; 727 ftop.f_or = NULL; 728 op->ors_filter = &ftop; 729 for(ip = id->attrs; ip; ip = ip->next) { 730 fptr = op->o_tmpcalloc( sizeof(Filter) + sizeof(MatchingRuleAssertion), 731 1, op->o_tmpmemctx ); 732 /* Use (attr:dnSubtreeMatch:=value) to catch subtree rename 733 * and subtree delete where supported */ 734 fptr->f_choice = LDAP_FILTER_EXT; 735 fptr->f_mra = (MatchingRuleAssertion *)(fptr+1); 736 fptr->f_mr_rule = mr_dnSubtreeMatch; 737 fptr->f_mr_rule_text = mr_dnSubtreeMatch->smr_bvoid; 738 fptr->f_mr_desc = ip->attr; 739 fptr->f_mr_dnattrs = 0; 740 fptr->f_next = ftop.f_or; 741 ftop.f_or = fptr; 742 } 743 744 for (;;) { 745 dependent_data *dp, *dp_next; 746 refint_attrs *ra, *ra_next; 747 748 /* Dequeue an op */ 749 ldap_pvt_thread_mutex_lock( &id->qmutex ); 750 rq = id->qhead; 751 if ( rq ) { 752 id->qhead = rq->next; 753 if ( !id->qhead ) 754 id->qtail = NULL; 755 } 756 ldap_pvt_thread_mutex_unlock( &id->qmutex ); 757 if ( !rq ) 758 break; 759 760 for (fptr = ftop.f_or; fptr; fptr = fptr->f_next ) 761 fptr->f_mr_value = rq->oldndn; 762 763 filter2bv_x( op, op->ors_filter, &op->ors_filterstr ); 764 765 /* callback gets the searched dn instead */ 766 cb.sc_private = rq; 767 cb.sc_response = refint_search_cb; 768 op->o_callback = &cb; 769 op->o_tag = LDAP_REQ_SEARCH; 770 op->ors_scope = LDAP_SCOPE_SUBTREE; 771 op->ors_deref = LDAP_DEREF_NEVER; 772 op->ors_limit = NULL; 773 op->ors_slimit = SLAP_NO_LIMIT; 774 op->ors_tlimit = SLAP_NO_LIMIT; 775 776 /* no attrs! */ 777 op->ors_attrs = slap_anlist_no_attrs; 778 779 slap_op_time( &op->o_time, &op->o_tincr ); 780 781 if ( rq->db != NULL ) { 782 op->o_bd = rq->db; 783 refint_repair( op, id, rq ); 784 785 } else { 786 BackendDB *be; 787 788 LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) { 789 /* we may want to skip cn=config */ 790 if ( be == LDAP_STAILQ_FIRST(&backendDB) ) { 791 continue; 792 } 793 794 if ( be->be_search && be->be_modify ) { 795 op->o_bd = be; 796 refint_repair( op, id, rq ); 797 } 798 } 799 } 800 801 for ( dp = rq->attrs; dp; dp = dp_next ) { 802 dp_next = dp->next; 803 for ( ra = dp->attrs; ra; ra = ra_next ) { 804 ra_next = ra->next; 805 ber_bvarray_free_x( ra->new_nvals, op->o_tmpmemctx ); 806 ber_bvarray_free_x( ra->new_vals, op->o_tmpmemctx ); 807 ber_bvarray_free_x( ra->old_nvals, op->o_tmpmemctx ); 808 ber_bvarray_free_x( ra->old_vals, op->o_tmpmemctx ); 809 op->o_tmpfree( ra, op->o_tmpmemctx ); 810 } 811 op->o_tmpfree( dp->ndn.bv_val, op->o_tmpmemctx ); 812 op->o_tmpfree( dp->dn.bv_val, op->o_tmpmemctx ); 813 op->o_tmpfree( dp, op->o_tmpmemctx ); 814 } 815 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 816 817 if ( !BER_BVISNULL( &rq->newndn )) { 818 ch_free( rq->newndn.bv_val ); 819 ch_free( rq->newdn.bv_val ); 820 } 821 ch_free( rq->oldndn.bv_val ); 822 ch_free( rq->olddn.bv_val ); 823 ch_free( rq ); 824 } 825 826 /* free filter */ 827 for ( fptr = ftop.f_or; fptr; ) { 828 Filter *f_next = fptr->f_next; 829 op->o_tmpfree( fptr, op->o_tmpmemctx ); 830 fptr = f_next; 831 } 832 833 /* wait until we get explicitly scheduled again */ 834 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 835 ldap_pvt_runqueue_stoptask( &slapd_rq, id->qtask ); 836 ldap_pvt_runqueue_resched( &slapd_rq,id->qtask, 1 ); 837 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 838 839 return NULL; 840 } 841 842 /* 843 ** refint_response 844 ** search for matching records and modify them 845 */ 846 847 static int 848 refint_response( 849 Operation *op, 850 SlapReply *rs 851 ) 852 { 853 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 854 refint_data *id = on->on_bi.bi_private; 855 BerValue pdn; 856 int ac; 857 refint_q *rq; 858 BackendDB *db = NULL; 859 refint_attrs *ip; 860 861 /* If the main op failed or is not a Delete or ModRdn, ignore it */ 862 if (( op->o_tag != LDAP_REQ_DELETE && op->o_tag != LDAP_REQ_MODRDN ) || 863 rs->sr_err != LDAP_SUCCESS ) 864 return SLAP_CB_CONTINUE; 865 866 /* 867 ** validate (and count) the list of attrs; 868 ** 869 */ 870 871 for(ip = id->attrs, ac = 0; ip; ip = ip->next, ac++); 872 if(!ac) { 873 Debug( LDAP_DEBUG_TRACE, 874 "refint_response called without any attributes\n", 0, 0, 0 ); 875 return SLAP_CB_CONTINUE; 876 } 877 878 /* 879 ** find the backend that matches our configured basedn; 880 ** make sure it exists and has search and modify methods; 881 ** 882 */ 883 884 if ( on->on_info->oi_origdb != frontendDB ) { 885 db = select_backend(&id->dn, 1); 886 887 if ( db ) { 888 if ( !db->be_search || !db->be_modify ) { 889 Debug( LDAP_DEBUG_TRACE, 890 "refint_response: backend missing search and/or modify\n", 891 0, 0, 0 ); 892 return SLAP_CB_CONTINUE; 893 } 894 } else { 895 Debug( LDAP_DEBUG_TRACE, 896 "refint_response: no backend for our baseDN %s??\n", 897 id->dn.bv_val, 0, 0 ); 898 return SLAP_CB_CONTINUE; 899 } 900 } 901 902 rq = ch_calloc( 1, sizeof( refint_q )); 903 ber_dupbv( &rq->olddn, &op->o_req_dn ); 904 ber_dupbv( &rq->oldndn, &op->o_req_ndn ); 905 rq->db = db; 906 rq->rdata = id; 907 908 if ( op->o_tag == LDAP_REQ_MODRDN ) { 909 if ( op->oq_modrdn.rs_newSup ) { 910 pdn = *op->oq_modrdn.rs_newSup; 911 } else { 912 dnParent( &op->o_req_dn, &pdn ); 913 } 914 build_new_dn( &rq->newdn, &pdn, &op->orr_newrdn, NULL ); 915 if ( op->oq_modrdn.rs_nnewSup ) { 916 pdn = *op->oq_modrdn.rs_nnewSup; 917 } else { 918 dnParent( &op->o_req_ndn, &pdn ); 919 } 920 build_new_dn( &rq->newndn, &pdn, &op->orr_nnewrdn, NULL ); 921 } 922 923 ldap_pvt_thread_mutex_lock( &id->qmutex ); 924 if ( id->qtail ) { 925 id->qtail->next = rq; 926 } else { 927 id->qhead = rq; 928 } 929 id->qtail = rq; 930 ldap_pvt_thread_mutex_unlock( &id->qmutex ); 931 932 ac = 0; 933 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); 934 if ( !id->qtask ) { 935 id->qtask = ldap_pvt_runqueue_insert( &slapd_rq, RUNQ_INTERVAL, 936 refint_qtask, id, "refint_qtask", 937 op->o_bd->be_suffix[0].bv_val ); 938 ac = 1; 939 } else { 940 if ( !ldap_pvt_runqueue_isrunning( &slapd_rq, id->qtask ) && 941 !id->qtask->next_sched.tv_sec ) { 942 id->qtask->interval.tv_sec = 0; 943 ldap_pvt_runqueue_resched( &slapd_rq, id->qtask, 0 ); 944 id->qtask->interval.tv_sec = RUNQ_INTERVAL; 945 ac = 1; 946 } 947 } 948 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex ); 949 if ( ac ) 950 slap_wake_listener(); 951 952 return SLAP_CB_CONTINUE; 953 } 954 955 /* 956 ** init_module is last so the symbols resolve "for free" -- 957 ** it expects to be called automagically during dynamic module initialization 958 */ 959 960 int refint_initialize() { 961 int rc; 962 963 mr_dnSubtreeMatch = mr_find( "dnSubtreeMatch" ); 964 if ( mr_dnSubtreeMatch == NULL ) { 965 Debug( LDAP_DEBUG_ANY, "refint_initialize: " 966 "unable to find MatchingRule 'dnSubtreeMatch'.\n", 967 0, 0, 0 ); 968 return 1; 969 } 970 971 /* statically declared just after the #includes at top */ 972 refint.on_bi.bi_type = "refint"; 973 refint.on_bi.bi_db_init = refint_db_init; 974 refint.on_bi.bi_db_destroy = refint_db_destroy; 975 refint.on_bi.bi_db_open = refint_open; 976 refint.on_bi.bi_db_close = refint_close; 977 refint.on_response = refint_response; 978 979 refint.on_bi.bi_cf_ocs = refintocs; 980 rc = config_register_schema ( refintcfg, refintocs ); 981 if ( rc ) return rc; 982 983 return(overlay_register(&refint)); 984 } 985 986 #if SLAPD_OVER_REFINT == SLAPD_MOD_DYNAMIC && defined(PIC) 987 int init_module(int argc, char *argv[]) { 988 return refint_initialize(); 989 } 990 #endif 991 992 #endif /* SLAPD_OVER_REFINT */ 993