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