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