components/quagga/patches/39-cve-2016-4049.patch
author Brian Utterback <brian.utterback@oracle.com>
Tue, 14 Feb 2017 18:22:31 -0800
changeset 7668 98611b4a801e
permissions -rw-r--r--
24928745 problem in SERVICE/QUAGGA

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);