1 /* $OpenLDAP: pkg/ldap/servers/slapd/mods.c,v 1.59.2.5 2008/02/11 23:26:44 kurt Exp $ */ 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 475