- dns over ssl support, ssl-service-pem and ssl-service-key files

can be given and then TCP queries are serviced wrapped in SSL.


git-svn-id: file:///svn/unbound/trunk@2530 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2011-10-31 14:48:48 +00:00
parent 28131d5845
commit aa0536dcb5
26 changed files with 2315 additions and 1601 deletions

View file

@ -239,7 +239,7 @@ endif
libunbound.la: $(LIBUNBOUND_OBJ) libunbound.la: $(LIBUNBOUND_OBJ)
$(INFO) Link $@ $(INFO) Link $@
ifeq ($(CHECKLOCK_SRC),) ifeq ($(CHECKLOCK_SRC),)
$Q$(LINK_LIB) $(UBSYMS) -o $@ $(sort $(LIBUNBOUND_OBJ)) -rpath $(libdir) $(LIBS) $Q$(LINK_LIB) $(UBSYMS) -o $@ $(sort $(LIBUNBOUND_OBJ)) -rpath $(libdir) -lssl $(LIBS)
else else
cp $(srcdir)/libunbound/ubsyms.def $(BUILD)clubsyms.def cp $(srcdir)/libunbound/ubsyms.def $(BUILD)clubsyms.def
echo lock_protect >> $(BUILD)clubsyms.def echo lock_protect >> $(BUILD)clubsyms.def
@ -252,7 +252,7 @@ else
echo checklock_init >> $(BUILD)clubsyms.def echo checklock_init >> $(BUILD)clubsyms.def
echo checklock_thrcreate >> $(BUILD)clubsyms.def echo checklock_thrcreate >> $(BUILD)clubsyms.def
echo checklock_thrjoin >> $(BUILD)clubsyms.def echo checklock_thrjoin >> $(BUILD)clubsyms.def
$Q$(LINK_LIB) $(CLUBSYMS) -o $@ $(sort $(LIBUNBOUND_OBJ)) -rpath $(libdir) $(LIBS) $Q$(LINK_LIB) $(CLUBSYMS) -o $@ $(sort $(LIBUNBOUND_OBJ)) -rpath $(libdir) -lssl $(LIBS)
endif endif
unbound$(EXEEXT): $(DAEMON_OBJ) libunbound.la unbound$(EXEEXT): $(DAEMON_OBJ) libunbound.la
@ -289,7 +289,7 @@ anchor-update$(EXEEXT): $(ANCHORUPD_OBJ) libunbound.la
unittest$(EXEEXT): $(UNITTEST_OBJ) unittest$(EXEEXT): $(UNITTEST_OBJ)
$(INFO) Link $@ $(INFO) Link $@
$Q$(LINK) -o $@ $(sort $(UNITTEST_OBJ)) $(LIBS) $Q$(LINK) -o $@ $(sort $(UNITTEST_OBJ)) -lssl $(LIBS)
testbound$(EXEEXT): $(TESTBOUND_OBJ) testbound$(EXEEXT): $(TESTBOUND_OBJ)
$(INFO) Link $@ $(INFO) Link $@
@ -297,7 +297,7 @@ testbound$(EXEEXT): $(TESTBOUND_OBJ)
lock-verify$(EXEEXT): $(LOCKVERIFY_OBJ) lock-verify$(EXEEXT): $(LOCKVERIFY_OBJ)
$(INFO) Link $@ $(INFO) Link $@
$Q$(LINK) -o $@ $(sort $(LOCKVERIFY_OBJ)) $(LIBS) $Q$(LINK) -o $@ $(sort $(LOCKVERIFY_OBJ)) -lssl $(LIBS)
petal$(EXEEXT): $(PETAL_OBJ) petal$(EXEEXT): $(PETAL_OBJ)
$(INFO) Link $@ $(INFO) Link $@
@ -305,15 +305,15 @@ petal$(EXEEXT): $(PETAL_OBJ)
pktview$(EXEEXT): $(PKTVIEW_OBJ) pktview$(EXEEXT): $(PKTVIEW_OBJ)
$(INFO) Link $@ $(INFO) Link $@
$Q$(LINK) -o $@ $(sort $(PKTVIEW_OBJ)) $(LIBS) $Q$(LINK) -o $@ $(sort $(PKTVIEW_OBJ)) -lssl $(LIBS)
signit$(EXEEXT): $(SIGNIT_OBJ) signit$(EXEEXT): $(SIGNIT_OBJ)
$(INFO) Link $@ $(INFO) Link $@
$Q$(LINK) -o $@ $(sort $(SIGNIT_OBJ)) $(LIBS) $Q$(LINK) -o $@ $(sort $(SIGNIT_OBJ)) -lssl $(LIBS)
memstats$(EXEEXT): $(MEMSTATS_OBJ) memstats$(EXEEXT): $(MEMSTATS_OBJ)
$(INFO) Link $@ $(INFO) Link $@
$Q$(LINK) -o $@ $(sort $(MEMSTATS_OBJ)) $(LIBS) $Q$(LINK) -o $@ $(sort $(MEMSTATS_OBJ)) -lssl $(LIBS)
asynclook$(EXEEXT): $(ASYNCLOOK_OBJ) libunbound.la asynclook$(EXEEXT): $(ASYNCLOOK_OBJ) libunbound.la
$(INFO) Link $@ $(INFO) Link $@
@ -321,15 +321,15 @@ asynclook$(EXEEXT): $(ASYNCLOOK_OBJ) libunbound.la
streamtcp$(EXEEXT): $(STREAMTCP_OBJ) streamtcp$(EXEEXT): $(STREAMTCP_OBJ)
$(INFO) Link $@ $(INFO) Link $@
$Q$(LINK) -o $@ $(sort $(STREAMTCP_OBJ)) $(LIBS) $Q$(LINK) -o $@ $(sort $(STREAMTCP_OBJ)) -lssl $(LIBS)
perf$(EXEEXT): $(PERF_OBJ) perf$(EXEEXT): $(PERF_OBJ)
$(INFO) Link $@ $(INFO) Link $@
$Q$(LINK) -o $@ $(sort $(PERF_OBJ)) $(LIBS) $Q$(LINK) -o $@ $(sort $(PERF_OBJ)) -lssl $(LIBS)
delayer$(EXEEXT): $(DELAYER_OBJ) delayer$(EXEEXT): $(DELAYER_OBJ)
$(INFO) Link $@ $(INFO) Link $@
$Q$(LINK) -o $@ $(sort $(DELAYER_OBJ)) $(LIBS) $Q$(LINK) -o $@ $(sort $(DELAYER_OBJ)) -lssl $(LIBS)
harvest$(EXEEXT): $(HARVEST_OBJ) libunbound.la harvest$(EXEEXT): $(HARVEST_OBJ) libunbound.la
$(INFO) Link $@ $(INFO) Link $@

View file

@ -528,6 +528,8 @@ daemon_delete(struct daemon* daemon)
free(daemon->chroot); free(daemon->chroot);
free(daemon->pidfile); free(daemon->pidfile);
free(daemon->env); free(daemon->env);
SSL_CTX_free((SSL_CTX*)daemon->listen_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->connect_sslctx);
free(daemon); free(daemon);
#ifdef LEX_HAS_YYLEX_DESTROY #ifdef LEX_HAS_YYLEX_DESTROY
/* lex cleanup */ /* lex cleanup */

View file

