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