mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-01-03 13:29:36 -05:00
control channel security.
git-svn-id: file:///svn/unbound/trunk@1229 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
parent
56b91454ba
commit
af57e5163d
13 changed files with 664 additions and 17 deletions
19
Makefile.in
19
Makefile.in
|
|
@ -76,6 +76,8 @@ DAEMON_SRC=$(patsubst $(srcdir)/%,%, $(wildcard $(srcdir)/daemon/*.c)) \
|
|||
DAEMON_OBJ=$(addprefix $(BUILD),$(DAEMON_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
CHECKCONF_SRC=smallapp/unbound-checkconf.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
CHECKCONF_OBJ=$(addprefix $(BUILD),$(CHECKCONF_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
CONTROL_SRC=smallapp/unbound-control.c smallapp/worker_cb.c $(COMMON_SRC)
|
||||
CONTROL_OBJ=$(addprefix $(BUILD),$(CONTROL_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
HOST_SRC=smallapp/unbound-host.c
|
||||
HOST_OBJ=$(addprefix $(BUILD),$(HOST_SRC:.c=.lo)) $(COMPAT_OBJ)
|
||||
TESTBOUND_SRC=testcode/testbound.c testcode/ldns-testpkts.c \
|
||||
|
|
@ -109,7 +111,7 @@ ALL_SRC=$(sort $(COMMON_SRC) $(UNITTEST_SRC) $(DAEMON_SRC) \
|
|||
$(TESTBOUND_SRC) $(LOCKVERIFY_SRC) $(PKTVIEW_SRC) $(SIGNIT_SRC) \
|
||||
$(MEMSTATS_SRC) $(CHECKCONF_SRC) $(LIBUNBOUND_SRC) $(HOST_SRC) \
|
||||
$(ASYNCLOOK_SRC) $(STREAMTCP_SRC) $(PERF_SRC) $(DELAYER_SRC) \
|
||||
$(HARVEST_SRC) )
|
||||
$(HARVEST_SRC) $(CONTROL_SRC))
|
||||
ALL_OBJ=$(addprefix $(BUILD),$(ALL_SRC:.c=.lo) \
|
||||
$(addprefix compat/,$(LIBOBJS:.o=.lo))) $(COMPAT_OBJ)
|
||||
|
||||
|
|
@ -126,7 +128,7 @@ $(BUILD)%.lo: $(srcdir)/%.c
|
|||
@-if test ! -d $(dir $@); then $(INSTALL) -d $(patsubst %/,%,$(dir $@)); fi
|
||||
$Q$(COMPILE) -c $< -o $@
|
||||
|
||||
all: $(COMMON_OBJ) unbound unbound-checkconf lib unbound-host
|
||||
all: $(COMMON_OBJ) unbound unbound-checkconf lib unbound-host unbound-control
|
||||
|
||||
TEST_BIN=asynclook delayer harvest lock-verify memstats perf pktview signit \
|
||||
streamtcp testbound unittest
|
||||
|
|
@ -158,6 +160,10 @@ unbound-checkconf: $(CHECKCONF_OBJ) $(ldnslib)
|
|||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(CHECKCONF_OBJ)) $(LIBS)
|
||||
|
||||
unbound-control: $(CONTROL_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(CONTROL_OBJ)) -lssl $(LIBS)
|
||||
|
||||
unbound-host: $(HOST_OBJ) libunbound.la $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(HOST_OBJ)) -L. -L.libs -lunbound $(LIBS)
|
||||
|
|
@ -168,7 +174,7 @@ unittest: $(UNITTEST_OBJ) $(ldnslib)
|
|||
|
||||
testbound: $(TESTBOUND_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
$Q$(LINK) -o $@ $(sort $(TESTBOUND_OBJ)) $(LIBS)
|
||||
$Q$(LINK) -o $@ $(sort $(TESTBOUND_OBJ)) -lssl $(LIBS)
|
||||
|
||||
lock-verify: $(LOCKVERIFY_OBJ) $(ldnslib)
|
||||
$(INFO) Link $@
|
||||
|
|
@ -258,6 +264,7 @@ doc:
|
|||
strip:
|
||||
strip unbound
|
||||
strip unbound-checkconf
|
||||
strip unbound-control
|
||||
strip unbound-host
|
||||
|
||||
install:
|
||||
|
|
@ -271,9 +278,11 @@ install:
|
|||
$(INSTALL) -m 755 -d $(DESTDIR)$(includedir)
|
||||
$(LIBTOOL) --mode=install cp unbound $(DESTDIR)$(sbindir)/unbound
|
||||
$(LIBTOOL) --mode=install cp unbound-checkconf $(DESTDIR)$(sbindir)/unbound-checkconf
|
||||
$(LIBTOOL) --mode=install cp unbound-control $(DESTDIR)$(sbindir)/unbound-control
|
||||
$(LIBTOOL) --mode=install cp unbound-host $(DESTDIR)$(sbindir)/unbound-host
|
||||
$(INSTALL) -c -m 644 doc/unbound.8 $(DESTDIR)$(mandir)/man8
|
||||
$(INSTALL) -c -m 644 doc/unbound-checkconf.8 $(DESTDIR)$(mandir)/man8
|
||||
$(INSTALL) -c -m 644 doc/unbound-control.8 $(DESTDIR)$(mandir)/man8
|
||||
$(INSTALL) -c -m 644 doc/unbound.conf.5 $(DESTDIR)$(mandir)/man5
|
||||
$(INSTALL) -c -m 644 $(srcdir)/doc/unbound-host.1 $(DESTDIR)$(mandir)/man1
|
||||
$(INSTALL) -c -m 644 doc/libunbound.3 $(DESTDIR)$(mandir)/man3
|
||||
|
|
@ -283,8 +292,8 @@ install:
|
|||
$(LIBTOOL) --mode=finish $(DESTDIR)$(libdir)
|
||||
|
||||
uninstall:
|
||||
rm -f -- $(DESTDIR)$(sbindir)/unbound $(DESTDIR)$(sbindir)/unbound-checkconf $(DESTDIR)$(sbindir)/unbound-host
|
||||
rm -f -- $(DESTDIR)$(mandir)/man8/unbound.8 $(DESTDIR)$(mandir)/man8/unbound-checkconf.8 $(DESTDIR)$(mandir)/man5/unbound.conf.5
|
||||
rm -f -- $(DESTDIR)$(sbindir)/unbound $(DESTDIR)$(sbindir)/unbound-checkconf $(DESTDIR)$(sbindir)/unbound-host $(DESTDIR)$(sbindir)/unbound-control
|
||||
rm -f -- $(DESTDIR)$(mandir)/man8/unbound.8 $(DESTDIR)$(mandir)/man8/unbound-checkconf.8 $(DESTDIR)$(mandir)/man5/unbound.conf.5 $(DESTDIR)$(mandir)/man8/unbound-control.8
|
||||
rm -f -- $(DESTDIR)$(mandir)/man1/unbound-host.1 $(DESTDIR)$(mandir)/man3/libunbound.3
|
||||
rm -f -- $(DESTDIR)$(includedir)/unbound.h
|
||||
$(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/libunbound.la
|
||||
|
|
|
|||
3
configure
vendored
3
configure
vendored
|
|
@ -25788,7 +25788,7 @@ _ACEOF
|
|||
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-checkconf.8 doc/unbound.conf.5"
|
||||
ac_config_files="$ac_config_files Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-checkconf.8 doc/unbound.conf.5 doc/unbound-control.8"
|
||||
|
||||
ac_config_headers="$ac_config_headers config.h"
|
||||
|
||||
|
|
@ -26350,6 +26350,7 @@ do
|
|||
"doc/unbound.8") CONFIG_FILES="$CONFIG_FILES doc/unbound.8" ;;
|
||||
"doc/unbound-checkconf.8") CONFIG_FILES="$CONFIG_FILES doc/unbound-checkconf.8" ;;
|
||||
"doc/unbound.conf.5") CONFIG_FILES="$CONFIG_FILES doc/unbound.conf.5" ;;
|
||||
"doc/unbound-control.8") CONFIG_FILES="$CONFIG_FILES doc/unbound-control.8" ;;
|
||||
"config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
|
||||
|
||||
*) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
|
||||
|
|
|
|||
|
|
@ -1056,6 +1056,6 @@ void *unbound_stat_realloc_log(void *ptr, size_t size, const char* file,
|
|||
#define UNBOUND_DNS_PORT 53
|
||||
])
|
||||
|
||||
AC_CONFIG_FILES([Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-checkconf.8 doc/unbound.conf.5])
|
||||
AC_CONFIG_FILES([Makefile doc/example.conf doc/libunbound.3 doc/unbound.8 doc/unbound-checkconf.8 doc/unbound.conf.5 doc/unbound-control.8])
|
||||
AC_CONFIG_HEADER([config.h])
|
||||
AC_OUTPUT
|
||||
|
|
|
|||
|
|
@ -164,6 +164,9 @@ daemon_init()
|
|||
signal_handling_record();
|
||||
checklock_start();
|
||||
ERR_load_crypto_strings();
|
||||
ERR_load_SSL_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
(void)SSL_library_init();
|
||||
#ifdef HAVE_TZSET
|
||||
/* init timezone info while we are not chrooted yet */
|
||||
tzset();
|
||||
|
|
|
|||
167
daemon/remote.c
167
daemon/remote.c
|
|
@ -45,6 +45,7 @@
|
|||
#include "config.h"
|
||||
#include "daemon/remote.h"
|
||||
#include "daemon/worker.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/net_help.h"
|
||||
|
|
@ -57,9 +58,27 @@
|
|||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
/** log ssl crypto err */
|
||||
static void
|
||||
log_crypto_err(const char* str)
|
||||
{
|
||||
/* error:[error code]:[library name]:[function name]:[reason string] */
|
||||
char buf[128];
|
||||
int 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);
|
||||
}
|
||||
}
|
||||
|
||||
struct daemon_remote*
|
||||
daemon_remote_create(struct worker* worker)
|
||||
{
|
||||
char* s_cert;
|
||||
char* s_key;
|
||||
struct config_file* cfg = worker->daemon->cfg;
|
||||
struct daemon_remote* rc = (struct daemon_remote*)calloc(1,
|
||||
sizeof(*rc));
|
||||
if(!rc) {
|
||||
|
|
@ -68,7 +87,43 @@ daemon_remote_create(struct worker* worker)
|
|||
}
|
||||
rc->worker = worker;
|
||||
rc->max_active = 10;
|
||||
/* TODO setup the context */
|
||||
|
||||
rc->ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
if(!rc->ctx) {
|
||||
log_crypto_err("could not SSL_CTX_new");
|
||||
free(rc);
|
||||
return NULL;
|
||||
}
|
||||
/* no SSLv2 because has defects */
|
||||
if(!(SSL_CTX_set_options(rc->ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)){
|
||||
log_crypto_err("could not set SSL_OP_NO_SSLv2");
|
||||
daemon_remote_delete(rc);
|
||||
return NULL;
|
||||
}
|
||||
s_cert = cfg->server_cert_file;
|
||||
s_key = cfg->server_key_file;
|
||||
if(cfg->chrootdir && cfg->chrootdir[0]) {
|
||||
if(strncmp(s_cert, cfg->chrootdir, strlen(cfg->chrootdir))==0)
|
||||
s_cert += strlen(cfg->chrootdir);
|
||||
if(strncmp(s_key, cfg->chrootdir, strlen(cfg->chrootdir))==0)
|
||||
s_key += strlen(cfg->chrootdir);
|
||||
}
|
||||
verbose(VERB_ALGO, "setup SSL certificates");
|
||||
if (!SSL_CTX_use_certificate_file(rc->ctx,s_cert,SSL_FILETYPE_PEM)
|
||||
|| !SSL_CTX_use_PrivateKey_file(rc->ctx,s_key,SSL_FILETYPE_PEM)
|
||||
|| !SSL_CTX_check_private_key(rc->ctx)) {
|
||||
log_crypto_err("Error setting up SSL_CTX key and cert");
|
||||
daemon_remote_delete(rc);
|
||||
return NULL;
|
||||
}
|
||||
if(!SSL_CTX_load_verify_locations(rc->ctx, s_cert, NULL)) {
|
||||
log_crypto_err("Error setting up SSL_CTX verify locations");
|
||||
daemon_remote_delete(rc);
|
||||
return NULL;
|
||||
}
|
||||
SSL_CTX_set_client_CA_list(rc->ctx, SSL_load_client_CA_file(s_cert));
|
||||
SSL_CTX_set_verify(rc->ctx, SSL_VERIFY_PEER, NULL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -256,13 +311,37 @@ int remote_accept_callback(struct comm_point* c, void* arg, int err,
|
|||
return 0;
|
||||
}
|
||||
log_addr(VERB_QUERY, "new control connection from", &addr, addrlen);
|
||||
n->c->do_not_close = 0;
|
||||
comm_point_stop_listening(n->c);
|
||||
comm_point_start_listening(n->c, -1, REMOTE_CONTROL_TCP_TIMEOUT);
|
||||
memcpy(&n->c->repinfo.addr, &addr, addrlen);
|
||||
n->c->repinfo.addrlen = addrlen;
|
||||
/* TODO setup ssl, assign fd */
|
||||
n->shake_state = rc_hs_read;
|
||||
n->ssl = SSL_new(rc->ctx);
|
||||
if(!n->ssl) {
|
||||
log_crypto_err("could not SSL_new");
|
||||
close(newfd);
|
||||
free(n);
|
||||
return 0;
|
||||
}
|
||||
SSL_set_accept_state(n->ssl);
|
||||
(void)SSL_set_mode(n->ssl, SSL_MODE_AUTO_RETRY);
|
||||
if(!SSL_set_fd(n->ssl, newfd)) {
|
||||
log_crypto_err("could not SSL_set_fd");
|
||||
close(newfd);
|
||||
SSL_free(n->ssl);
|
||||
free(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
n->rc = rc;
|
||||
n->next = rc->busy_list;
|
||||
rc->busy_list = n;
|
||||
rc->active ++;
|
||||
|
||||
/* perform the first nonblocking read already, for windows,
|
||||
* so it can return wouldblock. could be faster too. */
|
||||
(void)remote_control_callback(n->c, n, NETEVENT_NOERROR, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -285,26 +364,106 @@ clean_point(struct daemon_remote* rc, struct rc_state* s)
|
|||
{
|
||||
state_list_remove_elem(&rc->busy_list, s->c);
|
||||
rc->active --;
|
||||
if(s->ssl)
|
||||
if(s->ssl) {
|
||||
SSL_shutdown(s->ssl);
|
||||
SSL_free(s->ssl);
|
||||
}
|
||||
comm_point_delete(s->c);
|
||||
free(s);
|
||||
}
|
||||
|
||||
/** handle remote control request */
|
||||
static void
|
||||
handle_req(struct daemon_remote* rc, struct rc_state* s, SSL* ssl)
|
||||
{
|
||||
char* msg = "HTTP/1.0 200 OK\r\nContent-type: text/plain\r\n\r\n"
|
||||
"unbound server control channel\n";
|
||||
int r;
|
||||
char buf[1024];
|
||||
fd_set_block(s->c->fd);
|
||||
|
||||
ERR_clear_error();
|
||||
if((r=SSL_read(ssl, buf, (int)sizeof(buf)-1)) <= 0) {
|
||||
if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN)
|
||||
return;
|
||||
log_crypto_err("could not SSL_read");
|
||||
return;
|
||||
}
|
||||
buf[r] = 0;
|
||||
log_info("got '%s'", buf);
|
||||
|
||||
ERR_clear_error();
|
||||
if((r=SSL_write(ssl, msg, (int)strlen(msg))) <= 0) {
|
||||
if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN)
|
||||
return;
|
||||
log_crypto_err("could not SSL_write");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int remote_control_callback(struct comm_point* c, void* arg, int err,
|
||||
struct comm_reply* ATTR_UNUSED(rep))
|
||||
{
|
||||
struct rc_state* s = (struct rc_state*)arg;
|
||||
struct daemon_remote* rc = s->rc;
|
||||
int r;
|
||||
if(err != NETEVENT_NOERROR) {
|
||||
if(err==NETEVENT_TIMEOUT)
|
||||
log_err("remote control timed out");
|
||||
clean_point(rc, s);
|
||||
return 0;
|
||||
}
|
||||
/* TODO (continue to) setup the SSL connection */
|
||||
/* (continue to) setup the SSL connection */
|
||||
ERR_clear_error();
|
||||
r = SSL_do_handshake(s->ssl);
|
||||
if(r != 1) {
|
||||
r = SSL_get_error(s->ssl, r);
|
||||
if(r == SSL_ERROR_WANT_READ) {
|
||||
if(s->shake_state == rc_hs_read) {
|
||||
/* try again later */
|
||||
return 0;
|
||||
}
|
||||
s->shake_state = rc_hs_read;
|
||||
comm_point_listen_for_rw(c, 1, 0);
|
||||
return 0;
|
||||
} else if(r == SSL_ERROR_WANT_WRITE) {
|
||||
if(s->shake_state == rc_hs_write) {
|
||||
/* try again later */
|
||||
return 0;
|
||||
}
|
||||
s->shake_state = rc_hs_write;
|
||||
comm_point_listen_for_rw(c, 0, 1);
|
||||
return 0;
|
||||
} else {
|
||||
log_crypto_err("remote control failed ssl");
|
||||
clean_point(rc, s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
s->shake_state = rc_none;
|
||||
|
||||
/* once handshake has completed, check authentication */
|
||||
if(SSL_get_verify_result(s->ssl) == X509_V_OK) {
|
||||
X509* x = SSL_get_peer_certificate(s->ssl);
|
||||
if(!x) {
|
||||
verbose(VERB_DETAIL, "remote control connection "
|
||||
"provided no client certificate");
|
||||
clean_point(rc, s);
|
||||
return 0;
|
||||
}
|
||||
verbose(VERB_ALGO, "remote control connection authenticated");
|
||||
X509_free(x);
|
||||
} else {
|
||||
verbose(VERB_DETAIL, "remote control connection failed to "
|
||||
"authenticate with client certificate");
|
||||
clean_point(rc, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if OK start to actually handle the request */
|
||||
handle_req(rc, s, s->ssl);
|
||||
|
||||
verbose(VERB_ALGO, "remote control operation completed");
|
||||
clean_point(rc, s);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ struct comm_reply;
|
|||
struct comm_point;
|
||||
struct daemon_remote;
|
||||
|
||||
/** number of seconds timeout on incoming remote control handshake */
|
||||
#define REMOTE_CONTROL_TCP_TIMEOUT 120
|
||||
|
||||
/**
|
||||
* a busy control command connection, SSL state
|
||||
*/
|
||||
|
|
@ -61,6 +64,8 @@ struct rc_state {
|
|||
struct rc_state* next;
|
||||
/** the commpoint */
|
||||
struct comm_point* c;
|
||||
/** in the handshake part */
|
||||
enum { rc_none, rc_hs_read, rc_hs_write } shake_state;
|
||||
/** the ssl state */
|
||||
SSL* ssl;
|
||||
/** the rc this is part of */
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
11 September 2008: Wouter
|
||||
- set nonblocking on new TCP streams, because linux does not inherit
|
||||
the socket options to the accepted socket.
|
||||
- fix TCP timeouts.
|
||||
- SSL protected connection between server and unbound-control.
|
||||
|
||||
10 September 2008: Wouter
|
||||
- remove memleak in privacy addresses on reloads and quits.
|
||||
- remote control work.
|
||||
|
|
|
|||
|
|
@ -383,13 +383,13 @@ remote-control:
|
|||
# server-key-file: "@UNBOUND_RUN_DIR@/unbound_server.key"
|
||||
|
||||
# unbound server certificate file.
|
||||
# server-key-file: "@UNBOUND_RUN_DIR@/unbound_server.pem"
|
||||
# server-cert-file: "@UNBOUND_RUN_DIR@/unbound_server.pem"
|
||||
|
||||
# unbound-control key file.
|
||||
# control-key-file: "@UNBOUND_RUN_DIR@/unbound_control.key"
|
||||
|
||||
# unbound-control certificate file.
|
||||
# control-key-file: "@UNBOUND_RUN_DIR@/unbound_control.pem"
|
||||
# control-cert-file: "@UNBOUND_RUN_DIR@/unbound_control.pem"
|
||||
|
||||
# Stub zones.
|
||||
# Create entries like below, to make all queries for 'example.com' and
|
||||
|
|
|
|||
81
doc/unbound-control.8.in
Normal file
81
doc/unbound-control.8.in
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
.TH "unbound-control" "8" "@date@" "NLnet Labs" "unbound @version@"
|
||||
.\"
|
||||
.\" unbound-control.8 -- unbound remote control manual
|
||||
.\"
|
||||
.\" Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
.\"
|
||||
.\" See LICENSE for the license.
|
||||
.\"
|
||||
.\"
|
||||
.SH "NAME"
|
||||
.LP
|
||||
unbound-control
|
||||
\- Unbound remote server control utility.
|
||||
.SH "SYNOPSIS"
|
||||
.B unbound-control
|
||||
.RB [ \-h ]
|
||||
.RB [ \-c
|
||||
.IR cfgfile ]
|
||||
.RB [ \-s
|
||||
.IR server ]
|
||||
.IR command
|
||||
.SH "DESCRIPTION"
|
||||
.B Unbound-control
|
||||
Performs remote administration on the \fIunbound\fR(8) DNS server.
|
||||
It reads the configuration file, contacts the unbound server over SSL
|
||||
sends the command and displays the result.
|
||||
.P
|
||||
The available options are:
|
||||
.TP
|
||||
.B \-h
|
||||
Show the version and commandline option help.
|
||||
.TP
|
||||
.B \-c \fIcfgfile
|
||||
The config file to read with settings. If not given the default
|
||||
config file @ub_conf_file@ is used.
|
||||
.TP
|
||||
.B \-s \fIserver[@port]
|
||||
IPv4 or IPv6 address of the server to contact. If not given, the
|
||||
address is read from the config file.
|
||||
.SH "COMMANDS"
|
||||
There are several commands that the server understands.
|
||||
.TP
|
||||
.B start
|
||||
Start the server. Simply execs \fIunbound\fR(8).
|
||||
.TP
|
||||
.B stop
|
||||
Stop the server.
|
||||
.TP
|
||||
.B reload
|
||||
Reload the server.
|
||||
.SH "EXIT CODE"
|
||||
The unbound-control program exits with status code 1 on error.
|
||||
.SH "SET UP"
|
||||
The setup requires a self\-signed certificate and private keys for both
|
||||
the server and client. The script \fIunbound\-control\-setup\fR generates
|
||||
these in the default run directory, or with \-d in another directory.
|
||||
The script preserves private keys present in the directory.
|
||||
After running the script as root, turn on \fBcontrol-enable\fR in
|
||||
\fIunbound.conf\fR.
|
||||
.SH "BROWSER SUPPORT"
|
||||
It is also possible to administer via a browser. The client key needs
|
||||
to be loaded into the browser, the setup script (see above) has generated
|
||||
the file \fIunbound_control_browser.pfx\fR, with the client key and
|
||||
certificate. By default it is stored with an empty password.
|
||||
This can be loaded into a web browser, say Firefox, in the preferences \-
|
||||
advanced \- encryption \- view certificates \- your certs window.
|
||||
Then connect to the server control port (https://localhost:953) and
|
||||
create a security override to accept the self-signed certificate from
|
||||
the unbound server.
|
||||
.SH "FILES"
|
||||
.TP
|
||||
.I @ub_conf_file@
|
||||
unbound configuration file.
|
||||
.TP
|
||||
.I @UNBOUND_RUN_DIR@
|
||||
directory with private keys (unbound_server.key and unbound_control.key),
|
||||
self-signed certificates (unbound_server.pem and unbound_control.pem) and
|
||||
unbound_control_browser.pfx file.
|
||||
.SH "SEE ALSO"
|
||||
\fIunbound.conf\fR(5),
|
||||
\fIunbound\fR(8).
|
||||
|
|
@ -670,6 +670,52 @@ local\-data: 'example. TXT "text"'.
|
|||
If you need more complicated authoritative data, with referrals, wildcards,
|
||||
CNAME/DNAME support, or DNSSEC authoritative service, setup a stub\-zone for
|
||||
it as detailed in the stub zone section below.
|
||||
.SS "Remote Control Options"
|
||||
In the
|
||||
.B remote\-control:
|
||||
clause are the declarations for the remote control facility. If this is
|
||||
enabled, the \fIunbound\-control\fR(8) utility can be used to send
|
||||
commands to the running unbound server. The server uses these clauses
|
||||
to setup SSLv3 / TLSv1 security for the connection. The
|
||||
\fIunbound\-control\fR(8) utility also reads the \fBremote\-control\fR
|
||||
section for options. To setup the correct self-signed certificates use the
|
||||
\fIunbound\-control\-setup\fR(8) utility.
|
||||
.TP 5
|
||||
.B control\-enable: \fI<yes or no>
|
||||
The option is used to enable remote control, default is "no".
|
||||
If turned off, the server does not listen for control commands.
|
||||
.TP 5
|
||||
.B control\-interface: <ip address>
|
||||
Give IPv4 or IPv6 addresses to listen on for control commands.
|
||||
By default localhost (127.0.0.1 and ::1) is listened to.
|
||||
Use 0.0.0.0 and ::0 to listen to all interfaces.
|
||||
.TP 5
|
||||
.B control\-port: <port number>
|
||||
The port number to listen on for control commands, default is 953
|
||||
(that is the same port number named uses to listen to rndc).
|
||||
If you change this port number, and permissions have been dropped, a
|
||||
reload is not sufficient to open the port again, you must then restart.
|
||||
.TP 5
|
||||
.B server\-key\-file: "<private key file>"
|
||||
Path to the server private key, by default unbound_server.key.
|
||||
This file is generated by the \fIunbound\-control\-setup\fR utility.
|
||||
This file is used by the unbound server, but not by \fIunbound\-control\fR.
|
||||
.TP 5
|
||||
.B server\-cert\-file: "<certificate file.pem>"
|
||||
Path to the server self signed certificate, by default unbound_server.pem.
|
||||
This file is generated by the \fIunbound\-control\-setup\fR utility.
|
||||
This file is used by the unbound server, and also by \fIunbound\-control\fR.
|
||||
.TP 5
|
||||
.B control\-key\-file: "<private key file>"
|
||||
Path to the control client private key, by default unbound_control.key.
|
||||
This file is generated by the \fIunbound\-control\-setup\fR utility.
|
||||
This file is used by \fIunbound\-control\fR.
|
||||
.TP 5
|
||||
.B control\-cert\-file: "<certificate file.pem>"
|
||||
Path to the control client certificate, by default unbound_control.pem.
|
||||
This certificate has to be signed with the server certificate.
|
||||
This file is generated by the \fIunbound\-control\-setup\fR utility.
|
||||
This file is used by \fIunbound\-control\fR.
|
||||
.SS "Stub Zone Options"
|
||||
.LP
|
||||
There may be multiple
|
||||
|
|
|
|||
310
smallapp/unbound-control.c
Normal file
310
smallapp/unbound-control.c
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* checkconf/unbound-control.c - remote control utility for unbound.
|
||||
*
|
||||
* Copyright (c) 2008, NLnet Labs. All rights reserved.
|
||||
*
|
||||
* This software is open source.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Neither the name of the NLNET LABS nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "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 THE REGENTS OR CONTRIBUTORS 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* The remote control utility contacts the unbound server over ssl and
|
||||
* sends the command, receives the answer, and displays the result
|
||||
* from the commandline.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/locks.h"
|
||||
#include "util/net_help.h"
|
||||
|
||||
/** Give unbound-control usage, and exit (1). */
|
||||
static void
|
||||
usage()
|
||||
{
|
||||
printf("Usage: unbound-control [options] command\n");
|
||||
printf(" Remote control utility for unbound server.\n");
|
||||
printf("Options:\n");
|
||||
printf(" -c file config file, default is %s\n", CONFIGFILE);
|
||||
printf(" -s ip[@port] server address, if omitted config is used.\n");
|
||||
printf(" -h show this usage help.\n");
|
||||
printf("Commands:\n");
|
||||
printf(" start start server; runs unbound(8)\n");
|
||||
printf(" stop stops the server\n");
|
||||
printf(" reload reloads the server\n");
|
||||
printf("Version %s\n", PACKAGE_VERSION);
|
||||
printf("BSD licensed, see LICENSE in source package for details.\n");
|
||||
printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** exit with ssl error */
|
||||
static void ssl_err(const char* s)
|
||||
{
|
||||
fprintf(stderr, "error: %s\n", s);
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/** setup SSL context */
|
||||
static SSL_CTX*
|
||||
setup_ctx(struct config_file* cfg)
|
||||
{
|
||||
char* s_cert, *c_key, *c_cert;
|
||||
SSL_CTX* ctx;
|
||||
|
||||
s_cert = fname_after_chroot(cfg->server_cert_file, cfg, 1);
|
||||
c_key = fname_after_chroot(cfg->control_key_file, cfg, 1);
|
||||
c_cert = fname_after_chroot(cfg->control_cert_file, cfg, 1);
|
||||
if(!s_cert || !c_key || !c_cert)
|
||||
fatal_exit("out of memory");
|
||||
ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if(!ctx)
|
||||
ssl_err("could not allocate SSL_CTX pointer");
|
||||
if(!(SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2))
|
||||
ssl_err("could not set SSL_OP_NO_SSLv2");
|
||||
if(!SSL_CTX_use_certificate_file(ctx,c_cert,SSL_FILETYPE_PEM) ||
|
||||
!SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM)
|
||||
|| !SSL_CTX_check_private_key(ctx))
|
||||
ssl_err("Error setting up SSL_CTX client key and cert");
|
||||
if (SSL_CTX_load_verify_locations(ctx, s_cert, NULL) != 1)
|
||||
ssl_err("Error setting up SSL_CTX verify, server cert");
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
|
||||
|
||||
free(s_cert);
|
||||
free(c_key);
|
||||
free(c_cert);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/** contact the server with TCP connect */
|
||||
static int
|
||||
contact_server(char* svr, struct config_file* cfg)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
int fd;
|
||||
/* use svr or the first config entry */
|
||||
if(!svr) {
|
||||
if(cfg->control_ifs)
|
||||
svr = cfg->control_ifs->str;
|
||||
else svr = "127.0.0.1";
|
||||
}
|
||||
if(strchr(svr, '@')) {
|
||||
if(!extstrtoaddr(svr, &addr, &addrlen))
|
||||
fatal_exit("could not parse IP@port: %s", svr);
|
||||
} else {
|
||||
if(!ipstrtoaddr(svr, cfg->control_port, &addr, &addrlen))
|
||||
fatal_exit("could not parse IP: %s", svr);
|
||||
}
|
||||
fd = socket(addr_is_ip6(&addr, addrlen)?AF_INET6:AF_INET,
|
||||
SOCK_STREAM, 0);
|
||||
if(fd == -1) {
|
||||
#ifndef USE_WINSOCK
|
||||
fatal_exit("socket: %s", strerror(errno));
|
||||
#else
|
||||
fatal_exit("socket: %s", wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
}
|
||||
if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) {
|
||||
log_addr(0, "address", &addr, addrlen);
|
||||
#ifndef USE_WINSOCK
|
||||
log_err("connect: %s", strerror(errno));
|
||||
#else
|
||||
log_err("connect: %s", wsa_strerror(WSAGetLastError()));
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/** setup SSL on the connection */
|
||||
static SSL*
|
||||
setup_ssl(SSL_CTX* ctx, int fd)
|
||||
{
|
||||
SSL* ssl;
|
||||
X509* x;
|
||||
int r;
|
||||
|
||||
ssl = SSL_new(ctx);
|
||||
if(!ssl)
|
||||
ssl_err("could not SSL_new");
|
||||
SSL_set_connect_state(ssl);
|
||||
(void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
|
||||
if(!SSL_set_fd(ssl, fd))
|
||||
ssl_err("could not SSL_set_fd");
|
||||
while(1) {
|
||||
ERR_clear_error();
|
||||
if( (r=SSL_do_handshake(ssl)) == 1)
|
||||
break;
|
||||
r = SSL_get_error(ssl, r);
|
||||
if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE)
|
||||
ssl_err("SSL handshake failed");
|
||||
/* wants to be called again */
|
||||
}
|
||||
|
||||
/* check authenticity of server */
|
||||
if(SSL_get_verify_result(ssl) != X509_V_OK)
|
||||
ssl_err("SSL verification failed");
|
||||
x = SSL_get_peer_certificate(ssl);
|
||||
if(!x)
|
||||
ssl_err("Server presented no peer certificate");
|
||||
X509_free(x);
|
||||
return ssl;
|
||||
}
|
||||
|
||||
/** send command and display result */
|
||||
static void
|
||||
go_cmd(SSL* ssl, int argc, char* argv[])
|
||||
{
|
||||
char* cmd = "GET / HTTP/1.0\n\n";
|
||||
int r;
|
||||
char buf[1024];
|
||||
if(SSL_write(ssl, cmd, (int)strlen(cmd)) <= 0)
|
||||
ssl_err("could not SSL_write");
|
||||
while(1) {
|
||||
ERR_clear_error();
|
||||
if((r = SSL_read(ssl, buf, (int)sizeof(buf)-1)) <= 0) {
|
||||
if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) {
|
||||
/* EOF */
|
||||
break;
|
||||
}
|
||||
ssl_err("could not SSL_read");
|
||||
}
|
||||
buf[r] = 0;
|
||||
printf("%s", buf);
|
||||
}
|
||||
}
|
||||
|
||||
/** go ahead and read config, contact server and perform command and display */
|
||||
static void
|
||||
go(char* cfgfile, char* svr, int argc, char* argv[])
|
||||
{
|
||||
struct config_file* cfg;
|
||||
int fd;
|
||||
SSL_CTX* ctx;
|
||||
SSL* ssl;
|
||||
|
||||
/* read config */
|
||||
if(!(cfg = config_create()))
|
||||
fatal_exit("out of memory");
|
||||
if(!config_read(cfg, cfgfile))
|
||||
fatal_exit("could not read config file");
|
||||
if(!cfg->remote_control_enable)
|
||||
log_warn("control-enable is 'no' in the config file.");
|
||||
ctx = setup_ctx(cfg);
|
||||
|
||||
/* contact server */
|
||||
fd = contact_server(svr, cfg);
|
||||
ssl = setup_ssl(ctx, fd);
|
||||
|
||||
/* send command */
|
||||
go_cmd(ssl, argc, argv);
|
||||
|
||||
SSL_free(ssl);
|
||||
close(fd);
|
||||
SSL_CTX_free(ctx);
|
||||
config_delete(cfg);
|
||||
}
|
||||
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern int optind;
|
||||
/** getopt global, in case header files fail to declare it. */
|
||||
extern char* optarg;
|
||||
|
||||
/** Main routine for unbound-control */
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int c;
|
||||
char* cfgfile = CONFIGFILE;
|
||||
char* svr = NULL;
|
||||
log_ident_set("unbound-control");
|
||||
log_init(NULL, 0, NULL);
|
||||
checklock_start();
|
||||
#ifdef USE_WINSOCK
|
||||
if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0)
|
||||
fatal_exit("WSAStartup failed: %s", wsa_strerror(r));
|
||||
#endif
|
||||
|
||||
ERR_load_crypto_strings();
|
||||
ERR_load_SSL_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
(void)SSL_library_init();
|
||||
|
||||
if(!RAND_status()) {
|
||||
/* try to seed it */
|
||||
unsigned char buf[256];
|
||||
unsigned int v, seed=(unsigned)time(NULL) ^ (unsigned)getpid();
|
||||
size_t i;
|
||||
for(i=0; i<256/sizeof(v); i++) {
|
||||
memmove(buf+i*sizeof(v), &v, sizeof(v));
|
||||
v = v*seed + (unsigned int)i;
|
||||
}
|
||||
RAND_seed(buf, 256);
|
||||
log_warn("no entropy, seeding openssl PRNG with time\n");
|
||||
}
|
||||
|
||||
/* parse the options */
|
||||
while( (c=getopt(argc, argv, "c:s:h")) != -1) {
|
||||
switch(c) {
|
||||
case 'c':
|
||||
cfgfile = optarg;
|
||||
break;
|
||||
case 's':
|
||||
svr = optarg;
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if(argc == 0)
|
||||
usage();
|
||||
if(argc == 1 && strcmp(argv[0], "start")==0) {
|
||||
if(execlp("unbound", "unbound", "-c", cfgfile,
|
||||
(char*)NULL) < 0) {
|
||||
fatal_exit("could not exec unbound: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
go(cfgfile, svr, argc, argv);
|
||||
|
||||
#ifdef USE_WINSOCK
|
||||
WSACleanup();
|
||||
#endif
|
||||
checklock_stop();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -574,6 +574,7 @@ int comm_point_perform_accept(struct comm_point* c,
|
|||
log_addr(0, "remote address is", addr, *addrlen);
|
||||
return -1;
|
||||
}
|
||||
fd_set_nonblock(new_fd);
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
|
|
@ -950,14 +951,17 @@ void comm_point_local_handle_callback(int fd, short event, void* arg)
|
|||
}
|
||||
|
||||
void comm_point_raw_handle_callback(int ATTR_UNUSED(fd),
|
||||
short ATTR_UNUSED(event), void* arg)
|
||||
short event, void* arg)
|
||||
{
|
||||
struct comm_point* c = (struct comm_point*)arg;
|
||||
int err = NETEVENT_NOERROR;
|
||||
log_assert(c->type == comm_raw);
|
||||
comm_base_now(c->ev->base);
|
||||
|
||||
|
||||
if(event&EV_TIMEOUT)
|
||||
err = NETEVENT_TIMEOUT;
|
||||
fptr_ok(fptr_whitelist_comm_point_raw(c->callback));
|
||||
(void)(*c->callback)(c, c->cb_arg, NETEVENT_NOERROR, NULL);
|
||||
(void)(*c->callback)(c, c->cb_arg, err, NULL);
|
||||
}
|
||||
|
||||
struct comm_point*
|
||||
|
|
@ -1108,7 +1112,7 @@ comm_point_create_tcp_handler(struct comm_base *base,
|
|||
c->tcp_free = parent->tcp_free;
|
||||
parent->tcp_free = c;
|
||||
/* libevent stuff */
|
||||
evbits = EV_PERSIST | EV_READ;
|
||||
evbits = EV_PERSIST | EV_READ | EV_TIMEOUT;
|
||||
event_set(&c->ev->ev, c->fd, evbits, comm_point_tcp_handle_callback, c);
|
||||
if(event_base_set(base->eb->base, &c->ev->ev) != 0)
|
||||
{
|
||||
|
|
@ -1437,6 +1441,7 @@ comm_point_start_listening(struct comm_point* c, int newfd, int sec)
|
|||
return;
|
||||
}
|
||||
}
|
||||
c->ev->ev.ev_events |= EV_TIMEOUT;
|
||||
#ifndef S_SPLINT_S /* splint fails on struct timeval. */
|
||||
c->timeout->tv_sec = sec;
|
||||
c->timeout->tv_usec = 0;
|
||||
|
|
@ -1459,6 +1464,20 @@ comm_point_start_listening(struct comm_point* c, int newfd, int sec)
|
|||
}
|
||||
}
|
||||
|
||||
void comm_point_listen_for_rw(struct comm_point* c, int rd, int wr)
|
||||
{
|
||||
verbose(VERB_ALGO, "comm point listen_for_rw %d %d", c->fd, wr);
|
||||
if(event_del(&c->ev->ev) != 0) {
|
||||
log_err("event_del error to cplf");
|
||||
}
|
||||
c->ev->ev.ev_events &= ~(EV_READ|EV_WRITE);
|
||||
if(rd) c->ev->ev.ev_events |= EV_READ;
|
||||
if(wr) c->ev->ev.ev_events |= EV_WRITE;
|
||||
if(event_add(&c->ev->ev, c->timeout) != 0) {
|
||||
log_err("event_add failed. in cplf.");
|
||||
}
|
||||
}
|
||||
|
||||
size_t comm_point_get_mem(struct comm_point* c)
|
||||
{
|
||||
size_t s;
|
||||
|
|
|
|||
|
|
@ -443,6 +443,14 @@ void comm_point_stop_listening(struct comm_point* c);
|
|||
*/
|
||||
void comm_point_start_listening(struct comm_point* c, int newfd, int sec);
|
||||
|
||||
/**
|
||||
* Stop listening and start listening again for reading or writing.
|
||||
* @param c: commpoint
|
||||
* @param rd: if true, listens for reading.
|
||||
* @param wr: if true, listens for writing.
|
||||
*/
|
||||
void comm_point_listen_for_rw(struct comm_point* c, int rd, int wr);
|
||||
|
||||
/**
|
||||
* Get size of memory used by comm point.
|
||||
* For TCP handlers this includes subhandlers.
|
||||
|
|
|
|||
Loading…
Reference in a new issue