1 /*
2  * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include "gmid.h"
18 
19 #include <assert.h>
20 #include <errno.h>
21 #include <string.h>
22 
23 /*
24  * Sometimes it can be useful to inspect the fastcgi traffic as
25  * received by gmid.
26  *
27  * This will make gmid connect to a `debug.sock' socket (that must
28  * exists) in the current directory and send there a copy of what gets
29  * read.  The socket can be created and monitored e.g. with
30  *
31  *	rm -f debug.sock ; nc -Ulk ./debug.sock | hexdump -C
32  *
33  * NB: the sandbox must be disabled for this to work.
34  */
35 #define DEBUG_FCGI 0
36 
37 #if DEBUG_FCGI
38 # include <sys/un.h>
39 static int debug_socket = -1;
40 #endif
41 
42 struct fcgi_header {
43 	unsigned char version;
44 	unsigned char type;
45 	unsigned char req_id1;
46 	unsigned char req_id0;
47 	unsigned char content_len1;
48 	unsigned char content_len0;
49 	unsigned char padding;
50 	unsigned char reserved;
51 };
52 
53 /*
54  * number of bytes in a FCGI_HEADER.  Future version of the protocol
55  * will not reduce this number.
56  */
57 #define FCGI_HEADER_LEN	8
58 
59 /*
60  * values for the version component
61  */
62 #define FCGI_VERSION_1	1
63 
64 /*
65  * values for the type component
66  */
67 #define FCGI_BEGIN_REQUEST	 1
68 #define FCGI_ABORT_REQUEST	 2
69 #define FCGI_END_REQUEST	 3
70 #define FCGI_PARAMS		 4
71 #define FCGI_STDIN		 5
72 #define FCGI_STDOUT		 6
73 #define FCGI_STDERR		 7
74 #define FCGI_DATA		 8
75 #define FCGI_GET_VALUES		 9
76 #define FCGI_GET_VALUES_RESULT	10
77 #define FCGI_UNKNOWN_TYPE	11
78 #define FCGI_MAXTYPE		(FCGI_UNKNOWN_TYPE)
79 
80 struct fcgi_begin_req {
81 	unsigned char role1;
82 	unsigned char role0;
83 	unsigned char flags;
84 	unsigned char reserved[5];
85 };
86 
87 struct fcgi_begin_req_record {
88 	struct fcgi_header	header;
89 	struct fcgi_begin_req	body;
90 };
91 
92 /*
93  * mask for flags;
94  */
95 #define FCGI_KEEP_CONN		1
96 
97 /*
98  * values for the role
99  */
100 #define FCGI_RESPONDER	1
101 #define FCGI_AUTHORIZER	2
102 #define FCGI_FILTER	3
103 
104 struct fcgi_end_req_body {
105 	unsigned char app_status3;
106 	unsigned char app_status2;
107 	unsigned char app_status1;
108 	unsigned char app_status0;
109 	unsigned char proto_status;
110 	unsigned char reserved[3];
111 };
112 
113 /*
114  * values for proto_status
115  */
116 #define FCGI_REQUEST_COMPLETE	0
117 #define FCGI_CANT_MPX_CONN	1
118 #define FCGI_OVERLOADED		2
119 #define FCGI_UNKNOWN_ROLE	3
120 
121 /*
122  * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
123  * records.
124  */
125 #define FCGI_MAX_CONNS	"FCGI_MAX_CONNS"
126 #define FCGI_MAX_REQS	"FCGI_MAX_REQS"
127 #define FCGI_MPXS_CONNS	"FCGI_MPXS_CONNS"
128 
129 static int
prepare_header(struct fcgi_header * h,int type,int id,size_t size,size_t padding)130 prepare_header(struct fcgi_header *h, int type, int id, size_t size,
131     size_t padding)
132 {
133 	memset(h, 0, sizeof(*h));
134 
135 	/*
136 	 * id=0 is reserved for status messages.
137 	 */
138 	id++;
139 
140 	h->version = FCGI_VERSION_1;
141         h->type = type;
142 	h->req_id1 = (id >> 8);
143 	h->req_id0 = (id & 0xFF);
144 	h->content_len1 = (size >> 8);
145 	h->content_len0 = (size & 0xFF);
146 	h->padding = padding;
147 
148 	return 0;
149 }
150 
151 static int
fcgi_begin_request(int sock,int id)152 fcgi_begin_request(int sock, int id)
153 {
154 	struct fcgi_begin_req_record r;
155 
156 	if (id > UINT16_MAX)
157 		return -1;
158 
159 	memset(&r, 0, sizeof(r));
160 	prepare_header(&r.header, FCGI_BEGIN_REQUEST, id,
161 	    sizeof(r.body), 0);
162 	assert(sizeof(r.body) == FCGI_HEADER_LEN);
163 
164 	r.body.role1 = 0;
165 	r.body.role0 = FCGI_RESPONDER;
166 	r.body.flags = FCGI_KEEP_CONN;
167 
168 	if (write(sock, &r, sizeof(r)) != sizeof(r))
169 		return -1;
170 	return 0;
171 }
172 
173 static int
fcgi_send_param(int sock,int id,const char * name,const char * value)174 fcgi_send_param(int sock, int id, const char *name, const char *value)
175 {
176 	struct fcgi_header	h;
177         uint32_t		namlen, vallen, padlen;
178 	uint8_t			s[8];
179 	size_t			size;
180 	char			padding[8] = { 0 };
181 
182 	namlen = strlen(name);
183 	vallen = strlen(value);
184 	size = namlen + vallen + 8; /* 4 for the sizes */
185 	padlen = (8 - (size & 0x7)) & 0x7;
186 
187 	s[0] = ( namlen >> 24)         | 0x80;
188 	s[1] = ((namlen >> 16) & 0xFF);
189 	s[2] = ((namlen >>  8) & 0xFF);
190 	s[3] = ( namlen        & 0xFF);
191 
192 	s[4] = ( vallen >> 24)         | 0x80;
193 	s[5] = ((vallen >> 16) & 0xFF);
194 	s[6] = ((vallen >>  8) & 0xFF);
195 	s[7] = ( vallen        & 0xFF);
196 
197 	prepare_header(&h, FCGI_PARAMS, id, size, padlen);
198 
199 	if (write(sock, &h, sizeof(h))   != sizeof(h) ||
200 	    write(sock, s, sizeof(s))    != sizeof(s) ||
201 	    write(sock, name, namlen)    != namlen    ||
202 	    write(sock, value, vallen)   != vallen    ||
203 	    write(sock, padding, padlen) != padlen)
204 		return -1;
205 
206 	return 0;
207 }
208 
209 static int
fcgi_end_param(int sock,int id)210 fcgi_end_param(int sock, int id)
211 {
212 	struct fcgi_header h;
213 
214 	prepare_header(&h, FCGI_PARAMS, id, 0, 0);
215 	if (write(sock, &h, sizeof(h)) != sizeof(h))
216 		return -1;
217 
218 	prepare_header(&h, FCGI_STDIN, id, 0, 0);
219 	if (write(sock, &h, sizeof(h)) != sizeof(h))
220 		return -1;
221 
222 	return 0;
223 }
224 
225 static int
fcgi_abort_request(int sock,int id)226 fcgi_abort_request(int sock, int id)
227 {
228 	struct fcgi_header h;
229 
230 	prepare_header(&h, FCGI_ABORT_REQUEST, id, 0, 0);
231 	if (write(sock, &h, sizeof(h)) != sizeof(h))
232 		return -1;
233 
234 	return 0;
235 }
236 
237 static int
must_read(int sock,char * d,size_t len)238 must_read(int sock, char *d, size_t len)
239 {
240 	ssize_t r;
241 
242 #if DEBUG_FCGI
243 	if (debug_socket == -1) {
244 		struct sockaddr_un addr;
245 
246 		if ((debug_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
247 			err(1, "socket");
248 
249 		memset(&addr, 0, sizeof(addr));
250 		addr.sun_family = AF_UNIX;
251 		strlcpy(addr.sun_path, "./debug.sock", sizeof(addr.sun_path));
252 		if (connect(debug_socket, (struct sockaddr*)&addr, sizeof(addr))
253 		    == -1)
254 			err(1, "connect");
255 	}
256 #endif
257 
258 	for (;;) {
259 		switch (r = read(sock, d, len)) {
260 		case -1:
261 		case 0:
262 			return -1;
263 		default:
264 #if DEBUG_FCGI
265 			write(debug_socket, d, r);
266 #endif
267 
268 			if (r == (ssize_t)len)
269 				return 0;
270 			len -= r;
271 			d += r;
272 		}
273 	}
274 }
275 
276 static int
fcgi_read_header(int sock,struct fcgi_header * h)277 fcgi_read_header(int sock, struct fcgi_header *h)
278 {
279 	if (must_read(sock, (char*)h, sizeof(*h)) == -1)
280 		return -1;
281 	if (h->version != FCGI_VERSION_1) {
282 		errno = EINVAL;
283 		return -1;
284 	}
285 	return 0;
286 }
287 
288 static inline int
recid(struct fcgi_header * h)289 recid(struct fcgi_header *h)
290 {
291 	return h->req_id0 + (h->req_id1 << 8) - 1;
292 }
293 
294 static inline int
reclen(struct fcgi_header * h)295 reclen(struct fcgi_header *h)
296 {
297 	return h->content_len0 + (h->content_len1 << 8);
298 }
299 
300 static void
copy_mbuf(int fd,short ev,void * d)301 copy_mbuf(int fd, short ev, void *d)
302 {
303 	struct client	*c = d;
304 	struct mbuf	*mbuf;
305 	size_t		 len;
306 	ssize_t		 r;
307 	char		*data;
308 
309         for (;;) {
310 		mbuf = TAILQ_FIRST(&c->mbufhead);
311 		if (mbuf == NULL)
312 			break;
313 
314 		len = mbuf->len - mbuf->off;
315 		data = mbuf->data + mbuf->off;
316 		switch (r = tls_write(c->ctx, data, len)) {
317 		case -1:
318 			/*
319 			 * Can't close_conn here.  The application
320 			 * needs to be informed first, otherwise it
321 			 * can interfere with future connections.
322 			 * Check also that we're not doing recursion
323 			 * (copy_mbuf -> handle_fcgi -> copy_mbuf ...)
324 			 */
325 			if (c->next != NULL)
326 				goto end;
327 			fcgi_abort_request(0, c->id);
328 			return;
329 		case TLS_WANT_POLLIN:
330 			event_once(c->fd, EV_READ, &copy_mbuf, c, NULL);
331 			return;
332 		case TLS_WANT_POLLOUT:
333 			event_once(c->fd, EV_WRITE, &copy_mbuf, c, NULL);
334 			return;
335 		}
336 		mbuf->off += r;
337 
338 		if (mbuf->off == mbuf->len) {
339 			TAILQ_REMOVE(&c->mbufhead, mbuf, mbufs);
340 			free(mbuf);
341 		}
342 	}
343 
344 end:
345 	if (c->next != NULL)
346 		c->next(0, 0, c);
347 }
348 
349 static int
consume(int fd,size_t len)350 consume(int fd, size_t len)
351 {
352 	size_t	l;
353 	char	buf[64];
354 
355 	while (len != 0) {
356 		if ((l = len) > sizeof(buf))
357 			l =  sizeof(buf);
358 		if (must_read(fd, buf, l) == -1)
359                         return 0;
360 		len -= l;
361 	}
362 
363 	return 1;
364 }
365 
366 static void
close_all(struct fcgi * f)367 close_all(struct fcgi *f)
368 {
369 	size_t i;
370 	struct client *c;
371 
372 	for (i = 0; i < MAX_USERS; i++) {
373 		c = &clients[i];
374 
375 		if (c->fcgi != f->id)
376 			continue;
377 
378 		if (c->code != 0)
379 			close_conn(0, 0, c);
380 		else
381 			start_reply(c, CGI_ERROR, "CGI error");
382 	}
383 
384 	fcgi_close_backend(f);
385 }
386 
387 void
fcgi_close_backend(struct fcgi * f)388 fcgi_close_backend(struct fcgi *f)
389 {
390 	event_del(&f->e);
391 	close(f->fd);
392 	f->fd = -1;
393 	f->pending = 0;
394 	f->s = FCGI_OFF;
395 }
396 
397 void
handle_fcgi(int sock,short event,void * d)398 handle_fcgi(int sock, short event, void *d)
399 {
400 	struct fcgi		*f = d;
401 	struct fcgi_header	 h;
402 	struct fcgi_end_req_body end;
403 	struct client		*c;
404 	struct mbuf		*mbuf;
405 	size_t			 len;
406 
407 	if (fcgi_read_header(sock, &h) == -1)
408 		goto err;
409 
410 	c = try_client_by_id(recid(&h));
411 	if (c == NULL || c->fcgi != f->id)
412 		goto err;
413 
414 	len = reclen(&h);
415 
416 	switch (h.type) {
417 	case FCGI_END_REQUEST:
418 		if (len != sizeof(end))
419 			goto err;
420 		if (must_read(sock, (char*)&end, sizeof(end)) == -1)
421 			goto err;
422 		/* TODO: do something with the status? */
423 
424 		f->pending--;
425 		c->fcgi = -1;
426 		c->next = close_conn;
427 		event_once(c->fd, EV_WRITE, &copy_mbuf, c, NULL);
428 		break;
429 
430 	case FCGI_STDERR:
431 		/* discard stderr (for now) */
432 		if (!consume(sock, len))
433 			goto err;
434 		break;
435 
436 	case FCGI_STDOUT:
437 		if ((mbuf = calloc(1, sizeof(*mbuf) + len)) == NULL)
438 			fatal("calloc");
439 		mbuf->len = len;
440                 if (must_read(sock, mbuf->data, len) == -1) {
441 			free(mbuf);
442 			goto err;
443 		}
444 
445 		if (TAILQ_EMPTY(&c->mbufhead)) {
446 			TAILQ_INSERT_HEAD(&c->mbufhead, mbuf, mbufs);
447 			event_once(c->fd, EV_WRITE, &copy_mbuf, c, NULL);
448 		} else
449 			TAILQ_INSERT_TAIL(&c->mbufhead, mbuf, mbufs);
450 		break;
451 
452 	default:
453 		log_err(NULL, "got invalid fcgi record (type=%d)", h.type);
454                 goto err;
455 	}
456 
457 	if (!consume(sock, h.padding))
458 		goto err;
459 
460 	if (f->pending == 0 && shutting_down)
461 		fcgi_close_backend(f);
462 
463 	return;
464 
465 err:
466 	close_all(f);
467 }
468 
469 void
send_fcgi_req(struct fcgi * f,struct client * c)470 send_fcgi_req(struct fcgi *f, struct client *c)
471 {
472 	char		 addr[NI_MAXHOST], buf[22];
473 	int		 e;
474 	time_t		 tim;
475 	struct tm	 tminfo;
476 	struct envlist	*p;
477 
478 	f->pending++;
479 
480 	e = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr),
481 	    addr, sizeof(addr),
482 	    NULL, 0,
483 	    NI_NUMERICHOST);
484 	if (e != 0)
485 		fatal("getnameinfo failed");
486 
487 	c->next = NULL;
488 
489 	fcgi_begin_request(f->fd, c->id);
490 	fcgi_send_param(f->fd, c->id, "GATEWAY_INTERFACE", "CGI/1.1");
491 	fcgi_send_param(f->fd, c->id, "GEMINI_URL_PATH", c->iri.path);
492 	fcgi_send_param(f->fd, c->id, "QUERY_STRING", c->iri.query);
493 	fcgi_send_param(f->fd, c->id, "REMOTE_ADDR", addr);
494 	fcgi_send_param(f->fd, c->id, "REMOTE_HOST", addr);
495 	fcgi_send_param(f->fd, c->id, "REQUEST_METHOD", "");
496 	fcgi_send_param(f->fd, c->id, "SERVER_NAME", c->iri.host);
497 	fcgi_send_param(f->fd, c->id, "SERVER_PROTOCOL", "GEMINI");
498 	fcgi_send_param(f->fd, c->id, "SERVER_SOFTWARE", GMID_VERSION);
499 
500 	if (tls_peer_cert_provided(c->ctx)) {
501 		fcgi_send_param(f->fd, c->id, "AUTH_TYPE", "CERTIFICATE");
502 		fcgi_send_param(f->fd, c->id, "REMOTE_USER",
503 		    tls_peer_cert_subject(c->ctx));
504 		fcgi_send_param(f->fd, c->id, "TLS_CLIENT_ISSUER",
505 		    tls_peer_cert_issuer(c->ctx));
506 		fcgi_send_param(f->fd, c->id, "TLS_CLIENT_HASH",
507 		    tls_peer_cert_hash(c->ctx));
508 		fcgi_send_param(f->fd, c->id, "TLS_VERSION",
509 		    tls_conn_version(c->ctx));
510 		fcgi_send_param(f->fd, c->id, "TLS_CIPHER",
511 		    tls_conn_cipher(c->ctx));
512 
513 		snprintf(buf, sizeof(buf), "%d",
514 		    tls_conn_cipher_strength(c->ctx));
515 		fcgi_send_param(f->fd, c->id, "TLS_CIPHER_STRENGTH", buf);
516 
517 		tim = tls_peer_cert_notbefore(c->ctx);
518 		strftime(buf, sizeof(buf), "%FT%TZ",
519 		    gmtime_r(&tim, &tminfo));
520 		fcgi_send_param(f->fd, c->id, "TLS_CLIENT_NOT_BEFORE", buf);
521 
522 		tim = tls_peer_cert_notafter(c->ctx);
523 		strftime(buf, sizeof(buf), "%FT%TZ",
524 		    gmtime_r(&tim, &tminfo));
525 		fcgi_send_param(f->fd, c->id, "TLS_CLIENT_NOT_AFTER", buf);
526 
527 		TAILQ_FOREACH(p, &c->host->params, envs) {
528 			fcgi_send_param(f->fd, c->id, p->name, p->value);
529 		}
530 	} else
531 		fcgi_send_param(f->fd, c->id, "AUTH_TYPE", "");
532 
533 	if (fcgi_end_param(f->fd, c->id) == -1)
534 		close_all(f);
535 }
536