Commit message: network plugin: Fix heap overflow in parse_packet().

Emilien Gaspar has identified a heap overflow in parse_packet(), the
function used by the network plugin to parse incoming network packets.

This is a vulnerability in collectd, though the scope is not clear at
this point. At the very least specially crafted network packets can be
used to crash the daemon. We can't rule out a potential remote code
execution though.

Fixes: CVE-2016-6254
CVE: CVE-2016-6254
CWE: CWE-119
Vulnerability description: Heap-based buffer overflow in the parse_packet function in network.c in collectd before 5.4.3 and 5.x before 5.5.2 allows remote attackers to cause a denial of service (daemon crash) or possibly execute arbitrary code via a crafted network packet.

Project: collectd
File: network.c

static int parse_part_encr_aes256 (sockent_t *se, /* {{{ */
    void **ret_buffer, size_t *ret_buffer_size, int flags)
{
  static int warning_has_been_printed = 0;

  char *buffer;
  size_t buffer_size;
  size_t buffer_offset;

  part_header_t ph;
  size_t ph_length;

  buffer = *ret_buffer;
  buffer_size = *ret_buffer_size;
  buffer_offset = 0;

  /* parse_packet assures this minimum size. */
  assert (buffer_size >= (sizeof (ph.type) + sizeof (ph.length)));

  BUFFER_READ (&ph.type, sizeof (ph.type));
  BUFFER_READ (&ph.length, sizeof (ph.length));
  ph_length = ntohs (ph.length);

  if ((ph_length <= PART_ENCRYPTION_AES256_SIZE)
      || (ph_length > buffer_size))
  {
    ERROR ("network plugin: AES-256 encrypted part "
        "with invalid length received.");
    return (-1);
  }

  if (warning_has_been_printed == 0)
  {
    WARNING ("network plugin: Received encrypted packet, but the network "
        "plugin was not linked with libgcrypt, so I cannot "
        "decrypt it. The part will be discarded.");
    warning_has_been_printed = 1;
  }

  *ret_buffer += ph_length;
  *ret_buffer_size -= ph_length;

  return (0);
} /* }}} int parse_part_encr_aes256 */

Project: collectd
File: network.c

static int sockent_add (sockent_t *se) /* {{{ */
{
	sockent_t *last_ptr;

	if (se == NULL)
		return (-1);

	if (se->type == SOCKENT_TYPE_SERVER)
	{
		struct pollfd *tmp;
		size_t i;

		tmp = realloc (listen_sockets_pollfd,
				sizeof (*tmp) * (listen_sockets_num
					+ se->data.server.fd_num));
		if (tmp == NULL)
		{
			ERROR ("network plugin: realloc failed.");
			return (-1);
		}
		listen_sockets_pollfd = tmp;
		tmp = listen_sockets_pollfd + listen_sockets_num;

		for (i = 0; i < se->data.server.fd_num; i++)
		{
			memset (tmp + i, 0, sizeof (*tmp));
			tmp[i].fd = se->data.server.fd[i];
			tmp[i].events = POLLIN | POLLPRI;
			tmp[i].revents = 0;
		}

		listen_sockets_num += se->data.server.fd_num;

		if (listen_sockets == NULL)
		{
			listen_sockets = se;
			return (0);
		}
		last_ptr = listen_sockets;
	}
	else /* if (se->type == SOCKENT_TYPE_CLIENT) */
	{
		if (sending_sockets == NULL)
		{
			sending_sockets = se;
			return (0);
		}
		last_ptr = sending_sockets;
	}

	while (last_ptr->next != NULL)
		last_ptr = last_ptr->next;
	last_ptr->next = se;

	return (0);
} /* }}} int sockent_add */

Project: collectd
File: network.c

static int write_part_string (char **ret_buffer, int *ret_buffer_len,
		int type, const char *str, int str_len)
{
	char *buffer;
	int buffer_len;

	uint16_t pkg_type;
	uint16_t pkg_length;

	int offset;

	buffer_len = 2 * sizeof (uint16_t) + str_len + 1;
	if (*ret_buffer_len < buffer_len)
		return (-1);

	pkg_type = htons (type);
	pkg_length = htons (buffer_len);

	buffer = *ret_buffer;
	offset = 0;
	memcpy (buffer + offset, (void *) &pkg_type, sizeof (pkg_type));
	offset += sizeof (pkg_type);
	memcpy (buffer + offset, (void *) &pkg_length, sizeof (pkg_length));
	offset += sizeof (pkg_length);
	memcpy (buffer + offset, str, str_len);
	offset += str_len;
	memset (buffer + offset, '\0', 1);
	offset += 1;

	assert (offset == buffer_len);

	*ret_buffer = buffer + buffer_len;
	*ret_buffer_len -= buffer_len;

	return (0);
} /* int write_part_string */