Skip to content

Commit cdec7dd

Browse files
committed
Allow for tcb migration of sealed files
Signed-off-by: njeans <nerlajeanlouis@gmail.com>
1 parent ff71d7a commit cdec7dd

31 files changed

Lines changed: 1243 additions & 8 deletions

Documentation/devel/encfiles.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,3 +542,14 @@ Additional details
542542
file data. Therefore, the usual NIST limits on the total number of
543543
invocations of the encryption operation with the same key would be reached
544544
much slower.
545+
546+
- TCB migration: Gramine supports migration between different CPU SVN versions
547+
by enabling it using the ``allow_tcb_migration`` mount parameter in the
548+
Gramine manifest. This feature allows encrypted files to be accessed even when
549+
the CPU SVN has changed after applying microcode updates. The current CPU SVN
550+
is stored in the mount directory in a file named ``gramine.tcb_info``. On
551+
startup if the CPU SVN has changed, Gramine will unseal the files using the
552+
old CPU SVN and reseal them with the current CPU SVN. This feature can negatively
553+
impact integrity of files, as it allows files from a platform with a lower
554+
security level to be used. It is recommended to use this feature only in controlled
555+
environments where the security implications are understood.

Documentation/devel/features.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,8 @@ grows with time, as Gramine adds functionality required by real-world workloads.
13141314
-`/dev/attestation/keys/<key_name>` <sup>[25](#attestation)</sup>
13151315
-`/dev/attestation/keys/_sgx_mrenclave` <sup>[25](#attestation)</sup>
13161316
-`/dev/attestation/keys/_sgx_mrsigner` <sup>[25](#attestation)</sup>
1317+
-`/dev/attestation/keys/svn/_sgx_mrenclave/<cpu_svn>` <sup>[25](#attestation)</sup>
1318+
-`/dev/attestation/cpu_svn` <sup>[25](#attestation)</sup>
13171319
-`/dev/null` <sup>[23](#misc)</sup>
13181320
-`/dev/zero` <sup>[23](#misc)</sup>
13191321
-`/dev/random` <sup>[21](#randomness)</sup>
@@ -3245,6 +3247,10 @@ Gramine <low-level-dev-attestation-interface>`.
32453247
-`/dev/attestation/keys/<key_name>`
32463248
-`/dev/attestation/keys/_sgx_mrenclave` (only for SGX)
32473249
-`/dev/attestation/keys/_sgx_mrsigner` (only for SGX)
3250+
-`/dev/attestation/keys/svn/_sgx_mrenclave/<cpu_svn>` (only for SGX)
3251+
3252+
-`/dev/attestation/cpu_svn` (only for SGX)
3253+
32483254

32493255
</details><br />
32503256

Documentation/manifest-syntax.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1088,7 +1088,7 @@ Encrypted files
10881088
::
10891089

10901090
fs.mounts = [
1091-
{ type = "encrypted", path = "[PATH]", uri = "[URI]", key_name = "[KEY_NAME]", enable_recovery = [true|false] },
1091+
{ type = "encrypted", path = "[PATH]", uri = "[URI]", key_name = "[KEY_NAME]", enable_recovery = [true|false], allow_tcb_migration = [true|false] },
10921092
]
10931093

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

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

11651173
Untrusted shared memory

Documentation/pal/host-abi.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,15 @@ random bits, to obtain an attestation report and quote, etc.
364364
.. doxygenfunction:: PalGetSpecialKey
365365
:project: pal
366366

367+
.. doxygenfunction:: PalGetSpecialKeyForSVN
368+
:project: pal
369+
370+
.. doxygenfunction:: PalGetCPUSVN
371+
:project: pal
372+
373+
.. doxygenfunction:: PalSetCPUSVN
374+
:project: pal
375+
367376
.. doxygenfunction:: PalDeviceMap
368377
:project: pal
369378

libos/include/libos_fs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ struct libos_mount_params {
6666
/* Whether to enable file recovery (used by `chroot_encrypted` filesystem), false if not
6767
* applicable */
6868
bool enable_recovery;
69+
70+
/* Whether to allow TCB migration (used by `chroot_encrypted` filesystem), false if not
71+
* applicable */
72+
bool allow_tcb_migration;
6973
};
7074

7175
struct libos_fs_ops {

libos/include/libos_fs_encrypted.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@
2424

2525
#define RECOVERY_FILE_URI_SUFFIX ".gramine.recovery"
2626

27+
#define TCB_INFO_PERM_RW PERM_rw_rw_r__
28+
29+
#define TCB_INFO_FILE_NAME "gramine.tcb_info"
30+
31+
#define CPU_SVN_SIZE 16
32+
33+
#define OLD_TCB_FILE_URI_SUFFIX ".old_tcb"
34+
35+
typedef uint8_t cpu_svn_t[CPU_SVN_SIZE];
36+
2737
/*
2838
* Represents a named key for opening files. The key might not be set yet: value of a key can be
2939
* specified in the manifest, or set using `update_encrypted_files_key`. Before the key is set,
@@ -68,6 +78,13 @@ struct libos_encrypted_file {
6878
*/
6979
int init_encrypted_files(void);
7080

81+
/*
82+
* \brief sets the default CPU SVN used for encrypted file keys.
83+
*
84+
* This is only used for testing.
85+
*/
86+
int set_cpu_svn(const cpu_svn_t* cpu_svn);
87+
7188
/*
7289
* \brief Retrieve a key.
7390
*
@@ -95,6 +112,15 @@ int list_encrypted_files_keys(int (*callback)(struct libos_encrypted_files_key*
95112
*/
96113
int get_or_create_encrypted_files_key(const char* name, struct libos_encrypted_files_key** out_key);
97114

115+
/*
116+
* \brief Retrieve a key with a CPU SVN.
117+
*
118+
* Sets `*out_key` to a key with given name and CPU SVN. Creates a new key.
119+
*
120+
*/
121+
int create_encrypted_files_key_for_svn(const char* name, cpu_svn_t* cpu_svn,
122+
struct libos_encrypted_files_key** out_key);
123+
98124
/*
99125
* \brief Read value of given key.
100126
*
@@ -183,3 +209,5 @@ int encrypted_file_get_size(struct libos_encrypted_file* enc, file_off_t* out_si
183209
int encrypted_file_set_size(struct libos_encrypted_file* enc, file_off_t size);
184210

185211
int parse_pf_key(const char* key_str, pf_key_t* pf_key);
212+
213+
int handle_tcb_migration(const char* uri, const char* key_name);

libos/src/fs/chroot/encrypted.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "libos_fs.h"
4141
#include "libos_fs_encrypted.h"
4242
#include "libos_vma.h"
43+
#include "pal.h"
4344
#include "perm.h"
4445
#include "stat.h"
4546
#include "toml_utils.h"
@@ -60,16 +61,33 @@ static int chroot_encrypted_mount(struct libos_mount_params* params, void** moun
6061
log_error("'%s' is invalid file URI", params->uri);
6162
return -EINVAL;
6263
}
63-
64+
int ret;
6465
const char* key_name = params->key_name ?: "default";
6566

67+
if (params->allow_tcb_migration) {
68+
if (!strcmp(key_name, PAL_KEY_NAME_SGX_MRENCLAVE)) {
69+
log_warning("TCB migration will be supported for %s", params->uri);
70+
ret = handle_tcb_migration(params->uri, key_name);
71+
if (ret < 0) {
72+
log_error("TCB migration failed for %s", params->uri);
73+
return ret;
74+
}
75+
76+
} else {
77+
log_warning(
78+
"TCB migration is only supported for keys named %s, ignoring "
79+
"allow_tcb_migration for %s",
80+
PAL_KEY_NAME_SGX_MRENCLAVE, params->uri);
81+
}
82+
}
6683
struct libos_encrypted_files_key* key;
67-
int ret = get_or_create_encrypted_files_key(key_name, &key);
84+
ret = get_or_create_encrypted_files_key(key_name, &key);
6885
if (ret < 0)
6986
return ret;
7087

7188
*mount_data = key;
72-
return 0;
89+
return ret;
90+
7391
}
7492

7593
static ssize_t chroot_encrypted_checkpoint(void** checkpoint, void* mount_data) {

libos/src/fs/dev/attestation.c

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
#include "api.h"
19+
#include "hex.h"
1920
#include "libos_fs_encrypted.h"
2021
#include "libos_fs_pseudo.h"
2122
#include "pal.h"
@@ -84,6 +85,21 @@ static int user_report_data_save(struct libos_dentry* dent, const char* data, si
8485
return 0;
8586
}
8687

88+
#ifdef DEBUG
89+
static int cpu_svn_save(struct libos_dentry* dent, const char* data, size_t size) {
90+
log_debug("cpu_svn_save: saving %zu bytes", size);
91+
__UNUSED(dent);
92+
cpu_svn_t cpu_svn;
93+
if (size != sizeof(cpu_svn)) {
94+
log_warning("CPU SVN must be exactly %zu bytes, got %zu", sizeof(cpu_svn), size);
95+
return -EINVAL;
96+
}
97+
memcpy(&cpu_svn, data, sizeof(cpu_svn));
98+
99+
return set_cpu_svn(&cpu_svn);
100+
}
101+
#endif /* DEBUG */
102+
87103
/*!
88104
* \brief Modify target info used in `report` pseudo-file.
89105
*
@@ -239,6 +255,32 @@ static int quote_load(struct libos_dentry* dent, char** out_data, size_t* out_si
239255
return 0;
240256
}
241257

258+
/*!
259+
* \brief Get CPU SVN of the platform.
260+
*/
261+
static int cpu_svn_load(struct libos_dentry* dent, char** out_data, size_t* out_size) {
262+
__UNUSED(dent);
263+
264+
cpu_svn_t cpu_svn;
265+
size_t cpu_svn_size = sizeof(cpu_svn);
266+
int ret = PalGetCPUSVN(&cpu_svn, &cpu_svn_size);
267+
if (ret < 0) {
268+
log_warning("PalGetCPUSVN failed: %s", pal_strerror(ret));
269+
return pal_to_unix_errno(ret);
270+
}
271+
272+
char* str = calloc(1, cpu_svn_size);
273+
274+
if (!str)
275+
return -ENOMEM;
276+
277+
memcpy(str, &cpu_svn, sizeof(cpu_svn));
278+
279+
*out_data = str;
280+
*out_size = cpu_svn_size;
281+
return 0;
282+
}
283+
242284
/*!
243285
* \brief Get remote attestation type used.
244286
*
@@ -264,6 +306,22 @@ static bool key_name_exists(struct libos_dentry* parent, const char* name) {
264306
return key != NULL;
265307
}
266308

309+
static bool key_name_exists_svn(struct libos_dentry* parent, const char* name) {
310+
__UNUSED(parent);
311+
if (strlen(name) != 2 * sizeof(cpu_svn_t)) {
312+
log_warning("key_name_exists_svn: invalid key name length %zu of %s, expected %zu",
313+
strlen(name), name, 2 * sizeof(cpu_svn_t));
314+
return false;
315+
}
316+
cpu_svn_t cpu_svn;
317+
if (!hex2bytes((char*)name, strlen(name), &cpu_svn, sizeof(cpu_svn_t))) {
318+
log_warning("key_name_exists_svn: invalid key name format");
319+
return false;
320+
}
321+
322+
return true;
323+
}
324+
267325
struct key_list_names_data {
268326
readdir_callback_t callback;
269327
void* arg;
@@ -284,6 +342,13 @@ static int key_list_names(struct libos_dentry* parent, readdir_callback_t callba
284342
return list_encrypted_files_keys(&key_list_names_callback, &data);
285343
}
286344

345+
static int key_list_names_svn(struct libos_dentry* parent, readdir_callback_t callback, void* arg) {
346+
__UNUSED(parent);
347+
__UNUSED(callback);
348+
__UNUSED(arg);
349+
return 0;
350+
}
351+
287352
static int key_load(struct libos_dentry* dent, char** out_data, size_t* out_size) {
288353
struct libos_encrypted_files_key* key = get_encrypted_files_key(dent->name);
289354
if (!key)
@@ -307,6 +372,58 @@ static int key_load(struct libos_dentry* dent, char** out_data, size_t* out_size
307372
return 0;
308373
}
309374

375+
static int key_load_svn(struct libos_dentry* dent, char** out_data, size_t* out_size) {
376+
if (strlen(dent->name) != 2 * sizeof(cpu_svn_t)) {
377+
log_warning("key_name_exists_svn: invalid key name length");
378+
return false;
379+
}
380+
cpu_svn_t cpu_svn;
381+
382+
if (!hex2bytes((char*)dent->name, strlen(dent->name), &cpu_svn, sizeof(cpu_svn_t))) {
383+
log_warning("key_name_exists_svn: invalid key name format");
384+
return false;
385+
}
386+
387+
int ret;
388+
389+
char * key_name = dent->parent->name;
390+
struct libos_encrypted_files_key* key = NULL;
391+
key = calloc(1, sizeof(*key));
392+
if (!key) {
393+
log_error("Cannot allocate memory for key");
394+
ret = -ENOMEM;
395+
goto out;
396+
}
397+
ret = create_encrypted_files_key_for_svn(key_name, &cpu_svn, &key);
398+
if (ret < 0) {
399+
log_error("Cannot create or get key for SVN");
400+
goto out;
401+
}
402+
pf_key_t pf_key;
403+
bool is_set = read_encrypted_files_key(key, &pf_key);
404+
405+
if (is_set) {
406+
char* buf = malloc(sizeof(pf_key));
407+
if (!buf)
408+
return -ENOMEM;
409+
memcpy(buf, &pf_key, sizeof(pf_key));
410+
411+
*out_data = buf;
412+
*out_size = sizeof(pf_key);
413+
} else {
414+
*out_data = NULL;
415+
*out_size = 0;
416+
}
417+
ret = 0;
418+
out:
419+
if (key) {
420+
if (key->name)
421+
free(key->name);
422+
free(key);
423+
}
424+
return ret;
425+
}
426+
310427
static int key_save(struct libos_dentry* dent, const char* data, size_t size) {
311428
struct libos_encrypted_files_key* key = get_encrypted_files_key(dent->name);
312429
if (!key)
@@ -338,6 +455,13 @@ static int init_sgx_attestation(struct pseudo_node* attestation, struct pseudo_n
338455
pseudo_add_str(attestation, "attestation_type", attestation_type_load);
339456
pseudo_add_str(attestation, "my_target_info", &my_target_info_load);
340457
pseudo_add_str(attestation, "report", &report_load);
458+
struct pseudo_node* cpu_svn = pseudo_add_str(attestation, "cpu_svn", &cpu_svn_load);
459+
#ifdef DEBUG
460+
cpu_svn->perm = PSEUDO_PERM_FILE_RW;
461+
cpu_svn->str.save = &cpu_svn_save;
462+
#else
463+
cpu_svn->perm = PSEUDO_PERM_FILE_R;
464+
#endif /* DEBUG */
341465

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

489+
struct pseudo_node* keys_svn = pseudo_add_dir(keys, "svn");
490+
struct pseudo_node* keys_svn_mrenclave_key =
491+
pseudo_add_dir(keys_svn, PAL_KEY_NAME_SGX_MRENCLAVE);
492+
struct pseudo_node* key_cpu_svn = pseudo_add_str(keys_svn_mrenclave_key, NULL, &key_load_svn);
493+
key_cpu_svn->name_exists = &key_name_exists_svn;
494+
key_cpu_svn->list_names = &key_list_names_svn;
495+
365496
if (!strcmp(g_pal_public_state->attestation_type, "none")) {
366497
log_debug("host is Linux-SGX and remote attestation type is 'none', skipping "
367498
"/dev/attestation/quote file");

0 commit comments

Comments
 (0)