Commit message: None
CVE: CVE-2013-0292
CWE: CWE-20
Vulnerability description: The dbus_g_proxy_manager_filter function in dbus-gproxy in Dbus-glib before 0.100.1 does not properly verify the sender of NameOwnerChanged signals, which allows local users to gain privileges via a spoofed signal.

Project: dbus

dbus_g_proxy_manager_lookup_name_owner (DBusGProxyManager        *manager,
					const char               *name,
					DBusGProxyNameOwnerInfo **info,
					const char              **owner)
{
  DBusGProxyNameOwnerForeachData foreach_data;

  foreach_data.name = name;
  foreach_data.owner = NULL;
  foreach_data.info = NULL;
  
  g_hash_table_foreach (manager->owner_names, name_owner_foreach, &foreach_data);

  *info = foreach_data.info;
  *owner = foreach_data.owner;
  return *info != NULL;
}

Project: dbus

dbus_g_proxy_manager_ref (DBusGProxyManager *manager)
{
  g_assert (manager != NULL);
  g_assert (manager->refcount > 0);

  LOCK_MANAGER (manager);
  
  manager->refcount += 1;

  UNLOCK_MANAGER (manager);

  return manager;
}

Project: dbus

dbus_g_proxy_manager_replace_name_owner (DBusGProxyManager  *manager,
					 const char         *name,
					 const char         *prev_owner,
					 const char         *new_owner)
{
  GSList *names;
	  
  if (prev_owner[0] == '\0')
    {
      GSList *tmp;
      GSList *removed;

      /* We have a new service, look at unassociated proxies */

      removed = NULL;

      for (tmp = manager->unassociated_proxies; tmp ; tmp = tmp->next)
	{
	  DBusGProxy *proxy = tmp->data;
	  DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy);

	  if (!strcmp (priv->name, name))
	    {
	      removed = g_slist_prepend (removed, tmp);
	      
	      dbus_g_proxy_manager_monitor_name_owner (manager, new_owner, name);
	      priv->associated = TRUE;
	    }
	}

      for (tmp = removed; tmp; tmp = tmp->next)
	manager->unassociated_proxies = g_slist_delete_link (manager->unassociated_proxies, tmp->data);
      g_slist_free (removed);
    }
  else
    {
      DBusGProxyNameOwnerInfo *info;
      GSList *link;

      /* Name owner changed or deleted */ 

      names = g_hash_table_lookup (manager->owner_names, prev_owner);

      info = NULL;
      if (names != NULL)
        {
	  link = g_slist_find_custom (names, name, find_name_in_info);

	  if (link != NULL)
	    {
	      info = link->data;
	  
	      names = g_slist_delete_link (names, link);

              if (names == NULL)
                {
                  g_hash_table_remove (manager->owner_names, prev_owner);
                }
              else
                {
                  g_hash_table_insert (manager->owner_names,
                                       g_strdup (prev_owner), names);
                }
            }
        }

      if (new_owner[0] == '\0')
	{
	  DBusGProxyUnassociateData data;
	  GSList *tmp;

	  data.name = name;
	  data.destroyed = NULL;

	  /* A service went away, we need to unassociate proxies */
	  g_hash_table_foreach (manager->proxy_lists,
				unassociate_proxies, &data);

	  UNLOCK_MANAGER (manager);

          /* the destroyed list's data pointers are weak pointers, so that we
           * don't end up calling destroy on proxies which have already been
           * freed up as a result of other ones being destroyed */
	  for (tmp = data.destroyed; tmp; tmp = tmp->next)
            if (tmp->data != NULL)
              {
                g_object_remove_weak_pointer (G_OBJECT (tmp->data),
                    &(tmp->data));
                dbus_g_proxy_destroy (tmp->data);
              }
	  g_slist_free (data.destroyed);

	  LOCK_MANAGER (manager);

	  if (info)
	    {
	      g_free (info->name);
	      g_free (info);
	    }
	}
      else if (info)
	{
	  insert_nameinfo (manager, new_owner, info);
	}
    }
}

Project: dbus

dbus_g_proxy_manager_unregister (DBusGProxyManager *manager,
                                DBusGProxy        *proxy)
{
  DBusGProxyList *list;
  DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy);
  char *tri;
  
  LOCK_MANAGER (manager);

#ifndef G_DISABLE_CHECKS
  if (manager->proxy_lists == NULL)
    {
      g_warning ("Trying to unregister a proxy but there aren't any registered");
      return;
    }
