unbound_unixsock.diff   [plain text]


diff --git a/daemon/remote.c b/daemon/remote.c
index a2b2204..b6990f3 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -81,6 +81,11 @@
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#endif
 
 /* just for portability */
 #ifdef SQ
@@ -235,7 +240,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;
@@ -246,29 +252,74 @@ 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 UNIX socket! */
+		fd = create_domain_accept_sock(ip);
+/*
+ * When unbound starts, it first creates a socket and then
+ * drops privs, so the socket is created as root user.
+ * This is fine, but we would like to set _unbound user group
+ * for this socket, and permissions should be 0660 so only
+ * root and _unbound group members can invoke unbound-control.
+ * The username used here is the same as username that unbound
+ * uses for its worker processes.
+ */
+
+/*
+ * Note: this code is an exact copy of code from daemon.c
+ * Normally this should be either wrapped into a function,
+ * or gui/gid values should be retrieved at config parsing time
+ * and then stored in configfile structure.
+ * This requires action from unbound developers!
+*/
+#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();
 		}
+
+		chown(ip, 0, gid);
+		chmod(ip, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+#endif
+	} 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):""
 #else
 			""
 #endif
 			);
-		return 0;
+			return 0;
+		}
+
+		/* open fd */
+		fd = create_tcp_accept_sock(res, 1, &noproto);
+		freeaddrinfo(res);
 	}
 
-	/* open fd */
-	fd = create_tcp_accept_sock(res, 1, &noproto);
-	freeaddrinfo(res);
 	if(fd == -1 && noproto) {
 		if(!noproto_is_err)
 			return 1; /* return success, but do nothing */
@@ -305,7 +356,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;
 			}
@@ -313,12 +364,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;
 		}
diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c
index ea7ec3a..4cb04e2 100644
--- a/services/listen_dnsport.c
+++ b/services/listen_dnsport.c
@@ -55,6 +55,10 @@
 #endif
 #include <fcntl.h>
 
+#ifndef USE_WINSOCK
+#include <sys/un.h>
+#endif
+
 /** number of queued TCP connections for listen() */
 #define TCP_BACKLOG 5 
 
@@ -376,6 +380,53 @@ create_udp_sock(int family, int socktype, struct sockaddr* addr,
 }
 
 int
+create_domain_accept_sock(char *path) {
+	int s;
+	struct sockaddr_un unixaddr;
+
+#ifndef USE_WINSOCK
+	unixaddr.sun_len = sizeof(unixaddr);
+	unixaddr.sun_family = AF_UNIX;
+	strlcpy(unixaddr.sun_path, path, 104);
+
+	if((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+		log_err("Cannot create UNIX 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 UNIX socket %s (%s)",
+			path, strerror(errno));
+		return -1;
+	}
+
+	if(bind(s, (struct sockaddr *) &unixaddr,
+		sizeof(struct sockaddr_un)) == -1) {
+		log_err("Cannot bind UNIX 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("UNIX sockets are not supported");
+	return -1;
+#endif
+}
+
+int
 create_tcp_accept_sock(struct addrinfo *addr, int v6only, int* noproto)
 {
 	int s;
diff --git a/smallapp/unbound-control.c b/smallapp/unbound-control.c
index a872f92..10631fd 100644
--- a/smallapp/unbound-control.c
+++ b/smallapp/unbound-control.c
@@ -59,6 +59,8 @@
 #include "util/locks.h"
 #include "util/net_help.h"
 
+#include <sys/un.h>
+
 /** Give unbound-control usage, and exit (1). */
 static void
 usage()
@@ -158,6 +160,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) {
@@ -176,12 +179,21 @@ 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);
+	} else if(svr[0] == '/') {
+		struct sockaddr_un* unixsock = (struct sockaddr_un *) &addr;
+		unixsock->sun_family = AF_UNIX;
+		unixsock->sun_len = sizeof(unixsock);
+		strlcpy(unixsock->sun_path, svr, 104);
+		addrlen = sizeof(struct sockaddr_un);
+		addrfamily = AF_UNIX;
 	} 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 != AF_UNIX)
+		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));
diff --git a/util/net_help.c b/util/net_help.c
index b3136a3..5b5b4a3 100644
--- a/util/net_help.c
+++ b/util/net_help.c
@@ -45,6 +45,7 @@
 #include "util/module.h"
 #include "util/regional.h"
 #include <fcntl.h>
+#include <sys/un.h>
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 
@@ -135,7 +136,7 @@ log_addr(enum verbosity_value v, const char* str,
 {
 	uint16_t port;
 	const char* family = "unknown";
-	char dest[100];
+	char dest[108];
 	int af = (int)((struct sockaddr_in*)addr)->sin_family;
 	void* sinaddr = &((struct sockaddr_in*)addr)->sin_addr;
 	if(verbosity < v)
@@ -148,15 +149,23 @@ log_addr(enum verbosity_value v, const char* str,
 		case AF_UNIX: family="unix"; break;
 		default: break;
 	}
-	if(inet_ntop(af, sinaddr, dest, (socklen_t)sizeof(dest)) == 0) {
-		strncpy(dest, "(inet_ntop error)", sizeof(dest));
+
+	if(af != AF_UNIX) {
+		if(inet_ntop(af, sinaddr, dest, (socklen_t)sizeof(dest)) == 0) {
+			strncpy(dest, "(inet_ntop error)", sizeof(dest));
+		}
+		dest[sizeof(dest)-1] = 0;
+		port = ntohs(((struct sockaddr_in*)addr)->sin_port);
+		if(verbosity >= 4)
+			verbose(v, "%s %s %s port %d (len %d)", str, family,
+				dest, (int)port, (int)addrlen);
+		else	verbose(v, "%s %s port %d", str, dest, (int)port);
+	} else {
+		struct sockaddr_un* unixsock;
+		unixsock = (struct sockaddr_un *) addr;
+		strlcpy(dest, unixsock->sun_path, sizeof(dest));
+		verbose(v, "%s %s %s", str, family, dest);
 	}
-	dest[sizeof(dest)-1] = 0;
-	port = ntohs(((struct sockaddr_in*)addr)->sin_port);
-	if(verbosity >= 4)
-		verbose(v, "%s %s %s port %d (len %d)", str, family, dest, 
-			(int)port, (int)addrlen);
-	else	verbose(v, "%s %s port %d", str, dest, (int)port);
 }
 
 int