Pass hashing algorithms in verity fs opts

PiperOrigin-RevId: 340275942
This commit is contained in:
Chong Cai 2020-11-02 11:15:09 -08:00 committed by gVisor bot
parent 2eb3ee586b
commit ed4f857343
3 changed files with 442 additions and 387 deletions

View File

@ -276,9 +276,9 @@ func (fs *filesystem) verifyChild(ctx context.Context, parent *dentry, child *de
UID: parentStat.UID,
GID: parentStat.GID,
//TODO(b/156980949): Support passing other hash algorithms.
HashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
HashAlgorithms: fs.alg.toLinuxHashAlg(),
ReadOffset: int64(offset),
ReadSize: int64(merkletree.DigestSize(linux.FS_VERITY_HASH_ALG_SHA256)),
ReadSize: int64(merkletree.DigestSize(fs.alg.toLinuxHashAlg())),
Expected: parent.hash,
DataAndTreeInSameFile: true,
}); err != nil && err != io.EOF {
@ -352,7 +352,7 @@ func (fs *filesystem) verifyStat(ctx context.Context, d *dentry, stat linux.Stat
UID: stat.UID,
GID: stat.GID,
//TODO(b/156980949): Support passing other hash algorithms.
HashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
HashAlgorithms: fs.alg.toLinuxHashAlg(),
ReadOffset: 0,
// Set read size to 0 so only the metadata is verified.
ReadSize: 0,

View File

@ -79,6 +79,27 @@ var (
verityMu sync.RWMutex
)
// HashAlgorithm is a type specifying the algorithm used to hash the file
// content.
type HashAlgorithm int
// Currently supported hashing algorithms include SHA256 and SHA512.
const (
SHA256 HashAlgorithm = iota
SHA512
)
func (alg HashAlgorithm) toLinuxHashAlg() int {
switch alg {
case SHA256:
return linux.FS_VERITY_HASH_ALG_SHA256
case SHA512:
return linux.FS_VERITY_HASH_ALG_SHA512
default:
return 0
}
}
// FilesystemType implements vfs.FilesystemType.
//
// +stateify savable
@ -108,6 +129,10 @@ type filesystem struct {
// stores the root hash of the whole file system in bytes.
rootDentry *dentry
// alg is the algorithms used to hash the files in the verity file
// system.
alg HashAlgorithm
// renameMu synchronizes renaming with non-renaming operations in order
// to ensure consistent lock ordering between dentry.dirMu in different
// dentries.
@ -136,6 +161,10 @@ type InternalFilesystemOptions struct {
// LowerName is the name of the filesystem wrapped by verity fs.
LowerName string
// Alg is the algorithms used to hash the files in the verity file
// system.
Alg HashAlgorithm
// RootHash is the root hash of the overall verity file system.
RootHash []byte
@ -194,6 +223,7 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
fs := &filesystem{
creds: creds.Fork(),
alg: iopts.Alg,
lowerMount: mnt,
allowRuntimeEnable: iopts.AllowRuntimeEnable,
}
@ -627,7 +657,7 @@ func (fd *fileDescription) generateMerkle(ctx context.Context) ([]byte, uint64,
TreeReader: &merkleReader,
TreeWriter: &merkleWriter,
//TODO(b/156980949): Support passing other hash algorithms.
HashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
HashAlgorithms: fd.d.fs.alg.toLinuxHashAlg(),
}
switch atomic.LoadUint32(&fd.d.mode) & linux.S_IFMT {
@ -873,7 +903,7 @@ func (fd *fileDescription) PRead(ctx context.Context, dst usermem.IOSequence, of
UID: fd.d.uid,
GID: fd.d.gid,
//TODO(b/156980949): Support passing other hash algorithms.
HashAlgorithms: linux.FS_VERITY_HASH_ALG_SHA256,
HashAlgorithms: fd.d.fs.alg.toLinuxHashAlg(),
ReadOffset: offset,
ReadSize: dst.NumBytes(),
Expected: fd.d.hash,

View File

@ -43,7 +43,7 @@ const maxDataSize = 100000
// newVerityRoot creates a new verity mount, and returns the root. The
// underlying file system is tmpfs. If the error is not nil, then cleanup
// should be called when the root is no longer needed.
func newVerityRoot(t *testing.T) (*vfs.VirtualFilesystem, vfs.VirtualDentry, *kernel.Task, error) {
func newVerityRoot(t *testing.T, hashAlg HashAlgorithm) (*vfs.VirtualFilesystem, vfs.VirtualDentry, *kernel.Task, error) {
k, err := testutil.Boot()
if err != nil {
t.Fatalf("testutil.Boot: %v", err)
@ -70,6 +70,7 @@ func newVerityRoot(t *testing.T) (*vfs.VirtualFilesystem, vfs.VirtualDentry, *ke
InternalData: InternalFilesystemOptions{
RootMerkleFileName: rootMerkleFilename,
LowerName: "tmpfs",
Alg: hashAlg,
AllowRuntimeEnable: true,
NoCrashOnVerificationFailure: true,
},
@ -161,10 +162,13 @@ func corruptRandomBit(ctx context.Context, fd *vfs.FileDescription, size int) er
return nil
}
var hashAlgs = []HashAlgorithm{SHA256, SHA512}
// TestOpen ensures that when a file is created, the corresponding Merkle tree
// file and the root Merkle tree file exist.
func TestOpen(t *testing.T) {
vfsObj, root, ctx, err := newVerityRoot(t)
for _, alg := range hashAlgs {
vfsObj, root, ctx, err := newVerityRoot(t, alg)
if err != nil {
t.Fatalf("newVerityRoot: %v", err)
}
@ -197,11 +201,13 @@ func TestOpen(t *testing.T) {
t.Errorf("OpenAt root Merkle tree file %s: %v", merklePrefix+rootMerkleFilename, err)
}
}
}
// TestPReadUnmodifiedFileSucceeds ensures that pread from an untouched verity
// file succeeds after enabling verity for it.
func TestPReadUnmodifiedFileSucceeds(t *testing.T) {
vfsObj, root, ctx, err := newVerityRoot(t)
for _, alg := range hashAlgs {
vfsObj, root, ctx, err := newVerityRoot(t, alg)
if err != nil {
t.Fatalf("newVerityRoot: %v", err)
}
@ -229,11 +235,13 @@ func TestPReadUnmodifiedFileSucceeds(t *testing.T) {
t.Errorf("fd.PRead got read length %d, want %d", n, size)
}
}
}
// TestReadUnmodifiedFileSucceeds ensures that read from an untouched verity
// file succeeds after enabling verity for it.
func TestReadUnmodifiedFileSucceeds(t *testing.T) {
vfsObj, root, ctx, err := newVerityRoot(t)
for _, alg := range hashAlgs {
vfsObj, root, ctx, err := newVerityRoot(t, alg)
if err != nil {
t.Fatalf("newVerityRoot: %v", err)
}
@ -261,11 +269,13 @@ func TestReadUnmodifiedFileSucceeds(t *testing.T) {
t.Errorf("fd.PRead got read length %d, want %d", n, size)
}
}
}
// TestReopenUnmodifiedFileSucceeds ensures that reopen an untouched verity file
// succeeds after enabling verity for it.
func TestReopenUnmodifiedFileSucceeds(t *testing.T) {
vfsObj, root, ctx, err := newVerityRoot(t)
for _, alg := range hashAlgs {
vfsObj, root, ctx, err := newVerityRoot(t, alg)
if err != nil {
t.Fatalf("newVerityRoot: %v", err)
}
@ -295,11 +305,13 @@ func TestReopenUnmodifiedFileSucceeds(t *testing.T) {
t.Errorf("reopen enabled file failed: %v", err)
}
}
}
// TestPReadModifiedFileFails ensures that read from a modified verity file
// fails.
func TestPReadModifiedFileFails(t *testing.T) {
vfsObj, root, ctx, err := newVerityRoot(t)
for _, alg := range hashAlgs {
vfsObj, root, ctx, err := newVerityRoot(t, alg)
if err != nil {
t.Fatalf("newVerityRoot: %v", err)
}
@ -340,11 +352,13 @@ func TestPReadModifiedFileFails(t *testing.T) {
t.Fatalf("fd.PRead succeeded, expected failure")
}
}
}
// TestReadModifiedFileFails ensures that read from a modified verity file
// fails.
func TestReadModifiedFileFails(t *testing.T) {
vfsObj, root, ctx, err := newVerityRoot(t)
for _, alg := range hashAlgs {
vfsObj, root, ctx, err := newVerityRoot(t, alg)
if err != nil {
t.Fatalf("newVerityRoot: %v", err)
}
@ -385,11 +399,13 @@ func TestReadModifiedFileFails(t *testing.T) {
t.Fatalf("fd.Read succeeded, expected failure")
}
}
}
// TestModifiedMerkleFails ensures that read from a verity file fails if the
// corresponding Merkle tree file is modified.
func TestModifiedMerkleFails(t *testing.T) {
vfsObj, root, ctx, err := newVerityRoot(t)
for _, alg := range hashAlgs {
vfsObj, root, ctx, err := newVerityRoot(t, alg)
if err != nil {
t.Fatalf("newVerityRoot: %v", err)
}
@ -437,12 +453,14 @@ 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) {
vfsObj, root, ctx, err := newVerityRoot(t)
for _, alg := range hashAlgs {
vfsObj, root, ctx, err := newVerityRoot(t, alg)
if err != nil {
t.Fatalf("newVerityRoot: %v", err)
}
@ -515,11 +533,13 @@ func TestModifiedParentMerkleFails(t *testing.T) {
t.Errorf("OpenAt file with modified parent Merkle succeeded")
}
}
}
// TestUnmodifiedStatSucceeds ensures that stat of an untouched verity file
// succeeds after enabling verity for it.
func TestUnmodifiedStatSucceeds(t *testing.T) {
vfsObj, root, ctx, err := newVerityRoot(t)
for _, alg := range hashAlgs {
vfsObj, root, ctx, err := newVerityRoot(t, alg)
if err != nil {
t.Fatalf("newVerityRoot: %v", err)
}
@ -541,11 +561,13 @@ func TestUnmodifiedStatSucceeds(t *testing.T) {
t.Errorf("fd.Stat: %v", err)
}
}
}
// TestModifiedStatFails checks that getting stat for a file with modified stat
// should fail.
func TestModifiedStatFails(t *testing.T) {
vfsObj, root, ctx, err := newVerityRoot(t)
for _, alg := range hashAlgs {
vfsObj, root, ctx, err := newVerityRoot(t, alg)
if err != nil {
t.Fatalf("newVerityRoot: %v", err)
}
@ -578,6 +600,7 @@ func TestModifiedStatFails(t *testing.T) {
t.Errorf("fd.Stat succeeded when it should fail")
}
}
}
// TestOpenDeletedOrRenamedFileFails ensures that opening a deleted/renamed
// verity enabled file or the corresponding Merkle tree file fails with the
@ -616,7 +639,8 @@ func TestOpenDeletedFileFails(t *testing.T) {
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("remove:%t", tc.remove), func(t *testing.T) {
vfsObj, root, ctx, err := newVerityRoot(t)
for _, alg := range hashAlgs {
vfsObj, root, ctx, err := newVerityRoot(t, alg)
if err != nil {
t.Fatalf("newVerityRoot: %v", err)
}
@ -695,6 +719,7 @@ func TestOpenDeletedFileFails(t *testing.T) {
}); err != syserror.EIO {
t.Errorf("got OpenAt error: %v, expected EIO", err)
}
}
})
}
}