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
backsql_delete_attr_f(void * v_at,void * v_bda)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
backsql_delete_all_attrs(Operation * op,SlapReply * rs,SQLHDBC dbh,backsql_entryID * eid)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
backsql_delete_int(Operation * op,SlapReply * rs,SQLHDBC dbh,SQLHSTMT * sthp,backsql_entryID * eid,Entry ** ep)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
backsql_tree_delete_search_cb(Operation * op,SlapReply * rs)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
backsql_tree_delete(Operation * op,SlapReply * rs,SQLHDBC dbh,SQLHSTMT * sthp)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
backsql_delete(Operation * op,SlapReply * rs)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