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