Commit message: ospfd: CVE-2013-2236, stack overrun in apiserver

the OSPF API-server (exporting the LSDB and allowing announcement of
Opaque-LSAs) writes past the end of fixed on-stack buffers.  This leads
to an exploitable stack overflow.

For this condition to occur, the following two conditions must be true:
- Quagga is configured with --enable-opaque-lsa
- ospfd is started with the "-a" command line option

If either of these does not hold, the relevant code is not executed and
the issue does not get triggered.

Since the issue occurs on receiving large LSAs (larger than 1488 bytes),
it is possible for this to happen during normal operation of a network.
In particular, if there is an OSPF router with a large number of
interfaces, the Router-LSA of that router may exceed 1488 bytes and
trigger this, leading to an ospfd crash.

For an attacker to exploit this, s/he must be able to inject valid LSAs
into the OSPF domain.  Any best-practice protection measure (using
crypto authentication, restricting OSPF to internal interfaces, packet
filtering protocol 89, etc.) will prevent exploitation.  On top of that,
remote (not on an OSPF-speaking network segment) attackers will have
difficulties bringing up the adjacency needed to inject a LSA.

This patch only performs minimal changes to remove the possibility of a
stack overrun.  The OSPF API in general is quite ugly and needs a
rewrite.

Reported-by: Ricky Charlet <ricky.charlet@hp.com>
Cc: Florian Weimer <fweimer@redhat.com>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
CVE: CVE-2013-2236
CWE: CWE-119
Vulnerability description: Stack-based buffer overflow in the new_msg_lsa_change_notify function in the OSPFD API (ospf_api.c) in Quagga before 0.99.22.2, when --enable-opaque-lsa and the -a command line option are used, allows remote attackers to cause a denial of service (crash) via a large LSA.

Project: quagga
File: ospf_api.c

new_msg_reply (u_int32_t seqnr, u_char rc)
{
  struct msg *msg;
  struct msg_reply rmsg;

  /* Set return code */
  rmsg.errcode = rc;
  memset (&rmsg.pad, 0, sizeof (rmsg.pad));

  msg = msg_new (MSG_REPLY, &rmsg, seqnr, sizeof (struct msg_reply));

  return msg;
}

Project: quagga
File: ospf_api.c

new_msg_nsm_change (u_int32_t seqnr, struct in_addr ifaddr,
		    struct in_addr nbraddr,
		    struct in_addr router_id, u_char status)
{
  struct msg_nsm_change nmsg;

  nmsg.ifaddr = ifaddr;
  nmsg.nbraddr = nbraddr;
  nmsg.router_id = router_id;
  nmsg.status = status;
  memset (&nmsg.pad, 0, sizeof (nmsg.pad));

  return msg_new (MSG_NSM_CHANGE, &nmsg, seqnr,
		  sizeof (struct msg_nsm_change));
}

Project: quagga
File: ospf_api.c

new_msg_ready_notify (u_int32_t seqnr, u_char lsa_type,
		      u_char opaque_type, struct in_addr addr)
{
  struct msg_ready_notify rmsg;

  rmsg.lsa_type = lsa_type;
  rmsg.opaque_type = opaque_type;
  memset (&rmsg.pad, 0, sizeof (rmsg.pad));
  rmsg.addr = addr;

  return msg_new (MSG_READY_NOTIFY, &rmsg, seqnr,
		  sizeof (struct msg_ready_notify));
}

Project: quagga
File: ospf_api.c

new_msg_del_if (u_int32_t seqnr, struct in_addr ifaddr)
{
  struct msg_del_if dmsg;

  dmsg.ifaddr = ifaddr;

  return msg_new (MSG_DEL_IF, &dmsg, seqnr, sizeof (struct msg_del_if));
}

Project: quagga
File: ospf_api.c

