warp-vpn

point to point VPN implementation
git clone git://git.2f30.org/warp-vpn
Log | Files | Refs | README

warp.c (3804B)


      1 /*
      2  * Design overview:
      3  *
      4  * warp-vpn implements a point to point encrypted tunnel.  It supports
      5  * layer 2 (TAP) and layer 3 (TUN) tunnels.  TCP is the only supported
      6  * transport.
      7  *
      8  * warp-vpn is a client-server design.  There can only be a single client
      9  * connected to the server at any time.  Routing between clients is handled
     10  * by the networking stack on the server side.
     11  *
     12  * When a client connects there is a mutual challenge-response phase
     13  * as shown below:
     14  *
     15  * t0: server challenges client
     16  * t1: client responds to server's challenge
     17  * t2: client challenges server
     18  * t3: server responds to client's challenge
     19  *
     20  * The challenge-response algorithm is as follows:
     21  *
     22  * The challenge is a randomly generated 64-bit integer encrypted
     23  * with the pre-shared key and sent to the receiver.
     24  * The receiver decrypts it, adds 1, encrypts it and sends it back to
     25  * the sender.  The sender verifies the response.
     26  * This algorithm is the same as what Kerberos uses for authentication.
     27  *
     28  * The warp-vpn packet format is shown below:
     29  *
     30  * [NONCE] [PAYLOAD LEN] [PAYLOAD] [TAG]
     31  */
     32 
     33 #include <sys/resource.h>
     34 
     35 #include <signal.h>
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <string.h>
     39 #include <unistd.h>
     40 
     41 #include "arg.h"
     42 #include "warp.h"
     43 
     44 char *argv0;
     45 char *bindaddr;
     46 char *host;
     47 char *port = DEFPORT;
     48 int devtype = TUNDEV;
     49 int aftype = AF_INET;
     50 int sflag;
     51 
     52 void
     53 usage(void)
     54 {
     55 	fprintf(stderr, "usage: warp-vpn [-46] [-d] -s [-b address] [-p port] [-t devtype] [-u user] interface\n");
     56 	fprintf(stderr, "       warp-vpn [-46] [-d] -h host [-p port] [-t devtype] [-u user] interface\n");
     57 	exit(1);
     58 }
     59 
     60 int
     61 main(int argc, char *argv[])
     62 {
     63 	struct rlimit rlim;
     64 	char *arg, *pw;
     65 	char *user = NOPRIVUSER;
     66 	int devfd, listenfd, netfd;
     67 
     68 	ARGBEGIN {
     69 	case '4':
     70 		aftype = AF_INET;
     71 		break;
     72 	case '6':
     73 		aftype = AF_INET6;
     74 		break;
     75 	case 'd':
     76 		debug = 1;
     77 		break;
     78 	case 's':
     79 		sflag = 1;
     80 		break;
     81 	case 'b':
     82 		bindaddr = EARGF(usage());
     83 		break;
     84 	case 'h':
     85 		host = EARGF(usage());
     86 		break;
     87 	case 'p':
     88 		port = EARGF(usage());
     89 		break;
     90 	case 't':
     91 		arg = EARGF(usage());
     92 		if (strcasecmp(arg, "tun") == 0)
     93 			devtype = TUNDEV;
     94 		else if (strcasecmp(arg, "tap") == 0)
     95 			devtype = TAPDEV;
     96 		else
     97 			usage();
     98 		break;
     99 	case 'u':
    100 		user = EARGF(usage());
    101 		break;
    102 	default:
    103 		usage();
    104 	} ARGEND
    105 
    106 	if (argc != 1 || !(sflag ^ (host != NULL)))
    107 		usage();
    108 
    109 	loginit("warp-vpn");
    110 
    111 	/* disable core dumps as memory contains the pre-shared key */
    112 	rlim.rlim_cur = rlim.rlim_max = 0;
    113 	if (setrlimit(RLIMIT_CORE, &rlim) < 0)
    114 		fatalx("failed to disable core dumps");
    115 
    116 	signal(SIGPIPE, SIG_IGN);
    117 	if (!debug)
    118 		daemon(0, 0);
    119 
    120 	/* initialize tun/tap device */
    121 	devfd = devopen(argv[0]);
    122 
    123 	/* initialize crypto engine */
    124 	if (!(pw = getenv("PW")))
    125 		fatalx("PW is not set");
    126 	cryptoinit();
    127 	derivekey(pw);
    128 	memset(pw, 0, strlen(pw));
    129 
    130 	/* initialize networking engine */
    131 	netinit();
    132 
    133 	if (sflag) {
    134 		/* invoked as server */
    135 		listenfd = serverinit(bindaddr, port);
    136 		revokeprivs(user);
    137 		if (mypledge("stdio inet", NULL) < 0)
    138 			fatal("pledge");
    139 		for (;;) {
    140 			if (debug)
    141 				logdbgx("server is ready");
    142 			if ((netfd = serveraccept(listenfd)) < 0) {
    143 				netreset();
    144 				continue;
    145 			}
    146 			logdbgx("client %s is ready", peer_ntop(netfd));
    147 			tunnel(netfd, devfd);
    148 			logdbgx("client %s disconnected", peer_ntop(netfd));
    149 			close(netfd);
    150 			netreset();
    151 		}
    152 	} else {
    153 		/* invoked as client */
    154 		revokeprivs(user);
    155 		if (mypledge("stdio dns inet", NULL) < 0)
    156 			fatal("pledge");
    157 		for (;;) {
    158 			if ((netfd = clientconnect(host, port)) < 0) {
    159 				netreset();
    160 				sleep(RECONNECTTIMEO);
    161 				continue;
    162 			}
    163 			logdbgx("connected to %s", peer_ntop(netfd));
    164 			tunnel(netfd, devfd);
    165 			logdbgx("disconnected from %s", peer_ntop(netfd));
    166 			close(netfd);
    167 			netreset();
    168 			sleep(RECONNECTTIMEO);
    169 		}
    170 	}
    171 	return 0;
    172 }