aboutsummaryrefslogtreecommitdiff
path: root/actions/recovery.go
diff options
context:
space:
mode:
authorKaran Kurani <karankurani3k@gmail.com>2026-04-13 23:32:12 +0530
committerEric Biggers <ebiggers3@gmail.com>2026-04-14 23:30:23 -0700
commit2dee71cdc2a7bccead530a42dfd10736e8de45a9 (patch)
treee41917046ca1f9fbc8f2df748066710bd26369eb /actions/recovery.go
parent298ed2a6c44cde90b4262b884169c53b8deda508 (diff)
recovery: add O_NOFOLLOW|O_EXCL to prevent symlink-following in recovery file creation
WriteRecoveryInstructions() opens the recovery README with os.OpenFile using O_WRONLY|O_CREATE without O_NOFOLLOW. When fscrypt encrypt runs as root, this allows a local attacker to place a symlink at the recovery file path, causing root to write through the symlink and then fchown the target file to the attacker. Adding O_EXCL|O_NOFOLLOW aligns with the existing security pattern in filesystem.go:608 and filesystem.go:747.
Diffstat (limited to 'actions/recovery.go')
-rw-r--r--actions/recovery.go3
1 files changed, 2 insertions, 1 deletions
diff --git a/actions/recovery.go b/actions/recovery.go
index 2bb8a23..3000be6 100644
--- a/actions/recovery.go
+++ b/actions/recovery.go
@@ -23,6 +23,7 @@ import (
"os"
"strconv"
+ "golang.org/x/sys/unix"
"google.golang.org/protobuf/proto"
"github.com/google/fscrypt/crypto"
@@ -91,7 +92,7 @@ func AddRecoveryPassphrase(policy *Policy, dirname string) (*crypto.Key, *Protec
// passphrase in a different location if they actually need it.
func WriteRecoveryInstructions(recoveryPassphrase *crypto.Key, recoveryProtector *Protector,
policy *Policy, path string) error {
- file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600)
+ file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL|unix.O_NOFOLLOW, 0600)
if err != nil {
return err
}