Home | History | Annotate | Line # | Download | only in librewrite
      1 /*	$NetBSD: map.c,v 1.4 2025/09/05 21:16:23 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 2000-2024 The OpenLDAP Foundation.
      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 /* ACKNOWLEDGEMENT:
     18  * This work was initially developed by Pierangelo Masarati for
     19  * inclusion in OpenLDAP Software.
     20  */
     21 
     22 #include <portable.h>
     23 
     24 #include <stdio.h>
     25 
     26 #ifdef HAVE_PWD_H
     27 #include <pwd.h>
     28 #endif
     29 
     30 #include "rewrite-int.h"
     31 #include "rewrite-map.h"
     32 
     33 static int num_mappers;
     34 static const rewrite_mapper **mappers;
     35 #define	MAPPER_ALLOC	8
     36 
     37 struct rewrite_map *
     38 rewrite_map_parse(
     39 		struct rewrite_info *info,
     40 		const char *string,
     41 		const char **currpos
     42 )
     43 {
     44 	struct rewrite_map *map = NULL;
     45 	struct rewrite_subst *subst = NULL;
     46 	char *s, *begin = NULL, *end;
     47 	const char *p;
     48 	int l, cnt, mtx = 0, rc = 0;
     49 
     50 	assert( info != NULL );
     51 	assert( string != NULL );
     52 	assert( currpos != NULL );
     53 
     54 	*currpos = NULL;
     55 
     56 	/*
     57 	 * Go to the end of the map invocation (the right closing brace)
     58 	 */
     59 	for ( p = string, cnt = 1; p[ 0 ] != '\0' && cnt > 0; p++ ) {
     60 		if ( IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) {
     61 			/*
     62 			 * '%' marks the beginning of a new map
     63 			 */
     64 			if ( p[ 1 ] == '{' ) {
     65 				cnt++;
     66 			/*
     67 			 * '%' followed by a digit may mark the beginning
     68 			 * of an old map
     69 			 */
     70 			} else if ( isdigit( (unsigned char) p[ 1 ] ) && p[ 2 ] == '{' ) {
     71 				cnt++;
     72 				p++;
     73 			}
     74 
     75 			if ( p[ 1 ] != '\0' ) {
     76 				p++;
     77 			}
     78 
     79 		} else if ( p[ 0 ] == '}' ) {
     80 			cnt--;
     81 		}
     82 	}
     83 	if ( cnt != 0 ) {
     84 		return NULL;
     85 	}
     86 	*currpos = p;
     87 
     88 	/*
     89 	 * Copy the map invocation
     90 	 */
     91 	l = p - string - 1;
     92 	s = calloc( sizeof( char ), l + 1 );
     93 	if ( s == NULL ) {
     94 		return NULL;
     95 	}
     96 	AC_MEMCPY( s, string, l );
     97 	s[ l ] = 0;
     98 
     99 	/*
    100 	 * Isolate the map name (except for variable deref)
    101 	 */
    102 	switch ( s[ 0 ] ) {
    103 	case REWRITE_OPERATOR_VARIABLE_GET:
    104 	case REWRITE_OPERATOR_PARAM_GET:
    105 		break;
    106 
    107 	default:
    108 		begin = strchr( s, '(' );
    109 		if ( begin == NULL ) {
    110 			rc = -1;
    111 			goto cleanup;
    112 		}
    113 		begin[ 0 ] = '\0';
    114 		begin++;
    115 		break;
    116 	}
    117 
    118 	/*
    119 	 * Check for special map types
    120 	 */
    121 	p = s;
    122 	switch ( p[ 0 ] ) {
    123 	case REWRITE_OPERATOR_SUBCONTEXT:
    124 	case REWRITE_OPERATOR_COMMAND:
    125 	case REWRITE_OPERATOR_VARIABLE_SET:
    126 	case REWRITE_OPERATOR_VARIABLE_GET:
    127 	case REWRITE_OPERATOR_PARAM_GET:
    128 		p++;
    129 		break;
    130 	}
    131 
    132 	/*
    133 	 * Variable set and get may be repeated to indicate session-wide
    134 	 * instead of operation-wide variables
    135 	 */
    136 	switch ( p[ 0 ] ) {
    137         case REWRITE_OPERATOR_VARIABLE_SET:
    138 	case REWRITE_OPERATOR_VARIABLE_GET:
    139 		p++;
    140 		break;
    141 	}
    142 
    143 	/*
    144 	 * Variable get token can be appended to variable set to mean store
    145 	 * AND rewrite
    146 	 */
    147 	if ( p[ 0 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
    148 		p++;
    149 	}
    150 
    151 	/*
    152 	 * Check the syntax of the variable name
    153 	 */
    154 	if ( !isalpha( (unsigned char) p[ 0 ] ) ) {
    155 		rc = -1;
    156 		goto cleanup;
    157 	}
    158 	for ( p++; p[ 0 ] != '\0'; p++ ) {
    159 		if ( !isalnum( (unsigned char) p[ 0 ] ) ) {
    160 			rc = -1;
    161 			goto cleanup;
    162 		}
    163 	}
    164 
    165 	/*
    166 	 * Isolate the argument of the map (except for variable deref)
    167 	 */
    168 	switch ( s[ 0 ] ) {
    169 	case REWRITE_OPERATOR_VARIABLE_GET:
    170 	case REWRITE_OPERATOR_PARAM_GET:
    171 		break;
    172 
    173 	default:
    174 		end = strrchr( begin, ')' );
    175 		if ( end == NULL ) {
    176 			rc = -1;
    177 			goto cleanup;
    178 		}
    179 		end[ 0 ] = '\0';
    180 
    181 		/*
    182 	 	 * Compile the substitution pattern of the map argument
    183 	 	 */
    184 		subst = rewrite_subst_compile( info, begin );
    185 		if ( subst == NULL ) {
    186 			rc = -1;
    187 			goto cleanup;
    188 		}
    189 		break;
    190 	}
    191 
    192 	/*
    193 	 * Create the map
    194 	 */
    195 	map = calloc( sizeof( struct rewrite_map ), 1 );
    196 	if ( map == NULL ) {
    197 		rc = -1;
    198 		goto cleanup;
    199 	}
    200 	memset( map, 0, sizeof( struct rewrite_map ) );
    201 
    202 #ifdef USE_REWRITE_LDAP_PVT_THREADS
    203         if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) {
    204 		rc = -1;
    205 		goto cleanup;
    206 	}
    207 	++mtx;
    208 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
    209 
    210 	/*
    211 	 * No subst for variable deref
    212 	 */
    213 	switch ( s[ 0 ] ) {
    214 	case REWRITE_OPERATOR_VARIABLE_GET:
    215 	case REWRITE_OPERATOR_PARAM_GET:
    216 		break;
    217 
    218 	default:
    219 		map->lm_subst = subst;
    220 		break;
    221 	}
    222 
    223 	/*
    224 	 * Parses special map types
    225 	 */
    226 	switch ( s[ 0 ] ) {
    227 
    228 	/*
    229 	 * Subcontext
    230 	 */
    231 	case REWRITE_OPERATOR_SUBCONTEXT:		/* '>' */
    232 
    233 		/*
    234 		 * Fetch the rewrite context
    235 		 * it MUST have been defined previously
    236 		 */
    237 		map->lm_type = REWRITE_MAP_SUBCONTEXT;
    238 		map->lm_name = strdup( s + 1 );
    239 		if ( map->lm_name == NULL ) {
    240 			rc = -1;
    241 			goto cleanup;
    242 		}
    243 		map->lm_data = rewrite_context_find( info, s + 1 );
    244 		if ( map->lm_data == NULL ) {
    245 			rc = -1;
    246 			goto cleanup;
    247 		}
    248 		break;
    249 
    250 	/*
    251 	 * External command (not implemented yet)
    252 	 */
    253 	case REWRITE_OPERATOR_COMMAND:		/* '|' */
    254 		rc = -1;
    255 		goto cleanup;
    256 
    257 	/*
    258 	 * Variable set
    259 	 */
    260 	case REWRITE_OPERATOR_VARIABLE_SET:	/* '&' */
    261 		if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_SET ) {
    262 			if ( s[ 2 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
    263 				map->lm_type = REWRITE_MAP_SETW_SESN_VAR;
    264 				map->lm_name = strdup( s + 3 );
    265 			} else {
    266 				map->lm_type = REWRITE_MAP_SET_SESN_VAR;
    267 				map->lm_name = strdup( s + 2 );
    268 			}
    269 		} else {
    270 			if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
    271 				map->lm_type = REWRITE_MAP_SETW_OP_VAR;
    272 				map->lm_name = strdup( s + 2 );
    273 			} else {
    274 				map->lm_type = REWRITE_MAP_SET_OP_VAR;
    275 				map->lm_name = strdup( s + 1 );
    276 			}
    277 		}
    278 		if ( map->lm_name == NULL ) {
    279 			rc = -1;
    280 			goto cleanup;
    281 		}
    282 		break;
    283 
    284 	/*
    285 	 * Variable dereference
    286 	 */
    287 	case REWRITE_OPERATOR_VARIABLE_GET:	/* '*' */
    288 		if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
    289 			map->lm_type = REWRITE_MAP_GET_SESN_VAR;
    290 			map->lm_name = strdup( s + 2 );
    291 		} else {
    292 			map->lm_type = REWRITE_MAP_GET_OP_VAR;
    293 			map->lm_name = strdup( s + 1 );
    294 		}
    295 		if ( map->lm_name == NULL ) {
    296 			rc = -1;
    297 			goto cleanup;
    298 		}
    299 		break;
    300 
    301 	/*
    302 	 * Parameter
    303 	 */
    304 	case REWRITE_OPERATOR_PARAM_GET:		/* '$' */
    305 		map->lm_type = REWRITE_MAP_GET_PARAM;
    306 		map->lm_name = strdup( s + 1 );
    307 		if ( map->lm_name == NULL ) {
    308 			rc = -1;
    309 			goto cleanup;
    310 		}
    311 		break;
    312 
    313 	/*
    314 	 * Built-in map
    315 	 */
    316 	default:
    317 		map->lm_type = REWRITE_MAP_BUILTIN;
    318 		map->lm_name = strdup( s );
    319 		if ( map->lm_name == NULL ) {
    320 			rc = -1;
    321 			goto cleanup;
    322 		}
    323 		map->lm_data = rewrite_builtin_map_find( info, s );
    324 		if ( map->lm_data == NULL ) {
    325 			rc = -1;
    326 			goto cleanup;
    327 		}
    328 		break;
    329 
    330 	}
    331 
    332 cleanup:
    333 	free( s );
    334 	if ( rc ) {
    335 		if ( subst != NULL ) {
    336 			free( subst );
    337 		}
    338 		if ( map ) {
    339 #ifdef USE_REWRITE_LDAP_PVT_THREADS
    340 		        if ( mtx ) {
    341 				ldap_pvt_thread_mutex_destroy( &map->lm_mutex );
    342 			}
    343 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
    344 
    345 			if ( map->lm_name ) {
    346 				free( map->lm_name );
    347 				map->lm_name = NULL;
    348 			}
    349 			free( map );
    350 			map = NULL;
    351 		}
    352 	}
    353 
    354 	return map;
    355 }
    356 
    357 /*
    358  * Applies the new map type
    359  */
    360 int
    361 rewrite_map_apply(
    362 		struct rewrite_info *info,
    363 		struct rewrite_op *op,
    364 		struct rewrite_map *map,
    365 		struct berval *key,
    366 		struct berval *val
    367 )
    368 {
    369 	int rc = REWRITE_SUCCESS;
    370 
    371 	assert( info != NULL );
    372 	assert( op != NULL );
    373 	assert( map != NULL );
    374 	assert( key != NULL );
    375 	assert( val != NULL );
    376 
    377 	val->bv_val = NULL;
    378 	val->bv_len = 0;
    379 
    380 	switch ( map->lm_type ) {
    381 	case REWRITE_MAP_SUBCONTEXT:
    382 		rc = rewrite_context_apply( info, op,
    383 				( struct rewrite_context * )map->lm_data,
    384 				key->bv_val, &val->bv_val );
    385 		if ( val->bv_val != NULL ) {
    386 			if ( val->bv_val == key->bv_val ) {
    387 				val->bv_len = key->bv_len;
    388 				key->bv_val = NULL;
    389 			} else {
    390 				val->bv_len = strlen( val->bv_val );
    391 			}
    392 		}
    393 		break;
    394 
    395 	case REWRITE_MAP_SET_OP_VAR:
    396 	case REWRITE_MAP_SETW_OP_VAR:
    397 		rc = rewrite_var_set( &op->lo_vars, map->lm_name,
    398 				key->bv_val, 1 )
    399 			? REWRITE_SUCCESS : REWRITE_ERR;
    400 		if ( rc == REWRITE_SUCCESS ) {
    401 			if ( map->lm_type == REWRITE_MAP_SET_OP_VAR ) {
    402 				val->bv_val = strdup( "" );
    403 			} else {
    404 				val->bv_val = strdup( key->bv_val );
    405 				val->bv_len = key->bv_len;
    406 			}
    407 			if ( val->bv_val == NULL ) {
    408 				rc = REWRITE_ERR;
    409 			}
    410 		}
    411 		break;
    412 
    413 	case REWRITE_MAP_GET_OP_VAR: {
    414 		struct rewrite_var *var;
    415 
    416 		var = rewrite_var_find( op->lo_vars, map->lm_name );
    417 		if ( var == NULL ) {
    418 			rc = REWRITE_ERR;
    419 		} else {
    420 			val->bv_val = strdup( var->lv_value.bv_val );
    421 			val->bv_len = var->lv_value.bv_len;
    422 			if ( val->bv_val == NULL ) {
    423 				rc = REWRITE_ERR;
    424 			}
    425 		}
    426 		break;
    427 	}
    428 
    429 	case REWRITE_MAP_SET_SESN_VAR:
    430 	case REWRITE_MAP_SETW_SESN_VAR:
    431 		if ( op->lo_cookie == NULL ) {
    432 			rc = REWRITE_ERR;
    433 			break;
    434 		}
    435 		rc = rewrite_session_var_set( info, op->lo_cookie,
    436 				map->lm_name, key->bv_val );
    437 		if ( rc == REWRITE_SUCCESS ) {
    438 			if ( map->lm_type == REWRITE_MAP_SET_SESN_VAR ) {
    439 				val->bv_val = strdup( "" );
    440 			} else {
    441 				val->bv_val = strdup( key->bv_val );
    442 				val->bv_len = key->bv_len;
    443 			}
    444 			if ( val->bv_val == NULL ) {
    445 				rc = REWRITE_ERR;
    446 			}
    447 		}
    448 		break;
    449 
    450 	case REWRITE_MAP_GET_SESN_VAR:
    451 		rc = rewrite_session_var_get( info, op->lo_cookie,
    452 				map->lm_name, val );
    453 		break;
    454 
    455 	case REWRITE_MAP_GET_PARAM:
    456 		rc = rewrite_param_get( info, map->lm_name, val );
    457 		break;
    458 
    459 	case REWRITE_MAP_BUILTIN: {
    460 		struct rewrite_builtin_map *bmap = map->lm_data;
    461 
    462 		if ( bmap->lb_mapper && bmap->lb_mapper->rm_apply )
    463 			rc = bmap->lb_mapper->rm_apply( bmap->lb_private, key->bv_val,
    464 				val );
    465 		else
    466 			rc = REWRITE_ERR;
    467 		break;
    468 	}
    469 
    470 	default:
    471 		rc = REWRITE_ERR;
    472 		break;
    473 	}
    474 
    475 	return rc;
    476 }
    477 
    478 void
    479 rewrite_builtin_map_free(
    480 		void *tmp
    481 )
    482 {
    483 	struct rewrite_builtin_map *map = ( struct rewrite_builtin_map * )tmp;
    484 
    485 	assert( map != NULL );
    486 
    487 	if ( map->lb_mapper && map->lb_mapper->rm_destroy )
    488 		map->lb_mapper->rm_destroy( map->lb_private );
    489 
    490 	free( map->lb_name );
    491 	free( map );
    492 }
    493 
    494 int
    495 rewrite_map_destroy(
    496 		struct rewrite_map **pmap
    497 )
    498 {
    499 	struct rewrite_map *map;
    500 
    501 	assert( pmap != NULL );
    502 	assert( *pmap != NULL );
    503 
    504 	map = *pmap;
    505 
    506 #ifdef USE_REWRITE_LDAP_PVT_THREADS
    507 	ldap_pvt_thread_mutex_lock( &map->lm_mutex );
    508 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
    509 
    510 	if ( map->lm_name ) {
    511 		free( map->lm_name );
    512 		map->lm_name = NULL;
    513 	}
    514 
    515 	if ( map->lm_subst ) {
    516 		rewrite_subst_destroy( &map->lm_subst );
    517 	}
    518 
    519 #ifdef USE_REWRITE_LDAP_PVT_THREADS
    520 	ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
    521 	ldap_pvt_thread_mutex_destroy( &map->lm_mutex );
    522 #endif /* USE_REWRITE_LDAP_PVT_THREADS */
    523 
    524 	free( map );
    525 	*pmap = NULL;
    526 
    527 	return 0;
    528 }
    529 
    530 /* ldapmap.c */
    531 extern const rewrite_mapper rewrite_ldap_mapper;
    532 
    533 /* escapemap.c */
    534 extern const rewrite_mapper rewrite_escape_mapper;
    535 
    536 const rewrite_mapper *
    537 rewrite_mapper_find(
    538 	const char *name
    539 )
    540 {
    541 	int i;
    542 
    543 	if ( !strcasecmp( name, "ldap" ))
    544 		return &rewrite_ldap_mapper;
    545 
    546 	if ( !strcasecmp( name, "escape" ))
    547 		return &rewrite_escape_mapper;
    548 
    549 	for (i=0; i<num_mappers; i++)
    550 		if ( !strcasecmp( name, mappers[i]->rm_name ))
    551 			return mappers[i];
    552 	return NULL;
    553 }
    554 
    555 int
    556 rewrite_mapper_register(
    557 	const rewrite_mapper *map
    558 )
    559 {
    560 	if ( num_mappers % MAPPER_ALLOC == 0 ) {
    561 		const rewrite_mapper **mnew;
    562 		mnew = realloc( mappers, (num_mappers + MAPPER_ALLOC) *
    563 			sizeof( rewrite_mapper * ));
    564 		if ( mnew )
    565 			mappers = mnew;
    566 		else
    567 			return -1;
    568 	}
    569 	mappers[num_mappers++] = map;
    570 	return 0;
    571 }
    572 
    573 int
    574 rewrite_mapper_unregister(
    575 	const rewrite_mapper *map
    576 )
    577 {
    578 	int i;
    579 
    580 	for (i = 0; i<num_mappers; i++) {
    581 		if ( mappers[i] == map ) {
    582 			num_mappers--;
    583 			mappers[i] = mappers[num_mappers];
    584 			mappers[num_mappers] = NULL;
    585 			return 0;
    586 		}
    587 	}
    588 	/* not found */
    589 	return -1;
    590 }
    591