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