GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/relayd/relay_http.c Lines: 17 827 2.1 %
Date: 2017-11-07 Branches: 2 739 0.3 %

Line Branch Exec Source
1
/*	$OpenBSD: relay_http.c,v 1.67 2017/09/23 11:56:57 bluhm Exp $	*/
2
3
/*
4
 * Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/queue.h>
21
#include <sys/time.h>
22
#include <sys/socket.h>
23
#include <sys/tree.h>
24
25
#include <netinet/in.h>
26
#include <arpa/inet.h>
27
28
#include <limits.h>
29
#include <stdio.h>
30
#include <stdlib.h>
31
#include <errno.h>
32
#include <string.h>
33
#include <time.h>
34
#include <event.h>
35
#include <fnmatch.h>
36
#include <siphash.h>
37
#include <imsg.h>
38
#include <unistd.h>
39
40
#include "relayd.h"
41
#include "http.h"
42
43
static int	_relay_lookup_url(struct ctl_relay_event *, char *, char *,
44
		    char *, struct kv *);
45
int		 relay_lookup_url(struct ctl_relay_event *,
46
		    const char *, struct kv *);
47
int		 relay_lookup_query(struct ctl_relay_event *, struct kv *);
48
int		 relay_lookup_cookie(struct ctl_relay_event *, const char *,
49
		    struct kv *);
50
void		 relay_read_httpcontent(struct bufferevent *, void *);
51
void		 relay_read_httpchunks(struct bufferevent *, void *);
52
char		*relay_expand_http(struct ctl_relay_event *, char *,
53
		    char *, size_t);
54
int		 relay_writeheader_kv(struct ctl_relay_event *, struct kv *);
55
int		 relay_writeheader_http(struct ctl_relay_event *,
56
		    struct ctl_relay_event *);
57
int		 relay_writerequest_http(struct ctl_relay_event *,
58
		    struct ctl_relay_event *);
59
int		 relay_writeresponse_http(struct ctl_relay_event *,
60
		    struct ctl_relay_event *);
61
void		 relay_reset_http(struct ctl_relay_event *);
62
static int	 relay_httpmethod_cmp(const void *, const void *);
63
static int	 relay_httperror_cmp(const void *, const void *);
64
int		 relay_httpquery_test(struct ctl_relay_event *,
65
		    struct relay_rule *, struct kvlist *);
66
int		 relay_httpheader_test(struct ctl_relay_event *,
67
		    struct relay_rule *, struct kvlist *);
68
int		 relay_httppath_test(struct ctl_relay_event *,
69
		    struct relay_rule *, struct kvlist *);
70
int		 relay_httpurl_test(struct ctl_relay_event *,
71
		    struct relay_rule *, struct kvlist *);
72
int		 relay_httpcookie_test(struct ctl_relay_event *,
73
		    struct relay_rule *, struct kvlist *);
74
int		 relay_apply_actions(struct ctl_relay_event *, struct kvlist *);
75
int		 relay_match_actions(struct ctl_relay_event *,
76
		    struct relay_rule *, struct kvlist *, struct kvlist *);
77
void		 relay_httpdesc_free(struct http_descriptor *);
78
79
static struct relayd	*env = NULL;
80
81
static struct http_method	 http_methods[] = HTTP_METHODS;
82
static struct http_error	 http_errors[] = HTTP_ERRORS;
83
84
void
85
relay_http(struct relayd *x_env)
86
{
87
1924
	if (x_env != NULL)
88
		env = x_env;
89
90
	DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid());
91
92
	/* Sort the HTTP lookup arrays */
93
962
	qsort(http_methods, sizeof(http_methods) /
94
	    sizeof(http_methods[0]) - 1,
95
	    sizeof(http_methods[0]), relay_httpmethod_cmp);
96
962
	qsort(http_errors, sizeof(http_errors) /
97
	    sizeof(http_errors[0]) - 1,
98
	    sizeof(http_errors[0]), relay_httperror_cmp);
