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