xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/mods.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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 
38 int
modify_add_values(Entry * e,Modification * mod,int permissive,const char ** text,char * textbuf,size_t textlen)39 modify_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 
186 int
modify_delete_values(Entry * e,Modification * m,int perm,const char ** text,char * textbuf,size_t textlen)187 modify_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 
197 int
modify_delete_vindex(Entry * e,Modification * mod,int permissive,const char ** text,char * textbuf,size_t textlen,int * idx)198 modify_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 	}
364 return_result:
365 	if ( id2 )
366 		ch_free( id2 );
367 	return rc;
368 }
369 
370 int
modify_replace_values(Entry * e,Modification * mod,int permissive,const char ** text,char * textbuf,size_t textlen)371 modify_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 
387 int
modify_increment_values(Entry * e,Modification * mod,int permissive,const char ** text,char * textbuf,size_t textlen)388 modify_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 
463 void
slap_mod_free(Modification * mod,int freeit)464 slap_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 
477 void
slap_mods_free(Modifications * ml,int freevals)478 slap_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