Commit message: CVE 2010-4352: Reject deeply nested variants

Add DBUS_INVALID_NESTED_TOO_DEEPLY validity problem and a test that
should generate it.

Previously, we rejected deep nesting in the signature, but
variants allow dynamic message nesting, conditional only
on the depth of the message body.

The nesting limit is 64, which was also the limit in static
signatures.  Empirically, dynamic nesting depth observed on my
Fedora 14 system doesn't exceed 2; 64 is really a huge limit.

https://bugs.freedesktop.org/show_bug.cgi?id=32321

Signed-Off-By: Colin Walters <walters@verbum.org>
Signed-off-by: Will Thompson <will.thompson@collabora.co.uk>
CVE: CVE-2010-4352
CWE: CWE-399
Vulnerability description: Stack consumption vulnerability in D-Bus (aka DBus) before 1.4.1 allows local users to cause a denial of service (daemon crash) via a message containing many nested variants.

Project: dbus
File: dbus-message-factory.c

generate_from_message (DBusString            *data,
                       DBusValidity          *expected_validity,
                       DBusMessage           *message)
{
  dbus_message_set_serial (message, 1);
  dbus_message_lock (message);

  *expected_validity = DBUS_VALID;
  
  /* move for efficiency, since we'll nuke the message anyway */
  if (!_dbus_string_move (&message->header.data, 0,
                          data, 0))
    _dbus_assert_not_reached ("oom");

  if (!_dbus_string_copy (&message->body, 0,
                          data, _dbus_string_get_length (data)))
    _dbus_assert_not_reached ("oom");
}

Project: dbus
File: dbus-message-factory.c

generate_many_bodies (DBusMessageDataIter   *iter,
                      DBusString            *data,
                      DBusValidity          *expected_validity)
{
  return generate_outer (iter, data, expected_validity,
                         generate_many_bodies_inner);
}

Project: dbus
File: dbus-message-factory.c

generate_many_bodies_inner (DBusMessageDataIter *iter,
                            DBusMessage        **message_p)
{
  DBusMessage *message;
  DBusString signature;
  DBusString body;

  /* Keeping this small makes things go faster */
  message = dbus_message_new_method_call ("o.z.F",
                                          "/",
                                          "o.z.B",
                                          "Nah");
  if (message == NULL)
    _dbus_assert_not_reached ("oom");

  set_reply_serial (message);

  if (!_dbus_string_init (&signature) || !_dbus_string_init (&body))
    _dbus_assert_not_reached ("oom");
  
  if (dbus_internal_do_not_use_generate_bodies (iter_get_sequence (iter),
                                                message->byte_order,
                                                &signature, &body))
    {
      const char *v_SIGNATURE;

      v_SIGNATURE = _dbus_string_get_const_data (&signature);
      if (!_dbus_header_set_field_basic (&message->header,
                                         DBUS_HEADER_FIELD_SIGNATURE,
                                         DBUS_TYPE_SIGNATURE,
                                         &v_SIGNATURE))
        _dbus_assert_not_reached ("oom");

      if (!_dbus_string_move (&body, 0, &message->body, 0))
        _dbus_assert_not_reached ("oom");

      _dbus_marshal_set_uint32 (&message->header.data, BODY_LENGTH_OFFSET,
                                _dbus_string_get_length (&message->body),
                                message->byte_order);
      
      *message_p = message;
    }
  else
    {
      dbus_message_unref (message);
      *message_p = NULL;
    }
  
  _dbus_string_free (&signature);
  _dbus_string_free (&body);

  return *message_p != NULL;
}

Project: dbus
File: dbus-message-factory.c

generate_outer (DBusMessageDataIter   *iter,
                DBusString            *data,
                DBusValidity          *expected_validity,
                DBusInnerGeneratorFunc func)
{
  DBusMessage *message;

  message = NULL;
  if (!(*func)(iter, &message))
    return FALSE;

  iter_next (iter);
  
  _dbus_assert (message != NULL);

  generate_from_message (data, expected_validity, message);

  dbus_message_unref (message);

  return TRUE;
}

