docs: add todo's from Sibir's repo
[netsniff-ng.git] / src / curvetun.c
blob229f846d6e0bb7a8ab51d1044e0a504dc1f3a184
1 /*
2 * curvetun - the cipherspace wormhole creator
3 * Part of the netsniff-ng project
4 * By Daniel Borkmann <daniel@netsniff-ng.org>
5 * Copyright 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,
6 * Copyright 2011 Emmanuel Roullit.
7 * Subject to the GPL, version 2.
9 * This is curvetun, a lightweight, high-speed ECDH multiuser IP tunnel for
10 * Linux that is based on epoll(2). curvetun uses the Linux TUN/TAP interface
11 * and supports {IPv4,IPv6} over {IPv4,IPv6} with UDP or TCP as carrier
12 * protocols. It has an integrated packet forwarding trie, thus multiple
13 * users with different IPs can be handled via a single tunnel device on the
14 * server side and flows are scheduled for processing in a CPU-local manner.
15 * For transmission, packets are being compressed and encrypted by both, the
16 * client and the server side. As an appropriate key management, public-key
17 * cryptography based on elliptic curves are being used and packets are
18 * encrypted by a symmetric stream cipher (Salsa20) and authenticated by a MAC
19 * (Poly1305), where keys have previously been computed with the ECDH key
20 * agreement protocol (Curve25519). Cryptography is based on Daniel J.
21 * Bernsteins Networking and Cryptography library (NaCl).
23 * He used often to say there was only one Road; that it was like a great
24 * river: it's springs were at every doorstep and every path was it's
25 * tributary. "It's a dangerous business, Frodo, going out of your door,"
26 * he used to say. "You step into the Road, and if you don't keep your
27 * feet, there is no telling where you might be swept off to."
29 * -- The Lord of the Rings, Frodo about his uncle Bilbo Baggins,
30 * Chapter 'Three is Company'.
33 #define _GNU_SOURCE
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <fcntl.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <getopt.h>
40 #include <errno.h>
41 #include <stdbool.h>
42 #include <limits.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <sys/socket.h>
46 #include <sys/ptrace.h>
47 #include <netinet/in.h>
48 #include <unistd.h>
49 #include <signal.h>
51 #include "xutils.h"
52 #include "die.h"
53 #include "xmalloc.h"
54 #include "curvetun.h"
55 #include "curve.h"
56 #include "ct_usermgmt.h"
57 #include "ct_servmgmt.h"
58 #include "xio.h"
59 #include "tprintf.h"
60 #include "crypto_verify_32.h"
61 #include "crypto_box_curve25519xsalsa20poly1305.h"
62 #include "crypto_scalarmult_curve25519.h"
63 #include "crypto_auth_hmacsha512256.h"
65 #define CURVETUN_ENTROPY_SOURCE "/dev/random"
67 extern void print_stun_probe(char *server, uint16_t sport, uint16_t tunport);
69 enum working_mode {
70 MODE_UNKNOW,
71 MODE_KEYGEN,
72 MODE_EXPORT,
73 MODE_DUMPC,
74 MODE_DUMPS,
75 MODE_CLIENT,
76 MODE_SERVER,
79 volatile sig_atomic_t sigint = 0;
81 static const char *short_options = "kxc::svhp:t:d:uCS46DN";
82 static const struct option long_options[] = {
83 {"client", optional_argument, NULL, 'c'},
84 {"dev", required_argument, NULL, 'd'},
85 {"port", required_argument, NULL, 'p'},
86 {"stun", required_argument, NULL, 't'},
87 {"keygen", no_argument, NULL, 'k'},
88 {"export", no_argument, NULL, 'x'},
89 {"dumpc", no_argument, NULL, 'C'},
90 {"dumps", no_argument, NULL, 'S'},
91 {"no-logging", no_argument, NULL, 'N'},
92 {"server", no_argument, NULL, 's'},
93 {"udp", no_argument, NULL, 'u'},
94 {"ipv4", no_argument, NULL, '4'},
95 {"ipv6", no_argument, NULL, '6'},
96 {"nofork", no_argument, NULL, 'D'},
97 {"version", no_argument, NULL, 'v'},
98 {"help", no_argument, NULL, 'h'},
99 {NULL, 0, NULL, 0}
102 static void signal_handler(int number)
104 switch (number) {
105 case SIGINT:
106 sigint = 1;
107 break;
108 default:
109 break;
113 static void header(void)
115 printf("%s%s%s\n", colorize_start(bold), "curvetun "
116 VERSION_STRING, colorize_end());
119 static void help(void)
121 printf("\n%s %s, lightweight curve25519-based VPN/IP tunnel\n",
122 PROGNAME_STRING, VERSION_STRING);
123 puts("http://www.netsniff-ng.org\n\n"
124 "Usage: curvetun [options]\n"
125 "Options, general:\n"
126 " -k|--keygen Generate public/private keypair\n"
127 " -x|--export Export your public data for remote servers\n"
128 " -C|--dumpc Dump parsed clients\n"
129 " -S|--dumps Dump parsed servers\n"
130 " -D|--nofork Do not daemonize\n"
131 " -d|--dev <tun> Networking tunnel device, e.g. tun0\n"
132 " -v|--version Print version\n"
133 " -h|--help Print this help\n"
134 " -c|--client[=alias] Client mode, server alias optional\n"
135 " -s|--server Server mode, options follow below\n"
136 " -N|--no-logging Disable server logging (for better anonymity)\n"
137 " -p|--port <num> Server port number (mandatory)\n"
138 " -t|--stun <server> Show public IP/Port mapping via STUN\n"
139 " -u|--udp Use UDP as carrier instead of TCP\n"
140 " -4|--ipv4 Tunnel devices are IPv4\n"
141 " -6|--ipv6 Tunnel devices are IPv6\n"
142 " (default: same as carrier protocol)\n\n"
143 "Example:\n"
144 " See Documentation/Curvetun for a configuration example.\n"
145 " curvetun --keygen\n"
146 " curvetun --export\n"
147 " curvetun --server -4 -u -N --port 6666 --stun stunserver.org\n"
148 " curvetun --client=ethz\n\n"
149 "Note:\n"
150 " There is no default port specified, so that you are forced\n"
151 " to select your own! For client/server status messages see syslog!\n"
152 " This software is an experimental prototype intended for researchers.\n\n"
153 "Secret ingredient: 7647-14-5\n\n"
154 "Please report bugs to <bugs@netsniff-ng.org>\n"
155 "Copyright (C) 2011-2012 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n"
156 "License: GNU GPL version 2.0\n"
157 "This is free software: you are free to change and redistribute it.\n"
158 "There is NO WARRANTY, to the extent permitted by law.\n");
159 die();
162 static void version(void)
164 printf("\n%s %s, lightweight curve25519-based VPN/IP tunnel\n",
165 PROGNAME_STRING, VERSION_STRING);
166 puts("http://www.netsniff-ng.org\n\n"
167 "Please report bugs to <bugs@netsniff-ng.org>\n"
168 "Copyright (C) 2011-2012 Daniel Borkmann <daniel@netsniff-ng.org>\n"
169 "License: GNU GPL version 2.0\n"
170 "This is free software: you are free to change and redistribute it.\n"
171 "There is NO WARRANTY, to the extent permitted by law.\n");
172 die();
175 static void check_file_or_die(char *home, char *file, int maybeempty)
177 char path[PATH_MAX];
178 struct stat st;
180 memset(path, 0, sizeof(path));
181 slprintf(path, sizeof(path), "%s/%s", home, file);
183 if (stat(path, &st))
184 panic("No such file %s! Type --help for further information\n",
185 path);
187 if (!S_ISREG(st.st_mode))
188 panic("%s is not a regular file!\n", path);
190 if ((st.st_mode & ~S_IFREG) != (S_IRUSR | S_IWUSR))
191 panic("You have set too many permissions on %s (%o)!\n",
192 path, st.st_mode);
194 if (maybeempty == 0 && st.st_size == 0)
195 panic("%s is empty!\n", path);
198 static void check_config_exists_or_die(char *home)
200 if (!home)
201 panic("No home dir specified!\n");
203 check_file_or_die(home, FILE_CLIENTS, 1);
204 check_file_or_die(home, FILE_SERVERS, 1);
205 check_file_or_die(home, FILE_PRIVKEY, 0);
206 check_file_or_die(home, FILE_PUBKEY, 0);
207 check_file_or_die(home, FILE_USERNAM, 0);
210 static char *fetch_home_dir(void)
212 char *home = getenv("HOME");
213 if (!home)
214 panic("No HOME defined!\n");
215 return home;
218 static void write_username(char *home)
220 int fd, ret;
221 char path[PATH_MAX], *eof;
222 char user[512];
224 memset(path, 0, sizeof(path));
225 slprintf(path, sizeof(path), "%s/%s", home, FILE_USERNAM);
227 printf("Username: [%s] ", getenv("USER"));
228 fflush(stdout);
230 memset(user, 0, sizeof(user));
231 eof = fgets(user, sizeof(user), stdin);
232 user[sizeof(user) - 1] = 0;
233 user[strlen(user) - 1] = 0; /* omit last \n */
234 if (strlen(user) == 0)
235 strlcpy(user, getenv("USER"), sizeof(user));
237 fd = open_or_die_m(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
239 ret = write(fd, user, strlen(user));
240 if (ret != strlen(user))
241 panic("Could not write username!\n");
243 close(fd);
245 printf("Username written to %s!\n", path);
248 static void create_curvedir(char *home)
250 int ret;
251 char path[PATH_MAX];
253 memset(path, 0, sizeof(path));
254 slprintf(path, sizeof(path), "%s/%s", home, ".curvetun/");
256 errno = 0;
258 ret = mkdir(path, S_IRWXU);
259 if (ret < 0 && errno != EEXIST)
260 panic("Cannot create curvetun dir!\n");
262 printf("curvetun directory %s created!\n", path);
263 /* We also create empty files for clients and servers! */
265 memset(path, 0, sizeof(path));
266 slprintf(path, sizeof(path), "%s/%s", home, FILE_CLIENTS);
268 create_or_die(path, S_IRUSR | S_IWUSR);
270 printf("Empty client file written to %s!\n", path);
272 memset(path, 0, sizeof(path));
273 slprintf(path, sizeof(path), "%s/%s", home, FILE_SERVERS);
275 create_or_die(path, S_IRUSR | S_IWUSR);
277 printf("Empty server file written to %s!\n", path);
280 static void create_keypair(char *home)
282 int fd, err = 0;
283 ssize_t ret;
284 unsigned char publickey[crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES] = { 0 };
285 unsigned char secretkey[crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES] = { 0 };
286 char path[PATH_MAX];
287 const char * errstr = NULL;
289 printf("Reading from %s (this may take a while) ...\n", CURVETUN_ENTROPY_SOURCE);
291 fd = open_or_die(CURVETUN_ENTROPY_SOURCE, O_RDONLY);
293 ret = read_exact(fd, secretkey, sizeof(secretkey), 0);
294 if (ret != sizeof(secretkey)) {
295 err = EIO;
296 errstr = "Cannot read from "CURVETUN_ENTROPY_SOURCE"!\n";
297 goto out;
300 close(fd);
302 crypto_scalarmult_curve25519_base(publickey, secretkey);
304 memset(path, 0, sizeof(path));
305 slprintf(path, sizeof(path), "%s/%s", home, FILE_PUBKEY);
307 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
308 if (fd < 0) {
309 err = EIO;
310 errstr = "Cannot open pubkey file!\n";
311 goto out;
314 ret = write(fd, publickey, sizeof(publickey));
315 if (ret != sizeof(publickey)) {
316 err = EIO;
317 errstr = "Cannot write public key!\n";
318 goto out;
321 close(fd);
323 printf("Public key written to %s!\n", path);
325 memset(path, 0, sizeof(path));
326 slprintf(path, sizeof(path), "%s/%s", home, FILE_PRIVKEY);
328 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
329 if (fd < 0) {
330 err = EIO;
331 errstr = "Cannot open privkey file!\n";
332 goto out;
335 ret = write(fd, secretkey, sizeof(secretkey));
336 if (ret != sizeof(secretkey)) {
337 err = EIO;
338 errstr = "Cannot write private key!\n";
339 goto out;
341 out:
342 close(fd);
344 xmemset(publickey, 0, sizeof(publickey));
345 xmemset(secretkey, 0, sizeof(secretkey));
347 if (err)
348 panic("%s: %s", errstr, strerror(errno));
349 else
350 printf("Private key written to %s!\n", path);
353 static void check_config_keypair_or_die(char *home)
355 int fd, err;
356 ssize_t ret;
357 const char * errstr = NULL;
358 unsigned char publickey[crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES];
359 unsigned char publicres[crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES];
360 unsigned char secretkey[crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES];
361 char path[PATH_MAX];
363 memset(path, 0, sizeof(path));
364 slprintf(path, sizeof(path), "%s/%s", home, FILE_PRIVKEY);
366 fd = open(path, O_RDONLY);
367 if (fd < 0) {
368 err = EIO;
369 errstr = "Cannot open privkey file!\n";
370 goto out;
373 ret = read(fd, secretkey, sizeof(secretkey));
374 if (ret != sizeof(secretkey)) {
375 err = EIO;
376 errstr = "Cannot read private key!\n";
377 goto out;
380 close(fd);
382 memset(path, 0, sizeof(path));
383 slprintf(path, sizeof(path), "%s/%s", home, FILE_PUBKEY);
385 fd = open(path, O_RDONLY);
386 if (fd < 0) {
387 err = EIO;
388 errstr = "Cannot open pubkey file!\n";
389 goto out;
392 ret = read(fd, publickey, sizeof(publickey));
393 if (ret != sizeof(publickey)) {
394 err = EIO;
395 errstr = "Cannot read public key!\n";
396 goto out;
399 crypto_scalarmult_curve25519_base(publicres, secretkey);
401 err = crypto_verify_32(publicres, publickey);
402 if (err) {
403 err = EINVAL;
404 errstr = "WARNING: your keypair is corrupted!!! You need to "
405 "generate new keys!!!\n";
406 goto out;
408 out:
409 close(fd);
411 xmemset(publickey, 0, sizeof(publickey));
412 xmemset(publicres, 0, sizeof(publicres));
413 xmemset(secretkey, 0, sizeof(secretkey));
415 if (err)
416 panic("%s: %s\n", errstr, strerror(errno));
419 static int main_keygen(char *home)
421 create_curvedir(home);
422 write_username(home);
423 create_keypair(home);
424 check_config_keypair_or_die(home);
426 return 0;
429 static int main_export(char *home)
431 int fd, i;
432 ssize_t ret;
433 char path[PATH_MAX], tmp[64];
435 check_config_exists_or_die(home);
436 check_config_keypair_or_die(home);
438 printf("Your exported public information:\n\n");
440 memset(path, 0, sizeof(path));
441 slprintf(path, sizeof(path), "%s/%s", home, FILE_USERNAM);
443 fd = open_or_die(path, O_RDONLY);
445 while ((ret = read(fd, tmp, sizeof(tmp))) > 0) {
446 ret = write(STDOUT_FILENO, tmp, ret);
449 close(fd);
451 printf(";");
453 memset(path, 0, sizeof(path));
454 slprintf(path, sizeof(path), "%s/%s", home, FILE_PUBKEY);
456 fd = open_or_die(path, O_RDONLY);
458 ret = read(fd, tmp, sizeof(tmp));
459 if (ret != crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES)
460 panic("Cannot read public key!\n");
462 for (i = 0; i < ret; ++i)
463 if (i == ret - 1)
464 printf("%02x\n\n", (unsigned char) tmp[i]);
465 else
466 printf("%02x:", (unsigned char) tmp[i]);
468 close(fd);
469 fflush(stdout);
471 return 0;
474 static int main_dumpc(char *home)
476 check_config_exists_or_die(home);
477 check_config_keypair_or_die(home);
479 printf("Your clients:\n\n");
481 parse_userfile_and_generate_user_store_or_die(home);
483 dump_user_store();
485 destroy_user_store();
487 printf("\n");
488 die();
489 return 0;
492 static int main_dumps(char *home)
494 check_config_exists_or_die(home);
495 check_config_keypair_or_die(home);
497 printf("Your servers:\n\n");
499 parse_userfile_and_generate_serv_store_or_die(home);
501 dump_serv_store();
503 destroy_serv_store();
505 printf("\n");
506 die();
507 return 0;
510 static void daemonize(const char *lockfile)
512 char pidstr[8];
513 mode_t lperm = S_IRWXU | S_IRGRP | S_IXGRP; /* 0750 */
514 int lfp;
516 if (getppid() == 1)
517 return;
519 if (daemon(0, 0))
520 panic("Cannot daemonize: %s", strerror(errno));
522 umask(lperm);
523 if (lockfile) {
524 lfp = open(lockfile, O_RDWR | O_CREAT | O_EXCL,
525 S_IRUSR | S_IWUSR | S_IRGRP);
526 if (lfp < 0)
527 syslog_panic("Cannot create lockfile at %s! "
528 "curvetun server already running?\n",
529 lockfile);
531 slprintf(pidstr, sizeof(pidstr), "%u", getpid());
532 if (write(lfp, pidstr, strlen(pidstr)) <= 0)
533 syslog_panic("Could not write pid to pidfile %s",
534 lockfile);
536 close(lfp);
540 static int main_client(char *home, char *dev, char *alias, int daemon)
542 int ret, udp;
543 char *host, *port;
545 check_config_exists_or_die(home);
546 check_config_keypair_or_die(home);
548 parse_userfile_and_generate_serv_store_or_die(home);
550 get_serv_store_entry_by_alias(alias, alias ? strlen(alias) + 1 : 0,
551 &host, &port, &udp);
552 if (!host || !port || udp < 0)
553 panic("Did not find alias/entry in configuration!\n");
555 printf("Using [%s] -> %s:%s via %s as endpoint!\n",
556 alias ? : "default", host, port, udp ? "udp" : "tcp");
557 if (daemon)
558 daemonize(NULL);
560 ret = client_main(home, dev, host, port, udp);
562 destroy_serv_store();
564 return ret;
567 static int main_server(char *home, char *dev, char *port, int udp,
568 int ipv4, int daemon, int log)
570 int ret;
572 check_config_exists_or_die(home);
573 check_config_keypair_or_die(home);
575 if (daemon)
576 daemonize(LOCKFILE);
578 ret = server_main(home, dev, port, udp, ipv4, log);
580 unlink(LOCKFILE);
582 return ret;
585 int main(int argc, char **argv)
587 int ret = 0, c, opt_index, udp = 0, ipv4 = -1, daemon = 1, log = 1;
588 char *port = NULL, *stun = NULL, *dev = NULL, *home = NULL, *alias = NULL;
589 enum working_mode wmode = MODE_UNKNOW;
591 if (getuid() != geteuid())
592 seteuid(getuid());
594 home = fetch_home_dir();
596 while ((c = getopt_long(argc, argv, short_options, long_options,
597 &opt_index)) != EOF) {
598 switch (c) {
599 case 'h':
600 help();
601 break;
602 case 'v':
603 version();
604 break;
605 case 'D':
606 daemon = 0;
607 break;
608 case 'N':
609 log = 0;
610 break;
611 case 'C':
612 wmode = MODE_DUMPC;
613 break;
614 case 'S':
615 wmode = MODE_DUMPS;
616 break;
617 case 'c':
618 wmode = MODE_CLIENT;
619 if (optarg) {
620 if (*optarg == '=')
621 optarg++;
622 alias = xstrdup(optarg);
624 break;
625 case 'd':
626 dev = xstrdup(optarg);
627 break;
628 case 'k':
629 wmode = MODE_KEYGEN;
630 break;
631 case '4':
632 ipv4 = 1;
633 break;
634 case '6':
635 ipv4 = 0;
636 break;
637 case 'x':
638 wmode = MODE_EXPORT;
639 break;
640 case 's':
641 wmode = MODE_SERVER;
642 break;
643 case 'u':
644 udp = 1;
645 break;
646 case 't':
647 stun = xstrdup(optarg);
648 break;
649 case 'p':
650 port = xstrdup(optarg);
651 break;
652 case '?':
653 switch (optopt) {
654 case 't':
655 case 'd':
656 case 'u':
657 case 'p':
658 panic("Option -%c requires an argument!\n",
659 optopt);
660 default:
661 if (isprint(optopt))
662 whine("Unknown option character "
663 "`0x%X\'!\n", optopt);
664 die();
666 default:
667 break;
671 if (argc < 2)
672 help();
674 register_signal(SIGINT, signal_handler);
675 register_signal(SIGHUP, signal_handler);
676 register_signal(SIGTERM, signal_handler);
677 register_signal(SIGPIPE, signal_handler);
679 header();
681 curve25519_selftest();
683 switch (wmode) {
684 case MODE_KEYGEN:
685 ret = main_keygen(home);
686 break;
687 case MODE_EXPORT:
688 ret = main_export(home);
689 break;
690 case MODE_DUMPC:
691 ret = main_dumpc(home);
692 break;
693 case MODE_DUMPS:
694 ret = main_dumps(home);
695 break;
696 case MODE_CLIENT:
697 ret = main_client(home, dev, alias, daemon);
698 break;
699 case MODE_SERVER:
700 if (!port)
701 panic("No port specified!\n");
702 if (stun)
703 print_stun_probe(stun, 3478, strtoul(port, NULL, 10));
704 ret = main_server(home, dev, port, udp, ipv4, daemon, log);
705 break;
706 default:
707 die();
710 if (dev)
711 xfree(dev);
712 if (stun)
713 xfree(stun);
714 if (port)
715 xfree(port);
716 if (alias)
717 xfree(alias);
719 return ret;