99
962
}
100
101
void
102
relay_http_init(struct relay *rlay)
103
{
104
	rlay->rl_proto->close = relay_close_http;
105
106
	relay_http(NULL);
107
108
	/* Calculate skip step for the filter rules (may take a while) */
109
	relay_calc_skip_steps(&rlay->rl_proto->rules);
110
}
111
112
int
113
relay_httpdesc_init(struct ctl_relay_event *cre)
114
{
115
	struct http_descriptor	*desc;
116
117
	if ((desc = calloc(1, sizeof(*desc))) == NULL)
118
		return (-1);
119
120
	RB_INIT(&desc->http_headers);
121
	cre->desc = desc;
122
123
	return (0);
124
}
125
126
void
127
relay_httpdesc_free(struct http_descriptor *desc)
128
{
129
	free(desc->http_path);
130
	desc->http_path = NULL;
131
	free(desc->http_query);
132
	desc->http_query = NULL;
133
	free(desc->http_version);
134
	desc->http_version = NULL;
135
	free(desc->query_key);
136
	desc->query_key = NULL;
137
	free(desc->query_val);
138
	desc->query_val = NULL;
139
	kv_purge(&desc->http_headers);
140
	desc->http_lastheader = NULL;
141
}
142
143
void
144
relay_read_http(struct bufferevent *bev, void *arg)
145
{
146
	struct ctl_relay_event	*cre = arg;
147
	struct http_descriptor	*desc = cre->desc;
148
	struct rsession		*con = cre->con;
149
	struct relay		*rlay = con->se_relay;
150
	struct protocol		*proto = rlay->rl_proto;
151
	struct evbuffer		*src = EVBUFFER_INPUT(bev);
152
	char			*line = NULL, *key, *value;
153
	char			*urlproto, *host, *path;
154
	int			 action, unique, ret;
155
	const char		*errstr;
156
	size_t			 size, linelen;
157
	struct kv		*hdr = NULL;
158
159
	getmonotime(&con->se_tv_last);
160
	cre->timedout = 0;
161
162
	size = EVBUFFER_LENGTH(src);
163
	DPRINTF("%s: session %d: size %lu, to read %lld",
164
	    __func__, con->se_id, size, cre->toread);
165
	if (!size) {
166
		if (cre->dir == RELAY_DIR_RESPONSE)
167
			return;
168
		cre->toread = TOREAD_HTTP_HEADER;
169
		goto done;
170
	}
171
172
	while (!cre->done) {
173
		line = evbuffer_readln(src, &linelen, EVBUFFER_EOL_CRLF);
174
		if (line == NULL)
175
			break;
176
177
		/*
178
		 * An empty line indicates the end of the request.
179
		 * libevent already stripped the \r\n for us.
180
		 */
181
		if (linelen == 0) {
182
			cre->done = 1;
183
			free(line);
184
			break;
185
		}
186
		key = line;
187
188
		/* Limit the total header length minus \r\n */
189
		cre->headerlen += linelen;
190
		if (cre->headerlen > RELAY_MAXHEADERLENGTH) {
191
			free(line);
192
			relay_abort_http(con, 413, "request too large", 0);
193
			return;
194
		}
195
196
		/*
197
		 * The first line is the GET/POST/PUT/... request,
198
		 * subsequent lines are HTTP headers.
199
		 */
200
		if (++cre->line == 1)
201
			value = strchr(key, ' ');
202
		else if (*key == ' ' || *key == '\t')
203
			/* Multiline headers wrap with a space or tab */
204
			value = NULL;
205
		else
206
			value = strchr(key, ':');
207
		if (value == NULL) {
208
			if (cre->line <= 2) {
209
				free(line);
210
				relay_abort_http(con, 400, "malformed", 0);
211
				return;
212
			}
213
214
			/* Append line to the last header, if present */
215
			if (kv_extend(&desc->http_headers,
216
			    desc->http_lastheader, line) == NULL) {
217
				free(line);
218
				goto fail;
219
			}
220
221
			free(line);
222
			continue;
223
		}
224
		if (*value == ':') {
225
			*value++ = '\0';
226
			value += strspn(value, " \t\r\n");
227
		} else {
228
			*value++ = '\0';
229
		}
230
231
		DPRINTF("%s: session %d: header '%s: %s'", __func__,
232
		    con->se_id, key, value);
233
234
		/*
235
		 * Identify and handle specific HTTP request methods
236
		 */
237
		if (cre->line == 1 && cre->dir == RELAY_DIR_RESPONSE) {
238
			desc->http_method = HTTP_METHOD_RESPONSE;
239
			/*
240
			 * Decode response path and query
241
			 */
242
			desc->http_version = strdup(line);
243
			if (desc->http_version == NULL) {
244
				free(line);
245
				goto fail;
246
			}
247
			desc->http_rescode = strdup(value);
248
			if (desc->http_rescode == NULL) {
249
				free(line);
250
				goto fail;
251
			}
252
			desc->http_resmesg = strchr(desc->http_rescode, ' ');
253
			if (desc->http_resmesg == NULL) {
254
				free(line);
255
				goto fail;
256
			}
257
			*desc->http_resmesg++ = '\0';
258
			if ((desc->http_resmesg = strdup(desc->http_resmesg))
259
			    == NULL) {
260
				free(line);
261
				goto fail;
262
			}
263
			desc->http_status = strtonum(desc->http_rescode, 100,
264
			    599, &errstr);
265
			if (errstr) {
266
				DPRINTF("%s: http_status %s: errno %d, %s",
267
				    __func__, desc->http_rescode, errno,
268
				    errstr);
269
				free(line);
270
				goto fail;
271
			}
272
			DPRINTF("http_version %s http_rescode %s "
273
			    "http_resmesg %s", desc->http_version,
274
			    desc->http_rescode, desc->http_resmesg);
275
			goto lookup;
276
		} else if (cre->line == 1 && cre->dir == RELAY_DIR_REQUEST) {
277
			if ((desc->http_method = relay_httpmethod_byname(key))
278
			    == HTTP_METHOD_NONE) {
279
				free(line);
280
				goto fail;
281
			}
282
			/*
283
			 * Decode request path and query
284
			 */
285
			desc->http_path = strdup(value);
286
			if (desc->http_path == NULL) {
287
				free(line);
288
				goto fail;
289
			}
290
			desc->http_version = strchr(desc->http_path, ' ');
291
			if (desc->http_version == NULL) {
292
				free(line);
293
				goto fail;
294
			}
295
			*desc->http_version++ = '\0';
296
			desc->http_query = strchr(desc->http_path, '?');
297
			if (desc->http_query != NULL)
298
				*desc->http_query++ = '\0';
299
300
			/*
301
			 * Have to allocate the strings because they could
302
			 * be changed independently by the filters later.
303
			 */
304
			if ((desc->http_version =
305
			    strdup(desc->http_version)) == NULL) {
306
				free(line);
307
				goto fail;
308
			}
309
			if (desc->http_query != NULL &&
310
			    (desc->http_query =
311
			    strdup(desc->http_query)) == NULL) {
312
				free(line);
313
				goto fail;
314
			}
315
		} else if (desc->http_method != HTTP_METHOD_NONE &&
316
		    strcasecmp("Content-Length", key) == 0) {
317
			/*
318
			 * These methods should not have a body
319
			 * and thus no Content-Length header.
320
			 */
321
			if (desc->http_method == HTTP_METHOD_TRACE ||
322
			    desc->http_method == HTTP_METHOD_CONNECT) {
323
				relay_abort_http(con, 400, "malformed", 0);
324
				goto abort;
325
			}
326
			/*
327
			 * response with a status code of 1xx
328
			 * (Informational) or 204 (No Content) MUST
329
			 * not have a Content-Length (rfc 7230 3.3.3)
330
			 */
331
			if (desc->http_method == HTTP_METHOD_RESPONSE && (
332
			    ((desc->http_status >= 100 &&
333
			    desc->http_status < 200) ||
334
			    desc->http_status == 204))) {
335
				relay_abort_http(con, 500,
336
				    "Internal Server Error", 0);
337
				goto abort;
338
			}
339
			/*
340
			 * Need to read data from the client after the
341
			 * HTTP header.
342
			 * XXX What about non-standard clients not using
343
			 * the carriage return? And some browsers seem to
344
			 * include the line length in the content-length.
345
			 */
346
			cre->toread = strtonum(value, 0, LLONG_MAX, &errstr);
347
			if (errstr) {
348
				relay_abort_http(con, 500, errstr, 0);
349
				goto abort;
350
			}
351
		}
352
 lookup:
353
		if (strcasecmp("Transfer-Encoding", key) == 0 &&
354
		    strcasecmp("chunked", value) == 0)
355
			desc->http_chunked = 1;
356
357
		/* The following header should only occur once */
358
		if (strcasecmp("Host", key) == 0) {
359
			unique = 1;
360
361
			/*
362
			 * The path may contain a URL.  The host in the
363
			 * URL has to match the Host: value.
364
			 */
365
			if (parse_url(desc->http_path,
366
			    &urlproto, &host, &path) == 0) {
367
				ret = strcasecmp(host, value);
368
				free(urlproto);
369
				free(host);
370
				free(path);
371
				if (ret != 0) {
372
					relay_abort_http(con, 400,
373
					    "malformed host", 0);
374
					goto abort;
375
				}
376
			}
377
		} else
378
			unique = 0;
379
380
		if (cre->line != 1) {
381
			if ((hdr = kv_add(&desc->http_headers, key,
382
			    value, unique)) == NULL) {
383
				relay_abort_http(con, 400,
384
				    "malformed header", 0);
385
				goto abort;
386
			}
387
			desc->http_lastheader = hdr;
388
		}
389
390
		free(line);
391
	}
392
	if (cre->done) {
393
		if (desc->http_method == HTTP_METHOD_NONE) {
394
			relay_abort_http(con, 406, "no method", 0);
395
			return;
396
		}
397
398
		action = relay_test(proto, cre);
399
		switch (action) {
400
		case RES_FAIL:
401
			relay_close(con, "filter rule failed");
402
			return;
403
		case RES_BAD:
404
			relay_abort_http(con, 400, "Bad Request",
405
			    con->se_label);
406
			return;
407
		case RES_INTERNAL:
408
			relay_abort_http(con, 500, "Internal Server Error",
409
			    con->se_label);
410
			return;
411
		}
412
		if (action != RES_PASS) {
413
			relay_abort_http(con, 403, "Forbidden", con->se_label);
414
			return;
415
		}
416
417
		switch (desc->http_method) {
418
		case HTTP_METHOD_CONNECT:
419
			/* Data stream */
420
			cre->toread = TOREAD_UNLIMITED;
421
			bev->readcb = relay_read;
422
			break;
423
		case HTTP_METHOD_GET:
424
		case HTTP_METHOD_HEAD:
425
		/* WebDAV methods */
426
		case HTTP_METHOD_COPY:
427
		case HTTP_METHOD_MOVE:
428
			cre->toread = 0;
429
			break;
430
		case HTTP_METHOD_DELETE:
431
		case HTTP_METHOD_OPTIONS:
432
		case HTTP_METHOD_POST:
433
		case HTTP_METHOD_PUT:
434
		case HTTP_METHOD_RESPONSE:
435
		/* WebDAV methods */
436
		case HTTP_METHOD_PROPFIND:
437
		case HTTP_METHOD_PROPPATCH:
438
		case HTTP_METHOD_MKCOL:
439
		case HTTP_METHOD_LOCK:
440
		case HTTP_METHOD_UNLOCK:
441
		case HTTP_METHOD_VERSION_CONTROL:
442
		case HTTP_METHOD_REPORT:
443
		case HTTP_METHOD_CHECKOUT:
444
		case HTTP_METHOD_CHECKIN:
445
		case HTTP_METHOD_UNCHECKOUT:
446
		case HTTP_METHOD_MKWORKSPACE:
447
		case HTTP_METHOD_UPDATE:
448
		case HTTP_METHOD_LABEL:
449
		case HTTP_METHOD_MERGE:
450
		case HTTP_METHOD_BASELINE_CONTROL:
451
		case HTTP_METHOD_MKACTIVITY:
452
		case HTTP_METHOD_ORDERPATCH:
453
		case HTTP_METHOD_ACL:
454
		case HTTP_METHOD_MKREDIRECTREF:
455
		case HTTP_METHOD_UPDATEREDIRECTREF:
456
		case HTTP_METHOD_SEARCH:
457
		case HTTP_METHOD_PATCH:
458
			/* HTTP request payload */
459
			if (cre->toread > 0)
460
				bev->readcb = relay_read_httpcontent;
461
462
			/* Single-pass HTTP body */
463
			if (cre->toread < 0) {
464
				cre->toread = TOREAD_UNLIMITED;
465
				bev->readcb = relay_read;
466
			}
467
			break;
468
		default:
469
			/* HTTP handler */
470
			cre->toread = TOREAD_HTTP_HEADER;
471
			bev->readcb = relay_read_http;
472
			break;
473
		}
474
		if (desc->http_chunked) {
475
			/* Chunked transfer encoding */
476
			cre->toread = TOREAD_HTTP_CHUNK_LENGTH;
477
			bev->readcb = relay_read_httpchunks;
478
		}
479
480
		if (cre->dir == RELAY_DIR_REQUEST) {
481
			if (relay_writerequest_http(cre->dst, cre) == -1)
482
			    goto fail;
483
		} else {
484
			if (relay_writeresponse_http(cre->dst, cre) == -1)
485
			    goto fail;
486
		}
487
		if (relay_bufferevent_print(cre->dst, "\r\n") == -1 ||
488
		    relay_writeheader_http(cre->dst, cre) == -1 ||
489
		    relay_bufferevent_print(cre->dst, "\r\n") == -1)
490
			goto fail;
491
492
		relay_reset_http(cre);
493
 done:
494
		if (cre->dir == RELAY_DIR_REQUEST && cre->toread <= 0 &&
495
		    cre->dst->state != STATE_CONNECTED) {
496
			if (rlay->rl_conf.fwdmode == FWD_TRANS) {
497
				relay_bindanyreq(con, 0, IPPROTO_TCP);
498
				return;
499
			}
500
			if (relay_connect(con) == -1) {
501
				relay_abort_http(con, 502, "session failed", 0);
502
				return;
503
			}
504
		}
505
	}
506
	if (con->se_done) {
507
		relay_close(con, "last http read (done)");
508
		return;
509
	}
510
	switch (relay_splice(cre)) {
511
	case -1:
512
		relay_close(con, strerror(errno));
513
	case 1:
514
		return;
515
	case 0:
516
		break;
517
	}
518
	bufferevent_enable(bev, EV_READ);
519
	if (EVBUFFER_LENGTH(src) && bev->readcb != relay_read_http)
520
		bev->readcb(bev, arg);
521
	/* The callback readcb() might have freed the session. */
522
	return;
523
 fail:
524
	relay_abort_http(con, 500, strerror(errno), 0);
525
	return;
526
 abort:
527
	free(line);
528
}
529
530
void
531
relay_read_httpcontent(struct bufferevent *bev, void *arg)
532
{
533
	struct ctl_relay_event	*cre = arg;
534
	struct rsession		*con = cre->con;
535
	struct protocol		*proto = con->se_relay->rl_proto;
536
537
	struct evbuffer		*src = EVBUFFER_INPUT(bev);
538
	size_t			 size;
539
540
	getmonotime(&con->se_tv_last);
541
	cre->timedout = 0;
542
543
	size = EVBUFFER_LENGTH(src);
544
	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
545
	    con->se_id, size, cre->toread);
