xref: /netbsd-src/external/bsd/openldap/dist/libraries/librewrite/context.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1*549b59edSchristos /*	$NetBSD: context.c,v 1.3 2021/08/14 16:14:58 christos Exp $	*/
24e6df137Slukem 
3d11b170bStron /* $OpenLDAP$ */
42de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
52de962bdSlukem  *
6*549b59edSchristos  * Copyright 2000-2021 The OpenLDAP Foundation.
72de962bdSlukem  * All rights reserved.
82de962bdSlukem  *
92de962bdSlukem  * Redistribution and use in source and binary forms, with or without
102de962bdSlukem  * modification, are permitted only as authorized by the OpenLDAP
112de962bdSlukem  * Public License.
122de962bdSlukem  *
132de962bdSlukem  * A copy of this license is available in the file LICENSE in the
142de962bdSlukem  * top-level directory of the distribution or, alternatively, at
152de962bdSlukem  * <http://www.OpenLDAP.org/license.html>.
162de962bdSlukem  */
172de962bdSlukem /* ACKNOWLEDGEMENT:
182de962bdSlukem  * This work was initially developed by Pierangelo Masarati for
192de962bdSlukem  * inclusion in OpenLDAP Software.
202de962bdSlukem  */
212de962bdSlukem 
222de962bdSlukem #include <portable.h>
232de962bdSlukem 
242de962bdSlukem #include "rewrite-int.h"
252de962bdSlukem 
262de962bdSlukem /*
272de962bdSlukem  * Compares two struct rewrite_context based on the name;
282de962bdSlukem  * used by avl stuff
292de962bdSlukem  */
302de962bdSlukem static int
rewrite_context_cmp(const void * c1,const void * c2)312de962bdSlukem rewrite_context_cmp(
322de962bdSlukem 		const void *c1,
332de962bdSlukem 		const void *c2
342de962bdSlukem )
352de962bdSlukem {
362de962bdSlukem 	const struct rewrite_context *lc1, *lc2;
372de962bdSlukem 
382de962bdSlukem 	lc1 = (const struct rewrite_context *)c1;
392de962bdSlukem 	lc2 = (const struct rewrite_context *)c2;
402de962bdSlukem 
412de962bdSlukem 	assert( c1 != NULL );
422de962bdSlukem 	assert( c2 != NULL );
432de962bdSlukem 	assert( lc1->lc_name != NULL );
442de962bdSlukem 	assert( lc2->lc_name != NULL );
452de962bdSlukem 
462de962bdSlukem 	return strcasecmp( lc1->lc_name, lc2->lc_name );
472de962bdSlukem }
482de962bdSlukem 
492de962bdSlukem /*
502de962bdSlukem  * Returns -1 in case a duplicate struct rewrite_context
512de962bdSlukem  * has been inserted; used by avl stuff
522de962bdSlukem  */
532de962bdSlukem static int
rewrite_context_dup(void * c1,void * c2)542de962bdSlukem rewrite_context_dup(
552de962bdSlukem 		void *c1,
562de962bdSlukem 		void *c2
572de962bdSlukem 		)
582de962bdSlukem {
592de962bdSlukem 	struct rewrite_context *lc1, *lc2;
602de962bdSlukem 
612de962bdSlukem 	lc1 = (struct rewrite_context *)c1;
622de962bdSlukem 	lc2 = (struct rewrite_context *)c2;
632de962bdSlukem 
642de962bdSlukem 	assert( c1 != NULL );
652de962bdSlukem 	assert( c2 != NULL );
662de962bdSlukem 	assert( lc1->lc_name != NULL );
672de962bdSlukem 	assert( lc2->lc_name != NULL );
682de962bdSlukem 
692de962bdSlukem 	return( strcasecmp( lc1->lc_name, lc2->lc_name) == 0 ? -1 : 0 );
702de962bdSlukem }
712de962bdSlukem 
722de962bdSlukem /*
732de962bdSlukem  * Finds the context named rewriteContext in the context tree
742de962bdSlukem  */
752de962bdSlukem struct rewrite_context *
rewrite_context_find(struct rewrite_info * info,const char * rewriteContext)762de962bdSlukem rewrite_context_find(
772de962bdSlukem 		struct rewrite_info *info,
782de962bdSlukem 		const char *rewriteContext
792de962bdSlukem )
802de962bdSlukem {
812de962bdSlukem 	struct rewrite_context *context, c;
822de962bdSlukem 
832de962bdSlukem 	assert( info != NULL );
842de962bdSlukem 	assert( rewriteContext != NULL );
852de962bdSlukem 
862de962bdSlukem 	/*
872de962bdSlukem 	 * Fetches the required rewrite context
882de962bdSlukem 	 */
892de962bdSlukem 	c.lc_name = (char *)rewriteContext;
90*549b59edSchristos 	context = (struct rewrite_context *)ldap_avl_find( info->li_context,
912de962bdSlukem 			(caddr_t)&c, rewrite_context_cmp );
922de962bdSlukem 	if ( context == NULL ) {
932de962bdSlukem 		return NULL;
942de962bdSlukem 	}
952de962bdSlukem 
962de962bdSlukem 	/*
972de962bdSlukem 	 * De-aliases the context if required
982de962bdSlukem 	 */
992de962bdSlukem 	if ( context->lc_alias ) {
1002de962bdSlukem 		return context->lc_alias;
1012de962bdSlukem 	}
1022de962bdSlukem 
1032de962bdSlukem 	return context;
1042de962bdSlukem }
1052de962bdSlukem 
1062de962bdSlukem /*
1072de962bdSlukem  * Creates a new context called rewriteContext and stores in into the tree
1082de962bdSlukem  */
1092de962bdSlukem struct rewrite_context *
rewrite_context_create(struct rewrite_info * info,const char * rewriteContext)1102de962bdSlukem rewrite_context_create(
1112de962bdSlukem 		struct rewrite_info *info,
1122de962bdSlukem 		const char *rewriteContext
1132de962bdSlukem )
1142de962bdSlukem {
1152de962bdSlukem 	struct rewrite_context *context;
1162de962bdSlukem 	int rc;
1172de962bdSlukem 
1182de962bdSlukem 	assert( info != NULL );
1192de962bdSlukem 	assert( rewriteContext != NULL );
1202de962bdSlukem 
1212de962bdSlukem 	context = calloc( sizeof( struct rewrite_context ), 1 );
1222de962bdSlukem 	if ( context == NULL ) {
1232de962bdSlukem 		return NULL;
1242de962bdSlukem 	}
1252de962bdSlukem 
1262de962bdSlukem 	/*
1272de962bdSlukem 	 * Context name
1282de962bdSlukem 	 */
1292de962bdSlukem 	context->lc_name = strdup( rewriteContext );
1302de962bdSlukem 	if ( context->lc_name == NULL ) {
1312de962bdSlukem 		free( context );
1322de962bdSlukem 		return NULL;
1332de962bdSlukem 	}
1342de962bdSlukem 
1352de962bdSlukem 	/*
1362de962bdSlukem 	 * The first, empty rule
1372de962bdSlukem 	 */
1382de962bdSlukem 	context->lc_rule = calloc( sizeof( struct rewrite_rule ), 1 );
1392de962bdSlukem 	if ( context->lc_rule == NULL ) {
1402de962bdSlukem 		free( context->lc_name );
1412de962bdSlukem 		free( context );
1422de962bdSlukem 		return NULL;
1432de962bdSlukem 	}
1442de962bdSlukem 	memset( context->lc_rule, 0, sizeof( struct rewrite_rule ) );
1452de962bdSlukem 
1462de962bdSlukem 	/*
1472de962bdSlukem 	 * Add context to tree
1482de962bdSlukem 	 */
149*549b59edSchristos 	rc = ldap_avl_insert( &info->li_context, (caddr_t)context,
1502de962bdSlukem 			rewrite_context_cmp, rewrite_context_dup );
1512de962bdSlukem 	if ( rc == -1 ) {
1522de962bdSlukem 		free( context->lc_rule );
1532de962bdSlukem 		free( context->lc_name );
1542de962bdSlukem 		free( context );
1552de962bdSlukem 		return NULL;
1562de962bdSlukem 	}
1572de962bdSlukem 
1582de962bdSlukem 	return context;
1592de962bdSlukem }
1602de962bdSlukem 
1612de962bdSlukem /*
1622de962bdSlukem  * Finds the next rule according to a goto action statement,
1632de962bdSlukem  * or null in case of error.
1642de962bdSlukem  * Helper for rewrite_context_apply.
1652de962bdSlukem  */
1662de962bdSlukem static struct rewrite_rule *
rewrite_action_goto(struct rewrite_action * action,struct rewrite_rule * rule)1672de962bdSlukem rewrite_action_goto(
1682de962bdSlukem 		struct rewrite_action *action,
1692de962bdSlukem 		struct rewrite_rule *rule
1702de962bdSlukem )
1712de962bdSlukem {
1722de962bdSlukem 	int n;
1732de962bdSlukem 
1742de962bdSlukem 	assert( action != NULL );
1752de962bdSlukem 	assert( action->la_args != NULL );
1762de962bdSlukem 	assert( rule != NULL );
1772de962bdSlukem 
1782de962bdSlukem 	n = ((int *)action->la_args)[ 0 ];
1792de962bdSlukem 
1802de962bdSlukem 	if ( n > 0 ) {
1812de962bdSlukem 		for ( ; n > 1 && rule != NULL ; n-- ) {
1822de962bdSlukem 			rule = rule->lr_next;
1832de962bdSlukem 		}
1842de962bdSlukem 	} else if ( n <= 0 ) {
1852de962bdSlukem 		for ( ; n < 1 && rule != NULL ; n++ ) {
1862de962bdSlukem 			rule = rule->lr_prev;
1872de962bdSlukem 		}
1882de962bdSlukem 	}
1892de962bdSlukem 
1902de962bdSlukem 	return rule;
1912de962bdSlukem }
1922de962bdSlukem 
1932de962bdSlukem /*
1942de962bdSlukem  * Rewrites string according to context; may return:
1952de962bdSlukem  *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
1962de962bdSlukem  *      STOP:   fine, rule matched; stop processing following rules
1972de962bdSlukem  *      UNWILL: rule matched; force 'unwilling to perform'
1982de962bdSlukem  */
1992de962bdSlukem int
rewrite_context_apply(struct rewrite_info * info,struct rewrite_op * op,struct rewrite_context * context,const char * string,char ** result)2002de962bdSlukem rewrite_context_apply(
2012de962bdSlukem 		struct rewrite_info *info,
2022de962bdSlukem 		struct rewrite_op *op,
2032de962bdSlukem 		struct rewrite_context *context,
2042de962bdSlukem 		const char *string,
2052de962bdSlukem 		char **result
2062de962bdSlukem )
2072de962bdSlukem {
2082de962bdSlukem 	struct rewrite_rule *rule;
2092de962bdSlukem 	char *s, *res = NULL;
2102de962bdSlukem 	int return_code = REWRITE_REGEXEC_OK;
2112de962bdSlukem 
2122de962bdSlukem 	assert( info != NULL );
2132de962bdSlukem 	assert( op != NULL );
2142de962bdSlukem 	assert( context != NULL );
2152de962bdSlukem 	assert( context->lc_rule != NULL );
2162de962bdSlukem 	assert( string != NULL );
2172de962bdSlukem 	assert( result != NULL );
2182de962bdSlukem 
2192de962bdSlukem 	op->lo_depth++;
2202de962bdSlukem 
2212de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
2222de962bdSlukem 			" [depth=%d] string='%s'\n",
223*549b59edSchristos 			op->lo_depth, string );
224d11b170bStron 	assert( op->lo_depth > 0 );
2252de962bdSlukem 
2262de962bdSlukem 	s = (char *)string;
2272de962bdSlukem 
2282de962bdSlukem 	for ( rule = context->lc_rule->lr_next;
2292de962bdSlukem 			rule != NULL && op->lo_num_passes < info->li_max_passes;
2302de962bdSlukem 			rule = rule->lr_next, op->lo_num_passes++ ) {
2312de962bdSlukem 		int rc;
2322de962bdSlukem 
2332de962bdSlukem 		/*
2342de962bdSlukem 		 * Apply a single rule
2352de962bdSlukem 		 */
2362de962bdSlukem 		rc = rewrite_rule_apply( info, op, rule, s, &res );
2372de962bdSlukem 
2382de962bdSlukem 		/*
2392de962bdSlukem 		 * A rule may return:
2402de962bdSlukem 		 * 	OK 		with result != NULL if matched
2412de962bdSlukem 		 * 	ERR		if anything was wrong
2422de962bdSlukem 		 * 	UNWILLING	if the server should drop the request
2432de962bdSlukem 		 * the latter case in honored immediately;
2442de962bdSlukem 		 * the other two may require some special actions to take
2452de962bdSlukem 		 * place.
2462de962bdSlukem 		 */
2472de962bdSlukem 		switch ( rc ) {
2482de962bdSlukem 
2492de962bdSlukem 		case REWRITE_REGEXEC_ERR:
2502de962bdSlukem 			Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply"
251*549b59edSchristos 					" error ...\n" );
2522de962bdSlukem 
2532de962bdSlukem 			/*
2542de962bdSlukem 			 * Checks for special actions to be taken
2552de962bdSlukem 			 * in case of error ...
2562de962bdSlukem 			 */
2572de962bdSlukem 			if ( rule->lr_action != NULL ) {
2582de962bdSlukem 				struct rewrite_action *action;
2592de962bdSlukem 				int do_continue = 0;
2602de962bdSlukem 
2612de962bdSlukem 				for ( action = rule->lr_action;
2622de962bdSlukem 						action != NULL;
2632de962bdSlukem 						action = action->la_next ) {
2642de962bdSlukem 					switch ( action->la_type ) {
2652de962bdSlukem 
2662de962bdSlukem 					/*
2672de962bdSlukem 					 * This action takes precedence
2682de962bdSlukem 					 * over the others in case of failure
2692de962bdSlukem 					 */
2702de962bdSlukem 					case REWRITE_ACTION_IGNORE_ERR:
2712de962bdSlukem 						Debug( LDAP_DEBUG_ANY,
2722de962bdSlukem 					"==> rewrite_context_apply"
273*549b59edSchristos 					" ignoring error ...\n" );
2742de962bdSlukem 						do_continue = 1;
2752de962bdSlukem 						break;
2762de962bdSlukem 
2772de962bdSlukem 					/*
2782de962bdSlukem 					 * Goto is honored only if it comes
2792de962bdSlukem 					 * after ignore error
2802de962bdSlukem 					 */
2812de962bdSlukem 					case REWRITE_ACTION_GOTO:
2822de962bdSlukem 						if ( do_continue ) {
2832de962bdSlukem 							rule = rewrite_action_goto( action, rule );
2842de962bdSlukem 							if ( rule == NULL ) {
2852de962bdSlukem 								return_code = REWRITE_REGEXEC_ERR;
2862de962bdSlukem 								goto rc_end_of_context;
2872de962bdSlukem 							}
2882de962bdSlukem 						}
2892de962bdSlukem 						break;
2902de962bdSlukem 
2912de962bdSlukem 					/*
2922de962bdSlukem 					 * Other actions are ignored
2932de962bdSlukem 					 */
2942de962bdSlukem 					default:
2952de962bdSlukem 						break;
2962de962bdSlukem 					}
2972de962bdSlukem 				}
2982de962bdSlukem 
2992de962bdSlukem 				if ( do_continue ) {
3002de962bdSlukem 					if ( rule->lr_next == NULL ) {
3012de962bdSlukem 						res = s;
3022de962bdSlukem 					}
3032de962bdSlukem 					goto rc_continue;
3042de962bdSlukem 				}
3052de962bdSlukem 			}
3062de962bdSlukem 
3072de962bdSlukem 			/*
3082de962bdSlukem 			 * Default behavior is to bail out ...
3092de962bdSlukem 			 */
3102de962bdSlukem 			return_code = REWRITE_REGEXEC_ERR;
3112de962bdSlukem 			goto rc_end_of_context;
3122de962bdSlukem 
3132de962bdSlukem 		/*
3142de962bdSlukem 		 * OK means there were no errors or special return codes;
3152de962bdSlukem 		 * if res is defined, it means the rule matched and we
316*549b59edSchristos 		 * got a successful rewriting
3172de962bdSlukem 		 */
3182de962bdSlukem 		case REWRITE_REGEXEC_OK:
3192de962bdSlukem 
3202de962bdSlukem 			/*
3212de962bdSlukem 			 * It matched! Check for actions ...
3222de962bdSlukem 			 */
3232de962bdSlukem 			if ( res != NULL ) {
3242de962bdSlukem 				struct rewrite_action *action;
3252de962bdSlukem 
3262de962bdSlukem 				if ( s != string && s != res ) {
3272de962bdSlukem 					free( s );
3282de962bdSlukem 				}
3292de962bdSlukem 				s = res;
3302de962bdSlukem 
3312de962bdSlukem 				for ( action = rule->lr_action;
3322de962bdSlukem 						action != NULL;
3332de962bdSlukem 						action = action->la_next ) {
3342de962bdSlukem 
3352de962bdSlukem 					switch ( action->la_type ) {
3362de962bdSlukem 
3372de962bdSlukem 					/*
3382de962bdSlukem 					 * This ends the rewrite context
3392de962bdSlukem 					 * successfully
3402de962bdSlukem 					 */
3412de962bdSlukem 					case REWRITE_ACTION_STOP:
3422de962bdSlukem 						goto rc_end_of_context;
3432de962bdSlukem 
3442de962bdSlukem 					/*
3452de962bdSlukem 					 * This instructs the server to return
3462de962bdSlukem 					 * an `unwilling to perform' error
3472de962bdSlukem 					 * message
3482de962bdSlukem 					 */
3492de962bdSlukem 					case REWRITE_ACTION_UNWILLING:
3502de962bdSlukem 						return_code = REWRITE_REGEXEC_UNWILLING;
3512de962bdSlukem 						goto rc_end_of_context;
3522de962bdSlukem 
3532de962bdSlukem 					/*
3542de962bdSlukem 					 * This causes the processing to
3552de962bdSlukem 					 * jump n rules back and forth
3562de962bdSlukem 					 */
3572de962bdSlukem 					case REWRITE_ACTION_GOTO:
3582de962bdSlukem 						rule = rewrite_action_goto( action, rule );
3592de962bdSlukem 						if ( rule == NULL ) {
3602de962bdSlukem 							return_code = REWRITE_REGEXEC_ERR;
3612de962bdSlukem 							goto rc_end_of_context;
3622de962bdSlukem 						}
3632de962bdSlukem 						break;
3642de962bdSlukem 
3652de962bdSlukem 					/*
3662de962bdSlukem 					 * This ends the rewrite context
3672de962bdSlukem 					 * and returns a user-defined
3682de962bdSlukem 					 * error code
3692de962bdSlukem 					 */
3702de962bdSlukem 					case REWRITE_ACTION_USER:
3712de962bdSlukem 						return_code = ((int *)action->la_args)[ 0 ];
3722de962bdSlukem 						goto rc_end_of_context;
3732de962bdSlukem 
3742de962bdSlukem 					default:
3752de962bdSlukem 						/* ... */
3762de962bdSlukem 						break;
3772de962bdSlukem 					}
3782de962bdSlukem 				}
3792de962bdSlukem 
3802de962bdSlukem 			/*
3812de962bdSlukem 			 * If result was OK and string didn't match,
3822de962bdSlukem 			 * in case of last rule we need to set the
3832de962bdSlukem 			 * result back to the string
3842de962bdSlukem 			 */
3852de962bdSlukem 			} else if ( rule->lr_next == NULL ) {
3862de962bdSlukem 				res = s;
3872de962bdSlukem 			}
3882de962bdSlukem 
3892de962bdSlukem 			break;
3902de962bdSlukem 
3912de962bdSlukem 		/*
3922de962bdSlukem 		 * A STOP has propagated ...
3932de962bdSlukem 		 */
3942de962bdSlukem 		case REWRITE_REGEXEC_STOP:
3952de962bdSlukem 			goto rc_end_of_context;
3962de962bdSlukem 
3972de962bdSlukem 		/*
3982de962bdSlukem 		 * This will instruct the server to return
3992de962bdSlukem 		 * an `unwilling to perform' error message
4002de962bdSlukem 		 */
4012de962bdSlukem 		case REWRITE_REGEXEC_UNWILLING:
4022de962bdSlukem 			return_code = REWRITE_REGEXEC_UNWILLING;
4032de962bdSlukem 			goto rc_end_of_context;
4042de962bdSlukem 
4052de962bdSlukem 		/*
4062de962bdSlukem 		 * A user-defined error code has propagated ...
4072de962bdSlukem 		 */
4082de962bdSlukem 		default:
4092de962bdSlukem 			assert( rc >= REWRITE_REGEXEC_USER );
4102de962bdSlukem 			goto rc_end_of_context;
4112de962bdSlukem 
4122de962bdSlukem 		}
4132de962bdSlukem 
4142de962bdSlukem rc_continue:;	/* sent here by actions that require to continue */
4152de962bdSlukem 
4162de962bdSlukem 	}
4172de962bdSlukem 
4182de962bdSlukem rc_end_of_context:;
4192de962bdSlukem 	*result = res;
4202de962bdSlukem 
4212de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
4222de962bdSlukem 			" [depth=%d] res={%d,'%s'}\n",
4232de962bdSlukem 			op->lo_depth, return_code, ( res ? res : "NULL" ) );
4242de962bdSlukem 
4252de962bdSlukem 	assert( op->lo_depth > 0 );
4262de962bdSlukem 	op->lo_depth--;
4272de962bdSlukem 
4282de962bdSlukem 	return return_code;
4292de962bdSlukem }
4302de962bdSlukem 
4312de962bdSlukem void
rewrite_context_free(void * tmp)4322de962bdSlukem rewrite_context_free(
4332de962bdSlukem 		void *tmp
4342de962bdSlukem )
4352de962bdSlukem {
4362de962bdSlukem 	struct rewrite_context *context = (struct rewrite_context *)tmp;
4372de962bdSlukem 
4382de962bdSlukem 	assert( tmp != NULL );
4392de962bdSlukem 
4402de962bdSlukem 	rewrite_context_destroy( &context );
4412de962bdSlukem }
4422de962bdSlukem 
4432de962bdSlukem int
rewrite_context_destroy(struct rewrite_context ** pcontext)4442de962bdSlukem rewrite_context_destroy(
4452de962bdSlukem 		struct rewrite_context **pcontext
4462de962bdSlukem )
4472de962bdSlukem {
4482de962bdSlukem 	struct rewrite_context *context;
4492de962bdSlukem 	struct rewrite_rule *r;
4502de962bdSlukem 
4512de962bdSlukem 	assert( pcontext != NULL );
4522de962bdSlukem 	assert( *pcontext != NULL );
4532de962bdSlukem 
4542de962bdSlukem 	context = *pcontext;
4552de962bdSlukem 
4562de962bdSlukem 	assert( context->lc_rule != NULL );
4572de962bdSlukem 
4582de962bdSlukem 	for ( r = context->lc_rule->lr_next; r; ) {
4592de962bdSlukem 		struct rewrite_rule *cr = r;
4602de962bdSlukem 
4612de962bdSlukem 		r = r->lr_next;
4622de962bdSlukem 		rewrite_rule_destroy( &cr );
4632de962bdSlukem 	}
4642de962bdSlukem 
4652de962bdSlukem 	free( context->lc_rule );
4662de962bdSlukem 	context->lc_rule = NULL;
4672de962bdSlukem 
4682de962bdSlukem 	assert( context->lc_name != NULL );
4692de962bdSlukem 	free( context->lc_name );
4702de962bdSlukem 	context->lc_name = NULL;
4712de962bdSlukem 
4722de962bdSlukem 	free( context );
4732de962bdSlukem 	*pcontext = NULL;
4742de962bdSlukem 
4752de962bdSlukem 	return 0;
4762de962bdSlukem }
477