- all configuration options are stored as binary data in Secret
member of Item interface
- each option is stored as c-string 'name=value\0' and a secret value
consists from concatenation of corresponding strings for all options
- lookup attributes contain only 'libreportEventConfig' key and and the
key is associated with event name
Signed-off-by: Jakub Filak <jfilak(a)redhat.com>
---
src/gtk-helpers/secrets.c | 278 ++++++++++++++++++++++++++++++----------------
1 file changed, 185 insertions(+), 93 deletions(-)
diff --git a/src/gtk-helpers/secrets.c b/src/gtk-helpers/secrets.c
index ee6a348..07f3be3 100644
--- a/src/gtk-helpers/secrets.c
+++ b/src/gtk-helpers/secrets.c
@@ -29,6 +29,7 @@
#define SECRETS_COLLECTION_ALIAS "default"
#define SECRETS_SEARCH_ATTRIBUTE "libreportEventConfig"
+#define SECRETS_OPTION_VALUE_DELIMITER '='
/* 5s timeout*/
#define SECRETS_CALL_DEFAULT_TIMEOUT 5000
@@ -47,12 +48,23 @@
Collection is a set of Items identified by labels (label is a string).
Libreport uses event names as labels for items which hold event config data.
+ One item holds all configuration items for one event.
+
+ Item has a set of (name,value) pairs called lookup attributes.
+ Libreport uses 'libreportEventConfig' attribute for searching. The
+ attribute hodls an event name.
+ Item has a secret value. The value holds an arbitrary binary data.
+ Libreport uses the value to store configuration items.
+ The value consists from several c-strings where each option is
+ represented by one c-string (bytes followed by '\0').
+ Each option string is concatenation of option name, delimiter and option value.
+
+
+ For example, "report_Bugzilla" item will have the lookup attribute
+ like ("libreportEventConfig", "report_Bugzilla") and the secret
value
+ like ("Bugzilla_URL=https://bugzilla.redhat.com\0Bugzilla_Login=foo\0")
- Item is a set of (name,value) pairs called attributes.
- Libreport uses attributes to store config data.
- For example, "report_Bugzilla" item will have some attributes,
- like ("Bugzilla_BugzillaURL", "https://bugzilla.redhat.com").
DBus API:
=========
@@ -246,32 +258,6 @@ static GDBusProxy *secrets_object_get_properties_proxy(struct
secrets_object *ob
return obj->property_proxy;
}
-
-static bool secrets_object_set_dbus_property(struct secrets_object *obj,
- const char *property,
- GVariant *value)
-{
- GDBusProxy *properties = secrets_object_get_properties_proxy(obj);
-
- if (!properties)
- return false;
-
- /*
http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfac... */
- /* org.freedesktop.DBus.Properties.Set (IN String interface_name, IN String
property_name, IN Variant value); */
- GVariant *const resp = dbus_call_sync(properties,
- "Set",
- g_variant_new("(ssv)",
- obj->interface_name,
- property,
- value)
- );
-
- if (resp)
- g_variant_unref(resp);
-
- return resp;
-}
-
/*******************************************************************************/
/* struct secrets_service */
/*******************************************************************************/
@@ -472,6 +458,7 @@ static GVariant *secrets_prompt(const char *prompt_path,
.closure_marshal = NULL,
};
+ /* TODO : leak */
GDBusProxy *const prompt_proxy = get_dbus_proxy(prompt_path,
SECRETS_NAME_SPACE("Prompt"));
*dismissed = false;
@@ -544,6 +531,7 @@ static GVariant *secrets_service_unlock(const gchar *const *objects,
gchar *prompt = NULL;
GVariant *result = NULL;
+ /* TODO : leak */
g_variant_get(var, "(@aoo)", &result, &prompt);
*dismissed = false;
@@ -774,6 +762,7 @@ static struct secrets_object *secrets_service_create_collection(const
gchar *lab
gchar *prompt = NULL;
GVariant *collection_var = NULL;
+ /* TODO : leak */
g_variant_get(resp, "(@oo)", &collection_var, &prompt);
if (is_prompt_required(prompt))
@@ -810,7 +799,7 @@ static struct secrets_object *secrets_service_create_collection(const
gchar *lab
static GVariant *create_item_call_args(const char *event_name,
GVariant *attributes,
- GDBusProxy *session,
+ GVariant *secret,
bool full_property_name)
{
/* gnome-keyring accepts properties with namespace */
@@ -842,21 +831,60 @@ static GVariant *create_item_call_args(const char *event_name,
g_variant_builder_add(prop_builder, "{sv}", lbl_name,
g_variant_new_string(event_name));
g_variant_builder_add(prop_builder, "{sv}", att_name, attributes);
- GVariantBuilder *const param_builder =
g_variant_builder_new(G_VARIANT_TYPE("ay"));
+ GVariant *const prop = g_variant_builder_end(prop_builder);
+
+ return g_variant_new("(@a{sv}@(oayays)b)", prop, secret, FALSE);
+}
+
+static GVariant *create_secret_from_options(GDBusProxy *session, GList *options, bool
store_passwords)
+{
GVariantBuilder *const value_builder =
g_variant_builder_new(G_VARIANT_TYPE("ay"));
+ for (GList *iter = g_list_first(options); iter; iter = g_list_next(iter))
+ {
+ event_option_t *const op = (event_option_t *)iter->data;
+ /* TODO : is it still necessary? Passwords are encrypted now. */
+ if (op->eo_type != OPTION_TYPE_PASSWORD || store_passwords)
+ {
+ const char *byte = op->eo_name;
+ while(byte[0] != '\0')
+ g_variant_builder_add(value_builder, "y", *byte++);
+
+ g_variant_builder_add(value_builder, "y",
SECRETS_OPTION_VALUE_DELIMITER);
- GVariant *const secret = g_variant_new("(o@ay@ays)",
- g_dbus_proxy_get_object_path(session),
- g_variant_builder_end(param_builder),
- g_variant_builder_end(value_builder),
- "text/plain, charset=utf8");
+ byte = op->eo_value;
+ while(byte[0] != '\0')
+ g_variant_builder_add(value_builder, "y", *byte++);
- return g_variant_new("(@a{sv}@(oayays)b)",
g_variant_builder_end(prop_builder), secret, FALSE);
+ g_variant_builder_add(value_builder, "y", '\0');
+ }
+ }
+
+ GVariant *const value = g_variant_builder_end(value_builder);
+
+ return g_variant_new("(oay@ays)",
+ g_dbus_proxy_get_object_path(session),
+ /* param */ NULL,
+ value,
+ "application/libreport");
+}
+
+static GVariant *create_lookup_attributes(const char *event_name)
+{
+ GVariantBuilder *const attr_builder =
g_variant_builder_new(G_VARIANT_TYPE_DICTIONARY);
+ /* add extra attribute used for searching */
+ g_variant_builder_add(attr_builder, "{ss}", SECRETS_SEARCH_ATTRIBUTE,
event_name);
+
+ /* An example of attributes: */
+ /* { */
+ /* "libreportEventConfig": "event_name", */
+ /* } */
+ return g_variant_builder_end(attr_builder);
}
static bool secrets_collection_create_text_item(struct secrets_object *collection,
const char *event_name,
GVariant *attributes,
+ GVariant *secret,
bool *dismissed)
{
bool succ = false;
@@ -903,8 +931,10 @@ static bool secrets_collection_create_text_item(struct secrets_object
*collectio
/* prompt : A prompt object, or the special value '/' if no prompt
*/
/* is necessary. */
GVariant *const resp = g_dbus_proxy_call_sync(collection->proxy,
"CreateItem",
- create_item_call_args(event_name,
g_variant_ref(attributes),
- g_session_proxy,
options[choice]),
+ create_item_call_args(event_name,
+ g_variant_ref(attributes),
+ g_variant_ref(secret),
+ options[choice]),
G_DBUS_PROXY_FLAGS_NONE,
SECRETS_CALL_DEFAULT_TIMEOUT,
NULL, &error);
@@ -924,6 +954,7 @@ static bool secrets_collection_create_text_item(struct secrets_object
*collectio
gchar *prompt = NULL;
gchar *item = NULL;
+ /* TODO : leak */
g_variant_get(resp, "(oo)", &item, &prompt);
/* if prompt is no required the function is successfull */
@@ -941,6 +972,7 @@ static bool secrets_collection_create_text_item(struct secrets_object
*collectio
}
g_variant_unref(attributes);
+ g_variant_unref(secret);
return succ;
}
@@ -1052,68 +1084,119 @@ static bool get_default_collection(struct secrets_object
**collection,
return succ;
}
-static void load_event_options_from_item(GList *options,
+static void load_event_options_from_item(GDBusProxy *session,
+ GList *options,
struct secrets_object *item)
{
- GVariant *const attributes =
- g_dbus_proxy_get_cached_property(item->proxy, "Attributes");
+ { /* for backward compatibility */
+ /* load configuration from lookup attributes but don't store configuration in
lookup attributes */
+ GVariant *const attributes =
+ g_dbus_proxy_get_cached_property(item->proxy, "Attributes");
- GVariantIter *iter = NULL;
- g_variant_get(attributes, "a{ss}", &iter);
+ GVariantIter *iter = NULL;
+ g_variant_get(attributes, "a{ss}", &iter);
- gchar *name = NULL;
- gchar *value = NULL;
- while (g_variant_iter_loop(iter, "{ss}", &name, &value))
- {
- event_option_t *const option = get_event_option_from_list(name, options);
- if (option)
+ gchar *name = NULL;
+ gchar *value = NULL;
+ while (g_variant_iter_loop(iter, "{ss}", &name, &value))
{
- free(option->eo_value);
- option->eo_value = xstrdup(value);
- VERB2 log("loaded event option : '%s => %s'", name,
option->eo_value);
+ event_option_t *const option = get_event_option_from_list(name, options);
+ if (option)
+ {
+ free(option->eo_value);
+ option->eo_value = xstrdup(value);
+ VERB2 log("loaded event option : '%s' =>
'%s'", name, option->eo_value);
+ }
}
+
+ g_variant_unref(attributes);
}
- g_variant_unref(attributes);
-}
+ { /* read configuration from the secret value */
+ GVariant *const resp =
+ dbus_call_sync(item->proxy, "GetSecret",
g_variant_new("(o)", g_dbus_proxy_get_object_path(session)));
-static GVariant *create_attributes_from_options(GList *options,
- const char *event_name,
- bool store_passwords)
-{
- GVariantBuilder *const attr_builder =
g_variant_builder_new(G_VARIANT_TYPE_DICTIONARY);
- for (GList *iter = g_list_first(options); iter; iter = g_list_next(iter))
- { /* we store data in the search attributes */
- event_option_t *const op = (event_option_t *)iter->data;
- if (op->eo_type != OPTION_TYPE_PASSWORD || store_passwords)
- g_variant_builder_add(attr_builder, "{ss}", op->eo_name,
op->eo_value);
- }
+ if (!resp)
+ return;
- /* and finally, add extra attribute used for searching */
- g_variant_builder_add(attr_builder, "{ss}", SECRETS_SEARCH_ATTRIBUTE,
event_name);
+ if (g_variant_n_children(resp) == 0)
+ {
+ VERB1 log("response from GetSecret() method doesn't contain data:
'%s'", g_variant_print(resp, TRUE));
+ return;
+ }
- /* An example of attributes: */
- /* { */
- /* "BugzillaURL": "Value1", */
- /* "BugzillaPassword": "Value2", */
- /* "libreportEventConfig": "event_name", */
- /* } */
- return g_variant_builder_end(attr_builder);
+ /* All dbus responses are returned inside of an variant */
+ GVariant *const secret = g_variant_get_child_value(resp, 0);
+
+ /* DBus represents a structure as a tuple of values */
+ /* struct Secret
+ * {
+ * (o) object path
+ * (ay) byte array params
+ * (ay) byte array value
+ * (s) string content type
+ * }
+ */
+ if (g_variant_n_children(secret) != 4)
+ {
+ VERB1 log("secret value has invalid structure: '%s'",
g_variant_print(secret, TRUE));
+ return;
+ }
+
+ /* get (ay) byte array value */
+ GVariant *const secret_value = g_variant_get_child_value(secret, 2);
+
+ gsize nelems;
+ gconstpointer data = g_variant_get_fixed_array(secret_value, &nelems,
sizeof(guchar));
+
+ while(nelems > 0)
+ {
+ const size_t sz = strlen(data) + 1;
+ if (sz > nelems)
+ { /* check next size, if is too big then trailing 0 is probably missing */
+ VERB1 log("broken secret value, atttempts to read %zdB but only %zdB
are available)", sz, nelems);
+ break;
+ }
+ nelems -= sz;
+
+ char *name = xstrdup(data);
+ char *value = strchr(name, SECRETS_OPTION_VALUE_DELIMITER);
+ if (!value)
+ {
+ VERB1 log("secret value has invalid format: missing delimiter after
option name");
+ goto next_option;
+ }
+
+ *value++ = '\0';
+
+ event_option_t *const option = get_event_option_from_list(name, options);
+ if (option)
+ {
+ free(option->eo_value);
+ option->eo_value = xstrdup(value);
+ VERB2 log("loaded event option : '%s' =>
'%s'", name, option->eo_value);
+ }
+
+next_option:
+ free(name);
+ data += sz;
+ }
+
+ g_variant_unref(secret_value);
+ g_variant_unref(secret);
+ g_variant_unref(resp);
+ }
}
static bool find_item_by_event_name(const struct secrets_object *collection,
const char *event_name,
struct secrets_object **item)
{
- GVariantBuilder *const attrs = g_variant_builder_new(G_VARIANT_TYPE_DICTIONARY);
-
- /* For searching uses item's attribute holding an event name */
- g_variant_builder_add(attrs, "{ss}", SECRETS_SEARCH_ATTRIBUTE,
event_name);
-
- return secrets_collection_search_one_item(collection, g_variant_builder_end(attrs),
item);
+ return secrets_collection_search_one_item(collection,
create_lookup_attributes(event_name), item);
}
-static void load_settings(struct secrets_object *collection, const char *event_name,
event_config_t *ec)
+static void load_settings(GDBusProxy *session, struct secrets_object *collection,
+ const char *event_name, event_config_t *ec)
{
/* Locked property is not supported by ksecrets */
/* thus we have to perform Unlock for each call of load settings */
@@ -1128,7 +1211,7 @@ static void load_settings(struct secrets_object *collection, const
char *event_n
if (succ && item)
{
VERB2 log("loading event config : '%s'", event_name);
- load_event_options_from_item(ec->options, item);
+ load_event_options_from_item(session, ec->options, item);
secrets_object_delete(item);
item = NULL;
}
@@ -1145,7 +1228,7 @@ static void load_event_config(gpointer key, gpointer value, gpointer
user_data)
struct secrets_object *const collection = (struct secrets_object *)user_data;
if (is_event_config_user_storage_available())
- load_settings(collection, event_name, ec);
+ load_settings(g_session_proxy, collection, event_name, ec);
}
static bool save_options(struct secrets_object *collection,
@@ -1160,22 +1243,31 @@ static bool save_options(struct secrets_object *collection,
if (!succ)
return succ;
- GVariant *const attributes = create_attributes_from_options(options,
- event_name,
- store_passwords);
+ GVariant *const secret = create_secret_from_options(g_session_proxy, options,
store_passwords);
if (item)
- { /* item exists, change Attributes */
+ { /* item exists, change a secret */
VERB2 log("updating event config : '%s'", event_name);
+ /* can't be dismissed */
*dismissed = false;
- succ = secrets_object_set_dbus_property(item, "Attributes",
attributes);
+ GVariant *const args = g_variant_new("(@(oayays))", secret);
+ GVariant *const resp = dbus_call_sync(item->proxy, "SetSecret",
args);
+ succ = resp;
+
+ if (succ)
+ g_variant_unref(resp);
+
secrets_object_delete(item);
- item = NULL;
}
else
- { /* create a new item with Attributes */
+ { /* create a new item */
+ GVariant *const attributes = create_lookup_attributes(event_name);
VERB2 log("creating event config : '%s'", event_name);
- succ = secrets_collection_create_text_item(collection, event_name, attributes,
dismissed);
+ succ = secrets_collection_create_text_item(collection, event_name, attributes,
secret, dismissed);
+
+ /* I can't figure out why I have to do the following */
+ g_variant_unref(attributes);
+ g_variant_unref(secret);
}
return succ;
--
1.7.11.4