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