Blob


1 /*
2 * Copyright (c) 2021 Matthias Schmidt <xhr@giessen.ccc.de>
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 */
17 #define _GNU_SOURCE
19 #include <sys/file.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/syslog.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
26 #include <arpa/inet.h>
28 #include <net/if.h>
29 #include <netinet/in.h>
31 #include <openssl/err.h>
32 #include <openssl/ssl.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <limits.h>
37 #include <netdb.h>
38 #include <pthread.h>
39 #include <pwd.h>
40 #include <signal.h>
41 #include <syslog.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
48 #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) &&\
49 !defined(__DragonFly__)
50 #include <grp.h>
51 #endif /* __BSD__ */
53 #include "log.h"
54 #include "twind.h"
56 #define PID_BUF_SIZE 100
57 #define TWIND_USER "_twind"
58 #define _PATH_TWIND_CHROOT "/var/twind"
59 #define _PATH_TWIND_LOGS "/var/twind/logs"
60 #define _PATH_TWIND_CERT "/etc/twind/twind.cert.pem"
61 #define _PATH_TWIND_KEY "/etc/twind/twind.key.pem"
62 #define _PATH_TWIND_PID_CHROOT "/var/twind/twind.pid"
63 #define _PATH_TWIND_PID "twind.pid"
65 static void organize_termination(void);
66 static void open_sockets(int[2], int);
67 void *get_in_addr(struct sockaddr *);
68 void* main_request_handler(void*);
69 int receive_gemini_request(SSL*, char *);
70 int handle_incoming_connections(int, int, SSL_CTX *);
71 void fork_main_process(int[2], SSL_CTX *);
72 SSL_CTX* initialize_tls_context(void);
73 int open_pid_file(void);
74 static void drop_root(void);
76 #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) &&\
77 !defined(__DragonFly__)
78 void setproctitle(const char *, ...);
79 void setproctitle(const char *fmt, ...) {}
80 #endif /* __BSD__ */
82 static void
83 usage(void)
84 {
85 extern char *__progname;
87 fprintf(stderr, "usage: %s [-dfv] [-p port]\n", __progname);
88 exit(-1);
89 }
91 static void
92 signal_handler(int signal)
93 {
94 switch (signal) {
95 case SIGHUP:
96 reload_log_files = 1;
97 break;
98 case SIGINT:
99 case SIGTERM:
100 organize_termination();
101 break;
102 default:
103 fatalx("Unknown signal");
107 int
108 main(int argc, char *argv[])
110 SSL_CTX *sslctx = NULL;
111 int ch, fg_flag = 0, debug_flag = 0, verbose_flag = 0;
112 int tcpsock[2] = { -1, -1 }, port = 1965;
114 log_init(1, LOG_DAEMON); /* Log to stderr until daemonized. */
115 log_setverbose(1);
117 while ((ch = getopt(argc, argv, "dfp:vV")) != -1) {
118 switch(ch) {
119 case 'd':
120 debug_flag = 1;
121 break;
122 case 'f':
123 fg_flag = 1;
124 break;
125 case 'p':
126 port = atoi(optarg);
127 break;
128 case 'v':
129 verbose_flag = 1;
130 break;
131 case 'V':
132 fprintf(stderr, "Version %s\n", VERSION);
133 exit(-1);
134 default:
135 usage();
136 break;
140 argc -= optind;
141 argv += optind;
143 if (geteuid())
144 fatalx("need root privileges");
146 open_pid_file();
148 if (signal(SIGINT, signal_handler) == SIG_ERR)
149 fatalx("signal");
150 if (signal(SIGTERM, signal_handler) == SIG_ERR)
151 fatalx("signal");
152 if (signal(SIGHUP, signal_handler) == SIG_ERR)
153 fatalx("signal");
155 open_sockets(tcpsock, port);
157 sslctx = initialize_tls_context();
159 drop_root();
161 log_init(debug_flag, LOG_DAEMON);
162 log_setverbose(verbose_flag);
165 #ifdef __OpenBSD__
166 if (pledge("stdio inet dns proc rpath wpath cpath", NULL) == -1)
167 fatalx("pledge");
168 #endif /* __OpenBSD__ */
170 if (!fg_flag)
171 if (daemon(0, 0) == -1)
172 fatalx("daemonizing failed");
174 open_twind_logs();
176 fork_main_process(tcpsock, sslctx);
178 organize_termination();
180 return 0;
183 static void
184 organize_termination(void)
186 pid_t sub_pid;
188 close_twind_logs();
190 #ifdef __OpenBSD__
191 if (pledge("stdio", NULL) == -1)
192 fatalx("pledge");
193 #endif /* __OpenBSD__ */
195 log_debug("waiting for sub processes to terminate");
196 for (;;) {
197 sub_pid = wait(NULL);
198 if (sub_pid == -1) {
199 if (errno == ECHILD) {
200 /* All sub processes are terminated */
201 close_twind_logs();
202 log_debug("twind turns to dust");
203 exit(0);
204 } else {
205 fatalx("wait");
209 log_debug("Child %d terminated", sub_pid);
213 SSL_CTX*
214 initialize_tls_context(void)
216 SSL_CTX *sslctx;
218 SSL_load_error_strings();
219 OpenSSL_add_all_algorithms();
221 sslctx = SSL_CTX_new(TLS_method());
222 if (sslctx == NULL)
223 fatalx("Cannot initialize TLS CTX structure");
225 SSL_CTX_set_ecdh_auto(sslctx, 1);
227 /* Gemini requires TLSv1.2 minimum */
228 if (SSL_CTX_set_min_proto_version(sslctx, TLS1_2_VERSION) != 1)
229 fatalx("Cannot set minimum TLS version");
231 if (SSL_CTX_use_certificate_file(sslctx, _PATH_TWIND_CERT, SSL_FILETYPE_PEM)
232 != 1)
233 fatalx("Cannot load TLS certificate %s", _PATH_TWIND_CERT);
235 if (SSL_CTX_use_PrivateKey_file(sslctx, _PATH_TWIND_KEY, SSL_FILETYPE_PEM)
236 != 1)
237 fatalx("Cannot load TLS private key %s", _PATH_TWIND_KEY);
239 return sslctx;
242 void *
243 get_in_addr(struct sockaddr *sa)
245 if (sa->sa_family == AF_INET)
246 return &(((struct sockaddr_in*)sa)->sin_addr);
248 return &(((struct sockaddr_in6*)sa)->sin6_addr);
251 int
252 handle_incoming_connections(int counter, int tcpsock, SSL_CTX *sslctx)
254 struct sockaddr_storage addr;
255 struct client_connection *cc;
256 char str[INET6_ADDRSTRLEN];
257 pthread_t thread_id;
258 socklen_t len = sizeof(addr);
259 int ret, ssl_err;
261 #ifdef __OpenBSD__
262 /* We can get rid of proc pledge here */
263 if (pledge("stdio inet dns rpath wpath cpath", NULL) == -1)
264 fatalx("pledge");
265 #endif /* __OpenBSD__ */
267 memset(str, 0, sizeof(str));
269 while (1) {
270 if (reload_log_files == 1) {
271 reload_log_files = 0;
272 log_debug("Re-open log files");
273 close_twind_logs();
274 open_twind_logs();
277 ret = accept(tcpsock, (struct sockaddr *)&addr, &len);
278 if (ret < 0)
279 fatalx("Error when accepting connection");
281 cc = xmalloc(sizeof(struct client_connection));
283 inet_ntop(addr.ss_family, get_in_addr((struct sockaddr *)&addr), str, sizeof(str));
284 strlcpy(cc->client_addr, str, INET6_ADDRSTRLEN);
285 log_info("Connection from %s", cc->client_addr);
287 if ((cc->ssl_peer = SSL_new(sslctx)) == NULL) {
288 log_warn("Creating new TLS structure failed");
289 free(cc);
290 close(ret);
291 continue;
294 if (SSL_set_fd(cc->ssl_peer, ret) == 0) {
295 log_warn("TLS cannot set file descriptor");
296 SSL_free(cc->ssl_peer);
297 free(cc);
298 close(ret);
299 continue;
302 ssl_err = SSL_accept(cc->ssl_peer);
303 if (ssl_err < 0) {
304 ERR_print_errors_fp(stderr);
305 log_warn("Fatal TLS error. Cannot accept TLS connection");
306 SSL_shutdown(cc->ssl_peer);
307 SSL_free(cc->ssl_peer);
308 free(cc);
309 close(ret);
310 continue;
311 } else if (ssl_err == 0) {
312 log_warn("TLS handshake not successful");
313 SSL_shutdown(cc->ssl_peer);
314 SSL_free(cc->ssl_peer);
315 free(cc);
316 close(ret);
317 continue;
320 log_debug("SSL connection using %s", SSL_get_cipher(cc->ssl_peer));
322 if (pthread_create(&thread_id, NULL, main_request_handler, ((void*)cc))
323 != 0) {
324 log_warn("Cannot create handling thread");
325 SSL_shutdown(cc->ssl_peer);
326 SSL_free(cc->ssl_peer);
327 free(cc);
328 close(ret);
329 continue;
332 if (pthread_join(thread_id, NULL) != 0) {
333 log_warn("Error while joining thread");
334 SSL_shutdown(cc->ssl_peer);
335 SSL_free(cc->ssl_peer);
336 free(cc);
337 close(ret);
338 continue;
341 SSL_shutdown(cc->ssl_peer);
342 SSL_free(cc->ssl_peer);
343 free(cc);
344 close(ret);
347 return 0;
350 void
351 fork_main_process(int tcpsock[2], SSL_CTX *sslctx)
353 pid_t pid;
354 int i;
356 setproctitle("main");
358 /* Fork two main handler processes, one for IPv4, one for IPv6 */
359 for (i=0; i < 2; i++) {
360 if (tcpsock[i] == -1)
361 continue;
362 switch (pid = fork()) {
363 case -1:
364 fatalx("Cannot fork() main IPv%d handler process", i == 0 ? 4 : 6);
365 case 0:
366 log_debug("Main IPv%d handling process started: %d", i == 0 ? 4 : 6,
367 getpid());
368 setproctitle("v%d %s", i == 0 ? 4 : 6, "handler");
369 handle_incoming_connections(i, tcpsock[i], sslctx);
370 exit(0);
375 void *
376 main_request_handler(void *argp)
378 struct client_connection *cc = (struct client_connection *)argp;
379 char finalpath[MAXREQLEN];
380 char temp[MAXREQLEN];
381 char request[MAXREQLEN];
382 char *ext = NULL;
383 char *mime = NULL;
384 int ret;
386 memset(finalpath, 0, sizeof(finalpath));
387 memset(request, 0, sizeof(request));
388 memset(temp, 0, sizeof(temp));
390 if (receive_gemini_request(cc->ssl_peer, request) < 0) {
391 log_warn("Receiving initial request failed");
392 return NULL;
395 ret = get_path_from_request(request, finalpath);
396 if (ret == -1) { /* Malformed request */
397 log_error(cc, "Malformed request");
398 send_non_success_response(cc->ssl_peer, STATUS_BAD_REQUEST);
399 return NULL;
400 } else if (ret == -2) { /* 404 */
401 log_error(cc, "Request file not found");
402 send_non_success_response(cc->ssl_peer, STATUS_NOT_FOUND);
403 return NULL;
406 if ((ext = get_file_extension(finalpath)) == NULL) {
407 log_debug("Cannot get file extension from %s", finalpath);
408 } else {
409 if ((mime = get_mime_type(ext)) == NULL)
410 log_debug("Cannot get MIME type for %s", ext);
413 log_access(cc, finalpath);
415 if (send_response(cc->ssl_peer, STATUS_SUCCESS, finalpath, mime) < 0) {
416 log_warn("Sending response to client failed");
417 return NULL;
420 free(ext);
421 free(mime);
423 return NULL;
426 /*
427 * Gemini requests are a single CRLF-terminated line with the following structure:
429 * <URL><CR><LF>
431 * <URL> is a UTF-8 encoded absolute URL, including a scheme, of maximum length
432 * 1024 bytes.
433 */
434 int
435 receive_gemini_request(SSL *ssl_peer, char* request_buf)
437 if (SSL_read(ssl_peer, request_buf, MAXREQLEN) <= 0)
438 return -1;
440 return 0;
443 static void
444 open_sockets(int tcpsock[2], int port)
446 struct sockaddr_in addr4;
447 struct sockaddr_in6 addr6;
448 struct sockaddr *addr;
449 socklen_t len;
450 int opt = 1;
452 memset(&addr4, 0, sizeof(addr4));
453 addr4.sin_family = AF_INET;
454 addr4.sin_port = htons(port);
455 addr4.sin_addr.s_addr = INADDR_ANY;
456 addr = (struct sockaddr*)&addr4;
457 len = sizeof(addr4);
459 if ((tcpsock[0] = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
460 if (setsockopt(tcpsock[0], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))
461 == -1)
462 log_warn("setting SO_REUSEADDR on socket");
463 if (bind(tcpsock[0], addr, len) == -1) {
464 close(tcpsock[0]);
465 tcpsock[0] = -1;
467 if (listen(tcpsock[0], 5) == -1) {
468 close(tcpsock[0]);
469 tcpsock[0] = -1;
473 memset(&addr6, 0, sizeof(addr6));
474 addr6.sin6_family = AF_INET6;
475 addr6.sin6_port = htons(port);
476 addr6.sin6_addr = in6addr_any;
477 addr = (struct sockaddr*)&addr6;
478 len = sizeof(addr6);
480 if ((tcpsock[1] = socket(AF_INET6, SOCK_STREAM, 0)) != -1) {
481 if (setsockopt(tcpsock[1], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))
482 == -1)
483 log_warn("setting SO_REUSEADDR on socket");
484 if (bind(tcpsock[1], addr, len) == -1) {
485 close(tcpsock[1]);
486 tcpsock[1] = -1;
488 if (listen(tcpsock[1], 5) == -1) {
489 close(tcpsock[1]);
490 tcpsock[1] = -1;
494 if (tcpsock[0] == -1 && tcpsock[1] == -1) {
495 fatalx("Cannot bind to 0.0.0.0 or :: on Port 1965");
499 int
500 open_pid_file(void)
502 char buf[PID_BUF_SIZE];
503 char pid_path[MAXREQLEN];
504 int fd;
506 snprintf(pid_path, MAXREQLEN, "%s/%s",
507 _PATH_TWIND_CHROOT, _PATH_TWIND_PID);
508 if ((fd = open(pid_path, O_CREAT|O_RDWR, 0600)) == -1)
509 fatalx("Cannot open PID file");
511 if (flock(fd, LOCK_EX|LOCK_NB) == -1)
512 fatalx("Cannot get lock on PID file. Another instance running?");
514 /*
515 * We need to truncate the file since the new PID could be shorter than
516 * an old one in the file.
517 */
518 if (ftruncate(fd, 0) == -1)
519 fatalx("Cannot truncate PID file");
521 snprintf(buf, PID_BUF_SIZE, "%ld\n", (long) getpid());
522 if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf))
523 fatalx("Cannot write PID file");
525 return fd;
528 static void
529 drop_root(void)
531 struct passwd *pw;
533 if (!(pw = getpwnam(TWIND_USER)))
534 fatalx("Cannot find user entry for %s", TWIND_USER);
536 if (!pw->pw_uid)
537 fatalx("Cannot get UID entry for %s", TWIND_USER);
539 #ifdef __OpenBSD__
540 if (unveil(_PATH_TWIND_CERT, "r") == -1)
541 fatalx("unveil");
542 if (unveil(_PATH_TWIND_KEY, "r") == -1)
543 fatalx("unveil");
544 if (unveil(_PATH_TWIND_CHROOT, "r") == -1)
545 fatalx("unveil");
546 if (unveil(_PATH_TWIND_PID_CHROOT, "r") == -1)
547 fatalx("unveil");
548 if (unveil(_PATH_TWIND_LOGS, "cw") == -1)
549 log_warn("unveil");
550 if (unveil(NULL, NULL) == -1)
551 fatalx("unveil");
552 #endif /* __OpenBSD__ */
554 if (chroot(_PATH_TWIND_CHROOT) == -1)
555 fatalx("chroot() to %s failed", _PATH_TWIND_CHROOT);
556 if (chdir("/") == -1)
557 fatalx("chdir() failed");
559 if (setgroups(1, &pw->pw_gid) == -1)
560 fatalx("Cannot set group access list");
561 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
562 fatalx("Cannot set GUID to %d", pw->pw_gid);
563 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
564 fatalx("Cannot set UID to %d", pw->pw_uid);