1 /* translucent.c - translucent proxy module */ 2 /* $OpenLDAP: pkg/ldap/servers/slapd/overlays/translucent.c,v 1.13.2.16 2008/04/14 21:13:44 quanah Exp $ */ 3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 2004-2008 The OpenLDAP Foundation. 6 * Portions Copyright 2005 Symas Corporation. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17 /* ACKNOWLEDGEMENTS: 18 * This work was initially developed by Symas Corp. for inclusion in 19 * OpenLDAP Software. This work was sponsored by Hewlett-Packard. 20 */ 21 22 #include "portable.h" 23 24 #ifdef SLAPD_OVER_TRANSLUCENT 25 26 #include <stdio.h> 27 28 #include <ac/string.h> 29 #include <ac/socket.h> 30 31 #include "slap.h" 32 #include "lutil.h" 33 34 #include "config.h" 35 36 /* config block */ 37 typedef struct translucent_info { 38 BackendDB db; /* captive backend */ 39 AttributeName *local; /* valid attrs for local filters */ 40 AttributeName *remote; /* valid attrs for remote filters */ 41 int strict; 42 int no_glue; 43 int defer_db_open; 44 } translucent_info; 45 46 static ConfigLDAPadd translucent_ldadd; 47 static ConfigCfAdd translucent_cfadd; 48 49 static ConfigDriver translucent_cf_gen; 50 51 enum { 52 TRANS_LOCAL = 1, 53 TRANS_REMOTE 54 }; 55 56 static ConfigTable translucentcfg[] = { 57 { "translucent_strict", "on|off", 1, 2, 0, 58 ARG_ON_OFF|ARG_OFFSET, 59 (void *)offsetof(translucent_info, strict), 60 "( OLcfgOvAt:14.1 NAME 'olcTranslucentStrict' " 61 "DESC 'Reveal attribute deletion constraint violations' " 62 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, 63 { "translucent_no_glue", "on|off", 1, 2, 0, 64 ARG_ON_OFF|ARG_OFFSET, 65 (void *)offsetof(translucent_info, no_glue), 66 "( OLcfgOvAt:14.2 NAME 'olcTranslucentNoGlue' " 67 "DESC 'Disable automatic glue records for ADD and MODRDN' " 68 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, 69 { "translucent_local", "attr[,attr...]", 1, 2, 0, 70 ARG_STRING|ARG_MAGIC|TRANS_LOCAL, 71 translucent_cf_gen, 72 "( OLcfgOvAt:14.3 NAME 'olcTranslucentLocal' " 73 "DESC 'Attributes to use in local search filter' " 74 "SYNTAX OMsDirectoryString )", NULL, NULL }, 75 { "translucent_remote", "attr[,attr...]", 1, 2, 0, 76 ARG_STRING|ARG_MAGIC|TRANS_REMOTE, 77 translucent_cf_gen, 78 "( OLcfgOvAt:14.4 NAME 'olcTranslucentRemote' " 79 "DESC 'Attributes to use in remote search filter' " 80 "SYNTAX OMsDirectoryString )", NULL, NULL }, 81 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 82 }; 83 84 static ConfigTable transdummy[] = { 85 { "", "", 0, 0, 0, ARG_IGNORED, 86 NULL, "( OLcfgGlAt:13 NAME 'olcDatabase' " 87 "DESC 'The backend type for a database instance' " 88 "SUP olcBackend SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL }, 89 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 90 }; 91 92 static ConfigOCs translucentocs[] = { 93 { "( OLcfgOvOc:14.1 " 94 "NAME 'olcTranslucentConfig' " 95 "DESC 'Translucent configuration' " 96 "SUP olcOverlayConfig " 97 "MAY ( olcTranslucentStrict $ olcTranslucentNoGlue $" 98 " olcTranslucentLocal $ olcTranslucentRemote ) )", 99 Cft_Overlay, translucentcfg, NULL, translucent_cfadd }, 100 { "( OLcfgOvOc:14.2 " 101 "NAME 'olcTranslucentDatabase' " 102 "DESC 'Translucent target database configuration' " 103 "AUXILIARY )", Cft_Misc, transdummy, translucent_ldadd }, 104 { NULL, 0, NULL } 105 }; 106 /* for translucent_init() */ 107 108 static int 109 translucent_ldadd_cleanup( ConfigArgs *ca ) 110 { 111 slap_overinst *on = ca->ca_private; 112 translucent_info *ov = on->on_bi.bi_private; 113 114 ov->defer_db_open = 0; 115 return backend_startup_one( ca->be, &ca->reply ); 116 } 117 118 static int 119 translucent_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca ) 120 { 121 slap_overinst *on; 122 translucent_info *ov; 123 124 Debug(LDAP_DEBUG_TRACE, "==> translucent_ldadd\n", 0, 0, 0); 125 126 if ( cei->ce_type != Cft_Overlay || !cei->ce_bi || 127 cei->ce_bi->bi_cf_ocs != translucentocs ) 128 return LDAP_CONSTRAINT_VIOLATION; 129 130 on = (slap_overinst *)cei->ce_bi; 131 ov = on->on_bi.bi_private; 132 ca->be = &ov->db; 133 ca->ca_private = on; 134 if ( CONFIG_ONLINE_ADD( ca )) 135 ca->cleanup = translucent_ldadd_cleanup; 136 else 137 ov->defer_db_open = 0; 138 139 return LDAP_SUCCESS; 140 } 141 142 static int 143 translucent_cfadd( Operation *op, SlapReply *rs, Entry *e, ConfigArgs *ca ) 144 { 145 CfEntryInfo *cei = e->e_private; 146 slap_overinst *on = (slap_overinst *)cei->ce_bi; 147 translucent_info *ov = on->on_bi.bi_private; 148 struct berval bv; 149 150 Debug(LDAP_DEBUG_TRACE, "==> translucent_cfadd\n", 0, 0, 0); 151 152 /* FIXME: should not hardcode "olcDatabase" here */ 153 bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ), 154 "olcDatabase=%s", ov->db.bd_info->bi_type ); 155 if ( bv.bv_len < 0 || bv.bv_len >= sizeof( ca->cr_msg ) ) { 156 return -1; 157 } 158 bv.bv_val = ca->cr_msg; 159 ca->be = &ov->db; 160 ov->defer_db_open = 0; 161 162 /* We can only create this entry if the database is table-driven 163 */ 164 if ( ov->db.bd_info->bi_cf_ocs ) 165 config_build_entry( op, rs, cei, ca, &bv, 166 ov->db.bd_info->bi_cf_ocs, 167 &translucentocs[1] ); 168 169 return 0; 170 } 171 172 static int 173 translucent_cf_gen( ConfigArgs *c ) 174 { 175 slap_overinst *on = (slap_overinst *)c->bi; 176 translucent_info *ov = on->on_bi.bi_private; 177 AttributeName **an, *a2; 178 int i; 179 180 if ( c->type == TRANS_LOCAL ) 181 an = &ov->local; 182 else 183 an = &ov->remote; 184 185 if ( c->op == SLAP_CONFIG_EMIT ) { 186 if ( !*an ) 187 return 1; 188 for ( i = 0; !BER_BVISNULL(&(*an)[i].an_name); i++ ) { 189 value_add_one( &c->rvalue_vals, &(*an)[i].an_name ); 190 } 191 return ( i < 1 ); 192 } else if ( c->op == LDAP_MOD_DELETE ) { 193 if ( c->valx < 0 ) { 194 anlist_free( *an, 1, NULL ); 195 *an = NULL; 196 } else { 197 i = c->valx; 198 ch_free( (*an)[i].an_name.bv_val ); 199 do { 200 (*an)[i] = (*an)[i+1]; 201 } while ( !BER_BVISNULL( &(*an)[i].an_name )); 202 } 203 return 0; 204 } 205 a2 = str2anlist( *an, c->argv[1], "," ); 206 if ( !a2 ) { 207 snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s unable to parse attribute %s", 208 c->argv[0], c->argv[1] ); 209 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 210 "%s: %s\n", c->log, c->cr_msg, 0 ); 211 return ARG_BAD_CONF; 212 } 213 *an = a2; 214 return 0; 215 } 216 217 static slap_overinst translucent; 218 219 /* 220 ** glue_parent() 221 ** call syncrepl_add_glue() with the parent suffix; 222 ** 223 */ 224 225 static struct berval glue[] = { BER_BVC("top"), BER_BVC("glue"), BER_BVNULL }; 226 227 void glue_parent(Operation *op) { 228 Operation nop = *op; 229 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 230 struct berval ndn = BER_BVNULL; 231 Attribute *a; 232 Entry *e; 233 struct berval pdn; 234 235 dnParent( &op->o_req_ndn, &pdn ); 236 ber_dupbv_x( &ndn, &pdn, op->o_tmpmemctx ); 237 238 Debug(LDAP_DEBUG_TRACE, "=> glue_parent: fabricating glue for <%s>\n", ndn.bv_val, 0, 0); 239 240 e = entry_alloc(); 241 e->e_id = NOID; 242 ber_dupbv(&e->e_name, &ndn); 243 ber_dupbv(&e->e_nname, &ndn); 244 245 a = attr_alloc( slap_schema.si_ad_objectClass ); 246 a->a_numvals = 2; 247 a->a_vals = ch_malloc(sizeof(struct berval) * 3); 248 ber_dupbv(&a->a_vals[0], &glue[0]); 249 ber_dupbv(&a->a_vals[1], &glue[1]); 250 ber_dupbv(&a->a_vals[2], &glue[2]); 251 a->a_nvals = a->a_vals; 252 a->a_next = e->e_attrs; 253 e->e_attrs = a; 254 255 a = attr_alloc( slap_schema.si_ad_structuralObjectClass ); 256 a->a_numvals = 1; 257 a->a_vals = ch_malloc(sizeof(struct berval) * 2); 258 ber_dupbv(&a->a_vals[0], &glue[1]); 259 ber_dupbv(&a->a_vals[1], &glue[2]); 260 a->a_nvals = a->a_vals; 261 a->a_next = e->e_attrs; 262 e->e_attrs = a; 263 264 nop.o_req_dn = ndn; 265 nop.o_req_ndn = ndn; 266 nop.ora_e = e; 267 268 nop.o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig; 269 syncrepl_add_glue(&nop, e); 270 nop.o_bd->bd_info = (BackendInfo *) on; 271 272 op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx ); 273 274 return; 275 } 276 277 /* 278 ** dup_bervarray() 279 ** copy a BerVarray; 280 */ 281 282 BerVarray dup_bervarray(BerVarray b) { 283 int i, len; 284 BerVarray nb; 285 for(len = 0; b[len].bv_val; len++); 286 nb = ch_malloc((len+1) * sizeof(BerValue)); 287 for(i = 0; i < len; i++) ber_dupbv(&nb[i], &b[i]); 288 nb[len].bv_val = NULL; 289 nb[len].bv_len = 0; 290 return(nb); 291 } 292 293 /* 294 ** free_attr_chain() 295 ** free only the Attribute*, not the contents; 296 ** 297 */ 298 void free_attr_chain(Attribute *b) { 299 Attribute *a; 300 for(a=b; a; a=a->a_next) { 301 a->a_vals = NULL; 302 a->a_nvals = NULL; 303 } 304 attrs_free( b ); 305 return; 306 } 307 308 /* 309 ** translucent_add() 310 ** if not bound as root, send ACCESS error; 311 ** if glue, glue_parent(); 312 ** return CONTINUE; 313 ** 314 */ 315 316 static int translucent_add(Operation *op, SlapReply *rs) { 317 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 318 translucent_info *ov = on->on_bi.bi_private; 319 Debug(LDAP_DEBUG_TRACE, "==> translucent_add: %s\n", 320 op->o_req_dn.bv_val, 0, 0); 321 if(!be_isroot(op)) { 322 op->o_bd->bd_info = (BackendInfo *) on->on_info; 323 send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS, 324 "user modification of overlay database not permitted"); 325 op->o_bd->bd_info = (BackendInfo *) on; 326 return(rs->sr_err); 327 } 328 if(!ov->no_glue) glue_parent(op); 329 return(SLAP_CB_CONTINUE); 330 } 331 332 /* 333 ** translucent_modrdn() 334 ** if not bound as root, send ACCESS error; 335 ** if !glue, glue_parent(); 336 ** else return CONTINUE; 337 ** 338 */ 339 340 static int translucent_modrdn(Operation *op, SlapReply *rs) { 341 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 342 translucent_info *ov = on->on_bi.bi_private; 343 Debug(LDAP_DEBUG_TRACE, "==> translucent_modrdn: %s -> %s\n", 344 op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0); 345 if(!be_isroot(op)) { 346 op->o_bd->bd_info = (BackendInfo *) on->on_info; 347 send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS, 348 "user modification of overlay database not permitted"); 349 op->o_bd->bd_info = (BackendInfo *) on; 350 return(rs->sr_err); 351 } 352 if(!ov->no_glue) { 353 op->o_tag = LDAP_REQ_ADD; 354 glue_parent(op); 355 op->o_tag = LDAP_REQ_MODRDN; 356 } 357 return(SLAP_CB_CONTINUE); 358 } 359 360 /* 361 ** translucent_delete() 362 ** if not bound as root, send ACCESS error; 363 ** else return CONTINUE; 364 ** 365 */ 366 367 static int translucent_delete(Operation *op, SlapReply *rs) { 368 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 369 Debug(LDAP_DEBUG_TRACE, "==> translucent_delete: %s\n", 370 op->o_req_dn.bv_val, 0, 0); 371 if(!be_isroot(op)) { 372 op->o_bd->bd_info = (BackendInfo *) on->on_info; 373 send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS, 374 "user modification of overlay database not permitted"); 375 op->o_bd->bd_info = (BackendInfo *) on; 376 return(rs->sr_err); 377 } 378 return(SLAP_CB_CONTINUE); 379 } 380 381 static int 382 translucent_tag_cb( Operation *op, SlapReply *rs ) 383 { 384 op->o_tag = LDAP_REQ_MODIFY; 385 op->orm_modlist = op->o_callback->sc_private; 386 rs->sr_tag = slap_req2res( op->o_tag ); 387 388 return SLAP_CB_CONTINUE; 389 } 390 391 /* 392 ** translucent_modify() 393 ** modify in local backend if exists in both; 394 ** otherwise, add to local backend; 395 ** fail if not defined in captive backend; 396 ** 397 */ 398 399 static int translucent_modify(Operation *op, SlapReply *rs) { 400 SlapReply nrs = { REP_RESULT }; 401 402 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 403 translucent_info *ov = on->on_bi.bi_private; 404 Entry *e = NULL, *re = NULL; 405 Attribute *a, *ax; 406 Modifications *m, **mm; 407 BackendDB *db; 408 int del, rc, erc = 0; 409 slap_callback cb = { 0 }; 410 411 Debug(LDAP_DEBUG_TRACE, "==> translucent_modify: %s\n", 412 op->o_req_dn.bv_val, 0, 0); 413 414 if(ov->defer_db_open) { 415 send_ldap_error(op, rs, LDAP_UNAVAILABLE, 416 "remote DB not available"); 417 return(rs->sr_err); 418 } 419 /* 420 ** fetch entry from the captive backend; 421 ** if it did not exist, fail; 422 ** release it, if captive backend supports this; 423 ** 424 */ 425 426 db = op->o_bd; 427 op->o_bd = &ov->db; 428 rc = ov->db.bd_info->bi_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &re); 429 if(rc != LDAP_SUCCESS || re == NULL ) { 430 send_ldap_error((op), rs, LDAP_NO_SUCH_OBJECT, 431 "attempt to modify nonexistent local record"); 432 return(rs->sr_err); 433 } 434 op->o_bd = db; 435 /* 436 ** fetch entry from local backend; 437 ** if it exists: 438 ** foreach Modification: 439 ** if attr not present in local: 440 ** if Mod == LDAP_MOD_DELETE: 441 ** if remote attr not present, return NO_SUCH; 442 ** if remote attr present, drop this Mod; 443 ** else force this Mod to LDAP_MOD_ADD; 444 ** return CONTINUE; 445 ** 446 */ 447 448 op->o_bd->bd_info = (BackendInfo *) on->on_info; 449 rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e); 450 op->o_bd->bd_info = (BackendInfo *) on; 451 452 if(e && rc == LDAP_SUCCESS) { 453 Debug(LDAP_DEBUG_TRACE, "=> translucent_modify: found local entry\n", 0, 0, 0); 454 for(mm = &op->orm_modlist; *mm; ) { 455 m = *mm; 456 for(a = e->e_attrs; a; a = a->a_next) 457 if(a->a_desc == m->sml_desc) break; 458 if(a) { 459 mm = &m->sml_next; 460 continue; /* found local attr */ 461 } 462 if(m->sml_op == LDAP_MOD_DELETE) { 463 for(a = re->e_attrs; a; a = a->a_next) 464 if(a->a_desc == m->sml_desc) break; 465 /* not found remote attr */ 466 if(!a) { 467 erc = LDAP_NO_SUCH_ATTRIBUTE; 468 goto release; 469 } 470 if(ov->strict) { 471 erc = LDAP_CONSTRAINT_VIOLATION; 472 goto release; 473 } 474 Debug(LDAP_DEBUG_TRACE, 475 "=> translucent_modify: silently dropping delete: %s\n", 476 m->sml_desc->ad_cname.bv_val, 0, 0); 477 *mm = m->sml_next; 478 m->sml_next = NULL; 479 slap_mods_free(m, 1); 480 continue; 481 } 482 m->sml_op = LDAP_MOD_ADD; 483 mm = &m->sml_next; 484 } 485 erc = SLAP_CB_CONTINUE; 486 release: 487 if(re) { 488 if(ov->db.bd_info->bi_entry_release_rw) { 489 op->o_bd = &ov->db; 490 ov->db.bd_info->bi_entry_release_rw(op, re, 0); 491 op->o_bd = db; 492 } else 493 entry_free(re); 494 } 495 op->o_bd->bd_info = (BackendInfo *) on->on_info; 496 be_entry_release_r(op, e); 497 op->o_bd->bd_info = (BackendInfo *) on; 498 if(erc == SLAP_CB_CONTINUE) { 499 return(erc); 500 } else if(erc) { 501 send_ldap_error(op, rs, erc, 502 "attempt to delete nonexistent attribute"); 503 return(erc); 504 } 505 } 506 507 /* don't leak remote entry copy */ 508 if(re) { 509 if(ov->db.bd_info->bi_entry_release_rw) { 510 op->o_bd = &ov->db; 511 ov->db.bd_info->bi_entry_release_rw(op, re, 0); 512 op->o_bd = db; 513 } else 514 entry_free(re); 515 } 516 /* 517 ** foreach Modification: 518 ** if MOD_ADD or MOD_REPLACE, add Attribute; 519 ** if no Modifications were suitable: 520 ** if strict, throw CONSTRAINT_VIOLATION; 521 ** else, return early SUCCESS; 522 ** fabricate Entry with new Attribute chain; 523 ** glue_parent() for this Entry; 524 ** call bi_op_add() in local backend; 525 ** 526 */ 527 528 Debug(LDAP_DEBUG_TRACE, "=> translucent_modify: fabricating local add\n", 0, 0, 0); 529 a = NULL; 530 for(del = 0, ax = NULL, m = op->orm_modlist; m; m = m->sml_next) { 531 Attribute atmp; 532 if(((m->sml_op & LDAP_MOD_OP) != LDAP_MOD_ADD) && 533 ((m->sml_op & LDAP_MOD_OP) != LDAP_MOD_REPLACE)) { 534 Debug(LDAP_DEBUG_ANY, 535 "=> translucent_modify: silently dropped modification(%d): %s\n", 536 m->sml_op, m->sml_desc->ad_cname.bv_val, 0); 537 if((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) del++; 538 continue; 539 } 540 atmp.a_desc = m->sml_desc; 541 atmp.a_vals = m->sml_values; 542 atmp.a_nvals = m->sml_nvalues ? m->sml_nvalues : atmp.a_vals; 543 atmp.a_numvals = m->sml_numvals; 544 atmp.a_flags = 0; 545 a = attr_dup( &atmp ); 546 a->a_next = ax; 547 ax = a; 548 } 549 550 if(del && ov->strict) { 551 attrs_free( a ); 552 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, 553 "attempt to delete attributes from local database"); 554 return(rs->sr_err); 555 } 556 557 if(!ax) { 558 if(ov->strict) { 559 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, 560 "modification contained other than ADD or REPLACE"); 561 return(rs->sr_err); 562 } 563 /* rs->sr_text = "no valid modification found"; */ 564 rs->sr_err = LDAP_SUCCESS; 565 send_ldap_result(op, rs); 566 return(rs->sr_err); 567 } 568 569 e = entry_alloc(); 570 ber_dupbv( &e->e_name, &op->o_req_dn ); 571 ber_dupbv( &e->e_nname, &op->o_req_ndn ); 572 e->e_attrs = a; 573 574 op->o_tag = LDAP_REQ_ADD; 575 cb.sc_response = translucent_tag_cb; 576 cb.sc_private = op->orm_modlist; 577 op->oq_add.rs_e = e; 578 579 glue_parent(op); 580 581 cb.sc_next = op->o_callback; 582 op->o_callback = &cb; 583 rc = on->on_info->oi_orig->bi_op_add(op, &nrs); 584 if ( op->ora_e == e ) 585 entry_free( e ); 586 op->o_callback = cb.sc_next; 587 588 return(rc); 589 } 590 591 static int translucent_compare(Operation *op, SlapReply *rs) { 592 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 593 translucent_info *ov = on->on_bi.bi_private; 594 AttributeAssertion *ava = op->orc_ava; 595 Entry *e; 596 BackendDB *db; 597 int rc; 598 599 Debug(LDAP_DEBUG_TRACE, "==> translucent_compare: <%s> %s:%s\n", 600 op->o_req_dn.bv_val, ava->aa_desc->ad_cname.bv_val, ava->aa_value.bv_val); 601 602 /* 603 ** if the local backend has an entry for this attribute: 604 ** CONTINUE and let it do the compare; 605 ** 606 */ 607 rc = overlay_entry_get_ov(op, &op->o_req_ndn, NULL, ava->aa_desc, 0, &e, on); 608 if(rc == LDAP_SUCCESS && e) { 609 overlay_entry_release_ov(op, e, 0, on); 610 return(SLAP_CB_CONTINUE); 611 } 612 613 if(ov->defer_db_open) { 614 send_ldap_error(op, rs, LDAP_UNAVAILABLE, 615 "remote DB not available"); 616 return(rs->sr_err); 617 } 618 /* 619 ** call compare() in the captive backend; 620 ** return the result; 621 ** 622 */ 623 db = op->o_bd; 624 op->o_bd = &ov->db; 625 rc = ov->db.bd_info->bi_op_compare(op, rs); 626 op->o_bd = db; 627 628 return(rc); 629 } 630 631 /* 632 ** translucent_search_cb() 633 ** merge local data with remote data 634 ** 635 ** Four cases: 636 ** 1: remote search, no local filter 637 ** merge data and send immediately 638 ** 2: remote search, with local filter 639 ** merge data and save 640 ** 3: local search, no remote filter 641 ** merge data and send immediately 642 ** 4: local search, with remote filter 643 ** check list, merge, send, delete 644 */ 645 646 #define RMT_SIDE 0 647 #define LCL_SIDE 1 648 #define USE_LIST 2 649 650 typedef struct trans_ctx { 651 BackendDB *db; 652 slap_overinst *on; 653 Filter *orig; 654 Avlnode *list; 655 int step; 656 } trans_ctx; 657 658 static int translucent_search_cb(Operation *op, SlapReply *rs) { 659 trans_ctx *tc; 660 BackendDB *db; 661 slap_overinst *on; 662 translucent_info *ov; 663 Entry *le, *re; 664 Attribute *a, *ax, *an, *as = NULL; 665 int rc; 666 667 tc = op->o_callback->sc_private; 668 669 /* Don't let the op complete while we're gathering data */ 670 if ( rs->sr_type == REP_RESULT && ( tc->step & USE_LIST )) 671 return 0; 672 673 if(!op || !rs || rs->sr_type != REP_SEARCH || !rs->sr_entry) 674 return(SLAP_CB_CONTINUE); 675 676 Debug(LDAP_DEBUG_TRACE, "==> translucent_search_cb: %s\n", 677 rs->sr_entry->e_name.bv_val, 0, 0); 678 679 on = tc->on; 680 ov = on->on_bi.bi_private; 681 682 db = op->o_bd; 683 re = NULL; 684 685 /* If we have local, get remote */ 686 if ( tc->step & LCL_SIDE ) { 687 le = rs->sr_entry; 688 /* If entry is already on list, use it */ 689 if ( tc->step & USE_LIST ) { 690 re = tavl_delete( &tc->list, le, entry_dn_cmp ); 691 if ( re ) { 692 if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) { 693 rs->sr_flags ^= REP_ENTRY_MUSTRELEASE; 694 be_entry_release_r( op, rs->sr_entry ); 695 } 696 if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) { 697 rs->sr_flags ^= REP_ENTRY_MUSTBEFREED; 698 entry_free( rs->sr_entry ); 699 } 700 rc = test_filter( op, re, tc->orig ); 701 if ( rc == LDAP_COMPARE_TRUE ) { 702 rs->sr_flags |= REP_ENTRY_MUSTBEFREED; 703 rs->sr_entry = re; 704 return SLAP_CB_CONTINUE; 705 } else { 706 entry_free( re ); 707 rs->sr_entry = NULL; 708 return 0; 709 } 710 } 711 } 712 op->o_bd = &ov->db; 713 rc = be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &re ); 714 if ( rc == LDAP_SUCCESS && re ) { 715 Entry *tmp = entry_dup( re ); 716 be_entry_release_r( op, re ); 717 re = tmp; 718 } 719 } else { 720 /* Else we have remote, get local */ 721 op->o_bd = tc->db; 722 rc = overlay_entry_get_ov(op, &rs->sr_entry->e_nname, NULL, NULL, 0, &le, on); 723 if ( rc == LDAP_SUCCESS && le ) { 724 re = entry_dup( rs->sr_entry ); 725 if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) { 726 rs->sr_flags ^= REP_ENTRY_MUSTRELEASE; 727 be_entry_release_r( op, rs->sr_entry ); 728 } 729 if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) { 730 rs->sr_flags ^= REP_ENTRY_MUSTBEFREED; 731 entry_free( rs->sr_entry ); 732 } 733 } else { 734 le = NULL; 735 } 736 } 737 738 /* 739 ** if we got remote and local entry: 740 ** foreach local attr: 741 ** foreach remote attr: 742 ** if match, remote attr with local attr; 743 ** if new local, add to list; 744 ** append new local attrs to remote; 745 ** 746 */ 747 748 if ( re && le ) { 749 for(ax = le->e_attrs; ax; ax = ax->a_next) { 750 for(a = re->e_attrs; a; a = a->a_next) { 751 if(a->a_desc == ax->a_desc) { 752 if(a->a_vals != a->a_nvals) 753 ber_bvarray_free(a->a_nvals); 754 ber_bvarray_free(a->a_vals); 755 a->a_vals = dup_bervarray(ax->a_vals); 756 a->a_nvals = (ax->a_vals == ax->a_nvals) ? 757 a->a_vals : dup_bervarray(ax->a_nvals); 758 break; 759 } 760 } 761 if(a) continue; 762 an = attr_dup(ax); 763 an->a_next = as; 764 as = an; 765 } 766 /* Dispose of local entry */ 767 if ( tc->step & LCL_SIDE ) { 768 if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) { 769 rs->sr_flags ^= REP_ENTRY_MUSTRELEASE; 770 be_entry_release_r( op, rs->sr_entry ); 771 } 772 if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) { 773 rs->sr_flags ^= REP_ENTRY_MUSTBEFREED; 774 entry_free( rs->sr_entry ); 775 } 776 } else { 777 overlay_entry_release_ov(op, le, 0, on); 778 } 779 780 /* literally append, so locals are always last */ 781 if(as) { 782 if(re->e_attrs) { 783 for(ax = re->e_attrs; ax->a_next; ax = ax->a_next); 784 ax->a_next = as; 785 } else { 786 re->e_attrs = as; 787 } 788 } 789 /* If both filters, save entry for later */ 790 if ( tc->step == (USE_LIST|RMT_SIDE) ) { 791 tavl_insert( &tc->list, re, entry_dn_cmp, avl_dup_error ); 792 rs->sr_entry = NULL; 793 rc = 0; 794 } else { 795 /* send it now */ 796 rs->sr_entry = re; 797 rs->sr_flags |= REP_ENTRY_MUSTBEFREED; 798 rc = SLAP_CB_CONTINUE; 799 } 800 } else if ( le ) { 801 /* Only a local entry: remote was deleted 802 * Ought to delete the local too... 803 */ 804 rc = 0; 805 } else if ( tc->step & USE_LIST ) { 806 /* Only a remote entry, but both filters: 807 * Test the complete filter 808 */ 809 rc = test_filter( op, rs->sr_entry, tc->orig ); 810 if ( rc == LDAP_COMPARE_TRUE ) { 811 rc = SLAP_CB_CONTINUE; 812 } else { 813 rc = 0; 814 } 815 } else { 816 /* Only a remote entry, only remote filter: 817 * just pass thru 818 */ 819 rc = SLAP_CB_CONTINUE; 820 } 821 822 op->o_bd = db; 823 return rc; 824 } 825 826 /* Dup the filter, excluding invalid elements */ 827 static Filter * 828 trans_filter_dup(Operation *op, Filter *f, AttributeName *an) 829 { 830 Filter *n = NULL; 831 832 if ( !f ) 833 return NULL; 834 835 switch( f->f_choice & SLAPD_FILTER_MASK ) { 836 case SLAPD_FILTER_COMPUTED: 837 n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); 838 n->f_choice = f->f_choice; 839 n->f_result = f->f_result; 840 n->f_next = NULL; 841 break; 842 843 case LDAP_FILTER_PRESENT: 844 if ( ad_inlist( f->f_desc, an )) { 845 n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); 846 n->f_choice = f->f_choice; 847 n->f_desc = f->f_desc; 848 n->f_next = NULL; 849 } 850 break; 851 852 case LDAP_FILTER_EQUALITY: 853 case LDAP_FILTER_GE: 854 case LDAP_FILTER_LE: 855 case LDAP_FILTER_APPROX: 856 case LDAP_FILTER_SUBSTRINGS: 857 case LDAP_FILTER_EXT: 858 if ( !f->f_av_desc || ad_inlist( f->f_av_desc, an )) { 859 n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); 860 n->f_choice = f->f_choice; 861 n->f_ava = f->f_ava; 862 n->f_next = NULL; 863 } 864 break; 865 866 case LDAP_FILTER_AND: 867 case LDAP_FILTER_OR: 868 case LDAP_FILTER_NOT: { 869 Filter **p; 870 871 n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); 872 n->f_choice = f->f_choice; 873 n->f_next = NULL; 874 875 for ( p = &n->f_list, f = f->f_list; f; f = f->f_next ) { 876 *p = trans_filter_dup( op, f, an ); 877 if ( !*p ) 878 continue; 879 p = &(*p)->f_next; 880 } 881 /* nothing valid in this list */ 882 if ( !n->f_list ) { 883 op->o_tmpfree( n, op->o_tmpmemctx ); 884 return NULL; 885 } 886 /* Only 1 element in this list */ 887 if ((n->f_choice & SLAPD_FILTER_MASK) != LDAP_FILTER_NOT && 888 !n->f_list->f_next ) { 889 f = n->f_list; 890 *n = *f; 891 op->o_tmpfree( f, op->o_tmpmemctx ); 892 } 893 break; 894 } 895 } 896 return n; 897 } 898 899 static void 900 trans_filter_free( Operation *op, Filter *f ) 901 { 902 Filter *n, *p, *next; 903 904 f->f_choice &= SLAPD_FILTER_MASK; 905 906 switch( f->f_choice ) { 907 case LDAP_FILTER_AND: 908 case LDAP_FILTER_OR: 909 case LDAP_FILTER_NOT: 910 /* Free in reverse order */ 911 n = NULL; 912 for ( p = f->f_list; p; p = next ) { 913 next = p->f_next; 914 p->f_next = n; 915 n = p; 916 } 917 for ( p = n; p; p = next ) { 918 next = p->f_next; 919 trans_filter_free( op, p ); 920 } 921 break; 922 default: 923 break; 924 } 925 op->o_tmpfree( f, op->o_tmpmemctx ); 926 } 927 928 /* 929 ** translucent_search() 930 ** search via captive backend; 931 ** override results with any local data; 932 ** 933 */ 934 935 static int translucent_search(Operation *op, SlapReply *rs) { 936 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 937 translucent_info *ov = on->on_bi.bi_private; 938 slap_callback cb = { NULL, NULL, NULL, NULL }; 939 trans_ctx tc; 940 Filter *fl, *fr; 941 struct berval fbv; 942 int rc = 0; 943 944 Debug(LDAP_DEBUG_TRACE, "==> translucent_search: <%s> %s\n", 945 op->o_req_dn.bv_val, op->ors_filterstr.bv_val, 0); 946 947 if(ov->defer_db_open) { 948 send_ldap_error(op, rs, LDAP_UNAVAILABLE, 949 "remote DB not available"); 950 return(rs->sr_err); 951 } 952 953 fr = ov->remote ? trans_filter_dup( op, op->ors_filter, ov->remote ) : NULL; 954 fl = ov->local ? trans_filter_dup( op, op->ors_filter, ov->local ) : NULL; 955 cb.sc_response = (slap_response *) translucent_search_cb; 956 cb.sc_private = &tc; 957 cb.sc_next = op->o_callback; 958 959 tc.db = op->o_bd; 960 tc.on = on; 961 tc.orig = op->ors_filter; 962 tc.list = NULL; 963 tc.step = 0; 964 fbv = op->ors_filterstr; 965 966 op->o_callback = &cb; 967 968 if ( fr || !fl ) { 969 op->o_bd = &ov->db; 970 tc.step |= RMT_SIDE; 971 if ( fl ) { 972 tc.step |= USE_LIST; 973 op->ors_filter = fr; 974 filter2bv_x( op, fr, &op->ors_filterstr ); 975 } 976 rc = ov->db.bd_info->bi_op_search(op, rs); 977 op->o_bd = tc.db; 978 if ( fl ) { 979 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 980 } 981 } 982 if ( fl && !rc ) { 983 tc.step |= LCL_SIDE; 984 op->ors_filter = fl; 985 filter2bv_x( op, fl, &op->ors_filterstr ); 986 rc = overlay_op_walk( op, rs, op_search, on->on_info, on->on_next ); 987 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 988 } 989 op->ors_filterstr = fbv; 990 op->ors_filter = tc.orig; 991 op->o_callback = cb.sc_next; 992 /* Send out anything remaining on the list and finish */ 993 if ( tc.step & USE_LIST ) { 994 if ( tc.list ) { 995 Avlnode *av; 996 997 av = tavl_end( tc.list, TAVL_DIR_LEFT ); 998 while ( av ) { 999 rs->sr_entry = av->avl_data; 1000 rc = test_filter( op, rs->sr_entry, op->ors_filter ); 1001 if ( rc == LDAP_COMPARE_TRUE ) { 1002 rs->sr_flags = REP_ENTRY_MUSTBEFREED; 1003 rc = send_search_entry( op, rs ); 1004 if ( rc ) break; 1005 } else { 1006 entry_free( rs->sr_entry ); 1007 } 1008 av = tavl_next( av, TAVL_DIR_RIGHT ); 1009 } 1010 tavl_free( tc.list, NULL ); 1011 rs->sr_entry = NULL; 1012 } 1013 send_ldap_result( op, rs ); 1014 } 1015 1016 /* Free in reverse order */ 1017 if ( fl ) 1018 trans_filter_free( op, fl ); 1019 if ( fr ) 1020 trans_filter_free( op, fr ); 1021 1022 return rc; 1023 } 1024 1025 1026 /* 1027 ** translucent_bind() 1028 ** pass bind request to captive backend; 1029 ** 1030 */ 1031 1032 static int translucent_bind(Operation *op, SlapReply *rs) { 1033 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 1034 translucent_info *ov = on->on_bi.bi_private; 1035 BackendDB *db; 1036 int rc; 1037 1038 Debug(LDAP_DEBUG_TRACE, "translucent_bind: <%s> method %d\n", 1039 op->o_req_dn.bv_val, op->orb_method, 0); 1040 1041 if(ov->defer_db_open) { 1042 send_ldap_error(op, rs, LDAP_UNAVAILABLE, 1043 "remote DB not available"); 1044 return(rs->sr_err); 1045 } 1046 db = op->o_bd; 1047 op->o_bd = &ov->db; 1048 rc = ov->db.bd_info->bi_op_bind(op, rs); 1049 op->o_bd = db; 1050 return rc; 1051 } 1052 1053 /* 1054 ** translucent_connection_destroy() 1055 ** pass disconnect notification to captive backend; 1056 ** 1057 */ 1058 1059 static int translucent_connection_destroy(BackendDB *be, Connection *conn) { 1060 slap_overinst *on = (slap_overinst *) be->bd_info; 1061 translucent_info *ov = on->on_bi.bi_private; 1062 int rc = 0; 1063 1064 Debug(LDAP_DEBUG_TRACE, "translucent_connection_destroy\n", 0, 0, 0); 1065 1066 rc = ov->db.bd_info->bi_connection_destroy(&ov->db, conn); 1067 1068 return(rc); 1069 } 1070 1071 /* 1072 ** translucent_db_config() 1073 ** pass config directives to captive backend; 1074 ** parse unrecognized directives ourselves; 1075 ** 1076 */ 1077 1078 static int translucent_db_config( 1079 BackendDB *be, 1080 const char *fname, 1081 int lineno, 1082 int argc, 1083 char **argv 1084 ) 1085 { 1086 slap_overinst *on = (slap_overinst *) be->bd_info; 1087 translucent_info *ov = on->on_bi.bi_private; 1088 1089 Debug(LDAP_DEBUG_TRACE, "==> translucent_db_config: %s\n", 1090 argc ? argv[0] : "", 0, 0); 1091 1092 /* Something for the captive database? */ 1093 if ( ov->db.bd_info && ov->db.bd_info->bi_db_config ) 1094 return ov->db.bd_info->bi_db_config( &ov->db, fname, lineno, 1095 argc, argv ); 1096 return SLAP_CONF_UNKNOWN; 1097 } 1098 1099 /* 1100 ** translucent_db_init() 1101 ** initialize the captive backend; 1102 ** 1103 */ 1104 1105 static int translucent_db_init(BackendDB *be, ConfigReply *cr) { 1106 slap_overinst *on = (slap_overinst *) be->bd_info; 1107 translucent_info *ov; 1108 1109 Debug(LDAP_DEBUG_TRACE, "==> translucent_db_init\n", 0, 0, 0); 1110 1111 ov = ch_calloc(1, sizeof(translucent_info)); 1112 on->on_bi.bi_private = ov; 1113 ov->db = *be; 1114 ov->db.be_private = NULL; 1115 ov->db.be_pcl_mutexp = &ov->db.be_pcl_mutex; 1116 ov->defer_db_open = 1; 1117 1118 if ( !backend_db_init( "ldap", &ov->db, -1, NULL )) { 1119 Debug( LDAP_DEBUG_CONFIG, "translucent: unable to open captive back-ldap\n", 0, 0, 0); 1120 return 1; 1121 } 1122 SLAP_DBFLAGS(be) |= SLAP_DBFLAG_NO_SCHEMA_CHECK; 1123 SLAP_DBFLAGS(be) |= SLAP_DBFLAG_NOLASTMOD; 1124 1125 return 0; 1126 } 1127 1128 /* 1129 ** translucent_db_open() 1130 ** if the captive backend has an open() method, call it; 1131 ** 1132 */ 1133 1134 static int translucent_db_open(BackendDB *be, ConfigReply *cr) { 1135 slap_overinst *on = (slap_overinst *) be->bd_info; 1136 translucent_info *ov = on->on_bi.bi_private; 1137 int rc; 1138 1139 Debug(LDAP_DEBUG_TRACE, "==> translucent_db_open\n", 0, 0, 0); 1140 1141 /* need to inherit something from the original database... */ 1142 ov->db.be_def_limit = be->be_def_limit; 1143 ov->db.be_limits = be->be_limits; 1144 ov->db.be_acl = be->be_acl; 1145 ov->db.be_dfltaccess = be->be_dfltaccess; 1146 1147 if ( ov->defer_db_open ) 1148 return 0; 1149 1150 rc = backend_startup_one( &ov->db, cr ); 1151 1152 if(rc) Debug(LDAP_DEBUG_TRACE, 1153 "translucent: bi_db_open() returned error %d\n", rc, 0, 0); 1154 1155 return(rc); 1156 } 1157 1158 /* 1159 ** translucent_db_close() 1160 ** if the captive backend has a close() method, call it 1161 ** 1162 */ 1163 1164 static int 1165 translucent_db_close( BackendDB *be, ConfigReply *cr ) 1166 { 1167 slap_overinst *on = (slap_overinst *) be->bd_info; 1168 translucent_info *ov = on->on_bi.bi_private; 1169 int rc = 0; 1170 1171 Debug(LDAP_DEBUG_TRACE, "==> translucent_db_close\n", 0, 0, 0); 1172 1173 if ( ov && ov->db.bd_info && ov->db.bd_info->bi_db_close ) { 1174 rc = ov->db.bd_info->bi_db_close(&ov->db, NULL); 1175 } 1176 1177 return(rc); 1178 } 1179 1180 /* 1181 ** translucent_db_destroy() 1182 ** if the captive backend has a db_destroy() method, call it; 1183 ** free any config data 1184 ** 1185 */ 1186 1187 static int 1188 translucent_db_destroy( BackendDB *be, ConfigReply *cr ) 1189 { 1190 slap_overinst *on = (slap_overinst *) be->bd_info; 1191 translucent_info *ov = on->on_bi.bi_private; 1192 int rc = 0; 1193 1194 Debug(LDAP_DEBUG_TRACE, "==> translucent_db_destroy\n", 0, 0, 0); 1195 1196 if ( ov ) { 1197 if ( ov->remote ) 1198 anlist_free( ov->remote, 1, NULL ); 1199 if ( ov->local ) 1200 anlist_free( ov->local, 1, NULL ); 1201 if ( ov->db.be_private != NULL ) { 1202 backend_stopdown_one( &ov->db ); 1203 } 1204 1205 ch_free(ov); 1206 on->on_bi.bi_private = NULL; 1207 } 1208 1209 return(rc); 1210 } 1211 1212 /* 1213 ** translucent_initialize() 1214 ** initialize the slap_overinst with our entry points; 1215 ** 1216 */ 1217 1218 int translucent_initialize() { 1219 1220 int rc; 1221 1222 Debug(LDAP_DEBUG_TRACE, "==> translucent_initialize\n", 0, 0, 0); 1223 1224 translucent.on_bi.bi_type = "translucent"; 1225 translucent.on_bi.bi_db_init = translucent_db_init; 1226 translucent.on_bi.bi_db_config = translucent_db_config; 1227 translucent.on_bi.bi_db_open = translucent_db_open; 1228 translucent.on_bi.bi_db_close = translucent_db_close; 1229 translucent.on_bi.bi_db_destroy = translucent_db_destroy; 1230 translucent.on_bi.bi_op_bind = translucent_bind; 1231 translucent.on_bi.bi_op_add = translucent_add; 1232 translucent.on_bi.bi_op_modify = translucent_modify; 1233 translucent.on_bi.bi_op_modrdn = translucent_modrdn; 1234 translucent.on_bi.bi_op_delete = translucent_delete; 1235 translucent.on_bi.bi_op_search = translucent_search; 1236 translucent.on_bi.bi_op_compare = translucent_compare; 1237 translucent.on_bi.bi_connection_destroy = translucent_connection_destroy; 1238 1239 translucent.on_bi.bi_cf_ocs = translucentocs; 1240 rc = config_register_schema ( translucentcfg, translucentocs ); 1241 if ( rc ) return rc; 1242 1243 return(overlay_register(&translucent)); 1244 } 1245 1246 #if SLAPD_OVER_TRANSLUCENT == SLAPD_MOD_DYNAMIC && defined(PIC) 1247 int init_module(int argc, char *argv[]) { 1248 return translucent_initialize(); 1249 } 1250 #endif 1251 1252 #endif /* SLAPD_OVER_TRANSLUCENT */ 1253