546
	if (!size)
547
		return;
548
	if (relay_spliceadjust(cre) == -1)
549
		goto fail;
550
551
	if (cre->toread > 0) {
552
		/* Read content data */
553
		if ((off_t)size > cre->toread) {
554
			size = cre->toread;
555
			if (relay_bufferevent_write_chunk(cre->dst, src, size)
556
			    == -1)
557
				goto fail;
558
			cre->toread = 0;
559
		} else {
560
			if (relay_bufferevent_write_buffer(cre->dst, src) == -1)
561
				goto fail;
562
			cre->toread -= size;
563
		}
564
		DPRINTF("%s: done, size %lu, to read %lld", __func__,
565
		    size, cre->toread);
566
	}
567
	if (cre->toread == 0) {
568
		cre->toread = TOREAD_HTTP_HEADER;
569
		bev->readcb = relay_read_http;
570
	}
571
	if (con->se_done)
572
		goto done;
573
	bufferevent_enable(bev, EV_READ);
574
575
	if (cre->dst->bev && EVBUFFER_LENGTH(EVBUFFER_OUTPUT(cre->dst->bev)) >
576
	    (size_t)RELAY_MAX_PREFETCH * proto->tcpbufsiz)
577
		bufferevent_disable(cre->bev, EV_READ);
578
579
	if (bev->readcb != relay_read_httpcontent)
580
		bev->readcb(bev, arg);
581
	/* The callback readcb() might have freed the session. */
582
	return;
583
 done:
584
	relay_close(con, "last http content read");
585
	return;
586
 fail:
587
	relay_close(con, strerror(errno));
588
}
589
590
void
591
relay_read_httpchunks(struct bufferevent *bev, void *arg)
592
{
593
	struct ctl_relay_event	*cre = arg;
594
	struct rsession		*con = cre->con;
595
	struct protocol		*proto = con->se_relay->rl_proto;
596
	struct evbuffer		*src = EVBUFFER_INPUT(bev);
597
	char			*line;
598
	long long		 llval;
599
	size_t			 size, linelen;
600
601
	getmonotime(&con->se_tv_last);
602
	cre->timedout = 0;
603
604
	size = EVBUFFER_LENGTH(src);
605
	DPRINTF("%s: session %d: size %lu, to read %lld", __func__,
606
	    con->se_id, size, cre->toread);
607
	if (!size)
608
		return;
609
	if (relay_spliceadjust(cre) == -1)
610
		goto fail;
611
612
	if (cre->toread > 0) {
613
		/* Read chunk data */
614
		if ((off_t)size > cre->toread) {
615
			size = cre->toread;
616
			if (relay_bufferevent_write_chunk(cre->dst, src, size)
617
			    == -1)
618
				goto fail;
619
			cre->toread = 0;
620
		} else {
621
			if (relay_bufferevent_write_buffer(cre->dst, src) == -1)
622
				goto fail;
623
			cre->toread -= size;
624
		}
625
		DPRINTF("%s: done, size %lu, to read %lld", __func__,
626
		    size, cre->toread);
627
	}
628
	switch (cre->toread) {
629
	case TOREAD_HTTP_CHUNK_LENGTH:
630
		line = evbuffer_readln(src, &linelen, EVBUFFER_EOL_CRLF);
631
		if (line == NULL) {
632
			/* Ignore empty line, continue */
633
			bufferevent_enable(bev, EV_READ);
634
			return;
635
		}
636
		if (linelen == 0) {
637
			free(line);
638
			goto next;
639
		}
640
641
		/*
642
		 * Read prepended chunk size in hex, ignore the trailer.
643
		 * The returned signed value must not be negative.
644
		 */
645
		if (sscanf(line, "%llx", &llval) != 1 || llval < 0) {
646
			free(line);
647
			relay_close(con, "invalid chunk size");
648
			return;
649
		}
650
651
		if (relay_bufferevent_print(cre->dst, line) == -1 ||
652
		    relay_bufferevent_print(cre->dst, "\r\n") == -1) {
653
			free(line);
654
			goto fail;
655
		}
656
		free(line);
657
658
		if ((cre->toread = llval) == 0) {
659
			DPRINTF("%s: last chunk", __func__);
660
			cre->toread = TOREAD_HTTP_CHUNK_TRAILER;
661
		}
662
		break;
663
	case TOREAD_HTTP_CHUNK_TRAILER:
664
		/* Last chunk is 0 bytes followed by trailer and empty line */
665
		line = evbuffer_readln(src, &linelen, EVBUFFER_EOL_CRLF);
666
		if (line == NULL) {
667
			/* Ignore empty line, continue */
668
			bufferevent_enable(bev, EV_READ);
669
			return;
670
		}
671
		if (relay_bufferevent_print(cre->dst, line) == -1 ||
672
		    relay_bufferevent_print(cre->dst, "\r\n") == -1) {
673
			free(line);
674
			goto fail;
675
		}
676
		if (linelen == 0) {
677
			/* Switch to HTTP header mode */
678
			cre->toread = TOREAD_HTTP_HEADER;
679
			bev->readcb = relay_read_http;
680
		}
681
		free(line);
682
		break;
683
	case 0:
684
		/* Chunk is terminated by an empty newline */
685
		line = evbuffer_readln(src, &linelen, EVBUFFER_EOL_CRLF);
686
		free(line);
687
		if (relay_bufferevent_print(cre->dst, "\r\n") == -1)
688
			goto fail;
689
		cre->toread = TOREAD_HTTP_CHUNK_LENGTH;
690
		break;
691
	}
692
693
 next:
694
	if (con->se_done)
695
		goto done;
696
	bufferevent_enable(bev, EV_READ);
697
698
	if (cre->dst->bev && EVBUFFER_LENGTH(EVBUFFER_OUTPUT(cre->dst->bev)) >
699
	    (size_t)RELAY_MAX_PREFETCH * proto->tcpbufsiz)
700
		bufferevent_disable(cre->bev, EV_READ);
701
702
	if (EVBUFFER_LENGTH(src))
703
		bev->readcb(bev, arg);
704
	/* The callback readcb() might have freed the session. */
705
	return;
706
707
 done:
708
	relay_close(con, "last http chunk read (done)");
709
	return;
710
 fail:
711
	relay_close(con, strerror(errno));
712
}
713
714
void
715
relay_reset_http(struct ctl_relay_event *cre)
716
{
717
	struct http_descriptor	*desc = cre->desc;
718
719
	relay_httpdesc_free(desc);
720
	desc->http_method = 0;
721
	desc->http_chunked = 0;
722
	cre->headerlen = 0;
723
	cre->line = 0;
724
	cre->done = 0;
725
}
726
727
static int
728
_relay_lookup_url(struct ctl_relay_event *cre, char *host, char *path,
729
    char *query, struct kv *kv)
