fix: harden maintenance operations
This commit is contained in:
@@ -18,6 +18,22 @@ import (
|
||||
|
||||
type Owner struct{ UID, GID int }
|
||||
|
||||
// ExistingTrustedRoot returns the nearest existing ancestor without resolving
|
||||
// symlinks. Descriptor traversal will subsequently reject a symlink ancestor.
|
||||
func ExistingTrustedRoot(path string) string {
|
||||
candidate := filepath.Dir(filepath.Clean(path))
|
||||
for {
|
||||
if _, err := os.Lstat(candidate); err == nil || !errors.Is(err, fs.ErrNotExist) {
|
||||
return candidate
|
||||
}
|
||||
parent := filepath.Dir(candidate)
|
||||
if parent == candidate {
|
||||
return candidate
|
||||
}
|
||||
candidate = parent
|
||||
}
|
||||
}
|
||||
|
||||
func OpenNoFollow(path string, flags int, mode uint32) (*os.File, error) {
|
||||
fd, err := unix.Open(path, flags|unix.O_CLOEXEC|unix.O_NOFOLLOW, mode)
|
||||
if err != nil {
|
||||
@@ -55,6 +71,45 @@ type Store struct {
|
||||
Owner *Owner
|
||||
}
|
||||
|
||||
// OpenRegular opens a regular file through a descriptor-anchored parent path.
|
||||
// Parent components and the final file are never followed through symlinks.
|
||||
func (s Store) OpenRegular(path string, flags int, mode os.FileMode) (*os.File, error) {
|
||||
directory, base, err := s.openParent(path, flags&unix.O_CREAT != 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := directory.Chmod(0o700); err != nil {
|
||||
_ = directory.Close()
|
||||
return nil, err
|
||||
}
|
||||
if s.Owner != nil && (s.Owner.UID != os.Geteuid() || s.Owner.GID != os.Getegid()) {
|
||||
if err := directory.Chown(s.Owner.UID, s.Owner.GID); err != nil {
|
||||
_ = directory.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
file, err := OpenAt(directory, base, flags, uint32(mode.Perm()))
|
||||
_ = directory.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := regular(file); err != nil {
|
||||
_ = file.Close()
|
||||
return nil, err
|
||||
}
|
||||
if err := file.Chmod(mode.Perm()); err != nil {
|
||||
_ = file.Close()
|
||||
return nil, err
|
||||
}
|
||||
if s.Owner != nil && (s.Owner.UID != os.Geteuid() || s.Owner.GID != os.Getegid()) {
|
||||
if err := file.Chown(s.Owner.UID, s.Owner.GID); err != nil {
|
||||
_ = file.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (s Store) ReadFile(path string) ([]byte, error) {
|
||||
directory, base, err := s.openParent(path, false)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user