new_msg_sync_lsdb (u_int32_t seqnum, struct lsa_filter_type *filter)
{
  u_char buf[OSPF_API_MAX_MSG_SIZE];
  struct msg_sync_lsdb *smsg;
  int len;

  smsg = (struct msg_sync_lsdb *) buf;
  len = sizeof (struct msg_sync_lsdb) +
    filter->num_areas * sizeof (struct in_addr);
  smsg->filter.typemask = htons (filter->typemask);
  smsg->filter.origin = filter->origin;
  smsg->filter.num_areas = filter->num_areas;
  if (len > sizeof (buf))
    len = sizeof(buf);
  /* API broken - missing memcpy to fill data */
  return msg_new (MSG_SYNC_LSDB, smsg, seqnum, len);
}

Project: quagga
File: ospf_api.c

new_msg_lsa_change_notify (u_char msgtype,
			   u_int32_t seqnum,
			   struct in_addr ifaddr,
			   struct in_addr area_id,
			   u_char is_self_originated, struct lsa_header *data)
{
  u_char buf[OSPF_API_MAX_MSG_SIZE];
  struct msg_lsa_change_notify *nmsg;
  int len;

  assert (data);

  nmsg = (struct msg_lsa_change_notify *) buf;
  nmsg->ifaddr = ifaddr;
  nmsg->area_id = area_id;
  nmsg->is_self_originated = is_self_originated;
  memset (&nmsg->pad, 0, sizeof (nmsg->pad));

  len = ntohs (data->length);
  if (len > sizeof (buf) - offsetof (struct msg_lsa_change_notify, data))
    len = sizeof (buf) - offsetof (struct msg_lsa_change_notify, data);
  memcpy (&nmsg->data, data, len);
  len += sizeof (struct msg_lsa_change_notify) - sizeof (struct lsa_header);

  return msg_new (msgtype, nmsg, seqnum, len);
}

Project: quagga
File: ospf_api.c

new_msg_new_if (u_int32_t seqnr,
		struct in_addr ifaddr, struct in_addr area_id)
{
  struct msg_new_if nmsg;

  nmsg.ifaddr = ifaddr;
  nmsg.area_id = area_id;

  return msg_new (MSG_NEW_IF, &nmsg, seqnr, sizeof (struct msg_new_if));
}

Project: quagga
File: ospf_api.c

new_msg_originate_request (u_int32_t seqnum,
			   struct in_addr ifaddr,
			   struct in_addr area_id, struct lsa_header *data)
{
  struct msg_originate_request *omsg;
  int omsglen;
  char buf[OSPF_API_MAX_MSG_SIZE];

  omsg = (struct msg_originate_request *) buf;
  omsg->ifaddr = ifaddr;
  omsg->area_id = area_id;

  omsglen = ntohs (data->length);
  if (omsglen > sizeof (buf) - offsetof (struct msg_originate_request, data))
    omsglen = sizeof (buf) - offsetof (struct msg_originate_request, data);
  memcpy (&omsg->data, data, omsglen);
  omsglen += sizeof (struct msg_originate_request) - sizeof (struct lsa_header);

  return msg_new (MSG_ORIGINATE_REQUEST, omsg, seqnum, omsglen);
}

Project: quagga
File: ospf_api.c

new_msg_delete_request (u_int32_t seqnum,
			struct in_addr area_id, u_char lsa_type,
			u_char opaque_type, u_int32_t opaque_id)
{
  struct msg_delete_request dmsg;
  dmsg.area_id = area_id;
  dmsg.lsa_type = lsa_type;
  dmsg.opaque_type = opaque_type;
  dmsg.opaque_id = htonl (opaque_id);
  memset (&dmsg.pad, 0, sizeof (dmsg.pad));

  return msg_new (MSG_DELETE_REQUEST, &dmsg, seqnum,
		  sizeof (struct msg_delete_request));
}

Project: quagga
File: ospf_api.c

new_msg_ism_change (u_int32_t seqnr, struct in_addr ifaddr,
		    struct in_addr area_id, u_char status)
{
  struct msg_ism_change imsg;

  imsg.ifaddr = ifaddr;
  imsg.area_id = area_id;
  imsg.status = status;
  memset (&imsg.pad, 0, sizeof (imsg.pad));

  return msg_new (MSG_ISM_CHANGE, &imsg, seqnr,
		  sizeof (struct msg_ism_change));
}