Fix: store KWallet passwords via D-Bus (kwallet-query -w never persists)

kwallet-query --write-password returns success but does not create the folder
or persist the secret, so saved host passwords were silently lost and share
enumeration failed with NT_STATUS_LOGON_FAILURE. Write/delete now go through
the kwalletd6 D-Bus API via qdbus6 (open/createFolder/writePassword). Reads
stay on kwallet-query (they work). Also drop smbclient -N (was forcing an
anonymous session) and silence KWallet read stderr. Adds qt6-tools dep.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaroslav Beneš
2026-07-17 19:00:27 +02:00
parent e6eae24a7f
commit 742d2416d7
3 changed files with 23 additions and 11 deletions
+2 -1
View File
@@ -25,7 +25,8 @@ Install these with your distribution's package manager:
| `mount.cifs` | `cifs-utils` | mount CIFS shares | | `mount.cifs` | `cifs-utils` | mount CIFS shares |
| `smbclient` | `smbclient` | list shares on a host | | `smbclient` | `smbclient` | list shares on a host |
| `pkexec` | `polkit` | authorize the mount helper | | `pkexec` | `polkit` | authorize the mount helper |
| `kwallet-query` | `kwallet` (KF6) | store/read passwords in KWallet | | `kwallet-query` | `kwallet` (KF6) | read passwords from KWallet |
| `qdbus6` | `qt6-tools` | write passwords to KWallet (D-Bus) |
| `findmnt` | `util-linux` | detect mounted shares | | `findmnt` | `util-linux` | detect mounted shares |
On Debian/Ubuntu the equivalents are `plasma-workspace`, `cifs-utils`, On Debian/Ubuntu the equivalents are `plasma-workspace`, `cifs-utils`,
+1
View File
@@ -33,6 +33,7 @@ check pkexec "polkit"
check mount.cifs "cifs-utils" check mount.cifs "cifs-utils"
check smbclient "smbclient" check smbclient "smbclient"
check kwallet-query "kwallet (KF6)" check kwallet-query "kwallet (KF6)"
check qdbus6 "qt6-tools"
check findmnt "util-linux" check findmnt "util-linux"
if [ "$missing" -ne 0 ]; then if [ "$missing" -ne 0 ]; then
warn "resolve the above and re-run. Continuing anyway..." warn "resolve the above and re-run. Continuing anyway..."
+20 -10
View File
@@ -21,6 +21,8 @@
// KWallet folder + wallet used for all stored NAS passwords. // KWallet folder + wallet used for all stored NAS passwords.
var WALLET = "kdewallet"; var WALLET = "kdewallet";
var WALLET_FOLDER = "Synology NAS"; var WALLET_FOLDER = "Synology NAS";
var APPID = "synology-nas";
var KWD = "org.kde.kwalletd6 /modules/kwalletd6 org.kde.KWallet";
var HELPER = "/usr/lib/synology-nas/helper"; var HELPER = "/usr/lib/synology-nas/helper";
// POSIX single-quote escaping: wrap in '...' and replace ' with '\''. // POSIX single-quote escaping: wrap in '...' and replace ' with '\''.
@@ -73,8 +75,8 @@ function unc(host, share) {
// env var (read by smbclient) so it never reaches argv. // env var (read by smbclient) so it never reaches argv.
function enumerateCmd(host) { function enumerateCmd(host) {
var script = var script =
"export PASSWD=\"$(kwallet-query -f 'Synology NAS' -r \"$1\" " + WALLET + ")\"\n" + "export PASSWD=\"$(kwallet-query -f 'Synology NAS' -r \"$1\" " + WALLET + " 2>/dev/null)\"\n" +
"exec smbclient -L \"//$2\" -U \"$3\" -g -N 2>/dev/null"; "exec smbclient -L \"//$2\" -U \"$3\" -g 2>/dev/null";
return shCmd(script, [walletKey(host), host.host, host.username || ""]); return shCmd(script, [walletKey(host), host.host, host.username || ""]);
} }
@@ -99,7 +101,7 @@ function mountCmd(settings, host, share) {
"cf=\"$runtime/$1.cred\"\n" + "cf=\"$runtime/$1.cred\"\n" +
"trap 'rm -f \"$cf\"' EXIT INT TERM\n" + "trap 'rm -f \"$cf\"' EXIT INT TERM\n" +
"{ printf 'username=%s\\n' \"$3\"; printf 'domain=%s\\n' \"$4\"; " + "{ printf 'username=%s\\n' \"$3\"; printf 'domain=%s\\n' \"$4\"; " +
"printf 'password='; kwallet-query -f 'Synology NAS' -r \"$2\" " + WALLET + "; } > \"$cf\" || exit 1\n" + "printf 'password='; kwallet-query -f 'Synology NAS' -r \"$2\" " + WALLET + " 2>/dev/null; } > \"$cf\" || exit 1\n" +
"chmod 600 \"$cf\"\n" + "chmod 600 \"$cf\"\n" +
"exec pkexec " + HELPER + " mount \"$5\" \"$6\" \"$cf\" \"$7\" \"$8\" \"$9\" \"${10}\"\n"; "exec pkexec " + HELPER + " mount \"$5\" \"$6\" \"$cf\" \"$7\" \"$8\" \"$9\" \"${10}\"\n";
var args = [ var args = [
@@ -132,18 +134,26 @@ function openCmd(path) {
return shCmd("exec xdg-open \"$1\"", [path]); return shCmd("exec xdg-open \"$1\"", [path]);
} }
// Save a password to KWallet (used by the config page). kwallet-query reads the // Save a password to KWallet (used by the config page). Uses the kwalletd6
// secret from stdin; we feed it via printf from a positional param. The value // D-Bus API via qdbus6: `kwallet-query --write-password` reports success but
// is in this process's argv for the brief write (unavoidable with a CLI-only // does not actually persist (nor create the folder), so we open the wallet,
// KWallet path) — see the security notes in the README. // ensure the folder exists, and writePassword. The password is in this
// process's argv for the brief write — see the security notes in the README.
function savePasswordCmd(host, password) { function savePasswordCmd(host, password) {
var script = "printf '%s' \"$2\" | kwallet-query -f 'Synology NAS' -w \"$1\" " + WALLET; var script =
"h=$(qdbus6 " + KWD + ".open " + WALLET + " 0 " + APPID + ")\n" +
"[ -n \"$h\" ] || exit 1\n" +
"qdbus6 " + KWD + ".createFolder \"$h\" '" + WALLET_FOLDER + "' " + APPID + " >/dev/null\n" +
"exec qdbus6 " + KWD + ".writePassword \"$h\" '" + WALLET_FOLDER + "' \"$1\" \"$2\" " + APPID;
return shCmd(script, [walletKey(host), password]); return shCmd(script, [walletKey(host), password]);
} }
// Remove a host's stored password (best effort; overwrites with empty). // Remove a host's stored password (best effort).
function deletePasswordCmd(host) { function deletePasswordCmd(host) {
var script = "printf '' | kwallet-query -f 'Synology NAS' -w \"$1\" " + WALLET + " 2>/dev/null || true"; var script =
"h=$(qdbus6 " + KWD + ".open " + WALLET + " 0 " + APPID + ")\n" +
"[ -n \"$h\" ] || exit 0\n" +
"qdbus6 " + KWD + ".removeEntry \"$h\" '" + WALLET_FOLDER + "' \"$1\" " + APPID + " >/dev/null 2>&1 || true";
return shCmd(script, [walletKey(host)]); return shCmd(script, [walletKey(host)]);
} }