1/*	$NetBSD: mods.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: mods.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
30
31#include "portable.h"
32
33#include <ac/string.h>
34
35#include "slap.h"
36#include "lutil.h"
37
38int
39modify_add_values(
40	Entry		*e,
41	Modification	*mod,
42	int		permissive,
43	const char	**text,
44	char		*textbuf,
45	size_t		textlen )
46{
47	int		rc;
48	const char	*op;
49	Attribute	*a;
50	Modification	pmod = *mod;
51
52	switch ( mod->sm_op ) {
53	case LDAP_MOD_ADD:
54		op = "add";
55		break;
56	case LDAP_MOD_REPLACE:
57		op = "replace";
58		break;
59	default:
60		op = "?";
61		assert( 0 );
62	}
63
64	/* FIXME: Catch old code that doesn't set sm_numvals.
65	 */
66	if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
67		unsigned i;
68		for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
69		assert( mod->sm_numvals == i );
70	}
71
72	/* check if values to add exist in attribute */
73	a = attr_find( e->e_attrs, mod->sm_desc );
74	if ( a != NULL ) {
75		MatchingRule	*mr;
76		struct berval *cvals;
77		int		rc;
78		unsigned i, p, flags;
79
80		mr = mod->sm_desc->ad_type->sat_equality;
81		if( mr == NULL || !mr->smr_match ) {
82			/* do not allow add of additional attribute
83				if no equality rule exists */
84			*text = textbuf;
85			snprintf( textbuf, textlen,
86				"modify/%s: %s: no equality matching rule",
87				op, mod->sm_desc->ad_cname.bv_val );
88			return LDAP_INAPPROPRIATE_MATCHING;
89		}
90
91		if ( permissive ) {
92			i = mod->sm_numvals;
93			pmod.sm_values = (BerVarray)ch_malloc(
94				(i + 1) * sizeof( struct berval ));
95			if ( pmod.sm_nvalues != NULL ) {
96				pmod.sm_nvalues = (BerVarray)ch_malloc(
97					(i + 1) * sizeof( struct berval ));
98			}
99		}
100
101		/* no normalization is done in this routine nor
102		 * in the matching routines called by this routine.
103		 * values are now normalized once on input to the
104		 * server (whether from LDAP or from the underlying
105		 * database).
106		 */
107		if ( a->a_desc == slap_schema.si_ad_objectClass ) {
108			/* Needed by ITS#5517 */
109			flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX;
110
111		} else {
112			flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
113		}
114		if ( mod->sm_nvalues ) {
115			flags |= SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
116				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
117			cvals = mod->sm_nvalues;
118		} else {
119			cvals = mod->sm_values;
120		}
121		for ( p = i = 0; i < mod->sm_numvals; i++ ) {
122			unsigned	slot;
123
124			rc = attr_valfind( a, flags, &cvals[i], &slot, NULL );
125			if ( rc == LDAP_SUCCESS ) {
126				if ( !permissive ) {
127					/* value already exists */
128					*text = textbuf;
129					snprintf( textbuf, textlen,
130						"modify/%s: %s: value #%u already exists",
131						op, mod->sm_desc->ad_cname.bv_val, i );
132					return LDAP_TYPE_OR_VALUE_EXISTS;
133				}
134			} else if ( rc != LDAP_NO_SUCH_ATTRIBUTE ) {
135				return rc;
136			}
137
138			if ( permissive && rc ) {
139				if ( pmod.sm_nvalues ) {
140					pmod.sm_nvalues[p] = mod->sm_nvalues[i];
141				}
142				pmod.sm_values[p++] = mod->sm_values[i];
143			}
144		}
145
146		if ( permissive ) {
147			if ( p == 0 ) {
148				/* all new values match exist */
149				ch_free( pmod.sm_values );
150				if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
151				return LDAP_SUCCESS;
152			}
153
154			BER_BVZERO( &pmod.sm_values[p] );
155			if ( pmod.sm_nvalues ) {
156				BER_BVZERO( &pmod.sm_nvalues[p] );
157			}
158		}
159	}
160
161	/* no - add them */
162	if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
163		rc = ordered_value_add( e, mod->sm_desc, a,
164			pmod.sm_values, pmod.sm_nvalues );
165	} else {
166		rc = attr_merge( e, mod->sm_desc, pmod.sm_values, pmod.sm_nvalues );
167	}
168
169	if ( a != NULL && permissive ) {
170		ch_free( pmod.sm_values );
171		if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
172	}
173
174	if ( rc != 0 ) {
175		/* this should return result of attr_merge */
176		*text = textbuf;
177		snprintf( textbuf, textlen,
178			"modify/%s: %s: merge error (%d)",
179			op, mod->sm_desc->ad_cname.bv_val, rc );
180		return LDAP_OTHER;
181	}
182
183	return LDAP_SUCCESS;
184}
185
186int
187modify_delete_values(
188	Entry	*e,
189	Modification	*m,
190	int	perm,
191	const char	**text,
192	char *textbuf, size_t textlen )
193{
194	return modify_delete_vindex( e, m, perm, text, textbuf, textlen, NULL );
195}
196
197int
198modify_delete_vindex(
199	Entry	*e,
200	Modification	*mod,
201	int	permissive,
202	const char	**text,
203	char *textbuf, size_t textlen,
204	int *idx )
205{
206	Attribute	*a;
207	MatchingRule 	*mr = mod->sm_desc->ad_type->sat_equality;
208	struct berval *cvals;
209	int		*id2 = NULL;
210	int		rc = 0;
211	unsigned i, j, flags;
212	char		dummy = '\0';
213
214	/*
215	 * If permissive is set, then the non-existence of an
216	 * attribute is not treated as an error.
217	 */
218
219	/* delete the entire attribute */
220	if ( mod->sm_values == NULL ) {
221		rc = attr_delete( &e->e_attrs, mod->sm_desc );
222
223		if( permissive ) {
224			rc = LDAP_SUCCESS;
225		} else if( rc != LDAP_SUCCESS ) {
226			*text = textbuf;
227			snprintf( textbuf, textlen,
228				"modify/delete: %s: no such attribute",
229				mod->sm_desc->ad_cname.bv_val );
230			rc = LDAP_NO_SUCH_ATTRIBUTE;
231		}
232		return rc;
233	}
234
235	/* FIXME: Catch old code that doesn't set sm_numvals.
236	 */
237	if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
238		for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
239		assert( mod->sm_numvals == i );
240	}
241	if ( !idx ) {
242		id2 = ch_malloc( mod->sm_numvals * sizeof( int ));
243		idx = id2;
244	}
245
246	if( mr == NULL || !mr->smr_match ) {
247		/* disallow specific attributes from being deleted if
248			no equality rule */
249		*text = textbuf;
250		snprintf( textbuf, textlen,
251			"modify/delete: %s: no equality matching rule",
252			mod->sm_desc->ad_cname.bv_val );
253		rc = LDAP_INAPPROPRIATE_MATCHING;
254		goto return_result;
255	}
256
257	/* delete specific values - find the attribute first */
258	if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
259		if( permissive ) {
260			rc = LDAP_SUCCESS;
261			goto return_result;
262		}
263		*text = textbuf;
264		snprintf( textbuf, textlen,
265			"modify/delete: %s: no such attribute",
266			mod->sm_desc->ad_cname.bv_val );
267		rc = LDAP_NO_SUCH_ATTRIBUTE;
268		goto return_result;
269	}
270
271	if ( a->a_desc == slap_schema.si_ad_objectClass ) {
272		/* Needed by ITS#5517,ITS#5963 */
273		flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX;
274
275	} else {
276		flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
277	}
278	if ( mod->sm_nvalues ) {
279		flags |= SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
280			| SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
281		cvals = mod->sm_nvalues;
282	} else {
283		cvals = mod->sm_values;
284	}
285
286	/* Locate values to delete */
287	for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
288		unsigned sort;
289		rc = attr_valfind( a, flags, &cvals[i], &sort, NULL );
290		if ( rc == LDAP_SUCCESS ) {
291			idx[i] = sort;
292		} else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
293			if ( permissive ) {
294				idx[i] = -1;
295				continue;
296			}
297			*text = textbuf;
298			snprintf( textbuf, textlen,
299				"modify/delete: %s: no such value",
300				mod->sm_desc->ad_cname.bv_val );
301			goto return_result;
302		} else {
303			*text = textbuf;
304			snprintf( textbuf, textlen,
305				"modify/delete: %s: matching rule failed",
306				mod->sm_desc->ad_cname.bv_val );
307			goto return_result;
308		}
309	}
310
311	/* Delete the values */
312	for ( i = 0; i < mod->sm_numvals; i++ ) {
313		/* Skip permissive values that weren't found */
314		if ( idx[i] < 0 )
315			continue;
316		/* Skip duplicate delete specs */
317		if ( a->a_vals[idx[i]].bv_val == &dummy )
318			continue;
319		/* delete value and mark it as gone */
320		free( a->a_vals[idx[i]].bv_val );
321		a->a_vals[idx[i]].bv_val = &dummy;
322		if( a->a_nvals != a->a_vals ) {
323			free( a->a_nvals[idx[i]].bv_val );
324			a->a_nvals[idx[i]].bv_val = &dummy;
325		}
326		a->a_numvals--;
327	}
328
329	/* compact array skipping dummies */
330	for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
331		/* skip dummies */
332		if( a->a_vals[i].bv_val == &dummy ) {
333			assert( a->a_nvals[i].bv_val == &dummy );
334			continue;
335		}
336		if ( j != i ) {
337			a->a_vals[ j ] = a->a_vals[ i ];
338			if (a->a_nvals != a->a_vals) {
339				a->a_nvals[ j ] = a->a_nvals[ i ];
340			}
341		}
342		j++;
343	}
344
345	BER_BVZERO( &a->a_vals[j] );
346	if (a->a_nvals != a->a_vals) {
347		BER_BVZERO( &a->a_nvals[j] );
348	}
349
350	/* if no values remain, delete the entire attribute */
351	if ( !a->a_numvals ) {
352		if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
353			/* Can never happen */
354			*text = textbuf;
355			snprintf( textbuf, textlen,
356				"modify/delete: %s: no such attribute",
357				mod->sm_desc->ad_cname.bv_val );
358			rc = LDAP_NO_SUCH_ATTRIBUTE;
359		}
360	} else if ( a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
361		/* For an ordered attribute, renumber the value indices */
362		ordered_value_sort( a, 1 );
363	}
364return_result:
365	if ( id2 )
366		ch_free( id2 );
367	return rc;
368}
369
370int
371modify_replace_values(
372	Entry	*e,
373	Modification	*mod,
374	int		permissive,
375	const char	**text,
376	char *textbuf, size_t textlen )
377{
378	(void) attr_delete( &e->e_attrs, mod->sm_desc );
379
380	if ( mod->sm_values ) {
381		return modify_add_values( e, mod, permissive, text, textbuf, textlen );
382	}
383
384	return LDAP_SUCCESS;
385}
386
387int
388modify_increment_values(
389	Entry	*e,
390	Modification	*mod,
391	int	permissive,
392	const char	**text,
393	char *textbuf, size_t textlen )
394{
395	Attribute *a;
396	const char *syn_oid;
397
398	a = attr_find( e->e_attrs, mod->sm_desc );
399	if( a == NULL ) {
400		if ( permissive ) {
401			Modification modReplace = *mod;
402
403			modReplace.sm_op = LDAP_MOD_REPLACE;
404
405			return modify_add_values(e, &modReplace, permissive, text, textbuf, textlen);
406		} else {
407			*text = textbuf;
408			snprintf( textbuf, textlen,
409				"modify/increment: %s: no such attribute",
410				mod->sm_desc->ad_cname.bv_val );
411			return LDAP_NO_SUCH_ATTRIBUTE;
412		}
413	}
414
415	syn_oid = at_syntax( a->a_desc->ad_type );
416	if ( syn_oid && !strcmp( syn_oid, SLAPD_INTEGER_SYNTAX )) {
417		int i;
418		char str[sizeof(long)*3 + 2]; /* overly long */
419		long incr;
420
421		if ( lutil_atol( &incr, mod->sm_values[0].bv_val ) != 0 ) {
422			*text = "modify/increment: invalid syntax of increment";
423			return LDAP_INVALID_SYNTAX;
424		}
425
426		/* treat zero and errors as a no-op */
427		if( incr == 0 ) {
428			return LDAP_SUCCESS;
429		}
430
431		for( i = 0; !BER_BVISNULL( &a->a_nvals[i] ); i++ ) {
432			char *tmp;
433			long value;
434			size_t strln;
435			if ( lutil_atol( &value, a->a_nvals[i].bv_val ) != 0 ) {
436				*text = "modify/increment: invalid syntax of original value";
437				return LDAP_INVALID_SYNTAX;
438			}
439			strln = snprintf( str, sizeof(str), "%ld", value+incr );
440
441			tmp = SLAP_REALLOC( a->a_nvals[i].bv_val, strln+1 );
442			if( tmp == NULL ) {
443				*text = "modify/increment: reallocation error";
444				return LDAP_OTHER;
445			}
446			a->a_nvals[i].bv_val = tmp;
447			a->a_nvals[i].bv_len = strln;
448
449			AC_MEMCPY( a->a_nvals[i].bv_val, str, strln+1 );
450		}
451
452	} else {
453		snprintf( textbuf, textlen,
454			"modify/increment: %s: increment not supported for value syntax %s",
455			mod->sm_desc->ad_cname.bv_val,
456			syn_oid ? syn_oid : "(NULL)" );
457		return LDAP_CONSTRAINT_VIOLATION;
458	}
459
460	return LDAP_SUCCESS;
461}
462
463void
464slap_mod_free(
465	Modification	*mod,
466	int				freeit )
467{
468	if ( mod->sm_values != NULL ) ber_bvarray_free( mod->sm_values );
469	mod->sm_values = NULL;
470
471	if ( mod->sm_nvalues != NULL ) ber_bvarray_free( mod->sm_nvalues );
472	mod->sm_nvalues = NULL;
473
474	if( freeit ) free( mod );
475}
476
477void
478slap_mods_free(
479    Modifications	*ml,
480    int			freevals )
481{
482	Modifications *next;
483
484	for ( ; ml != NULL; ml = next ) {
485		next = ml->sml_next;
486
487		if ( freevals )
488			slap_mod_free( &ml->sml_mod, 0 );
489		free( ml );
490	}
491}
492
493