1 /* 2 * keyvalue - PCRE matching and substitution for mod_redirect and mod_rewrite 3 * 4 * Fully-rewritten from original 5 * Copyright(c) 2018 Glenn Strauss gstrauss()gluelogic.com All rights reserved 6 * License: BSD 3-clause (same as lighttpd) 7 */ 8 #include "first.h" 9 10 #include "keyvalue.h" 11 #include "plugin_config.h" /* struct cond_match_t */ 12 #include "burl.h" 13 #include "log.h" 14 15 #include <stdlib.h> 16 #include <string.h> 17 18 #ifdef HAVE_PCRE2_H 19 #define PCRE2_CODE_UNIT_WIDTH 8 20 #include <pcre2.h> 21 #elif defined(HAVE_PCRE_H) 22 #include <pcre.h> 23 #ifndef PCRE_STUDY_JIT_COMPILE 24 #define PCRE_STUDY_JIT_COMPILE 0 25 #define pcre_free_study(x) pcre_free(x) 26 #endif 27 #endif 28 29 #ifdef HAVE_PCRE2_H 30 static struct pcre2_real_match_data_8 *keyvalue_match_data; 31 #endif 32 33 typedef struct pcre_keyvalue { 34 #ifdef HAVE_PCRE2_H 35 pcre2_code *code; 36 struct pcre2_real_match_data_8 *match_data; 37 #elif defined(HAVE_PCRE_H) 38 pcre *key; 39 pcre_extra *key_extra; 40 #endif 41 buffer value; 42 } pcre_keyvalue; 43 44 pcre_keyvalue_buffer *pcre_keyvalue_buffer_init(void) { 45 pcre_keyvalue_buffer *kvb; 46 47 kvb = calloc(1, sizeof(*kvb)); 48 force_assert(NULL != kvb); 49 50 return kvb; 51 } 52 53 int pcre_keyvalue_buffer_append(log_error_st *errh, pcre_keyvalue_buffer *kvb, const buffer *key, const buffer *value, const int pcre_jit) { 54 55 #ifdef HAVE_PCRE 56 57 pcre_keyvalue *kv; 58 59 if (0 == (kvb->used & 3)) { /*(allocate in groups of 4)*/ 60 kvb->kv = realloc(kvb->kv, (kvb->used + 4) * sizeof(*kvb->kv)); 61 force_assert(NULL != kvb->kv); 62 } 63 64 kv = kvb->kv + kvb->used++; 65 66 /* copy persistent config data, and elide free() in free_data below */ 67 memcpy(&kv->value, value, sizeof(buffer)); 68 /*buffer_copy_buffer(&kv->value, value);*/ 69 70 #ifdef HAVE_PCRE2_H 71 72 int errcode; 73 PCRE2_SIZE erroff; 74 PCRE2_UCHAR errbuf[1024]; 75 76 kv->code = pcre2_compile((PCRE2_SPTR)BUF_PTR_LEN(key), 77 PCRE2_UTF, &errcode, &erroff, NULL); 78 if (NULL == kv->code) { 79 pcre2_get_error_message(errcode, errbuf, sizeof(errbuf)); 80 log_error(errh, __FILE__, __LINE__, 81 "pcre2_compile: %s at offset %zu, regex: %s", 82 (char *)errbuf, erroff, key->ptr); 83 return 0; 84 } 85 86 if (pcre_jit) { 87 errcode = pcre2_jit_compile(kv->code, PCRE2_JIT_COMPLETE); 88 if (0 != errcode && errcode != PCRE2_ERROR_JIT_BADOPTION) { 89 pcre2_get_error_message(errcode, errbuf, sizeof(errbuf)); 90 log_error(errh, __FILE__, __LINE__, 91 "pcre2_jit_compile: %s, regex: %s", (char *)errbuf, key->ptr); 92 /*return 0;*/ 93 } 94 } 95 96 uint32_t captures; 97 errcode = pcre2_pattern_info(kv->code, PCRE2_INFO_CAPTURECOUNT, &captures); 98 if (0 != errcode) { 99 pcre2_get_error_message(errcode, errbuf, sizeof(errbuf)); 100 log_error(errh, __FILE__, __LINE__, 101 "pcre2_pattern_info: %s, regex: %s", (char *)errbuf, key->ptr); 102 return 0; 103 } 104 else if (captures > 19) { 105 log_error(errh, __FILE__, __LINE__, 106 "Too many captures in regex, " 107 "use (?:...) instead of (...): %s", key->ptr); 108 return 0; 109 } 110 111 #if 1 /*(share single keyvalue_match_data among all keyvalue regexes)*/ 112 if (NULL == keyvalue_match_data) { 113 keyvalue_match_data = pcre2_match_data_create(20, NULL); 114 force_assert(keyvalue_match_data); 115 } 116 kv->match_data = keyvalue_match_data; 117 #else 118 kv->match_data = pcre2_match_data_create_from_pattern(kv->code, NULL); 119 force_assert(kv->match_data); 120 #endif 121 122 #elif defined(HAVE_PCRE_H) 123 124 const char *errptr; 125 int erroff; 126 127 kv->key_extra = NULL; 128 129 if (NULL == (kv->key = pcre_compile(key->ptr, 130 0, &errptr, &erroff, NULL))) { 131 132 log_error(errh, __FILE__, __LINE__, 133 "rexexp compilation error at %s", errptr); 134 return 0; 135 } 136 137 const int study_options = pcre_jit ? PCRE_STUDY_JIT_COMPILE : 0; 138 if (NULL == (kv->key_extra = pcre_study(kv->key, study_options, &errptr)) 139 && errptr != NULL) { 140 log_error(errh, __FILE__, __LINE__, 141 "studying regex failed: %s -> %s\n", 142 key->ptr, errptr); 143 return 0; 144 } 145 146 #endif 147 148 #else /* !HAVE_PCRE */ 149 150 static int logged_message = 0; 151 if (logged_message) return 1; 152 logged_message = 1; 153 log_error(errh, __FILE__, __LINE__, 154 "pcre support is missing, please install libpcre and the headers"); 155 UNUSED(kvb); 156 UNUSED(key); 157 UNUSED(value); 158 UNUSED(pcre_jit); 159 160 #endif /* !HAVE_PCRE */ 161 162 return 1; 163 } 164 165 void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb) { 166 #ifdef HAVE_PCRE 167 pcre_keyvalue *kv = kvb->kv; 168 for (int i = 0, used = (int)kvb->used; i < used; ++i, ++kv) { 169 #ifdef HAVE_PCRE2_H 170 if (kv->code) pcre2_code_free(kv->code); 171 #if 1 172 if (keyvalue_match_data) { 173 pcre2_match_data_free(keyvalue_match_data); 174 keyvalue_match_data = NULL; 175 } 176 #else 177 if (kv->match_data) pcre2_match_data_free(kv->match_data); 178 #endif 179 #elif defined(HAVE_PCRE_H) 180 if (kv->key) pcre_free(kv->key); 181 if (kv->key_extra) pcre_free_study(kv->key_extra); 182 /*free (kv->value.ptr);*//*(see pcre_keyvalue_buffer_append)*/ 183 #endif 184 } 185 186 if (kvb->kv) free(kvb->kv); 187 #endif 188 free(kvb); 189 } 190 191 #ifdef HAVE_PCRE 192 193 static void pcre_keyvalue_buffer_append_match(buffer *b, const pcre_keyvalue_ctx *ctx, unsigned int num, int flags) { 194 if (num < (unsigned int)ctx->n) { /* n is always > 0 */ 195 #ifdef HAVE_PCRE2_H 196 const PCRE2_SIZE *ovec = (PCRE2_SIZE *)ctx->ovec; 197 #elif defined(HAVE_PCRE_H) 198 const int *ovec = (int *)ctx->ovec; 199 #endif 200 const size_t off = (size_t)ovec[(num <<= 1)]; /*(num *= 2)*/ 201 const size_t len = (size_t)ovec[num+1] - off; 202 burl_append(b, ctx->subject + off, len, flags); 203 } 204 } 205 206 static void pcre_keyvalue_buffer_append_ctxmatch(buffer *b, const pcre_keyvalue_ctx *ctx, unsigned int num, int flags) { 207 const struct cond_match_t * const cache = ctx->cache; 208 if (!cache) return; /* no enclosing match context */ 209 if (num < (unsigned int)cache->captures) { 210 #ifdef HAVE_PCRE2_H 211 const PCRE2_SIZE *ovec = (PCRE2_SIZE *)cache->matches; 212 #elif defined(HAVE_PCRE_H) 213 const int *ovec = (int *)cache->matches; 214 #endif 215 const size_t off = (size_t)ovec[(num <<= 1)]; /*(num *= 2)*/ 216 const size_t len = (size_t)ovec[num+1] - off; 217 burl_append(b, cache->comp_value->ptr + off, len, flags); 218 } 219 } 220 221 static int pcre_keyvalue_buffer_subst_ext(buffer *b, const char *pattern, const pcre_keyvalue_ctx *ctx) { 222 const unsigned char *p = (unsigned char *)pattern+2;/* +2 past ${} or %{} */ 223 int flags = 0; 224 while (!light_isdigit(*p) && *p != '}' && *p != '\0') { 225 if (0) { 226 } 227 else if (p[0] == 'e' && p[1] == 's' && p[2] == 'c') { 228 p+=3; 229 if (p[0] == ':') { 230 flags |= BURL_ENCODE_ALL; 231 p+=1; 232 } 233 else if (0 == strncmp((const char *)p, "ape:", 4)) { 234 flags |= BURL_ENCODE_ALL; 235 p+=4; 236 } 237 else if (0 == strncmp((const char *)p, "nde:", 4)) { 238 flags |= BURL_ENCODE_NDE; 239 p+=4; 240 } 241 else if (0 == strncmp((const char *)p, "psnde:", 6)) { 242 flags |= BURL_ENCODE_PSNDE; 243 p+=6; 244 } 245 else { /* skip unrecognized esc... */ 246 p = (const unsigned char *)strchr((const char *)p, ':'); 247 if (NULL == p) return -1; 248 ++p; 249 } 250 } 251 else if (p[0] == 'n' && p[1] == 'o') { 252 p+=2; 253 if (0 == strncmp((const char *)p, "esc:", 4)) { 254 flags |= BURL_ENCODE_NONE; 255 p+=4; 256 } 257 else if (0 == strncmp((const char *)p, "escape:", 7)) { 258 flags |= BURL_ENCODE_NONE; 259 p+=7; 260 } 261 else { /* skip unrecognized no... */ 262 p = (const unsigned char *)strchr((const char *)p, ':'); 263 if (NULL == p) return -1; 264 ++p; 265 } 266 } 267 else if (p[0] == 't' && p[1] == 'o') { 268 p+=2; 269 if (0 == strncmp((const char *)p, "lower:", 6)) { 270 flags |= BURL_TOLOWER; 271 p+=6; 272 } 273 else if (0 == strncmp((const char *)p, "upper:", 6)) { 274 flags |= BURL_TOLOWER; 275 p+=6; 276 } 277 else { /* skip unrecognized to... */ 278 p = (const unsigned char *)strchr((const char *)p, ':'); 279 if (NULL == p) return -1; 280 ++p; 281 } 282 } 283 else if (p[0] == 'u' && p[1] == 'r' && p[2] == 'l' && p[3] == '.') { 284 const struct burl_parts_t * const burl = ctx->burl; 285 p+=4; 286 if (0 == strncmp((const char *)p, "scheme}", 7)) { 287 if (burl->scheme) 288 burl_append(b, BUF_PTR_LEN(burl->scheme), flags); 289 p+=6; 290 } 291 else if (0 == strncmp((const char *)p, "authority}", 10)) { 292 if (burl->authority) 293 burl_append(b, BUF_PTR_LEN(burl->authority), flags); 294 p+=9; 295 } 296 else if (0 == strncmp((const char *)p, "port}", 5)) { 297 buffer_append_int(b, (int)burl->port); 298 p+=4; 299 } 300 else if (0 == strncmp((const char *)p, "path}", 5)) { 301 const buffer * const target = burl->path; 302 const uint32_t len = buffer_clen(target); 303 const char * const ptr = target->ptr; 304 const char * const qmark = memchr(ptr, '?', len); 305 burl_append(b, ptr, qmark ? (uint32_t)(qmark-ptr) : len, flags); 306 p+=4; 307 } 308 else if (0 == strncmp((const char *)p, "query}", 6)) { 309 if (burl->query) 310 burl_append(b, BUF_PTR_LEN(burl->query), flags); 311 p+=5; 312 } 313 else { /* skip unrecognized url.* */ 314 p = (const unsigned char *)strchr((const char *)p, '}'); 315 if (NULL == p) return -1; 316 } 317 break; 318 } 319 else if (p[0] == 'q' && p[1] == 's' && p[2] == 'a' && p[3] == '}') { 320 const buffer *qs = ctx->burl->query; 321 if (qs && !buffer_is_unset(qs)) { 322 if (NULL != strchr(b->ptr, '?')) { 323 if (!buffer_is_blank(qs)) 324 buffer_append_string_len(b, CONST_STR_LEN("&")); 325 } 326 else { 327 buffer_append_string_len(b, CONST_STR_LEN("?")); 328 } 329 burl_append(b, BUF_PTR_LEN(qs), flags); 330 } 331 p+=3; 332 break; 333 } 334 else if (p[0] == 'e' && p[1] == 'n' && p[2] == 'c' 335 && 0 == strncmp((const char *)p+3, "b64u:", 5)) { 336 flags |= BURL_ENCODE_B64U; 337 p+=8; 338 } 339 else if (p[0] == 'd' && p[1] == 'e' && p[2] == 'c' 340 && 0 == strncmp((const char *)p+3, "b64u:", 5)) { 341 flags |= BURL_DECODE_B64U; 342 p+=8; 343 } 344 else ++p; /* skip unrecognized char */ 345 } 346 if (*p == '\0') return -1; 347 if (*p != '}') { /* light_isdigit(*p) */ 348 unsigned int num = *p - '0'; 349 ++p; 350 if (light_isdigit(*p)) num = num * 10 + (*p++ - '0'); 351 if (*p != '}') { 352 p = (const unsigned char *)strchr((const char *)p, '}'); 353 if (NULL == p) return -1; 354 } 355 if (0 == flags) flags = BURL_ENCODE_PSNDE; /* default */ 356 pattern[0] == '$' /*(else '%')*/ 357 ? pcre_keyvalue_buffer_append_match(b, ctx, num, flags) 358 : pcre_keyvalue_buffer_append_ctxmatch(b, ctx, num, flags); 359 } 360 return (int)(p + 1 - (unsigned char *)pattern - 2); 361 } 362 363 static void pcre_keyvalue_buffer_subst(buffer *b, const buffer *patternb, const pcre_keyvalue_ctx *ctx) { 364 const char *pattern = patternb->ptr; 365 const size_t pattern_len = buffer_clen(patternb); 366 size_t start = 0; 367 368 /* search for $... or %... pattern substitutions */ 369 370 buffer_clear(b); 371 372 for (size_t k = 0; k + 1 < pattern_len; ++k) { 373 if (pattern[k] == '$' || pattern[k] == '%') { 374 375 buffer_append_string_len(b, pattern + start, k - start); 376 377 if (pattern[k + 1] == '{') { 378 int num = pcre_keyvalue_buffer_subst_ext(b, pattern+k, ctx); 379 if (num < 0) return; /* error; truncate result */ 380 k += (size_t)num; 381 } else if (light_isdigit(((unsigned char *)pattern)[k + 1])) { 382 unsigned int num = (unsigned int)pattern[k + 1] - '0'; 383 pattern[k] == '$' /*(else '%')*/ 384 ? pcre_keyvalue_buffer_append_match(b, ctx, num, 0) 385 : pcre_keyvalue_buffer_append_ctxmatch(b, ctx, num, 0); 386 } else { 387 /* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */ 388 buffer_append_string_len(b, pattern+k, pattern[k] == pattern[k+1] ? 1 : 2); 389 } 390 391 k++; 392 start = k + 1; 393 } 394 } 395 396 buffer_append_string_len(b, pattern + start, pattern_len - start); 397 } 398 399 handler_t pcre_keyvalue_buffer_process(const pcre_keyvalue_buffer *kvb, pcre_keyvalue_ctx *ctx, const buffer *input, buffer *result) { 400 const pcre_keyvalue *kv = kvb->kv; 401 for (int i = 0, used = (int)kvb->used; i < used; ++i, ++kv) { 402 #ifdef HAVE_PCRE2_H 403 int n = pcre2_match(kv->code, (PCRE2_SPTR)BUF_PTR_LEN(input), 404 0, 0, kv->match_data, NULL); 405 #else 406 #define N 20 407 int ovec[N * 3]; 408 #undef N 409 int n = pcre_exec(kv->key, kv->key_extra, BUF_PTR_LEN(input), 410 0, 0, ovec, sizeof(ovec)/sizeof(int)); 411 #endif 412 if (n < 0) { 413 #ifdef HAVE_PCRE2_H 414 if (n != PCRE2_ERROR_NOMATCH) 415 #else 416 if (n != PCRE_ERROR_NOMATCH) 417 #endif 418 return HANDLER_ERROR; 419 } 420 else if (buffer_is_blank(&kv->value)) { 421 /* short-circuit if blank replacement pattern 422 * (do not attempt to match against remaining kvb rules) */ 423 ctx->m = i; 424 return HANDLER_GO_ON; 425 } 426 else { /* it matched */ 427 ctx->m = i; 428 ctx->n = n; 429 ctx->subject = input->ptr; 430 #ifdef HAVE_PCRE2_H 431 ctx->ovec = pcre2_get_ovector_pointer(kv->match_data); 432 #else 433 ctx->ovec = ovec; 434 #endif 435 pcre_keyvalue_buffer_subst(result, &kv->value, ctx); 436 return HANDLER_FINISHED; 437 } 438 } 439 440 return HANDLER_GO_ON; 441 } 442 443 #else /* !HAVE_PCRE */ 444 445 handler_t pcre_keyvalue_buffer_process(const pcre_keyvalue_buffer *kvb, pcre_keyvalue_ctx *ctx, const buffer *input, buffer *result) { 446 UNUSED(kvb); 447 UNUSED(ctx); 448 UNUSED(input); 449 UNUSED(result); 450 return HANDLER_GO_ON; 451 } 452 453 #endif /* !HAVE_PCRE */ 454 455 456 /* modified from burl_normalize_basic() to handle %% extra encoding layer */ 457 458 /* c (char) and n (nibble) MUST be unsigned integer types */ 459 #define li_cton(c,n) \ 460 (((n) = (c) - '0') <= 9 || (((n) = ((c)&0xdf) - 'A') <= 5 ? ((n) += 10) : 0)) 461 462 static void pcre_keyvalue_burl_percent_toupper (buffer *b) 463 { 464 const unsigned char * const s = (unsigned char *)b->ptr; 465 const int used = (int)buffer_clen(b); 466 unsigned int n1, n2; 467 for (int i = 0; i < used; ++i) { 468 if (s[i]=='%' && li_cton(s[i+1],n1) && li_cton(s[i+2],n2)) { 469 if (s[i+1] >= 'a') b->ptr[i+1] &= 0xdf; /* uppercase hex */ 470 if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */ 471 i+=2; 472 } 473 } 474 } 475 476 static void pcre_keyvalue_burl_percent_percent_toupper (buffer *b) 477 { 478 const unsigned char * const s = (unsigned char *)b->ptr; 479 const int used = (int)buffer_clen(b); 480 unsigned int n1, n2; 481 for (int i = 0; i < used; ++i) { 482 if (s[i] == '%' && s[i+1]=='%' 483 && li_cton(s[i+2],n1) && li_cton(s[i+3],n2)) { 484 if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */ 485 if (s[i+3] >= 'a') b->ptr[i+3] &= 0xdf; /* uppercase hex */ 486 i+=3; 487 } 488 } 489 } 490 491 static const char hex_chars_uc[] = "0123456789ABCDEF"; 492 493 static void pcre_keyvalue_burl_percent_high_UTF8 (buffer *b, buffer *t) 494 { 495 const unsigned char * const s = (unsigned char *)b->ptr; 496 unsigned char *p; 497 const int used = (int)buffer_clen(b); 498 unsigned int count = 0, j = 0; 499 for (int i = 0; i < used; ++i) { 500 if (s[i] > 0x7F) ++count; 501 } 502 if (0 == count) return; 503 504 p = (unsigned char *)buffer_string_prepare_copy(t, used+(count*2)); 505 for (int i = 0; i < used; ++i, ++j) { 506 if (s[i] <= 0x7F) 507 p[j] = s[i]; 508 else { 509 p[j] = '%'; 510 p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF]; 511 p[++j] = hex_chars_uc[s[i] & 0xF]; 512 } 513 } 514 buffer_copy_string_len(b, (char *)p, (size_t)j); 515 } 516 517 static void pcre_keyvalue_burl_percent_percent_high_UTF8 (buffer *b, buffer *t) 518 { 519 const unsigned char * const s = (unsigned char *)b->ptr; 520 unsigned char *p; 521 const int used = (int)buffer_clen(b); 522 unsigned int count = 0, j = 0; 523 for (int i = 0; i < used; ++i) { 524 if (s[i] > 0x7F) ++count; 525 } 526 if (0 == count) return; 527 528 p = (unsigned char *)buffer_string_prepare_copy(t, used+(count*3)); 529 for (int i = 0; i < used; ++i, ++j) { 530 if (s[i] <= 0x7F) 531 p[j] = s[i]; 532 else { 533 p[j] = '%'; 534 p[++j] = '%'; 535 p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF]; 536 p[++j] = hex_chars_uc[s[i] & 0xF]; 537 } 538 } 539 buffer_copy_string_len(b, (char *)p, (size_t)j); 540 } 541 542 /* Basic normalization of regex and regex replacement to mirror some of 543 * the normalizations performed on request URI (for better compatibility). 544 * Note: not currently attempting to replace unnecessary percent-encoding 545 * (would need to know if regex was intended to match url-path or 546 * query-string or both, and then would have to regex-escape if those 547 * chars where special regex chars such as . * + ? ( ) [ ] | and more) 548 * Not attempting to percent-encode chars which should be encoded, again 549 * since regex might target url-path, query-string, or both, and we would 550 * have to avoid percent-encoding special regex chars. 551 * Also not attempting to detect unnecessarily regex-escape in, e.g. %\x\x 552 * Preserve improper %-encoded sequences which are not %XX (using hex chars) 553 * Intentionally not performing path simplification (e.g. ./ ../) 554 * If regex-specific normalizations begin to be made to k here, 555 * must revisit callers, e.g. one configfile.c use on non-regex string. 556 * "%%" (percent_percent) is used in regex replacement strings since 557 * otherwise "%n" is used to indicate regex backreference where n is number. 558 */ 559 560 void pcre_keyvalue_burl_normalize_key (buffer *k, buffer *t) 561 { 562 pcre_keyvalue_burl_percent_toupper(k); 563 pcre_keyvalue_burl_percent_high_UTF8(k, t); 564 } 565 566 void pcre_keyvalue_burl_normalize_value (buffer *v, buffer *t) 567 { 568 pcre_keyvalue_burl_percent_percent_toupper(v); 569 pcre_keyvalue_burl_percent_percent_high_UTF8(v, t); 570 } 571