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