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