1 /* $NetBSD: rule.c,v 1.2 2020/08/11 13:15:39 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 2000-2020 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 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 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 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 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 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 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 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