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