xref: /lighttpd1.4/src/keyvalue.c (revision 54c07b54)
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