1 /* $OpenLDAP: pkg/ldap/servers/slapd/modrdn.c,v 1.170.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 1999, Juan C. Gomez, All rights reserved. 16 * This software is not subject to any license of Silicon Graphics 17 * Inc. or Purdue University. 18 * 19 * Redistribution and use in source and binary forms are permitted 20 * without restriction or fee of any kind as long as this notice 21 * is preserved. 22 */ 23 /* Portions Copyright (c) 1995 Regents of the University of Michigan. 24 * All rights reserved. 25 * 26 * Redistribution and use in source and binary forms are permitted 27 * provided that this notice is preserved and that due credit is given 28 * to the University of Michigan at Ann Arbor. The name of the University 29 * may not be used to endorse or promote products derived from this 30 * software without specific prior written permission. This software 31 * is provided ``as is'' without express or implied warranty. 32 */ 33 34 #include "portable.h" 35 36 #include <stdio.h> 37 38 #include <ac/socket.h> 39 #include <ac/string.h> 40 41 #include "slap.h" 42 43 int 44 do_modrdn( 45 Operation *op, 46 SlapReply *rs 47 ) 48 { 49 struct berval dn = BER_BVNULL; 50 struct berval newrdn = BER_BVNULL; 51 struct berval newSuperior = BER_BVNULL; 52 ber_int_t deloldrdn; 53 54 struct berval pnewSuperior = BER_BVNULL; 55 56 struct berval nnewSuperior = BER_BVNULL; 57 58 ber_len_t length; 59 60 Debug( LDAP_DEBUG_TRACE, "%s do_modrdn\n", 61 op->o_log_prefix, 0, 0 ); 62 /* 63 * Parse the modrdn request. It looks like this: 64 * 65 * ModifyRDNRequest := SEQUENCE { 66 * entry DistinguishedName, 67 * newrdn RelativeDistinguishedName 68 * deleteoldrdn BOOLEAN, 69 * newSuperior [0] LDAPDN OPTIONAL (v3 Only!) 70 * } 71 */ 72 73 if ( ber_scanf( op->o_ber, "{mmb", &dn, &newrdn, &deloldrdn ) 74 == LBER_ERROR ) 75 { 76 Debug( LDAP_DEBUG_ANY, "%s do_modrdn: ber_scanf failed\n", 77 op->o_log_prefix, 0, 0 ); 78 send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" ); 79 return SLAPD_DISCONNECT; 80 } 81 82 /* Check for newSuperior parameter, if present scan it */ 83 84 if ( ber_peek_tag( op->o_ber, &length ) == LDAP_TAG_NEWSUPERIOR ) { 85 if ( op->o_protocol < LDAP_VERSION3 ) { 86 /* Connection record indicates v2 but field 87 * newSuperior is present: report error. 88 */ 89 Debug( LDAP_DEBUG_ANY, 90 "%s do_modrdn: newSuperior requires LDAPv3\n", 91 op->o_log_prefix, 0, 0 ); 92 93 send_ldap_discon( op, rs, 94 LDAP_PROTOCOL_ERROR, "newSuperior requires LDAPv3" ); 95 rs->sr_err = SLAPD_DISCONNECT; 96 goto cleanup; 97 } 98 99 if ( ber_scanf( op->o_ber, "m", &newSuperior ) 100 == LBER_ERROR ) { 101 102 Debug( LDAP_DEBUG_ANY, "%s do_modrdn: ber_scanf(\"m\") failed\n", 103 op->o_log_prefix, 0, 0 ); 104 105 send_ldap_discon( op, rs, 106 LDAP_PROTOCOL_ERROR, "decoding error" ); 107 rs->sr_err = SLAPD_DISCONNECT; 108 goto cleanup; 109 } 110 op->orr_newSup = &pnewSuperior; 111 op->orr_nnewSup = &nnewSuperior; 112 } 113 114 Debug( LDAP_DEBUG_ARGS, 115 "do_modrdn: dn (%s) newrdn (%s) newsuperior (%s)\n", 116 dn.bv_val, newrdn.bv_val, 117 newSuperior.bv_len ? newSuperior.bv_val : "" ); 118 119 if ( ber_scanf( op->o_ber, /*{*/ "}") == LBER_ERROR ) { 120 Debug( LDAP_DEBUG_ANY, "%s do_modrdn: ber_scanf failed\n", 121 op->o_log_prefix, 0, 0 ); 122 send_ldap_discon( op, rs, 123 LDAP_PROTOCOL_ERROR, "decoding error" ); 124 rs->sr_err = SLAPD_DISCONNECT; 125 goto cleanup; 126 } 127 128 if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) { 129 Debug( LDAP_DEBUG_ANY, "%s do_modrdn: get_ctrls failed\n", 130 op->o_log_prefix, 0, 0 ); 131 /* get_ctrls has sent results. Now clean up. */ 132 goto cleanup; 133 } 134 135 rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn, op->o_tmpmemctx ); 136 if( rs->sr_err != LDAP_SUCCESS ) { 137 Debug( LDAP_DEBUG_ANY, "%s do_modrdn: invalid dn (%s)\n", 138 op->o_log_prefix, dn.bv_val, 0 ); 139 send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" ); 140 goto cleanup; 141 } 142 143 /* FIXME: should have/use rdnPretty / rdnNormalize routines */ 144 145 rs->sr_err = dnPrettyNormal( NULL, &newrdn, &op->orr_newrdn, &op->orr_nnewrdn, op->o_tmpmemctx ); 146 if( rs->sr_err != LDAP_SUCCESS ) { 147 Debug( LDAP_DEBUG_ANY, "%s do_modrdn: invalid newrdn (%s)\n", 148 op->o_log_prefix, newrdn.bv_val, 0 ); 149 send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid new RDN" ); 150 goto cleanup; 151 } 152 153 if( rdn_validate( &op->orr_newrdn ) != LDAP_SUCCESS ) { 154 Debug( LDAP_DEBUG_ANY, "%s do_modrdn: invalid rdn (%s)\n", 155 op->o_log_prefix, op->orr_newrdn.bv_val, 0 ); 156 send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid new RDN" ); 157 goto cleanup; 158 } 159 160 if( op->orr_newSup ) { 161 rs->sr_err = dnPrettyNormal( NULL, &newSuperior, &pnewSuperior, 162 &nnewSuperior, op->o_tmpmemctx ); 163 if( rs->sr_err != LDAP_SUCCESS ) { 164 Debug( LDAP_DEBUG_ANY, 165 "%s do_modrdn: invalid newSuperior (%s)\n", 166 op->o_log_prefix, newSuperior.bv_val, 0 ); 167 send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid newSuperior" ); 168 goto cleanup; 169 } 170 } 171 172 Statslog( LDAP_DEBUG_STATS, "%s MODRDN dn=\"%s\"\n", 173 op->o_log_prefix, op->o_req_dn.bv_val, 0, 0, 0 ); 174 175 op->orr_deleteoldrdn = deloldrdn; 176 op->orr_modlist = NULL; 177 178 /* prepare modlist of modifications from old/new RDN */ 179 rs->sr_err = slap_modrdn2mods( op, rs ); 180 if ( rs->sr_err != LDAP_SUCCESS ) { 181 send_ldap_result( op, rs ); 182 goto cleanup; 183 } 184 185 op->o_bd = frontendDB; 186 rs->sr_err = frontendDB->be_modrdn( op, rs ); 187 188 #ifdef LDAP_X_TXN 189 if( rs->sr_err == LDAP_X_TXN_SPECIFY_OKAY ) { 190 /* skip cleanup */ 191 } 192 #endif 193 194 cleanup: 195 op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx ); 196 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx ); 197 198 op->o_tmpfree( op->orr_newrdn.bv_val, op->o_tmpmemctx ); 199 op->o_tmpfree( op->orr_nnewrdn.bv_val, op->o_tmpmemctx ); 200 201 if ( op->orr_modlist != NULL ) 202 slap_mods_free( op->orr_modlist, 1 ); 203 204 if ( !BER_BVISNULL( &pnewSuperior ) ) { 205 op->o_tmpfree( pnewSuperior.bv_val, op->o_tmpmemctx ); 206 } 207 if ( !BER_BVISNULL( &nnewSuperior ) ) { 208 op->o_tmpfree( nnewSuperior.bv_val, op->o_tmpmemctx ); 209 } 210 211 return rs->sr_err; 212 } 213 214 int 215 fe_op_modrdn( Operation *op, SlapReply *rs ) 216 { 217 struct berval dest_ndn = BER_BVNULL, dest_pndn, pdn = BER_BVNULL; 218 BackendDB *op_be, *bd = op->o_bd; 219 ber_slen_t diff; 220 221 if( op->o_req_ndn.bv_len == 0 ) { 222 Debug( LDAP_DEBUG_ANY, "%s do_modrdn: root dse!\n", 223 op->o_log_prefix, 0, 0 ); 224 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 225 "cannot rename the root DSE" ); 226 goto cleanup; 227 228 } else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) { 229 Debug( LDAP_DEBUG_ANY, "%s do_modrdn: subschema subentry: %s (%ld)\n", 230 op->o_log_prefix, frontendDB->be_schemandn.bv_val, (long)frontendDB->be_schemandn.bv_len ); 231 232 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 233 "cannot rename subschema subentry" ); 234 goto cleanup; 235 } 236 237 if( op->orr_nnewSup ) { 238 dest_pndn = *op->orr_nnewSup; 239 } else { 240 dnParent( &op->o_req_ndn, &dest_pndn ); 241 } 242 build_new_dn( &dest_ndn, &dest_pndn, &op->orr_nnewrdn, op->o_tmpmemctx ); 243 244 diff = (ber_slen_t) dest_ndn.bv_len - (ber_slen_t) op->o_req_ndn.bv_len; 245 if ( diff > 0 ? dnIsSuffix( &dest_ndn, &op->o_req_ndn ) 246 : diff < 0 && dnIsSuffix( &op->o_req_ndn, &dest_ndn ) ) 247 { 248 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 249 diff > 0 ? "cannot place an entry below itself" 250 : "cannot place an entry above itself" ); 251 goto cleanup; 252 } 253 254 /* 255 * We could be serving multiple database backends. Select the 256 * appropriate one, or send a referral to our "referral server" 257 * if we don't hold it. 258 */ 259 op->o_bd = select_backend( &op->o_req_ndn, 1 ); 260 if ( op->o_bd == NULL ) { 261 op->o_bd = bd; 262 rs->sr_ref = referral_rewrite( default_referral, 263 NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 264 if (!rs->sr_ref) rs->sr_ref = default_referral; 265 266 if ( rs->sr_ref != NULL ) { 267 rs->sr_err = LDAP_REFERRAL; 268 send_ldap_result( op, rs ); 269 270 if (rs->sr_ref != default_referral) ber_bvarray_free( rs->sr_ref ); 271 } else { 272 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 273 "no global superior knowledge" ); 274 } 275 goto cleanup; 276 } 277 278 /* If we've got a glued backend, check the real backend */ 279 op_be = op->o_bd; 280 if ( SLAP_GLUE_INSTANCE( op->o_bd )) { 281 op->o_bd = select_backend( &op->o_req_ndn, 0 ); 282 } 283 284 /* check restrictions */ 285 if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) { 286 send_ldap_result( op, rs ); 287 goto cleanup; 288 } 289 290 /* check for referrals */ 291 if ( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) { 292 goto cleanup; 293 } 294 295 /* check that destination DN is in the same backend as source DN */ 296 if ( select_backend( &dest_ndn, 0 ) != op->o_bd ) { 297 send_ldap_error( op, rs, LDAP_AFFECTS_MULTIPLE_DSAS, 298 "cannot rename between DSAs" ); 299 goto cleanup; 300 } 301 302 /* 303 * do the modrdn if 1 && (2 || 3) 304 * 1) there is a modrdn function implemented in this backend; 305 * 2) this backend is master for what it holds; 306 * 3) it's a replica and the dn supplied is the update_ndn. 307 */ 308 if ( op->o_bd->be_modrdn ) { 309 /* do the update here */ 310 int repl_user = be_isupdate( op ); 311 if ( !SLAP_SINGLE_SHADOW(op->o_bd) || repl_user ) 312 { 313 op->o_bd = op_be; 314 op->o_bd->be_modrdn( op, rs ); 315 316 if ( op->o_bd->be_delete ) { 317 struct berval org_req_dn = BER_BVNULL; 318 struct berval org_req_ndn = BER_BVNULL; 319 struct berval org_dn = BER_BVNULL; 320 struct berval org_ndn = BER_BVNULL; 321 int org_managedsait; 322 323 org_req_dn = op->o_req_dn; 324 org_req_ndn = op->o_req_ndn; 325 org_dn = op->o_dn; 326 org_ndn = op->o_ndn; 327 org_managedsait = get_manageDSAit( op ); 328 op->o_dn = op->o_bd->be_rootdn; 329 op->o_ndn = op->o_bd->be_rootndn; 330 op->o_managedsait = SLAP_CONTROL_NONCRITICAL; 331 332 while ( rs->sr_err == LDAP_SUCCESS && 333 op->o_delete_glue_parent ) { 334 op->o_delete_glue_parent = 0; 335 if ( !be_issuffix( op->o_bd, &op->o_req_ndn )) { 336 slap_callback cb = { NULL }; 337 cb.sc_response = slap_null_cb; 338 dnParent( &op->o_req_ndn, &pdn ); 339 op->o_req_dn = pdn; 340 op->o_req_ndn = pdn; 341 op->o_callback = &cb; 342 op->o_bd->be_delete( op, rs ); 343 } else { 344 break; 345 } 346 } 347 op->o_managedsait = org_managedsait; 348 op->o_dn = org_dn; 349 op->o_ndn = org_ndn; 350 op->o_req_dn = org_req_dn; 351 op->o_req_ndn = org_req_ndn; 352 op->o_delete_glue_parent = 0; 353 } 354 355 } else { 356 BerVarray defref = op->o_bd->be_update_refs 357 ? op->o_bd->be_update_refs : default_referral; 358 359 if ( defref != NULL ) { 360 rs->sr_ref = referral_rewrite( defref, 361 NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT ); 362 if (!rs->sr_ref) rs->sr_ref = defref; 363 364 rs->sr_err = LDAP_REFERRAL; 365 send_ldap_result( op, rs ); 366 367 if (rs->sr_ref != defref) ber_bvarray_free( rs->sr_ref ); 368 } else { 369 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 370 "shadow context; no update referral" ); 371 } 372 } 373 } else { 374 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, 375 "operation not supported within namingContext" ); 376 } 377 378 cleanup:; 379 if ( dest_ndn.bv_val != NULL ) 380 ber_memfree_x( dest_ndn.bv_val, op->o_tmpmemctx ); 381 op->o_bd = bd; 382 return rs->sr_err; 383 } 384 385 int 386 slap_modrdn2mods( 387 Operation *op, 388 SlapReply *rs ) 389 { 390 int a_cnt, d_cnt; 391 LDAPRDN old_rdn = NULL; 392 LDAPRDN new_rdn = NULL; 393 394 assert( !BER_BVISEMPTY( &op->oq_modrdn.rs_newrdn ) ); 395 assert( !op->orr_deleteoldrdn || !BER_BVISEMPTY( &op->o_req_dn ) ); 396 397 if ( ldap_bv2rdn_x( &op->oq_modrdn.rs_newrdn, &new_rdn, 398 (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) ) { 399 Debug( LDAP_DEBUG_TRACE, 400 "%s slap_modrdn2mods: can't figure out " 401 "type(s)/value(s) of newrdn\n", 402 op->o_log_prefix, 0, 0 ); 403 rs->sr_err = LDAP_INVALID_DN_SYNTAX; 404 rs->sr_text = "unknown type(s) used in RDN"; 405 goto done; 406 } 407 408 if ( op->oq_modrdn.rs_deleteoldrdn ) { 409 if ( ldap_bv2rdn_x( &op->o_req_dn, &old_rdn, 410 (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) ) { 411 Debug( LDAP_DEBUG_TRACE, 412 "%s slap_modrdn2mods: can't figure out " 413 "type(s)/value(s) of oldrdn\n", 414 op->o_log_prefix, 0, 0 ); 415 rs->sr_err = LDAP_OTHER; 416 rs->sr_text = "cannot parse RDN from old DN"; 417 goto done; 418 } 419 } 420 rs->sr_text = NULL; 421 422 /* Add new attribute values to the entry */ 423 for ( a_cnt = 0; new_rdn[a_cnt]; a_cnt++ ) { 424 AttributeDescription *desc = NULL; 425 Modifications *mod_tmp; 426 427 rs->sr_err = slap_bv2ad( &new_rdn[a_cnt]->la_attr, &desc, &rs->sr_text ); 428 429 if ( rs->sr_err != LDAP_SUCCESS ) { 430 Debug( LDAP_DEBUG_TRACE, 431 "%s slap_modrdn2mods: %s: %s (new)\n", 432 op->o_log_prefix, 433 rs->sr_text, 434 new_rdn[ a_cnt ]->la_attr.bv_val ); 435 goto done; 436 } 437 438 /* Apply modification */ 439 mod_tmp = ( Modifications * )ch_malloc( sizeof( Modifications ) ); 440 mod_tmp->sml_desc = desc; 441 BER_BVZERO( &mod_tmp->sml_type ); 442 mod_tmp->sml_numvals = 1; 443 mod_tmp->sml_values = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) ); 444 ber_dupbv( &mod_tmp->sml_values[0], &new_rdn[a_cnt]->la_value ); 445 mod_tmp->sml_values[1].bv_val = NULL; 446 if( desc->ad_type->sat_equality->smr_normalize) { 447 mod_tmp->sml_nvalues = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) ); 448 (void) (*desc->ad_type->sat_equality->smr_normalize)( 449 SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, 450 desc->ad_type->sat_syntax, 451 desc->ad_type->sat_equality, 452 &mod_tmp->sml_values[0], 453 &mod_tmp->sml_nvalues[0], NULL ); 454 mod_tmp->sml_nvalues[1].bv_val = NULL; 455 } else { 456 mod_tmp->sml_nvalues = NULL; 457 } 458 mod_tmp->sml_op = SLAP_MOD_SOFTADD; 459 mod_tmp->sml_flags = 0; 460 mod_tmp->sml_next = op->orr_modlist; 461 op->orr_modlist = mod_tmp; 462 } 463 464 /* Remove old rdn value if required */ 465 if ( op->orr_deleteoldrdn ) { 466 for ( d_cnt = 0; old_rdn[d_cnt]; d_cnt++ ) { 467 AttributeDescription *desc = NULL; 468 Modifications *mod_tmp; 469 470 rs->sr_err = slap_bv2ad( &old_rdn[d_cnt]->la_attr, &desc, &rs->sr_text ); 471 if ( rs->sr_err != LDAP_SUCCESS ) { 472 Debug( LDAP_DEBUG_TRACE, 473 "%s slap_modrdn2mods: %s: %s (old)\n", 474 op->o_log_prefix, 475 rs->sr_text, 476 old_rdn[d_cnt]->la_attr.bv_val ); 477 goto done; 478 } 479 480 /* Apply modification */ 481 mod_tmp = ( Modifications * )ch_malloc( sizeof( Modifications ) ); 482 mod_tmp->sml_desc = desc; 483 BER_BVZERO( &mod_tmp->sml_type ); 484 mod_tmp->sml_numvals = 1; 485 mod_tmp->sml_values = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) ); 486 ber_dupbv( &mod_tmp->sml_values[0], &old_rdn[d_cnt]->la_value ); 487 mod_tmp->sml_values[1].bv_val = NULL; 488 if( desc->ad_type->sat_equality->smr_normalize) { 489 mod_tmp->sml_nvalues = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) ); 490 (void) (*desc->ad_type->sat_equality->smr_normalize)( 491 SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, 492 desc->ad_type->sat_syntax, 493 desc->ad_type->sat_equality, 494 &mod_tmp->sml_values[0], 495 &mod_tmp->sml_nvalues[0], NULL ); 496 mod_tmp->sml_nvalues[1].bv_val = NULL; 497 } else { 498 mod_tmp->sml_nvalues = NULL; 499 } 500 mod_tmp->sml_op = LDAP_MOD_DELETE; 501 mod_tmp->sml_flags = 0; 502 mod_tmp->sml_next = op->orr_modlist; 503 op->orr_modlist = mod_tmp; 504 } 505 } 506 507 done: 508 509 /* LDAP v2 supporting correct attribute handling. */ 510 if ( rs->sr_err != LDAP_SUCCESS && op->orr_modlist != NULL ) { 511 Modifications *tmp; 512 513 for ( ; op->orr_modlist != NULL; op->orr_modlist = tmp ) { 514 tmp = op->orr_modlist->sml_next; 515 ch_free( op->orr_modlist ); 516 } 517 } 518 519 if ( new_rdn != NULL ) { 520 ldap_rdnfree_x( new_rdn, op->o_tmpmemctx ); 521 } 522 if ( old_rdn != NULL ) { 523 ldap_rdnfree_x( old_rdn, op->o_tmpmemctx ); 524 } 525 526 return rs->sr_err; 527 } 528 529