xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/variant/variant.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: variant.c,v 1.2 2021/08/14 16:14:54 christos Exp $	*/
2 
3 /* variant.c - variant overlay */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2016-2021 Symas Corporation.
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 /* ACKNOWLEDGEMENTS:
18  * This work was developed in 2016-2017 by Ondřej Kuzník for Symas Corp.
19  */
20 
21 #include <sys/cdefs.h>
22 __RCSID("$NetBSD: variant.c,v 1.2 2021/08/14 16:14:54 christos Exp $");
23 
24 #include "portable.h"
25 
26 #ifdef SLAPD_OVER_VARIANT
27 
28 #include "slap.h"
29 #include "slap-config.h"
30 #include "ldap_queue.h"
31 
32 typedef enum variant_type_t {
33 	VARIANT_INFO_PLAIN = 1 << 0,
34 	VARIANT_INFO_REGEX = 1 << 1,
35 
36 	VARIANT_INFO_ALL = ~0
37 } variant_type_t;
38 
39 typedef struct variant_info_t {
40 	int passReplication;
41 	LDAP_STAILQ_HEAD(variant_list, variantEntry_info) variants, regex_variants;
42 } variant_info_t;
43 
44 typedef struct variantEntry_info {
45 	variant_info_t *ov;
46 	struct berval dn;
47 	variant_type_t type;
48 	regex_t *regex;
49 	LDAP_SLIST_HEAD(attribute_list, variantAttr_info) attributes;
50 	LDAP_STAILQ_ENTRY(variantEntry_info) next;
51 } variantEntry_info;
52 
53 typedef struct variantAttr_info {
54 	variantEntry_info *variant;
55 	struct berval dn;
56 	AttributeDescription *attr, *alternative;
57 	LDAP_SLIST_ENTRY(variantAttr_info) next;
58 } variantAttr_info;
59 
60 static int
variant_build_dn(Operation * op,variantAttr_info * vai,int nmatch,regmatch_t * pmatch,struct berval * out)61 variant_build_dn(
62 		Operation *op,
63 		variantAttr_info *vai,
64 		int nmatch,
65 		regmatch_t *pmatch,
66 		struct berval *out )
67 {
68 	struct berval dn, *ndn = &op->o_req_ndn;
69 	char *dest, *p, *prev, *end = vai->dn.bv_val + vai->dn.bv_len;
70 	size_t len = vai->dn.bv_len;
71 	int rc;
72 
73 	p = vai->dn.bv_val;
74 	while ( (p = memchr( p, '$', end - p )) != NULL ) {
75 		len -= 1;
76 		p += 1;
77 
78 		if ( ( *p >= '0' ) && ( *p <= '9' ) ) {
79 			int i = *p - '0';
80 
81 			len += ( pmatch[i].rm_eo - pmatch[i].rm_so );
82 		} else if ( *p != '$' ) {
83 			/* Should have been checked at configuration time */
84 			assert(0);
85 		}
86 		len -= 1;
87 		p += 1;
88 	}
89 
90 	dest = dn.bv_val = ch_realloc( out->bv_val, len + 1 );
91 	dn.bv_len = len;
92 
93 	prev = vai->dn.bv_val;
94 	while ( (p = memchr( prev, '$', end - prev )) != NULL ) {
95 		len = p - prev;
96 		AC_MEMCPY( dest, prev, len );
97 		dest += len;
98 		p += 1;
99 
100 		if ( ( *p >= '0' ) && ( *p <= '9' ) ) {
101 			int i = *p - '0';
102 			len = pmatch[i].rm_eo - pmatch[i].rm_so;
103 
104 			AC_MEMCPY( dest, ndn->bv_val + pmatch[i].rm_so, len );
105 			dest += len;
106 		} else if ( *p == '$' ) {
107 			*dest++ = *p;
108 		}
109 		prev = p + 1;
110 	}
111 	len = end - prev;
112 	AC_MEMCPY( dest, prev, len );
113 	dest += len;
114 	*dest = '\0';
115 
116 	rc = dnNormalize( 0, NULL, NULL, &dn, out, NULL );
117 	ch_free( dn.bv_val );
118 
119 	return rc;
120 }
121 
122 static int
variant_build_entry(Operation * op,variantEntry_info * vei,struct berval * dn,Entry ** ep,int nmatch,regmatch_t * pmatch)123 variant_build_entry(
124 		Operation *op,
125 		variantEntry_info *vei,
126 		struct berval *dn,
127 		Entry **ep,
128 		int nmatch,
129 		regmatch_t *pmatch )
130 {
131 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
132 	BackendDB *be_orig = op->o_bd, *db;
133 	struct berval ndn = BER_BVNULL;
134 	variantAttr_info *vai;
135 	Attribute *a;
136 	BerVarray nvals;
137 	Entry *e;
138 	unsigned int i;
139 	int rc;
140 
141 	assert( ep );
142 	assert( !*ep );
143 
144 	rc = overlay_entry_get_ov( op, dn, NULL, NULL, 0, &e, on );
145 	if ( rc == LDAP_SUCCESS && is_entry_referral( e ) ) {
146 		overlay_entry_release_ov( op, e, 0, on );
147 		rc = LDAP_REFERRAL;
148 	}
149 
150 	if ( rc != LDAP_SUCCESS ) {
151 		goto done;
152 	}
153 
154 	*ep = entry_dup( e );
155 	overlay_entry_release_ov( op, e, 0, on );
156 
157 	LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
158 		if ( vei->type == VARIANT_INFO_REGEX ) {
159 			rc = variant_build_dn( op, vai, nmatch, pmatch, &ndn );
160 			if ( rc != LDAP_SUCCESS ) {
161 				goto done;
162 			}
163 		} else {
164 			ndn = vai->dn;
165 		}
166 
167 		(void)attr_delete( &(*ep)->e_attrs, vai->attr );
168 		op->o_bd = be_orig;
169 
170 		/* only select backend if not served by ours, would retrace all
171 		 * overlays again */
172 		db = select_backend( &ndn, 0 );
173 		if ( db && db != be_orig->bd_self ) {
174 			op->o_bd = db;
175 			rc = be_entry_get_rw( op, &ndn, NULL, vai->alternative, 0, &e );
176 		} else {
177 			rc = overlay_entry_get_ov(
178 					op, &ndn, NULL, vai->alternative, 0, &e, on );
179 		}
180 
181 		switch ( rc ) {
182 			case LDAP_SUCCESS:
183 				break;
184 			case LDAP_INSUFFICIENT_ACCESS:
185 			case LDAP_NO_SUCH_ATTRIBUTE:
186 			case LDAP_NO_SUCH_OBJECT:
187 				rc = LDAP_SUCCESS;
188 				continue;
189 				break;
190 			default:
191 				goto done;
192 				break;
193 		}
194 
195 		a = attr_find( e->e_attrs, vai->alternative );
196 
197 		/* back-ldif doesn't check the attribute exists in the entry before
198 		 * returning it */
199 		if ( a ) {
200 			if ( a->a_nvals ) {
201 				nvals = a->a_nvals;
202 			} else {
203 				nvals = a->a_vals;
204 			}
205 
206 			for ( i = 0; i < a->a_numvals; i++ ) {
207 				if ( backend_access( op, e, &ndn, vai->alternative, &nvals[i],
208 							ACL_READ, NULL ) != LDAP_SUCCESS ) {
209 					continue;
210 				}
211 
212 				rc = attr_merge_one( *ep, vai->attr, &a->a_vals[i], &nvals[i] );
213 				if ( rc != LDAP_SUCCESS ) {
214 					break;
215 				}
216 			}
217 		}
218 
219 		if ( db && db != be_orig->bd_self ) {
220 			be_entry_release_rw( op, e, 0 );
221 		} else {
222 			overlay_entry_release_ov( op, e, 0, on );
223 		}
224 		if ( rc != LDAP_SUCCESS ) {
225 			goto done;
226 		}
227 	}
228 
229 done:
230 	op->o_bd = be_orig;
231 	if ( rc != LDAP_SUCCESS && *ep ) {
232 		entry_free( *ep );
233 		*ep = NULL;
234 	}
235 	if ( vei->type == VARIANT_INFO_REGEX ) {
236 		ch_free( ndn.bv_val );
237 	}
238 
239 	return rc;
240 }
241 
242 static int
variant_find_config(Operation * op,variant_info_t * ov,struct berval * ndn,int which,variantEntry_info ** veip,size_t nmatch,regmatch_t * pmatch)243 variant_find_config(
244 		Operation *op,
245 		variant_info_t *ov,
246 		struct berval *ndn,
247 		int which,
248 		variantEntry_info **veip,
249 		size_t nmatch,
250 		regmatch_t *pmatch )
251 {
252 	variantEntry_info *vei;
253 
254 	assert( veip );
255 
256 	if ( which & VARIANT_INFO_PLAIN ) {
257 		int diff;
258 
259 		LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
260 			dnMatch( &diff, 0, NULL, NULL, ndn, &vei->dn );
261 			if ( diff ) continue;
262 
263 			*veip = vei;
264 			return LDAP_SUCCESS;
265 		}
266 	}
267 
268 	if ( which & VARIANT_INFO_REGEX ) {
269 		LDAP_STAILQ_FOREACH( vei, &ov->regex_variants, next ) {
270 			if ( regexec( vei->regex, ndn->bv_val, nmatch, pmatch, 0 ) ) {
271 				continue;
272 			}
273 
274 			*veip = vei;
275 			return LDAP_SUCCESS;
276 		}
277 	}
278 
279 	return SLAP_CB_CONTINUE;
280 }
281 
282 static int
variant_op_add(Operation * op,SlapReply * rs)283 variant_op_add( Operation *op, SlapReply *rs )
284 {
285 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
286 	variant_info_t *ov = on->on_bi.bi_private;
287 	variantEntry_info *vei;
288 	int rc;
289 
290 	/* Replication always uses the rootdn */
291 	if ( ov->passReplication && SLAPD_SYNC_IS_SYNCCONN(op->o_connid) &&
292 			be_isroot( op ) ) {
293 		return SLAP_CB_CONTINUE;
294 	}
295 
296 	Debug( LDAP_DEBUG_TRACE, "variant_op_add: "
297 			"dn=%s\n", op->o_req_ndn.bv_val );
298 
299 	rc = variant_find_config(
300 			op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, 0, NULL );
301 	if ( rc == LDAP_SUCCESS ) {
302 		variantAttr_info *vai;
303 
304 		LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
305 			Attribute *a;
306 			for ( a = op->ora_e->e_attrs; a; a = a->a_next ) {
307 				if ( a->a_desc == vai->attr ) {
308 					rc = LDAP_CONSTRAINT_VIOLATION;
309 					send_ldap_error( op, rs, rc,
310 							"variant: trying to add variant attributes" );
311 					goto done;
312 				}
313 			}
314 		}
315 	}
316 	rc = SLAP_CB_CONTINUE;
317 
318 done:
319 	Debug( LDAP_DEBUG_TRACE, "variant_op_add: "
320 			"finished with %d\n",
321 			rc );
322 	return rc;
323 }
324 
325 static int
variant_op_compare(Operation * op,SlapReply * rs)326 variant_op_compare( Operation *op, SlapReply *rs )
327 {
328 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
329 	variant_info_t *ov = on->on_bi.bi_private;
330 	variantEntry_info *vei;
331 	regmatch_t pmatch[10];
332 	int rc, nmatch = sizeof(pmatch) / sizeof(regmatch_t);
333 
334 	Debug( LDAP_DEBUG_TRACE, "variant_op_compare: "
335 			"dn=%s\n", op->o_req_ndn.bv_val );
336 
337 	rc = variant_find_config(
338 			op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, nmatch, pmatch );
339 	if ( rc == LDAP_SUCCESS ) {
340 		Entry *e = NULL;
341 
342 		rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
343 		/* in case of error, just let the backend deal with the mod and the
344 		 * client should get a meaningful error back */
345 		if ( rc != LDAP_SUCCESS ) {
346 			rc = SLAP_CB_CONTINUE;
347 		} else {
348 			rc = slap_compare_entry( op, e, op->orc_ava );
349 
350 			entry_free( e );
351 			e = NULL;
352 		}
353 	}
354 
355 	if ( rc != SLAP_CB_CONTINUE ) {
356 		rs->sr_err = rc;
357 		send_ldap_result( op, rs );
358 	}
359 
360 	Debug( LDAP_DEBUG_TRACE, "variant_op_compare: "
361 			"finished with %d\n", rc );
362 	return rc;
363 }
364 
365 static int
variant_cmp_op(const void * l,const void * r)366 variant_cmp_op( const void *l, const void *r )
367 {
368 	const Operation *left = l, *right = r;
369 	int diff;
370 
371 	dnMatch( &diff, 0, NULL, NULL, (struct berval *)&left->o_req_ndn,
372 			(void *)&right->o_req_ndn );
373 
374 	return diff;
375 }
376 
377 static int
variant_run_mod(void * nop,void * arg)378 variant_run_mod( void *nop, void *arg )
379 {
380 	SlapReply nrs = { REP_RESULT };
381 	slap_callback cb = { 0 };
382 	Operation *op = nop;
383 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
384 	int *rc = arg;
385 
386 	cb.sc_response = slap_null_cb;
387 	op->o_callback = &cb;
388 
389 	Debug( LDAP_DEBUG_TRACE, "variant_run_mod: "
390 			"running mod on dn=%s\n",
391 			op->o_req_ndn.bv_val );
392 	*rc = on->on_info->oi_orig->bi_op_modify( op, &nrs );
393 	Debug( LDAP_DEBUG_TRACE, "variant_run_mod: "
394 			"finished with %d\n", *rc );
395 
396 	return ( *rc != LDAP_SUCCESS );
397 }
398 
399 /** Move the Modifications back to the original Op so that they can be disposed
400  * of by the original creator
401  */
402 static int
variant_reassign_mods(void * nop,void * arg)403 variant_reassign_mods( void *nop, void *arg )
404 {
405 	Operation *op = nop, *orig_op = arg;
406 	Modifications *mod;
407 
408 	assert( op->orm_modlist );
409 
410 	for ( mod = op->orm_modlist; mod->sml_next; mod = mod->sml_next )
411 		/* get the tail mod */;
412 
413 	mod->sml_next = orig_op->orm_modlist;
414 	orig_op->orm_modlist = op->orm_modlist;
415 
416 	return LDAP_SUCCESS;
417 }
418 
419 void
variant_free_op(void * op)420 variant_free_op( void *op )
421 {
422 	ch_free( ((Operation *)op)->o_req_ndn.bv_val );
423 	ch_free( op );
424 }
425 
426 static int
variant_op_mod(Operation * op,SlapReply * rs)427 variant_op_mod( Operation *op, SlapReply *rs )
428 {
429 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
430 	variant_info_t *ov = on->on_bi.bi_private;
431 	variantEntry_info *vei;
432 	variantAttr_info *vai;
433 	Avlnode *ops = NULL;
434 	Entry *e = NULL;
435 	Modifications *mod, *nextmod;
436 	regmatch_t pmatch[10];
437 	int rc, nmatch = sizeof(pmatch) / sizeof(regmatch_t);
438 
439 	/* Replication always uses the rootdn */
440 	if ( ov->passReplication && SLAPD_SYNC_IS_SYNCCONN(op->o_connid) &&
441 			be_isroot( op ) ) {
442 		return SLAP_CB_CONTINUE;
443 	}
444 
445 	Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
446 			"dn=%s\n", op->o_req_ndn.bv_val );
447 
448 	rc = variant_find_config(
449 			op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, nmatch, pmatch );
450 	if ( rc != LDAP_SUCCESS ) {
451 		Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
452 				"not a variant\n" );
453 		rc = SLAP_CB_CONTINUE;
454 		goto done;
455 	}
456 
457 	rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
458 	/* in case of error, just let the backend deal with the mod and the client
459 	 * should get a meaningful error back */
460 	if ( rc != LDAP_SUCCESS ) {
461 		Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
462 				"failed to retrieve entry\n" );
463 		rc = SLAP_CB_CONTINUE;
464 		goto done;
465 	}
466 
467 	rc = acl_check_modlist( op, e, op->orm_modlist );
468 	entry_free( e );
469 
470 	if ( !rc ) {
471 		rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
472 		send_ldap_error( op, rs, rc, "" );
473 		return rc;
474 	}
475 
476 	for ( mod = op->orm_modlist; mod; mod = nextmod ) {
477 		Operation needle = { .o_req_ndn = BER_BVNULL }, *nop;
478 
479 		nextmod = mod->sml_next;
480 
481 		LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
482 			if ( vai->attr == mod->sml_desc ) {
483 				break;
484 			}
485 		}
486 
487 		if ( vai ) {
488 			if ( vei->type == VARIANT_INFO_REGEX ) {
489 				rc = variant_build_dn(
490 						op, vai, nmatch, pmatch, &needle.o_req_ndn );
491 				if ( rc != LDAP_SUCCESS ) {
492 					continue;
493 				}
494 			} else {
495 				needle.o_req_ndn = vai->dn;
496 			}
497 
498 			nop = ldap_avl_find( ops, &needle, variant_cmp_op );
499 			if ( nop == NULL ) {
500 				nop = ch_calloc( 1, sizeof(Operation) );
501 				*nop = *op;
502 
503 				ber_dupbv( &nop->o_req_ndn, &needle.o_req_ndn );
504 				nop->o_req_dn = nop->o_req_ndn;
505 				nop->orm_modlist = NULL;
506 
507 				rc = ldap_avl_insert( &ops, nop, variant_cmp_op, ldap_avl_dup_error );
508 				assert( rc == 0 );
509 			}
510 			mod->sml_desc = vai->alternative;
511 
512 			op->orm_modlist = nextmod;
513 			mod->sml_next = nop->orm_modlist;
514 			nop->orm_modlist = mod;
515 
516 			if ( vei->type == VARIANT_INFO_REGEX ) {
517 				ch_free( needle.o_req_ndn.bv_val );
518 			}
519 		}
520 	}
521 
522 	if ( !ops ) {
523 		Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
524 				"no variant attributes in mod\n" );
525 		return SLAP_CB_CONTINUE;
526 	}
527 
528 	/*
529 	 * First run original Operation
530 	 * This will take care of making sure the entry exists as well.
531 	 *
532 	 * FIXME?
533 	 * Since we cannot make the subsequent Ops atomic wrt. this one, we just
534 	 * let it send the response as well. After all, the changes on the main DN
535 	 * have finished by then
536 	 */
537 	rc = on->on_info->oi_orig->bi_op_modify( op, rs );
538 	if ( rc == LDAP_SUCCESS ) {
539 		/* FIXME: if a mod fails, should we attempt to apply the rest? */
540 		ldap_avl_apply( ops, variant_run_mod, &rc, -1, AVL_INORDER );
541 	}
542 
543 	ldap_avl_apply( ops, variant_reassign_mods, op, -1, AVL_INORDER );
544 	ldap_avl_free( ops, variant_free_op );
545 
546 done:
547 	Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
548 			"finished with %d\n", rc );
549 	return rc;
550 }
551 
552 static int
variant_search_response(Operation * op,SlapReply * rs)553 variant_search_response( Operation *op, SlapReply *rs )
554 {
555 	slap_overinst *on = op->o_callback->sc_private;
556 	variant_info_t *ov = on->on_bi.bi_private;
557 	variantEntry_info *vei;
558 	int rc;
559 
560 	if ( rs->sr_type == REP_RESULT ) {
561 		ch_free( op->o_callback );
562 		op->o_callback = NULL;
563 	}
564 
565 	if ( rs->sr_type != REP_SEARCH ) {
566 		return SLAP_CB_CONTINUE;
567 	}
568 
569 	rc = variant_find_config(
570 			op, ov, &rs->sr_entry->e_nname, VARIANT_INFO_PLAIN, &vei, 0, NULL );
571 	if ( rc == LDAP_SUCCESS ) {
572 		rs->sr_nentries--;
573 		return rc;
574 	}
575 
576 	return SLAP_CB_CONTINUE;
577 }
578 
579 static int
variant_op_search(Operation * op,SlapReply * rs)580 variant_op_search( Operation *op, SlapReply *rs )
581 {
582 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
583 	variant_info_t *ov = on->on_bi.bi_private;
584 	variantEntry_info *vei;
585 	slap_callback *cb;
586 	Entry *e = NULL;
587 	regmatch_t pmatch[10];
588 	int variantInScope = 0, rc = SLAP_CB_CONTINUE,
589 		nmatch = sizeof(pmatch) / sizeof(regmatch_t);
590 
591 	if ( ov->passReplication && ( op->o_sync > SLAP_CONTROL_IGNORED ) ) {
592 		return SLAP_CB_CONTINUE;
593 	}
594 
595 	Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
596 			"dn=%s, scope=%d\n",
597 			op->o_req_ndn.bv_val, op->ors_scope );
598 
599 	LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
600 		if ( !dnIsSuffixScope( &vei->dn, &op->o_req_ndn, op->ors_scope ) )
601 			continue;
602 
603 		variantInScope = 1;
604 
605 		rc = variant_build_entry( op, vei, &vei->dn, &e, 0, NULL );
606 		if ( rc == LDAP_NO_SUCH_OBJECT || rc == LDAP_REFERRAL ) {
607 			rc = SLAP_CB_CONTINUE;
608 			continue;
609 		} else if ( rc != LDAP_SUCCESS ) {
610 			Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
611 					"failed to retrieve entry: dn=%s\n",
612 					vei->dn.bv_val );
613 			goto done;
614 		}
615 
616 		if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
617 			Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
618 					"entry matched: dn=%s\n",
619 					vei->dn.bv_val );
620 			rs->sr_entry = e;
621 			rs->sr_attrs = op->ors_attrs;
622 			rc = send_search_entry( op, rs );
623 		}
624 		entry_free( e );
625 		e = NULL;
626 	}
627 
628 	/* Three options:
629 	 * - the entry has been handled above, in that case vei->type is VARIANT_INFO_PLAIN
630 	 * - the entry matches a regex, use the first one and we're finished
631 	 * - no configuration matches entry - do nothing
632 	 */
633 	if ( op->ors_scope == LDAP_SCOPE_BASE &&
634 			variant_find_config( op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei,
635 					nmatch, pmatch ) == LDAP_SUCCESS &&
636 			vei->type == VARIANT_INFO_REGEX ) {
637 		rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
638 		if ( rc == LDAP_NO_SUCH_OBJECT || rc == LDAP_REFERRAL ) {
639 			rc = SLAP_CB_CONTINUE;
640 		} else if ( rc != LDAP_SUCCESS ) {
641 			Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
642 					"failed to retrieve entry: dn=%s\n",
643 					vei->dn.bv_val );
644 			goto done;
645 		} else {
646 			if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
647 				Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
648 						"entry matched: dn=%s\n",
649 						vei->dn.bv_val );
650 				rs->sr_entry = e;
651 				rs->sr_attrs = op->ors_attrs;
652 				rc = send_search_entry( op, rs );
653 			}
654 			entry_free( e );
655 			e = NULL;
656 			goto done;
657 		}
658 	}
659 	rc = SLAP_CB_CONTINUE;
660 
661 	if ( variantInScope ) {
662 		cb = ch_calloc( 1, sizeof(slap_callback) );
663 		cb->sc_private = on;
664 		cb->sc_response = variant_search_response;
665 		cb->sc_next = op->o_callback;
666 
667 		op->o_callback = cb;
668 	}
669 
670 done:
671 	if ( rc != SLAP_CB_CONTINUE ) {
672 		rs->sr_err = (rc == LDAP_SUCCESS) ? rc : LDAP_OTHER;
673 		send_ldap_result( op, rs );
674 	}
675 	Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
676 			"finished with %d\n", rc );
677 	return rc;
678 }
679 
680 /* Configuration */
681 
682 static ConfigLDAPadd variant_ldadd;
683 static ConfigLDAPadd variant_regex_ldadd;
684 static ConfigLDAPadd variant_attr_ldadd;
685 
686 static ConfigDriver variant_set_dn;
687 static ConfigDriver variant_set_regex;
688 static ConfigDriver variant_set_alt_dn;
689 static ConfigDriver variant_set_alt_pattern;
690 static ConfigDriver variant_set_attribute;
691 static ConfigDriver variant_add_alt_attr;
692 static ConfigDriver variant_add_alt_attr_regex;
693 
694 static ConfigCfAdd variant_cfadd;
695 
696 enum
697 {
698 	VARIANT_ATTR = 1,
699 	VARIANT_ATTR_ALT,
700 
701 	VARIANT_LAST,
702 };
703 
704 static ConfigTable variant_cfg[] = {
705 	{ "passReplication", "on|off", 2, 2, 0,
706 		ARG_ON_OFF|ARG_OFFSET,
707 		(void *)offsetof( variant_info_t, passReplication ),
708 		"( OLcfgOvAt:FIXME.1 NAME 'olcVariantPassReplication' "
709 			"DESC 'Whether to let searches with replication control "
710 				"pass unmodified' "
711 			"SYNTAX OMsBoolean "
712 			"SINGLE-VALUE )",
713 			NULL, NULL
714 	},
715 	{ "variantDN", "dn", 2, 2, 0,
716 		ARG_DN|ARG_QUOTE|ARG_MAGIC,
717 		variant_set_dn,
718 		"( OLcfgOvAt:FIXME.2 NAME 'olcVariantEntry' "
719 			"DESC 'DN of the variant entry' "
720 			"EQUALITY distinguishedNameMatch "
721 			"SYNTAX OMsDN "
722 			"SINGLE-VALUE )",
723 			NULL, NULL
724 	},
725 	{ "variantRegex", "regex", 2, 2, 0,
726 		ARG_BERVAL|ARG_QUOTE|ARG_MAGIC,
727 		variant_set_regex,
728 		"( OLcfgOvAt:FIXME.6 NAME 'olcVariantEntryRegex' "
729 			"DESC 'Pattern for the variant entry' "
730 			"EQUALITY caseExactMatch "
731 			"SYNTAX OMsDirectoryString "
732 			"SINGLE-VALUE )",
733 			NULL, NULL
734 	},
735 	/* These have no equivalent in slapd.conf */
736 	{ "", NULL, 2, 2, 0,
737 		ARG_STRING|ARG_MAGIC|VARIANT_ATTR,
738 		variant_set_attribute,
739 		"( OLcfgOvAt:FIXME.3 NAME 'olcVariantVariantAttribute' "
740 			"DESC 'Attribute to fill in the entry' "
741 			"EQUALITY caseIgnoreMatch "
742 			"SYNTAX OMsDirectoryString "
743 			"SINGLE-VALUE )",
744 			NULL, NULL
745 	},
746 	{ "", NULL, 2, 2, 0,
747 		ARG_STRING|ARG_MAGIC|VARIANT_ATTR_ALT,
748 		variant_set_attribute,
749 		"( OLcfgOvAt:FIXME.4 NAME 'olcVariantAlternativeAttribute' "
750 			"DESC 'Attribute to take from the alternative entry' "
751 			"EQUALITY caseIgnoreMatch "
752 			"SYNTAX OMsDirectoryString "
753 			"SINGLE-VALUE )",
754 			NULL, NULL
755 	},
756 	{ "", NULL, 2, 2, 0,
757 		ARG_DN|ARG_QUOTE|ARG_MAGIC,
758 		variant_set_alt_dn,
759 		"( OLcfgOvAt:FIXME.5 NAME 'olcVariantAlternativeEntry' "
760 			"DESC 'DN of the alternative entry' "
761 			"EQUALITY distinguishedNameMatch "
762 			"SYNTAX OMsDN "
763 			"SINGLE-VALUE )",
764 			NULL, NULL
765 	},
766 	{ "", NULL, 2, 2, 0,
767 		ARG_BERVAL|ARG_QUOTE|ARG_MAGIC,
768 		variant_set_alt_pattern,
769 		"( OLcfgOvAt:FIXME.7 NAME 'olcVariantAlternativeEntryPattern' "
770 			"DESC 'Replacement pattern to locate the alternative entry' "
771 			"EQUALITY caseExactMatch "
772 			"SYNTAX OMsDirectoryString "
773 			"SINGLE-VALUE )",
774 			NULL, NULL
775 	},
776 	/* slapd.conf alternatives for the four above */
777 	{ "variantSpec", "attr attr2 dn", 4, 4, 0,
778 		ARG_QUOTE|ARG_MAGIC,
779 		variant_add_alt_attr,
780 		NULL, NULL, NULL
781 	},
782 	{ "variantRegexSpec", "attr attr2 pattern", 4, 4, 0,
783 		ARG_QUOTE|ARG_MAGIC,
784 		variant_add_alt_attr_regex,
785 		NULL, NULL, NULL
786 	},
787 
788 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
789 };
790 
791 static ConfigOCs variant_ocs[] = {
792 	{ "( OLcfgOvOc:FIXME.1 "
793 		"NAME 'olcVariantConfig' "
794 		"DESC 'Variant overlay configuration' "
795 		"SUP olcOverlayConfig "
796 		"MAY ( olcVariantPassReplication ) )",
797 		Cft_Overlay, variant_cfg, NULL, variant_cfadd },
798 	{ "( OLcfgOvOc:FIXME.2 "
799 		"NAME 'olcVariantVariant' "
800 		"DESC 'Variant configuration' "
801 		"MUST ( olcVariantEntry ) "
802 		"MAY ( name ) "
803 		"SUP top "
804 		"STRUCTURAL )",
805 		Cft_Misc, variant_cfg, variant_ldadd },
806 	{ "( OLcfgOvOc:FIXME.3 "
807 		"NAME 'olcVariantAttribute' "
808 		"DESC 'Variant attribute description' "
809 		"MUST ( olcVariantVariantAttribute $ "
810 			"olcVariantAlternativeAttribute $ "
811 			"olcVariantAlternativeEntry "
812 		") "
813 		"MAY name "
814 		"SUP top "
815 		"STRUCTURAL )",
816 		Cft_Misc, variant_cfg, variant_attr_ldadd },
817 	{ "( OLcfgOvOc:FIXME.4 "
818 		"NAME 'olcVariantRegex' "
819 		"DESC 'Variant configuration' "
820 		"MUST ( olcVariantEntryRegex ) "
821 		"MAY ( name ) "
822 		"SUP top "
823 		"STRUCTURAL )",
824 		Cft_Misc, variant_cfg, variant_regex_ldadd },
825 	{ "( OLcfgOvOc:FIXME.5 "
826 		"NAME 'olcVariantAttributePattern' "
827 		"DESC 'Variant attribute description' "
828 		"MUST ( olcVariantVariantAttribute $ "
829 			"olcVariantAlternativeAttribute $ "
830 			"olcVariantAlternativeEntryPattern "
831 		") "
832 		"MAY name "
833 		"SUP top "
834 		"STRUCTURAL )",
835 		Cft_Misc, variant_cfg, variant_attr_ldadd },
836 
837 	{ NULL, 0, NULL }
838 };
839 
840 static int
variant_set_dn(ConfigArgs * ca)841 variant_set_dn( ConfigArgs *ca )
842 {
843 	variantEntry_info *vei2, *vei = ca->ca_private;
844 	slap_overinst *on = (slap_overinst *)ca->bi;
845 	variant_info_t *ov = on->on_bi.bi_private;
846 	int diff;
847 
848 	if ( ca->op == SLAP_CONFIG_EMIT ) {
849 		value_add_one( &ca->rvalue_vals, &vei->dn );
850 		return LDAP_SUCCESS;
851 	} else if ( ca->op == LDAP_MOD_DELETE ) {
852 		ber_memfree( vei->dn.bv_val );
853 		BER_BVZERO( &vei->dn );
854 		return LDAP_SUCCESS;
855 	}
856 
857 	if ( !vei ) {
858 		vei = ch_calloc( 1, sizeof(variantEntry_info) );
859 		vei->ov = ov;
860 		vei->type = VARIANT_INFO_PLAIN;
861 		LDAP_SLIST_INIT(&vei->attributes);
862 		LDAP_STAILQ_ENTRY_INIT(vei, next);
863 		LDAP_STAILQ_INSERT_TAIL(&ov->variants, vei, next);
864 
865 		ca->ca_private = vei;
866 	}
867 	vei->dn = ca->value_ndn;
868 	ber_memfree( ca->value_dn.bv_val );
869 
870 	/* Each DN should only be listed once */
871 	LDAP_STAILQ_FOREACH( vei2, &vei->ov->variants, next ) {
872 		if ( vei == vei2 ) continue;
873 
874 		dnMatch( &diff, 0, NULL, NULL, &vei->dn, &vei2->dn );
875 		if ( !diff ) {
876 			ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
877 			return ca->reply.err;
878 		}
879 	}
880 
881 	return LDAP_SUCCESS;
882 }
883 
884 static int
variant_set_regex(ConfigArgs * ca)885 variant_set_regex( ConfigArgs *ca )
886 {
887 	variantEntry_info *vei2, *vei = ca->ca_private;
888 	slap_overinst *on = (slap_overinst *)ca->bi;
889 	variant_info_t *ov = on->on_bi.bi_private;
890 
891 	if ( ca->op == SLAP_CONFIG_EMIT ) {
892 		ca->value_bv = vei->dn;
893 		return LDAP_SUCCESS;
894 	} else if ( ca->op == LDAP_MOD_DELETE ) {
895 		ber_memfree( vei->dn.bv_val );
896 		BER_BVZERO( &vei->dn );
897 		regfree( vei->regex );
898 		return LDAP_SUCCESS;
899 	}
900 
901 	if ( !vei ) {
902 		vei = ch_calloc( 1, sizeof(variantEntry_info) );
903 		vei->ov = ov;
904 		vei->type = VARIANT_INFO_REGEX;
905 		LDAP_SLIST_INIT(&vei->attributes);
906 		LDAP_STAILQ_ENTRY_INIT(vei, next);
907 		LDAP_STAILQ_INSERT_TAIL(&ov->regex_variants, vei, next);
908 
909 		ca->ca_private = vei;
910 	}
911 	vei->dn = ca->value_bv;
912 
913 	/* Each regex should only be listed once */
914 	LDAP_STAILQ_FOREACH( vei2, &vei->ov->regex_variants, next ) {
915 		if ( vei == vei2 ) continue;
916 
917 		if ( !ber_bvcmp( &ca->value_bv, &vei2->dn ) ) {
918 			ch_free( vei );
919 			ca->ca_private = NULL;
920 			ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
921 			return ca->reply.err;
922 		}
923 	}
924 
925 	vei->regex = ch_calloc( 1, sizeof(regex_t) );
926 	if ( regcomp( vei->regex, vei->dn.bv_val, REG_EXTENDED ) ) {
927 		ch_free( vei->regex );
928 		ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
929 		return ca->reply.err;
930 	}
931 
932 	return LDAP_SUCCESS;
933 }
934 
935 static int
variant_set_alt_dn(ConfigArgs * ca)936 variant_set_alt_dn( ConfigArgs *ca )
937 {
938 	variantAttr_info *vai = ca->ca_private;
939 
940 	if ( ca->op == SLAP_CONFIG_EMIT ) {
941 		value_add_one( &ca->rvalue_vals, &vai->dn );
942 		return LDAP_SUCCESS;
943 	} else if ( ca->op == LDAP_MOD_DELETE ) {
944 		ber_memfree( vai->dn.bv_val );
945 		BER_BVZERO( &vai->dn );
946 		return LDAP_SUCCESS;
947 	}
948 
949 	vai->dn = ca->value_ndn;
950 	ber_memfree( ca->value_dn.bv_val );
951 
952 	return LDAP_SUCCESS;
953 }
954 
955 static int
variant_set_alt_pattern(ConfigArgs * ca)956 variant_set_alt_pattern( ConfigArgs *ca )
957 {
958 	variantAttr_info *vai = ca->ca_private;
959 	char *p = ca->value_bv.bv_val,
960 		 *end = ca->value_bv.bv_val + ca->value_bv.bv_len;
961 
962 	if ( ca->op == SLAP_CONFIG_EMIT ) {
963 		ca->value_bv = vai->dn;
964 		return LDAP_SUCCESS;
965 	} else if ( ca->op == LDAP_MOD_DELETE ) {
966 		ber_memfree( vai->dn.bv_val );
967 		BER_BVZERO( &vai->dn );
968 		return LDAP_SUCCESS;
969 	}
970 
971 	while ( (p = memchr( p, '$', end - p )) != NULL ) {
972 		p += 1;
973 
974 		if ( ( ( *p >= '0' ) && ( *p <= '9' ) ) || ( *p == '$' ) ) {
975 			p += 1;
976 		} else {
977 			Debug( LDAP_DEBUG_ANY, "variant_set_alt_pattern: "
978 					"invalid replacement pattern supplied '%s'\n",
979 					ca->value_bv.bv_val );
980 			ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
981 			return ca->reply.err;
982 		}
983 	}
984 
985 	vai->dn = ca->value_bv;
986 
987 	return LDAP_SUCCESS;
988 }
989 
990 static int
variant_set_attribute(ConfigArgs * ca)991 variant_set_attribute( ConfigArgs *ca )
992 {
993 	variantAttr_info *vai2, *vai = ca->ca_private;
994 	char *s = ca->value_string;
995 	const char *text;
996 	AttributeDescription **ad;
997 	int rc;
998 
999 	if ( ca->type == VARIANT_ATTR ) {
1000 		ad = &vai->attr;
1001 	} else {
1002 		ad = &vai->alternative;
1003 	}
1004 
1005 	if ( ca->op == SLAP_CONFIG_EMIT ) {
1006 		ca->value_string = ch_strdup( (*ad)->ad_cname.bv_val );
1007 		return LDAP_SUCCESS;
1008 	} else if ( ca->op == LDAP_MOD_DELETE ) {
1009 		*ad = NULL;
1010 		return LDAP_SUCCESS;
1011 	}
1012 
1013 	if ( *s == '{' ) {
1014 		s = strchr( s, '}' );
1015 		if ( !s ) {
1016 			ca->reply.err = LDAP_UNDEFINED_TYPE;
1017 			return ca->reply.err;
1018 		}
1019 		s += 1;
1020 	}
1021 
1022 	rc = slap_str2ad( s, ad, &text );
1023 	ber_memfree( ca->value_string );
1024 	if ( rc ) {
1025 		return rc;
1026 	}
1027 
1028 	/* Both attributes have to share the same syntax */
1029 	if ( vai->attr && vai->alternative &&
1030 			vai->attr->ad_type->sat_syntax !=
1031 					vai->alternative->ad_type->sat_syntax ) {
1032 		ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
1033 		return ca->reply.err;
1034 	}
1035 
1036 	if ( ca->type == VARIANT_ATTR ) {
1037 		/* Each attribute should only be listed once */
1038 		LDAP_SLIST_FOREACH( vai2, &vai->variant->attributes, next ) {
1039 			if ( vai == vai2 ) continue;
1040 			if ( vai->attr == vai2->attr ) {
1041 				ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
1042 				return ca->reply.err;
1043 			}
1044 		}
1045 	}
1046 
1047 	return LDAP_SUCCESS;
1048 }
1049 
1050 static int
variant_add_alt_attr(ConfigArgs * ca)1051 variant_add_alt_attr( ConfigArgs *ca )
1052 {
1053 	slap_overinst *on = (slap_overinst *)ca->bi;
1054 	variant_info_t *ov = on->on_bi.bi_private;
1055 	variantEntry_info *vei =
1056 			LDAP_STAILQ_LAST( &ov->variants, variantEntry_info, next );
1057 	variantAttr_info *vai;
1058 	struct berval dn, ndn;
1059 	int rc;
1060 
1061 	vai = ch_calloc( 1, sizeof(variantAttr_info) );
1062 	vai->variant = vei;
1063 	LDAP_SLIST_ENTRY_INIT( vai, next );
1064 	ca->ca_private = vai;
1065 
1066 	ca->value_string = ch_strdup( ca->argv[1] );
1067 	ca->type = VARIANT_ATTR;
1068 	rc = variant_set_attribute( ca );
1069 	if ( rc != LDAP_SUCCESS ) {
1070 		goto done;
1071 	}
1072 
1073 	ca->value_string = ch_strdup( ca->argv[2] );
1074 	ca->type = VARIANT_ATTR_ALT;
1075 	rc = variant_set_attribute( ca );
1076 	if ( rc != LDAP_SUCCESS ) {
1077 		goto done;
1078 	}
1079 
1080 	dn.bv_val = ca->argv[3];
1081 	dn.bv_len = strlen( dn.bv_val );
1082 	rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
1083 	if ( rc != LDAP_SUCCESS ) {
1084 		goto done;
1085 	}
1086 
1087 	ca->type = 0;
1088 	BER_BVZERO( &ca->value_dn );
1089 	ca->value_ndn = ndn;
1090 	rc = variant_set_alt_dn( ca );
1091 	if ( rc != LDAP_SUCCESS ) {
1092 		ch_free( ndn.bv_val );
1093 		goto done;
1094 	}
1095 
1096 done:
1097 	if ( rc == LDAP_SUCCESS ) {
1098 		LDAP_SLIST_INSERT_HEAD( &vei->attributes, vai, next );
1099 	} else {
1100 		ca->reply.err = rc;
1101 	}
1102 
1103 	return rc;
1104 }
1105 
1106 static int
variant_add_alt_attr_regex(ConfigArgs * ca)1107 variant_add_alt_attr_regex( ConfigArgs *ca )
1108 {
1109 	slap_overinst *on = (slap_overinst *)ca->bi;
1110 	variant_info_t *ov = on->on_bi.bi_private;
1111 	variantEntry_info *vei =
1112 			LDAP_STAILQ_LAST( &ov->regex_variants, variantEntry_info, next );
1113 	variantAttr_info *vai;
1114 	int rc;
1115 
1116 	vai = ch_calloc( 1, sizeof(variantAttr_info) );
1117 	vai->variant = vei;
1118 	LDAP_SLIST_ENTRY_INIT( vai, next );
1119 	ca->ca_private = vai;
1120 
1121 	ca->value_string = ch_strdup( ca->argv[1] );
1122 	ca->type = VARIANT_ATTR;
1123 	rc = variant_set_attribute( ca );
1124 	if ( rc != LDAP_SUCCESS ) {
1125 		goto done;
1126 	}
1127 
1128 	ca->value_string = ch_strdup( ca->argv[2] );
1129 	ca->type = VARIANT_ATTR_ALT;
1130 	rc = variant_set_attribute( ca );
1131 	if ( rc != LDAP_SUCCESS ) {
1132 		goto done;
1133 	}
1134 
1135 	ca->type = 0;
1136 	ber_str2bv( ca->argv[3], 0, 1, &ca->value_bv );
1137 	rc = variant_set_alt_pattern( ca );
1138 	if ( rc != LDAP_SUCCESS ) {
1139 		goto done;
1140 	}
1141 
1142 done:
1143 	if ( rc == LDAP_SUCCESS ) {
1144 		LDAP_SLIST_INSERT_HEAD( &vei->attributes, vai, next );
1145 	} else {
1146 		ca->reply.err = rc;
1147 	}
1148 
1149 	return rc;
1150 }
1151 
1152 static int
variant_ldadd_cleanup(ConfigArgs * ca)1153 variant_ldadd_cleanup( ConfigArgs *ca )
1154 {
1155 	variantEntry_info *vei = ca->ca_private;
1156 	slap_overinst *on = (slap_overinst *)ca->bi;
1157 	variant_info_t *ov = on->on_bi.bi_private;
1158 
1159 	if ( ca->reply.err != LDAP_SUCCESS ) {
1160 		assert( LDAP_SLIST_EMPTY(&vei->attributes) );
1161 		ch_free( vei );
1162 		return LDAP_SUCCESS;
1163 	}
1164 
1165 	if ( vei->type == VARIANT_INFO_PLAIN ) {
1166 		LDAP_STAILQ_INSERT_TAIL(&ov->variants, vei, next);
1167 	} else {
1168 		LDAP_STAILQ_INSERT_TAIL(&ov->regex_variants, vei, next);
1169 	}
1170 
1171 	return LDAP_SUCCESS;
1172 }
1173 
1174 static int
variant_ldadd(CfEntryInfo * cei,Entry * e,ConfigArgs * ca)1175 variant_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
1176 {
1177 	slap_overinst *on;
1178 	variant_info_t *ov;
1179 	variantEntry_info *vei;
1180 
1181 	if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
1182 			cei->ce_bi->bi_cf_ocs != variant_ocs )
1183 		return LDAP_CONSTRAINT_VIOLATION;
1184 
1185 	on = (slap_overinst *)cei->ce_bi;
1186 	ov = on->on_bi.bi_private;
1187 
1188 	vei = ch_calloc( 1, sizeof(variantEntry_info) );
1189 	vei->ov = ov;
1190 	vei->type = VARIANT_INFO_PLAIN;
1191 	LDAP_SLIST_INIT(&vei->attributes);
1192 	LDAP_STAILQ_ENTRY_INIT(vei, next);
1193 
1194 	ca->bi = cei->ce_bi;
1195 	ca->ca_private = vei;
1196 	config_push_cleanup( ca, variant_ldadd_cleanup );
1197 	/* config_push_cleanup is only run in the case of online config but we use it to
1198 	 * save the new config when done with the entry */
1199 	ca->lineno = 0;
1200 
1201 	return LDAP_SUCCESS;
1202 }
1203 
1204 static int
variant_regex_ldadd(CfEntryInfo * cei,Entry * e,ConfigArgs * ca)1205 variant_regex_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
1206 {
1207 	slap_overinst *on;
1208 	variant_info_t *ov;
1209 	variantEntry_info *vei;
1210 
1211 	if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
1212 			cei->ce_bi->bi_cf_ocs != variant_ocs )
1213 		return LDAP_CONSTRAINT_VIOLATION;
1214 
1215 	on = (slap_overinst *)cei->ce_bi;
1216 	ov = on->on_bi.bi_private;
1217 
1218 	vei = ch_calloc( 1, sizeof(variantEntry_info) );
1219 	vei->ov = ov;
1220 	vei->type = VARIANT_INFO_REGEX;
1221 	LDAP_SLIST_INIT(&vei->attributes);
1222 	LDAP_STAILQ_ENTRY_INIT(vei, next);
1223 
1224 	ca->bi = cei->ce_bi;
1225 	ca->ca_private = vei;
1226 	config_push_cleanup( ca, variant_ldadd_cleanup );
1227 	/* config_push_cleanup is only run in the case of online config but we use it to
1228 	 * save the new config when done with the entry */
1229 	ca->lineno = 0;
1230 
1231 	return LDAP_SUCCESS;
1232 }
1233 
1234 static int
variant_attr_ldadd_cleanup(ConfigArgs * ca)1235 variant_attr_ldadd_cleanup( ConfigArgs *ca )
1236 {
1237 	variantAttr_info *vai = ca->ca_private;
1238 	variantEntry_info *vei = vai->variant;
1239 
1240 	if ( ca->reply.err != LDAP_SUCCESS ) {
1241 		ch_free( vai );
1242 		return LDAP_SUCCESS;
1243 	}
1244 
1245 	LDAP_SLIST_INSERT_HEAD(&vei->attributes, vai, next);
1246 
1247 	return LDAP_SUCCESS;
1248 }
1249 
1250 static int
variant_attr_ldadd(CfEntryInfo * cei,Entry * e,ConfigArgs * ca)1251 variant_attr_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
1252 {
1253 	variantEntry_info *vei;
1254 	variantAttr_info *vai;
1255 	CfEntryInfo *parent = cei->ce_parent;
1256 
1257 	if ( cei->ce_type != Cft_Misc || !parent || !parent->ce_bi ||
1258 			parent->ce_bi->bi_cf_ocs != variant_ocs )
1259 		return LDAP_CONSTRAINT_VIOLATION;
1260 
1261 	vei = (variantEntry_info *)cei->ce_private;
1262 
1263 	vai = ch_calloc( 1, sizeof(variantAttr_info) );
1264 	vai->variant = vei;
1265 	LDAP_SLIST_ENTRY_INIT(vai, next);
1266 
1267 	ca->ca_private = vai;
1268 	config_push_cleanup( ca, variant_attr_ldadd_cleanup );
1269 	/* config_push_cleanup is only run in the case of online config but we use it to
1270 	 * save the new config when done with the entry */
1271 	ca->lineno = 0;
1272 
1273 	return LDAP_SUCCESS;
1274 }
1275 
1276 static int
variant_cfadd(Operation * op,SlapReply * rs,Entry * p,ConfigArgs * ca)1277 variant_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1278 {
1279 	slap_overinst *on = (slap_overinst *)ca->bi;
1280 	variant_info_t *ov = on->on_bi.bi_private;
1281 	variantEntry_info *vei;
1282 	variantAttr_info *vai;
1283 	Entry *e;
1284 	struct berval rdn;
1285 	int i = 0;
1286 
1287 	LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
1288 		int j = 0;
1289 		rdn.bv_len = snprintf(
1290 				ca->cr_msg, sizeof(ca->cr_msg), "name={%d}variant", i++ );
1291 		rdn.bv_val = ca->cr_msg;
1292 
1293 		ca->ca_private = vei;
1294 		e = config_build_entry(
1295 				op, rs, p->e_private, ca, &rdn, &variant_ocs[1], NULL );
1296 		assert( e );
1297 
1298 		LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
1299 			rdn.bv_len = snprintf( ca->cr_msg, sizeof(ca->cr_msg),
1300 					"olcVariantVariantAttribute={%d}%s", j++,
1301 					vai->attr->ad_cname.bv_val );
1302 			rdn.bv_val = ca->cr_msg;
1303 
1304 			ca->ca_private = vai;
1305 			config_build_entry(
1306 					op, rs, e->e_private, ca, &rdn, &variant_ocs[2], NULL );
1307 		}
1308 	}
1309 
1310 	LDAP_STAILQ_FOREACH( vei, &ov->regex_variants, next ) {
1311 		int j = 0;
1312 		rdn.bv_len = snprintf(
1313 				ca->cr_msg, sizeof(ca->cr_msg), "name={%d}regex", i++ );
1314 		rdn.bv_val = ca->cr_msg;
1315 
1316 		ca->ca_private = vei;
1317 		e = config_build_entry(
1318 				op, rs, p->e_private, ca, &rdn, &variant_ocs[3], NULL );
1319 		assert( e );
1320 
1321 		LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
1322 			rdn.bv_len = snprintf( ca->cr_msg, sizeof(ca->cr_msg),
1323 					"olcVariantVariantAttribute={%d}%s", j++,
1324 					vai->attr->ad_cname.bv_val );
1325 			rdn.bv_val = ca->cr_msg;
1326 
1327 			ca->ca_private = vai;
1328 			config_build_entry(
1329 					op, rs, e->e_private, ca, &rdn, &variant_ocs[4], NULL );
1330 		}
1331 	}
1332 	return LDAP_SUCCESS;
1333 }
1334 
1335 static slap_overinst variant;
1336 
1337 static int
variant_db_init(BackendDB * be,ConfigReply * cr)1338 variant_db_init( BackendDB *be, ConfigReply *cr )
1339 {
1340 	slap_overinst *on = (slap_overinst *)be->bd_info;
1341 	variant_info_t *ov;
1342 
1343 	if ( SLAP_ISGLOBALOVERLAY(be) ) {
1344 		Debug( LDAP_DEBUG_ANY, "variant overlay must be instantiated within "
1345 				"a database.\n" );
1346 		return 1;
1347 	}
1348 
1349 	ov = ch_calloc( 1, sizeof(variant_info_t) );
1350 	LDAP_STAILQ_INIT(&ov->variants);
1351 	LDAP_STAILQ_INIT(&ov->regex_variants);
1352 
1353 	on->on_bi.bi_private = ov;
1354 
1355 	return LDAP_SUCCESS;
1356 }
1357 
1358 static int
variant_db_destroy(BackendDB * be,ConfigReply * cr)1359 variant_db_destroy( BackendDB *be, ConfigReply *cr )
1360 {
1361 	slap_overinst *on = (slap_overinst *)be->bd_info;
1362 	variant_info_t *ov = on->on_bi.bi_private;
1363 
1364 	if ( ov ) {
1365 		while ( !LDAP_STAILQ_EMPTY( &ov->variants ) ) {
1366 			variantEntry_info *vei = LDAP_STAILQ_FIRST( &ov->variants );
1367 			LDAP_STAILQ_REMOVE_HEAD( &ov->variants, next );
1368 
1369 			while ( !LDAP_SLIST_EMPTY( &vei->attributes ) ) {
1370 				variantAttr_info *vai = LDAP_SLIST_FIRST( &vei->attributes );
1371 				LDAP_SLIST_REMOVE_HEAD( &vei->attributes, next );
1372 
1373 				ber_memfree( vai->dn.bv_val );
1374 				ch_free( vai );
1375 			}
1376 			ber_memfree( vei->dn.bv_val );
1377 			ch_free( vei );
1378 		}
1379 		while ( !LDAP_STAILQ_EMPTY( &ov->regex_variants ) ) {
1380 			variantEntry_info *vei = LDAP_STAILQ_FIRST( &ov->regex_variants );
1381 			LDAP_STAILQ_REMOVE_HEAD( &ov->regex_variants, next );
1382 
1383 			while ( !LDAP_SLIST_EMPTY( &vei->attributes ) ) {
1384 				variantAttr_info *vai = LDAP_SLIST_FIRST( &vei->attributes );
1385 				LDAP_SLIST_REMOVE_HEAD( &vei->attributes, next );
1386 
1387 				ber_memfree( vai->dn.bv_val );
1388 				ch_free( vai );
1389 			}
1390 			ber_memfree( vei->dn.bv_val );
1391 			ch_free( vei );
1392 		}
1393 		ch_free( ov );
1394 	}
1395 
1396 	return LDAP_SUCCESS;
1397 }
1398 
1399 int
variant_initialize()1400 variant_initialize()
1401 {
1402 	int rc;
1403 
1404 	variant.on_bi.bi_type = "variant";
1405 	variant.on_bi.bi_db_init = variant_db_init;
1406 	variant.on_bi.bi_db_destroy = variant_db_destroy;
1407 
1408 	variant.on_bi.bi_op_add = variant_op_add;
1409 	variant.on_bi.bi_op_compare = variant_op_compare;
1410 	variant.on_bi.bi_op_modify = variant_op_mod;
1411 	variant.on_bi.bi_op_search = variant_op_search;
1412 
1413 	variant.on_bi.bi_cf_ocs = variant_ocs;
1414 
1415 	rc = config_register_schema( variant_cfg, variant_ocs );
1416 	if ( rc ) return rc;
1417 
1418 	return overlay_register( &variant );
1419 }
1420 
1421 #if SLAPD_OVER_VARIANT == SLAPD_MOD_DYNAMIC
1422 int
init_module(int argc,char * argv[])1423 init_module( int argc, char *argv[] )
1424 {
1425 	return variant_initialize();
1426 }
1427 #endif
1428 
1429 #endif /* SLAPD_OVER_VARIANT */
1430