@ -80,6 +80,8 @@ struct daemon {
struct listen_port* rc_ports; struct listen_port* rc_ports;
/** remote control connections management (for first worker) */ /** remote control connections management (for first worker) */
struct daemon_remote* rc; struct daemon_remote* rc;
/** ssl context for listening to dnstcp over ssl, and connecting ssl */
void* listen_sslctx, *connect_sslctx;
/** num threads allocated */ /** num threads allocated */
int num; int num;
/** the worker entries */ /** the worker entries */

View file

@ -92,21 +92,6 @@
/** if true, inhibits a lot of =0 lines from the stats output */ /** if true, inhibits a lot of =0 lines from the stats output */
static const int inhibit_zero = 1; static const int inhibit_zero = 1;
/** log ssl crypto err */
static void
log_crypto_err(const char* str)
{
/* error:[error code]:[library name]:[function name]:[reason string] */
char buf[128];
unsigned long e;
ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
log_err("%s crypto %s", str, buf);
while( (e=ERR_get_error()) ) {
ERR_error_string_n(e, buf, sizeof(buf));
log_err("and additionally crypto %s", buf);
}
}
/** subtract timers and the values do not overflow or become negative */ /** subtract timers and the values do not overflow or become negative */
static void static void
timeval_subtract(struct timeval* d, const struct timeval* end, timeval_subtract(struct timeval* d, const struct timeval* end,

View file

@ -55,6 +55,7 @@
#include "services/cache/infra.h" #include "services/cache/infra.h"
#include "util/data/msgreply.h" #include "util/data/msgreply.h"
#include "util/module.h" #include "util/module.h"
#include "util/net_help.h"
#include <signal.h> #include <signal.h>
#include <fcntl.h> #include <fcntl.h>
#include <openssl/crypto.h> #include <openssl/crypto.h>
@ -446,6 +447,13 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
/* read ssl keys while superuser and outside chroot */ /* read ssl keys while superuser and outside chroot */
if(!(daemon->rc = daemon_remote_create(cfg))) if(!(daemon->rc = daemon_remote_create(cfg)))
fatal_exit("could not set up remote-control"); fatal_exit("could not set up remote-control");
if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
if(!(daemon->listen_sslctx = listen_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL)))
fatal_exit("could not set up listen SSL_CTX");
}
if(!(daemon->connect_sslctx = connect_sslctx_create(NULL, NULL, NULL)))
fatal_exit("could not set up connect SSL_CTX");
#ifdef HAVE_KILL #ifdef HAVE_KILL
/* check old pid file before forking */ /* check old pid file before forking */

View file

@ -1092,7 +1092,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
} }
worker->front = listen_create(worker->base, ports, worker->front = listen_create(worker->base, ports,
cfg->msg_buffer_size, (int)cfg->incoming_num_tcp, cfg->msg_buffer_size, (int)cfg->incoming_num_tcp,
worker_handle_request, worker); worker->daemon->listen_sslctx, worker_handle_request, worker);
if(!worker->front) { if(!worker->front) {
log_err("could not create listening sockets"); log_err("could not create listening sockets");
worker_delete(worker); worker_delete(worker);
@ -1105,7 +1105,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
worker->daemon->env->infra_cache, worker->rndstate, worker->daemon->env->infra_cache, worker->rndstate,
cfg->use_caps_bits_for_id, worker->ports, worker->numports, cfg->use_caps_bits_for_id, worker->ports, worker->numports,
cfg->unwanted_threshold, &worker_alloc_cleanup, worker, cfg->unwanted_threshold, &worker_alloc_cleanup, worker,
cfg->do_udp); cfg->do_udp, worker->daemon->connect_sslctx);
if(!worker->back) { if(!worker->back) {
log_err("could not create outgoing sockets"); log_err("could not create outgoing sockets");
worker_delete(worker); worker_delete(worker);
@ -1255,9 +1255,9 @@ worker_send_query(uint8_t* qname, size_t qnamelen, uint16_t qtype,
e->qstate = q; e->qstate = q;
e->qsent = outnet_serviced_query(worker->back, qname, e->qsent = outnet_serviced_query(worker->back, qname,
qnamelen, qtype, qclass, flags, dnssec, want_dnssec, qnamelen, qtype, qclass, flags, dnssec, want_dnssec,
q->env->cfg->tcp_upstream, addr, addrlen, zone, zonelen, q->env->cfg->tcp_upstream || q->env->cfg->ssl_upstream, addr,
worker_handle_service_reply, e, worker->back->udp_buff, addrlen, zone, zonelen, worker_handle_service_reply, e,
&outbound_entry_compare); worker->back->udp_buff, &outbound_entry_compare);
if(!e->qsent) { if(!e->qsent) {
return NULL; return NULL;
} }

View file

@ -1,3 +1,7 @@
31 October 2011: Wouter
- dns over ssl support, ssl-service-pem and ssl-service-key files
can be given and then TCP queries are serviced wrapped in SSL.
27 October 2011: Wouter 27 October 2011: Wouter
- lame-ttl and lame-size options no longer exist, it is integrated - lame-ttl and lame-size options no longer exist, it is integrated
with the host info. They are ignored (with verbose warning) if with the host info. They are ignored (with verbose warning) if

View file

@ -44,6 +44,7 @@
#include "config.h" #include "config.h"
#include <ldns/dname.h> #include <ldns/dname.h>
#include <ldns/wire2host.h> #include <ldns/wire2host.h>
#include <openssl/ssl.h>
#include "libunbound/libworker.h" #include "libunbound/libworker.h"
#include "libunbound/context.h" #include "libunbound/context.h"
#include "libunbound/unbound.h" #include "libunbound/unbound.h"
@ -84,6 +85,7 @@ libworker_delete(struct libworker* w)
ub_randfree(w->env->rnd); ub_randfree(w->env->rnd);
free(w->env); free(w->env);
} }
SSL_CTX_free(w->sslctx);
outside_network_delete(w->back); outside_network_delete(w->back);
comm_base_delete(w->base); comm_base_delete(w->base);
free(w); free(w);
@ -124,6 +126,13 @@ libworker_setup(struct ub_ctx* ctx, int is_bg)
forwards_delete(w->env->fwds); forwards_delete(w->env->fwds);
w->env->fwds = NULL; w->env->fwds = NULL;
} }
if(cfg->ssl_upstream) {
w->sslctx = connect_sslctx_create(NULL, NULL, NULL);
if(!w->sslctx) {
libworker_delete(w);
return NULL;
}
}
if(!w->is_bg || w->is_bg_thread) { if(!w->is_bg || w->is_bg_thread) {
lock_basic_unlock(&ctx->cfglock); lock_basic_unlock(&ctx->cfglock);
} }
@ -171,7 +180,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg)
cfg->do_tcp?cfg->outgoing_num_tcp:0, cfg->do_tcp?cfg->outgoing_num_tcp:0,
w->env->infra_cache, w->env->rnd, cfg->use_caps_bits_for_id, w->env->infra_cache, w->env->rnd, cfg->use_caps_bits_for_id,
ports, numports, cfg->unwanted_threshold, ports, numports, cfg->unwanted_threshold,
&libworker_alloc_cleanup, w, cfg->do_udp); &libworker_alloc_cleanup, w, cfg->do_udp, w->sslctx);
if(!w->is_bg || w->is_bg_thread) { if(!w->is_bg || w->is_bg_thread) {
lock_basic_unlock(&ctx->cfglock); lock_basic_unlock(&ctx->cfglock);
} }
@ -695,9 +704,9 @@ struct outbound_entry* libworker_send_query(uint8_t* qname, size_t qnamelen,
e->qstate = q; e->qstate = q;
e->qsent = outnet_serviced_query(w->back, qname, e->qsent = outnet_serviced_query(w->back, qname,
qnamelen, qtype, qclass, flags, dnssec, want_dnssec, qnamelen, qtype, qclass, flags, dnssec, want_dnssec,
q->env->cfg->tcp_upstream, addr, addrlen, zone, zonelen, q->env->cfg->tcp_upstream || q->env->cfg->ssl_upstream, addr,
libworker_handle_service_reply, e, w->back->udp_buff, addrlen, zone, zonelen, libworker_handle_service_reply, e,
&outbound_entry_compare); w->back->udp_buff, &outbound_entry_compare);
if(!e->qsent) { if(!e->qsent) {
return NULL; return NULL;
} }

View file

@ -81,6 +81,8 @@ struct libworker {
struct outside_network* back; struct outside_network* back;
/** random() table for this worker. */ /** random() table for this worker. */
struct ub_randstate* rndstate; struct ub_randstate* rndstate;
/** sslcontext for SSL wrapped DNS over TCP queries */
void* sslctx;
}; };
/** /**

View file

@ -723,7 +723,7 @@ listen_cp_insert(struct comm_point* c, struct listen_dnsport* front)
struct listen_dnsport* struct listen_dnsport*
listen_create(struct comm_base* base, struct listen_port* ports, listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, size_t bufsize, int tcp_accept_count, void* sslctx,
comm_point_callback_t* cb, void *cb_arg) comm_point_callback_t* cb, void *cb_arg)
{ {
struct listen_dnsport* front = (struct listen_dnsport*) struct listen_dnsport* front = (struct listen_dnsport*)
@ -736,17 +736,21 @@ listen_create(struct comm_base* base, struct listen_port* ports,
free(front); free(front);
return NULL; return NULL;
} }
if(sslctx) {
verbose(VERB_ALGO, "setup for SSL-wrapped TCP service");
}
/* create comm points as needed */ /* create comm points as needed */
while(ports) { while(ports) {
struct comm_point* cp = NULL; struct comm_point* cp = NULL;
if(ports->ftype == listen_type_udp) if(ports->ftype == listen_type_udp)
cp = comm_point_create_udp(base, ports->fd, cp = comm_point_create_udp(base, ports->fd,
front->udp_buff, cb, cb_arg); front->udp_buff, cb, cb_arg);
else if(ports->ftype == listen_type_tcp) else if(ports->ftype == listen_type_tcp) {
cp = comm_point_create_tcp(base, ports->fd, cp = comm_point_create_tcp(base, ports->fd,
tcp_accept_count, bufsize, cb, cb_arg); tcp_accept_count, bufsize, cb, cb_arg);
else if(ports->ftype == listen_type_udpancil) cp->ssl = sslctx;
} else if(ports->ftype == listen_type_udpancil)
cp = comm_point_create_udp_ancil(base, ports->fd, cp = comm_point_create_udp_ancil(base, ports->fd,
front->udp_buff, cb, cb_arg); front->udp_buff, cb, cb_arg);
if(!cp) { if(!cp) {

View file

@ -121,6 +121,7 @@ void listening_ports_free(struct listen_port* list);
* @param bufsize: size of datagram buffer. * @param bufsize: size of datagram buffer.
* @param tcp_accept_count: max number of simultaneous TCP connections * @param tcp_accept_count: max number of simultaneous TCP connections
* from clients. * from clients.
* @param sslctx: nonNULL if ssl context.
* @param cb: callback function when a request arrives. It is passed * @param cb: callback function when a request arrives. It is passed
* the packet and user argument. Return true to send a reply. * the packet and user argument. Return true to send a reply.
* @param cb_arg: user data argument for callback function. * @param cb_arg: user data argument for callback function.
@ -128,7 +129,7 @@ void listening_ports_free(struct listen_port* list);
*/ */
struct listen_dnsport* listen_create(struct comm_base* base, struct listen_dnsport* listen_create(struct comm_base* base,
struct listen_port* ports, size_t bufsize, int tcp_accept_count, struct listen_port* ports, size_t bufsize, int tcp_accept_count,
comm_point_callback_t* cb, void* cb_arg); void* sslctx, comm_point_callback_t* cb, void* cb_arg);
/** /**
* delete the listening structure * delete the listening structure

View file

@ -535,7 +535,8 @@ outside_network_create(struct comm_base *base, size_t bufsize,
int do_ip6, size_t num_tcp, struct infra_cache* infra, int do_ip6, size_t num_tcp, struct infra_cache* infra,
struct ub_randstate* rnd, int use_caps_for_id, int* availports, struct ub_randstate* rnd, int use_caps_for_id, int* availports,
int numavailports, size_t unwanted_threshold, int numavailports, size_t unwanted_threshold,
void (*unwanted_action)(void*), void* unwanted_param, int do_udp) void (*unwanted_action)(void*), void* unwanted_param, int do_udp,
void* sslctx)
{ {
struct outside_network* outnet = (struct outside_network*) struct outside_network* outnet = (struct outside_network*)
calloc(1, sizeof(struct outside_network)); calloc(1, sizeof(struct outside_network));
@ -549,6 +550,7 @@ outside_network_create(struct comm_base *base, size_t bufsize,
outnet->num_tcp = num_tcp; outnet->num_tcp = num_tcp;
outnet->infra = infra; outnet->infra = infra;
outnet->rnd = rnd; outnet->rnd = rnd;
outnet->sslctx = sslctx;
outnet->svcd_overhead = 0; outnet->svcd_overhead = 0;
outnet->want_to_quit = 0; outnet->want_to_quit = 0;
outnet->unwanted_threshold = unwanted_threshold; outnet->unwanted_threshold = unwanted_threshold;

View file

@ -118,6 +118,8 @@ struct outside_network {
struct infra_cache* infra; struct infra_cache* infra;
/** where to get random numbers */ /** where to get random numbers */
struct ub_randstate* rnd; struct ub_randstate* rnd;
/** ssl context to create ssl wrapped TCP with DNS connections */
void* sslctx;
/** /**
* Array of tcp pending used for outgoing TCP connections. * Array of tcp pending used for outgoing TCP connections.
@ -369,6 +371,7 @@ struct serviced_query {
* @param unwanted_action: the action to take. * @param unwanted_action: the action to take.
* @param unwanted_param: user parameter to action. * @param unwanted_param: user parameter to action.
* @param do_udp: if udp is done. * @param do_udp: if udp is done.
* @param sslctx: context to create outgoing connections with (if enabled).
* @return: the new structure (with no pending answers) or NULL on error. * @return: the new structure (with no pending answers) or NULL on error.
*/ */
struct outside_network* outside_network_create(struct comm_base* base, struct outside_network* outside_network_create(struct comm_base* base,
@ -376,7 +379,8 @@ struct outside_network* outside_network_create(struct comm_base* base,
int do_ip4, int do_ip6, size_t num_tcp, struct infra_cache* infra, int do_ip4, int do_ip6, size_t num_tcp, struct infra_cache* infra,
struct ub_randstate* rnd, int use_caps_for_id, int* availports, struct ub_randstate* rnd, int use_caps_for_id, int* availports,
int numavailports, size_t unwanted_threshold, int numavailports, size_t unwanted_threshold,
void (*unwanted_action)(void*), void* unwanted_param, int do_udp); void (*unwanted_action)(void*), void* unwanted_param, int do_udp,
void* sslctx);
/** /**
* Delete outside_network structure. * Delete outside_network structure.

View file

@ -708,7 +708,7 @@ run_scenario(struct replay_runtime* runtime)
struct listen_dnsport* struct listen_dnsport*
listen_create(struct comm_base* base, struct listen_port* ATTR_UNUSED(ports), listen_create(struct comm_base* base, struct listen_port* ATTR_UNUSED(ports),
size_t bufsize, int ATTR_UNUSED(tcp_accept_count), size_t bufsize, int ATTR_UNUSED(tcp_accept_count),
comm_point_callback_t* cb, void* cb_arg) void* ATTR_UNUSED(sslctx), comm_point_callback_t* cb, void* cb_arg)
{ {
struct replay_runtime* runtime = (struct replay_runtime*)base; struct replay_runtime* runtime = (struct replay_runtime*)base;
struct listen_dnsport* l= calloc(1, sizeof(struct listen_dnsport)); struct listen_dnsport* l= calloc(1, sizeof(struct listen_dnsport));
@ -877,7 +877,7 @@ outside_network_create(struct comm_base* base, size_t bufsize,
int ATTR_UNUSED(use_caps_for_id), int* ATTR_UNUSED(availports), int ATTR_UNUSED(use_caps_for_id), int* ATTR_UNUSED(availports),
int ATTR_UNUSED(numavailports), size_t ATTR_UNUSED(unwanted_threshold), int ATTR_UNUSED(numavailports), size_t ATTR_UNUSED(unwanted_threshold),
void (*unwanted_action)(void*), void* ATTR_UNUSED(unwanted_param), void (*unwanted_action)(void*), void* ATTR_UNUSED(unwanted_param),
int ATTR_UNUSED(do_udp)) int ATTR_UNUSED(do_udp), void* ATTR_UNUSED(sslctx))
{ {
struct replay_runtime* runtime = (struct replay_runtime*)base; struct replay_runtime* runtime = (struct replay_runtime*)base;
struct outside_network* outnet = calloc(1, struct outside_network* outnet = calloc(1,

View file

@ -49,6 +49,7 @@
#include "util/log.h" #include "util/log.h"
#include "util/net_help.h" #include "util/net_help.h"
#include "util/data/msgencode.h" #include "util/data/msgencode.h"
#include "util/data/msgparse.h"
#include "util/data/msgreply.h" #include "util/data/msgreply.h"
#include "util/data/dname.h" #include "util/data/dname.h"
@ -65,6 +66,7 @@ static void usage(char* argv[])
printf("-f server what ipaddr@portnr to send the queries to\n"); printf("-f server what ipaddr@portnr to send the queries to\n");
printf("-u use UDP. No retries are attempted.\n"); printf("-u use UDP. No retries are attempted.\n");
printf("-n do not wait for an answer.\n"); printf("-n do not wait for an answer.\n");
printf("-s use ssl\n");
printf("-h this help text\n"); printf("-h this help text\n");
exit(1); exit(1);
} }
@ -105,7 +107,7 @@ open_svr(const char* svr, int udp)
/** write a query over the TCP fd */ /** write a query over the TCP fd */
static void static void
write_q(int fd, int udp, ldns_buffer* buf, int id, write_q(int fd, int udp, SSL* ssl, ldns_buffer* buf, uint16_t id,
const char* strname, const char* strtype, const char* strclass) const char* strname, const char* strtype, const char* strclass)
{ {
struct query_info qinfo; struct query_info qinfo;
@ -128,65 +130,111 @@ write_q(int fd, int udp, ldns_buffer* buf, int id,
/* make query */ /* make query */
qinfo_query_encode(buf, &qinfo); qinfo_query_encode(buf, &qinfo);
ldns_buffer_write_u16_at(buf, 0, (uint16_t)id); ldns_buffer_write_u16_at(buf, 0, id);
ldns_buffer_write_u16_at(buf, 2, BIT_RD); ldns_buffer_write_u16_at(buf, 2, BIT_RD);
if(1) {
/* add EDNS DO */
struct edns_data edns;
memset(&edns, 0, sizeof(edns));
edns.edns_present = 1;
edns.bits = EDNS_DO;
edns.udp_size = 4096;
attach_edns_record(buf, &edns);
}
/* send it */ /* send it */
if(!udp) { if(!udp) {
len = (uint16_t)ldns_buffer_limit(buf); len = (uint16_t)ldns_buffer_limit(buf);
len = htons(len); len = htons(len);
if(send(fd, (void*)&len, sizeof(len), 0)<(ssize_t)sizeof(len)){ if(ssl) {
if(SSL_write(ssl, (void*)&len, (int)sizeof(len)) <= 0) {
log_crypto_err("cannot SSL_write");
exit(1);
}
} else {
if(send(fd, (void*)&len, sizeof(len), 0) <
(ssize_t)sizeof(len)){
#ifndef USE_WINSOCK #ifndef USE_WINSOCK
perror("send() len failed"); perror("send() len failed");
#else #else
printf("send len: %s\n", printf("send len: %s\n",
wsa_strerror(WSAGetLastError())); wsa_strerror(WSAGetLastError()));
#endif
exit(1);
}
}
}
if(ssl) {
if(SSL_write(ssl, (void*)ldns_buffer_begin(buf),
(int)ldns_buffer_limit(buf)) <= 0) {
log_crypto_err("cannot SSL_write");
exit(1);
}
} else {
if(send(fd, (void*)ldns_buffer_begin(buf),
ldns_buffer_limit(buf), 0) <
(ssize_t)ldns_buffer_limit(buf)) {
#ifndef USE_WINSOCK
perror("send() data failed");
#else
printf("send data: %s\n", wsa_strerror(WSAGetLastError()));
#endif #endif
exit(1); exit(1);
} }
} }
if(send(fd, (void*)ldns_buffer_begin(buf), ldns_buffer_limit(buf), 0) <
(ssize_t)ldns_buffer_limit(buf)) {
#ifndef USE_WINSOCK
perror("send() data failed");
#else
printf("send data: %s\n", wsa_strerror(WSAGetLastError()));
#endif
exit(1);
}
free(qinfo.qname); free(qinfo.qname);
} }
/** receive DNS datagram over TCP and print it */ /** receive DNS datagram over TCP and print it */
static void static void
recv_one(int fd, int udp, ldns_buffer* buf) recv_one(int fd, int udp, SSL* ssl, ldns_buffer* buf)
{ {
uint16_t len; uint16_t len;
ldns_pkt* pkt; ldns_pkt* pkt;
ldns_status status; ldns_status status;
if(!udp) { if(!udp) {
if(recv(fd, (void*)&len, sizeof(len), 0)<(ssize_t)sizeof(len)){ if(ssl) {
if(SSL_read(ssl, (void*)&len, (int)sizeof(len)) <= 0) {
log_crypto_err("could not SSL_read");
exit(1);
}
} else {
if(recv(fd, (void*)&len, sizeof(len), 0) <
(ssize_t)sizeof(len)) {
#ifndef USE_WINSOCK #ifndef USE_WINSOCK
perror("read() len failed"); perror("read() len failed");
#else #else
printf("read len: %s\n", printf("read len: %s\n",
wsa_strerror(WSAGetLastError())); wsa_strerror(WSAGetLastError()));
#endif #endif
exit(1); exit(1);
}
} }
len = ntohs(len); len = ntohs(len);
ldns_buffer_clear(buf); ldns_buffer_clear(buf);
ldns_buffer_set_limit(buf, len); ldns_buffer_set_limit(buf, len);
if(recv(fd, (void*)ldns_buffer_begin(buf), len, 0) < if(ssl) {
(ssize_t)len) { int r = SSL_read(ssl, (void*)ldns_buffer_begin(buf),
(int)len);
if(r <= 0) {
log_crypto_err("could not SSL_read");
exit(1);
}
if(r != (int)len)
fatal_exit("ssl_read %d of %d", r, len);
} else {
if(recv(fd, (void*)ldns_buffer_begin(buf), len, 0) <
(ssize_t)len) {
#ifndef USE_WINSOCK #ifndef USE_WINSOCK
perror("read() data failed"); perror("read() data failed");
#else #else
printf("read data: %s\n", printf("read data: %s\n",
wsa_strerror(WSAGetLastError())); wsa_strerror(WSAGetLastError()));
#endif #endif
exit(1); exit(1);
}
} }
} else { } else {
ssize_t l; ssize_t l;
@ -220,20 +268,34 @@ recv_one(int fd, int udp, ldns_buffer* buf)
/** send the TCP queries and print answers */ /** send the TCP queries and print answers */
static void static void
send_em(const char* svr, int udp, int noanswer, int num, char** qs) send_em(const char* svr, int udp, int usessl, int noanswer, int num, char** qs)
{ {
ldns_buffer* buf = ldns_buffer_new(65553); ldns_buffer* buf = ldns_buffer_new(65553);
int fd = open_svr(svr, udp); int fd = open_svr(svr, udp);
int i; int i;
SSL_CTX* ctx;
SSL* ssl;
if(!buf) fatal_exit("out of memory"); if(!buf) fatal_exit("out of memory");
if(usessl) {
ctx = connect_sslctx_create(NULL, NULL, NULL);
if(!ctx) fatal_exit("cannot create ssl ctx");
ssl = outgoing_ssl_fd(ctx, fd);
if(!ssl) fatal_exit("cannot create ssl");
}
for(i=0; i<num; i+=3) { for(i=0; i<num; i+=3) {
printf("\nNext query is %s %s %s\n", qs[i], qs[i+1], qs[i+2]); printf("\nNext query is %s %s %s\n", qs[i], qs[i+1], qs[i+2]);
write_q(fd, udp, buf, i, qs[i], qs[i+1], qs[i+2]); write_q(fd, udp, ssl, buf, ldns_get_random(), qs[i],
qs[i+1], qs[i+2]);
/* print at least one result */ /* print at least one result */
if(!noanswer) if(!noanswer)
recv_one(fd, udp, buf); recv_one(fd, udp, ssl, buf);
} }
if(usessl) {
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
}
#ifndef USE_WINSOCK #ifndef USE_WINSOCK
close(fd); close(fd);
#else #else
@ -268,6 +330,7 @@ int main(int argc, char** argv)
const char* svr = "127.0.0.1"; const char* svr = "127.0.0.1";
int udp = 0; int udp = 0;
int noanswer = 0; int noanswer = 0;
int usessl = 0;
#ifdef USE_WINSOCK #ifdef USE_WINSOCK
WSADATA wsa_data; WSADATA wsa_data;
@ -292,7 +355,7 @@ int main(int argc, char** argv)
if(argc == 1) { if(argc == 1) {
usage(argv); usage(argv);
} }
while( (c=getopt(argc, argv, "f:hnu")) != -1) { while( (c=getopt(argc, argv, "f:hnsu")) != -1) {
switch(c) { switch(c) {
case 'f': case 'f':
svr = optarg; svr = optarg;
@ -303,6 +366,9 @@ int main(int argc, char** argv)
case 'u': case 'u':
udp = 1; udp = 1;
break; break;
case 's':
usessl = 1;
break;
case 'h': case 'h':
case '?': case '?':
default: default:
@ -316,7 +382,12 @@ int main(int argc, char** argv)
printf("queries must be multiples of name,type,class\n"); printf("queries must be multiples of name,type,class\n");
return 1; return 1;
} }
send_em(svr, udp, noanswer, argc, argv); if(usessl) {
ERR_load_SSL_strings();
OpenSSL_add_all_algorithms();
SSL_library_init();
}
send_em(svr, udp, usessl, noanswer, argc, argv);
checklock_stop(); checklock_stop();
#ifdef USE_WINSOCK #ifdef USE_WINSOCK
WSACleanup(); WSACleanup();

View file

@ -88,6 +88,9 @@ config_create(void)
cfg->do_udp = 1; cfg->do_udp = 1;
cfg->do_tcp = 1; cfg->do_tcp = 1;
cfg->tcp_upstream = 0; cfg->tcp_upstream = 0;
cfg->ssl_service_key = NULL;
cfg->ssl_service_pem = NULL;
cfg->ssl_upstream = 0;
cfg->use_syslog = 1; cfg->use_syslog = 1;
cfg->log_time_ascii = 0; cfg->log_time_ascii = 0;
cfg->log_queries = 0; cfg->log_queries = 0;
@ -326,6 +329,9 @@ int config_set_option(struct config_file* cfg, const char* opt,
else S_YNO("do-udp:", do_udp) else S_YNO("do-udp:", do_udp)
else S_YNO("do-tcp:", do_tcp) else S_YNO("do-tcp:", do_tcp)
else S_YNO("tcp-upstream:", tcp_upstream) else S_YNO("tcp-upstream:", tcp_upstream)
else S_YNO("ssl-upstream:", ssl_upstream)
else S_STR("ssl-service-key:", ssl_service_key)
else S_STR("ssl-service-pem:", ssl_service_pem)
else S_YNO("interface-automatic:", if_automatic) else S_YNO("interface-automatic:", if_automatic)
else S_YNO("do-daemonize:", do_daemonize) else S_YNO("do-daemonize:", do_daemonize)
else S_NUMBER_NONZERO("port:", port) else S_NUMBER_NONZERO("port:", port)
@ -574,6 +580,9 @@ config_get_option(struct config_file* cfg, const char* opt,
else O_YNO(opt, "do-udp", do_udp) else O_YNO(opt, "do-udp", do_udp)
else O_YNO(opt, "do-tcp", do_tcp) else O_YNO(opt, "do-tcp", do_tcp)
else O_YNO(opt, "tcp-upstream", tcp_upstream) else O_YNO(opt, "tcp-upstream", tcp_upstream)
else O_YNO(opt, "ssl-upstream", ssl_upstream)
else O_STR(opt, "ssl-service-key", ssl_service_key)
else O_STR(opt, "ssl-service-pem", ssl_service_pem)
else O_YNO(opt, "do-daemonize", do_daemonize) else O_YNO(opt, "do-daemonize", do_daemonize)
else O_STR(opt, "chroot", chrootdir) else O_STR(opt, "chroot", chrootdir)
else O_STR(opt, "username", username) else O_STR(opt, "username", username)
@ -728,6 +737,8 @@ config_delete(struct config_file* cfg)
free(cfg->logfile); free(cfg->logfile);
free(cfg->pidfile); free(cfg->pidfile);
free(cfg->target_fetch_policy); free(cfg->target_fetch_policy);
free(cfg->ssl_service_key);
free(cfg->ssl_service_pem);
if(cfg->ifs) { if(cfg->ifs) {
int i; int i;
for(i=0; i<cfg->num_ifs; i++) for(i=0; i<cfg->num_ifs; i++)

View file

@ -79,6 +79,13 @@ struct config_file {
/** tcp upstream queries (no UDP upstream queries) */ /** tcp upstream queries (no UDP upstream queries) */
int tcp_upstream; int tcp_upstream;
/** private key file for dnstcp-ssl service (enabled if not NULL) */
char* ssl_service_key;
/** public key file for dnstcp-ssl service */
char* ssl_service_pem;
/** if outgoing tcp connections use SSL */
int ssl_upstream;
/** outgoing port range number of ports (per thread) */ /** outgoing port range number of ports (per thread) */
int outgoing_num_ports; int outgoing_num_ports;
/** number of outgoing tcp buffers per (per thread) */ /** number of outgoing tcp buffers per (per thread) */

File diff suppressed because it is too large Load diff

View file

@ -137,6 +137,9 @@ do-ip6{COLON} { YDVAR(1, VAR_DO_IP6) }
do-udp{COLON} { YDVAR(1, VAR_DO_UDP) } do-udp{COLON} { YDVAR(1, VAR_DO_UDP) }
do-tcp{COLON} { YDVAR(1, VAR_DO_TCP) } do-tcp{COLON} { YDVAR(1, VAR_DO_TCP) }
tcp-upstream{COLON} { YDVAR(1, VAR_TCP_UPSTREAM) } tcp-upstream{COLON} { YDVAR(1, VAR_TCP_UPSTREAM) }
ssl-upstream{COLON} { YDVAR(1, VAR_SSL_UPSTREAM) }
ssl-service-key{COLON} { YDVAR(1, VAR_SSL_SERVICE_KEY) }
ssl-service-pem{COLON} { YDVAR(1, VAR_SSL_SERVICE_PEM) }
do-daemonize{COLON} { YDVAR(1, VAR_DO_DAEMONIZE) } do-daemonize{COLON} { YDVAR(1, VAR_DO_DAEMONIZE) }
interface{COLON} { YDVAR(1, VAR_INTERFACE) } interface{COLON} { YDVAR(1, VAR_INTERFACE) }
outgoing-interface{COLON} { YDVAR(1, VAR_OUTGOING_INTERFACE) } outgoing-interface{COLON} { YDVAR(1, VAR_OUTGOING_INTERFACE) }

File diff suppressed because it is too large Load diff

View file

@ -156,7 +156,10 @@
VAR_HARDEN_BELOW_NXDOMAIN = 373, VAR_HARDEN_BELOW_NXDOMAIN = 373,
VAR_IGNORE_CD_FLAG = 374, VAR_IGNORE_CD_FLAG = 374,
VAR_LOG_QUERIES = 375, VAR_LOG_QUERIES = 375,
VAR_TCP_UPSTREAM = 376 VAR_TCP_UPSTREAM = 376,
VAR_SSL_UPSTREAM = 377,
VAR_SSL_SERVICE_KEY = 378,
VAR_SSL_SERVICE_PEM = 379
}; };
#endif #endif
/* Tokens. */ /* Tokens. */
@ -279,6 +282,9 @@
#define VAR_IGNORE_CD_FLAG 374 #define VAR_IGNORE_CD_FLAG 374
#define VAR_LOG_QUERIES 375 #define VAR_LOG_QUERIES 375
#define VAR_TCP_UPSTREAM 376 #define VAR_TCP_UPSTREAM 376
#define VAR_SSL_UPSTREAM 377
#define VAR_SSL_SERVICE_KEY 378
#define VAR_SSL_SERVICE_PEM 379
@ -295,7 +301,7 @@ typedef union YYSTYPE
/* Line 1685 of yacc.c */ /* Line 1685 of yacc.c */
#line 299 "util/configparser.h" #line 305 "util/configparser.h"
} YYSTYPE; } YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define yystype YYSTYPE /* obsolescent; will be withdrawn */

View file

@ -102,7 +102,8 @@ extern struct config_parser_state* cfg_parser;
%token VAR_AUTO_TRUST_ANCHOR_FILE VAR_KEEP_MISSING VAR_ADD_HOLDDOWN %token VAR_AUTO_TRUST_ANCHOR_FILE VAR_KEEP_MISSING VAR_ADD_HOLDDOWN
%token VAR_DEL_HOLDDOWN VAR_SO_RCVBUF VAR_EDNS_BUFFER_SIZE VAR_PREFETCH %token VAR_DEL_HOLDDOWN VAR_SO_RCVBUF VAR_EDNS_BUFFER_SIZE VAR_PREFETCH
%token VAR_PREFETCH_KEY VAR_SO_SNDBUF VAR_HARDEN_BELOW_NXDOMAIN %token VAR_PREFETCH_KEY VAR_SO_SNDBUF VAR_HARDEN_BELOW_NXDOMAIN
%token VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES VAR_TCP_UPSTREAM %token VAR_IGNORE_CD_FLAG VAR_LOG_QUERIES VAR_TCP_UPSTREAM VAR_SSL_UPSTREAM
%token VAR_SSL_SERVICE_KEY VAR_SSL_SERVICE_PEM
%% %%
toplevelvars: /* empty */ | toplevelvars toplevelvar ; toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@ -157,7 +158,8 @@ content_server: server_num_threads | server_verbosity | server_port |
server_del_holddown | server_keep_missing | server_so_rcvbuf | server_del_holddown | server_keep_missing | server_so_rcvbuf |
server_edns_buffer_size | server_prefetch | server_prefetch_key | server_edns_buffer_size | server_prefetch | server_prefetch_key |
server_so_sndbuf | server_harden_below_nxdomain | server_ignore_cd_flag | server_so_sndbuf | server_harden_below_nxdomain | server_ignore_cd_flag |
server_log_queries | server_tcp_upstream server_log_queries | server_tcp_upstream | server_ssl_upstream |
server_ssl_service_key | server_ssl_service_pem
; ;
stubstart: VAR_STUB_ZONE stubstart: VAR_STUB_ZONE
{ {
@ -374,6 +376,29 @@ server_tcp_upstream: VAR_TCP_UPSTREAM STRING_ARG
free($2); free($2);
} }
; ;
server_ssl_upstream: VAR_SSL_UPSTREAM STRING_ARG
{
OUTYY(("P(server_ssl_upstream:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->ssl_upstream = (strcmp($2, "yes")==0);
free($2);
}
;
server_ssl_service_key: VAR_SSL_SERVICE_KEY STRING_ARG
{
OUTYY(("P(server_ssl_service_key:%s)\n", $2));
free(cfg_parser->cfg->ssl_service_key);
cfg_parser->cfg->ssl_service_key = $2;
}
;
server_ssl_service_pem: VAR_SSL_SERVICE_PEM STRING_ARG
{
OUTYY(("P(server_ssl_service_pem:%s)\n", $2));
free(cfg_parser->cfg->ssl_service_pem);
cfg_parser->cfg->ssl_service_pem = $2;
}
;
server_do_daemonize: VAR_DO_DAEMONIZE STRING_ARG server_do_daemonize: VAR_DO_DAEMONIZE STRING_ARG
{ {
OUTYY(("P(server_do_daemonize:%s)\n", $2)); OUTYY(("P(server_do_daemonize:%s)\n", $2));

View file

@ -45,6 +45,8 @@
#include "util/module.h" #include "util/module.h"
#include "util/regional.h" #include "util/regional.h"
#include <fcntl.h> #include <fcntl.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
/** max length of an IP address (the address portion) that we allow */ /** max length of an IP address (the address portion) that we allow */
#define MAX_ADDR_STRLEN 128 /* characters */ #define MAX_ADDR_STRLEN 128 /* characters */
@ -553,3 +555,139 @@ void sock_list_merge(struct sock_list** list, struct regional* region,
sock_list_insert(list, &p->addr, p->len, region); sock_list_insert(list, &p->addr, p->len, region);
} }
} }
void
log_crypto_err(const char* str)
{
/* error:[error code]:[library name]:[function name]:[reason string] */
char buf[128];
unsigned long e;
ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
log_err("%s crypto %s", str, buf);
while( (e=ERR_get_error()) ) {
ERR_error_string_n(e, buf, sizeof(buf));
log_err("and additionally crypto %s", buf);
}
}
void* listen_sslctx_create(char* key, char* pem, char* verifypem)
{
SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
if(!ctx) {
log_crypto_err("could not SSL_CTX_new");
return NULL;
}
/* no SSLv2 because has defects */
if(!(SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)){
log_crypto_err("could not set SSL_OP_NO_SSLv2");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_use_certificate_file(ctx, pem, SSL_FILETYPE_PEM)) {
log_err("error for cert file: %s", pem);
log_crypto_err("error in SSL_CTX use_certificate_file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
log_err("error for private key file: %s", key);
log_crypto_err("Error in SSL_CTX use_PrivateKey_file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_check_private_key(ctx)) {
log_err("error for key file: %s", key);
log_crypto_err("Error in SSL_CTX check_private_key");
SSL_CTX_free(ctx);
return NULL;
}
if(verifypem && verifypem[0]) {
if(!SSL_CTX_load_verify_locations(ctx, verifypem, NULL)) {
log_crypto_err("Error in SSL_CTX verify locations");
SSL_CTX_free(ctx);
return NULL;
}
SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(
verifypem));
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
}
return ctx;
}
void* connect_sslctx_create(char* key, char* pem, char* verifypem)
{
SSL_CTX* ctx = SSL_CTX_new(SSLv23_client_method());
if(!ctx) {
log_crypto_err("could not allocate SSL_CTX pointer");
return NULL;
}
if(!(SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)) {
log_crypto_err("could not set SSL_OP_NO_SSLv2");
SSL_CTX_free(ctx);
return NULL;
}
if(key && key[0]) {
if(!SSL_CTX_use_certificate_file(ctx, pem, SSL_FILETYPE_PEM)) {
log_err("error in client certificate %s", pem);
log_crypto_err("error in certificate file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
log_err("error in client private key %s", key);
log_crypto_err("error in key file");
SSL_CTX_free(ctx);
return NULL;
}
if(!SSL_CTX_check_private_key(ctx)) {
log_err("error in client key %s", key);
log_crypto_err("error in SSL_CTX_check_private_key");
SSL_CTX_free(ctx);
return NULL;
}
}
if(verifypem && verifypem[0]) {
if(!SSL_CTX_load_verify_locations(ctx, verifypem, NULL) != 1) {
log_crypto_err("error in SSL_CTX verify");
SSL_CTX_free(ctx);
return NULL;
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
}
return ctx;
}
void* incoming_ssl_fd(void* sslctx, int fd)
{
SSL* ssl = SSL_new((SSL_CTX*)sslctx);
if(!ssl) {
log_crypto_err("could not SSL_new");
return NULL;
}
SSL_set_accept_state(ssl);
(void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
if(!SSL_set_fd(ssl, fd)) {
log_crypto_err("could not SSL_set_fd");
SSL_free(ssl);
return NULL;
}
return ssl;
}
void* outgoing_ssl_fd(void* sslctx, int fd)
{
SSL* ssl = SSL_new((SSL_CTX*)sslctx);
if(!ssl) {
log_crypto_err("could not SSL_new");
return NULL;
}
SSL_set_connect_state(ssl);
(void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
if(!SSL_set_fd(ssl, fd)) {
log_crypto_err("could not SSL_set_fd");
SSL_free(ssl);
return NULL;
}
return ssl;
}

View file

@ -323,4 +323,44 @@ int sock_list_find(struct sock_list* list, struct sockaddr_storage* addr,
void sock_list_merge(struct sock_list** list, struct regional* region, void sock_list_merge(struct sock_list** list, struct regional* region,
struct sock_list* add); struct sock_list* add);
/**
* Log libcrypto error with descriptive string. Calls log_err().
* @param str: what failed.
*/
void log_crypto_err(const char* str);
/**
* create SSL listen context
* @param key: private key file.
* @param pem: public key cert.
* @param verifypem: if nonNULL, verifylocation file.
* return SSL_CTX* or NULL on failure (logged).
*/
void* listen_sslctx_create(char* key, char* pem, char* verifypem);
/**
* create SSL connect context
* @param key: if nonNULL (also pem nonNULL), the client private key.
* @param pem: client public key (or NULL if key is NULL).
* @param verifypem: if nonNULL used for verifylocation file.
* @return SSL_CTX* or NULL on failure (logged).
*/
void* connect_sslctx_create(char* key, char* pem, char* verifypem);
/**
* accept a new fd and wrap it in a BIO in SSL
* @param sslctx: the SSL_CTX to use (from listen_sslctx_create()).
* @param fd: from accept, nonblocking.
* @return SSL or NULL on alloc failure.
*/
void* incoming_ssl_fd(void* sslctx, int fd);
/**
* connect a new fd and wrap it in a BIO in SSL
* @param sslctx: the SSL_CTX to use (from connect_sslctx_create())
* @param fd: from connect.
* @return SSL or NULL on alloc failure
*/
void* outgoing_ssl_fd(void* sslctx, int fd);
#endif /* NET_HELP_H */ #endif /* NET_HELP_H */

View file

@ -44,6 +44,8 @@
#include "util/log.h" #include "util/log.h"
#include "util/net_help.h" #include "util/net_help.h"
#include "util/fptr_wlist.h" #include "util/fptr_wlist.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
/* -------- Start of local definitions -------- */ /* -------- Start of local definitions -------- */
/** if CMSG_ALIGN is not defined on this platform, a workaround */ /** if CMSG_ALIGN is not defined on this platform, a workaround */
@ -683,6 +685,32 @@ int comm_point_perform_accept(struct comm_point* c,
return new_fd; return new_fd;
} }
#ifdef USE_WINSOCK
static long win_bio_cb(BIO *b, int oper, const char* ATTR_UNUSED(argp),
int ATTR_UNUSED(argi), long argl, long retvalue)
{
verbose(VERB_ALGO, "bio_cb %d, %s %s %s", oper,
(oper&BIO_CB_RETURN)?"return":"before",
(oper&BIO_CB_READ)?"read":((oper&BIO_CB_WRITE)?"write":"other"),
WSAGetLastError()==WSAEWOULDBLOCK?"wsawb":"");
/* on windows, check if previous operation caused EWOULDBLOCK */
if( (oper == (BIO_CB_READ|BIO_CB_RETURN) && argl == 0) ||
(oper == (BIO_CB_GETS|BIO_CB_RETURN) && argl == 0)) {
if(WSAGetLastError() == WSAEWOULDBLOCK)
winsock_tcp_wouldblock((struct event*)
BIO_get_callback_arg(b), EV_READ);
}
if( (oper == (BIO_CB_WRITE|BIO_CB_RETURN) && argl == 0) ||
(oper == (BIO_CB_PUTS|BIO_CB_RETURN) && argl == 0)) {
if(WSAGetLastError() == WSAEWOULDBLOCK)
winsock_tcp_wouldblock((struct event*)
BIO_get_callback_arg(b), EV_WRITE);
}
/* return original return value */
return retvalue;
}
#endif
void void
comm_point_tcp_accept_callback(int fd, short event, void* arg) comm_point_tcp_accept_callback(int fd, short event, void* arg)
{ {
@ -706,6 +734,21 @@ comm_point_tcp_accept_callback(int fd, short event, void* arg)
&c_hdl->repinfo.addrlen); &c_hdl->repinfo.addrlen);
if(new_fd == -1) if(new_fd == -1)
return; return;
if(c->ssl) {
c_hdl->ssl = incoming_ssl_fd(c->ssl, new_fd);
if(!c_hdl->ssl)
return;
c_hdl->ssl_shake_state = comm_ssl_shake_read;
#ifdef USE_WINSOCK
/* set them both just in case, but usually they are the same BIO */
BIO_set_callback(SSL_get_rbio(c_hdl->ssl), &win_bio_cb);
BIO_set_callback_arg(SSL_get_rbio(c_hdl->ssl),
(char*)comm_point_internal(c_hdl));
BIO_set_callback(SSL_get_wbio(c_hdl->ssl), &win_bio_cb);
BIO_set_callback_arg(SSL_get_wbio(c_hdl->ssl),
(char*)comm_point_internal(c_hdl));
#endif
}
/* grab the tcp handler buffers */ /* grab the tcp handler buffers */
c->tcp_free = c_hdl->tcp_free; c->tcp_free = c_hdl->tcp_free;
@ -722,6 +765,11 @@ static void
reclaim_tcp_handler(struct comm_point* c) reclaim_tcp_handler(struct comm_point* c)
{ {
log_assert(c->type == comm_tcp); log_assert(c->type == comm_tcp);
if(c->ssl) {
SSL_shutdown(c->ssl);
SSL_free(c->ssl);
c->ssl = NULL;
}
comm_point_close(c); comm_point_close(c);
if(c->tcp_parent) { if(c->tcp_parent) {
c->tcp_free = c->tcp_parent->tcp_free; c->tcp_free = c->tcp_parent->tcp_free;
@ -764,6 +812,231 @@ tcp_callback_reader(struct comm_point* c)
} }
} }
/** continue ssl handshake */
static int
ssl_handshake(struct comm_point* c)
{
int r;
if(c->ssl_shake_state == comm_ssl_shake_hs_read) {
/* read condition satisfied back to writing */
comm_point_listen_for_rw(c, 1, 1);
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}
if(c->ssl_shake_state == comm_ssl_shake_hs_write) {
/* write condition satisfied, back to reading */
comm_point_listen_for_rw(c, 1, 0);
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}
ERR_clear_error();
r = SSL_do_handshake(c->ssl);
if(r != 1) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_WANT_READ) {
if(c->ssl_shake_state == comm_ssl_shake_read)
return 1;
c->ssl_shake_state = comm_ssl_shake_read;
comm_point_listen_for_rw(c, 1, 0);
return 1;
} else if(want == SSL_ERROR_WANT_WRITE) {
if(c->ssl_shake_state == comm_ssl_shake_write)
return 1;
c->ssl_shake_state = comm_ssl_shake_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(r == 0) {
return 0; /* closed */
} else if(want == SSL_ERROR_SYSCALL) {
/* SYSCALL and errno==0 means closed uncleanly */
if(errno != 0)
log_err("SSL_handshake syscall: %s",
strerror(errno));
return 0;
} else {
log_crypto_err("ssl handshake failed");
log_addr(1, "ssl handshake failed", &c->repinfo.addr,
c->repinfo.addrlen);
return 0;
}
}
/* this is where peer verification could take place */
log_addr(VERB_ALGO, "SSL connection from", &c->repinfo.addr,
c->repinfo.addrlen);
/* setup listen rw correctly */
if(c->tcp_is_reading) {
if(c->ssl_shake_state != comm_ssl_shake_read)
comm_point_listen_for_rw(c, 1, 0);
} else {
comm_point_listen_for_rw(c, 1, 1);
}
c->ssl_shake_state = comm_ssl_shake_none;
return 1;
}
/** ssl read callback on TCP */
static int
ssl_handle_read(struct comm_point* c)
{
int r;
if(c->ssl_shake_state != comm_ssl_shake_none) {
if(!ssl_handshake(c))
return 0;
if(c->ssl_shake_state != comm_ssl_shake_none)
return 1;
}
if(c->tcp_byte_count < sizeof(uint16_t)) {
/* read length bytes */
ERR_clear_error();
if((r=SSL_read(c->ssl, (void*)ldns_buffer_at(c->buffer,
c->tcp_byte_count), (int)(sizeof(uint16_t) -
c->tcp_byte_count))) <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* shutdown, closed */
} else if(want == SSL_ERROR_WANT_READ) {
return 1; /* read more later */
} else if(want == SSL_ERROR_WANT_WRITE) {
c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_read");
return 0;
}
c->tcp_byte_count += r;
if(c->tcp_byte_count != sizeof(uint16_t))
return 1;
if(ldns_buffer_read_u16_at(c->buffer, 0) >
ldns_buffer_capacity(c->buffer)) {
verbose(VERB_QUERY, "ssl: dropped larger than buffer");
return 0;
}
ldns_buffer_set_limit(c->buffer,
ldns_buffer_read_u16_at(c->buffer, 0));
if(ldns_buffer_limit(c->buffer) < LDNS_HEADER_SIZE) {
verbose(VERB_QUERY, "ssl: dropped bogus too short.");
return 0;
}
verbose(VERB_ALGO, "Reading ssl tcp query of length %d",
(int)ldns_buffer_limit(c->buffer));
}
log_assert(ldns_buffer_remaining(c->buffer) > 0);
ERR_clear_error();
r = SSL_read(c->ssl, (void*)ldns_buffer_current(c->buffer),
(int)ldns_buffer_remaining(c->buffer));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* shutdown, closed */
} else if(want == SSL_ERROR_WANT_READ) {
return 1; /* read more later */
} else if(want == SSL_ERROR_WANT_WRITE) {
c->ssl_shake_state = comm_ssl_shake_hs_write;
comm_point_listen_for_rw(c, 0, 1);
return 1;
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_read syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_read");
return 0;
}
ldns_buffer_skip(c->buffer, (ssize_t)r);
if(ldns_buffer_remaining(c->buffer) <= 0) {
tcp_callback_reader(c);
}
return 1;
}
/** ssl write callback on TCP */
static int
ssl_handle_write(struct comm_point* c)
{
int r;
if(c->ssl_shake_state != comm_ssl_shake_none) {
if(!ssl_handshake(c))
return 0;
if(c->ssl_shake_state != comm_ssl_shake_none)
return 1;
}
/* ignore return, if fails we may simply block */
(void)SSL_set_mode(c->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
if(c->tcp_byte_count < sizeof(uint16_t)) {
uint16_t len = htons(ldns_buffer_limit(c->buffer));
ERR_clear_error();
r = SSL_write(c->ssl,
(void*)(((uint8_t*)&len)+c->tcp_byte_count),
(int)(sizeof(uint16_t)-c->tcp_byte_count));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* closed */
} else if(want == SSL_ERROR_WANT_READ) {
c->ssl_shake_state = comm_ssl_shake_read;
comm_point_listen_for_rw(c, 1, 0);
return 1; /* wait for read condition */
} else if(want == SSL_ERROR_WANT_WRITE) {
return 1; /* write more later */
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_write syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_write");
return 0;
}
c->tcp_byte_count += r;
if(c->tcp_byte_count < sizeof(uint16_t))
return 1;
ldns_buffer_set_position(c->buffer, c->tcp_byte_count -
sizeof(uint16_t));
if(ldns_buffer_remaining(c->buffer) == 0) {
tcp_callback_writer(c);
return 1;
}
}
log_assert(ldns_buffer_remaining(c->buffer) > 0);
ERR_clear_error();
r = SSL_write(c->ssl, (void*)ldns_buffer_current(c->buffer),
(int)ldns_buffer_remaining(c->buffer));
if(r <= 0) {
int want = SSL_get_error(c->ssl, r);
if(want == SSL_ERROR_ZERO_RETURN) {
return 0; /* closed */
} else if(want == SSL_ERROR_WANT_READ) {
c->ssl_shake_state = comm_ssl_shake_read;
comm_point_listen_for_rw(c, 1, 0);
return 1; /* wait for read condition */
} else if(want == SSL_ERROR_WANT_WRITE) {
return 1; /* write more later */
} else if(want == SSL_ERROR_SYSCALL) {
if(errno != 0)
log_err("SSL_write syscall: %s",
strerror(errno));
return 0;
}
log_crypto_err("could not SSL_write");
return 0;
}
ldns_buffer_skip(c->buffer, (ssize_t)r);
if(ldns_buffer_remaining(c->buffer) == 0) {
tcp_callback_writer(c);
}
return 1;
}
/** Handle tcp reading callback. /** Handle tcp reading callback.
* @param fd: file descriptor of socket. * @param fd: file descriptor of socket.
* @param c: comm point to read from into buffer. * @param c: comm point to read from into buffer.
@ -777,6 +1050,8 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
log_assert(c->type == comm_tcp || c->type == comm_local); log_assert(c->type == comm_tcp || c->type == comm_local);
if(!c->tcp_is_reading) if(!c->tcp_is_reading)
return 0; return 0;
if(c->ssl)
return ssl_handle_read(c);
log_assert(fd != -1); log_assert(fd != -1);
if(c->tcp_byte_count < sizeof(uint16_t)) { if(c->tcp_byte_count < sizeof(uint16_t)) {
@ -915,6 +1190,8 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
return 0; return 0;
} }
} }
if(c->ssl)
return ssl_handle_write(c);
if(c->tcp_byte_count < sizeof(uint16_t)) { if(c->tcp_byte_count < sizeof(uint16_t)) {
uint16_t len = htons(ldns_buffer_limit(c->buffer)); uint16_t len = htons(ldns_buffer_limit(c->buffer));
@ -1476,6 +1753,10 @@ comm_point_delete(struct comm_point* c)
{ {
if(!c) if(!c)
return; return;
if(c->type == comm_tcp && c->ssl) {
SSL_shutdown(c->ssl);
SSL_free(c->ssl);
}
comm_point_close(c); comm_point_close(c);
if(c->tcp_handlers) { if(c->tcp_handlers) {
int i; int i;

View file

@ -160,6 +160,23 @@ struct comm_point {
For tcp_accept the first entry, for tcp_handlers the next one. */ For tcp_accept the first entry, for tcp_handlers the next one. */
struct comm_point* tcp_free; struct comm_point* tcp_free;
/* -------- SSL TCP DNS ------- */
/** the SSL object with rw bio (owned) or for commaccept ctx ref */
void* ssl;
/** handshake state for init and renegotiate */
enum {
/** no handshake, it has been done */
comm_ssl_shake_none = 0,
/** ssl initial handshake wants to read */
comm_ssl_shake_read,
/** ssl initial handshake wants to write */
comm_ssl_shake_write,
/** ssl_write wants to read */
comm_ssl_shake_hs_read,
/** ssl_read wants to write */
comm_ssl_shake_hs_write
} ssl_shake_state;
/** is this a UDP, TCP-accept or TCP socket. */ /** is this a UDP, TCP-accept or TCP socket. */
enum comm_point_type { enum comm_point_type {
/** UDP socket - handle datagrams. */ /** UDP socket - handle datagrams. */