730
{
731
	struct rsession		*con = cre->con;
732
	char			*val, *md = NULL;
733
	int			 ret = RES_FAIL;
734
	const char		*str = NULL;
735
736
	if (asprintf(&val, "%s%s%s%s",
737
	    host, path,
738
	    query == NULL ? "" : "?",
739
	    query == NULL ? "" : query) == -1) {
740
		relay_abort_http(con, 500, "failed to allocate URL", 0);
741
		return (RES_FAIL);
742
	}
743
744
	switch (kv->kv_digest) {
745
	case DIGEST_SHA1:
746
	case DIGEST_MD5:
747
		if ((md = digeststr(kv->kv_digest,
748
		    val, strlen(val), NULL)) == NULL) {
749
			relay_abort_http(con, 500,
750
			    "failed to allocate digest", 0);
751
			goto fail;
752
		}
753
		str = md;
754
		break;
755
	case DIGEST_NONE:
756
		str = val;
757
		break;
758
	}
759
760
	DPRINTF("%s: session %d: %s, %s: %d", __func__, con->se_id,
761
	    str, kv->kv_key, strcasecmp(kv->kv_key, str));
762
763
	if (strcasecmp(kv->kv_key, str) == 0) {
764
		ret = RES_DROP;
765
		goto fail;
766
	}
767
768
	ret = RES_PASS;
769
 fail:
770
	free(md);
771
	free(val);
772
	return (ret);
773
}
774
775
int
776
relay_lookup_url(struct ctl_relay_event *cre, const char *host, struct kv *kv)
777
{
778
	struct http_descriptor	*desc = (struct http_descriptor *)cre->desc;
779
	int			 i, j, dots;
780
	char			*hi[RELAY_MAXLOOKUPLEVELS], *p, *pp, *c, ch;
781
	char			 ph[HOST_NAME_MAX+1];
782
	int			 ret;
783
784
	if (desc->http_path == NULL)
785
		return (RES_PASS);
786
787
	/*
788
	 * This is an URL lookup algorithm inspired by
789
	 * http://code.google.com/apis/safebrowsing/
790
	 *     developers_guide.html#PerformingLookups
791
	 */
792
793
	DPRINTF("%s: host '%s', path '%s', query '%s'",
794
	    __func__, host, desc->http_path,
795
	    desc->http_query == NULL ? "" : desc->http_query);
796
797
	if (canonicalize_host(host, ph, sizeof(ph)) == NULL) {
798
		return (RES_BAD);
799
	}
800
801
	bzero(hi, sizeof(hi));
802
	for (dots = -1, i = strlen(ph) - 1; i > 0; i--) {
803
		if (ph[i] == '.' && ++dots)
804
			hi[dots - 1] = &ph[i + 1];
805
		if (dots > (RELAY_MAXLOOKUPLEVELS - 2))
806
			break;
807
	}
808
	if (dots == -1)
809
		dots = 0;
810
	hi[dots] = ph;
811
812
	if ((pp = strdup(desc->http_path)) == NULL) {
813
		return (RES_INTERNAL);
814
	}
815
	for (i = (RELAY_MAXLOOKUPLEVELS - 1); i >= 0; i--) {
816
		if (hi[i] == NULL)
817
			continue;
818
819
		/* 1. complete path with query */
820
		if (desc->http_query != NULL)
821
			if ((ret = _relay_lookup_url(cre, hi[i],
822
			    pp, desc->http_query, kv)) != RES_PASS)
823
				goto done;
824
825
		/* 2. complete path without query */
826
		if ((ret = _relay_lookup_url(cre, hi[i],
827
		    pp, NULL, kv)) != RES_PASS)
828
			goto done;
829
830
		/* 3. traverse path */
831
		for (j = 0, p = strchr(pp, '/');
832
		    p != NULL; p = strchr(p, '/'), j++) {
833
			if (j > (RELAY_MAXLOOKUPLEVELS - 2) || *(++p) == '\0')
834
				break;
835
			c = &pp[p - pp];
836
			ch = *c;
837
			*c = '\0';
838
			if ((ret = _relay_lookup_url(cre, hi[i],
839
			    pp, NULL, kv)) != RES_PASS)
840
				goto done;
841
			*c = ch;
842
		}
843
	}
844
845
	ret = RES_PASS;
846
 done:
847
	free(pp);
848
	return (ret);
849
}
850
851
int
852
relay_lookup_cookie(struct ctl_relay_event *cre, const char *str,
853
    struct kv *kv)
854
{
855
	char			*val, *ptr, *key, *value;
856
	int			 ret;
857
858
	if ((val = strdup(str)) == NULL) {
859
		return (RES_INTERNAL);
860
	}
861
862
	for (ptr = val; ptr != NULL && strlen(ptr);) {
863
		if (*ptr == ' ')
864
			*ptr++ = '\0';
865
		key = ptr;
866
		if ((ptr = strchr(ptr, ';')) != NULL)
867
			*ptr++ = '\0';
868
		/*
869
		 * XXX We do not handle attributes
870
		 * ($Path, $Domain, or $Port)
871
		 */
872
		if (*key == '$')
873
			continue;
874
875
		if ((value =
876
		    strchr(key, '=')) == NULL ||
877
		    strlen(value) < 1)
878
			continue;
879
		*value++ = '\0';
880
		if (*value == '"')
881
			*value++ = '\0';
882
		if (value[strlen(value) - 1] == '"')
883
			value[strlen(value) - 1] = '\0';
884
885
		DPRINTF("%s: key %s = %s, %s = %s : %d",
886
		    __func__, key, value, kv->kv_key, kv->kv_value,
887
		    strcasecmp(kv->kv_key, key));
888
889
		if (strcasecmp(kv->kv_key, key) == 0 &&
890
		    ((kv->kv_value == NULL) ||
891
		    (fnmatch(kv->kv_value, value,
892
		    FNM_CASEFOLD) != FNM_NOMATCH))) {
893
			ret = RES_DROP;
894
			goto done;
895
		}
896
	}
897
898
	ret = RES_PASS;
899
900
 done:
901
	free(val);
902
	return (ret);
903
}
904
905
int
906
relay_lookup_query(struct ctl_relay_event *cre, struct kv *kv)
907
{
908
	struct http_descriptor	*desc = cre->desc;
909
	struct kv		*match = &desc->http_matchquery;
910
	char			*val, *ptr, *tmpkey = NULL, *tmpval = NULL;
911
	int			 ret = -1;
912
913
	if (desc->http_query == NULL)
914
		return (-1);
915
	if ((val = strdup(desc->http_query)) == NULL) {
916
		return (RES_INTERNAL);
917
	}
918
919
	ptr = val;
920
	while (ptr != NULL && strlen(ptr)) {
921
		tmpkey = ptr;
922
		if ((ptr = strchr(ptr, '&')) != NULL)
923
			*ptr++ = '\0';
924
		if ((tmpval = strchr(tmpkey, '=')) == NULL || strlen(tmpval)
925
		    < 1)
926
			continue;
927
		*tmpval++ = '\0';
928
929
		if (fnmatch(kv->kv_key, tmpkey, 0) != FNM_NOMATCH &&
930
		    (kv->kv_value == NULL || fnmatch(kv->kv_value, tmpval, 0)
931
		    != FNM_NOMATCH))
932
			break;
933
		else
934
			tmpkey = NULL;
935
	}
936
937
	if (tmpkey == NULL || tmpval == NULL)
938
		goto done;
939
940
	match->kv_key = strdup(tmpkey);
941
	if (match->kv_key == NULL)
942
		goto done;
943
	match->kv_value = strdup(tmpval);
944
	if (match->kv_key == NULL)
945
		goto done;
946
	ret = 0;
947
948
 done:
949
	free(val);
950
	return (ret);
951
}
952
953
ssize_t
954
relay_http_time(time_t t, char *tmbuf, size_t len)
955
{
956
	struct tm		 tm;
957
958
	/* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */
959
	if (t == -1 || gmtime_r(&t, &tm) == NULL)
960
		return (-1);
961
	else
962
		return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm));
963
}
964
965
void
966
relay_abort_http(struct rsession *con, u_int code, const char *msg,
967
    u_int16_t labelid)