#endif

  tri = tristring_from_proxy (proxy);
  
  list = g_hash_table_lookup (manager->proxy_lists, tri);

#ifndef G_DISABLE_CHECKS
  if (list == NULL)
    {
      g_warning ("Trying to unregister a proxy but it isn't registered");
      return;
    }
#endif

  g_assert (g_slist_find (list->proxies, proxy) != NULL);
  
  list->proxies = g_slist_remove (list->proxies, proxy);

  g_assert (g_slist_find (list->proxies, proxy) == NULL);

  if (!priv->for_owner)
    {
      if (!priv->associated)
	{
	  GSList *link;

	  if (priv->name_call != 0)
	    {
	      dbus_g_proxy_cancel_call (manager->bus_proxy, priv->name_call);
	      priv->name_call = 0;
	    }
	  else
	    {
              link = g_slist_find (manager->unassociated_proxies, proxy);
              if (link != NULL)
                {
                  manager->unassociated_proxies = g_slist_delete_link (
                      manager->unassociated_proxies, link);
                }
	    }
	}
      else
	{
	  g_assert (priv->name_call == 0);
	  
	  dbus_g_proxy_manager_unmonitor_name_owner (manager, priv->name);
	}
    }

  if (list->proxies == NULL)
    {
      char *rule;
      g_hash_table_remove (manager->proxy_lists,
                           tri);

      rule = g_proxy_get_signal_match_rule (proxy);
      dbus_bus_remove_match (manager->connection,
                             rule, NULL);
      g_free (rule);

      if (priv->name)
        {
          guint *refcount;
          refcount = g_hash_table_lookup (manager->owner_match_rules, priv->name);
          (*refcount)--;

          if (*refcount == 0)
            {
              rule = get_owner_match_rule (priv->name);
              dbus_bus_remove_match (manager->connection,
                                     rule, NULL);
              g_free (rule);
              g_hash_table_remove (manager->owner_match_rules, priv->name);
            }
        }
    }
  
  if (g_hash_table_size (manager->proxy_lists) == 0)
    {
      g_hash_table_destroy (manager->proxy_lists);
      manager->proxy_lists = NULL;
    }

  if (manager->owner_match_rules != NULL &&
      g_hash_table_size (manager->owner_match_rules) == 0)
    {
      g_hash_table_destroy (manager->owner_match_rules);
      manager->owner_match_rules = NULL;
    }

  g_free (tri);
      
  UNLOCK_MANAGER (manager);
}

Project: dbus

find_name_in_info (gconstpointer a, gconstpointer b)
{
  const DBusGProxyNameOwnerInfo *info = a;
  const char *name = b;

  return strcmp (info->name, name);
}

Project: dbus

got_name_owner_cb (DBusGProxy       *bus_proxy,
		   DBusGProxyCall   *call,
		   void             *user_data)
{
  DBusGProxy *proxy = user_data;
  DBusGProxyPrivate *priv = DBUS_G_PROXY_GET_PRIVATE(proxy);
  GError *error;
  char *owner;

  error = NULL;
  owner = NULL;

  LOCK_MANAGER (priv->manager);

  if (!dbus_g_proxy_end_call (bus_proxy, call, &error,
			      G_TYPE_STRING, &owner,
			      G_TYPE_INVALID))
    {
      if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_NAME_HAS_NO_OWNER)
	{
	  priv->manager->unassociated_proxies = g_slist_prepend (priv->manager->unassociated_proxies, proxy);
	}
      else if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION)
	g_warning ("Couldn't get name owner (%s): %s",
		   dbus_g_error_get_name (error),
		   error->message);
      else
	g_warning ("Couldn't get name owner (code %d): %s",
                   error->code, error->message);
      g_clear_error (&error);
      goto out;
    }
  else
    {
      dbus_g_proxy_manager_monitor_name_owner (priv->manager, owner, priv->name);
      priv->associated = TRUE;
    }

 out:
  priv->name_call = NULL;
  UNLOCK_MANAGER (priv->manager);
  g_free (owner);
}

Project: dbus

name_owner_foreach (gpointer key, gpointer val, gpointer data)
{
  const char *owner;
  DBusGProxyNameOwnerForeachData *foreach_data;
  GSList *names;
  GSList *link;

  owner = key;
  names = val;
  foreach_data = data;

  if (foreach_data->owner != NULL)
    return;

  g_assert (foreach_data->info == NULL);

  link = g_slist_find_custom (names, foreach_data->name, find_name_in_info);
  if (link)
    {
      foreach_data->owner = owner;
      foreach_data->info = link->data;
    }
}