summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2015-01-05 14:55:52 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2015-01-05 14:55:52 +0000
commit3ff5baf8cb6770d63f234c261e9af15848bb939c (patch)
tree3b39302660451fc317bd1ffae0cd574e24c2dbdb
parent7954be7fa5ea70de36aacebb8bcca2a70af709f4 (diff)
downloadsrc-test2-3ff5baf8cb6770d63f234c261e9af15848bb939c.tar.gz
src-test2-3ff5baf8cb6770d63f234c261e9af15848bb939c.zip
Add support for using a local socket for the remote control connection
by specifying uts path instead of (or in addition to) an IP address as an argument to the control-interface configuration variable. Add support for unencrypted and unauthenticated control connections through a new configuration variable, control-use-cert. To avoid the complexity of supporting both SSL socket and plain socket descriptors in the same code, we just use an unencrypted SSL context and forego authentication. The downside is that we still have to perform DH kex when establishing the connection. This patch was derived (with significant modifications) from the contrib/unbound_unixsock.diff patch originally submitted by Ilya Bakulin of Genua mbH.
Notes
Notes: svn path=/vendor/unbound/dist/; revision=276698
-rw-r--r--config.h.in3
-rw-r--r--configure.ac2
-rw-r--r--daemon/remote.c118
-rw-r--r--daemon/remote.h2
-rw-r--r--daemon/unbound.c39
-rw-r--r--doc/unbound.conf.5.in33
-rw-r--r--services/listen_dnsport.c54
-rw-r--r--services/listen_dnsport.h9
-rw-r--r--smallapp/unbound-checkconf.c2
-rw-r--r--smallapp/unbound-control.c81
-rw-r--r--util/config_file.c16
-rw-r--r--util/config_file.h4
-rw-r--r--util/configlexer.lex1
-rw-r--r--util/configparser.y13
-rw-r--r--util/net_help.c4
15 files changed, 292 insertions, 89 deletions
diff --git a/config.h.in b/config.h.in
index 2b7770b5c23a..7a8a5dd58e9c 100644
--- a/config.h.in
+++ b/config.h.in
@@ -380,6 +380,9 @@
/* Define to 1 if you have the <sys/uio.h> header file. */
#undef HAVE_SYS_UIO_H
+/* Define to 1 if you have the <sys/un.h> header file. */
+#undef HAVE_SYS_UN_H
+
/* Define to 1 if you have the <sys/wait.h> header file. */
#undef HAVE_SYS_WAIT_H
diff --git a/configure.ac b/configure.ac
index 7e5da1a9f046..37e1bf6f4bca 100644
--- a/configure.ac
+++ b/configure.ac
@@ -266,7 +266,7 @@ AC_CHECK_TOOL(STRIP, strip)
ACX_LIBTOOL_C_ONLY
# Checks for header files.
-AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h sys/param.h sys/socket.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h],,, [AC_INCLUDES_DEFAULT])
+AC_CHECK_HEADERS([stdarg.h stdbool.h netinet/in.h sys/param.h sys/socket.h sys/un.h sys/uio.h sys/resource.h arpa/inet.h syslog.h netdb.h sys/wait.h pwd.h glob.h grp.h login_cap.h winsock2.h ws2tcpip.h endian.h],,, [AC_INCLUDES_DEFAULT])
# check for types.
# Using own tests for int64* because autoconf builtin only give 32bit.
diff --git a/daemon/remote.c b/daemon/remote.c
index ff3d769d4e54..a1d2628a904a 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -46,6 +46,10 @@
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
+#ifndef HEADER_DH_H
+#include <openssl/dh.h>
+#endif
+
#include <ctype.h>
#include "daemon/remote.h"
#include "daemon/worker.h"
@@ -82,6 +86,9 @@
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
@@ -131,6 +138,39 @@ timeval_divide(struct timeval* avg, const struct timeval* sum, size_t d)
#endif
}
+/*
+ * The following function was generated using the openssl utility, using
+ * the command : "openssl dhparam -dsaparam -C 512"
+ */
+DH *get_dh512()
+{
+ static unsigned char dh512_p[]={
+ 0xC9,0xD7,0x05,0xDA,0x5F,0xAB,0x14,0xE8,0x11,0x56,0x77,0x85,
+ 0xB1,0x24,0x2C,0x95,0x60,0xEA,0xE2,0x10,0x6F,0x0F,0x84,0xEC,
+ 0xF4,0x45,0xE8,0x90,0x7A,0xA7,0x03,0xFF,0x5B,0x88,0x53,0xDE,
+ 0xC4,0xDE,0xBC,0x42,0x78,0x71,0x23,0x7E,0x24,0xA5,0x5E,0x4E,
+ 0xEF,0x6F,0xFF,0x5F,0xAF,0xBE,0x8A,0x77,0x62,0xB4,0x65,0x82,
+ 0x7E,0xC9,0xED,0x2F,
+ };
+ static unsigned char dh512_g[]={
+ 0x8D,0x3A,0x52,0xBC,0x8A,0x71,0x94,0x33,0x2F,0xE1,0xE8,0x4C,
+ 0x73,0x47,0x03,0x4E,0x7D,0x40,0xE5,0x84,0xA0,0xB5,0x6D,0x10,
+ 0x6F,0x90,0x43,0x05,0x1A,0xF9,0x0B,0x6A,0xD1,0x2A,0x9C,0x25,
+ 0x0A,0xB9,0xD1,0x14,0xDC,0x35,0x1C,0x48,0x7C,0xC6,0x0C,0x6D,
+ 0x32,0x1D,0xD3,0xC8,0x10,0xA8,0x82,0x14,0xA2,0x1C,0xF4,0x53,
+ 0x23,0x3B,0x1C,0xB9,
+ };
+ DH *dh;
+
+ if ((dh=DH_new()) == NULL) return(NULL);
+ dh->p=BN_bin2bn(dh512_p,sizeof(dh512_p),NULL);
+ dh->g=BN_bin2bn(dh512_g,sizeof(dh512_g),NULL);
+ if ((dh->p == NULL) || (dh->g == NULL))
+ { DH_free(dh); return(NULL); }
+ dh->length = 160;
+ return(dh);
+}
+
struct daemon_remote*
daemon_remote_create(struct config_file* cfg)
{
@@ -165,6 +205,24 @@ daemon_remote_create(struct config_file* cfg)
daemon_remote_delete(rc);
return NULL;
}
+
+ if (cfg->remote_control_use_cert == 0) {
+ /* No certificates are requested */
+ if(!SSL_CTX_set_cipher_list(rc->ctx, "aNULL")) {
+ log_crypto_err("Failed to set aNULL cipher list");
+ return NULL;
+ }
+
+ /* Since we have no certificates and hence no source of
+ * DH params, let's generate and set them
+ */
+ if(!SSL_CTX_set_tmp_dh(rc->ctx,get_dh512())) {
+ log_crypto_err("Wanted to set DH param, but failed");
+ return NULL;
+ }
+ return rc;
+ }
+ rc->use_cert = 1;
s_cert = fname_after_chroot(cfg->server_cert_file, cfg, 1);
s_key = fname_after_chroot(cfg->server_key_file, cfg, 1);
if(!s_cert || !s_key) {
@@ -244,7 +302,8 @@ void daemon_remote_delete(struct daemon_remote* rc)
* @return false on failure.
*/
static int
-add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err)
+add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err,
+ struct config_file* cfg)
{
struct addrinfo hints;
struct addrinfo* res;
@@ -255,29 +314,46 @@ add_open(const char* ip, int nr, struct listen_port** list, int noproto_is_err)
snprintf(port, sizeof(port), "%d", nr);
port[sizeof(port)-1]=0;
memset(&hints, 0, sizeof(hints));
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
- if((r = getaddrinfo(ip, port, &hints, &res)) != 0 || !res) {
-#ifdef USE_WINSOCK
- if(!noproto_is_err && r == EAI_NONAME) {
- /* tried to lookup the address as name */
- return 1; /* return success, but do nothing */
+
+ if(ip[0] == '/') {
+ /* This looks like a local socket */
+ fd = create_local_accept_sock(ip, &noproto);
+ /*
+ * Change socket ownership and permissions so users other
+ * than root can access it provided they are in the same
+ * group as the user we run as.
+ */
+ if(fd != -1) {
+ if (cfg->username && cfg->username[0])
+ chown(ip, cfg->uid, cfg->gid);
+ chmod(ip, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
}
+ } else {
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+ if((r = getaddrinfo(ip, port, &hints, &res)) != 0 || !res) {
+#ifdef USE_WINSOCK
+ if(!noproto_is_err && r == EAI_NONAME) {
+ /* tried to lookup the address as name */
+ return 1; /* return success, but do nothing */
+ }
#endif /* USE_WINSOCK */
- log_err("control interface %s:%s getaddrinfo: %s %s",
- ip?ip:"default", port, gai_strerror(r),
+ log_err("control interface %s:%s getaddrinfo: %s %s",
+ ip?ip:"default", port, gai_strerror(r),
#ifdef EAI_SYSTEM
- r==EAI_SYSTEM?(char*)strerror(errno):""
+ r==EAI_SYSTEM?(char*)strerror(errno):""
#else
- ""
+ ""
#endif
);
- return 0;
+ return 0;
+ }
+
+ /* open fd */
+ fd = create_tcp_accept_sock(res, 1, &noproto, 0);
+ freeaddrinfo(res);
}
- /* open fd */
- fd = create_tcp_accept_sock(res, 1, &noproto, 0);
- freeaddrinfo(res);
if(fd == -1 && noproto) {
if(!noproto_is_err)
return 1; /* return success, but do nothing */
@@ -314,7 +390,7 @@ struct listen_port* daemon_remote_open_ports(struct config_file* cfg)
if(cfg->control_ifs) {
struct config_strlist* p;
for(p = cfg->control_ifs; p; p = p->next) {
- if(!add_open(p->str, cfg->control_port, &l, 1)) {
+ if(!add_open(p->str, cfg->control_port, &l, 1, cfg)) {
listening_ports_free(l);
return NULL;
}
@@ -322,12 +398,12 @@ struct listen_port* daemon_remote_open_ports(struct config_file* cfg)
} else {
/* defaults */
if(cfg->do_ip6 &&
- !add_open("::1", cfg->control_port, &l, 0)) {
+ !add_open("::1", cfg->control_port, &l, 0, cfg)) {
listening_ports_free(l);
return NULL;
}
if(cfg->do_ip4 &&
- !add_open("127.0.0.1", cfg->control_port, &l, 1)) {
+ !add_open("127.0.0.1", cfg->control_port, &l, 1, cfg)) {
listening_ports_free(l);
return NULL;
}
@@ -2434,7 +2510,9 @@ int remote_control_callback(struct comm_point* c, void* arg, int err,
s->shake_state = rc_none;
/* once handshake has completed, check authentication */
- if(SSL_get_verify_result(s->ssl) == X509_V_OK) {
+ if (!rc->use_cert) {
+ verbose(VERB_ALGO, "unauthenticated remote control connection");
+ } else 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 "
diff --git a/daemon/remote.h b/daemon/remote.h
index cc670b70128f..b25bfb1af611 100644
--- a/daemon/remote.h
+++ b/daemon/remote.h
@@ -89,6 +89,8 @@ struct daemon_remote {
struct worker* worker;
/** commpoints for accepting remote control connections */
struct listen_list* accept_list;
+ /* if certificates are used */
+ int use_cert;
/** number of active commpoints that are handling remote control */
int active;
/** max active commpoints */
diff --git a/daemon/unbound.c b/daemon/unbound.c
index 5ded5a964cc3..e48a6b5ea951 100644
--- a/daemon/unbound.c
+++ b/daemon/unbound.c
@@ -441,20 +441,14 @@ static void
perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
const char** cfgfile)
{
+ log_assert(cfg);
+
#ifdef HAVE_GETPWNAM
struct passwd *pwd = NULL;
- uid_t uid;
- gid_t gid;
- /* initialize, but not to 0 (root) */
- memset(&uid, 112, sizeof(uid));
- memset(&gid, 112, sizeof(gid));
- log_assert(cfg);
if(cfg->username && cfg->username[0]) {
if((pwd = getpwnam(cfg->username)) == NULL)
fatal_exit("user '%s' does not exist.", cfg->username);
- uid = pwd->pw_uid;
- gid = pwd->pw_gid;
/* endpwent below, in case we need pwd for setusercontext */
}
#endif
@@ -511,18 +505,11 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
#ifdef HAVE_KILL
if(cfg->pidfile && cfg->pidfile[0]) {
writepid(daemon->pidfile, getpid());
- if(!(cfg->chrootdir && cfg->chrootdir[0]) ||
- (cfg->chrootdir && cfg->chrootdir[0] &&
- strncmp(daemon->pidfile, cfg->chrootdir,
- strlen(cfg->chrootdir))==0)) {
- /* delete of pidfile could potentially work,
- * chown to get permissions */
- if(cfg->username && cfg->username[0]) {
- if(chown(daemon->pidfile, uid, gid) == -1) {
+ if(cfg->username && cfg->username[0]) {
+ if(chown(daemon->pidfile, cfg->uid, cfg->gid) == -1) {
log_err("cannot chown %u.%u %s: %s",
- (unsigned)uid, (unsigned)gid,
+ (unsigned)cfg->uid, (unsigned)cfg->gid,
daemon->pidfile, strerror(errno));
- }
}
}
}
@@ -537,7 +524,7 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
/* setusercontext does initgroups, setuid, setgid, and
* also resource limits from login config, but we
* still call setresuid, setresgid to be sure to set all uid*/
- if(setusercontext(NULL, pwd, uid, (unsigned)
+ if(setusercontext(NULL, pwd, cfg->uid, (unsigned)
LOGIN_SETALL & ~LOGIN_SETUSER & ~LOGIN_SETGROUP) != 0)
log_warn("unable to setusercontext %s: %s",
cfg->username, strerror(errno));
@@ -601,27 +588,27 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
#ifdef HAVE_GETPWNAM
if(cfg->username && cfg->username[0]) {
# ifdef HAVE_INITGROUPS
- if(initgroups(cfg->username, gid) != 0)
+ if(initgroups(cfg->username, cfg->gid) != 0)
log_warn("unable to initgroups %s: %s",
cfg->username, strerror(errno));
# endif /* HAVE_INITGROUPS */
endpwent();
#ifdef HAVE_SETRESGID
- if(setresgid(gid,gid,gid) != 0)
+ if(setresgid(cfg->gid,cfg->gid,cfg->gid) != 0)
#elif defined(HAVE_SETREGID) && !defined(DARWIN_BROKEN_SETREUID)
- if(setregid(gid,gid) != 0)
+ if(setregid(cfg->gid,cfg->gid) != 0)
#else /* use setgid */
- if(setgid(gid) != 0)
+ if(setgid(cfg->gid) != 0)
#endif /* HAVE_SETRESGID */
fatal_exit("unable to set group id of %s: %s",
cfg->username, strerror(errno));
#ifdef HAVE_SETRESUID
- if(setresuid(uid,uid,uid) != 0)
+ if(setresuid(cfg->uid,cfg->uid,cfg->uid) != 0)
#elif defined(HAVE_SETREUID) && !defined(DARWIN_BROKEN_SETREUID)
- if(setreuid(uid,uid) != 0)
+ if(setreuid(cfg->uid,cfg->uid) != 0)
#else /* use setuid */
- if(setuid(uid) != 0)
+ if(setuid(cfg->uid) != 0)
#endif /* HAVE_SETRESUID */
fatal_exit("unable to set user id of %s: %s",
cfg->username, strerror(errno));
diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in
index f08a01b31844..9b23fd64da1b 100644
--- a/doc/unbound.conf.5.in
+++ b/doc/unbound.conf.5.in
@@ -958,36 +958,47 @@ to setup SSLv3 / TLSv1 security for the connection. The
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>
+.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.
+.B control\-interface: \fI<ip address or path>
+Give IPv4 or IPv6 addresses or local socket path 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.
+If you change this and permissions have been dropped, you must restart
+the server for the change to take effect.
.TP 5
-.B control\-port: <port number>
-The port number to listen on for control commands, default is 8953.
-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.
+.B control\-port: \fI<port number>
+The port number to listen on for IPv4 or IPv6 control interfaces,
+default is 8953.
+If you change this and permissions have been dropped, you must restart
+the server for the change to take effect.
.TP 5
-.B server\-key\-file: "<private key file>"
+.B control-use-cert: \fI<yes or no>
+Whether to require certificate authentication of control connections.
+The default is "yes".
+This should not be changed unless there are other mechanisms in place
+to prevent untrusted users from accessing the remote control
+interface.
+.TP 5
+.B server\-key\-file: \fI<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>"
+.B server\-cert\-file: \fI<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>"
+.B control\-key\-file: \fI<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>"
+.B control\-cert\-file: \fI<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.
diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c
index b7ffb6d3fad3..69c68b2807ff 100644
--- a/services/listen_dnsport.c
+++ b/services/listen_dnsport.c
@@ -56,6 +56,10 @@
#endif
#include <fcntl.h>
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
/** number of queued TCP connections for listen() */
#define TCP_BACKLOG 256
@@ -571,6 +575,56 @@ create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
return s;
}
+int
+create_local_accept_sock(char *path, int* noproto)
+{
+#ifdef HAVE_SYS_UN_H
+ int s;
+ struct sockaddr_un sun;
+
+ sun.sun_len = sizeof(sun);
+ sun.sun_family = AF_LOCAL;
+ strlcpy(sun.sun_path, path, 104);
+
+ if ((s = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) {
+ log_err("Cannot create local socket %s (%s)",
+ path, strerror(errno));
+ return -1;
+ }
+
+ if (unlink(path) && errno != ENOENT) {
+ /* The socket already exists and cannot be removed */
+ log_err("Cannot remove old local socket %s (%s)",
+ path, strerror(errno));
+ return -1;
+ }
+
+ if (bind(s, (struct sockaddr *)&sun,
+ sizeof(struct sockaddr_un)) == -1) {
+ log_err("Cannot bind local socket %s (%s)",
+ path, strerror(errno));
+ return -1;
+ }
+
+ if (!fd_set_nonblock(s)) {
+ log_err("Cannot set non-blocking mode");
+ return -1;
+ }
+
+ if (listen(s, TCP_BACKLOG) == -1) {
+ log_err("can't listen: %s", strerror(errno));
+ return -1;
+ }
+
+ return s;
+#else
+ log_err("Local sockets are not supported");
+ *noproto = 1;
+ return -1;
+#endif
+}
+
+
/**
* Create socket from getaddrinfo results
*/
diff --git a/services/listen_dnsport.h b/services/listen_dnsport.h
index 075f6d281d5d..0513b5d6c01b 100644
--- a/services/listen_dnsport.h
+++ b/services/listen_dnsport.h
@@ -207,4 +207,13 @@ int create_udp_sock(int family, int socktype, struct sockaddr* addr,
int create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto,
int* reuseport);
+/**
+ * Create and bind local listening socket
+ * @param path: path to the socket.
+ * @param noproto: on error, this is set true if cause is that local sockets
+ * are not supported.
+ * @return: the socket. -1 on error.
+ */
+int create_local_accept_sock(char* path, int* noproto);
+
#endif /* LISTEN_DNSPORT_H */
diff --git a/smallapp/unbound-checkconf.c b/smallapp/unbound-checkconf.c
index e83867f26844..7723c3357695 100644
--- a/smallapp/unbound-checkconf.c
+++ b/smallapp/unbound-checkconf.c
@@ -416,7 +416,7 @@ morechecks(struct config_file* cfg, const char* fname)
endpwent();
}
#endif
- if(cfg->remote_control_enable) {
+ if(cfg->remote_control_enable && cfg->remote_control_use_cert) {
check_chroot_string("server-key-file", &cfg->server_key_file,
cfg->chrootdir, cfg);
check_chroot_string("server-cert-file", &cfg->server_cert_file,
diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c
index ff86184a8162..37e840bad799 100644
--- a/smallapp/unbound-control.c
+++ b/smallapp/unbound-control.c
@@ -59,6 +59,10 @@
#include "util/locks.h"
#include "util/net_help.h"
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
/** Give unbound-control usage, and exit (1). */
static void
usage()
@@ -139,29 +143,37 @@ 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");
+ if(cfg->remote_control_use_cert) {
+ 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_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3))
- ssl_err("could not set SSL_OP_NO_SSLv3");
- 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);
+ if(cfg->remote_control_use_cert) {
+ if(!(SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3))
+ ssl_err("could not set SSL_OP_NO_SSLv3");
+ 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);
+ free(s_cert);
+ free(c_key);
+ free(c_cert);
+ } else {
+ /* Use ciphers that don't require authentication */
+ if(!SSL_CTX_set_cipher_list(ctx, "aNULL"))
+ ssl_err("Error setting NULL cipher!");
+ }
return ctx;
}
@@ -171,6 +183,7 @@ contact_server(const char* svr, struct config_file* cfg, int statuscmd)
{
struct sockaddr_storage addr;
socklen_t addrlen;
+ int addrfamily = 0;
int fd;
/* use svr or the first config entry */
if(!svr) {
@@ -189,12 +202,23 @@ contact_server(const char* svr, struct config_file* cfg, int statuscmd)
if(strchr(svr, '@')) {
if(!extstrtoaddr(svr, &addr, &addrlen))
fatal_exit("could not parse IP@port: %s", svr);
+#ifdef HAVE_SYS_UN_H
+ } else if(svr[0] == '/') {
+ struct sockaddr_un* sun = (struct sockaddr_un *) &addr;
+ sun->sun_family = AF_LOCAL;
+ sun->sun_len = sizeof(sun);
+ strlcpy(sun->sun_path, svr, 104);
+ addrlen = sizeof(struct sockaddr_un);
+ addrfamily = AF_LOCAL;
+#endif
} 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(addrfamily == 0)
+ addrfamily = addr_is_ip6(&addr, addrlen)?AF_INET6:AF_INET;
+ fd = socket(addrfamily, SOCK_STREAM, 0);
if(fd == -1) {
#ifndef USE_WINSOCK
fatal_exit("socket: %s", strerror(errno));
@@ -223,7 +247,7 @@ contact_server(const char* svr, struct config_file* cfg, int statuscmd)
/** setup SSL on the connection */
static SSL*
-setup_ssl(SSL_CTX* ctx, int fd)
+setup_ssl(SSL_CTX* ctx, int fd, struct config_file* cfg)
{
SSL* ssl;
X509* x;
@@ -249,10 +273,13 @@ setup_ssl(SSL_CTX* ctx, int fd)
/* 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);
+ if(cfg->remote_control_use_cert) {
+ x = SSL_get_peer_certificate(ssl);
+ if(!x)
+ ssl_err("Server presented no peer certificate");
+ X509_free(x);
+ }
+
return ssl;
}
@@ -330,11 +357,11 @@ go(const char* cfgfile, char* svr, int quiet, int argc, char* argv[])
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, argc>0&&strcmp(argv[0],"status")==0);
- ssl = setup_ssl(ctx, fd);
-
+ ssl = setup_ssl(ctx, fd, cfg);
+
/* send command */
ret = go_cmd(ssl, quiet, argc, argv);
diff --git a/util/config_file.c b/util/config_file.c
index 35bc6452a0ce..bb39cf9bd7a2 100644
--- a/util/config_file.c
+++ b/util/config_file.c
@@ -60,6 +60,9 @@
#ifdef HAVE_GLOB_H
# include <glob.h>
#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
/** global config during parsing */
struct config_parser_state* cfg_parser = 0;
@@ -131,6 +134,8 @@ config_create(void)
goto error_exit;
init_outgoing_availports(cfg->outgoing_avail_ports, 65536);
if(!(cfg->username = strdup(UB_USERNAME))) goto error_exit;
+ cfg->uid = (uid_t)-1;
+ cfg->gid = (gid_t)-1;
#ifdef HAVE_CHROOT
if(!(cfg->chrootdir = strdup(CHROOT_DIR))) goto error_exit;
#endif
@@ -799,6 +804,17 @@ config_read(struct config_file* cfg, const char* filename, const char* chroot)
errno=EINVAL;
return 0;
}
+
+#ifdef HAVE_GETPWNAM
+ /* translate username into uid and gid */
+ if(cfg->username && cfg->username[0]) {
+ struct passwd *pwd;
+ if((pwd = getpwnam(cfg->username)) == NULL)
+ log_err("user '%s' does not exist.", cfg->username);
+ cfg->uid = pwd->pw_uid;
+ cfg->gid = pwd->pw_gid;
+ }
+#endif
return 1;
}
diff --git a/util/config_file.h b/util/config_file.h
index 49ffbdde4a56..fd35d7863ae1 100644
--- a/util/config_file.h
+++ b/util/config_file.h
@@ -192,6 +192,8 @@ struct config_file {
char* chrootdir;
/** username to change to, if not "". */
char* username;
+ uid_t uid;
+ gid_t gid;
/** working directory */
char* directory;
/** filename to log to. */
@@ -282,6 +284,8 @@ struct config_file {
struct config_strlist* control_ifs;
/** port number for the control port */
int control_port;
+ /** use certificates for remote control */
+ int remote_control_use_cert;
/** private key file for server */
char* server_key_file;
/** certificate file for server */
diff --git a/util/configlexer.lex b/util/configlexer.lex
index 7ee7b9bd9a92..eea1b858400c 100644
--- a/util/configlexer.lex
+++ b/util/configlexer.lex
@@ -315,6 +315,7 @@ remote-control{COLON} { YDVAR(0, VAR_REMOTE_CONTROL) }
control-enable{COLON} { YDVAR(1, VAR_CONTROL_ENABLE) }
control-interface{COLON} { YDVAR(1, VAR_CONTROL_INTERFACE) }
control-port{COLON} { YDVAR(1, VAR_CONTROL_PORT) }
+control-use-cert{COLON} { YDVAR(1, VAR_CONTROL_USE_CERT) }
server-key-file{COLON} { YDVAR(1, VAR_SERVER_KEY_FILE) }
server-cert-file{COLON} { YDVAR(1, VAR_SERVER_CERT_FILE) }
control-key-file{COLON} { YDVAR(1, VAR_CONTROL_KEY_FILE) }
diff --git a/util/configparser.y b/util/configparser.y
index 7a92d9ee7899..cbb5e16bd813 100644
--- a/util/configparser.y
+++ b/util/configparser.y
@@ -95,6 +95,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_PRIVATE_DOMAIN VAR_REMOTE_CONTROL VAR_CONTROL_ENABLE
%token VAR_CONTROL_INTERFACE VAR_CONTROL_PORT VAR_SERVER_KEY_FILE
%token VAR_SERVER_CERT_FILE VAR_CONTROL_KEY_FILE VAR_CONTROL_CERT_FILE
+%token VAR_CONTROL_USE_CERT
%token VAR_EXTENDED_STATISTICS VAR_LOCAL_DATA_PTR VAR_JOSTLE_TIMEOUT
%token VAR_STUB_PRIME VAR_UNWANTED_REPLY_THRESHOLD VAR_LOG_TIME_ASCII
%token VAR_DOMAIN_INSECURE VAR_PYTHON VAR_PYTHON_SCRIPT VAR_VAL_SIG_SKEW_MIN
@@ -1270,7 +1271,7 @@ contents_rc: contents_rc content_rc
| ;
content_rc: rc_control_enable | rc_control_interface | rc_control_port |
rc_server_key_file | rc_server_cert_file | rc_control_key_file |
- rc_control_cert_file
+ rc_control_cert_file | rc_control_use_cert
;
rc_control_enable: VAR_CONTROL_ENABLE STRING_ARG
{
@@ -1298,6 +1299,16 @@ rc_control_interface: VAR_CONTROL_INTERFACE STRING_ARG
yyerror("out of memory");
}
;
+rc_control_use_cert: VAR_CONTROL_USE_CERT STRING_ARG
+ {
+ OUTYY(("P(control_use_cert:%s)\n", $2));
+ if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
+ yyerror("expected yes or no.");
+ else cfg_parser->cfg->remote_control_use_cert =
+ (strcmp($2, "yes")==0);
+ free($2);
+ }
+ ;
rc_server_key_file: VAR_SERVER_KEY_FILE STRING_ARG
{
OUTYY(("P(rc_server_key_file:%s)\n", $2));
diff --git a/util/net_help.c b/util/net_help.c
index 8c2bac7372fc..335ee7499e7d 100644
--- a/util/net_help.c
+++ b/util/net_help.c
@@ -156,7 +156,7 @@ log_addr(enum verbosity_value v, const char* str,
case AF_INET6: family="ip6";
sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr;
break;
- case AF_UNIX: family="unix"; break;
+ case AF_LOCAL: family="local"; break;
default: break;
}
if(inet_ntop(af, sinaddr, dest, (socklen_t)sizeof(dest)) == 0) {
@@ -313,7 +313,7 @@ void log_name_addr(enum verbosity_value v, const char* str, uint8_t* zone,
case AF_INET6: family="";
sinaddr = &((struct sockaddr_in6*)addr)->sin6_addr;
break;
- case AF_UNIX: family="unix_family "; break;
+ case AF_LOCAL: family="local "; break;
default: break;
}
if(inet_ntop(af, sinaddr, dest, (socklen_t)sizeof(dest)) == 0) {