1 /*        $NetBSD: context.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  * Compares two struct rewrite_context based on the name;
28  * used by avl stuff
29  */
30 static int
rewrite_context_cmp(const void * c1,const void * c2)31 rewrite_context_cmp(
32                     const void *c1,
33                     const void *c2
34 )
35 {
36           const struct rewrite_context *lc1, *lc2;
37 
38           lc1 = (const struct rewrite_context *)c1;
39           lc2 = (const struct rewrite_context *)c2;
40 
41           assert( c1 != NULL );
42           assert( c2 != NULL );
43           assert( lc1->lc_name != NULL );
44           assert( lc2->lc_name != NULL );
45 
46           return strcasecmp( lc1->lc_name, lc2->lc_name );
47 }
48 
49 /*
50  * Returns -1 in case a duplicate struct rewrite_context
51  * has been inserted; used by avl stuff
52  */
53 static int
rewrite_context_dup(void * c1,void * c2)54 rewrite_context_dup(
55                     void *c1,
56                     void *c2
57                     )
58 {
59           struct rewrite_context *lc1, *lc2;
60 
61           lc1 = (struct rewrite_context *)c1;
62           lc2 = (struct rewrite_context *)c2;
63 
64           assert( c1 != NULL );
65           assert( c2 != NULL );
66           assert( lc1->lc_name != NULL );
67           assert( lc2->lc_name != NULL );
68 
69           return( strcasecmp( lc1->lc_name, lc2->lc_name) == 0 ? -1 : 0 );
70 }
71 
72 /*
73  * Finds the context named rewriteContext in the context tree
74  */
75 struct rewrite_context *
rewrite_context_find(struct rewrite_info * info,const char * rewriteContext)76 rewrite_context_find(
77                     struct rewrite_info *info,
78                     const char *rewriteContext
79 )
80 {
81           struct rewrite_context *context, c;
82 
83           assert( info != NULL );
84           assert( rewriteContext != NULL );
85 
86           /*
87            * Fetches the required rewrite context
88            */
89           c.lc_name = (char *)rewriteContext;
90           context = (struct rewrite_context *)ldap_avl_find( info->li_context,
91                               (caddr_t)&c, rewrite_context_cmp );
92           if ( context == NULL ) {
93                     return NULL;
94           }
95 
96           /*
97            * De-aliases the context if required
98            */
99           if ( context->lc_alias ) {
100                     return context->lc_alias;
101           }
102 
103           return context;
104 }
105 
106 /*
107  * Creates a new context called rewriteContext and stores in into the tree
108  */
109 struct rewrite_context *
rewrite_context_create(struct rewrite_info * info,const char * rewriteContext)110 rewrite_context_create(
111                     struct rewrite_info *info,
112                     const char *rewriteContext
113 )
114 {
115           struct rewrite_context *context;
116           int rc;
117 
118           assert( info != NULL );
119           assert( rewriteContext != NULL );
120 
121           context = calloc( sizeof( struct rewrite_context ), 1 );
122           if ( context == NULL ) {
123                     return NULL;
124           }
125 
126           /*
127            * Context name
128            */
129           context->lc_name = strdup( rewriteContext );
130           if ( context->lc_name == NULL ) {
131                     free( context );
132                     return NULL;
133           }
134 
135           /*
136            * The first, empty rule
137            */
138           context->lc_rule = calloc( sizeof( struct rewrite_rule ), 1 );
139           if ( context->lc_rule == NULL ) {
140                     free( context->lc_name );
141                     free( context );
142                     return NULL;
143           }
144           memset( context->lc_rule, 0, sizeof( struct rewrite_rule ) );
145 
146           /*
147            * Add context to tree
148            */
149           rc = ldap_avl_insert( &info->li_context, (caddr_t)context,
150                               rewrite_context_cmp, rewrite_context_dup );
151           if ( rc == -1 ) {
152                     free( context->lc_rule );
153                     free( context->lc_name );
154                     free( context );
155                     return NULL;
156           }
157 
158           return context;
159 }
160 
161 /*
162  * Finds the next rule according to a goto action statement,
163  * or null in case of error.
164  * Helper for rewrite_context_apply.
165  */
166 static struct rewrite_rule *
rewrite_action_goto(struct rewrite_action * action,struct rewrite_rule * rule)167 rewrite_action_goto(
168                     struct rewrite_action *action,
169                     struct rewrite_rule *rule
170 )
171 {
172           int n;
173 
174           assert( action != NULL );
175           assert( action->la_args != NULL );
176           assert( rule != NULL );
177 
178           n = ((int *)action->la_args)[ 0 ];
179 
180           if ( n > 0 ) {
181                     for ( ; n > 1 && rule != NULL ; n-- ) {
182                               rule = rule->lr_next;
183                     }
184           } else if ( n <= 0 ) {
185                     for ( ; n < 1 && rule != NULL ; n++ ) {
186                               rule = rule->lr_prev;
187                     }
188           }
189 
190           return rule;
191 }
192 
193 /*
194  * Rewrites string according to context; may return:
195  *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
196  *      STOP:   fine, rule matched; stop processing following rules
197  *      UNWILL: rule matched; force 'unwilling to perform'
198  */
199 int
rewrite_context_apply(struct rewrite_info * info,struct rewrite_op * op,struct rewrite_context * context,const char * string,char ** result)200 rewrite_context_apply(
201                     struct rewrite_info *info,
202                     struct rewrite_op *op,
203                     struct rewrite_context *context,
204                     const char *string,
205                     char **result
206 )
207 {
208           struct rewrite_rule *rule;
209           char *s, *res = NULL;
210           int return_code = REWRITE_REGEXEC_OK;
211 
212           assert( info != NULL );
213           assert( op != NULL );
214           assert( context != NULL );
215           assert( context->lc_rule != NULL );
216           assert( string != NULL );
217           assert( result != NULL );
218 
219           op->lo_depth++;
220 
221           Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
222                               " [depth=%d] string='%s'\n",
223                               op->lo_depth, string );
224           assert( op->lo_depth > 0 );
225 
226           s = (char *)string;
227 
228           for ( rule = context->lc_rule->lr_next;
229                               rule != NULL && op->lo_num_passes < info->li_max_passes;
230                               rule = rule->lr_next, op->lo_num_passes++ ) {
231                     int rc;
232 
233                     /*
234                      * Apply a single rule
235                      */
236                     rc = rewrite_rule_apply( info, op, rule, s, &res );
237 
238                     /*
239                      * A rule may return:
240                      *        OK                  with result != NULL if matched
241                      *        ERR                 if anything was wrong
242                      *        UNWILLING if the server should drop the request
243                      * the latter case in honored immediately;
244                      * the other two may require some special actions to take
245                      * place.
246                      */
247                     switch ( rc ) {
248 
249                     case REWRITE_REGEXEC_ERR:
250                               Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply"
251                                                   " error ...\n" );
252 
253                               /*
254                                * Checks for special actions to be taken
255                                * in case of error ...
256                                */
257                               if ( rule->lr_action != NULL ) {
258                                         struct rewrite_action *action;
259                                         int do_continue = 0;
260 
261                                         for ( action = rule->lr_action;
262                                                             action != NULL;
263                                                             action = action->la_next ) {
264                                                   switch ( action->la_type ) {
265 
266                                                   /*
267                                                    * This action takes precedence
268                                                    * over the others in case of failure
269                                                    */
270                                                   case REWRITE_ACTION_IGNORE_ERR:
271                                                             Debug( LDAP_DEBUG_ANY,
272                                                   "==> rewrite_context_apply"
273                                                   " ignoring error ...\n" );
274                                                             do_continue = 1;
275                                                             break;
276 
277                                                   /*
278                                                    * Goto is honored only if it comes
279                                                    * after ignore error
280                                                    */
281                                                   case REWRITE_ACTION_GOTO:
282                                                             if ( do_continue ) {
283                                                                       rule = rewrite_action_goto( action, rule );
284                                                                       if ( rule == NULL ) {
285                                                                                 return_code = REWRITE_REGEXEC_ERR;
286                                                                                 goto rc_end_of_context;
287                                                                       }
288                                                             }
289                                                             break;
290 
291                                                   /*
292                                                    * Other actions are ignored
293                                                    */
294                                                   default:
295                                                             break;
296                                                   }
297                                         }
298 
299                                         if ( do_continue ) {
300                                                   if ( rule->lr_next == NULL ) {
301                                                             res = s;
302                                                   }
303                                                   goto rc_continue;
304                                         }
305                               }
306 
307                               /*
308                                * Default behavior is to bail out ...
309                                */
310                               return_code = REWRITE_REGEXEC_ERR;
311                               goto rc_end_of_context;
312 
313                     /*
314                      * OK means there were no errors or special return codes;
315                      * if res is defined, it means the rule matched and we
316                      * got a successful rewriting
317                      */
318                     case REWRITE_REGEXEC_OK:
319 
320                               /*
321                                * It matched! Check for actions ...
322                                */
323                               if ( res != NULL ) {
324                                         struct rewrite_action *action;
325 
326                                         if ( s != string && s != res ) {
327                                                   free( s );
328                                         }
329                                         s = res;
330 
331                                         for ( action = rule->lr_action;
332                                                             action != NULL;
333                                                             action = action->la_next ) {
334 
335                                                   switch ( action->la_type ) {
336 
337                                                   /*
338                                                    * This ends the rewrite context
339                                                    * successfully
340                                                    */
341                                                   case REWRITE_ACTION_STOP:
342                                                             goto rc_end_of_context;
343 
344                                                   /*
345                                                    * This instructs the server to return
346                                                    * an `unwilling to perform' error
347                                                    * message
348                                                    */
349                                                   case REWRITE_ACTION_UNWILLING:
350                                                             return_code = REWRITE_REGEXEC_UNWILLING;
351                                                             goto rc_end_of_context;
352 
353                                                   /*
354                                                    * This causes the processing to
355                                                    * jump n rules back and forth
356                                                    */
357                                                   case REWRITE_ACTION_GOTO:
358                                                             rule = rewrite_action_goto( action, rule );
359                                                             if ( rule == NULL ) {
360                                                                       return_code = REWRITE_REGEXEC_ERR;
361                                                                       goto rc_end_of_context;
362                                                             }
363                                                             break;
364 
365                                                   /*
366                                                    * This ends the rewrite context
367                                                    * and returns a user-defined
368                                                    * error code
369                                                    */
370                                                   case REWRITE_ACTION_USER:
371                                                             return_code = ((int *)action->la_args)[ 0 ];
372                                                             goto rc_end_of_context;
373 
374                                                   default:
375                                                             /* ... */
376                                                             break;
377                                                   }
378                                         }
379 
380                               /*
381                                * If result was OK and string didn't match,
382                                * in case of last rule we need to set the
383                                * result back to the string
384                                */
385                               } else if ( rule->lr_next == NULL ) {
386                                         res = s;
387                               }
388 
389                               break;
390 
391                     /*
392                      * A STOP has propagated ...
393                      */
394                     case REWRITE_REGEXEC_STOP:
395                               goto rc_end_of_context;
396 
397                     /*
398                      * This will instruct the server to return
399                      * an `unwilling to perform' error message
400                      */
401                     case REWRITE_REGEXEC_UNWILLING:
402                               return_code = REWRITE_REGEXEC_UNWILLING;
403                               goto rc_end_of_context;
404 
405                     /*
406                      * A user-defined error code has propagated ...
407                      */
408                     default:
409                               assert( rc >= REWRITE_REGEXEC_USER );
410                               goto rc_end_of_context;
411 
412                     }
413 
414 rc_continue:;       /* sent here by actions that require to continue */
415 
416           }
417 
418 rc_end_of_context:;
419           *result = res;
420 
421           Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
422                               " [depth=%d] res={%d,'%s'}\n",
423                               op->lo_depth, return_code, ( res ? res : "NULL" ) );
424 
425           assert( op->lo_depth > 0 );
426           op->lo_depth--;
427 
428           return return_code;
429 }
430 
431 void
rewrite_context_free(void * tmp)432 rewrite_context_free(
433                     void *tmp
434 )
435 {
436           struct rewrite_context *context = (struct rewrite_context *)tmp;
437 
438           assert( tmp != NULL );
439 
440           rewrite_context_destroy( &context );
441 }
442 
443 int
rewrite_context_destroy(struct rewrite_context ** pcontext)444 rewrite_context_destroy(
445                     struct rewrite_context **pcontext
446 )
447 {
448           struct rewrite_context *context;
449           struct rewrite_rule *r;
450 
451           assert( pcontext != NULL );
452           assert( *pcontext != NULL );
453 
454           context = *pcontext;
455 
456           assert( context->lc_rule != NULL );
457 
458           for ( r = context->lc_rule->lr_next; r; ) {
459                     struct rewrite_rule *cr = r;
460 
461                     r = r->lr_next;
462                     rewrite_rule_destroy( &cr );
463           }
464 
465           free( context->lc_rule );
466           context->lc_rule = NULL;
467 
468           assert( context->lc_name != NULL );
469           free( context->lc_name );
470           context->lc_name = NULL;
471 
472           free( context );
473           *pcontext = NULL;
474 
475           return 0;
476 }
477