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 38int 39modify_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 186int 187modify_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 197int 198modify_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 } 364return_result: 365 if ( id2 ) 366 ch_free( id2 ); 367 return rc; 368} 369 370int 371modify_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 387int 388modify_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 463void 464slap_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 477void 478slap_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