968
{
969
	struct relay		*rlay = con->se_relay;
970
	struct bufferevent	*bev = con->se_in.bev;
971
	const char		*httperr = NULL, *text = "";
972
	char			*httpmsg, *body = NULL;
973
	char			 tmbuf[32], hbuf[128];
974
	const char		*style, *label = NULL;
975
	int			 bodylen;
976
977
	if ((httperr = relay_httperror_byid(code)) == NULL)
978
		httperr = "Unknown Error";
979
980
	if (labelid != 0)
981
		label = label_id2name(labelid);
982
983
	/* In some cases this function may be called from generic places */
984
	if (rlay->rl_proto->type != RELAY_PROTO_HTTP ||
985
	    (rlay->rl_proto->flags & F_RETURN) == 0) {
986
		relay_close(con, msg);
987
		return;
988
	}
989
990
	if (bev == NULL)
991
		goto done;
992
993
	/* Some system information */
994
	if (print_host(&rlay->rl_conf.ss, hbuf, sizeof(hbuf)) == NULL)
995
		goto done;
996
997
	if (relay_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0)
998
		goto done;
999
1000
	/* Do not send details of the Internal Server Error */
1001
	switch (code) {
1002
	case 500:
1003
		break;
1004
	default:
1005
		text = msg;
1006
		break;
1007
	}
1008
1009
	/* A CSS stylesheet allows minimal customization by the user */
1010
	style = (rlay->rl_proto->style != NULL) ? rlay->rl_proto->style :
1011
	    "body { background-color: #a00000; color: white; font-family: "
1012
	    "'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }\n"
1013
	    "hr { border: 0; border-bottom: 1px dashed; }\n";
1014
1015
	/* Generate simple HTTP+HTML error document */
1016
	if ((bodylen = asprintf(&body,
1017
	    "<!DOCTYPE html>\n"
1018
	    "<html>\n"
1019
	    "<head>\n"
1020
	    "<title>%03d %s</title>\n"
1021
	    "<style type=\"text/css\"><!--\n%s\n--></style>\n"
1022
	    "</head>\n"
1023
	    "<body>\n"
1024
	    "<h1>%s</h1>\n"
1025
	    "<div id='m'>%s</div>\n"
1026
	    "<div id='l'>%s</div>\n"
1027
	    "<hr><address>%s at %s port %d</address>\n"
1028
	    "</body>\n"
1029
	    "</html>\n",
1030
	    code, httperr, style, httperr, text,
1031
	    label == NULL ? "" : label,
1032
	    RELAYD_SERVERNAME, hbuf, ntohs(rlay->rl_conf.port))) == -1)
1033
		goto done;
1034
1035
	/* Generate simple HTTP+HTML error document */
1036
	if (asprintf(&httpmsg,
1037
	    "HTTP/1.0 %03d %s\r\n"
1038
	    "Date: %s\r\n"
1039
	    "Server: %s\r\n"
1040
	    "Connection: close\r\n"
1041
	    "Content-Type: text/html\r\n"
1042
	    "Content-Length: %d\r\n"
1043
	    "\r\n"
1044
	    "%s",
1045
	    code, httperr, tmbuf, RELAYD_SERVERNAME, bodylen, body) == -1)
1046
		goto done;
1047
1048
	/* Dump the message without checking for success */
1049
	relay_dump(&con->se_in, httpmsg, strlen(httpmsg));
1050
	free(httpmsg);
1051
1052
 done:
1053
	free(body);
1054
	if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1)
1055
		relay_close(con, msg);
1056
	else {
1057
		relay_close(con, httpmsg);
1058
		free(httpmsg);
1059
	}
1060
}
1061
1062
void
1063
relay_close_http(struct rsession *con)
1064
{
1065
	struct http_descriptor	*desc[2] = {
1066
		con->se_in.desc, con->se_out.desc
1067
	};
1068
	int			 i;
1069
1070
	for (i = 0; i < 2; i++) {
1071
		if (desc[i] == NULL)
1072
			continue;
1073
		relay_httpdesc_free(desc[i]);
1074
		free(desc[i]);
1075
	}
1076
}
1077
1078
char *
1079
relay_expand_http(struct ctl_relay_event *cre, char *val, char *buf,
1080
    size_t len)
1081
{
1082
	struct rsession	*con = cre->con;
1083
	struct relay	*rlay = con->se_relay;
1084
	char		 ibuf[128];
1085
1086
	if (strlcpy(buf, val, len) >= len)
1087
		return (NULL);
1088
1089
	if (strstr(val, "$REMOTE_") != NULL) {
1090
		if (strstr(val, "$REMOTE_ADDR") != NULL) {
1091
			if (print_host(&cre->ss, ibuf, sizeof(ibuf)) == NULL)
1092
				return (NULL);
1093
			if (expand_string(buf, len,
1094
			    "$REMOTE_ADDR", ibuf) != 0)
1095
				return (NULL);
1096
		}
1097
		if (strstr(val, "$REMOTE_PORT") != NULL) {
1098
			snprintf(ibuf, sizeof(ibuf), "%u", ntohs(cre->port));
1099
			if (expand_string(buf, len,
1100
			    "$REMOTE_PORT", ibuf) != 0)
1101
				return (NULL);
1102
		}
1103
	}
1104
	if (strstr(val, "$SERVER_") != NULL) {
1105
		if (strstr(val, "$SERVER_ADDR") != NULL) {
1106
			if (print_host(&rlay->rl_conf.ss,
1107
			    ibuf, sizeof(ibuf)) == NULL)
1108
				return (NULL);
1109
			if (expand_string(buf, len,
1110
			    "$SERVER_ADDR", ibuf) != 0)
1111
				return (NULL);
1112
		}
1113
		if (strstr(val, "$SERVER_PORT") != NULL) {
1114
			snprintf(ibuf, sizeof(ibuf), "%u",
1115
			    ntohs(rlay->rl_conf.port));
1116
			if (expand_string(buf, len,
1117
			    "$SERVER_PORT", ibuf) != 0)
1118
				return (NULL);
1119
		}
1120
		if (strstr(val, "$SERVER_NAME") != NULL) {
1121
			if (expand_string(buf, len,
1122
			    "$SERVER_NAME", RELAYD_SERVERNAME) != 0)
1123
				return (NULL);
1124
		}
1125
	}
1126
	if (strstr(val, "$TIMEOUT") != NULL) {
1127
		snprintf(ibuf, sizeof(ibuf), "%lld",
1128
		    (long long)rlay->rl_conf.timeout.tv_sec);
1129
		if (expand_string(buf, len, "$TIMEOUT", ibuf) != 0)
1130
			return (NULL);
1131
	}
1132
1133
	return (buf);
1134
}
1135
1136
int
1137
relay_writerequest_http(struct ctl_relay_event *dst,
1138
    struct ctl_relay_event *cre)
1139
{
1140
	struct http_descriptor	*desc = (struct http_descriptor *)cre->desc;
1141
	const char		*name = NULL;
1142
1143
	if ((name = relay_httpmethod_byid(desc->http_method)) == NULL)
1144
		return (-1);
1145
1146
	if (relay_bufferevent_print(dst, name) == -1 ||
1147
	    relay_bufferevent_print(dst, " ") == -1 ||
1148
	    relay_bufferevent_print(dst, desc->http_path) == -1 ||
1149
	    (desc->http_query != NULL &&
1150
	    (relay_bufferevent_print(dst, "?") == -1 ||
1151
	    relay_bufferevent_print(dst, desc->http_query) == -1)) ||
1152
	    relay_bufferevent_print(dst, " ") == -1 ||
1153
	    relay_bufferevent_print(dst, desc->http_version) == -1)
1154
		return (-1);
1155
1156
	return (0);
1157
}
1158
1159
int
1160
relay_writeresponse_http(struct ctl_relay_event *dst,
1161
    struct ctl_relay_event *cre)
1162
{
1163
	struct http_descriptor	*desc = (struct http_descriptor *)cre->desc;
1164
1165
	DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version,
1166
	    desc->http_rescode, desc->http_resmesg);
1167
1168
	if (relay_bufferevent_print(dst, desc->http_version) == -1 ||
1169
	    relay_bufferevent_print(dst, " ") == -1 ||
1170
	    relay_bufferevent_print(dst, desc->http_rescode) == -1 ||
1171
	    relay_bufferevent_print(dst, " ") == -1 ||
1172
	    relay_bufferevent_print(dst, desc->http_resmesg) == -1)
1173
		return (-1);
1174
1175
	return (0);
1176
}
1177
1178
int
1179
relay_writeheader_kv(struct ctl_relay_event *dst, struct kv *hdr)
1180
{
1181
	char			*ptr;
1182
	const char		*key;
1183
1184
	if (hdr->kv_flags & KV_FLAG_INVALID)
1185
		return (0);
1186
1187
	/* The key might have been updated in the parent */
1188
	if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL)
1189
		key = hdr->kv_parent->kv_key;
1190
	else
1191
		key = hdr->kv_key;
1192
1193
	ptr = hdr->kv_value;
1194
	if (relay_bufferevent_print(dst, key) == -1 ||
1195
	    (ptr != NULL &&
1196
	    (relay_bufferevent_print(dst, ": ") == -1 ||
1197
	    relay_bufferevent_print(dst, ptr) == -1 ||
1198
	    relay_bufferevent_print(dst, "\r\n") == -1)))
1199
		return (-1);
1200
	DPRINTF("%s: %s: %s", __func__, key,
1201
	    hdr->kv_value == NULL ? "" : hdr->kv_value);
1202
1203
	return (0);
