]> xenbits.xensource.com Git - libvirt.git/commitdiff
network: use dnsmasq --bind-dynamic when available
authorLaine Stump <laine@laine.org>
Thu, 22 Nov 2012 02:21:02 +0000 (21:21 -0500)
committerLaine Stump <laine@laine.org>
Thu, 29 Nov 2012 20:02:39 +0000 (15:02 -0500)
This bug resolves CVE-2012-3411, which is described in the following
bugzilla report:

  https://bugzilla.redhat.com/show_bug.cgi?id=833033

The following report is specifically for libvirt on Fedora:

  https://bugzilla.redhat.com/show_bug.cgi?id=874702

In short, a dnsmasq instance run with the intention of listening for
DHCP/DNS requests only on a libvirt virtual network (which is
constructed using a Linux host bridge) would also answer queries sent
from outside the virtualization host.

This patch takes advantage of a new dnsmasq option "--bind-dynamic",
which will cause the listening socket to be setup such that it will
only receive those requests that actually come in via the bridge
interface. In order for this behavior to actually occur, not only must
"--bind-interfaces" be replaced with "--bind-dynamic", but also all
"--listen-address" options must be replaced with a single
"--interface" option. Fully:

   --bind-interfaces --except-interface lo --listen-address x.x.x.x ...

(with --listen-address possibly repeated) is replaced with:

   --bind-dynamic --interface virbrX

Of course libvirt can't use this new option if the host's dnsmasq
doesn't have it, but we still want libvirt to function (because the
great majority of libvirt installations, which only have mode='nat'
networks using RFC1918 private address ranges (e.g. 192.168.122.0/24),
are immune to this vulnerability from anywhere beyond the local subnet
of the host), so we use the new dnsmasqCaps API to check if dnsmasq
supports the new option and, if not, we use the "old" option style
instead. In order to assure that this permissiveness doesn't lead to a
vulnerable system, we do check for non-private addresses in this case,
and refuse to start the network if both a) we are using the old-style
options, and b) the network has a publicly routable IP
address. Hopefully this will provide the proper balance of not being
disruptive to those not practically affected, and making sure that
those who *are* affected get their dnsmasq upgraded.

(--bind-dynamic was added to dnsmasq in upstream commit
54dd393f3938fc0c19088fbd319b95e37d81a2b0, which was included in
dnsmasq-2.63)

src/network/bridge_driver.c
tests/networkxml2argvdata/isolated-network.argv
tests/networkxml2argvdata/nat-network-dns-hosts.argv
tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv
tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml
tests/networkxml2argvdata/nat-network-dns-srv-record.argv
tests/networkxml2argvdata/nat-network-dns-txt-record.argv
tests/networkxml2argvdata/nat-network.argv
tests/networkxml2argvdata/netboot-network.argv
tests/networkxml2argvdata/netboot-proxy-network.argv
tests/networkxml2argvdata/routed-network.argv

index 2387143f12fada8c2bfe85c12a44225322f273ee..75f3c3a3987285fe1a14398d2a2a773bace4584f 100644 (file)
@@ -657,7 +657,7 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
      * Needed to ensure dnsmasq uses same algorithm for processing
      * multiple namedriver entries in /etc/resolv.conf as GLibC.
      */
-    virCommandAddArgList(cmd, "--strict-order", "--bind-interfaces", NULL);
+    virCommandAddArg(cmd, "--strict-order");
 
     if (network->def->domain)
         virCommandAddArgPair(cmd, "--domain", network->def->domain);
@@ -672,9 +672,60 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
     /* *no* conf file */
     virCommandAddArg(cmd, "--conf-file=");
 
