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