From 2b0325975aa3439f47f22211ffcc0bf2d3d8edcb Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 11 Jul 2013 10:47:50 +0200 Subject: curvetun: renamed client, server, and management files Rename those files so that they are conform to the rest of the files we have in our repository. Signed-off-by: Daniel Borkmann --- ct_client.c | 437 -------------------------- ct_server.c | 812 ------------------------------------------------ ct_servmgmt.c | 267 ---------------- ct_servmgmt.h | 15 - ct_usermgmt.c | 760 -------------------------------------------- ct_usermgmt.h | 43 --- curvetun.c | 4 +- curvetun/Makefile | 8 +- curvetun_client.c | 437 ++++++++++++++++++++++++++ curvetun_mgmt_servers.c | 267 ++++++++++++++++ curvetun_mgmt_servers.h | 15 + curvetun_mgmt_users.c | 760 ++++++++++++++++++++++++++++++++++++++++++++ curvetun_mgmt_users.h | 43 +++ curvetun_server.c | 812 ++++++++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 2340 insertions(+), 2340 deletions(-) delete mode 100644 ct_client.c delete mode 100644 ct_server.c delete mode 100644 ct_servmgmt.c delete mode 100644 ct_servmgmt.h delete mode 100644 ct_usermgmt.c delete mode 100644 ct_usermgmt.h create mode 100644 curvetun_client.c create mode 100644 curvetun_mgmt_servers.c create mode 100644 curvetun_mgmt_servers.h create mode 100644 curvetun_mgmt_users.c create mode 100644 curvetun_mgmt_users.h create mode 100644 curvetun_server.c diff --git a/ct_client.c b/ct_client.c deleted file mode 100644 index 37b6090..0000000 --- a/ct_client.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * curvetun - the cipherspace wormhole creator - * Part of the netsniff-ng project - * Copyright 2011 Daniel Borkmann , - * Subject to the GPL, version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "built_in.h" -#include "die.h" -#include "str.h" -#include "sock.h" -#include "ioops.h" -#include "curve.h" -#include "xmalloc.h" -#include "corking.h" -#include "ioexact.h" -#include "curvetun.h" -#include "ct_servmgmt.h" -#include "ct_usermgmt.h" -#include "crypto_auth_hmacsha512256.h" - -extern volatile sig_atomic_t sigint; -static volatile sig_atomic_t closed_by_server = 0; - -static void handler_udp_tun_to_net(int sfd, int dfd, struct curve25519_proto *p, - struct curve25519_struct *c, char *buff, - size_t len) -{ - char *cbuff; - ssize_t rlen, clen; - struct ct_proto *hdr; - size_t off = sizeof(struct ct_proto) + crypto_box_zerobytes; - - if (!buff || len <= off) - return; - - memset(buff, 0, len); - while ((rlen = read(sfd, buff + off, len - off)) > 0) { - hdr = (struct ct_proto *) buff; - - memset(hdr, 0, sizeof(*hdr)); - hdr->flags = 0; - - clen = curve25519_encode(c, p, (unsigned char *) (buff + off - - crypto_box_zerobytes), (rlen + - crypto_box_zerobytes), (unsigned char **) - &cbuff); - if (unlikely(clen <= 0)) - goto close; - - hdr->payload = htons((uint16_t) clen); - - set_udp_cork(dfd); - - write_exact(dfd, hdr, sizeof(struct ct_proto), 0); - write_exact(dfd, cbuff, clen, 0); - - set_udp_uncork(dfd); - - memset(buff, 0, len); - } - - return; -close: - closed_by_server = 1; -} - -static void handler_udp_net_to_tun(int sfd, int dfd, struct curve25519_proto *p, - struct curve25519_struct *c, char *buff, - size_t len) -{ - char *cbuff; - ssize_t rlen, clen; - struct ct_proto *hdr; - struct sockaddr_storage naddr; - - socklen_t nlen = sizeof(naddr); - - if (!buff || !len) - return; - - memset(&naddr, 0, sizeof(naddr)); - while ((rlen = recvfrom(sfd, buff, len, 0, (struct sockaddr *) &naddr, - &nlen)) > 0) { - hdr = (struct ct_proto *) buff; - - if (unlikely(rlen < sizeof(struct ct_proto))) - goto close; - if (unlikely(rlen - sizeof(*hdr) != ntohs(hdr->payload))) - goto close; - if (unlikely(ntohs(hdr->payload) == 0)) - goto close; - if (hdr->flags & PROTO_FLAG_EXIT) - goto close; - - clen = curve25519_decode(c, p, (unsigned char *) buff + - sizeof(struct ct_proto), - rlen - sizeof(struct ct_proto), - (unsigned char **) &cbuff, NULL); - if (unlikely(clen <= 0)) - goto close; - - cbuff += crypto_box_zerobytes; - clen -= crypto_box_zerobytes; - - if (write(dfd, cbuff, clen)) { ; } - } - - return; -close: - closed_by_server = 1; -} - -static void handler_tcp_tun_to_net(int sfd, int dfd, struct curve25519_proto *p, - struct curve25519_struct *c, char *buff, - size_t len) -{ - char *cbuff; - ssize_t rlen, clen; - struct ct_proto *hdr; - size_t off = sizeof(struct ct_proto) + crypto_box_zerobytes; - - if (!buff || len <= off) - return; - - memset(buff, 0, len); - while ((rlen = read(sfd, buff + off, len - off)) > 0) { - hdr = (struct ct_proto *) buff; - - memset(hdr, 0, sizeof(*hdr)); - hdr->flags = 0; - - clen = curve25519_encode(c, p, (unsigned char *) (buff + off - - crypto_box_zerobytes), (rlen + - crypto_box_zerobytes), (unsigned char **) - &cbuff); - if (unlikely(clen <= 0)) - goto close; - - hdr->payload = htons((uint16_t) clen); - - set_tcp_cork(dfd); - - write_exact(dfd, hdr, sizeof(struct ct_proto), 0); - write_exact(dfd, cbuff, clen, 0); - - set_tcp_uncork(dfd); - - memset(buff, 0, len); - } - - return; -close: - closed_by_server = 1; -} - -extern ssize_t handler_tcp_read(int fd, char *buff, size_t len); - -static void handler_tcp_net_to_tun(int sfd, int dfd, struct curve25519_proto *p, - struct curve25519_struct *c, char *buff, - size_t len) -{ - char *cbuff; - ssize_t rlen, clen; - struct ct_proto *hdr; - - if (!buff || !len) - return; - - while ((rlen = handler_tcp_read(sfd, buff, len)) > 0) { - hdr = (struct ct_proto *) buff; - - if (unlikely(rlen < sizeof(struct ct_proto))) - goto close; - if (unlikely(rlen - sizeof(*hdr) != ntohs(hdr->payload))) - goto close; - if (unlikely(ntohs(hdr->payload) == 0)) - goto close; - if (hdr->flags & PROTO_FLAG_EXIT) - goto close; - - clen = curve25519_decode(c, p, (unsigned char *) buff + - sizeof(struct ct_proto), - rlen - sizeof(struct ct_proto), - (unsigned char **) &cbuff, NULL); - if (unlikely(clen <= 0)) - goto close; - - cbuff += crypto_box_zerobytes; - clen -= crypto_box_zerobytes; - - if (write(dfd, cbuff, clen)) { ; } - } - - return; -close: - closed_by_server = 1; -} - -static void notify_init(int fd, int udp, struct curve25519_proto *p, - struct curve25519_struct *c, char *home) -{ - int fd2, i; - ssize_t err, clen; - size_t us_len, msg_len, pad; - struct ct_proto hdr; - char username[256], path[PATH_MAX], *us, *cbuff, *msg; - unsigned char auth[crypto_auth_hmacsha512256_BYTES], *token; - - memset(&hdr, 0, sizeof(hdr)); - hdr.flags |= PROTO_FLAG_INIT; - - memset(path, 0, sizeof(path)); - slprintf(path, sizeof(path), "%s/%s", home, FILE_USERNAM); - - fd2 = open_or_die(path, O_RDONLY); - - memset(username, 0, sizeof(username)); - err = read(fd2, username, sizeof(username)); - username[sizeof(username) - 1] = 0; - - close(fd2); - - token = get_serv_store_entry_auth_token(); - if (!token) - syslog_panic("Cannot find auth token for server!\n"); - - us_len = sizeof(struct username_struct) + crypto_box_zerobytes; - us = xzmalloc(us_len); - - err = username_msg(username, strlen(username) + 1, - us + crypto_box_zerobytes, - us_len - crypto_box_zerobytes); - if (unlikely(err)) - syslog_panic("Cannot create init message!\n"); - - clen = curve25519_encode(c, p, (unsigned char *) us, us_len, - (unsigned char **) &cbuff); - if (unlikely(clen <= 0)) - syslog_panic("Init encrypt error!\n"); - - err = crypto_auth_hmacsha512256(auth, (unsigned char *) cbuff, clen, token); - if (unlikely(err)) - syslog_panic("Cannot create init hmac message!\n"); - - pad = ((uint32_t) secrand()) % 200; - msg_len = clen + sizeof(auth) + pad; - - msg = xzmalloc(msg_len); - memcpy(msg, auth, sizeof(auth)); - memcpy(msg + sizeof(auth), cbuff, clen); - - for (i = sizeof(auth) + clen; i < msg_len; ++i) - msg[i] = (uint8_t) secrand(); - - hdr.payload = htons((uint16_t) msg_len); - - set_sock_cork(fd, udp); - - write_exact(fd, &hdr, sizeof(struct ct_proto), 0); - write_exact(fd, msg, msg_len, 0); - - set_sock_uncork(fd, udp); - - xfree(msg); - xfree(us); -} - -static void notify_close(int fd) -{ - struct ct_proto hdr; - - memset(&hdr, 0, sizeof(hdr)); - - hdr.flags |= PROTO_FLAG_EXIT; - hdr.payload = 0; - - write_exact(fd, &hdr, sizeof(hdr), 0); -} - -int client_main(char *home, char *dev, char *host, char *port, int udp) -{ - int fd = -1, tunfd = 0, retry_server = 0; - int ret, try = 1, i; - struct addrinfo hints, *ahead, *ai; - struct pollfd fds[2]; - struct curve25519_proto *p; - struct curve25519_struct *c; - char *buff; - size_t blen = TUNBUFF_SIZ; //FIXME - -retry: - if (!retry_server) { - openlog("curvetun", LOG_PID | LOG_CONS | LOG_NDELAY, LOG_DAEMON); - syslog(LOG_INFO, "curvetun client booting!\n"); - } - - c = curve25519_tfm_alloc(); - p = get_serv_store_entry_proto_inf(); - if (!p) - syslog_panic("Cannot proto!\n"); - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = udp ? SOCK_DGRAM : SOCK_STREAM; - hints.ai_protocol = udp ? IPPROTO_UDP : IPPROTO_TCP; - hints.ai_flags = AI_NUMERICSERV; - - ret = getaddrinfo(host, port, &hints, &ahead); - if (ret < 0) { - syslog(LOG_ERR, "Cannot get address info! Retry!\n"); - curve25519_tfm_free(c); - fd = -1; - retry_server = 1; - closed_by_server = 0; - sleep(1); - goto retry; - } - - for (ai = ahead; ai != NULL && fd < 0; ai = ai->ai_next) { - fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (fd < 0) - continue; - ret = connect(fd, ai->ai_addr, ai->ai_addrlen); - if (ret < 0) { - syslog(LOG_ERR, "Cannot connect to remote, try %d: %s!\n", - try++, strerror(errno)); - close(fd); - fd = -1; - continue; - } - - set_socket_keepalive(fd); - set_mtu_disc_dont(fd); - if (!udp) - set_tcp_nodelay(fd); - } - - freeaddrinfo(ahead); - - if (fd < 0) { - syslog(LOG_ERR, "Cannot create socket! Retry!\n"); - curve25519_tfm_free(c); - fd = -1; - retry_server = 1; - closed_by_server = 0; - sleep(1); - goto retry; - } - - if (!retry_server) - tunfd = tun_open_or_die(dev ? dev : DEVNAME_CLIENT, - IFF_TUN | IFF_NO_PI); - - set_nonblocking_sloppy(fd); - set_nonblocking_sloppy(tunfd); - - memset(fds, 0, sizeof(fds)); - fds[0].fd = fd; - fds[1].fd = tunfd; - fds[0].events = POLLIN; - fds[1].events = POLLIN; - - buff = xmalloc_aligned(blen, 64); - - notify_init(fd, udp, p, c, home); - - syslog(LOG_INFO, "curvetun client ready!\n"); - - while (likely(!sigint && !closed_by_server)) { - poll(fds, 2, -1); - for (i = 0; i < 2; ++i) { - if ((fds[i].revents & POLLIN) != POLLIN) - continue; - if (fds[i].fd == tunfd) { - if (udp) - handler_udp_tun_to_net(tunfd, fd, p, c, - buff, blen); - else - handler_tcp_tun_to_net(tunfd, fd, p, c, - buff, blen); - } else if (fds[i].fd == fd) { - if (udp) - handler_udp_net_to_tun(fd, tunfd, p, c, - buff, blen); - else - handler_tcp_net_to_tun(fd, tunfd, p, c, - buff, blen); - } - } - } - - syslog(LOG_INFO, "curvetun client prepare shut down!\n"); - - if (!closed_by_server) - notify_close(fd); - - xfree(buff); - close(fd); - curve25519_tfm_free(c); - - /* tundev still active */ - if (closed_by_server && !sigint) { - syslog(LOG_ERR, "curvetun connection retry attempt!\n"); - fd = -1; - retry_server = 1; - closed_by_server = 0; - sleep(1); - goto retry; - } - - close(tunfd); - syslog(LOG_INFO, "curvetun client shut down!\n"); - closelog(); - - return 0; -} diff --git a/ct_server.c b/ct_server.c deleted file mode 100644 index 9737ffd..0000000 --- a/ct_server.c +++ /dev/null @@ -1,812 +0,0 @@ -/* - * curvetun - the cipherspace wormhole creator - * Part of the netsniff-ng project - * Copyright 2011 Daniel Borkmann , - * Subject to the GPL, version 2. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "die.h" -#include "epoll2.h" -#include "ioops.h" -#include "xmalloc.h" -#include "curvetun.h" -#include "curve.h" -#include "ioexact.h" -#include "corking.h" -#include "cpus.h" -#include "sock.h" -#include "built_in.h" -#include "ct_usermgmt.h" -#include "cpusched.h" -#include "trie.h" - -struct parent_info { - int efd; - int refd; - int tunfd; - int ipv4; - int udp; -}; - -struct worker_struct { - pthread_t trid; - int efd[2]; - unsigned int cpu; - struct parent_info parent; - int (*handler)(int fd, const struct worker_struct *ws, - char *buff, size_t len); - struct curve25519_struct *c; -}; - -static struct worker_struct *threadpool = NULL; - -static int auth_log = 1; - -extern volatile sig_atomic_t sigint; - -static int handler_udp_tun_to_net(int fd, const struct worker_struct *ws, - char *buff, size_t len) __pure; -static int handler_udp_net_to_tun(int fd, const struct worker_struct *ws, - char *buff, size_t len) __pure; -static int handler_udp(int fd, const struct worker_struct *ws, - char *buff, size_t len) __pure; -static int handler_tcp_tun_to_net(int fd, const struct worker_struct *ws, - char *buff, size_t len) __pure; -static int handler_tcp_net_to_tun(int fd, const struct worker_struct *ws, - char *buff, size_t len) __pure; -static int handler_tcp(int fd, const struct worker_struct *ws, - char *buff, size_t len) __pure; -ssize_t handler_tcp_read(int fd, char *buff, size_t len); -static void *worker(void *self) __pure; - -static int handler_udp_tun_to_net(int fd, const struct worker_struct *ws, - char *buff, size_t len) -{ - int dfd, keep = 1; - char *cbuff; - ssize_t rlen, err, clen; - struct ct_proto *hdr; - struct curve25519_proto *p; - struct sockaddr_storage naddr; - socklen_t nlen; - size_t off = sizeof(struct ct_proto) + crypto_box_zerobytes; - - if (!buff || len <= off) - return 0; - - memset(buff, 0, len); - while ((rlen = read(fd, buff + off, len - off)) > 0) { - dfd = -1; nlen = 0; p = NULL; - - memset(&naddr, 0, sizeof(naddr)); - - hdr = (struct ct_proto *) buff; - memset(hdr, 0, sizeof(*hdr)); - hdr->flags = 0; - - trie_addr_lookup(buff + off, rlen, ws->parent.ipv4, &dfd, &naddr, - (size_t *) &nlen); - if (unlikely(dfd < 0 || nlen == 0)) { - memset(buff, 0, len); - continue; - } - - err = get_user_by_sockaddr(&naddr, nlen, &p); - if (unlikely(err || !p)) { - memset(buff, 0, len); - continue; - } - - clen = curve25519_encode(ws->c, p, (unsigned char *) (buff + off - - crypto_box_zerobytes), (rlen + - crypto_box_zerobytes), (unsigned char **) - &cbuff); - if (unlikely(clen <= 0)) { - memset(buff, 0, len); - continue; - } - - hdr->payload = htons((uint16_t) clen); - - set_udp_cork(dfd); - - sendto(dfd, hdr, sizeof(struct ct_proto), 0, (struct sockaddr *) - &naddr, nlen); - sendto(dfd, cbuff, clen, 0, (struct sockaddr *) &naddr, nlen); - - set_udp_uncork(dfd); - - memset(buff, 0, len); - } - - return keep; -} - -static void handler_udp_notify_close(int fd, struct sockaddr_storage *addr) -{ - struct ct_proto hdr; - - memset(&hdr, 0, sizeof(hdr)); - hdr.flags |= PROTO_FLAG_EXIT; - hdr.payload = 0; - - sendto(fd, &hdr, sizeof(hdr), 0, (struct sockaddr *) addr, - sizeof(*addr)); -} - -static int handler_udp_net_to_tun(int fd, const struct worker_struct *ws, - char *buff, size_t len) -{ - int keep = 1; - char *cbuff; - ssize_t rlen, err, clen; - struct ct_proto *hdr; - struct curve25519_proto *p; - struct sockaddr_storage naddr; - socklen_t nlen = sizeof(naddr); - - if (!buff || !len) - return 0; - - memset(&naddr, 0, sizeof(naddr)); - while ((rlen = recvfrom(fd, buff, len, 0, (struct sockaddr *) &naddr, - &nlen)) > 0) { - p = NULL; - - hdr = (struct ct_proto *) buff; - - if (unlikely(rlen < sizeof(struct ct_proto))) - goto close; - if (unlikely(rlen - sizeof(*hdr) != ntohs(hdr->payload))) - goto close; - if (unlikely(ntohs(hdr->payload) == 0)) - goto close; - if (hdr->flags & PROTO_FLAG_EXIT) { -close: - remove_user_by_sockaddr(&naddr, nlen); - trie_addr_remove_addr(&naddr, nlen); - handler_udp_notify_close(fd, &naddr); - - return keep; - } - if (hdr->flags & PROTO_FLAG_INIT) { - syslog_maybe(auth_log, LOG_INFO, "Got initial userhash " - "from remote end!\n"); - - if (unlikely(rlen - sizeof(*hdr) < - sizeof(struct username_struct))) - goto close; - - err = try_register_user_by_sockaddr(ws->c, - buff + sizeof(struct ct_proto), - rlen - sizeof(struct ct_proto), - &naddr, nlen, auth_log); - if (unlikely(err)) - goto close; - - goto next; - } - - err = get_user_by_sockaddr(&naddr, nlen, &p); - if (unlikely(err || !p)) - goto close; - - clen = curve25519_decode(ws->c, p, (unsigned char *) buff + - sizeof(struct ct_proto), - rlen - sizeof(struct ct_proto), - (unsigned char **) &cbuff, NULL); - if (unlikely(clen <= 0)) - goto close; - - cbuff += crypto_box_zerobytes; - clen -= crypto_box_zerobytes; - - err = trie_addr_maybe_update(cbuff, clen, ws->parent.ipv4, - fd, &naddr, nlen); - if (unlikely(err)) - goto next; - - err = write(ws->parent.tunfd, cbuff, clen); -next: - nlen = sizeof(naddr); - memset(&naddr, 0, sizeof(naddr)); - } - - return keep; -} - -static int handler_udp(int fd, const struct worker_struct *ws, - char *buff, size_t len) -{ - int ret = 0; - - if (fd == ws->parent.tunfd) - ret = handler_udp_tun_to_net(fd, ws, buff, len); - else - ret = handler_udp_net_to_tun(fd, ws, buff, len); - - return ret; -} - -static int handler_tcp_tun_to_net(int fd, const struct worker_struct *ws, - char *buff, size_t len) -{ - int dfd, keep = 1; - char *cbuff; - ssize_t rlen, err, clen; - struct ct_proto *hdr; - struct curve25519_proto *p; - socklen_t nlen; - size_t off = sizeof(struct ct_proto) + crypto_box_zerobytes; - - if (!buff || len <= off) - return 0; - - memset(buff, 0, len); - while ((rlen = read(fd, buff + off, len - off)) > 0) { - dfd = -1; p = NULL; - - hdr = (struct ct_proto *) buff; - memset(hdr, 0, sizeof(*hdr)); - hdr->flags = 0; - - trie_addr_lookup(buff + off, rlen, ws->parent.ipv4, &dfd, NULL, - (size_t *) &nlen); - if (unlikely(dfd < 0)) { - memset(buff, 0, len); - continue; - } - - err = get_user_by_socket(dfd, &p); - if (unlikely(err || !p)) { - memset(buff, 0, len); - continue; - } - - clen = curve25519_encode(ws->c, p, (unsigned char *) (buff + off - - crypto_box_zerobytes), (rlen + - crypto_box_zerobytes), (unsigned char **) - &cbuff); - if (unlikely(clen <= 0)) { - memset(buff, 0, len); - continue; - } - - hdr->payload = htons((uint16_t) clen); - - set_tcp_cork(dfd); - - write_exact(dfd, hdr, sizeof(struct ct_proto), 0); - write_exact(dfd, cbuff, clen, 0); - - set_tcp_uncork(dfd); - - memset(buff, 0, len); - } - - return keep; -} - -ssize_t handler_tcp_read(int fd, char *buff, size_t len) -{ - ssize_t rlen; - struct ct_proto *hdr = (struct ct_proto *) buff; - - if (!buff || !len) - return 0; - - /* May exit on EAGAIN if 0 Byte read */ - rlen = read_exact(fd, buff, sizeof(struct ct_proto), 1); - if (rlen < 0) - return rlen; - if (unlikely(ntohs(hdr->payload) > len - sizeof(struct ct_proto))) { - errno = ENOMEM; - return 1; /* Force server to close connection */ - } - - /* May not exit on EAGAIN if 0 Byte read */ - rlen = read_exact(fd, buff + sizeof(struct ct_proto), - ntohs(hdr->payload), 0); - if (rlen < 0) - return rlen; - - return sizeof(struct ct_proto) + rlen; -} - -static void handler_tcp_notify_close(int fd) -{ - struct ct_proto hdr; - - memset(&hdr, 0, sizeof(hdr)); - hdr.flags |= PROTO_FLAG_EXIT; - hdr.payload = 0; - - if (write(fd, &hdr, sizeof(hdr))) { ; } -} - -static int handler_tcp_net_to_tun(int fd, const struct worker_struct *ws, - char *buff, size_t len) -{ - int keep = 1, count = 0; - char *cbuff; - ssize_t rlen, err, clen; - struct ct_proto *hdr; - struct curve25519_proto *p; - - if (!buff || !len) - return 0; - - while ((rlen = handler_tcp_read(fd, buff, len)) > 0) { - p = NULL; - - hdr = (struct ct_proto *) buff; - - if (unlikely(rlen < sizeof(struct ct_proto))) - goto close; - if (unlikely(rlen - sizeof(*hdr) != ntohs(hdr->payload))) - goto close; - if (unlikely(ntohs(hdr->payload) == 0)) - goto close; - if (hdr->flags & PROTO_FLAG_EXIT) { -close: - remove_user_by_socket(fd); - trie_addr_remove(fd); - handler_tcp_notify_close(fd); - rlen = write(ws->parent.efd, &fd, sizeof(fd)); - - keep = 0; - return keep; - } - if (hdr->flags & PROTO_FLAG_INIT) { - syslog_maybe(auth_log, LOG_INFO, "Got initial userhash " - "from remote end!\n"); - - if (unlikely(rlen - sizeof(*hdr) < - sizeof(struct username_struct))) - goto close; - - err = try_register_user_by_socket(ws->c, - buff + sizeof(struct ct_proto), - rlen - sizeof(struct ct_proto), - fd, auth_log); - if (unlikely(err)) - goto close; - - continue; - } - - err = get_user_by_socket(fd, &p); - if (unlikely(err || !p)) - continue; - - clen = curve25519_decode(ws->c, p, (unsigned char *) buff + - sizeof(struct ct_proto), - rlen - sizeof(struct ct_proto), - (unsigned char **) &cbuff, NULL); - if (unlikely(clen <= 0)) - continue; - - cbuff += crypto_box_zerobytes; - clen -= crypto_box_zerobytes; - - err = trie_addr_maybe_update(cbuff, clen, ws->parent.ipv4, - fd, NULL, 0); - if (unlikely(err)) - continue; - - err = write(ws->parent.tunfd, cbuff, clen); - - count++; - if (count == 10) { - write_exact(ws->efd[1], &fd, sizeof(fd), 1); - /* Read later next data and let others process */ - return keep; - } - } - - return keep; -} - -static int handler_tcp(int fd, const struct worker_struct *ws, - char *buff, size_t len) -{ - int ret = 0; - - if (fd == ws->parent.tunfd) - ret = handler_tcp_tun_to_net(fd, ws, buff, len); - else - ret = handler_tcp_net_to_tun(fd, ws, buff, len); - - return ret; -} - -static void *worker(void *self) -{ - int fd, old_state; - ssize_t ret; - size_t blen = TUNBUFF_SIZ; //FIXME - struct worker_struct *ws = self; - struct pollfd fds; - char *buff; - - fds.fd = ws->efd[0]; - fds.events = POLLIN; - - ws->c = curve25519_tfm_alloc(); - buff = xmalloc_aligned(blen, 64); - - syslog(LOG_INFO, "curvetun thread on CPU%u up!\n", ws->cpu); - - pthread_cleanup_push(curve25519_tfm_free_void, ws->c); - pthread_cleanup_push(xfree_func, buff); - - while (likely(!sigint)) { - poll(&fds, 1, -1); - if ((fds.revents & POLLIN) != POLLIN) - continue; - - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); - - while ((ret = read_exact(ws->efd[0], &fd, sizeof(fd), 1)) > 0) { - if (ret != sizeof(fd)) { - sched_yield(); - continue; - } - - ret = ws->handler(fd, ws, buff, blen); - if (ret) - write_exact(ws->parent.refd, &fd, sizeof(fd), 1); - } - - pthread_setcancelstate(old_state, NULL); - } - - syslog(LOG_INFO, "curvetun thread on CPU%u down!\n", ws->cpu); - - pthread_cleanup_pop(1); - pthread_cleanup_pop(1); - - pthread_exit((void *) ((long) ws->cpu)); -} - -static void thread_spawn_or_panic(unsigned int cpus, int efd, int refd, - int tunfd, int ipv4, int udp) -{ - int i, ret; - cpu_set_t cpuset; - unsigned int threads; - - threads = cpus * THREADS_PER_CPU; - - for (i = 0; i < threads; ++i) { - CPU_ZERO(&cpuset); - threadpool[i].cpu = i % cpus; - CPU_SET(threadpool[i].cpu, &cpuset); - - ret = pipe2(threadpool[i].efd, O_NONBLOCK); - if (ret < 0) - syslog_panic("Cannot create event socket!\n"); - - threadpool[i].c = xmalloc_aligned(sizeof(*threadpool[i].c), 64); - threadpool[i].parent.efd = efd; - threadpool[i].parent.refd = refd; - threadpool[i].parent.tunfd = tunfd; - threadpool[i].parent.ipv4 = ipv4; - threadpool[i].parent.udp = udp; - threadpool[i].handler = udp ? handler_udp : handler_tcp; - - ret = pthread_create(&threadpool[i].trid, NULL, - worker, &threadpool[i]); - if (ret < 0) - syslog_panic("Thread creation failed!\n"); - - ret = pthread_setaffinity_np(threadpool[i].trid, - sizeof(cpuset), &cpuset); - if (ret < 0) - syslog_panic("Thread CPU migration failed!\n"); - - pthread_detach(threadpool[i].trid); - } - - sleep(1); -} - -static void thread_finish(unsigned int cpus) -{ - int i; - unsigned int threads; - - threads = cpus * THREADS_PER_CPU; - - for (i = 0; i < threads; ++i) { - while (pthread_join(threadpool[i].trid, NULL) < 0) - ; - - close(threadpool[i].efd[0]); - close(threadpool[i].efd[1]); - } -} - -int server_main(char *home, char *dev, char *port, int udp, int ipv4, int log) -{ - int lfd = -1, kdpfd, nfds, nfd, curfds, efd[2], refd[2], tunfd, i; - unsigned int cpus = 0, threads, udp_cpu = 0; - ssize_t ret; - struct epoll_event *events; - struct addrinfo hints, *ahead, *ai; - - auth_log = !!log; - openlog("curvetun", LOG_PID | LOG_CONS | LOG_NDELAY, LOG_DAEMON); - - syslog(LOG_INFO, "curvetun server booting!\n"); - syslog_maybe(!auth_log, LOG_INFO, "curvetun user logging disabled!\n"); - - parse_userfile_and_generate_user_store_or_die(home); - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = udp ? SOCK_DGRAM : SOCK_STREAM; - hints.ai_protocol = udp ? IPPROTO_UDP : IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE; - - ret = getaddrinfo(NULL, port, &hints, &ahead); - if (ret < 0) - syslog_panic("Cannot get address info!\n"); - - for (ai = ahead; ai != NULL && lfd < 0; ai = ai->ai_next) { - lfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (lfd < 0) - continue; - if (ai->ai_family == AF_INET6) { -#ifdef IPV6_V6ONLY - ret = set_ipv6_only(lfd); - if (ret < 0) { - close(lfd); - lfd = -1; - continue; - } -#else - close(lfd); - lfd = -1; - continue; -#endif /* IPV6_V6ONLY */ - } - - set_reuseaddr(lfd); - set_mtu_disc_dont(lfd); - - ret = bind(lfd, ai->ai_addr, ai->ai_addrlen); - if (ret < 0) { - close(lfd); - lfd = -1; - continue; - } - - if (!udp) { - ret = listen(lfd, 5); - if (ret < 0) { - close(lfd); - lfd = -1; - continue; - } - } - - if (ipv4 == -1) { - ipv4 = (ai->ai_family == AF_INET6 ? 0 : - (ai->ai_family == AF_INET ? 1 : -1)); - } - - syslog_maybe(auth_log, LOG_INFO, "curvetun on IPv%d via %s " - "on port %s!\n", ai->ai_family == AF_INET ? 4 : 6, - udp ? "UDP" : "TCP", port); - syslog_maybe(auth_log, LOG_INFO, "Allowed overlay proto is " - "IPv%d!\n", ipv4 ? 4 : 6); - } - - freeaddrinfo(ahead); - - if (lfd < 0 || ipv4 < 0) - syslog_panic("Cannot create socket!\n"); - - tunfd = tun_open_or_die(dev ? dev : DEVNAME_SERVER, IFF_TUN | IFF_NO_PI); - - pipe_or_die(efd, O_NONBLOCK); - pipe_or_die(refd, O_NONBLOCK); - - set_nonblocking(lfd); - - events = xzmalloc(MAX_EPOLL_SIZE * sizeof(*events)); - for (i = 0; i < MAX_EPOLL_SIZE; ++i) - events[i].data.fd = -1; - - kdpfd = epoll_create(MAX_EPOLL_SIZE); - if (kdpfd < 0) - syslog_panic("Cannot create socket!\n"); - - set_epoll_descriptor(kdpfd, EPOLL_CTL_ADD, lfd, - udp ? EPOLLIN | EPOLLET | EPOLLONESHOT : EPOLLIN); - set_epoll_descriptor(kdpfd, EPOLL_CTL_ADD, efd[0], EPOLLIN); - set_epoll_descriptor(kdpfd, EPOLL_CTL_ADD, refd[0], EPOLLIN); - set_epoll_descriptor(kdpfd, EPOLL_CTL_ADD, tunfd, - EPOLLIN | EPOLLET | EPOLLONESHOT); - curfds = 4; - - trie_init(); - - cpus = get_number_cpus_online(); - threads = cpus * THREADS_PER_CPU; - if (!ispow2(threads)) - syslog_panic("Thread number not power of two!\n"); - - threadpool = xzmalloc(sizeof(*threadpool) * threads); - thread_spawn_or_panic(cpus, efd[1], refd[1], tunfd, ipv4, udp); - - init_cpusched(threads); - - register_socket(tunfd); - register_socket(lfd); - - syslog(LOG_INFO, "curvetun up and running!\n"); - - while (likely(!sigint)) { - nfds = epoll_wait(kdpfd, events, curfds, -1); - if (nfds < 0) { - syslog(LOG_ERR, "epoll_wait error: %s\n", - strerror(errno)); - break; - } - - for (i = 0; i < nfds; ++i) { - if (unlikely(events[i].data.fd < 0)) - continue; - - if (events[i].data.fd == lfd && !udp) { - int ncpu; - char hbuff[256], sbuff[256]; - struct sockaddr_storage taddr; - socklen_t tlen; - - tlen = sizeof(taddr); - nfd = accept(lfd, (struct sockaddr *) &taddr, - &tlen); - if (nfd < 0) { - syslog(LOG_ERR, "accept error: %s\n", - strerror(errno)); - continue; - } - - if (curfds + 1 > MAX_EPOLL_SIZE) { - close(nfd); - continue; - } - - curfds++; - - ncpu = register_socket(nfd); - - memset(hbuff, 0, sizeof(hbuff)); - memset(sbuff, 0, sizeof(sbuff)); - getnameinfo((struct sockaddr *) &taddr, tlen, - hbuff, sizeof(hbuff), - sbuff, sizeof(sbuff), - NI_NUMERICHOST | NI_NUMERICSERV); - - syslog_maybe(auth_log, LOG_INFO, "New connection " - "from %s:%s (%d active client connections) - id %d on CPU%d", - hbuff, sbuff, curfds-4, nfd, ncpu); - - set_nonblocking(nfd); - set_socket_keepalive(nfd); - set_tcp_nodelay(nfd); - ret = set_epoll_descriptor2(kdpfd, EPOLL_CTL_ADD, - nfd, EPOLLIN | EPOLLET | EPOLLONESHOT); - if (ret < 0) { - close(nfd); - curfds--; - continue; - } - } else if (events[i].data.fd == refd[0]) { - int fd_one; - - ret = read_exact(refd[0], &fd_one, - sizeof(fd_one), 1); - if (ret != sizeof(fd_one) || fd_one <= 0) - continue; - - ret = set_epoll_descriptor2(kdpfd, EPOLL_CTL_MOD, - fd_one, EPOLLIN | EPOLLET | EPOLLONESHOT); - if (ret < 0) { - close(fd_one); - continue; - } - } else if (events[i].data.fd == efd[0]) { - int fd_del, test; - - ret = read_exact(efd[0], &fd_del, - sizeof(fd_del), 1); - if (ret != sizeof(fd_del) || fd_del <= 0) - continue; - - ret = read(fd_del, &test, sizeof(test)); - if (ret < 0 && errno == EBADF) - continue; - - ret = set_epoll_descriptor2(kdpfd, EPOLL_CTL_DEL, - fd_del, 0); - if (ret < 0) { - close(fd_del); - continue; - } - - close(fd_del); - curfds--; - unregister_socket(fd_del); - - syslog_maybe(auth_log, LOG_INFO, "Closed connection " - "with id %d (%d active client connections remain)\n", fd_del, - curfds-4); - } else { - int cpu, fd_work = events[i].data.fd; - - if (!udp) - cpu = socket_to_cpu(fd_work); - else - udp_cpu = (udp_cpu + 1) & (threads - 1); - - write_exact(threadpool[udp ? udp_cpu : cpu].efd[1], - &fd_work, sizeof(fd_work), 1); - } - } - } - - syslog(LOG_INFO, "curvetun prepare shut down!\n"); - - close(lfd); - close(efd[0]); - close(efd[1]); - close(refd[0]); - close(refd[1]); - close(tunfd); - - thread_finish(cpus); - - xfree(threadpool); - xfree(events); - - unregister_socket(lfd); - unregister_socket(tunfd); - - destroy_cpusched(); - - trie_cleanup(); - - destroy_user_store(); - - syslog(LOG_INFO, "curvetun shut down!\n"); - closelog(); - - return 0; -} diff --git a/ct_servmgmt.c b/ct_servmgmt.c deleted file mode 100644 index f069ab9..0000000 --- a/ct_servmgmt.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * netsniff-ng - the packet sniffing beast - * Copyright 2011 Daniel Borkmann. - * Subject to the GPL, version 2. - */ - -#include -#include -#include -#include -#include -#include - -#include "die.h" -#include "str.h" -#include "built_in.h" -#include "locking.h" -#include "xmalloc.h" -#include "curvetun.h" -#include "curve.h" -#include "crypto.h" -#include "ct_servmgmt.h" - -struct server_store { - int udp; - char alias[256]; - char host[256]; - char port[6]; /* 5 + \0 */ - unsigned char publickey[crypto_box_pub_key_size]; - struct curve25519_proto proto_inf; - unsigned char auth_token[crypto_auth_hmacsha512256_KEYBYTES]; - struct server_store *next; -}; - -static struct server_store *store = NULL; -static struct server_store *selected = NULL; -static struct rwlock store_lock; - -static struct server_store *server_store_alloc(void) -{ - return xzmalloc(sizeof(struct server_store)); -} - -static void server_store_free(struct server_store *ss) -{ - if (!ss) - return; - memset(ss, 0, sizeof(struct server_store)); - xfree(ss); -} - -enum parse_states { - PARSE_ALIAS, - PARSE_SERVER, - PARSE_PORT, - PARSE_CARRIER, - PARSE_PUBKEY, - PARSE_DONE, -}; - -static int parse_line(char *line, char *homedir) -{ - char *str; - enum parse_states s = PARSE_ALIAS; - struct server_store *elem; - unsigned char pkey[crypto_box_pub_key_size]; - - elem = server_store_alloc(); - elem->next = store; - - str = strtok(line, ";"); - for (; str != NULL;) { - switch (s) { - case PARSE_ALIAS: - strlcpy(elem->alias, str, sizeof(elem->alias)); - s = PARSE_SERVER; - break; - case PARSE_SERVER: - strlcpy(elem->host, str, sizeof(elem->host)); - s = PARSE_PORT; - break; - case PARSE_PORT: - strlcpy(elem->port, str, sizeof(elem->port)); - s = PARSE_CARRIER; - break; - case PARSE_CARRIER: - if (!strncmp("udp", str, strlen("udp"))) - elem->udp = 1; - else if (!strncmp("tcp", str, strlen("tcp"))) - elem->udp = 0; - else { - syslog(LOG_ERR, "Incorrect carrier type !(udp|tcp) in server spec.\n"); - return -EIO; - } - s = PARSE_PUBKEY; - break; - case PARSE_PUBKEY: - if (!curve25519_pubkey_hexparse_32(pkey, sizeof(pkey), - str, strlen(str))) - return -EINVAL; - memcpy(elem->publickey, pkey, sizeof(elem->publickey)); - memcpy(elem->auth_token, pkey, sizeof(elem->auth_token)); - curve25519_proto_init(&elem->proto_inf, elem->publickey, sizeof(elem->publickey)); - s = PARSE_DONE; - break; - case PARSE_DONE: - break; - default: - return -EIO; - } - - str = strtok(NULL, ";"); - } - - store = elem; - return s == PARSE_DONE ? 0 : -EIO; -} - -void parse_userfile_and_generate_serv_store_or_die(char *homedir) -{ - FILE *fp; - char path[PATH_MAX], buff[1024]; - int line = 1, ret; - - memset(path, 0, sizeof(path)); - slprintf(path, sizeof(path), "%s/%s", homedir, FILE_SERVERS); - - rwlock_init(&store_lock); - rwlock_wr_lock(&store_lock); - - fp = fopen(path, "r"); - if (!fp) - panic("Cannot open server file!\n"); - - memset(buff, 0, sizeof(buff)); - while (fgets(buff, sizeof(buff), fp) != NULL) { - buff[sizeof(buff) - 1] = 0; - /* A comment. Skip this line */ - if (buff[0] == '#' || buff[0] == '\n') { - memset(buff, 0, sizeof(buff)); - line++; - continue; - } - - ret = parse_line(buff, homedir); - if (ret < 0) - panic("Cannot parse line %d from clients!\n", line); - line++; - memset(buff, 0, sizeof(buff)); - } - - fclose(fp); - - if (store == NULL) - panic("No registered servers found!\n"); - - rwlock_unlock(&store_lock); -} - -void dump_serv_store(void) -{ - int i; - struct server_store *elem; - - rwlock_rd_lock(&store_lock); - elem = store; - while (elem) { - printf("[%s] -> %s:%s via %s -> ", elem->alias, - elem->host, elem->port, - elem->udp ? "udp" : "tcp"); - for (i = 0; i < sizeof(elem->publickey); ++i) - if (i == (sizeof(elem->publickey) - 1)) - printf("%02x\n", (unsigned char) - elem->publickey[i]); - else - printf("%02x:", (unsigned char) - elem->publickey[i]); - elem = elem->next; - } - rwlock_unlock(&store_lock); -} - -void destroy_serv_store(void) -{ - struct server_store *elem, *nelem = NULL; - - rwlock_wr_lock(&store_lock); - selected = NULL; - elem = store; - while (elem) { - nelem = elem->next; - elem->next = NULL; - server_store_free(elem); - elem = nelem; - } - rwlock_unlock(&store_lock); - rwlock_destroy(&store_lock); -} - -void get_serv_store_entry_by_alias(char *alias, size_t len, - char **host, char **port, int *udp) -{ - struct server_store *elem; - - rwlock_rd_lock(&store_lock); - elem = store; - if (!alias) { - while (elem && elem->next) - elem = elem->next; - if (elem) { - (*host) = elem->host; - (*port) = elem->port; - (*udp) = elem->udp; - selected = elem; - } else { - rwlock_unlock(&store_lock); - goto nothing; - } - } else { - while (elem) { - if (!strncmp(elem->alias, alias, - min(len, strlen(elem->alias) + 1))) - break; - elem = elem->next; - } - if (elem) { - (*host) = elem->host; - (*port) = elem->port; - (*udp) = elem->udp; - selected = elem; - } else { - rwlock_unlock(&store_lock); - goto nothing; - } - } - rwlock_unlock(&store_lock); - - return; -nothing: - (*host) = NULL; - (*port) = NULL; - (*udp) = -1; -} - -struct curve25519_proto *get_serv_store_entry_proto_inf(void) -{ - struct curve25519_proto *ret = NULL; - - rwlock_rd_lock(&store_lock); - if (selected) - ret = &selected->proto_inf; - rwlock_unlock(&store_lock); - - return ret; -} - -unsigned char *get_serv_store_entry_auth_token(void) -{ - unsigned char *ret = NULL; - - rwlock_rd_lock(&store_lock); - if (selected) - ret = selected->auth_token; - rwlock_unlock(&store_lock); - - return ret; -} diff --git a/ct_servmgmt.h b/ct_servmgmt.h deleted file mode 100644 index 9c5cd84..0000000 --- a/ct_servmgmt.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef CT_SERVMGMT_H -#define CT_SERVMGMT_H - -#include -#include "curve.h" - -extern void parse_userfile_and_generate_serv_store_or_die(char *homedir); -extern void dump_serv_store(void); -extern void get_serv_store_entry_by_alias(char *alias, size_t len, - char **host, char **port, int *udp); -extern struct curve25519_proto *get_serv_store_entry_proto_inf(void); -extern unsigned char *get_serv_store_entry_auth_token(void); -extern void destroy_serv_store(void); - -#endif /* CT_SERVMGMT_H */ diff --git a/ct_usermgmt.c b/ct_usermgmt.c deleted file mode 100644 index c303cd5..0000000 --- a/ct_usermgmt.c +++ /dev/null @@ -1,760 +0,0 @@ -/* - * netsniff-ng - the packet sniffing beast - * Copyright 2011 Daniel Borkmann. - * Subject to the GPL, version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "die.h" -#include "ct_usermgmt.h" -#include "locking.h" -#include "xmalloc.h" -#include "ioexact.h" -#include "ioops.h" -#include "str.h" -#include "curvetun.h" -#include "curve.h" -#include "crypto.h" -#include "hash.h" - -struct user_store { - char username[256]; - unsigned char publickey[crypto_box_pub_key_size]; - struct curve25519_proto proto_inf; - struct user_store *next; -}; - -struct sock_map_entry { - int fd; - struct curve25519_proto *proto; - struct sock_map_entry *next; -}; - -struct sockaddr_map_entry { - struct sockaddr_storage *sa; - size_t sa_len; - struct curve25519_proto *proto; - struct sockaddr_map_entry *next; -}; - -static struct user_store *store = NULL; -static struct rwlock store_lock; - -static struct hash_table sock_mapper; -static struct rwlock sock_map_lock; - -static struct hash_table sockaddr_mapper; -static struct rwlock sockaddr_map_lock; - -static unsigned char token[crypto_auth_hmacsha512256_KEYBYTES]; - -static void init_sock_mapper(void) -{ - rwlock_init(&sock_map_lock); - - rwlock_wr_lock(&sock_map_lock); - - memset(&sock_mapper, 0, sizeof(sock_mapper)); - init_hash(&sock_mapper); - - rwlock_unlock(&sock_map_lock); -} - -static void init_sockaddr_mapper(void) -{ - rwlock_init(&sockaddr_map_lock); - rwlock_wr_lock(&sockaddr_map_lock); - - memset(&sockaddr_mapper, 0, sizeof(sockaddr_mapper)); - init_hash(&sockaddr_mapper); - - rwlock_unlock(&sockaddr_map_lock); -} - -static int cleanup_batch_sock_mapper(void *ptr) -{ - struct sock_map_entry *next; - struct sock_map_entry *e = ptr; - - if (!e) - return 0; - - while ((next = e->next)) { - e->next = NULL; - xfree(e); - e = next; - } - - xfree(e); - - return 0; -} - -static void destroy_sock_mapper(void) -{ - rwlock_wr_lock(&sock_map_lock); - for_each_hash(&sock_mapper, cleanup_batch_sock_mapper); - free_hash(&sock_mapper); - rwlock_unlock(&sock_map_lock); - - rwlock_destroy(&sock_map_lock); -} - -static int cleanup_batch_sockaddr_mapper(void *ptr) -{ - struct sockaddr_map_entry *next; - struct sockaddr_map_entry *e = ptr; - - if (!e) - return 0; - - while ((next = e->next)) { - e->next = NULL; - xfree(e); - e = next; - } - - xfree(e); - return 0; -} - -static void destroy_sockaddr_mapper(void) -{ - rwlock_wr_lock(&sockaddr_map_lock); - for_each_hash(&sockaddr_mapper, cleanup_batch_sockaddr_mapper); - free_hash(&sockaddr_mapper); - rwlock_unlock(&sockaddr_map_lock); - - rwlock_destroy(&sockaddr_map_lock); -} - -static struct user_store *user_store_alloc(void) -{ - return xzmalloc(sizeof(struct user_store)); -} - -static void user_store_free(struct user_store *us) -{ - if (!us) - return; - memset(us, 0, sizeof(struct user_store)); - xfree(us); -} - -/* already in lock */ -static int __check_duplicate_username(char *username, size_t len) -{ - int duplicate = 0; - struct user_store *elem = store; - - while (elem) { - if (!memcmp(elem->username, username, - strlen(elem->username) + 1)) { - duplicate = 1; - break; - } - elem = elem->next; - } - - return duplicate; -} - -/* already in lock */ -static int __check_duplicate_pubkey(unsigned char *pubkey, size_t len) -{ - int duplicate = 0; - struct user_store *elem = store; - - while (elem) { - if (!memcmp(elem->publickey, pubkey, - sizeof(elem->publickey))) { - duplicate = 1; - break; - } - elem = elem->next; - } - - return duplicate; -} - -enum parse_states { - PARSE_USERNAME, - PARSE_PUBKEY, - PARSE_DONE, -}; - -static int parse_line(char *line, char *homedir) -{ - char *str; - enum parse_states s = PARSE_USERNAME; - struct user_store *elem; - unsigned char pkey[crypto_box_pub_key_size]; - - elem = user_store_alloc(); - elem->next = store; - - str = strtok(line, ";"); - for (; str != NULL;) { - switch (s) { - case PARSE_USERNAME: - if (__check_duplicate_username(str, strlen(str) + 1)) - return -EINVAL; - strlcpy(elem->username, str, sizeof(elem->username)); - s = PARSE_PUBKEY; - break; - case PARSE_PUBKEY: - if (!curve25519_pubkey_hexparse_32(pkey, sizeof(pkey), - str, strlen(str))) - return -EINVAL; - if (__check_duplicate_pubkey(pkey, sizeof(pkey))) - return -EINVAL; - memcpy(elem->publickey, pkey, sizeof(elem->publickey)); - curve25519_proto_init(&elem->proto_inf, elem->publickey, sizeof(elem->publickey)); - s = PARSE_DONE; - break; - case PARSE_DONE: - break; - default: - return -EIO; - } - - str = strtok(NULL, ";"); - } - - store = elem; - return s == PARSE_DONE ? 0 : -EIO; -} - -void parse_userfile_and_generate_user_store_or_die(char *homedir) -{ - FILE *fp; - char path[PATH_MAX], buff[512]; - int line = 1, ret, fd; - - memset(path, 0, sizeof(path)); - slprintf(path, sizeof(path), "%s/%s", homedir, FILE_CLIENTS); - - rwlock_init(&store_lock); - rwlock_wr_lock(&store_lock); - - fp = fopen(path, "r"); - if (!fp) - panic("Cannot open client file!\n"); - - memset(buff, 0, sizeof(buff)); - while (fgets(buff, sizeof(buff), fp) != NULL) { - buff[sizeof(buff) - 1] = 0; - /* A comment. Skip this line */ - if (buff[0] == '#' || buff[0] == '\n') { - memset(buff, 0, sizeof(buff)); - line++; - continue; - } - - ret = parse_line(buff, homedir); - if (ret < 0) - panic("Cannot parse line %d from clients!\n", line); - line++; - memset(buff, 0, sizeof(buff)); - } - - fclose(fp); - - if (store == NULL) - panic("No registered clients found!\n"); - - rwlock_unlock(&store_lock); - - init_sock_mapper(); - init_sockaddr_mapper(); - - /* - * Pubkey is also used as a hmac of the initial packet to check - * the integrity of the packet, so that we know if it's just random - * garbage or a 'valid' packet. Again, just for the integrity! - */ - - memset(path, 0, sizeof(path)); - slprintf(path, sizeof(path), "%s/%s", homedir, FILE_PUBKEY); - - fd = open_or_die(path, O_RDONLY); - ret = read(fd, token, sizeof(token)); - if (ret != crypto_auth_hmacsha512256_KEYBYTES) - panic("Cannot read public key!\n"); - close(fd); -} - -void dump_user_store(void) -{ - int i; - struct user_store *elem; - - rwlock_rd_lock(&store_lock); - - elem = store; - while (elem) { - printf("%s -> ", elem->username); - for (i = 0; i < sizeof(elem->publickey); ++i) - if (i == (sizeof(elem->publickey) - 1)) - printf("%02x\n", (unsigned char) - elem->publickey[i]); - else - printf("%02x:", (unsigned char) - elem->publickey[i]); - elem = elem->next; - } - - rwlock_unlock(&store_lock); -} - -void destroy_user_store(void) -{ - struct user_store *elem, *nelem = NULL; - - rwlock_wr_lock(&store_lock); - - elem = store; - while (elem) { - nelem = elem->next; - elem->next = NULL; - user_store_free(elem); - elem = nelem; - } - rwlock_unlock(&store_lock); - - rwlock_destroy(&store_lock); - - destroy_sock_mapper(); - destroy_sockaddr_mapper(); -} - -int username_msg(char *username, size_t len, char *dst, size_t dlen) -{ - int fd; - ssize_t ret; - uint32_t salt; - unsigned char h[crypto_hash_sha512_BYTES]; - struct username_struct *us = (struct username_struct *) dst; - char *uname; - size_t uname_len; - - if (dlen < sizeof(struct username_struct)) - return -ENOMEM; - - uname_len = 512; - uname = xzmalloc(uname_len); - - fd = open_or_die("/dev/random", O_RDONLY); - ret = read_exact(fd, &salt, sizeof(salt), 0); - if (ret != sizeof(salt)) - panic("Cannot read from /dev/random!\n"); - close(fd); - - slprintf(uname, uname_len, "%s%u", username, salt); - crypto_hash_sha512(h, (unsigned char *) uname, strlen(uname)); - - us->salt = htonl(salt); - memcpy(us->hash, h, sizeof(us->hash)); - - xfree(uname); - return 0; -} - -enum is_user_enum username_msg_is_user(char *src, size_t slen, char *username, - size_t len) -{ - char *uname; - size_t uname_len; - uint32_t salt; - struct username_struct *us = (struct username_struct *) src; - unsigned char h[crypto_hash_sha512_BYTES]; - - if (slen < sizeof(struct username_struct)) { - errno = ENOMEM; - return USERNAMES_ERR; - } - - uname_len = 512; - uname = xzmalloc(uname_len); - - salt = ntohl(us->salt); - - slprintf(uname, uname_len, "%s%u", username, salt); - crypto_hash_sha512(h, (unsigned char *) uname, strlen(uname)); - xfree(uname); - - if (!crypto_verify_32(&h[0], &us->hash[0]) && - !crypto_verify_32(&h[32], &us->hash[32])) - return USERNAMES_OK; - else - return USERNAMES_NE; -} - -static int register_user_by_socket(int fd, struct curve25519_proto *proto) -{ - void **pos; - struct sock_map_entry *entry; - - rwlock_wr_lock(&sock_map_lock); - - entry = xzmalloc(sizeof(*entry)); - entry->fd = fd; - entry->proto = proto; - - pos = insert_hash(entry->fd, entry, &sock_mapper); - if (pos) { - entry->next = (*pos); - (*pos) = entry; - } - - rwlock_unlock(&sock_map_lock); - - return 0; -} - -static int register_user_by_sockaddr(struct sockaddr_storage *sa, - size_t sa_len, - struct curve25519_proto *proto) -{ - void **pos; - struct sockaddr_map_entry *entry; - unsigned int hash = hash_name((char *) sa, sa_len); - - rwlock_wr_lock(&sockaddr_map_lock); - - entry = xzmalloc(sizeof(*entry)); - entry->sa = xmemdupz(sa, sa_len); - entry->sa_len = sa_len; - entry->proto = proto; - - pos = insert_hash(hash, entry, &sockaddr_mapper); - if (pos) { - entry->next = (*pos); - (*pos) = entry; - } - - rwlock_unlock(&sockaddr_map_lock); - - return 0; -} - -int try_register_user_by_socket(struct curve25519_struct *c, - char *src, size_t slen, int sock, int log) -{ - int ret = -1; - char *cbuff = NULL; - size_t real_len = 132; - ssize_t clen; - struct user_store *elem; - enum is_user_enum err; - unsigned char auth[crypto_auth_hmacsha512256_BYTES]; - struct taia arrival_taia; - - /* assert(132 == clen + sizeof(auth)); */ - /* - * Check hmac first, if malicious, drop immediately before we - * investigate more efforts. - */ - if (slen < real_len) - return -1; - - taia_now(&arrival_taia); - - memcpy(auth, src, sizeof(auth)); - - src += sizeof(auth); - real_len -= sizeof(auth); - - if (crypto_auth_hmacsha512256_verify(auth, (unsigned char *) src, - real_len, token)) { - syslog(LOG_ERR, "Bad packet hmac for id %d! Dropping!\n", sock); - return -1; - } else { - if (log) - syslog(LOG_INFO, "Good packet hmac for id %d!\n", sock); - } - - rwlock_rd_lock(&store_lock); - - elem = store; - while (elem) { - clen = curve25519_decode(c, &elem->proto_inf, - (unsigned char *) src, real_len, - (unsigned char **) &cbuff, - &arrival_taia); - if (clen <= 0) { - elem = elem->next; - continue; - } - - cbuff += crypto_box_zerobytes; - clen -= crypto_box_zerobytes; - - if (log) - syslog(LOG_INFO, "Packet decoded successfully for id %d!\n", sock); - - err = username_msg_is_user(cbuff, clen, elem->username, - strlen(elem->username) + 1); - if (err == USERNAMES_OK) { - if (log) - syslog(LOG_INFO, "Found user %s for id %d! Registering ...\n", - elem->username, sock); - ret = register_user_by_socket(sock, &elem->proto_inf); - break; - } - - elem = elem->next; - } - - rwlock_unlock(&store_lock); - - if (ret == -1) - syslog(LOG_ERR, "User not found! Dropping connection!\n"); - - return ret; -} - -int try_register_user_by_sockaddr(struct curve25519_struct *c, - char *src, size_t slen, - struct sockaddr_storage *sa, - size_t sa_len, int log) -{ - int ret = -1; - char *cbuff = NULL; - struct user_store *elem; - ssize_t clen; - size_t real_len = 132; - enum is_user_enum err; - unsigned char auth[crypto_auth_hmacsha512256_BYTES]; - struct taia arrival_taia; - - /* assert(132 == clen + sizeof(auth)); */ - /* - * Check hmac first, if malicious, drop immediately before we - * investigate more efforts. - */ - if (slen < real_len) - return -1; - - taia_now(&arrival_taia); - - memcpy(auth, src, sizeof(auth)); - - src += sizeof(auth); - real_len -= sizeof(auth); - - if (crypto_auth_hmacsha512256_verify(auth, (unsigned char *) src, - real_len, token)) { - syslog(LOG_ERR, "Got bad packet hmac! Dropping!\n"); - return -1; - } else { - if (log) - syslog(LOG_INFO, "Got good packet hmac!\n"); - } - - rwlock_rd_lock(&store_lock); - - elem = store; - while (elem) { - clen = curve25519_decode(c, &elem->proto_inf, - (unsigned char *) src, real_len, - (unsigned char **) &cbuff, - &arrival_taia); - if (clen <= 0) { - elem = elem->next; - continue; - } - - cbuff += crypto_box_zerobytes; - clen -= crypto_box_zerobytes; - - if (log) - syslog(LOG_INFO, "Packet decoded successfully!\n"); - - err = username_msg_is_user(cbuff, clen, elem->username, - strlen(elem->username) + 1); - if (err == USERNAMES_OK) { - if (log) - syslog(LOG_INFO, "Found user %s! Registering ...\n", - elem->username); - ret = register_user_by_sockaddr(sa, sa_len, - &elem->proto_inf); - break; - } - - elem = elem->next; - } - - rwlock_unlock(&store_lock); - - if (ret == -1) - syslog(LOG_ERR, "User not found! Dropping connection!\n"); - - return ret; -} - -int get_user_by_socket(int fd, struct curve25519_proto **proto) -{ - int ret = -1; - struct sock_map_entry *entry; - - errno = 0; - - rwlock_rd_lock(&sock_map_lock); - - entry = lookup_hash(fd, &sock_mapper); - while (entry && fd != entry->fd) - entry = entry->next; - if (entry && fd == entry->fd) { - (*proto) = entry->proto; - ret = 0; - } else { - (*proto) = NULL; - errno = ENOENT; - } - - rwlock_unlock(&sock_map_lock); - - return ret; -} - -int get_user_by_sockaddr(struct sockaddr_storage *sa, size_t sa_len, - struct curve25519_proto **proto) -{ - int ret = -1; - struct sockaddr_map_entry *entry; - unsigned int hash = hash_name((char *) sa, sa_len); - - errno = 0; - - rwlock_rd_lock(&sockaddr_map_lock); - - entry = lookup_hash(hash, &sockaddr_mapper); - while (entry && entry->sa_len == sa_len && - memcmp(sa, entry->sa, entry->sa_len)) - entry = entry->next; - if (entry && entry->sa_len == sa_len && - !memcmp(sa, entry->sa, entry->sa_len)) { - (*proto) = entry->proto; - ret = 0; - } else { - (*proto) = NULL; - errno = ENOENT; - } - - rwlock_unlock(&sockaddr_map_lock); - - return ret; -} - -static struct sock_map_entry *socket_to_sock_map_entry(int fd) -{ - struct sock_map_entry *entry, *ret = NULL; - - errno = 0; - - rwlock_rd_lock(&sock_map_lock); - - entry = lookup_hash(fd, &sock_mapper); - while (entry && fd != entry->fd) - entry = entry->next; - if (entry && fd == entry->fd) - ret = entry; - else - errno = ENOENT; - - rwlock_unlock(&sock_map_lock); - - return ret; -} - -void remove_user_by_socket(int fd) -{ - struct sock_map_entry *pos; - struct sock_map_entry *entry = socket_to_sock_map_entry(fd); - - if (!entry) - return; - - rwlock_wr_lock(&sock_map_lock); - - pos = remove_hash(entry->fd, entry, entry->next, &sock_mapper); - while (pos && pos->next && pos->next != entry) - pos = pos->next; - if (pos && pos->next && pos->next == entry) - pos->next = entry->next; - - memset(entry->proto->enonce, 0, sizeof(entry->proto->enonce)); - memset(entry->proto->dnonce, 0, sizeof(entry->proto->dnonce)); - - entry->proto = NULL; - entry->next = NULL; - - xfree(entry); - - rwlock_unlock(&sock_map_lock); -} - -static struct sockaddr_map_entry * -sockaddr_to_sockaddr_map_entry(struct sockaddr_storage *sa, size_t sa_len) -{ - struct sockaddr_map_entry *entry, *ret = NULL; - unsigned int hash = hash_name((char *) sa, sa_len); - - errno = 0; - - rwlock_rd_lock(&sockaddr_map_lock); - - entry = lookup_hash(hash, &sockaddr_mapper); - while (entry && entry->sa_len == sa_len && - memcmp(sa, entry->sa, entry->sa_len)) - entry = entry->next; - if (entry && entry->sa_len == sa_len && - !memcmp(sa, entry->sa, entry->sa_len)) - ret = entry; - else - errno = ENOENT; - - rwlock_unlock(&sockaddr_map_lock); - - return ret; -} - -void remove_user_by_sockaddr(struct sockaddr_storage *sa, size_t sa_len) -{ - struct sockaddr_map_entry *pos; - struct sockaddr_map_entry *entry; - unsigned int hash = hash_name((char *) sa, sa_len); - - entry = sockaddr_to_sockaddr_map_entry(sa, sa_len); - if (!entry) - return; - - rwlock_wr_lock(&sockaddr_map_lock); - - pos = remove_hash(hash, entry, entry->next, &sockaddr_mapper); - while (pos && pos->next && pos->next != entry) - pos = pos->next; - if (pos && pos->next && pos->next == entry) - pos->next = entry->next; - - memset(entry->proto->enonce, 0, sizeof(entry->proto->enonce)); - memset(entry->proto->dnonce, 0, sizeof(entry->proto->dnonce)); - - entry->proto = NULL; - entry->next = NULL; - - xfree(entry->sa); - xfree(entry); - - rwlock_unlock(&sockaddr_map_lock); -} diff --git a/ct_usermgmt.h b/ct_usermgmt.h deleted file mode 100644 index ff9d601..0000000 --- a/ct_usermgmt.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef CT_USERMGMT_H -#define CT_USERMGMT_H - -#include - -#include "curve.h" -#include "crypto_hash_sha512.h" - -enum is_user_enum { - USERNAMES_OK = 0, /* Usernames match, valid 'token' */ - USERNAMES_NE, /* Usernames do not match */ - USERNAMES_TS, /* Usernames match, but 'token' invalid, - Drop connection here */ - USERNAMES_ERR, -}; - -struct username_struct { - uint32_t salt; - uint8_t hash[crypto_hash_sha512_BYTES]; -}; - -extern int username_msg(char *username, size_t len, char *dst, size_t dlen); -extern enum is_user_enum username_msg_is_user(char *src, size_t slen, - char *username, size_t len); -extern void parse_userfile_and_generate_user_store_or_die(char *homedir); -extern void dump_user_store(void); -extern void destroy_user_store(void); - -extern int get_user_by_socket(int sock, struct curve25519_proto **proto); -extern int get_user_by_sockaddr(struct sockaddr_storage *sa, size_t sa_len, - struct curve25519_proto **proto); -extern int try_register_user_by_socket(struct curve25519_struct *c, - char *src, size_t slen, int sock, - int log); -extern int try_register_user_by_sockaddr(struct curve25519_struct *c, - char *src, size_t slen, - struct sockaddr_storage *sa, - size_t sa_len, int log); -extern void remove_user_by_socket(int sock); -extern void remove_user_by_sockaddr(struct sockaddr_storage *sa, - size_t sa_len); - -#endif /* CT_USERMGMT_H */ diff --git a/curvetun.c b/curvetun.c index 6d93ec9..00bbecc 100644 --- a/curvetun.c +++ b/curvetun.c @@ -35,8 +35,8 @@ #include "curvetun.h" #include "curve.h" #include "config.h" -#include "ct_usermgmt.h" -#include "ct_servmgmt.h" +#include "curvetun_mgmt_users.h" +#include "curvetun_mgmt_servers.h" #include "ioops.h" #include "tprintf.h" #include "crypto.h" diff --git a/curvetun/Makefile b/curvetun/Makefile index 5882683..4f28adf 100644 --- a/curvetun/Makefile +++ b/curvetun/Makefile @@ -22,8 +22,8 @@ curvetun-objs = xmalloc.o \ ioexact.o \ ioops.o \ cpusched.o \ - ct_usermgmt.o \ - ct_servmgmt.o \ - ct_server.o \ - ct_client.o \ + curvetun_mgmt_servers.o \ + curvetun_mgmt_users.o \ + curvetun_server.o \ + curvetun_client.o \ curvetun.o diff --git a/curvetun_client.c b/curvetun_client.c new file mode 100644 index 0000000..e102db0 --- /dev/null +++ b/curvetun_client.c @@ -0,0 +1,437 @@ +/* + * curvetun - the cipherspace wormhole creator + * Part of the netsniff-ng project + * Copyright 2011 Daniel Borkmann , + * Subject to the GPL, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "built_in.h" +#include "die.h" +#include "str.h" +#include "sock.h" +#include "ioops.h" +#include "curve.h" +#include "xmalloc.h" +#include "corking.h" +#include "ioexact.h" +#include "curvetun.h" +#include "curvetun_mgmt_servers.h" +#include "curvetun_mgmt_users.h" +#include "crypto_auth_hmacsha512256.h" + +extern volatile sig_atomic_t sigint; +static volatile sig_atomic_t closed_by_server = 0; + +static void handler_udp_tun_to_net(int sfd, int dfd, struct curve25519_proto *p, + struct curve25519_struct *c, char *buff, + size_t len) +{ + char *cbuff; + ssize_t rlen, clen; + struct ct_proto *hdr; + size_t off = sizeof(struct ct_proto) + crypto_box_zerobytes; + + if (!buff || len <= off) + return; + + memset(buff, 0, len); + while ((rlen = read(sfd, buff + off, len - off)) > 0) { + hdr = (struct ct_proto *) buff; + + memset(hdr, 0, sizeof(*hdr)); + hdr->flags = 0; + + clen = curve25519_encode(c, p, (unsigned char *) (buff + off - + crypto_box_zerobytes), (rlen + + crypto_box_zerobytes), (unsigned char **) + &cbuff); + if (unlikely(clen <= 0)) + goto close; + + hdr->payload = htons((uint16_t) clen); + + set_udp_cork(dfd); + + write_exact(dfd, hdr, sizeof(struct ct_proto), 0); + write_exact(dfd, cbuff, clen, 0); + + set_udp_uncork(dfd); + + memset(buff, 0, len); + } + + return; +close: + closed_by_server = 1; +} + +static void handler_udp_net_to_tun(int sfd, int dfd, struct curve25519_proto *p, + struct curve25519_struct *c, char *buff, + size_t len) +{ + char *cbuff; + ssize_t rlen, clen; + struct ct_proto *hdr; + struct sockaddr_storage naddr; + + socklen_t nlen = sizeof(naddr); + + if (!buff || !len) + return; + + memset(&naddr, 0, sizeof(naddr)); + while ((rlen = recvfrom(sfd, buff, len, 0, (struct sockaddr *) &naddr, + &nlen)) > 0) { + hdr = (struct ct_proto *) buff; + + if (unlikely(rlen < sizeof(struct ct_proto))) + goto close; + if (unlikely(rlen - sizeof(*hdr) != ntohs(hdr->payload))) + goto close; + if (unlikely(ntohs(hdr->payload) == 0)) + goto close; + if (hdr->flags & PROTO_FLAG_EXIT) + goto close; + + clen = curve25519_decode(c, p, (unsigned char *) buff + + sizeof(struct ct_proto), + rlen - sizeof(struct ct_proto), + (unsigned char **) &cbuff, NULL); + if (unlikely(clen <= 0)) + goto close; + + cbuff += crypto_box_zerobytes; + clen -= crypto_box_zerobytes; + + if (write(dfd, cbuff, clen)) { ; } + } + + return; +close: + closed_by_server = 1; +} + +static void handler_tcp_tun_to_net(int sfd, int dfd, struct curve25519_proto *p, + struct curve25519_struct *c, char *buff, + size_t len) +{ + char *cbuff; + ssize_t rlen, clen; + struct ct_proto *hdr; + size_t off = sizeof(struct ct_proto) + crypto_box_zerobytes; + + if (!buff || len <= off) + return; + + memset(buff, 0, len); + while ((rlen = read(sfd, buff + off, len - off)) > 0) { + hdr = (struct ct_proto *) buff; + + memset(hdr, 0, sizeof(*hdr)); + hdr->flags = 0; + + clen = curve25519_encode(c, p, (unsigned char *) (buff + off - + crypto_box_zerobytes), (rlen + + crypto_box_zerobytes), (unsigned char **) + &cbuff); + if (unlikely(clen <= 0)) + goto close; + + hdr->payload = htons((uint16_t) clen); + + set_tcp_cork(dfd); + + write_exact(dfd, hdr, sizeof(struct ct_proto), 0); + write_exact(dfd, cbuff, clen, 0); + + set_tcp_uncork(dfd); + + memset(buff, 0, len); + } + + return; +close: + closed_by_server = 1; +} + +extern ssize_t handler_tcp_read(int fd, char *buff, size_t len); + +static void handler_tcp_net_to_tun(int sfd, int dfd, struct curve25519_proto *p, + struct curve25519_struct *c, char *buff, + size_t len) +{ + char *cbuff; + ssize_t rlen, clen; + struct ct_proto *hdr; + + if (!buff || !len) + return; + + while ((rlen = handler_tcp_read(sfd, buff, len)) > 0) { + hdr = (struct ct_proto *) buff; + + if (unlikely(rlen < sizeof(struct ct_proto))) + goto close; + if (unlikely(rlen - sizeof(*hdr) != ntohs(hdr->payload))) + goto close; + if (unlikely(ntohs(hdr->payload) == 0)) + goto close; + if (hdr->flags & PROTO_FLAG_EXIT) + goto close; + + clen = curve25519_decode(c, p, (unsigned char *) buff + + sizeof(struct ct_proto), + rlen - sizeof(struct ct_proto), + (unsigned char **) &cbuff, NULL); + if (unlikely(clen <= 0)) + goto close; + + cbuff += crypto_box_zerobytes; + clen -= crypto_box_zerobytes; + + if (write(dfd, cbuff, clen)) { ; } + } + + return; +close: + closed_by_server = 1; +} + +static void notify_init(int fd, int udp, struct curve25519_proto *p, + struct curve25519_struct *c, char *home) +{ + int fd2, i; + ssize_t err, clen; + size_t us_len, msg_len, pad; + struct ct_proto hdr; + char username[256], path[PATH_MAX], *us, *cbuff, *msg; + unsigned char auth[crypto_auth_hmacsha512256_BYTES], *token; + + memset(&hdr, 0, sizeof(hdr)); + hdr.flags |= PROTO_FLAG_INIT; + + memset(path, 0, sizeof(path)); + slprintf(path, sizeof(path), "%s/%s", home, FILE_USERNAM); + + fd2 = open_or_die(path, O_RDONLY); + + memset(username, 0, sizeof(username)); + err = read(fd2, username, sizeof(username)); + username[sizeof(username) - 1] = 0; + + close(fd2); + + token = get_serv_store_entry_auth_token(); + if (!token) + syslog_panic("Cannot find auth token for server!\n"); + + us_len = sizeof(struct username_struct) + crypto_box_zerobytes; + us = xzmalloc(us_len); + + err = username_msg(username, strlen(username) + 1, + us + crypto_box_zerobytes, + us_len - crypto_box_zerobytes); + if (unlikely(err)) + syslog_panic("Cannot create init message!\n"); + + clen = curve25519_encode(c, p, (unsigned char *) us, us_len, + (unsigned char **) &cbuff); + if (unlikely(clen <= 0)) + syslog_panic("Init encrypt error!\n"); + + err = crypto_auth_hmacsha512256(auth, (unsigned char *) cbuff, clen, token); + if (unlikely(err)) + syslog_panic("Cannot create init hmac message!\n"); + + pad = ((uint32_t) secrand()) % 200; + msg_len = clen + sizeof(auth) + pad; + + msg = xzmalloc(msg_len); + memcpy(msg, auth, sizeof(auth)); + memcpy(msg + sizeof(auth), cbuff, clen); + + for (i = sizeof(auth) + clen; i < msg_len; ++i) + msg[i] = (uint8_t) secrand(); + + hdr.payload = htons((uint16_t) msg_len); + + set_sock_cork(fd, udp); + + write_exact(fd, &hdr, sizeof(struct ct_proto), 0); + write_exact(fd, msg, msg_len, 0); + + set_sock_uncork(fd, udp); + + xfree(msg); + xfree(us); +} + +static void notify_close(int fd) +{ + struct ct_proto hdr; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.flags |= PROTO_FLAG_EXIT; + hdr.payload = 0; + + write_exact(fd, &hdr, sizeof(hdr), 0); +} + +int client_main(char *home, char *dev, char *host, char *port, int udp) +{ + int fd = -1, tunfd = 0, retry_server = 0; + int ret, try = 1, i; + struct addrinfo hints, *ahead, *ai; + struct pollfd fds[2]; + struct curve25519_proto *p; + struct curve25519_struct *c; + char *buff; + size_t blen = TUNBUFF_SIZ; //FIXME + +retry: + if (!retry_server) { + openlog("curvetun", LOG_PID | LOG_CONS | LOG_NDELAY, LOG_DAEMON); + syslog(LOG_INFO, "curvetun client booting!\n"); + } + + c = curve25519_tfm_alloc(); + p = get_serv_store_entry_proto_inf(); + if (!p) + syslog_panic("Cannot proto!\n"); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = udp ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_protocol = udp ? IPPROTO_UDP : IPPROTO_TCP; + hints.ai_flags = AI_NUMERICSERV; + + ret = getaddrinfo(host, port, &hints, &ahead); + if (ret < 0) { + syslog(LOG_ERR, "Cannot get address info! Retry!\n"); + curve25519_tfm_free(c); + fd = -1; + retry_server = 1; + closed_by_server = 0; + sleep(1); + goto retry; + } + + for (ai = ahead; ai != NULL && fd < 0; ai = ai->ai_next) { + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd < 0) + continue; + ret = connect(fd, ai->ai_addr, ai->ai_addrlen); + if (ret < 0) { + syslog(LOG_ERR, "Cannot connect to remote, try %d: %s!\n", + try++, strerror(errno)); + close(fd); + fd = -1; + continue; + } + + set_socket_keepalive(fd); + set_mtu_disc_dont(fd); + if (!udp) + set_tcp_nodelay(fd); + } + + freeaddrinfo(ahead); + + if (fd < 0) { + syslog(LOG_ERR, "Cannot create socket! Retry!\n"); + curve25519_tfm_free(c); + fd = -1; + retry_server = 1; + closed_by_server = 0; + sleep(1); + goto retry; + } + + if (!retry_server) + tunfd = tun_open_or_die(dev ? dev : DEVNAME_CLIENT, + IFF_TUN | IFF_NO_PI); + + set_nonblocking_sloppy(fd); + set_nonblocking_sloppy(tunfd); + + memset(fds, 0, sizeof(fds)); + fds[0].fd = fd; + fds[1].fd = tunfd; + fds[0].events = POLLIN; + fds[1].events = POLLIN; + + buff = xmalloc_aligned(blen, 64); + + notify_init(fd, udp, p, c, home); + + syslog(LOG_INFO, "curvetun client ready!\n"); + + while (likely(!sigint && !closed_by_server)) { + poll(fds, 2, -1); + for (i = 0; i < 2; ++i) { + if ((fds[i].revents & POLLIN) != POLLIN) + continue; + if (fds[i].fd == tunfd) { + if (udp) + handler_udp_tun_to_net(tunfd, fd, p, c, + buff, blen); + else + handler_tcp_tun_to_net(tunfd, fd, p, c, + buff, blen); + } else if (fds[i].fd == fd) { + if (udp) + handler_udp_net_to_tun(fd, tunfd, p, c, + buff, blen); + else + handler_tcp_net_to_tun(fd, tunfd, p, c, + buff, blen); + } + } + } + + syslog(LOG_INFO, "curvetun client prepare shut down!\n"); + + if (!closed_by_server) + notify_close(fd); + + xfree(buff); + close(fd); + curve25519_tfm_free(c); + + /* tundev still active */ + if (closed_by_server && !sigint) { + syslog(LOG_ERR, "curvetun connection retry attempt!\n"); + fd = -1; + retry_server = 1; + closed_by_server = 0; + sleep(1); + goto retry; + } + + close(tunfd); + syslog(LOG_INFO, "curvetun client shut down!\n"); + closelog(); + + return 0; +} diff --git a/curvetun_mgmt_servers.c b/curvetun_mgmt_servers.c new file mode 100644 index 0000000..ee6d732 --- /dev/null +++ b/curvetun_mgmt_servers.c @@ -0,0 +1,267 @@ +/* + * netsniff-ng - the packet sniffing beast + * Copyright 2011 Daniel Borkmann. + * Subject to the GPL, version 2. + */ + +#include +#include +#include +#include +#include +#include + +#include "die.h" +#include "str.h" +#include "built_in.h" +#include "locking.h" +#include "xmalloc.h" +#include "curvetun.h" +#include "curve.h" +#include "curvetun_mgmt_servers.h" +#include "crypto.h" + +struct server_store { + int udp; + char alias[256]; + char host[256]; + char port[6]; /* 5 + \0 */ + unsigned char publickey[crypto_box_pub_key_size]; + struct curve25519_proto proto_inf; + unsigned char auth_token[crypto_auth_hmacsha512256_KEYBYTES]; + struct server_store *next; +}; + +static struct server_store *store = NULL; +static struct server_store *selected = NULL; +static struct rwlock store_lock; + +static struct server_store *server_store_alloc(void) +{ + return xzmalloc(sizeof(struct server_store)); +} + +static void server_store_free(struct server_store *ss) +{ + if (!ss) + return; + memset(ss, 0, sizeof(struct server_store)); + xfree(ss); +} + +enum parse_states { + PARSE_ALIAS, + PARSE_SERVER, + PARSE_PORT, + PARSE_CARRIER, + PARSE_PUBKEY, + PARSE_DONE, +}; + +static int parse_line(char *line, char *homedir) +{ + char *str; + enum parse_states s = PARSE_ALIAS; + struct server_store *elem; + unsigned char pkey[crypto_box_pub_key_size]; + + elem = server_store_alloc(); + elem->next = store; + + str = strtok(line, ";"); + for (; str != NULL;) { + switch (s) { + case PARSE_ALIAS: + strlcpy(elem->alias, str, sizeof(elem->alias)); + s = PARSE_SERVER; + break; + case PARSE_SERVER: + strlcpy(elem->host, str, sizeof(elem->host)); + s = PARSE_PORT; + break; + case PARSE_PORT: + strlcpy(elem->port, str, sizeof(elem->port)); + s = PARSE_CARRIER; + break; + case PARSE_CARRIER: + if (!strncmp("udp", str, strlen("udp"))) + elem->udp = 1; + else if (!strncmp("tcp", str, strlen("tcp"))) + elem->udp = 0; + else { + syslog(LOG_ERR, "Incorrect carrier type !(udp|tcp) in server spec.\n"); + return -EIO; + } + s = PARSE_PUBKEY; + break; + case PARSE_PUBKEY: + if (!curve25519_pubkey_hexparse_32(pkey, sizeof(pkey), + str, strlen(str))) + return -EINVAL; + memcpy(elem->publickey, pkey, sizeof(elem->publickey)); + memcpy(elem->auth_token, pkey, sizeof(elem->auth_token)); + curve25519_proto_init(&elem->proto_inf, elem->publickey, sizeof(elem->publickey)); + s = PARSE_DONE; + break; + case PARSE_DONE: + break; + default: + return -EIO; + } + + str = strtok(NULL, ";"); + } + + store = elem; + return s == PARSE_DONE ? 0 : -EIO; +} + +void parse_userfile_and_generate_serv_store_or_die(char *homedir) +{ + FILE *fp; + char path[PATH_MAX], buff[1024]; + int line = 1, ret; + + memset(path, 0, sizeof(path)); + slprintf(path, sizeof(path), "%s/%s", homedir, FILE_SERVERS); + + rwlock_init(&store_lock); + rwlock_wr_lock(&store_lock); + + fp = fopen(path, "r"); + if (!fp) + panic("Cannot open server file!\n"); + + memset(buff, 0, sizeof(buff)); + while (fgets(buff, sizeof(buff), fp) != NULL) { + buff[sizeof(buff) - 1] = 0; + /* A comment. Skip this line */ + if (buff[0] == '#' || buff[0] == '\n') { + memset(buff, 0, sizeof(buff)); + line++; + continue; + } + + ret = parse_line(buff, homedir); + if (ret < 0) + panic("Cannot parse line %d from clients!\n", line); + line++; + memset(buff, 0, sizeof(buff)); + } + + fclose(fp); + + if (store == NULL) + panic("No registered servers found!\n"); + + rwlock_unlock(&store_lock); +} + +void dump_serv_store(void) +{ + int i; + struct server_store *elem; + + rwlock_rd_lock(&store_lock); + elem = store; + while (elem) { + printf("[%s] -> %s:%s via %s -> ", elem->alias, + elem->host, elem->port, + elem->udp ? "udp" : "tcp"); + for (i = 0; i < sizeof(elem->publickey); ++i) + if (i == (sizeof(elem->publickey) - 1)) + printf("%02x\n", (unsigned char) + elem->publickey[i]); + else + printf("%02x:", (unsigned char) + elem->publickey[i]); + elem = elem->next; + } + rwlock_unlock(&store_lock); +} + +void destroy_serv_store(void) +{ + struct server_store *elem, *nelem = NULL; + + rwlock_wr_lock(&store_lock); + selected = NULL; + elem = store; + while (elem) { + nelem = elem->next; + elem->next = NULL; + server_store_free(elem); + elem = nelem; + } + rwlock_unlock(&store_lock); + rwlock_destroy(&store_lock); +} + +void get_serv_store_entry_by_alias(char *alias, size_t len, + char **host, char **port, int *udp) +{ + struct server_store *elem; + + rwlock_rd_lock(&store_lock); + elem = store; + if (!alias) { + while (elem && elem->next) + elem = elem->next; + if (elem) { + (*host) = elem->host; + (*port) = elem->port; + (*udp) = elem->udp; + selected = elem; + } else { + rwlock_unlock(&store_lock); + goto nothing; + } + } else { + while (elem) { + if (!strncmp(elem->alias, alias, + min(len, strlen(elem->alias) + 1))) + break; + elem = elem->next; + } + if (elem) { + (*host) = elem->host; + (*port) = elem->port; + (*udp) = elem->udp; + selected = elem; + } else { + rwlock_unlock(&store_lock); + goto nothing; + } + } + rwlock_unlock(&store_lock); + + return; +nothing: + (*host) = NULL; + (*port) = NULL; + (*udp) = -1; +} + +struct curve25519_proto *get_serv_store_entry_proto_inf(void) +{ + struct curve25519_proto *ret = NULL; + + rwlock_rd_lock(&store_lock); + if (selected) + ret = &selected->proto_inf; + rwlock_unlock(&store_lock); + + return ret; +} + +unsigned char *get_serv_store_entry_auth_token(void) +{ + unsigned char *ret = NULL; + + rwlock_rd_lock(&store_lock); + if (selected) + ret = selected->auth_token; + rwlock_unlock(&store_lock); + + return ret; +} diff --git a/curvetun_mgmt_servers.h b/curvetun_mgmt_servers.h new file mode 100644 index 0000000..9c5cd84 --- /dev/null +++ b/curvetun_mgmt_servers.h @@ -0,0 +1,15 @@ +#ifndef CT_SERVMGMT_H +#define CT_SERVMGMT_H + +#include +#include "curve.h" + +extern void parse_userfile_and_generate_serv_store_or_die(char *homedir); +extern void dump_serv_store(void); +extern void get_serv_store_entry_by_alias(char *alias, size_t len, + char **host, char **port, int *udp); +extern struct curve25519_proto *get_serv_store_entry_proto_inf(void); +extern unsigned char *get_serv_store_entry_auth_token(void); +extern void destroy_serv_store(void); + +#endif /* CT_SERVMGMT_H */ diff --git a/curvetun_mgmt_users.c b/curvetun_mgmt_users.c new file mode 100644 index 0000000..665ceb3 --- /dev/null +++ b/curvetun_mgmt_users.c @@ -0,0 +1,760 @@ +/* + * netsniff-ng - the packet sniffing beast + * Copyright 2011 Daniel Borkmann. + * Subject to the GPL, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "die.h" +#include "locking.h" +#include "xmalloc.h" +#include "ioexact.h" +#include "ioops.h" +#include "str.h" +#include "curvetun.h" +#include "curve.h" +#include "crypto.h" +#include "curvetun_mgmt_users.h" +#include "hash.h" + +struct user_store { + char username[256]; + unsigned char publickey[crypto_box_pub_key_size]; + struct curve25519_proto proto_inf; + struct user_store *next; +}; + +struct sock_map_entry { + int fd; + struct curve25519_proto *proto; + struct sock_map_entry *next; +}; + +struct sockaddr_map_entry { + struct sockaddr_storage *sa; + size_t sa_len; + struct curve25519_proto *proto; + struct sockaddr_map_entry *next; +}; + +static struct user_store *store = NULL; +static struct rwlock store_lock; + +static struct hash_table sock_mapper; +static struct rwlock sock_map_lock; + +static struct hash_table sockaddr_mapper; +static struct rwlock sockaddr_map_lock; + +static unsigned char token[crypto_auth_hmacsha512256_KEYBYTES]; + +static void init_sock_mapper(void) +{ + rwlock_init(&sock_map_lock); + + rwlock_wr_lock(&sock_map_lock); + + memset(&sock_mapper, 0, sizeof(sock_mapper)); + init_hash(&sock_mapper); + + rwlock_unlock(&sock_map_lock); +} + +static void init_sockaddr_mapper(void) +{ + rwlock_init(&sockaddr_map_lock); + rwlock_wr_lock(&sockaddr_map_lock); + + memset(&sockaddr_mapper, 0, sizeof(sockaddr_mapper)); + init_hash(&sockaddr_mapper); + + rwlock_unlock(&sockaddr_map_lock); +} + +static int cleanup_batch_sock_mapper(void *ptr) +{ + struct sock_map_entry *next; + struct sock_map_entry *e = ptr; + + if (!e) + return 0; + + while ((next = e->next)) { + e->next = NULL; + xfree(e); + e = next; + } + + xfree(e); + + return 0; +} + +static void destroy_sock_mapper(void) +{ + rwlock_wr_lock(&sock_map_lock); + for_each_hash(&sock_mapper, cleanup_batch_sock_mapper); + free_hash(&sock_mapper); + rwlock_unlock(&sock_map_lock); + + rwlock_destroy(&sock_map_lock); +} + +static int cleanup_batch_sockaddr_mapper(void *ptr) +{ + struct sockaddr_map_entry *next; + struct sockaddr_map_entry *e = ptr; + + if (!e) + return 0; + + while ((next = e->next)) { + e->next = NULL; + xfree(e); + e = next; + } + + xfree(e); + return 0; +} + +static void destroy_sockaddr_mapper(void) +{ + rwlock_wr_lock(&sockaddr_map_lock); + for_each_hash(&sockaddr_mapper, cleanup_batch_sockaddr_mapper); + free_hash(&sockaddr_mapper); + rwlock_unlock(&sockaddr_map_lock); + + rwlock_destroy(&sockaddr_map_lock); +} + +static struct user_store *user_store_alloc(void) +{ + return xzmalloc(sizeof(struct user_store)); +} + +static void user_store_free(struct user_store *us) +{ + if (!us) + return; + memset(us, 0, sizeof(struct user_store)); + xfree(us); +} + +/* already in lock */ +static int __check_duplicate_username(char *username, size_t len) +{ + int duplicate = 0; + struct user_store *elem = store; + + while (elem) { + if (!memcmp(elem->username, username, + strlen(elem->username) + 1)) { + duplicate = 1; + break; + } + elem = elem->next; + } + + return duplicate; +} + +/* already in lock */ +static int __check_duplicate_pubkey(unsigned char *pubkey, size_t len) +{ + int duplicate = 0; + struct user_store *elem = store; + + while (elem) { + if (!memcmp(elem->publickey, pubkey, + sizeof(elem->publickey))) { + duplicate = 1; + break; + } + elem = elem->next; + } + + return duplicate; +} + +enum parse_states { + PARSE_USERNAME, + PARSE_PUBKEY, + PARSE_DONE, +}; + +static int parse_line(char *line, char *homedir) +{ + char *str; + enum parse_states s = PARSE_USERNAME; + struct user_store *elem; + unsigned char pkey[crypto_box_pub_key_size]; + + elem = user_store_alloc(); + elem->next = store; + + str = strtok(line, ";"); + for (; str != NULL;) { + switch (s) { + case PARSE_USERNAME: + if (__check_duplicate_username(str, strlen(str) + 1)) + return -EINVAL; + strlcpy(elem->username, str, sizeof(elem->username)); + s = PARSE_PUBKEY; + break; + case PARSE_PUBKEY: + if (!curve25519_pubkey_hexparse_32(pkey, sizeof(pkey), + str, strlen(str))) + return -EINVAL; + if (__check_duplicate_pubkey(pkey, sizeof(pkey))) + return -EINVAL; + memcpy(elem->publickey, pkey, sizeof(elem->publickey)); + curve25519_proto_init(&elem->proto_inf, elem->publickey, sizeof(elem->publickey)); + s = PARSE_DONE; + break; + case PARSE_DONE: + break; + default: + return -EIO; + } + + str = strtok(NULL, ";"); + } + + store = elem; + return s == PARSE_DONE ? 0 : -EIO; +} + +void parse_userfile_and_generate_user_store_or_die(char *homedir) +{ + FILE *fp; + char path[PATH_MAX], buff[512]; + int line = 1, ret, fd; + + memset(path, 0, sizeof(path)); + slprintf(path, sizeof(path), "%s/%s", homedir, FILE_CLIENTS); + + rwlock_init(&store_lock); + rwlock_wr_lock(&store_lock); + + fp = fopen(path, "r"); + if (!fp) + panic("Cannot open client file!\n"); + + memset(buff, 0, sizeof(buff)); + while (fgets(buff, sizeof(buff), fp) != NULL) { + buff[sizeof(buff) - 1] = 0; + /* A comment. Skip this line */ + if (buff[0] == '#' || buff[0] == '\n') { + memset(buff, 0, sizeof(buff)); + line++; + continue; + } + + ret = parse_line(buff, homedir); + if (ret < 0) + panic("Cannot parse line %d from clients!\n", line); + line++; + memset(buff, 0, sizeof(buff)); + } + + fclose(fp); + + if (store == NULL) + panic("No registered clients found!\n"); + + rwlock_unlock(&store_lock); + + init_sock_mapper(); + init_sockaddr_mapper(); + + /* + * Pubkey is also used as a hmac of the initial packet to check + * the integrity of the packet, so that we know if it's just random + * garbage or a 'valid' packet. Again, just for the integrity! + */ + + memset(path, 0, sizeof(path)); + slprintf(path, sizeof(path), "%s/%s", homedir, FILE_PUBKEY); + + fd = open_or_die(path, O_RDONLY); + ret = read(fd, token, sizeof(token)); + if (ret != crypto_auth_hmacsha512256_KEYBYTES) + panic("Cannot read public key!\n"); + close(fd); +} + +void dump_user_store(void) +{ + int i; + struct user_store *elem; + + rwlock_rd_lock(&store_lock); + + elem = store; + while (elem) { + printf("%s -> ", elem->username); + for (i = 0; i < sizeof(elem->publickey); ++i) + if (i == (sizeof(elem->publickey) - 1)) + printf("%02x\n", (unsigned char) + elem->publickey[i]); + else + printf("%02x:", (unsigned char) + elem->publickey[i]); + elem = elem->next; + } + + rwlock_unlock(&store_lock); +} + +void destroy_user_store(void) +{ + struct user_store *elem, *nelem = NULL; + + rwlock_wr_lock(&store_lock); + + elem = store; + while (elem) { + nelem = elem->next; + elem->next = NULL; + user_store_free(elem); + elem = nelem; + } + rwlock_unlock(&store_lock); + + rwlock_destroy(&store_lock); + + destroy_sock_mapper(); + destroy_sockaddr_mapper(); +} + +int username_msg(char *username, size_t len, char *dst, size_t dlen) +{ + int fd; + ssize_t ret; + uint32_t salt; + unsigned char h[crypto_hash_sha512_BYTES]; + struct username_struct *us = (struct username_struct *) dst; + char *uname; + size_t uname_len; + + if (dlen < sizeof(struct username_struct)) + return -ENOMEM; + + uname_len = 512; + uname = xzmalloc(uname_len); + + fd = open_or_die("/dev/random", O_RDONLY); + ret = read_exact(fd, &salt, sizeof(salt), 0); + if (ret != sizeof(salt)) + panic("Cannot read from /dev/random!\n"); + close(fd); + + slprintf(uname, uname_len, "%s%u", username, salt); + crypto_hash_sha512(h, (unsigned char *) uname, strlen(uname)); + + us->salt = htonl(salt); + memcpy(us->hash, h, sizeof(us->hash)); + + xfree(uname); + return 0; +} + +enum is_user_enum username_msg_is_user(char *src, size_t slen, char *username, + size_t len) +{ + char *uname; + size_t uname_len; + uint32_t salt; + struct username_struct *us = (struct username_struct *) src; + unsigned char h[crypto_hash_sha512_BYTES]; + + if (slen < sizeof(struct username_struct)) { + errno = ENOMEM; + return USERNAMES_ERR; + } + + uname_len = 512; + uname = xzmalloc(uname_len); + + salt = ntohl(us->salt); + + slprintf(uname, uname_len, "%s%u", username, salt); + crypto_hash_sha512(h, (unsigned char *) uname, strlen(uname)); + xfree(uname); + + if (!crypto_verify_32(&h[0], &us->hash[0]) && + !crypto_verify_32(&h[32], &us->hash[32])) + return USERNAMES_OK; + else + return USERNAMES_NE; +} + +static int register_user_by_socket(int fd, struct curve25519_proto *proto) +{ + void **pos; + struct sock_map_entry *entry; + + rwlock_wr_lock(&sock_map_lock); + + entry = xzmalloc(sizeof(*entry)); + entry->fd = fd; + entry->proto = proto; + + pos = insert_hash(entry->fd, entry, &sock_mapper); + if (pos) { + entry->next = (*pos); + (*pos) = entry; + } + + rwlock_unlock(&sock_map_lock); + + return 0; +} + +static int register_user_by_sockaddr(struct sockaddr_storage *sa, + size_t sa_len, + struct curve25519_proto *proto) +{ + void **pos; + struct sockaddr_map_entry *entry; + unsigned int hash = hash_name((char *) sa, sa_len); + + rwlock_wr_lock(&sockaddr_map_lock); + + entry = xzmalloc(sizeof(*entry)); + entry->sa = xmemdupz(sa, sa_len); + entry->sa_len = sa_len; + entry->proto = proto; + + pos = insert_hash(hash, entry, &sockaddr_mapper); + if (pos) { + entry->next = (*pos); + (*pos) = entry; + } + + rwlock_unlock(&sockaddr_map_lock); + + return 0; +} + +int try_register_user_by_socket(struct curve25519_struct *c, + char *src, size_t slen, int sock, int log) +{ + int ret = -1; + char *cbuff = NULL; + size_t real_len = 132; + ssize_t clen; + struct user_store *elem; + enum is_user_enum err; + unsigned char auth[crypto_auth_hmacsha512256_BYTES]; + struct taia arrival_taia; + + /* assert(132 == clen + sizeof(auth)); */ + /* + * Check hmac first, if malicious, drop immediately before we + * investigate more efforts. + */ + if (slen < real_len) + return -1; + + taia_now(&arrival_taia); + + memcpy(auth, src, sizeof(auth)); + + src += sizeof(auth); + real_len -= sizeof(auth); + + if (crypto_auth_hmacsha512256_verify(auth, (unsigned char *) src, + real_len, token)) { + syslog(LOG_ERR, "Bad packet hmac for id %d! Dropping!\n", sock); + return -1; + } else { + if (log) + syslog(LOG_INFO, "Good packet hmac for id %d!\n", sock); + } + + rwlock_rd_lock(&store_lock); + + elem = store; + while (elem) { + clen = curve25519_decode(c, &elem->proto_inf, + (unsigned char *) src, real_len, + (unsigned char **) &cbuff, + &arrival_taia); + if (clen <= 0) { + elem = elem->next; + continue; + } + + cbuff += crypto_box_zerobytes; + clen -= crypto_box_zerobytes; + + if (log) + syslog(LOG_INFO, "Packet decoded successfully for id %d!\n", sock); + + err = username_msg_is_user(cbuff, clen, elem->username, + strlen(elem->username) + 1); + if (err == USERNAMES_OK) { + if (log) + syslog(LOG_INFO, "Found user %s for id %d! Registering ...\n", + elem->username, sock); + ret = register_user_by_socket(sock, &elem->proto_inf); + break; + } + + elem = elem->next; + } + + rwlock_unlock(&store_lock); + + if (ret == -1) + syslog(LOG_ERR, "User not found! Dropping connection!\n"); + + return ret; +} + +int try_register_user_by_sockaddr(struct curve25519_struct *c, + char *src, size_t slen, + struct sockaddr_storage *sa, + size_t sa_len, int log) +{ + int ret = -1; + char *cbuff = NULL; + struct user_store *elem; + ssize_t clen; + size_t real_len = 132; + enum is_user_enum err; + unsigned char auth[crypto_auth_hmacsha512256_BYTES]; + struct taia arrival_taia; + + /* assert(132 == clen + sizeof(auth)); */ + /* + * Check hmac first, if malicious, drop immediately before we + * investigate more efforts. + */ + if (slen < real_len) + return -1; + + taia_now(&arrival_taia); + + memcpy(auth, src, sizeof(auth)); + + src += sizeof(auth); + real_len -= sizeof(auth); + + if (crypto_auth_hmacsha512256_verify(auth, (unsigned char *) src, + real_len, token)) { + syslog(LOG_ERR, "Got bad packet hmac! Dropping!\n"); + return -1; + } else { + if (log) + syslog(LOG_INFO, "Got good packet hmac!\n"); + } + + rwlock_rd_lock(&store_lock); + + elem = store; + while (elem) { + clen = curve25519_decode(c, &elem->proto_inf, + (unsigned char *) src, real_len, + (unsigned char **) &cbuff, + &arrival_taia); + if (clen <= 0) { + elem = elem->next; + continue; + } + + cbuff += crypto_box_zerobytes; + clen -= crypto_box_zerobytes; + + if (log) + syslog(LOG_INFO, "Packet decoded successfully!\n"); + + err = username_msg_is_user(cbuff, clen, elem->username, + strlen(elem->username) + 1); + if (err == USERNAMES_OK) { + if (log) + syslog(LOG_INFO, "Found user %s! Registering ...\n", + elem->username); + ret = register_user_by_sockaddr(sa, sa_len, + &elem->proto_inf); + break; + } + + elem = elem->next; + } + + rwlock_unlock(&store_lock); + + if (ret == -1) + syslog(LOG_ERR, "User not found! Dropping connection!\n"); + + return ret; +} + +int get_user_by_socket(int fd, struct curve25519_proto **proto) +{ + int ret = -1; + struct sock_map_entry *entry; + + errno = 0; + + rwlock_rd_lock(&sock_map_lock); + + entry = lookup_hash(fd, &sock_mapper); + while (entry && fd != entry->fd) + entry = entry->next; + if (entry && fd == entry->fd) { + (*proto) = entry->proto; + ret = 0; + } else { + (*proto) = NULL; + errno = ENOENT; + } + + rwlock_unlock(&sock_map_lock); + + return ret; +} + +int get_user_by_sockaddr(struct sockaddr_storage *sa, size_t sa_len, + struct curve25519_proto **proto) +{ + int ret = -1; + struct sockaddr_map_entry *entry; + unsigned int hash = hash_name((char *) sa, sa_len); + + errno = 0; + + rwlock_rd_lock(&sockaddr_map_lock); + + entry = lookup_hash(hash, &sockaddr_mapper); + while (entry && entry->sa_len == sa_len && + memcmp(sa, entry->sa, entry->sa_len)) + entry = entry->next; + if (entry && entry->sa_len == sa_len && + !memcmp(sa, entry->sa, entry->sa_len)) { + (*proto) = entry->proto; + ret = 0; + } else { + (*proto) = NULL; + errno = ENOENT; + } + + rwlock_unlock(&sockaddr_map_lock); + + return ret; +} + +static struct sock_map_entry *socket_to_sock_map_entry(int fd) +{ + struct sock_map_entry *entry, *ret = NULL; + + errno = 0; + + rwlock_rd_lock(&sock_map_lock); + + entry = lookup_hash(fd, &sock_mapper); + while (entry && fd != entry->fd) + entry = entry->next; + if (entry && fd == entry->fd) + ret = entry; + else + errno = ENOENT; + + rwlock_unlock(&sock_map_lock); + + return ret; +} + +void remove_user_by_socket(int fd) +{ + struct sock_map_entry *pos; + struct sock_map_entry *entry = socket_to_sock_map_entry(fd); + + if (!entry) + return; + + rwlock_wr_lock(&sock_map_lock); + + pos = remove_hash(entry->fd, entry, entry->next, &sock_mapper); + while (pos && pos->next && pos->next != entry) + pos = pos->next; + if (pos && pos->next && pos->next == entry) + pos->next = entry->next; + + memset(entry->proto->enonce, 0, sizeof(entry->proto->enonce)); + memset(entry->proto->dnonce, 0, sizeof(entry->proto->dnonce)); + + entry->proto = NULL; + entry->next = NULL; + + xfree(entry); + + rwlock_unlock(&sock_map_lock); +} + +static struct sockaddr_map_entry * +sockaddr_to_sockaddr_map_entry(struct sockaddr_storage *sa, size_t sa_len) +{ + struct sockaddr_map_entry *entry, *ret = NULL; + unsigned int hash = hash_name((char *) sa, sa_len); + + errno = 0; + + rwlock_rd_lock(&sockaddr_map_lock); + + entry = lookup_hash(hash, &sockaddr_mapper); + while (entry && entry->sa_len == sa_len && + memcmp(sa, entry->sa, entry->sa_len)) + entry = entry->next; + if (entry && entry->sa_len == sa_len && + !memcmp(sa, entry->sa, entry->sa_len)) + ret = entry; + else + errno = ENOENT; + + rwlock_unlock(&sockaddr_map_lock); + + return ret; +} + +void remove_user_by_sockaddr(struct sockaddr_storage *sa, size_t sa_len) +{ + struct sockaddr_map_entry *pos; + struct sockaddr_map_entry *entry; + unsigned int hash = hash_name((char *) sa, sa_len); + + entry = sockaddr_to_sockaddr_map_entry(sa, sa_len); + if (!entry) + return; + + rwlock_wr_lock(&sockaddr_map_lock); + + pos = remove_hash(hash, entry, entry->next, &sockaddr_mapper); + while (pos && pos->next && pos->next != entry) + pos = pos->next; + if (pos && pos->next && pos->next == entry) + pos->next = entry->next; + + memset(entry->proto->enonce, 0, sizeof(entry->proto->enonce)); + memset(entry->proto->dnonce, 0, sizeof(entry->proto->dnonce)); + + entry->proto = NULL; + entry->next = NULL; + + xfree(entry->sa); + xfree(entry); + + rwlock_unlock(&sockaddr_map_lock); +} diff --git a/curvetun_mgmt_users.h b/curvetun_mgmt_users.h new file mode 100644 index 0000000..ff9d601 --- /dev/null +++ b/curvetun_mgmt_users.h @@ -0,0 +1,43 @@ +#ifndef CT_USERMGMT_H +#define CT_USERMGMT_H + +#include + +#include "curve.h" +#include "crypto_hash_sha512.h" + +enum is_user_enum { + USERNAMES_OK = 0, /* Usernames match, valid 'token' */ + USERNAMES_NE, /* Usernames do not match */ + USERNAMES_TS, /* Usernames match, but 'token' invalid, + Drop connection here */ + USERNAMES_ERR, +}; + +struct username_struct { + uint32_t salt; + uint8_t hash[crypto_hash_sha512_BYTES]; +}; + +extern int username_msg(char *username, size_t len, char *dst, size_t dlen); +extern enum is_user_enum username_msg_is_user(char *src, size_t slen, + char *username, size_t len); +extern void parse_userfile_and_generate_user_store_or_die(char *homedir); +extern void dump_user_store(void); +extern void destroy_user_store(void); + +extern int get_user_by_socket(int sock, struct curve25519_proto **proto); +extern int get_user_by_sockaddr(struct sockaddr_storage *sa, size_t sa_len, + struct curve25519_proto **proto); +extern int try_register_user_by_socket(struct curve25519_struct *c, + char *src, size_t slen, int sock, + int log); +extern int try_register_user_by_sockaddr(struct curve25519_struct *c, + char *src, size_t slen, + struct sockaddr_storage *sa, + size_t sa_len, int log); +extern void remove_user_by_socket(int sock); +extern void remove_user_by_sockaddr(struct sockaddr_storage *sa, + size_t sa_len); + +#endif /* CT_USERMGMT_H */ diff --git a/curvetun_server.c b/curvetun_server.c new file mode 100644 index 0000000..bb704b0 --- /dev/null +++ b/curvetun_server.c @@ -0,0 +1,812 @@ +/* + * curvetun - the cipherspace wormhole creator + * Part of the netsniff-ng project + * Copyright 2011 Daniel Borkmann , + * Subject to the GPL, version 2. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "die.h" +#include "epoll2.h" +#include "ioops.h" +#include "xmalloc.h" +#include "curvetun.h" +#include "curve.h" +#include "ioexact.h" +#include "corking.h" +#include "cpus.h" +#include "sock.h" +#include "built_in.h" +#include "curvetun_mgmt_users.h" +#include "cpusched.h" +#include "trie.h" + +struct parent_info { + int efd; + int refd; + int tunfd; + int ipv4; + int udp; +}; + +struct worker_struct { + pthread_t trid; + int efd[2]; + unsigned int cpu; + struct parent_info parent; + int (*handler)(int fd, const struct worker_struct *ws, + char *buff, size_t len); + struct curve25519_struct *c; +}; + +static struct worker_struct *threadpool = NULL; + +static int auth_log = 1; + +extern volatile sig_atomic_t sigint; + +static int handler_udp_tun_to_net(int fd, const struct worker_struct *ws, + char *buff, size_t len) __pure; +static int handler_udp_net_to_tun(int fd, const struct worker_struct *ws, + char *buff, size_t len) __pure; +static int handler_udp(int fd, const struct worker_struct *ws, + char *buff, size_t len) __pure; +static int handler_tcp_tun_to_net(int fd, const struct worker_struct *ws, + char *buff, size_t len) __pure; +static int handler_tcp_net_to_tun(int fd, const struct worker_struct *ws, + char *buff, size_t len) __pure; +static int handler_tcp(int fd, const struct worker_struct *ws, + char *buff, size_t len) __pure; +ssize_t handler_tcp_read(int fd, char *buff, size_t len); +static void *worker(void *self) __pure; + +static int handler_udp_tun_to_net(int fd, const struct worker_struct *ws, + char *buff, size_t len) +{ + int dfd, keep = 1; + char *cbuff; + ssize_t rlen, err, clen; + struct ct_proto *hdr; + struct curve25519_proto *p; + struct sockaddr_storage naddr; + socklen_t nlen; + size_t off = sizeof(struct ct_proto) + crypto_box_zerobytes; + + if (!buff || len <= off) + return 0; + + memset(buff, 0, len); + while ((rlen = read(fd, buff + off, len - off)) > 0) { + dfd = -1; nlen = 0; p = NULL; + + memset(&naddr, 0, sizeof(naddr)); + + hdr = (struct ct_proto *) buff; + memset(hdr, 0, sizeof(*hdr)); + hdr->flags = 0; + + trie_addr_lookup(buff + off, rlen, ws->parent.ipv4, &dfd, &naddr, + (size_t *) &nlen); + if (unlikely(dfd < 0 || nlen == 0)) { + memset(buff, 0, len); + continue; + } + + err = get_user_by_sockaddr(&naddr, nlen, &p); + if (unlikely(err || !p)) { + memset(buff, 0, len); + continue; + } + + clen = curve25519_encode(ws->c, p, (unsigned char *) (buff + off - + crypto_box_zerobytes), (rlen + + crypto_box_zerobytes), (unsigned char **) + &cbuff); + if (unlikely(clen <= 0)) { + memset(buff, 0, len); + continue; + } + + hdr->payload = htons((uint16_t) clen); + + set_udp_cork(dfd); + + sendto(dfd, hdr, sizeof(struct ct_proto), 0, (struct sockaddr *) + &naddr, nlen); + sendto(dfd, cbuff, clen, 0, (struct sockaddr *) &naddr, nlen); + + set_udp_uncork(dfd); + + memset(buff, 0, len); + } + + return keep; +} + +static void handler_udp_notify_close(int fd, struct sockaddr_storage *addr) +{ + struct ct_proto hdr; + + memset(&hdr, 0, sizeof(hdr)); + hdr.flags |= PROTO_FLAG_EXIT; + hdr.payload = 0; + + sendto(fd, &hdr, sizeof(hdr), 0, (struct sockaddr *) addr, + sizeof(*addr)); +} + +static int handler_udp_net_to_tun(int fd, const struct worker_struct *ws, + char *buff, size_t len) +{ + int keep = 1; + char *cbuff; + ssize_t rlen, err, clen; + struct ct_proto *hdr; + struct curve25519_proto *p; + struct sockaddr_storage naddr; + socklen_t nlen = sizeof(naddr); + + if (!buff || !len) + return 0; + + memset(&naddr, 0, sizeof(naddr)); + while ((rlen = recvfrom(fd, buff, len, 0, (struct sockaddr *) &naddr, + &nlen)) > 0) { + p = NULL; + + hdr = (struct ct_proto *) buff; + + if (unlikely(rlen < sizeof(struct ct_proto))) + goto close; + if (unlikely(rlen - sizeof(*hdr) != ntohs(hdr->payload))) + goto close; + if (unlikely(ntohs(hdr->payload) == 0)) + goto close; + if (hdr->flags & PROTO_FLAG_EXIT) { +close: + remove_user_by_sockaddr(&naddr, nlen); + trie_addr_remove_addr(&naddr, nlen); + handler_udp_notify_close(fd, &naddr); + + return keep; + } + if (hdr->flags & PROTO_FLAG_INIT) { + syslog_maybe(auth_log, LOG_INFO, "Got initial userhash " + "from remote end!\n"); + + if (unlikely(rlen - sizeof(*hdr) < + sizeof(struct username_struct))) + goto close; + + err = try_register_user_by_sockaddr(ws->c, + buff + sizeof(struct ct_proto), + rlen - sizeof(struct ct_proto), + &naddr, nlen, auth_log); + if (unlikely(err)) + goto close; + + goto next; + } + + err = get_user_by_sockaddr(&naddr, nlen, &p); + if (unlikely(err || !p)) + goto close; + + clen = curve25519_decode(ws->c, p, (unsigned char *) buff + + sizeof(struct ct_proto), + rlen - sizeof(struct ct_proto), + (unsigned char **) &cbuff, NULL); + if (unlikely(clen <= 0)) + goto close; + + cbuff += crypto_box_zerobytes; + clen -= crypto_box_zerobytes; + + err = trie_addr_maybe_update(cbuff, clen, ws->parent.ipv4, + fd, &naddr, nlen); + if (unlikely(err)) + goto next; + + err = write(ws->parent.tunfd, cbuff, clen); +next: + nlen = sizeof(naddr); + memset(&naddr, 0, sizeof(naddr)); + } + + return keep; +} + +static int handler_udp(int fd, const struct worker_struct *ws, + char *buff, size_t len) +{ + int ret = 0; + + if (fd == ws->parent.tunfd) + ret = handler_udp_tun_to_net(fd, ws, buff, len); + else + ret = handler_udp_net_to_tun(fd, ws, buff, len); + + return ret; +} + +static int handler_tcp_tun_to_net(int fd, const struct worker_struct *ws, + char *buff, size_t len) +{ + int dfd, keep = 1; + char *cbuff; + ssize_t rlen, err, clen; + struct ct_proto *hdr; + struct curve25519_proto *p; + socklen_t nlen; + size_t off = sizeof(struct ct_proto) + crypto_box_zerobytes; + + if (!buff || len <= off) + return 0; + + memset(buff, 0, len); + while ((rlen = read(fd, buff + off, len - off)) > 0) { + dfd = -1; p = NULL; + + hdr = (struct ct_proto *) buff; + memset(hdr, 0, sizeof(*hdr)); + hdr->flags = 0; + + trie_addr_lookup(buff + off, rlen, ws->parent.ipv4, &dfd, NULL, + (size_t *) &nlen); + if (unlikely(dfd < 0)) { + memset(buff, 0, len); + continue; + } + + err = get_user_by_socket(dfd, &p); + if (unlikely(err || !p)) { + memset(buff, 0, len); + continue; + } + + clen = curve25519_encode(ws->c, p, (unsigned char *) (buff + off - + crypto_box_zerobytes), (rlen + + crypto_box_zerobytes), (unsigned char **) + &cbuff); + if (unlikely(clen <= 0)) { + memset(buff, 0, len); + continue; + } + + hdr->payload = htons((uint16_t) clen); + + set_tcp_cork(dfd); + + write_exact(dfd, hdr, sizeof(struct ct_proto), 0); + write_exact(dfd, cbuff, clen, 0); + + set_tcp_uncork(dfd); + + memset(buff, 0, len); + } + + return keep; +} + +ssize_t handler_tcp_read(int fd, char *buff, size_t len) +{ + ssize_t rlen; + struct ct_proto *hdr = (struct ct_proto *) buff; + + if (!buff || !len) + return 0; + + /* May exit on EAGAIN if 0 Byte read */ + rlen = read_exact(fd, buff, sizeof(struct ct_proto), 1); + if (rlen < 0) + return rlen; + if (unlikely(ntohs(hdr->payload) > len - sizeof(struct ct_proto))) { + errno = ENOMEM; + return 1; /* Force server to close connection */ + } + + /* May not exit on EAGAIN if 0 Byte read */ + rlen = read_exact(fd, buff + sizeof(struct ct_proto), + ntohs(hdr->payload), 0); + if (rlen < 0) + return rlen; + + return sizeof(struct ct_proto) + rlen; +} + +static void handler_tcp_notify_close(int fd) +{ + struct ct_proto hdr; + + memset(&hdr, 0, sizeof(hdr)); + hdr.flags |= PROTO_FLAG_EXIT; + hdr.payload = 0; + + if (write(fd, &hdr, sizeof(hdr))) { ; } +} + +static int handler_tcp_net_to_tun(int fd, const struct worker_struct *ws, + char *buff, size_t len) +{ + int keep = 1, count = 0; + char *cbuff; + ssize_t rlen, err, clen; + struct ct_proto *hdr; + struct curve25519_proto *p; + + if (!buff || !len) + return 0; + + while ((rlen = handler_tcp_read(fd, buff, len)) > 0) { + p = NULL; + + hdr = (struct ct_proto *) buff; + + if (unlikely(rlen < sizeof(struct ct_proto))) + goto close; + if (unlikely(rlen - sizeof(*hdr) != ntohs(hdr->payload))) + goto close; + if (unlikely(ntohs(hdr->payload) == 0)) + goto close; + if (hdr->flags & PROTO_FLAG_EXIT) { +close: + remove_user_by_socket(fd); + trie_addr_remove(fd); + handler_tcp_notify_close(fd); + rlen = write(ws->parent.efd, &fd, sizeof(fd)); + + keep = 0; + return keep; + } + if (hdr->flags & PROTO_FLAG_INIT) { + syslog_maybe(auth_log, LOG_INFO, "Got initial userhash " + "from remote end!\n"); + + if (unlikely(rlen - sizeof(*hdr) < + sizeof(struct username_struct))) + goto close; + + err = try_register_user_by_socket(ws->c, + buff + sizeof(struct ct_proto), + rlen - sizeof(struct ct_proto), + fd, auth_log); + if (unlikely(err)) + goto close; + + continue; + } + + err = get_user_by_socket(fd, &p); + if (unlikely(err || !p)) + continue; + + clen = curve25519_decode(ws->c, p, (unsigned char *) buff + + sizeof(struct ct_proto), + rlen - sizeof(struct ct_proto), + (unsigned char **) &cbuff, NULL); + if (unlikely(clen <= 0)) + continue; + + cbuff += crypto_box_zerobytes; + clen -= crypto_box_zerobytes; + + err = trie_addr_maybe_update(cbuff, clen, ws->parent.ipv4, + fd, NULL, 0); + if (unlikely(err)) + continue; + + err = write(ws->parent.tunfd, cbuff, clen); + + count++; + if (count == 10) { + write_exact(ws->efd[1], &fd, sizeof(fd), 1); + /* Read later next data and let others process */ + return keep; + } + } + + return keep; +} + +static int handler_tcp(int fd, const struct worker_struct *ws, + char *buff, size_t len) +{ + int ret = 0; + + if (fd == ws->parent.tunfd) + ret = handler_tcp_tun_to_net(fd, ws, buff, len); + else + ret = handler_tcp_net_to_tun(fd, ws, buff, len); + + return ret; +} + +static void *worker(void *self) +{ + int fd, old_state; + ssize_t ret; + size_t blen = TUNBUFF_SIZ; //FIXME + struct worker_struct *ws = self; + struct pollfd fds; + char *buff; + + fds.fd = ws->efd[0]; + fds.events = POLLIN; + + ws->c = curve25519_tfm_alloc(); + buff = xmalloc_aligned(blen, 64); + + syslog(LOG_INFO, "curvetun thread on CPU%u up!\n", ws->cpu); + + pthread_cleanup_push(curve25519_tfm_free_void, ws->c); + pthread_cleanup_push(xfree_func, buff); + + while (likely(!sigint)) { + poll(&fds, 1, -1); + if ((fds.revents & POLLIN) != POLLIN) + continue; + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); + + while ((ret = read_exact(ws->efd[0], &fd, sizeof(fd), 1)) > 0) { + if (ret != sizeof(fd)) { + sched_yield(); + continue; + } + + ret = ws->handler(fd, ws, buff, blen); + if (ret) + write_exact(ws->parent.refd, &fd, sizeof(fd), 1); + } + + pthread_setcancelstate(old_state, NULL); + } + + syslog(LOG_INFO, "curvetun thread on CPU%u down!\n", ws->cpu); + + pthread_cleanup_pop(1); + pthread_cleanup_pop(1); + + pthread_exit((void *) ((long) ws->cpu)); +} + +static void thread_spawn_or_panic(unsigned int cpus, int efd, int refd, + int tunfd, int ipv4, int udp) +{ + int i, ret; + cpu_set_t cpuset; + unsigned int threads; + + threads = cpus * THREADS_PER_CPU; + + for (i = 0; i < threads; ++i) { + CPU_ZERO(&cpuset); + threadpool[i].cpu = i % cpus; + CPU_SET(threadpool[i].cpu, &cpuset); + + ret = pipe2(threadpool[i].efd, O_NONBLOCK); + if (ret < 0) + syslog_panic("Cannot create event socket!\n"); + + threadpool[i].c = xmalloc_aligned(sizeof(*threadpool[i].c), 64); + threadpool[i].parent.efd = efd; + threadpool[i].parent.refd = refd; + threadpool[i].parent.tunfd = tunfd; + threadpool[i].parent.ipv4 = ipv4; + threadpool[i].parent.udp = udp; + threadpool[i].handler = udp ? handler_udp : handler_tcp; + + ret = pthread_create(&threadpool[i].trid, NULL, + worker, &threadpool[i]); + if (ret < 0) + syslog_panic("Thread creation failed!\n"); + + ret = pthread_setaffinity_np(threadpool[i].trid, + sizeof(cpuset), &cpuset); + if (ret < 0) + syslog_panic("Thread CPU migration failed!\n"); + + pthread_detach(threadpool[i].trid); + } + + sleep(1); +} + +static void thread_finish(unsigned int cpus) +{ + int i; + unsigned int threads; + + threads = cpus * THREADS_PER_CPU; + + for (i = 0; i < threads; ++i) { + while (pthread_join(threadpool[i].trid, NULL) < 0) + ; + + close(threadpool[i].efd[0]); + close(threadpool[i].efd[1]); + } +} + +int server_main(char *home, char *dev, char *port, int udp, int ipv4, int log) +{ + int lfd = -1, kdpfd, nfds, nfd, curfds, efd[2], refd[2], tunfd, i; + unsigned int cpus = 0, threads, udp_cpu = 0; + ssize_t ret; + struct epoll_event *events; + struct addrinfo hints, *ahead, *ai; + + auth_log = !!log; + openlog("curvetun", LOG_PID | LOG_CONS | LOG_NDELAY, LOG_DAEMON); + + syslog(LOG_INFO, "curvetun server booting!\n"); + syslog_maybe(!auth_log, LOG_INFO, "curvetun user logging disabled!\n"); + + parse_userfile_and_generate_user_store_or_die(home); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = udp ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_protocol = udp ? IPPROTO_UDP : IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + ret = getaddrinfo(NULL, port, &hints, &ahead); + if (ret < 0) + syslog_panic("Cannot get address info!\n"); + + for (ai = ahead; ai != NULL && lfd < 0; ai = ai->ai_next) { + lfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (lfd < 0) + continue; + if (ai->ai_family == AF_INET6) { +#ifdef IPV6_V6ONLY + ret = set_ipv6_only(lfd); + if (ret < 0) { + close(lfd); + lfd = -1; + continue; + } +#else + close(lfd); + lfd = -1; + continue; +#endif /* IPV6_V6ONLY */ + } + + set_reuseaddr(lfd); + set_mtu_disc_dont(lfd); + + ret = bind(lfd, ai->ai_addr, ai->ai_addrlen); + if (ret < 0) { + close(lfd); + lfd = -1; + continue; + } + + if (!udp) { + ret = listen(lfd, 5); + if (ret < 0) { + close(lfd); + lfd = -1; + continue; + } + } + + if (ipv4 == -1) { + ipv4 = (ai->ai_family == AF_INET6 ? 0 : + (ai->ai_family == AF_INET ? 1 : -1)); + } + + syslog_maybe(auth_log, LOG_INFO, "curvetun on IPv%d via %s " + "on port %s!\n", ai->ai_family == AF_INET ? 4 : 6, + udp ? "UDP" : "TCP", port); + syslog_maybe(auth_log, LOG_INFO, "Allowed overlay proto is " + "IPv%d!\n", ipv4 ? 4 : 6); + } + + freeaddrinfo(ahead); + + if (lfd < 0 || ipv4 < 0) + syslog_panic("Cannot create socket!\n"); + + tunfd = tun_open_or_die(dev ? dev : DEVNAME_SERVER, IFF_TUN | IFF_NO_PI); + + pipe_or_die(efd, O_NONBLOCK); + pipe_or_die(refd, O_NONBLOCK); + + set_nonblocking(lfd); + + events = xzmalloc(MAX_EPOLL_SIZE * sizeof(*events)); + for (i = 0; i < MAX_EPOLL_SIZE; ++i) + events[i].data.fd = -1; + + kdpfd = epoll_create(MAX_EPOLL_SIZE); + if (kdpfd < 0) + syslog_panic("Cannot create socket!\n"); + + set_epoll_descriptor(kdpfd, EPOLL_CTL_ADD, lfd, + udp ? EPOLLIN | EPOLLET | EPOLLONESHOT : EPOLLIN); + set_epoll_descriptor(kdpfd, EPOLL_CTL_ADD, efd[0], EPOLLIN); + set_epoll_descriptor(kdpfd, EPOLL_CTL_ADD, refd[0], EPOLLIN); + set_epoll_descriptor(kdpfd, EPOLL_CTL_ADD, tunfd, + EPOLLIN | EPOLLET | EPOLLONESHOT); + curfds = 4; + + trie_init(); + + cpus = get_number_cpus_online(); + threads = cpus * THREADS_PER_CPU; + if (!ispow2(threads)) + syslog_panic("Thread number not power of two!\n"); + + threadpool = xzmalloc(sizeof(*threadpool) * threads); + thread_spawn_or_panic(cpus, efd[1], refd[1], tunfd, ipv4, udp); + + init_cpusched(threads); + + register_socket(tunfd); + register_socket(lfd); + + syslog(LOG_INFO, "curvetun up and running!\n"); + + while (likely(!sigint)) { + nfds = epoll_wait(kdpfd, events, curfds, -1); + if (nfds < 0) { + syslog(LOG_ERR, "epoll_wait error: %s\n", + strerror(errno)); + break; + } + + for (i = 0; i < nfds; ++i) { + if (unlikely(events[i].data.fd < 0)) + continue; + + if (events[i].data.fd == lfd && !udp) { + int ncpu; + char hbuff[256], sbuff[256]; + struct sockaddr_storage taddr; + socklen_t tlen; + + tlen = sizeof(taddr); + nfd = accept(lfd, (struct sockaddr *) &taddr, + &tlen); + if (nfd < 0) { + syslog(LOG_ERR, "accept error: %s\n", + strerror(errno)); + continue; + } + + if (curfds + 1 > MAX_EPOLL_SIZE) { + close(nfd); + continue; + } + + curfds++; + + ncpu = register_socket(nfd); + + memset(hbuff, 0, sizeof(hbuff)); + memset(sbuff, 0, sizeof(sbuff)); + getnameinfo((struct sockaddr *) &taddr, tlen, + hbuff, sizeof(hbuff), + sbuff, sizeof(sbuff), + NI_NUMERICHOST | NI_NUMERICSERV); + + syslog_maybe(auth_log, LOG_INFO, "New connection " + "from %s:%s (%d active client connections) - id %d on CPU%d", + hbuff, sbuff, curfds-4, nfd, ncpu); + + set_nonblocking(nfd); + set_socket_keepalive(nfd); + set_tcp_nodelay(nfd); + ret = set_epoll_descriptor2(kdpfd, EPOLL_CTL_ADD, + nfd, EPOLLIN | EPOLLET | EPOLLONESHOT); + if (ret < 0) { + close(nfd); + curfds--; + continue; + } + } else if (events[i].data.fd == refd[0]) { + int fd_one; + + ret = read_exact(refd[0], &fd_one, + sizeof(fd_one), 1); + if (ret != sizeof(fd_one) || fd_one <= 0) + continue; + + ret = set_epoll_descriptor2(kdpfd, EPOLL_CTL_MOD, + fd_one, EPOLLIN | EPOLLET | EPOLLONESHOT); + if (ret < 0) { + close(fd_one); + continue; + } + } else if (events[i].data.fd == efd[0]) { + int fd_del, test; + + ret = read_exact(efd[0], &fd_del, + sizeof(fd_del), 1); + if (ret != sizeof(fd_del) || fd_del <= 0) + continue; + + ret = read(fd_del, &test, sizeof(test)); + if (ret < 0 && errno == EBADF) + continue; + + ret = set_epoll_descriptor2(kdpfd, EPOLL_CTL_DEL, + fd_del, 0); + if (ret < 0) { + close(fd_del); + continue; + } + + close(fd_del); + curfds--; + unregister_socket(fd_del); + + syslog_maybe(auth_log, LOG_INFO, "Closed connection " + "with id %d (%d active client connections remain)\n", fd_del, + curfds-4); + } else { + int cpu, fd_work = events[i].data.fd; + + if (!udp) + cpu = socket_to_cpu(fd_work); + else + udp_cpu = (udp_cpu + 1) & (threads - 1); + + write_exact(threadpool[udp ? udp_cpu : cpu].efd[1], + &fd_work, sizeof(fd_work), 1); + } + } + } + + syslog(LOG_INFO, "curvetun prepare shut down!\n"); + + close(lfd); + close(efd[0]); + close(efd[1]); + close(refd[0]); + close(refd[1]); + close(tunfd); + + thread_finish(cpus); + + xfree(threadpool); + xfree(events); + + unregister_socket(lfd); + unregister_socket(tunfd); + + destroy_cpusched(); + + trie_cleanup(); + + destroy_user_store(); + + syslog(LOG_INFO, "curvetun shut down!\n"); + closelog(); + + return 0; +} -- cgit v1.2.3-54-g00ecf