-    virCommandAddArgList(cmd,
-                         "--except-interface", "lo",
-                         NULL);
+    if (dnsmasqCapsGet(caps, DNSMASQ_CAPS_BIND_DYNAMIC)) {
+        /* using --bind-dynamic with only --interface (no
+         * --listen-address) prevents dnsmasq from responding to dns
+         * queries that arrive on some interface other than our bridge
+         * interface (in other words, requests originating somewhere
+         * other than one of the virtual guests connected directly to
+         * this network). This was added in response to CVE 2012-3411.
+         */
+        virCommandAddArgList(cmd,
+                             "--bind-dynamic",
+                             "--interface", network->def->bridge,
+                             NULL);
+    } else {
+        virCommandAddArgList(cmd,
+                             "--bind-interfaces",
+                             "--except-interface", "lo",
+                             NULL);
+        /*
+         * --interface does not actually work with dnsmasq < 2.47,
+         * due to DAD for ipv6 addresses on the interface.
+         *
+         * virCommandAddArgList(cmd, "--interface", network->def->bridge, NULL);
+         *
+         * So listen on all defined IPv[46] addresses
+         */
+        for (ii = 0;
+             (tmpipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
+             ii++) {
+            char *ipaddr = virSocketAddrFormat(&tmpipdef->address);
+
+            if (!ipaddr)
+                goto cleanup;
+            /* also part of CVE 2012-3411 - if the host's version of
+             * dnsmasq doesn't have --bind-dynamic, only allow listening on
+             * private/local IP addresses (see RFC1918/RFC3484/RFC4193)
+             */
+            if (!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 "
+                                 "(see CVE-2012-3411). You must either upgrade dnsmasq, "
+                                 "or use a private/local subnet range for this network "
+                                 "(as described in RFC1918/RFC3484/RFC4193)."), ipaddr,
+                               (int)version / 1000000, (int)(version % 1000000) / 1000);
+                goto cleanup;
+            }
+            virCommandAddArgList(cmd, "--listen-address", ipaddr, NULL);
+            VIR_FREE(ipaddr);
+        }
+    }
 
     /* If this is an isolated network, set the default route option
      * (3) to be empty to avoid setting a default route that's
@@ -740,24 +791,6 @@ networkBuildDnsmasqArgv(virNetworkObjPtr network,
         }
     }
 
-    /*
-     * --interface does not actually work with dnsmasq < 2.47,
-     * due to DAD for ipv6 addresses on the interface.
-     *
-     * virCommandAddArgList(cmd, "--interface", ipdef->bridge, NULL);
-     *
-     * So listen on all defined IPv[46] addresses
-     */
-    for (ii = 0;
-         (tmpipdef = virNetworkDefGetIpByIndex(network->def, AF_UNSPEC, ii));
-         ii++) {
-        char *ipaddr = virSocketAddrFormat(&tmpipdef->address);
-        if (!ipaddr)
-            goto cleanup;
-        virCommandAddArgList(cmd, "--listen-address", ipaddr, NULL);
-        VIR_FREE(ipaddr);
-    }
-
     if (ipdef) {
         for (r = 0 ; r < ipdef->nranges ; r++) {
             char *saddr = virSocketAddrFormat(&ipdef->ranges[r].start);
index 13e77b26a0fbf02ffb47c9fb423ada91decb46ea..3d8601e686fe57bbae3647aca404265f51f4dc90 100644 (file)
@@ -1,7 +1,8 @@
-@DNSMASQ@ --strict-order --bind-interfaces \
+@DNSMASQ@ --strict-order \
 --local=// --domain-needed --conf-file= \
---except-interface lo --dhcp-option=3 --no-resolv \
+--bind-interfaces --except-interface lo \
 --listen-address 192.168.152.1 \
+--dhcp-option=3 --no-resolv \
 --dhcp-range 192.168.152.2,192.168.152.254 \
 --dhcp-leasefile=/var/lib/libvirt/dnsmasq/private.leases --dhcp-lease-max=253 \
 --dhcp-no-override \
index 03a067693540dafd3823a34412e7f221c2db9b40..e5143acbbecb6ec518a457c2c423547d2946514f 100644 (file)
@@ -1,4 +1,5 @@
-@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \
+@DNSMASQ@ --strict-order --domain=example.com \
 --local=/example.com/ --domain-needed \
---conf-file= --except-interface lo --listen-address 192.168.122.1 \
+--conf-file= \
+--bind-dynamic --interface virbr0 \
 --expand-hosts --addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts\
index 210a60c01c9be2b66fa07bb991cd734dfb7f32a9..031da3fc3cd2def96a0f978219ace34927a6f22a 100644 (file)
@@ -1,14 +1,13 @@
 @DNSMASQ@ \
 --strict-order \
---bind-interfaces \
 --local=// --domain-needed --conf-file= \
---except-interface lo \
---srv-host=name.tcp.,,,, \
+--bind-interfaces --except-interface lo \
 --listen-address 192.168.122.1 \
 --listen-address 192.168.123.1 \
---listen-address 2001:db8:ac10:fe01::1 \
---listen-address 2001:db8:ac10:fd01::1 \
+--listen-address fc00:db8:ac10:fe01::1 \
+--listen-address fc00:db8:ac10:fd01::1 \
 --listen-address 10.24.10.1 \
+--srv-host=name.tcp.,,,, \
 --dhcp-range 192.168.122.2,192.168.122.254 \
 --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
 --dhcp-lease-max=253 \
index e9b7680603ce27cb67b9fb3bbe7c987edbcf9bff..f6f24e1208b56a0cf4e00ea157bb49addb0f0caf 100644 (file)
@@ -17,9 +17,9 @@
   </ip>
   <ip family='ipv4' address='192.168.123.1' netmask='255.255.255.0'>
   </ip>
-  <ip family='ipv6' address='2001:db8:ac10:fe01::1' prefix='64'>
+  <ip family='ipv6' address='fc00:db8:ac10:fe01::1' prefix='64'>
   </ip>
-  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+  <ip family='ipv6' address='fc00:db8:ac10:fd01::1' prefix='64'>
   </ip>
   <ip family='ipv4' address='10.24.10.1'>
   </ip>
index 833d3cd0f5134681b62b5dadd4269778e533372f..beff591a34dbe5b59bc0f25e8df11017a4924542 100644 (file)
@@ -1,14 +1,8 @@
 @DNSMASQ@ \
 --strict-order \
---bind-interfaces \
 --local=// --domain-needed --conf-file= \
---except-interface lo \
+--bind-dynamic --interface virbr0 \
 --srv-host=name.tcp.test-domain-name,.,1024,10,10 \
---listen-address 192.168.122.1 \
---listen-address 192.168.123.1 \
---listen-address 2001:db8:ac10:fe01::1 \
---listen-address 2001:db8:ac10:fd01::1 \
---listen-address 10.24.10.1 \
 --dhcp-range 192.168.122.2,192.168.122.254 \
 --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
 --dhcp-lease-max=253 \
index 348150779116738161069d10f947aa96ef445de7..fc164f6d78c37add9038616727b631c55de7d6b3 100644 (file)
@@ -1,9 +1,7 @@
-@DNSMASQ@ --strict-order --bind-interfaces \
+@DNSMASQ@ --strict-order \
 --local=// --domain-needed --conf-file= \
---except-interface lo '--txt-record=example,example value' \
---listen-address 192.168.122.1 --listen-address 192.168.123.1 \
---listen-address 2001:db8:ac10:fe01::1 \
---listen-address 2001:db8:ac10:fd01::1 --listen-address 10.24.10.1 \
+--bind-dynamic --interface virbr0 \
+'--txt-record=example,example value' \
 --dhcp-range 192.168.122.2,192.168.122.254 \
 --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
 --dhcp-lease-max=253 --dhcp-no-override \
index 37fd2fca91c4ca3d298ca4212710a820661a4137..6303f76a1f7e5000d55befd3a21d8152c7aeabfe 100644 (file)
@@ -1,8 +1,6 @@
-@DNSMASQ@ --strict-order --bind-interfaces \
+@DNSMASQ@ --strict-order \
 --local=// --domain-needed --conf-file= \
---except-interface lo --listen-address 192.168.122.1 \
---listen-address 192.168.123.1 --listen-address 2001:db8:ac10:fe01::1 \
---listen-address 2001:db8:ac10:fd01::1 --listen-address 10.24.10.1 \
+--bind-dynamic --interface virbr0 \
 --dhcp-range 192.168.122.2,192.168.122.254 \
 --dhcp-leasefile=/var/lib/libvirt/dnsmasq/default.leases \
 --dhcp-lease-max=253 --dhcp-no-override \
index 5408eb71c99d42777bc1ae0a97ed05d8cd04021c..9699e5c8ebac9717a1d0d0085a49be7fb4cc71d9 100644 (file)
@@ -1,6 +1,6 @@
-@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \
+@DNSMASQ@ --strict-order --domain=example.com \
 --local=/example.com/ --domain-needed --conf-file= \
---except-interface lo --listen-address 192.168.122.1 \
+--bind-interfaces --except-interface lo --listen-address 192.168.122.1 \
 --dhcp-range 192.168.122.2,192.168.122.254 \
 --dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases \
 --dhcp-lease-max=253 --dhcp-no-override --expand-hosts \
index 21e01e377f7962cbb404bf22af64eb315404c2a6..9ac301832eb84a799af2ccc337a33b03cb60478d 100644 (file)
@@ -1,6 +1,7 @@
-@DNSMASQ@ --strict-order --bind-interfaces --domain=example.com \
+@DNSMASQ@ --strict-order --domain=example.com \
 --local=/example.com/ --domain-needed --conf-file= \
---except-interface lo --listen-address 192.168.122.1 \
+--bind-interfaces --except-interface lo \
+--listen-address 192.168.122.1 \
 --dhcp-range 192.168.122.2,192.168.122.254 \
 --dhcp-leasefile=/var/lib/libvirt/dnsmasq/netboot.leases \
 --dhcp-lease-max=253 --dhcp-no-override --expand-hosts \
index 9fedb2b4621a8ec75be8da06973e2dc703545244..700c904a3fb97f0e8d5053dc71d50fc156c3c915 100644 (file)
@@ -1,4 +1,4 @@
-@DNSMASQ@ --strict-order --bind-interfaces \
+@DNSMASQ@ --strict-order \
 --local=// --domain-needed --conf-file= \
---except-interface lo --listen-address 192.168.122.1 \
+--bind-dynamic --interface virbr1 \
 --addn-hosts=/var/lib/libvirt/dnsmasq/local.addnhosts\