Commit message: _dbus_header_byteswap: change the first byte of the message, not just the struct member

This has been wrong approximately forever, for instance see:
http://lists.freedesktop.org/archives/dbus/2007-March/007357.html

Bug: https://bugs.freedesktop.org/show_bug.cgi?id=38120
Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=629938
Reviewed-by: Will Thompson <will.thompson@collabora.co.uk>
CVE: CVE-2011-2200
CWE: CWE-20
Vulnerability description: The _dbus_header_byteswap function in dbus-marshal-header.c in D-Bus (aka DBus) 1.2.x before 1.2.28, 1.4.x before 1.4.12, and 1.5.x before 1.5.4 does not properly handle a non-native byte order, which allows local users to cause a denial of service (connection loss), obtain potentially sensitive information, or conduct unspecified state-modification attacks via crafted messages.

Project: dbus
File: dbus-marshal-header.c

_dbus_header_get_field_basic (DBusHeader    *header,
                              int            field,
                              int            type,
                              void          *value)
{
  _dbus_assert (field != DBUS_HEADER_FIELD_INVALID);
  _dbus_assert (field <= DBUS_HEADER_FIELD_LAST);
  _dbus_assert (_dbus_header_field_types[field].code == field);
  /* in light of this you might ask why the type is passed in;
   * the only rationale I can think of is so the caller has
   * to specify its expectation and breaks if we change it
   */
  _dbus_assert (type == EXPECTED_TYPE_OF_FIELD (field));

  if (!_dbus_header_cache_check (header, field))
    return FALSE;

  _dbus_assert (header->fields[field].value_pos >= 0);

  _dbus_marshal_read_basic (&header->data,
                            header->fields[field].value_pos,
                            type, value, header->byte_order,
                            NULL);

  return TRUE;
}

Project: dbus
File: dbus-marshal-header.c

_dbus_header_get_message_type (DBusHeader *header)
{
  int type;

  type = _dbus_string_get_byte (&header->data, TYPE_OFFSET);
  _dbus_assert (type != DBUS_MESSAGE_TYPE_INVALID);

  return type;
}

Project: dbus
File: dbus-marshal-header.c

