1 /* unique.c - attribute uniqueness module */ 2 /* $OpenLDAP: pkg/ldap/servers/slapd/overlays/unique.c,v 1.20.2.9 2008/07/09 23:45:53 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 2004,2006-2007 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 Corporation for 19 * inclusion in OpenLDAP Software, with subsequent enhancements by 20 * Matthew Backes at Symas Corporation. This work was sponsored by 21 * Hewlett-Packard. 22 */ 23 24 #include "portable.h" 25 26 #ifdef SLAPD_OVER_UNIQUE 27 28 #include <stdio.h> 29 30 #include <ac/string.h> 31 #include <ac/socket.h> 32 33 #include "slap.h" 34 #include "config.h" 35 36 #define UNIQUE_DEFAULT_URI ("ldap:///??sub") 37 38 static slap_overinst unique; 39 40 typedef struct unique_attrs_s { 41 struct unique_attrs_s *next; /* list of attrs */ 42 AttributeDescription *attr; 43 } unique_attrs; 44 45 typedef struct unique_domain_uri_s { 46 struct unique_domain_uri_s *next; 47 struct berval dn; 48 struct berval ndn; 49 struct berval filter; 50 struct unique_attrs_s *attrs; 51 int scope; 52 } unique_domain_uri; 53 54 typedef struct unique_domain_s { 55 struct unique_domain_s *next; 56 struct berval domain_spec; 57 struct unique_domain_uri_s *uri; 58 char ignore; /* polarity of attributes */ 59 char strict; /* null considered unique too */ 60 } unique_domain; 61 62 typedef struct unique_data_s { 63 struct unique_domain_s *domains; 64 struct unique_domain_s *legacy; 65 char legacy_strict_set; 66 } unique_data; 67 68 typedef struct unique_counter_s { 69 struct berval *ndn; 70 int count; 71 } unique_counter; 72 73 enum { 74 UNIQUE_BASE = 1, 75 UNIQUE_IGNORE, 76 UNIQUE_ATTR, 77 UNIQUE_STRICT, 78 UNIQUE_URI 79 }; 80 81 static ConfigDriver unique_cf_base; 82 static ConfigDriver unique_cf_attrs; 83 static ConfigDriver unique_cf_strict; 84 static ConfigDriver unique_cf_uri; 85 86 static ConfigTable uniquecfg[] = { 87 { "unique_base", "basedn", 2, 2, 0, ARG_DN|ARG_MAGIC|UNIQUE_BASE, 88 unique_cf_base, "( OLcfgOvAt:10.1 NAME 'olcUniqueBase' " 89 "DESC 'Subtree for uniqueness searches' " 90 "EQUALITY distinguishedNameMatch " 91 "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL }, 92 { "unique_ignore", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_IGNORE, 93 unique_cf_attrs, "( OLcfgOvAt:10.2 NAME 'olcUniqueIgnore' " 94 "DESC 'Attributes for which uniqueness shall not be enforced' " 95 "EQUALITY caseIgnoreMatch " 96 "ORDERING caseIgnoreOrderingMatch " 97 "SUBSTR caseIgnoreSubstringsMatch " 98 "SYNTAX OMsDirectoryString )", NULL, NULL }, 99 { "unique_attributes", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_ATTR, 100 unique_cf_attrs, "( OLcfgOvAt:10.3 NAME 'olcUniqueAttribute' " 101 "DESC 'Attributes for which uniqueness shall be enforced' " 102 "EQUALITY caseIgnoreMatch " 103 "ORDERING caseIgnoreOrderingMatch " 104 "SUBSTR caseIgnoreSubstringsMatch " 105 "SYNTAX OMsDirectoryString )", NULL, NULL }, 106 { "unique_strict", "on|off", 1, 2, 0, ARG_MAGIC|UNIQUE_STRICT, 107 unique_cf_strict, "( OLcfgOvAt:10.4 NAME 'olcUniqueStrict' " 108 "DESC 'Enforce uniqueness of null values' " 109 "EQUALITY booleanMatch " 110 "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, 111 { "unique_uri", "ldapuri", 2, 3, 0, ARG_MAGIC|UNIQUE_URI, 112 unique_cf_uri, "( OLcfgOvAt:10.5 NAME 'olcUniqueURI' " 113 "DESC 'List of keywords and LDAP URIs for a uniqueness domain' " 114 "EQUALITY caseExactMatch " 115 "ORDERING caseExactOrderingMatch " 116 "SUBSTR caseExactSubstringsMatch " 117 "SYNTAX OMsDirectoryString )", NULL, NULL }, 118 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 119 }; 120 121 static ConfigOCs uniqueocs[] = { 122 { "( OLcfgOvOc:10.1 " 123 "NAME 'olcUniqueConfig' " 124 "DESC 'Attribute value uniqueness configuration' " 125 "SUP olcOverlayConfig " 126 "MAY ( olcUniqueBase $ olcUniqueIgnore $ " 127 "olcUniqueAttribute $ olcUniqueStrict $ " 128 "olcUniqueURI ) )", 129 Cft_Overlay, uniquecfg }, 130 { NULL, 0, NULL } 131 }; 132 133 static void 134 unique_free_domain_uri ( unique_domain_uri *uri ) 135 { 136 unique_domain_uri *next_uri = NULL; 137 unique_attrs *attr, *next_attr = NULL; 138 139 while ( uri ) { 140 next_uri = uri->next; 141 ch_free ( uri->dn.bv_val ); 142 ch_free ( uri->ndn.bv_val ); 143 ch_free ( uri->filter.bv_val ); 144 attr = uri->attrs; 145 while ( attr ) { 146 next_attr = attr->next; 147 ch_free (attr); 148 attr = next_attr; 149 } 150 ch_free ( uri ); 151 uri = next_uri; 152 } 153 } 154 155 /* free an entire stack of domains */ 156 static void 157 unique_free_domain ( unique_domain *domain ) 158 { 159 unique_domain *next_domain = NULL; 160 161 while ( domain ) { 162 next_domain = domain->next; 163 ch_free ( domain->domain_spec.bv_val ); 164 unique_free_domain_uri ( domain->uri ); 165 ch_free ( domain ); 166 domain = next_domain; 167 } 168 } 169 170 static int 171 unique_new_domain_uri ( unique_domain_uri **urip, 172 const LDAPURLDesc *url_desc, 173 ConfigArgs *c ) 174 { 175 int i, rc = LDAP_SUCCESS; 176 unique_domain_uri *uri; 177 struct berval bv = {0, NULL}; 178 BackendDB *be = (BackendDB *)c->be; 179 char ** attr_str; 180 AttributeDescription * ad; 181 const char * text; 182 183 uri = ch_calloc ( 1, sizeof ( unique_domain_uri ) ); 184 185 if ( url_desc->lud_dn && url_desc->lud_dn[0] ) { 186 ber_str2bv( url_desc->lud_dn, 0, 0, &bv ); 187 rc = dnPrettyNormal( NULL, 188 &bv, 189 &uri->dn, 190 &uri->ndn, 191 NULL ); 192 if ( rc != LDAP_SUCCESS ) { 193 snprintf( c->cr_msg, sizeof( c->cr_msg ), 194 "<%s> invalid DN %d (%s)", 195 url_desc->lud_dn, rc, ldap_err2string( rc )); 196 rc = ARG_BAD_CONF; 197 goto exit; 198 } 199 200 if ( !dnIsSuffix ( &uri->ndn, &be->be_nsuffix[0] ) ) { 201 snprintf( c->cr_msg, sizeof( c->cr_msg ), 202 "dn <%s> is not a suffix of backend base dn <%s>", 203 uri->dn.bv_val, 204 be->be_nsuffix[0].bv_val ); 205 rc = ARG_BAD_CONF; 206 goto exit; 207 } 208 } 209 210 attr_str = url_desc->lud_attrs; 211 if ( attr_str ) { 212 for ( i=0; attr_str[i]; ++i ) { 213 unique_attrs * attr; 214 ad = NULL; 215 if ( slap_str2ad ( attr_str[i], &ad, &text ) 216 == LDAP_SUCCESS) { 217 attr = ch_calloc ( 1, 218 sizeof ( unique_attrs ) ); 219 attr->attr = ad; 220 attr->next = uri->attrs; 221 uri->attrs = attr; 222 } else { 223 snprintf( c->cr_msg, sizeof( c->cr_msg ), 224 "unique: attribute: %s: %s", 225 attr_str[i], text ); 226 rc = ARG_BAD_CONF; 227 goto exit; 228 } 229 } 230 } 231 232 uri->scope = url_desc->lud_scope; 233 if ( !uri->scope ) { 234 snprintf( c->cr_msg, sizeof( c->cr_msg ), 235 "unique: uri with base scope will always be unique"); 236 rc = ARG_BAD_CONF; 237 goto exit; 238 } 239 240 if (url_desc->lud_filter) { 241 Filter *f = str2filter( url_desc->lud_filter ); 242 if ( !f ) { 243 snprintf( c->cr_msg, sizeof( c->cr_msg ), 244 "unique: bad filter"); 245 rc = ARG_BAD_CONF; 246 goto exit; 247 } 248 /* make sure the strfilter is in normal form (ITS#5581) */ 249 filter2bv( f, &uri->filter ); 250 filter_free( f ); 251 } 252 exit: 253 uri->next = *urip; 254 *urip = uri; 255 if ( rc ) { 256 Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 257 "%s: %s\n", c->log, c->cr_msg, 0 ); 258 unique_free_domain_uri ( uri ); 259 *urip = NULL; 260 } 261 return rc; 262 } 263 264 static int 265 unique_new_domain_uri_basic ( unique_domain_uri **urip, 266 ConfigArgs *c ) 267 { 268 LDAPURLDesc *url_desc = NULL; 269 int rc; 270 271 rc = ldap_url_parse ( UNIQUE_DEFAULT_URI, &url_desc ); 272 if ( rc ) return rc; 273 rc = unique_new_domain_uri ( urip, url_desc, c ); 274 ldap_free_urldesc ( url_desc ); 275 return rc; 276 } 277 278 /* if *domain is non-null, it's pushed down the stack. 279 * note that the entire stack is freed if there is an error, 280 * so build added domains in a separate stack before adding them 281 * 282 * domain_specs look like 283 * 284 * [strict ][ignore ]uri[[ uri]...] 285 * e.g. "ldap:///ou=foo,o=bar?uid?sub ldap:///ou=baz,o=bar?uid?sub" 286 * "strict ldap:///ou=accounts,o=bar?uid,uidNumber?one" 287 * etc 288 * 289 * so finally strictness is per-domain 290 * but so is ignore-state, and that would be better as a per-url thing 291 */ 292 static int 293 unique_new_domain ( unique_domain **domainp, 294 char *domain_spec, 295 ConfigArgs *c ) 296 { 297 char *uri_start; 298 int rc = LDAP_SUCCESS; 299 int uri_err = 0; 300 unique_domain * domain; 301 LDAPURLDesc *url_desc, *url_descs = NULL; 302 303 Debug(LDAP_DEBUG_TRACE, "==> unique_new_domain <%s>\n", 304 domain_spec, 0, 0); 305 306 domain = ch_calloc ( 1, sizeof (unique_domain) ); 307 ber_str2bv( domain_spec, 0, 1, &domain->domain_spec ); 308 309 uri_start = domain_spec; 310 if ( strncasecmp ( uri_start, "ignore ", 311 STRLENOF( "ignore " ) ) == 0 ) { 312 domain->ignore = 1; 313 uri_start += STRLENOF( "ignore " ); 314 } 315 if ( strncasecmp ( uri_start, "strict ", 316 STRLENOF( "strict " ) ) == 0 ) { 317 domain->strict = 1; 318 uri_start += STRLENOF( "strict " ); 319 if ( !domain->ignore 320 && strncasecmp ( uri_start, "ignore ", 321 STRLENOF( "ignore " ) ) == 0 ) { 322 domain->ignore = 1; 323 uri_start += STRLENOF( "ignore " ); 324 } 325 } 326 rc = ldap_url_parselist_ext ( &url_descs, uri_start, " ", 0 ); 327 if ( rc ) { 328 snprintf( c->cr_msg, sizeof( c->cr_msg ), 329 "<%s> invalid ldap urilist", 330 uri_start ); 331 rc = ARG_BAD_CONF; 332 goto exit; 333 } 334 335 for ( url_desc = url_descs; 336 url_desc; 337 url_desc = url_descs->lud_next ) { 338 rc = unique_new_domain_uri ( &domain->uri, 339 url_desc, 340 c ); 341 if ( rc ) { 342 rc = ARG_BAD_CONF; 343 uri_err = 1; 344 goto exit; 345 } 346 } 347 348 exit: 349 if ( url_descs ) ldap_free_urldesc ( url_descs ); 350 domain->next = *domainp; 351 *domainp = domain; 352 if ( rc ) { 353 Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 354 "%s: %s\n", c->log, c->cr_msg, 0 ); 355 unique_free_domain ( domain ); 356 *domainp = NULL; 357 } 358 return rc; 359 } 360 361 static int 362 unique_cf_base( ConfigArgs *c ) 363 { 364 BackendDB *be = (BackendDB *)c->be; 365 slap_overinst *on = (slap_overinst *)c->bi; 366 unique_data *private = (unique_data *) on->on_bi.bi_private; 367 unique_domain *domains = private->domains; 368 unique_domain *legacy = private->legacy; 369 int rc = ARG_BAD_CONF; 370 371 switch ( c->op ) { 372 case SLAP_CONFIG_EMIT: 373 rc = 0; 374 if ( legacy && legacy->uri && legacy->uri->dn.bv_val ) { 375 rc = value_add_one ( &c->rvalue_vals, 376 &legacy->uri->dn ); 377 if ( rc ) return rc; 378 rc = value_add_one ( &c->rvalue_nvals, 379 &legacy->uri->ndn ); 380 if ( rc ) return rc; 381 } 382 break; 383 case LDAP_MOD_DELETE: 384 assert ( legacy && legacy->uri && legacy->uri->dn.bv_val ); 385 rc = 0; 386 ch_free ( legacy->uri->dn.bv_val ); 387 ch_free ( legacy->uri->ndn.bv_val ); 388 BER_BVZERO( &legacy->uri->dn ); 389 BER_BVZERO( &legacy->uri->ndn ); 390 if ( !legacy->uri->attrs ) { 391 unique_free_domain_uri ( legacy->uri ); 392 legacy->uri = NULL; 393 } 394 if ( !legacy->uri && !private->legacy_strict_set ) { 395 unique_free_domain ( legacy ); 396 private->legacy = legacy = NULL; 397 } 398 break; 399 case LDAP_MOD_ADD: 400 case SLAP_CONFIG_ADD: 401 if ( domains ) { 402 snprintf( c->cr_msg, sizeof( c->cr_msg ), 403 "cannot set legacy attrs when URIs are present" ); 404 Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n", 405 c->cr_msg, NULL, NULL ); 406 rc = ARG_BAD_CONF; 407 break; 408 } 409 if ( !dnIsSuffix ( &c->value_ndn, 410 &be->be_nsuffix[0] ) ) { 411 snprintf( c->cr_msg, sizeof( c->cr_msg ), 412 "dn is not a suffix of backend base" ); 413 Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n", 414 c->cr_msg, NULL, NULL ); 415 rc = ARG_BAD_CONF; 416 break; 417 } 418 if ( !legacy ) { 419 unique_new_domain ( &private->legacy, 420 UNIQUE_DEFAULT_URI, 421 c ); 422 legacy = private->legacy; 423 } 424 if ( !legacy->uri ) 425 unique_new_domain_uri_basic ( &legacy->uri, c ); 426 ch_free ( legacy->uri->dn.bv_val ); 427 ch_free ( legacy->uri->ndn.bv_val ); 428 legacy->uri->dn = c->value_dn; 429 legacy->uri->ndn = c->value_ndn; 430 rc = 0; 431 break; 432 default: 433 abort(); 434 } 435 436 return rc; 437 } 438 439 static int 440 unique_cf_attrs( ConfigArgs *c ) 441 { 442 slap_overinst *on = (slap_overinst *)c->bi; 443 unique_data *private = (unique_data *) on->on_bi.bi_private; 444 unique_domain *domains = private->domains; 445 unique_domain *legacy = private->legacy; 446 unique_attrs *new_attrs = NULL; 447 unique_attrs *attr, *next_attr, *reverse_attrs; 448 unique_attrs **attrp; 449 int rc = ARG_BAD_CONF; 450 int i; 451 452 switch ( c->op ) { 453 case SLAP_CONFIG_EMIT: 454 if ( legacy 455 && (c->type == UNIQUE_IGNORE) == legacy->ignore 456 && legacy->uri ) 457 for ( attr = legacy->uri->attrs; 458 attr; 459 attr = attr->next ) 460 value_add_one( &c->rvalue_vals, 461 &attr->attr->ad_cname ); 462 rc = 0; 463 break; 464 case LDAP_MOD_DELETE: 465 if ( legacy 466 && (c->type == UNIQUE_IGNORE) == legacy->ignore 467 && legacy->uri 468 && legacy->uri->attrs) { 469 if ( c->valx < 0 ) { /* delete all */ 470 for ( attr = legacy->uri->attrs; 471 attr; 472 attr = next_attr ) { 473 next_attr = attr->next; 474 ch_free ( attr ); 475 } 476 legacy->uri->attrs = NULL; 477 } else { /* delete by index */ 478 attrp = &legacy->uri->attrs; 479 for ( i=0; i < c->valx; ++i ) 480 attrp = &(*attrp)->next; 481 attr = *attrp; 482 *attrp = attr->next; 483 ch_free (attr); 484 } 485 if ( !legacy->uri->attrs 486 && !legacy->uri->dn.bv_val ) { 487 unique_free_domain_uri ( legacy->uri ); 488 legacy->uri = NULL; 489 } 490 if ( !legacy->uri && !private->legacy_strict_set ) { 491 unique_free_domain ( legacy ); 492 private->legacy = legacy = NULL; 493 } 494 } 495 rc = 0; 496 break; 497 case LDAP_MOD_ADD: 498 case SLAP_CONFIG_ADD: 499 if ( domains ) { 500 snprintf( c->cr_msg, sizeof( c->cr_msg ), 501 "cannot set legacy attrs when URIs are present" ); 502 Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n", 503 c->cr_msg, NULL, NULL ); 504 rc = ARG_BAD_CONF; 505 break; 506 } 507 if ( legacy 508 && legacy->uri 509 && legacy->uri->attrs 510 && (c->type == UNIQUE_IGNORE) != legacy->ignore ) { 511 snprintf( c->cr_msg, sizeof( c->cr_msg ), 512 "cannot set both attrs and ignore-attrs" ); 513 Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n", 514 c->cr_msg, NULL, NULL ); 515 rc = ARG_BAD_CONF; 516 break; 517 } 518 if ( !legacy ) { 519 unique_new_domain ( &private->legacy, 520 UNIQUE_DEFAULT_URI, 521 c ); 522 legacy = private->legacy; 523 } 524 if ( !legacy->uri ) 525 unique_new_domain_uri_basic ( &legacy->uri, c ); 526 rc = 0; 527 for ( i=1; c->argv[i]; ++i ) { 528 AttributeDescription * ad = NULL; 529 const char * text; 530 if ( slap_str2ad ( c->argv[i], &ad, &text ) 531 == LDAP_SUCCESS) { 532 533 attr = ch_calloc ( 1, 534 sizeof ( unique_attrs ) ); 535 attr->attr = ad; 536 attr->next = new_attrs; 537 new_attrs = attr; 538 } else { 539 snprintf( c->cr_msg, sizeof( c->cr_msg ), 540 "unique: attribute: %s: %s", 541 c->argv[i], text ); 542 for ( attr = new_attrs; 543 attr; 544 attr=next_attr ) { 545 next_attr = attr->next; 546 ch_free ( attr ); 547 } 548 rc = ARG_BAD_CONF; 549 break; 550 } 551 } 552 if ( rc ) break; 553 554 /* (nconc legacy->uri->attrs (nreverse new_attrs)) */ 555 reverse_attrs = NULL; 556 for ( attr = new_attrs; 557 attr; 558 attr = next_attr ) { 559 next_attr = attr->next; 560 attr->next = reverse_attrs; 561 reverse_attrs = attr; 562 } 563 for ( attrp = &legacy->uri->attrs; 564 *attrp; 565 attrp = &(*attrp)->next ) ; 566 *attrp = reverse_attrs; 567 568 legacy->ignore = ( c->type == UNIQUE_IGNORE ); 569 break; 570 default: 571 abort(); 572 } 573 574 if ( rc ) { 575 Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 576 "%s: %s\n", c->log, c->cr_msg, 0 ); 577 } 578 return rc; 579 } 580 581 static int 582 unique_cf_strict( ConfigArgs *c ) 583 { 584 slap_overinst *on = (slap_overinst *)c->bi; 585 unique_data *private = (unique_data *) on->on_bi.bi_private; 586 unique_domain *domains = private->domains; 587 unique_domain *legacy = private->legacy; 588 int rc = ARG_BAD_CONF; 589 590 switch ( c->op ) { 591 case SLAP_CONFIG_EMIT: 592 /* We process the boolean manually instead of using 593 * ARG_ON_OFF so that we can three-state it; 594 * olcUniqueStrict is either TRUE, FALSE, or missing, 595 * and missing is necessary to add olcUniqueURIs... 596 */ 597 if ( private->legacy_strict_set ) { 598 struct berval bv; 599 bv.bv_val = legacy->strict ? "TRUE" : "FALSE"; 600 bv.bv_len = legacy->strict ? 601 STRLENOF("TRUE") : 602 STRLENOF("FALSE"); 603 value_add_one ( &c->rvalue_vals, &bv ); 604 } 605 rc = 0; 606 break; 607 case LDAP_MOD_DELETE: 608 if ( legacy ) { 609 legacy->strict = 0; 610 if ( ! legacy->uri ) { 611 unique_free_domain ( legacy ); 612 private->legacy = NULL; 613 } 614 } 615 private->legacy_strict_set = 0; 616 rc = 0; 617 break; 618 case LDAP_MOD_ADD: 619 case SLAP_CONFIG_ADD: 620 if ( domains ) { 621 snprintf( c->cr_msg, sizeof( c->cr_msg ), 622 "cannot set legacy attrs when URIs are present" ); 623 Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n", 624 c->cr_msg, NULL, NULL ); 625 rc = ARG_BAD_CONF; 626 break; 627 } 628 if ( ! legacy ) { 629 unique_new_domain ( &private->legacy, 630 UNIQUE_DEFAULT_URI, 631 c ); 632 legacy = private->legacy; 633 } 634 /* ... not using ARG_ON_OFF makes this necessary too */ 635 assert ( c->argc == 2 ); 636 legacy->strict = (strcasecmp ( c->argv[1], "TRUE" ) == 0); 637 private->legacy_strict_set = 1; 638 rc = 0; 639 break; 640 default: 641 abort(); 642 } 643 644 return rc; 645 } 646 647 static int 648 unique_cf_uri( ConfigArgs *c ) 649 { 650 slap_overinst *on = (slap_overinst *)c->bi; 651 unique_data *private = (unique_data *) on->on_bi.bi_private; 652 unique_domain *domains = private->domains; 653 unique_domain *legacy = private->legacy; 654 unique_domain *domain = NULL, **domainp = NULL; 655 int rc = ARG_BAD_CONF; 656 int i; 657 658 switch ( c->op ) { 659 case SLAP_CONFIG_EMIT: 660 for ( domain = domains; 661 domain; 662 domain = domain->next ) { 663 rc = value_add_one ( &c->rvalue_vals, 664 &domain->domain_spec ); 665 if ( rc ) break; 666 } 667 break; 668 case LDAP_MOD_DELETE: 669 if ( c->valx < 0 ) { /* delete them all! */ 670 unique_free_domain ( domains ); 671 private->domains = NULL; 672 } else { /* delete just one */ 673 domainp = &private->domains; 674 for ( i=0; i < c->valx && *domainp; ++i ) 675 domainp = &(*domainp)->next; 676 677 /* If *domainp is null, we walked off the end 678 * of the list. This happens when back-config 679 * and the overlay are out-of-sync, like when 680 * rejecting changes before ITS#4752 gets 681 * fixed. 682 * 683 * This should never happen, but will appear 684 * if you backport this version of 685 * slapo-unique without the config-undo fixes 686 * 687 * test024 Will hit this case in such a 688 * situation. 689 */ 690 assert (*domainp != NULL); 691 692 domain = *domainp; 693 *domainp = domain->next; 694 domain->next = NULL; 695 unique_free_domain ( domain ); 696 } 697 rc = 0; 698 break; 699 700 case SLAP_CONFIG_ADD: /* fallthrough */ 701 case LDAP_MOD_ADD: 702 if ( legacy ) { 703 snprintf( c->cr_msg, sizeof( c->cr_msg ), 704 "cannot set Uri when legacy attrs are present" ); 705 Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n", 706 c->cr_msg, NULL, NULL ); 707 rc = ARG_BAD_CONF; 708 break; 709 } 710 rc = 0; 711 if ( c->line ) rc = unique_new_domain ( &domain, c->line, c ); 712 else rc = unique_new_domain ( &domain, c->argv[1], c ); 713 if ( rc ) break; 714 assert ( domain->next == NULL ); 715 for ( domainp = &private->domains; 716 *domainp; 717 domainp = &(*domainp)->next ) ; 718 *domainp = domain; 719 720 break; 721 722 default: 723 abort (); 724 } 725 726 return rc; 727 } 728 729 /* 730 ** allocate new unique_data; 731 ** initialize, copy basedn; 732 ** store in on_bi.bi_private; 733 ** 734 */ 735 736 static int 737 unique_db_init( 738 BackendDB *be, 739 ConfigReply *cr 740 ) 741 { 742 slap_overinst *on = (slap_overinst *)be->bd_info; 743 unique_data **privatep = (unique_data **) &on->on_bi.bi_private; 744 745 Debug(LDAP_DEBUG_TRACE, "==> unique_db_init\n", 0, 0, 0); 746 747 *privatep = ch_calloc ( 1, sizeof ( unique_data ) ); 748 749 return 0; 750 } 751 752 static int 753 unique_db_destroy( 754 BackendDB *be, 755 ConfigReply *cr 756 ) 757 { 758 slap_overinst *on = (slap_overinst *)be->bd_info; 759 unique_data **privatep = (unique_data **) &on->on_bi.bi_private; 760 unique_data *private = *privatep; 761 762 Debug(LDAP_DEBUG_TRACE, "==> unique_db_destroy\n", 0, 0, 0); 763 764 if ( private ) { 765 unique_domain *domains = private->domains; 766 unique_domain *legacy = private->legacy; 767 768 unique_free_domain ( domains ); 769 unique_free_domain ( legacy ); 770 ch_free ( private ); 771 *privatep = NULL; 772 } 773 774 return 0; 775 } 776 777 static int 778 unique_open( 779 BackendDB *be, 780 ConfigReply *cr 781 ) 782 { 783 Debug(LDAP_DEBUG_TRACE, "unique_open: overlay initialized\n", 0, 0, 0); 784 785 return 0; 786 } 787 788 789 /* 790 ** Leave unique_data but wipe out config 791 ** 792 */ 793 794 static int 795 unique_close( 796 BackendDB *be, 797 ConfigReply *cr 798 ) 799 { 800 slap_overinst *on = (slap_overinst *) be->bd_info; 801 unique_data **privatep = (unique_data **) &on->on_bi.bi_private; 802 unique_data *private = *privatep; 803 804 Debug(LDAP_DEBUG_TRACE, "==> unique_close\n", 0, 0, 0); 805 806 if ( private ) { 807 unique_domain *domains = private->domains; 808 unique_domain *legacy = private->legacy; 809 810 unique_free_domain ( domains ); 811 unique_free_domain ( legacy ); 812 memset ( private, 0, sizeof ( unique_data ) ); 813 } 814 815 return ( 0 ); 816 } 817 818 819 /* 820 ** search callback 821 ** if this is a REP_SEARCH, count++; 822 ** 823 */ 824 825 static int count_attr_cb( 826 Operation *op, 827 SlapReply *rs 828 ) 829 { 830 unique_counter *uc; 831 832 /* because you never know */ 833 if(!op || !rs) return(0); 834 835 /* Only search entries are interesting */ 836 if(rs->sr_type != REP_SEARCH) return(0); 837 838 uc = op->o_callback->sc_private; 839 840 /* Ignore the current entry */ 841 if ( dn_match( uc->ndn, &rs->sr_entry->e_nname )) return(0); 842 843 Debug(LDAP_DEBUG_TRACE, "==> count_attr_cb <%s>\n", 844 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0); 845 846 uc->count++; 847 848 return(0); 849 } 850 851 /* count the length of one attribute ad 852 * (and all of its values b) 853 * in the proposed filter 854 */ 855 static int 856 count_filter_len( 857 unique_domain *domain, 858 unique_domain_uri *uri, 859 AttributeDescription *ad, 860 BerVarray b 861 ) 862 { 863 unique_attrs *attr; 864 int i; 865 int ks = 0; 866 867 while ( !is_at_operational( ad->ad_type ) ) { 868 if ( uri->attrs ) { 869 for ( attr = uri->attrs; attr; attr = attr->next ) { 870 if ( ad == attr->attr ) { 871 break; 872 } 873 } 874 if ( ( domain->ignore && attr ) 875 || (!domain->ignore && !attr )) { 876 break; 877 } 878 } 879 if ( b && b[0].bv_val ) { 880 for (i = 0; b[i].bv_val; i++ ) { 881 /* note: make room for filter escaping... */ 882 ks += ( 3 * b[i].bv_len ) + ad->ad_cname.bv_len + STRLENOF( "(=)" ); 883 } 884 } else if ( domain->strict ) { 885 ks += ad->ad_cname.bv_len + STRLENOF( "(=*)" ); /* (attr=*) */ 886 } 887 break; 888 } 889 890 return ks; 891 } 892 893 static char * 894 build_filter( 895 unique_domain *domain, 896 unique_domain_uri *uri, 897 AttributeDescription *ad, 898 BerVarray b, 899 char *kp, 900 int ks, 901 void *ctx 902 ) 903 { 904 unique_attrs *attr; 905 int i; 906 907 while ( !is_at_operational( ad->ad_type ) ) { 908 if ( uri->attrs ) { 909 for ( attr = uri->attrs; attr; attr = attr->next ) { 910 if ( ad == attr->attr ) { 911 break; 912 } 913 } 914 if ( ( domain->ignore && attr ) 915 || (!domain->ignore && !attr )) { 916 break; 917 } 918 } 919 if ( b && b[0].bv_val ) { 920 for ( i = 0; b[i].bv_val; i++ ) { 921 struct berval bv; 922 int len; 923 924 ldap_bv2escaped_filter_value_x( &b[i], &bv, 1, ctx ); 925 len = snprintf( kp, ks, "(%s=%s)", ad->ad_cname.bv_val, bv.bv_val ); 926 assert( len >= 0 && len < ks ); 927 kp += len; 928 if ( bv.bv_val != b[i].bv_val ) { 929 ber_memfree_x( bv.bv_val, ctx ); 930 } 931 } 932 } else if ( domain->strict ) { 933 int len; 934 len = snprintf( kp, ks, "(%s=*)", ad->ad_cname.bv_val ); 935 assert( len >= 0 && len < ks ); 936 kp += len; 937 } 938 break; 939 } 940 return kp; 941 } 942 943 static int 944 unique_search( 945 Operation *op, 946 Operation *nop, 947 struct berval * dn, 948 int scope, 949 SlapReply *rs, 950 struct berval *key 951 ) 952 { 953 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 954 SlapReply nrs = { REP_RESULT }; 955 slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */ 956 unique_counter uq = { NULL, 0 }; 957 int rc; 958 959 Debug(LDAP_DEBUG_TRACE, "==> unique_search %s\n", key, 0, 0); 960 961 nop->ors_filter = str2filter_x(nop, key->bv_val); 962 nop->ors_filterstr = *key; 963 964 cb.sc_response = (slap_response*)count_attr_cb; 965 cb.sc_private = &uq; 966 nop->o_callback = &cb; 967 nop->o_tag = LDAP_REQ_SEARCH; 968 nop->ors_scope = scope; 969 nop->ors_deref = LDAP_DEREF_NEVER; 970 nop->ors_limit = NULL; 971 nop->ors_slimit = SLAP_NO_LIMIT; 972 nop->ors_tlimit = SLAP_NO_LIMIT; 973 nop->ors_attrs = slap_anlist_no_attrs; 974 nop->ors_attrsonly = 1; 975 976 uq.ndn = &op->o_req_ndn; 977 978 nop->o_req_ndn = *dn; 979 nop->o_ndn = op->o_bd->be_rootndn; 980 981 nop->o_bd = on->on_info->oi_origdb; 982 rc = nop->o_bd->be_search(nop, &nrs); 983 filter_free_x(nop, nop->ors_filter); 984 op->o_tmpfree( key->bv_val, op->o_tmpmemctx ); 985 986 if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) { 987 op->o_bd->bd_info = (BackendInfo *) on->on_info; 988 send_ldap_error(op, rs, rc, "unique_search failed"); 989 return(rs->sr_err); 990 } 991 992 Debug(LDAP_DEBUG_TRACE, "=> unique_search found %d records\n", uq.count, 0, 0); 993 994 if(uq.count) { 995 op->o_bd->bd_info = (BackendInfo *) on->on_info; 996 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, 997 "some attributes not unique"); 998 return(rs->sr_err); 999 } 1000 1001 return(SLAP_CB_CONTINUE); 1002 } 1003 1004 static int 1005 unique_add( 1006 Operation *op, 1007 SlapReply *rs 1008 ) 1009 { 1010 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 1011 unique_data *private = (unique_data *) on->on_bi.bi_private; 1012 unique_domain *domains = private->domains; 1013 unique_domain *legacy = private->legacy; 1014 unique_domain *domain; 1015 Operation nop = *op; 1016 Attribute *a; 1017 char *key, *kp; 1018 struct berval bvkey; 1019 int rc = SLAP_CB_CONTINUE; 1020 1021 Debug(LDAP_DEBUG_TRACE, "==> unique_add <%s>\n", 1022 op->o_req_dn.bv_val, 0, 0); 1023 1024 for ( domain = legacy ? legacy : domains; 1025 domain; 1026 domain = domain->next ) 1027 { 1028 unique_domain_uri *uri; 1029 int ks = STRLENOF("(|)"); 1030 1031 for ( uri = domain->uri; 1032 uri; 1033 uri = uri->next ) 1034 { 1035 int len; 1036 1037 if ( uri->ndn.bv_val 1038 && !dnIsSuffix( &op->o_req_ndn, &uri->ndn )) 1039 continue; 1040 1041 if(!(a = op->ora_e->e_attrs)) { 1042 op->o_bd->bd_info = (BackendInfo *) on->on_info; 1043 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX, 1044 "unique_add() got null op.ora_e.e_attrs"); 1045 rc = rs->sr_err; 1046 break; 1047 1048 } else { 1049 for(; a; a = a->a_next) { 1050 ks += count_filter_len ( domain, 1051 uri, 1052 a->a_desc, 1053 a->a_vals); 1054 } 1055 } 1056 1057 /* skip this domain-uri if it isn't involved */ 1058 if ( !ks ) continue; 1059 1060 /* terminating NUL */ 1061 ks++; 1062 1063 if ( uri->filter.bv_val && uri->filter.bv_len ) 1064 ks += uri->filter.bv_len + STRLENOF ("(&)"); 1065 kp = key = op->o_tmpalloc(ks, op->o_tmpmemctx); 1066 1067 if ( uri->filter.bv_val && uri->filter.bv_len ) { 1068 len = snprintf (kp, ks, "(&%s", uri->filter.bv_val); 1069 assert( len >= 0 && len < ks ); 1070 kp += len; 1071 } 1072 len = snprintf(kp, ks - (kp - key), "(|"); 1073 assert( len >= 0 && len < ks - (kp - key) ); 1074 kp += len; 1075 1076 for(a = op->ora_e->e_attrs; a; a = a->a_next) 1077 kp = build_filter(domain, 1078 uri, 1079 a->a_desc, 1080 a->a_vals, 1081 kp, 1082 ks - ( kp - key ), 1083 op->o_tmpmemctx); 1084 1085 len = snprintf(kp, ks - (kp - key), ")"); 1086 assert( len >= 0 && len < ks - (kp - key) ); 1087 kp += len; 1088 if ( uri->filter.bv_val && uri->filter.bv_len ) { 1089 len = snprintf(kp, ks - (kp - key), ")"); 1090 assert( len >= 0 && len < ks - (kp - key) ); 1091 kp += len; 1092 } 1093 bvkey.bv_val = key; 1094 bvkey.bv_len = kp - key; 1095 1096 rc = unique_search ( op, 1097 &nop, 1098 uri->ndn.bv_val ? 1099 &uri->ndn : 1100 &op->o_bd->be_nsuffix[0], 1101 uri->scope, 1102 rs, 1103 &bvkey); 1104 1105 if ( rc != SLAP_CB_CONTINUE ) break; 1106 } 1107 if ( rc != SLAP_CB_CONTINUE ) break; 1108 } 1109 1110 return rc; 1111 } 1112 1113 1114 static int 1115 unique_modify( 1116 Operation *op, 1117 SlapReply *rs 1118 ) 1119 { 1120 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 1121 unique_data *private = (unique_data *) on->on_bi.bi_private; 1122 unique_domain *domains = private->domains; 1123 unique_domain *legacy = private->legacy; 1124 unique_domain *domain; 1125 Operation nop = *op; 1126 Modifications *m; 1127 char *key, *kp; 1128 struct berval bvkey; 1129 int rc = SLAP_CB_CONTINUE; 1130 1131 Debug(LDAP_DEBUG_TRACE, "==> unique_modify <%s>\n", 1132 op->o_req_dn.bv_val, 0, 0); 1133 1134 for ( domain = legacy ? legacy : domains; 1135 domain; 1136 domain = domain->next ) 1137 { 1138 unique_domain_uri *uri; 1139 int ks = STRLENOF("(|)"); 1140 1141 for ( uri = domain->uri; 1142 uri; 1143 uri = uri->next ) 1144 { 1145 int len; 1146 1147 if ( uri->ndn.bv_val 1148 && !dnIsSuffix( &op->o_req_ndn, &uri->ndn )) 1149 continue; 1150 1151 if ( !(m = op->orm_modlist) ) { 1152 op->o_bd->bd_info = (BackendInfo *) on->on_info; 1153 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX, 1154 "unique_modify() got null op.orm_modlist"); 1155 rc = rs->sr_err; 1156 break; 1157 1158 } else 1159 for ( ; m; m = m->sml_next) 1160 if ( (m->sml_op & LDAP_MOD_OP) 1161 != LDAP_MOD_DELETE ) 1162 ks += count_filter_len 1163 ( domain, 1164 uri, 1165 m->sml_desc, 1166 m->sml_values); 1167 1168 /* skip this domain-uri if it isn't involved */ 1169 if ( !ks ) continue; 1170 1171 /* terminating NUL */ 1172 ks++; 1173 1174 if ( uri->filter.bv_val && uri->filter.bv_len ) 1175 ks += uri->filter.bv_len + STRLENOF ("(&)"); 1176 kp = key = op->o_tmpalloc(ks, op->o_tmpmemctx); 1177 1178 if ( uri->filter.bv_val && uri->filter.bv_len ) { 1179 len = snprintf(kp, ks, "(&%s", uri->filter.bv_val); 1180 assert( len >= 0 && len < ks ); 1181 kp += len; 1182 } 1183 len = snprintf(kp, ks - (kp - key), "(|"); 1184 assert( len >= 0 && len < ks - (kp - key) ); 1185 kp += len; 1186 1187 for(m = op->orm_modlist; m; m = m->sml_next) 1188 if ( (m->sml_op & LDAP_MOD_OP) 1189 != LDAP_MOD_DELETE ) 1190 kp = build_filter ( domain, 1191 uri, 1192 m->sml_desc, 1193 m->sml_values, 1194 kp, 1195 ks - (kp - key), 1196 op->o_tmpmemctx ); 1197 1198 len = snprintf(kp, ks - (kp - key), ")"); 1199 assert( len >= 0 && len < ks - (kp - key) ); 1200 kp += len; 1201 if ( uri->filter.bv_val && uri->filter.bv_len ) { 1202 len = snprintf (kp, ks - (kp - key), ")"); 1203 assert( len >= 0 && len < ks - (kp - key) ); 1204 kp += len; 1205 } 1206 bvkey.bv_val = key; 1207 bvkey.bv_len = kp - key; 1208 1209 rc = unique_search ( op, 1210 &nop, 1211 uri->ndn.bv_val ? 1212 &uri->ndn : 1213 &op->o_bd->be_nsuffix[0], 1214 uri->scope, 1215 rs, 1216 &bvkey); 1217 1218 if ( rc != SLAP_CB_CONTINUE ) break; 1219 } 1220 if ( rc != SLAP_CB_CONTINUE ) break; 1221 } 1222 1223 return rc; 1224 } 1225 1226 1227 static int 1228 unique_modrdn( 1229 Operation *op, 1230 SlapReply *rs 1231 ) 1232 { 1233 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 1234 unique_data *private = (unique_data *) on->on_bi.bi_private; 1235 unique_domain *domains = private->domains; 1236 unique_domain *legacy = private->legacy; 1237 unique_domain *domain; 1238 Operation nop = *op; 1239 char *key, *kp; 1240 struct berval bvkey; 1241 LDAPRDN newrdn; 1242 struct berval bv[2]; 1243 int rc = SLAP_CB_CONTINUE; 1244 1245 Debug(LDAP_DEBUG_TRACE, "==> unique_modrdn <%s> <%s>\n", 1246 op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0); 1247 1248 for ( domain = legacy ? legacy : domains; 1249 domain; 1250 domain = domain->next ) 1251 { 1252 unique_domain_uri *uri; 1253 int ks = STRLENOF("(|)"); 1254 1255 for ( uri = domain->uri; 1256 uri; 1257 uri = uri->next ) 1258 { 1259 int i, len; 1260 1261 if ( uri->ndn.bv_val 1262 && !dnIsSuffix( &op->o_req_ndn, &uri->ndn ) 1263 && (!op->orr_nnewSup 1264 || !dnIsSuffix( op->orr_nnewSup, &uri->ndn ))) 1265 continue; 1266 1267 if ( ldap_bv2rdn_x ( &op->oq_modrdn.rs_newrdn, 1268 &newrdn, 1269 (char **)&rs->sr_text, 1270 LDAP_DN_FORMAT_LDAP, 1271 op->o_tmpmemctx ) ) { 1272 op->o_bd->bd_info = (BackendInfo *) on->on_info; 1273 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX, 1274 "unknown type(s) used in RDN"); 1275 rc = rs->sr_err; 1276 break; 1277 } 1278 1279 rc = SLAP_CB_CONTINUE; 1280 for ( i=0; newrdn[i]; i++) { 1281 AttributeDescription *ad = NULL; 1282 if ( slap_bv2ad( &newrdn[i]->la_attr, &ad, &rs->sr_text )) { 1283 ldap_rdnfree_x( newrdn, op->o_tmpmemctx ); 1284 rs->sr_err = LDAP_INVALID_SYNTAX; 1285 send_ldap_result( op, rs ); 1286 rc = rs->sr_err; 1287 break; 1288 } 1289 newrdn[i]->la_private = ad; 1290 } 1291 if ( rc != SLAP_CB_CONTINUE ) break; 1292 1293 bv[1].bv_val = NULL; 1294 bv[1].bv_len = 0; 1295 1296 for ( i=0; newrdn[i]; i++ ) { 1297 bv[0] = newrdn[i]->la_value; 1298 ks += count_filter_len ( domain, 1299 uri, 1300 newrdn[i]->la_private, 1301 bv); 1302 } 1303 1304 /* skip this domain if it isn't involved */ 1305 if ( !ks ) continue; 1306 1307 /* terminating NUL */ 1308 ks++; 1309 1310 if ( uri->filter.bv_val && uri->filter.bv_len ) 1311 ks += uri->filter.bv_len + STRLENOF ("(&)"); 1312 kp = key = op->o_tmpalloc(ks, op->o_tmpmemctx); 1313 1314 if ( uri->filter.bv_val && uri->filter.bv_len ) { 1315 len = snprintf(kp, ks, "(&%s", uri->filter.bv_val); 1316 assert( len >= 0 && len < ks ); 1317 kp += len; 1318 } 1319 len = snprintf(kp, ks - (kp - key), "(|"); 1320 assert( len >= 0 && len < ks - (kp - key) ); 1321 kp += len; 1322 1323 for ( i=0; newrdn[i]; i++) { 1324 bv[0] = newrdn[i]->la_value; 1325 kp = build_filter ( domain, 1326 uri, 1327 newrdn[i]->la_private, 1328 bv, 1329 kp, 1330 ks - (kp - key ), 1331 op->o_tmpmemctx); 1332 } 1333 1334 len = snprintf(kp, ks - (kp - key), ")"); 1335 assert( len >= 0 && len < ks - (kp - key) ); 1336 kp += len; 1337 if ( uri->filter.bv_val && uri->filter.bv_len ) { 1338 len = snprintf (kp, ks - (kp - key), ")"); 1339 assert( len >= 0 && len < ks - (kp - key) ); 1340 kp += len; 1341 } 1342 bvkey.bv_val = key; 1343 bvkey.bv_len = kp - key; 1344 1345 rc = unique_search ( op, 1346 &nop, 1347 uri->ndn.bv_val ? 1348 &uri->ndn : 1349 &op->o_bd->be_nsuffix[0], 1350 uri->scope, 1351 rs, 1352 &bvkey); 1353 1354 if ( rc != SLAP_CB_CONTINUE ) break; 1355 } 1356 if ( rc != SLAP_CB_CONTINUE ) break; 1357 } 1358 1359 return rc; 1360 } 1361 1362 /* 1363 ** init_module is last so the symbols resolve "for free" -- 1364 ** it expects to be called automagically during dynamic module initialization 1365 */ 1366 1367 int 1368 unique_initialize() 1369 { 1370 int rc; 1371 1372 /* statically declared just after the #includes at top */ 1373 memset (&unique, 0, sizeof(unique)); 1374 1375 unique.on_bi.bi_type = "unique"; 1376 unique.on_bi.bi_db_init = unique_db_init; 1377 unique.on_bi.bi_db_destroy = unique_db_destroy; 1378 unique.on_bi.bi_db_open = unique_open; 1379 unique.on_bi.bi_db_close = unique_close; 1380 unique.on_bi.bi_op_add = unique_add; 1381 unique.on_bi.bi_op_modify = unique_modify; 1382 unique.on_bi.bi_op_modrdn = unique_modrdn; 1383 1384 unique.on_bi.bi_cf_ocs = uniqueocs; 1385 rc = config_register_schema( uniquecfg, uniqueocs ); 1386 if ( rc ) return rc; 1387 1388 return(overlay_register(&unique)); 1389 } 1390 1391 #if SLAPD_OVER_UNIQUE == SLAPD_MOD_DYNAMIC && defined(PIC) 1392 int init_module(int argc, char *argv[]) { 1393 return unique_initialize(); 1394 } 1395 #endif 1396 1397 #endif /* SLAPD_OVER_UNIQUE */ 1398