From 8989e7aecdd2513cd1d32e0dfde30f5eb3d9b170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sun, 27 Sep 2026 22:46:21 +0000 Subject: [PATCH] credential/libsecret: load secrets explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit keyring_get() searches with SECRET_SEARCH_LOAD_SECRETS, then passes secret_item_get_secret() of the first match unchecked to secret_value_get_text() and secret_value_unref(). As libsecret documents, that secret can be NULL: the search does not load secrets of locked items, such as when SECRET_SEARCH_UNLOCK fails to unlock them, and it ignores errors from loading secrets. The GNOME keyring daemon also silently leaves out of its reply any item which is locked or which was deleted after the search matched it, e.g. by a concurrent "credential erase" from another git process. We then print secret_value_get_text: assertion 'value' failed secret_value_unref: assertion 'value != NULL' failed before git falls back to prompting for the password. We could keep the flag and load the secret explicitly only when it is NULL, but SECRET_SEARCH_LOAD_SECRETS is not part of the search call: libsecret implements it as a separate GetSecrets D-Bus call after SearchItems. Drop the flag and instead always load the one secret we use with secret_item_load_secret_sync(), which reports errors. This takes as many D-Bus calls as before, and leaves a single code path that runs every time, rather than a fallback that only runs in a rare race. libsecret's own secret-tool also loads each secret explicitly after searching. An inaccessible item now produces a useful error message instead of the assertion failures, and git still falls back to prompting. The race needs a concurrent process or a locked keyring to trigger, so there is no test. Signed-off-by: Daniel Martí Signed-off-by: Junio C Hamano --- .../libsecret/git-credential-libsecret.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/contrib/credential/libsecret/git-credential-libsecret.c b/contrib/credential/libsecret/git-credential-libsecret.c index 941b2afd5e..ad4f60e4d7 100644 --- a/contrib/credential/libsecret/git-credential-libsecret.c +++ b/contrib/credential/libsecret/git-credential-libsecret.c @@ -126,7 +126,7 @@ static int keyring_get(struct credential *c) items = secret_service_search_sync(service, &schema, attributes, - SECRET_SEARCH_LOAD_SECRETS | SECRET_SEARCH_UNLOCK, + SECRET_SEARCH_UNLOCK, NULL, &error); g_hash_table_unref(attributes); @@ -143,6 +143,18 @@ static int keyring_get(struct credential *c) gchar **parts; item = items->data; + + /* + * Load the secret explicitly rather than via + * SECRET_SEARCH_LOAD_SECRETS, which skips locked items and + * ignores load failures, leaving the secret NULL. + */ + if (!secret_item_load_secret_sync(item, NULL, &error)) { + g_critical("could not load secret: %s", error->message); + g_error_free(error); + g_list_free_full(items, g_object_unref); + return EXIT_FAILURE; + } secret = secret_item_get_secret(item); attributes = secret_item_get_attributes(item);