1/* $OpenLDAP$ */
2/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 1998-2011 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
38int
39do_modify(
40    Operation	*op,
41    SlapReply	*rs )
42{
43	struct berval dn = BER_BVNULL;
44	char		textbuf[ SLAP_TEXT_BUFLEN ];
45	size_t		textlen = sizeof( textbuf );
46#ifdef LDAP_DEBUG
47	Modifications	*tmp;
48#endif
49
50	Debug( LDAP_DEBUG_TRACE, "%s do_modify\n",
51		op->o_log_prefix, 0, 0 );
52	/*
53	 * Parse the modify request.  It looks like this:
54	 *
55	 *	ModifyRequest := [APPLICATION 6] SEQUENCE {
56	 *		name	DistinguishedName,
57	 *		mods	SEQUENCE OF SEQUENCE {
58	 *			operation	ENUMERATED {
59	 *				add	(0),
60	 *				delete	(1),
61	 *				replace	(2)
62	 *			},
63	 *			modification	SEQUENCE {
64	 *				type	AttributeType,
65	 *				values	SET OF AttributeValue
66	 *			}
67	 *		}
68	 *	}
69	 */
70
71	if ( ber_scanf( op->o_ber, "{m" /*}*/, &dn ) == LBER_ERROR ) {
72		Debug( LDAP_DEBUG_ANY, "%s do_modify: ber_scanf failed\n",
73			op->o_log_prefix, 0, 0 );
74		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
75		return SLAPD_DISCONNECT;
76	}
77
78	Debug( LDAP_DEBUG_ARGS, "%s do_modify: dn (%s)\n",
79		op->o_log_prefix, dn.bv_val, 0 );
80
81	rs->sr_err = slap_parse_modlist( op, rs, op->o_ber, &op->oq_modify );
82	if ( rs->sr_err != LDAP_SUCCESS ) {
83		Debug( LDAP_DEBUG_ANY, "%s do_modify: slap_parse_modlist failed err=%d msg=%s\n",
84			op->o_log_prefix, rs->sr_err, rs->sr_text );
85		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, 0, 0 );
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, 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 = LDAP_SUCCESS;
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	if ( nvals <= 1 )
709		goto ret;
710
711	/* For Modifications, sml_nvalues is NULL if normalization wasn't needed.
712	 * For Attributes, sml_nvalues == sml_values when normalization isn't needed.
713	 */
714	if ( ml->sml_nvalues && ml->sml_nvalues != ml->sml_values ) {
715		cv = ml->sml_nvalues;
716		is_norm = 1;
717	} else {
718		cv = ml->sml_values;
719		is_norm = 0;
720	}
721
722	if ( ad == slap_schema.si_ad_objectClass )
723		mr = NULL;	/* shortcut matching */
724	else
725		mr = ad->ad_type->sat_equality;
726
727	/* record indices to preserve input ordering */
728	ix = slap_sl_malloc( nvals * sizeof(int), ctx );
729	for (i=0; i<nvals; i++) ix[i] = i;
730
731	ir = nvals-1;
732	l = 0;
733	jstack = 0;
734
735	for(;;) {
736		if (ir - l < SMALL) {	/* Insertion sort */
737			match=1;
738			for (j=l+1;j<=ir;j++) {
739				SETA(j);
740				for (i=j-1;i>=0;i--) {
741					COMP(cv[IX(i)], a);
742					if ( match <= 0 )
743						break;
744					SET(i+1,i);
745				}
746				GETA(i+1);
747				if ( match == 0 ) goto done;
748			}
749			if ( jstack == 0 ) break;
750			if ( match == 0 ) break;
751			ir = istack[jstack--];
752			l = istack[jstack--];
753		} else {
754			k = (l + ir) >> 1;	/* Choose median of left, center, right */
755			EXCH(k, l+1);
756			COMP( cv[IX(l)], cv[IX(ir)] );
757			if ( match > 0 ) {
758				EXCH(l, ir);
759			} else if ( match == 0 ) {
760				i = ir;
761				break;
762			}
763			COMP( cv[IX(l+1)], cv[IX(ir)] );
764			if ( match > 0 ) {
765				EXCH(l+1, ir);
766			} else if ( match == 0 ) {
767				i = ir;
768				break;
769			}
770			COMP( cv[IX(l)], cv[IX(l+1)] );
771			if ( match > 0 ) {
772				EXCH(l, l+1);
773			} else if ( match == 0 ) {
774				i = l;
775				break;
776			}
777			i = l+1;
778			j = ir;
779			a = cv[IX(i)];
780			for(;;) {
781				do {
782					i++;
783					COMP( cv[IX(i)], a );
784				} while( match < 0 );
785				while( match > 0 ) {
786					j--;
787					COMP( cv[IX(j)], a );
788				}
789				if (j < i) {
790					match = 1;
791					break;
792				}
793				if ( match == 0 ) {
794					i = l+1;
795					break;
796				}
797				EXCH(i,j);
798			}
799			if ( match == 0 )
800				break;
801			EXCH(l+1,j);
802			jstack += 2;
803			if (ir-i+1 >= j) {
804				istack[jstack] = ir;
805				istack[jstack-1] = i;
806				ir = j;
807			} else {
808				istack[jstack] = j;
809				istack[jstack-1] = l;
810				l = i;
811			}
812		}
813	}
814	done:
815	if ( match == 0 && i >= 0 )
816		*dup = ix[i];
817
818	/* For sorted attributes, put the values in index order */
819	if ( rc == LDAP_SUCCESS && match &&
820		( ad->ad_type->sat_flags & SLAP_AT_SORTED_VAL )) {
821		BerVarray tmpv = slap_sl_malloc( sizeof( struct berval ) * nvals, ctx );
822		for ( i = 0; i<nvals; i++ )
823			tmpv[i] = cv[ix[i]];
824		for ( i = 0; i<nvals; i++ )
825			cv[i] = tmpv[i];
826		/* Check if the non-normalized array needs to move too */
827		if ( is_norm ) {
828			cv = ml->sml_values;
829			for ( i = 0; i<nvals; i++ )
830				tmpv[i] = cv[ix[i]];
831			for ( i = 0; i<nvals; i++ )
832				cv[i] = tmpv[i];
833		}
834		slap_sl_free( tmpv, ctx );
835	}
836
837	slap_sl_free( ix, ctx );
838
839	if ( rc == LDAP_SUCCESS && match == 0 ) {
840		/* value exists already */
841		assert( i >= 0 );
842		assert( i < nvals );
843		rc = LDAP_TYPE_OR_VALUE_EXISTS;
844	}
845 ret:
846	return rc;
847}
848
849/* Enter with bv->bv_len = sizeof buffer, returns with
850 * actual length of string
851 */
852void slap_timestamp( time_t *tm, struct berval *bv )
853{
854	struct tm ltm;
855
856	ldap_pvt_gmtime( tm, &ltm );
857
858	bv->bv_len = lutil_gentime( bv->bv_val, bv->bv_len, &ltm );
859}
860
861/* Called for all modify and modrdn ops. If the current op was replicated
862 * from elsewhere, all of the attrs should already be present.
863 */
864void slap_mods_opattrs(
865	Operation *op,
866	Modifications **modsp,
867	int manage_ctxcsn )
868{
869	struct berval name, timestamp, csn = BER_BVNULL;
870	struct berval nname;
871	char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
872	char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
873	Modifications *mod, **modtail, *modlast;
874	int gotcsn = 0, gotmname = 0, gotmtime = 0;
875
876	if ( SLAP_LASTMOD( op->o_bd ) && !op->orm_no_opattrs ) {
877		char *ptr;
878		timestamp.bv_val = timebuf;
879		for ( modtail = modsp; *modtail; modtail = &(*modtail)->sml_next ) {
880			if ( (*modtail)->sml_op != LDAP_MOD_ADD &&
881				(*modtail)->sml_op != SLAP_MOD_SOFTADD &&
882				(*modtail)->sml_op != SLAP_MOD_ADD_IF_NOT_PRESENT &&
883				(*modtail)->sml_op != LDAP_MOD_REPLACE )
884			{
885				continue;
886			}
887
888			if ( (*modtail)->sml_desc == slap_schema.si_ad_entryCSN )
889			{
890				csn = (*modtail)->sml_values[0];
891				gotcsn = 1;
892
893			} else if ( (*modtail)->sml_desc == slap_schema.si_ad_modifiersName )
894			{
895				gotmname = 1;
896
897			} else if ( (*modtail)->sml_desc == slap_schema.si_ad_modifyTimestamp )
898			{
899				gotmtime = 1;
900			}
901		}
902
903		if ( BER_BVISEMPTY( &op->o_csn )) {
904			if ( !gotcsn ) {
905				csn.bv_val = csnbuf;
906				csn.bv_len = sizeof( csnbuf );
907				slap_get_csn( op, &csn, manage_ctxcsn );
908
909			} else {
910				if ( manage_ctxcsn ) {
911					slap_queue_csn( op, &csn );
912				}
913			}
914
915		} else {
916			csn = op->o_csn;
917		}
918
919		ptr = ber_bvchr( &csn, '#' );
920		if ( ptr ) {
921			timestamp.bv_len = STRLENOF("YYYYMMDDHHMMSSZ");
922			AC_MEMCPY( timebuf, csn.bv_val, timestamp.bv_len );
923			timebuf[timestamp.bv_len-1] = 'Z';
924			timebuf[timestamp.bv_len] = '\0';
925
926		} else {
927			time_t now = slap_get_time();
928
929			timestamp.bv_len = sizeof(timebuf);
930
931			slap_timestamp( &now, &timestamp );
932		}
933
934		if ( BER_BVISEMPTY( &op->o_dn ) ) {
935			BER_BVSTR( &name, SLAPD_ANONYMOUS );
936			nname = name;
937
938		} else {
939			name = op->o_dn;
940			nname = op->o_ndn;
941		}
942
943		if ( !gotcsn ) {
944			mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
945			mod->sml_op = LDAP_MOD_REPLACE;
946			mod->sml_flags = SLAP_MOD_INTERNAL;
947			mod->sml_next = NULL;
948			BER_BVZERO( &mod->sml_type );
949			mod->sml_desc = slap_schema.si_ad_entryCSN;
950			mod->sml_numvals = 1;
951			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
952			ber_dupbv( &mod->sml_values[0], &csn );
953			BER_BVZERO( &mod->sml_values[1] );
954			assert( !BER_BVISNULL( &mod->sml_values[0] ) );
955			mod->sml_nvalues = NULL;
956			*modtail = mod;
957			modlast = mod;
958			modtail = &mod->sml_next;
959		}
960
961		if ( !gotmname ) {
962			mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
963			mod->sml_op = LDAP_MOD_REPLACE;
964			mod->sml_flags = SLAP_MOD_INTERNAL;
965			mod->sml_next = NULL;
966			BER_BVZERO( &mod->sml_type );
967			mod->sml_desc = slap_schema.si_ad_modifiersName;
968			mod->sml_numvals = 1;
969			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
970			ber_dupbv( &mod->sml_values[0], &name );
971			BER_BVZERO( &mod->sml_values[1] );
972			assert( !BER_BVISNULL( &mod->sml_values[0] ) );
973			mod->sml_nvalues =
974				(BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
975			ber_dupbv( &mod->sml_nvalues[0], &nname );
976			BER_BVZERO( &mod->sml_nvalues[1] );
977			assert( !BER_BVISNULL( &mod->sml_nvalues[0] ) );
978			*modtail = mod;
979			modtail = &mod->sml_next;
980		}
981
982		if ( !gotmtime ) {
983			mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
984			mod->sml_op = LDAP_MOD_REPLACE;
985			mod->sml_flags = SLAP_MOD_INTERNAL;
986			mod->sml_next = NULL;
987			BER_BVZERO( &mod->sml_type );
988			mod->sml_desc = slap_schema.si_ad_modifyTimestamp;
989			mod->sml_numvals = 1;
990			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
991			ber_dupbv( &mod->sml_values[0], &timestamp );
992			BER_BVZERO( &mod->sml_values[1] );
993			assert( !BER_BVISNULL( &mod->sml_values[0] ) );
994			mod->sml_nvalues = NULL;
995			*modtail = mod;
996			modtail = &mod->sml_next;
997		}
998	}
999}
1000
1001int
1002slap_parse_modlist(
1003	Operation *op,
1004	SlapReply *rs,
1005	BerElement *ber,
1006	req_modify_s *ms )
1007{
1008	ber_tag_t	tag;
1009	ber_len_t	len;
1010	char		*last;
1011	Modifications	**modtail = &ms->rs_mods.rs_modlist;
1012
1013	ms->rs_mods.rs_modlist = NULL;
1014	ms->rs_increment = 0;
1015
1016	rs->sr_err = LDAP_SUCCESS;
1017
1018	/* collect modifications & save for later */
1019	for ( tag = ber_first_element( ber, &len, &last );
1020		tag != LBER_DEFAULT;
1021		tag = ber_next_element( ber, &len, last ) )
1022	{
1023		ber_int_t mop;
1024		Modifications tmp, *mod;
1025
1026		tmp.sml_nvalues = NULL;
1027
1028		if ( ber_scanf( ber, "{e{m[W]}}", &mop,
1029		    &tmp.sml_type, &tmp.sml_values ) == LBER_ERROR )
1030		{
1031			rs->sr_text = "decoding modlist error";
1032			rs->sr_err = LDAP_PROTOCOL_ERROR;
1033			goto done;
1034		}
1035
1036		mod = (Modifications *) ch_malloc( sizeof(Modifications) );
1037		mod->sml_op = mop;
1038		mod->sml_flags = 0;
1039		mod->sml_type = tmp.sml_type;
1040		mod->sml_values = tmp.sml_values;
1041		mod->sml_nvalues = NULL;
1042		mod->sml_desc = NULL;
1043		mod->sml_next = NULL;
1044		*modtail = mod;
1045
1046		switch( mop ) {
1047		case LDAP_MOD_ADD:
1048			if ( mod->sml_values == NULL ) {
1049				rs->sr_text = "modify/add operation requires values";
1050				rs->sr_err = LDAP_PROTOCOL_ERROR;
1051				goto done;
1052			}
1053
1054			/* fall through */
1055
1056		case LDAP_MOD_DELETE:
1057		case LDAP_MOD_REPLACE:
1058			break;
1059
1060		case LDAP_MOD_INCREMENT:
1061			if( op->o_protocol >= LDAP_VERSION3 ) {
1062				ms->rs_increment++;
1063				if ( mod->sml_values == NULL ) {
1064					rs->sr_text = "modify/increment operation requires value";
1065					rs->sr_err = LDAP_PROTOCOL_ERROR;
1066					goto done;
1067				}
1068
1069				if ( !BER_BVISNULL( &mod->sml_values[ 1 ] ) ) {
1070					rs->sr_text = "modify/increment operation requires single value";
1071					rs->sr_err = LDAP_PROTOCOL_ERROR;
1072					goto done;
1073				}
1074
1075				break;
1076			}
1077			/* fall thru */
1078
1079		default:
1080			rs->sr_text = "unrecognized modify operation";
1081			rs->sr_err = LDAP_PROTOCOL_ERROR;
1082			goto done;
1083		}
1084
1085		modtail = &mod->sml_next;
1086	}
1087	*modtail = NULL;
1088
1089done:
1090	if ( rs->sr_err != LDAP_SUCCESS ) {
1091		slap_mods_free( ms->rs_mods.rs_modlist, 1 );
1092		ms->rs_mods.rs_modlist = NULL;
1093		ms->rs_increment = 0;
1094	}
1095
1096	return rs->sr_err;
1097}
1098
1099