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.
This commit is contained in:
parent
7954be7fa5
commit
3ff5baf8cb
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/vendor/unbound/dist/; revision=276698
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
118
daemon/remote.c
118
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 "
|
||||
|
@ -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 */
|
||||
|
@ -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));
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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) }
|
||||
|
@ -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));
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user