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