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
43int
44do_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
190cleanup:
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
198int
199fe_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
341cleanup:;
342	op->o_bd = bd;
343	return rs->sr_err;
344}
345
346/*
347 * Obsolete constraint checking.
348 */
349int
350slap_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 */
383int
384slap_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
425int
426slap_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 */
457int 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
684int
685slap_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 */
858void 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 */
870void 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
1007int
1008slap_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
1095done:
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