xio: refactor fopencookie related functions
[netsniff-ng.git] / curvetun.c
blobd85e6ad59d26ec1665d2ed5dfe5bbdf8c2fbc037
1 /*
2 * curvetun - the cipherspace wormhole creator
3 * Part of the netsniff-ng project
4 * Copyright 2011 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,
5 * Copyright 2011 Emmanuel Roullit.
6 * Subject to the GPL, version 2.
7 */
9 #define _GNU_SOURCE
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <fcntl.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <getopt.h>
16 #include <errno.h>
17 #include <stdbool.h>
18 #include <limits.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/socket.h>
22 #include <sys/ptrace.h>
23 #include <sys/fsuid.h>
24 #include <netinet/in.h>
25 #include <unistd.h>
26 #include <signal.h>
28 #include "xutils.h"
29 #include "die.h"
30 #include "str.h"
31 #include "cookie.h"
32 #include "xmalloc.h"
33 #include "curvetun.h"
34 #include "curve.h"
35 #include "ct_usermgmt.h"
36 #include "ct_servmgmt.h"
37 #include "xio.h"
38 #include "tprintf.h"
39 #include "crypto_verify_32.h"
40 #include "crypto_box_curve25519xsalsa20poly1305.h"
41 #include "crypto_scalarmult_curve25519.h"
42 #include "crypto_auth_hmacsha512256.h"
44 #define CURVETUN_ENTROPY_SOURCE "/dev/random"
46 extern void print_stun_probe(char *server, uint16_t sport, uint16_t tunport);
48 enum working_mode {
49 MODE_UNKNOW,
50 MODE_KEYGEN,
51 MODE_EXPORT,
52 MODE_DUMPC,
53 MODE_DUMPS,
54 MODE_CLIENT,
55 MODE_SERVER,
58 volatile sig_atomic_t sigint = 0;
60 static const char *short_options = "kxc::svhp:t:d:uCS46DN";
61 static const struct option long_options[] = {
62 {"client", optional_argument, NULL, 'c'},
63 {"dev", required_argument, NULL, 'd'},
64 {"port", required_argument, NULL, 'p'},
65 {"stun", required_argument, NULL, 't'},
66 {"keygen", no_argument, NULL, 'k'},
67 {"export", no_argument, NULL, 'x'},
68 {"dumpc", no_argument, NULL, 'C'},
69 {"dumps", no_argument, NULL, 'S'},
70 {"no-logging", no_argument, NULL, 'N'},
71 {"server", no_argument, NULL, 's'},
72 {"udp", no_argument, NULL, 'u'},
73 {"ipv4", no_argument, NULL, '4'},
74 {"ipv6", no_argument, NULL, '6'},
75 {"nofork", no_argument, NULL, 'D'},
76 {"version", no_argument, NULL, 'v'},
77 {"help", no_argument, NULL, 'h'},
78 {NULL, 0, NULL, 0}
81 static void signal_handler(int number)
83 switch (number) {
84 case SIGINT:
85 case SIGTERM:
86 sigint = 1;
87 break;
88 default:
89 break;
93 static void __noreturn help(void)
95 printf("\ncurvetun %s, lightweight curve25519-based VPN/IP tunnel\n", VERSION_STRING);
96 puts("http://www.netsniff-ng.org\n\n"
97 "Usage: curvetun [options]\n"
98 "Options, general:\n"
99 " -d|--dev <tun> Networking tunnel device, e.g. tun0\n"
100 " -p|--port <num> Server port number (mandatory)\n"
101 " -t|--stun <server> Show public IP/Port mapping via STUN\n"
102 " -c|--client[=alias] Client mode, server alias optional\n"
103 " -k|--keygen Generate public/private keypair\n"
104 " -x|--export Export your public data for remote servers\n"
105 " -C|--dumpc Dump parsed clients\n"
106 " -S|--dumps Dump parsed servers\n"
107 " -D|--nofork Do not daemonize\n"
108 " -s|--server Server mode, options follow below\n"
109 " -N|--no-logging Disable server logging (for better anonymity)\n"
110 " -u|--udp Use UDP as carrier instead of TCP\n"
111 " -4|--ipv4 Tunnel devices are IPv4\n"
112 " -6|--ipv6 Tunnel devices are IPv6\n"
113 " -v|--version Print version and exit\n"
114 " -h|--help Print this help and exit\n\n"
115 "Example:\n"
116 " See curvetun's man page for a configuration example.\n"
117 " curvetun --server -4 -u -N --port 6666 --stun stunserver.org\n"
118 " curvetun --client=ethz\n\n"
119 " curvetun --keygen\n"
120 " curvetun --export\n"
121 "Note:\n"
122 " There is no default port specified, so that you are forced\n"
123 " to select your own! For client/server status messages see syslog!\n"
124 " This software is an experimental prototype intended for researchers.\n\n"
125 "Secret ingredient: 7647-14-5\n\n"
126 "Please report bugs to <bugs@netsniff-ng.org>\n"
127 "Copyright (C) 2011-2013 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n"
128 "Swiss federal institute of technology (ETH Zurich)\n"
129 "License: GNU GPL version 2.0\n"
130 "This is free software: you are free to change and redistribute it.\n"
131 "There is NO WARRANTY, to the extent permitted by law.\n");
132 die();
135 static void __noreturn version(void)
137 printf("\ncurvetun %s, lightweight curve25519-based VPN/IP tunnel\n", VERSION_LONG);
138 puts("http://www.netsniff-ng.org\n\n"
139 "Please report bugs to <bugs@netsniff-ng.org>\n"
140 "Copyright (C) 2011-2013 Daniel Borkmann <dborkma@tik.ee.ethz.ch>,\n"
141 "Swiss federal institute of technology (ETH Zurich)\n"
142 "License: GNU GPL version 2.0\n"
143 "This is free software: you are free to change and redistribute it.\n"
144 "There is NO WARRANTY, to the extent permitted by law.\n");
145 die();
148 static void check_file_or_die(char *home, char *file, int maybeempty)
150 char path[PATH_MAX];
151 struct stat st;
153 memset(path, 0, sizeof(path));
154 slprintf(path, sizeof(path), "%s/%s", home, file);
156 if (stat(path, &st))
157 panic("No such file %s! Type --help for further information\n",
158 path);
160 if (!S_ISREG(st.st_mode))
161 panic("%s is not a regular file!\n", path);
163 if ((st.st_mode & ~S_IFREG) != (S_IRUSR | S_IWUSR))
164 panic("You have set too many permissions on %s (%o)!\n",
165 path, st.st_mode);
167 if (maybeempty == 0 && st.st_size == 0)
168 panic("%s is empty!\n", path);
171 static void check_config_exists_or_die(char *home)
173 if (!home)
174 panic("No home dir specified!\n");
176 check_file_or_die(home, FILE_CLIENTS, 1);
177 check_file_or_die(home, FILE_SERVERS, 1);
178 check_file_or_die(home, FILE_PRIVKEY, 0);
179 check_file_or_die(home, FILE_PUBKEY, 0);
180 check_file_or_die(home, FILE_USERNAM, 0);
183 static char *fetch_home_dir(void)
185 char *home = getenv("HOME");
186 if (!home)
187 panic("No HOME defined!\n");
188 return home;
191 static void write_username(char *home)
193 int fd, ret;
194 char path[PATH_MAX];
195 char user[512];
197 memset(path, 0, sizeof(path));
198 slprintf(path, sizeof(path), "%s/%s", home, FILE_USERNAM);
200 printf("Username: [%s] ", getenv("USER"));
201 fflush(stdout);
203 memset(user, 0, sizeof(user));
204 if (fgets(user, sizeof(user), stdin) == NULL)
205 panic("Could not read from stdin!\n");
206 user[sizeof(user) - 1] = 0;
207 user[strlen(user) - 1] = 0; /* omit last \n */
208 if (strlen(user) == 0)
209 strlcpy(user, getenv("USER"), sizeof(user));
211 fd = open_or_die_m(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
213 ret = write(fd, user, strlen(user));
214 if (ret != strlen(user))
215 panic("Could not write username!\n");
217 close(fd);
219 printf("Username written to %s!\n", path);
222 static void create_curvedir(char *home)
224 int ret;
225 char path[PATH_MAX];
227 memset(path, 0, sizeof(path));
228 slprintf(path, sizeof(path), "%s/%s", home, ".curvetun/");
230 errno = 0;
232 ret = mkdir(path, S_IRWXU);
233 if (ret < 0 && errno != EEXIST)
234 panic("Cannot create curvetun dir!\n");
236 printf("curvetun directory %s created!\n", path);
237 /* We also create empty files for clients and servers! */
239 memset(path, 0, sizeof(path));
240 slprintf(path, sizeof(path), "%s/%s", home, FILE_CLIENTS);
242 create_or_die(path, S_IRUSR | S_IWUSR);
244 printf("Empty client file written to %s!\n", path);
246 memset(path, 0, sizeof(path));
247 slprintf(path, sizeof(path), "%s/%s", home, FILE_SERVERS);
249 create_or_die(path, S_IRUSR | S_IWUSR);
251 printf("Empty server file written to %s!\n", path);
254 static void create_keypair(char *home)
256 int fd, err = 0;
257 ssize_t ret;
258 unsigned char publickey[crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES] = { 0 };
259 unsigned char secretkey[crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES] = { 0 };
260 char path[PATH_MAX];
261 const char * errstr = NULL;
263 printf("Reading from %s (this may take a while) ...\n", CURVETUN_ENTROPY_SOURCE);
265 fd = open_or_die(CURVETUN_ENTROPY_SOURCE, O_RDONLY);
267 ret = read_exact(fd, secretkey, sizeof(secretkey), 0);
268 if (ret != sizeof(secretkey)) {
269 err = EIO;
270 errstr = "Cannot read from "CURVETUN_ENTROPY_SOURCE"!\n";
271 goto out;
274 close(fd);
276 crypto_scalarmult_curve25519_base(publickey, secretkey);
278 memset(path, 0, sizeof(path));
279 slprintf(path, sizeof(path), "%s/%s", home, FILE_PUBKEY);
281 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
282 if (fd < 0) {
283 err = EIO;
284 errstr = "Cannot open pubkey file!\n";
285 goto out_noclose;
288 ret = write(fd, publickey, sizeof(publickey));
289 if (ret != sizeof(publickey)) {
290 err = EIO;
291 errstr = "Cannot write public key!\n";
292 goto out;
295 close(fd);
297 printf("Public key written to %s!\n", path);
299 memset(path, 0, sizeof(path));
300 slprintf(path, sizeof(path), "%s/%s", home, FILE_PRIVKEY);
302 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
303 if (fd < 0) {
304 err = EIO;
305 errstr = "Cannot open privkey file!\n";
306 goto out_noclose;
309 ret = write(fd, secretkey, sizeof(secretkey));
310 if (ret != sizeof(secretkey)) {
311 err = EIO;
312 errstr = "Cannot write private key!\n";
313 goto out;
315 out:
316 close(fd);
317 out_noclose:
318 xmemset(publickey, 0, sizeof(publickey));
319 xmemset(secretkey, 0, sizeof(secretkey));
321 if (err)
322 panic("%s: %s", errstr, strerror(errno));
323 else
324 printf("Private key written to %s!\n", path);
327 static void check_config_keypair_or_die(char *home)
329 int fd, err;
330 ssize_t ret;
331 const char * errstr = NULL;
332 unsigned char publickey[crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES];
333 unsigned char publicres[crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES];
334 unsigned char secretkey[crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES];
335 char path[PATH_MAX];
337 memset(path, 0, sizeof(path));
338 slprintf(path, sizeof(path), "%s/%s", home, FILE_PRIVKEY);
340 fd = open(path, O_RDONLY);
341 if (fd < 0) {
342 err = EIO;
343 errstr = "Cannot open privkey file!\n";
344 goto out;
347 ret = read(fd, secretkey, sizeof(secretkey));
348 if (ret != sizeof(secretkey)) {
349 err = EIO;
350 errstr = "Cannot read private key!\n";
351 goto out;
354 close(fd);
356 memset(path, 0, sizeof(path));
357 slprintf(path, sizeof(path), "%s/%s", home, FILE_PUBKEY);
359 fd = open(path, O_RDONLY);
360 if (fd < 0) {
361 err = EIO;
362 errstr = "Cannot open pubkey file!\n";
363 goto out;
366 ret = read(fd, publickey, sizeof(publickey));
367 if (ret != sizeof(publickey)) {
368 err = EIO;
369 errstr = "Cannot read public key!\n";
370 goto out;
373 crypto_scalarmult_curve25519_base(publicres, secretkey);
375 err = crypto_verify_32(publicres, publickey);
376 if (err) {
377 err = EINVAL;
378 errstr = "WARNING: your keypair is corrupted!!! You need to "
379 "generate new keys!!!\n";
380 goto out;
382 out:
383 close(fd);
385 xmemset(publickey, 0, sizeof(publickey));
386 xmemset(publicres, 0, sizeof(publicres));
387 xmemset(secretkey, 0, sizeof(secretkey));
389 if (err)
390 panic("%s: %s\n", errstr, strerror(errno));
393 static int main_keygen(char *home)
395 create_curvedir(home);
396 write_username(home);
397 create_keypair(home);
398 check_config_keypair_or_die(home);
400 return 0;
403 static int main_export(char *home)
405 int fd, i;
406 ssize_t ret;
407 char path[PATH_MAX], tmp[64];
409 check_config_exists_or_die(home);
410 check_config_keypair_or_die(home);
412 printf("Your exported public information:\n\n");
414 memset(path, 0, sizeof(path));
415 slprintf(path, sizeof(path), "%s/%s", home, FILE_USERNAM);
417 fd = open_or_die(path, O_RDONLY);
419 while ((ret = read(fd, tmp, sizeof(tmp))) > 0) {
420 ret = write(STDOUT_FILENO, tmp, ret);
423 close(fd);
425 printf(";");
427 memset(path, 0, sizeof(path));
428 slprintf(path, sizeof(path), "%s/%s", home, FILE_PUBKEY);
430 fd = open_or_die(path, O_RDONLY);
432 ret = read(fd, tmp, sizeof(tmp));
433 if (ret != crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES)
434 panic("Cannot read public key!\n");
436 for (i = 0; i < ret; ++i)
437 if (i == ret - 1)
438 printf("%02x\n\n", (unsigned char) tmp[i]);
439 else
440 printf("%02x:", (unsigned char) tmp[i]);
442 close(fd);
443 fflush(stdout);
445 return 0;
448 static int main_dumpc(char *home)
450 check_config_exists_or_die(home);
451 check_config_keypair_or_die(home);
453 printf("Your clients:\n\n");
455 parse_userfile_and_generate_user_store_or_die(home);
457 dump_user_store();
459 destroy_user_store();
461 printf("\n");
462 die();
463 return 0;
466 static int main_dumps(char *home)
468 check_config_exists_or_die(home);
469 check_config_keypair_or_die(home);
471 printf("Your servers:\n\n");
473 parse_userfile_and_generate_serv_store_or_die(home);
475 dump_serv_store();
477 destroy_serv_store();
479 printf("\n");
480 die();
481 return 0;
484 static void daemonize(const char *lockfile)
486 char pidstr[8];
487 mode_t lperm = S_IRWXU | S_IRGRP | S_IXGRP; /* 0750 */
488 int lfp;
490 if (getppid() == 1)
491 return;
493 if (daemon(0, 1))
494 panic("Cannot daemonize: %s", strerror(errno));
496 to_std_log(&stdout);
497 to_std_log(&stderr);
499 umask(lperm);
500 if (lockfile) {
501 lfp = open(lockfile, O_RDWR | O_CREAT | O_EXCL,
502 S_IRUSR | S_IWUSR | S_IRGRP);
503 if (lfp < 0)
504 syslog_panic("Cannot create lockfile at %s! "
505 "curvetun server already running?\n",
506 lockfile);
508 slprintf(pidstr, sizeof(pidstr), "%u", getpid());
509 if (write(lfp, pidstr, strlen(pidstr)) <= 0)
510 syslog_panic("Could not write pid to pidfile %s",
511 lockfile);
513 close(lfp);
517 static int main_client(char *home, char *dev, char *alias, int daemon)
519 int ret, udp;
520 char *host, *port;
522 check_config_exists_or_die(home);
523 check_config_keypair_or_die(home);
525 parse_userfile_and_generate_serv_store_or_die(home);
527 get_serv_store_entry_by_alias(alias, alias ? strlen(alias) + 1 : 0,
528 &host, &port, &udp);
529 if (!host || !port || udp < 0)
530 panic("Did not find alias/entry in configuration!\n");
532 printf("Using [%s] -> %s:%s via %s as endpoint!\n",
533 alias ? : "default", host, port, udp ? "udp" : "tcp");
534 if (daemon)
535 daemonize(NULL);
537 ret = client_main(home, dev, host, port, udp);
539 destroy_serv_store();
541 return ret;
544 static int main_server(char *home, char *dev, char *port, int udp,
545 int ipv4, int daemon, int log)
547 int ret;
549 check_config_exists_or_die(home);
550 check_config_keypair_or_die(home);
552 if (daemon)
553 daemonize(LOCKFILE);
555 ret = server_main(home, dev, port, udp, ipv4, log);
557 unlink(LOCKFILE);
559 return ret;
562 int main(int argc, char **argv)
564 int ret = 0, c, opt_index, udp = 0, ipv4 = -1, daemon = 1, log = 1;
565 char *port = NULL, *stun = NULL, *dev = NULL, *home = NULL, *alias = NULL;
566 enum working_mode wmode = MODE_UNKNOW;
568 setfsuid(getuid());
569 setfsgid(getgid());
571 home = fetch_home_dir();
573 while ((c = getopt_long(argc, argv, short_options, long_options,
574 &opt_index)) != EOF) {
575 switch (c) {
576 case 'h':
577 help();
578 break;
579 case 'v':
580 version();
581 break;
582 case 'D':
583 daemon = 0;
584 break;
585 case 'N':
586 log = 0;
587 break;
588 case 'C':
589 wmode = MODE_DUMPC;
590 break;
591 case 'S':
592 wmode = MODE_DUMPS;
593 break;
594 case 'c':
595 wmode = MODE_CLIENT;
596 if (optarg) {
597 if (*optarg == '=')
598 optarg++;
599 alias = xstrdup(optarg);
601 break;
602 case 'd':
603 dev = xstrdup(optarg);
604 break;
605 case 'k':
606 wmode = MODE_KEYGEN;
607 break;
608 case '4':
609 ipv4 = 1;
610 break;
611 case '6':
612 ipv4 = 0;
613 break;
614 case 'x':
615 wmode = MODE_EXPORT;
616 break;
617 case 's':
618 wmode = MODE_SERVER;
619 break;
620 case 'u':
621 udp = 1;
622 break;
623 case 't':
624 stun = xstrdup(optarg);
625 break;
626 case 'p':
627 port = xstrdup(optarg);
628 break;
629 case '?':
630 switch (optopt) {
631 case 't':
632 case 'd':
633 case 'u':
634 case 'p':
635 panic("Option -%c requires an argument!\n",
636 optopt);
637 default:
638 if (isprint(optopt))
639 printf("Unknown option character `0x%X\'!\n", optopt);
640 die();
642 default:
643 break;
647 if (argc < 2)
648 help();
650 register_signal(SIGINT, signal_handler);
651 register_signal(SIGHUP, signal_handler);
652 register_signal(SIGTERM, signal_handler);
653 register_signal(SIGPIPE, signal_handler);
655 curve25519_selftest();
657 switch (wmode) {
658 case MODE_KEYGEN:
659 ret = main_keygen(home);
660 break;
661 case MODE_EXPORT:
662 ret = main_export(home);
663 break;
664 case MODE_DUMPC:
665 ret = main_dumpc(home);
666 break;
667 case MODE_DUMPS:
668 ret = main_dumps(home);
669 break;
670 case MODE_CLIENT:
671 ret = main_client(home, dev, alias, daemon);
672 break;
673 case MODE_SERVER:
674 if (!port)
675 panic("No port specified!\n");
676 if (stun)
677 print_stun_probe(stun, 3478, strtoul(port, NULL, 10));
678 ret = main_server(home, dev, port, udp, ipv4, daemon, log);
679 break;
680 default:
681 die();
684 free(dev);
685 free(stun);
686 free(port);
687 free(alias);
689 return ret;