]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/delete.c
Happy New Year
[openldap] / servers / slapd / back-sql / delete.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2018 The OpenLDAP Foundation.
5  * Portions Copyright 1999 Dmitry Kovalev.
6  * Portions Copyright 2002 Pierangelo Masarati.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Dmitry Kovalev for inclusion
19  * by OpenLDAP Software.  Additional significant contributors include
20  * Pierangelo Masarati.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include "ac/string.h"
28
29 #include "slap.h"
30 #include "proto-sql.h"
31
32 typedef struct backsql_delete_attr_t {
33         Operation               *op;
34         SlapReply               *rs;
35         SQLHDBC                 dbh; 
36         backsql_entryID         *e_id;
37 } backsql_delete_attr_t;
38
39 static int
40 backsql_delete_attr_f( void *v_at, void *v_bda )
41 {
42         backsql_at_map_rec      *at = (backsql_at_map_rec *)v_at;
43         backsql_delete_attr_t   *bda = (backsql_delete_attr_t *)v_bda;
44         int                     rc;
45
46         rc = backsql_modify_delete_all_values( bda->op,
47                         bda->rs, bda->dbh, bda->e_id, at );
48
49         if ( rc != LDAP_SUCCESS ) {
50                 return BACKSQL_AVL_STOP;
51         }
52
53         return BACKSQL_AVL_CONTINUE;
54 }
55
56 static int
57 backsql_delete_all_attrs(
58         Operation               *op,
59         SlapReply               *rs,
60         SQLHDBC                 dbh, 
61         backsql_entryID         *eid )
62 {
63         backsql_delete_attr_t   bda;
64         int                     rc;
65
66         bda.op = op;
67         bda.rs = rs;
68         bda.dbh = dbh;
69         bda.e_id = eid;
70         
71         rc = avl_apply( eid->eid_oc->bom_attrs, backsql_delete_attr_f, &bda,
72                         BACKSQL_AVL_STOP, AVL_INORDER );
73         if ( rc == BACKSQL_AVL_STOP ) {
74                 return rs->sr_err;
75         }
76
77         return LDAP_SUCCESS;
78 }
79
80 static int
81 backsql_delete_int(
82         Operation       *op,
83         SlapReply       *rs,
84         SQLHDBC         dbh,
85         SQLHSTMT        *sthp,
86         backsql_entryID *eid,
87         Entry           **ep )
88 {
89         backsql_info            *bi = (backsql_info*)op->o_bd->be_private;
90         SQLHSTMT                sth = SQL_NULL_HSTMT;
91         RETCODE                 rc;
92         int                     prc = LDAP_SUCCESS;
93         /* first parameter no */
94         SQLUSMALLINT            pno = 0;
95
96         sth = *sthp;
97
98         /* avl_apply ... */
99         rs->sr_err = backsql_delete_all_attrs( op, rs, dbh, eid );
100         if ( rs->sr_err != LDAP_SUCCESS ) {
101                 goto done;
102         }
103
104         rc = backsql_Prepare( dbh, &sth, eid->eid_oc->bom_delete_proc, 0 );
105         if ( rc != SQL_SUCCESS ) {
106                 Debug( LDAP_DEBUG_TRACE,
107                         "   backsql_delete(): "
108                         "error preparing delete query\n", 
109                         0, 0, 0 );
110                 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
111
112                 rs->sr_err = LDAP_OTHER;
113                 rs->sr_text = "SQL-backend error";
114                 *ep = NULL;
115                 goto done;
116         }
117
118         if ( BACKSQL_IS_DEL( eid->eid_oc->bom_expect_return ) ) {
119                 pno = 1;
120                 rc = backsql_BindParamInt( sth, 1, SQL_PARAM_OUTPUT, &prc );
121                 if ( rc != SQL_SUCCESS ) {
122                         Debug( LDAP_DEBUG_TRACE,
123                                 "   backsql_delete(): "
124                                 "error binding output parameter for objectClass %s\n",
125                                 eid->eid_oc->bom_oc->soc_cname.bv_val, 0, 0 );
126                         backsql_PrintErrors( bi->sql_db_env, dbh, 
127                                 sth, rc );
128                         SQLFreeStmt( sth, SQL_DROP );
129
130                         rs->sr_text = "SQL-backend error";
131                         rs->sr_err = LDAP_OTHER;
132                         *ep = NULL;
133                         goto done;
134                 }
135         }
136
137         rc = backsql_BindParamID( sth, pno + 1, SQL_PARAM_INPUT, &eid->eid_keyval );
138         if ( rc != SQL_SUCCESS ) {
139                 Debug( LDAP_DEBUG_TRACE,
140                         "   backsql_delete(): "
141                         "error binding keyval parameter for objectClass %s\n",
142                         eid->eid_oc->bom_oc->soc_cname.bv_val, 0, 0 );
143                 backsql_PrintErrors( bi->sql_db_env, dbh, 
144                         sth, rc );
145                 SQLFreeStmt( sth, SQL_DROP );
146
147                 rs->sr_text = "SQL-backend error";
148                 rs->sr_err = LDAP_OTHER;
149                 *ep = NULL;
150                 goto done;
151         }
152
153         rc = SQLExecute( sth );
154         if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
155                 rs->sr_err = LDAP_SUCCESS;
156
157         } else {
158                 Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
159                         "delete_proc execution failed (rc=%d, prc=%d)\n",
160                         rc, prc, 0 );
161
162
163                 if ( prc != LDAP_SUCCESS ) {
164                         /* SQL procedure executed fine 
165                          * but returned an error */
166                         rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
167
168                 } else {
169                         backsql_PrintErrors( bi->sql_db_env, dbh,
170                                         sth, rc );
171                         rs->sr_err = LDAP_OTHER;
172                 }
173                 SQLFreeStmt( sth, SQL_DROP );
174                 goto done;
175         }
176         SQLFreeStmt( sth, SQL_DROP );
177
178         /* delete "auxiliary" objectClasses, if any... */
179         rc = backsql_Prepare( dbh, &sth, bi->sql_delobjclasses_stmt, 0 );
180         if ( rc != SQL_SUCCESS ) {
181                 Debug( LDAP_DEBUG_TRACE,
182                         "   backsql_delete(): "
183                         "error preparing ldap_entry_objclasses delete query\n", 
184                         0, 0, 0 );
185                 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
186
187                 rs->sr_err = LDAP_OTHER;
188                 rs->sr_text = "SQL-backend error";
189                 *ep = NULL;
190                 goto done;
191         }
192
193         rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT, &eid->eid_id );
194         if ( rc != SQL_SUCCESS ) {
195                 Debug( LDAP_DEBUG_TRACE,
196                         "   backsql_delete(): "
197                         "error binding auxiliary objectClasses "
198                         "entry ID parameter for objectClass %s\n",
199                         eid->eid_oc->bom_oc->soc_cname.bv_val, 0, 0 );
200                 backsql_PrintErrors( bi->sql_db_env, dbh, 
201                         sth, rc );
202                 SQLFreeStmt( sth, SQL_DROP );
203
204                 rs->sr_text = "SQL-backend error";
205                 rs->sr_err = LDAP_OTHER;
206                 *ep = NULL;
207                 goto done;
208         }
209
210         rc = SQLExecute( sth );
211         switch ( rc ) {
212         case SQL_NO_DATA:
213                 /* apparently there were no "auxiliary" objectClasses
214                  * for this entry... */
215         case SQL_SUCCESS:
216                 break;
217
218         default:
219                 Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
220                         "failed to delete record from ldap_entry_objclasses\n", 
221                         0, 0, 0 );
222                 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
223                 SQLFreeStmt( sth, SQL_DROP );
224                 rs->sr_err = LDAP_OTHER;
225                 rs->sr_text = "SQL-backend error";
226                 *ep = NULL;
227                 goto done;
228         }
229         SQLFreeStmt( sth, SQL_DROP );
230
231         /* delete entry... */
232         rc = backsql_Prepare( dbh, &sth, bi->sql_delentry_stmt, 0 );
233         if ( rc != SQL_SUCCESS ) {
234                 Debug( LDAP_DEBUG_TRACE,
235                         "   backsql_delete(): "
236                         "error preparing ldap_entries delete query\n", 
237                         0, 0, 0 );
238                 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
239
240                 rs->sr_err = LDAP_OTHER;
241                 rs->sr_text = "SQL-backend error";
242                 *ep = NULL;
243                 goto done;
244         }
245
246         rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT, &eid->eid_id );
247         if ( rc != SQL_SUCCESS ) {
248                 Debug( LDAP_DEBUG_TRACE,
249                         "   backsql_delete(): "
250                         "error binding entry ID parameter "
251                         "for objectClass %s\n",
252                         eid->eid_oc->bom_oc->soc_cname.bv_val, 0, 0 );
253                 backsql_PrintErrors( bi->sql_db_env, dbh, 
254                         sth, rc );
255                 SQLFreeStmt( sth, SQL_DROP );
256
257                 rs->sr_text = "SQL-backend error";
258                 rs->sr_err = LDAP_OTHER;
259                 *ep = NULL;
260                 goto done;
261         }
262
263         rc = SQLExecute( sth );
264         if ( rc != SQL_SUCCESS ) {
265                 Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
266                         "failed to delete record from ldap_entries\n", 
267                         0, 0, 0 );
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 = &sc;
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, 0, 0 );
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                         0, 0, 0 );
422                 rs->sr_text = ( rs->sr_err == LDAP_OTHER )
423                         ? "SQL-backend error" : NULL;
424                 e = NULL;
425                 goto done;
426         }
427
428         /*
429          * Get the entry
430          */
431         bsi.bsi_e = &d;
432         rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
433                         LDAP_SCOPE_BASE, 
434                         (time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs,
435                         ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY | BACKSQL_ISF_GET_OC ) );
436         switch ( rs->sr_err ) {
437         case LDAP_SUCCESS:
438                 break;
439
440         case LDAP_REFERRAL:
441                 if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
442                                 dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
443                 {
444                         rs->sr_err = LDAP_SUCCESS;
445                         rs->sr_text = NULL;
446                         rs->sr_matched = NULL;
447                         if ( rs->sr_ref ) {
448                                 ber_bvarray_free( rs->sr_ref );
449                                 rs->sr_ref = NULL;
450                         }
451                         break;
452                 }
453                 e = &d;
454                 /* fallthru */
455
456         default:
457                 Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
458                         "could not retrieve deleteDN ID - no such entry\n", 
459                         0, 0, 0 );
460                 if ( !BER_BVISNULL( &d.e_nname ) ) {
461                         /* FIXME: should always be true! */
462                         e = &d;
463
464                 } else {
465                         e = NULL;
466                 }
467                 goto done;
468         }
469
470         if ( get_assert( op ) &&
471                         ( test_filter( op, &d, get_assertion( op ) )
472                           != LDAP_COMPARE_TRUE ) )
473         {
474                 rs->sr_err = LDAP_ASSERTION_FAILED;
475                 e = &d;
476                 goto done;
477         }
478
479         if ( !access_allowed( op, &d, slap_schema.si_ad_entry, 
480                         NULL, ACL_WDEL, NULL ) )
481         {
482                 Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
483                         "no write access to entry\n", 
484                         0, 0, 0 );
485                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
486                 e = &d;
487                 goto done;
488         }
489
490         rs->sr_err = backsql_has_children( op, dbh, &op->o_req_ndn );
491         switch ( rs->sr_err ) {
492         case LDAP_COMPARE_FALSE:
493                 rs->sr_err = LDAP_SUCCESS;
494                 break;
495
496         case LDAP_COMPARE_TRUE:
497 #ifdef SLAP_CONTROL_X_TREE_DELETE
498                 if ( get_treeDelete( op ) ) {
499                         rs->sr_err = LDAP_SUCCESS;
500                         break;
501                 }
502 #endif /* SLAP_CONTROL_X_TREE_DELETE */
503
504                 Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
505                         "entry \"%s\" has children\n",
506                         op->o_req_dn.bv_val, 0, 0 );
507                 rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
508                 rs->sr_text = "subordinate objects must be deleted first";
509                 /* fallthru */
510
511         default:
512                 e = &d;
513                 goto done;
514         }
515
516         assert( bsi.bsi_base_id.eid_oc != NULL );
517         oc = bsi.bsi_base_id.eid_oc;
518         if ( oc->bom_delete_proc == NULL ) {
519                 Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
520                         "delete procedure is not defined "
521                         "for this objectclass - aborting\n", 0, 0, 0 );
522                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
523                 rs->sr_text = "operation not permitted within namingContext";
524                 e = NULL;
525                 goto done;
526         }
527
528         /*
529          * Get the parent
530          */
531         e_id = bsi.bsi_base_id;
532         memset( &bsi.bsi_base_id, 0, sizeof( bsi.bsi_base_id ) );
533         if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
534                 dnParent( &op->o_req_ndn, &pdn );
535                 bsi.bsi_e = &p;
536                 rs->sr_err = backsql_init_search( &bsi, &pdn,
537                                 LDAP_SCOPE_BASE, 
538                                 (time_t)(-1), NULL, dbh, op, rs,
539                                 slap_anlist_no_attrs,
540                                 BACKSQL_ISF_GET_ENTRY );
541                 if ( rs->sr_err != LDAP_SUCCESS ) {
542                         Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
543                                 "could not retrieve deleteDN ID "
544                                 "- no such entry\n", 
545                                 0, 0, 0 );
546                         e = &p;
547                         goto done;
548                 }
549
550                 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
551
552                 /* check parent for "children" acl */
553                 if ( !access_allowed( op, &p, slap_schema.si_ad_children, 
554                                 NULL, ACL_WDEL, NULL ) )
555                 {
556                         Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
557                                 "no write access to parent\n", 
558                                 0, 0, 0 );
559                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
560                         e = &p;
561                         goto done;
562
563                 }
564         }
565
566         e = &d;
567 #ifdef SLAP_CONTROL_X_TREE_DELETE
568         if ( get_treeDelete( op ) ) {
569                 backsql_tree_delete( op, rs, dbh, &sth );
570                 if ( rs->sr_err == LDAP_OTHER || rs->sr_err == LDAP_SUCCESS )
571                 {
572                         e = NULL;
573                 }
574
575         } else
576 #endif /* SLAP_CONTROL_X_TREE_DELETE */
577         {
578                 backsql_delete_int( op, rs, dbh, &sth, &e_id, &e );
579         }
580
581         /*
582          * Commit only if all operations succeed
583          */
584         if ( sth != SQL_NULL_HSTMT ) {
585                 SQLUSMALLINT    CompletionType = SQL_ROLLBACK;
586         
587                 if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
588                         assert( e == NULL );
589                         CompletionType = SQL_COMMIT;
590                 }
591
592                 SQLTransact( SQL_NULL_HENV, dbh, CompletionType );
593         }
594
595 done:;
596         if ( e != NULL ) {
597                 if ( !access_allowed( op, e, slap_schema.si_ad_entry, NULL,
598                                         ACL_DISCLOSE, NULL ) )
599                 {
600                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
601                         rs->sr_text = NULL;
602                         rs->sr_matched = NULL;
603                         if ( rs->sr_ref ) {
604                                 ber_bvarray_free( rs->sr_ref );
605                                 rs->sr_ref = NULL;
606                         }
607                 }
608         }
609
610         if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) {
611                 rs->sr_err = LDAP_X_NO_OPERATION;
612         }
613
614         send_ldap_result( op, rs );
615
616         Debug( LDAP_DEBUG_TRACE, "<==backsql_delete()\n", 0, 0, 0 );
617
618         if ( !BER_BVISNULL( &e_id.eid_ndn ) ) {
619                 (void)backsql_free_entryID( &e_id, 0, op->o_tmpmemctx );
620         }
621
622         if ( !BER_BVISNULL( &d.e_nname ) ) {
623                 backsql_entry_clean( op, &d );
624         }
625
626         if ( !BER_BVISNULL( &p.e_nname ) ) {
627                 backsql_entry_clean( op, &p );
628         }
629
630         if ( rs->sr_ref ) {
631                 ber_bvarray_free( rs->sr_ref );
632                 rs->sr_ref = NULL;
633         }
634
635         return rs->sr_err;
636 }
637