This patch fixes CVE-2016-4049, "bgpd bgp_dump_routes_func() Lets Remote Users
Cause the Target Service to Crash" This bug is fixed in Quagga version
1.1.0 and the patch can be removed after upgrading to a version later than
that.
See:
http://git.savannah.gnu.org/gitweb/?p=quagga.git;a=commit;h=b4e011985232f28d98e4df88c7cb13ee8f95ef46
*** bgpd/bgp_dump.c
--- bgpd/bgp_dump.c
*************** bgp_dump_routes_index_table(struct bgp *
*** 271,281 ****
}
/* 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;
--- 271,364 ----
}
+ 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 bgp_info *info;
struct bgp_node *rn;
struct bgp *bgp;
*************** bgp_dump_routes_func (int afi, int first
*** 294,380 ****
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);
!
}
fflush (bgp_dump_routes.fp);
--- 377,393 ----
if(first_run)
bgp_dump_routes_index_table(bgp);
/* Walk down each BGP route. */
table = bgp->rib[afi][SAFI_UNICAST];
for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
{
! info = rn->info;
! while (info)
! {
! info = bgp_dump_route_node_record(afi, rn, info, seq);
! seq++;
! }
}
fflush (bgp_dump_routes.fp);
*************** bgp_dump_init (void)
*** 854,861 ****
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);
install_node (&bgp_dump_node, config_write_bgp_dump);
--- 867,874 ----
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 << 1)
! + BGP_DUMP_MSG_HEADER + BGP_DUMP_HEADER_SIZE);
install_node (&bgp_dump_node, config_write_bgp_dump);
*** lib/stream.c
--- lib/stream.c
*************** stream_set_getp (struct stream *s, size_
*** 214,219 ****
--- 214,243 ----
s->getp = pos;
}
+ void
+ stream_set_endp (struct stream *s, size_t pos)
+ {
+ STREAM_VERIFY_SANE(s);
+
+ if (!ENDP_VALID(s, pos))
+ {
+ STREAM_BOUND_WARN (s, "set endp");
+ return;
+ }
+
+ /*
+ * Make sure the current read pointer is not beyond the new endp.
+ */
+ if (s->getp > pos)
+ {
+ STREAM_BOUND_WARN(s, "set endp");
+ return;
+ }
+
+ s->endp = pos;
+ STREAM_VERIFY_SANE(s);
+ }
+
/* Forward pointer. */
void
stream_forward_getp (struct stream *s, size_t size)
*** lib/stream.h
--- lib/stream.h
*************** extern size_t stream_get_size (struct st
*** 146,151 ****
--- 146,152 ----
extern u_char *stream_get_data (struct stream *);
extern void stream_set_getp (struct stream *, size_t);
+ extern void stream_set_endp (struct stream *, size_t);
extern void stream_forward_getp (struct stream *, size_t);
extern void stream_forward_endp (struct stream *, size_t);