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