summaryrefslogtreecommitdiff
path: root/dbus/dbus-gobject.c
diff options
context:
space:
mode:
Diffstat (limited to 'dbus/dbus-gobject.c')
-rw-r--r--dbus/dbus-gobject.c412
1 files changed, 361 insertions, 51 deletions
diff --git a/dbus/dbus-gobject.c b/dbus/dbus-gobject.c
index ec93ad4..1755f2c 100644
--- a/dbus/dbus-gobject.c
+++ b/dbus/dbus-gobject.c
@@ -44,2 +44,4 @@ typedef struct
static GStaticRWLock globals_lock = G_STATIC_RW_LOCK_INIT;
+/* See comments in check_property_access */
+static gboolean disable_legacy_property_access = FALSE;
static GHashTable *marshal_table = NULL;
@@ -47,2 +49,19 @@ static GData *error_metadata = NULL;
+/* Ugly yes - but we have to accept strings from both formats */
+static gboolean
+compare_strings_ignoring_uscore_vs_dash (const char *a, const char *b)
+{
+ guint i;
+
+ for (i = 0; a[i] && b[i]; i++)
+ {
+ if ((a[i] == '-' && b[i] == '_')
+ || (a[i] == '_' && b[i] == '-'))
+ continue;
+ if (a[i] != b[i])
+ return FALSE;
+ }
+ return (a[i] == '\0') && (b[i] == '\0');
+}
+
static char*
@@ -272,3 +291,3 @@ method_output_signature_from_object_info (const DBusGObjectInfo *object,
static const char *
-propsig_iterate (const char *data, const char **iface, const char **name)
+signal_iterate (const char *data, const char **iface, const char **name)
{
@@ -282,2 +301,104 @@ propsig_iterate (const char *data, const char **iface, const char **name)
+static const char *
+property_iterate (const char *data,
+ int format_version,
+ const char **iface,
+ const char **exported_name,
+ const char **name_uscored,
+ const char **access_type)
+{
+ *iface = data;
+
+ data = string_table_next (data);
+ *exported_name = data;
+
+ data = string_table_next (data);
+ if (format_version == 1)
+ {
+ *name_uscored = data;
+ data = string_table_next (data);
+ *access_type = data;
+ return string_table_next (data);
+ }
+ else
+ {
+ /* This tells the caller they need to compute it */
+ *name_uscored = NULL;
+ /* We don't know here, however note that we will still check against the
+ * readable/writable flags from GObject's metadata.
+ */
+ *access_type = "readwrite";
+ return data;
+ }
+}
+
+/**
+ * property_info_from_object_info:
+ * @object: introspection data
+ * @interface_name: (allow-none): Expected interface name, or %NULL for any
+ * @property_name: Expected property name (can use "-" or "_" as separator)
+ * @access_type: (out): Can be one of "read", "write", "readwrite"
+ *
+ * Look up property introspection data for the given interface/name pair.
+ *
+ * Returns: %TRUE if property was found
+ */
+static gboolean
+property_info_from_object_info (const DBusGObjectInfo *object,
+ const char *interface_name,
+ const char *property_name,
+ const char **access_type)
+{
+ const char *properties_iter;
+
+ properties_iter = object->exported_properties;
+ while (properties_iter != NULL && *properties_iter)
+ {
+ const char *cur_interface_name;
+ const char *cur_property_name;
+ const char *cur_uscore_property_name;
+ const char *cur_access_type;
+
+
+ properties_iter = property_iterate (properties_iter, object->format_version,
+ &cur_interface_name, &cur_property_name,
+ &cur_uscore_property_name, &cur_access_type);
+
+ if (interface_name && strcmp (interface_name, cur_interface_name) != 0)
+ continue;
+
+ /* This big pile of ugly is necessary to support the matrix resulting from multiplying
+ * (v0 data, v1 data) * (FooBar, foo-bar)
+ * In v1 data we have both forms of string, so we do a comparison against both without
+ * having to malloc.
+ * For v0 data, we need to reconstruct the foo-bar form.
+ *
+ * Adding to the complexity is that we *also* have to ignore the distinction between
+ * '-' and '_', because g_object_{get,set} does.
+ */
+ /* First, compare against the primary property name - no malloc required */
+ if (!compare_strings_ignoring_uscore_vs_dash (property_name, cur_property_name))
+ {
+ if (cur_uscore_property_name != NULL
+ && !compare_strings_ignoring_uscore_vs_dash (property_name, cur_uscore_property_name))
+ continue;
+ else
+ {
+ /* v0 metadata, construct uscore */
+ char *tmp_uscored;
+ gboolean matches;
+ tmp_uscored = _dbus_gutils_wincaps_to_uscore (cur_property_name);
+ matches = compare_strings_ignoring_uscore_vs_dash (property_name, tmp_uscored);
+ g_free (tmp_uscored);
+ if (!matches)
+ continue;
+ }
+ }
+
+ *access_type = cur_access_type;
+ return TRUE;
+ }
+ return FALSE;
+}
+
static GQuark
@@ -292,7 +413,13 @@ dbus_g_object_type_dbus_metadata_quark (void)
-static GList *
-lookup_object_info (GObject *object)
+/* Iterator function should return FALSE to stop iteration, TRUE to continue */
+typedef gboolean (*ForeachObjectInfoFn) (const DBusGObjectInfo *info,
+ GType gtype,
+ gpointer user_data);
+
+static void
+foreach_object_info (GObject *object,
+ ForeachObjectInfoFn callback,
+ gpointer user_data)
{
GType *interfaces, *p;
- GList *info_list = NULL;
const DBusGObjectInfo *info;
@@ -306,3 +433,6 @@ lookup_object_info (GObject *object)
if (info != NULL && info->format_version >= 0)
- info_list = g_list_prepend (info_list, (gpointer) info);
+ {
+ if (!callback (info, *p, user_data))
+ break;
+ }
}
@@ -315,9 +445,28 @@ lookup_object_info (GObject *object)
if (info != NULL && info->format_version >= 0)
- info_list = g_list_prepend (info_list, (gpointer) info);
+ {
+ if (!callback (info, classtype, user_data))
+ break;
+ }
}
- /* if needed only:
- return g_list_reverse (info_list);
- */
-
+}
+
+static gboolean
+lookup_object_info_cb (const DBusGObjectInfo *info,
+ GType gtype,
+ gpointer user_data)
+{
+ GList **list = (GList **) user_data;
+
+ *list = g_list_prepend (*list, (gpointer) info);
+ return TRUE;
+}
+
+static GList *
+lookup_object_info (GObject *object)
+{
+ GList *info_list = NULL;
+
+ foreach_object_info (object, lookup_object_info_cb, &info_list);
+
return info_list;
@@ -325,2 +474,52 @@ lookup_object_info (GObject *object)
+typedef struct {
+ const char *iface;
+ const DBusGObjectInfo *info;
+ gboolean fallback;
+ GType iface_type;
+} LookupObjectInfoByIfaceData;
+
+static gboolean
+lookup_object_info_by_iface_cb (const DBusGObjectInfo *info,
+ GType gtype,
+ gpointer user_data)
+{
+ LookupObjectInfoByIfaceData *lookup_data = (LookupObjectInfoByIfaceData *) user_data;
+
+ /* If interface is not specified, choose the first info */
+ if (lookup_data->fallback && (!lookup_data->iface || strlen (lookup_data->iface) == 0))
+ {
+ lookup_data->info = info;
+ lookup_data->iface_type = gtype;
+ }
+ else if (info->exported_properties && !strcmp (info->exported_properties, lookup_data->iface))
+ {
+ lookup_data->info = info;
+ lookup_data->iface_type = gtype;
+ }
+
+ return !lookup_data->info;
+}
+
+static const DBusGObjectInfo *
+lookup_object_info_by_iface (GObject *object,
+ const char *iface,
+ gboolean fallback,
+ GType *out_iface_type)
+{
+ LookupObjectInfoByIfaceData data;
+
+ data.iface = iface;
+ data.info = NULL;
+ data.fallback = fallback;
+ data.iface_type = 0;
+
+ foreach_object_info (object, lookup_object_info_by_iface_cb, &data);
+
+ if (out_iface_type && data.info)
+ *out_iface_type = data.iface_type;
+
+ return data.info;
+}
+
static void
@@ -445,3 +644,6 @@ write_interface (gpointer key, gpointer val, gpointer user_data)
{
+ const char *iface;
const char *propname;
+ const char *propname_uscore;
+ const char *access_type;
GParamSpec *spec;
@@ -452,19 +654,27 @@ write_interface (gpointer key, gpointer val, gpointer user_data)
- propname = properties->data;
spec = NULL;
- s = _dbus_gutils_wincaps_to_uscore (propname);
+ property_iterate (properties->data, object_info->format_version, &iface, &propname, &propname_uscore, &access_type);
- spec = g_object_class_find_property (g_type_class_peek (data->gtype), s);
+ if (!propname_uscore)
+ {
+ char *s = _dbus_gutils_wincaps_to_uscore (propname);
+ spec = g_object_class_find_property (g_type_class_peek (data->gtype), s);
+ g_free (s);
+ }
+ else
+ {
+ spec = g_object_class_find_property (g_type_class_peek (data->gtype), propname_uscore);
+ }
g_assert (spec != NULL);
- g_free (s);
-
+
dbus_type = _dbus_gtype_to_signature (G_PARAM_SPEC_VALUE_TYPE (spec));
g_assert (dbus_type != NULL);
-
- can_set = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
- (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
-
+
+ can_set = strcmp (access_type, "readwrite") == 0
+ && ((spec->flags & G_PARAM_WRITABLE) != 0
+ && (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
+
can_get = (spec->flags & G_PARAM_READABLE) != 0;
-
+
if (can_set || can_get)
@@ -488,6 +698,4 @@ write_interface (gpointer key, gpointer val, gpointer user_data)
}
-
- g_free (dbus_type);
- g_string_append (xml, " </property>\n");
+ g_free (dbus_type);
}
@@ -558,3 +766,3 @@ introspect_interfaces (GObject *object, GString *xml)
- propsig = propsig_iterate (propsig, &iface, &signame);
+ propsig = signal_iterate (propsig, &iface, &signame);
@@ -569,9 +777,11 @@ introspect_interfaces (GObject *object, GString *xml)
const char *propname;
+ const char *propname_uscore;
+ const char *access_type;
- propsig = propsig_iterate (propsig, &iface, &propname);
+ propsig = property_iterate (propsig, info->format_version, &iface, &propname, &propname_uscore, &access_type);
values = lookup_values (interfaces, iface);
- values->properties = g_slist_prepend (values->properties, (gpointer) propname);
+ values->properties = g_slist_prepend (values->properties, (gpointer)iface);
}
-
+
memset (&data, 0, sizeof (data));
@@ -1273,2 +1483,66 @@ invoke_object_method (GObject *object,
+static gboolean
+check_property_access (DBusConnection *connection,
+ DBusMessage *message,
+ GObject *object,
+ const char *wincaps_propiface,
+ const char *requested_propname,
+ const char *uscore_propname,
+ gboolean is_set)
+{
+ const DBusGObjectInfo *object_info;
+ const char *access_type;
+ DBusMessage *ret;
+
+ if (!is_set && !disable_legacy_property_access)
+ return TRUE;
+
+ object_info = lookup_object_info_by_iface (object, wincaps_propiface, TRUE, NULL);
+ if (!object_info)
+ {
+ ret = dbus_message_new_error_printf (message,
+ DBUS_ERROR_ACCESS_DENIED,
+ "Interface \"%s\" isn't exported (or may not exist), can't access property \"%s\"",
+ wincaps_propiface,
+ requested_propname);
+ dbus_connection_send (connection, ret, NULL);
+ dbus_message_unref (ret);
+ return FALSE;
+ }
+
+ /* Try both forms of property names: "foo_bar" or "FooBar"; for historical
+ * reasons we accept both.
+ */
+ if (object_info
+ && !(property_info_from_object_info (object_info, wincaps_propiface, requested_propname, &access_type)
+ || property_info_from_object_info (object_info, wincaps_propiface, uscore_propname, &access_type)))
+ {
+ ret = dbus_message_new_error_printf (message,
+ DBUS_ERROR_ACCESS_DENIED,
+ "Property \"%s\" of interface \"%s\" isn't exported (or may not exist)",
+ requested_propname,
+ wincaps_propiface);
+ dbus_connection_send (connection, ret, NULL);
+ dbus_message_unref (ret);
+ return FALSE;
+ }
+
+ if (strcmp (access_type, "readwrite") == 0)
+ return TRUE;
+ else if (is_set ? strcmp (access_type, "read") == 0
+ : strcmp (access_type, "write") == 0)
+ {
+ ret = dbus_message_new_error_printf (message,
+ DBUS_ERROR_ACCESS_DENIED,
+ "Property \"%s\" of interface \"%s\" is not %s",
+ requested_propname,
+ wincaps_propiface,
+ is_set ? "settable" : "readable");
+ dbus_connection_send (connection, ret, NULL);
+ dbus_message_unref (ret);
+ return FALSE;
+ }
+ return TRUE;
+}
+
static DBusHandlerResult
@@ -1283,4 +1557,4 @@ gobject_message_function (DBusConnection *connection,
char *s;
- const char *wincaps_propname;
- /* const char *wincaps_propiface; */
+ const char *requested_propname;
+ const char *wincaps_propiface;
DBusMessageIter iter;
@@ -1301,3 +1575,4 @@ gobject_message_function (DBusConnection *connection,
/* If no metainfo, we can still do properties and signals
- * via standard GLib introspection
+ * via standard GLib introspection. Note we do now check
+ * property access against the metainfo if available.
*/
@@ -1324,6 +1599,4 @@ gobject_message_function (DBusConnection *connection,
}
- /* We never use the interface name; if we did, we'd need to
- * remember that it can be empty string for "pick one for me"
- */
- /* dbus_message_iter_get_basic (&iter, &wincaps_propiface); */
+
+ dbus_message_iter_get_basic (&iter, &wincaps_propiface);
dbus_message_iter_next (&iter);
@@ -1335,9 +1608,15 @@ gobject_message_function (DBusConnection *connection,
}
- dbus_message_iter_get_basic (&iter, &wincaps_propname);
+
+ dbus_message_iter_get_basic (&iter, &requested_propname);
dbus_message_iter_next (&iter);
-
- s = _dbus_gutils_wincaps_to_uscore (wincaps_propname);
- pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object),
- s);
+ s = _dbus_gutils_wincaps_to_uscore (requested_propname);
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), s);
+
+ if (!check_property_access (connection, message, object, wincaps_propiface, requested_propname, s, setter))
+ {
+ g_free (s);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
@@ -1366,7 +1645,2 @@ gobject_message_function (DBusConnection *connection,
}
- else
- {
- g_assert_not_reached ();
- ret = NULL;
- }
@@ -1381,2 +1655,12 @@ gobject_message_function (DBusConnection *connection,
}
+ else
+ {
+ DBusMessage *ret;
+
+ ret = dbus_message_new_error_printf (message,
+ DBUS_ERROR_INVALID_ARGS,
+ "No such property %s", requested_propname);
+ dbus_connection_send (connection, ret, NULL);
+ dbus_message_unref (ret);
+ }
@@ -1499,4 +1783,4 @@ export_signals (DBusGConnection *connection, const GList *info_list, GObject *ob
- sigdata = propsig_iterate (sigdata, &iface, &signame);
-
+ sigdata = signal_iterate (sigdata, &iface, &signame);
+
s = _dbus_gutils_wincaps_to_uscore (signame);
@@ -1659,2 +1943,28 @@ dbus_g_error_info_free (gpointer p)
/**
+ * dbus_glib_global_set_disable_legacy_property_access:
+ *
+ * Historically, DBus-GLib allowed read/write access to any property
+ * regardless of the access flags specified in the introspection XML,
+ * and regardless of the DBus interface given.
+ *
+ * As of version 0.88, DBus-GLib by default allows read-only access to
+ * every GObject property of an object exported to the bus, regardless
+ * of whether or not the property is listed in the type info installed
+ * with dbus_g_object_type_install_info() and regardless of whether
+ * the correct interface is specified. Write access is denied.
+ *
+ * After calling this method, it will be required that the property is
+ * exported on the given interface, and even read-only access will be
+ * checked. This method changes behavior globally for the entire
+ * process.
+ *
+ * Since: 0.88
+ */
+void
+dbus_glib_global_set_disable_legacy_property_access (void)
+{
+ disable_legacy_property_access = TRUE;
+}
+
+/**
* dbus_g_object_type_install_info:
@@ -2312,3 +2622,3 @@ _dbus_gobject_test (const char *test_data_dir)
g_assert (*sigdata != '\0');
- sigdata = propsig_iterate (sigdata, &iface, &signame);
+ sigdata = signal_iterate (sigdata, &iface, &signame);
g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.MyObject"));
@@ -2316,3 +2626,3 @@ _dbus_gobject_test (const char *test_data_dir)
g_assert (*sigdata != '\0');
- sigdata = propsig_iterate (sigdata, &iface, &signame);
+ sigdata = signal_iterate (sigdata, &iface, &signame);
g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
@@ -2320,3 +2630,3 @@ _dbus_gobject_test (const char *test_data_dir)
g_assert (*sigdata != '\0');
- sigdata = propsig_iterate (sigdata, &iface, &signame);
+ sigdata = signal_iterate (sigdata, &iface, &signame);
g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
@@ -2324,3 +2634,3 @@ _dbus_gobject_test (const char *test_data_dir)
g_assert (*sigdata != '\0');
- sigdata = propsig_iterate (sigdata, &iface, &signame);
+ sigdata = signal_iterate (sigdata, &iface, &signame);
g_assert (!strcmp (iface, "org.freedesktop.DBus.Tests.FooObject"));
@@ -2328,3 +2638,3 @@ _dbus_gobject_test (const char *test_data_dir)
g_assert (*sigdata == '\0');
-
+