1204
}
1205
1206
int
1207
relay_writeheader_http(struct ctl_relay_event *dst, struct ctl_relay_event
1208
    *cre)
1209
{
1210
	struct kv		*hdr, *kv;
1211
	struct http_descriptor	*desc = (struct http_descriptor *)cre->desc;
1212
1213
	RB_FOREACH(hdr, kvtree, &desc->http_headers) {
1214
		if (relay_writeheader_kv(dst, hdr) == -1)
1215
			return (-1);
1216
		TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) {
1217
			if (relay_writeheader_kv(dst, kv) == -1)
1218
				return (-1);
1219
		}
1220
	}
1221
1222
	return (0);
1223
}
1224
1225
enum httpmethod
1226
relay_httpmethod_byname(const char *name)
1227
{
1228
	enum httpmethod		 id = HTTP_METHOD_NONE;
1229
28
	struct http_method	 method, *res = NULL;
1230
1231
	/* Set up key */
1232
14
	method.method_name = name;
1233
1234
28
	if ((res = bsearch(&method, http_methods,
1235
	    sizeof(http_methods) / sizeof(http_methods[0]) - 1,
1236
14
	    sizeof(http_methods[0]), relay_httpmethod_cmp)) != NULL)
1237
14
		id = res->method_id;
1238
1239
14
	return (id);
1240
14
}
1241
1242
const char *
1243
relay_httpmethod_byid(u_int id)
1244
{
1245
	const char	*name = NULL;
1246
	int		 i;
1247
1248
	for (i = 0; http_methods[i].method_name != NULL; i++) {
1249
		if (http_methods[i].method_id == id) {
1250
			name = http_methods[i].method_name;
1251
			break;
1252
		}
1253
	}
1254
1255
	return (name);
1256
}
1257
1258
static int
1259
relay_httpmethod_cmp(const void *a, const void *b)
1260
{
1261
255948
	const struct http_method *ma = a;
1262
127974
	const struct http_method *mb = b;
1263
1264
	/*
1265
	 * RFC 2616 section 5.1.1 says that the method is case
1266
	 * sensitive so we don't do a strcasecmp here.
1267
	 */
1268
127974
	return (strcmp(ma->method_name, mb->method_name));
1269
}
1270
1271
const char *
1272
relay_httperror_byid(u_int id)
1273
{
1274
	struct http_error	 error, *res = NULL;
1275
1276
	/* Set up key */
1277
	error.error_code = (int)id;
1278
1279
	res = bsearch(&error, http_errors,
1280
	    sizeof(http_errors) / sizeof(http_errors[0]) - 1,
1281
	    sizeof(http_errors[0]), relay_httperror_cmp);
1282
1283
	return (res->error_name);
1284
}
1285
1286
static int
1287
relay_httperror_cmp(const void *a, const void *b)
1288
{
1289
529100
	const struct http_error *ea = a;
1290
264550
	const struct http_error *eb = b;
1291
264550
	return (ea->error_code - eb->error_code);
1292
}
1293
1294
int
1295
relay_httpquery_test(struct ctl_relay_event *cre, struct relay_rule *rule,
1296
    struct kvlist *actions)
1297
{
1298
	struct http_descriptor	*desc = cre->desc;
1299
	struct kv		*match = &desc->http_matchquery;
1300
	struct kv		*kv = &rule->rule_kv[KEY_TYPE_QUERY];
1301
	int			 res = 0;
1302
1303
	if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_QUERY)
1304
		return (0);
1305
	else if (kv->kv_key == NULL)
1306
		return (0);
1307
	else if ((res = relay_lookup_query(cre, kv)) != 0)
1308
		return (res);
1309
1310
	relay_match(actions, kv, match, NULL);
1311
1312
	return (0);
1313
}
1314
1315
int
1316
relay_httpheader_test(struct ctl_relay_event *cre, struct relay_rule *rule,
1317
    struct kvlist *actions)
1318
{
1319
	struct http_descriptor	*desc = cre->desc;
1320
	struct kv		*kv = &rule->rule_kv[KEY_TYPE_HEADER];
1321
	struct kv		*match;
1322
1323
	if (kv->kv_type != KEY_TYPE_HEADER)
1324
		return (0);
1325
1326
	match = kv_find(&desc->http_headers, kv);
1327
1328
	if (kv->kv_option == KEY_OPTION_APPEND ||
1329
	    kv->kv_option == KEY_OPTION_SET) {
1330
		/* header can be NULL and will be added later */
1331
	} else if (match == NULL) {
1332
		/* Fail if header doesn't exist */
1333
		return (-1);
1334
	} else {
1335
		if (fnmatch(kv->kv_key, match->kv_key,
1336
		    FNM_CASEFOLD) == FNM_NOMATCH)
1337
			return (-1);
1338
		if (kv->kv_value != NULL &&
1339
		    match->kv_value != NULL &&
1340
		    fnmatch(kv->kv_value, match->kv_value, 0) == FNM_NOMATCH)
1341
			return (-1);
1342
	}
1343
1344
	relay_match(actions, kv, match, &desc->http_headers);
1345
1346
	return (0);
1347
}
1348
1349
int
1350
relay_httppath_test(struct ctl_relay_event *cre, struct relay_rule *rule,
1351
    struct kvlist *actions)
1352
{
1353
	struct http_descriptor	*desc = cre->desc;
1354
	struct kv		*kv = &rule->rule_kv[KEY_TYPE_PATH];
1355
	struct kv		*match = &desc->http_pathquery;
1356
	const char		*query;
1357
1358
	if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_PATH)
1359
		return (0);
1360
	else if (kv->kv_key == NULL)
1361
		return (0);
1362
	else if (fnmatch(kv->kv_key, desc->http_path, 0) == FNM_NOMATCH)
1363
		return (-1);
1364
	else if (kv->kv_value != NULL && kv->kv_option == KEY_OPTION_NONE) {
1365
		query = desc->http_query == NULL ? "" : desc->http_query;
1366
		if (fnmatch(kv->kv_value, query, FNM_CASEFOLD) == FNM_NOMATCH)
1367
			return (-1);
1368
	}
1369
1370
	relay_match(actions, kv, match, NULL);
1371
1372
	return (0);
1373
}
1374
1375
int
1376
relay_httpurl_test(struct ctl_relay_event *cre, struct relay_rule *rule,
1377
    struct kvlist *actions)
1378
{
1379
	struct http_descriptor	*desc = cre->desc;
1380
	struct kv		*host, key;
1381
	struct kv		*kv = &rule->rule_kv[KEY_TYPE_URL];
1382
	struct kv		*match = &desc->http_pathquery;
1383
	int			 res;
1384
1385
	if (cre->dir == RELAY_DIR_RESPONSE || kv->kv_type != KEY_TYPE_URL ||
1386
	    kv->kv_key == NULL)
1387
		return (0);
1388
1389
	key.kv_key = "Host";
1390
	host = kv_find(&desc->http_headers, &key);
1391
1392
	if (host == NULL || host->kv_value == NULL)
1393
		return (0);
1394
	else if (rule->rule_action != RULE_ACTION_BLOCK &&
1395
	    kv->kv_option == KEY_OPTION_LOG &&
1396
	    fnmatch(kv->kv_key, match->kv_key, FNM_CASEFOLD) != FNM_NOMATCH) {
1397
		/* fnmatch url only for logging */
1398
	} else if ((res = relay_lookup_url(cre, host->kv_value, kv)) != 0)
1399
		return (res);
1400
	relay_match(actions, kv, match, NULL);
1401
1402
	return (0);
1403
}
1404
1405
int
1406
relay_httpcookie_test(struct ctl_relay_event *cre, struct relay_rule *rule,
1407
    struct kvlist *actions)
1408
{
1409
	struct http_descriptor	*desc = cre->desc;
1410
	struct kv		*kv = &rule->rule_kv[KEY_TYPE_COOKIE], key;
1411
	struct kv		*match = NULL;
1412
	int			 res;
1413
1414
	if (kv->kv_type != KEY_TYPE_COOKIE)
1415
		return (0);
1416
1417
	switch (cre->dir) {
1418
	case RELAY_DIR_REQUEST:
1419
		key.kv_key = "Cookie";
1420
		break;
1421
	case RELAY_DIR_RESPONSE:
1422
		key.kv_key = "Set-Cookie";
1423
		break;
1424
	default:
1425
		return (0);
1426
		/* NOTREACHED */
1427
		break;
1428
	}
1429
1430
	if (kv->kv_option == KEY_OPTION_APPEND ||
1431
	    kv->kv_option == KEY_OPTION_SET) {
1432
		/* no cookie, can be NULL and will be added later */
1433
	} else {
1434
		match = kv_find(&desc->http_headers, &key);
1435
		if (match == NULL)
1436
			return (-1);
1437
		if (kv->kv_key == NULL || match->kv_value == NULL)
1438
			return (0);
1439
		else if ((res = relay_lookup_cookie(cre, match->kv_value,
1440
		    kv)) != 0)
1441
			return (res);
1442
	}
1443
1444
	relay_match(actions, kv, match, &desc->http_headers);
1445
1446
	return (0);
1447
}
1448
1449
int
1450
relay_match_actions(struct ctl_relay_event *cre, struct relay_rule *rule,
1451
    struct kvlist *matches, struct kvlist *actions)
