Home | History | Annotate | Line # | Download | only in slapd
      1 /*	$NetBSD: modify.c,v 1.4 2025/09/05 21:16:25 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 1998-2024 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.4 2025/09/05 21:16:25 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 
     43 int
     44 do_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 
    190 cleanup:
    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 
    198 int
    199 fe_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 
    341 cleanup:;
    342 	op->o_bd = bd;
    343 	return rs->sr_err;
    344 }
    345 
    346 /*
    347  * Obsolete constraint checking.
    348  */
    349 int
    350 slap_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  */
    383 int
    384 slap_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 
    425 int
    426 slap_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  */
    457 int 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 
    684 int
    685 slap_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  */
    858 void slap_timestamp( time_t *tm, struct berval *bv )
    859 {
    860 	struct tm ltm;
    861 
    862 	ldap_pvt_gmtime( tm, &ltm );
    863 
    864 	bv->bv_len = lutil_gentime( bv->bv_val, bv->bv_len, &ltm );
    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  */
    870 void 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, &timestamp );
    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], &timestamp );
    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 
   1007 int
   1008 slap_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 
   1095 done:
   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