diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2015-02-21 18:11:12 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2015-02-21 18:11:12 +0000 |
commit | 6e121a026ffd681418cc8e43647afacdd13f149a (patch) | |
tree | dc02aa9d0c2a46afc53886f498844bfaa958124d /contrib/librdns/curve.c | |
parent | f9d2d85d1473ae53c13c99747ef809649f5137bf (diff) | |
download | rspamd-6e121a026ffd681418cc8e43647afacdd13f149a.tar.gz rspamd-6e121a026ffd681418cc8e43647afacdd13f149a.zip |
Move ucl and rdns to contrib.
Diffstat (limited to 'contrib/librdns/curve.c')
-rw-r--r-- | contrib/librdns/curve.c | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/contrib/librdns/curve.c b/contrib/librdns/curve.c new file mode 100644 index 000000000..4c2a64135 --- /dev/null +++ b/contrib/librdns/curve.c @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2014, Vsevolod Stakhov + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> + +#include "rdns.h" +#include "dns_private.h" +#include "rdns_curve.h" +#include "ottery.h" +#include "ref.h" +#include "logger.h" + +#ifdef TWEETNACL + +#include <tweetnacl.h> + +void +randombytes(uint8_t *data, uint64_t len) +{ + ottery_rand_bytes (data, len); +} +void sodium_memzero (uint8_t *data, uint64_t len) +{ + volatile uint8_t *p = data; + + while (len--) { + *p = '\0'; + } +} +void sodium_init(void) +{ + +} + +ssize_t rdns_curve_send (struct rdns_request *req, void *plugin_data); +ssize_t rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len, + void *plugin_data, struct rdns_request **req_out); +void rdns_curve_finish_request (struct rdns_request *req, void *plugin_data); +void rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data); + +struct rdns_curve_entry { + char *name; + unsigned char pk[crypto_box_PUBLICKEYBYTES]; + UT_hash_handle hh; +}; + +struct rdns_curve_nm_entry { + unsigned char k[crypto_box_BEFORENMBYTES]; + struct rdns_curve_entry *entry; + struct rdns_curve_nm_entry *prev, *next; +}; + +struct rdns_curve_client_key { + unsigned char pk[crypto_box_PUBLICKEYBYTES]; + unsigned char sk[crypto_box_SECRETKEYBYTES]; + struct rdns_curve_nm_entry *nms; + uint64_t counter; + unsigned int uses; + ref_entry_t ref; +}; + +struct rdns_curve_request { + struct rdns_request *req; + struct rdns_curve_client_key *key; + struct rdns_curve_entry *entry; + struct rdns_curve_nm_entry *nm; + unsigned char nonce[crypto_box_NONCEBYTES]; + UT_hash_handle hh; +}; + +struct rdns_curve_ctx { + struct rdns_curve_entry *entries; + struct rdns_curve_client_key *cur_key; + struct rdns_curve_request *requests; + double key_refresh_interval; + void *key_refresh_event; + struct rdns_resolver *resolver; +}; + +static struct rdns_curve_client_key * +rdns_curve_client_key_new (struct rdns_curve_ctx *ctx) +{ + struct rdns_curve_client_key *new; + struct rdns_curve_nm_entry *nm; + struct rdns_curve_entry *entry, *tmp; + + new = calloc (1, sizeof (struct rdns_curve_client_key)); + crypto_box_keypair (new->pk, new->sk); + + HASH_ITER (hh, ctx->entries, entry, tmp) { + nm = calloc (1, sizeof (struct rdns_curve_nm_entry)); + nm->entry = entry; + crypto_box_beforenm (nm->k, entry->pk, new->sk); + + DL_APPEND (new->nms, nm); + } + + new->counter = ottery_rand_uint64 (); + + return new; +} + +static struct rdns_curve_nm_entry * +rdns_curve_find_nm (struct rdns_curve_client_key *key, struct rdns_curve_entry *entry) +{ + struct rdns_curve_nm_entry *nm; + + DL_FOREACH (key->nms, nm) { + if (nm->entry == entry) { + return nm; + } + } + + return NULL; +} + +static void +rdns_curve_client_key_free (struct rdns_curve_client_key *key) +{ + struct rdns_curve_nm_entry *nm, *tmp; + + DL_FOREACH_SAFE (key->nms, nm, tmp) { + sodium_memzero (nm->k, sizeof (nm->k)); + free (nm); + } + sodium_memzero (key->sk, sizeof (key->sk)); + free (key); +} + +struct rdns_curve_ctx* +rdns_curve_ctx_new (double key_refresh_interval) +{ + struct rdns_curve_ctx *new; + + new = calloc (1, sizeof (struct rdns_curve_ctx)); + new->key_refresh_interval = key_refresh_interval; + + return new; +} + +void +rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx, + const char *name, const unsigned char *pubkey) +{ + struct rdns_curve_entry *entry; + bool success = true; + + entry = malloc (sizeof (struct rdns_curve_entry)); + if (entry != NULL) { + entry->name = strdup (name); + if (entry->name == NULL) { + success = false; + } + memcpy (entry->pk, pubkey, sizeof (entry->pk)); + if (success) { + HASH_ADD_KEYPTR (hh, ctx->entries, entry->name, strlen (entry->name), entry); + } + } +} + +#define rdns_curve_write_hex(in, out, offset, base) do { \ + *(out) |= ((in)[(offset)] - (base)) << ((1 - offset) * 4); \ +} while (0) + +static bool +rdns_curve_hex_to_byte (const char *in, unsigned char *out) +{ + int i; + + for (i = 0; i <= 1; i ++) { + if (in[i] >= '0' && in[i] <= '9') { + rdns_curve_write_hex (in, out, i, '0'); + } + else if (in[i] >= 'a' && in[i] <= 'f') { + rdns_curve_write_hex (in, out, i, 'a' - 10); + } + else if (in[i] >= 'A' && in[i] <= 'F') { + rdns_curve_write_hex (in, out, i, 'A' - 10); + } + else { + return false; + } + } + return true; +} + +#undef rdns_curve_write_hex + +unsigned char * +rdns_curve_key_from_hex (const char *hex) +{ + unsigned int len = strlen (hex), i; + unsigned char *res = NULL; + + if (len == crypto_box_PUBLICKEYBYTES * 2) { + res = calloc (1, crypto_box_PUBLICKEYBYTES); + for (i = 0; i < crypto_box_PUBLICKEYBYTES; i ++) { + if (!rdns_curve_hex_to_byte (&hex[i * 2], &res[i])) { + free (res); + return NULL; + } + } + } + + return res; +} + +void +rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx) +{ + struct rdns_curve_entry *entry, *tmp; + + HASH_ITER (hh, ctx->entries, entry, tmp) { + free (entry->name); + free (entry); + } + + free (ctx); +} + +static void +rdns_curve_refresh_key_callback (void *user_data) +{ + struct rdns_curve_ctx *ctx = user_data; + struct rdns_resolver *resolver; + + resolver = ctx->resolver; + rdns_info ("refresh dnscurve keys"); + REF_RELEASE (ctx->cur_key); + ctx->cur_key = rdns_curve_client_key_new (ctx); + REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free); +} + +void +rdns_curve_register_plugin (struct rdns_resolver *resolver, + struct rdns_curve_ctx *ctx) +{ + struct rdns_plugin *plugin; + + if (!resolver->async_binded) { + return; + } + + plugin = calloc (1, sizeof (struct rdns_plugin)); + if (plugin != NULL) { + plugin->data = ctx; + plugin->type = RDNS_PLUGIN_CURVE; + plugin->cb.curve_plugin.send_cb = rdns_curve_send; + plugin->cb.curve_plugin.recv_cb = rdns_curve_recv; + plugin->cb.curve_plugin.finish_cb = rdns_curve_finish_request; + plugin->dtor = rdns_curve_dtor; + sodium_init (); + ctx->cur_key = rdns_curve_client_key_new (ctx); + REF_INIT_RETAIN (ctx->cur_key, rdns_curve_client_key_free); + + if (ctx->key_refresh_interval > 0) { + ctx->key_refresh_event = resolver->async->add_periodic ( + resolver->async->data, ctx->key_refresh_interval, + rdns_curve_refresh_key_callback, ctx); + } + ctx->resolver = resolver; + rdns_resolver_register_plugin (resolver, plugin); + } +} + +ssize_t +rdns_curve_send (struct rdns_request *req, void *plugin_data) +{ + struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data; + struct rdns_curve_entry *entry; + struct iovec iov[4]; + unsigned char *m; + static const char qmagic[] = "Q6fnvWj8"; + struct rdns_curve_request *creq; + struct rdns_curve_nm_entry *nm; + ssize_t ret, boxed_len; + + /* Check for key */ + HASH_FIND_STR (ctx->entries, req->io->srv->name, entry); + if (entry != NULL) { + nm = rdns_curve_find_nm (ctx->cur_key, entry); + creq = malloc (sizeof (struct rdns_curve_request)); + if (creq == NULL) { + return -1; + } + + boxed_len = req->pos + crypto_box_ZEROBYTES; + m = malloc (boxed_len); + if (m == NULL) { + return -1; + } + + /* Ottery is faster than sodium native PRG that uses /dev/random only */ + memcpy (creq->nonce, &ctx->cur_key->counter, sizeof (uint64_t)); + ottery_rand_bytes (creq->nonce + sizeof (uint64_t), 12 - sizeof (uint64_t)); + sodium_memzero (creq->nonce + 12, crypto_box_NONCEBYTES - 12); + + sodium_memzero (m, crypto_box_ZEROBYTES); + memcpy (m + crypto_box_ZEROBYTES, req->packet, req->pos); + + if (crypto_box_afternm (m, m, boxed_len, + creq->nonce, nm->k) == -1) { + sodium_memzero (m, boxed_len); + free (m); + return -1; + } + + creq->key = ctx->cur_key; + REF_RETAIN (ctx->cur_key); + creq->entry = entry; + creq->req = req; + creq->nm = nm; + HASH_ADD_KEYPTR (hh, ctx->requests, creq->nonce, 12, creq); + req->curve_plugin_data = creq; + + ctx->cur_key->counter ++; + ctx->cur_key->uses ++; + + /* Now form a dnscurve packet */ + iov[0].iov_base = (void *)qmagic; + iov[0].iov_len = sizeof (qmagic) - 1; + iov[1].iov_base = ctx->cur_key->pk; + iov[1].iov_len = sizeof (ctx->cur_key->pk); + iov[2].iov_base = creq->nonce; + iov[2].iov_len = 12; + iov[3].iov_base = m + crypto_box_BOXZEROBYTES; + iov[3].iov_len = boxed_len - crypto_box_BOXZEROBYTES; + + ret = writev (req->io->sock, iov, sizeof (iov) / sizeof (iov[0])); + sodium_memzero (m, boxed_len); + free (m); + } + else { + ret = write (req->io->sock, req->packet, req->pos); + req->curve_plugin_data = NULL; + } + + return ret; +} + +ssize_t +rdns_curve_recv (struct rdns_io_channel *ioc, void *buf, size_t len, void *plugin_data, + struct rdns_request **req_out) +{ + struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data; + ssize_t ret, boxlen; + static const char rmagic[] = "R6fnvWJ8"; + unsigned char *p, *box; + unsigned char enonce[crypto_box_NONCEBYTES]; + struct rdns_curve_request *creq; + struct rdns_resolver *resolver; + + resolver = ctx->resolver; + ret = read (ioc->sock, buf, len); + + if (ret <= 0 || ret < 64) { + /* Definitely not a DNSCurve packet */ + return ret; + } + + if (memcmp (buf, rmagic, sizeof (rmagic) - 1) == 0) { + /* Likely DNSCurve packet */ + p = ((unsigned char *)buf) + 8; + HASH_FIND (hh, ctx->requests, p, 12, creq); + if (creq == NULL) { + rdns_info ("unable to find nonce in the internal hash"); + return ret; + } + memcpy (enonce, p, crypto_box_NONCEBYTES); + p += crypto_box_NONCEBYTES; + boxlen = ret - crypto_box_NONCEBYTES + + crypto_box_BOXZEROBYTES - + sizeof (rmagic) + 1; + if (boxlen < 0) { + return ret; + } + box = malloc (boxlen); + sodium_memzero (box, crypto_box_BOXZEROBYTES); + memcpy (box + crypto_box_BOXZEROBYTES, p, + boxlen - crypto_box_BOXZEROBYTES); + + if (crypto_box_open_afternm (box, box, boxlen, enonce, creq->nm->k) != -1) { + memcpy (buf, box + crypto_box_ZEROBYTES, + boxlen - crypto_box_ZEROBYTES); + ret = boxlen - crypto_box_ZEROBYTES; + *req_out = creq->req; + } + else { + rdns_info ("unable open cryptobox of size %d", (int)boxlen); + } + free (box); + } + + return ret; +} + +void +rdns_curve_finish_request (struct rdns_request *req, void *plugin_data) +{ + struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data; + struct rdns_curve_request *creq = req->curve_plugin_data; + + if (creq != NULL) { + REF_RELEASE (creq->key); + HASH_DELETE (hh, ctx->requests, creq); + } +} + +void +rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data) +{ + struct rdns_curve_ctx *ctx = (struct rdns_curve_ctx *)plugin_data; + + if (ctx->key_refresh_event != NULL) { + resolver->async->del_periodic (resolver->async->data, + ctx->key_refresh_event); + } + REF_RELEASE (ctx->cur_key); +} + +#else + +/* Fake functions */ +struct rdns_curve_ctx* rdns_curve_ctx_new (double rekey_interval) +{ + return NULL; +} +void rdns_curve_ctx_add_key (struct rdns_curve_ctx *ctx, + const char *name, const unsigned char *pubkey) +{ + +} +void rdns_curve_ctx_destroy (struct rdns_curve_ctx *ctx) +{ + +} +void rdns_curve_register_plugin (struct rdns_resolver *resolver, + struct rdns_curve_ctx *ctx) +{ + +} + +unsigned char * +rdns_curve_key_from_hex (const char *hex) +{ + return NULL; +} +#endif |