1 /* $NetBSD: constraint.c,v 1.1.1.3 2010/03/08 02:14:20 lukem Exp $ */ 2 3 /* OpenLDAP: pkg/ldap/servers/slapd/overlays/constraint.c,v 1.2.2.17 2008/11/10 18:24:27 quanah Exp */ 4 /* constraint.c - Overlay to constrain attributes to certain values */ 5 /* 6 * Copyright 2003-2004 Hewlett-Packard Company 7 * Copyright 2007 Emmanuel Dreyfus 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 /* 19 * Authors: Neil Dunbar <neil.dunbar@hp.com> 20 * Emmannuel Dreyfus <manu@netbsd.org> 21 */ 22 #include "portable.h" 23 24 #ifdef SLAPD_OVER_CONSTRAINT 25 26 #include <stdio.h> 27 28 #include <ac/string.h> 29 #include <ac/socket.h> 30 #include <ac/regex.h> 31 32 #include "lutil.h" 33 #include "slap.h" 34 #include "config.h" 35 36 /* 37 * This overlay limits the values which can be placed into an 38 * attribute, over and above the limits placed by the schema. 39 * 40 * It traps only LDAP adds and modify commands (and only seeks to 41 * control the add and modify value mods of a modify) 42 */ 43 44 #define REGEX_STR "regex" 45 #define URI_STR "uri" 46 #define SET_STR "set" 47 #define SIZE_STR "size" 48 #define COUNT_STR "count" 49 50 /* 51 * Linked list of attribute constraints which we should enforce. 52 * This is probably a sub optimal structure - some form of sorted 53 * array would be better if the number of attributes contrained is 54 * likely to be much bigger than 4 or 5. We stick with a list for 55 * the moment. 56 */ 57 58 typedef struct constraint { 59 struct constraint *ap_next; 60 AttributeDescription **ap; 61 62 LDAPURLDesc *restrict_lud; 63 struct berval restrict_ndn; 64 Filter *restrict_filter; 65 struct berval restrict_val; 66 67 regex_t *re; 68 LDAPURLDesc *lud; 69 int set; 70 size_t size; 71 size_t count; 72 AttributeDescription **attrs; 73 struct berval val; /* constraint value */ 74 struct berval dn; 75 struct berval filter; 76 } constraint; 77 78 enum { 79 CONSTRAINT_ATTRIBUTE = 1 80 }; 81 82 static ConfigDriver constraint_cf_gen; 83 84 static ConfigTable constraintcfg[] = { 85 { "constraint_attribute", "attribute[list]> (regex|uri|set|size|count) <value> [<restrict URI>]", 86 4, 0, 0, ARG_MAGIC | CONSTRAINT_ATTRIBUTE, constraint_cf_gen, 87 "( OLcfgOvAt:13.1 NAME 'olcConstraintAttribute' " 88 "DESC 'constraint for list of attributes' " 89 "EQUALITY caseIgnoreMatch " 90 "SYNTAX OMsDirectoryString )", NULL, NULL }, 91 { NULL, NULL, 0, 0, 0, ARG_IGNORED } 92 }; 93 94 static ConfigOCs constraintocs[] = { 95 { "( OLcfgOvOc:13.1 " 96 "NAME 'olcConstraintConfig' " 97 "DESC 'Constraint overlay configuration' " 98 "SUP olcOverlayConfig " 99 "MAY ( olcConstraintAttribute ) )", 100 Cft_Overlay, constraintcfg }, 101 { NULL, 0, NULL } 102 }; 103 104 static void 105 constraint_free( constraint *cp, int freeme ) 106 { 107 if (cp->restrict_lud) 108 ldap_free_urldesc(cp->restrict_lud); 109 if (!BER_BVISNULL(&cp->restrict_ndn)) 110 ch_free(cp->restrict_ndn.bv_val); 111 if (cp->restrict_filter != NULL && cp->restrict_filter != slap_filter_objectClass_pres) 112 filter_free(cp->restrict_filter); 113 if (!BER_BVISNULL(&cp->restrict_val)) 114 ch_free(cp->restrict_val.bv_val); 115 if (cp->re) { 116 regfree(cp->re); 117 ch_free(cp->re); 118 } 119 if (!BER_BVISNULL(&cp->val)) 120 ch_free(cp->val.bv_val); 121 if (cp->lud) 122 ldap_free_urldesc(cp->lud); 123 if (cp->attrs) 124 ch_free(cp->attrs); 125 if (cp->ap) 126 ch_free(cp->ap); 127 if (freeme) 128 ch_free(cp); 129 } 130 131 static int 132 constraint_cf_gen( ConfigArgs *c ) 133 { 134 slap_overinst *on = (slap_overinst *)(c->bi); 135 constraint *cn = on->on_bi.bi_private, *cp; 136 struct berval bv; 137 int i, rc = 0; 138 constraint ap = { NULL }; 139 const char *text = NULL; 140 141 switch ( c->op ) { 142 case SLAP_CONFIG_EMIT: 143 switch (c->type) { 144 case CONSTRAINT_ATTRIBUTE: 145 for (cp=cn; cp; cp=cp->ap_next) { 146 char *s; 147 char *tstr = NULL; 148 int quotes = 0; 149 int j; 150 151 bv.bv_len = STRLENOF(" "); 152 for (j = 0; cp->ap[j]; j++) { 153 bv.bv_len += cp->ap[j]->ad_cname.bv_len; 154 } 155 156 /* room for commas */ 157 bv.bv_len += j - 1; 158 159 if (cp->re) { 160 tstr = REGEX_STR; 161 } else if (cp->lud) { 162 tstr = URI_STR; 163 quotes = 1; 164 } else if (cp->set) { 165 tstr = SET_STR; 166 quotes = 1; 167 } else if (cp->size) { 168 tstr = SIZE_STR; 169 } else if (cp->count) { 170 tstr = COUNT_STR; 171 } 172 173 bv.bv_len += strlen(tstr); 174 bv.bv_len += cp->val.bv_len + 2*quotes; 175 176 if (cp->restrict_lud != NULL) { 177 bv.bv_len += cp->restrict_val.bv_len + STRLENOF(" restrict=\"\""); 178 } 179 180 s = bv.bv_val = ch_malloc(bv.bv_len + 1); 181 182 s = lutil_strncopy( s, cp->ap[0]->ad_cname.bv_val, cp->ap[0]->ad_cname.bv_len ); 183 for (j = 1; cp->ap[j]; j++) { 184 *s++ = ','; 185 s = lutil_strncopy( s, cp->ap[j]->ad_cname.bv_val, cp->ap[j]->ad_cname.bv_len ); 186 } 187 *s++ = ' '; 188 s = lutil_strcopy( s, tstr ); 189 *s++ = ' '; 190 if ( quotes ) *s++ = '"'; 191 s = lutil_strncopy( s, cp->val.bv_val, cp->val.bv_len ); 192 if ( quotes ) *s++ = '"'; 193 if (cp->restrict_lud != NULL) { 194 s = lutil_strcopy( s, " restrict=\"" ); 195 s = lutil_strncopy( s, cp->restrict_val.bv_val, cp->restrict_val.bv_len ); 196 *s++ = '"'; 197 } 198 *s = '\0'; 199 200 rc = value_add_one( &c->rvalue_vals, &bv ); 201 if (rc == LDAP_SUCCESS) 202 rc = value_add_one( &c->rvalue_nvals, &bv ); 203 ch_free(bv.bv_val); 204 if (rc) return rc; 205 } 206 break; 207 default: 208 abort(); 209 break; 210 } 211 break; 212 case LDAP_MOD_DELETE: 213 switch (c->type) { 214 case CONSTRAINT_ATTRIBUTE: 215 if (!cn) break; /* nothing to do */ 216 217 if (c->valx < 0) { 218 /* zap all constraints */ 219 while (cn) { 220 cp = cn->ap_next; 221 constraint_free( cn, 1 ); 222 cn = cp; 223 } 224 225 on->on_bi.bi_private = NULL; 226 } else { 227 constraint **cpp; 228 229 /* zap constraint numbered 'valx' */ 230 for(i=0, cp = cn, cpp = &cn; 231 (cp) && (i<c->valx); 232 i++, cpp = &cp->ap_next, cp = *cpp); 233 234 if (cp) { 235 /* zap cp, and join cpp to cp->ap_next */ 236 *cpp = cp->ap_next; 237 constraint_free( cp, 1 ); 238 } 239 on->on_bi.bi_private = cn; 240 } 241 break; 242 243 default: 244 abort(); 245 break; 246 } 247 break; 248 case SLAP_CONFIG_ADD: 249 case LDAP_MOD_ADD: 250 switch (c->type) { 251 case CONSTRAINT_ATTRIBUTE: { 252 int j; 253 char **attrs = ldap_str2charray( c->argv[1], "," ); 254 255 for ( j = 0; attrs[j]; j++) 256 /* just count */ ; 257 ap.ap = ch_calloc( sizeof(AttributeDescription*), j + 1 ); 258 for ( j = 0; attrs[j]; j++) { 259 if ( slap_str2ad( attrs[j], &ap.ap[j], &text ) ) { 260 snprintf( c->cr_msg, sizeof( c->cr_msg ), 261 "%s <%s>: %s\n", c->argv[0], attrs[j], text ); 262 rc = ARG_BAD_CONF; 263 goto done; 264 } 265 } 266 267 if ( strcasecmp( c->argv[2], REGEX_STR ) == 0) { 268 int err; 269 270 ap.re = ch_malloc( sizeof(regex_t) ); 271 if ((err = regcomp( ap.re, 272 c->argv[3], REG_EXTENDED )) != 0) { 273 char errmsg[1024]; 274 275 regerror( err, ap.re, errmsg, sizeof(errmsg) ); 276 ch_free(ap.re); 277 snprintf( c->cr_msg, sizeof( c->cr_msg ), 278 "%s %s: Illegal regular expression \"%s\": Error %s", 279 c->argv[0], c->argv[1], c->argv[3], errmsg); 280 ap.re = NULL; 281 rc = ARG_BAD_CONF; 282 goto done; 283 } 284 ber_str2bv( c->argv[3], 0, 1, &ap.val ); 285 } else if ( strcasecmp( c->argv[2], SIZE_STR ) == 0 ) { 286 size_t size; 287 288 if ( ( size = atoi(c->argv[3]) ) != 0 ) 289 ap.size = size; 290 } else if ( strcasecmp( c->argv[2], COUNT_STR ) == 0 ) { 291 size_t count; 292 293 if ( ( count = atoi(c->argv[3]) ) != 0 ) 294 ap.count = count; 295 } else if ( strcasecmp( c->argv[2], URI_STR ) == 0 ) { 296 int err; 297 298 err = ldap_url_parse(c->argv[3], &ap.lud); 299 if ( err != LDAP_URL_SUCCESS ) { 300 snprintf( c->cr_msg, sizeof( c->cr_msg ), 301 "%s %s: Invalid URI \"%s\"", 302 c->argv[0], c->argv[1], c->argv[3]); 303 rc = ARG_BAD_CONF; 304 goto done; 305 } 306 307 if (ap.lud->lud_host != NULL) { 308 snprintf( c->cr_msg, sizeof( c->cr_msg ), 309 "%s %s: unsupported hostname in URI \"%s\"", 310 c->argv[0], c->argv[1], c->argv[3]); 311 ldap_free_urldesc(ap.lud); 312 rc = ARG_BAD_CONF; 313 goto done; 314 } 315 316 for ( i=0; ap.lud->lud_attrs[i]; i++); 317 /* FIXME: This is worthless without at least one attr */ 318 if ( i ) { 319 ap.attrs = ch_malloc( (i+1)*sizeof(AttributeDescription *)); 320 for ( i=0; ap.lud->lud_attrs[i]; i++) { 321 ap.attrs[i] = NULL; 322 if ( slap_str2ad( ap.lud->lud_attrs[i], &ap.attrs[i], &text ) ) { 323 ch_free( ap.attrs ); 324 snprintf( c->cr_msg, sizeof( c->cr_msg ), 325 "%s <%s>: %s\n", c->argv[0], ap.lud->lud_attrs[i], text ); 326 rc = ARG_BAD_CONF; 327 goto done; 328 } 329 } 330 ap.attrs[i] = NULL; 331 } 332 333 if (ap.lud->lud_dn == NULL) { 334 ap.lud->lud_dn = ch_strdup(""); 335 } else { 336 struct berval dn, ndn; 337 338 ber_str2bv( ap.lud->lud_dn, 0, 0, &dn ); 339 if (dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ) ) { 340 /* cleanup */ 341 snprintf( c->cr_msg, sizeof( c->cr_msg ), 342 "%s %s: URI %s DN normalization failed", 343 c->argv[0], c->argv[1], c->argv[3] ); 344 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 345 "%s: %s\n", c->log, c->cr_msg, 0 ); 346 rc = ARG_BAD_CONF; 347 goto done; 348 } 349 ldap_memfree( ap.lud->lud_dn ); 350 ap.lud->lud_dn = ndn.bv_val; 351 } 352 353 if (ap.lud->lud_filter == NULL) { 354 ap.lud->lud_filter = ch_strdup("objectClass=*"); 355 } else if ( ap.lud->lud_filter[0] == '(' ) { 356 ber_len_t len = strlen( ap.lud->lud_filter ); 357 if ( ap.lud->lud_filter[len - 1] != ')' ) { 358 snprintf( c->cr_msg, sizeof( c->cr_msg ), 359 "%s %s: invalid URI filter: %s", 360 c->argv[0], c->argv[1], ap.lud->lud_filter ); 361 rc = ARG_BAD_CONF; 362 goto done; 363 } 364 AC_MEMCPY( &ap.lud->lud_filter[0], &ap.lud->lud_filter[1], len - 2 ); 365 ap.lud->lud_filter[len - 2] = '\0'; 366 } 367 368 ber_str2bv( c->argv[3], 0, 1, &ap.val ); 369 370 } else if ( strcasecmp( c->argv[2], SET_STR ) == 0 ) { 371 ap.set = 1; 372 ber_str2bv( c->argv[3], 0, 1, &ap.val ); 373 374 } else { 375 snprintf( c->cr_msg, sizeof( c->cr_msg ), 376 "%s %s: Unknown constraint type: %s", 377 c->argv[0], c->argv[1], c->argv[2] ); 378 rc = ARG_BAD_CONF; 379 goto done; 380 } 381 382 if ( c->argc > 4 ) { 383 int argidx; 384 385 for ( argidx = 4; argidx < c->argc; argidx++ ) { 386 if ( strncasecmp( c->argv[argidx], "restrict=", STRLENOF("restrict=") ) == 0 ) { 387 int err; 388 char *arg = c->argv[argidx] + STRLENOF("restrict="); 389 390 err = ldap_url_parse(arg, &ap.restrict_lud); 391 if ( err != LDAP_URL_SUCCESS ) { 392 snprintf( c->cr_msg, sizeof( c->cr_msg ), 393 "%s %s: Invalid restrict URI \"%s\"", 394 c->argv[0], c->argv[1], arg); 395 rc = ARG_BAD_CONF; 396 goto done; 397 } 398 399 if (ap.restrict_lud->lud_host != NULL) { 400 snprintf( c->cr_msg, sizeof( c->cr_msg ), 401 "%s %s: unsupported hostname in restrict URI \"%s\"", 402 c->argv[0], c->argv[1], arg); 403 rc = ARG_BAD_CONF; 404 goto done; 405 } 406 407 if ( ap.restrict_lud->lud_attrs != NULL ) { 408 if ( ap.restrict_lud->lud_attrs[0] != '\0' ) { 409 snprintf( c->cr_msg, sizeof( c->cr_msg ), 410 "%s %s: attrs not allowed in restrict URI %s\n", 411 c->argv[0], c->argv[1], arg); 412 rc = ARG_BAD_CONF; 413 goto done; 414 } 415 ldap_memvfree((void *)ap.restrict_lud->lud_attrs); 416 ap.restrict_lud->lud_attrs = NULL; 417 } 418 419 if (ap.restrict_lud->lud_dn != NULL) { 420 if (ap.restrict_lud->lud_dn[0] == '\0') { 421 ldap_memfree(ap.restrict_lud->lud_dn); 422 ap.restrict_lud->lud_dn = NULL; 423 424 } else { 425 struct berval dn, ndn; 426 int j; 427 428 ber_str2bv(ap.restrict_lud->lud_dn, 0, 0, &dn); 429 if (dnNormalize(0, NULL, NULL, &dn, &ndn, NULL)) { 430 /* cleanup */ 431 snprintf( c->cr_msg, sizeof( c->cr_msg ), 432 "%s %s: restrict URI %s DN normalization failed", 433 c->argv[0], c->argv[1], arg ); 434 rc = ARG_BAD_CONF; 435 goto done; 436 } 437 438 assert(c->be != NULL); 439 if (c->be->be_nsuffix == NULL) { 440 snprintf( c->cr_msg, sizeof( c->cr_msg ), 441 "%s %s: restrict URI requires suffix", 442 c->argv[0], c->argv[1] ); 443 rc = ARG_BAD_CONF; 444 goto done; 445 } 446 447 for ( j = 0; !BER_BVISNULL(&c->be->be_nsuffix[j]); j++) { 448 if (dnIsSuffix(&ndn, &c->be->be_nsuffix[j])) break; 449 } 450 451 if (BER_BVISNULL(&c->be->be_nsuffix[j])) { 452 /* error */ 453 snprintf( c->cr_msg, sizeof( c->cr_msg ), 454 "%s %s: restrict URI DN %s not within database naming context(s)", 455 c->argv[0], c->argv[1], dn.bv_val ); 456 rc = ARG_BAD_CONF; 457 goto done; 458 } 459 460 ap.restrict_ndn = ndn; 461 } 462 } 463 464 if (ap.restrict_lud->lud_filter != NULL) { 465 ap.restrict_filter = str2filter(ap.restrict_lud->lud_filter); 466 if (ap.restrict_filter == NULL) { 467 /* error */ 468 snprintf( c->cr_msg, sizeof( c->cr_msg ), 469 "%s %s: restrict URI filter %s invalid", 470 c->argv[0], c->argv[1], ap.restrict_lud->lud_filter ); 471 rc = ARG_BAD_CONF; 472 goto done; 473 } 474 } 475 476 ber_str2bv(c->argv[argidx], 0, 1, &ap.restrict_val); 477 478 } else { 479 /* cleanup */ 480 snprintf( c->cr_msg, sizeof( c->cr_msg ), 481 "%s %s: unrecognized arg #%d (%s)", 482 c->argv[0], c->argv[1], argidx, c->argv[argidx] ); 483 rc = ARG_BAD_CONF; 484 goto done; 485 } 486 } 487 } 488 489 done:; 490 if ( rc == LDAP_SUCCESS ) { 491 constraint *a2 = ch_calloc( sizeof(constraint), 1 ); 492 a2->ap_next = on->on_bi.bi_private; 493 a2->ap = ap.ap; 494 a2->re = ap.re; 495 a2->val = ap.val; 496 a2->lud = ap.lud; 497 a2->set = ap.set; 498 a2->size = ap.size; 499 a2->count = ap.count; 500 if ( a2->lud ) { 501 ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn); 502 ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter); 503 } 504 a2->attrs = ap.attrs; 505 a2->restrict_lud = ap.restrict_lud; 506 a2->restrict_ndn = ap.restrict_ndn; 507 a2->restrict_filter = ap.restrict_filter; 508 a2->restrict_val = ap.restrict_val; 509 on->on_bi.bi_private = a2; 510 511 } else { 512 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, 513 "%s: %s\n", c->log, c->cr_msg, 0 ); 514 constraint_free( &ap, 0 ); 515 } 516 517 ldap_memvfree((void**)attrs); 518 } break; 519 default: 520 abort(); 521 break; 522 } 523 break; 524 default: 525 abort(); 526 } 527 528 return rc; 529 } 530 531 static int 532 constraint_uri_cb( Operation *op, SlapReply *rs ) 533 { 534 if(rs->sr_type == REP_SEARCH) { 535 int *foundp = op->o_callback->sc_private; 536 537 *foundp = 1; 538 539 Debug(LDAP_DEBUG_TRACE, "==> constraint_uri_cb <%s>\n", 540 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0); 541 } 542 return 0; 543 } 544 545 static int 546 constraint_violation( constraint *c, struct berval *bv, Operation *op, SlapReply *rs) 547 { 548 if ((!c) || (!bv)) return LDAP_SUCCESS; 549 550 if ((c->re) && 551 (regexec(c->re, bv->bv_val, 0, NULL, 0) == REG_NOMATCH)) 552 return LDAP_CONSTRAINT_VIOLATION; /* regular expression violation */ 553 554 if ((c->size) && (bv->bv_len > c->size)) 555 return LDAP_CONSTRAINT_VIOLATION; /* size violation */ 556 557 if (c->lud) { 558 Operation nop = *op; 559 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 560 slap_callback cb; 561 SlapReply nrs = { REP_RESULT }; 562 int i; 563 int found; 564 int rc; 565 size_t len; 566 struct berval filterstr; 567 char *ptr; 568 569 found = 0; 570 571 nrs.sr_entry = NULL; 572 nrs.sr_nentries = 0; 573 574 cb.sc_next = NULL; 575 cb.sc_response = constraint_uri_cb; 576 cb.sc_cleanup = NULL; 577 cb.sc_private = &found; 578 579 nop.o_protocol = LDAP_VERSION3; 580 nop.o_tag = LDAP_REQ_SEARCH; 581 nop.o_time = slap_get_time(); 582 if (c->lud->lud_dn) { 583 struct berval dn; 584 585 ber_str2bv(c->lud->lud_dn, 0, 0, &dn); 586 nop.o_req_dn = dn; 587 nop.o_req_ndn = dn; 588 nop.o_bd = select_backend(&nop.o_req_ndn, 1 ); 589 if (!nop.o_bd) { 590 return LDAP_NO_SUCH_OBJECT; /* unexpected error */ 591 } 592 if (!nop.o_bd->be_search) { 593 return LDAP_OTHER; /* unexpected error */ 594 } 595 } else { 596 nop.o_req_dn = nop.o_bd->be_nsuffix[0]; 597 nop.o_req_ndn = nop.o_bd->be_nsuffix[0]; 598 nop.o_bd = on->on_info->oi_origdb; 599 } 600 nop.o_do_not_cache = 1; 601 nop.o_callback = &cb; 602 603 nop.ors_scope = c->lud->lud_scope; 604 nop.ors_deref = LDAP_DEREF_NEVER; 605 nop.ors_slimit = SLAP_NO_LIMIT; 606 nop.ors_tlimit = SLAP_NO_LIMIT; 607 nop.ors_limit = NULL; 608 609 nop.ors_attrsonly = 0; 610 nop.ors_attrs = slap_anlist_no_attrs; 611 612 len = STRLENOF("(&(") + 613 c->filter.bv_len + 614 STRLENOF(")(|"); 615 616 for (i = 0; c->attrs[i]; i++) { 617 len += STRLENOF("(") + 618 c->attrs[i]->ad_cname.bv_len + 619 STRLENOF("=") + 620 bv->bv_len + 621 STRLENOF(")"); 622 } 623 624 len += STRLENOF("))"); 625 filterstr.bv_len = len; 626 filterstr.bv_val = op->o_tmpalloc(len + 1, op->o_tmpmemctx); 627 628 ptr = filterstr.bv_val + 629 snprintf(filterstr.bv_val, len, "(&(%s)(|", c->lud->lud_filter); 630 for (i = 0; c->attrs[i]; i++) { 631 *ptr++ = '('; 632 ptr = lutil_strcopy( ptr, c->attrs[i]->ad_cname.bv_val ); 633 *ptr++ = '='; 634 ptr = lutil_strcopy( ptr, bv->bv_val ); 635 *ptr++ = ')'; 636 } 637 *ptr++ = ')'; 638 *ptr++ = ')'; 639 *ptr++ = '\0'; 640 641 nop.ors_filterstr = filterstr; 642 nop.ors_filter = str2filter_x(&nop, filterstr.bv_val); 643 if ( nop.ors_filter == NULL ) { 644 Debug( LDAP_DEBUG_ANY, 645 "%s constraint_violation uri filter=\"%s\" invalid\n", 646 op->o_log_prefix, filterstr.bv_val, 0 ); 647 rc = LDAP_OTHER; 648 649 } else { 650 Debug(LDAP_DEBUG_TRACE, 651 "==> constraint_violation uri filter = %s\n", 652 filterstr.bv_val, 0, 0); 653 654 rc = nop.o_bd->be_search( &nop, &nrs ); 655 656 Debug(LDAP_DEBUG_TRACE, 657 "==> constraint_violation uri rc = %d, found = %d\n", 658 rc, found, 0); 659 } 660 op->o_tmpfree(filterstr.bv_val, op->o_tmpmemctx); 661 662 if ((rc != LDAP_SUCCESS) && (rc != LDAP_NO_SUCH_OBJECT)) { 663 return rc; /* unexpected error */ 664 } 665 666 if (!found) 667 return LDAP_CONSTRAINT_VIOLATION; /* constraint violation */ 668 669 } 670 671 return LDAP_SUCCESS; 672 } 673 674 static char * 675 print_message( struct berval *errtext, AttributeDescription *a ) 676 { 677 char *ret; 678 int sz; 679 680 sz = errtext->bv_len + sizeof(" on ") + a->ad_cname.bv_len; 681 ret = ch_malloc(sz); 682 snprintf( ret, sz, "%s on %s", errtext->bv_val, a->ad_cname.bv_val ); 683 return ret; 684 } 685 686 static unsigned 687 constraint_count_attr(Entry *e, AttributeDescription *ad) 688 { 689 struct Attribute *a; 690 691 if ((a = attr_find(e->e_attrs, ad)) != NULL) 692 return a->a_numvals; 693 return 0; 694 } 695 696 static int 697 constraint_check_restrict( Operation *op, constraint *c, Entry *e ) 698 { 699 assert( c->restrict_lud != NULL ); 700 701 if ( c->restrict_lud->lud_dn != NULL ) { 702 int diff = e->e_nname.bv_len - c->restrict_ndn.bv_len; 703 704 if ( diff < 0 ) { 705 return 0; 706 } 707 708 if ( c->restrict_lud->lud_scope == LDAP_SCOPE_BASE ) { 709 return bvmatch( &e->e_nname, &c->restrict_ndn ); 710 } 711 712 if ( !dnIsSuffix( &e->e_nname, &c->restrict_ndn ) ) { 713 return 0; 714 } 715 716 if ( c->restrict_lud->lud_scope != LDAP_SCOPE_SUBTREE ) { 717 struct berval pdn; 718 719 if ( diff == 0 ) { 720 return 0; 721 } 722 723 dnParent( &e->e_nname, &pdn ); 724 725 if ( c->restrict_lud->lud_scope == LDAP_SCOPE_ONELEVEL 726 && pdn.bv_len != c->restrict_ndn.bv_len ) 727 { 728 return 0; 729 } 730 } 731 } 732 733 if ( c->restrict_filter != NULL ) { 734 int rc; 735 struct berval save_dn = op->o_dn, save_ndn = op->o_ndn; 736 737 op->o_dn = op->o_bd->be_rootdn; 738 op->o_ndn = op->o_bd->be_rootndn; 739 rc = test_filter( op, e, c->restrict_filter ); 740 op->o_dn = save_dn; 741 op->o_ndn = save_ndn; 742 743 if ( rc != LDAP_COMPARE_TRUE ) { 744 return 0; 745 } 746 } 747 748 return 1; 749 } 750 751 static int 752 constraint_add( Operation *op, SlapReply *rs ) 753 { 754 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 755 Attribute *a; 756 constraint *c = on->on_bi.bi_private, *cp; 757 BerVarray b = NULL; 758 int i; 759 struct berval rsv = BER_BVC("add breaks constraint"); 760 int rc; 761 char *msg = NULL; 762 763 if (get_relax(op)) { 764 return SLAP_CB_CONTINUE; 765 } 766 767 if ((a = op->ora_e->e_attrs) == NULL) { 768 op->o_bd->bd_info = (BackendInfo *)(on->on_info); 769 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX, 770 "constraint_add: no attrs"); 771 return(rs->sr_err); 772 } 773 774 for(; a; a = a->a_next ) { 775 /* we don't constrain operational attributes */ 776 if (is_at_operational(a->a_desc->ad_type)) continue; 777 778 for(cp = c; cp; cp = cp->ap_next) { 779 int j; 780 for (j = 0; cp->ap[j]; j++) { 781 if (cp->ap[j] == a->a_desc) break; 782 } 783 if (cp->ap[j] == NULL) continue; 784 if ((b = a->a_vals) == NULL) continue; 785 786 if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, op->ora_e) == 0) { 787 continue; 788 } 789 790 Debug(LDAP_DEBUG_TRACE, 791 "==> constraint_add, " 792 "a->a_numvals = %u, cp->count = %lu\n", 793 a->a_numvals, (unsigned long) cp->count, 0); 794 795 if ((cp->count != 0) && (a->a_numvals > cp->count)) { 796 rc = LDAP_CONSTRAINT_VIOLATION; 797 goto add_violation; 798 } 799 800 for ( i = 0; b[i].bv_val; i++ ) { 801 rc = constraint_violation( cp, &b[i], op, rs ); 802 if ( rc ) { 803 goto add_violation; 804 } 805 } 806 807 if (cp->set && acl_match_set(&cp->val, op, op->ora_e, NULL) == 0) { 808 rc = LDAP_CONSTRAINT_VIOLATION; 809 goto add_violation; /* constraint violation */ 810 } 811 812 } 813 } 814 815 /* Default is to just fall through to the normal processing */ 816 return SLAP_CB_CONTINUE; 817 818 add_violation: 819 op->o_bd->bd_info = (BackendInfo *)(on->on_info); 820 if (rc == LDAP_CONSTRAINT_VIOLATION ) { 821 msg = print_message( &rsv, a->a_desc ); 822 } 823 send_ldap_error(op, rs, rc, msg ); 824 ch_free(msg); 825 return (rs->sr_err); 826 } 827 828 829 static int 830 constraint_update( Operation *op, SlapReply *rs ) 831 { 832 slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; 833 Backend *be = op->o_bd; 834 constraint *c = on->on_bi.bi_private, *cp; 835 Entry *target_entry = NULL, *target_entry_copy = NULL; 836 Modifications *modlist, *m; 837 BerVarray b = NULL; 838 int i; 839 struct berval rsv = BER_BVC("modify breaks constraint"); 840 int rc; 841 char *msg = NULL; 842 843 if (get_relax(op)) { 844 return SLAP_CB_CONTINUE; 845 } 846 847 switch ( op->o_tag ) { 848 case LDAP_REQ_MODIFY: 849 modlist = op->orm_modlist; 850 break; 851 852 case LDAP_REQ_MODRDN: 853 modlist = op->orr_modlist; 854 break; 855 856 default: 857 /* impossible! assert? */ 858 return LDAP_OTHER; 859 } 860 861 Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_update()\n", 0,0,0); 862 if ((m = modlist) == NULL) { 863 op->o_bd->bd_info = (BackendInfo *)(on->on_info); 864 send_ldap_error(op, rs, LDAP_INVALID_SYNTAX, 865 "constraint_update() got null modlist"); 866 return(rs->sr_err); 867 } 868 869 /* Do we need to count attributes? */ 870 for(cp = c; cp; cp = cp->ap_next) { 871 if (cp->count != 0 || cp->set || cp->restrict_lud != 0) { 872 op->o_bd = on->on_info->oi_origdb; 873 rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry ); 874 op->o_bd = be; 875 876 if (rc != 0 || target_entry == NULL) { 877 Debug(LDAP_DEBUG_TRACE, 878 "==> constraint_update rc = %d DN=\"%s\"%s\n", 879 rc, op->o_req_ndn.bv_val, 880 target_entry ? "" : " not found" ); 881 if ( rc == 0 ) 882 rc = LDAP_CONSTRAINT_VIOLATION; 883 goto mod_violation; 884 } 885 break; 886 } 887 } 888 889 rc = LDAP_CONSTRAINT_VIOLATION; 890 for(;m; m = m->sml_next) { 891 unsigned ce = 0; 892 893 if (is_at_operational( m->sml_desc->ad_type )) continue; 894 895 if ((( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_ADD) && 896 (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_REPLACE) && 897 (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_DELETE)) 898 continue; 899 /* we only care about ADD and REPLACE modifications */ 900 /* and DELETE are used to track attribute count */ 901 if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL)) 902 continue; 903 904 /* Get this attribute count, if needed */ 905 if (target_entry) 906 ce = constraint_count_attr(target_entry, m->sml_desc); 907 908 for(cp = c; cp; cp = cp->ap_next) { 909 int j; 910 for (j = 0; cp->ap[j]; j++) { 911 if (cp->ap[j] == m->sml_desc) { 912 break; 913 } 914 } 915 if (cp->ap[j] == NULL) continue; 916 917 if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, target_entry) == 0) { 918 continue; 919 } 920 921 if (cp->count != 0) { 922 unsigned ca; 923 924 if (m->sml_op == LDAP_MOD_DELETE) 925 ce = 0; 926 927 for (ca = 0; b[ca].bv_val; ++ca); 928 929 Debug(LDAP_DEBUG_TRACE, 930 "==> constraint_update ce = %u, " 931 "ca = %u, cp->count = %lu\n", 932 ce, ca, (unsigned long) cp->count); 933 934 if (m->sml_op == LDAP_MOD_ADD) { 935 if (ca + ce > cp->count) { 936 rc = LDAP_CONSTRAINT_VIOLATION; 937 goto mod_violation; 938 } 939 } 940 if (m->sml_op == LDAP_MOD_REPLACE) { 941 if (ca > cp->count) { 942 rc = LDAP_CONSTRAINT_VIOLATION; 943 goto mod_violation; 944 } 945 ce = ca; 946 } 947 } 948 949 /* DELETE are to be ignored beyond this point */ 950 if (( m->sml_op & LDAP_MOD_OP ) == LDAP_MOD_DELETE) 951 continue; 952 953 for ( i = 0; b[i].bv_val; i++ ) { 954 rc = constraint_violation( cp, &b[i], op, rs ); 955 if ( rc ) { 956 goto mod_violation; 957 } 958 } 959 960 if (cp->set && target_entry) { 961 if (target_entry_copy == NULL) { 962 Modifications *ml; 963 964 target_entry_copy = entry_dup(target_entry); 965 966 /* if rename, set the new entry's name 967 * (in normalized form only) */ 968 if ( op->o_tag == LDAP_REQ_MODRDN ) { 969 struct berval pdn, ndn = BER_BVNULL; 970 971 if ( op->orr_nnewSup ) { 972 pdn = *op->orr_nnewSup; 973 974 } else { 975 dnParent( &target_entry_copy->e_nname, &pdn ); 976 } 977 978 build_new_dn( &ndn, &pdn, &op->orr_nnewrdn, NULL ); 979 980 ber_memfree( target_entry_copy->e_nname.bv_val ); 981 target_entry_copy->e_nname = ndn; 982 ber_bvreplace( &target_entry_copy->e_name, &ndn ); 983 } 984 985 /* apply modifications, in an attempt 986 * to estimate what the entry would 987 * look like in case all modifications 988 * pass */ 989 for ( ml = modlist; ml; ml = ml->sml_next ) { 990 Modification *mod = &ml->sml_mod; 991 const char *text; 992 char textbuf[SLAP_TEXT_BUFLEN]; 993 size_t textlen = sizeof(textbuf); 994 int err; 995 996 switch ( mod->sm_op ) { 997 case LDAP_MOD_ADD: 998 err = modify_add_values( target_entry_copy, 999 mod, get_permissiveModify(op), 1000 &text, textbuf, textlen ); 1001 break; 1002 1003 case LDAP_MOD_DELETE: 1004 err = modify_delete_values( target_entry_copy, 1005 mod, get_permissiveModify(op), 1006 &text, textbuf, textlen ); 1007 break; 1008 1009 case LDAP_MOD_REPLACE: 1010 err = modify_replace_values( target_entry_copy, 1011 mod, get_permissiveModify(op), 1012 &text, textbuf, textlen ); 1013 break; 1014 1015 case LDAP_MOD_INCREMENT: 1016 err = modify_increment_values( target_entry_copy, 1017 mod, get_permissiveModify(op), 1018 &text, textbuf, textlen ); 1019 break; 1020 1021 case SLAP_MOD_SOFTADD: 1022 mod->sm_op = LDAP_MOD_ADD; 1023 err = modify_add_values( target_entry_copy, 1024 mod, get_permissiveModify(op), 1025 &text, textbuf, textlen ); 1026 mod->sm_op = SLAP_MOD_SOFTADD; 1027 if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) { 1028 err = LDAP_SUCCESS; 1029 } 1030 break; 1031 1032 default: 1033 err = LDAP_OTHER; 1034 break; 1035 } 1036 1037 if ( err != LDAP_SUCCESS ) { 1038 rc = err; 1039 goto mod_violation; 1040 } 1041 } 1042 } 1043 1044 if ( acl_match_set(&cp->val, op, target_entry_copy, NULL) == 0) { 1045 rc = LDAP_CONSTRAINT_VIOLATION; 1046 goto mod_violation; 1047 } 1048 } 1049 } 1050 } 1051 1052 if (target_entry) { 1053 op->o_bd = on->on_info->oi_origdb; 1054 be_entry_release_r(op, target_entry); 1055 op->o_bd = be; 1056 } 1057 1058 if (target_entry_copy) { 1059 entry_free(target_entry_copy); 1060 } 1061 1062 return SLAP_CB_CONTINUE; 1063 1064 mod_violation: 1065 /* violation */ 1066 if (target_entry) { 1067 op->o_bd = on->on_info->oi_origdb; 1068 be_entry_release_r(op, target_entry); 1069 op->o_bd = be; 1070 } 1071 1072 if (target_entry_copy) { 1073 entry_free(target_entry_copy); 1074 } 1075 1076 op->o_bd->bd_info = (BackendInfo *)(on->on_info); 1077 if ( rc == LDAP_CONSTRAINT_VIOLATION ) { 1078 msg = print_message( &rsv, m->sml_desc ); 1079 } 1080 send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, msg ); 1081 ch_free(msg); 1082 return (rs->sr_err); 1083 } 1084 1085 static int 1086 constraint_close( 1087 BackendDB *be, 1088 ConfigReply *cr ) 1089 { 1090 slap_overinst *on = (slap_overinst *) be->bd_info; 1091 constraint *ap, *a2; 1092 1093 for ( ap = on->on_bi.bi_private; ap; ap = a2 ) { 1094 a2 = ap->ap_next; 1095 constraint_free( ap, 1 ); 1096 } 1097 1098 return 0; 1099 } 1100 1101 static slap_overinst constraint_ovl; 1102 1103 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC 1104 static 1105 #endif 1106 int 1107 constraint_initialize( void ) { 1108 int rc; 1109 1110 constraint_ovl.on_bi.bi_type = "constraint"; 1111 constraint_ovl.on_bi.bi_db_close = constraint_close; 1112 constraint_ovl.on_bi.bi_op_add = constraint_add; 1113 constraint_ovl.on_bi.bi_op_modify = constraint_update; 1114 constraint_ovl.on_bi.bi_op_modrdn = constraint_update; 1115 1116 constraint_ovl.on_bi.bi_private = NULL; 1117 1118 constraint_ovl.on_bi.bi_cf_ocs = constraintocs; 1119 rc = config_register_schema( constraintcfg, constraintocs ); 1120 if (rc) return rc; 1121 1122 return overlay_register( &constraint_ovl ); 1123 } 1124 1125 #if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC 1126 int init_module(int argc, char *argv[]) { 1127 return constraint_initialize(); 1128 } 1129 #endif 1130 1131 #endif /* defined(SLAPD_OVER_CONSTRAINT) */ 1132 1133