Public Access
Let the Linux head check for updates, and seal its device key in the TPM
Two things the RPM shipped without, and the reason they arrive together is
that both are the same question — what may this platform ask of the machine,
and what must it not ask of the user.
◆ UPDATES: THIS CLIENT CHECKS AND FETCHES, AND PACKAGEKIT INSTALLS.
ADR 0013 rule 11 said there was no updater at all, and that went too far. It
conflated noticing that a newer build exists with installing it: the second is
what the trust argument is about, and the first is not — a client that checks
a fixed address and tells you what it found has taken no capability from
anybody. Written as a flat refusal, the rule bought its safety by paying a cost
that was avoidable, and the ADR now says so in its own words.
So LinuxUpdateChannel reads the rolling nightly release, compares versions the
way rpm compares them, downloads the package, verifies its digest and hands the
file to PackageKit over gdbus. Polkit asks; the user may say no; nothing is
torn down before the answer, because ApplyingEndsTheProcess is false and the
shell already knows what that means — it is the phone's shape, and declining an
update must not cost somebody their open shells.
Three details that are load-bearing rather than incidental:
- The comparison is rpmvercmp and not SemVer, because rpm is what will accept
or refuse the install. Same argument ADR 0014 makes for versionCode. The
installed side is read out of the rpm database rather than derived from the
assembly, so the SemVer-to-rpm translation exists in exactly one place. All
28 expected values in RpmVersionTests came out of rpm itself rather than out
of a reading of the algorithm.
- The digest is checked and is not a signature. Velopack verifies its own
downloads and Android checks a signature; an RPM over HTTPS has neither. The
manifest names a SHA-256 and a mismatch deletes the file rather than leaving
something installable-by-hand in the profile directory.
- ONLY_TRUSTED is cleared, which is what routes the request to polkit's
package-install-untrusted action. The package is unsigned, so requiring
trust would fail every update at the last step.
◆ THE DEVICE KEY: SEALED IN THE TPM, BEHIND A PIN THE TPM CHECKS.
ADR 0007's amendment is the bar: a gate inside the process is not a gate. The
key is sealed into a TPM 2.0 keyedhash object whose userAuth comes from a PIN,
so unsealing is a command the chip refuses; the blob is encrypted to the storage
hierarchy and is useless on any other machine; nothing here ever compares a PIN.
The sealed object deliberately carries no NoDA attribute, and that one omission
is the whole reason four digits is enough: every wrong answer advances the TPM's
dictionary-attack counter and the chip stops answering entirely once it trips.
Measured at four wrong tries against a software TPM, after which the correct PIN
gets nothing either. It is asserted by a test rather than left to a reading of
the template, because removing the attribute leaves every other assertion
passing.
This is weaker than Hello and Touch ID and the difference is invisible, so it is
written down in three places rather than smoothed over: those raise their prompt
from inside the platform and this one is a window we draw. The prompt is
deliberately plain for that reason — teaching people that a convincing DodoSSH
PIN box is a system dialog is the one thing this design must not do.
Rejected, each for failing the amendment: the Secret Service API, which has no
per-use consent and is DPAPI's shape exactly; fprintd, which answers a question
and holds nothing, so skipping the call skips the gate; and a polkit-gated root
helper, which would work and puts a privileged component in a package that
installs nothing setuid.
Availability is narrower than "has a TPM": on Fedora /dev/tpmrm0 is root:tss and
desktop users are not in tss, so the ordinary case reports unavailable and keeps
asking for the passphrase. Shipping a udev rule to widen that is refused — it is
the administrator's decision, not an application package's.
◆ WHAT IS ACTUALLY VERIFIED, SINCE THAT IS THE PART THAT USUALLY ISN'T.
This is the only device key store in the repository with real coverage, and it
is only possible because the PIN is a delegate and the TPM is a device: seal,
unseal, wrong PIN, lockout, decline and a foreign blob all run unattended
against a software TPM. Windows and macOS cannot be tested at all — their
prompts block a suite forever, which WindowsDeviceKeyStoreTests records.
TSS.Net's own TcpTpmDevice cannot reach swtpm: it opens the platform socket too
and exchanges a handshake only the Microsoft reference simulator answers, so
Connect() blocks with no error and no timeout. SoftwareTpm speaks the data
channel and nothing else.
The suite also lied once and was fixed: the lockout test trips shared state, and
without a DictionaryAttackLockReset in Dispose every later test got null from a
correct PIN — six of them assert null and passed for entirely the wrong reason.
End to end on a real Fedora 44: a genuine linux-x64 publish packages, installs,
and every DT_NEEDED in the installed tree resolves except liblttng-ust, which is
the one this spec deliberately excludes. What no runner covers is /dev/tpmrm0,
PackageKit and polkit, and Phase 19 grew five checks for exactly those.
Also fixed: the magic-bytes literal picked up raw control characters, which made
LinuxDeviceKeyStore.cs binary to git — no diffs and no line-ending normalisation.
This commit is contained in:
1 parent
1bf79c7c26
commit
4552538533
25 files changed
+2646
-105
No files matched your search
+83
-17
@@ -2844,16 +2844,23 @@ This is the single reason the bundle is named `DodoSSH.Desktop.app`.
|
||||
|
||||
## Phase 19 — Installing the Linux client, and the things dnf does not check
|
||||
|
||||
The Linux counterpart of phases 16 and 18, and the shortest of the three because the platform takes two of
|
||||
their questions off the table. There is no updater here, so nothing in this phase asks whether an update
|
||||
arrives; and there is no signing, because nothing on this platform refuses an unsigned package — a distinct
|
||||
statement from "unsigned is fine", and `build/linux/dodossh.spec` says what stands in for a signature: the
|
||||
package comes from the project's own forge over TLS and from nowhere else.
|
||||
The Linux counterpart of phases 16 and 18. One of their questions is off the table — there is no signing,
|
||||
because nothing on this platform refuses an unsigned package, which is a distinct statement from "unsigned
|
||||
is fine"; `build/linux/dodossh.spec` says what stands in for a signature, which is that the package comes
|
||||
from the project's own forge over TLS and from nowhere else.
|
||||
|
||||
What that leaves is the set nobody else can answer. **CI builds this package and installs nothing**, and
|
||||
every check in `scripts/ci-linux-rpm.sh` is a check on what the package *says* — its dependencies, its file
|
||||
list, its modes. None of them start the application, and the failures below are all of the shape "installs
|
||||
perfectly and then does not work".
|
||||
Everything else is here, and two things are here that neither other phase has. **The update does not
|
||||
install itself**, so 19.7 is about watching a polkit prompt appear and saying no to it, where phases 16 and
|
||||
18 watch an application replace itself. And **the device key's gate is a PIN rather than a gesture**, with
|
||||
the TPM's own lockout behind it — so 19.6 is the one check in this document that deliberately breaks
|
||||
something and then tells you how to put it back.
|
||||
|
||||
What this phase is for is the set nobody else can answer. **CI builds this package and installs nothing**,
|
||||
and every check in `scripts/ci-linux-rpm.sh` is a check on what the package *says* — its dependencies, its
|
||||
file list, its modes. The code does better: `LinuxDeviceKeyStoreTests` runs the whole seal, unseal,
|
||||
wrong-PIN and lockout path against a software TPM, and `LinuxUpdateChannelTests` covers the version
|
||||
comparison and the digest. What none of them touch is `/dev/tpmrm0`, PackageKit, polkit and a real desktop
|
||||
session, and the failures below are all of the shape "installs perfectly and then does not work".
|
||||
|
||||
Needs a Fedora 44 machine or newer with a graphical session. Build the package first:
|
||||
|
||||
@@ -2902,17 +2909,76 @@ are the package's binary name, so they can only disagree if one was edited. A bl
|
||||
48-pixel file missing and the 256 being scaled down; `dodossh-icon.ps1` draws four sizes and the spec
|
||||
installs four.
|
||||
|
||||
### 19.4 The passphrase is asked for, and no device key is offered
|
||||
### 19.4 With no access to the TPM, no device key is offered at all
|
||||
|
||||
Open the security settings page.
|
||||
On a stock Fedora install your account is not in the `tss` group, so this is the state most people are in.
|
||||
Confirm it — `id -nG | tr ' ' '
|
||||
' | grep -x tss` prints nothing — then open the security settings page.
|
||||
|
||||
**Pass:** registering a device key is **not** offered, and unlocking asks for the passphrase every time.
|
||||
|
||||
**Failure means:** being offered it is `DesktopDeviceKeyStores.ForThisMachine` having returned something
|
||||
other than `UnavailableDeviceKeyStore` on a platform with nothing to back it. Accepting the offer would put
|
||||
a wrap on the server that nothing can ever open.
|
||||
**Failure means:** being offered it is `LinuxDeviceKeyStore.IsSupported` having answered from the presence
|
||||
of the device node rather than from opening it. Accepting the offer would put a wrap on the server that
|
||||
nothing can ever open — and the failure would arrive at the next unlock rather than now.
|
||||
|
||||
### 19.5 The nightly installs beside the release rather than over it
|
||||
### 19.5 With access to the TPM, the PIN unlocks and the TPM is what checks it
|
||||
|
||||
`sudo usermod -aG tss $USER`, log out and back in, then register a device key from the security settings
|
||||
page and choose a PIN. Lock the vault and unlock it again.
|
||||
|
||||
**Pass:** registering asks for a PIN twice and shows no other prompt. Unlocking asks for it once and opens
|
||||
the vault. Cancelling that prompt lands on the passphrase screen, which works.
|
||||
|
||||
**Failure means:** no offer after joining the group is `/dev/tpmrm0` still unreadable — check `ls -l` on it
|
||||
and that the login really was restarted, because group membership is fixed at session start. A PIN prompt
|
||||
that opens the vault whatever you type would mean the unseal is not reaching the TPM at all, which is the
|
||||
one failure that would make this whole feature decorative.
|
||||
|
||||
### 19.6 A wrong PIN gets nothing, and the TPM stops answering
|
||||
|
||||
With a device key registered, lock the vault and enter a wrong PIN. Do it several times.
|
||||
|
||||
**Pass:** each attempt lands on the passphrase screen. After a handful, even the **correct** PIN lands
|
||||
there too — the TPM has entered lockout and is refusing everyone, which is the point.
|
||||
|
||||
**Failure means:** wrong PINs that never stop being accepted for the attempt is the `NoDA` attribute having
|
||||
been set on the sealed object, which would make a four-digit PIN guessable at full speed by anything holding
|
||||
the blob and the machine. `LinuxDeviceKeyStoreTests` asserts this against a software TPM; this is the same
|
||||
assertion against real firmware, where the lockout is real and recovery takes hours.
|
||||
|
||||
**Then put it back:** `sudo tpm2_dictionarylockout --clear-lockout`, or wait out the recovery time. Note
|
||||
which, because a user who trips this will ask — and the honest answer is that it needs an administrator.
|
||||
|
||||
### 19.7 An update is noticed, fetched, and installed by PackageKit and not by DodoSSH
|
||||
|
||||
With the nightly installed, wait for a newer one to be published — or press **CHECK NOW** on
|
||||
PREFERENCES → UPDATES once there is one.
|
||||
|
||||
**Pass:** the banner names the new version and downloads it. Pressing install raises the desktop's own
|
||||
*"Authentication is required to install software"* prompt, drawn by polkit and not by DodoSSH. Accepting it
|
||||
installs the package; declining it changes nothing and leaves the vault unlocked and every shell open.
|
||||
DodoSSH does **not** restart, and the version on the preferences screen does not change until you close and
|
||||
reopen it.
|
||||
|
||||
**Failure means:** an update that is offered and then refused by PackageKit is the version comparison
|
||||
disagreeing with rpm — `rpm -q --queryformat '%{VERSION}-%{RELEASE}' dodossh-nightly` against the
|
||||
`rpmVersion` in the release's `linux-nightly.json` is the comparison being made, and `RpmVersionTests`
|
||||
covers the algorithm. A message naming PackageKit means it is not installed; it is a weak dependency, so a
|
||||
minimal install legitimately lacks it. **The vault locking or shells closing when you decline is the
|
||||
serious one** — it means `ApplyingEndsTheProcess` has been set true, which punishes somebody for saying no.
|
||||
|
||||
### 19.8 A tampered package is refused before PackageKit ever sees it
|
||||
|
||||
Not reachable without editing the feed, so this is the one to reason about rather than perform unless you
|
||||
are changing that code: the manifest's `sha256` is checked after the download and the file is deleted if it
|
||||
does not match.
|
||||
|
||||
**Pass:** `~/.local/share/dodossh.nightly/updates/` holds a package only when its digest matched.
|
||||
|
||||
**Failure means:** a leftover `.rpm` there after a failed update is a file somebody could reasonably install
|
||||
by hand, named as though the application had endorsed it.
|
||||
|
||||
### 19.9 The nightly installs beside the release rather than over it
|
||||
|
||||
With the release package installed, install the nightly one too.
|
||||
|
||||
@@ -2925,7 +2991,7 @@ nightly that opens the release's vault already unlocked is the profile separatio
|
||||
`DodoChannel=nightly` reaching `ClientPaths`, not from the packaging, so check
|
||||
`~/.local/share/dodossh.nightly` exists rather than looking at the spec.
|
||||
|
||||
### 19.6 Nothing in the installed tree is writable by anyone but root
|
||||
### 19.10 Nothing in the installed tree is writable by anyone but root
|
||||
|
||||
```bash
|
||||
find /opt/DodoSSH ! -user root -o -perm -o+w
|
||||
@@ -2939,7 +3005,7 @@ model walked around rather than attacked. `ci-linux-rpm.sh` refuses to produce s
|
||||
sets every mode absolutely, so a hit here means one of those two was changed — it cannot arrive by accident
|
||||
from a publish directory.
|
||||
|
||||
### 19.7 Removing it leaves the vault, and leaves the other channel alone
|
||||
### 19.11 Removing it leaves the vault, and leaves the other channel alone
|
||||
|
||||
Register a device, sync something, then `sudo dnf remove dodossh`.
|
||||
|
||||
|
||||
Reference in new issue
Block a user