1 /* $NetBSD: modrdn.c,v 1.3 2021/08/14 16:15:01 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1999-2021 The OpenLDAP Foundation. 7 * Portions Copyright 1999 Dmitry Kovalev. 8 * Portions Copyright 2002 Pierangelo Masarati. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted only as authorized by the OpenLDAP 13 * Public License. 14 * 15 * A copy of this license is available in the file LICENSE in the 16 * top-level directory of the distribution or, alternatively, at 17 * <http://www.OpenLDAP.org/license.html>. 18 */ 19 /* ACKNOWLEDGEMENTS: 20 * This work was initially developed by Dmitry Kovalev for inclusion 21 * by OpenLDAP Software. Additional significant contributors include 22 * Pierangelo Masarati. 23 */ 24 25 #include <sys/cdefs.h> 26 __RCSID("$NetBSD: modrdn.c,v 1.3 2021/08/14 16:15:01 christos Exp $"); 27 28 #include "portable.h" 29 30 #include <stdio.h> 31 #include <sys/types.h> 32 #include "ac/string.h" 33 34 #include "slap.h" 35 #include "proto-sql.h" 36 37 int 38 backsql_modrdn( Operation *op, SlapReply *rs ) 39 { 40 backsql_info *bi = (backsql_info*)op->o_bd->be_private; 41 SQLHDBC dbh = SQL_NULL_HDBC; 42 SQLHSTMT sth = SQL_NULL_HSTMT; 43 RETCODE rc; 44 backsql_entryID e_id = BACKSQL_ENTRYID_INIT, 45 n_id = BACKSQL_ENTRYID_INIT; 46 backsql_srch_info bsi = { 0 }; 47 backsql_oc_map_rec *oc = NULL; 48 struct berval pdn = BER_BVNULL, pndn = BER_BVNULL, 49 *new_pdn = NULL, *new_npdn = NULL, 50 new_dn = BER_BVNULL, new_ndn = BER_BVNULL, 51 realnew_dn = BER_BVNULL; 52 Entry r = { 0 }, 53 p = { 0 }, 54 n = { 0 }, 55 *e = NULL; 56 int manageDSAit = get_manageDSAit( op ); 57 struct berval *newSuperior = op->oq_modrdn.rs_newSup; 58 59 Debug( LDAP_DEBUG_TRACE, "==>backsql_modrdn() renaming entry \"%s\", " 60 "newrdn=\"%s\", newSuperior=\"%s\"\n", 61 op->o_req_dn.bv_val, op->oq_modrdn.rs_newrdn.bv_val, 62 newSuperior ? newSuperior->bv_val : "(NULL)" ); 63 64 rs->sr_err = backsql_get_db_conn( op, &dbh ); 65 if ( rs->sr_err != LDAP_SUCCESS ) { 66 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): " 67 "could not get connection handle - exiting\n" ); 68 rs->sr_text = ( rs->sr_err == LDAP_OTHER ) 69 ? "SQL-backend error" : NULL; 70 e = NULL; 71 goto done; 72 } 73 74 bsi.bsi_e = &r; 75 rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn, 76 LDAP_SCOPE_BASE, 77 (time_t)(-1), NULL, dbh, op, rs, 78 slap_anlist_all_attributes, 79 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY | BACKSQL_ISF_GET_OC ) ); 80 switch ( rs->sr_err ) { 81 case LDAP_SUCCESS: 82 break; 83 84 case LDAP_REFERRAL: 85 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) && 86 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) ) 87 { 88 rs->sr_err = LDAP_SUCCESS; 89 rs->sr_text = NULL; 90 rs->sr_matched = NULL; 91 if ( rs->sr_ref ) { 92 ber_bvarray_free( rs->sr_ref ); 93 rs->sr_ref = NULL; 94 } 95 break; 96 } 97 e = &r; 98 /* fallthru */ 99 100 default: 101 Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): " 102 "could not retrieve modrdnDN ID - no such entry\n" ); 103 if ( !BER_BVISNULL( &r.e_nname ) ) { 104 /* FIXME: should always be true! */ 105 e = &r; 106 107 } else { 108 e = NULL; 109 } 110 goto done; 111 } 112 113 Debug( LDAP_DEBUG_TRACE, 114 " backsql_modrdn(): entry id=" BACKSQL_IDFMT "\n", 115 BACKSQL_IDARG(e_id.eid_id) ); 116 117 if ( get_assert( op ) && 118 ( test_filter( op, &r, get_assertion( op ) ) 119 != LDAP_COMPARE_TRUE ) ) 120 { 121 rs->sr_err = LDAP_ASSERTION_FAILED; 122 e = &r; 123 goto done; 124 } 125 126 if ( backsql_has_children( op, dbh, &op->o_req_ndn ) == LDAP_COMPARE_TRUE ) { 127 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): " 128 "entry \"%s\" has children\n", 129 op->o_req_dn.bv_val ); 130 rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF; 131 rs->sr_text = "subtree rename not supported"; 132 e = &r; 133 goto done; 134 } 135 136 /* 137 * Check for entry access to target 138 */ 139 if ( !access_allowed( op, &r, slap_schema.si_ad_entry, 140 NULL, ACL_WRITE, NULL ) ) { 141 Debug( LDAP_DEBUG_TRACE, " no access to entry\n" ); 142 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 143 goto done; 144 } 145 146 dnParent( &op->o_req_dn, &pdn ); 147 dnParent( &op->o_req_ndn, &pndn ); 148 149 /* 150 * namingContext "" is not supported 151 */ 152 if ( BER_BVISEMPTY( &pdn ) ) { 153 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): " 154 "parent is \"\" - aborting\n" ); 155 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 156 rs->sr_text = "not allowed within namingContext"; 157 e = NULL; 158 goto done; 159 } 160 161 /* 162 * Check for children access to parent 163 */ 164 bsi.bsi_e = &p; 165 e_id = bsi.bsi_base_id; 166 memset( &bsi.bsi_base_id, 0, sizeof( bsi.bsi_base_id ) ); 167 rs->sr_err = backsql_init_search( &bsi, &pndn, 168 LDAP_SCOPE_BASE, 169 (time_t)(-1), NULL, dbh, op, rs, 170 slap_anlist_all_attributes, 171 BACKSQL_ISF_GET_ENTRY ); 172 173 Debug( LDAP_DEBUG_TRACE, 174 " backsql_modrdn(): old parent entry id is " BACKSQL_IDFMT "\n", 175 BACKSQL_IDARG(bsi.bsi_base_id.eid_id) ); 176 177 if ( rs->sr_err != LDAP_SUCCESS ) { 178 Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): " 179 "could not retrieve renameDN ID - no such entry\n" ); 180 e = &p; 181 goto done; 182 } 183 184 if ( !access_allowed( op, &p, slap_schema.si_ad_children, NULL, 185 newSuperior ? ACL_WDEL : ACL_WRITE, NULL ) ) 186 { 187 Debug( LDAP_DEBUG_TRACE, " no access to parent\n" ); 188 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 189 goto done; 190 } 191 192 if ( newSuperior ) { 193 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx ); 194 195 /* 196 * namingContext "" is not supported 197 */ 198 if ( BER_BVISEMPTY( newSuperior ) ) { 199 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): " 200 "newSuperior is \"\" - aborting\n" ); 201 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 202 rs->sr_text = "not allowed within namingContext"; 203 e = NULL; 204 goto done; 205 } 206 207 new_pdn = newSuperior; 208 new_npdn = op->oq_modrdn.rs_nnewSup; 209 210 /* 211 * Check for children access to new parent 212 */ 213 bsi.bsi_e = &n; 214 rs->sr_err = backsql_init_search( &bsi, new_npdn, 215 LDAP_SCOPE_BASE, 216 (time_t)(-1), NULL, dbh, op, rs, 217 slap_anlist_all_attributes, 218 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) ); 219 if ( rs->sr_err != LDAP_SUCCESS ) { 220 Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): " 221 "could not retrieve renameDN ID - no such entry\n" ); 222 e = &n; 223 goto done; 224 } 225 226 n_id = bsi.bsi_base_id; 227 228 Debug( LDAP_DEBUG_TRACE, 229 " backsql_modrdn(): new parent entry id=" BACKSQL_IDFMT "\n", 230 BACKSQL_IDARG(n_id.eid_id) ); 231 232 if ( !access_allowed( op, &n, slap_schema.si_ad_children, 233 NULL, ACL_WADD, NULL ) ) { 234 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): " 235 "no access to new parent \"%s\"\n", 236 new_pdn->bv_val ); 237 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 238 e = &n; 239 goto done; 240 } 241 242 } else { 243 n_id = bsi.bsi_base_id; 244 new_pdn = &pdn; 245 new_npdn = &pndn; 246 } 247 248 memset( &bsi.bsi_base_id, 0, sizeof( bsi.bsi_base_id ) ); 249 250 if ( newSuperior && dn_match( &pndn, new_npdn ) ) { 251 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): " 252 "newSuperior is equal to old parent - ignored\n" ); 253 newSuperior = NULL; 254 } 255 256 if ( newSuperior && dn_match( &op->o_req_ndn, new_npdn ) ) { 257 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): " 258 "newSuperior is equal to entry being moved " 259 "- aborting\n" ); 260 rs->sr_err = LDAP_OTHER; 261 rs->sr_text = "newSuperior is equal to old DN"; 262 e = &r; 263 goto done; 264 } 265 266 build_new_dn( &new_dn, new_pdn, &op->oq_modrdn.rs_newrdn, 267 op->o_tmpmemctx ); 268 build_new_dn( &new_ndn, new_npdn, &op->oq_modrdn.rs_nnewrdn, 269 op->o_tmpmemctx ); 270 271 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): new entry dn is \"%s\"\n", 272 new_dn.bv_val ); 273 274 realnew_dn = new_dn; 275 if ( backsql_api_dn2odbc( op, rs, &realnew_dn ) ) { 276 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(\"%s\"): " 277 "backsql_api_dn2odbc(\"%s\") failed\n", 278 op->o_req_dn.bv_val, realnew_dn.bv_val ); 279 SQLFreeStmt( sth, SQL_DROP ); 280 281 rs->sr_text = "SQL-backend error"; 282 rs->sr_err = LDAP_OTHER; 283 e = NULL; 284 goto done; 285 } 286 287 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): " 288 "executing renentry_stmt\n" ); 289 290 rc = backsql_Prepare( dbh, &sth, bi->sql_renentry_stmt, 0 ); 291 if ( rc != SQL_SUCCESS ) { 292 Debug( LDAP_DEBUG_TRACE, 293 " backsql_modrdn(): " 294 "error preparing renentry_stmt\n" ); 295 backsql_PrintErrors( bi->sql_db_env, dbh, 296 sth, rc ); 297 298 rs->sr_text = "SQL-backend error"; 299 rs->sr_err = LDAP_OTHER; 300 e = NULL; 301 goto done; 302 } 303 304 rc = backsql_BindParamBerVal( sth, 1, SQL_PARAM_INPUT, &realnew_dn ); 305 if ( rc != SQL_SUCCESS ) { 306 Debug( LDAP_DEBUG_TRACE, 307 " backsql_modrdn(): " 308 "error binding DN parameter for objectClass %s\n", 309 oc->bom_oc->soc_cname.bv_val ); 310 backsql_PrintErrors( bi->sql_db_env, dbh, 311 sth, rc ); 312 SQLFreeStmt( sth, SQL_DROP ); 313 314 rs->sr_text = "SQL-backend error"; 315 rs->sr_err = LDAP_OTHER; 316 e = NULL; 317 goto done; 318 } 319 320 rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT, &n_id.eid_id ); 321 if ( rc != SQL_SUCCESS ) { 322 Debug( LDAP_DEBUG_TRACE, 323 " backsql_modrdn(): " 324 "error binding parent ID parameter for objectClass %s\n", 325 oc->bom_oc->soc_cname.bv_val ); 326 backsql_PrintErrors( bi->sql_db_env, dbh, 327 sth, rc ); 328 SQLFreeStmt( sth, SQL_DROP ); 329 330 rs->sr_text = "SQL-backend error"; 331 rs->sr_err = LDAP_OTHER; 332 e = NULL; 333 goto done; 334 } 335 336 rc = backsql_BindParamID( sth, 3, SQL_PARAM_INPUT, &e_id.eid_keyval ); 337 if ( rc != SQL_SUCCESS ) { 338 Debug( LDAP_DEBUG_TRACE, 339 " backsql_modrdn(): " 340 "error binding entry ID parameter for objectClass %s\n", 341 oc->bom_oc->soc_cname.bv_val ); 342 backsql_PrintErrors( bi->sql_db_env, dbh, 343 sth, rc ); 344 SQLFreeStmt( sth, SQL_DROP ); 345 346 rs->sr_text = "SQL-backend error"; 347 rs->sr_err = LDAP_OTHER; 348 e = NULL; 349 goto done; 350 } 351 352 rc = backsql_BindParamID( sth, 4, SQL_PARAM_INPUT, &e_id.eid_id ); 353 if ( rc != SQL_SUCCESS ) { 354 Debug( LDAP_DEBUG_TRACE, 355 " backsql_modrdn(): " 356 "error binding ID parameter for objectClass %s\n", 357 oc->bom_oc->soc_cname.bv_val ); 358 backsql_PrintErrors( bi->sql_db_env, dbh, 359 sth, rc ); 360 SQLFreeStmt( sth, SQL_DROP ); 361 362 rs->sr_text = "SQL-backend error"; 363 rs->sr_err = LDAP_OTHER; 364 e = NULL; 365 goto done; 366 } 367 368 rc = SQLExecute( sth ); 369 if ( rc != SQL_SUCCESS ) { 370 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): " 371 "could not rename ldap_entries record\n" ); 372 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc ); 373 SQLFreeStmt( sth, SQL_DROP ); 374 rs->sr_err = LDAP_OTHER; 375 rs->sr_text = "SQL-backend error"; 376 e = NULL; 377 goto done; 378 } 379 SQLFreeStmt( sth, SQL_DROP ); 380 381 slap_mods_opattrs( op, &op->orr_modlist, 1 ); 382 383 assert( e_id.eid_oc != NULL ); 384 oc = e_id.eid_oc; 385 386 if ( op->orr_modlist != NULL ) { 387 rs->sr_err = backsql_modify_internal( op, rs, dbh, oc, &e_id, op->orr_modlist ); 388 slap_graduate_commit_csn( op ); 389 if ( rs->sr_err != LDAP_SUCCESS ) { 390 e = &r; 391 goto done; 392 } 393 } 394 395 if ( BACKSQL_CHECK_SCHEMA( bi ) ) { 396 char textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' }; 397 398 backsql_entry_clean( op, &r ); 399 (void)backsql_free_entryID( &e_id, 0, op->o_tmpmemctx ); 400 401 bsi.bsi_e = &r; 402 rs->sr_err = backsql_init_search( &bsi, &new_ndn, 403 LDAP_SCOPE_BASE, 404 (time_t)(-1), NULL, dbh, op, rs, 405 slap_anlist_all_attributes, 406 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) ); 407 switch ( rs->sr_err ) { 408 case LDAP_SUCCESS: 409 break; 410 411 case LDAP_REFERRAL: 412 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) && 413 dn_match( &new_ndn, &bsi.bsi_e->e_nname ) ) 414 { 415 rs->sr_err = LDAP_SUCCESS; 416 rs->sr_text = NULL; 417 rs->sr_matched = NULL; 418 if ( rs->sr_ref ) { 419 ber_bvarray_free( rs->sr_ref ); 420 rs->sr_ref = NULL; 421 } 422 break; 423 } 424 e = &r; 425 /* fallthru */ 426 427 default: 428 Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): " 429 "could not retrieve modrdnDN ID - no such entry\n" ); 430 if ( !BER_BVISNULL( &r.e_nname ) ) { 431 /* FIXME: should always be true! */ 432 e = &r; 433 434 } else { 435 e = NULL; 436 } 437 goto done; 438 } 439 440 e_id = bsi.bsi_base_id; 441 442 rs->sr_err = entry_schema_check( op, &r, NULL, 0, 0, NULL, 443 &rs->sr_text, textbuf, sizeof( textbuf ) ); 444 if ( rs->sr_err != LDAP_SUCCESS ) { 445 Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(\"%s\"): " 446 "entry failed schema check -- aborting\n", 447 r.e_name.bv_val ); 448 e = NULL; 449 goto done; 450 } 451 } 452 453 done:; 454 if ( e != NULL ) { 455 if ( !access_allowed( op, e, slap_schema.si_ad_entry, NULL, 456 ACL_DISCLOSE, NULL ) ) 457 { 458 rs->sr_err = LDAP_NO_SUCH_OBJECT; 459 rs->sr_text = NULL; 460 rs->sr_matched = NULL; 461 if ( rs->sr_ref ) { 462 ber_bvarray_free( rs->sr_ref ); 463 rs->sr_ref = NULL; 464 } 465 } 466 } 467 468 /* 469 * Commit only if all operations succeed 470 */ 471 if ( sth != SQL_NULL_HSTMT ) { 472 SQLUSMALLINT CompletionType = SQL_ROLLBACK; 473 474 if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) { 475 CompletionType = SQL_COMMIT; 476 } 477 478 SQLTransact( SQL_NULL_HENV, dbh, CompletionType ); 479 } 480 481 if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) { 482 rs->sr_err = LDAP_X_NO_OPERATION; 483 } 484 485 send_ldap_result( op, rs ); 486 slap_graduate_commit_csn( op ); 487 488 if ( !BER_BVISNULL( &realnew_dn ) && realnew_dn.bv_val != new_dn.bv_val ) { 489 ch_free( realnew_dn.bv_val ); 490 } 491 492 if ( !BER_BVISNULL( &new_dn ) ) { 493 slap_sl_free( new_dn.bv_val, op->o_tmpmemctx ); 494 } 495 496 if ( !BER_BVISNULL( &new_ndn ) ) { 497 slap_sl_free( new_ndn.bv_val, op->o_tmpmemctx ); 498 } 499 500 if ( !BER_BVISNULL( &e_id.eid_ndn ) ) { 501 (void)backsql_free_entryID( &e_id, 0, op->o_tmpmemctx ); 502 } 503 504 if ( !BER_BVISNULL( &n_id.eid_ndn ) ) { 505 (void)backsql_free_entryID( &n_id, 0, op->o_tmpmemctx ); 506 } 507 508 if ( !BER_BVISNULL( &r.e_nname ) ) { 509 backsql_entry_clean( op, &r ); 510 } 511 512 if ( !BER_BVISNULL( &p.e_nname ) ) { 513 backsql_entry_clean( op, &p ); 514 } 515 516 if ( !BER_BVISNULL( &n.e_nname ) ) { 517 backsql_entry_clean( op, &n ); 518 } 519 520 if ( rs->sr_ref ) { 521 ber_bvarray_free( rs->sr_ref ); 522 rs->sr_ref = NULL; 523 } 524 525 Debug( LDAP_DEBUG_TRACE, "<==backsql_modrdn()\n" ); 526 527 return rs->sr_err; 528 } 529 530