// Package recoverytrust validates persisted destructive intent and its private keys. package recoverytrust import ( "fmt" "os" "path/filepath" ) // ValidateDirectory requires both the transaction directory and // its sibling-artifact parent to be protected. Ancestors may be sticky shared // directories (e.g. /tmp) only above that protected parent. Trusting only a // 0700 transaction directory is insufficient when its parent can replace it. func ValidateDirectory(path string, protectedLevels int) error { info, err := os.Lstat(path) if err != nil { return fmt.Errorf("inspect recovery directory: %w", err) } if !info.IsDir() || info.Mode()&os.ModeSymlink != 0 { return fmt.Errorf("recovery directory is not a real directory: %s", path) } resolved, err := filepath.EvalSymlinks(path) if err != nil { return err } for depth := 0; ; depth++ { info, err := os.Lstat(resolved) if err != nil { return err } if err := validateRecoveryPermissions(resolved, info, depth >= protectedLevels); err != nil { return fmt.Errorf("untrusted recovery directory: %w", err) } parent := filepath.Dir(resolved) if parent == resolved { return nil } resolved = parent } } // ValidateOwnedDirectory checks a recovery artifact itself. Authenticated installers // may use shared application parents; the private backup still must be owned and // protected against mutation by other users. func ValidateOwnedDirectory(path string) error { info, err := os.Lstat(path) if err != nil { return fmt.Errorf("inspect recovery directory: %w", err) } if !info.IsDir() || info.Mode()&os.ModeSymlink != 0 { return fmt.Errorf("recovery directory is not a real directory: %s", path) } return validateRecoveryPermissions(path, info, false) } func ValidateFile(path string) error { info, err := os.Lstat(path) if err != nil { return fmt.Errorf("inspect recovery file: %w", err) } if !info.Mode().IsRegular() { return fmt.Errorf("recovery file is not regular: %s", path) } if err := validateRecoveryPermissions(path, info, false); err != nil { return fmt.Errorf("untrusted recovery file: %w", err) } return nil } // ValidatePrivateFile rejects a key/journal that other users can read or modify. func ValidatePrivateFile(path string) error { if err := ValidateFile(path); err != nil { return err } info, err := os.Lstat(path) if err != nil { return err } return validatePrivatePermissions(path, info) }