summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCan Erkin Acar <canacar@cvs.openbsd.org>2004-03-12 18:40:17 +0000
committerCan Erkin Acar <canacar@cvs.openbsd.org>2004-03-12 18:40:17 +0000
commitd8cac78d3ded828ac290d48b4bceae795b625c1b (patch)
treeaf78c6baf0750f2dbc6c41c372b23503444c0744
parent219726e2ed4752974040e1ef99dd506299841401 (diff)
Privilege seperation for named. Allows named to handle address/interface
changes without restart. If you use non-standard ports in named configuration make sure they are > 1024. Also /var/named/etc/rndc.key (if any) must be readable by group named. Initial work and testing by itojun@, jakob@, hints, help from henning@, avsm@, beck@. ok henning, beck, avsm, deraadt
-rw-r--r--usr.sbin/bind/bin/named/main.c12
-rw-r--r--usr.sbin/bind/bin/named/server.c4
-rw-r--r--usr.sbin/bind/lib/isc/Makefile.in3
-rw-r--r--usr.sbin/bind/lib/isc/include/isc/socket.h2
-rw-r--r--usr.sbin/bind/lib/isc/unix/Makefile.in6
-rw-r--r--usr.sbin/bind/lib/isc/unix/include/isc/privsep.h42
-rw-r--r--usr.sbin/bind/lib/isc/unix/privsep.c404
-rw-r--r--usr.sbin/bind/lib/isc/unix/privsep_fdpass.c116
-rw-r--r--usr.sbin/bind/lib/isc/unix/socket.c13
9 files changed, 597 insertions, 5 deletions
diff --git a/usr.sbin/bind/bin/named/main.c b/usr.sbin/bind/bin/named/main.c
index 5a3ebbd4024..0eec853876d 100644
--- a/usr.sbin/bind/bin/named/main.c
+++ b/usr.sbin/bind/bin/named/main.c
@@ -31,6 +31,7 @@
#include <isc/hash.h>
#include <isc/os.h>
#include <isc/platform.h>
+#include <isc/privsep.h>
#include <isc/resource.h>
#include <isc/task.h>
#include <isc/timer.h>
@@ -511,7 +512,9 @@ setup(void) {
}
#endif
+#if 0 /* Not used due to privsep */
ns_os_chroot(ns_g_chrootdir);
+#endif
/*
* For operating systems which have a capability mechanism, now
@@ -538,6 +541,15 @@ setup(void) {
if (!ns_g_foreground)
ns_os_daemonize();
+ /*
+ * Privilege separation
+ */
+ isc_priv_init(ns_g_logstderr);
+ isc_drop_privs(ns_g_username);
+ isc_socket_privsep(1);
+
+ /* process is now unprivileged and inside a chroot */
+
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
ISC_LOG_NOTICE, "starting BIND %s%s", ns_g_version,
saved_command_line);
diff --git a/usr.sbin/bind/bin/named/server.c b/usr.sbin/bind/bin/named/server.c
index fee23ff2c15..e0449007e85 100644
--- a/usr.sbin/bind/bin/named/server.c
+++ b/usr.sbin/bind/bin/named/server.c
@@ -2069,10 +2069,12 @@ load_configuration(const char *filename, ns_server_t *server,
}
/*
- * Relinquish root privileges.
+ * Relinquish root privileges. Not used due to privsep
*/
+#if 0
if (first_time)
ns_os_changeuser();
+#endif
/*
* Configure the logging system.
diff --git a/usr.sbin/bind/lib/isc/Makefile.in b/usr.sbin/bind/lib/isc/Makefile.in
index 033827e6f61..6947247207d 100644
--- a/usr.sbin/bind/lib/isc/Makefile.in
+++ b/usr.sbin/bind/lib/isc/Makefile.in
@@ -36,7 +36,8 @@ UNIXOBJS = @ISC_ISCIPV6_O@ \
unix/errno2result.@O@ unix/file.@O@ unix/fsaccess.@O@ \
unix/interfaceiter.@O@ unix/keyboard.@O@ unix/net.@O@ \
unix/os.@O@ unix/resource.@O@ unix/socket.@O@ unix/stdio.@O@ \
- unix/stdtime.@O@ unix/strerror.@O@ unix/syslog.@O@ unix/time.@O@
+ unix/stdtime.@O@ unix/strerror.@O@ unix/syslog.@O@ unix/time.@O@ \
+ unix/privsep.@O@ unix/privsep_fdpass.@O@
NLSOBJS = nls/msgcat.@O@
diff --git a/usr.sbin/bind/lib/isc/include/isc/socket.h b/usr.sbin/bind/lib/isc/include/isc/socket.h
index 9915f3af4d1..2144c81d0b6 100644
--- a/usr.sbin/bind/lib/isc/include/isc/socket.h
+++ b/usr.sbin/bind/lib/isc/include/isc/socket.h
@@ -327,6 +327,8 @@ isc_socket_bind(isc_socket_t *sock, isc_sockaddr_t *addressp);
*/
isc_result_t
+isc_socket_privsep(int);
+isc_result_t
isc_socket_listen(isc_socket_t *sock, unsigned int backlog);
/*
* Set listen mode on the socket. After this call, the only function that
diff --git a/usr.sbin/bind/lib/isc/unix/Makefile.in b/usr.sbin/bind/lib/isc/unix/Makefile.in
index 769c14d6857..12d12b00e0f 100644
--- a/usr.sbin/bind/lib/isc/unix/Makefile.in
+++ b/usr.sbin/bind/lib/isc/unix/Makefile.in
@@ -33,14 +33,16 @@ OBJS = @ISC_IPV6_O@ \
app.@O@ dir.@O@ entropy.@O@ errno2result.@O@ file.@O@ \
fsaccess.@O@ interfaceiter.@O@ keyboard.@O@ net.@O@ \
os.@O@ resource.@O@ socket.@O@ stdio.@O@ stdtime.@O@ \
- strerror.@O@ syslog.@O@ time.@O@
+ strerror.@O@ syslog.@O@ time.@O@ \
+ privsep.@O@ privsep_fdpass.@O@
# Alphabetically
SRCS = @ISC_IPV6_C@ \
app.c dir.c entropy.c errno2result.c file.c \
fsaccess.c interfaceiter.c keyboard.c net.c \
os.c resource.c socket.c stdio.c stdtime.c \
- strerror.c syslog.c time.c
+ strerror.c syslog.c time.c \
+ privsep.c privsep_fdpass.c
SUBDIRS = include
TARGETS = ${OBJS}
diff --git a/usr.sbin/bind/lib/isc/unix/include/isc/privsep.h b/usr.sbin/bind/lib/isc/unix/include/isc/privsep.h
new file mode 100644
index 00000000000..5da4e2a6ed3
--- /dev/null
+++ b/usr.sbin/bind/lib/isc/unix/include/isc/privsep.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2003 Can Erkin Acar
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _PRIVSEP_H_
+#define _PRIVSEP_H_
+
+enum cmd_types {
+ PRIV_BIND /* bind to a privileged port */
+};
+
+/* Privilege separation */
+int isc_priv_init(int);
+int isc_drop_privs(const char *username);
+
+struct sockaddr;
+int isc_priv_bind(int, struct sockaddr *, socklen_t);
+
+/* File descriptor send/recv */
+void send_fd(int, int);
+int receive_fd(int);
+
+/* communications over the channel */
+int may_read(int, void *, size_t);
+void must_read(int, void *, size_t);
+void must_write(int, const void *, size_t);
+
+extern int priv_fd;
+
+#endif
diff --git a/usr.sbin/bind/lib/isc/unix/privsep.c b/usr.sbin/bind/lib/isc/unix/privsep.c
new file mode 100644
index 00000000000..22d890aed3f
--- /dev/null
+++ b/usr.sbin/bind/lib/isc/unix/privsep.c
@@ -0,0 +1,404 @@
+/* $OpenBSD: privsep.c,v 1.1 2004/03/12 18:40:15 canacar Exp $ */
+
+/*
+ * Copyright (c) 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2004 Can Erkin Acar <canacar@openbsd.org>
+ * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/privsep.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+enum priv_state {
+ STATE_RUN,
+ STATE_QUIT
+};
+
+/* allowed privileged port numbers */
+#define NAMED_PORT_DEFAULT 53
+#define RNDC_PORT_DEFAULT 953
+#define LWRES_PORT_DEFAULT 921
+
+int debug_level = LOG_DEBUG;
+int log_stderr = 1;
+int priv_fd = -1;
+
+static volatile pid_t child_pid = -1;
+static volatile sig_atomic_t cur_state = STATE_RUN;
+
+static int check_bind(const struct sockaddr *, socklen_t);
+static void fatal(const char *);
+static void logmsg(int, const char *, ...);
+static void parent_bind(int);
+static void sig_pass_to_chld(int);
+static void sig_got_chld(int);
+static void write_command(int, int);
+
+int
+isc_priv_init(int lstderr)
+{
+ int i, socks[2], cmd;
+
+ logmsg(LOG_NOTICE, "Starting privilege seperation");
+
+ log_stderr = lstderr;
+
+ /* Create sockets */
+ if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
+ fatal("socketpair() failed");
+
+ switch (child_pid = fork()) {
+ case -1:
+ fatal("failed to fork() for privsep");
+ case 0:
+ close(socks[0]);
+ priv_fd = socks[1];
+ return (0);
+ default:
+ break;
+ }
+
+ for (i = 1; i < _NSIG; i++)
+ signal(i, SIG_DFL);
+
+ signal(SIGALRM, sig_pass_to_chld);
+ signal(SIGTERM, sig_pass_to_chld);
+ signal(SIGHUP, sig_pass_to_chld);
+ signal(SIGCHLD, sig_got_chld);
+
+ /* Father - close unneeded sockets */
+ for (i = STDERR_FILENO + 1; i < socks[0]; i++)
+ close(i);
+ closefrom(socks[0] + 1);
+
+ setproctitle("[priv]");
+
+ while (cur_state != STATE_QUIT) {
+ if (may_read(socks[0], &cmd, sizeof(int)))
+ break;
+ switch (cmd) {
+ case PRIV_BIND:
+ parent_bind(socks[0]);
+ break;
+ default:
+ logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
+ _exit(1);
+ /* NOTREACHED */
+ }
+ }
+
+ _exit(0);
+}
+
+int
+isc_drop_privs(const char *username)
+{
+ struct passwd *pw;
+
+ if ((pw = getpwnam(username)) == NULL) {
+ logmsg(LOG_ERR, "unknown user %s", username);
+ exit(1);
+ }
+
+ if (chroot(pw->pw_dir) == -1)
+ fatal("chroot failed");
+
+ if (chdir("/"))
+ fatal("chdir failed");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setegid(pw->pw_gid) || setgid(pw->pw_gid) ||
+ seteuid(pw->pw_uid) || setuid(pw->pw_uid))
+ fatal("can't drop privileges");
+
+ endpwent();
+ return (0);
+}
+
+static int
+check_bind(const struct sockaddr *sa, socklen_t salen)
+{
+ const char *pname = child_pid ? "[priv]" : "[child]";
+ in_port_t port;
+
+ if (sa == NULL) {
+ logmsg(LOG_ERR, "%s: NULL address", pname);
+ return (1);
+ }
+
+ if (sa->sa_len != salen) {
+ logmsg(LOG_ERR, "%s: length mismatch: %d %d", pname,
+ (int) sa->sa_len, (int) salen);
+ return (1);
+ }
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ if (salen != sizeof(struct sockaddr_in)) {
+ logmsg(LOG_ERR, "%s: Invalid inet address length",
+ pname);
+ return (1);
+ }
+ port = ((const struct sockaddr_in *)sa)->sin_port;
+ break;
+ case AF_INET6:
+ if (salen != sizeof(struct sockaddr_in6)) {
+ logmsg(LOG_ERR, "%s: Invalid inet6 address length",
+ pname);
+ return (1);
+ }
+ port = ((const struct sockaddr_in6 *)sa)->sin6_port;
+ break;
+ default:
+ logmsg(LOG_ERR, "%s: unknown address family", pname);
+ return (1);
+ }
+
+ port = ntohs(port);
+
+ if (port != NAMED_PORT_DEFAULT && port != RNDC_PORT_DEFAULT &&
+ port != LWRES_PORT_DEFAULT) {
+ if (port || child_pid)
+ logmsg(LOG_ERR, "%s: disallowed port %u", pname, port);
+ return (1);
+ }
+
+ return (0);
+}
+
+static void
+parent_bind(int fd)
+{
+ int sock, status;
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+ int er;
+
+ logmsg(LOG_DEBUG, "[priv]: msg PRIV_BIND received");
+
+ sock = receive_fd(fd);
+ must_read(fd, &sslen, sizeof(sslen));
+ if (sslen == 0 || sslen > sizeof(ss))
+ _exit(1);
+
+ must_read(fd, &ss, sslen);
+
+ if (check_bind((struct sockaddr *) &ss, sslen))
+ _exit(1);
+
+ status = bind(sock, (struct sockaddr *)&ss, sslen);
+ er = errno;
+ must_write(fd, &er, sizeof(er));
+ must_write(fd, &status, sizeof(status));
+
+ close(sock);
+}
+
+/* Bind to allowed privileged ports using privsep, or try to bind locally */
+int
+isc_priv_bind(int fd, struct sockaddr *sa, socklen_t salen)
+{
+ int status, er;
+
+ if (priv_fd < 0)
+ errx(1, "%s called from privileged portion", __func__);
+
+ if (check_bind(sa, salen)) {
+ logmsg(LOG_DEBUG, "Binding locally");
+ status = bind(fd, sa, salen);
+ } else {
+ logmsg(LOG_DEBUG, "Binding privsep");
+ write_command(priv_fd, PRIV_BIND);
+ send_fd(priv_fd, fd);
+ must_write(priv_fd, &salen, sizeof(salen));
+ must_write(priv_fd, sa, salen);
+ must_read(priv_fd, &er, sizeof(er));
+ must_read(priv_fd, &status, sizeof(status));
+ errno = er;
+ }
+
+ return (status);
+}
+
+/* If priv parent gets a TERM or HUP, pass it through to child instead */
+static void
+sig_pass_to_chld(int sig)
+{
+ int save_err = errno;
+
+ if (child_pid != -1)
+ kill(child_pid, sig);
+ errno = save_err;
+}
+
+
+/* When child dies, move into the shutdown state */
+static void
+sig_got_chld(int sig)
+{
+ pid_t pid;
+ int status;
+ int save_err = errno;
+
+ do {
+ pid = waitpid(child_pid, &status, WNOHANG);
+ } while (pid == -1 && errno == EINTR);
+
+ if (pid == child_pid && (WIFEXITED(status) || WIFSIGNALED(status)) &&
+ cur_state < STATE_QUIT)
+ cur_state = STATE_QUIT;
+
+ errno = save_err;
+}
+
+/* Read all data or return 1 for error. */
+int
+may_read(int fd, void *buf, size_t n)
+{
+ char *s = buf;
+ ssize_t res;
+ size_t pos = 0;
+
+ while (n > pos) {
+ res = read(fd, s + pos, n - pos);
+ switch (res) {
+ case -1:
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ case 0:
+ return (1);
+ default:
+ pos += res;
+ }
+ }
+ return (0);
+}
+
+/* Read data with the assertion that it all must come through, or
+ * else abort the process. Based on atomicio() from openssh. */
+void
+must_read(int fd, void *buf, size_t n)
+{
+ char *s = buf;
+ ssize_t res;
+ size_t pos = 0;
+
+ while (n > pos) {
+ res = read(fd, s + pos, n - pos);
+ switch (res) {
+ case -1:
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ case 0:
+ _exit(0);
+ default:
+ pos += res;
+ }
+ }
+}
+
+/* Write data with the assertion that it all has to be written, or
+ * else abort the process. Based on atomicio() from openssh. */
+void
+must_write(int fd, const void *buf, size_t n)
+{
+ const char *s = buf;
+ ssize_t res;
+ size_t pos = 0;
+
+ while (n > pos) {
+ res = write(fd, s + pos, n - pos);
+ switch (res) {
+ case -1:
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ case 0:
+ _exit(0);
+ default:
+ pos += res;
+ }
+ }
+}
+
+
+/* write a command to the peer */
+static void
+write_command(int fd, int cmd)
+{
+ must_write(fd, &cmd, sizeof(cmd));
+}
+
+static void
+logmsg(int pri, const char *message, ...)
+{
+ va_list ap;
+ if (pri > debug_level)
+ return;
+
+ va_start(ap, message);
+ if (log_stderr) {
+ vfprintf(stderr, message, ap);
+ fprintf(stderr, "\n");
+ } else
+ vsyslog(pri, message, ap);
+
+ va_end(ap);
+}
+
+/* from bgpd */
+static void
+fatal(const char *emsg)
+{
+ const char *pname;
+
+ if (child_pid == -1)
+ pname = "bind";
+ else if (child_pid)
+ pname = "bind [priv]";
+ else
+ pname = "bind [child]";
+
+ if (emsg == NULL)
+ logmsg(LOG_CRIT, "fatal in %s: %s", pname, strerror(errno));
+ else
+ if (errno)
+ logmsg(LOG_CRIT, "fatal in %s: %s: %s",
+ pname, emsg, strerror(errno));
+ else
+ logmsg(LOG_CRIT, "fatal in %s: %s", pname, emsg);
+
+ if (child_pid)
+ _exit(1);
+ else /* parent copes via SIGCHLD */
+ exit(1);
+}
diff --git a/usr.sbin/bind/lib/isc/unix/privsep_fdpass.c b/usr.sbin/bind/lib/isc/unix/privsep_fdpass.c
new file mode 100644
index 00000000000..4a99118aa71
--- /dev/null
+++ b/usr.sbin/bind/lib/isc/unix/privsep_fdpass.c
@@ -0,0 +1,116 @@
+/* $OpenBSD: privsep_fdpass.c,v 1.1 2004/03/12 18:40:15 canacar Exp $ */
+
+/*
+ * Copyright 2001 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Copyright (c) 2002 Matthieu Herrb
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 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
+ * COPYRIGHT HOLDERS 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.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <isc/privsep.h>
+
+void
+send_fd(int sock, int fd)
+{
+ struct msghdr msg;
+ char tmp[CMSG_SPACE(sizeof(int))];
+ struct cmsghdr *cmsg;
+ struct iovec vec;
+ int result = 0;
+ ssize_t n;
+
+ memset(&msg, 0, sizeof(msg));
+
+ if (fd >= 0) {
+ msg.msg_control = (caddr_t)tmp;
+ msg.msg_controllen = CMSG_LEN(sizeof(int));
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int *)CMSG_DATA(cmsg) = fd;
+ } else
+ result = errno;
+
+ vec.iov_base = &result;
+ vec.iov_len = sizeof(int);
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+
+ if ((n = sendmsg(sock, &msg, 0)) == -1)
+ warn("%s: sendmsg(%d)", __func__, sock);
+ if (n != sizeof(int))
+ warnx("%s: sendmsg: expected sent 1 got %ld",
+ __func__, (long)n);
+}
+
+int
+receive_fd(int sock)
+{
+ struct msghdr msg;
+ char tmp[CMSG_SPACE(sizeof(int))];
+ struct cmsghdr *cmsg;
+ struct iovec vec;
+ ssize_t n;
+ int result;
+ int fd;
+
+ memset(&msg, 0, sizeof(msg));
+ vec.iov_base = &result;
+ vec.iov_len = sizeof(int);
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+ msg.msg_control = tmp;
+ msg.msg_controllen = sizeof(tmp);
+
+ if ((n = recvmsg(sock, &msg, 0)) == -1)
+ warn("%s: recvmsg", __func__);
+ if (n != sizeof(int))
+ warnx("%s: recvmsg: expected received 1 got %ld",
+ __func__, (long)n);
+ if (result == 0) {
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (cmsg->cmsg_type != SCM_RIGHTS)
+ warnx("%s: expected type %d got %d", __func__,
+ SCM_RIGHTS, cmsg->cmsg_type);
+ fd = (*(int *)CMSG_DATA(cmsg));
+ return (fd);
+ } else {
+ errno = result;
+ return (-1);
+ }
+}
diff --git a/usr.sbin/bind/lib/isc/unix/socket.c b/usr.sbin/bind/lib/isc/unix/socket.c
index ec0bd2cd8f1..3cfc215e735 100644
--- a/usr.sbin/bind/lib/isc/unix/socket.c
+++ b/usr.sbin/bind/lib/isc/unix/socket.c
@@ -44,6 +44,7 @@
#include <isc/net.h>
#include <isc/platform.h>
#include <isc/print.h>
+#include <isc/privsep.h>
#include <isc/region.h>
#include <isc/socket.h>
#include <isc/strerror.h>
@@ -229,6 +230,8 @@ struct isc_socketmgr {
static isc_socketmgr_t *socketmgr = NULL;
#endif /* ISC_PLATFORM_USETHREADS */
+static int privsep = 0;
+
#define CLOSED 0 /* this one must be zero */
#define MANAGED 1
#define CLOSE_PENDING 2
@@ -2800,7 +2803,9 @@ isc_socket_bind(isc_socket_t *sock, isc_sockaddr_t *sockaddr) {
ISC_MSG_FAILED, "failed"));
/* Press on... */
}
- if (bind(sock->fd, &sockaddr->type.sa, sockaddr->length) < 0) {
+ if ((privsep ?
+ isc_priv_bind(sock->fd, &sockaddr->type.sa, sockaddr->length) :
+ bind(sock->fd, &sockaddr->type.sa, sockaddr->length)) < 0) {
UNLOCK(&sock->lock);
switch (errno) {
case EACCES:
@@ -2827,6 +2832,12 @@ isc_socket_bind(isc_socket_t *sock, isc_sockaddr_t *sockaddr) {
return (ISC_R_SUCCESS);
}
+isc_result_t
+isc_socket_privsep(int flag) {
+ privsep = flag;
+ return (ISC_R_SUCCESS);
+}
+
/*
* Set up to listen on a given socket. We do this by creating an internal
* event that will be dispatched when the socket has read activity. The