1452
{
1453
	struct rsession		*con = cre->con;
1454
	struct kv		*kv, *tmp;
1455
1456
	/*
1457
	 * Apply the following options instantly (action per match).
1458
	 */
1459
	if (rule->rule_table != NULL)
1460
		con->se_table = rule->rule_table;
1461
1462
	if (rule->rule_tag != 0)
1463
		con->se_tag = rule->rule_tag == -1 ? 0 : rule->rule_tag;
1464
1465
	if (rule->rule_label != 0)
1466
		con->se_label = rule->rule_label == -1 ? 0 : rule->rule_label;
1467
1468
	/*
1469
	 * Apply the remaining options once after evaluation.
1470
	 */
1471
	if (matches == NULL) {
1472
		/* 'pass' or 'block' rule */
1473
		TAILQ_FOREACH_SAFE(kv, &rule->rule_kvlist, kv_rule_entry, tmp) {
1474
			TAILQ_INSERT_TAIL(actions, kv, kv_action_entry);
1475
			TAILQ_REMOVE(&rule->rule_kvlist, kv, kv_rule_entry);
1476
		}
1477
	} else {
1478
		/* 'match' rule */
1479
		TAILQ_FOREACH(kv, matches, kv_match_entry) {
1480
			TAILQ_INSERT_TAIL(actions, kv, kv_action_entry);
1481
		}
1482
	}
1483
1484
	return (0);
1485
}
1486
1487
int
1488
relay_apply_actions(struct ctl_relay_event *cre, struct kvlist *actions)
1489
{
1490
	struct rsession		*con = cre->con;
1491
	struct http_descriptor	*desc = cre->desc;
1492
	struct kv		*host = NULL;
1493
	const char		*value;
1494
	struct kv		*kv, *match, *kp, *mp, kvcopy, matchcopy, key;
1495
	int			 addkv, ret;
1496
	char			 buf[IBUF_READ_SIZE], *ptr;
1497
	char			*msg = NULL;
1498
	const char		*meth = NULL;
1499
1500
	memset(&kvcopy, 0, sizeof(kvcopy));
1501
	memset(&matchcopy, 0, sizeof(matchcopy));
1502
1503
	ret = -1;
1504
	kp = mp = NULL;
1505
	TAILQ_FOREACH(kv, actions, kv_action_entry) {
1506
		kp = NULL;
1507
		match = kv->kv_match;
1508
		addkv = 0;
1509
1510
		/*
1511
		 * Although marked as deleted, give a chance to non-critical
1512
		 * actions, ie. log, to be performed
1513
		 */
1514
		if (match != NULL && (match->kv_flags & KV_FLAG_INVALID))
1515
			goto matchdel;
1516
1517
		switch (kv->kv_option) {
1518
		case KEY_OPTION_APPEND:
1519
		case KEY_OPTION_SET:
1520
			switch (kv->kv_type) {
1521
			case KEY_TYPE_PATH:
1522
				if (kv->kv_option == KEY_OPTION_APPEND) {
1523
					if (kv_setkey(match, "%s%s",
1524
					    match->kv_key, kv->kv_key) == -1)
1525
						goto fail;
1526
				} else {
1527
					if (kv_setkey(match, "%s",
1528
					    kv->kv_value) == -1)
1529
						goto fail;
1530
				}
1531
				break;
1532
			case KEY_TYPE_COOKIE:
1533
				kp = &kvcopy;
1534
				if (kv_inherit(kp, kv) == NULL)
1535
					goto fail;
1536
				if (kv_set(kp, "%s=%s;", kp->kv_key,
1537
				    kp->kv_value) == -1)
1538
					goto fail;
1539
				if (kv_setkey(kp, "%s", cre->dir ==
1540
				    RELAY_DIR_REQUEST ?
1541
				    "Cookie" : "Set-Cookie") == -1)
1542
					goto fail;
1543
				/* FALLTHROUGH cookie is a header */
1544
			case KEY_TYPE_HEADER:
1545
				if (match == NULL) {
1546
					addkv = 1;
1547
					break;
1548
				}
1549
				if (match->kv_value == NULL ||
1550
				    kv->kv_option == KEY_OPTION_SET) {
1551
					if (kv_set(match, "%s",
1552
					    kv->kv_value) == -1)
1553
						goto fail;
1554
				} else
1555
					addkv = 1;
1556
				break;
1557
			default:
1558
				/* query, url not supported */
1559
				break;
1560
			}
1561
			break;
1562
		case KEY_OPTION_REMOVE:
1563
			switch (kv->kv_type) {
1564
			case KEY_TYPE_PATH:
1565
				if (kv_setkey(match, "/") == -1)
1566
					goto fail;
1567
				break;
1568
			case KEY_TYPE_COOKIE:
1569
			case KEY_TYPE_HEADER:
1570
				if (kv->kv_matchtree != NULL)
1571
					match->kv_flags |= KV_FLAG_INVALID;
1572
				else
1573
					kv_free(match);
1574
				match = kv->kv_match = NULL;
1575
				break;
1576
			default:
1577
				/* query and url not supported */
1578
				break;
1579
			}
1580
			break;
1581
		case KEY_OPTION_HASH:
1582
			switch (kv->kv_type) {
1583
			case KEY_TYPE_PATH:
1584
				value = match->kv_key;
1585
				break;
1586
			default:
1587
				value = match->kv_value;
1588
				break;
1589
			}
1590
			SipHash24_Update(&con->se_siphashctx,
1591
			    value, strlen(value));
1592
			break;
1593
		case KEY_OPTION_LOG:
1594
			/* perform this later */
1595
			break;
1596
		default:
1597
			fatalx("%s: invalid action", __func__);
1598
			/* NOTREACHED */
1599
		}
1600
1601
		/* from now on, reads from kp writes to kv */
1602
		if (kp == NULL)
1603
			kp = kv;
1604
		if (addkv && kv->kv_matchtree != NULL) {
1605
			/* Add new entry to the list (eg. new HTTP header) */
1606
			if ((match = kv_add(kv->kv_matchtree, kp->kv_key,
1607
			    kp->kv_value, 0)) == NULL)
1608
				goto fail;
1609
			match->kv_option = kp->kv_option;
1610
			match->kv_type = kp->kv_type;
1611
			kv->kv_match = match;
1612
		}
1613
		if (match != NULL && kp->kv_flags & KV_FLAG_MACRO) {
1614
			bzero(buf, sizeof(buf));
1615
			if ((ptr = relay_expand_http(cre, kp->kv_value, buf,
1616
			    sizeof(buf))) == NULL)
1617
				goto fail;
1618
			if (kv_set(match, ptr) == -1)
1619
				goto fail;
1620
		}
1621
1622
 matchdel:
1623
		switch (kv->kv_option) {
1624
		case KEY_OPTION_LOG:
1625
			if (match == NULL)
1626
				break;
1627
			mp = &matchcopy;
1628
			if (kv_inherit(mp, match) == NULL)
1629
				goto fail;
1630
			if (mp->kv_flags & KV_FLAG_INVALID) {
1631
				if (kv_set(mp, "%s (removed)",
1632
				    mp->kv_value) == -1)
1633
					goto fail;
1634
			}
1635
			switch (kv->kv_type) {
1636
			case KEY_TYPE_URL:
1637
				key.kv_key = "Host";
1638
				host = kv_find(&desc->http_headers, &key);
1639
				switch (kv->kv_digest) {
1640
				case DIGEST_NONE:
1641
					if (host == NULL ||
1642
					    host->kv_value == NULL)
1643
						break;
1644
					if (kv_setkey(mp, "%s%s",
1645
					    host->kv_value, mp->kv_key) ==
1646
					    -1)
1647
						goto fail;
1648
					break;
1649
				default:
1650
					if (kv_setkey(mp, "%s", kv->kv_key)
1651
					    == -1)
1652
						goto fail;
1653
					break;
1654
				}
1655
				break;
1656
			default:
1657
				break;
1658
			}
1659
			if (kv_log(con, mp, con->se_label, cre->dir)
1660
			    == -1)
1661
				goto fail;
1662
			break;
1663
		default:
1664
			break;
1665
		}
1666
1667
		/* actions applied, cleanup kv */
1668
		kv->kv_match = NULL;
1669
		kv->kv_matchtree = NULL;
1670
		TAILQ_REMOVE(actions, kv, kv_match_entry);
1671
1672
		kv_free(&kvcopy);
1673
		kv_free(&matchcopy);
1674
	}
1675
1676
	/*
1677
	 * log tag for request and response, request method
1678
	 * and end of request marker ","
1679
	 */
1680
	if ((con->se_log != NULL) &&
1681
	    ((meth = relay_httpmethod_byid(desc->http_method)) != NULL) &&
1682
	    (asprintf(&msg, " %s",meth) >= 0))
1683
		evbuffer_add(con->se_log, msg, strlen(msg));
1684
	free(msg);
1685
	relay_log(con, cre->dir == RELAY_DIR_REQUEST ? "" : ";");
1686
	ret = 0;
1687
 fail:
1688
	kv_free(&kvcopy);
1689
	kv_free(&matchcopy);
1690
1691
	return (ret);
1692
}
1693
1694
#define	RELAY_GET_SKIP_STEP(i)						\
1695
	do {								\
1696
		r = r->rule_skip[i];					\
1697
		DPRINTF("%s:%d: skip %d rules", __func__, __LINE__, i);	\
1698
	} while (0)