_dbus_header_load (DBusHeader        *header,
                   DBusValidationMode mode,
                   DBusValidity      *validity,
                   int                byte_order,
                   int                fields_array_len,
                   int                header_len,
                   int                body_len,
                   const DBusString  *str,
                   int                start,
                   int                len)
{
  int leftover;
  DBusValidity v;
  DBusTypeReader reader;
  DBusTypeReader array_reader;
  unsigned char v_byte;
  dbus_uint32_t v_uint32;
  dbus_uint32_t serial;
  int padding_start;
  int padding_len;
  int i;

  _dbus_assert (start == (int) _DBUS_ALIGN_VALUE (start, 8));
  _dbus_assert (header_len <= len);
  _dbus_assert (_dbus_string_get_length (&header->data) == 0);

  if (!_dbus_string_copy_len (str, start, header_len, &header->data, 0))
    {
      _dbus_verbose ("Failed to copy buffer into new header\n");
      *validity = DBUS_VALIDITY_UNKNOWN_OOM_ERROR;
      return FALSE;
    }

  if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
    {
      leftover = len - header_len - body_len - start;
    }
  else
    {
      v = _dbus_validate_body_with_reason (&_dbus_header_signature_str, 0,
                                           byte_order,
                                           &leftover,
                                           str, start, len);
      
      if (v != DBUS_VALID)
        {
          *validity = v;
          goto invalid;
        }
    }

  _dbus_assert (leftover < len);

  padding_len = header_len - (FIRST_FIELD_OFFSET + fields_array_len);
  padding_start = start + FIRST_FIELD_OFFSET + fields_array_len;
  _dbus_assert (start + header_len == (int) _DBUS_ALIGN_VALUE (padding_start, 8));
  _dbus_assert (start + header_len == padding_start + padding_len);

  if (mode != DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
    {
      if (!_dbus_string_validate_nul (str, padding_start, padding_len))
        {
          *validity = DBUS_INVALID_ALIGNMENT_PADDING_NOT_NUL;
          goto invalid;
        }
    }

  header->padding = padding_len;

  if (mode == DBUS_VALIDATION_MODE_WE_TRUST_THIS_DATA_ABSOLUTELY)
    {
      *validity = DBUS_VALID;
      return TRUE;
    }

  /* We now know the data is well-formed, but we have to check that
   * it's valid.
   */

  _dbus_type_reader_init (&reader,
                          byte_order,
                          &_dbus_header_signature_str, 0,
                          str, start);

  /* BYTE ORDER */
  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BYTE_ORDER_OFFSET);
  _dbus_type_reader_read_basic (&reader, &v_byte);
  _dbus_type_reader_next (&reader);

  _dbus_assert (v_byte == byte_order);
  header->byte_order = byte_order;

  /* MESSAGE TYPE */
  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == TYPE_OFFSET);
  _dbus_type_reader_read_basic (&reader, &v_byte);
  _dbus_type_reader_next (&reader);

  /* unknown message types are supposed to be ignored, so only validation here is
   * that it isn't invalid
   */
  if (v_byte == DBUS_MESSAGE_TYPE_INVALID)
    {
      *validity = DBUS_INVALID_BAD_MESSAGE_TYPE;
      goto invalid;
    }

  /* FLAGS */
  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FLAGS_OFFSET);
  _dbus_type_reader_read_basic (&reader, &v_byte);
  _dbus_type_reader_next (&reader);

  /* unknown flags should be ignored */

  /* PROTOCOL VERSION */
  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_BYTE);
  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == VERSION_OFFSET);
  _dbus_type_reader_read_basic (&reader, &v_byte);
  _dbus_type_reader_next (&reader);

  if (v_byte != DBUS_MAJOR_PROTOCOL_VERSION)
    {
      *validity = DBUS_INVALID_BAD_PROTOCOL_VERSION;
      goto invalid;
    }

  /* BODY LENGTH */
  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32);
  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == BODY_LENGTH_OFFSET);
  _dbus_type_reader_read_basic (&reader, &v_uint32);
  _dbus_type_reader_next (&reader);

  _dbus_assert (body_len == (signed) v_uint32);

  /* SERIAL */
  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_UINT32);
  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == SERIAL_OFFSET);
  _dbus_type_reader_read_basic (&reader, &serial);
  _dbus_type_reader_next (&reader);

  if (serial == 0)
    {
      *validity = DBUS_INVALID_BAD_SERIAL;
      goto invalid;
    }

  _dbus_assert (_dbus_type_reader_get_current_type (&reader) == DBUS_TYPE_ARRAY);
  _dbus_assert (_dbus_type_reader_get_value_pos (&reader) == FIELDS_ARRAY_LENGTH_OFFSET);

  _dbus_type_reader_recurse (&reader, &array_reader);
  while (_dbus_type_reader_get_current_type (&array_reader) != DBUS_TYPE_INVALID)
    {
      DBusTypeReader struct_reader;
      DBusTypeReader variant_reader;
      unsigned char field_code;

      _dbus_assert (_dbus_type_reader_get_current_type (&array_reader) == DBUS_TYPE_STRUCT);

      _dbus_type_reader_recurse (&array_reader, &struct_reader);

      _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_BYTE);
      _dbus_type_reader_read_basic (&struct_reader, &field_code);
      _dbus_type_reader_next (&struct_reader);

      if (field_code == DBUS_HEADER_FIELD_INVALID)
        {
          _dbus_verbose ("invalid header field code\n");
          *validity = DBUS_INVALID_HEADER_FIELD_CODE;
          goto invalid;
        }

      if (field_code > DBUS_HEADER_FIELD_LAST)
        {
          _dbus_verbose ("unknown header field code %d, skipping\n",
                         field_code);
          goto next_field;
        }

      _dbus_assert (_dbus_type_reader_get_current_type (&struct_reader) == DBUS_TYPE_VARIANT);
      _dbus_type_reader_recurse (&struct_reader, &variant_reader);

      v = load_and_validate_field (header, field_code, &variant_reader);
      if (v != DBUS_VALID)
        {
          _dbus_verbose ("Field %d was invalid\n", field_code);
          *validity = v;
          goto invalid;
        }

    next_field:
      _dbus_type_reader_next (&array_reader);
    }

  /* Anything we didn't fill in is now known not to exist */
  i = 0;
  while (i <= DBUS_HEADER_FIELD_LAST)
    {
      if (header->fields[i].value_pos == _DBUS_HEADER_FIELD_VALUE_UNKNOWN)
        header->fields[i].value_pos = _DBUS_HEADER_FIELD_VALUE_NONEXISTENT;
      ++i;
    }

  v = check_mandatory_fields (header);
  if (v != DBUS_VALID)
    {
      _dbus_verbose ("Mandatory fields were missing, code %d\n", v);
      *validity = v;
      goto invalid;
    }

  *validity = DBUS_VALID;
  return TRUE;

 invalid:
  _dbus_string_set_length (&header->data, 0);
  return FALSE;
}

Project: dbus
File: dbus-marshal-header.c

check_mandatory_fields (DBusHeader *header)
{
#define REQUIRE_FIELD(name) do { if (header->fields[DBUS_HEADER_FIELD_##name].value_pos < 0) return DBUS_INVALID_MISSING_##name; } while (0)

  switch (_dbus_header_get_message_type (header))
    {
    case DBUS_MESSAGE_TYPE_SIGNAL:
      REQUIRE_FIELD (INTERFACE);
      /* FALL THRU - signals also require the path and member */
    case DBUS_MESSAGE_TYPE_METHOD_CALL:
      REQUIRE_FIELD (PATH);
      REQUIRE_FIELD (MEMBER);
      break;
    case DBUS_MESSAGE_TYPE_ERROR:
      REQUIRE_FIELD (ERROR_NAME);
      REQUIRE_FIELD (REPLY_SERIAL);
      break;
    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
      REQUIRE_FIELD (REPLY_SERIAL);
      break;
    default:
      /* other message types allowed but ignored */
      break;
    }

  return DBUS_VALID;
}

Project: dbus
File: dbus-marshal-header.c

correct_header_padding (DBusHeader *header)
{
  int unpadded_len;

  _dbus_assert (header->padding == 7);

  _dbus_string_shorten (&header->data, header->padding);
  unpadded_len = _dbus_string_get_length (&header->data);

  if (!_dbus_string_align_length (&header->data, 8))
    _dbus_assert_not_reached ("couldn't pad header though enough padding was preallocated");

  header->padding = _dbus_string_get_length (&header->data) - unpadded_len;
}