summaryrefslogtreecommitdiff
path: root/curvetun_mgmt_users.c
diff options
context:
space:
mode:
Diffstat (limited to 'curvetun_mgmt_users.c')
-rw-r--r--curvetun_mgmt_users.c760
1 files changed, 760 insertions, 0 deletions
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 <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <syslog.h>
+#include <limits.h>
+#include <arpa/inet.h>
+
+#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);
+}