1 /* $OpenLDAP: pkg/ldap/servers/slapd/overlays/constraint.c,v 1.2.2.8 2008/05/27 19:59:47 quanah Exp $ */ 2 /* constraint.c - Overlay to constrain attributes to certain values */ 3 /* 4 * Copyright 2003-2004 Hewlett-Packard Company 5 * Copyright 2007 Emmanuel Dreyfus 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted only as authorized by the OpenLDAP 10 * Public License. 11 * 12 * A copy of this license is available in the file LICENSE in the 13 * top-level directory of the distribution or, alternatively, at 14 * <http://www.OpenLDAP.org/license.html>. 15 */ 16 /* 17 * Authors: Neil Dunbar <neil.dunbar@hp.com> 18 * Emmannuel Dreyfus <manu@netbsd.org> 19 */ 20 #include "portable.h" 21 22 #ifdef SLAPD_OVER_CONSTRAINT 23 24 #include <stdio.h> 25 26 #include <ac/string.h> 27 #include <ac/socket.h> 28 #include <ac/regex.h> 29 30 #include "lutil.h" 31 #include "slap.h" 32 #include "config.h" 33 34 /* 35 * This overlay limits the values which can be placed into an 36 * attribute, over and above the limits placed by the schema. 37 * 38 * It traps only LDAP adds and modify commands (and only seeks to 39 * control the add and modify value mods of a modify) 40 */ 41 42 #define REGEX_STR "regex" 43 #define URI_STR "uri" 44 #define SIZE_STR "size" 45 #define COUNT_STR "count" 46 47 /* 48 * Linked list of attribute constraints which we should enforce. 49 * This is probably a sub optimal structure - some form of sorted 50 * array would be better if the number of attributes contrained is 51 * likely to be much bigger than 4 or 5. We stick with a list for 52 * the moment. 53 */ 54 55 typedef struct constraint { 56 struct constraint *ap_next; 57 AttributeDescription *ap; 58 regex_t *re; 59 LDAPURLDesc *lud; 60 size_t size; 61 size_t count; 62 AttributeDescription **attrs; 63 struct berval val; /* constraint value */ 64 struct berval dn; 65 struct berval filter; 66 } constraint; 67 68 enum { 69 CONSTRAINT_ATTRIBUTE = 1 70 }; 71 72 static ConfigDriver constraint_cf_gen; 73 74 static ConfigTable constraintcfg[] = { 75 { "constraint_attribute", "attribute> (regex|uri) <value", 76 4, 4, 0, ARG_MAGIC | CONSTRAINT_ATTRIBUTE, constraint_cf_gen, 77 "( OLcfgOvAt:13.1 NAME 'olcConstraintAttribute' " 78 "DESC 'regular expression constraint for attribute' " 79 "EQUALITY caseIgnoreMatch " 80 "SYNTAX OMsDirectoryString )", NULL, NULL }, 81 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 82 }; 83 84 static ConfigOCs constraintocs[] = { 85 { "( OLcfgOvOc:13.1 " 86 "NAME 'olcConstraintConfig' " 87 "DESC 'Constraint overlay configuration' " 88 "SUP olcOverlayConfig " 89 "MAY ( olcConstraintAttribute ) )", 90 Cft_Overlay, constraintcfg }, 91 { NULL, 0, NULL } 92 }; 93 94 static void 95 constraint_free( constraint *cp ) 96 { 97 if (cp->re) { 98 regfree(cp->re); 99 ch_free(cp->re); 100 } 101 if (!BER_BVISNULL(&cp->val)) 102 ch_free(cp->val.bv_val); 103 if (cp->lud) 104 ldap_free_urldesc(cp->lud); 105 if (cp->attrs) 106 ch_free(cp->attrs); 107 ch_free(cp); 108 } 109 110 static int 111 constraint_cf_gen( ConfigArgs *c ) 112 { 113 slap_overinst *on = (slap_overinst *)(c->bi); 114 constraint *cn = on->on_bi.bi_private, *cp; 115 struct berval bv; 116 int i, rc = 0; 117 constraint ap = { NULL, NULL, NULL }, *a2 = NULL; 118 const char *text = NULL; 119 120 switch ( c->op ) { 121 case SLAP_CONFIG_EMIT: 122 switch (c->type) { 123 case CONSTRAINT_ATTRIBUTE: 124 for (cp=cn; cp; cp=cp->ap_next) { 125 int len; 126 char *s; 127 char *tstr = NULL; 128 129 len = cp->ap->ad_cname.bv_len + 3; 130 if (cp->re) { 131 len += STRLENOF(REGEX_STR); 132 tstr = REGEX_STR; 133 } else if (cp->lud) { 134 len += STRLENOF(URI_STR); 135 tstr = URI_STR; 136 } else if (cp->size) { 137 len += STRLENOF(SIZE_STR); 138 tstr = SIZE_STR; 139 } else if (cp->count) { 140 len += STRLENOF(COUNT_STR); 141 tstr = COUNT_STR; 142 } 143 len += cp->val.bv_len; 144 145 s = ch_malloc(len); 146 147 bv.bv_len = snprintf(s, len, "%s %s %s", cp->ap->ad_cname.bv_val, 148 tstr, cp->val.bv_val); 149 bv.bv_val = s; 150 rc = value_add_one( &c->rvalue_vals, &bv ); 151 if (rc) return rc; 152 rc = value_add_one( &c->rvalue_nvals, &bv ); 153 if (rc) return rc; 154 ch_free(s); 155 } 156 break; 157 default: 158 abort(); 159 break; 160 } 161 break; 162 case LDAP_MOD_DELETE: 163 switch (c->type) { 164 case CONSTRAINT_ATTRIBUTE: 165 if (!cn) break; /* nothing to do */ 166 167 if (c->valx < 0) { 168 /* zap all constraints */ 169 while (cn) { 170 cp = cn->ap_next; 171 constraint_free( cn ); 172 cn = cp; 173 } 174 175 on->on_bi.bi_private = NULL; 176 } else { 177 constraint **cpp; 178 179 /* zap constraint numbered 'valx' */ 180 for(i=0, cp = cn, cpp = &cn; 181 (cp) && (i<c->valx); 182 i++, cpp = &cp->ap_next, cp = *cpp); 183 184 if (cp) { 185 /* zap cp, and join cpp to cp->ap_next */ 186 *cpp = cp->ap_next; 187 constraint_free( cp ); 188 } 189 on->on_bi.bi_private = cn; 190 } 191 break; 192 193 default: 194 abort(); 195 break; 196 } 197 break; 198 case SLAP_CONFIG_ADD: 199 case LDAP_MOD_ADD: 200 switch (c->type) { 201 case CONSTRAINT_ATTRIBUTE: 202 if ( slap_str2ad( c->argv[1], &ap.ap, &text ) ) { 203 snprintf( c->cr_msg, sizeof( c->cr_msg ), 204 "%s <%s>: %s\n", c->argv[0], c->argv[1], text ); 205 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 206 "%s: %s\n", c->log, c->cr_msg, 0 ); 207 return( ARG_BAD_CONF ); 208 } 209 210 if ( strcasecmp( c->argv[2], REGEX_STR ) == 0) { 211 int err; 212 213 ap.re = ch_malloc( sizeof(regex_t) ); 214 if ((err = regcomp( ap.re, 215 c->argv[3], REG_EXTENDED )) != 0) { 216 char errmsg[1024]; 217 218 regerror( err, ap.re, errmsg, sizeof(errmsg) ); 219 ch_free(ap.re); 220 snprintf( c->cr_msg, sizeof( c->cr_msg ), 221 "%s %s: Illegal regular expression \"%s\": Error %s", 222 c->argv[0], c->argv[1], c->argv[3], errmsg); 223 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 224 "%s: %s\n", c->log, c->cr_msg, 0 ); 225 ap.re = NULL; 226 return( ARG_BAD_CONF ); 227 } 228 ber_str2bv( c->argv[3], 0, 1, &ap.val ); 229 } else if ( strcasecmp( c->argv[2], SIZE_STR ) == 0 ) { 230 size_t size; 231 232 if ( ( size = atoi(c->argv[3]) ) != 0 ) 233 ap.size = size; 234 } else if ( strcasecmp( c->argv[2], COUNT_STR ) == 0 ) { 235 size_t count; 236 237 if ( ( count = atoi(c->argv[3]) ) != 0 ) 238 ap.count = count; 239 } else if ( strcasecmp( c->argv[2], URI_STR ) == 0 ) { 240 int err; 241 242 err = ldap_url_parse(c->argv[3], &ap.lud); 243 if ( err != LDAP_URL_SUCCESS ) { 244 snprintf( c->cr_msg, sizeof( c->cr_msg ), 245 "%s %s: Invalid URI \"%s\"", 246 c->argv[0], c->argv[1], c->argv[3]); 247 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 248 "%s: %s\n", c->log, c->cr_msg, 0 ); 249 return( ARG_BAD_CONF ); 250 } 251 252 if (ap.lud->lud_host != NULL) { 253 snprintf( c->cr_msg, sizeof( c->cr_msg ), 254 "%s %s: unsupported hostname in URI \"%s\"", 255 c->argv[0], c->argv[1], c->argv[3]); 256 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 257 "%s: %s\n", c->log, c->cr_msg, 0 ); 258 259 ldap_free_urldesc(ap.lud); 260 261 return( ARG_BAD_CONF ); 262 } 263 264 for ( i=0; ap.lud->lud_attrs[i]; i++); 265 /* FIXME: This is worthless without at least one attr */ 266 if ( i ) { 267 ap.attrs = ch_malloc( (i+1)*sizeof(AttributeDescription *)); 268 for ( i=0; ap.lud->lud_attrs[i]; i++) { 269 ap.attrs[i] = NULL; 270 if ( slap_str2ad( ap.lud->lud_attrs[i], &ap.attrs[i], &text ) ) { 271 ch_free( ap.attrs ); 272 snprintf( c->cr_msg, sizeof( c->cr_msg ), 273 "%s <%s>: %s\n", c->argv[0], ap.lud->lud_attrs[i], text ); 274 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 275 "%s: %s\n", c->log, c->cr_msg, 0 ); 276 return( ARG_BAD_CONF ); 277 } 278 } 279 ap.attrs[i] = NULL; 280 } 281 282 if (ap.lud->lud_dn == NULL) 283 ap.lud->lud_dn = ch_strdup(""); 284 285 if (ap.lud->lud_filter == NULL) 286 ap.lud->lud_filter = ch_strdup("objectClass=*"); 287 288 ber_str2bv( c->argv[3], 0, 1, &ap.val ); 289 } else { 290 snprintf( c->cr_msg, sizeof( c->cr_msg ), 291 "%s %s: Unknown constraint type: %s", 292 c->argv[0], c->argv[1], c->argv[2] ); 293 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 294 "%s: %s\n", c->log, c->cr_msg, 0 ); 295 return ( ARG_BAD_CONF ); 296 } 297 298 a2 = ch_calloc( sizeof(constraint), 1 ); 299 a2->ap_next = on->on_bi.bi_private; 300 a2->ap = ap.ap; 301 a2->re = ap.re; 302 a2->val = ap.val; 303 a2->lud = ap.lud; 304 a2->size = ap.size; 305 a2->count = ap.count; 306 if ( a2->lud ) { 307 ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn); 308 ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter); 309 } 310 a2->attrs = ap.attrs; 311 on->on_bi.bi_private = a2; 312 break; 313 default: 314 abort(); 315 break; 316 } 317 break; 318 default: 319 abort(); 320 } 321 322 return rc; 323 } 324 325 static int 326 constraint_uri_cb( Operation *op, SlapReply *rs ) 327 { 328 if(rs->sr_type == REP_SEARCH) { 329 int *foundp = op->o_callback->sc_private; 330 331 *foundp = 1; 332 333 Debug(LDAP_DEBUG_TRACE, "==> constraint_uri_cb <%s>\n", 334 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0); 335 } 336 return 0; 337 } 338 339 static int 340 constraint_violation( constraint *c, struct berval *bv, Operation *op, SlapReply *rs) 341 { 342 if ((!c) || (!bv)) return 0; 343 344 if ((c->re) && 345 (regexec(c->re, bv->bv_val, 0, NULL, 0) == REG_NOMATCH)) 346 return 1; /* regular expression violation */ 347 348 if ((c->size) && (bv->bv_len > c->size)) 349 return 1; /* size violation */ 350 351 if (c->lud) { 352 Operation nop = *op; 353 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 354 slap_callback cb; 355 SlapReply nrs = { REP_RESULT }; 356 int i; 357 int found; 358 int rc; 359 size_t len; 360 struct berval filterstr; 361 char *ptr; 362 363 found = 0; 364 365 nrs.sr_entry = NULL; 366 nrs.sr_nentries = 0; 367 368 cb.sc_next = NULL; 369 cb.sc_response = constraint_uri_cb; 370 cb.sc_cleanup = NULL; 371 cb.sc_private = &found; 372 373 nop.o_protocol = LDAP_VERSION3; 374 nop.o_tag = LDAP_REQ_SEARCH; 375 nop.o_time = slap_get_time(); 376 if (c->lud->lud_dn) { 377 struct berval dn; 378 379 ber_str2bv(c->lud->lud_dn, 0, 0, &dn); 380 nop.o_req_dn = dn; 381 nop.o_req_ndn = dn; 382 nop.o_bd = select_backend(&nop.o_req_ndn, 1 ); 383 if (!nop.o_bd || !nop.o_bd->be_search) { 384 return 1; /* unexpected error */ 385 } 386 } else { 387 nop.o_req_dn = nop.o_bd->be_nsuffix[0]; 388 nop.o_req_ndn = nop.o_bd->be_nsuffix[0]; 389 nop.o_bd = on->on_info->oi_origdb; 390 } 391 nop.o_do_not_cache = 1; 392 nop.o_callback = &cb; 393 394 nop.ors_scope = c->lud->lud_scope; 395 nop.ors_deref = LDAP_DEREF_NEVER; 396 nop.ors_slimit = SLAP_NO_LIMIT; 397 nop.ors_tlimit = SLAP_NO_LIMIT; 398 nop.ors_limit = NULL; 399 400 nop.ors_attrsonly = 0; 401 nop.ors_attrs = slap_anlist_no_attrs; 402 403 len = STRLENOF("(&(") + 404 c->filter.bv_len + 405 STRLENOF(")(|"); 406 407 for (i = 0; c->attrs[i]; i++) { 408 len += STRLENOF("(") + 409 c->attrs[i]->ad_cname.bv_len + 410 STRLENOF("=") + 411 bv->bv_len + 412 STRLENOF(")"); 413 } 414 415 len += STRLENOF("))"); 416 filterstr.bv_len = len; 417 filterstr.bv_val = op->o_tmpalloc(len + 1, op->o_tmpmemctx); 418 419 ptr = filterstr.bv_val + 420 snprintf(filterstr.bv_val, len, "(&(%s)(|", c->lud->lud_filter); 421 for (i = 0; c->attrs[i]; i++) { 422 *ptr++ = '('; 423 ptr = lutil_strcopy( ptr, c->attrs[i]->ad_cname.bv_val ); 424 *ptr++ = '='; 425 ptr = lutil_strcopy( ptr, bv->bv_val ); 426 *ptr++ = ')'; 427 } 428 *ptr++ = ')'; 429 *ptr++ = ')'; 430 431 Debug(LDAP_DEBUG_TRACE, 432 "==> constraint_violation uri filter = %s\n", 433 filterstr.bv_val, 0, 0); 434 435 nop.ors_filterstr = filterstr; 436 nop.ors_filter = str2filter_x(&nop, filterstr.bv_val); 437 438 rc = nop.o_bd->be_search( &nop, &nrs ); 439 440 op->o_tmpfree(filterstr.bv_val, op->o_tmpmemctx); 441 Debug(LDAP_DEBUG_TRACE, 442 "==> constraint_violation uri rc = %d, found = %d\n", 443 rc, found, 0); 444 445 if((rc != LDAP_SUCCESS) && (rc != LDAP_NO_SUCH_OBJECT)) { 446 send_ldap_error(op, rs, rc, 447 "constraint_violation uri search failed"); 448 return 1; /* unexpected error */ 449 } 450 451 if (!found) 452 return 1; /* constraint violation */ 453 454 } 455 456 return 0; 457 } 458 459 static char * 460 print_message( struct berval *errtext, AttributeDescription *a ) 461 { 462 char *ret; 463 int sz; 464 465 sz = errtext->bv_len + sizeof(" on ") + a->ad_cname.bv_len; 466 ret = ch_malloc(sz); 467 snprintf( ret, sz, "%s on %s", errtext->bv_val, a->ad_cname.bv_val ); 468 return ret; 469 } 470 471 static unsigned 472 constraint_count_attr(Entry *e, AttributeDescription *ad) 473 { 474 struct Attribute *a; 475 476 if ((a = attr_find(e->e_attrs, ad)) != NULL) 477 return a->a_numvals; 478 return 0; 479 } 480 481 static int 482 constraint_add( Operation *op, SlapReply *rs ) 483 { 484 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 485 Backend *be = op->o_bd; 486 Attribute *a; 487 constraint *c = on->on_bi.bi_private, *cp; 488 BerVarray b = NULL; 489 int i; 490 struct berval rsv = BER_BVC("add breaks constraint"); 491 char *msg; 492 493 if ((a = op->ora_e->e_attrs) == NULL) { 494 op->o_bd->bd_info = (BackendInfo *)(on->on_info); 495 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX, 496 "constraint_add() got null op.ora_e.e_attrs"); 497 return(rs->sr_err); 498 } 499 500 for(; a; a = a->a_next ) { 501 /* we don't constrain operational attributes */ 502 if (is_at_operational(a->a_desc->ad_type)) continue; 503 504 for(cp = c; cp; cp = cp->ap_next) { 505 if (cp->ap != a->a_desc) continue; 506 if ((b = a->a_vals) == NULL) continue; 507 508 Debug(LDAP_DEBUG_TRACE, 509 "==> constraint_add, " 510 "a->a_numvals = %d, cp->count = %d\n", 511 a->a_numvals, cp->count, 0); 512 513 if ((cp->count != 0) && (a->a_numvals > cp->count)) 514 goto add_violation; 515 516 for(i=0; b[i].bv_val; i++) 517 if (constraint_violation( cp, &b[i], op, rs)) 518 goto add_violation; 519 } 520 } 521 /* Default is to just fall through to the normal processing */ 522 return SLAP_CB_CONTINUE; 523 524 add_violation: 525 op->o_bd->bd_info = (BackendInfo *)(on->on_info); 526 msg = print_message( &rsv, a->a_desc ); 527 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, msg ); 528 ch_free(msg); 529 return (rs->sr_err); 530 } 531 532 533 static int 534 constraint_modify( Operation *op, SlapReply *rs ) 535 { 536 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 537 Backend *be = op->o_bd; 538 constraint *c = on->on_bi.bi_private, *cp; 539 Entry *target_entry = NULL; 540 Modifications *m; 541 BerVarray b = NULL; 542 int i; 543 struct berval rsv = BER_BVC("modify breaks constraint"); 544 char *msg; 545 546 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_modify()", 0,0,0); 547 if ((m = op->orm_modlist) == NULL) { 548 op->o_bd->bd_info = (BackendInfo *)(on->on_info); 549 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX, 550 "constraint_modify() got null orm_modlist"); 551 return(rs->sr_err); 552 } 553 554 /* Do we need to count attributes? */ 555 for(cp = c; cp; cp = cp->ap_next) { 556 if (cp->count != 0) { 557 int rc; 558 559 op->o_bd = on->on_info->oi_origdb; 560 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry ); 561 op->o_bd = be; 562 563 if (rc != 0 || target_entry == NULL) { 564 Debug(LDAP_DEBUG_TRACE, 565 "==> constraint_modify rc = %d\n", 566 rc, 0, 0); 567 goto mod_violation; 568 } 569 break; 570 } 571 } 572 573 for(;m; m = m->sml_next) { 574 int ce = 0; 575 576 /* Get this attribute count, if needed */ 577 if (target_entry) 578 ce = constraint_count_attr(target_entry, m->sml_desc); 579 580 if (is_at_operational( m->sml_desc->ad_type )) continue; 581 if ((( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_ADD) && 582 (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_REPLACE) && 583 (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_DELETE)) 584 continue; 585 /* we only care about ADD and REPLACE modifications */ 586 /* and DELETE are used to track attribute count */ 587 if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL)) 588 continue; 589 590 for(cp = c; cp; cp = cp->ap_next) { 591 if (cp->ap != m->sml_desc) continue; 592 593 if (cp->count != 0) { 594 int ca; 595 596 if (m->sml_op == LDAP_MOD_DELETE) 597 ce = 0; 598 599 for (ca = 0; b[ca].bv_val; ++ca); 600 601 Debug(LDAP_DEBUG_TRACE, 602 "==> constraint_modify ce = %d, " 603 "ca = %d, cp->count = %d\n", 604 ce, ca, cp->count); 605 606 if (m->sml_op == LDAP_MOD_ADD) 607 if (ca + ce > cp->count) 608 goto mod_violation; 609 if (m->sml_op == LDAP_MOD_REPLACE) { 610 if (ca > cp->count) 611 goto mod_violation; 612 ce = ca; 613 } 614 } 615 616 /* DELETE are to be ignored beyond this point */ 617 if (( m->sml_op & LDAP_MOD_OP ) == LDAP_MOD_DELETE) 618 continue; 619 620 for(i=0; b[i].bv_val; i++) 621 if (constraint_violation( cp, &b[i], op, rs)) 622 goto mod_violation; 623 } 624 } 625 626 if (target_entry) { 627 op->o_bd = on->on_info->oi_origdb; 628 be_entry_release_r(op, target_entry); 629 op->o_bd = be; 630 } 631 return SLAP_CB_CONTINUE; 632 mod_violation: 633 /* violation */ 634 if (target_entry) { 635 op->o_bd = on->on_info->oi_origdb; 636 be_entry_release_r(op, target_entry); 637 op->o_bd = be; 638 } 639 op->o_bd->bd_info = (BackendInfo *)(on->on_info); 640 msg = print_message( &rsv, m->sml_desc ); 641 send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, msg ); 642 ch_free(msg); 643 return (rs->sr_err); 644 } 645 646 static int 647 constraint_close( 648 BackendDB *be, 649 ConfigReply *cr ) 650 { 651 slap_overinst *on = (slap_overinst *) be->bd_info; 652 constraint *ap, *a2; 653 654 for ( ap = on->on_bi.bi_private; ap; ap = a2 ) { 655 a2 = ap->ap_next; 656 constraint_free( ap ); 657 } 658 659 return 0; 660 } 661 662 static slap_overinst constraint_ovl; 663 664 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC 665 static 666 #endif 667 int 668 constraint_initialize( void ) { 669 int rc; 670 671 constraint_ovl.on_bi.bi_type = "constraint"; 672 constraint_ovl.on_bi.bi_db_close = constraint_close; 673 constraint_ovl.on_bi.bi_op_add = constraint_add; 674 constraint_ovl.on_bi.bi_op_modify = constraint_modify; 675 676 constraint_ovl.on_bi.bi_private = NULL; 677 678 constraint_ovl.on_bi.bi_cf_ocs = constraintocs; 679 rc = config_register_schema( constraintcfg, constraintocs ); 680 if (rc) return rc; 681 682 return overlay_register( &constraint_ovl ); 683 } 684 685 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC 686 int init_module(int argc, char *argv[]) { 687 return constraint_initialize(); 688 } 689 #endif 690 691 #endif /* defined(SLAPD_OVER_CONSTRAINT) */ 692 693