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