]> git.sur5r.net Git - openldap/blob - servers/slapd/mods.c
Happy New Year
[openldap] / servers / slapd / mods.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2018 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                 unsigned 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;
73                 unsigned i, p, 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                 if ( a->a_desc == slap_schema.si_ad_objectClass ) {
103                         /* Needed by ITS#5517 */
104                         flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX;
105
106                 } else {
107                         flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
108                 }
109                 if ( mod->sm_nvalues ) {
110                         flags |= SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
111                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
112                         cvals = mod->sm_nvalues;
113                 } else {
114                         cvals = mod->sm_values;
115                 }
116                 for ( p = i = 0; i < mod->sm_numvals; i++ ) {
117                         unsigned        slot;
118
119                         rc = attr_valfind( a, flags, &cvals[i], &slot, NULL );
120                         if ( rc == LDAP_SUCCESS ) {
121                                 if ( !permissive ) {
122                                         /* value already exists */
123                                         *text = textbuf;
124                                         snprintf( textbuf, textlen,
125                                                 "modify/%s: %s: value #%u already exists",
126                                                 op, mod->sm_desc->ad_cname.bv_val, i );
127                                         return LDAP_TYPE_OR_VALUE_EXISTS;
128                                 }
129                         } else if ( rc != LDAP_NO_SUCH_ATTRIBUTE ) {
130                                 return rc;
131                         }
132
133                         if ( permissive && rc ) {
134                                 if ( pmod.sm_nvalues ) {
135                                         pmod.sm_nvalues[p] = mod->sm_nvalues[i];
136                                 }
137                                 pmod.sm_values[p++] = mod->sm_values[i];
138                         }
139                 }
140
141                 if ( permissive ) {
142                         if ( p == 0 ) {
143                                 /* all new values match exist */
144                                 ch_free( pmod.sm_values );
145                                 if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
146                                 return LDAP_SUCCESS;
147                         }
148
149                         BER_BVZERO( &pmod.sm_values[p] );
150                         if ( pmod.sm_nvalues ) {
151                                 BER_BVZERO( &pmod.sm_nvalues[p] );
152                         }
153                 }
154         }
155
156         /* no - add them */
157         if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
158                 rc = ordered_value_add( e, mod->sm_desc, a,
159                         pmod.sm_values, pmod.sm_nvalues );
160         } else {
161                 rc = attr_merge( e, mod->sm_desc, pmod.sm_values, pmod.sm_nvalues );
162         }
163
164         if ( a != NULL && permissive ) {
165                 ch_free( pmod.sm_values );
166                 if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
167         }
168
169         if ( rc != 0 ) {
170                 /* this should return result of attr_merge */
171                 *text = textbuf;
172                 snprintf( textbuf, textlen,
173                         "modify/%s: %s: merge error (%d)",
174                         op, mod->sm_desc->ad_cname.bv_val, rc );
175                 return LDAP_OTHER;
176         }
177
178         return LDAP_SUCCESS;
179 }
180
181 int
182 modify_delete_values(
183         Entry   *e,
184         Modification    *m,
185         int     perm,
186         const char      **text,
187         char *textbuf, size_t textlen )
188 {
189         return modify_delete_vindex( e, m, perm, text, textbuf, textlen, NULL );
190 }
191
192 int
193 modify_delete_vindex(
194         Entry   *e,
195         Modification    *mod,
196         int     permissive,
197         const char      **text,
198         char *textbuf, size_t textlen,
199         int *idx )
200 {
201         Attribute       *a;
202         MatchingRule    *mr = mod->sm_desc->ad_type->sat_equality;
203         struct berval *cvals;
204         int             *id2 = NULL;
205         int             rc = 0;
206         unsigned i, j, flags;
207         char            dummy = '\0';
208
209         /*
210          * If permissive is set, then the non-existence of an 
211          * attribute is not treated as an error.
212          */
213
214         /* delete the entire attribute */
215         if ( mod->sm_values == NULL ) {
216                 rc = attr_delete( &e->e_attrs, mod->sm_desc );
217
218                 if( permissive ) {
219                         rc = LDAP_SUCCESS;
220                 } else if( rc != LDAP_SUCCESS ) {
221                         *text = textbuf;
222                         snprintf( textbuf, textlen,
223                                 "modify/delete: %s: no such attribute",
224                                 mod->sm_desc->ad_cname.bv_val );
225                         rc = LDAP_NO_SUCH_ATTRIBUTE;
226                 }
227                 return rc;
228         }
229
230         /* FIXME: Catch old code that doesn't set sm_numvals.
231          */
232         if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
233                 for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
234                 assert( mod->sm_numvals == i );
235         }
236         if ( !idx ) {
237                 id2 = ch_malloc( mod->sm_numvals * sizeof( int ));
238                 idx = id2;
239         }
240
241         if( mr == NULL || !mr->smr_match ) {
242                 /* disallow specific attributes from being deleted if
243                         no equality rule */
244                 *text = textbuf;
245                 snprintf( textbuf, textlen,
246                         "modify/delete: %s: no equality matching rule",
247                         mod->sm_desc->ad_cname.bv_val );
248                 rc = LDAP_INAPPROPRIATE_MATCHING;
249                 goto return_result;
250         }
251
252         /* delete specific values - find the attribute first */
253         if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
254                 if( permissive ) {
255                         rc = LDAP_SUCCESS;
256                         goto return_result;
257                 }
258                 *text = textbuf;
259                 snprintf( textbuf, textlen,
260                         "modify/delete: %s: no such attribute",
261                         mod->sm_desc->ad_cname.bv_val );
262                 rc = LDAP_NO_SUCH_ATTRIBUTE;
263                 goto return_result;
264         }
265
266         if ( a->a_desc == slap_schema.si_ad_objectClass ) {
267                 /* Needed by ITS#5517,ITS#5963 */
268                 flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX;
269
270         } else {
271                 flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
272         }
273         if ( mod->sm_nvalues ) {
274                 flags |= SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
275                         | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
276                 cvals = mod->sm_nvalues;
277         } else {
278                 cvals = mod->sm_values;
279         }
280
281         /* Locate values to delete */
282         for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
283                 unsigned sort;
284                 rc = attr_valfind( a, flags, &cvals[i], &sort, NULL );
285                 if ( rc == LDAP_SUCCESS ) {
286                         idx[i] = sort;
287                 } else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
288                         if ( permissive ) {
289                                 idx[i] = -1;
290                                 continue;
291                         }
292                         *text = textbuf;
293                         snprintf( textbuf, textlen,
294                                 "modify/delete: %s: no such value",
295                                 mod->sm_desc->ad_cname.bv_val );
296                         goto return_result;
297                 } else {
298                         *text = textbuf;
299                         snprintf( textbuf, textlen,
300                                 "modify/delete: %s: matching rule failed",
301                                 mod->sm_desc->ad_cname.bv_val );
302                         goto return_result;
303                 }
304         }
305
306         /* Delete the values */
307         for ( i = 0; i < mod->sm_numvals; i++ ) {
308                 /* Skip permissive values that weren't found */
309                 if ( idx[i] < 0 )
310                         continue;
311                 /* Skip duplicate delete specs */
312                 if ( a->a_vals[idx[i]].bv_val == &dummy )
313                         continue;
314                 /* delete value and mark it as gone */
315                 free( a->a_vals[idx[i]].bv_val );
316                 a->a_vals[idx[i]].bv_val = &dummy;
317                 if( a->a_nvals != a->a_vals ) {
318                         free( a->a_nvals[idx[i]].bv_val );
319                         a->a_nvals[idx[i]].bv_val = &dummy;
320                 }
321                 a->a_numvals--;
322         }
323
324         /* compact array skipping dummies */
325         for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
326                 /* skip dummies */
327                 if( a->a_vals[i].bv_val == &dummy ) {
328                         assert( a->a_nvals[i].bv_val == &dummy );
329                         continue;
330                 }
331                 if ( j != i ) {
332                         a->a_vals[ j ] = a->a_vals[ i ];
333                         if (a->a_nvals != a->a_vals) {
334                                 a->a_nvals[ j ] = a->a_nvals[ i ];
335                         }
336                 }
337                 j++;
338         }
339
340         BER_BVZERO( &a->a_vals[j] );
341         if (a->a_nvals != a->a_vals) {
342                 BER_BVZERO( &a->a_nvals[j] );
343         }
344
345         /* if no values remain, delete the entire attribute */
346         if ( !a->a_numvals ) {
347                 if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
348                         /* Can never happen */
349                         *text = textbuf;
350                         snprintf( textbuf, textlen,
351                                 "modify/delete: %s: no such attribute",
352                                 mod->sm_desc->ad_cname.bv_val );
353                         rc = LDAP_NO_SUCH_ATTRIBUTE;
354                 }
355         } else if ( a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
356                 /* For an ordered attribute, renumber the value indices */
357                 ordered_value_sort( a, 1 );
358         }
359 return_result:
360         if ( id2 )
361                 ch_free( id2 );
362         return rc;
363 }
364
365 int
366 modify_replace_values(
367         Entry   *e,
368         Modification    *mod,
369         int             permissive,
370         const char      **text,
371         char *textbuf, size_t textlen )
372 {
373         (void) attr_delete( &e->e_attrs, mod->sm_desc );
374
375         if ( mod->sm_values ) {
376                 return modify_add_values( e, mod, permissive, text, textbuf, textlen );
377         }
378
379         return LDAP_SUCCESS;
380 }
381
382 int
383 modify_increment_values(
384         Entry   *e,
385         Modification    *mod,
386         int     permissive,
387         const char      **text,
388         char *textbuf, size_t textlen )
389 {
390         Attribute *a;
391         const char *syn_oid;
392
393         a = attr_find( e->e_attrs, mod->sm_desc );
394         if( a == NULL ) {
395                 if ( permissive ) {
396                         Modification modReplace = *mod;
397
398                         modReplace.sm_op = LDAP_MOD_REPLACE;
399
400                         return modify_add_values(e, &modReplace, permissive, text, textbuf, textlen);
401                 } else {
402                         *text = textbuf;
403                         snprintf( textbuf, textlen,
404                                 "modify/increment: %s: no such attribute",
405                                 mod->sm_desc->ad_cname.bv_val );
406                         return LDAP_NO_SUCH_ATTRIBUTE;
407                 }
408         }
409
410         syn_oid = at_syntax( a->a_desc->ad_type );
411         if ( syn_oid && !strcmp( syn_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                         syn_oid ? syn_oid : "(NULL)" );
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