Project: dbus
File: dbus-message-factory.c

generate_trivial (DBusMessageDataIter   *iter,
                  DBusString            *data,
                  DBusValidity          *expected_validity)
{
  return generate_outer (iter, data, expected_validity,
                         generate_trivial_inner);
}

Project: dbus
File: dbus-message-factory.c

generate_trivial_inner (DBusMessageDataIter *iter,
                        DBusMessage        **message_p)
{
  DBusMessage *message;

  switch (iter_get_sequence (iter))
    {
    case 0:
      message = dbus_message_new_method_call ("org.freedesktop.TextEditor",
                                              "/foo/bar",
                                              "org.freedesktop.DocumentFactory",
                                              "Create");
      break;
    case 1:
      message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_RETURN);
      set_reply_serial (message);
      break;
    case 2:
      message = dbus_message_new_signal ("/foo/bar",
                                         "org.freedesktop.DocumentFactory",
                                         "Created");
      break;
    case 3:
      message = dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);

      if (!dbus_message_set_error_name (message,
                                        "org.freedesktop.TestErrorName"))
        _dbus_assert_not_reached ("oom");
      
      {
        DBusMessageIter iter;
        const char *v_STRING = "This is an error";
        
        dbus_message_iter_init_append (message, &iter);
        if (!dbus_message_iter_append_basic (&iter,
                                             DBUS_TYPE_STRING,
                                             &v_STRING))
          _dbus_assert_not_reached ("oom");
      }
      
      set_reply_serial (message);
      break;
    default:
      return FALSE;
    }
  
  if (message == NULL)
    _dbus_assert_not_reached ("oom");

  *message_p = message;
  
  return TRUE;
}

Project: dbus
File: dbus-message-factory.c

iter_first_in_series (DBusMessageDataIter *iter)
{
  int i;

  i = iter->depth;
  while (i < _DBUS_MESSAGE_DATA_MAX_NESTING)
    {
      if (iter->sequence_nos[i] != 0)
        return FALSE;
      ++i;
    }
  return TRUE;
}

Project: dbus
File: dbus-message-factory.c

iter_get_sequence (DBusMessageDataIter *iter)
{
  _dbus_assert (iter->sequence_nos[iter->depth] >= 0);
  return iter->sequence_nos[iter->depth];
}

Project: dbus
File: dbus-message-factory.c

iter_next (DBusMessageDataIter *iter)
{
  iter->sequence_nos[iter->depth] += 1;
}

Project: dbus
File: dbus-message-factory.c

iter_unrecurse (DBusMessageDataIter *iter)
{
  iter->depth -= 1;
  _dbus_assert (iter->depth >= 0);
}

Project: dbus
File: dbus-message-factory.c

simple_error (void)
{
  DBusMessage *message;
  message =  dbus_message_new (DBUS_MESSAGE_TYPE_ERROR);
  if (message == NULL)
    _dbus_assert_not_reached ("oom");

  if (!dbus_message_set_error_name (message, "foo.bar"))
    _dbus_assert_not_reached ("oom");
  
  set_reply_serial (message);
  
   return message;
 }

Project: dbus
File: dbus-message-factory.c

simple_method_call (void)
{
  DBusMessage *message;
  /* Keeping this small makes stuff go faster */
  message = dbus_message_new_method_call ("o.b.Q",
                                          "/f/b",
                                          "o.b.Z",
                                          "Fro");
  if (message == NULL)
    _dbus_assert_not_reached ("oom");
  return message;
}

Project: dbus
File: dbus-message-factory.c

simple_signal (void)
{
  DBusMessage *message;
  message = dbus_message_new_signal ("/f/b",
                                     "o.b.Z",
                                     "Fro");
  if (message == NULL)
    _dbus_assert_not_reached ("oom");
  return message;
}