1699
1700
#define	RELAY_GET_NEXT_STEP						\
1701
	do {								\
1702
		DPRINTF("%s:%d: next rule", __func__, __LINE__);	\
1703
		goto nextrule;						\
1704
	} while (0)
1705
1706
int
1707
relay_test(struct protocol *proto, struct ctl_relay_event *cre)
1708
{
1709
	struct rsession		*con;
1710
	struct http_descriptor	*desc = cre->desc;
1711
	struct relay_rule	*r = NULL, *rule = NULL;
1712
	u_int			 cnt = 0;
1713
	u_int			 action = RES_PASS;
1714
	struct kvlist		 actions, matches;
1715
	struct kv		*kv;
1716
	int			 res = 0;
1717
1718
	con = cre->con;
1719
	TAILQ_INIT(&actions);
1720
1721
	r = TAILQ_FIRST(&proto->rules);
1722
	while (r != NULL) {
1723
		cnt++;
1724
1725
		TAILQ_INIT(&matches);
1726
		TAILQ_INIT(&r->rule_kvlist);
1727
1728
		if (r->rule_dir && r->rule_dir != cre->dir)
1729
			RELAY_GET_SKIP_STEP(RULE_SKIP_DIR);
1730
		else if (proto->type != r->rule_proto)
1731
			RELAY_GET_SKIP_STEP(RULE_SKIP_PROTO);
1732
		else if (r->rule_af != AF_UNSPEC &&
1733
		    (cre->ss.ss_family != r->rule_af ||
1734
		     cre->dst->ss.ss_family != r->rule_af))
1735
			RELAY_GET_SKIP_STEP(RULE_SKIP_AF);
1736
		else if (RELAY_ADDR_CMP(&r->rule_src, &cre->ss) != 0)
1737
			RELAY_GET_SKIP_STEP(RULE_SKIP_SRC);
1738
		else if (RELAY_ADDR_CMP(&r->rule_dst, &cre->dst->ss) != 0)
1739
			RELAY_GET_SKIP_STEP(RULE_SKIP_DST);
1740
		else if (r->rule_method != HTTP_METHOD_NONE &&
1741
		    (desc->http_method == HTTP_METHOD_RESPONSE ||
1742
		     desc->http_method != r->rule_method))
1743
			RELAY_GET_SKIP_STEP(RULE_SKIP_METHOD);
1744
		else if (r->rule_tagged && con->se_tag != r->rule_tagged)
1745
			RELAY_GET_NEXT_STEP;
1746
		else if (relay_httpheader_test(cre, r, &matches) != 0)
1747
			RELAY_GET_NEXT_STEP;
1748
		else if ((res = relay_httpquery_test(cre, r, &matches)) != 0)
1749
			RELAY_GET_NEXT_STEP;
1750
		else if (relay_httppath_test(cre, r, &matches) != 0)
1751
			RELAY_GET_NEXT_STEP;
1752
		else if ((res = relay_httpurl_test(cre, r, &matches)) != 0)
1753
			RELAY_GET_NEXT_STEP;
1754
		else if ((res = relay_httpcookie_test(cre, r, &matches)) != 0)
1755
			RELAY_GET_NEXT_STEP;
1756
		else {
1757
			DPRINTF("%s: session %d: matched rule %d",
1758
			    __func__, con->se_id, r->rule_id);
1759
1760
			if (r->rule_action == RULE_ACTION_MATCH) {
1761
				if (relay_match_actions(cre, r, &matches,
1762
				    &actions) != 0) {
1763
					/* Something bad happened, drop */
1764
					action = RES_DROP;
1765
					break;
1766
				}
1767
				RELAY_GET_NEXT_STEP;
1768
			} else if (r->rule_action == RULE_ACTION_BLOCK)
1769
				action = RES_DROP;
1770
			else if (r->rule_action == RULE_ACTION_PASS)
1771
				action = RES_PASS;
1772
1773
			/* Rule matched */
1774
			rule = r;
1775
1776
			/* Temporarily save actions */
1777
			TAILQ_FOREACH(kv, &matches, kv_match_entry) {
1778
				TAILQ_INSERT_TAIL(&rule->rule_kvlist,
1779
				    kv, kv_rule_entry);
1780
			}
1781
1782
			if (rule->rule_flags & RULE_FLAG_QUICK)
1783
				break;
1784
1785
 nextrule:
1786
			/* Continue to find last matching policy */
1787
			DPRINTF("%s: session %d, res %d", __func__,
1788
			    con->se_id, res);
1789
			if (res == RES_BAD || res == RES_INTERNAL)
1790
				return (res);
1791
			res = 0;
1792
			r = TAILQ_NEXT(r, rule_entry);
1793
		}
1794
	}
1795
1796
	if (rule != NULL && relay_match_actions(cre, rule, NULL, &actions)
1797
	    != 0) {
1798
		/* Something bad happened, drop */
1799
		action = RES_DROP;
1800
	}
1801
1802
	if (relay_apply_actions(cre, &actions) != 0) {
1803
		/* Something bad happened, drop */
1804
		action = RES_DROP;
1805
	}
1806
1807
	DPRINTF("%s: session %d: action %d", __func__,
1808
	    con->se_id, action);
1809
1810
	return (action);
1811
}
1812
1813
#define	RELAY_SET_SKIP_STEPS(i)						\
1814
	do {								\
1815
		while (head[i] != cur) {				\
1816
			head[i]->rule_skip[i] = cur;			\
1817
			head[i] = TAILQ_NEXT(head[i], rule_entry);	\
1818
		}							\
1819
	} while (0)
1820
1821
/* This code is derived from pf_calc_skip_steps() from pf.c */
1822
void
1823
relay_calc_skip_steps(struct relay_rules *rules)
1824
{
1825
	struct relay_rule	*head[RULE_SKIP_COUNT], *cur, *prev;
1826
	int			 i;
1827
1828
	cur = TAILQ_FIRST(rules);
1829
	prev = cur;
1830
	for (i = 0; i < RULE_SKIP_COUNT; ++i)
1831
		head[i] = cur;
1832
	while (cur != NULL) {
1833
		if (cur->rule_dir != prev->rule_dir)
1834
			RELAY_SET_SKIP_STEPS(RULE_SKIP_DIR);
1835
		else if (cur->rule_proto != prev->rule_proto)
1836
			RELAY_SET_SKIP_STEPS(RULE_SKIP_PROTO);
1837
		else if (cur->rule_af != prev->rule_af)
1838
			RELAY_SET_SKIP_STEPS(RULE_SKIP_AF);
1839
		else if (RELAY_ADDR_NEQ(&cur->rule_src, &prev->rule_src))
1840
			RELAY_SET_SKIP_STEPS(RULE_SKIP_SRC);
1841
		else if (RELAY_ADDR_NEQ(&cur->rule_dst, &prev->rule_dst))
1842
			RELAY_SET_SKIP_STEPS(RULE_SKIP_DST);
1843
		else if (cur->rule_method != prev->rule_method)
1844
			RELAY_SET_SKIP_STEPS(RULE_SKIP_METHOD);
1845
1846
		prev = cur;
1847
		cur = TAILQ_NEXT(cur, rule_entry);
1848
	}
1849
	for (i = 0; i < RULE_SKIP_COUNT; ++i)
1850
		RELAY_SET_SKIP_STEPS(i);
1851
}
1852
1853
void
1854
relay_match(struct kvlist *actions, struct kv *kv, struct kv *match,
1855
    struct kvtree *matchtree)
1856
{
1857
	if (kv->kv_option != KEY_OPTION_NONE) {
1858
		kv->kv_match = match;
1859
		kv->kv_matchtree = matchtree;
1860
		TAILQ_INSERT_TAIL(actions, kv, kv_match_entry);
1861
	}
1862
}