diff -Nru quagga-0.99.22.4/debian/changelog quagga-0.99.22.4/debian/changelog --- quagga-0.99.22.4/debian/changelog 2014-03-21 15:22:07.000000000 +0000 +++ quagga-0.99.22.4/debian/changelog 2016-10-12 20:04:35.000000000 +0000 @@ -1,3 +1,26 @@ +quagga (0.99.22.4-3ubuntu1.2) trusty-security; urgency=medium + + * SECURITY UPDATE: insecure directory permissions + - debian/quagga.postinst: set proper directory permissions on + /etc/quagga, /var/log/quagga, /var/run/quagga. + - CVE-2016-4036 + * SECURITY UPDATE: denial of service via a large BGP packet + - debian/patches/dump_fix.patch: create multiple MRT records if there + is too much data for a prefix in bgpd/bgp_dump.c. + - CVE-2016-4049 + + -- Marc Deslauriers Wed, 12 Oct 2016 16:03:58 -0400 + +quagga (0.99.22.4-3ubuntu1.1) trusty-security; urgency=medium + + * SECURITY UPDATE: denial of service or arbitrary code execution via + Labeled-VPN SAFI and crafted packet + - debian/patches/CVE-2016-2342.patch: sanity check lengths in + bgpd/bgp_mplsvpn.c. + - CVE-2016-2342 + + -- Marc Deslauriers Wed, 23 Mar 2016 08:15:51 -0400 + quagga (0.99.22.4-3ubuntu1) trusty; urgency=medium * Fix build failure with readline-6.3. diff -Nru quagga-0.99.22.4/debian/control quagga-0.99.22.4/debian/control --- quagga-0.99.22.4/debian/control 2014-01-01 18:23:09.000000000 +0000 +++ quagga-0.99.22.4/debian/control 2016-03-23 12:15:59.000000000 +0000 @@ -1,7 +1,8 @@ Source: quagga Section: net Priority: optional -Maintainer: Christian Hammers +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Christian Hammers Uploaders: Florian Weimer Build-Depends: debhelper (>= 7.0.50~), libncurses5-dev, libreadline-dev, texlive-latex-base, texlive-generic-recommended, libpam0g-dev | libpam-dev, libcap-dev, texinfo (>= 4.7), imagemagick, ghostscript, groff, po-debconf, autotools-dev, libpcre3-dev, gawk, chrpath, libsnmp-dev Standards-Version: 3.9.5 diff -Nru quagga-0.99.22.4/debian/patches/CVE-2016-2342.patch quagga-0.99.22.4/debian/patches/CVE-2016-2342.patch --- quagga-0.99.22.4/debian/patches/CVE-2016-2342.patch 1970-01-01 00:00:00.000000000 +0000 +++ quagga-0.99.22.4/debian/patches/CVE-2016-2342.patch 2016-03-23 12:15:26.000000000 +0000 @@ -0,0 +1,136 @@ +Backport of: + +From a3bc7e9400b214a0f078fdb19596ba54214a1442 Mon Sep 17 00:00:00 2001 +From: Donald Sharp +Date: Wed, 27 Jan 2016 16:54:45 +0000 +Subject: [PATCH] bgpd: Fix VU#270232, VPNv4 NLRI parser memcpys to stack on unchecked length + +Address CERT vulnerability report VU#270232, memcpy to stack data structure +based on length field from packet data whose length field upper-bound was +not properly checked. + +This likely allows BGP peers that are enabled to send Labeled-VPN SAFI +routes to Quagga bgpd to remotely exploit Quagga bgpd. + +Mitigation: Do not enable Labeled-VPN SAFI with untrusted neighbours. + +Impact: Labeled-VPN SAFI is not enabled by default. + +* bgp_mplsvpn.c: (bgp_nlri_parse_vpnv4) The prefixlen is checked for + lower-bound, but not for upper-bound against received data length. + The packet data is then memcpy'd to the stack based on the prefixlen. + + Extend the prefixlen check to ensure it is within the bound of the NLRI + packet data AND the on-stack prefix structure AND the maximum size for the + address family. + +Reported-by: Kostya Kortchinsky + +This commit a joint effort between: + +Lou Berger +Donald Sharp +Paul Jakma / +--- + bgpd/bgp_mplsvpn.c | 52 ++++++++++++++++++++++++++++++++++++---------------- + 1 files changed, 36 insertions(+), 16 deletions(-) + +Index: quagga-0.99.22.4/bgpd/bgp_mplsvpn.c +=================================================================== +--- quagga-0.99.22.4.orig/bgpd/bgp_mplsvpn.c 2016-03-23 08:14:18.279641211 -0400 ++++ quagga-0.99.22.4/bgpd/bgp_mplsvpn.c 2016-03-23 08:15:12.624302065 -0400 +@@ -102,6 +102,7 @@ + pnt = packet->nlri; + lim = pnt + packet->length; + ++#define VPN_PREFIXLEN_MIN_BYTES (3 + 8) /* label + RD */ + for (; pnt < lim; pnt += psize) + { + /* Clear prefix structure. */ +@@ -109,19 +110,40 @@ + + /* Fetch prefix length. */ + prefixlen = *pnt++; +- p.family = AF_INET; ++ p.family = afi2family (packet->afi); + psize = PSIZE (prefixlen); +- +- if (prefixlen < 88) +- { +- zlog_err ("prefix length is less than 88: %d", prefixlen); +- return -1; +- } +- ++ ++ /* sanity check against packet data */ ++ if (prefixlen < VPN_PREFIXLEN_MIN_BYTES*8 || (pnt + psize) > lim) ++ { ++ zlog_err ("prefix length (%d) is less than 88" ++ " or larger than received (%u)", ++ prefixlen, (uint)(lim-pnt)); ++ return -1; ++ } ++ ++ /* sanity check against storage for the IP address portion */ ++ if ((psize - VPN_PREFIXLEN_MIN_BYTES) > (ssize_t) sizeof(p.u)) ++ { ++ zlog_err ("prefix length (%d) exceeds prefix storage (%zu)", ++ prefixlen - VPN_PREFIXLEN_MIN_BYTES*8, sizeof(p.u)); ++ return -1; ++ } ++ ++ /* Sanity check against max bitlen of the address family */ ++ if ((psize - VPN_PREFIXLEN_MIN_BYTES) > prefix_blen (&p)) ++ { ++ zlog_err ("prefix length (%d) exceeds family (%u) max byte length (%u)", ++ prefixlen - VPN_PREFIXLEN_MIN_BYTES*8, ++ p.family, prefix_blen (&p)); ++ return -1; ++ ++ } ++ + label = decode_label (pnt); + + /* Copyr label to prefix. */ +- tagpnt = pnt;; ++ tagpnt = pnt; + + /* Copy routing distinguisher to rd. */ + memcpy (&prd.val, pnt + 3, 8); +@@ -140,8 +162,9 @@ + return -1; + } + +- p.prefixlen = prefixlen - 88; +- memcpy (&p.u.prefix, pnt + 11, psize - 11); ++ p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES*8; ++ memcpy (&p.u.prefix, pnt + VPN_PREFIXLEN_MIN_BYTES, ++ psize - VPN_PREFIXLEN_MIN_BYTES); + + #if 0 + if (type == RD_TYPE_AS) +@@ -152,9 +175,6 @@ + rd_ip.val, inet_ntoa (p.u.prefix4), p.prefixlen); + #endif /* 0 */ + +- if (pnt + psize > lim) +- return -1; +- + if (attr) + bgp_update (peer, &p, attr, AFI_IP, SAFI_MPLS_VPN, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0); +@@ -162,12 +182,12 @@ + bgp_withdraw (peer, &p, attr, AFI_IP, SAFI_MPLS_VPN, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt); + } +- + /* Packet length consistency check. */ + if (pnt != lim) + return -1; +- ++ + return 0; ++#undef VPN_PREFIXLEN_MIN_BYTES + } + + int diff -Nru quagga-0.99.22.4/debian/patches/dump_fix.patch quagga-0.99.22.4/debian/patches/dump_fix.patch --- quagga-0.99.22.4/debian/patches/dump_fix.patch 1970-01-01 00:00:00.000000000 +0000 +++ quagga-0.99.22.4/debian/patches/dump_fix.patch 2016-08-22 08:27:07.000000000 +0000 @@ -0,0 +1,207 @@ +Description: create multiple MRT records if there is too much data for a prefix. + See https://lists.quagga.net/pipermail/quagga-dev/2016-January/014699.html + (adjusted for 0.99.23) +Author: Evgeny Uskov + +--- a/bgpd/bgp_dump.c 2016-08-22 13:55:04.046255177 +0200 ++++ b/bgpd/bgp_dump.c 2016-08-22 13:55:58.390212180 +0200 +@@ -271,11 +271,94 @@ + } + + ++static struct bgp_info * ++bgp_dump_route_node_record (int afi, struct bgp_node *rn, struct bgp_info *info, unsigned int seq) ++{ ++ struct stream *obuf; ++ size_t sizep; ++ size_t endp; ++ ++ obuf = bgp_dump_obuf; ++ stream_reset(obuf); ++ ++ /* MRT header */ ++ if (afi == AFI_IP) ++ bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST); ++ else if (afi == AFI_IP6) ++ bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST); ++ ++ /* Sequence number */ ++ stream_putl(obuf, seq); ++ ++ /* Prefix length */ ++ stream_putc (obuf, rn->p.prefixlen); ++ ++ /* Prefix */ ++ if (afi == AFI_IP) ++ { ++ /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */ ++ stream_write(obuf, (u_char *)&rn->p.u.prefix4, (rn->p.prefixlen+7)/8); ++ } ++ else if (afi == AFI_IP6) ++ { ++ /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */ ++ stream_write (obuf, (u_char *)&rn->p.u.prefix6, (rn->p.prefixlen+7)/8); ++ } ++ ++ /* Save where we are now, so we can overwride the entry count later */ ++ sizep = stream_get_endp(obuf); ++ ++ /* Entry count */ ++ uint16_t entry_count = 0; ++ ++ /* Entry count, note that this is overwritten later */ ++ stream_putw(obuf, 0); ++ ++ endp = stream_get_endp(obuf); ++ for (; info; info = info->next) ++ { ++ size_t cur_endp; ++ ++ /* Peer index */ ++ stream_putw(obuf, info->peer->table_dump_index); ++ ++ /* Originated */ ++#ifdef HAVE_CLOCK_MONOTONIC ++ stream_putl (obuf, time(NULL) - (bgp_clock() - info->uptime)); ++#else ++ stream_putl (obuf, info->uptime); ++#endif /* HAVE_CLOCK_MONOTONIC */ ++ ++ /* Dump attribute. */ ++ /* Skip prefix & AFI/SAFI for MP_NLRI */ ++ bgp_dump_routes_attr (obuf, info->attr, &rn->p); ++ ++ cur_endp = stream_get_endp(obuf); ++ if (cur_endp > BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER ++ + BGP_DUMP_HEADER_SIZE) ++ { ++ stream_set_endp(obuf, endp); ++ break; ++ } ++ ++ entry_count++; ++ endp = cur_endp; ++ } ++ ++ /* Overwrite the entry count, now that we know the right number */ ++ stream_putw_at (obuf, sizep, entry_count); ++ ++ bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); ++ fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); ++ ++ return info; ++} ++ ++ + /* Runs under child process. */ + static unsigned int + bgp_dump_routes_func (int afi, int first_run, unsigned int seq) + { +- struct stream *obuf; + struct bgp_info *info; + struct bgp_node *rn; + struct bgp *bgp; +@@ -294,87 +377,17 @@ + if(first_run) + bgp_dump_routes_index_table(bgp); + +- obuf = bgp_dump_obuf; +- stream_reset(obuf); +- + /* Walk down each BGP route. */ + table = bgp->rib[afi][SAFI_UNICAST]; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + { +- if(!rn->info) +- continue; +- +- stream_reset(obuf); +- +- /* MRT header */ +- if (afi == AFI_IP) +- { +- bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST); +- } +-#ifdef HAVE_IPV6 +- else if (afi == AFI_IP6) +- { +- bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST); +- } +-#endif /* HAVE_IPV6 */ +- +- /* Sequence number */ +- stream_putl(obuf, seq); +- +- /* Prefix length */ +- stream_putc (obuf, rn->p.prefixlen); +- +- /* Prefix */ +- if (afi == AFI_IP) +- { +- /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */ +- stream_write(obuf, (u_char *)&rn->p.u.prefix4, (rn->p.prefixlen+7)/8); +- } +-#ifdef HAVE_IPV6 +- else if (afi == AFI_IP6) +- { +- /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */ +- stream_write (obuf, (u_char *)&rn->p.u.prefix6, (rn->p.prefixlen+7)/8); +- } +-#endif /* HAVE_IPV6 */ +- +- /* Save where we are now, so we can overwride the entry count later */ +- int sizep = stream_get_endp(obuf); +- +- /* Entry count */ +- uint16_t entry_count = 0; +- +- /* Entry count, note that this is overwritten later */ +- stream_putw(obuf, 0); +- +- for (info = rn->info; info; info = info->next) +- { +- entry_count++; +- +- /* Peer index */ +- stream_putw(obuf, info->peer->table_dump_index); +- +- /* Originated */ +-#ifdef HAVE_CLOCK_MONOTONIC +- stream_putl (obuf, time(NULL) - (bgp_clock() - info->uptime)); +-#else +- stream_putl (obuf, info->uptime); +-#endif /* HAVE_CLOCK_MONOTONIC */ +- +- /* Dump attribute. */ +- /* Skip prefix & AFI/SAFI for MP_NLRI */ +- bgp_dump_routes_attr (obuf, info->attr, &rn->p); +- } +- +- /* Overwrite the entry count, now that we know the right number */ +- stream_putw_at (obuf, sizep, entry_count); +- +- seq++; +- +- bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); +- fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); +- ++ info = rn->info; ++ while (info) ++ { ++ info = bgp_dump_route_node_record(afi, rn, info, seq); ++ seq++; ++ } + } + + fflush (bgp_dump_routes.fp); +@@ -854,8 +867,7 @@ + memset (&bgp_dump_updates, 0, sizeof (struct bgp_dump)); + memset (&bgp_dump_routes, 0, sizeof (struct bgp_dump)); + +- bgp_dump_obuf = stream_new (BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER +- + BGP_DUMP_HEADER_SIZE); ++ bgp_dump_obuf = stream_new (0x4000); + + install_node (&bgp_dump_node, config_write_bgp_dump); + diff -Nru quagga-0.99.22.4/debian/patches/series quagga-0.99.22.4/debian/patches/series --- quagga-0.99.22.4/debian/patches/series 2014-03-21 15:19:44.000000000 +0000 +++ quagga-0.99.22.4/debian/patches/series 2016-10-12 20:04:18.000000000 +0000 @@ -4,3 +4,5 @@ 75_vtysh__vtysh.c__PAGER.diff 50_vtysh__vtysh.conf.sample.diff readline-6.3.diff +CVE-2016-2342.patch +dump_fix.patch diff -Nru quagga-0.99.22.4/debian/quagga.postinst quagga-0.99.22.4/debian/quagga.postinst --- quagga-0.99.22.4/debian/quagga.postinst 2012-02-25 17:46:49.000000000 +0000 +++ quagga-0.99.22.4/debian/quagga.postinst 2016-10-12 20:04:24.000000000 +0000 @@ -3,6 +3,14 @@ if [ -n "$DEBIAN_SCRIPT_DEBUG" ]; then set -v -x; DEBIAN_SCRIPT_TRACE=1; fi ${DEBIAN_SCRIPT_TRACE:+ echo "#42#DEBUG# RUNNING $0 $*"} + +if [ "$1" = "configure" ]; then + # Fix permissions for installed elements. + find /etc/quagga -type f -print0 | xargs -0 -I{} sh -c 'chown quagga:quagga {} ; chmod 640 {}' + find /var/log/quagga -type f -print0 | xargs -0 -I{} sh -c 'chown quagga:quagga {} ; chmod 640 {}' + find /var/run/quagga -type f -print0 | xargs -0 -I{} sh -c 'chown quagga:quagga {} ; chmod 600 {}' +fi + # This is most likely due to the answer "no" to the "really stop the server" # question in the prerm script. if [ "$1" = "abort-upgrade" ]; then