]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldap/search.c
ITS#3291 delay use of slap_known_controls until all configuration is done
[openldap] / servers / slapd / back-ldap / search.c
1 /* search.c - ldap backend search function */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1999-2004 The OpenLDAP Foundation.
6  * Portions Copyright 1999-2003 Howard Chu.
7  * Portions Copyright 2000-2003 Pierangelo Masarati.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by the Howard Chu for inclusion
20  * in OpenLDAP Software and subsequently enhanced by Pierangelo
21  * Masarati.
22  */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27
28 #include <ac/socket.h>
29 #include <ac/string.h>
30 #include <ac/time.h>
31
32 #include "slap.h"
33 #include "back-ldap.h"
34 #undef ldap_debug       /* silence a warning in ldap-int.h */
35 #include "../../../libraries/libldap/ldap-int.h"
36
37 #include "lutil.h"
38
39 static int
40 ldap_build_entry( Operation *op, LDAPMessage *e, Entry *ent,
41          struct berval *bdn, int flags );
42 #define LDAP_BUILD_ENTRY_PRIVATE        0x01
43
44 int
45 ldap_back_search(
46     Operation   *op,
47     SlapReply *rs )
48 {
49         struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;
50         struct ldapconn *lc;
51         struct timeval  tv;
52         LDAPMessage             *res, *e;
53         int     rc = 0, msgid; 
54         struct berval match = BER_BVNULL;
55         char **mapped_attrs = NULL;
56         struct berval mbase;
57         struct berval mfilter = BER_BVNULL;
58         int dontfreetext = 0;
59         int freeconn = 0;
60         dncookie dc;
61         LDAPControl **ctrls = NULL;
62
63         lc = ldap_back_getconn(op, rs);
64         if ( !lc ) {
65                 return( -1 );
66         }
67
68         /*
69          * FIXME: in case of values return filter, we might want
70          * to map attrs and maybe rewrite value
71          */
72         if ( !ldap_back_dobind( lc, op, rs ) ) {
73                 return( -1 );
74         }
75
76         /* should we check return values? */
77         if ( op->ors_deref != -1 ) {
78                 ldap_set_option( lc->ld, LDAP_OPT_DEREF, (void *)&op->ors_deref );
79         }
80
81         if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
82                 tv.tv_sec = op->ors_tlimit;
83                 tv.tv_usec = 0;
84
85         } else {
86                 tv.tv_sec = 0;
87         }
88
89         /*
90          * Rewrite the search base, if required
91          */
92         dc.rwmap = &li->rwmap;
93 #ifdef ENABLE_REWRITE
94         dc.conn = op->o_conn;
95         dc.rs = rs;
96         dc.ctx = "searchBase";
97 #else
98         dc.tofrom = 1;
99         dc.normalized = 0;
100 #endif
101         if ( ldap_back_dn_massage( &dc, &op->o_req_ndn, &mbase ) ) {
102                 send_ldap_result( op, rs );
103                 return -1;
104         }
105
106         rc = ldap_back_filter_map_rewrite( &dc, op->ors_filter,
107                         &mfilter, BACKLDAP_MAP );
108
109         switch ( rc ) {
110         case LDAP_SUCCESS:
111                 break;
112
113         case LDAP_COMPARE_FALSE:
114                 rs->sr_err = LDAP_SUCCESS;
115                 rs->sr_text = NULL;
116                 rc = 0;
117                 goto finish;
118
119         default:
120                 rs->sr_err = LDAP_OTHER;
121                 rs->sr_text = "Rewrite error";
122                 dontfreetext = 1;
123                 rc = -1;
124                 goto finish;
125         }
126
127         rs->sr_err = ldap_back_map_attrs( &li->rwmap.rwm_at,
128                         op->ors_attrs,
129                         BACKLDAP_MAP, &mapped_attrs );
130         if ( rs->sr_err != LDAP_SUCCESS ) {
131                 rc = -1;
132                 goto finish;
133         }
134
135         ctrls = op->o_ctrls;
136 #ifdef LDAP_BACK_PROXY_AUTHZ
137         rc = ldap_back_proxy_authz_ctrl( lc, op, rs, &ctrls );
138         if ( rc != LDAP_SUCCESS ) {
139                 dontfreetext = 1;
140                 goto finish;
141         }
142 #endif /* LDAP_BACK_PROXY_AUTHZ */
143         
144         rs->sr_err = ldap_search_ext( lc->ld, mbase.bv_val,
145                         op->ors_scope, mfilter.bv_val,
146                         mapped_attrs, op->ors_attrsonly,
147                         ctrls, NULL,
148                         tv.tv_sec ? &tv : NULL, op->ors_slimit,
149                         &msgid );
150
151         if ( rs->sr_err != LDAP_SUCCESS ) {
152 fail:;
153                 rc = ldap_back_op_result( lc, op, rs, msgid, 0 );
154                 if ( freeconn ) {
155                         ldap_back_freeconn( op, lc );
156                         lc = NULL;
157                 }
158                 goto finish;
159         }
160
161         /* We pull apart the ber result, stuff it into a slapd entry, and
162          * let send_search_entry stuff it back into ber format. Slow & ugly,
163          * but this is necessary for version matching, and for ACL processing.
164          */
165
166         for ( rc = 0; rc != -1; rc = ldap_result( lc->ld, msgid, 0, &tv, &res ) )
167         {
168                 /* check for abandon */
169                 if ( op->o_abandon ) {
170                         ldap_abandon( lc->ld, msgid );
171                         rc = 0;
172                         goto finish;
173                 }
174
175                 if ( rc == 0 ) {
176                         tv.tv_sec = 0;
177                         tv.tv_usec = 100000;
178                         ldap_pvt_thread_yield();
179
180                 } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
181                         Entry ent = {0};
182                         struct berval bdn;
183                         int abort = 0;
184                         e = ldap_first_entry( lc->ld, res );
185                         rc = ldap_build_entry( op, e, &ent, &bdn,
186                                         LDAP_BUILD_ENTRY_PRIVATE );
187                        if ( rc == LDAP_SUCCESS ) {
188                                 rs->sr_entry = &ent;
189                                 rs->sr_attrs = op->ors_attrs;
190                                 rs->sr_operational_attrs = NULL;
191                                 rs->sr_flags = 0;
192                                 abort = send_search_entry( op, rs );
193                                 while (ent.e_attrs) {
194                                         Attribute *a;
195                                         BerVarray v;
196
197                                         a = ent.e_attrs;
198                                         ent.e_attrs = a->a_next;
199
200                                         v = a->a_vals;
201                                         if ( a->a_vals != &slap_dummy_bv ) {
202                                                 ber_bvarray_free(a->a_vals);
203                                         }
204                                         if ( a->a_nvals != v ) {
205                                                 ber_bvarray_free(a->a_nvals);
206                                         }
207                                         ch_free(a);
208                                 }
209                                 
210                                 if ( ent.e_dn && ( ent.e_dn != bdn.bv_val ) )
211                                         free( ent.e_dn );
212                                 if ( ent.e_ndn )
213                                         free( ent.e_ndn );
214                         }
215                         ldap_msgfree( res );
216                         if ( abort ) {
217                                 ldap_abandon( lc->ld, msgid );
218                                 goto finish;
219                         }
220
221                 } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
222                         char            **references = NULL;
223                         int             cnt;
224
225                         rc = ldap_parse_reference( lc->ld, res,
226                                         &references, &rs->sr_ctrls, 1 );
227
228                         if ( rc != LDAP_SUCCESS ) {
229                                 continue;
230                         }
231
232                         if ( references == NULL ) {
233                                 continue;
234                         }
235
236                         for ( cnt = 0; references[ cnt ]; cnt++ )
237                                 /* NO OP */ ;
238                                 
239                         rs->sr_ref = ch_calloc( cnt + 1, sizeof( struct berval ) );
240
241                         for ( cnt = 0; references[ cnt ]; cnt++ ) {
242                                 ber_str2bv( references[ cnt ], 0, 0, &rs->sr_ref[ cnt ] );
243                         }
244
245                         /* ignore return value by now */
246                         ( void )send_search_reference( op, rs );
247
248                         /* cleanup */
249                         if ( references ) {
250                                 ldap_value_free( references );
251                                 ch_free( rs->sr_ref );
252                                 rs->sr_ref = NULL;
253                         }
254
255                         if ( rs->sr_ctrls ) {
256                                 ldap_controls_free( rs->sr_ctrls );
257                                 rs->sr_ctrls = NULL;
258                         }
259
260                 } else {
261                         rc = ldap_parse_result( lc->ld, res, &rs->sr_err,
262                                         &match.bv_val, (char **)&rs->sr_text,
263                                         NULL, NULL, 1 );
264                         if (rc != LDAP_SUCCESS ) {
265                                 rs->sr_err = rc;
266                         }
267                         rs->sr_err = slap_map_api2result( rs );
268                         rc = 0;
269                         break;
270                 }
271         }
272
273         if ( rc == -1 ) {
274                 /* FIXME: invalidate the connection? */
275                 rs->sr_err = LDAP_SERVER_DOWN;
276                 freeconn = 1;
277                 goto fail;
278         }
279
280         /*
281          * Rewrite the matched portion of the search base, if required
282          */
283         if ( match.bv_val && *match.bv_val ) {
284                 struct berval mdn;
285
286 #ifdef ENABLE_REWRITE
287                 dc.ctx = "matchedDN";
288 #else
289                 dc.tofrom = 0;
290                 dc.normalized = 0;
291 #endif
292                 match.bv_len = strlen( match.bv_val );
293                 ldap_back_dn_massage( &dc, &match, &mdn );
294                 rs->sr_matched = mdn.bv_val;
295         }
296         if ( rs->sr_v2ref ) {
297                 rs->sr_err = LDAP_REFERRAL;
298         }
299
300 finish:;
301         send_ldap_result( op, rs );
302
303 #ifdef LDAP_BACK_PROXY_AUTHZ
304         if ( ctrls && ctrls != op->o_ctrls ) {
305                 free( ctrls[ 0 ] );
306                 free( ctrls );
307         }
308 #endif /* LDAP_BACK_PROXY_AUTHZ */
309
310         if ( match.bv_val ) {
311                 if ( rs->sr_matched != match.bv_val ) {
312                         free( (char *)rs->sr_matched );
313                 }
314                 rs->sr_matched = NULL;
315                 LDAP_FREE( match.bv_val );
316         }
317         if ( rs->sr_text ) {
318                 if ( !dontfreetext ) {
319                         LDAP_FREE( (char *)rs->sr_text );
320                 }
321                 rs->sr_text = NULL;
322         }
323         if ( mapped_attrs ) {
324                 ch_free( mapped_attrs );
325         }
326         if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
327                 ch_free( mfilter.bv_val );
328         }
329         if ( mbase.bv_val != op->o_req_ndn.bv_val ) {
330                 free( mbase.bv_val );
331         }
332         
333         return rc;
334 }
335
336 static int
337 ldap_build_entry(
338         Operation *op,
339         LDAPMessage *e,
340         Entry *ent,
341         struct berval *bdn,
342         int flags
343 )
344 {
345         struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;
346         struct berval a, mapped;
347         BerElement ber = *e->lm_ber;
348         Attribute *attr, **attrp;
349         struct berval *bv;
350         const char *text;
351         int last;
352         int private = flags & LDAP_BUILD_ENTRY_PRIVATE;
353         dncookie dc;
354
355         /* safe assumptions ... */
356         assert( ent );
357         ent->e_bv.bv_val = NULL;
358
359         if ( ber_scanf( &ber, "{m{", bdn ) == LBER_ERROR ) {
360                 return LDAP_DECODING_ERROR;
361         }
362
363         /*
364          * Rewrite the dn of the result, if needed
365          */
366         dc.rwmap = &li->rwmap;
367 #ifdef ENABLE_REWRITE
368         dc.conn = op->o_conn;
369         dc.rs = NULL;
370         dc.ctx = "searchResult";
371 #else
372         dc.tofrom = 0;
373         dc.normalized = 0;
374 #endif
375         if ( ldap_back_dn_massage( &dc, bdn, &ent->e_name ) ) {
376                 return LDAP_OTHER;
377         }
378
379         /*
380          * Note: this may fail if the target host(s) schema differs
381          * from the one known to the meta, and a DN with unknown
382          * attributes is returned.
383          * 
384          * FIXME: should we log anything, or delegate to dnNormalize?
385          */
386         /* Note: if the distinguished values or the naming attributes
387          * change, should we massage them as well?
388          */
389         if ( dnNormalize( 0, NULL, NULL, &ent->e_name, &ent->e_nname,
390                 op->o_tmpmemctx ) != LDAP_SUCCESS )
391         {
392                 return LDAP_INVALID_DN_SYNTAX;
393         }
394
395         attrp = &ent->e_attrs;
396
397 #ifdef ENABLE_REWRITE
398         dc.ctx = "searchAttrDN";
399 #endif
400         while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
401                 int                             i;
402                 slap_syntax_validate_func       *validate;
403                 slap_syntax_transform_func      *pretty;
404
405                 ldap_back_map( &li->rwmap.rwm_at, &a, &mapped, BACKLDAP_REMAP );
406                 if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) )
407                         continue;
408                 attr = (Attribute *)ch_malloc( sizeof( Attribute ) );
409                 if ( attr == NULL )
410                         continue;
411                 attr->a_flags = 0;
412                 attr->a_next = 0;
413                 attr->a_desc = NULL;
414                 if ( slap_bv2ad( &mapped, &attr->a_desc, &text ) != LDAP_SUCCESS ) {
415                         if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text ) 
416                                         != LDAP_SUCCESS )
417                         {
418 #ifdef NEW_LOGGING
419                                 LDAP_LOG( BACK_LDAP, DETAIL1, 
420                                         "slap_bv2undef_ad(%s):  %s\n", mapped.bv_val, text, 0 );
421 #else /* !NEW_LOGGING */
422                                 Debug( LDAP_DEBUG_ANY, 
423                                         "slap_bv2undef_ad(%s):  %s\n", mapped.bv_val, text, 0 );
424 #endif /* !NEW_LOGGING */
425                                 ch_free( attr );
426                                 continue;
427                         }
428                 }
429
430                 /* no subschemaSubentry */
431                 if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry ) {
432
433                         /* 
434                          * We eat target's subschemaSubentry because
435                          * a search for this value is likely not
436                          * to resolve to the appropriate backend;
437                          * later, the local subschemaSubentry is
438                          * added.
439                          */
440                         ( void )ber_scanf( &ber, "x" /* [W] */ );
441
442                         ch_free(attr);
443                         continue;
444                 }
445                 
446                 if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR
447                                 || attr->a_vals == NULL )
448                 {
449                         /*
450                          * Note: attr->a_vals can be null when using
451                          * values result filter
452                          */
453                         if ( private ) {
454                                 attr->a_vals = &slap_dummy_bv;
455                         } else {
456                                 attr->a_vals = ch_malloc( sizeof( struct berval ) );
457                                 BER_BVZERO( &attr->a_vals[0] );
458                         }
459                         last = 0;
460
461                 } else {
462                         for ( last = 0; !BER_BVISNULL( &attr->a_vals[last] ); last++ );
463                 }
464
465                 if ( last == 0 ) {
466                         /* empty */
467                 } else if ( attr->a_desc == slap_schema.si_ad_objectClass
468                                 || attr->a_desc == slap_schema.si_ad_structuralObjectClass )
469                 {
470                         for ( bv = attr->a_vals; !BER_BVISNULL( bv ); bv++ ) {
471                                 ldap_back_map( &li->rwmap.rwm_oc, bv, &mapped,
472                                                 BACKLDAP_REMAP );
473                                 if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
474                                         LBER_FREE( bv->bv_val );
475                                         BER_BVZERO( bv );
476                                         if (--last < 0)
477                                                 break;
478                                         *bv = attr->a_vals[last];
479                                         BER_BVZERO( &attr->a_vals[last] );
480                                         bv--;
481
482                                 } else if ( mapped.bv_val != bv->bv_val ) {
483                                         /*
484                                          * FIXME: after LBER_FREEing
485                                          * the value is replaced by
486                                          * ch_alloc'ed memory
487                                          */
488                                         LBER_FREE( bv->bv_val );
489                                         ber_dupbv( bv, &mapped );
490                                 }
491                         }
492
493                 /*
494                  * It is necessary to try to rewrite attributes with
495                  * dn syntax because they might be used in ACLs as
496                  * members of groups; since ACLs are applied to the
497                  * rewritten stuff, no dn-based subject clause could
498                  * be used at the ldap backend side (see
499                  * http://www.OpenLDAP.org/faq/data/cache/452.html)
500                  * The problem can be overcome by moving the dn-based
501                  * ACLs to the target directory server, and letting
502                  * everything pass thru the ldap backend.
503                  */
504                 } else if ( attr->a_desc->ad_type->sat_syntax ==
505                                 slap_schema.si_syn_distinguishedName )
506                 {
507                         ldap_dnattr_result_rewrite( &dc, attr->a_vals );
508                 }
509
510                 validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate;
511                 pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
512
513                 if ( !validate && !pretty ) {
514                         attr->a_nvals = NULL;
515                         attr_free( attr );
516                         goto next_attr;
517                 }
518
519                 for ( i = 0; i < last; i++ ) {
520                         struct berval   pval;
521                         int             rc;
522
523                         if ( pretty ) {
524                                 rc = pretty( attr->a_desc->ad_type->sat_syntax,
525                                         &attr->a_vals[i], &pval, NULL );
526                         } else {
527                                 rc = validate( attr->a_desc->ad_type->sat_syntax,
528                                         &attr->a_vals[i] );
529                         }
530
531                         if ( rc != LDAP_SUCCESS ) {
532                                 attr->a_nvals = NULL;
533                                 attr_free( attr );
534                                 goto next_attr;
535                         }
536
537                         if ( pretty ) {
538                                 LBER_FREE( attr->a_vals[i].bv_val );
539                                 attr->a_vals[i] = pval;
540                         }
541                 }
542
543                 if ( last && attr->a_desc->ad_type->sat_equality &&
544                                 attr->a_desc->ad_type->sat_equality->smr_normalize )
545                 {
546                         attr->a_nvals = ch_malloc( ( last + 1 )*sizeof( struct berval ) );
547                         for ( i = 0; i < last; i++ ) {
548                                 int             rc;
549
550                                 /*
551                                  * check that each value is valid per syntax
552                                  * and pretty if appropriate
553                                  */
554                                 rc = attr->a_desc->ad_type->sat_equality->smr_normalize(
555                                         SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
556                                         attr->a_desc->ad_type->sat_syntax,
557                                         attr->a_desc->ad_type->sat_equality,
558                                         &attr->a_vals[i], &attr->a_nvals[i],
559                                         NULL /* op->o_tmpmemctx */ );
560
561                                 if ( rc != LDAP_SUCCESS ) {
562                                         BER_BVZERO( &attr->a_nvals[i] );
563                                         ch_free( attr );
564                                         goto next_attr;
565                                 }
566                         }
567                         BER_BVZERO( &attr->a_nvals[i] );
568
569                 } else {
570                         attr->a_nvals = attr->a_vals;
571                 }
572                 *attrp = attr;
573                 attrp = &attr->a_next;
574
575 next_attr:;
576         }
577
578         /* make sure it's free'able */
579         if ( !private && ent->e_name.bv_val == bdn->bv_val ) {
580                 ber_dupbv( &ent->e_name, bdn );
581         }
582         return LDAP_SUCCESS;
583 }
584
585 /* return 0 IFF we can retrieve the entry with ndn
586  */
587 int
588 ldap_back_entry_get(
589         Operation *op,
590         struct berval   *ndn,
591         ObjectClass *oc,
592         AttributeDescription *at,
593         int rw,
594         Entry **ent
595 )
596 {
597         struct ldapinfo *li = (struct ldapinfo *) op->o_bd->be_private;    
598         struct ldapconn *lc;
599         int rc = 1, is_oc;
600         struct berval mapped = BER_BVNULL, bdn, mdn;
601         LDAPMessage     *result = NULL, *e = NULL;
602         char *gattr[3];
603         char *filter = NULL;
604         Connection *oconn;
605         SlapReply rs;
606         dncookie dc;
607
608         /* Tell getconn this is a privileged op */
609         is_oc = op->o_do_not_cache;
610         op->o_do_not_cache = 1;
611         lc = ldap_back_getconn(op, &rs);
612         oconn = op->o_conn;
613         op->o_conn = NULL;
614         if ( !lc || !ldap_back_dobind(lc, op, &rs) ) {
615                 op->o_do_not_cache = is_oc;
616                 op->o_conn = oconn;
617                 return 1;
618         }
619         op->o_do_not_cache = is_oc;
620         op->o_conn = oconn;
621
622         /*
623          * Rewrite the search base, if required
624          */
625         dc.rwmap = &li->rwmap;
626 #ifdef ENABLE_REWRITE
627         dc.conn = op->o_conn;
628         dc.rs = &rs;
629         dc.ctx = "searchBase";
630 #else
631         dc.tofrom = 1;
632         dc.normalized = 1;
633 #endif
634         if ( ldap_back_dn_massage( &dc, ndn, &mdn ) ) {
635                 return 1;
636         }
637
638         if ( at ) {
639                 ldap_back_map(&li->rwmap.rwm_at, &at->ad_cname, &mapped, BACKLDAP_MAP);
640                 if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
641                         rc = 1;
642                         goto cleanup;
643                 }
644                 is_oc = ( strcasecmp( "objectclass", mapped.bv_val ) == 0 );
645                 if ( oc && !is_oc ) {
646                         gattr[0] = "objectclass";
647                         gattr[1] = mapped.bv_val;
648                         gattr[2] = NULL;
649
650                 } else {
651                         gattr[0] = mapped.bv_val;
652                         gattr[1] = NULL;
653                 }
654         }
655
656         if (oc) {
657                 char *ptr;
658                 ldap_back_map(&li->rwmap.rwm_oc, &oc->soc_cname, &mapped,
659                                                 BACKLDAP_MAP);
660                 filter = ch_malloc( STRLENOF( "(objectclass=)" ) + mapped.bv_len + 1 );
661                 ptr = lutil_strcopy( filter, "(objectclass=" );
662                 ptr = lutil_strcopy( ptr, mapped.bv_val );
663                 *ptr++ = ')';
664                 *ptr++ = '\0';
665         }
666
667         if ( ldap_search_ext_s( lc->ld, mdn.bv_val, LDAP_SCOPE_BASE, filter,
668                                 gattr, 0, NULL, NULL, LDAP_NO_LIMIT,
669                                 LDAP_NO_LIMIT, &result) != LDAP_SUCCESS )
670         {
671                 goto cleanup;
672         }
673
674         e = ldap_first_entry( lc->ld, result );
675         if ( e == NULL ) {
676                 goto cleanup;
677         }
678
679         *ent = ch_calloc( 1, sizeof( Entry ) );
680
681         rc = ldap_build_entry( op, e, *ent, &bdn, 0 );
682
683         if ( rc != LDAP_SUCCESS ) {
684                 ch_free( *ent );
685                 *ent = NULL;
686         }
687
688 cleanup:
689         if ( result ) {
690                 ldap_msgfree( result );
691         }
692
693         if ( filter ) {
694                 ch_free( filter );
695         }
696
697         if ( mdn.bv_val != ndn->bv_val ) {
698                 ch_free( mdn.bv_val );
699         }
700
701         return rc;
702 }
703