<commit_before>/*
Copyright 2022 The Ceph-CSI Authors.
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 fscrypt

/*
#include <linux/fs.h>
*/
import "C"

import (
	"context"
	"errors"
	"fmt"
	"os"
	"os/user"
	"path"
	"time"
	"unsafe"

	fscryptactions "github.com/google/fscrypt/actions"
	fscryptcrypto "github.com/google/fscrypt/crypto"
	fscryptfilesystem "github.com/google/fscrypt/filesystem"
	fscryptmetadata "github.com/google/fscrypt/metadata"
	"github.com/pkg/xattr"
	"golang.org/x/sys/unix"

	"github.com/ceph/ceph-csi/internal/kms"
	"github.com/ceph/ceph-csi/internal/util"
	"github.com/ceph/ceph-csi/internal/util/log"
)

const (
	FscryptHashingTimeTarget = 1 * time.Second
	FscryptProtectorPrefix   = "ceph-csi"
	FscryptSubdir            = "ceph-csi-encrypted"
	encryptionPassphraseSize = 64
)

func AppendEncyptedSubdirectory(dir string) string {
	return path.Join(dir, FscryptSubdir)
}

// getPassphrase returns the passphrase from the configured Ceph CSI KMS to be used as a protector key in fscrypt.
func getPassphrase(ctx context.Context, encryption util.VolumeEncryption, volID string) (string, error) {
	var (
		passphrase string
		err        error
	)

	switch encryption.KMS.RequiresDEKStore() {
	case kms.DEKStoreIntegrated:
		passphrase, err = encryption.GetCryptoPassphrase(volID)
		if err != nil {
			log.ErrorLog(ctx, "fscrypt: failed to get passphrase from KMS: %v", err)

			return "", err
		}
	case kms.DEKStoreMetadata:
		passphrase, err = encryption.KMS.GetSecret(volID)
		if err != nil {
			log.ErrorLog(ctx, "fscrypt: failed to GetSecret: %v", err)

			return "", err
		}
	}

	return passphrase, nil
}

// createKeyFuncFromVolumeEncryption returns an fscrypt key function returning
// encryption keys form a VolumeEncryption struct.
func createKeyFuncFromVolumeEncryption(
	ctx context.Context,
	encryption util.VolumeEncryption,
	volID string,
) (func(fscryptactions.ProtectorInfo, bool) (*fscryptcrypto.Key, error), error) {
	passphrase, err := getPassphrase(ctx, encryption, volID)
	if err != nil {
		return nil, err
	}

	keyFunc := func(info fscryptactions.ProtectorInfo, retry bool) (*fscryptcrypto.Key, error) {
		key, err := fscryptcrypto.NewBlankKey(32)
		copy(key.Data(), passphrase)

		return key, err
	}

	return keyFunc, nil
}

// unlockExisting tries to unlock an already set up fscrypt directory using keys from Ceph CSI.
func unlockExisting(
	ctx context.Context,
	fscryptContext *fscryptactions.Context,
	encryptedPath string, protectorName string,
	keyFn func(fscryptactions.ProtectorInfo, bool) (*fscryptcrypto.Key, error),
) error {
	var err error

	policy, err := fscryptactions.GetPolicyFromPath(fscryptContext, encryptedPath)
	if err != nil {<commit_msg>fscrypt: Fetch passphrase when keyFn is invoked not created<commit_after>/*
Copyright 2022 The Ceph-CSI Authors.
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 fscrypt

/*
#include <linux/fs.h>
*/
import "C"

import (
	"context"
	"errors"
	"fmt"
	"os"
	"os/user"
	"path"
	"time"
	"unsafe"

	fscryptactions "github.com/google/fscrypt/actions"
	fscryptcrypto "github.com/google/fscrypt/crypto"
	fscryptfilesystem "github.com/google/fscrypt/filesystem"
	fscryptmetadata "github.com/google/fscrypt/metadata"
	"github.com/pkg/xattr"
	"golang.org/x/sys/unix"

	"github.com/ceph/ceph-csi/internal/kms"
	"github.com/ceph/ceph-csi/internal/util"
	"github.com/ceph/ceph-csi/internal/util/log"
)

const (
	FscryptHashingTimeTarget = 1 * time.Second
	FscryptProtectorPrefix   = "ceph-csi"
	FscryptSubdir            = "ceph-csi-encrypted"
	encryptionPassphraseSize = 64
)

func AppendEncyptedSubdirectory(dir string) string {
	return path.Join(dir, FscryptSubdir)
}

// getPassphrase returns the passphrase from the configured Ceph CSI KMS to be used as a protector key in fscrypt.
func getPassphrase(ctx context.Context, encryption util.VolumeEncryption, volID string) (string, error) {
	var (
		passphrase string
		err        error
	)

	switch encryption.KMS.RequiresDEKStore() {
	case kms.DEKStoreIntegrated:
		passphrase, err = encryption.GetCryptoPassphrase(volID)
		if err != nil {
			log.ErrorLog(ctx, "fscrypt: failed to get passphrase from KMS: %v", err)

			return "", err
		}
	case kms.DEKStoreMetadata:
		passphrase, err = encryption.KMS.GetSecret(volID)
		if err != nil {
			log.ErrorLog(ctx, "fscrypt: failed to GetSecret: %v", err)

			return "", err
		}
	}

	return passphrase, nil
}

// createKeyFuncFromVolumeEncryption returns an fscrypt key function returning
// encryption keys form a VolumeEncryption struct.
func createKeyFuncFromVolumeEncryption(
	ctx context.Context,
	encryption util.VolumeEncryption,
	volID string,
) (func(fscryptactions.ProtectorInfo, bool) (*fscryptcrypto.Key, error), error) {
	keyFunc := func(info fscryptactions.ProtectorInfo, retry bool) (*fscryptcrypto.Key, error) {
		passphrase, err := getPassphrase(ctx, encryption, volID)
		if err != nil {
			return nil, err
		}

		key, err := fscryptcrypto.NewBlankKey(encryptionPassphraseSize / 2)
		copy(key.Data(), passphrase)

		return key, err
	}

	return keyFunc, nil
}

// unlockExisting tries to unlock an already set up fscrypt directory using keys from Ceph CSI.
func unlockExisting(
	ctx context.Context,
	fscryptContext *fscryptactions.Context,
	encryptedPath string, protectorName string,
	keyFn func(fscryptactions.ProtectorInfo, bool) (*fscryptcrypto.Key, error),
) error {
	var err error

	policy, err := fscryptactions.GetPolicyFromPath(fscryptContext, encryptedPath)
	if err != nil {
{'fraction_non_alphanumeric': 0.09820322180916977, 'fraction_numerical': 0.003872366790582404, 'mean_word_length': 4.537735849056604, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3723689', 'n_tokens_mistral': 2239, 'n_tokens_neox': 2138, 'n_words': 609}