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