1 /* $NetBSD: delete.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: delete.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 typedef struct backsql_delete_attr_t { 38 Operation *op; 39 SlapReply *rs; 40 SQLHDBC dbh; 41 backsql_entryID *e_id; 42 } backsql_delete_attr_t; 43 44 static int 45 backsql_delete_attr_f( void *v_at, void *v_bda ) 46 { 47 backsql_at_map_rec *at = (backsql_at_map_rec *)v_at; 48 backsql_delete_attr_t *bda = (backsql_delete_attr_t *)v_bda; 49 int rc; 50 51 rc = backsql_modify_delete_all_values( bda->op, 52 bda->rs, bda->dbh, bda->e_id, at ); 53 54 if ( rc != LDAP_SUCCESS ) { 55 return BACKSQL_AVL_STOP; 56 } 57 58 return BACKSQL_AVL_CONTINUE; 59 } 60 61 static int 62 backsql_delete_all_attrs( 63 Operation *op, 64 SlapReply *rs, 65 SQLHDBC dbh, 66 backsql_entryID *eid ) 67 { 68 backsql_delete_attr_t bda; 69 int rc; 70 71 bda.op = op; 72 bda.rs = rs; 73 bda.dbh = dbh; 74 bda.e_id = eid; 75 76 rc = ldap_avl_apply( eid->eid_oc->bom_attrs, backsql_delete_attr_f, &bda, 77 BACKSQL_AVL_STOP, AVL_INORDER ); 78 if ( rc == BACKSQL_AVL_STOP ) { 79 return rs->sr_err; 80 } 81 82 return LDAP_SUCCESS; 83 } 84 85 static int 86 backsql_delete_int( 87 Operation *op, 88 SlapReply *rs, 89 SQLHDBC dbh, 90 SQLHSTMT *sthp, 91 backsql_entryID *eid, 92 Entry **ep ) 93 { 94 backsql_info *bi = (backsql_info*)op->o_bd->be_private; 95 SQLHSTMT sth = SQL_NULL_HSTMT; 96 RETCODE rc; 97 int prc = LDAP_SUCCESS; 98 /* first parameter no */ 99 SQLUSMALLINT pno = 0; 100 101 sth = *sthp; 102 103 /* ldap_avl_apply ... */ 104 rs->sr_err = backsql_delete_all_attrs( op, rs, dbh, eid ); 105 if ( rs->sr_err != LDAP_SUCCESS ) { 106 goto done; 107 } 108 109 rc = backsql_Prepare( dbh, &sth, eid->eid_oc->bom_delete_proc, 0 ); 110 if ( rc != SQL_SUCCESS ) { 111 Debug( LDAP_DEBUG_TRACE, 112 " backsql_delete(): " 113 "error preparing delete query\n" ); 114 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc ); 115 116 rs->sr_err = LDAP_OTHER; 117 rs->sr_text = "SQL-backend error"; 118 *ep = NULL; 119 goto done; 120 } 121 122 if ( BACKSQL_IS_DEL( eid->eid_oc->bom_expect_return ) ) { 123 pno = 1; 124 rc = backsql_BindParamInt( sth, 1, SQL_PARAM_OUTPUT, &prc ); 125 if ( rc != SQL_SUCCESS ) { 126 Debug( LDAP_DEBUG_TRACE, 127 " backsql_delete(): " 128 "error binding output parameter for objectClass %s\n", 129 eid->eid_oc->bom_oc->soc_cname.bv_val ); 130 backsql_PrintErrors( bi->sql_db_env, dbh, 131 sth, rc ); 132 SQLFreeStmt( sth, SQL_DROP ); 133 134 rs->sr_text = "SQL-backend error"; 135 rs->sr_err = LDAP_OTHER; 136 *ep = NULL; 137 goto done; 138 } 139 } 140 141 rc = backsql_BindParamID( sth, pno + 1, SQL_PARAM_INPUT, &eid->eid_keyval ); 142 if ( rc != SQL_SUCCESS ) { 143 Debug( LDAP_DEBUG_TRACE, 144 " backsql_delete(): " 145 "error binding keyval parameter for objectClass %s\n", 146 eid->eid_oc->bom_oc->soc_cname.bv_val ); 147 backsql_PrintErrors( bi->sql_db_env, dbh, 148 sth, rc ); 149 SQLFreeStmt( sth, SQL_DROP ); 150 151 rs->sr_text = "SQL-backend error"; 152 rs->sr_err = LDAP_OTHER; 153 *ep = NULL; 154 goto done; 155 } 156 157 rc = SQLExecute( sth ); 158 if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) { 159 rs->sr_err = LDAP_SUCCESS; 160 161 } else { 162 Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " 163 "delete_proc execution failed (rc=%d, prc=%d)\n", 164 rc, prc ); 165 166 167 if ( prc != LDAP_SUCCESS ) { 168 /* SQL procedure executed fine 169 * but returned an error */ 170 rs->sr_err = BACKSQL_SANITIZE_ERROR( prc ); 171 172 } else { 173 backsql_PrintErrors( bi->sql_db_env, dbh, 174 sth, rc ); 175 rs->sr_err = LDAP_OTHER; 176 } 177 SQLFreeStmt( sth, SQL_DROP ); 178 goto done; 179 } 180 SQLFreeStmt( sth, SQL_DROP ); 181 182 /* delete "auxiliary" objectClasses, if any... */ 183 rc = backsql_Prepare( dbh, &sth, bi->sql_delobjclasses_stmt, 0 ); 184 if ( rc != SQL_SUCCESS ) { 185 Debug( LDAP_DEBUG_TRACE, 186 " backsql_delete(): " 187 "error preparing ldap_entry_objclasses delete query\n" ); 188 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc ); 189 190 rs->sr_err = LDAP_OTHER; 191 rs->sr_text = "SQL-backend error"; 192 *ep = NULL; 193 goto done; 194 } 195 196 rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT, &eid->eid_id ); 197 if ( rc != SQL_SUCCESS ) { 198 Debug( LDAP_DEBUG_TRACE, 199 " backsql_delete(): " 200 "error binding auxiliary objectClasses " 201 "entry ID parameter for objectClass %s\n", 202 eid->eid_oc->bom_oc->soc_cname.bv_val ); 203 backsql_PrintErrors( bi->sql_db_env, dbh, 204 sth, rc ); 205 SQLFreeStmt( sth, SQL_DROP ); 206 207 rs->sr_text = "SQL-backend error"; 208 rs->sr_err = LDAP_OTHER; 209 *ep = NULL; 210 goto done; 211 } 212 213 rc = SQLExecute( sth ); 214 switch ( rc ) { 215 case SQL_NO_DATA: 216 /* apparently there were no "auxiliary" objectClasses 217 * for this entry... */ 218 case SQL_SUCCESS: 219 break; 220 221 default: 222 Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " 223 "failed to delete record from ldap_entry_objclasses\n" ); 224 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc ); 225 SQLFreeStmt( sth, SQL_DROP ); 226 rs->sr_err = LDAP_OTHER; 227 rs->sr_text = "SQL-backend error"; 228 *ep = NULL; 229 goto done; 230 } 231 SQLFreeStmt( sth, SQL_DROP ); 232 233 /* delete entry... */ 234 rc = backsql_Prepare( dbh, &sth, bi->sql_delentry_stmt, 0 ); 235 if ( rc != SQL_SUCCESS ) { 236 Debug( LDAP_DEBUG_TRACE, 237 " backsql_delete(): " 238 "error preparing ldap_entries delete query\n" ); 239 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc ); 240 241 rs->sr_err = LDAP_OTHER; 242 rs->sr_text = "SQL-backend error"; 243 *ep = NULL; 244 goto done; 245 } 246 247 rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT, &eid->eid_id ); 248 if ( rc != SQL_SUCCESS ) { 249 Debug( LDAP_DEBUG_TRACE, 250 " backsql_delete(): " 251 "error binding entry ID parameter " 252 "for objectClass %s\n", 253 eid->eid_oc->bom_oc->soc_cname.bv_val ); 254 backsql_PrintErrors( bi->sql_db_env, dbh, 255 sth, rc ); 256 SQLFreeStmt( sth, SQL_DROP ); 257 258 rs->sr_text = "SQL-backend error"; 259 rs->sr_err = LDAP_OTHER; 260 *ep = NULL; 261 goto done; 262 } 263 264 rc = SQLExecute( sth ); 265 if ( rc != SQL_SUCCESS ) { 266 Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " 267 "failed to delete record from ldap_entries\n" ); 268 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc ); 269 SQLFreeStmt( sth, SQL_DROP ); 270 rs->sr_err = LDAP_OTHER; 271 rs->sr_text = "SQL-backend error"; 272 *ep = NULL; 273 goto done; 274 } 275 SQLFreeStmt( sth, SQL_DROP ); 276 277 rs->sr_err = LDAP_SUCCESS; 278 *ep = NULL; 279 280 done:; 281 *sthp = sth; 282 283 return rs->sr_err; 284 } 285 286 typedef struct backsql_tree_delete_t { 287 Operation *btd_op; 288 int btd_rc; 289 backsql_entryID *btd_eid; 290 } backsql_tree_delete_t; 291 292 static int 293 backsql_tree_delete_search_cb( Operation *op, SlapReply *rs ) 294 { 295 if ( rs->sr_type == REP_SEARCH ) { 296 backsql_tree_delete_t *btd; 297 backsql_entryID *eid; 298 299 btd = (backsql_tree_delete_t *)op->o_callback->sc_private; 300 301 if ( !access_allowed( btd->btd_op, rs->sr_entry, 302 slap_schema.si_ad_entry, NULL, ACL_WDEL, NULL ) 303 || !access_allowed( btd->btd_op, rs->sr_entry, 304 slap_schema.si_ad_children, NULL, ACL_WDEL, NULL ) ) 305 { 306 btd->btd_rc = LDAP_INSUFFICIENT_ACCESS; 307 return rs->sr_err = LDAP_UNAVAILABLE; 308 } 309 310 assert( rs->sr_entry != NULL ); 311 assert( rs->sr_entry->e_private != NULL ); 312 313 eid = (backsql_entryID *)rs->sr_entry->e_private; 314 assert( eid->eid_oc != NULL ); 315 if ( eid->eid_oc == NULL || eid->eid_oc->bom_delete_proc == NULL ) { 316 btd->btd_rc = LDAP_UNWILLING_TO_PERFORM; 317 return rs->sr_err = LDAP_UNAVAILABLE; 318 } 319 320 eid = backsql_entryID_dup( eid, op->o_tmpmemctx ); 321 eid->eid_next = btd->btd_eid; 322 btd->btd_eid = eid; 323 } 324 325 return 0; 326 } 327 328 static int 329 backsql_tree_delete( 330 Operation *op, 331 SlapReply *rs, 332 SQLHDBC dbh, 333 SQLHSTMT *sthp ) 334 { 335 Operation op2 = *op; 336 slap_callback sc = { 0 }; 337 SlapReply rs2 = { REP_RESULT }; 338 backsql_tree_delete_t btd = { 0 }; 339 340 int rc; 341 342 /* 343 * - perform an internal subtree search as the rootdn 344 * - for each entry 345 * - check access 346 * - check objectClass and delete method(s) 347 * - for each entry 348 * - delete 349 * - if successful, commit 350 */ 351 352 op2.o_tag = LDAP_REQ_SEARCH; 353 op2.o_protocol = LDAP_VERSION3; 354 355 btd.btd_op = op; 356 sc.sc_private = &btd; 357 sc.sc_response = backsql_tree_delete_search_cb; 358 op2.o_callback = ≻ 359 360 op2.o_dn = op->o_bd->be_rootdn; 361 op2.o_ndn = op->o_bd->be_rootndn; 362 363 op2.o_managedsait = SLAP_CONTROL_CRITICAL; 364 365 op2.ors_scope = LDAP_SCOPE_SUBTREE; 366 op2.ors_deref = LDAP_DEREF_NEVER; 367 op2.ors_slimit = SLAP_NO_LIMIT; 368 op2.ors_tlimit = SLAP_NO_LIMIT; 369 op2.ors_filter = (Filter *)slap_filter_objectClass_pres; 370 op2.ors_filterstr = *slap_filterstr_objectClass_pres; 371 op2.ors_attrs = slap_anlist_all_attributes; 372 op2.ors_attrsonly = 0; 373 374 rc = op->o_bd->be_search( &op2, &rs2 ); 375 if ( rc != LDAP_SUCCESS ) { 376 rc = rs->sr_err = btd.btd_rc; 377 rs->sr_text = "subtree delete not possible"; 378 send_ldap_result( op, rs ); 379 goto clean; 380 } 381 382 for ( ; btd.btd_eid != NULL; 383 btd.btd_eid = backsql_free_entryID( btd.btd_eid, 384 1, op->o_tmpmemctx ) ) 385 { 386 Entry *e = (void *)0xbad; 387 rc = backsql_delete_int( op, rs, dbh, sthp, btd.btd_eid, &e ); 388 if ( rc != LDAP_SUCCESS ) { 389 break; 390 } 391 } 392 393 clean:; 394 for ( ; btd.btd_eid != NULL; 395 btd.btd_eid = backsql_free_entryID( btd.btd_eid, 396 1, op->o_tmpmemctx ) ) 397 ; 398 399 return rc; 400 } 401 402 int 403 backsql_delete( Operation *op, SlapReply *rs ) 404 { 405 SQLHDBC dbh = SQL_NULL_HDBC; 406 SQLHSTMT sth = SQL_NULL_HSTMT; 407 backsql_oc_map_rec *oc = NULL; 408 backsql_srch_info bsi = { 0 }; 409 backsql_entryID e_id = { 0 }; 410 Entry d = { 0 }, p = { 0 }, *e = NULL; 411 struct berval pdn = BER_BVNULL; 412 int manageDSAit = get_manageDSAit( op ); 413 414 Debug( LDAP_DEBUG_TRACE, "==>backsql_delete(): deleting entry \"%s\"\n", 415 op->o_req_ndn.bv_val ); 416 417 rs->sr_err = backsql_get_db_conn( op, &dbh ); 418 if ( rs->sr_err != LDAP_SUCCESS ) { 419 Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " 420 "could not get connection handle - exiting\n" ); 421 rs->sr_text = ( rs->sr_err == LDAP_OTHER ) 422 ? "SQL-backend error" : NULL; 423 e = NULL; 424 goto done; 425 } 426 427 /* 428 * Get the entry 429 */ 430 bsi.bsi_e = &d; 431 rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn, 432 LDAP_SCOPE_BASE, 433 (time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs, 434 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY | BACKSQL_ISF_GET_OC ) ); 435 switch ( rs->sr_err ) { 436 case LDAP_SUCCESS: 437 break; 438 439 case LDAP_REFERRAL: 440 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) && 441 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) ) 442 { 443 rs->sr_err = LDAP_SUCCESS; 444 rs->sr_text = NULL; 445 rs->sr_matched = NULL; 446 if ( rs->sr_ref ) { 447 ber_bvarray_free( rs->sr_ref ); 448 rs->sr_ref = NULL; 449 } 450 break; 451 } 452 e = &d; 453 /* fallthru */ 454 455 default: 456 Debug( LDAP_DEBUG_TRACE, "backsql_delete(): " 457 "could not retrieve deleteDN ID - no such entry\n" ); 458 if ( !BER_BVISNULL( &d.e_nname ) ) { 459 /* FIXME: should always be true! */ 460 e = &d; 461 462 } else { 463 e = NULL; 464 } 465 goto done; 466 } 467 468 if ( get_assert( op ) && 469 ( test_filter( op, &d, get_assertion( op ) ) 470 != LDAP_COMPARE_TRUE ) ) 471 { 472 rs->sr_err = LDAP_ASSERTION_FAILED; 473 e = &d; 474 goto done; 475 } 476 477 if ( !access_allowed( op, &d, slap_schema.si_ad_entry, 478 NULL, ACL_WDEL, NULL ) ) 479 { 480 Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " 481 "no write access to entry\n" ); 482 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 483 e = &d; 484 goto done; 485 } 486 487 rs->sr_err = backsql_has_children( op, dbh, &op->o_req_ndn ); 488 switch ( rs->sr_err ) { 489 case LDAP_COMPARE_FALSE: 490 rs->sr_err = LDAP_SUCCESS; 491 break; 492 493 case LDAP_COMPARE_TRUE: 494 #ifdef SLAP_CONTROL_X_TREE_DELETE 495 if ( get_treeDelete( op ) ) { 496 rs->sr_err = LDAP_SUCCESS; 497 break; 498 } 499 #endif /* SLAP_CONTROL_X_TREE_DELETE */ 500 501 Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " 502 "entry \"%s\" has children\n", 503 op->o_req_dn.bv_val ); 504 rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF; 505 rs->sr_text = "subordinate objects must be deleted first"; 506 /* fallthru */ 507 508 default: 509 e = &d; 510 goto done; 511 } 512 513 assert( bsi.bsi_base_id.eid_oc != NULL ); 514 oc = bsi.bsi_base_id.eid_oc; 515 if ( oc->bom_delete_proc == NULL ) { 516 Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " 517 "delete procedure is not defined " 518 "for this objectclass - aborting\n" ); 519 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 520 rs->sr_text = "operation not permitted within namingContext"; 521 e = NULL; 522 goto done; 523 } 524 525 /* 526 * Get the parent 527 */ 528 e_id = bsi.bsi_base_id; 529 memset( &bsi.bsi_base_id, 0, sizeof( bsi.bsi_base_id ) ); 530 if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) { 531 dnParent( &op->o_req_ndn, &pdn ); 532 bsi.bsi_e = &p; 533 rs->sr_err = backsql_init_search( &bsi, &pdn, 534 LDAP_SCOPE_BASE, 535 (time_t)(-1), NULL, dbh, op, rs, 536 slap_anlist_no_attrs, 537 BACKSQL_ISF_GET_ENTRY ); 538 if ( rs->sr_err != LDAP_SUCCESS ) { 539 Debug( LDAP_DEBUG_TRACE, "backsql_delete(): " 540 "could not retrieve deleteDN ID " 541 "- no such entry\n" ); 542 e = &p; 543 goto done; 544 } 545 546 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx ); 547 548 /* check parent for "children" acl */ 549 if ( !access_allowed( op, &p, slap_schema.si_ad_children, 550 NULL, ACL_WDEL, NULL ) ) 551 { 552 Debug( LDAP_DEBUG_TRACE, " backsql_delete(): " 553 "no write access to parent\n" ); 554 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 555 e = &p; 556 goto done; 557 558 } 559 } 560 561 e = &d; 562 #ifdef SLAP_CONTROL_X_TREE_DELETE 563 if ( get_treeDelete( op ) ) { 564 backsql_tree_delete( op, rs, dbh, &sth ); 565 if ( rs->sr_err == LDAP_OTHER || rs->sr_err == LDAP_SUCCESS ) 566 { 567 e = NULL; 568 } 569 570 } else 571 #endif /* SLAP_CONTROL_X_TREE_DELETE */ 572 { 573 backsql_delete_int( op, rs, dbh, &sth, &e_id, &e ); 574 } 575 576 /* 577 * Commit only if all operations succeed 578 */ 579 if ( sth != SQL_NULL_HSTMT ) { 580 SQLUSMALLINT CompletionType = SQL_ROLLBACK; 581 582 if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) { 583 assert( e == NULL ); 584 CompletionType = SQL_COMMIT; 585 } 586 587 SQLTransact( SQL_NULL_HENV, dbh, CompletionType ); 588 } 589 590 done:; 591 if ( e != NULL ) { 592 if ( !access_allowed( op, e, slap_schema.si_ad_entry, NULL, 593 ACL_DISCLOSE, NULL ) ) 594 { 595 rs->sr_err = LDAP_NO_SUCH_OBJECT; 596 rs->sr_text = NULL; 597 rs->sr_matched = NULL; 598 if ( rs->sr_ref ) { 599 ber_bvarray_free( rs->sr_ref ); 600 rs->sr_ref = NULL; 601 } 602 } 603 } 604 605 if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) { 606 rs->sr_err = LDAP_X_NO_OPERATION; 607 } 608 609 send_ldap_result( op, rs ); 610 611 Debug( LDAP_DEBUG_TRACE, "<==backsql_delete()\n" ); 612 613 if ( !BER_BVISNULL( &e_id.eid_ndn ) ) { 614 (void)backsql_free_entryID( &e_id, 0, op->o_tmpmemctx ); 615 } 616 617 if ( !BER_BVISNULL( &d.e_nname ) ) { 618 backsql_entry_clean( op, &d ); 619 } 620 621 if ( !BER_BVISNULL( &p.e_nname ) ) { 622 backsql_entry_clean( op, &p ); 623 } 624 625 if ( rs->sr_ref ) { 626 ber_bvarray_free( rs->sr_ref ); 627 rs->sr_ref = NULL; 628 } 629 630 return rs->sr_err; 631 } 632 633