1
0
Fork 0
mirror of https://github.com/Mic92/sops-nix.git synced 2025-03-05 08:07:16 +00:00

sops-install-secrets: symlinkSecret: set uid/gid (with Fchownat) (#32)

Co-authored-by: Jörg Thalheim <Mic92@users.noreply.github.com>
This commit is contained in:
Cole Mickens 2020-08-24 08:24:43 +00:00 committed by GitHub
parent b5db7b2516
commit 24fd158fe6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -15,6 +15,7 @@ import (
"path/filepath"
"strconv"
"strings"
"syscall"
"github.com/Mic92/sops-nix/pkgs/sshkeys"
@ -102,6 +103,28 @@ type appContext struct {
checkMode CheckMode
}
func secureSymlinkChown(symlinkToCheck, expectedTarget string, owner, group int) error {
fd, err := unix.Open(symlinkToCheck, unix.O_CLOEXEC|unix.O_PATH|unix.O_NOFOLLOW, 0)
if err != nil {
return fmt.Errorf("Failed to open %s: %w", symlinkToCheck, err)
}
defer unix.Close(fd)
buf := make([]byte, len(expectedTarget) + 1) // oversize by one to detect trunc
n, err := unix.Readlinkat(fd, "", buf)
if err != nil {
return fmt.Errorf("couldn't readlinkat %s", symlinkToCheck)
}
if n > len(expectedTarget) || string(buf[:n]) != expectedTarget {
return fmt.Errorf("symlink %s does not point to %s", symlinkToCheck, expectedTarget)
}
err = unix.Fchownat(fd, "", owner, group, unix.AT_EMPTY_PATH)
if err != nil {
return fmt.Errorf("cannot change owner of '%s' to %d/%d: %w", symlinkToCheck, owner, group, err)
}
return nil
}
func readManifest(path string) (*manifest, error) {
file, err := os.Open(path)
if err != nil {
@ -116,6 +139,17 @@ func readManifest(path string) (*manifest, error) {
return &m, nil
}
func linksAreEqual(linkTarget, targetFile string, info os.FileInfo, secret *secret) bool {
validUG := true;
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
validUG = validUG && int(stat.Uid) == secret.owner
validUG = validUG && int(stat.Gid) == secret.group
} else {
panic("Failed to cast fileInfo Sys() to *syscall.Stat_t. This is possibly an unsupported OS.")
}
return linkTarget == targetFile && validUG
}
func symlinkSecret(targetFile string, secret *secret) error {
for {
stat, err := os.Lstat(secret.Path)
@ -123,6 +157,9 @@ func symlinkSecret(targetFile string, secret *secret) error {
if err := os.Symlink(targetFile, secret.Path); err != nil {
return fmt.Errorf("Cannot create symlink '%s': %w", secret.Path, err)
}
if err := secureSymlinkChown(secret.Path, targetFile, secret.owner, secret.group); err != nil {
return fmt.Errorf("Cannot chown symlink '%s': %w", secret.Path, err)
}
return nil
} else if err != nil {
return fmt.Errorf("Cannot stat '%s': %w", secret.Path, err)
@ -133,7 +170,7 @@ func symlinkSecret(targetFile string, secret *secret) error {
continue
} else if err != nil {
return fmt.Errorf("Cannot read symlink '%s': %w", secret.Path, err)
} else if linkTarget == targetFile {
} else if linksAreEqual(linkTarget, targetFile, stat, secret) {
return nil
}
}
@ -154,7 +191,7 @@ func symlinkSecrets(targetDir string, secrets []secret) error {
return fmt.Errorf("Cannot create parent directory of '%s': %w", secret.Path, err)
}
if err := symlinkSecret(targetFile, &secret); err != nil {
return err
return fmt.Errorf("Failed to symlink secret '%s': %w", secret.Path, err)
}
}
return nil