Skip to content

libsecret calls can block forever when the Secret Service prompts #74

Description

@StuartMeeks

Severity: medium — an unattended CLI hangs with no output and no way to recover. Verified: reproduced against KWallet's shim.

Found while validating #19.

What happens

All four libsecret entry points are invoked with a NULL GCancellable:

LibsecretCredentialManager.cs:549   secret_password_storev_sync(..., IntPtr.Zero, out var error)
LibsecretCredentialManager.cs:574   secret_password_lookupv_sync(..., IntPtr.Zero, out var error)
LibsecretCredentialManager.cs:617   secret_password_clearv_sync(..., IntPtr.Zero, out var error)
LibsecretCredentialManager.cs:633   secret_password_searchv_sync(..., IntPtr.Zero, out var error)

Prompting is part of the Secret Service contract — an implementation may need to unlock a collection before serving a request. When it does, these synchronous calls block until the prompt is answered. With no GUI there is nothing to answer it, so they block indefinitely: no timeout, no cancellation, no output.

Reproduction

Against KWallet's ksecretd on the default collection, a single AddCredentialAsync never returns. ksecretd logs:

kf.i18n: 0 instead of 1 arguments to message "<html><head/><body><..." supplied before conversion
Using kwallet without parent window!

— it is trying to raise a wallet-unlock dialog. The probe had to be killed at 120s.

GNOME Keyring does not expose this today because CI and the dev box run it already-unlocked, but the same path exists there: a locked login keyring will prompt.

Why it matters beyond KWallet

The public API is Task-returning and ImportCredentialsCommand/ExportCredentialsCommand already thread a CancellationToken from Spectre.Console.Cli. A caller who cancels gets no cancellation at all once execution is inside one of these calls, because the token never reaches libsecret.

Fix sketch

Create a GCancellable per operation, wire it to the caller's CancellationToken (g_cancellable_cancel on token registration), and pass it instead of IntPtr.Zero. ICredentialManager's methods would need to accept a CancellationToken to carry it end to end, which is a breaking interface change — so either that, or a bounded internal timeout as a smaller non-breaking step.

Worth deciding which before starting: the interface change is a major-version item, the timeout is not.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions