]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/bind.c
a bit more reworking and streamline; the change to the LAST_CONN test seems to elimin...
[openldap] / servers / slapd / back-meta / bind.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2005 The OpenLDAP Foundation.
5  * Portions Copyright 2001-2003 Pierangelo Masarati.
6  * Portions Copyright 1999-2003 Howard Chu.
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 the Howard Chu for inclusion
19  * in OpenLDAP Software and subsequently enhanced by Pierangelo
20  * Masarati.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/socket.h>
28 #include <ac/string.h>
29
30
31 #define AVL_INTERNAL
32 #include "slap.h"
33 #include "../back-ldap/back-ldap.h"
34 #include "back-meta.h"
35
36 static LDAP_REBIND_PROC meta_back_rebind;
37
38 static int
39 meta_back_single_bind(
40         Operation               *op,
41         SlapReply               *rs,
42         metaconn_t              *mc,
43         int                     candidate );
44
45 int
46 meta_back_bind( Operation *op, SlapReply *rs )
47 {
48         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
49         metaconn_t      *mc;
50
51         int             rc = LDAP_OTHER,
52                         i, gotit = 0, isroot = 0;
53
54         SlapReply       *candidates = meta_back_candidates_get( op );
55
56         rs->sr_err = LDAP_SUCCESS;
57
58         Debug( LDAP_DEBUG_ARGS, "meta_back_bind: dn: %s.\n%s%s",
59                         op->o_req_dn.bv_val, "", "" );
60
61         if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
62                 isroot = 1;
63                 ber_dupbv( &op->orb_edn, be_root_dn( op->o_bd ) );
64         }
65
66         /* we need meta_back_getconn() not send result even on error,
67          * because we want to intercept the error and make it
68          * invalidCredentials */
69         mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_DONTSEND );
70         if ( !mc ) {
71                 Debug( LDAP_DEBUG_ANY,
72                                 "meta_back_bind: no target "
73                                 "for dn \"%s\" (%d: %s).\n",
74                                 op->o_req_dn.bv_val, rs->sr_err,
75                                 rs->sr_text ? rs->sr_text : "" );
76                 /* FIXME: there might be cases where we don't want
77                  * to map the error onto invalidCredentials */
78                 switch ( rs->sr_err ) {
79                 case LDAP_NO_SUCH_OBJECT:
80                 case LDAP_UNWILLING_TO_PERFORM:
81                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
82                         rs->sr_text = NULL;
83                         break;
84                 }
85                 send_ldap_result( op, rs );
86                 return rs->sr_err;
87         }
88
89         /*
90          * Each target is scanned ...
91          */
92         mc->mc_auth_target = META_BOUND_NONE;
93         for ( i = 0; i < mi->mi_ntargets; i++ ) {
94                 int             lerr;
95                 Operation       op2 = *op;
96
97                 /*
98                  * Skip non-candidates
99                  */
100                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
101                         continue;
102                 }
103
104                 if ( gotit == 0 ) {
105                         gotit = 1;
106
107                 } else if ( isroot == 0 ) {
108                         /*
109                          * A bind operation is expected to have
110                          * ONE CANDIDATE ONLY!
111                          */
112                         Debug( LDAP_DEBUG_ANY,
113                                         "==>meta_back_bind: more than one"
114                                         " candidate is trying to bind...\n",
115                                         0, 0, 0 );
116                 }
117
118                 if ( isroot && !BER_BVISNULL( &mi->mi_targets[ i ].mt_pseudorootdn ) )
119                 {
120                         op2.o_req_dn = mi->mi_targets[ i ].mt_pseudorootdn;
121                         op2.o_req_ndn = mi->mi_targets[ i ].mt_pseudorootdn;
122                         op2.orb_cred = mi->mi_targets[ i ].mt_pseudorootpw;
123                         op2.orb_method = LDAP_AUTH_SIMPLE;
124                 }
125                 
126                 lerr = meta_back_single_bind( &op2, rs, mc, i );
127                 if ( lerr != LDAP_SUCCESS ) {
128                         rs->sr_err = lerr;
129                         candidates[ i ].sr_tag = META_NOT_CANDIDATE;
130
131                 } else {
132                         rc = LDAP_SUCCESS;
133                 }
134         }
135
136         if ( isroot ) {
137                 mc->mc_auth_target = META_BOUND_ALL;
138         }
139
140         /*
141          * rc is LDAP_SUCCESS if at least one bind succeeded,
142          * err is the last error that occurred during a bind;
143          * if at least (and at most?) one bind succeedes, fine.
144          */
145         if ( rc != LDAP_SUCCESS ) {
146                 
147                 /*
148                  * deal with bind failure ...
149                  */
150
151                 /*
152                  * no target was found within the naming context, 
153                  * so bind must fail with invalid credentials
154                  */
155                 if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
156                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
157                 }
158
159                 rs->sr_err = slap_map_api2result( rs );
160                 send_ldap_result( op, rs );
161                 return rs->sr_err;
162         }
163
164         return LDAP_SUCCESS;
165 }
166
167 /*
168  * meta_back_single_bind
169  *
170  * attempts to perform a bind with creds
171  */
172 static int
173 meta_back_single_bind(
174         Operation               *op,
175         SlapReply               *rs,
176         metaconn_t              *mc,
177         int                     candidate )
178 {
179         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
180         metatarget_t            *mt = &mi->mi_targets[ candidate ];
181         struct berval           mdn = BER_BVNULL;
182         dncookie                dc;
183         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
184         int                     msgid,
185                                 rebinding = 0;
186         
187         /*
188          * Rewrite the bind dn if needed
189          */
190         dc.target = &mi->mi_targets[ candidate ];
191         dc.conn = op->o_conn;
192         dc.rs = rs;
193         dc.ctx = "bindDN";
194
195         if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
196                 send_ldap_result( op, rs );
197                 return -1;
198         }
199
200         /* FIXME: this fixes the bind problem right now; we need
201          * to use the asynchronous version to get the "matched"
202          * and more in case of failure ... */
203         /* FIXME: should be check if at least some of the op->o_ctrls
204          * can/should be passed? */
205 rebind:;
206         rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
207                         LDAP_SASL_SIMPLE, &op->orb_cred,
208                         op->o_ctrls, NULL, &msgid );
209         if ( rs->sr_err == LDAP_SUCCESS ) {
210                 LDAPMessage     *res;
211                 struct timeval  tv;
212                 int             rc;
213                 int             nretries = mt->mt_nretries;
214
215                 /*
216                  * handle response!!!
217                  */
218 retry:;
219                 tv.tv_sec = 0;
220                 tv.tv_usec = META_BIND_TIMEOUT;
221                 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
222                 case 0:
223                         Debug( LDAP_DEBUG_ANY, "%s meta_back_single_bind: ldap_result=%d nretries=%d\n",
224                                 op->o_log_prefix, 0, nretries );
225
226                         if ( nretries != META_RETRY_NEVER ) {
227                                 ldap_pvt_thread_yield();
228                                 if ( nretries > 0 ) {
229                                         nretries--;
230                                 }
231                                 goto retry;
232                         }
233                         rs->sr_err = LDAP_BUSY;
234                         if ( rebinding ) {
235                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
236                                 break;
237                         }
238
239                         /* FIXME: some times the request times out
240                          * while the other party is not willing to
241                          * send a response any more.  Give it a second
242                          * chance with a freshly bound connection */
243                         rebinding = 1;
244                         nretries = mt->mt_nretries;
245                         /* fallthru */
246
247                 case -1:
248                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
249                                         &rs->sr_err );
250
251                         if ( rebinding ) {
252                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
253                         }
254
255                         Debug( LDAP_DEBUG_ANY, "### %s meta_back_single_bind: err=%d nretries=%d\n",
256                                 op->o_log_prefix, rs->sr_err, nretries );
257
258                         rc = slap_map_api2result( rs );
259                         if ( rs->sr_err == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
260                                 ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
261                                 msc->msc_ld = NULL;
262                                 msc->msc_bound = 0;
263
264                                 /* mc here must be the regular mc, reset and ready for init */
265                                 rc = meta_back_init_one_conn( op, rs, mt, msc,
266                                                 LDAP_BACK_DONTSEND );
267                                 if ( rc ) {
268                                         if ( nretries > 0 ) {
269                                                 nretries--;
270                                         }
271                                         ldap_pvt_thread_yield();
272                                         goto rebind;
273                                 }
274                         }
275                         break;
276
277                 default:
278                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
279                                         NULL, NULL, NULL, NULL, 1 );
280                         if ( rc != LDAP_SUCCESS ) {
281                                 rs->sr_err = rc;
282                         }
283                         break;
284                 }
285         }
286
287         if ( rs->sr_err != LDAP_SUCCESS ) {
288                 rs->sr_err = slap_map_api2result( rs );
289                 goto return_results;
290         }
291
292         ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_dn );
293         msc->msc_bound = META_BOUND;
294         mc->mc_auth_target = candidate;
295
296         if ( LDAP_BACK_SAVECRED( mi ) ) {
297                 ber_bvreplace( &msc->msc_cred, &op->orb_cred );
298                 ldap_set_rebind_proc( msc->msc_ld, meta_back_rebind, msc );
299         }
300
301         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
302                         && op->o_req_ndn.bv_len != 0 )
303         {
304                 ( void )meta_dncache_update_entry( &mi->mi_cache,
305                                 &op->o_req_ndn, candidate );
306         }
307
308 return_results:;
309         if ( mdn.bv_val != op->o_req_dn.bv_val ) {
310                 free( mdn.bv_val );
311         }
312
313         return rs->sr_err;
314 }
315
316 /*
317  * meta_back_single_dobind
318  */
319 int
320 meta_back_single_dobind(
321         Operation               *op,
322         SlapReply               *rs,
323         metaconn_t              *mc,
324         int                     candidate,
325         ldap_back_send_t        sendok,
326         int                     nretries )
327 {
328         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
329         metatarget_t            *mt = &mi->mi_targets[ candidate ];
330         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
331         int                     rc;
332         struct berval           cred = BER_BVC( "" );
333         int                     msgid,
334                                 rebinding = 0,
335                                 save_nretries = nretries;
336
337         /*
338          * Otherwise an anonymous bind is performed
339          * (note: if the target was already bound, the anonymous
340          * bind clears the previous bind).
341          */
342         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
343                 ber_memfree( msc->msc_bound_ndn.bv_val );
344                 BER_BVZERO( &msc->msc_bound_ndn );
345         }
346                 
347         if ( !BER_BVISNULL( &msc->msc_cred ) ) {
348                 /* destroy sensitive data */
349                 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
350                 ber_memfree( msc->msc_cred.bv_val );
351                 BER_BVZERO( &msc->msc_cred );
352         }
353
354         /* FIXME: should we check if at least some of the op->o_ctrls
355          * can/should be passed? */
356 rebind:;
357         rc = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred,
358                         NULL, NULL, &msgid );
359         if ( rc == LDAP_SUCCESS ) {
360                 LDAPMessage     *res;
361                 struct timeval  tv;
362
363                 /*
364                  * handle response!!!
365                  */
366 retry:;
367                 tv.tv_sec = 0;
368                 tv.tv_usec = META_BIND_TIMEOUT;
369                 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
370                 case 0:
371                         Debug( LDAP_DEBUG_ANY, "%s meta_back_single_dobind: ldap_result=%d nretries=%d\n",
372                                 op->o_log_prefix, 0, nretries );
373
374                         if ( nretries != META_RETRY_NEVER ) {
375                                 ldap_pvt_thread_yield();
376                                 if ( nretries > 0 ) {
377                                         nretries--;
378                                 }
379                                 goto retry;
380                         }
381
382                         rc = LDAP_BUSY;
383                         if ( rebinding ) {
384                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
385                                 break;
386                         }
387
388                         /* FIXME: some times the request times out
389                          * while the other party is not willing to
390                          * send a response any more.  Give it a second
391                          * chance with a freshly bound connection */
392                         rebinding = 1;
393                         nretries = save_nretries;
394                         /* fallthru */
395
396                 case -1:
397                         ldap_get_option( msc->msc_ld,
398                                         LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
399
400                         if ( rebinding ) {
401                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
402                         }
403
404                         Debug( LDAP_DEBUG_ANY, "### %s meta_back_single_dobind: err=%d nretries=%d\n",
405                                         op->o_log_prefix, rs->sr_err, nretries );
406
407                         rc = slap_map_api2result( rs );
408                         if ( rc == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
409                                 ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
410                                 msc->msc_ld = NULL;
411                                 msc->msc_bound = 0;
412
413                                 /* mc here must be the regular mc, reset and ready for init */
414                                 rc = meta_back_init_one_conn( op, rs, mt, msc, LDAP_BACK_DONTSEND );
415
416                                 if ( rc == LDAP_SUCCESS ) {
417                                         ldap_pvt_thread_yield();
418                                         if ( nretries > 0 ) {
419                                                 nretries--;
420                                         }
421                                         goto rebind;
422                                 }
423                         }
424                         break;
425
426                 default:
427                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
428                                         NULL, NULL, NULL, NULL, 1 );
429                         if ( rc == LDAP_SUCCESS ) {
430                                 rc = slap_map_api2result( rs );
431                         }
432                         break;
433                 }
434
435         } else {
436                 rc = slap_map_api2result( rs );
437         }
438
439         rs->sr_err = rc;
440         if ( rc != LDAP_SUCCESS && ( sendok & LDAP_BACK_SENDERR ) ) {
441                 send_ldap_result( op, rs );
442         }
443
444         return rc;
445 }
446
447 /*
448  * meta_back_dobind
449  */
450 int
451 meta_back_dobind(
452         Operation               *op,
453         SlapReply               *rs,
454         metaconn_t              *mc,
455         ldap_back_send_t        sendok )
456 {
457         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
458
459         int                     bound = 0, i;
460
461         SlapReply               *candidates = meta_back_candidates_get( op );
462
463         ldap_pvt_thread_mutex_lock( &mc->mc_mutex );
464
465         Debug( LDAP_DEBUG_TRACE,
466                 "%s meta_back_dobind: conn=%ld\n",
467                 op->o_log_prefix, mc->mc_conn->c_connid, 0 );
468
469         /*
470          * all the targets are bound as pseudoroot
471          */
472         if ( mc->mc_auth_target == META_BOUND_ALL ) {
473                 bound = 1;
474                 goto done;
475         }
476
477         for ( i = 0; i < mi->mi_ntargets; i++ ) {
478                 metatarget_t            *mt = &mi->mi_targets[ i ];
479                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
480                 int                     rc;
481
482                 /*
483                  * Not a candidate
484                  */
485                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
486                         continue;
487                 }
488
489                 assert( msc->msc_ld != NULL );
490
491                 /*
492                  * If the target is already bound it is skipped
493                  */
494                 if ( msc->msc_bound == META_BOUND && mc->mc_auth_target == i ) {
495                         ++bound;
496
497                         Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind[%d]: "
498                                         "authcTarget\n",
499                                         op->o_log_prefix, i, 0 );
500                         continue;
501                 }
502
503                 rc = meta_back_single_dobind( op, rs, mc, i,
504                                 LDAP_BACK_DONTSEND, mt->mt_nretries );
505                 if ( rc != LDAP_SUCCESS ) {
506                         rs->sr_err = slap_map_api2result( rs );
507
508                         Debug( LDAP_DEBUG_ANY, "%s meta_back_dobind[%d]: "
509                                         "(anonymous) err=%d\n",
510                                         op->o_log_prefix, i, rc );
511
512                         /*
513                          * null cred bind should always succeed
514                          * as anonymous, so a failure means
515                          * the target is no longer candidate possibly
516                          * due to technical reasons (remote host down?)
517                          * so better clear the handle
518                          */
519                         candidates[ i ].sr_tag = META_NOT_CANDIDATE;
520                         continue;
521                 } /* else */
522                 
523                 Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind[%d]: "
524                                 "(anonymous)\n",
525                                 op->o_log_prefix, i, 0 );
526
527                 msc->msc_bound = META_ANONYMOUS;
528                 ++bound;
529         }
530
531 done:;
532         ldap_pvt_thread_mutex_unlock( &mc->mc_mutex );
533
534         Debug( LDAP_DEBUG_TRACE,
535                 "%s meta_back_dobind: conn=%ld bound=%d\n",
536                 op->o_log_prefix, mc->mc_conn->c_connid, bound );
537
538         if ( bound == 0 && ( sendok & LDAP_BACK_SENDERR ) ) {
539                 if ( rs->sr_err == LDAP_SUCCESS ) {
540                         rs->sr_err = LDAP_BUSY;
541                 }
542                 send_ldap_result( op, rs );
543         }
544
545         return( bound > 0 );
546 }
547
548 /*
549  * meta_back_rebind
550  *
551  * This is a callback used for chasing referrals using the same
552  * credentials as the original user on this session.
553  */
554 static int 
555 meta_back_rebind(
556         LDAP                    *ld,
557         LDAP_CONST char         *url,
558         ber_tag_t               request,
559         ber_int_t               msgid,
560         void                    *params )
561 {
562         metasingleconn_t        *msc = ( metasingleconn_t * )params;
563
564         return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
565                         LDAP_SASL_SIMPLE, &msc->msc_cred,
566                         NULL, NULL, NULL );
567 }
568
569 /*
570  * FIXME: error return must be handled in a cleaner way ...
571  */
572 int
573 meta_back_op_result(
574         metaconn_t      *mc,
575         Operation       *op,
576         SlapReply       *rs,
577         int             candidate )
578 {
579         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
580
581         int                     i,
582                                 rerr = LDAP_SUCCESS;
583         char                    *rmsg = NULL;
584         char                    *rmatch = NULL;
585         int                     free_rmsg = 0,
586                                 free_rmatch = 0;
587
588         if ( candidate != META_TARGET_NONE ) {
589                 metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
590
591                 rs->sr_err = LDAP_SUCCESS;
592
593                 ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
594                 if ( rs->sr_err != LDAP_SUCCESS ) {
595                         /*
596                          * better check the type of error. In some cases
597                          * (search ?) it might be better to return a
598                          * success if at least one of the targets gave
599                          * positive result ...
600                          */
601                         ldap_get_option( msc->msc_ld,
602                                         LDAP_OPT_ERROR_STRING, &rmsg );
603                         ldap_get_option( msc->msc_ld,
604                                         LDAP_OPT_MATCHED_DN, &rmatch );
605                         rerr = rs->sr_err = slap_map_api2result( rs );
606
607                         if ( rmsg ) {
608                                 free_rmsg = 1;
609                         }
610                         if ( rmatch ) {
611                                 free_rmatch = 1;
612                         }
613
614                         Debug(LDAP_DEBUG_ANY,
615                                         "==> meta_back_op_result: target"
616                                         " <%d> sending msg \"%s\""
617                                         " (matched \"%s\")\n", 
618                                         candidate, ( rmsg ? rmsg : "" ),
619                                         ( rmatch ? rmatch : "" ) );
620                 }
621
622         } else {
623                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
624                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
625                         char                    *msg = NULL;
626                         char                    *match = NULL;
627
628                         rs->sr_err = LDAP_SUCCESS;
629
630                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
631                         if ( rs->sr_err != LDAP_SUCCESS ) {
632                                 /*
633                                  * better check the type of error. In some cases
634                                  * (search ?) it might be better to return a
635                                  * success if at least one of the targets gave
636                                  * positive result ...
637                                  */
638                                 ldap_get_option( msc->msc_ld,
639                                                 LDAP_OPT_ERROR_STRING, &msg );
640                                 ldap_get_option( msc->msc_ld,
641                                                 LDAP_OPT_MATCHED_DN, &match );
642                                 rs->sr_err = slap_map_api2result( rs );
643         
644                                 Debug(LDAP_DEBUG_ANY,
645                                                 "==> meta_back_op_result: target"
646                                                 " <%d> sending msg \"%s\""
647                                                 " (matched \"%s\")\n", 
648                                                 i, ( msg ? msg : "" ),
649                                                 ( match ? match : "" ) );
650         
651                                 /*
652                                  * FIXME: need to rewrite "match" (need rwinfo)
653                                  */
654                                 switch ( rs->sr_err ) {
655                                 default:
656                                         rerr = rs->sr_err;
657                                         if ( rmsg ) {
658                                                 ber_memfree( rmsg );
659                                         }
660                                         rmsg = msg;
661                                         free_rmsg = 1;
662                                         msg = NULL;
663                                         if ( rmatch ) {
664                                                 ber_memfree( rmatch );
665                                         }
666                                         rmatch = match;
667                                         free_rmatch = 1;
668                                         match = NULL;
669                                         break;
670                                 }
671         
672                                 /* better test the pointers before freeing? */
673                                 if ( match ) {
674                                         free( match );
675                                 }
676                                 if ( msg ) {
677                                         free( msg );
678                                 }
679                         }
680                 }
681         }
682         
683         rs->sr_err = rerr;
684         rs->sr_text = rmsg;
685         rs->sr_matched = rmatch;
686         send_ldap_result( op, rs );
687         if ( free_rmsg ) {
688                 ber_memfree( rmsg );
689         }
690         if ( free_rmatch ) {
691                 ber_memfree( rmatch );
692         }
693         rs->sr_text = NULL;
694         rs->sr_matched = NULL;
695
696         return ( ( rerr == LDAP_SUCCESS ) ? 0 : -1 );
697 }
698