xref: /lighttpd1.4/src/keyvalue.c (revision e11f72b1)
1 #include "first.h"
2 
3 #include "keyvalue.h"
4 #include "plugin_config.h" /* struct cond_match_t */
5 #include "burl.h"
6 #include "log.h"
7 
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #ifdef HAVE_PCRE_H
12 #include <pcre.h>
13 #endif
14 
15 typedef struct pcre_keyvalue {
16 #ifdef HAVE_PCRE_H
17 	pcre *key;
18 	pcre_extra *key_extra;
19 #endif
20 	buffer value;
21 } pcre_keyvalue;
22 
23 pcre_keyvalue_buffer *pcre_keyvalue_buffer_init(void) {
24 	pcre_keyvalue_buffer *kvb;
25 
26 	kvb = calloc(1, sizeof(*kvb));
27 	force_assert(NULL != kvb);
28 
29 	return kvb;
30 }
31 
32 int pcre_keyvalue_buffer_append(log_error_st *errh, pcre_keyvalue_buffer *kvb, const buffer *key, const buffer *value) {
33 #ifdef HAVE_PCRE_H
34 	const char *errptr;
35 	int erroff;
36 	pcre_keyvalue *kv;
37 
38 	if (0 == (kvb->used & 3)) { /*(allocate in groups of 4)*/
39 		kvb->kv = realloc(kvb->kv, (kvb->used + 4) * sizeof(*kvb->kv));
40 		force_assert(NULL != kvb->kv);
41 	}
42 
43 	kv = kvb->kv + kvb->used++;
44 	kv->key_extra = NULL;
45 
46         /* copy persistent config data, and elide free() in free_data below */
47 	memcpy(&kv->value, value, sizeof(buffer));
48 	/*buffer_copy_buffer(&kv->value, value);*/
49 
50 	if (NULL == (kv->key = pcre_compile(key->ptr,
51 					  0, &errptr, &erroff, NULL))) {
52 
53 		log_error(errh, __FILE__, __LINE__,
54 		  "rexexp compilation error at %s", errptr);
55 		return 0;
56 	}
57 
58 	if (NULL == (kv->key_extra = pcre_study(kv->key, 0, &errptr)) &&
59 			errptr != NULL) {
60 		return 0;
61 	}
62 #else
63 	static int logged_message = 0;
64 	if (logged_message) return 1;
65 	logged_message = 1;
66 	log_error(errh, __FILE__, __LINE__,
67 	  "pcre support is missing, please install libpcre and the headers");
68 	UNUSED(kvb);
69 	UNUSED(key);
70 	UNUSED(value);
71 #endif
72 
73 	return 1;
74 }
75 
76 void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb) {
77 #ifdef HAVE_PCRE_H
78 	for (uint32_t i = 0; i < kvb->used; ++i) {
79 		pcre_keyvalue * const kv = kvb->kv+i;
80 		if (kv->key) pcre_free(kv->key);
81 		if (kv->key_extra) pcre_free(kv->key_extra);
82 		/*free (kv->value.ptr);*//*(see pcre_keyvalue_buffer_append)*/
83 	}
84 
85 	if (kvb->kv) free(kvb->kv);
86 #endif
87 	free(kvb);
88 }
89 
90 #ifdef HAVE_PCRE_H
91 static void pcre_keyvalue_buffer_append_match(buffer *b, const char **list, int n, unsigned int num, int flags) {
92     if (num < (unsigned int)n) { /* n is always > 0 */
93         burl_append(b, list[num], strlen(list[num]), flags);
94     }
95 }
96 
97 static void pcre_keyvalue_buffer_append_ctxmatch(buffer *b, pcre_keyvalue_ctx *ctx, unsigned int num, int flags) {
98     const struct cond_match_t * const cache = ctx->cache;
99     if (!cache) return; /* no enclosing match context */
100     if ((int)num < ctx->cond_match_count) {
101         const int off = cache->matches[(num <<= 1)]; /*(num *= 2)*/
102         const int len = cache->matches[num+1] - off;
103         burl_append(b, cache->comp_value->ptr + off, (size_t)len, flags);
104     }
105 }
106 
107 static int pcre_keyvalue_buffer_subst_ext(buffer *b, const char *pattern, const char **list, int n, pcre_keyvalue_ctx *ctx) {
108     const unsigned char *p = (unsigned char *)pattern+2;/* +2 past ${} or %{} */
109     int flags = 0;
110     while (!light_isdigit(*p) && *p != '}' && *p != '\0') {
111         if (0) {
112         }
113         else if (p[0] == 'e' && p[1] == 's' && p[2] == 'c') {
114             p+=3;
115             if (p[0] == ':') {
116                 flags |= BURL_ENCODE_ALL;
117                 p+=1;
118             }
119             else if (0 == strncmp((const char *)p, "ape:", 4)) {
120                 flags |= BURL_ENCODE_ALL;
121                 p+=4;
122             }
123             else if (0 == strncmp((const char *)p, "nde:", 4)) {
124                 flags |= BURL_ENCODE_NDE;
125                 p+=4;
126             }
127             else if (0 == strncmp((const char *)p, "psnde:", 6)) {
128                 flags |= BURL_ENCODE_PSNDE;
129                 p+=6;
130             }
131             else { /* skip unrecognized esc... */
132                 p = (const unsigned char *)strchr((const char *)p, ':');
133                 if (NULL == p) return -1;
134                 ++p;
135             }
136         }
137         else if (p[0] == 'n' && p[1] == 'o') {
138             p+=2;
139             if (0 == strncmp((const char *)p, "esc:", 4)) {
140                 flags |= BURL_ENCODE_NONE;
141                 p+=4;
142             }
143             else if (0 == strncmp((const char *)p, "escape:", 7)) {
144                 flags |= BURL_ENCODE_NONE;
145                 p+=7;
146             }
147             else { /* skip unrecognized no... */
148                 p = (const unsigned char *)strchr((const char *)p, ':');
149                 if (NULL == p) return -1;
150                 ++p;
151             }
152         }
153         else if (p[0] == 't' && p[1] == 'o') {
154             p+=2;
155             if (0 == strncmp((const char *)p, "lower:", 6)) {
156                 flags |= BURL_TOLOWER;
157                 p+=6;
158             }
159             else if (0 == strncmp((const char *)p, "upper:", 6)) {
160                 flags |= BURL_TOLOWER;
161                 p+=6;
162             }
163             else { /* skip unrecognized to... */
164                 p = (const unsigned char *)strchr((const char *)p, ':');
165                 if (NULL == p) return -1;
166                 ++p;
167             }
168         }
169         else if (p[0] == 'u' && p[1] == 'r' && p[2] == 'l' && p[3] == '.') {
170             p+=4;
171             if (0 == strncmp((const char *)p, "scheme}", 7)) {
172                 burl_append(b, CONST_BUF_LEN(ctx->burl->scheme), flags);
173                 p+=6;
174             }
175             else if (0 == strncmp((const char *)p, "authority}", 10)) {
176                 burl_append(b, CONST_BUF_LEN(ctx->burl->authority), flags);
177                 p+=9;
178             }
179             else if (0 == strncmp((const char *)p, "port}", 5)) {
180                 buffer_append_int(b, (int)ctx->burl->port);
181                 p+=4;
182             }
183             else if (0 == strncmp((const char *)p, "path}", 5)) {
184                 const buffer * const target = ctx->burl->path;
185                 const uint32_t len = buffer_string_length(target);
186                 const char * const ptr = target->ptr;
187                 const char * const qmark = memchr(ptr, '?', len);
188                 burl_append(b, ptr, qmark ? (uint32_t)(qmark-ptr) : len, flags);
189                 p+=4;
190             }
191             else if (0 == strncmp((const char *)p, "query}", 6)) {
192                 burl_append(b, CONST_BUF_LEN(ctx->burl->query), flags);
193                 p+=5;
194             }
195             else { /* skip unrecognized url.* */
196                 p = (const unsigned char *)strchr((const char *)p, '}');
197                 if (NULL == p) return -1;
198             }
199             break;
200         }
201         else if (p[0] == 'q' && p[1] == 's' && p[2] == 'a' && p[3] == '}') {
202             const buffer *qs = ctx->burl->query;
203             if (!buffer_is_empty(qs)) {
204                 if (NULL != strchr(b->ptr, '?')) {
205                     if (!buffer_string_is_empty(qs))
206                         buffer_append_string_len(b, CONST_STR_LEN("&"));
207                 }
208                 else {
209                     buffer_append_string_len(b, CONST_STR_LEN("?"));
210                 }
211                 burl_append(b, CONST_BUF_LEN(qs), flags);
212             }
213             p+=3;
214             break;
215         }
216         else if (p[0] == 'e' && p[1] == 'n' && p[2] == 'c'
217                  && 0 == strncmp((const char *)p+3, "b64u:", 5)) {
218             flags |= BURL_ENCODE_B64U;
219             p+=8;
220         }
221         else if (p[0] == 'd' && p[1] == 'e' && p[2] == 'c'
222                  && 0 == strncmp((const char *)p+3, "b64u:", 5)) {
223             flags |= BURL_DECODE_B64U;
224             p+=8;
225         }
226         else ++p;  /* skip unrecognized char */
227     }
228     if (*p == '\0') return -1;
229     if (*p != '}') { /* light_isdigit(*p) */
230         unsigned int num = *p - '0';
231         ++p;
232         if (light_isdigit(*p)) num = num * 10 + (*p++ - '0');
233         if (*p != '}') {
234             p = (const unsigned char *)strchr((const char *)p, '}');
235             if (NULL == p) return -1;
236         }
237         if (0 == flags) flags = BURL_ENCODE_PSNDE; /* default */
238         pattern[0] == '$' /*(else '%')*/
239           ? pcre_keyvalue_buffer_append_match(b, list, n, num, flags)
240           : pcre_keyvalue_buffer_append_ctxmatch(b, ctx, num, flags);
241     }
242     return (int)(p + 1 - (unsigned char *)pattern - 2);
243 }
244 
245 static void pcre_keyvalue_buffer_subst(buffer *b, const buffer *patternb, const char **list, int n, pcre_keyvalue_ctx *ctx) {
246 	const char *pattern = patternb->ptr;
247 	const size_t pattern_len = buffer_string_length(patternb);
248 	size_t start = 0;
249 
250 	/* search for $... or %... pattern substitutions */
251 
252 	buffer_clear(b);
253 
254 	for (size_t k = 0; k + 1 < pattern_len; ++k) {
255 		if (pattern[k] == '$' || pattern[k] == '%') {
256 
257 			buffer_append_string_len(b, pattern + start, k - start);
258 
259 			if (pattern[k + 1] == '{') {
260 				int num = pcre_keyvalue_buffer_subst_ext(b, pattern+k, list, n, ctx);
261 				if (num < 0) return; /* error; truncate result */
262 				k += (size_t)num;
263 			} else if (light_isdigit(((unsigned char *)pattern)[k + 1])) {
264 				unsigned int num = (unsigned int)pattern[k + 1] - '0';
265 				pattern[k] == '$' /*(else '%')*/
266 				  ? pcre_keyvalue_buffer_append_match(b, list, n, num, 0)
267 				  : pcre_keyvalue_buffer_append_ctxmatch(b, ctx, num, 0);
268 			} else {
269 				/* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */
270 				buffer_append_string_len(b, pattern+k, pattern[k] == pattern[k+1] ? 1 : 2);
271 			}
272 
273 			k++;
274 			start = k + 1;
275 		}
276 	}
277 
278 	buffer_append_string_len(b, pattern + start, pattern_len - start);
279 }
280 
281 handler_t pcre_keyvalue_buffer_process(const pcre_keyvalue_buffer *kvb, pcre_keyvalue_ctx *ctx, const buffer *input, buffer *result) {
282     for (int i = 0, used = (int)kvb->used; i < used; ++i) {
283         const pcre_keyvalue * const kv = kvb->kv+i;
284         #define N 20
285         int ovec[N * 3];
286         #undef N
287         int n = pcre_exec(kv->key, kv->key_extra, CONST_BUF_LEN(input),
288                           0, 0, ovec, sizeof(ovec)/sizeof(int));
289         if (n < 0) {
290             if (n != PCRE_ERROR_NOMATCH) {
291                 return HANDLER_ERROR;
292             }
293         }
294         else if (buffer_string_is_empty(&kv->value)) {
295             /* short-circuit if blank replacement pattern
296              * (do not attempt to match against remaining kvb rules) */
297             ctx->m = i;
298             return HANDLER_GO_ON;
299         }
300         else { /* it matched */
301             const char **list;
302             ctx->m = i;
303             pcre_get_substring_list(input->ptr, ovec, n, &list);
304             pcre_keyvalue_buffer_subst(result, &kv->value, list, n, ctx);
305             pcre_free(list);
306             return HANDLER_FINISHED;
307         }
308     }
309 
310     return HANDLER_GO_ON;
311 }
312 #else
313 handler_t pcre_keyvalue_buffer_process(const pcre_keyvalue_buffer *kvb, pcre_keyvalue_ctx *ctx, const buffer *input, buffer *result) {
314     UNUSED(kvb);
315     UNUSED(ctx);
316     UNUSED(input);
317     UNUSED(result);
318     return HANDLER_GO_ON;
319 }
320 #endif
321 
322 
323 /* modified from burl_normalize_basic() to handle %% extra encoding layer */
324 
325 /* c (char) and n (nibble) MUST be unsigned integer types */
326 #define li_cton(c,n) \
327   (((n) = (c) - '0') <= 9 || (((n) = ((c)&0xdf) - 'A') <= 5 ? ((n) += 10) : 0))
328 
329 static void pcre_keyvalue_burl_percent_toupper (buffer *b)
330 {
331     const unsigned char * const s = (unsigned char *)b->ptr;
332     const int used = (int)buffer_string_length(b);
333     unsigned int n1, n2;
334     for (int i = 0; i < used; ++i) {
335         if (s[i]=='%' && li_cton(s[i+1],n1) && li_cton(s[i+2],n2)) {
336             if (s[i+1] >= 'a') b->ptr[i+1] &= 0xdf; /* uppercase hex */
337             if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */
338             i+=2;
339         }
340     }
341 }
342 
343 static void pcre_keyvalue_burl_percent_percent_toupper (buffer *b)
344 {
345     const unsigned char * const s = (unsigned char *)b->ptr;
346     const int used = (int)buffer_string_length(b);
347     unsigned int n1, n2;
348     for (int i = 0; i < used; ++i) {
349         if (s[i] == '%' && s[i+1]=='%'
350             && li_cton(s[i+2],n1) && li_cton(s[i+3],n2)) {
351             if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */
352             if (s[i+3] >= 'a') b->ptr[i+3] &= 0xdf; /* uppercase hex */
353             i+=3;
354         }
355     }
356 }
357 
358 static const char hex_chars_uc[] = "0123456789ABCDEF";
359 
360 static void pcre_keyvalue_burl_percent_high_UTF8 (buffer *b, buffer *t)
361 {
362     const unsigned char * const s = (unsigned char *)b->ptr;
363     unsigned char *p;
364     const int used = (int)buffer_string_length(b);
365     unsigned int count = 0, j = 0;
366     for (int i = 0; i < used; ++i) {
367         if (s[i] > 0x7F) ++count;
368     }
369     if (0 == count) return;
370 
371     p = (unsigned char *)buffer_string_prepare_copy(t, used+(count*2));
372     for (int i = 0; i < used; ++i, ++j) {
373         if (s[i] <= 0x7F)
374             p[j] = s[i];
375         else {
376             p[j]   = '%';
377             p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF];
378             p[++j] = hex_chars_uc[s[i] & 0xF];
379         }
380     }
381     buffer_commit(t, j);
382     buffer_copy_buffer(b, t);
383 }
384 
385 static void pcre_keyvalue_burl_percent_percent_high_UTF8 (buffer *b, buffer *t)
386 {
387     const unsigned char * const s = (unsigned char *)b->ptr;
388     unsigned char *p;
389     const int used = (int)buffer_string_length(b);
390     unsigned int count = 0, j = 0;
391     for (int i = 0; i < used; ++i) {
392         if (s[i] > 0x7F) ++count;
393     }
394     if (0 == count) return;
395 
396     p = (unsigned char *)buffer_string_prepare_copy(t, used+(count*3));
397     for (int i = 0; i < used; ++i, ++j) {
398         if (s[i] <= 0x7F)
399             p[j] = s[i];
400         else {
401             p[j]   = '%';
402             p[++j] = '%';
403             p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF];
404             p[++j] = hex_chars_uc[s[i] & 0xF];
405         }
406     }
407     buffer_commit(t, j);
408     buffer_copy_buffer(b, t);
409 }
410 
411 /* Basic normalization of regex and regex replacement to mirror some of
412  * the normalizations performed on request URI (for better compatibility).
413  * Note: not currently attempting to replace unnecessary percent-encoding
414  * (would need to know if regex was intended to match url-path or
415  *  query-string or both, and then would have to regex-escape if those
416  *  chars where special regex chars such as . * + ? ( ) [ ] | and more)
417  * Not attempting to percent-encode chars which should be encoded, again
418  * since regex might target url-path, query-string, or both, and we would
419  * have to avoid percent-encoding special regex chars.
420  * Also not attempting to detect unnecessarily regex-escape in, e.g. %\x\x
421  * Preserve improper %-encoded sequences which are not %XX (using hex chars)
422  * Intentionally not performing path simplification (e.g. ./ ../)
423  * If regex-specific normalizations begin to be made to k here,
424  * must revisit callers, e.g. one configfile.c use on non-regex string.
425  * "%%" (percent_percent) is used in regex replacement strings since
426  * otherwise "%n" is used to indicate regex backreference where n is number.
427  */
428 
429 void pcre_keyvalue_burl_normalize_key (buffer *k, buffer *t)
430 {
431     pcre_keyvalue_burl_percent_toupper(k);
432     pcre_keyvalue_burl_percent_high_UTF8(k, t);
433 }
434 
435 void pcre_keyvalue_burl_normalize_value (buffer *v, buffer *t)
436 {
437     pcre_keyvalue_burl_percent_percent_toupper(v);
438     pcre_keyvalue_burl_percent_percent_high_UTF8(v, t);
439 }
440