1/* rdnval.c - RDN value overlay */ 2/* $OpenLDAP$ */ 3/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 4 * 5 * Copyright 1998-2011 The OpenLDAP Foundation. 6 * Portions Copyright 2008 Pierangelo Masarati. 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 Pierangelo Masarati 19 * for inclusion in OpenLDAP Software. 20 */ 21 22#include "portable.h" 23 24#ifdef SLAPD_OVER_RDNVAL 25 26#include <stdio.h> 27 28#include "ac/string.h" 29#include "ac/socket.h" 30 31#include "slap.h" 32#include "config.h" 33 34#include "lutil.h" 35 36/* 37 * Maintain an attribute (rdnValue) that contains the values of each AVA 38 * that builds up the RDN of an entry. This is required for interoperation 39 * with Samba4. It mimics the "name" attribute provided by Active Directory. 40 * The naming attributes must be directoryString-valued, or compatible. 41 * For example, IA5String values are cast into directoryString unless 42 * consisting of the empty string (""). 43 */ 44 45static AttributeDescription *ad_rdnValue; 46static Syntax *syn_IA5String; 47 48static slap_overinst rdnval; 49 50static int 51rdnval_is_valid( AttributeDescription *desc, struct berval *value ) 52{ 53 if ( desc->ad_type->sat_syntax == slap_schema.si_syn_directoryString ) { 54 return 1; 55 } 56 57 if ( desc->ad_type->sat_syntax == syn_IA5String 58 && !BER_BVISEMPTY( value ) ) 59 { 60 return 1; 61 } 62 63 return 0; 64} 65 66static int 67rdnval_unique_check_cb( Operation *op, SlapReply *rs ) 68{ 69 if ( rs->sr_type == REP_SEARCH ) { 70 int *p = (int *)op->o_callback->sc_private; 71 (*p)++; 72 } 73 74 return 0; 75} 76 77static int 78rdnval_unique_check( Operation *op, BerVarray vals ) 79{ 80 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 81 82 BackendDB db = *op->o_bd; 83 Operation op2 = *op; 84 SlapReply rs2 = { 0 }; 85 int i; 86 BerVarray fvals; 87 char *ptr; 88 int gotit = 0; 89 slap_callback cb = { 0 }; 90 91 /* short-circuit attempts to add suffix entry */ 92 if ( op->o_tag == LDAP_REQ_ADD 93 && be_issuffix( op->o_bd, &op->o_req_ndn ) ) 94 { 95 return LDAP_SUCCESS; 96 } 97 98 op2.o_bd = &db; 99 op2.o_bd->bd_info = (BackendInfo *)on->on_info; 100 op2.o_tag = LDAP_REQ_SEARCH; 101 op2.o_dn = op->o_bd->be_rootdn; 102 op2.o_ndn = op->o_bd->be_rootndn; 103 op2.o_callback = &cb; 104 cb.sc_response = rdnval_unique_check_cb; 105 cb.sc_private = (void *)&gotit; 106 107 dnParent( &op->o_req_ndn, &op2.o_req_dn ); 108 op2.o_req_ndn = op2.o_req_dn; 109 110 op2.ors_limit = NULL; 111 op2.ors_slimit = 1; 112 op2.ors_tlimit = SLAP_NO_LIMIT; 113 op2.ors_attrs = slap_anlist_no_attrs; 114 op2.ors_attrsonly = 1; 115 op2.ors_deref = LDAP_DEREF_NEVER; 116 op2.ors_scope = LDAP_SCOPE_ONELEVEL; 117 118 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) 119 /* just count */ ; 120 121 fvals = op->o_tmpcalloc( sizeof( struct berval ), i + 1, 122 op->o_tmpmemctx ); 123 124 op2.ors_filterstr.bv_len = 0; 125 if ( i > 1 ) { 126 op2.ors_filterstr.bv_len = STRLENOF( "(&)" ); 127 } 128 129 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) { 130 ldap_bv2escaped_filter_value_x( &vals[ i ], &fvals[ i ], 131 1, op->o_tmpmemctx ); 132 op2.ors_filterstr.bv_len += ad_rdnValue->ad_cname.bv_len 133 + fvals[ i ].bv_len + STRLENOF( "(=)" ); 134 } 135 136 op2.ors_filterstr.bv_val = op->o_tmpalloc( op2.ors_filterstr.bv_len + 1, op->o_tmpmemctx ); 137 138 ptr = op2.ors_filterstr.bv_val; 139 if ( i > 1 ) { 140 ptr = lutil_strcopy( ptr, "(&" ); 141 } 142 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) { 143 *ptr++ = '('; 144 ptr = lutil_strncopy( ptr, ad_rdnValue->ad_cname.bv_val, ad_rdnValue->ad_cname.bv_len ); 145 *ptr++ = '='; 146 ptr = lutil_strncopy( ptr, fvals[ i ].bv_val, fvals[ i ].bv_len ); 147 *ptr++ = ')'; 148 } 149 150 if ( i > 1 ) { 151 *ptr++ = ')'; 152 } 153 *ptr = '\0'; 154 155 assert( ptr == op2.ors_filterstr.bv_val + op2.ors_filterstr.bv_len ); 156 op2.ors_filter = str2filter_x( op, op2.ors_filterstr.bv_val ); 157 assert( op2.ors_filter != NULL ); 158 159 (void)op2.o_bd->be_search( &op2, &rs2 ); 160 161 filter_free_x( op, op2.ors_filter, 1 ); 162 op->o_tmpfree( op2.ors_filterstr.bv_val, op->o_tmpmemctx ); 163 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) { 164 if ( vals[ i ].bv_val != fvals[ i ].bv_val ) { 165 op->o_tmpfree( fvals[ i ].bv_val, op->o_tmpmemctx ); 166 } 167 } 168 op->o_tmpfree( fvals, op->o_tmpmemctx ); 169 170 if ( rs2.sr_err != LDAP_SUCCESS || gotit > 0 ) { 171 return LDAP_CONSTRAINT_VIOLATION; 172 } 173 174 return LDAP_SUCCESS; 175} 176 177static int 178rdnval_rdn2vals( 179 Operation *op, 180 SlapReply *rs, 181 struct berval *dn, 182 struct berval *ndn, 183 BerVarray *valsp, 184 BerVarray *nvalsp, 185 int *numvalsp ) 186{ 187 LDAPRDN rdn = NULL, nrdn = NULL; 188 int nAVA, i; 189 190 assert( *valsp == NULL ); 191 assert( *nvalsp == NULL ); 192 193 *numvalsp = 0; 194 195 if ( ldap_bv2rdn_x( dn, &rdn, (char **)&rs->sr_text, 196 LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) ) 197 { 198 Debug( LDAP_DEBUG_TRACE, 199 "%s rdnval: can't figure out " 200 "type(s)/value(s) of rdn DN=\"%s\"\n", 201 op->o_log_prefix, dn->bv_val, 0 ); 202 rs->sr_err = LDAP_INVALID_DN_SYNTAX; 203 rs->sr_text = "unknown type(s) used in RDN"; 204 205 goto done; 206 } 207 208 if ( ldap_bv2rdn_x( ndn, &nrdn, 209 (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) ) 210 { 211 Debug( LDAP_DEBUG_TRACE, 212 "%s rdnval: can't figure out " 213 "type(s)/value(s) of normalized rdn DN=\"%s\"\n", 214 op->o_log_prefix, ndn->bv_val, 0 ); 215 rs->sr_err = LDAP_INVALID_DN_SYNTAX; 216 rs->sr_text = "unknown type(s) used in RDN"; 217 218 goto done; 219 } 220 221 for ( nAVA = 0; rdn[ nAVA ]; nAVA++ ) 222 /* count'em */ ; 223 224 /* NOTE: we assume rdn and nrdn contain the same AVAs! */ 225 226 *valsp = SLAP_CALLOC( sizeof( struct berval ), nAVA + 1 ); 227 *nvalsp = SLAP_CALLOC( sizeof( struct berval ), nAVA + 1 ); 228 229 /* Add new attribute values to the entry */ 230 for ( i = 0; rdn[ i ]; i++ ) { 231 AttributeDescription *desc = NULL; 232 233 rs->sr_err = slap_bv2ad( &rdn[ i ]->la_attr, 234 &desc, &rs->sr_text ); 235 236 if ( rs->sr_err != LDAP_SUCCESS ) { 237 Debug( LDAP_DEBUG_TRACE, 238 "%s rdnval: %s: %s\n", 239 op->o_log_prefix, 240 rs->sr_text, 241 rdn[ i ]->la_attr.bv_val ); 242 goto done; 243 } 244 245 if ( !rdnval_is_valid( desc, &rdn[ i ]->la_value ) ) { 246 Debug( LDAP_DEBUG_TRACE, 247 "%s rdnval: syntax of naming attribute '%s' " 248 "not compatible with directoryString", 249 op->o_log_prefix, rdn[ i ]->la_attr.bv_val, 0 ); 250 continue; 251 } 252 253 if ( value_find_ex( desc, 254 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 255 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, 256 *nvalsp, 257 &nrdn[ i ]->la_value, 258 op->o_tmpmemctx ) 259 == LDAP_NO_SUCH_ATTRIBUTE ) 260 { 261 ber_dupbv( &(*valsp)[ *numvalsp ], &rdn[ i ]->la_value ); 262 ber_dupbv( &(*nvalsp)[ *numvalsp ], &nrdn[ i ]->la_value ); 263 264 (*numvalsp)++; 265 } 266 } 267 268 if ( rdnval_unique_check( op, *valsp ) != LDAP_SUCCESS ) { 269 rs->sr_err = LDAP_CONSTRAINT_VIOLATION; 270 rs->sr_text = "rdnValue not unique within siblings"; 271 goto done; 272 } 273 274done:; 275 if ( rdn != NULL ) { 276 ldap_rdnfree_x( rdn, op->o_tmpmemctx ); 277 } 278 279 if ( nrdn != NULL ) { 280 ldap_rdnfree_x( nrdn, op->o_tmpmemctx ); 281 } 282 283 if ( rs->sr_err != LDAP_SUCCESS ) { 284 if ( *valsp != NULL ) { 285 ber_bvarray_free( *valsp ); 286 ber_bvarray_free( *nvalsp ); 287 *valsp = NULL; 288 *nvalsp = NULL; 289 *numvalsp = 0; 290 } 291 } 292 293 return rs->sr_err; 294} 295 296static int 297rdnval_op_add( Operation *op, SlapReply *rs ) 298{ 299 Attribute *a, **ap; 300 int numvals = 0; 301 BerVarray vals = NULL, nvals = NULL; 302 int rc; 303 304 /* NOTE: should we accept an entry still in mods format? */ 305 assert( op->ora_e != NULL ); 306 307 if ( BER_BVISEMPTY( &op->ora_e->e_nname ) ) { 308 return SLAP_CB_CONTINUE; 309 } 310 311 a = attr_find( op->ora_e->e_attrs, ad_rdnValue ); 312 if ( a != NULL ) { 313 /* TODO: check consistency? */ 314 return SLAP_CB_CONTINUE; 315 } 316 317 rc = rdnval_rdn2vals( op, rs, &op->ora_e->e_name, &op->ora_e->e_nname, 318 &vals, &nvals, &numvals ); 319 if ( rc != LDAP_SUCCESS ) { 320 send_ldap_result( op, rs ); 321 } 322 323 a = attr_alloc( ad_rdnValue ); 324 325 a->a_vals = vals; 326 a->a_nvals = nvals; 327 a->a_numvals = numvals; 328 329 for ( ap = &op->ora_e->e_attrs; *ap != NULL; ap = &(*ap)->a_next ) 330 /* goto tail */ ; 331 332 *ap = a; 333 334 return SLAP_CB_CONTINUE; 335} 336 337static int 338rdnval_op_rename( Operation *op, SlapReply *rs ) 339{ 340 Modifications *ml, **mlp; 341 int numvals = 0; 342 BerVarray vals = NULL, nvals = NULL; 343 struct berval old; 344 int rc; 345 346 dnRdn( &op->o_req_dn, &old ); 347 if ( dn_match( &old, &op->orr_newrdn ) ) { 348 dnRdn( &op->o_req_ndn, &old ); 349 if ( dn_match( &old, &op->orr_nnewrdn ) ) { 350 return SLAP_CB_CONTINUE; 351 } 352 } 353 354 rc = rdnval_rdn2vals( op, rs, &op->orr_newrdn, &op->orr_nnewrdn, 355 &vals, &nvals, &numvals ); 356 if ( rc != LDAP_SUCCESS ) { 357 send_ldap_result( op, rs ); 358 } 359 360 ml = SLAP_CALLOC( sizeof( Modifications ), 1 ); 361 ml->sml_values = vals; 362 ml->sml_nvalues = nvals; 363 364 ml->sml_numvals = numvals; 365 366 ml->sml_op = LDAP_MOD_REPLACE; 367 ml->sml_flags = SLAP_MOD_INTERNAL; 368 ml->sml_desc = ad_rdnValue; 369 ml->sml_type = ad_rdnValue->ad_cname; 370 371 for ( mlp = &op->orr_modlist; *mlp != NULL; mlp = &(*mlp)->sml_next ) 372 /* goto tail */ ; 373 374 *mlp = ml; 375 376 return SLAP_CB_CONTINUE; 377} 378 379static int 380rdnval_db_init( 381 BackendDB *be, 382 ConfigReply *cr) 383{ 384 if ( SLAP_ISGLOBALOVERLAY( be ) ) { 385 Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 386 "rdnval_db_init: rdnval cannot be used as global overlay.\n" ); 387 return 1; 388 } 389 390 if ( be->be_nsuffix == NULL ) { 391 Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 392 "rdnval_db_init: database must have suffix\n" ); 393 return 1; 394 } 395 396 if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) { 397 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 398 "rdnval_db_init: missing rootdn for database DN=\"%s\", YMMV\n", 399 be->be_suffix[ 0 ].bv_val ); 400 } 401 402 return 0; 403} 404 405typedef struct rdnval_mod_t { 406 struct berval ndn; 407 BerVarray vals; 408 BerVarray nvals; 409 int numvals; 410 struct rdnval_mod_t *next; 411} rdnval_mod_t; 412 413typedef struct { 414 BackendDB *bd; 415 rdnval_mod_t *mods; 416} rdnval_repair_cb_t; 417 418static int 419rdnval_repair_cb( Operation *op, SlapReply *rs ) 420{ 421 int rc; 422 rdnval_repair_cb_t *rcb = op->o_callback->sc_private; 423 rdnval_mod_t *mod; 424 BerVarray vals = NULL, nvals = NULL; 425 int numvals = 0; 426 ber_len_t len; 427 BackendDB *save_bd = op->o_bd; 428 429 switch ( rs->sr_type ) { 430 case REP_SEARCH: 431 break; 432 433 case REP_SEARCHREF: 434 case REP_RESULT: 435 return rs->sr_err; 436 437 default: 438 assert( 0 ); 439 } 440 441 assert( rs->sr_entry != NULL ); 442 443 op->o_bd = rcb->bd; 444 rc = rdnval_rdn2vals( op, rs, &rs->sr_entry->e_name, &rs->sr_entry->e_nname, 445 &vals, &nvals, &numvals ); 446 op->o_bd = save_bd; 447 if ( rc != LDAP_SUCCESS ) { 448 return 0; 449 } 450 451 len = sizeof( rdnval_mod_t ) + rs->sr_entry->e_nname.bv_len + 1; 452 mod = op->o_tmpalloc( len, op->o_tmpmemctx ); 453 mod->ndn.bv_len = rs->sr_entry->e_nname.bv_len; 454 mod->ndn.bv_val = (char *)&mod[1]; 455 lutil_strncopy( mod->ndn.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len ); 456 mod->vals = vals; 457 mod->nvals = nvals; 458 mod->numvals = numvals; 459 460 mod->next = rcb->mods; 461 rcb->mods = mod; 462 463 Debug( LDAP_DEBUG_TRACE, "%s: rdnval_repair_cb: scheduling entry DN=\"%s\" for repair\n", 464 op->o_log_prefix, rs->sr_entry->e_name.bv_val, 0 ); 465 466 return 0; 467} 468 469static int 470rdnval_repair( BackendDB *be ) 471{ 472 slap_overinst *on = (slap_overinst *)be->bd_info; 473 void *ctx = ldap_pvt_thread_pool_context(); 474 Connection conn = { 0 }; 475 OperationBuffer opbuf; 476 Operation *op; 477 BackendDB db; 478 slap_callback sc = { 0 }; 479 rdnval_repair_cb_t rcb = { 0 }; 480 SlapReply rs = { REP_RESULT }; 481 rdnval_mod_t *rmod; 482 int nrepaired = 0; 483 484 connection_fake_init2( &conn, &opbuf, ctx, 0 ); 485 op = &opbuf.ob_op; 486 487 op->o_tag = LDAP_REQ_SEARCH; 488 memset( &op->oq_search, 0, sizeof( op->oq_search ) ); 489 490 assert( !BER_BVISNULL( &be->be_nsuffix[ 0 ] ) ); 491 492 op->o_bd = select_backend( &be->be_nsuffix[ 0 ], 0 ); 493 assert( op->o_bd != NULL ); 494 assert( op->o_bd->be_nsuffix != NULL ); 495 496 op->o_req_dn = op->o_bd->be_suffix[ 0 ]; 497 op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ]; 498 499 op->o_dn = op->o_bd->be_rootdn; 500 op->o_ndn = op->o_bd->be_rootndn; 501 502 op->ors_scope = LDAP_SCOPE_SUBTREE; 503 op->ors_tlimit = SLAP_NO_LIMIT; 504 op->ors_slimit = SLAP_NO_LIMIT; 505 op->ors_attrs = slap_anlist_no_attrs; 506 507 op->ors_filterstr.bv_len = STRLENOF( "(!(=*))" ) + ad_rdnValue->ad_cname.bv_len; 508 op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx ); 509 snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1, 510 "(!(%s=*))", ad_rdnValue->ad_cname.bv_val ); 511 512 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val ); 513 if ( op->ors_filter == NULL ) { 514 rs.sr_err = LDAP_OTHER; 515 goto done_search; 516 } 517 518 op->o_callback = ≻ 519 sc.sc_response = rdnval_repair_cb; 520 sc.sc_private = &rcb; 521 rcb.bd = &db; 522 db = *be; 523 db.bd_info = (BackendInfo *)on; 524 525 (void)op->o_bd->bd_info->bi_op_search( op, &rs ); 526 527 op->o_tag = LDAP_REQ_MODIFY; 528 sc.sc_response = slap_null_cb; 529 sc.sc_private = NULL; 530 memset( &op->oq_modify, 0, sizeof( req_modify_s ) ); 531 532 for ( rmod = rcb.mods; rmod != NULL; ) { 533 rdnval_mod_t *rnext; 534 535 Modifications *mod; 536 SlapReply rs2 = { REP_RESULT }; 537 538 mod = (Modifications *) ch_malloc( sizeof( Modifications ) ); 539 mod->sml_flags = SLAP_MOD_INTERNAL; 540 mod->sml_op = LDAP_MOD_REPLACE; 541 mod->sml_desc = ad_rdnValue; 542 mod->sml_type = ad_rdnValue->ad_cname; 543 mod->sml_values = rmod->vals; 544 mod->sml_nvalues = rmod->nvals; 545 mod->sml_numvals = rmod->numvals; 546 mod->sml_next = NULL; 547 548 op->o_req_dn = rmod->ndn; 549 op->o_req_ndn = rmod->ndn; 550 551 op->orm_modlist = mod; 552 553 op->o_bd->be_modify( op, &rs2 ); 554 555 slap_mods_free( op->orm_modlist, 1 ); 556 if ( rs2.sr_err == LDAP_SUCCESS ) { 557 Debug( LDAP_DEBUG_TRACE, "%s: rdnval_repair: entry DN=\"%s\" repaired\n", 558 op->o_log_prefix, rmod->ndn.bv_val, 0 ); 559 nrepaired++; 560 561 } else { 562 Debug( LDAP_DEBUG_ANY, "%s: rdnval_repair: entry DN=\"%s\" repair failed (%d)\n", 563 op->o_log_prefix, rmod->ndn.bv_val, rs2.sr_err ); 564 } 565 566 rnext = rmod->next; 567 op->o_tmpfree( rmod, op->o_tmpmemctx ); 568 rmod = rnext; 569 } 570 571done_search:; 572 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 573 filter_free_x( op, op->ors_filter, 1 ); 574 575 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO, 576 "rdnval: repaired=%d\n", nrepaired ); 577 578 return 0; 579} 580 581/* search all entries without parentUUID; "repair" them */ 582static int 583rdnval_db_open( 584 BackendDB *be, 585 ConfigReply *cr ) 586{ 587 if ( SLAP_SINGLE_SHADOW( be ) ) { 588 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 589 "rdnval incompatible with shadow database \"%s\".\n", 590 be->be_suffix[ 0 ].bv_val ); 591 return 1; 592 } 593 594 return rdnval_repair( be ); 595} 596 597static struct { 598 char *desc; 599 AttributeDescription **adp; 600} as[] = { 601 { "( 1.3.6.1.4.1.4203.666.1.58 " 602 "NAME 'rdnValue' " 603 "DESC 'the value of the naming attributes' " 604 "SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' " 605 "EQUALITY caseIgnoreMatch " 606 "USAGE dSAOperation " 607 "NO-USER-MODIFICATION " 608 ")", 609 &ad_rdnValue }, 610 { NULL } 611}; 612 613int 614rdnval_initialize(void) 615{ 616 int code, i; 617 618 for ( i = 0; as[ i ].desc != NULL; i++ ) { 619 code = register_at( as[ i ].desc, as[ i ].adp, 0 ); 620 if ( code ) { 621 Debug( LDAP_DEBUG_ANY, 622 "rdnval_initialize: register_at #%d failed\n", 623 i, 0, 0 ); 624 return code; 625 } 626 627 /* Allow Manager to set these as needed */ 628 if ( is_at_no_user_mod( (*as[ i ].adp)->ad_type ) ) { 629 (*as[ i ].adp)->ad_type->sat_flags |= 630 SLAP_AT_MANAGEABLE; 631 } 632 } 633 634 syn_IA5String = syn_find( "1.3.6.1.4.1.1466.115.121.1.26" ); 635 if ( syn_IA5String == NULL ) { 636 Debug( LDAP_DEBUG_ANY, 637 "rdnval_initialize: unable to find syntax '1.3.6.1.4.1.1466.115.121.1.26' (IA5String)\n", 638 0, 0, 0 ); 639 return LDAP_OTHER; 640 } 641 642 rdnval.on_bi.bi_type = "rdnval"; 643 644 rdnval.on_bi.bi_op_add = rdnval_op_add; 645 rdnval.on_bi.bi_op_modrdn = rdnval_op_rename; 646 647 rdnval.on_bi.bi_db_init = rdnval_db_init; 648 rdnval.on_bi.bi_db_open = rdnval_db_open; 649 650 return overlay_register( &rdnval ); 651} 652 653#if SLAPD_OVER_RDNVAL == SLAPD_MOD_DYNAMIC 654int 655init_module( int argc, char *argv[] ) 656{ 657 return rdnval_initialize(); 658} 659#endif /* SLAPD_OVER_RDNVAL == SLAPD_MOD_DYNAMIC */ 660 661#endif /* SLAPD_OVER_RDNVAL */ 662