xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/rwmmap.c (revision 9aa0541bdf64142d9a27c2cf274394d60182818f)
1 /*	$NetBSD: rwmmap.c,v 1.1.1.3 2010/12/12 15:23:43 adam Exp $	*/
2 
3 /* rwmmap.c - rewrite/mapping routines */
4 /* OpenLDAP: pkg/ldap/servers/slapd/overlays/rwmmap.c,v 1.31.2.15 2010/04/19 19:31:17 quanah Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 1999-2010 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 "portable.h"
27 
28 #ifdef SLAPD_OVER_RWM
29 
30 #include <stdio.h>
31 
32 #include <ac/string.h>
33 #include <ac/socket.h>
34 
35 #include "slap.h"
36 #include "rwm.h"
37 
38 #undef ldap_debug	/* silence a warning in ldap-int.h */
39 #include "../../../libraries/libldap/ldap-int.h"
40 
41 int
42 rwm_mapping_cmp( const void *c1, const void *c2 )
43 {
44 	struct ldapmapping *map1 = (struct ldapmapping *)c1;
45 	struct ldapmapping *map2 = (struct ldapmapping *)c2;
46 	int rc = map1->m_src.bv_len - map2->m_src.bv_len;
47 
48 	if ( rc ) {
49 		return rc;
50 	}
51 
52 	return strcasecmp( map1->m_src.bv_val, map2->m_src.bv_val );
53 }
54 
55 int
56 rwm_mapping_dup( void *c1, void *c2 )
57 {
58 	struct ldapmapping *map1 = (struct ldapmapping *)c1;
59 	struct ldapmapping *map2 = (struct ldapmapping *)c2;
60 	int rc = map1->m_src.bv_len - map2->m_src.bv_len;
61 
62 	if ( rc ) {
63 		return 0;
64 	}
65 
66 	return ( ( strcasecmp( map1->m_src.bv_val, map2->m_src.bv_val ) == 0 ) ? -1 : 0 );
67 }
68 
69 int
70 rwm_map_init( struct ldapmap *lm, struct ldapmapping **m )
71 {
72 	struct ldapmapping	*mapping;
73 	const char		*text;
74 	int			rc;
75 
76 	assert( m != NULL );
77 
78 	*m = NULL;
79 
80 	mapping = (struct ldapmapping *)ch_calloc( 2,
81 			sizeof( struct ldapmapping ) );
82 	if ( mapping == NULL ) {
83 		return LDAP_NO_MEMORY;
84 	}
85 
86 	/* NOTE: this is needed to make sure that
87 	 *	rwm-map attribute *
88 	 * does not  filter out all attributes including objectClass */
89 	rc = slap_str2ad( "objectClass", &mapping[0].m_src_ad, &text );
90 	if ( rc != LDAP_SUCCESS ) {
91 		ch_free( mapping );
92 		return rc;
93 	}
94 
95 	mapping[0].m_dst_ad = mapping[0].m_src_ad;
96 	ber_dupbv( &mapping[0].m_src, &mapping[0].m_src_ad->ad_cname );
97 	ber_dupbv( &mapping[0].m_dst, &mapping[0].m_src );
98 
99 	mapping[1].m_src = mapping[0].m_src;
100 	mapping[1].m_dst = mapping[0].m_dst;
101 	mapping[1].m_src_ad = mapping[0].m_src_ad;
102 	mapping[1].m_dst_ad = mapping[1].m_src_ad;
103 
104 	avl_insert( &lm->map, (caddr_t)&mapping[0],
105 			rwm_mapping_cmp, rwm_mapping_dup );
106 	avl_insert( &lm->remap, (caddr_t)&mapping[1],
107 			rwm_mapping_cmp, rwm_mapping_dup );
108 
109 	*m = mapping;
110 
111 	return rc;
112 }
113 
114 int
115 rwm_mapping( struct ldapmap *map, struct berval *s, struct ldapmapping **m, int remap )
116 {
117 	Avlnode *tree;
118 	struct ldapmapping fmapping;
119 
120 	if ( map == NULL ) {
121 		return 0;
122 	}
123 
124 	assert( m != NULL );
125 
126 	/* let special attrnames slip through (ITS#5760) */
127 	if ( bvmatch( s, slap_bv_no_attrs )
128 		|| bvmatch( s, slap_bv_all_user_attrs )
129 		|| bvmatch( s, slap_bv_all_operational_attrs ) )
130 	{
131 		*m = NULL;
132 		return 0;
133 	}
134 
135 	if ( remap == RWM_REMAP ) {
136 		tree = map->remap;
137 
138 	} else {
139 		tree = map->map;
140 	}
141 
142 	fmapping.m_src = *s;
143 	*m = (struct ldapmapping *)avl_find( tree, (caddr_t)&fmapping,
144 			rwm_mapping_cmp );
145 
146 	if ( *m == NULL ) {
147 		return map->drop_missing;
148 	}
149 
150 	return 0;
151 }
152 
153 void
154 rwm_map( struct ldapmap *map, struct berval *s, struct berval *bv, int remap )
155 {
156 	struct ldapmapping *mapping;
157 
158 	/* map->map may be NULL when mapping is configured,
159 	 * but map->remap can't */
160 	if ( map->remap == NULL ) {
161 		*bv = *s;
162 		return;
163 	}
164 
165 	BER_BVZERO( bv );
166 	( void )rwm_mapping( map, s, &mapping, remap );
167 	if ( mapping != NULL ) {
168 		if ( !BER_BVISNULL( &mapping->m_dst ) ) {
169 			*bv = mapping->m_dst;
170 		}
171 		return;
172 	}
173 
174 	if ( !map->drop_missing ) {
175 		*bv = *s;
176 	}
177 }
178 
179 /*
180  * Map attribute names in place
181  */
182 int
183 rwm_map_attrnames(
184 	Operation	*op,
185 	struct ldapmap	*at_map,
186 	struct ldapmap	*oc_map,
187 	AttributeName	*an,
188 	AttributeName	**anp,
189 	int		remap )
190 {
191 	int		i, j;
192 
193 	assert( anp != NULL );
194 
195 	*anp = NULL;
196 
197 	if ( an == NULL ) {
198 		return LDAP_SUCCESS;
199 	}
200 
201 	for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ )
202 		/* just count */ ;
203 	*anp = op->o_tmpalloc( ( i + 1 )* sizeof( AttributeName ),
204 		op->o_tmpmemctx );
205 	if ( *anp == NULL ) {
206 		return LDAP_NO_MEMORY;
207 	}
208 
209 	for ( i = 0, j = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
210 		struct ldapmapping	*m;
211 		int			at_drop_missing = 0,
212 					oc_drop_missing = 0;
213 
214 		if ( an[i].an_desc ) {
215 			if ( !at_map ) {
216 				/* FIXME: better leave as is? */
217 				continue;
218 			}
219 
220 			at_drop_missing = rwm_mapping( at_map, &an[i].an_name, &m, remap );
221 			if ( at_drop_missing || ( m && BER_BVISNULL( &m->m_dst ) ) ) {
222 				continue;
223 			}
224 
225 			if ( !m ) {
226 				(*anp)[j] = an[i];
227 				j++;
228 				continue;
229 			}
230 
231 			(*anp)[j] = an[i];
232 			if ( remap == RWM_MAP ) {
233 				(*anp)[j].an_name = m->m_dst;
234 				(*anp)[j].an_desc = m->m_dst_ad;
235 			} else {
236 				(*anp)[j].an_name = m->m_src;
237 				(*anp)[j].an_desc = m->m_src_ad;
238 
239 			}
240 
241 			j++;
242 			continue;
243 
244 		} else if ( an[i].an_oc ) {
245 			if ( !oc_map ) {
246 				/* FIXME: better leave as is? */
247 				continue;
248 			}
249 
250 			oc_drop_missing = rwm_mapping( oc_map, &an[i].an_name, &m, remap );
251 
252 			if ( oc_drop_missing || ( m && BER_BVISNULL( &m->m_dst ) ) ) {
253 				continue;
254 			}
255 
256 			if ( !m ) {
257 				(*anp)[j] = an[i];
258 				j++;
259 				continue;
260 			}
261 
262 			(*anp)[j] = an[i];
263 			if ( remap == RWM_MAP ) {
264 				(*anp)[j].an_name = m->m_dst;
265 				(*anp)[j].an_oc = m->m_dst_oc;
266 			} else {
267 				(*anp)[j].an_name = m->m_src;
268 				(*anp)[j].an_oc = m->m_src_oc;
269 			}
270 
271 		} else {
272 			at_drop_missing = rwm_mapping( at_map, &an[i].an_name, &m, remap );
273 
274 			if ( at_drop_missing || !m ) {
275 				oc_drop_missing = rwm_mapping( oc_map, &an[i].an_name, &m, remap );
276 
277 				/* if both at_map and oc_map required to drop missing,
278 				 * then do it */
279 				if ( oc_drop_missing && at_drop_missing ) {
280 					continue;
281 				}
282 
283 				/* if no oc_map mapping was found and at_map required
284 				 * to drop missing, then do it; otherwise, at_map wins
285 				 * and an is considered an attr and is left unchanged */
286 				if ( !m ) {
287 					if ( at_drop_missing ) {
288 						continue;
289 					}
290 					(*anp)[j] = an[i];
291 					j++;
292 					continue;
293 				}
294 
295 				if ( BER_BVISNULL( &m->m_dst ) ) {
296 					continue;
297 				}
298 
299 				(*anp)[j] = an[i];
300 				if ( remap == RWM_MAP ) {
301 					(*anp)[j].an_name = m->m_dst;
302 					(*anp)[j].an_oc = m->m_dst_oc;
303 				} else {
304 					(*anp)[j].an_name = m->m_src;
305 					(*anp)[j].an_oc = m->m_src_oc;
306 				}
307 				j++;
308 				continue;
309 			}
310 
311 			if ( !BER_BVISNULL( &m->m_dst ) ) {
312 				(*anp)[j] = an[i];
313 				if ( remap == RWM_MAP ) {
314 					(*anp)[j].an_name = m->m_dst;
315 					(*anp)[j].an_desc = m->m_dst_ad;
316 				} else {
317 					(*anp)[j].an_name = m->m_src;
318 					(*anp)[j].an_desc = m->m_src_ad;
319 				}
320 				j++;
321 				continue;
322 			}
323 		}
324 	}
325 
326 	if ( j == 0 && i != 0 ) {
327 		memset( &(*anp)[0], 0, sizeof( AttributeName ) );
328 		(*anp)[0].an_name = *slap_bv_no_attrs;
329 		j = 1;
330 	}
331 	memset( &(*anp)[j], 0, sizeof( AttributeName ) );
332 
333 	return LDAP_SUCCESS;
334 }
335 
336 #if 0 /* unused! */
337 int
338 rwm_map_attrs(
339 	struct ldapmap	*at_map,
340 	AttributeName	*an,
341 	int		remap,
342 	char		***mapped_attrs )
343 {
344 	int i, j;
345 	char **na;
346 
347 	if ( an == NULL ) {
348 		*mapped_attrs = NULL;
349 		return LDAP_SUCCESS;
350 	}
351 
352 	for ( i = 0; !BER_BVISNULL( &an[ i ].an_name ); i++ )
353 		/* count'em */ ;
354 
355 	na = (char **)ch_calloc( i + 1, sizeof( char * ) );
356 	if ( na == NULL ) {
357 		*mapped_attrs = NULL;
358 		return LDAP_NO_MEMORY;
359 	}
360 
361 	for ( i = j = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
362 		struct ldapmapping	*mapping;
363 
364 		if ( rwm_mapping( at_map, &an[i].an_name, &mapping, remap ) ) {
365 			continue;
366 		}
367 
368 		if ( !mapping ) {
369 			na[ j++ ] = an[ i ].an_name.bv_val;
370 
371 		} else if ( !BER_BVISNULL( &mapping->m_dst ) ) {
372 			na[ j++ ] = mapping->m_dst.bv_val;
373 		}
374 	}
375 
376 	if ( j == 0 && i != 0 ) {
377 		na[ j++ ] = LDAP_NO_ATTRS;
378 	}
379 
380 	na[ j ] = NULL;
381 
382 	*mapped_attrs = na;
383 
384 	return LDAP_SUCCESS;
385 }
386 #endif
387 
388 static int
389 map_attr_value(
390 	dncookie		*dc,
391 	AttributeDescription 	**adp,
392 	struct berval		*mapped_attr,
393 	struct berval		*value,
394 	struct berval		*mapped_value,
395 	int			remap,
396 	void			*memctx )
397 {
398 	struct berval		vtmp = BER_BVNULL;
399 	int			freeval = 0;
400 	AttributeDescription	*ad = *adp;
401 	struct ldapmapping	*mapping = NULL;
402 
403 	rwm_mapping( &dc->rwmap->rwm_at, &ad->ad_cname, &mapping, remap );
404 	if ( mapping == NULL ) {
405 		if ( dc->rwmap->rwm_at.drop_missing ) {
406 			return -1;
407 		}
408 
409 		*mapped_attr = ad->ad_cname;
410 
411 	} else {
412 		*mapped_attr = mapping->m_dst;
413 	}
414 
415 	if ( value != NULL ) {
416 		assert( mapped_value != NULL );
417 
418 		if ( ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
419 				|| ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
420 		{
421 			dncookie 	fdc = *dc;
422 			int		rc;
423 
424 			fdc.ctx = "searchFilterAttrDN";
425 
426 			vtmp = *value;
427 			rc = rwm_dn_massage_normalize( &fdc, value, &vtmp );
428 			switch ( rc ) {
429 			case LDAP_SUCCESS:
430 				if ( vtmp.bv_val != value->bv_val ) {
431 					freeval = 1;
432 				}
433 				break;
434 
435 			case LDAP_UNWILLING_TO_PERFORM:
436 			case LDAP_OTHER:
437 			default:
438 				return -1;
439 			}
440 
441 		} else if ( ad->ad_type->sat_equality->smr_usage & SLAP_MR_MUTATION_NORMALIZER ) {
442 			if ( ad->ad_type->sat_equality->smr_normalize(
443 				(SLAP_MR_DENORMALIZE|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX),
444 				NULL, NULL, value, &vtmp, memctx ) )
445 			{
446 				return -1;
447 			}
448 			freeval = 2;
449 
450 		} else if ( ad == slap_schema.si_ad_objectClass
451 				|| ad == slap_schema.si_ad_structuralObjectClass )
452 		{
453 			rwm_map( &dc->rwmap->rwm_oc, value, &vtmp, remap );
454 			if ( BER_BVISNULL( &vtmp ) || BER_BVISEMPTY( &vtmp ) ) {
455 				vtmp = *value;
456 			}
457 
458 		} else {
459 			vtmp = *value;
460 		}
461 
462 		filter_escape_value_x( &vtmp, mapped_value, memctx );
463 
464 		switch ( freeval ) {
465 		case 1:
466 			ch_free( vtmp.bv_val );
467 			break;
468 
469 		case 2:
470 			ber_memfree_x( vtmp.bv_val, memctx );
471 			break;
472 		}
473 	}
474 
475 	if ( mapping != NULL ) {
476 		assert( mapping->m_dst_ad != NULL );
477 		*adp = mapping->m_dst_ad;
478 	}
479 
480 	return 0;
481 }
482 
483 static int
484 rwm_int_filter_map_rewrite(
485 	Operation		*op,
486 	dncookie		*dc,
487 	Filter			*f,
488 	struct berval		*fstr )
489 {
490 	int		i;
491 	Filter		*p;
492 	AttributeDescription *ad;
493 	struct berval	atmp,
494 			vtmp,
495 			*tmp;
496 	static struct berval
497 			/* better than nothing... */
498 			ber_bvfalse = BER_BVC( "(!(objectClass=*))" ),
499 			ber_bvtf_false = BER_BVC( "(|)" ),
500 			/* better than nothing... */
501 			ber_bvtrue = BER_BVC( "(objectClass=*)" ),
502 			ber_bvtf_true = BER_BVC( "(&)" ),
503 #if 0
504 			/* no longer needed; preserved for completeness */
505 			ber_bvundefined = BER_BVC( "(?=undefined)" ),
506 #endif
507 			ber_bverror = BER_BVC( "(?=error)" ),
508 			ber_bvunknown = BER_BVC( "(?=unknown)" ),
509 			ber_bvnone = BER_BVC( "(?=none)" );
510 	ber_len_t	len;
511 
512 	assert( fstr != NULL );
513 	BER_BVZERO( fstr );
514 
515 	if ( f == NULL ) {
516 		ber_dupbv_x( fstr, &ber_bvnone, op->o_tmpmemctx );
517 		return LDAP_OTHER;
518 	}
519 
520 	if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
521 		goto computed;
522 	}
523 
524 	switch ( f->f_choice & SLAPD_FILTER_MASK ) {
525 	case LDAP_FILTER_EQUALITY:
526 		ad = f->f_av_desc;
527 		if ( map_attr_value( dc, &ad, &atmp,
528 			&f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
529 		{
530 			goto computed;
531 		}
532 
533 		fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(=)" );
534 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
535 
536 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
537 			atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
538 
539 		op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
540 		break;
541 
542 	case LDAP_FILTER_GE:
543 		ad = f->f_av_desc;
544 		if ( map_attr_value( dc, &ad, &atmp,
545 			&f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
546 		{
547 			goto computed;
548 		}
549 
550 		fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(>=)" );
551 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
552 
553 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
554 			atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
555 
556 		op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
557 		break;
558 
559 	case LDAP_FILTER_LE:
560 		ad = f->f_av_desc;
561 		if ( map_attr_value( dc, &ad, &atmp,
562 			&f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
563 		{
564 			goto computed;
565 		}
566 
567 		fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(<=)" );
568 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
569 
570 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
571 			atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
572 
573 		op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
574 		break;
575 
576 	case LDAP_FILTER_APPROX:
577 		ad = f->f_av_desc;
578 		if ( map_attr_value( dc, &ad, &atmp,
579 			&f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
580 		{
581 			goto computed;
582 		}
583 
584 		fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(~=)" );
585 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
586 
587 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
588 			atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
589 
590 		op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
591 		break;
592 
593 	case LDAP_FILTER_SUBSTRINGS:
594 		ad = f->f_sub_desc;
595 		if ( map_attr_value( dc, &ad, &atmp,
596 			NULL, NULL, RWM_MAP, op->o_tmpmemctx ) )
597 		{
598 			goto computed;
599 		}
600 
601 		/* cannot be a DN ... */
602 
603 		fstr->bv_len = atmp.bv_len + STRLENOF( "(=*)" );
604 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );
605 
606 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
607 			atmp.bv_val );
608 
609 		if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
610 			len = fstr->bv_len;
611 
612 			filter_escape_value_x( &f->f_sub_initial, &vtmp, op->o_tmpmemctx );
613 
614 			fstr->bv_len += vtmp.bv_len;
615 			fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
616 				op->o_tmpmemctx );
617 
618 			snprintf( &fstr->bv_val[len - 2], vtmp.bv_len + 3,
619 				/* "(attr=" */ "%s*)",
620 				vtmp.bv_len ? vtmp.bv_val : "" );
621 
622 			op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
623 		}
624 
625 		if ( f->f_sub_any != NULL ) {
626 			for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
627 				len = fstr->bv_len;
628 				filter_escape_value_x( &f->f_sub_any[i], &vtmp,
629 					op->o_tmpmemctx );
630 
631 				fstr->bv_len += vtmp.bv_len + 1;
632 				fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
633 					op->o_tmpmemctx );
634 
635 				snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
636 					/* "(attr=[init]*[any*]" */ "%s*)",
637 					vtmp.bv_len ? vtmp.bv_val : "" );
638 				op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
639 			}
640 		}
641 
642 		if ( !BER_BVISNULL( &f->f_sub_final ) ) {
643 			len = fstr->bv_len;
644 
645 			filter_escape_value_x( &f->f_sub_final, &vtmp, op->o_tmpmemctx );
646 
647 			fstr->bv_len += vtmp.bv_len;
648 			fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
649 				op->o_tmpmemctx );
650 
651 			snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
652 				/* "(attr=[init*][any*]" */ "%s)",
653 				vtmp.bv_len ? vtmp.bv_val : "" );
654 
655 			op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
656 		}
657 
658 		break;
659 
660 	case LDAP_FILTER_PRESENT:
661 		ad = f->f_desc;
662 		if ( map_attr_value( dc, &ad, &atmp,
663 			NULL, NULL, RWM_MAP, op->o_tmpmemctx ) )
664 		{
665 			goto computed;
666 		}
667 
668 		fstr->bv_len = atmp.bv_len + STRLENOF( "(=*)" );
669 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
670 
671 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
672 			atmp.bv_val );
673 		break;
674 
675 	case LDAP_FILTER_AND:
676 	case LDAP_FILTER_OR:
677 	case LDAP_FILTER_NOT:
678 		fstr->bv_len = STRLENOF( "(%)" );
679 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );
680 
681 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
682 			f->f_choice == LDAP_FILTER_AND ? '&' :
683 			f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
684 
685 		for ( p = f->f_list; p != NULL; p = p->f_next ) {
686 			int	rc;
687 
688 			len = fstr->bv_len;
689 
690 			rc = rwm_int_filter_map_rewrite( op, dc, p, &vtmp );
691 			if ( rc != LDAP_SUCCESS ) {
692 				return rc;
693 			}
694 
695 			fstr->bv_len += vtmp.bv_len;
696 			fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
697 				op->o_tmpmemctx );
698 
699 			snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2,
700 				/*"("*/ "%s)", vtmp.bv_len ? vtmp.bv_val : "" );
701 
702 			op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
703 		}
704 
705 		break;
706 
707 	case LDAP_FILTER_EXT: {
708 		if ( f->f_mr_desc ) {
709 			ad = f->f_mr_desc;
710 			if ( map_attr_value( dc, &ad, &atmp,
711 				&f->f_mr_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
712 			{
713 				goto computed;
714 			}
715 
716 		} else {
717 			BER_BVSTR( &atmp, "" );
718 			filter_escape_value_x( &f->f_mr_value, &vtmp, op->o_tmpmemctx );
719 		}
720 
721 
722 		fstr->bv_len = atmp.bv_len +
723 			( f->f_mr_dnattrs ? STRLENOF( ":dn" ) : 0 ) +
724 			( f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_len + 1 : 0 ) +
725 			vtmp.bv_len + STRLENOF( "(:=)" );
726 		fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
727 
728 		snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
729 			atmp.bv_val,
730 			f->f_mr_dnattrs ? ":dn" : "",
731 			!BER_BVISEMPTY( &f->f_mr_rule_text ) ? ":" : "",
732 			!BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_val : "",
733 			vtmp.bv_len ? vtmp.bv_val : "" );
734 		op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
735 		break;
736 	}
737 
738 	case -1:
739 computed:;
740 		filter_free_x( op, f, 0 );
741 		f->f_choice = SLAPD_FILTER_COMPUTED;
742 		f->f_result = SLAPD_COMPARE_UNDEFINED;
743 		/* fallthru */
744 
745 	case SLAPD_FILTER_COMPUTED:
746 		switch ( f->f_result ) {
747 		case LDAP_COMPARE_FALSE:
748 		/* FIXME: treat UNDEFINED as FALSE */
749 		case SLAPD_COMPARE_UNDEFINED:
750 			if ( dc->rwmap->rwm_flags & RWM_F_SUPPORT_T_F ) {
751 				tmp = &ber_bvtf_false;
752 				break;
753 			}
754 			tmp = &ber_bvfalse;
755 			break;
756 
757 		case LDAP_COMPARE_TRUE:
758 			if ( dc->rwmap->rwm_flags & RWM_F_SUPPORT_T_F ) {
759 				tmp = &ber_bvtf_true;
760 				break;
761 			}
762 			tmp = &ber_bvtrue;
763 			break;
764 
765 		default:
766 			tmp = &ber_bverror;
767 			break;
768 		}
769 
770 		ber_dupbv_x( fstr, tmp, op->o_tmpmemctx );
771 		break;
772 
773 	default:
774 		ber_dupbv_x( fstr, &ber_bvunknown, op->o_tmpmemctx );
775 		break;
776 	}
777 
778 	return LDAP_SUCCESS;
779 }
780 
781 int
782 rwm_filter_map_rewrite(
783 	Operation		*op,
784 	dncookie		*dc,
785 	Filter			*f,
786 	struct berval		*fstr )
787 {
788 	int		rc;
789 	dncookie 	fdc;
790 	struct berval	ftmp;
791 
792 	rc = rwm_int_filter_map_rewrite( op, dc, f, fstr );
793 
794 	if ( rc != 0 ) {
795 		return rc;
796 	}
797 
798 	fdc = *dc;
799 	ftmp = *fstr;
800 
801 	fdc.ctx = "searchFilter";
802 
803 	switch ( rewrite_session( fdc.rwmap->rwm_rw, fdc.ctx,
804 				( !BER_BVISEMPTY( &ftmp ) ? ftmp.bv_val : "" ),
805 				fdc.conn, &fstr->bv_val ) )
806 	{
807 	case REWRITE_REGEXEC_OK:
808 		if ( !BER_BVISNULL( fstr ) ) {
809 			fstr->bv_len = strlen( fstr->bv_val );
810 
811 		} else {
812 			*fstr = ftmp;
813 		}
814 
815 		Debug( LDAP_DEBUG_ARGS,
816 			"[rw] %s: \"%s\" -> \"%s\"\n",
817 			fdc.ctx, ftmp.bv_val, fstr->bv_val );
818 		if ( fstr->bv_val != ftmp.bv_val ) {
819 			ber_bvreplace_x( &ftmp, fstr, op->o_tmpmemctx );
820 			ch_free( fstr->bv_val );
821 			*fstr = ftmp;
822 		}
823 		rc = LDAP_SUCCESS;
824 		break;
825 
826  	case REWRITE_REGEXEC_UNWILLING:
827 		if ( fdc.rs ) {
828 			fdc.rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
829 			fdc.rs->sr_text = "Operation not allowed";
830 		}
831 		op->o_tmpfree( ftmp.bv_val, op->o_tmpmemctx );
832 		rc = LDAP_UNWILLING_TO_PERFORM;
833 		break;
834 
835 	case REWRITE_REGEXEC_ERR:
836 		if ( fdc.rs ) {
837 			fdc.rs->sr_err = LDAP_OTHER;
838 			fdc.rs->sr_text = "Rewrite error";
839 		}
840 		op->o_tmpfree( ftmp.bv_val, op->o_tmpmemctx );
841 		rc = LDAP_OTHER;
842 		break;
843 	}
844 
845 	return rc;
846 }
847 
848 /*
849  * I don't like this much, but we need two different
850  * functions because different heap managers may be
851  * in use in back-ldap/meta to reduce the amount of
852  * calls to malloc routines, and some of the free()
853  * routines may be macros with args
854  */
855 int
856 rwm_referral_rewrite(
857 	Operation		*op,
858 	SlapReply		*rs,
859 	void			*cookie,
860 	BerVarray		a_vals,
861 	BerVarray		*pa_nvals )
862 {
863 	slap_overinst		*on = (slap_overinst *) op->o_bd->bd_info;
864 	struct ldaprwmap	*rwmap =
865 			(struct ldaprwmap *)on->on_bi.bi_private;
866 
867 	int			i, last;
868 
869 	dncookie		dc;
870 	struct berval		dn = BER_BVNULL,
871 				ndn = BER_BVNULL;
872 
873 	assert( a_vals != NULL );
874 
875 	/*
876 	 * Rewrite the dn if needed
877 	 */
878 	dc.rwmap = rwmap;
879 	dc.conn = op->o_conn;
880 	dc.rs = rs;
881 	dc.ctx = (char *)cookie;
882 
883 	for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ )
884 		;
885 	last--;
886 
887 	if ( pa_nvals != NULL ) {
888 		if ( *pa_nvals == NULL ) {
889 			*pa_nvals = ch_malloc( ( last + 2 ) * sizeof(struct berval) );
890 			memset( *pa_nvals, 0, ( last + 2 ) * sizeof(struct berval) );
891 		}
892 	}
893 
894 	for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
895 		struct berval	olddn = BER_BVNULL,
896 				oldval;
897 		int		rc;
898 		LDAPURLDesc	*ludp;
899 
900 		oldval = a_vals[i];
901 		rc = ldap_url_parse( oldval.bv_val, &ludp );
902 		if ( rc != LDAP_URL_SUCCESS ) {
903 			/* leave attr untouched if massage failed */
904 			if ( pa_nvals && BER_BVISNULL( &(*pa_nvals)[i] ) ) {
905 				ber_dupbv( &(*pa_nvals)[i], &oldval );
906 			}
907 			continue;
908 		}
909 
910 		/* FIXME: URLs like "ldap:///dc=suffix" if passed
911 		 * thru ldap_url_parse() and ldap_url_desc2str()
912 		 * get rewritten as "ldap:///dc=suffix??base";
913 		 * we don't want this to occur... */
914 		if ( ludp->lud_scope == LDAP_SCOPE_BASE ) {
915 			ludp->lud_scope = LDAP_SCOPE_DEFAULT;
916 		}
917 
918 		ber_str2bv( ludp->lud_dn, 0, 0, &olddn );
919 
920 		dn = olddn;
921 		if ( pa_nvals ) {
922 			ndn = olddn;
923 			rc = rwm_dn_massage_pretty_normalize( &dc, &olddn,
924 					&dn, &ndn );
925 		} else {
926 			rc = rwm_dn_massage_pretty( &dc, &olddn, &dn );
927 		}
928 
929 		switch ( rc ) {
930 		case LDAP_UNWILLING_TO_PERFORM:
931 			/*
932 			 * FIXME: need to check if it may be considered
933 			 * legal to trim values when adding/modifying;
934 			 * it should be when searching (e.g. ACLs).
935 			 */
936 			ch_free( a_vals[i].bv_val );
937 			if (last > i ) {
938 				a_vals[i] = a_vals[last];
939 				if ( pa_nvals ) {
940 					(*pa_nvals)[i] = (*pa_nvals)[last];
941 				}
942 			}
943 			BER_BVZERO( &a_vals[last] );
944 			if ( pa_nvals ) {
945 				BER_BVZERO( &(*pa_nvals)[last] );
946 			}
947 			last--;
948 			break;
949 
950 		case LDAP_SUCCESS:
951 			if ( !BER_BVISNULL( &dn ) && dn.bv_val != olddn.bv_val ) {
952 				char	*newurl;
953 
954 				ludp->lud_dn = dn.bv_val;
955 				newurl = ldap_url_desc2str( ludp );
956 				ludp->lud_dn = olddn.bv_val;
957 				ch_free( dn.bv_val );
958 				if ( newurl == NULL ) {
959 					/* FIXME: leave attr untouched
960 					 * even if ldap_url_desc2str failed...
961 					 */
962 					break;
963 				}
964 
965 				ber_str2bv( newurl, 0, 1, &a_vals[i] );
966 				LDAP_FREE( newurl );
967 
968 				if ( pa_nvals ) {
969 					ludp->lud_dn = ndn.bv_val;
970 					newurl = ldap_url_desc2str( ludp );
971 					ludp->lud_dn = olddn.bv_val;
972 					ch_free( ndn.bv_val );
973 					if ( newurl == NULL ) {
974 						/* FIXME: leave attr untouched
975 						 * even if ldap_url_desc2str failed...
976 						 */
977 						ch_free( a_vals[i].bv_val );
978 						a_vals[i] = oldval;
979 						break;
980 					}
981 
982 					if ( !BER_BVISNULL( &(*pa_nvals)[i] ) ) {
983 						ch_free( (*pa_nvals)[i].bv_val );
984 					}
985 					ber_str2bv( newurl, 0, 1, &(*pa_nvals)[i] );
986 					LDAP_FREE( newurl );
987 				}
988 
989 				ch_free( oldval.bv_val );
990 				ludp->lud_dn = olddn.bv_val;
991 			}
992 			break;
993 
994 		default:
995 			/* leave attr untouched if massage failed */
996 			if ( pa_nvals && BER_BVISNULL( &(*pa_nvals)[i] ) ) {
997 				ber_dupbv( &(*pa_nvals)[i], &a_vals[i] );
998 			}
999 			break;
1000 		}
1001 		ldap_free_urldesc( ludp );
1002 	}
1003 
1004 	return 0;
1005 }
1006 
1007 /*
1008  * I don't like this much, but we need two different
1009  * functions because different heap managers may be
1010  * in use in back-ldap/meta to reduce the amount of
1011  * calls to malloc routines, and some of the free()
1012  * routines may be macros with args
1013  */
1014 int
1015 rwm_dnattr_rewrite(
1016 	Operation		*op,
1017 	SlapReply		*rs,
1018 	void			*cookie,
1019 	BerVarray		a_vals,
1020 	BerVarray		*pa_nvals )
1021 {
1022 	slap_overinst		*on = (slap_overinst *) op->o_bd->bd_info;
1023 	struct ldaprwmap	*rwmap =
1024 			(struct ldaprwmap *)on->on_bi.bi_private;
1025 
1026 	int			i, last;
1027 
1028 	dncookie		dc;
1029 	struct berval		dn = BER_BVNULL,
1030 				ndn = BER_BVNULL;
1031 	BerVarray		in;
1032 
1033 	if ( a_vals ) {
1034 		in = a_vals;
1035 
1036 	} else {
1037 		if ( pa_nvals == NULL || *pa_nvals == NULL ) {
1038 			return LDAP_OTHER;
1039 		}
1040 		in = *pa_nvals;
1041 	}
1042 
1043 	/*
1044 	 * Rewrite the dn if needed
1045 	 */
1046 	dc.rwmap = rwmap;
1047 	dc.conn = op->o_conn;
1048 	dc.rs = rs;
1049 	dc.ctx = (char *)cookie;
1050 
1051 	for ( last = 0; !BER_BVISNULL( &in[last] ); last++ );
1052 	last--;
1053 	if ( pa_nvals != NULL ) {
1054 		if ( *pa_nvals == NULL ) {
1055 			*pa_nvals = ch_malloc( ( last + 2 ) * sizeof(struct berval) );
1056 			memset( *pa_nvals, 0, ( last + 2 ) * sizeof(struct berval) );
1057 		}
1058 	}
1059 
1060 	for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) {
1061 		int		rc;
1062 
1063 		if ( a_vals ) {
1064 			dn = in[i];
1065 			if ( pa_nvals ) {
1066 				ndn = (*pa_nvals)[i];
1067 				rc = rwm_dn_massage_pretty_normalize( &dc, &in[i], &dn, &ndn );
1068 			} else {
1069 				rc = rwm_dn_massage_pretty( &dc, &in[i], &dn );
1070 			}
1071 		} else {
1072 			ndn = in[i];
1073 			rc = rwm_dn_massage_normalize( &dc, &in[i], &ndn );
1074 		}
1075 
1076 		switch ( rc ) {
1077 		case LDAP_UNWILLING_TO_PERFORM:
1078 			/*
1079 			 * FIXME: need to check if it may be considered
1080 			 * legal to trim values when adding/modifying;
1081 			 * it should be when searching (e.g. ACLs).
1082 			 */
1083 			ch_free( in[i].bv_val );
1084 			if (last > i ) {
1085 				in[i] = in[last];
1086 				if ( a_vals && pa_nvals ) {
1087 					(*pa_nvals)[i] = (*pa_nvals)[last];
1088 				}
1089 			}
1090 			BER_BVZERO( &in[last] );
1091 			if ( a_vals && pa_nvals ) {
1092 				BER_BVZERO( &(*pa_nvals)[last] );
1093 			}
1094 			last--;
1095 			break;
1096 
1097 		case LDAP_SUCCESS:
1098 			if ( a_vals ) {
1099 				if ( !BER_BVISNULL( &dn ) && dn.bv_val != a_vals[i].bv_val ) {
1100 					ch_free( a_vals[i].bv_val );
1101 					a_vals[i] = dn;
1102 
1103 					if ( pa_nvals ) {
1104 						if ( !BER_BVISNULL( &(*pa_nvals)[i] ) ) {
1105 							ch_free( (*pa_nvals)[i].bv_val );
1106 						}
1107 						(*pa_nvals)[i] = ndn;
1108 					}
1109 				}
1110 
1111 			} else {
1112 				if ( !BER_BVISNULL( &ndn ) && ndn.bv_val != (*pa_nvals)[i].bv_val ) {
1113 					ch_free( (*pa_nvals)[i].bv_val );
1114 					(*pa_nvals)[i] = ndn;
1115 				}
1116 			}
1117 			break;
1118 
1119 		default:
1120 			/* leave attr untouched if massage failed */
1121 			if ( a_vals && pa_nvals && BER_BVISNULL( &(*pa_nvals)[i] ) ) {
1122 				dnNormalize( 0, NULL, NULL, &a_vals[i], &(*pa_nvals)[i], NULL );
1123 			}
1124 			break;
1125 		}
1126 	}
1127 
1128 	return 0;
1129 }
1130 
1131 int
1132 rwm_referral_result_rewrite(
1133 	dncookie		*dc,
1134 	BerVarray		a_vals )
1135 {
1136 	int		i, last;
1137 
1138 	for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ );
1139 	last--;
1140 
1141 	for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
1142 		struct berval	dn,
1143 				olddn = BER_BVNULL;
1144 		int		rc;
1145 		LDAPURLDesc	*ludp;
1146 
1147 		rc = ldap_url_parse( a_vals[i].bv_val, &ludp );
1148 		if ( rc != LDAP_URL_SUCCESS ) {
1149 			/* leave attr untouched if massage failed */
1150 			continue;
1151 		}
1152 
1153 		/* FIXME: URLs like "ldap:///dc=suffix" if passed
1154 		 * thru ldap_url_parse() and ldap_url_desc2str()
1155 		 * get rewritten as "ldap:///dc=suffix??base";
1156 		 * we don't want this to occur... */
1157 		if ( ludp->lud_scope == LDAP_SCOPE_BASE ) {
1158 			ludp->lud_scope = LDAP_SCOPE_DEFAULT;
1159 		}
1160 
1161 		ber_str2bv( ludp->lud_dn, 0, 0, &olddn );
1162 
1163 		dn = olddn;
1164 		rc = rwm_dn_massage_pretty( dc, &olddn, &dn );
1165 		switch ( rc ) {
1166 		case LDAP_UNWILLING_TO_PERFORM:
1167 			/*
1168 			 * FIXME: need to check if it may be considered
1169 			 * legal to trim values when adding/modifying;
1170 			 * it should be when searching (e.g. ACLs).
1171 			 */
1172 			ch_free( a_vals[i].bv_val );
1173 			if ( last > i ) {
1174 				a_vals[i] = a_vals[last];
1175 			}
1176 			BER_BVZERO( &a_vals[last] );
1177 			last--;
1178 			i--;
1179 			break;
1180 
1181 		default:
1182 			/* leave attr untouched if massage failed */
1183 			if ( !BER_BVISNULL( &dn ) && olddn.bv_val != dn.bv_val ) {
1184 				char	*newurl;
1185 
1186 				ludp->lud_dn = dn.bv_val;
1187 				newurl = ldap_url_desc2str( ludp );
1188 				if ( newurl == NULL ) {
1189 					/* FIXME: leave attr untouched
1190 					 * even if ldap_url_desc2str failed...
1191 					 */
1192 					break;
1193 				}
1194 
1195 				ch_free( a_vals[i].bv_val );
1196 				ber_str2bv( newurl, 0, 1, &a_vals[i] );
1197 				LDAP_FREE( newurl );
1198 				ludp->lud_dn = olddn.bv_val;
1199 			}
1200 			break;
1201 		}
1202 
1203 		ldap_free_urldesc( ludp );
1204 	}
1205 
1206 	return 0;
1207 }
1208 
1209 int
1210 rwm_dnattr_result_rewrite(
1211 	dncookie		*dc,
1212 	BerVarray		a_vals,
1213 	BerVarray		a_nvals )
1214 {
1215 	int		i, last;
1216 
1217 	for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ );
1218 	last--;
1219 
1220 	for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
1221 		struct berval	pdn, ndn = BER_BVNULL;
1222 		int		rc;
1223 
1224 		pdn = a_vals[i];
1225 		rc = rwm_dn_massage_pretty_normalize( dc, &a_vals[i], &pdn, &ndn );
1226 		switch ( rc ) {
1227 		case LDAP_UNWILLING_TO_PERFORM:
1228 			/*
1229 			 * FIXME: need to check if it may be considered
1230 			 * legal to trim values when adding/modifying;
1231 			 * it should be when searching (e.g. ACLs).
1232 			 */
1233 			assert( a_vals[i].bv_val != a_nvals[i].bv_val );
1234 			ch_free( a_vals[i].bv_val );
1235 			ch_free( a_nvals[i].bv_val );
1236 			if ( last > i ) {
1237 				a_vals[i] = a_vals[last];
1238 				a_nvals[i] = a_nvals[last];
1239 			}
1240 			BER_BVZERO( &a_vals[last] );
1241 			BER_BVZERO( &a_nvals[last] );
1242 			last--;
1243 			break;
1244 
1245 		default:
1246 			/* leave attr untouched if massage failed */
1247 			if ( !BER_BVISNULL( &pdn ) && a_vals[i].bv_val != pdn.bv_val ) {
1248 				ch_free( a_vals[i].bv_val );
1249 				a_vals[i] = pdn;
1250 			}
1251 			if ( !BER_BVISNULL( &ndn ) && a_nvals[i].bv_val != ndn.bv_val ) {
1252 				ch_free( a_nvals[i].bv_val );
1253 				a_nvals[i] = ndn;
1254 			}
1255 			break;
1256 		}
1257 	}
1258 
1259 	return 0;
1260 }
1261 
1262 void
1263 rwm_mapping_dst_free( void *v_mapping )
1264 {
1265 	struct ldapmapping *mapping = v_mapping;
1266 
1267 	if ( BER_BVISEMPTY( &mapping[0].m_dst ) ) {
1268 		rwm_mapping_free( &mapping[ -1 ] );
1269 	}
1270 }
1271 
1272 void
1273 rwm_mapping_free( void *v_mapping )
1274 {
1275 	struct ldapmapping *mapping = v_mapping;
1276 
1277 	if ( !BER_BVISNULL( &mapping[0].m_src ) ) {
1278 		ch_free( mapping[0].m_src.bv_val );
1279 	}
1280 
1281 	if ( mapping[0].m_flags & RWMMAP_F_FREE_SRC ) {
1282 		if ( mapping[0].m_flags & RWMMAP_F_IS_OC ) {
1283 			if ( mapping[0].m_src_oc ) {
1284 				ch_free( mapping[0].m_src_oc );
1285 			}
1286 
1287 		} else {
1288 			if ( mapping[0].m_src_ad ) {
1289 				ch_free( mapping[0].m_src_ad );
1290 			}
1291 		}
1292 	}
1293 
1294 	if ( !BER_BVISNULL( &mapping[0].m_dst ) ) {
1295 		ch_free( mapping[0].m_dst.bv_val );
1296 	}
1297 
1298 	if ( mapping[0].m_flags & RWMMAP_F_FREE_DST ) {
1299 		if ( mapping[0].m_flags & RWMMAP_F_IS_OC ) {
1300 			if ( mapping[0].m_dst_oc ) {
1301 				ch_free( mapping[0].m_dst_oc );
1302 			}
1303 
1304 		} else {
1305 			if ( mapping[0].m_dst_ad ) {
1306 				ch_free( mapping[0].m_dst_ad );
1307 			}
1308 		}
1309 	}
1310 
1311 	ch_free( mapping );
1312 
1313 }
1314 
1315 #endif /* SLAPD_OVER_RWM */
1316