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 #endif +#ifdef HAVE_PWD_H +#include +#include +#include +#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 +#ifndef USE_WINSOCK +#include +#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 + /** 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 +#include #include #include @@ -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