Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Documentation/devel/encfiles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -542,3 +542,14 @@ Additional details
file data. Therefore, the usual NIST limits on the total number of
invocations of the encryption operation with the same key would be reached
much slower.

- TCB migration: Gramine supports migration between different CPU SVN versions
by enabling it using the ``allow_tcb_migration`` mount parameter in the
Gramine manifest. This feature allows encrypted files to be accessed even when
the CPU SVN has changed after applying microcode updates. The current CPU SVN
is stored in the mount directory in a file named ``gramine.tcb_info``. On
startup if the CPU SVN has changed, Gramine will unseal the files using the
old CPU SVN and reseal them with the current CPU SVN. This feature can negatively
impact integrity of files, as it allows files from a platform with a lower
security level to be used. It is recommended to use this feature only in controlled
environments where the security implications are understood.
6 changes: 6 additions & 0 deletions Documentation/devel/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,8 @@ grows with time, as Gramine adds functionality required by real-world workloads.
- ☑ `/dev/attestation/keys/<key_name>` <sup>[25](#attestation)</sup>
- ☑ `/dev/attestation/keys/_sgx_mrenclave` <sup>[25](#attestation)</sup>
- ☑ `/dev/attestation/keys/_sgx_mrsigner` <sup>[25](#attestation)</sup>
- ☑ `/dev/attestation/keys/svn/_sgx_mrenclave/<cpu_svn>` <sup>[25](#attestation)</sup>
- ☑ `/dev/attestation/cpu_svn` <sup>[25](#attestation)</sup>
- ☑ `/dev/null` <sup>[23](#misc)</sup>
- ☑ `/dev/zero` <sup>[23](#misc)</sup>
- ☑ `/dev/random` <sup>[21](#randomness)</sup>
Expand Down Expand Up @@ -3245,6 +3247,10 @@ Gramine <low-level-dev-attestation-interface>`.
- ☑ `/dev/attestation/keys/<key_name>`
- ☑ `/dev/attestation/keys/_sgx_mrenclave` (only for SGX)
- ☑ `/dev/attestation/keys/_sgx_mrsigner` (only for SGX)
- ☑ `/dev/attestation/keys/svn/_sgx_mrenclave/<cpu_svn>` (only for SGX)

- ☑ `/dev/attestation/cpu_svn` (only for SGX)


</details><br />

Expand Down
10 changes: 9 additions & 1 deletion Documentation/manifest-syntax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1088,7 +1088,7 @@ Encrypted files
::

fs.mounts = [
{ type = "encrypted", path = "[PATH]", uri = "[URI]", key_name = "[KEY_NAME]", enable_recovery = [true|false] },
{ type = "encrypted", path = "[PATH]", uri = "[URI]", key_name = "[KEY_NAME]", enable_recovery = [true|false], allow_tcb_migration = [true|false] },
]

fs.insecure__keys.[KEY_NAME] = "[32-character hex value]"
Expand Down Expand Up @@ -1160,6 +1160,14 @@ or disabling of recovery for different mounted files or directories. Note that
enabling this feature can negatively impact performance, as it writes to a
second shadow file for later recovery purposes on each flush.

The ``allow_tcb_migration`` mount parameter (default: ``false``) determines whether
the TCB migration feature is enabled for the mount. This feature allows sealed files to
be migrated to latest CPU SVN version after applying microcode updates. This feature
is only valid for ``key_name`` of ``"_sgx_mrenclave"``. Enabling this feature can
negatively impact security, as it allow the enclave to unseal files that were created
with an old potentially vulnerable CPU SVN version. It is the responsibility of the
app developer to verify the integrity of the sealed files if this feature is enabled.

.. _untrusted-shared-memory:

Untrusted shared memory
Expand Down
9 changes: 9 additions & 0 deletions Documentation/pal/host-abi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,15 @@ random bits, to obtain an attestation report and quote, etc.
.. doxygenfunction:: PalGetSpecialKey
:project: pal

.. doxygenfunction:: PalGetSpecialKeyForSVN
:project: pal

.. doxygenfunction:: PalGetCPUSVN
:project: pal

.. doxygenfunction:: PalSetCPUSVN
:project: pal

.. doxygenfunction:: PalDeviceMap
:project: pal

Expand Down
4 changes: 4 additions & 0 deletions libos/include/libos_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ struct libos_mount_params {
/* Whether to enable file recovery (used by `chroot_encrypted` filesystem), false if not
* applicable */
bool enable_recovery;

/* Whether to allow TCB migration (used by `chroot_encrypted` filesystem), false if not
* applicable */
bool allow_tcb_migration;
};

struct libos_fs_ops {
Expand Down
28 changes: 28 additions & 0 deletions libos/include/libos_fs_encrypted.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@

#define RECOVERY_FILE_URI_SUFFIX ".gramine.recovery"

#define TCB_INFO_PERM_RW PERM_rw_rw_r__

#define TCB_INFO_FILE_NAME "gramine.tcb_info"

#define CPU_SVN_SIZE 16

#define OLD_TCB_FILE_URI_SUFFIX ".old_tcb"

typedef uint8_t cpu_svn_t[CPU_SVN_SIZE];

/*
* Represents a named key for opening files. The key might not be set yet: value of a key can be
* specified in the manifest, or set using `update_encrypted_files_key`. Before the key is set,
Expand Down Expand Up @@ -68,6 +78,13 @@ struct libos_encrypted_file {
*/
int init_encrypted_files(void);

/*
* \brief sets the default CPU SVN used for encrypted file keys.
*
* This is only used for testing.
*/
int set_cpu_svn(const cpu_svn_t* cpu_svn);

/*
* \brief Retrieve a key.
*
Expand Down Expand Up @@ -95,6 +112,15 @@ int list_encrypted_files_keys(int (*callback)(struct libos_encrypted_files_key*
*/
int get_or_create_encrypted_files_key(const char* name, struct libos_encrypted_files_key** out_key);

/*
* \brief Retrieve a key with a CPU SVN.
*
* Sets `*out_key` to a key with given name and CPU SVN. Creates a new key.
*
*/
int create_encrypted_files_key_for_svn(const char* name, cpu_svn_t* cpu_svn,
struct libos_encrypted_files_key** out_key);

/*
* \brief Read value of given key.
*
Expand Down Expand Up @@ -183,3 +209,5 @@ int encrypted_file_get_size(struct libos_encrypted_file* enc, file_off_t* out_si
int encrypted_file_set_size(struct libos_encrypted_file* enc, file_off_t size);

int parse_pf_key(const char* key_str, pf_key_t* pf_key);

int handle_tcb_migration(const char* uri, const char* key_name);
24 changes: 21 additions & 3 deletions libos/src/fs/chroot/encrypted.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "libos_fs.h"
#include "libos_fs_encrypted.h"
#include "libos_vma.h"
#include "pal.h"
#include "perm.h"
#include "stat.h"
#include "toml_utils.h"
Expand All @@ -60,16 +61,33 @@ static int chroot_encrypted_mount(struct libos_mount_params* params, void** moun
log_error("'%s' is invalid file URI", params->uri);
return -EINVAL;
}

int ret;
const char* key_name = params->key_name ?: "default";

if (params->allow_tcb_migration) {
if (!strcmp(key_name, PAL_KEY_NAME_SGX_MRENCLAVE)) {
log_warning("TCB migration will be supported for %s", params->uri);
ret = handle_tcb_migration(params->uri, key_name);
if (ret < 0) {
log_error("TCB migration failed for %s", params->uri);
return ret;
}

} else {
log_warning(
"TCB migration is only supported for keys named %s, ignoring "
"allow_tcb_migration for %s",
PAL_KEY_NAME_SGX_MRENCLAVE, params->uri);
}
}
struct libos_encrypted_files_key* key;
int ret = get_or_create_encrypted_files_key(key_name, &key);
ret = get_or_create_encrypted_files_key(key_name, &key);
if (ret < 0)
return ret;

*mount_data = key;
return 0;
return ret;

}

static ssize_t chroot_encrypted_checkpoint(void** checkpoint, void* mount_data) {
Expand Down
131 changes: 131 additions & 0 deletions libos/src/fs/dev/attestation.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

#include "api.h"
#include "hex.h"
#include "libos_fs_encrypted.h"
#include "libos_fs_pseudo.h"
#include "pal.h"
Expand Down Expand Up @@ -84,6 +85,21 @@ static int user_report_data_save(struct libos_dentry* dent, const char* data, si
return 0;
}

#ifdef DEBUG
static int cpu_svn_save(struct libos_dentry* dent, const char* data, size_t size) {
log_debug("cpu_svn_save: saving %zu bytes", size);
__UNUSED(dent);
cpu_svn_t cpu_svn;
if (size != sizeof(cpu_svn)) {
log_warning("CPU SVN must be exactly %zu bytes, got %zu", sizeof(cpu_svn), size);
return -EINVAL;
}
memcpy(&cpu_svn, data, sizeof(cpu_svn));

return set_cpu_svn(&cpu_svn);
}
#endif /* DEBUG */

/*!
* \brief Modify target info used in `report` pseudo-file.
*
Expand Down Expand Up @@ -239,6 +255,32 @@ static int quote_load(struct libos_dentry* dent, char** out_data, size_t* out_si
return 0;
}

/*!
* \brief Get CPU SVN of the platform.
*/
static int cpu_svn_load(struct libos_dentry* dent, char** out_data, size_t* out_size) {
__UNUSED(dent);

cpu_svn_t cpu_svn;
size_t cpu_svn_size = sizeof(cpu_svn);
int ret = PalGetCPUSVN(&cpu_svn, &cpu_svn_size);
if (ret < 0) {
log_warning("PalGetCPUSVN failed: %s", pal_strerror(ret));
return pal_to_unix_errno(ret);
}

char* str = calloc(1, cpu_svn_size);

if (!str)
return -ENOMEM;

memcpy(str, &cpu_svn, sizeof(cpu_svn));

*out_data = str;
*out_size = cpu_svn_size;
return 0;
}

/*!
* \brief Get remote attestation type used.
*
Expand All @@ -264,6 +306,22 @@ static bool key_name_exists(struct libos_dentry* parent, const char* name) {
return key != NULL;
}

static bool key_name_exists_svn(struct libos_dentry* parent, const char* name) {
__UNUSED(parent);
if (strlen(name) != 2 * sizeof(cpu_svn_t)) {
log_warning("key_name_exists_svn: invalid key name length %zu of %s, expected %zu",
strlen(name), name, 2 * sizeof(cpu_svn_t));
return false;
}
cpu_svn_t cpu_svn;
if (!hex2bytes((char*)name, strlen(name), &cpu_svn, sizeof(cpu_svn_t))) {
log_warning("key_name_exists_svn: invalid key name format");
return false;
}

return true;
}

struct key_list_names_data {
readdir_callback_t callback;
void* arg;
Expand All @@ -284,6 +342,13 @@ static int key_list_names(struct libos_dentry* parent, readdir_callback_t callba
return list_encrypted_files_keys(&key_list_names_callback, &data);
}

static int key_list_names_svn(struct libos_dentry* parent, readdir_callback_t callback, void* arg) {
__UNUSED(parent);
__UNUSED(callback);
__UNUSED(arg);
return 0;
}

static int key_load(struct libos_dentry* dent, char** out_data, size_t* out_size) {
struct libos_encrypted_files_key* key = get_encrypted_files_key(dent->name);
if (!key)
Expand All @@ -307,6 +372,58 @@ static int key_load(struct libos_dentry* dent, char** out_data, size_t* out_size
return 0;
}

static int key_load_svn(struct libos_dentry* dent, char** out_data, size_t* out_size) {
if (strlen(dent->name) != 2 * sizeof(cpu_svn_t)) {
log_warning("key_name_exists_svn: invalid key name length");
return false;
}
cpu_svn_t cpu_svn;

if (!hex2bytes((char*)dent->name, strlen(dent->name), &cpu_svn, sizeof(cpu_svn_t))) {
log_warning("key_name_exists_svn: invalid key name format");
return false;
}

int ret;

char * key_name = dent->parent->name;
struct libos_encrypted_files_key* key = NULL;
key = calloc(1, sizeof(*key));
if (!key) {
log_error("Cannot allocate memory for key");
ret = -ENOMEM;
goto out;
}
ret = create_encrypted_files_key_for_svn(key_name, &cpu_svn, &key);
if (ret < 0) {
log_error("Cannot create or get key for SVN");
goto out;
}
pf_key_t pf_key;
bool is_set = read_encrypted_files_key(key, &pf_key);

if (is_set) {
char* buf = malloc(sizeof(pf_key));
if (!buf)
return -ENOMEM;
memcpy(buf, &pf_key, sizeof(pf_key));

*out_data = buf;
*out_size = sizeof(pf_key);
} else {
*out_data = NULL;
*out_size = 0;
}
ret = 0;
out:
if (key) {
if (key->name)
free(key->name);
free(key);
}
return ret;
}

static int key_save(struct libos_dentry* dent, const char* data, size_t size) {
struct libos_encrypted_files_key* key = get_encrypted_files_key(dent->name);
if (!key)
Expand Down Expand Up @@ -338,6 +455,13 @@ static int init_sgx_attestation(struct pseudo_node* attestation, struct pseudo_n
pseudo_add_str(attestation, "attestation_type", attestation_type_load);
pseudo_add_str(attestation, "my_target_info", &my_target_info_load);
pseudo_add_str(attestation, "report", &report_load);
struct pseudo_node* cpu_svn = pseudo_add_str(attestation, "cpu_svn", &cpu_svn_load);
#ifdef DEBUG
cpu_svn->perm = PSEUDO_PERM_FILE_RW;
cpu_svn->str.save = &cpu_svn_save;
#else
cpu_svn->perm = PSEUDO_PERM_FILE_R;
#endif /* DEBUG */

struct pseudo_node* user_report_data = pseudo_add_str(attestation, "user_report_data", NULL);
user_report_data->perm = PSEUDO_PERM_FILE_RW;
Expand All @@ -362,6 +486,13 @@ static int init_sgx_attestation(struct pseudo_node* attestation, struct pseudo_n
pseudo_add_str(keys, PAL_KEY_NAME_SGX_MRENCLAVE, &key_load);
pseudo_add_str(keys, PAL_KEY_NAME_SGX_MRSIGNER, &key_load);

struct pseudo_node* keys_svn = pseudo_add_dir(keys, "svn");
struct pseudo_node* keys_svn_mrenclave_key =
pseudo_add_dir(keys_svn, PAL_KEY_NAME_SGX_MRENCLAVE);
struct pseudo_node* key_cpu_svn = pseudo_add_str(keys_svn_mrenclave_key, NULL, &key_load_svn);
key_cpu_svn->name_exists = &key_name_exists_svn;
key_cpu_svn->list_names = &key_list_names_svn;

if (!strcmp(g_pal_public_state->attestation_type, "none")) {
log_debug("host is Linux-SGX and remote attestation type is 'none', skipping "
"/dev/attestation/quote file");
Expand Down
Loading