diff options
| author | Denis Ovsienko <infrastation@yandex.ru> | 2011-09-26 13:18:51 +0400 | 
|---|---|---|
| committer | Denis Ovsienko <infrastation@yandex.ru> | 2011-09-26 18:40:48 +0400 | 
| commit | 552563a1c443ec876edd92bf79f29ff3afe2c01e (patch) | |
| tree | c0bf1cdbf045a493f1cbb4623e85724eeea4dc7f | |
| parent | 308687b7d73c5cacf927a3a33efbfaea627ccc09 (diff) | |
ospf6d: CVE-2011-3323 (fortify packet reception)
This vulnerability (CERT-FI #514840) was reported by CROSS project.
ospf6d processes IPv6 prefix structures in incoming packets without
verifying that the declared prefix length is valid. This leads to a
crash
caused by out of bounds memory access.
* ospf6_abr.h: new macros for size/alignment validation
* ospf6_asbr.h: idem
* ospf6_intra.h: idem
* ospf6_lsa.h: idem
* ospf6_message.h: idem
* ospf6_proto.h: idem
* ospf6_message.c
  * ospf6_packet_minlen: helper array for ospf6_packet_examin()
  * ospf6_lsa_minlen: helper array for ospf6_lsa_examin()
  * ospf6_hello_recv(): do not call ospf6_header_examin(), let upper
    layer verify the input data
  * ospf6_dbdesc_recv(): idem
  * ospf6_lsreq_recv(): idem
  * ospf6_lsupdate_recv(): idem
  * ospf6_lsack_recv(): idem
  * ospf6_prefixes_examin(): new function, implements A.4.1
  * ospf6_lsa_examin(): new function, implements A.4
  * ospf6_lsaseq_examin(): new function, an interface to above
  * ospf6_packet_examin(): new function, implements A.3
  * ospf6_rxpacket_examin(): new function, replaces
    ospf6_header_examin()
  * ospf6_header_examin(): sayonara
  * ospf6_receive(): perform passive interface check earliest possible,
    employ ospf6_rxpacket_examin()
| -rw-r--r-- | ospf6d/ospf6_abr.h | 2 | ||||
| -rw-r--r-- | ospf6d/ospf6_asbr.h | 1 | ||||
| -rw-r--r-- | ospf6d/ospf6_intra.h | 6 | ||||
| -rw-r--r-- | ospf6d/ospf6_lsa.h | 1 | ||||
| -rw-r--r-- | ospf6d/ospf6_message.c | 547 | ||||
| -rw-r--r-- | ospf6d/ospf6_message.h | 7 | ||||
| -rw-r--r-- | ospf6d/ospf6_proto.h | 1 | 
7 files changed, 492 insertions, 73 deletions
| diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h index 86d00280..816f5964 100644 --- a/ospf6d/ospf6_abr.h +++ b/ospf6d/ospf6_abr.h @@ -35,6 +35,7 @@ extern unsigned char conf_debug_ospf6_abr;    (conf_debug_ospf6_abr)  /* Inter-Area-Prefix-LSA */ +#define OSPF6_INTER_PREFIX_LSA_MIN_SIZE        4U /* w/o IPv6 prefix */  struct ospf6_inter_prefix_lsa  {    u_int32_t metric; @@ -42,6 +43,7 @@ struct ospf6_inter_prefix_lsa  };  /* Inter-Area-Router-LSA */ +#define OSPF6_INTER_ROUTER_LSA_FIX_SIZE       12U  struct ospf6_inter_router_lsa  {    u_char mbz; diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index 72644dfc..db4561de 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -51,6 +51,7 @@ struct ospf6_external_info  };  /* AS-External-LSA */ +#define OSPF6_AS_EXTERNAL_LSA_MIN_SIZE         4U /* w/o IPv6 prefix */  struct ospf6_as_external_lsa  {    u_int32_t bits_metric; diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h index 31643fd8..3810174e 100644 --- a/ospf6d/ospf6_intra.h +++ b/ospf6d/ospf6_intra.h @@ -69,6 +69,7 @@ extern u_int32_t conf_debug_ospf6_brouter_specific_area_id;     conf_debug_ospf6_brouter_specific_area_id == (area_id))  /* Router-LSA */ +#define OSPF6_ROUTER_LSA_MIN_SIZE              4U  struct ospf6_router_lsa  {    u_char bits; @@ -77,6 +78,7 @@ struct ospf6_router_lsa  };  /* Link State Description in Router-LSA */ +#define OSPF6_ROUTER_LSDESC_FIX_SIZE          16U  struct ospf6_router_lsdesc  {    u_char    type; @@ -105,6 +107,7 @@ struct ospf6_router_lsdesc    (((struct ospf6_router_lsdesc *)(x))->neighbor_router_id)  /* Network-LSA */ +#define OSPF6_NETWORK_LSA_MIN_SIZE             4U  struct ospf6_network_lsa  {    u_char reserved; @@ -113,6 +116,7 @@ struct ospf6_network_lsa  };  /* Link State Description in Router-LSA */ +#define OSPF6_NETWORK_LSDESC_FIX_SIZE          4U  struct ospf6_network_lsdesc  {    u_int32_t router_id; @@ -121,6 +125,7 @@ struct ospf6_network_lsdesc    (((struct ospf6_network_lsdesc *)(x))->router_id)  /* Link-LSA */ +#define OSPF6_LINK_LSA_MIN_SIZE               24U /* w/o 1st IPv6 prefix */  struct ospf6_link_lsa  {    u_char          priority; @@ -131,6 +136,7 @@ struct ospf6_link_lsa  };  /* Intra-Area-Prefix-LSA */ +#define OSPF6_INTRA_PREFIX_LSA_MIN_SIZE       12U /* w/o 1st IPv6 prefix */  struct ospf6_intra_prefix_lsa  {    u_int16_t prefix_num; diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index c5772768..13b89900 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -79,6 +79,7 @@    (ntohs (type) & OSPF6_LSTYPE_SCOPE_MASK)  /* LSA Header */ +#define OSPF6_LSA_HEADER_SIZE                 20U  struct ospf6_lsa_header  {    u_int16_t age;        /* LS age */ diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index beec272b..362e8a96 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -39,6 +39,11 @@  #include "ospf6_neighbor.h"  #include "ospf6_interface.h" +/* for structures and macros ospf6_lsa_examin() needs */ +#include "ospf6_abr.h" +#include "ospf6_asbr.h" +#include "ospf6_intra.h" +  #include "ospf6_flood.h"  #include "ospf6d.h" @@ -48,6 +53,34 @@ unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0};  const char *ospf6_message_type_str[] =    { "Unknown", "Hello", "DbDesc", "LSReq", "LSUpdate", "LSAck" }; +/* Minimum (besides the standard OSPF packet header) lengths for OSPF +   packets of particular types, offset is the "type" field. */ +const u_int16_t ospf6_packet_minlen[OSPF6_MESSAGE_TYPE_ALL] = +{ +  0, +  OSPF6_HELLO_MIN_SIZE, +  OSPF6_DB_DESC_MIN_SIZE, +  OSPF6_LS_REQ_MIN_SIZE, +  OSPF6_LS_UPD_MIN_SIZE, +  OSPF6_LS_ACK_MIN_SIZE +}; + +/* Minimum (besides the standard LSA header) lengths for LSAs of particular +   types, offset is the "LSA function code" portion of "LSA type" field. */ +const u_int16_t ospf6_lsa_minlen[OSPF6_LSTYPE_SIZE] = +{ +  0, +  /* 0x2001 */ OSPF6_ROUTER_LSA_MIN_SIZE, +  /* 0x2002 */ OSPF6_NETWORK_LSA_MIN_SIZE, +  /* 0x2003 */ OSPF6_INTER_PREFIX_LSA_MIN_SIZE, +  /* 0x2004 */ OSPF6_INTER_ROUTER_LSA_FIX_SIZE, +  /* 0x4005 */ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE, +  /* 0x2006 */ 0, +  /* 0x2007 */ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE, +  /* 0x0008 */ OSPF6_LINK_LSA_MIN_SIZE, +  /* 0x2009 */ OSPF6_INTRA_PREFIX_LSA_MIN_SIZE +}; +  /* print functions */  static void @@ -226,52 +259,6 @@ ospf6_lsack_print (struct ospf6_header *oh)      zlog_debug ("Trailing garbage exists");  } -/* Receive function */ -static int -ospf6_header_examin (struct in6_addr *src, struct in6_addr *dst, -                     struct ospf6_interface *oi, struct ospf6_header *oh) -{ -  u_char type; -  type = OSPF6_MESSAGE_TYPE_CANONICAL (oh->type); - -  /* version check */ -  if (oh->version != OSPFV3_VERSION) -    { -      if (IS_OSPF6_DEBUG_MESSAGE (type, RECV)) -        zlog_debug ("Message with unknown version"); -      return MSG_NG; -    } - -  /* Area-ID check */ -  if (oh->area_id != oi->area->area_id) -    { -      if (oh->area_id == BACKBONE_AREA_ID) -        { -          if (IS_OSPF6_DEBUG_MESSAGE (type, RECV)) -            zlog_debug ("Message may be via Virtual Link: not supported"); -          return MSG_NG; -        } - -      if (IS_OSPF6_DEBUG_MESSAGE (type, RECV)) -        zlog_debug ("Area-ID mismatch"); -      return MSG_NG; -    } - -  /* Instance-ID check */ -  if (oh->instance_id != oi->instance_id) -    { -      if (IS_OSPF6_DEBUG_MESSAGE (type, RECV)) -        zlog_debug ("Instance-ID mismatch"); -      return MSG_NG; -    } - -  /* Router-ID check */ -  if (oh->router_id == oi->area->ospf6->router_id) -    zlog_warn ("Detect duplicate Router-ID"); - -  return MSG_OK; -} -  static void  ospf6_hello_recv (struct in6_addr *src, struct in6_addr *dst,                    struct ospf6_interface *oi, struct ospf6_header *oh) @@ -283,9 +270,6 @@ ospf6_hello_recv (struct in6_addr *src, struct in6_addr *dst,    int neighborchange = 0;    int backupseen = 0; -  if (ospf6_header_examin (src, dst, oi, oh) != MSG_OK) -    return; -    hello = (struct ospf6_hello *)      ((caddr_t) oh + sizeof (struct ospf6_header)); @@ -817,9 +801,6 @@ ospf6_dbdesc_recv (struct in6_addr *src, struct in6_addr *dst,    struct ospf6_neighbor *on;    struct ospf6_dbdesc *dbdesc; -  if (ospf6_header_examin (src, dst, oi, oh) != MSG_OK) -    return; -    on = ospf6_neighbor_lookup (oh->router_id, oi);    if (on == NULL)      { @@ -869,9 +850,6 @@ ospf6_lsreq_recv (struct in6_addr *src, struct in6_addr *dst,    struct ospf6_lsdb *lsdb = NULL;    struct ospf6_lsa *lsa; -  if (ospf6_header_examin (src, dst, oi, oh) != MSG_OK) -    return; -    on = ospf6_neighbor_lookup (oh->router_id, oi);    if (on == NULL)      { @@ -946,6 +924,433 @@ ospf6_lsreq_recv (struct in6_addr *src, struct in6_addr *dst,      thread_add_event (master, ospf6_lsupdate_send_neighbor, on, 0);  } +/* Verify, that the specified memory area contains exactly N valid IPv6 +   prefixes as specified by RFC5340, A.4.1. */ +static unsigned +ospf6_prefixes_examin +( +  struct ospf6_prefix *current, /* start of buffer    */ +  unsigned length, +  const u_int32_t req_num_pfxs  /* always compared with the actual number of prefixes */ +) +{ +  u_char requested_pfx_bytes; +  u_int32_t real_num_pfxs = 0; + +  while (length) +  { +    if (length < OSPF6_PREFIX_MIN_SIZE) +    { +      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +        zlog_debug ("%s: undersized IPv6 prefix header", __func__); +      return MSG_NG; +    } +    /* safe to look deeper */ +    if (current->prefix_length > IPV6_MAX_BITLEN) +    { +      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +        zlog_debug ("%s: invalid PrefixLength (%u bits)", __func__, current->prefix_length); +      return MSG_NG; +    } +    /* covers both fixed- and variable-sized fields */ +    requested_pfx_bytes = OSPF6_PREFIX_MIN_SIZE + OSPF6_PREFIX_SPACE (current->prefix_length); +    if (requested_pfx_bytes > length) +    { +      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +        zlog_debug ("%s: undersized IPv6 prefix", __func__); +      return MSG_NG; +    } +    /* next prefix */ +    length -= requested_pfx_bytes; +    current = (struct ospf6_prefix *) ((caddr_t) current + requested_pfx_bytes); +    real_num_pfxs++; +  } +  if (real_num_pfxs != req_num_pfxs) +  { +    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +      zlog_debug ("%s: IPv6 prefix number mismatch (%u required, %u real)", +                  __func__, req_num_pfxs, real_num_pfxs); +    return MSG_NG; +  } +  return MSG_OK; +} + +/* Verify an LSA to have a valid length and dispatch further (where +   appropriate) to check if the contents, including nested IPv6 prefixes, +   is properly sized/aligned within the LSA. Note that this function gets +   LSA type in network byte order, uses in host byte order and passes to +   ospf6_lstype_name() in network byte order again. */ +static unsigned +ospf6_lsa_examin (struct ospf6_lsa_header *lsah, const u_int16_t lsalen, const u_char headeronly) +{ +  struct ospf6_intra_prefix_lsa *intra_prefix_lsa; +  struct ospf6_as_external_lsa *as_external_lsa; +  struct ospf6_link_lsa *link_lsa; +  unsigned exp_length; +  u_int8_t ltindex; +  u_int16_t lsatype; + +  /* In case an additional minimum length constraint is defined for current +     LSA type, make sure that this constraint is met. */ +  lsatype = ntohs (lsah->type); +  ltindex = lsatype & OSPF6_LSTYPE_FCODE_MASK; +  if +  ( +    ltindex < OSPF6_LSTYPE_SIZE && +    ospf6_lsa_minlen[ltindex] && +    lsalen < ospf6_lsa_minlen[ltindex] + OSPF6_LSA_HEADER_SIZE +  ) +  { +    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +      zlog_debug ("%s: undersized (%u B) LSA", __func__, lsalen); +    return MSG_NG; +  } +  switch (lsatype) +  { +  case OSPF6_LSTYPE_ROUTER: +    /* RFC5340 A.4.3, LSA header + OSPF6_ROUTER_LSA_MIN_SIZE bytes followed +       by N>=0 interface descriptions. */ +    if ((lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_ROUTER_LSA_MIN_SIZE) % OSPF6_ROUTER_LSDESC_FIX_SIZE) +    { +      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +        zlog_debug ("%s: interface description alignment error", __func__); +      return MSG_NG; +    } +    break; +  case OSPF6_LSTYPE_NETWORK: +    /* RFC5340 A.4.4, LSA header + OSPF6_NETWORK_LSA_MIN_SIZE bytes +       followed by N>=0 attached router descriptions. */ +    if ((lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_NETWORK_LSA_MIN_SIZE) % OSPF6_NETWORK_LSDESC_FIX_SIZE) +    { +      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +        zlog_debug ("%s: router description alignment error", __func__); +      return MSG_NG; +    } +    break; +  case OSPF6_LSTYPE_INTER_PREFIX: +    /* RFC5340 A.4.5, LSA header + OSPF6_INTER_PREFIX_LSA_MIN_SIZE bytes +       followed by 3-4 fields of a single IPv6 prefix. */ +    if (headeronly) +      break; +    return ospf6_prefixes_examin +    ( +      (struct ospf6_prefix *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE + OSPF6_INTER_PREFIX_LSA_MIN_SIZE), +      lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_INTER_PREFIX_LSA_MIN_SIZE, +      1 +    ); +  case OSPF6_LSTYPE_INTER_ROUTER: +    /* RFC5340 A.4.6, fixed-size LSA. */ +    if (lsalen > OSPF6_LSA_HEADER_SIZE + OSPF6_INTER_ROUTER_LSA_FIX_SIZE) +    { +      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +        zlog_debug ("%s: oversized (%u B) LSA", __func__, lsalen); +      return MSG_NG; +    } +    break; +  case OSPF6_LSTYPE_AS_EXTERNAL: /* RFC5340 A.4.7, same as A.4.8. */ +  case OSPF6_LSTYPE_TYPE_7: +    /* RFC5340 A.4.8, LSA header + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE bytes +       followed by 3-4 fields of IPv6 prefix and 3 conditional LSA fields: +       16 bytes of forwarding address, 4 bytes of external route tag, +       4 bytes of referenced link state ID. */ +    if (headeronly) +      break; +    as_external_lsa = (struct ospf6_as_external_lsa *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE); +    exp_length = OSPF6_LSA_HEADER_SIZE + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE; +    /* To find out if the last optional field (Referenced Link State ID) is +       assumed in this LSA, we need to access fixed fields of the IPv6 +       prefix before ospf6_prefix_examin() confirms its sizing. */ +    if (exp_length + OSPF6_PREFIX_MIN_SIZE > lsalen) +    { +      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +        zlog_debug ("%s: undersized (%u B) LSA header", __func__, lsalen); +      return MSG_NG; +    } +    /* forwarding address */ +    if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F)) +      exp_length += 16; +    /* external route tag */ +    if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) +      exp_length += 4; +    /* referenced link state ID */ +    if (as_external_lsa->prefix.u._prefix_referenced_lstype) +      exp_length += 4; +    /* All the fixed-size fields (mandatory and optional) must fit. I.e., +       this check does not include any IPv6 prefix fields. */ +    if (exp_length > lsalen) +    { +      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +        zlog_debug ("%s: undersized (%u B) LSA header", __func__, lsalen); +      return MSG_NG; +    } +    /* The last call completely covers the remainder (IPv6 prefix). */ +    return ospf6_prefixes_examin +    ( +      (struct ospf6_prefix *) ((caddr_t) as_external_lsa + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE), +      lsalen - exp_length, +      1 +    ); +  case OSPF6_LSTYPE_LINK: +    /* RFC5340 A.4.9, LSA header + OSPF6_LINK_LSA_MIN_SIZE bytes followed +       by N>=0 IPv6 prefix blocks (with N declared beforehand). */ +    if (headeronly) +      break; +    link_lsa = (struct ospf6_link_lsa *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE); +    return ospf6_prefixes_examin +    ( +      (struct ospf6_prefix *) ((caddr_t) link_lsa + OSPF6_LINK_LSA_MIN_SIZE), +      lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_LINK_LSA_MIN_SIZE, +      ntohl (link_lsa->prefix_num) /* 32 bits */ +    ); +  case OSPF6_LSTYPE_INTRA_PREFIX: +  /* RFC5340 A.4.10, LSA header + OSPF6_INTRA_PREFIX_LSA_MIN_SIZE bytes +     followed by N>=0 IPv6 prefixes (with N declared beforehand). */ +    if (headeronly) +      break; +    intra_prefix_lsa = (struct ospf6_intra_prefix_lsa *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE); +    return ospf6_prefixes_examin +    ( +      (struct ospf6_prefix *) ((caddr_t) intra_prefix_lsa + OSPF6_INTRA_PREFIX_LSA_MIN_SIZE), +      lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_INTRA_PREFIX_LSA_MIN_SIZE, +      ntohs (intra_prefix_lsa->prefix_num) /* 16 bits */ +    ); +  } +  /* No additional validation is possible for unknown LSA types, which are +     themselves valid in OPSFv3, hence the default decision is to accept. */ +  return MSG_OK; +} + +/* Verify if the provided input buffer is a valid sequence of LSAs. This +   includes verification of LSA blocks length/alignment and dispatching +   of deeper-level checks. */ +static unsigned +ospf6_lsaseq_examin +( +  struct ospf6_lsa_header *lsah, /* start of buffered data */ +  size_t length, +  const u_char headeronly, +  /* When declared_num_lsas is not 0, compare it to the real number of LSAs +     and treat the difference as an error. */ +  const u_int32_t declared_num_lsas +) +{ +  u_int32_t counted_lsas = 0; + +  while (length) +  { +    u_int16_t lsalen; +    if (length < OSPF6_LSA_HEADER_SIZE) +    { +      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +        zlog_debug ("%s: undersized (%u B) trailing (#%u) LSA header", +                    __func__, length, counted_lsas); +      return MSG_NG; +    } +    /* save on ntohs() calls here and in the LSA validator */ +    lsalen = OSPF6_LSA_SIZE (lsah); +    if (lsalen < OSPF6_LSA_HEADER_SIZE) +    { +      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +        zlog_debug ("%s: malformed LSA header #%u, declared length is %u B", +                    __func__, counted_lsas, lsalen); +      return MSG_NG; +    } +    if (headeronly) +    { +      /* less checks here and in ospf6_lsa_examin() */ +      if (MSG_OK != ospf6_lsa_examin (lsah, lsalen, 1)) +      { +        if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +          zlog_debug ("%s: anomaly in header-only %s LSA #%u", __func__, +                      ospf6_lstype_name (lsah->type), counted_lsas); +        return MSG_NG; +      } +      lsah = (struct ospf6_lsa_header *) ((caddr_t) lsah + OSPF6_LSA_HEADER_SIZE); +      length -= OSPF6_LSA_HEADER_SIZE; +    } +    else +    { +      /* make sure the input buffer is deep enough before further checks */ +      if (lsalen > length) +      { +        if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +          zlog_debug ("%s: anomaly in %s LSA #%u: declared length is %u B, buffered length is %u B", +                      __func__, ospf6_lstype_name (lsah->type), counted_lsas, lsalen, length); +        return MSG_NG; +      } +      if (MSG_OK != ospf6_lsa_examin (lsah, lsalen, 0)) +      { +        if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +          zlog_debug ("%s: anomaly in %s LSA #%u", __func__, +                      ospf6_lstype_name (lsah->type), counted_lsas); +        return MSG_NG; +      } +      lsah = (struct ospf6_lsa_header *) ((caddr_t) lsah + lsalen); +      length -= lsalen; +    } +    counted_lsas++; +  } + +  if (declared_num_lsas && counted_lsas != declared_num_lsas) +  { +    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +      zlog_debug ("%s: #LSAs declared (%u) does not match actual (%u)", +                  __func__, declared_num_lsas, counted_lsas); +    return MSG_NG; +  } +  return MSG_OK; +} + +/* Verify a complete OSPF packet for proper sizing/alignment. */ +static unsigned +ospf6_packet_examin (struct ospf6_header *oh, const unsigned bytesonwire) +{ +  struct ospf6_lsupdate *lsupd; +  unsigned test; + +  /* length, 1st approximation */ +  if (bytesonwire < OSPF6_HEADER_SIZE) +  { +    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +      zlog_debug ("%s: undersized (%u B) packet", __func__, bytesonwire); +    return MSG_NG; +  } +  /* Now it is safe to access header fields. */ +  if (bytesonwire != ntohs (oh->length)) +  { +    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +      zlog_debug ("%s: packet length error (%u real, %u declared)", +                  __func__, bytesonwire, ntohs (oh->length)); +    return MSG_NG; +  } +  /* version check */ +  if (oh->version != OSPFV3_VERSION) +  { +    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +      zlog_debug ("%s: invalid (%u) protocol version", __func__, oh->version); +    return MSG_NG; +  } +  /* length, 2nd approximation */ +  if +  ( +    oh->type < OSPF6_MESSAGE_TYPE_ALL && +    ospf6_packet_minlen[oh->type] && +    bytesonwire < OSPF6_HEADER_SIZE + ospf6_packet_minlen[oh->type] +  ) +  { +    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +      zlog_debug ("%s: undersized (%u B) %s packet", __func__, +                  bytesonwire, ospf6_message_type_str[oh->type]); +    return MSG_NG; +  } +  /* type-specific deeper validation */ +  switch (oh->type) +  { +  case OSPF6_MESSAGE_TYPE_HELLO: +    /* RFC5340 A.3.2, packet header + OSPF6_HELLO_MIN_SIZE bytes followed +       by N>=0 router-IDs. */ +    if (0 == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_HELLO_MIN_SIZE) % 4) +      return MSG_OK; +    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +      zlog_debug ("%s: alignment error in %s packet", +                  __func__, ospf6_message_type_str[oh->type]); +    return MSG_NG; +  case OSPF6_MESSAGE_TYPE_DBDESC: +    /* RFC5340 A.3.3, packet header + OSPF6_DB_DESC_MIN_SIZE bytes followed +       by N>=0 header-only LSAs. */ +    test = ospf6_lsaseq_examin +    ( +      (struct ospf6_lsa_header *) ((caddr_t) oh + OSPF6_HEADER_SIZE + OSPF6_DB_DESC_MIN_SIZE), +      bytesonwire - OSPF6_HEADER_SIZE - OSPF6_DB_DESC_MIN_SIZE, +      1, +      0 +    ); +    break; +  case OSPF6_MESSAGE_TYPE_LSREQ: +    /* RFC5340 A.3.4, packet header + N>=0 LS description blocks. */ +    if (0 == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_REQ_MIN_SIZE) % OSPF6_LSREQ_LSDESC_FIX_SIZE) +      return MSG_OK; +    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +      zlog_debug ("%s: alignment error in %s packet", +                  __func__, ospf6_message_type_str[oh->type]); +    return MSG_NG; +  case OSPF6_MESSAGE_TYPE_LSUPDATE: +    /* RFC5340 A.3.5, packet header + OSPF6_LS_UPD_MIN_SIZE bytes followed +       by N>=0 full LSAs (with N declared beforehand). */ +    lsupd = (struct ospf6_lsupdate *) ((caddr_t) oh + OSPF6_HEADER_SIZE); +    test = ospf6_lsaseq_examin +    ( +      (struct ospf6_lsa_header *) ((caddr_t) lsupd + OSPF6_LS_UPD_MIN_SIZE), +      bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_UPD_MIN_SIZE, +      0, +      ntohl (lsupd->lsa_number) /* 32 bits */ +    ); +    break; +  case OSPF6_MESSAGE_TYPE_LSACK: +    /* RFC5340 A.3.6, packet header + N>=0 header-only LSAs. */ +    test = ospf6_lsaseq_examin +    ( +      (struct ospf6_lsa_header *) ((caddr_t) oh + OSPF6_HEADER_SIZE + OSPF6_LS_ACK_MIN_SIZE), +      bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_ACK_MIN_SIZE, +      1, +      0 +    ); +    break; +  default: +    if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +      zlog_debug ("%s: invalid (%u) message type", __func__, oh->type); +    return MSG_NG; +  } +  if (test != MSG_OK && IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +    zlog_debug ("%s: anomaly in %s packet", __func__, ospf6_message_type_str[oh->type]); +  return test; +} + +/* Verify particular fields of otherwise correct received OSPF packet to +   meet the requirements of RFC. */ +static int +ospf6_rxpacket_examin (struct ospf6_interface *oi, struct ospf6_header *oh, const unsigned bytesonwire) +{ +  char buf[2][INET_ADDRSTRLEN]; + +  if (MSG_OK != ospf6_packet_examin (oh, bytesonwire)) +    return MSG_NG; + +  /* Area-ID check */ +  if (oh->area_id != oi->area->area_id) +  { +    if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) +    { +      if (oh->area_id == BACKBONE_AREA_ID) +        zlog_debug ("%s: Message may be via Virtual Link: not supported", __func__); +      else +        zlog_debug +        ( +          "%s: Area-ID mismatch (my %s, rcvd %s)", __func__, +          inet_ntop (AF_INET, &oi->area->area_id, buf[0], INET_ADDRSTRLEN), +          inet_ntop (AF_INET, &oh->area_id, buf[1], INET_ADDRSTRLEN) +         ); +    } +    return MSG_NG; +  } + +  /* Instance-ID check */ +  if (oh->instance_id != oi->instance_id) +  { +    if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) +      zlog_debug ("%s: Instance-ID mismatch (my %u, rcvd %u)", __func__, oi->instance_id, oh->instance_id); +    return MSG_NG; +  } + +  /* Router-ID check */ +  if (oh->router_id == oi->area->ospf6->router_id) +  { +    zlog_warn ("%s: Duplicate Router-ID (%s)", __func__, inet_ntop (AF_INET, &oh->router_id, buf[0], INET_ADDRSTRLEN)); +    return MSG_NG; +  } +  return MSG_OK; +} +  static void  ospf6_lsupdate_recv (struct in6_addr *src, struct in6_addr *dst,                       struct ospf6_interface *oi, struct ospf6_header *oh) @@ -955,9 +1360,6 @@ ospf6_lsupdate_recv (struct in6_addr *src, struct in6_addr *dst,    unsigned long num;    char *p; -  if (ospf6_header_examin (src, dst, oi, oh) != MSG_OK) -    return; -    on = ospf6_neighbor_lookup (oh->router_id, oi);    if (on == NULL)      { @@ -1035,8 +1437,6 @@ ospf6_lsack_recv (struct in6_addr *src, struct in6_addr *dst,    struct ospf6_lsdb *lsdb = NULL;    assert (oh->type == OSPF6_MESSAGE_TYPE_LSACK); -  if (ospf6_header_examin (src, dst, oi, oh) != MSG_OK) -    return;    on = ospf6_neighbor_lookup (oh->router_id, oi);    if (on == NULL) @@ -1201,11 +1601,6 @@ ospf6_receive (struct thread *thread)        zlog_err ("Excess message read");        return 0;      } -  else if (len < sizeof (struct ospf6_header)) -    { -      zlog_err ("Deficient message read"); -      return 0; -    }    oi = ospf6_interface_lookup_by_ifindex (ifindex);    if (oi == NULL || oi->area == NULL) @@ -1213,8 +1608,22 @@ ospf6_receive (struct thread *thread)        zlog_debug ("Message received on disabled interface");        return 0;      } +  if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE)) +    { +      if (IS_OSPF6_DEBUG_MESSAGE (OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) +        zlog_debug ("%s: Ignore message on passive interface %s", +                    __func__, oi->interface->name); +      return 0; +    }    oh = (struct ospf6_header *) recvbuf; +  if (ospf6_rxpacket_examin (oi, oh, len) != MSG_OK) +    return 0; + +  /* Being here means, that no sizing/alignment issues were detected in +     the input packet. This renders the additional checks performed below +     and also in the type-specific dispatching functions a dead code, +     which can be dismissed in a cleanup-focused review round later. */    /* Log */    if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) @@ -1251,14 +1660,6 @@ ospf6_receive (struct thread *thread)          }      } -  if (CHECK_FLAG (oi->flag, OSPF6_INTERFACE_PASSIVE)) -    { -      if (IS_OSPF6_DEBUG_MESSAGE (oh->type, RECV)) -        zlog_debug ("Ignore message on passive interface %s", -                   oi->interface->name); -      return 0; -    } -    switch (oh->type)      {        case OSPF6_MESSAGE_TYPE_HELLO: diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h index ebb6308e..b24d2e62 100644 --- a/ospf6d/ospf6_message.h +++ b/ospf6d/ospf6_message.h @@ -52,6 +52,7 @@ extern const char *ospf6_message_type_str[];    (ospf6_message_type_str[ OSPF6_MESSAGE_TYPE_CANONICAL (T) ])  /* OSPFv3 packet header */ +#define OSPF6_HEADER_SIZE                     16U  struct ospf6_header  {    u_char    version; @@ -67,6 +68,7 @@ struct ospf6_header  #define OSPF6_MESSAGE_END(H) ((caddr_t) (H) + ntohs ((H)->length))  /* Hello */ +#define OSPF6_HELLO_MIN_SIZE                  20U  struct ospf6_hello  {    u_int32_t interface_id; @@ -80,6 +82,7 @@ struct ospf6_hello  };  /* Database Description */ +#define OSPF6_DB_DESC_MIN_SIZE                12U  struct ospf6_dbdesc  {    u_char    reserved1; @@ -96,7 +99,9 @@ struct ospf6_dbdesc  #define OSPF6_DBDESC_IBIT  (0x04) /* initial bit */  /* Link State Request */ +#define OSPF6_LS_REQ_MIN_SIZE                  0U  /* It is just a sequence of entries below */ +#define OSPF6_LSREQ_LSDESC_FIX_SIZE           12U  struct ospf6_lsreq_entry  {    u_int16_t reserved;     /* Must Be Zero */ @@ -106,6 +111,7 @@ struct ospf6_lsreq_entry  };  /* Link State Update */ +#define OSPF6_LS_UPD_MIN_SIZE                  4U  struct ospf6_lsupdate  {    u_int32_t lsa_number; @@ -113,6 +119,7 @@ struct ospf6_lsupdate  };  /* Link State Acknowledgement */ +#define OSPF6_LS_ACK_MIN_SIZE                  0U  /* It is just a sequence of LSA Headers */  /* Function definition */ diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h index a8c1b1a0..64625004 100644 --- a/ospf6d/ospf6_proto.h +++ b/ospf6d/ospf6_proto.h @@ -73,6 +73,7 @@  #define OSPF6_OPT_V6 (1 << 0)   /* IPv6 forwarding Capability */  /* OSPF6 Prefix */ +#define OSPF6_PREFIX_MIN_SIZE                  4U /* .length == 0 */  struct ospf6_prefix  {    u_int8_t prefix_length; | 
