1 /*        $NetBSD: rule.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  * Appends a rule to the double linked list of rules
28  * Helper for rewrite_rule_compile
29  */
30 static int
append_rule(struct rewrite_context * context,struct rewrite_rule * rule)31 append_rule(
32                     struct rewrite_context *context,
33                     struct rewrite_rule *rule
34 )
35 {
36           struct rewrite_rule *r;
37 
38           assert( context != NULL );
39           assert( context->lc_rule != NULL );
40           assert( rule != NULL );
41 
42           for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next );
43           r->lr_next = rule;
44           rule->lr_prev = r;
45 
46           return REWRITE_SUCCESS;
47 }
48 
49 /*
50  * Appends an action to the linked list of actions
51  * Helper for rewrite_rule_compile
52  */
53 static int
append_action(struct rewrite_action ** pbase,struct rewrite_action * action)54 append_action(
55                     struct rewrite_action **pbase,
56                     struct rewrite_action *action
57 )
58 {
59           struct rewrite_action **pa;
60 
61           assert( pbase != NULL );
62           assert( action != NULL );
63 
64           for ( pa = pbase; *pa != NULL; pa = &(*pa)->la_next );
65           *pa = action;
66 
67           return REWRITE_SUCCESS;
68 }
69 
70 static int
destroy_action(struct rewrite_action ** paction)71 destroy_action(
72                     struct rewrite_action **paction
73 )
74 {
75           struct rewrite_action         *action;
76 
77           assert( paction != NULL );
78           assert( *paction != NULL );
79 
80           action = *paction;
81 
82           /* do something */
83           switch ( action->la_type ) {
84           case REWRITE_FLAG_GOTO:
85           case REWRITE_FLAG_USER: {
86                     int *pi = (int *)action->la_args;
87 
88                     if ( pi ) {
89                               free( pi );
90                     }
91                     break;
92           }
93 
94           default:
95                     break;
96           }
97 
98           free( action );
99           *paction = NULL;
100 
101           return 0;
102 }
103 
104 static void
destroy_actions(struct rewrite_action * paction)105 destroy_actions(
106           struct rewrite_action *paction
107 )
108 {
109           struct rewrite_action *next;
110 
111           for (; paction; paction = next) {
112                     next = paction->la_next;
113                     destroy_action( &paction );
114           }
115 }
116 
117 /*
118  */
119 int
rewrite_rule_compile(struct rewrite_info * info,struct rewrite_context * context,const char * pattern,const char * result,const char * flagstring)120 rewrite_rule_compile(
121                     struct rewrite_info *info,
122                     struct rewrite_context *context,
123                     const char *pattern,
124                     const char *result,
125                     const char *flagstring
126 )
127 {
128           int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE;
129           int mode = REWRITE_RECURSE;
130           int max_passes;
131 
132           struct rewrite_rule *rule = NULL;
133           struct rewrite_subst *subst = NULL;
134           struct rewrite_action *action = NULL, *first_action = NULL;
135 
136           const char *p;
137 
138           assert( info != NULL );
139           assert( context != NULL );
140           assert( pattern != NULL );
141           assert( result != NULL );
142           /*
143            * A null flagstring should be allowed
144            */
145 
146           max_passes = info->li_max_passes_per_rule;
147 
148           /*
149            * Take care of substitution string
150            */
151           subst = rewrite_subst_compile( info, result );
152           if ( subst == NULL ) {
153                     return REWRITE_ERR;
154           }
155 
156           /*
157            * Take care of flags
158            */
159           for ( p = flagstring; p[ 0 ] != '\0'; p++ ) {
160                     switch( p[ 0 ] ) {
161 
162                     /*
163                      * REGEX flags
164                      */
165                     case REWRITE_FLAG_HONORCASE:            /* 'C' */
166                               /*
167                                * Honor case (default is case insensitive)
168                                */
169                               flags &= ~REWRITE_REGEX_ICASE;
170                               break;
171 
172                     case REWRITE_FLAG_BASICREGEX:                     /* 'R' */
173                               /*
174                                * Use POSIX Basic Regular Expression syntax
175                                * instead of POSIX Extended Regular Expression
176                                * syntax (default)
177                                */
178                               flags &= ~REWRITE_REGEX_EXTENDED;
179                               break;
180 
181                     /*
182                      * Execution mode flags
183                      */
184                     case REWRITE_FLAG_EXECONCE:             /* ':' */
185                               /*
186                                * Apply rule once only
187                                */
188                               mode &= ~REWRITE_RECURSE;
189                               mode |= REWRITE_EXEC_ONCE;
190                               break;
191 
192                     /*
193                      * Special action flags
194                      */
195                     case REWRITE_FLAG_STOP:                           /* '@' */
196                               /*
197                                * Bail out after applying rule
198                                */
199                               action = calloc( sizeof( struct rewrite_action ), 1 );
200                               if ( action == NULL ) {
201                                         goto fail;
202                               }
203 
204                               action->la_type = REWRITE_ACTION_STOP;
205                               break;
206 
207                     case REWRITE_FLAG_UNWILLING:            /* '#' */
208                               /*
209                                * Matching objs will be marked as gone!
210                                */
211                               action = calloc( sizeof( struct rewrite_action ), 1 );
212                               if ( action == NULL ) {
213                                         goto fail;
214                               }
215 
216                               mode &= ~REWRITE_RECURSE;
217                               mode |= REWRITE_EXEC_ONCE;
218                               action->la_type = REWRITE_ACTION_UNWILLING;
219                               break;
220 
221                     case REWRITE_FLAG_GOTO:                                     /* 'G' */
222                               /*
223                                * After applying rule, jump N rules
224                                */
225 
226                     case REWRITE_FLAG_USER: {                         /* 'U' */
227                               /*
228                                * After applying rule, return user-defined
229                                * error code
230                                */
231                               char *next = NULL;
232                               int *d;
233 
234                               if ( p[ 1 ] != '{' ) {
235                                         goto fail;
236                               }
237 
238                               d = malloc( sizeof( int ) );
239                               if ( d == NULL ) {
240                                         goto fail;
241                               }
242 
243                               d[ 0 ] = strtol( &p[ 2 ], &next, 0 );
244                               if ( next == &p[ 2 ] || next[0] != '}' ) {
245                                         free( d );
246                                         goto fail;
247                               }
248 
249                               action = calloc( sizeof( struct rewrite_action ), 1 );
250                               if ( action == NULL ) {
251                                         free( d );
252                                         goto fail;
253                               }
254                               switch ( p[ 0 ] ) {
255                               case REWRITE_FLAG_GOTO:
256                                         action->la_type = REWRITE_ACTION_GOTO;
257                                         break;
258 
259                               case REWRITE_FLAG_USER:
260                                         action->la_type = REWRITE_ACTION_USER;
261                                         break;
262 
263                               default:
264                                         assert(0);
265                               }
266 
267                               action->la_args = (void *)d;
268 
269                               p = next; /* p is incremented by the for ... */
270 
271                               break;
272                     }
273 
274                     case REWRITE_FLAG_MAX_PASSES: {                             /* 'U' */
275                               /*
276                                * Set the number of max passes per rule
277                                */
278                               char *next = NULL;
279 
280                               if ( p[ 1 ] != '{' ) {
281                                         goto fail;
282                               }
283 
284                               max_passes = strtol( &p[ 2 ], &next, 0 );
285                               if ( next == &p[ 2 ] || next[0] != '}' ) {
286                                         goto fail;
287                               }
288 
289                               if ( max_passes < 1 ) {
290                                         /* FIXME: nonsense ... */
291                                         max_passes = 1;
292                               }
293 
294                               p = next; /* p is incremented by the for ... */
295 
296                               break;
297                     }
298 
299                     case REWRITE_FLAG_IGNORE_ERR:               /* 'I' */
300                               /*
301                                * Ignore errors!
302                                */
303                               action = calloc( sizeof( struct rewrite_action ), 1 );
304                               if ( action == NULL ) {
305                                         goto fail;
306                               }
307 
308                               action->la_type = REWRITE_ACTION_IGNORE_ERR;
309                               break;
310 
311                     /*
312                      * Other flags ...
313                      */
314                     default:
315                               /*
316                                * Unimplemented feature (complain only)
317                                */
318                               break;
319                     }
320 
321                     /*
322                      * Stupid way to append to a list ...
323                      */
324                     if ( action != NULL ) {
325                               append_action( &first_action, action );
326                               action = NULL;
327                     }
328           }
329 
330           /*
331            * Finally, rule allocation
332            */
333           rule = calloc( sizeof( struct rewrite_rule ), 1 );
334           if ( rule == NULL ) {
335                     goto fail;
336           }
337 
338           /*
339            * REGEX compilation (luckily I don't need to take care of this ...)
340            */
341           if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
342                     goto fail;
343           }
344 
345           /*
346            * Just to remember them ...
347            */
348           rule->lr_pattern = strdup( pattern );
349           rule->lr_subststring = strdup( result );
350           rule->lr_flagstring = strdup( flagstring );
351           if ( rule->lr_pattern == NULL
352                     || rule->lr_subststring == NULL
353                     || rule->lr_flagstring == NULL )
354           {
355                     goto fail;
356           }
357 
358           /*
359            * Load compiled data into rule
360            */
361           rule->lr_subst = subst;
362 
363           /*
364            * Set various parameters
365            */
366           rule->lr_flags = flags;                 /* don't really need any longer ... */
367           rule->lr_mode = mode;
368           rule->lr_max_passes = max_passes;
369           rule->lr_action = first_action;
370 
371           /*
372            * Append rule at the end of the rewrite context
373            */
374           append_rule( context, rule );
375 
376           return REWRITE_SUCCESS;
377 
378 fail:
379           if ( rule ) {
380                     if ( rule->lr_pattern ) free( rule->lr_pattern );
381                     if ( rule->lr_subststring ) free( rule->lr_subststring );
382                     if ( rule->lr_flagstring ) free( rule->lr_flagstring );
383                     free( rule );
384           }
385           destroy_actions( first_action );
386           free( subst );
387           return REWRITE_ERR;
388 }
389 
390 /*
391  * Rewrites string according to rule; may return:
392  *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
393  *      STOP:   fine, rule matched; stop processing following rules
394  *      UNWILL: rule matched; force 'unwilling to perform'
395  */
396 int
rewrite_rule_apply(struct rewrite_info * info,struct rewrite_op * op,struct rewrite_rule * rule,const char * arg,char ** result)397 rewrite_rule_apply(
398                     struct rewrite_info *info,
399                     struct rewrite_op *op,
400                     struct rewrite_rule *rule,
401                     const char *arg,
402                     char **result
403                     )
404 {
405           size_t nmatch = REWRITE_MAX_MATCH;
406           regmatch_t match[ REWRITE_MAX_MATCH ];
407 
408           int rc = REWRITE_SUCCESS;
409 
410           char *string;
411           int strcnt = 0;
412           struct berval val = { 0, NULL };
413 
414           assert( info != NULL );
415           assert( op != NULL );
416           assert( rule != NULL );
417           assert( arg != NULL );
418           assert( result != NULL );
419 
420           *result = NULL;
421 
422           string = (char *)arg;
423 
424           /*
425            * In case recursive match is required (default)
426            */
427 recurse:;
428 
429           Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
430                               " rule='%s' string='%s' [%d pass(es)]\n",
431                               rule->lr_pattern, string, strcnt + 1 );
432 
433           op->lo_num_passes++;
434 
435           rc = regexec( &rule->lr_regex, string, nmatch, match, 0 );
436           if ( rc != 0 ) {
437                     if ( *result == NULL && string != arg ) {
438                               free( string );
439                     }
440 
441                     /*
442                      * No match is OK; *result = NULL means no match
443                      */
444                     return REWRITE_REGEXEC_OK;
445           }
446 
447           rc = rewrite_subst_apply( info, op, rule->lr_subst, string,
448                               match, &val );
449 
450           *result = val.bv_val;
451           val.bv_val = NULL;
452           if ( string != arg ) {
453                     free( string );
454                     string = NULL;
455           }
456 
457           if ( rc != REWRITE_REGEXEC_OK ) {
458                     return rc;
459           }
460 
461           if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE
462                               && op->lo_num_passes < info->li_max_passes
463                               && ++strcnt < rule->lr_max_passes ) {
464                     string = *result;
465 
466                     goto recurse;
467           }
468 
469           return REWRITE_REGEXEC_OK;
470 }
471 
472 int
rewrite_rule_destroy(struct rewrite_rule ** prule)473 rewrite_rule_destroy(
474                     struct rewrite_rule **prule
475                     )
476 {
477           struct rewrite_rule *rule;
478 
479           assert( prule != NULL );
480           assert( *prule != NULL );
481 
482           rule = *prule;
483 
484           if ( rule->lr_pattern ) {
485                     free( rule->lr_pattern );
486                     rule->lr_pattern = NULL;
487           }
488 
489           if ( rule->lr_subststring ) {
490                     free( rule->lr_subststring );
491                     rule->lr_subststring = NULL;
492           }
493 
494           if ( rule->lr_flagstring ) {
495                     free( rule->lr_flagstring );
496                     rule->lr_flagstring = NULL;
497           }
498 
499           if ( rule->lr_subst ) {
500                     rewrite_subst_destroy( &rule->lr_subst );
501           }
502 
503           regfree( &rule->lr_regex );
504 
505           destroy_actions( rule->lr_action );
506 
507           free( rule );
508           *prule = NULL;
509 
510           return 0;
511 }
512 
513