Pass hashing algorithms in verity fs opts
PiperOrigin-RevId: 340275942
This commit is contained in:
parent
2eb3ee586b
commit
ed4f857343
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue