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