1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2000-2021 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 "rewrite-int.h"
23 
24 /*
25  * Compiles a substitution pattern
26  */
27 struct rewrite_subst *
rewrite_subst_compile(struct rewrite_info * info,const char * str)28 rewrite_subst_compile(
29 		struct rewrite_info *info,
30 		const char *str
31 )
32 {
33 	size_t subs_len;
34 	struct berval *subs = NULL, *tmps;
35 	struct rewrite_submatch *submatch = NULL, *tmpsm;
36 
37 	struct rewrite_subst *s = NULL;
38 
39 	char *result, *begin, *p;
40 	int nsub = 0, l;
41 
42 	assert( info != NULL );
43 	assert( str != NULL );
44 
45 	result = strdup( str );
46 	if ( result == NULL ) {
47 		return NULL;
48 	}
49 
50 	/*
51 	 * Take care of substitution string
52 	 */
53 	for ( p = begin = result, subs_len = 0; p[ 0 ] != '\0'; p++ ) {
54 
55 		/*
56 		 * Keep only single escapes '%'
57 		 */
58 		if (  !IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) {
59 			continue;
60 		}
61 
62 		if (  IS_REWRITE_SUBMATCH_ESCAPE( p[ 1 ] ) ) {
63 			/* Pull &p[1] over p, including the trailing '\0' */
64 			AC_MEMCPY((char *)p, &p[ 1 ], strlen( p ) );
65 			continue;
66 		}
67 
68 		tmps = ( struct berval * )realloc( subs,
69 				sizeof( struct berval )*( nsub + 1 ) );
70 		if ( tmps == NULL ) {
71 			goto cleanup;
72 		}
73 		subs = tmps;
74 		subs[ nsub ].bv_val = NULL;
75 
76 		tmpsm = ( struct rewrite_submatch * )realloc( submatch,
77 				sizeof( struct rewrite_submatch )*( nsub + 1 ) );
78 		if ( tmpsm == NULL ) {
79 			goto cleanup;
80 		}
81 		submatch = tmpsm;
82 		submatch[ nsub ].ls_map = NULL;
83 
84 		/*
85 		 * I think an `if l > 0' at runtime is better outside than
86 		 * inside a function call ...
87 		 */
88 		l = p - begin;
89 		if ( l > 0 ) {
90 			subs_len += l;
91 			subs[ nsub ].bv_len = l;
92 			subs[ nsub ].bv_val = malloc( l + 1 );
93 			if ( subs[ nsub ].bv_val == NULL ) {
94 				goto cleanup;
95 			}
96 			AC_MEMCPY( subs[ nsub ].bv_val, begin, l );
97 			subs[ nsub ].bv_val[ l ] = '\0';
98 		} else {
99 			subs[ nsub ].bv_val = NULL;
100 			subs[ nsub ].bv_len = 0;
101 		}
102 
103 		/*
104 		 * Substitution pattern
105 		 */
106 		if ( isdigit( (unsigned char) p[ 1 ] ) ) {
107 			int d = p[ 1 ] - '0';
108 
109 			/*
110 			 * Add a new value substitution scheme
111 			 */
112 
113 			submatch[ nsub ].ls_submatch = d;
114 
115 			/*
116 			 * If there is no argument, use default
117 			 * (substitute substring as is)
118 			 */
119 			if ( p[ 2 ] != '{' ) {
120 				submatch[ nsub ].ls_type =
121 					REWRITE_SUBMATCH_ASIS;
122 				submatch[ nsub ].ls_map = NULL;
123 				begin = ++p + 1;
124 
125 			} else {
126 				struct rewrite_map *map;
127 
128 				submatch[ nsub ].ls_type =
129 					REWRITE_SUBMATCH_XMAP;
130 
131 				map = rewrite_xmap_parse( info,
132 						p + 3, (const char **)&begin );
133 				if ( map == NULL ) {
134 					goto cleanup;
135 				}
136 				submatch[ nsub ].ls_map = map;
137 				p = begin - 1;
138 			}
139 
140 		/*
141 		 * Map with args ...
142 		 */
143 		} else if ( p[ 1 ] == '{' ) {
144 			struct rewrite_map *map;
145 
146 			map = rewrite_map_parse( info, p + 2,
147 					(const char **)&begin );
148 			if ( map == NULL ) {
149 				goto cleanup;
150 			}
151 			p = begin - 1;
152 
153 			/*
154 			 * Add a new value substitution scheme
155 			 */
156 			submatch[ nsub ].ls_type =
157 				REWRITE_SUBMATCH_MAP_W_ARG;
158 			submatch[ nsub ].ls_map = map;
159 
160 		/*
161 		 * Escape '%' ...
162 		 */
163 		} else if ( p[ 1 ] == '%' ) {
164 			AC_MEMCPY( &p[ 1 ], &p[ 2 ], strlen( &p[ 1 ] ) );
165 			continue;
166 
167 		} else {
168 			goto cleanup;
169 		}
170 
171 		nsub++;
172 	}
173 
174 	/*
175 	 * Last part of string
176 	 */
177 	tmps = (struct berval * )realloc( subs, sizeof( struct berval )*( nsub + 1 ) );
178 	if ( tmps == NULL ) {
179 		/*
180 		 * XXX need to free the value subst stuff!
181 		 */
182 		free( subs );
183 		goto cleanup;
184 	}
185 	subs = tmps;
186 	l = p - begin;
187 	if ( l > 0 ) {
188 		subs_len += l;
189 		subs[ nsub ].bv_len = l;
190 		subs[ nsub ].bv_val = malloc( l + 1 );
191 		if ( subs[ nsub ].bv_val == NULL ) {
192 			goto cleanup;
193 		}
194 		AC_MEMCPY( subs[ nsub ].bv_val, begin, l );
195 		subs[ nsub ].bv_val[ l ] = '\0';
196 	} else {
197 		subs[ nsub ].bv_val = NULL;
198 		subs[ nsub ].bv_len = 0;
199 	}
200 
201 	s = calloc( sizeof( struct rewrite_subst ), 1 );
202 	if ( s == NULL ) {
203 		goto cleanup;
204 	}
205 
206 	s->lt_subs_len = subs_len;
207 	s->lt_subs = subs;
208 	s->lt_num_submatch = nsub;
209 	s->lt_submatch = submatch;
210 	subs = NULL;
211 	submatch = NULL;
212 
213 cleanup:;
214 	if ( subs ) {
215 		for ( l=0; l<nsub; l++ ) {
216 			free( subs[nsub].bv_val );
217 		}
218 		free( subs );
219 	}
220 	if ( submatch ) {
221 		for ( l=0; l<nsub; l++ ) {
222 			free( submatch[nsub].ls_map );
223 		}
224 		free( submatch );
225 	}
226 	free( result );
227 
228 	return s;
229 }
230 
231 /*
232  * Copies the match referred to by submatch and fetched in string by match.
233  * Helper for rewrite_rule_apply.
234  */
235 static int
submatch_copy(struct rewrite_submatch * submatch,const char * string,const regmatch_t * match,struct berval * val)236 submatch_copy(
237 		struct rewrite_submatch *submatch,
238 		const char *string,
239 		const regmatch_t *match,
240 		struct berval *val
241 )
242 {
243 	int		c, l;
244 	const char	*s;
245 
246 	assert( submatch != NULL );
247 	assert( submatch->ls_type == REWRITE_SUBMATCH_ASIS
248 			|| submatch->ls_type == REWRITE_SUBMATCH_XMAP );
249 	assert( string != NULL );
250 	assert( match != NULL );
251 	assert( val != NULL );
252 	assert( val->bv_val == NULL );
253 
254 	c = submatch->ls_submatch;
255 	s = string + match[ c ].rm_so;
256 	l = match[ c ].rm_eo - match[ c ].rm_so;
257 
258 	val->bv_len = l;
259 	val->bv_val = malloc( l + 1 );
260 	if ( val->bv_val == NULL ) {
261 		return REWRITE_ERR;
262 	}
263 
264 	AC_MEMCPY( val->bv_val, s, l );
265 	val->bv_val[ l ] = '\0';
266 
267 	return REWRITE_SUCCESS;
268 }
269 
270 /*
271  * Substitutes a portion of rewritten string according to substitution
272  * pattern using submatches
273  */
274 int
rewrite_subst_apply(struct rewrite_info * info,struct rewrite_op * op,struct rewrite_subst * subst,const char * string,const regmatch_t * match,struct berval * val)275 rewrite_subst_apply(
276 		struct rewrite_info *info,
277 		struct rewrite_op *op,
278 		struct rewrite_subst *subst,
279 		const char *string,
280 		const regmatch_t *match,
281 		struct berval *val
282 )
283 {
284 	struct berval *submatch = NULL;
285 	char *res = NULL;
286 	int n = 0, l, cl;
287 	int rc = REWRITE_REGEXEC_OK;
288 
289 	assert( info != NULL );
290 	assert( op != NULL );
291 	assert( subst != NULL );
292 	assert( string != NULL );
293 	assert( match != NULL );
294 	assert( val != NULL );
295 
296 	assert( val->bv_val == NULL );
297 
298 	val->bv_val = NULL;
299 	val->bv_len = 0;
300 
301 	/*
302 	 * Prepare room for submatch expansion
303 	 */
304 	if ( subst->lt_num_submatch > 0 ) {
305 		submatch = calloc( sizeof( struct berval ),
306 				subst->lt_num_submatch );
307 		if ( submatch == NULL ) {
308 			return REWRITE_REGEXEC_ERR;
309 		}
310 	}
311 
312 	/*
313 	 * Resolve submatches (simple subst, map expansion and so).
314 	 */
315 	for ( n = 0, l = 0; n < subst->lt_num_submatch; n++ ) {
316 		struct berval	key = { 0, NULL };
317 
318 		submatch[ n ].bv_val = NULL;
319 
320 		/*
321 		 * Get key
322 		 */
323 		switch ( subst->lt_submatch[ n ].ls_type ) {
324 		case REWRITE_SUBMATCH_ASIS:
325 		case REWRITE_SUBMATCH_XMAP:
326 			rc = submatch_copy( &subst->lt_submatch[ n ],
327 					string, match, &key );
328 			if ( rc != REWRITE_SUCCESS ) {
329 				rc = REWRITE_REGEXEC_ERR;
330 				goto cleanup;
331 			}
332 			break;
333 
334 		case REWRITE_SUBMATCH_MAP_W_ARG:
335 			switch ( subst->lt_submatch[ n ].ls_map->lm_type ) {
336 			case REWRITE_MAP_GET_OP_VAR:
337 			case REWRITE_MAP_GET_SESN_VAR:
338 			case REWRITE_MAP_GET_PARAM:
339 				rc = REWRITE_SUCCESS;
340 				break;
341 
342 			default:
343 				rc = rewrite_subst_apply( info, op,
344 					subst->lt_submatch[ n ].ls_map->lm_subst,
345 					string, match, &key);
346 			}
347 
348 			if ( rc != REWRITE_SUCCESS ) {
349 				goto cleanup;
350 			}
351 			break;
352 
353 		default:
354 			Debug( LDAP_DEBUG_ANY, "Not Implemented\n" );
355 			rc = REWRITE_ERR;
356 			break;
357 		}
358 
359 		if ( rc != REWRITE_SUCCESS ) {
360 			rc = REWRITE_REGEXEC_ERR;
361 			goto cleanup;
362 		}
363 
364 		/*
365 		 * Resolve key
366 		 */
367 		switch ( subst->lt_submatch[ n ].ls_type ) {
368 		case REWRITE_SUBMATCH_ASIS:
369 			submatch[ n ] = key;
370 			rc = REWRITE_SUCCESS;
371 			break;
372 
373 		case REWRITE_SUBMATCH_XMAP:
374 			rc = rewrite_xmap_apply( info, op,
375 					subst->lt_submatch[ n ].ls_map,
376 					&key, &submatch[ n ] );
377 			free( key.bv_val );
378 			key.bv_val = NULL;
379 			break;
380 
381 		case REWRITE_SUBMATCH_MAP_W_ARG:
382 			rc = rewrite_map_apply( info, op,
383 					subst->lt_submatch[ n ].ls_map,
384 					&key, &submatch[ n ] );
385 			free( key.bv_val );
386 			key.bv_val = NULL;
387 			break;
388 
389 		default:
390 			/*
391 			 * When implemented, this might return the
392                          * exit status of a rewrite context,
393                          * which may include a stop, or an
394                          * unwilling to perform
395                          */
396 			rc = REWRITE_ERR;
397 			break;
398 		}
399 
400 		if ( rc != REWRITE_SUCCESS ) {
401 			rc = REWRITE_REGEXEC_ERR;
402 			goto cleanup;
403 		}
404 
405 		/*
406                  * Increment the length of the resulting string
407                  */
408 		l += submatch[ n ].bv_len;
409 	}
410 
411 	/*
412          * Alloc result buffer
413          */
414 	l += subst->lt_subs_len;
415 	res = malloc( l + 1 );
416 	if ( res == NULL ) {
417 		rc = REWRITE_REGEXEC_ERR;
418 		goto cleanup;
419 	}
420 
421 	/*
422 	 * Apply submatches (possibly resolved thru maps)
423 	 */
424         for ( n = 0, cl = 0; n < subst->lt_num_submatch; n++ ) {
425 		if ( subst->lt_subs[ n ].bv_val != NULL ) {
426                 	AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
427 					subst->lt_subs[ n ].bv_len );
428 			cl += subst->lt_subs[ n ].bv_len;
429 		}
430 		AC_MEMCPY( res + cl, submatch[ n ].bv_val,
431 				submatch[ n ].bv_len );
432 		cl += submatch[ n ].bv_len;
433 	}
434 	if ( subst->lt_subs[ n ].bv_val != NULL ) {
435 		AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
436 				subst->lt_subs[ n ].bv_len );
437 		cl += subst->lt_subs[ n ].bv_len;
438 	}
439 	res[ cl ] = '\0';
440 
441 	val->bv_val = res;
442 	val->bv_len = l;
443 
444 cleanup:;
445 	if ( submatch ) {
446         	for ( ; --n >= 0; ) {
447 			if ( submatch[ n ].bv_val ) {
448 				free( submatch[ n ].bv_val );
449 			}
450 		}
451 		free( submatch );
452 	}
453 
454 	return rc;
455 }
456 
457 /*
458  * frees data
459  */
460 int
rewrite_subst_destroy(struct rewrite_subst ** psubst)461 rewrite_subst_destroy(
462 		struct rewrite_subst **psubst
463 )
464 {
465 	int			n;
466 	struct rewrite_subst	*subst;
467 
468 	assert( psubst != NULL );
469 	assert( *psubst != NULL );
470 
471 	subst = *psubst;
472 
473 	for ( n = 0; n < subst->lt_num_submatch; n++ ) {
474 		if ( subst->lt_subs[ n ].bv_val ) {
475 			free( subst->lt_subs[ n ].bv_val );
476 			subst->lt_subs[ n ].bv_val = NULL;
477 		}
478 
479 		switch ( subst->lt_submatch[ n ].ls_type ) {
480 		case REWRITE_SUBMATCH_ASIS:
481 			break;
482 
483 		case REWRITE_SUBMATCH_XMAP:
484 			rewrite_xmap_destroy( &subst->lt_submatch[ n ].ls_map );
485 			break;
486 
487 		case REWRITE_SUBMATCH_MAP_W_ARG:
488 			rewrite_map_destroy( &subst->lt_submatch[ n ].ls_map );
489 			break;
490 
491 		default:
492 			break;
493 		}
494 	}
495 
496 	free( subst->lt_submatch );
497 	subst->lt_submatch = NULL;
498 
499 	/* last one */
500 	if ( subst->lt_subs[ n ].bv_val ) {
501 		free( subst->lt_subs[ n ].bv_val );
502 		subst->lt_subs[ n ].bv_val = NULL;
503 	}
504 
505 	free( subst->lt_subs );
506 	subst->lt_subs = NULL;
507 
508 	free( subst );
509 	*psubst = NULL;
510 
511 	return 0;
512 }
513 
514