Home | History | Annotate | Line # | Download | only in adremap
adremap.c revision 1.1
      1 /*	$NetBSD: adremap.c,v 1.1 2021/08/14 16:05:15 christos Exp $	*/
      2 
      3 /* adremap.c - Case-folding and DN-value remapping for AD proxies */
      4 /* $OpenLDAP$ */
      5 /*
      6  * Copyright 2015 Howard Chu <hyc (at) symas.com>.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted only as authorized by the OpenLDAP
     11  * Public License.
     12  *
     13  * A copy of this license is available in the file LICENSE in the
     14  * top-level directory of the distribution or, alternatively, at
     15  * <http://www.OpenLDAP.org/license.html>.
     16  */
     17 
     18 #include <sys/cdefs.h>
     19 __RCSID("$NetBSD: adremap.c,v 1.1 2021/08/14 16:05:15 christos Exp $");
     20 
     21 #include "portable.h"
     22 
     23 /*
     24  * This file implements an overlay that performs two remapping functions
     25  * to allow older POSIX clients to use Microsoft AD:
     26  * 1: downcase the values of a configurable list of attributes
     27  * 2: dereference some DN-valued attributes and convert to their simple names
     28  *	   e.g. generate memberUid based on member
     29  */
     30 
     31 #ifdef SLAPD_OVER_ADREMAP
     32 
     33 #include <ldap.h>
     34 #include "lutil.h"
     35 #include "slap.h"
     36 #include <ac/errno.h>
     37 #include <ac/time.h>
     38 #include <ac/string.h>
     39 #include <ac/ctype.h>
     40 #include "slap-config.h"
     41 
     42 typedef struct adremap_dnv {
     43 	struct adremap_dnv *ad_next;
     44 	AttributeDescription *ad_dnattr;	/* DN-valued attr to deref */
     45 	AttributeDescription *ad_deref;		/* target attr's value to retrieve */
     46 	AttributeDescription *ad_newattr;	/* New attr to collect new values */
     47 	ObjectClass *ad_group;		/* group objectclass on target */
     48 	ObjectClass *ad_mapgrp;		/* group objectclass to map */
     49 	ObjectClass *ad_refgrp;		/* objectclass of target DN */
     50 	struct berval ad_refbase;	/* base DN of target entries */
     51 } adremap_dnv;
     52 /* example: member uid memberUid */
     53 
     54 typedef struct adremap_case {
     55 	struct adremap_case *ac_next;
     56 	AttributeDescription *ac_attr;
     57 } adremap_case;
     58 
     59 /* Per-instance configuration information */
     60 typedef struct adremap_info {
     61 	adremap_case *ai_case;	/* attrs to downcase */
     62 	adremap_dnv *ai_dnv;	/* DN attrs to remap */
     63 } adremap_info;
     64 
     65 enum {
     66 	ADREMAP_CASE = 1,
     67 	ADREMAP_DNV
     68 };
     69 
     70 static ConfigDriver adremap_cf_case;
     71 static ConfigDriver adremap_cf_dnv;
     72 
     73 /* configuration attribute and objectclass */
     74 static ConfigTable adremapcfg[] = {
     75 	{ "adremap-downcase", "attrs", 2, 0, 0,
     76 	  ARG_MAGIC|ADREMAP_CASE, adremap_cf_case,
     77 	  "( OLcfgCtAt:6.1 "
     78 	  "NAME 'olcADremapDowncase' "
     79 	  "DESC 'List of attributes to casefold to lower case' "
     80 	  "EQUALITY caseIgnoreMatch "
     81 	  "SYNTAX OMsDirectoryString )", NULL, NULL },
     82 	{ "adremap-dnmap", "dnattr targetattr newattr remoteOC localOC targetOC baseDN", 8, 8, 0,
     83 	  ARG_MAGIC|ADREMAP_DNV, adremap_cf_dnv,
     84 	  "( OLcfgCtAt:6.2 "
     85 	  "NAME 'olcADremapDNmap' "
     86 	  "DESC 'DN attr to map, attr from target to use, attr to generate, objectclass of remote"
     87 	   " group, objectclass mapped group, objectclass of target entry, base DN of target entry' "
     88 	  "EQUALITY caseIgnoreMatch "
     89 	  "SYNTAX OMsDirectoryString )", NULL, NULL },
     90 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
     91 };
     92 
     93 static ConfigOCs adremapocs[] = {
     94 	{ "( OLcfgCtOc:6.1 "
     95 	  "NAME 'olcADremapConfig' "
     96 	  "DESC 'AD remap configuration' "
     97 	  "SUP olcOverlayConfig "
     98 	  "MAY ( olcADremapDowncase $ olcADremapDNmap ) )",
     99 	  Cft_Overlay, adremapcfg, NULL, NULL },
    100 	{ NULL, 0, NULL }
    101 };
    102 
    103 static int
    104 adremap_cf_case(ConfigArgs *c)
    105 {
    106 	BackendDB *be = (BackendDB *)c->be;
    107 	slap_overinst *on = (slap_overinst *)c->bi;
    108 	adremap_info *ai = on->on_bi.bi_private;
    109 	adremap_case *ac, **a2;
    110 	int rc = ARG_BAD_CONF;
    111 
    112 	switch(c->op) {
    113 	case SLAP_CONFIG_EMIT:
    114 		for (ac = ai->ai_case; ac; ac=ac->ac_next) {
    115 			rc = value_add_one(&c->rvalue_vals, &ac->ac_attr->ad_cname);
    116 			if (rc) break;
    117 		}
    118 		break;
    119 	case LDAP_MOD_DELETE:
    120 		if (c->valx < 0) {
    121 			for (ac = ai->ai_case; ac; ac=ai->ai_case) {
    122 				ai->ai_case = ac->ac_next;
    123 				ch_free(ac);
    124 			}
    125 		} else {
    126 			int i;
    127 			for (i=0, a2 = &ai->ai_case; i<c->valx; i++, a2 = &(*a2)->ac_next);
    128 			ac = *a2;
    129 			*a2 = ac->ac_next;
    130 			ch_free(ac);
    131 		}
    132 		rc = 0;
    133 		break;
    134 	default: {
    135 		const char *text;
    136 		adremap_case ad;
    137 		ad.ac_attr = NULL;
    138 		rc = slap_str2ad(c->argv[1], &ad.ac_attr, &text);
    139 		if (rc) break;
    140 		for (a2 = &ai->ai_case; *a2; a2 = &(*a2)->ac_next);
    141 		ac = ch_malloc(sizeof(adremap_case));
    142 		ac->ac_next = NULL;
    143 		ac->ac_attr = ad.ac_attr;
    144 		*a2 = ac;
    145 		break;
    146 		}
    147 	}
    148 	return rc;
    149 }
    150 
    151 static int
    152 adremap_cf_dnv(ConfigArgs *c)
    153 {
    154 	BackendDB *be = (BackendDB *)c->be;
    155 	slap_overinst *on = (slap_overinst *)c->bi;
    156 	adremap_info *ai = on->on_bi.bi_private;
    157 	adremap_dnv *ad, **a2;
    158 	int rc = ARG_BAD_CONF;
    159 
    160 	switch(c->op) {
    161 	case SLAP_CONFIG_EMIT:
    162 		for (ad = ai->ai_dnv; ad; ad=ad->ad_next) {
    163 			char *ptr;
    164 			struct berval bv;
    165 			bv.bv_len = ad->ad_dnattr->ad_cname.bv_len + ad->ad_deref->ad_cname.bv_len + ad->ad_newattr->ad_cname.bv_len + 2;
    166 			bv.bv_len += ad->ad_group->soc_cname.bv_len + ad->ad_mapgrp->soc_cname.bv_len + ad->ad_refgrp->soc_cname.bv_len + 3;
    167 			bv.bv_len += ad->ad_refbase.bv_len + 3;
    168 			bv.bv_val = ch_malloc(bv.bv_len + 1);
    169 			ptr = lutil_strcopy(bv.bv_val, ad->ad_dnattr->ad_cname.bv_val);
    170 			*ptr++ = ' ';
    171 			ptr = lutil_strcopy(ptr, ad->ad_deref->ad_cname.bv_val);
    172 			*ptr++ = ' ';
    173 			ptr = lutil_strcopy(ptr, ad->ad_newattr->ad_cname.bv_val);
    174 			*ptr++ = ' ';
    175 			ptr = lutil_strcopy(ptr, ad->ad_group->soc_cname.bv_val);
    176 			*ptr++ = ' ';
    177 			ptr = lutil_strcopy(ptr, ad->ad_mapgrp->soc_cname.bv_val);
    178 			*ptr++ = ' ';
    179 			ptr = lutil_strcopy(ptr, ad->ad_refgrp->soc_cname.bv_val);
    180 			*ptr++ = ' ';
    181 			*ptr++ = '"';
    182 			ptr = lutil_strcopy(ptr, ad->ad_refbase.bv_val);
    183 			*ptr++ = '"';
    184 			*ptr = '\0';
    185 			ber_bvarray_add(&c->rvalue_vals, &bv);
    186 		}
    187 		if (ai->ai_dnv) rc = 0;
    188 		break;
    189 	case LDAP_MOD_DELETE:
    190 		if (c->valx < 0) {
    191 			for (ad = ai->ai_dnv; ad; ad=ai->ai_dnv) {
    192 				ai->ai_dnv = ad->ad_next;
    193 				ch_free(ad);
    194 			}
    195 		} else {
    196 			int i;
    197 			for (i=0, a2 = &ai->ai_dnv; i<c->valx; i++, a2 = &(*a2)->ad_next);
    198 			ad = *a2;
    199 			*a2 = ad->ad_next;
    200 			ch_free(ad);
    201 		}
    202 		rc = 0;
    203 		break;
    204 	default: {
    205 		const char *text;
    206 		adremap_dnv av = {0};
    207 		struct berval dn;
    208 		rc = slap_str2ad(c->argv[1], &av.ad_dnattr, &text);
    209 		if (rc) break;
    210 		if (av.ad_dnattr->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName) {
    211 			rc = 1;
    212 			snprintf(c->cr_msg, sizeof(c->cr_msg), "<%s> not a DN-valued attribute",
    213 				c->argv[0]);
    214 			Debug(LDAP_DEBUG_ANY, "%s: %s(%s)\n", c->log, c->cr_msg, c->argv[1]);
    215 			break;
    216 		}
    217 		rc = slap_str2ad(c->argv[2], &av.ad_deref, &text);
    218 		if (rc) break;
    219 		rc = slap_str2ad(c->argv[3], &av.ad_newattr, &text);
    220 		if (rc) break;
    221 		av.ad_group = oc_find(c->argv[4]);
    222 		if (!av.ad_group) {
    223 			rc = 1;
    224 			break;
    225 		}
    226 		av.ad_mapgrp = oc_find(c->argv[5]);
    227 		if (!av.ad_mapgrp) {
    228 			rc = 1;
    229 			break;
    230 		}
    231 		av.ad_refgrp = oc_find(c->argv[6]);
    232 		if (!av.ad_refgrp) {
    233 			rc = 1;
    234 			break;
    235 		}
    236 		ber_str2bv(c->argv[7], 0, 0, &dn);
    237 		rc = dnNormalize(0, NULL, NULL, &dn, &av.ad_refbase, NULL);
    238 		if (rc) break;
    239 
    240 		for (a2 = &ai->ai_dnv; *a2; a2 = &(*a2)->ad_next);
    241 		ad = ch_malloc(sizeof(adremap_dnv));
    242 		ad->ad_next = NULL;
    243 		ad->ad_dnattr = av.ad_dnattr;
    244 		ad->ad_deref = av.ad_deref;
    245 		ad->ad_newattr = av.ad_newattr;
    246 		ad->ad_group = av.ad_group;
    247 		ad->ad_mapgrp = av.ad_mapgrp;
    248 		ad->ad_refgrp = av.ad_refgrp;
    249 		ad->ad_refbase = av.ad_refbase;
    250 		*a2 = ad;
    251 		break;
    252 		}
    253 	}
    254 	return rc;
    255 }
    256 
    257 typedef struct adremap_ctx {
    258 	slap_overinst *on;
    259 	AttributeName an;
    260 	AttributeDescription *ad;
    261 	int an_swap;
    262 } adremap_ctx;
    263 
    264 static int
    265 adremap_search_resp(
    266 	Operation *op,
    267 	SlapReply *rs
    268 )
    269 {
    270 	adremap_ctx *ctx = op->o_callback->sc_private;
    271 	slap_overinst *on = ctx->on;
    272 	adremap_info *ai = on->on_bi.bi_private;
    273 	adremap_case *ac;
    274 	adremap_dnv *ad;
    275 	Attribute *a;
    276 	Entry *e;
    277 
    278 	if (rs->sr_type != REP_SEARCH)
    279 		return SLAP_CB_CONTINUE;
    280 
    281 	/* we munged the attr list, restore it to original */
    282 	if (ctx->an_swap) {
    283 		int i;
    284 		ctx->an_swap = 0;
    285 		for (i=0; rs->sr_attrs[i].an_name.bv_val; i++) {
    286 			if (rs->sr_attrs[i].an_desc == ctx->ad) {
    287 				rs->sr_attrs[i] = ctx->an;
    288 				break;
    289 			}
    290 		}
    291 		/* Usually rs->sr_attrs is just op->ors_attrs, but
    292 		 * overlays like rwm may make a new copy. Fix both
    293 		 * if needed.
    294 		 */
    295 		if (op->ors_attrs != rs->sr_attrs) {
    296 			for (i=0; op->ors_attrs[i].an_name.bv_val; i++) {
    297 				if (op->ors_attrs[i].an_desc == ctx->ad) {
    298 					op->ors_attrs[i] = ctx->an;
    299 					break;
    300 				}
    301 			}
    302 		}
    303 	}
    304 	e = rs->sr_entry;
    305 	for (ac = ai->ai_case; ac; ac = ac->ac_next) {
    306 		a = attr_find(e->e_attrs, ac->ac_attr);
    307 		if (a) {
    308 			int i, j;
    309 			if (!(rs->sr_flags & REP_ENTRY_MODIFIABLE)) {
    310 				e = entry_dup(e);
    311 				rs_replace_entry(op, rs, on, e);
    312 				rs->sr_flags |= REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
    313 				a = attr_find(e->e_attrs, ac->ac_attr);
    314 			}
    315 			for (i=0; i<a->a_numvals; i++) {
    316 				unsigned char *c = a->a_vals[i].bv_val;
    317 				for (j=0; j<a->a_vals[i].bv_len; j++)
    318 					if (isupper(c[j]))
    319 						c[j] = tolower(c[j]);
    320 			}
    321 		}
    322 	}
    323 	for (ad = ai->ai_dnv; ad; ad = ad->ad_next) {
    324 		a = attr_find(e->e_attrs, ad->ad_dnattr);
    325 		if (a) {
    326 			Entry *n;
    327 			Attribute *dr;
    328 			int i, rc;
    329 			if (!(rs->sr_flags & REP_ENTRY_MODIFIABLE)) {
    330 				e = entry_dup(e);
    331 				rs_replace_entry(op, rs, on, e);
    332 				rs->sr_flags |= REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
    333 				a = attr_find(e->e_attrs, ad->ad_dnattr);
    334 			}
    335 			for (i=0; i<a->a_numvals; i++) {
    336 				struct berval dv;
    337 				dv = ad->ad_deref->ad_cname;
    338 					/* If the RDN uses the deref attr, just use it directly */
    339 				if (a->a_nvals[i].bv_val[dv.bv_len] == '=' &&
    340 					!memcmp(a->a_nvals[i].bv_val, dv.bv_val, dv.bv_len)) {
    341 					struct berval bv, nv;
    342 					char *ptr;
    343 					bv = a->a_vals[i];
    344 					nv = a->a_nvals[i];
    345 					bv.bv_val += dv.bv_len + 1;
    346 					ptr = strchr(bv.bv_val, ',');
    347 					if (ptr)
    348 						bv.bv_len = ptr - bv.bv_val;
    349 					else
    350 						bv.bv_len -= dv.bv_len+1;
    351 					nv.bv_val += dv.bv_len + 1;
    352 					ptr = strchr(nv.bv_val, ',');
    353 					if (ptr)
    354 						nv.bv_len = ptr - nv.bv_val;
    355 					else
    356 						nv.bv_len -= dv.bv_len+1;
    357 					attr_merge_one(e, ad->ad_newattr, &bv, &nv);
    358 				} else {
    359 					/* otherwise look up the deref attr */
    360 					n = NULL;
    361 					rc = be_entry_get_rw(op, &a->a_nvals[i], NULL, ad->ad_deref, 0, &n);
    362 					if (!rc && n) {
    363 						dr = attr_find(n->e_attrs, ad->ad_deref);
    364 						if (dr)
    365 							attr_merge_one(e, ad->ad_newattr, dr->a_vals, dr->a_nvals);
    366 						be_entry_release_r(op, n);
    367 					}
    368 				}
    369 			}
    370 		}
    371 	}
    372 	return SLAP_CB_CONTINUE;
    373 }
    374 
    375 static int adremap_refsearch(
    376 	Operation *op,
    377 	SlapReply *rs
    378 )
    379 {
    380 	if (rs->sr_type == REP_SEARCH) {
    381 		slap_callback *sc = op->o_callback;
    382 		struct berval *dn = sc->sc_private;
    383 		ber_dupbv_x(dn, &rs->sr_entry->e_nname, op->o_tmpmemctx);
    384 		return LDAP_SUCCESS;
    385 	}
    386 	return rs->sr_err;
    387 }
    388 
    389 static adremap_dnv *adremap_filter(
    390 	Operation *op,
    391 	adremap_info *ai
    392 )
    393 {
    394 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
    395 	Filter *f = op->ors_filter, *fn = NULL;
    396 	adremap_dnv *ad = NULL;
    397 	struct berval bv;
    398 	int fextra = 0;
    399 
    400 	/* Do we need to munge the filter? First see if it's of
    401 	 * the form (objectClass=<mapgrp>)
    402 	 * or form (&(objectClass=<mapgrp>)...)
    403 	 * or form (&(&(objectClass=<mapgrp>)...)...)
    404 	 */
    405 	if (f->f_choice == LDAP_FILTER_AND && f->f_and) {
    406 		fextra = 1;
    407 		f = f->f_and;
    408 		fn = f->f_next;
    409 	}
    410 	if (f->f_choice == LDAP_FILTER_AND && f->f_and) {
    411 		fextra = 2;
    412 		f = f->f_and;
    413 	}
    414 	if (f->f_choice == LDAP_FILTER_EQUALITY &&
    415 		f->f_av_desc == slap_schema.si_ad_objectClass) {
    416 		struct berval bv = f->f_av_value;
    417 
    418 		for (ad = ai->ai_dnv; ad; ad = ad->ad_next) {
    419 			if (!ber_bvstrcasecmp( &bv, &ad->ad_mapgrp->soc_cname )) {
    420 			/* Now check to see if next element is (<newattr>=foo) */
    421 				Filter *fnew;
    422 				if (fn && fn->f_choice == LDAP_FILTER_EQUALITY &&
    423 					fn->f_av_desc == ad->ad_newattr) {
    424 					Filter fr[3];
    425 					AttributeAssertion aa[2] = {0};
    426 					Operation op2;
    427 					slap_callback cb = {0};
    428 					SlapReply rs = {REP_RESULT};
    429 					struct berval dn = BER_BVNULL;
    430 
    431 					/* It's a match, setup a search with filter
    432 					 * (&(objectclass=<refgrp>)(<deref>=foo))
    433 					 */
    434 					fr[0].f_choice = LDAP_FILTER_AND;
    435 					fr[0].f_and = &fr[1];
    436 					fr[0].f_next = NULL;
    437 
    438 					fr[1].f_choice = LDAP_FILTER_EQUALITY;
    439 					fr[1].f_ava = &aa[0];
    440 					fr[1].f_av_desc = slap_schema.si_ad_objectClass;
    441 					fr[1].f_av_value = ad->ad_refgrp->soc_cname;
    442 					fr[1].f_next = &fr[2];
    443 
    444 					fr[2].f_choice = LDAP_FILTER_EQUALITY;
    445 					fr[2].f_ava = &aa[1];
    446 					fr[2].f_av_desc = ad->ad_deref;
    447 					fr[2].f_av_value = fn->f_av_value;
    448 					fr[2].f_next = NULL;
    449 
    450 					/* Search with this filter to retrieve target DN */
    451 					op2 = *op;
    452 					op2.o_callback = &cb;
    453 					cb.sc_response = adremap_refsearch;
    454 					cb.sc_private = &dn;
    455 					op2.o_req_dn = ad->ad_refbase;
    456 					op2.o_req_ndn = ad->ad_refbase;
    457 					op2.ors_filter = fr;
    458 					filter2bv_x(op, fr, &op2.ors_filterstr);
    459 					op2.ors_deref = LDAP_DEREF_NEVER;
    460 					op2.ors_slimit = 1;
    461 					op2.ors_tlimit = SLAP_NO_LIMIT;
    462 					op2.ors_attrs = slap_anlist_no_attrs;
    463 					op2.ors_attrsonly = 1;
    464 					op2.o_no_schema_check = 1;
    465 					op2.o_bd->bd_info = (BackendInfo *)on->on_info;
    466 					op2.o_bd->be_search(&op2, &rs);
    467 					op2.o_bd->bd_info = (BackendInfo *)on;
    468 					op->o_tmpfree(op2.ors_filterstr.bv_val, op->o_tmpmemctx);
    469 
    470 					if (!dn.bv_len) {	/* no match was found */
    471 						ad = NULL;
    472 						break;
    473 					}
    474 
    475 					if (rs.sr_err) {	/* sizelimit exceeded, etc.: invalid name */
    476 						op->o_tmpfree(dn.bv_val, op->o_tmpmemctx);
    477 						ad = NULL;
    478 						break;
    479 					}
    480 
    481 					/* Build a new filter of form
    482 					 * (&(objectclass=<group>)(<dnattr>=foo-DN)...)
    483 					 */
    484 					f = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
    485 					f->f_choice = LDAP_FILTER_AND;
    486 					fnew = f;
    487 					f->f_next = NULL;
    488 
    489 					f->f_and = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
    490 					f = f->f_and;
    491 					f->f_choice = LDAP_FILTER_EQUALITY;
    492 					f->f_ava = op->o_tmpcalloc(1, sizeof(AttributeAssertion), op->o_tmpmemctx);
    493 					f->f_av_desc = slap_schema.si_ad_objectClass;
    494 					ber_dupbv_x(&f->f_av_value, &ad->ad_group->soc_cname, op->o_tmpmemctx);
    495 
    496 					f->f_next = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
    497 					f = f->f_next;
    498 					f->f_choice = LDAP_FILTER_EQUALITY;
    499 					f->f_ava = op->o_tmpcalloc(1, sizeof(AttributeAssertion), op->o_tmpmemctx);
    500 					f->f_av_desc = ad->ad_dnattr;
    501 					f->f_av_value = dn;
    502 
    503 					f->f_next = fn->f_next;
    504 					fn->f_next = NULL;
    505 				} else {
    506 					/* Build a new filter of form
    507 					 * (objectclass=<group>)
    508 					 */
    509 					f->f_next = NULL;	/* disconnect old chain */
    510 
    511 					f = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
    512 					f->f_choice = LDAP_FILTER_EQUALITY;
    513 					f->f_ava = op->o_tmpcalloc(1, sizeof(AttributeAssertion), op->o_tmpmemctx);
    514 					f->f_av_desc = slap_schema.si_ad_objectClass;
    515 					ber_dupbv_x(&f->f_av_value, &ad->ad_group->soc_cname, op->o_tmpmemctx);
    516 
    517 					/* If there was a wrapping (&), attach it. */
    518 					if (fextra) {
    519 						fnew = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
    520 						fnew->f_choice = LDAP_FILTER_AND;
    521 						fnew->f_and = f;
    522 						fnew->f_next = NULL;
    523 						f->f_next = fn;
    524 					} else {
    525 						fnew = f;
    526 						f->f_next = NULL;
    527 					}
    528 				}
    529 				if (fextra > 1) {
    530 					f = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
    531 					f->f_choice = LDAP_FILTER_AND;
    532 					f->f_and = fnew->f_and;
    533 					f->f_next = f->f_and->f_next;
    534 					f->f_and->f_next = op->ors_filter->f_and->f_and->f_next;
    535 					op->ors_filter->f_and->f_and->f_next = NULL;
    536 					fnew->f_and = f;
    537 				}
    538 				filter_free_x(op, op->ors_filter, 1);
    539 				op->o_tmpfree(op->ors_filterstr.bv_val, op->o_tmpmemctx);
    540 				op->ors_filter = fnew;
    541 				filter2bv_x(op, op->ors_filter, &op->ors_filterstr);
    542 				break;
    543 			}
    544 		}
    545 	}
    546 	return ad;
    547 }
    548 
    549 static int
    550 adremap_search(
    551 	Operation *op,
    552 	SlapReply *rs
    553 )
    554 {
    555 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
    556 	adremap_info *ai = (adremap_info *) on->on_bi.bi_private;
    557 	adremap_ctx *ctx;
    558 	adremap_dnv *ad = NULL;
    559 	slap_callback *cb;
    560 
    561 	/* Is this our own internal search? Ignore it */
    562 	if (op->o_no_schema_check)
    563 		return SLAP_CB_CONTINUE;
    564 
    565 	if (ai->ai_dnv)
    566 		/* check for filter match, fallthru if none */
    567 		ad = adremap_filter(op, ai);
    568 
    569 	cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(adremap_ctx), op->o_tmpmemctx);
    570 	cb->sc_response = adremap_search_resp;
    571 	cb->sc_private = cb+1;
    572 	cb->sc_next = op->o_callback;
    573 	op->o_callback = cb;
    574 	ctx = cb->sc_private;
    575 	ctx->on = on;
    576 	if (ad && op->ors_attrs) {	/* see if we need to remap a search attr */
    577 		int i;
    578 		for (i=0; op->ors_attrs[i].an_name.bv_val; i++) {
    579 			if (op->ors_attrs[i].an_desc == ad->ad_newattr) {
    580 				ctx->an_swap = 1;
    581 				ctx->ad = ad->ad_dnattr;
    582 				ctx->an = op->ors_attrs[i];
    583 				op->ors_attrs[i].an_desc = ad->ad_dnattr;
    584 				op->ors_attrs[i].an_name = ad->ad_dnattr->ad_cname;
    585 				break;
    586 			}
    587 		}
    588 	}
    589 	return SLAP_CB_CONTINUE;
    590 }
    591 
    592 static int
    593 adremap_db_init(
    594 	BackendDB *be,
    595 	ConfigReply *cr
    596 )
    597 {
    598 	slap_overinst *on = (slap_overinst *) be->bd_info;
    599 
    600 	/* initialize private structure to store configuration */
    601 	on->on_bi.bi_private = ch_calloc( 1, sizeof(adremap_info) );
    602 
    603 	return 0;
    604 }
    605 
    606 static int
    607 adremap_db_destroy(
    608 	BackendDB *be,
    609 	ConfigReply *cr
    610 )
    611 {
    612 	slap_overinst *on = (slap_overinst *) be->bd_info;
    613 	adremap_info *ai = (adremap_info *) on->on_bi.bi_private;
    614 	adremap_case *ac;
    615 	adremap_dnv *ad;
    616 
    617 	/* free config */
    618 	for (ac = ai->ai_case; ac; ac = ai->ai_case) {
    619 		ai->ai_case = ac->ac_next;
    620 		ch_free(ac);
    621 	}
    622 	for (ad = ai->ai_dnv; ad; ad = ai->ai_dnv) {
    623 		ai->ai_dnv = ad->ad_next;
    624 		ch_free(ad);
    625 	}
    626 	free( ai );
    627 
    628 	return 0;
    629 }
    630 
    631 static slap_overinst adremap;
    632 
    633 int adremap_initialize()
    634 {
    635 	int i, code;
    636 
    637 	adremap.on_bi.bi_type = "adremap";
    638 	adremap.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
    639 	adremap.on_bi.bi_db_init = adremap_db_init;
    640 	adremap.on_bi.bi_db_destroy = adremap_db_destroy;
    641 	adremap.on_bi.bi_op_search = adremap_search;
    642 
    643 	/* register configuration directives */
    644 	adremap.on_bi.bi_cf_ocs = adremapocs;
    645 	code = config_register_schema( adremapcfg, adremapocs );
    646 	if ( code ) return code;
    647 
    648 	return overlay_register( &adremap );
    649 }
    650 
    651 #if SLAPD_OVER_ADREMAP == SLAPD_MOD_DYNAMIC
    652 int init_module(int argc, char *argv[]) {
    653 	return adremap_initialize();
    654 }
    655 #endif
    656 
    657 #endif	/* defined(SLAPD_OVER_ADREMAP) */
    658