diff options
-rw-r--r-- | pkg/sentry/fsimpl/verity/verity_test.go | 130 |
1 files changed, 105 insertions, 25 deletions
diff --git a/pkg/sentry/fsimpl/verity/verity_test.go b/pkg/sentry/fsimpl/verity/verity_test.go index 9b5641092..8bcc14131 100644 --- a/pkg/sentry/fsimpl/verity/verity_test.go +++ b/pkg/sentry/fsimpl/verity/verity_test.go @@ -67,7 +67,7 @@ func newVerityRoot(ctx context.Context) (*vfs.VirtualFilesystem, vfs.VirtualDent }, }) if err != nil { - return nil, vfs.VirtualDentry{}, nil, fmt.Errorf("failed to create verity root mount: %v", err) + return nil, vfs.VirtualDentry{}, nil, fmt.Errorf("NewMountNamespace: %v", err) } root := mntns.Root() return vfsObj, root, func() { @@ -130,11 +130,11 @@ func corruptRandomBit(ctx context.Context, fd *vfs.FileDescription, size int) er randomPos := int64(rand.Intn(size)) byteToModify := make([]byte, 1) if _, err := fd.PRead(ctx, usermem.BytesIOSequence(byteToModify), randomPos, vfs.ReadOptions{}); err != nil { - return fmt.Errorf("lowerFD.PRead failed: %v", err) + return fmt.Errorf("lowerFD.PRead: %v", err) } byteToModify[0] ^= 1 if _, err := fd.PWrite(ctx, usermem.BytesIOSequence(byteToModify), randomPos, vfs.WriteOptions{}); err != nil { - return fmt.Errorf("lowerFD.PWrite failed: %v", err) + return fmt.Errorf("lowerFD.PWrite: %v", err) } return nil } @@ -145,13 +145,13 @@ func TestOpen(t *testing.T) { ctx := contexttest.Context(t) vfsObj, root, cleanup, err := newVerityRoot(ctx) if err != nil { - t.Fatalf("Failed to create new verity root: %v", err) + t.Fatalf("newVerityRoot: %v", err) } defer cleanup() filename := "verity-test-file" if _, _, err := newFileFD(ctx, vfsObj, root, filename, 0644); err != nil { - t.Fatalf("Failed to create new file fd: %v", err) + t.Fatalf("newFileFD: %v", err) } // Ensure that the corresponding Merkle tree file is created. @@ -163,7 +163,7 @@ func TestOpen(t *testing.T) { }, &vfs.OpenOptions{ Flags: linux.O_RDONLY, }); err != nil { - t.Errorf("Failed to open Merkle tree file %s: %v", merklePrefix+filename, err) + t.Errorf("OpenAt Merkle tree file %s: %v", merklePrefix+filename, err) } // Ensure the root merkle tree file is created. @@ -174,7 +174,7 @@ func TestOpen(t *testing.T) { }, &vfs.OpenOptions{ Flags: linux.O_RDONLY, }); err != nil { - t.Errorf("Failed to open root Merkle tree file %s: %v", merklePrefix+rootMerkleFilename, err) + t.Errorf("OpenAt root Merkle tree file %s: %v", merklePrefix+rootMerkleFilename, err) } } @@ -184,27 +184,27 @@ func TestReadUntouchedFileSucceeds(t *testing.T) { ctx := contexttest.Context(t) vfsObj, root, cleanup, err := newVerityRoot(ctx) if err != nil { - t.Fatalf("Failed to create new verity root: %v", err) + t.Fatalf("newVerityRoot: %v", err) } defer cleanup() filename := "verity-test-file" fd, size, err := newFileFD(ctx, vfsObj, root, filename, 0644) if err != nil { - t.Fatalf("Failed to create new file fd: %v", err) + t.Fatalf("newFileFD: %v", err) } // Enable verity on the file and confirm a normal read succeeds. var args arch.SyscallArguments args[1] = arch.SyscallArgument{Value: linux.FS_IOC_ENABLE_VERITY} if _, err := fd.Ioctl(ctx, nil /* uio */, args); err != nil { - t.Fatalf("Ioctl failed: %v", err) + t.Fatalf("Ioctl: %v", err) } buf := make([]byte, size) n, err := fd.PRead(ctx, usermem.BytesIOSequence(buf), 0 /* offset */, vfs.ReadOptions{}) if err != nil && err != io.EOF { - t.Fatalf("fd.PRead failed: %v", err) + t.Fatalf("fd.PRead: %v", err) } if n != int64(size) { @@ -218,21 +218,21 @@ func TestReopenUntouchedFileSucceeds(t *testing.T) { ctx := contexttest.Context(t) vfsObj, root, cleanup, err := newVerityRoot(ctx) if err != nil { - t.Fatalf("Failed to create new verity root: %v", err) + t.Fatalf("newVerityRoot: %v", err) } defer cleanup() filename := "verity-test-file" fd, _, err := newFileFD(ctx, vfsObj, root, filename, 0644) if err != nil { - t.Fatalf("Failed to create new file fd: %v", err) + t.Fatalf("newFileFD: %v", err) } // Enable verity on the file and confirms a normal read succeeds. var args arch.SyscallArguments args[1] = arch.SyscallArgument{Value: linux.FS_IOC_ENABLE_VERITY} if _, err := fd.Ioctl(ctx, nil /* uio */, args); err != nil { - t.Fatalf("Ioctl failed: %v", err) + t.Fatalf("Ioctl: %v", err) } // Ensure reopening the verity enabled file succeeds. @@ -253,21 +253,21 @@ func TestModifiedFileFails(t *testing.T) { ctx := contexttest.Context(t) vfsObj, root, cleanup, err := newVerityRoot(ctx) if err != nil { - t.Fatalf("Failed to create new verity root: %v", err) + t.Fatalf("newVerityRoot: %v", err) } defer cleanup() filename := "verity-test-file" fd, size, err := newFileFD(ctx, vfsObj, root, filename, 0644) if err != nil { - t.Fatalf("Failed to create new file fd: %v", err) + t.Fatalf("newFileFD: %v", err) } // Enable verity on the file. var args arch.SyscallArguments args[1] = arch.SyscallArgument{Value: linux.FS_IOC_ENABLE_VERITY} if _, err := fd.Ioctl(ctx, nil /* uio */, args); err != nil { - t.Fatalf("Ioctl failed: %v", err) + t.Fatalf("Ioctl: %v", err) } // Open a new lowerFD that's read/writable. @@ -280,11 +280,11 @@ func TestModifiedFileFails(t *testing.T) { Flags: linux.O_RDWR, }) if err != nil { - t.Fatalf("Open lowerFD failed: %v", err) + t.Fatalf("OpenAt: %v", err) } if err := corruptRandomBit(ctx, lowerFD, size); err != nil { - t.Fatalf("%v", err) + t.Fatalf("corruptRandomBit: %v", err) } // Confirm that read from the modified file fails. @@ -300,21 +300,21 @@ func TestModifiedMerkleFails(t *testing.T) { ctx := contexttest.Context(t) vfsObj, root, cleanup, err := newVerityRoot(ctx) if err != nil { - t.Fatalf("Failed to create new verity root: %v", err) + t.Fatalf("newVerityRoot: %v", err) } defer cleanup() filename := "verity-test-file" fd, size, err := newFileFD(ctx, vfsObj, root, filename, 0644) if err != nil { - t.Fatalf("Failed to create new file fd: %v", err) + t.Fatalf("newFileFD: %v", err) } // Enable verity on the file. var args arch.SyscallArguments args[1] = arch.SyscallArgument{Value: linux.FS_IOC_ENABLE_VERITY} if _, err := fd.Ioctl(ctx, nil /* uio */, args); err != nil { - t.Fatalf("Ioctl failed: %v", err) + t.Fatalf("Ioctl: %v", err) } // Open a new lowerMerkleFD that's read/writable. @@ -327,17 +327,17 @@ func TestModifiedMerkleFails(t *testing.T) { Flags: linux.O_RDWR, }) if err != nil { - t.Fatalf("Open lowerMerkleFD failed: %v", err) + t.Fatalf("OpenAt: %v", err) } // Flip a random bit in the Merkle tree file. stat, err := lowerMerkleFD.Stat(ctx, vfs.StatOptions{}) if err != nil { - t.Fatalf("Failed to get lowerMerkleFD stat: %v", err) + t.Fatalf("stat: %v", err) } merkleSize := int(stat.Size) if err := corruptRandomBit(ctx, lowerMerkleFD, merkleSize); err != nil { - t.Fatalf("%v", err) + t.Fatalf("corruptRandomBit: %v", err) } // Confirm that read from a file with modified Merkle tree fails. @@ -347,3 +347,83 @@ func TestModifiedMerkleFails(t *testing.T) { t.Fatalf("fd.PRead succeeded with modified Merkle file") } } + +// TestModifiedParentMerkleFails ensures that open a verity enabled file in a +// verity enabled directory fails if the hashes related to the target file in +// the parent Merkle tree file is modified. +func TestModifiedParentMerkleFails(t *testing.T) { + ctx := contexttest.Context(t) + vfsObj, root, cleanup, err := newVerityRoot(ctx) + if err != nil { + t.Fatalf("newVerityRoot: %v", err) + } + defer cleanup() + + filename := "verity-test-file" + fd, _, err := newFileFD(ctx, vfsObj, root, filename, 0644) + if err != nil { + t.Fatalf("newFileFD: %v", err) + } + + // Enable verity on the file. + var args arch.SyscallArguments + args[1] = arch.SyscallArgument{Value: linux.FS_IOC_ENABLE_VERITY} + if _, err := fd.Ioctl(ctx, nil /* uio */, args); err != nil { + t.Fatalf("Ioctl: %v", err) + } + + // Enable verity on the parent directory. + parentFD, err := vfsObj.OpenAt(ctx, auth.CredentialsFromContext(ctx), &vfs.PathOperation{ + Root: root, + Start: root, + }, &vfs.OpenOptions{ + Flags: linux.O_RDONLY, + }) + if err != nil { + t.Fatalf("OpenAt: %v", err) + } + + if _, err := parentFD.Ioctl(ctx, nil /* uio */, args); err != nil { + t.Fatalf("Ioctl: %v", err) + } + + // Open a new lowerMerkleFD that's read/writable. + parentLowerMerkleVD := fd.Impl().(*fileDescription).d.parent.lowerMerkleVD + + parentLowerMerkleFD, err := vfsObj.OpenAt(ctx, auth.CredentialsFromContext(ctx), &vfs.PathOperation{ + Root: parentLowerMerkleVD, + Start: parentLowerMerkleVD, + }, &vfs.OpenOptions{ + Flags: linux.O_RDWR, + }) + if err != nil { + t.Fatalf("OpenAt: %v", err) + } + + // Flip a random bit in the parent Merkle tree file. + // This parent directory contains only one child, so any random + // modification in the parent Merkle tree should cause verification + // failure when opening the child file. + stat, err := parentLowerMerkleFD.Stat(ctx, vfs.StatOptions{}) + if err != nil { + t.Fatalf("stat: %v", err) + } + parentMerkleSize := int(stat.Size) + if err := corruptRandomBit(ctx, parentLowerMerkleFD, parentMerkleSize); err != nil { + t.Fatalf("corruptRandomBit: %v", err) + } + + parentLowerMerkleFD.DecRef(ctx) + + // Ensure reopening the verity enabled file fails. + if _, err = vfsObj.OpenAt(ctx, auth.CredentialsFromContext(ctx), &vfs.PathOperation{ + Root: root, + Start: root, + Path: fspath.Parse(filename), + }, &vfs.OpenOptions{ + Flags: linux.O_RDONLY, + Mode: linux.ModeRegular, + }); err == nil { + t.Errorf("OpenAt file with modified parent Merkle succeeded") + } +} |