mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-12-29 08:20:20 +00:00
176 lines
6.0 KiB
Go
176 lines
6.0 KiB
Go
|
/*
|
||
|
* keyring.go - Add/remove encryption policy keys to/from kernel
|
||
|
*
|
||
|
* Copyright 2019 Google LLC
|
||
|
* Author: Eric Biggers (ebiggers@google.com)
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||
|
* the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||
|
* License for the specific language governing permissions and limitations under
|
||
|
* the License.
|
||
|
*/
|
||
|
|
||
|
// Package keyring manages adding, removing, and getting the status of
|
||
|
// encryption policy keys to/from the kernel. Most public functions are in
|
||
|
// keyring.go, and they delegate to either user_keyring.go or fs_keyring.go,
|
||
|
// depending on whether a user keyring or a filesystem keyring is being used.
|
||
|
//
|
||
|
// v2 encryption policies always use the filesystem keyring.
|
||
|
// v1 policies use the user keyring by default, but can be configured to use the
|
||
|
// filesystem keyring instead (requires root and kernel v5.4+).
|
||
|
package keyring
|
||
|
|
||
|
import (
|
||
|
"encoding/hex"
|
||
|
"os/user"
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
"golang.org/x/sys/unix"
|
||
|
|
||
|
"github.com/google/fscrypt/crypto"
|
||
|
"github.com/google/fscrypt/filesystem"
|
||
|
"github.com/google/fscrypt/metadata"
|
||
|
"github.com/google/fscrypt/util"
|
||
|
)
|
||
|
|
||
|
// Keyring error values
|
||
|
var (
|
||
|
ErrKeyAddedByOtherUsers = errors.New("other users have added the key too")
|
||
|
ErrKeyFilesOpen = errors.New("some files using the key are still open")
|
||
|
ErrKeyNotPresent = errors.New("key not present or already removed")
|
||
|
ErrV2PoliciesUnsupported = errors.New("kernel is too old to support v2 encryption policies")
|
||
|
)
|
||
|
|
||
|
// Options are the options which specify *which* keyring the key should be
|
||
|
// added/removed/gotten to, and how.
|
||
|
type Options struct {
|
||
|
// Mount is the filesystem to which the key should be
|
||
|
// added/removed/gotten.
|
||
|
Mount *filesystem.Mount
|
||
|
// User is the user for whom the key should be added/removed/gotten.
|
||
|
User *user.User
|
||
|
// UseFsKeyringForV1Policies is true if keys for v1 encryption policies
|
||
|
// should be put in the filesystem's keyring (if supported) rather than
|
||
|
// in the user's keyring. Note that this makes AddEncryptionKey and
|
||
|
// RemoveEncryptionKey require root privileges.
|
||
|
UseFsKeyringForV1Policies bool
|
||
|
}
|
||
|
|
||
|
func shouldUseFsKeyring(descriptor string, options *Options) (bool, error) {
|
||
|
// For v1 encryption policy keys, use the filesystem keyring if
|
||
|
// use_fs_keyring_for_v1_policies is set in /etc/fscrypt.conf and the
|
||
|
// kernel supports it.
|
||
|
if len(descriptor) == hex.EncodedLen(unix.FSCRYPT_KEY_DESCRIPTOR_SIZE) {
|
||
|
return options.UseFsKeyringForV1Policies && IsFsKeyringSupported(options.Mount), nil
|
||
|
}
|
||
|
// For v2 encryption policy keys, always use the filesystem keyring; the
|
||
|
// kernel doesn't support any other way.
|
||
|
if !IsFsKeyringSupported(options.Mount) {
|
||
|
return true, ErrV2PoliciesUnsupported
|
||
|
}
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
// buildKeyDescription builds the description for an fscrypt key of type
|
||
|
// "logon". For ext4 and f2fs, it uses the legacy filesystem-specific prefixes
|
||
|
// for compatibility with kernels before v4.8 and v4.6 respectively. For other
|
||
|
// filesystems it uses the generic prefix "fscrypt".
|
||
|
func buildKeyDescription(options *Options, descriptor string) string {
|
||
|
switch options.Mount.FilesystemType {
|
||
|
case "ext4", "f2fs":
|
||
|
return options.Mount.FilesystemType + ":" + descriptor
|
||
|
default:
|
||
|
return unix.FSCRYPT_KEY_DESC_PREFIX + descriptor
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// AddEncryptionKey adds an encryption policy key to a kernel keyring. It uses
|
||
|
// either the filesystem keyring for the target Mount or the user keyring for
|
||
|
// the target User.
|
||
|
func AddEncryptionKey(key *crypto.Key, descriptor string, options *Options) error {
|
||
|
if err := util.CheckValidLength(metadata.PolicyKeyLen, key.Len()); err != nil {
|
||
|
return errors.Wrap(err, "policy key")
|
||
|
}
|
||
|
useFsKeyring, err := shouldUseFsKeyring(descriptor, options)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if useFsKeyring {
|
||
|
return fsAddEncryptionKey(key, descriptor, options.Mount, options.User)
|
||
|
}
|
||
|
return userAddKey(key, buildKeyDescription(options, descriptor), options.User)
|
||
|
}
|
||
|
|
||
|
// RemoveEncryptionKey removes an encryption policy key from a kernel keyring.
|
||
|
// It uses either the filesystem keyring for the target Mount or the user
|
||
|
// keyring for the target User.
|
||
|
func RemoveEncryptionKey(descriptor string, options *Options, allUsers bool) error {
|
||
|
useFsKeyring, err := shouldUseFsKeyring(descriptor, options)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if useFsKeyring {
|
||
|
user := options.User
|
||
|
if allUsers {
|
||
|
user = nil
|
||
|
}
|
||
|
return fsRemoveEncryptionKey(descriptor, options.Mount, user)
|
||
|
}
|
||
|
return userRemoveKey(buildKeyDescription(options, descriptor), options.User)
|
||
|
}
|
||
|
|
||
|
// KeyStatus is an enum that represents the status of a key in a kernel keyring.
|
||
|
type KeyStatus int
|
||
|
|
||
|
// The possible values of KeyStatus.
|
||
|
const (
|
||
|
KeyStatusUnknown = 0 + iota
|
||
|
KeyAbsent
|
||
|
KeyAbsentButFilesBusy
|
||
|
KeyPresent
|
||
|
KeyPresentButOnlyOtherUsers
|
||
|
)
|
||
|
|
||
|
func (status KeyStatus) String() string {
|
||
|
switch status {
|
||
|
case KeyStatusUnknown:
|
||
|
return "Unknown"
|
||
|
case KeyAbsent:
|
||
|
return "Absent"
|
||
|
case KeyAbsentButFilesBusy:
|
||
|
return "AbsentButFilesBusy"
|
||
|
case KeyPresent:
|
||
|
return "Present"
|
||
|
case KeyPresentButOnlyOtherUsers:
|
||
|
return "PresentButOnlyOtherUsers"
|
||
|
default:
|
||
|
return strconv.Itoa(int(status))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GetEncryptionKeyStatus gets the status of an encryption policy key in a
|
||
|
// kernel keyring. It uses either the filesystem keyring for the target Mount
|
||
|
// or the user keyring for the target User.
|
||
|
func GetEncryptionKeyStatus(descriptor string, options *Options) (KeyStatus, error) {
|
||
|
useFsKeyring, err := shouldUseFsKeyring(descriptor, options)
|
||
|
if err != nil {
|
||
|
return KeyStatusUnknown, err
|
||
|
}
|
||
|
if useFsKeyring {
|
||
|
return fsGetEncryptionKeyStatus(descriptor, options.Mount, options.User)
|
||
|
}
|
||
|
_, _, err = userFindKey(buildKeyDescription(options, descriptor), options.User)
|
||
|
if err != nil {
|
||
|
return KeyAbsent, nil
|
||
|
}
|
||
|
return KeyPresent, nil
|
||
|
}
|