1/*	$NetBSD: search.c,v 1.3 2021/08/14 16:14:59 christos Exp $	*/
2
3/* search.c - ldap backend search function */
4/* $OpenLDAP$ */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1999-2021 The OpenLDAP Foundation.
8 * Portions Copyright 1999-2003 Howard Chu.
9 * Portions Copyright 2000-2003 Pierangelo Masarati.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted only as authorized by the OpenLDAP
14 * Public License.
15 *
16 * A copy of this license is available in the file LICENSE in the
17 * top-level directory of the distribution or, alternatively, at
18 * <http://www.OpenLDAP.org/license.html>.
19 */
20/* ACKNOWLEDGEMENTS:
21 * This work was initially developed by the Howard Chu for inclusion
22 * in OpenLDAP Software and subsequently enhanced by Pierangelo
23 * Masarati.
24 */
25
26#include <sys/cdefs.h>
27__RCSID("$NetBSD: search.c,v 1.3 2021/08/14 16:14:59 christos Exp $");
28
29#include "portable.h"
30
31#include <stdio.h>
32
33#include <ac/socket.h>
34#include <ac/string.h>
35#include <ac/time.h>
36
37#include "slap.h"
38#include "back-ldap.h"
39#include "../../../libraries/liblber/lber-int.h"
40
41#include "lutil.h"
42
43static int
44ldap_build_entry( Operation *op, LDAPMessage *e, Entry *ent,
45	 struct berval *bdn, int remove_unknown_schema );
46
47
48static ObjectClass *
49oc_bvfind_undef_ex( struct berval *ocname, int flag )
50{
51	ObjectClass	*oc	= oc_bvfind( ocname );
52
53	if ( oc || flag ) {
54		/* oc defined or remove-unknown-schema flag set */
55		return oc;
56	}
57
58	return oc_bvfind_undef( ocname );
59}
60
61
62/*
63 * replaces (&) with (objectClass=*) and (|) with (!(objectClass=*))
64 * as the best replacement for RFC 4526 absolute true/absolute false
65 * filters; the only difference (AFAIK) is that they require search
66 * access to objectClass.
67 *
68 * filter->bv_val may be alloc'd on the thread's slab, if equal to
69 * op->ors_filterstr.bv_val, or realloc'd on the thread's slab otherwise.
70 */
71static int
72ldap_back_munge_filter(
73	Operation	*op,
74	struct berval	*filter )
75{
76	char *ptr;
77	int gotit = 0;
78
79	Debug( LDAP_DEBUG_ARGS, "=> ldap_back_munge_filter \"%s\"\n",
80			filter->bv_val );
81
82	for ( ptr = strchr( filter->bv_val, '(' );
83			ptr;
84			ptr = strchr( ptr, '(' ) )
85	{
86		static struct berval
87			bv_t = BER_BVC( "(&)" ),
88			bv_f = BER_BVC( "(|)" ),
89			bv_T = BER_BVC( "(objectClass=*)" ),
90			bv_F = BER_BVC( "(!(objectClass=*))" );
91		struct berval *oldbv = NULL,
92			*newbv = NULL,
93			oldfilter = BER_BVNULL;
94
95		if ( ptr[2] != ')' ) {
96			ptr++;
97			continue;
98		}
99
100		switch ( ptr[1] ) {
101		case '&':
102			oldbv = &bv_t;
103			newbv = &bv_T;
104			break;
105
106		case '|':
107			oldbv = &bv_f;
108			newbv = &bv_F;
109			break;
110
111		default:
112			/* should be an error */
113			continue;
114		}
115
116		oldfilter = *filter;
117		filter->bv_len += newbv->bv_len - oldbv->bv_len;
118		if ( filter->bv_val == op->ors_filterstr.bv_val ) {
119			filter->bv_val = op->o_tmpalloc( filter->bv_len + 1,
120					op->o_tmpmemctx );
121
122			AC_MEMCPY( filter->bv_val, op->ors_filterstr.bv_val,
123					ptr - oldfilter.bv_val );
124
125		} else {
126			filter->bv_val = op->o_tmprealloc( filter->bv_val,
127					filter->bv_len + 1, op->o_tmpmemctx );
128		}
129
130		ptr = filter->bv_val + ( ptr - oldfilter.bv_val );
131
132		AC_MEMCPY( &ptr[ newbv->bv_len ],
133				&ptr[ oldbv->bv_len ],
134				oldfilter.bv_len - ( ptr - filter->bv_val ) - oldbv->bv_len + 1 );
135		AC_MEMCPY( ptr, newbv->bv_val, newbv->bv_len );
136
137		ptr += newbv->bv_len;
138
139		gotit++;
140	}
141
142	Debug( LDAP_DEBUG_ARGS, "<= ldap_back_munge_filter \"%s\" (%d)\n",
143			filter->bv_val, gotit );
144
145	return gotit;
146}
147
148int
149ldap_back_search(
150		Operation	*op,
151		SlapReply	*rs )
152{
153	ldapinfo_t	*li = (ldapinfo_t *) op->o_bd->be_private;
154
155	ldapconn_t	*lc = NULL;
156	struct timeval	tv;
157	time_t		stoptime = (time_t)(-1);
158	LDAPMessage	*res,
159			*e;
160	int		rc = 0,
161			msgid;
162	struct berval	match = BER_BVNULL,
163			filter = BER_BVNULL;
164	int		i, x;
165	char		**attrs = NULL;
166	int		freetext = 0, filter_undef = 0;
167	int		do_retry = 1, dont_retry = 0;
168	LDAPControl	**ctrls = NULL;
169	char		**references = NULL;
170	int		remove_unknown_schema =
171				 LDAP_BACK_OMIT_UNKNOWN_SCHEMA (li);
172
173	rs_assert_ready( rs );
174	rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */
175
176	if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
177		return rs->sr_err;
178	}
179
180	/*
181	 * FIXME: in case of values return filter, we might want
182	 * to map attrs and maybe rewrite value
183	 */
184
185	if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
186		tv.tv_sec = op->ors_tlimit;
187		tv.tv_usec = 0;
188		stoptime = op->o_time + op->ors_tlimit;
189
190	} else {
191		LDAP_BACK_TV_SET( &tv );
192	}
193
194	i = 0;
195	if ( op->ors_attrs ) {
196		for ( ; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++ )
197			/* just count attrs */ ;
198	}
199
200	x = 0;
201	if ( op->o_bd->be_extra_anlist ) {
202		for ( ; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++ )
203			/* just count attrs */ ;
204	}
205
206	if ( i > 0 || x > 0 ) {
207		int j = 0;
208
209		attrs = op->o_tmpalloc( ( i + x + 1 )*sizeof( char * ),
210			op->o_tmpmemctx );
211		if ( attrs == NULL ) {
212			rs->sr_err = LDAP_NO_MEMORY;
213			rc = -1;
214			goto finish;
215		}
216
217		if ( i > 0 ) {
218			for ( i = 0; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++, j++ ) {
219				attrs[ j ] = op->ors_attrs[i].an_name.bv_val;
220			}
221		}
222
223		if ( x > 0 ) {
224			for ( x = 0; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++, j++ ) {
225				if ( op->o_bd->be_extra_anlist[x].an_desc &&
226					ad_inlist( op->o_bd->be_extra_anlist[x].an_desc, op->ors_attrs ) )
227				{
228					continue;
229				}
230
231				attrs[ j ] = op->o_bd->be_extra_anlist[x].an_name.bv_val;
232			}
233		}
234
235		attrs[ j ] = NULL;
236	}
237
238	ctrls = op->o_ctrls;
239	rc = ldap_back_controls_add( op, rs, lc, &ctrls );
240	if ( rc != LDAP_SUCCESS ) {
241		goto finish;
242	}
243
244	/* deal with <draft-zeilenga-ldap-t-f> filters */
245	filter = op->ors_filterstr;
246retry:
247	/* this goes after retry because ldap_back_munge_filter()
248	 * optionally replaces RFC 4526 T-F filters (&) (|)
249	 * if already computed, they will be re-installed
250	 * by filter2bv_undef_x() later */
251	if ( !LDAP_BACK_T_F( li ) ) {
252		ldap_back_munge_filter( op, &filter );
253	}
254
255	rs->sr_err = ldap_pvt_search( lc->lc_ld, op->o_req_dn.bv_val,
256			op->ors_scope, filter.bv_val,
257			attrs, op->ors_attrsonly, ctrls, NULL,
258			tv.tv_sec ? &tv : NULL,
259			op->ors_slimit, op->ors_deref, &msgid );
260
261	ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
262	ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_SEARCH ], 1 );
263	ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
264
265	if ( rs->sr_err != LDAP_SUCCESS ) {
266		switch ( rs->sr_err ) {
267		case LDAP_SERVER_DOWN:
268			if ( do_retry ) {
269				do_retry = 0;
270				if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_DONTSEND ) ) {
271					goto retry;
272				}
273			}
274
275			if ( lc == NULL ) {
276				/* reset by ldap_back_retry ... */
277				rs->sr_err = slap_map_api2result( rs );
278
279			} else {
280				rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_DONTSEND );
281			}
282
283			goto finish;
284
285		case LDAP_FILTER_ERROR:
286			/* first try? */
287			if ( !filter_undef &&
288				strstr( filter.bv_val, "(?" ) &&
289				!LDAP_BACK_NOUNDEFFILTER( li ) )
290			{
291				BER_BVZERO( &filter );
292				filter2bv_undef_x( op, op->ors_filter, 1, &filter );
293				filter_undef = 1;
294				goto retry;
295			}
296
297			/* invalid filters return success with no data */
298			rs->sr_err = LDAP_SUCCESS;
299			rs->sr_text = NULL;
300			goto finish;
301
302		default:
303			rs->sr_err = slap_map_api2result( rs );
304			rs->sr_text = NULL;
305			goto finish;
306		}
307	}
308
309	/* if needed, initialize timeout */
310	if ( li->li_timeout[ SLAP_OP_SEARCH ] ) {
311		if ( tv.tv_sec == 0 || tv.tv_sec > li->li_timeout[ SLAP_OP_SEARCH ] ) {
312			tv.tv_sec = li->li_timeout[ SLAP_OP_SEARCH ];
313			tv.tv_usec = 0;
314		}
315	}
316
317	/* We pull apart the ber result, stuff it into a slapd entry, and
318	 * let send_search_entry stuff it back into ber format. Slow & ugly,
319	 * but this is necessary for version matching, and for ACL processing.
320	 */
321
322	for ( rc = -2; rc != -1; rc = ldap_result( lc->lc_ld, msgid, LDAP_MSG_ONE, &tv, &res ) )
323	{
324		/* check for abandon */
325		if ( op->o_abandon || LDAP_BACK_CONN_ABANDON( lc ) ) {
326			if ( rc > 0 ) {
327				ldap_msgfree( res );
328			}
329			(void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
330			rc = SLAPD_ABANDON;
331			goto finish;
332		}
333
334		if ( rc == 0 || rc == -2 ) {
335			ldap_pvt_thread_yield();
336
337			/* check timeout */
338			if ( li->li_timeout[ SLAP_OP_SEARCH ] ) {
339				if ( rc == 0 ) {
340					(void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
341					rs->sr_text = "Operation timed out";
342					rc = rs->sr_err = op->o_protocol >= LDAP_VERSION3 ?
343						LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
344					goto finish;
345				}
346
347			} else {
348				LDAP_BACK_TV_SET( &tv );
349			}
350
351			/* check time limit */
352			if ( op->ors_tlimit != SLAP_NO_LIMIT
353					&& slap_get_time() > stoptime )
354			{
355				(void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
356				rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
357				goto finish;
358			}
359			continue;
360
361		} else {
362			/* only touch when activity actually took place... */
363			if ( li->li_idle_timeout ) {
364				lc->lc_time = op->o_time;
365			}
366
367			/* don't retry any more */
368			dont_retry = 1;
369		}
370
371
372		if ( rc == LDAP_RES_SEARCH_ENTRY ) {
373			Entry		ent = { 0 };
374			struct berval	bdn = BER_BVNULL;
375
376			do_retry = 0;
377
378			e = ldap_first_entry( lc->lc_ld, res );
379			rc = ldap_build_entry( op, e, &ent, &bdn,
380						remove_unknown_schema);
381			if ( rc == LDAP_SUCCESS ) {
382				ldap_get_entry_controls( lc->lc_ld, res, &rs->sr_ctrls );
383				rs->sr_entry = &ent;
384				rs->sr_attrs = op->ors_attrs;
385				rs->sr_operational_attrs = NULL;
386				rs->sr_flags = 0;
387				rs->sr_err = LDAP_SUCCESS;
388				rc = rs->sr_err = send_search_entry( op, rs );
389				if ( rs->sr_ctrls ) {
390					ldap_controls_free( rs->sr_ctrls );
391					rs->sr_ctrls = NULL;
392				}
393				rs->sr_entry = NULL;
394				rs->sr_flags = 0;
395				if ( !BER_BVISNULL( &ent.e_name ) ) {
396					assert( ent.e_name.bv_val != bdn.bv_val );
397					op->o_tmpfree( ent.e_name.bv_val, op->o_tmpmemctx );
398					BER_BVZERO( &ent.e_name );
399				}
400				if ( !BER_BVISNULL( &ent.e_nname ) ) {
401					op->o_tmpfree( ent.e_nname.bv_val, op->o_tmpmemctx );
402					BER_BVZERO( &ent.e_nname );
403				}
404				entry_clean( &ent );
405			}
406			ldap_msgfree( res );
407			switch ( rc ) {
408			case LDAP_SUCCESS:
409			case LDAP_INSUFFICIENT_ACCESS:
410				break;
411
412			default:
413				if ( rc == LDAP_UNAVAILABLE ) {
414					rc = rs->sr_err = LDAP_OTHER;
415				} else {
416					(void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
417				}
418				goto finish;
419			}
420
421		} else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
422			if ( LDAP_BACK_NOREFS( li ) ) {
423				ldap_msgfree( res );
424				continue;
425			}
426
427			do_retry = 0;
428			rc = ldap_parse_reference( lc->lc_ld, res,
429					&references, &rs->sr_ctrls, 1 );
430
431			if ( rc != LDAP_SUCCESS ) {
432				continue;
433			}
434
435			/* FIXME: there MUST be at least one */
436			if ( references && references[ 0 ] && references[ 0 ][ 0 ] ) {
437				int		cnt;
438
439				for ( cnt = 0; references[ cnt ]; cnt++ )
440					/* NO OP */ ;
441
442				/* FIXME: there MUST be at least one */
443				rs->sr_ref = op->o_tmpalloc( ( cnt + 1 ) * sizeof( struct berval ),
444					op->o_tmpmemctx );
445
446				for ( cnt = 0; references[ cnt ]; cnt++ ) {
447					ber_str2bv( references[ cnt ], 0, 0, &rs->sr_ref[ cnt ] );
448				}
449				BER_BVZERO( &rs->sr_ref[ cnt ] );
450
451				/* ignore return value by now */
452				RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
453				rs->sr_entry = NULL;
454				( void )send_search_reference( op, rs );
455
456			} else {
457				Debug( LDAP_DEBUG_ANY,
458					"%s ldap_back_search: "
459					"got SEARCH_REFERENCE "
460					"with no referrals\n",
461					op->o_log_prefix );
462			}
463
464			/* cleanup */
465			if ( references ) {
466				ber_memvfree( (void **)references );
467				op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
468				rs->sr_ref = NULL;
469				references = NULL;
470			}
471
472			if ( rs->sr_ctrls ) {
473				ldap_controls_free( rs->sr_ctrls );
474				rs->sr_ctrls = NULL;
475			}
476
477		} else if ( rc == LDAP_RES_INTERMEDIATE ) {
478			/* FIXME: response controls
479			 * are passed without checks */
480			rc = ldap_parse_intermediate( lc->lc_ld,
481				res,
482				(char **)&rs->sr_rspoid,
483				&rs->sr_rspdata,
484				&rs->sr_ctrls,
485				0 );
486			if ( rc != LDAP_SUCCESS ) {
487				continue;
488			}
489
490			slap_send_ldap_intermediate( op, rs );
491
492			if ( rs->sr_rspoid != NULL ) {
493				ber_memfree( (char *)rs->sr_rspoid );
494				rs->sr_rspoid = NULL;
495			}
496
497			if ( rs->sr_rspdata != NULL ) {
498				ber_bvfree( rs->sr_rspdata );
499				rs->sr_rspdata = NULL;
500			}
501
502			if ( rs->sr_ctrls != NULL ) {
503				ldap_controls_free( rs->sr_ctrls );
504				rs->sr_ctrls = NULL;
505			}
506
507		} else {
508			char		*err = NULL;
509
510			rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
511					&match.bv_val, &err,
512					&references, &rs->sr_ctrls, 1 );
513			if ( rc == LDAP_SUCCESS ) {
514				if ( err ) {
515					rs->sr_text = err;
516					freetext = 1;
517				}
518			} else {
519				rs->sr_err = rc;
520			}
521			rs->sr_err = slap_map_api2result( rs );
522
523			/* RFC 4511: referrals can only appear
524			 * if result code is LDAP_REFERRAL */
525			if ( references
526				&& references[ 0 ]
527				&& references[ 0 ][ 0 ] )
528			{
529				if ( rs->sr_err != LDAP_REFERRAL ) {
530					Debug( LDAP_DEBUG_ANY,
531						"%s ldap_back_search: "
532						"got referrals with err=%d\n",
533						op->o_log_prefix,
534						rs->sr_err );
535
536				} else {
537					int	cnt;
538
539					for ( cnt = 0; references[ cnt ]; cnt++ )
540						/* NO OP */ ;
541
542					rs->sr_ref = op->o_tmpalloc( ( cnt + 1 ) * sizeof( struct berval ),
543						op->o_tmpmemctx );
544
545					for ( cnt = 0; references[ cnt ]; cnt++ ) {
546						/* duplicating ...*/
547						ber_str2bv( references[ cnt ], 0, 0, &rs->sr_ref[ cnt ] );
548					}
549					BER_BVZERO( &rs->sr_ref[ cnt ] );
550				}
551
552			} else if ( rs->sr_err == LDAP_REFERRAL ) {
553				Debug( LDAP_DEBUG_ANY,
554					"%s ldap_back_search: "
555					"got err=%d with null "
556					"or empty referrals\n",
557					op->o_log_prefix,
558					rs->sr_err );
559
560				rs->sr_err = LDAP_NO_SUCH_OBJECT;
561			}
562
563			if ( match.bv_val != NULL ) {
564				match.bv_len = strlen( match.bv_val );
565			}
566
567			rc = 0;
568			break;
569		}
570
571		/* if needed, restore timeout */
572		if ( li->li_timeout[ SLAP_OP_SEARCH ] ) {
573			if ( tv.tv_sec == 0 || tv.tv_sec > li->li_timeout[ SLAP_OP_SEARCH ] ) {
574				tv.tv_sec = li->li_timeout[ SLAP_OP_SEARCH ];
575				tv.tv_usec = 0;
576			}
577		}
578	}
579
580 	if ( rc == -1 ) {
581		if ( dont_retry == 0 ) {
582			if ( do_retry ) {
583				do_retry = 0;
584				if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_DONTSEND ) ) {
585					goto retry;
586				}
587			}
588
589			rs->sr_err = LDAP_SERVER_DOWN;
590			rs->sr_err = slap_map_api2result( rs );
591			goto finish;
592
593		} else if ( LDAP_BACK_ONERR_STOP( li ) ) {
594			/* if onerr == STOP */
595			rs->sr_err = LDAP_SERVER_DOWN;
596			rs->sr_err = slap_map_api2result( rs );
597			goto finish;
598		}
599	}
600
601	/*
602	 * Rewrite the matched portion of the search base, if required
603	 */
604	if ( !BER_BVISNULL( &match ) && !BER_BVISEMPTY( &match ) ) {
605		struct berval	pmatch;
606
607		if ( dnPretty( NULL, &match, &pmatch, op->o_tmpmemctx ) != LDAP_SUCCESS ) {
608			pmatch.bv_val = match.bv_val;
609			match.bv_val = NULL;
610		}
611		rs->sr_matched = pmatch.bv_val;
612		rs->sr_flags |= REP_MATCHED_MUSTBEFREED;
613	}
614
615finish:;
616	if ( !BER_BVISNULL( &match ) ) {
617		ber_memfree( match.bv_val );
618	}
619
620	if ( rs->sr_v2ref ) {
621		rs->sr_err = LDAP_REFERRAL;
622	}
623
624	if ( LDAP_BACK_QUARANTINE( li ) ) {
625		ldap_back_quarantine( op, rs );
626	}
627
628	if ( filter.bv_val != op->ors_filterstr.bv_val ) {
629		op->o_tmpfree( filter.bv_val, op->o_tmpmemctx );
630	}
631
632#if 0
633	/* let send_ldap_result play cleanup handlers (ITS#4645) */
634	if ( rc != SLAPD_ABANDON )
635#endif
636	{
637		send_ldap_result( op, rs );
638	}
639
640	(void)ldap_back_controls_free( op, rs, &ctrls );
641
642	if ( rs->sr_ctrls ) {
643		ldap_controls_free( rs->sr_ctrls );
644		rs->sr_ctrls = NULL;
645	}
646
647	if ( rs->sr_text ) {
648		if ( freetext ) {
649			ber_memfree( (char *)rs->sr_text );
650		}
651		rs->sr_text = NULL;
652	}
653
654	if ( rs->sr_ref ) {
655		op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
656		rs->sr_ref = NULL;
657	}
658
659	if ( references ) {
660		ber_memvfree( (void **)references );
661	}
662
663	if ( attrs ) {
664		op->o_tmpfree( attrs, op->o_tmpmemctx );
665	}
666
667	if ( lc != NULL ) {
668		ldap_back_release_conn( li, lc );
669	}
670
671	if ( rs->sr_err == LDAP_UNAVAILABLE &&
672		/* if we originally bound and wanted rebind-as-user, must drop
673		 * the connection now because we just discarded the credentials.
674		 * ITS#7464, #8142
675		 */
676		LDAP_BACK_SAVECRED( li ) && SLAP_IS_AUTHZ_BACKEND( op ) )
677		rs->sr_err = SLAPD_DISCONNECT;
678	return rs->sr_err;
679}
680
681static int
682ldap_build_entry(
683		Operation	*op,
684		LDAPMessage	*e,
685		Entry		*ent,
686		struct berval	*bdn,
687		int remove_unknown_schema)
688{
689	struct berval	a;
690	BerElement	ber = *ldap_get_message_ber( e );
691	Attribute	*attr, **attrp;
692	const char	*text;
693	int		last;
694	char *lastb;
695	ber_len_t len;
696
697	/* safe assumptions ... */
698	assert( ent != NULL );
699	BER_BVZERO( &ent->e_bv );
700
701	if ( ber_scanf( &ber, "{m", bdn ) == LBER_ERROR ) {
702		return LDAP_DECODING_ERROR;
703	}
704
705	/*
706	 * Note: this may fail if the target host(s) schema differs
707	 * from the one known to the meta, and a DN with unknown
708	 * attributes is returned.
709	 *
710	 * FIXME: should we log anything, or delegate to dnNormalize?
711	 */
712	/* Note: if the distinguished values or the naming attributes
713	 * change, should we massage them as well?
714	 */
715	if ( dnPrettyNormal( NULL, bdn, &ent->e_name, &ent->e_nname,
716		op->o_tmpmemctx ) != LDAP_SUCCESS )
717	{
718		return LDAP_INVALID_DN_SYNTAX;
719	}
720
721	ent->e_attrs = NULL;
722	if ( ber_first_element( &ber, &len, &lastb ) != LBER_SEQUENCE ) {
723		return LDAP_SUCCESS;
724	}
725
726	attrp = &ent->e_attrs;
727	while ( ber_next_element( &ber, &len, lastb ) == LBER_SEQUENCE &&
728		ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
729		int				i;
730		slap_syntax_validate_func	*validate;
731		slap_syntax_transform_func	*pretty;
732
733		attr = attr_alloc( NULL );
734		if ( attr == NULL ) {
735			return LDAP_OTHER;
736		}
737		if ( slap_bv2ad( &a, &attr->a_desc, &text )
738				!= LDAP_SUCCESS )
739		{
740			if ( slap_bv2undef_ad( &a, &attr->a_desc, &text,
741				 (remove_unknown_schema ? SLAP_AD_NOINSERT : SLAP_AD_PROXIED )) != LDAP_SUCCESS )
742			{
743				Debug( LDAP_DEBUG_ANY,
744					"%s ldap_build_entry: "
745					"slap_bv2undef_ad(%s): %s\n",
746					op->o_log_prefix, a.bv_val, text );
747
748				( void )ber_scanf( &ber, "x" /* [W] */ );
749				attr_free( attr );
750				continue;
751			}
752		}
753
754		/* no subschemaSubentry */
755		if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry
756			|| attr->a_desc == slap_schema.si_ad_entryDN )
757		{
758
759			/*
760			 * We eat target's subschemaSubentry because
761			 * a search for this value is likely not
762			 * to resolve to the appropriate backend;
763			 * later, the local subschemaSubentry is
764			 * added.
765			 *
766			 * We also eat entryDN because the frontend
767			 * will reattach it without checking if already
768			 * present...
769			 */
770			( void )ber_scanf( &ber, "x" /* [W] */ );
771			attr_free( attr );
772			continue;
773		}
774
775		if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR
776				|| attr->a_vals == NULL )
777		{
778			/*
779			 * Note: attr->a_vals can be null when using
780			 * values result filter
781			 */
782			attr->a_vals = (struct berval *)&slap_dummy_bv;
783		}
784
785		validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate;
786		pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
787
788		if ( !validate && !pretty ) {
789			attr->a_nvals = NULL;
790			attr_free( attr );
791			goto next_attr;
792		}
793
794		for ( i = 0; !BER_BVISNULL( &attr->a_vals[i] ); i++ ) ;
795		last = i;
796
797		/*
798		 * check that each value is valid per syntax
799		 * and pretty if appropriate
800		 */
801		for ( i = 0; i<last; i++ ) {
802			struct berval	pval;
803			int		rc;
804
805			if ( pretty ) {
806				rc = ordered_value_pretty( attr->a_desc,
807					&attr->a_vals[i], &pval, NULL );
808
809			} else {
810				rc = ordered_value_validate( attr->a_desc,
811					&attr->a_vals[i], 0 );
812			}
813
814			if ( rc != LDAP_SUCCESS ) {
815				ObjectClass *oc;
816
817				/* check if, by chance, it's an undefined objectClass */
818				if ( attr->a_desc == slap_schema.si_ad_objectClass &&
819						( oc = oc_bvfind_undef_ex( &attr->a_vals[i],
820								remove_unknown_schema ) ) != NULL )
821				{
822					ber_dupbv( &pval, &oc->soc_cname );
823					rc = LDAP_SUCCESS;
824
825				} else {
826					ber_memfree( attr->a_vals[i].bv_val );
827					if ( --last == i ) {
828						BER_BVZERO( &attr->a_vals[i] );
829						break;
830					}
831					attr->a_vals[i] = attr->a_vals[last];
832					BER_BVZERO( &attr->a_vals[last] );
833					i--;
834				}
835			}
836
837			if ( rc == LDAP_SUCCESS && pretty ) {
838				ber_memfree( attr->a_vals[i].bv_val );
839				attr->a_vals[i] = pval;
840			}
841		}
842		attr->a_numvals = last = i;
843		if ( last == 0 && attr->a_vals != &slap_dummy_bv ) {
844			attr->a_nvals = NULL;
845			attr_free( attr );
846			goto next_attr;
847		}
848
849		if ( last && attr->a_desc->ad_type->sat_equality &&
850				attr->a_desc->ad_type->sat_equality->smr_normalize )
851		{
852			attr->a_nvals = ch_malloc( ( last + 1 )*sizeof( struct berval ) );
853			for ( i = 0; i < last; i++ ) {
854				int		rc;
855
856				rc = ordered_value_normalize(
857					SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
858					attr->a_desc,
859					attr->a_desc->ad_type->sat_equality,
860					&attr->a_vals[i], &attr->a_nvals[i],
861					NULL );
862
863				if ( rc != LDAP_SUCCESS ) {
864					ber_memfree( attr->a_vals[i].bv_val );
865					if ( --last == i ) {
866						BER_BVZERO( &attr->a_vals[i] );
867						break;
868					}
869					attr->a_vals[i] = attr->a_vals[last];
870					BER_BVZERO( &attr->a_vals[last] );
871					i--;
872				}
873			}
874			BER_BVZERO( &attr->a_nvals[i] );
875			if ( last == 0 ) {
876				attr_free( attr );
877				goto next_attr;
878			}
879
880		} else {
881			attr->a_nvals = attr->a_vals;
882		}
883
884		attr->a_numvals = last;
885
886		/* Handle sorted vals, strip dups but keep the attr */
887		if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
888			while ( attr->a_numvals > 1 ) {
889				int rc = slap_sort_vals( (Modifications *)attr, &text, &i, op->o_tmpmemctx );
890				if ( rc != LDAP_TYPE_OR_VALUE_EXISTS )
891					break;
892
893				/* Strip duplicate values */
894				if ( attr->a_nvals != attr->a_vals )
895					ber_memfree( attr->a_nvals[i].bv_val );
896				ber_memfree( attr->a_vals[i].bv_val );
897				attr->a_numvals--;
898
899				assert( i >= 0 );
900				if ( (unsigned)i < attr->a_numvals ) {
901					attr->a_vals[i] = attr->a_vals[attr->a_numvals];
902					if ( attr->a_nvals != attr->a_vals )
903						attr->a_nvals[i] = attr->a_nvals[attr->a_numvals];
904				}
905				BER_BVZERO(&attr->a_vals[attr->a_numvals]);
906				if ( attr->a_nvals != attr->a_vals )
907					BER_BVZERO(&attr->a_nvals[attr->a_numvals]);
908			}
909			attr->a_flags |= SLAP_ATTR_SORTED_VALS;
910		}
911
912		*attrp = attr;
913		attrp = &attr->a_next;
914
915next_attr:;
916	}
917
918	return LDAP_SUCCESS;
919}
920
921/* return 0 IFF we can retrieve the entry with ndn
922 */
923int
924ldap_back_entry_get(
925		Operation		*op,
926		struct berval		*ndn,
927		ObjectClass		*oc,
928		AttributeDescription	*at,
929		int			rw,
930		Entry			**ent )
931{
932	ldapinfo_t	*li = (ldapinfo_t *) op->o_bd->be_private;
933
934	ldapconn_t	*lc = NULL;
935	int		rc;
936	struct berval	bdn;
937	LDAPMessage	*result = NULL,
938			*e = NULL;
939	char		*attr[3], **attrp = NULL;
940	char		*filter = NULL;
941	SlapReply	rs;
942	int		do_retry = 1;
943	LDAPControl	**ctrls = NULL;
944	Operation op2 = *op;
945
946	int		remove_unknown_schema =
947				LDAP_BACK_OMIT_UNKNOWN_SCHEMA (li);
948	*ent = NULL;
949
950	/* Tell getconn this is a privileged op */
951	op2.o_do_not_cache = 1;
952	/* use rootdn to be doubly explicit this is privileged */
953	op2.o_dn = op->o_bd->be_rootdn;
954	op2.o_ndn = op->o_bd->be_rootndn;
955	/* ldap_back_entry_get() is an entry lookup, so it does not need
956	 * to know what the entry is being looked up for */
957	op2.o_tag = LDAP_REQ_SEARCH;
958	op2.o_ctrls = NULL;
959	rc = ldap_back_dobind( &lc, &op2, &rs, LDAP_BACK_DONTSEND );
960	if ( !rc ) {
961		return rs.sr_err;
962	}
963
964	if ( at ) {
965		attrp = attr;
966		if ( oc && at != slap_schema.si_ad_objectClass ) {
967			attr[0] = slap_schema.si_ad_objectClass->ad_cname.bv_val;
968			attr[1] = at->ad_cname.bv_val;
969			attr[2] = NULL;
970
971		} else {
972			attr[0] = at->ad_cname.bv_val;
973			attr[1] = NULL;
974		}
975	}
976
977	if ( oc ) {
978		char	*ptr;
979
980		filter = op->o_tmpalloc( STRLENOF( "(objectClass=" ")" )
981				+ oc->soc_cname.bv_len + 1, op->o_tmpmemctx );
982		ptr = lutil_strcopy( filter, "(objectClass=" );
983		ptr = lutil_strcopy( ptr, oc->soc_cname.bv_val );
984		*ptr++ = ')';
985		*ptr++ = '\0';
986	}
987
988retry:
989	ctrls = NULL;
990	rc = ldap_back_controls_add( &op2, &rs, lc, &ctrls );
991	if ( rc != LDAP_SUCCESS ) {
992		goto cleanup;
993	}
994
995	/* TODO: timeout? */
996	rc = ldap_pvt_search_s( lc->lc_ld, ndn->bv_val, LDAP_SCOPE_BASE, filter,
997				attrp, LDAP_DEREF_NEVER, ctrls, NULL,
998				NULL, LDAP_NO_LIMIT, 0, &result );
999	if ( rc != LDAP_SUCCESS ) {
1000		if ( rc == LDAP_SERVER_DOWN && do_retry ) {
1001			do_retry = 0;
1002			if ( ldap_back_retry( &lc, &op2, &rs, LDAP_BACK_DONTSEND ) ) {
1003				/* if the identity changed, there might be need to re-authz */
1004				(void)ldap_back_controls_free( &op2, &rs, &ctrls );
1005				goto retry;
1006			}
1007		}
1008		goto cleanup;
1009	}
1010
1011	e = ldap_first_entry( lc->lc_ld, result );
1012	if ( e == NULL ) {
1013		/* the entry exists, but it doesn't match the filter? */
1014		rc = LDAP_NO_RESULTS_RETURNED;
1015		goto cleanup;
1016	}
1017
1018	*ent = entry_alloc();
1019	if ( *ent == NULL ) {
1020		rc = LDAP_NO_MEMORY;
1021		goto cleanup;
1022	}
1023
1024	rc = ldap_build_entry( op, e, *ent, &bdn, remove_unknown_schema );
1025
1026	if ( rc != LDAP_SUCCESS ) {
1027		entry_free( *ent );
1028		*ent = NULL;
1029	}
1030
1031cleanup:
1032	(void)ldap_back_controls_free( &op2, &rs, &ctrls );
1033
1034	if ( result ) {
1035		ldap_msgfree( result );
1036	}
1037
1038	if ( filter ) {
1039		op->o_tmpfree( filter, op->o_tmpmemctx );
1040	}
1041
1042	if ( lc != NULL ) {
1043		ldap_back_release_conn( li, lc );
1044	}
1045
1046	return rc;
1047}
1048