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