fix: run DWH verification with read-only registry access
This commit is contained in:
@@ -288,16 +288,38 @@ func (d *Dir) Names() ([]string, error) {
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// Lock opens or creates a protected 0600 lock file and acquires an exclusive
|
||||
// Lock opens or creates a protected 0640 lock file and acquires an exclusive
|
||||
// advisory lock. Close releases the lock and descriptor.
|
||||
func (d *Dir) Lock(name string) (*Lock, error) {
|
||||
return d.lock(name, syscall.LOCK_EX)
|
||||
}
|
||||
|
||||
// LockShared opens or creates a protected 0600 lock file and acquires a shared
|
||||
// advisory lock. Close releases the lock and descriptor.
|
||||
// LockShared opens an existing protected 0640 lock read-only and acquires a
|
||||
// shared advisory lock. It never creates or changes registry state.
|
||||
func (d *Dir) LockShared(name string) (*Lock, error) {
|
||||
return d.lock(name, syscall.LOCK_SH)
|
||||
if err := validName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := d.check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fd, err := syscall.Openat(d.fd, name, syscall.O_RDONLY|syscall.O_NOFOLLOW|syscall.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validateRegularFD(fd, 0o640); err != nil {
|
||||
_ = syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
if err := compareWithLstat(fd, filepath.Join(d.path, name), false); err != nil {
|
||||
_ = syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
if err := syscall.Flock(fd, syscall.LOCK_SH); err != nil {
|
||||
_ = syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
return &Lock{fd: fd}, nil
|
||||
}
|
||||
|
||||
func (d *Dir) lock(name string, lockMode int) (*Lock, error) {
|
||||
@@ -310,14 +332,14 @@ func (d *Dir) lock(name string, lockMode int) (*Lock, error) {
|
||||
for {
|
||||
fd, err := syscall.Openat(d.fd, name, syscall.O_RDWR|syscall.O_NOFOLLOW|syscall.O_CLOEXEC, 0)
|
||||
if errors.Is(err, syscall.ENOENT) {
|
||||
fd, err = syscall.Openat(d.fd, name, syscall.O_RDWR|syscall.O_CREAT|syscall.O_EXCL|syscall.O_NOFOLLOW|syscall.O_CLOEXEC, 0o600)
|
||||
fd, err = syscall.Openat(d.fd, name, syscall.O_RDWR|syscall.O_CREAT|syscall.O_EXCL|syscall.O_NOFOLLOW|syscall.O_CLOEXEC, 0o640)
|
||||
if errors.Is(err, syscall.EEXIST) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := syscall.Fchmod(fd, 0o600); err != nil {
|
||||
if err := syscall.Fchmod(fd, 0o640); err != nil {
|
||||
_ = syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
@@ -329,7 +351,7 @@ func (d *Dir) lock(name string, lockMode int) (*Lock, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validateRegularFD(fd, 0o600); err != nil {
|
||||
if err := validateRegularFD(fd, 0o640); err != nil {
|
||||
_ = syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
@@ -495,7 +517,7 @@ func validateDirectoryFD(fd int) error {
|
||||
if stat.Mode&syscall.S_IFMT != syscall.S_IFDIR {
|
||||
return fmt.Errorf("%w: not a directory", ErrUnsafe)
|
||||
}
|
||||
if stat.Mode&0o7022 != 0 {
|
||||
if stat.Mode&0o5022 != 0 {
|
||||
return fmt.Errorf("%w: unsafe directory mode %04o", ErrUnsafe, stat.Mode&0o7777)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -93,6 +93,33 @@ func TestProtectedPathsRejectSymlinks(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProtectedPathsRejectUnsafeModes(t *testing.T) {
|
||||
t.Run("setgid protected directories", func(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
if err := os.Chmod(root, 0o750|os.ModeSetgid); err != nil {
|
||||
t.Fatalf("Chmod(root) error = %v", err)
|
||||
}
|
||||
dir, err := OpenDir(root)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenDir(2750 root) error = %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = dir.Close() })
|
||||
for _, mode := range []os.FileMode{
|
||||
0o750 | os.ModeSetuid,
|
||||
0o750 | os.ModeSetgid | os.ModeSetuid,
|
||||
0o750 | os.ModeSticky,
|
||||
0o770 | os.ModeSetgid,
|
||||
0o752 | os.ModeSetgid,
|
||||
} {
|
||||
path := t.TempDir()
|
||||
if err := os.Chmod(path, mode); err != nil {
|
||||
t.Fatalf("Chmod(%v) error = %v", mode, err)
|
||||
}
|
||||
if _, err := OpenDir(path); err == nil {
|
||||
t.Fatalf("OpenDir(%v) error = nil, want unsafe-mode refusal", mode)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("root", func(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
if err := os.Chmod(root, 0o770); err != nil {
|
||||
@@ -238,6 +265,22 @@ func TestLockSharedAllowsReadersAndBlocksExclusiveWriter(t *testing.T) {
|
||||
t.Fatal("Dir lacks shared advisory locking")
|
||||
}
|
||||
|
||||
if _, err := firstLocker.LockShared("registry.lock"); err == nil {
|
||||
t.Fatal("LockShared(missing) error = nil, want preprovisioned-lock refusal")
|
||||
}
|
||||
if _, err := os.Lstat(filepath.Join(root, "registry.lock")); !errors.Is(err, os.ErrNotExist) {
|
||||
t.Fatalf("LockShared(missing) created lock: %v", err)
|
||||
}
|
||||
bootstrap, err := third.Lock("registry.lock")
|
||||
if err != nil {
|
||||
t.Fatalf("Lock(bootstrap) error = %v", err)
|
||||
}
|
||||
if err := bootstrap.Close(); err != nil {
|
||||
t.Fatalf("Close(bootstrap) error = %v", err)
|
||||
}
|
||||
if info, err := os.Stat(filepath.Join(root, "registry.lock")); err != nil || info.Mode().Perm() != 0o640 {
|
||||
t.Fatalf("bootstrapped lock mode = (%v, %v), want 0640", info, err)
|
||||
}
|
||||
firstShared, err := firstLocker.LockShared("registry.lock")
|
||||
if err != nil {
|
||||
t.Fatalf("LockShared(first) error = %v", err)
|
||||
|
||||
Reference in New Issue
Block a user