aboutsummaryrefslogtreecommitdiff
path: root/metadata/policy.go
diff options
context:
space:
mode:
authorebiggers <ebiggers@google.com>2020-01-22 18:28:23 -0800
committerGitHub <noreply@github.com>2020-01-22 18:28:23 -0800
commit059482129c5fdafebc582887a4ae4ef80988b708 (patch)
tree8ec373c41a677ff6949148b56f4aeaafe22791a6 /metadata/policy.go
parent80654f23ebfd552277ed217a2c5e1d0bb1374189 (diff)
parentfe2939cc7e50f4c6025253efdf7380c04fac9ae1 (diff)
Merge pull request #148 from ebiggers/fscrypt-key-mgmt-improvements
Filesystem keyring and v2 encryption policy support
Diffstat (limited to 'metadata/policy.go')
-rw-r--r--metadata/policy.go170
1 files changed, 125 insertions, 45 deletions
diff --git a/metadata/policy.go b/metadata/policy.go
index f9af44a..b95bf42 100644
--- a/metadata/policy.go
+++ b/metadata/policy.go
@@ -42,14 +42,14 @@ var (
ErrBadEncryptionOptions = util.SystemError("invalid encryption options provided")
)
-// policyIoctl is a wrapper for the ioctl syscall. It passes the correct
-// pointers and file descriptors to the IOCTL syscall. This function also takes
-// some of the unclear errors returned by the syscall and translates then into
-// more specific error strings.
-func policyIoctl(file *os.File, request uintptr, policy *unix.FscryptPolicyV1) error {
+// policyIoctl is a wrapper around the ioctls that get and set encryption
+// policies: FS_IOC_GET_ENCRYPTION_POLICY, FS_IOC_GET_ENCRYPTION_POLICY_EX, and
+// FS_IOC_SET_ENCRYPTION_POLICY. It translates the raw errno values into more
+// descriptive errors.
+func policyIoctl(file *os.File, request uintptr, arg unsafe.Pointer) error {
// The returned errno value can sometimes give strange errors, so we
// return encryption specific errors.
- _, _, errno := unix.Syscall(unix.SYS_IOCTL, file.Fd(), request, uintptr(unsafe.Pointer(policy)))
+ _, _, errno := unix.Syscall(unix.SYS_IOCTL, file.Fd(), request, uintptr(arg))
switch errno {
case 0:
return nil
@@ -61,7 +61,6 @@ func policyIoctl(file *os.File, request uintptr, policy *unix.FscryptPolicyV1) e
// ENOENT was returned instead of ENODATA on some filesystems before v4.11.
return ErrNotEncrypted
case unix.EEXIST:
- // EINVAL was returned instead of EEXIST on some filesystems before v4.11.
return ErrEncrypted
default:
return errno
@@ -75,6 +74,42 @@ var (
unix.FSCRYPT_POLICY_FLAGS_PAD_16, unix.FSCRYPT_POLICY_FLAGS_PAD_32}
)
+// flagsToPadding returns the amount of padding specified in the policy flags.
+func flagsToPadding(flags uint8) int64 {
+ paddingFlag := int64(flags & unix.FS_POLICY_FLAGS_PAD_MASK)
+
+ // This lookup should always succeed
+ padding, ok := util.Lookup(paddingFlag, flagsArray, paddingArray)
+ if !ok {
+ log.Panicf("padding flag of %x not found", paddingFlag)
+ }
+ return padding
+}
+
+func buildV1PolicyData(policy *unix.FscryptPolicyV1) *PolicyData {
+ return &PolicyData{
+ KeyDescriptor: hex.EncodeToString(policy.Master_key_descriptor[:]),
+ Options: &EncryptionOptions{
+ Padding: flagsToPadding(policy.Flags),
+ Contents: EncryptionOptions_Mode(policy.Contents_encryption_mode),
+ Filenames: EncryptionOptions_Mode(policy.Filenames_encryption_mode),
+ PolicyVersion: 1,
+ },
+ }
+}
+
+func buildV2PolicyData(policy *unix.FscryptPolicyV2) *PolicyData {
+ return &PolicyData{
+ KeyDescriptor: hex.EncodeToString(policy.Master_key_identifier[:]),
+ Options: &EncryptionOptions{
+ Padding: flagsToPadding(policy.Flags),
+ Contents: EncryptionOptions_Mode(policy.Contents_encryption_mode),
+ Filenames: EncryptionOptions_Mode(policy.Filenames_encryption_mode),
+ PolicyVersion: 2,
+ },
+ }
+}
+
// GetPolicy returns the Policy data for the given directory or file (includes
// the KeyDescriptor and the encryption options). Returns an error if the
// path is not encrypted or the policy couldn't be retrieved.
@@ -85,28 +120,36 @@ func GetPolicy(path string) (*PolicyData, error) {
}
defer file.Close()
- var policy unix.FscryptPolicyV1
- if err = policyIoctl(file, unix.FS_IOC_GET_ENCRYPTION_POLICY, &policy); err != nil {
+ // First try the new version of the ioctl. This works for both v1 and v2 policies.
+ var arg unix.FscryptGetPolicyExArg
+ arg.Size = uint64(unsafe.Sizeof(arg.Policy))
+ policyPtr := util.Ptr(arg.Policy[:])
+ err = policyIoctl(file, unix.FS_IOC_GET_ENCRYPTION_POLICY_EX, unsafe.Pointer(&arg))
+ if err == ErrEncryptionNotSupported {
+ // Fall back to the old version of the ioctl. This works for v1 policies only.
+ err = policyIoctl(file, unix.FS_IOC_GET_ENCRYPTION_POLICY, policyPtr)
+ arg.Size = uint64(unsafe.Sizeof(unix.FscryptPolicyV1{}))
+ }
+ if err != nil {
return nil, errors.Wrapf(err, "get encryption policy %s", path)
}
-
- // Convert the padding flag into an amount of padding
- paddingFlag := int64(policy.Flags & unix.FSCRYPT_POLICY_FLAGS_PAD_MASK)
-
- // This lookup should always succeed
- padding, ok := util.Lookup(paddingFlag, flagsArray, paddingArray)
- if !ok {
- log.Panicf("padding flag of %x not found", paddingFlag)
+ switch arg.Policy[0] { // arg.policy.version
+ case unix.FSCRYPT_POLICY_V1:
+ if arg.Size != uint64(unsafe.Sizeof(unix.FscryptPolicyV1{})) {
+ // should never happen
+ return nil, errors.New("unexpected size for v1 policy")
+ }
+ return buildV1PolicyData((*unix.FscryptPolicyV1)(policyPtr)), nil
+ case unix.FSCRYPT_POLICY_V2:
+ if arg.Size != uint64(unsafe.Sizeof(unix.FscryptPolicyV2{})) {
+ // should never happen
+ return nil, errors.New("unexpected size for v2 policy")
+ }
+ return buildV2PolicyData((*unix.FscryptPolicyV2)(policyPtr)), nil
+ default:
+ return nil, errors.Errorf("unsupported encryption policy version [%d]",
+ arg.Policy[0])
}
-
- return &PolicyData{
- KeyDescriptor: hex.EncodeToString(policy.Master_key_descriptor[:]),
- Options: &EncryptionOptions{
- Padding: padding,
- Contents: EncryptionOptions_Mode(policy.Contents_encryption_mode),
- Filenames: EncryptionOptions_Mode(policy.Filenames_encryption_mode),
- },
- }, nil
}
// For improved performance, use the DIRECT_KEY flag when using ciphers that
@@ -121,6 +164,52 @@ func shouldUseDirectKeyFlag(options *EncryptionOptions) bool {
return options.Contents == EncryptionOptions_Adiantum
}
+func buildPolicyFlags(options *EncryptionOptions) uint8 {
+ // This lookup should always succeed (as policy is valid)
+ flags, ok := util.Lookup(options.Padding, paddingArray, flagsArray)
+ if !ok {
+ log.Panicf("padding of %d was not found", options.Padding)
+ }
+ if shouldUseDirectKeyFlag(options) {
+ flags |= unix.FSCRYPT_POLICY_FLAG_DIRECT_KEY
+ }
+ return uint8(flags)
+}
+
+func setV1Policy(file *os.File, options *EncryptionOptions, descriptorBytes []byte) error {
+ policy := unix.FscryptPolicyV1{
+ Version: unix.FSCRYPT_POLICY_V1,
+ Contents_encryption_mode: uint8(options.Contents),
+ Filenames_encryption_mode: uint8(options.Filenames),
+ Flags: uint8(buildPolicyFlags(options)),
+ }
+
+ // The descriptor should always be the correct length (as policy is valid)
+ if len(descriptorBytes) != unix.FSCRYPT_KEY_DESCRIPTOR_SIZE {
+ log.Panic("wrong descriptor size for v1 policy")
+ }
+ copy(policy.Master_key_descriptor[:], descriptorBytes)
+
+ return policyIoctl(file, unix.FS_IOC_SET_ENCRYPTION_POLICY, unsafe.Pointer(&policy))
+}
+
+func setV2Policy(file *os.File, options *EncryptionOptions, descriptorBytes []byte) error {
+ policy := unix.FscryptPolicyV2{
+ Version: unix.FSCRYPT_POLICY_V2,
+ Contents_encryption_mode: uint8(options.Contents),
+ Filenames_encryption_mode: uint8(options.Filenames),
+ Flags: uint8(buildPolicyFlags(options)),
+ }
+
+ // The descriptor should always be the correct length (as policy is valid)
+ if len(descriptorBytes) != unix.FSCRYPT_KEY_IDENTIFIER_SIZE {
+ log.Panic("wrong descriptor size for v2 policy")
+ }
+ copy(policy.Master_key_identifier[:], descriptorBytes)
+
+ return policyIoctl(file, unix.FS_IOC_SET_ENCRYPTION_POLICY, unsafe.Pointer(&policy))
+}
+
// SetPolicy sets up the specified directory to be encrypted with the specified
// policy. Returns an error if we cannot set the policy for any reason (not a
// directory, invalid options or KeyDescriptor, etc).
@@ -135,30 +224,21 @@ func SetPolicy(path string, data *PolicyData) error {
return errors.Wrap(err, "invalid policy")
}
- // This lookup should always succeed (as policy is valid)
- flags, ok := util.Lookup(data.Options.Padding, paddingArray, flagsArray)
- if !ok {
- log.Panicf("padding of %d was not found", data.Options.Padding)
- }
-
descriptorBytes, err := hex.DecodeString(data.KeyDescriptor)
if err != nil {
- return errors.New("invalid descriptor: " + data.KeyDescriptor)
- }
-
- if shouldUseDirectKeyFlag(data.Options) {
- flags |= unix.FSCRYPT_POLICY_FLAG_DIRECT_KEY
+ return errors.New("invalid key descriptor: " + data.KeyDescriptor)
}
- policy := unix.FscryptPolicyV1{
- Version: unix.FSCRYPT_POLICY_V1,
- Contents_encryption_mode: uint8(data.Options.Contents),
- Filenames_encryption_mode: uint8(data.Options.Filenames),
- Flags: uint8(flags),
+ switch data.Options.PolicyVersion {
+ case 1:
+ err = setV1Policy(file, data.Options, descriptorBytes)
+ case 2:
+ err = setV2Policy(file, data.Options, descriptorBytes)
+ default:
+ err = errors.Errorf("policy version of %d is invalid", data.Options.PolicyVersion)
}
- copy(policy.Master_key_descriptor[:], descriptorBytes)
- if err = policyIoctl(file, unix.FS_IOC_SET_ENCRYPTION_POLICY, &policy); err == unix.EINVAL {
+ if err == unix.EINVAL {
// Before kernel v4.11, many different errors all caused unix.EINVAL to be returned.
// We try to disambiguate this error here. This disambiguation will not always give
// the correct error due to a potential race condition on path.
@@ -195,7 +275,7 @@ func CheckSupport(path string) error {
Flags: math.MaxUint8,
}
- err = policyIoctl(file, unix.FS_IOC_SET_ENCRYPTION_POLICY, &badPolicy)
+ err = policyIoctl(file, unix.FS_IOC_SET_ENCRYPTION_POLICY, unsafe.Pointer(&badPolicy))
switch err {
case nil:
log.Panicf(`FS_IOC_SET_ENCRYPTION_POLICY succeeded when it should have failed.