/* * 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)) { user_store_free(elem); 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))) { user_store_free(elem); return -EINVAL; } if (__check_duplicate_pubkey(pkey, sizeof(pkey))) { user_store_free(elem); 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: user_store_free(elem); 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); }