From 4b31da3478ea8d4279aaba4ebe55a332ce2772fa Mon Sep 17 00:00:00 2001 From: Laine Stump Date: Mon, 17 Dec 2012 12:49:18 -0500 Subject: [PATCH] network: don't require private addresses if dnsmasq uses SO_BINDTODEVICE This is yet another refinement to the fix for CVE-2012-3411: https://bugzilla.redhat.com/show_bug.cgi?id=833033 It turns out that it would be very intrusive to correctly backport the entire --bind-dynamic option to older dnsmasq versions (e.g. dnsmasq-2.48 that is used on RHEL6.x and CentOS 6.x), but very simple to patch those versions to just use SO_BINDTODEVICE on all their listening sockets (SO_BINDTODEVICE also has the desired effect of permitting only traffic that was received on the interface(s) where dnsmasq was set to listen.) This patch modifies the dnsmasq capabilities detection to detect the string: --bind-interfaces with SO_BINDTODEVICE in the output of "dnsmasq --version", and in that case realize that using the old --bind-interfaces option is just as safe as --bind-dynamic (and therefore *not* forbid creation of networks that use public IP address ranges). If -bind-dynamic is available, it is still preferred over --bind-interfaces. Note that this patch does no harm in upstream, or in any distro's downstream if it happens to end up there, but builds for distros that have a new enough dnsmasq to support --bind-dynamic do *NOT* need to specifically backport this patch; it's only required for distro releases that have dnsmasq too old to have --bind-dynamic (and those distros will need to add the SO_BINDTODEVICE patch to dnsmasq, *including the extra string in the --version output*, as well. --- src/network/bridge_driver.c | 10 ++++++---- src/util/dnsmasq.c | 18 ++++++++++++++---- src/util/dnsmasq.h | 1 + 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 7f3eb54f13..9740fbd143 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -726,15 +726,17 @@ networkDnsmasqConfContents(virNetworkObjPtr network, * dnsmasq doesn't have bind-dynamic, only allow listening on * private/local IP addresses (see RFC1918/RFC3484/RFC4193) */ - if (!virSocketAddrIsPrivate(&tmpipdef->address)) { + if (!dnsmasqCapsGet(caps, DNSMASQ_CAPS_BINDTODEVICE) && + !virSocketAddrIsPrivate(&tmpipdef->address)) { unsigned long version = dnsmasqCapsGetVersion(caps); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Publicly routable address %s is prohibited. " "The version of dnsmasq on this host (%d.%d) " - "doesn't support the bind-dynamic option, " - "which is required for safe operation on a " - "publicly routable subnet " + "doesn't support the bind-dynamic option or " + "use SO_BINDTODEVICE on listening sockets, " + "one of which is required for safe operation " + "on a publicly routable subnet " "(see CVE-2012-3411). You must either " "upgrade dnsmasq, or use a private/local " "subnet range for this network " diff --git a/src/util/dnsmasq.c b/src/util/dnsmasq.c index e8eab1e29e..8d714dc7a9 100644 --- a/src/util/dnsmasq.c +++ b/src/util/dnsmasq.c @@ -664,10 +664,20 @@ dnsmasqCapsSetFromBuffer(dnsmasqCapsPtr caps, const char *buf) if (strstr(buf, "--bind-dynamic")) dnsmasqCapsSet(caps, DNSMASQ_CAPS_BIND_DYNAMIC); - VIR_INFO("dnsmasq version is %d.%d, --bind-dynamic is %s", - (int)caps->version / 1000000, (int)(caps->version % 1000000) / 1000, - dnsmasqCapsGet(caps, DNSMASQ_CAPS_BIND_DYNAMIC) - ? "present" : "NOT present"); + /* if this string is a part of the --version output, dnsmasq + * has been patched to use SO_BINDTODEVICE when listening, + * so that it will only accept requests that arrived on the + * listening interface(s) + */ + if (strstr(buf, "--bind-interfaces with SO_BINDTODEVICE")) + dnsmasqCapsSet(caps, DNSMASQ_CAPS_BINDTODEVICE); + + VIR_INFO("dnsmasq version is %d.%d, --bind-dynamic is %spresent, " + "SO_BINDTODEVICE is %sin use", + (int)caps->version / 1000000, + (int)(caps->version % 1000000) / 1000, + dnsmasqCapsGet(caps, DNSMASQ_CAPS_BIND_DYNAMIC) ? "" : "NOT ", + dnsmasqCapsGet(caps, DNSMASQ_CAPS_BIND_DYNAMIC) ? "" : "NOT "); return 0; fail: diff --git a/src/util/dnsmasq.h b/src/util/dnsmasq.h index 7a39232e53..b83bc96c9e 100644 --- a/src/util/dnsmasq.h +++ b/src/util/dnsmasq.h @@ -68,6 +68,7 @@ typedef struct typedef enum { DNSMASQ_CAPS_BIND_DYNAMIC = 0, /* support for --bind-dynamic */ + DNSMASQ_CAPS_BINDTODEVICE = 1, /* uses SO_BINDTODEVICE for --bind-interfaces */ DNSMASQ_CAPS_LAST, /* this must always be the last item */ } dnsmasqCapsFlags; -- GitLab