https://bugs.kde.org/show_bug.cgi?id=520509

            Bug ID: 520509
           Summary: ksecretd silently corrupts binary secrets when
                    `content_type` starts with "text/"
    Classification: Frameworks and Libraries
           Product: frameworks-kwallet
      Version First 6.24.0
       Reported In:
          Platform: Compiled Sources
                OS: Linux
            Status: REPORTED
          Severity: normal
          Priority: NOR
         Component: general
          Assignee: [email protected]
          Reporter: [email protected]
                CC: [email protected]
  Target Milestone: ---

Created attachment 192531
  --> https://bugs.kde.org/attachment.cgi?id=192531&action=edit
MRE

DESCRIPTION

ksecretd irreversibly corrupts binary secret values stored via
org.freedesktop.secrets when the secret's content_type starts with "text/".
Non-UTF-8 byte sequences are replaced with U+FFFD (0xEF 0xBF 0xBD), destroying
the data. Secrets stored with "application/octet-stream" are not affected.

Root cause: In KWalletFreedesktopItem::SetSecret
(src/runtime/ksecretd/kwalletfreedesktopitem.cpp), when
mimeType.startsWith("text/"), the value bytes are passed through
QString::fromUtf8() and stored as a KWallet password. QString::fromUtf8()
replaces invalid UTF-8 sequences with U+FFFD. On read-back via GetSecret, the
mangled data is returned.

The freedesktop Secret Service spec
(https://specifications.freedesktop.org/secret-service/latest-single/) defines
"value" as an array of bytes. The service must not interpret or transcode the
value based on content_type — it is metadata for clients only.

Real-world impact: Atlassian CLI (acli), Salesforce CLI, Zed editor, and any
app using go-keyring (which always sets content_type="text/plain;
charset=utf8") cannot store credentials on KDE.

STEPS TO REPRODUCE
1. Save the attached mre_ksecretd.py
2. Install deps: sudo apt install libdbus-1-dev libglib2.0-dev
3. Run: uv run mre_ksecretd.py (or: pip install dbus-python && python
mre_ksecretd.py)


OBSERVED RESULT

```
--- text/plain; charset=utf8 ---
Original:  83 bytes, hex: 1f8b08000000000002fff348cdc9c9d75128c9c8
Retrieved: 163 bytes, hex: 1fefbfbd08000000000002efbfbdefbfbd48efbf
Result: FAIL — 40 U+FFFD replacements

--- application/octet-stream ---
Original:  83 bytes, hex: 1f8b08000000000002fff348cdc9c9d75128c9c8
Retrieved: 83 bytes, hex: 1f8b08000000000002fff348cdc9c9d75128c9c8
Result: PASS

83 bytes stored, 163 bytes retrieved. Every non-UTF-8 byte replaced with 0xEF
0xBF 0xBD. Data irreversibly destroyed.
```

EXPECTED RESULT
Both content types should round-trip the secret value without modification. 83
bytes in, 83 identical bytes out.

SOFTWARE/OS VERSIONS
Operating System: Kubuntu 26.04 LTS
KDE Plasma Version: 6.6.4
KDE Frameworks Version: 6.24.0
Qt Version: 6.10.2
Kernel Version: 7.0.0-15-generic (64-bit)


ADDITIONAL INFORMATION

Suggested fix: When mimeType starts with "text/", validate that the byte array
is valid UTF-8 before passing to QString::fromUtf8(). If validation fails,
either fall back to Stream (binary) storage, or reject the operation with an
error (preferable to silent data corruption).

Related issues (same symptoms, no root cause identified):
- https://github.com/forcedotcom/cli/issues/2174 (Salesforce CLI — tokens
corrupted in KWallet)
- https://github.com/zed-industries/zed/issues/34024 (Zed editor — secrets not
readable from KWallet)

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to