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