1/* $NetBSD: modify.c,v 1.3 2021/08/14 16:14:58 christos Exp $ */ 2 3/* $OpenLDAP$ */ 4/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2021 The OpenLDAP Foundation. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17/* Portions Copyright (c) 1995 Regents of the University of Michigan. 18 * All rights reserved. 19 * 20 * Redistribution and use in source and binary forms are permitted 21 * provided that this notice is preserved and that due credit is given 22 * to the University of Michigan at Ann Arbor. The name of the University 23 * may not be used to endorse or promote products derived from this 24 * software without specific prior written permission. This software 25 * is provided ``as is'' without express or implied warranty. 26 */ 27 28#include <sys/cdefs.h> 29__RCSID("$NetBSD: modify.c,v 1.3 2021/08/14 16:14:58 christos Exp $"); 30 31#include "portable.h" 32 33#include <stdio.h> 34 35#include <ac/socket.h> 36#include <ac/string.h> 37#include <ac/time.h> 38 39#include "slap.h" 40#include "lutil.h" 41 42 43int 44do_modify( 45 Operation *op, 46 SlapReply *rs ) 47{ 48 struct berval dn = BER_BVNULL; 49 char textbuf[ SLAP_TEXT_BUFLEN ]; 50 size_t textlen = sizeof( textbuf ); 51#ifdef LDAP_DEBUG 52 Modifications *tmp; 53#endif 54 55 Debug( LDAP_DEBUG_TRACE, "%s do_modify\n", 56 op->o_log_prefix ); 57 /* 58 * Parse the modify request. It looks like this: 59 * 60 * ModifyRequest := [APPLICATION 6] SEQUENCE { 61 * name DistinguishedName, 62 * mods SEQUENCE OF SEQUENCE { 63 * operation ENUMERATED { 64 * add (0), 65 * delete (1), 66 * replace (2) 67 * }, 68 * modification SEQUENCE { 69 * type AttributeType, 70 * values SET OF AttributeValue 71 * } 72 * } 73 * } 74 */ 75 76 if ( ber_scanf( op->o_ber, "{m" /*}*/, &dn ) == LBER_ERROR ) { 77 Debug( LDAP_DEBUG_ANY, "%s do_modify: ber_scanf failed\n", 78 op->o_log_prefix ); 79 send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" ); 80 return SLAPD_DISCONNECT; 81 } 82 83 Debug( LDAP_DEBUG_ARGS, "%s do_modify: dn (%s)\n", 84 op->o_log_prefix, dn.bv_val ); 85 86 rs->sr_err = slap_parse_modlist( op, rs, op->o_ber, &op->oq_modify ); 87 if ( rs->sr_err != LDAP_SUCCESS ) { 88 Debug( LDAP_DEBUG_ANY, "%s do_modify: slap_parse_modlist failed err=%d msg=%s\n", 89 op->o_log_prefix, rs->sr_err, rs->sr_text ); 90 send_ldap_result( op, rs ); 91 goto cleanup; 92 } 93 94 if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) { 95 Debug( LDAP_DEBUG_ANY, "%s do_modify: get_ctrls failed\n", 96 op->o_log_prefix ); 97 /* get_ctrls has sent results. Now clean up. */ 98 goto cleanup; 99 } 100 101 rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn, 102 op->o_tmpmemctx ); 103 if( rs->sr_err != LDAP_SUCCESS ) { 104 Debug( LDAP_DEBUG_ANY, "%s do_modify: invalid dn (%s)\n", 105 op->o_log_prefix, dn.bv_val ); 106 send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" ); 107 goto cleanup; 108 } 109 110 op->orm_no_opattrs = 0; 111 112#ifdef LDAP_DEBUG 113 Debug( LDAP_DEBUG_ARGS, "%s modifications:\n", 114 op->o_log_prefix ); 115 116 for ( tmp = op->orm_modlist; tmp != NULL; tmp = tmp->sml_next ) { 117 Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n", 118 tmp->sml_op == LDAP_MOD_ADD ? "add" : 119 (tmp->sml_op == LDAP_MOD_INCREMENT ? "increment" : 120 (tmp->sml_op == LDAP_MOD_DELETE ? "delete" : 121 "replace")), tmp->sml_type.bv_val ); 122 123 if ( tmp->sml_values == NULL ) { 124 Debug( LDAP_DEBUG_ARGS, "\t\tno values\n" ); 125 } else if ( BER_BVISNULL( &tmp->sml_values[ 0 ] ) ) { 126 Debug( LDAP_DEBUG_ARGS, "\t\tzero values\n" ); 127 } else if ( BER_BVISNULL( &tmp->sml_values[ 1 ] ) ) { 128 Debug( LDAP_DEBUG_ARGS, "\t\tone value, length %ld\n", 129 (long) tmp->sml_values[0].bv_len ); 130 } else { 131 Debug( LDAP_DEBUG_ARGS, "\t\tmultiple values\n" ); 132 } 133 } 134 135 if (LogTest( LDAP_DEBUG_STATS ) ) { 136 char abuf[BUFSIZ/2], *ptr = abuf; 137 int len = 0; 138 139 Debug( LDAP_DEBUG_STATS, "%s MOD dn=\"%s\"\n", 140 op->o_log_prefix, op->o_req_dn.bv_val ); 141 142 for ( tmp = op->orm_modlist; tmp != NULL; tmp = tmp->sml_next ) { 143 if (len + 1 + tmp->sml_type.bv_len > sizeof(abuf)) { 144 Debug( LDAP_DEBUG_STATS, "%s MOD attr=%s\n", 145 op->o_log_prefix, abuf ); 146 147 len = 0; 148 ptr = abuf; 149 150 if( 1 + tmp->sml_type.bv_len > sizeof(abuf)) { 151 Debug( LDAP_DEBUG_STATS, "%s MOD attr=%s\n", 152 op->o_log_prefix, tmp->sml_type.bv_val ); 153 continue; 154 } 155 } 156 if (len) { 157 *ptr++ = ' '; 158 len++; 159 } 160 ptr = lutil_strcopy(ptr, tmp->sml_type.bv_val); 161 len += tmp->sml_type.bv_len; 162 } 163 if (len) { 164 Debug( LDAP_DEBUG_STATS, "%s MOD attr=%s\n", 165 op->o_log_prefix, abuf ); 166 } 167 } 168#endif /* LDAP_DEBUG */ 169 170 rs->sr_err = slap_mods_check( op, op->orm_modlist, 171 &rs->sr_text, textbuf, textlen, NULL ); 172 173 if ( rs->sr_err != LDAP_SUCCESS ) { 174 send_ldap_result( op, rs ); 175 goto cleanup; 176 } 177 178 op->o_bd = frontendDB; 179 rs->sr_err = frontendDB->be_modify( op, rs ); 180 if ( rs->sr_err == SLAPD_ASYNCOP ) { 181 /* skip cleanup */ 182 return rs->sr_err; 183 } 184 185 if( rs->sr_err == LDAP_TXN_SPECIFY_OKAY ) { 186 /* skip cleanup */ 187 return rs->sr_err; 188 } 189 190cleanup: 191 op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx ); 192 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx ); 193 if ( op->orm_modlist != NULL ) slap_mods_free( op->orm_modlist, 1 ); 194 195 return rs->sr_err; 196} 197 198int 199fe_op_modify( Operation *op, SlapReply *rs ) 200{ 201 BackendDB *op_be, *bd = op->o_bd; 202 char textbuf[ SLAP_TEXT_BUFLEN ]; 203 size_t textlen = sizeof( textbuf ); 204 205 if ( BER_BVISEMPTY( &op->o_req_ndn ) ) { 206 Debug( LDAP_DEBUG_ANY, "%s do_modify: root dse!\n", 207 op->o_log_prefix ); 208 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 209 "modify upon the root DSE not supported" ); 210 goto cleanup; 211 212 } else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) { 213 Debug( LDAP_DEBUG_ANY, "%s do_modify: subschema subentry!\n", 214 op->o_log_prefix ); 215 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 216 "modification of subschema subentry not supported" ); 217 goto cleanup; 218 } 219 220 /* 221 * We could be serving multiple database backends. Select the 222 * appropriate one, or send a referral to our "referral server" 223 * if we don't hold it. 224 */ 225 op->o_bd = select_backend( &op->o_req_ndn, 1 ); 226 if ( op->o_bd == NULL ) { 227 op->o_bd = bd; 228 rs->sr_ref = referral_rewrite( default_referral, 229 NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 230 if ( !rs->sr_ref ) { 231 rs->sr_ref = default_referral; 232 } 233 234 if ( rs->sr_ref != NULL ) { 235 rs->sr_err = LDAP_REFERRAL; 236 send_ldap_result( op, rs ); 237 238 if ( rs->sr_ref != default_referral ) { 239 ber_bvarray_free( rs->sr_ref ); 240 } 241 242 } else { 243 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 244 "no global superior knowledge" ); 245 } 246 goto cleanup; 247 } 248 249 /* If we've got a glued backend, check the real backend */ 250 op_be = op->o_bd; 251 if ( SLAP_GLUE_INSTANCE( op->o_bd )) { 252 op->o_bd = select_backend( &op->o_req_ndn, 0 ); 253 } 254 255 /* check restrictions */ 256 if ( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) { 257 send_ldap_result( op, rs ); 258 goto cleanup; 259 } 260 261 /* check for referrals */ 262 if ( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) { 263 goto cleanup; 264 } 265 266 rs->sr_err = slap_mods_obsolete_check( op, op->orm_modlist, 267 &rs->sr_text, textbuf, textlen ); 268 if ( rs->sr_err != LDAP_SUCCESS ) { 269 send_ldap_result( op, rs ); 270 goto cleanup; 271 } 272 273 /* check for modify/increment support */ 274 if ( op->orm_increment && !SLAP_INCREMENT( op->o_bd ) ) { 275 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 276 "modify/increment not supported in context" ); 277 goto cleanup; 278 } 279 280 /* 281 * do the modify if 1 && (2 || 3) 282 * 1) there is a modify function implemented in this backend; 283 * 2) this backend is the provider for what it holds; 284 * 3) it's a replica and the dn supplied is the update_ndn. 285 */ 286 if ( op->o_bd->be_modify ) { 287 /* do the update here */ 288 int repl_user = be_isupdate( op ); 289 290 /* 291 * Multimaster slapd does not have to check for replicator dn 292 * because it accepts each modify request 293 */ 294 if ( !SLAP_SINGLE_SHADOW(op->o_bd) || repl_user ) { 295 int update = !BER_BVISEMPTY( &op->o_bd->be_update_ndn ); 296 297 if ( !update ) { 298 rs->sr_err = slap_mods_no_user_mod_check( op, op->orm_modlist, 299 &rs->sr_text, textbuf, textlen ); 300 if ( rs->sr_err != LDAP_SUCCESS ) { 301 send_ldap_result( op, rs ); 302 goto cleanup; 303 } 304 } 305 if ( op->o_txnSpec ) { 306 txn_preop( op, rs ); 307 goto cleanup; 308 } 309 op->o_bd = op_be; 310 op->o_bd->be_modify( op, rs ); 311 312 } else { /* send a referral */ 313 BerVarray defref = op->o_bd->be_update_refs 314 ? op->o_bd->be_update_refs : default_referral; 315 if ( defref != NULL ) { 316 rs->sr_ref = referral_rewrite( defref, 317 NULL, &op->o_req_dn, 318 LDAP_SCOPE_DEFAULT ); 319 if ( rs->sr_ref == NULL ) { 320 /* FIXME: must duplicate, because 321 * overlays may muck with it */ 322 rs->sr_ref = defref; 323 } 324 rs->sr_err = LDAP_REFERRAL; 325 send_ldap_result( op, rs ); 326 if ( rs->sr_ref != defref ) { 327 ber_bvarray_free( rs->sr_ref ); 328 } 329 330 } else { 331 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 332 "shadow context; no update referral" ); 333 } 334 } 335 336 } else { 337 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 338 "operation not supported within namingContext" ); 339 } 340 341cleanup:; 342 op->o_bd = bd; 343 return rs->sr_err; 344} 345 346/* 347 * Obsolete constraint checking. 348 */ 349int 350slap_mods_obsolete_check( 351 Operation *op, 352 Modifications *ml, 353 const char **text, 354 char *textbuf, 355 size_t textlen ) 356{ 357 if( get_relax( op ) ) return LDAP_SUCCESS; 358 359 for ( ; ml != NULL; ml = ml->sml_next ) { 360 if ( is_at_obsolete( ml->sml_desc->ad_type ) && 361 (( ml->sml_op != LDAP_MOD_REPLACE && 362 ml->sml_op != LDAP_MOD_DELETE ) || 363 ml->sml_values != NULL )) 364 { 365 /* 366 * attribute is obsolete, 367 * only allow replace/delete with no values 368 */ 369 snprintf( textbuf, textlen, 370 "%s: attribute is obsolete", 371 ml->sml_type.bv_val ); 372 *text = textbuf; 373 return LDAP_CONSTRAINT_VIOLATION; 374 } 375 } 376 377 return LDAP_SUCCESS; 378} 379 380/* 381 * No-user-modification constraint checking. 382 */ 383int 384slap_mods_no_user_mod_check( 385 Operation *op, 386 Modifications *ml, 387 const char **text, 388 char *textbuf, 389 size_t textlen ) 390{ 391 for ( ; ml != NULL; ml = ml->sml_next ) { 392 if ( !is_at_no_user_mod( ml->sml_desc->ad_type ) ) { 393 continue; 394 } 395 396 if ( ml->sml_flags & SLAP_MOD_INTERNAL ) { 397 continue; 398 } 399 400 if ( get_relax( op ) ) { 401 if ( ml->sml_desc->ad_type->sat_flags & SLAP_AT_MANAGEABLE ) { 402 ml->sml_flags |= SLAP_MOD_MANAGING; 403 continue; 404 } 405 406 /* attribute not manageable */ 407 snprintf( textbuf, textlen, 408 "%s: no-user-modification attribute not manageable", 409 ml->sml_type.bv_val ); 410 411 } else { 412 /* user modification disallowed */ 413 snprintf( textbuf, textlen, 414 "%s: no user modification allowed", 415 ml->sml_type.bv_val ); 416 } 417 418 *text = textbuf; 419 return LDAP_CONSTRAINT_VIOLATION; 420 } 421 422 return LDAP_SUCCESS; 423} 424 425int 426slap_mods_no_repl_user_mod_check( 427 Operation *op, 428 Modifications *ml, 429 const char **text, 430 char *textbuf, 431 size_t textlen ) 432{ 433 Modifications *mods; 434 Modifications *modp; 435 436 for ( mods = ml; mods != NULL; mods = mods->sml_next ) { 437 assert( mods->sml_op == LDAP_MOD_ADD ); 438 439 /* check doesn't already appear */ 440 for ( modp = ml; modp != NULL; modp = modp->sml_next ) { 441 if ( mods->sml_desc == modp->sml_desc && mods != modp ) { 442 snprintf( textbuf, textlen, 443 "attribute '%s' provided more than once", 444 mods->sml_desc->ad_cname.bv_val ); 445 *text = textbuf; 446 return LDAP_TYPE_OR_VALUE_EXISTS; 447 } 448 } 449 } 450 451 return LDAP_SUCCESS; 452} 453 454/* 455 * Do basic attribute type checking and syntax validation. 456 */ 457int slap_mods_check( 458 Operation *op, 459 Modifications *ml, 460 const char **text, 461 char *textbuf, 462 size_t textlen, 463 void *ctx ) 464{ 465 int rc; 466 467 for( ; ml != NULL; ml = ml->sml_next ) { 468 AttributeDescription *ad = NULL; 469 470 /* convert to attribute description */ 471 if ( ml->sml_desc == NULL ) { 472 rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text ); 473 if( rc != LDAP_SUCCESS ) { 474 if ( get_no_schema_check( op )) { 475 rc = slap_bv2undef_ad( &ml->sml_type, &ml->sml_desc, 476 text, 0 ); 477 } 478 } 479 if( rc != LDAP_SUCCESS ) { 480 snprintf( textbuf, textlen, "%s: %s", 481 ml->sml_type.bv_val, *text ); 482 *text = textbuf; 483 return rc; 484 } 485 } 486 487 ad = ml->sml_desc; 488 489 if( slap_syntax_is_binary( ad->ad_type->sat_syntax ) 490 && !slap_ad_is_binary( ad )) 491 { 492 /* attribute requires binary transfer */ 493 snprintf( textbuf, textlen, 494 "%s: requires ;binary transfer", 495 ml->sml_type.bv_val ); 496 *text = textbuf; 497 return LDAP_UNDEFINED_TYPE; 498 } 499 500 if( !slap_syntax_is_binary( ad->ad_type->sat_syntax ) 501 && slap_ad_is_binary( ad )) 502 { 503 /* attribute does not require binary transfer */ 504 snprintf( textbuf, textlen, 505 "%s: disallows ;binary transfer", 506 ml->sml_type.bv_val ); 507 *text = textbuf; 508 return LDAP_UNDEFINED_TYPE; 509 } 510 511 if( slap_ad_is_tag_range( ad )) { 512 /* attribute requires binary transfer */ 513 snprintf( textbuf, textlen, 514 "%s: inappropriate use of tag range option", 515 ml->sml_type.bv_val ); 516 *text = textbuf; 517 return LDAP_UNDEFINED_TYPE; 518 } 519 520#if 0 521 if ( is_at_obsolete( ad->ad_type ) && 522 (( ml->sml_op != LDAP_MOD_REPLACE && 523 ml->sml_op != LDAP_MOD_DELETE ) || 524 ml->sml_values != NULL )) 525 { 526 /* 527 * attribute is obsolete, 528 * only allow replace/delete with no values 529 */ 530 snprintf( textbuf, textlen, 531 "%s: attribute is obsolete", 532 ml->sml_type.bv_val ); 533 *text = textbuf; 534 return LDAP_CONSTRAINT_VIOLATION; 535 } 536#endif 537 538 if ( ml->sml_op == LDAP_MOD_INCREMENT && 539#ifdef SLAPD_REAL_SYNTAX 540 !is_at_syntax( ad->ad_type, SLAPD_REAL_SYNTAX ) && 541#endif 542 !is_at_syntax( ad->ad_type, SLAPD_INTEGER_SYNTAX ) ) 543 { 544 /* 545 * attribute values must be INTEGER or REAL 546 */ 547 snprintf( textbuf, textlen, 548 "%s: attribute syntax inappropriate for increment", 549 ml->sml_type.bv_val ); 550 *text = textbuf; 551 return LDAP_CONSTRAINT_VIOLATION; 552 } 553 554 /* 555 * check values 556 */ 557 if( ml->sml_values != NULL ) { 558 ber_len_t nvals; 559 slap_syntax_validate_func *validate = 560 ad->ad_type->sat_syntax->ssyn_validate; 561 slap_syntax_transform_func *pretty = 562 ad->ad_type->sat_syntax->ssyn_pretty; 563 564 if( !pretty && !validate ) { 565 *text = "no validator for syntax"; 566 snprintf( textbuf, textlen, 567 "%s: no validator for syntax %s", 568 ml->sml_type.bv_val, 569 ad->ad_type->sat_syntax->ssyn_oid ); 570 *text = textbuf; 571 return LDAP_INVALID_SYNTAX; 572 } 573 574 /* 575 * check that each value is valid per syntax 576 * and pretty if appropriate 577 */ 578 for ( nvals = 0; !BER_BVISNULL( &ml->sml_values[nvals] ); nvals++ ) { 579 struct berval pval; 580 581 if ( pretty ) { 582 rc = ordered_value_pretty( ad, 583 &ml->sml_values[nvals], &pval, ctx ); 584 } else { 585 rc = ordered_value_validate( ad, 586 &ml->sml_values[nvals], ml->sml_op ); 587 } 588 589 if( rc != 0 ) { 590 snprintf( textbuf, textlen, 591 "%s: value #%ld invalid per syntax", 592 ml->sml_type.bv_val, (long) nvals ); 593 *text = textbuf; 594 return LDAP_INVALID_SYNTAX; 595 } 596 597 if( pretty ) { 598 ber_memfree_x( ml->sml_values[nvals].bv_val, ctx ); 599 ml->sml_values[nvals] = pval; 600 } 601 } 602 ml->sml_values[nvals].bv_len = 0; 603 ml->sml_numvals = nvals; 604 605 /* 606 * a rough single value check... an additional check is needed 607 * to catch add of single value to existing single valued attribute 608 */ 609 if ((ml->sml_op == LDAP_MOD_ADD || ml->sml_op == LDAP_MOD_REPLACE) 610 && nvals > 1 && is_at_single_value( ad->ad_type )) 611 { 612 snprintf( textbuf, textlen, 613 "%s: multiple values provided", 614 ml->sml_type.bv_val ); 615 *text = textbuf; 616 return LDAP_CONSTRAINT_VIOLATION; 617 } 618 619 /* if the type has a normalizer, generate the 620 * normalized values. otherwise leave them NULL. 621 * 622 * this is different from the rule for attributes 623 * in an entry - in an attribute list, the normalized 624 * value is set equal to the non-normalized value 625 * when there is no normalizer. 626 */ 627 if( nvals && ad->ad_type->sat_equality && 628 ad->ad_type->sat_equality->smr_normalize ) 629 { 630 ml->sml_nvalues = slap_sl_malloc( 631 (nvals+1)*sizeof(struct berval), ctx ); 632 633 for ( nvals = 0; !BER_BVISNULL( &ml->sml_values[nvals] ); nvals++ ) { 634 rc = ordered_value_normalize( 635 SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX, 636 ad, 637 ad->ad_type->sat_equality, 638 &ml->sml_values[nvals], &ml->sml_nvalues[nvals], ctx ); 639 if ( rc ) { 640 Debug( LDAP_DEBUG_ANY, 641 "<= str2entry NULL (ssyn_normalize %d)\n", 642 rc ); 643 snprintf( textbuf, textlen, 644 "%s: value #%ld normalization failed", 645 ml->sml_type.bv_val, (long) nvals ); 646 *text = textbuf; 647 BER_BVZERO( &ml->sml_nvalues[nvals] ); 648 return rc; 649 } 650 } 651 652 BER_BVZERO( &ml->sml_nvalues[nvals] ); 653 } 654 655 /* check for duplicates, but ignore Deletes. 656 */ 657 if( nvals > 1 && ml->sml_op != LDAP_MOD_DELETE ) { 658 int i; 659 rc = slap_sort_vals( ml, text, &i, ctx ); 660 if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) { 661 /* value exists already */ 662 snprintf( textbuf, textlen, 663 "%s: value #%d provided more than once", 664 ml->sml_desc->ad_cname.bv_val, i ); 665 *text = textbuf; 666 } 667 if ( rc ) 668 return rc; 669 } 670 } else { 671 ml->sml_numvals = 0; 672 } 673 } 674 675 return LDAP_SUCCESS; 676} 677 678/* Sort a set of values. An (Attribute *) may be used interchangeably here 679 * instead of a (Modifications *) structure. 680 * 681 * Uses Quicksort + Insertion sort for small arrays 682 */ 683 684int 685slap_sort_vals( 686 Modifications *ml, 687 const char **text, 688 int *dup, 689 void *ctx ) 690{ 691 AttributeDescription *ad; 692 MatchingRule *mr; 693 int istack[sizeof(int)*16]; 694 int i, j, k, l, ir, jstack, match, *ix, itmp, nvals, rc = LDAP_SUCCESS; 695 int is_norm; 696 struct berval a, *cv; 697 698#define SMALL 8 699#define SWAP(a,b,tmp) tmp=(a);(a)=(b);(b)=tmp 700#define COMP(a,b) match=0; rc = ordered_value_match( &match, \ 701 ad, mr, SLAP_MR_EQUALITY \ 702 | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX \ 703 | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH \ 704 | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, \ 705 &(a), &(b), text ); 706 707#define IX(x) ix[x] 708#define EXCH(x,y) SWAP(ix[x],ix[y],itmp) 709#define SETA(x) itmp = ix[x]; a = cv[itmp] 710#define GETA(x) ix[x] = itmp; 711#define SET(x,y) ix[x] = ix[y] 712 713 ad = ml->sml_desc; 714 nvals = ml->sml_numvals; 715 if ( nvals <= 1 ) 716 goto ret; 717 718 /* For Modifications, sml_nvalues is NULL if normalization wasn't needed. 719 * For Attributes, sml_nvalues == sml_values when normalization isn't needed. 720 */ 721 if ( ml->sml_nvalues && ml->sml_nvalues != ml->sml_values ) { 722 cv = ml->sml_nvalues; 723 is_norm = 1; 724 } else { 725 cv = ml->sml_values; 726 is_norm = 0; 727 } 728 729 if ( ad == slap_schema.si_ad_objectClass ) 730 mr = NULL; /* shortcut matching */ 731 else 732 mr = ad->ad_type->sat_equality; 733 734 /* record indices to preserve input ordering */ 735 ix = slap_sl_malloc( nvals * sizeof(int), ctx ); 736 for (i=0; i<nvals; i++) ix[i] = i; 737 738 ir = nvals-1; 739 l = 0; 740 jstack = 0; 741 742 for(;;) { 743 if (ir - l < SMALL) { /* Insertion sort */ 744 match=1; 745 for (j=l+1;j<=ir;j++) { 746 SETA(j); 747 for (i=j-1;i>=0;i--) { 748 COMP(cv[IX(i)], a); 749 if ( match <= 0 ) 750 break; 751 SET(i+1,i); 752 } 753 GETA(i+1); 754 if ( match == 0 ) goto done; 755 } 756 if ( jstack == 0 ) break; 757 ir = istack[jstack--]; 758 l = istack[jstack--]; 759 } else { 760 k = (l + ir) >> 1; /* Choose median of left, center, right */ 761 EXCH(k, l+1); 762 COMP( cv[IX(l)], cv[IX(ir)] ); 763 if ( match > 0 ) { 764 EXCH(l, ir); 765 } else if ( match == 0 ) { 766 i = ir; 767 break; 768 } 769 COMP( cv[IX(l+1)], cv[IX(ir)] ); 770 if ( match > 0 ) { 771 EXCH(l+1, ir); 772 } else if ( match == 0 ) { 773 i = ir; 774 break; 775 } 776 COMP( cv[IX(l)], cv[IX(l+1)] ); 777 if ( match > 0 ) { 778 EXCH(l, l+1); 779 } else if ( match == 0 ) { 780 i = l; 781 break; 782 } 783 i = l+1; 784 j = ir; 785 a = cv[IX(i)]; 786 for(;;) { 787 do { 788 i++; 789 COMP( cv[IX(i)], a ); 790 } while( match < 0 ); 791 while( match > 0 ) { 792 j--; 793 COMP( cv[IX(j)], a ); 794 } 795 if (j < i) { 796 match = 1; 797 break; 798 } 799 if ( match == 0 ) { 800 i = l+1; 801 break; 802 } 803 EXCH(i,j); 804 } 805 if ( match == 0 ) 806 break; 807 EXCH(l+1,j); 808 jstack += 2; 809 if (ir-i+1 > j-l) { 810 istack[jstack] = ir; 811 istack[jstack-1] = i; 812 ir = j; 813 } else { 814 istack[jstack] = j; 815 istack[jstack-1] = l; 816 l = i; 817 } 818 } 819 } 820 done: 821 if ( match == 0 && i >= 0 ) 822 *dup = ix[i]; 823 824 /* For sorted attributes, put the values in index order */ 825 if ( rc == LDAP_SUCCESS && match && 826 ( ad->ad_type->sat_flags & SLAP_AT_SORTED_VAL )) { 827 BerVarray tmpv = slap_sl_malloc( sizeof( struct berval ) * nvals, ctx ); 828 for ( i = 0; i<nvals; i++ ) 829 tmpv[i] = cv[ix[i]]; 830 for ( i = 0; i<nvals; i++ ) 831 cv[i] = tmpv[i]; 832 /* Check if the non-normalized array needs to move too */ 833 if ( is_norm ) { 834 cv = ml->sml_values; 835 for ( i = 0; i<nvals; i++ ) 836 tmpv[i] = cv[ix[i]]; 837 for ( i = 0; i<nvals; i++ ) 838 cv[i] = tmpv[i]; 839 } 840 slap_sl_free( tmpv, ctx ); 841 } 842 843 slap_sl_free( ix, ctx ); 844 845 if ( rc == LDAP_SUCCESS && match == 0 ) { 846 /* value exists already */ 847 assert( i >= 0 ); 848 assert( i < nvals ); 849 rc = LDAP_TYPE_OR_VALUE_EXISTS; 850 } 851 ret: 852 return rc; 853} 854 855/* Enter with bv->bv_len = sizeof buffer, returns with 856 * actual length of string 857 */ 858void slap_timestamp( time_t *tm, struct berval *bv ) 859{ 860 struct tm ltm; 861 862 ldap_pvt_gmtime( tm, <m ); 863 864 bv->bv_len = lutil_gentime( bv->bv_val, bv->bv_len, <m ); 865} 866 867/* Called for all modify and modrdn ops. If the current op was replicated 868 * from elsewhere, all of the attrs should already be present. 869 */ 870void slap_mods_opattrs( 871 Operation *op, 872 Modifications **modsp, 873 int manage_ctxcsn ) 874{ 875 struct berval name, timestamp, csn = BER_BVNULL; 876 struct berval nname; 877 char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ]; 878 char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ]; 879 Modifications *mod, **modtail, *modlast; 880 int gotcsn = 0, gotmname = 0, gotmtime = 0; 881 882 if ( SLAP_LASTMOD( op->o_bd ) && !op->orm_no_opattrs ) { 883 char *ptr; 884 timestamp.bv_val = timebuf; 885 for ( modtail = modsp; *modtail; modtail = &(*modtail)->sml_next ) { 886 if ( (*modtail)->sml_op != LDAP_MOD_ADD && 887 (*modtail)->sml_op != SLAP_MOD_SOFTADD && 888 (*modtail)->sml_op != SLAP_MOD_ADD_IF_NOT_PRESENT && 889 (*modtail)->sml_op != LDAP_MOD_REPLACE ) 890 { 891 continue; 892 } 893 894 if ( (*modtail)->sml_desc == slap_schema.si_ad_entryCSN ) 895 { 896 csn = (*modtail)->sml_values[0]; 897 gotcsn = 1; 898 899 } else if ( (*modtail)->sml_desc == slap_schema.si_ad_modifiersName ) 900 { 901 gotmname = 1; 902 903 } else if ( (*modtail)->sml_desc == slap_schema.si_ad_modifyTimestamp ) 904 { 905 gotmtime = 1; 906 } 907 } 908 909 if ( BER_BVISEMPTY( &op->o_csn )) { 910 if ( !gotcsn ) { 911 csn.bv_val = csnbuf; 912 csn.bv_len = sizeof( csnbuf ); 913 slap_get_csn( op, &csn, manage_ctxcsn ); 914 915 } else { 916 if ( manage_ctxcsn ) { 917 slap_queue_csn( op, &csn ); 918 } 919 } 920 921 } else { 922 csn = op->o_csn; 923 } 924 925 ptr = ber_bvchr( &csn, '#' ); 926 if ( ptr ) { 927 timestamp.bv_len = STRLENOF("YYYYMMDDHHMMSSZ"); 928 AC_MEMCPY( timebuf, csn.bv_val, timestamp.bv_len ); 929 timebuf[timestamp.bv_len-1] = 'Z'; 930 timebuf[timestamp.bv_len] = '\0'; 931 932 } else { 933 time_t now = slap_get_time(); 934 935 timestamp.bv_len = sizeof(timebuf); 936 937 slap_timestamp( &now, ×tamp ); 938 } 939 940 if ( BER_BVISEMPTY( &op->o_dn ) ) { 941 BER_BVSTR( &name, SLAPD_ANONYMOUS ); 942 nname = name; 943 944 } else { 945 name = op->o_dn; 946 nname = op->o_ndn; 947 } 948 949 if ( !gotcsn ) { 950 mod = (Modifications *) ch_malloc( sizeof( Modifications ) ); 951 mod->sml_op = LDAP_MOD_REPLACE; 952 mod->sml_flags = SLAP_MOD_INTERNAL; 953 mod->sml_next = NULL; 954 BER_BVZERO( &mod->sml_type ); 955 mod->sml_desc = slap_schema.si_ad_entryCSN; 956 mod->sml_numvals = 1; 957 mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); 958 ber_dupbv( &mod->sml_values[0], &csn ); 959 BER_BVZERO( &mod->sml_values[1] ); 960 assert( !BER_BVISNULL( &mod->sml_values[0] ) ); 961 mod->sml_nvalues = NULL; 962 *modtail = mod; 963 modlast = mod; 964 modtail = &mod->sml_next; 965 } 966 967 if ( !gotmname ) { 968 mod = (Modifications *) ch_malloc( sizeof( Modifications ) ); 969 mod->sml_op = LDAP_MOD_REPLACE; 970 mod->sml_flags = SLAP_MOD_INTERNAL; 971 mod->sml_next = NULL; 972 BER_BVZERO( &mod->sml_type ); 973 mod->sml_desc = slap_schema.si_ad_modifiersName; 974 mod->sml_numvals = 1; 975 mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); 976 ber_dupbv( &mod->sml_values[0], &name ); 977 BER_BVZERO( &mod->sml_values[1] ); 978 assert( !BER_BVISNULL( &mod->sml_values[0] ) ); 979 mod->sml_nvalues = 980 (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); 981 ber_dupbv( &mod->sml_nvalues[0], &nname ); 982 BER_BVZERO( &mod->sml_nvalues[1] ); 983 assert( !BER_BVISNULL( &mod->sml_nvalues[0] ) ); 984 *modtail = mod; 985 modtail = &mod->sml_next; 986 } 987 988 if ( !gotmtime ) { 989 mod = (Modifications *) ch_malloc( sizeof( Modifications ) ); 990 mod->sml_op = LDAP_MOD_REPLACE; 991 mod->sml_flags = SLAP_MOD_INTERNAL; 992 mod->sml_next = NULL; 993 BER_BVZERO( &mod->sml_type ); 994 mod->sml_desc = slap_schema.si_ad_modifyTimestamp; 995 mod->sml_numvals = 1; 996 mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) ); 997 ber_dupbv( &mod->sml_values[0], ×tamp ); 998 BER_BVZERO( &mod->sml_values[1] ); 999 assert( !BER_BVISNULL( &mod->sml_values[0] ) ); 1000 mod->sml_nvalues = NULL; 1001 *modtail = mod; 1002 modtail = &mod->sml_next; 1003 } 1004 } 1005} 1006 1007int 1008slap_parse_modlist( 1009 Operation *op, 1010 SlapReply *rs, 1011 BerElement *ber, 1012 req_modify_s *ms ) 1013{ 1014 ber_tag_t tag; 1015 ber_len_t len; 1016 char *last; 1017 Modifications **modtail = &ms->rs_mods.rs_modlist; 1018 1019 ms->rs_mods.rs_modlist = NULL; 1020 ms->rs_increment = 0; 1021 1022 rs->sr_err = LDAP_SUCCESS; 1023 1024 /* collect modifications & save for later */ 1025 for ( tag = ber_first_element( ber, &len, &last ); 1026 tag != LBER_DEFAULT; 1027 tag = ber_next_element( ber, &len, last ) ) 1028 { 1029 ber_int_t mop; 1030 Modifications tmp, *mod; 1031 1032 tmp.sml_nvalues = NULL; 1033 1034 if ( ber_scanf( ber, "{e{m[W]}}", &mop, 1035 &tmp.sml_type, &tmp.sml_values ) == LBER_ERROR ) 1036 { 1037 rs->sr_text = "decoding modlist error"; 1038 rs->sr_err = LDAP_PROTOCOL_ERROR; 1039 goto done; 1040 } 1041 1042 mod = (Modifications *) ch_malloc( sizeof(Modifications) ); 1043 mod->sml_op = mop; 1044 mod->sml_flags = 0; 1045 mod->sml_type = tmp.sml_type; 1046 mod->sml_values = tmp.sml_values; 1047 mod->sml_nvalues = NULL; 1048 mod->sml_desc = NULL; 1049 mod->sml_next = NULL; 1050 *modtail = mod; 1051 1052 switch( mop ) { 1053 case LDAP_MOD_ADD: 1054 if ( mod->sml_values == NULL ) { 1055 rs->sr_text = "modify/add operation requires values"; 1056 rs->sr_err = LDAP_PROTOCOL_ERROR; 1057 goto done; 1058 } 1059 1060 /* fall through */ 1061 1062 case LDAP_MOD_DELETE: 1063 case LDAP_MOD_REPLACE: 1064 break; 1065 1066 case LDAP_MOD_INCREMENT: 1067 if( op->o_protocol >= LDAP_VERSION3 ) { 1068 ms->rs_increment++; 1069 if ( mod->sml_values == NULL ) { 1070 rs->sr_text = "modify/increment operation requires value"; 1071 rs->sr_err = LDAP_PROTOCOL_ERROR; 1072 goto done; 1073 } 1074 1075 if ( !BER_BVISNULL( &mod->sml_values[ 1 ] ) ) { 1076 rs->sr_text = "modify/increment operation requires single value"; 1077 rs->sr_err = LDAP_PROTOCOL_ERROR; 1078 goto done; 1079 } 1080 1081 break; 1082 } 1083 /* fall thru */ 1084 1085 default: 1086 rs->sr_text = "unrecognized modify operation"; 1087 rs->sr_err = LDAP_PROTOCOL_ERROR; 1088 goto done; 1089 } 1090 1091 modtail = &mod->sml_next; 1092 } 1093 *modtail = NULL; 1094 1095done: 1096 if ( rs->sr_err != LDAP_SUCCESS ) { 1097 slap_mods_free( ms->rs_mods.rs_modlist, 1 ); 1098 ms->rs_mods.rs_modlist = NULL; 1099 ms->rs_increment = 0; 1100 } 1101 1102 return rs->sr_err; 1103} 1104 1105