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