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