Merge branch '62-flatpak-libsecret-fails-to-read-credentials-saved-by-other-process' into 'master'

Resolve "Flatpak: libsecret fails to read credentials saved by other process"

Closes #62

See merge request GNOME/libsecret!99
This commit is contained in:
Niels De Graef 2023-09-18 11:42:54 +00:00
commit 97d5c139ee

View File

@ -54,6 +54,7 @@ struct _SecretFileCollection
guint64 usage_count; guint64 usage_count;
GBytes *key; GBytes *key;
GVariant *items; GVariant *items;
guint64 file_last_modified;
}; };
static void secret_file_collection_async_initable_iface (GAsyncInitableIface *iface); static void secret_file_collection_async_initable_iface (GAsyncInitableIface *iface);
@ -68,6 +69,22 @@ enum {
PROP_PASSWORD PROP_PASSWORD
}; };
static guint64
get_file_last_modified (SecretFileCollection *self)
{
GFileInfo *info;
guint64 res;
info = g_file_query_info (self->file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
if (info == NULL)
return 0;
res = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
g_object_unref (info);
return res;
}
static gboolean static gboolean
do_derive_key (SecretFileCollection *self) do_derive_key (SecretFileCollection *self)
{ {
@ -296,17 +313,13 @@ secret_file_collection_class_init (SecretFileCollectionClass *klass)
egg_libgcrypt_initialize (); egg_libgcrypt_initialize ();
} }
static void static gboolean
on_load_contents (GObject *source_object, load_contents (SecretFileCollection *self,
GAsyncResult *result, gchar *contents, /* takes ownership */
gpointer user_data) gsize length,
GError **error)
{ {
GFile *file = G_FILE (source_object);
GTask *task = G_TASK (user_data);
SecretFileCollection *self = g_task_get_source_object (task);
gchar *contents;
gchar *p; gchar *p;
gsize length;
GVariant *variant; GVariant *variant;
GVariant *salt_array; GVariant *salt_array;
guint32 salt_size; guint32 salt_size;
@ -315,70 +328,25 @@ on_load_contents (GObject *source_object,
guint64 usage_count; guint64 usage_count;
gconstpointer data; gconstpointer data;
gsize n_data; gsize n_data;
GError *error = NULL;
gboolean ret;
ret = g_file_load_contents_finish (file, result,
&contents, &length,
&self->etag,
&error);
if (!ret) {
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
GVariantBuilder builder;
guint8 salt[SALT_SIZE];
g_clear_error (&error);
gcry_create_nonce (salt, sizeof(salt));
self->salt = g_bytes_new (salt, sizeof(salt));
self->iteration_count = ITERATION_COUNT;
self->modified = g_date_time_new_now_utc ();
self->usage_count = 0;
if (!do_derive_key (self)) {
g_task_return_new_error (task,
SECRET_ERROR,
SECRET_ERROR_PROTOCOL,
"couldn't derive key");
g_object_unref (task);
return;
}
g_variant_builder_init (&builder,
G_VARIANT_TYPE ("a(a{say}ay)"));
self->items = g_variant_builder_end (&builder);
g_variant_ref_sink (self->items);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
g_task_return_error (task, error);
g_object_unref (task);
return;
}
p = contents; p = contents;
if (length < KEYRING_FILE_HEADER_LEN || if (length < KEYRING_FILE_HEADER_LEN ||
memcmp (p, KEYRING_FILE_HEADER, KEYRING_FILE_HEADER_LEN) != 0) { memcmp (p, KEYRING_FILE_HEADER, KEYRING_FILE_HEADER_LEN) != 0) {
g_task_return_new_error (task, g_set_error_literal (error,
SECRET_ERROR, SECRET_ERROR,
SECRET_ERROR_INVALID_FILE_FORMAT, SECRET_ERROR_INVALID_FILE_FORMAT,
"file header mismatch"); "file header mismatch");
g_object_unref (task); return FALSE;
return;
} }
p += KEYRING_FILE_HEADER_LEN; p += KEYRING_FILE_HEADER_LEN;
length -= KEYRING_FILE_HEADER_LEN; length -= KEYRING_FILE_HEADER_LEN;
if (length < 2 || *p != MAJOR_VERSION || *(p + 1) != MINOR_VERSION) { if (length < 2 || *p != MAJOR_VERSION || *(p + 1) != MINOR_VERSION) {
g_task_return_new_error (task, g_set_error_literal (error,
SECRET_ERROR, SECRET_ERROR,
SECRET_ERROR_INVALID_FILE_FORMAT, SECRET_ERROR_INVALID_FILE_FORMAT,
"version mismatch"); "version mismatch");
g_object_unref (task); return FALSE;
return;
} }
p += 2; p += 2;
length -= 2; length -= 2;
@ -407,19 +375,125 @@ on_load_contents (GObject *source_object,
g_assert (n_data == salt_size); g_assert (n_data == salt_size);
self->salt = g_bytes_new (data, n_data); self->salt = g_bytes_new (data, n_data);
g_variant_unref (salt_array);
g_variant_unref (variant);
if (!do_derive_key (self)) { if (!do_derive_key (self)) {
g_task_return_new_error (task, g_set_error_literal (error,
SECRET_ERROR, SECRET_ERROR,
SECRET_ERROR_PROTOCOL, SECRET_ERROR_PROTOCOL,
"couldn't derive key"); "couldn't derive key");
goto out; return FALSE;
} }
g_task_return_boolean (task, TRUE); return TRUE;
}
static gboolean
init_empty_file (SecretFileCollection *self,
GError **error)
{
GVariantBuilder builder;
guint8 salt[SALT_SIZE];
gcry_create_nonce (salt, sizeof(salt));
self->salt = g_bytes_new (salt, sizeof(salt));
self->iteration_count = ITERATION_COUNT;
self->modified = g_date_time_new_now_utc ();
self->usage_count = 0;
if (!do_derive_key (self)) {
g_set_error_literal (error,
SECRET_ERROR,
SECRET_ERROR_PROTOCOL,
"couldn't derive key");
return FALSE;
}
g_variant_builder_init (&builder,
G_VARIANT_TYPE ("a(a{say}ay)"));
self->items = g_variant_builder_end (&builder);
g_variant_ref_sink (self->items);
return TRUE;
}
static void
ensure_up_to_date (SecretFileCollection *self)
{
guint64 last_modified;
last_modified = get_file_last_modified (self);
if (last_modified != self->file_last_modified) {
gchar *contents = NULL;
gsize length = 0;
gboolean success;
GError *error = NULL;
self->file_last_modified = last_modified;
g_clear_pointer (&self->etag, g_free);
success = g_file_load_contents (self->file, NULL, &contents, &length, &self->etag, &error);
if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
g_clear_error (&error);
success = init_empty_file (self, &error);
}
if (success)
success = load_contents (self, contents, length, &error);
if (!success)
g_debug ("Failed to load file contents: %s", error ? error->message : "Unknown error");
g_clear_error (&error);
}
}
static void
on_load_contents (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GFile *file = G_FILE (source_object);
GTask *task = G_TASK (user_data);
SecretFileCollection *self = g_task_get_source_object (task);
gchar *contents;
gsize length;
GError *error = NULL;
gboolean ret;
self->file_last_modified = get_file_last_modified (self);
ret = g_file_load_contents_finish (file, result,
&contents, &length,
&self->etag,
&error);
if (!ret) {
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
g_clear_error (&error);
if (init_empty_file (self, &error)) {
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
}
g_task_return_error (task, error);
g_object_unref (task);
return;
}
ret = load_contents (self, contents, length, &error);
if (ret)
g_task_return_boolean (task, ret);
else
g_task_return_error (task, error);
out:
g_variant_unref (salt_array);
g_variant_unref (variant);
g_object_unref (task); g_object_unref (task);
} }
@ -546,6 +620,8 @@ secret_file_collection_replace (SecretFileCollection *self,
GDateTime *created = NULL; GDateTime *created = NULL;
GDateTime *modified; GDateTime *modified;
ensure_up_to_date (self);
hashed_attributes = hash_attributes (self, attributes); hashed_attributes = hash_attributes (self, attributes);
if (!hashed_attributes) { if (!hashed_attributes) {
g_set_error (error, g_set_error (error,
@ -657,6 +733,8 @@ secret_file_collection_search (SecretFileCollection *self,
GVariant *child; GVariant *child;
GList *result = NULL; GList *result = NULL;
ensure_up_to_date (self);
g_variant_iter_init (&iter, self->items); g_variant_iter_init (&iter, self->items);
while ((child = g_variant_iter_next_value (&iter)) != NULL) { while ((child = g_variant_iter_next_value (&iter)) != NULL) {
GVariant *hashed_attributes; GVariant *hashed_attributes;
@ -750,6 +828,8 @@ secret_file_collection_clear (SecretFileCollection *self,
GVariant *child; GVariant *child;
gboolean removed = FALSE; gboolean removed = FALSE;
ensure_up_to_date (self);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(a{say}ay)")); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(a{say}ay)"));
g_variant_iter_init (&items, self->items); g_variant_iter_init (&items, self->items);
while ((child = g_variant_iter_next_value (&items)) != NULL) { while ((child = g_variant_iter_next_value (&items)) != NULL) {
@ -791,6 +871,8 @@ on_replace_contents (GObject *source_object,
return; return;
} }
self->file_last_modified = get_file_last_modified (self);
g_task_return_boolean (task, TRUE); g_task_return_boolean (task, TRUE);
g_object_unref (task); g_object_unref (task);
} }