Skip to content

Commit 99fefdc

Browse files
fix(sandbox): drop the stored secret whenever provisioning fails
The cleanup added for partial provisioning left the window it existed for uncovered. It only removed the on-disk secret when this run had written one, and it derived the secret path after the logon-rights grant, so a failure before that point had nothing to remove. That is exactly the case that matters. Provisioning ALWAYS sets the account's password, including resetting a pre-existing account's, so from the moment it returns the stored secret is already stale. A failure in the rights grant then left that stale secret on disk against a password that had just changed, and the next command failed the logon and reported a broken sandbox instead of falling back. The path is now resolved from the account name before anything can fail, and removal is unconditional rather than gated on having written one. Absent beats stale: the command path treats a missing secret as "not provisioned" and falls back to the restricted token, which is the outcome a failed setup should leave behind. Raised by CodeRabbit, twice from different angles, on the commit that added the cleanup.
1 parent 52f843a commit 99fefdc

1 file changed

Lines changed: 15 additions & 11 deletions

File tree

‎internal/sandbox/windows_identity_runtime_windows.go‎

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,20 @@ func provisionWindowsSandboxPrincipalForSetup(config WindowsSandboxCommandConfig
144144
// deleting it because a later run failed would turn a partial failure into a
145145
// total one.
146146
rightsGranted := false
147-
secretWritten := false
148-
secretPath := ""
147+
// Resolved from the account name rather than the identity, so it is known
148+
// before anything can fail. Deriving it later, after the rights grant, left
149+
// the one window this cleanup exists for uncovered: provisioning ALWAYS sets
150+
// the password, including resetting a pre-existing account's, so from the
151+
// moment it returns the stored secret is already stale. A failure before the
152+
// path was computed then had nothing to remove.
153+
secretPath, secretPathErr := windowsSandboxSecretPath(config.SandboxHome, windowsSandboxUserName(key))
149154
undo := func() {
150-
if secretWritten && secretPath != "" {
151-
// Dropping the secret is also the repair for a pre-existing account
152-
// whose password this run reset: the stored secret no longer matches,
153-
// and absent beats stale, since the command path treats a missing
154-
// secret as "not provisioned" and falls back rather than failing.
155+
// Unconditionally, not only when this run wrote one. Provisioning has
156+
// already replaced the account's password by the time any of this can
157+
// fail, so whatever is on disk cannot authenticate. Absent beats stale:
158+
// the command path treats a missing secret as "not provisioned" and falls
159+
// back, while a stale one fails the logon and reports a broken sandbox.
160+
if secretPath != "" {
155161
_ = removeWindowsSandboxSecret(secretPath)
156162
}
157163
if identity.SID != nil && rightsGranted {
@@ -173,10 +179,9 @@ func provisionWindowsSandboxPrincipalForSetup(config WindowsSandboxCommandConfig
173179
return windowsSandboxIdentity{}, err
174180
}
175181
rightsGranted = true
176-
secretPath, err = windowsSandboxSecretPath(config.SandboxHome, identity.Username)
177-
if err != nil {
182+
if secretPathErr != nil {
178183
undo()
179-
return windowsSandboxIdentity{}, err
184+
return windowsSandboxIdentity{}, secretPathErr
180185
}
181186
// The secret is rewritten every run so it stays in step with the account.
182187
// provisionWindowsSandboxIdentity guarantees the password it returns is the
@@ -187,7 +192,6 @@ func provisionWindowsSandboxPrincipalForSetup(config WindowsSandboxCommandConfig
187192
undo()
188193
return windowsSandboxIdentity{}, err
189194
}
190-
secretWritten = true
191195
return identity, nil
192196
}
193197

0 commit comments

Comments
 (0)