|
|
@@ -441,7 +441,408 @@ rdns_curve_dtor (struct rdns_resolver *resolver, void *plugin_data) |
|
|
|
} |
|
|
|
REF_RELEASE (ctx->cur_key); |
|
|
|
} |
|
|
|
#elif defined(USE_RSPAMD_CRYPTOBOX) |
|
|
|
|
|
|
|
#include "cryptobox.h" |
|
|
|
|
|
|
|
#define crypto_box_ZEROBYTES 32 |
|
|
|
#define crypto_box_BOXZEROBYTES 16 |
|
|
|
|
|
|
|
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; |
|
|
|
rspamd_pk_t pk; |
|
|
|
UT_hash_handle hh; |
|
|
|
}; |
|
|
|
|
|
|
|
struct rdns_curve_nm_entry { |
|
|
|
rspamd_nm_t k; |
|
|
|
struct rdns_curve_entry *entry; |
|
|
|
struct rdns_curve_nm_entry *prev, *next; |
|
|
|
}; |
|
|
|
|
|
|
|
struct rdns_curve_client_key { |
|
|
|
rspamd_pk_t pk; |
|
|
|
rspamd_sk_t sk; |
|
|
|
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; |
|
|
|
rspamd_nonce_t nonce; |
|
|
|
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)); |
|
|
|
rspamd_cryptobox_keypair (new->pk, new->sk, RSPAMD_CRYPTOBOX_MODE_25519); |
|
|
|
|
|
|
|
HASH_ITER (hh, ctx->entries, entry, tmp) { |
|
|
|
nm = calloc (1, sizeof (struct rdns_curve_nm_entry)); |
|
|
|
nm->entry = entry; |
|
|
|
rspamd_cryptobox_nm (nm->k, entry->pk, new->sk, |
|
|
|
RSPAMD_CRYPTOBOX_MODE_25519); |
|
|
|
|
|
|
|
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) { |
|
|
|
rspamd_explicit_memzero (nm->k, sizeof (nm->k)); |
|
|
|
free (nm); |
|
|
|
} |
|
|
|
|
|
|
|
rspamd_explicit_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 == rspamd_cryptobox_pk_bytes (RSPAMD_CRYPTOBOX_MODE_25519) * 2) { |
|
|
|
res = calloc (1, rspamd_cryptobox_pk_bytes (RSPAMD_CRYPTOBOX_MODE_25519)); |
|
|
|
for (i = 0; |
|
|
|
i < rspamd_cryptobox_pk_bytes (RSPAMD_CRYPTOBOX_MODE_25519); |
|
|
|
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; |
|
|
|
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)); |
|
|
|
rspamd_explicit_memzero (creq->nonce + 12, |
|
|
|
rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519) - 12); |
|
|
|
|
|
|
|
rspamd_explicit_memzero (m, crypto_box_ZEROBYTES); |
|
|
|
memcpy (m + crypto_box_ZEROBYTES, req->packet, req->pos); |
|
|
|
|
|
|
|
rspamd_cryptobox_encrypt_nm_inplace (m + crypto_box_ZEROBYTES, |
|
|
|
boxed_len, |
|
|
|
creq->nonce, |
|
|
|
nm->k, |
|
|
|
m, |
|
|
|
RSPAMD_CRYPTOBOX_MODE_25519); |
|
|
|
|
|
|
|
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])); |
|
|
|
rspamd_explicit_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[24]; |
|
|
|
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, rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519)); |
|
|
|
p += rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519); |
|
|
|
boxlen = ret - rspamd_cryptobox_nonce_bytes (RSPAMD_CRYPTOBOX_MODE_25519) + |
|
|
|
crypto_box_BOXZEROBYTES - |
|
|
|
sizeof (rmagic) + 1; |
|
|
|
if (boxlen < 0) { |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
box = malloc (boxlen); |
|
|
|
rspamd_explicit_memzero (box, crypto_box_BOXZEROBYTES); |
|
|
|
memcpy (box + crypto_box_BOXZEROBYTES, p, |
|
|
|
boxlen - crypto_box_BOXZEROBYTES); |
|
|
|
|
|
|
|
if (!rspamd_cryptobox_decrypt_nm_inplace ( |
|
|
|
box + rspamd_cryptobox_mac_bytes (RSPAMD_CRYPTOBOX_MODE_25519), |
|
|
|
boxlen - rspamd_cryptobox_mac_bytes (RSPAMD_CRYPTOBOX_MODE_25519), |
|
|
|
enonce, creq->nm->k, box, RSPAMD_CRYPTOBOX_MODE_25519)) { |
|
|
|
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 */ |