A template restore destroys the template; refuse it, and stop caching a failed load
The paired test I built cannot exist on this hardware, and finding that out cost Jorijn his enrolled finger. Restoring an earlier container byte for byte made the trustlet read its header, UNLINK it, look for a .bak and advance the RPMB counter -- for both containers. QTEE binds every SFS object to a hardware anti-rollback counter, so an earlier version is not stale to it, it is tampered, and the response to tampering is deletion. No copy can undo that. The journal has warned about this counter since August in the context of the group index; it applies to the template itself just as hard, and I did not check before building a feature on the assumption that it did not. So restore refuses and explains, with the measurement in the message. Snapshots stay: they are still forensic evidence of what a template looked like, they just cannot be put back. Changing a template means enrolling again. The daemon compounded it. The group-selection cache stored "0 templates loaded" as a valid answer, so once the trustlet had unlinked everything, every later claim answered "skipped (already active, 0 template(s))" and never tried again. It now caches only a positive result, so a failed load is retried rather than made permanent.
This commit is contained in:
parent
756093e847
commit
30bb987c27
2 changed files with 38 additions and 3 deletions
|
|
@ -1230,7 +1230,12 @@ public:
|
|||
// store: an enrolment, or a template removed underneath us.
|
||||
int SetActiveGroup(std::uint32_t gid, bool force = false) {
|
||||
namespace ta = fingerprintd::ta;
|
||||
if (!force && gid == gid_ && templatesLoaded_ >= 0) {
|
||||
// > 0, never >= 0: caching a ZERO turns a failed load into a
|
||||
// permanent one. It did exactly that on 2026-09-05 -- the trustlet
|
||||
// unlinked a restored template, reported 0, and every later claim
|
||||
// answered "skipped (already active, 0 template(s))" without ever
|
||||
// trying again.
|
||||
if (!force && gid == gid_ && templatesLoaded_ > 0) {
|
||||
std::println("SET_ACTIVE_GROUP gid={} skipped (already active, {} template(s))",
|
||||
gid, templatesLoaded_);
|
||||
return templatesLoaded_;
|
||||
|
|
|
|||
Loading…
Reference in a new issue