]> git.ipfire.org Git - thirdparty/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>
Fri, 30 Nov 2012 02:52:30 +0000 (21:52 -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)

(cherry picked from commit 753ff83a50263d6975f88d6605d4b5ddfcc97560)
Conflicts:
        src/network/bridge_driver.c
        * needed to change virReportError() to the older
          networkReportError()

tests/networkxml2argvdata/nat-network-dns-txt-record.argv
        * this test file has an example of an arg with embedded space,
          which gets sorrounded by '' in newer releases. Other
          items on the same line had been modified.

tests/networkxml2argvdata/routed-network.argv
        * in the newer releases, this test file had an --addn-hosts
          arg that didn't exist on this branch. Again, it was in the
          surrounding context of the changes that had been made on
          master.

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 b9332281e276f41f84679dbfaa81bdb5093d2aee..8010797d6cb59a00949a90844147ae41eeffb991 100644 (file)
@@ -495,7 +495,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);
@@ -510,9 +510,64 @@ 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);
+
+                networkReportError(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
@@ -578,24 +633,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 048c72b28709ba0c9dee6455678922df6e3d0c03..d629192e88fb8fb14da2275f6168fd80f9453922 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 a1e420055e93632d85f4dadc0d6e7495374f9954..c38b954979ae5eff449c34ea4ca475934acb9e9f 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 8af38c416f1c8a7107da69d9f2260fe69d4c8c99..311b0d76f923ebef44d0ba65c80fcfed0d9a006c 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 80a2fce3859f96bd3095198416b6854d5c949291..cbdf50db9a1f57ad8a9ec2cf62f7be388df29105 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 1dc8f73d16675a3acc3f169815dc116d82720b97..967ca945917dd4dbba45dc164f5b12ccb12d92d7 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 5a85ec2e7a2cfcce5504ae7cf1523303a38c722f..bcd6fad5d14ecbb7dc36c1240456997fbb41477e 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 --enable-tftp \
index 36836b09d32cd47b5b95a8e0bc00f164d68952ba..8c5ef9bf443dc2455b688b5ffeca0b87fa08e51c 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 77e802f8434360aedc719379bf2a1483d725428c..eacdf2d70a36f8ccf358d74cef39766aa8145aab 100644 (file)
@@ -1,3 +1,3 @@
-@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\