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