On Nov 21, 2012, at 8:55 PM, Laine Stump <la...@laine.org> wrote: > 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 | 77 +++++++++++++++------- > tests/networkxml2argvdata/isolated-network.argv | 5 +- > .../networkxml2argvdata/nat-network-dns-hosts.argv | 5 +- > .../nat-network-dns-srv-record-minimal.argv | 9 ++- > .../nat-network-dns-srv-record-minimal.xml | 4 +- > .../nat-network-dns-srv-record.argv | 8 +-- > .../nat-network-dns-txt-record.argv | 8 +-- > tests/networkxml2argvdata/nat-network.argv | 6 +- > tests/networkxml2argvdata/netboot-network.argv | 4 +- > .../networkxml2argvdata/netboot-proxy-network.argv | 5 +- > tests/networkxml2argvdata/routed-network.argv | 4 +- > 11 files changed, 80 insertions(+), 55 deletions(-) > > diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c > index 34923ea..b3eb11c 100644 > --- a/src/network/bridge_driver.c > +++ b/src/network/bridge_driver.c > @@ -658,7 +658,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); > @@ -673,9 +673,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/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/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 > @@ -741,24 +792,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); > diff --git a/tests/networkxml2argvdata/isolated-network.argv > b/tests/networkxml2argvdata/isolated-network.argv > index 13e77b2..3d8601e 100644 > --- a/tests/networkxml2argvdata/isolated-network.argv > +++ b/tests/networkxml2argvdata/isolated-network.argv > @@ -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 \ > diff --git a/tests/networkxml2argvdata/nat-network-dns-hosts.argv > b/tests/networkxml2argvdata/nat-network-dns-hosts.argv > index 03a0676..e5143ac 100644 > --- a/tests/networkxml2argvdata/nat-network-dns-hosts.argv > +++ b/tests/networkxml2argvdata/nat-network-dns-hosts.argv > @@ -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\ > diff --git > a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv > b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv > index 210a60c..031da3f 100644 > --- a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv > +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.argv > @@ -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 \ > diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml > b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml > index e9b7680..f6f24e1 100644 > --- a/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml > +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record-minimal.xml > @@ -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> > diff --git a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv > b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv > index 833d3cd..beff591 100644 > --- a/tests/networkxml2argvdata/nat-network-dns-srv-record.argv > +++ b/tests/networkxml2argvdata/nat-network-dns-srv-record.argv > @@ -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 \ > diff --git a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv > b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv > index 3481507..fc164f6 100644 > --- a/tests/networkxml2argvdata/nat-network-dns-txt-record.argv > +++ b/tests/networkxml2argvdata/nat-network-dns-txt-record.argv > @@ -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 \ > diff --git a/tests/networkxml2argvdata/nat-network.argv > b/tests/networkxml2argvdata/nat-network.argv > index 37fd2fc..6303f76 100644 > --- a/tests/networkxml2argvdata/nat-network.argv > +++ b/tests/networkxml2argvdata/nat-network.argv > @@ -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 \ > diff --git a/tests/networkxml2argvdata/netboot-network.argv > b/tests/networkxml2argvdata/netboot-network.argv > index 5408eb7..9699e5c 100644 > --- a/tests/networkxml2argvdata/netboot-network.argv > +++ b/tests/networkxml2argvdata/netboot-network.argv > @@ -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 \ > diff --git a/tests/networkxml2argvdata/netboot-proxy-network.argv > b/tests/networkxml2argvdata/netboot-proxy-network.argv > index 21e01e3..9ac3018 100644 > --- a/tests/networkxml2argvdata/netboot-proxy-network.argv > +++ b/tests/networkxml2argvdata/netboot-proxy-network.argv > @@ -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 \ > diff --git a/tests/networkxml2argvdata/routed-network.argv > b/tests/networkxml2argvdata/routed-network.argv > index 9fedb2b..700c904 100644 > --- a/tests/networkxml2argvdata/routed-network.argv > +++ b/tests/networkxml2argvdata/routed-network.argv > @@ -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\ > -- > 1.7.11.7 > > -- > libvir-list mailing list > libvir-list@redhat.com > https://www.redhat.com/mailman/listinfo/libvir-list
ACK. This all looks good. -- Doug -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list