Skip to content

Commit

Permalink
Single-process-lifetime rollback protection for protected files (WIP)
Browse files Browse the repository at this point in the history
* adds libos_encrypted_volume as mount-data for protected fileystem which
  includes map <name, <last-root-hash, ...>> to keep track of root hashes
  across open/close cycles of a particular value, ensuring consistency across
  the whole enclave life-time

Signed-off-by: g2flyer <michael.steiner@intel.com>
  • Loading branch information
g2flyer committed Apr 23, 2024
1 parent e6abd2b commit 2233cf6
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 66 deletions.
17 changes: 14 additions & 3 deletions common/src/protected_files/protected_files.c
Original file line number Diff line number Diff line change
Expand Up @@ -1184,23 +1184,31 @@ void pf_set_callbacks(pf_read_f read_f, pf_write_f write_f, pf_fsync_f fsync_f,
}

pf_status_t pf_open(pf_handle_t handle, const char* path, uint64_t underlying_size,
pf_file_mode_t mode, bool create, const pf_key_t* key, pf_context_t** context) {
pf_file_mode_t mode, bool create, const pf_key_t* key,
pf_mac_t* opening_root_gmac, pf_context_t** context) {
if (!g_initialized)
return PF_STATUS_UNINITIALIZED;

pf_status_t status;
*context = ipf_open(path, mode, create, handle, underlying_size, key, &status);
if (opening_root_gmac != NULL) {
memcpy(opening_root_gmac, (*context)->file_metadata.plain_part.metadata_gmac,
sizeof(pf_mac_t));
}
return status;
}

pf_status_t pf_close(pf_context_t* pf) {
pf_status_t pf_close(pf_context_t* pf, pf_mac_t* closing_root_gmac) {
if (!g_initialized)
return PF_STATUS_UNINITIALIZED;

if (ipf_close(pf)) {
free(pf);
return PF_STATUS_SUCCESS;
}
if (closing_root_gmac != NULL) {
memcpy(closing_root_gmac, pf->file_metadata.plain_part.metadata_gmac, sizeof(pf_mac_t));
}

pf_status_t ret = pf->last_error;
free(pf);
Expand Down Expand Up @@ -1267,7 +1275,7 @@ pf_status_t pf_set_size(pf_context_t* pf, uint64_t size) {
return PF_STATUS_SUCCESS;
}

pf_status_t pf_rename(pf_context_t* pf, const char* new_path) {
pf_status_t pf_rename(pf_context_t* pf, const char* new_path, pf_mac_t* new_root_gmac) {
if (!g_initialized)
return PF_STATUS_UNINITIALIZED;

Expand All @@ -1283,6 +1291,9 @@ pf_status_t pf_rename(pf_context_t* pf, const char* new_path) {
pf->need_writing = true;
if (!ipf_internal_flush(pf))
return pf->last_error;
if (new_root_gmac != NULL) {
memcpy(new_root_gmac, pf->file_metadata.plain_part.metadata_gmac, sizeof(pf_mac_t));
}

return PF_STATUS_SUCCESS;
}
Expand Down
32 changes: 18 additions & 14 deletions common/src/protected_files/protected_files.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,28 +214,31 @@ const char* pf_strerror(int err);
/*!
* \brief Open a protected file.
*
* \param handle Open underlying file handle.
* \param path Path to the file. If NULL and \p create is false, don't check path
* for validity.
* \param underlying_size Underlying file size.
* \param mode Access mode.
* \param create Overwrite file contents if true.
* \param key Wrap key.
* \param[out] context PF context for later calls.
* \param handle Open underlying file handle.
* \param path Path to the file. If NULL and \p create is false, don't check path
* for validity.
* \param underlying_size Underlying file size.
* \param mode Access mode.
* \param create Overwrite file contents if true.
* \param key Wrap key.
* \param opening_root_gmac If non-NULL, !create & successfull open, returns root-hash of file
* \param[out] context PF context for later calls.
*
* \returns PF status.
*/
pf_status_t pf_open(pf_handle_t handle, const char* path, uint64_t underlying_size,
pf_file_mode_t mode, bool create, const pf_key_t* key, pf_context_t** context);
pf_file_mode_t mode, bool create, const pf_key_t* key,
pf_mac_t* opening_root_gmac, pf_context_t** context);

/*!
* \brief Close a protected file and commit all changes to disk.
*
* \param pf PF context.
* \param pf PF context.
* \param closing_root_gmac If non-NULL, returns root-hash of file at closing time
*
* \returns PF status.
*/
pf_status_t pf_close(pf_context_t* pf);
pf_status_t pf_close(pf_context_t* pf, pf_mac_t* closing_root_gmac);

/*!
* \brief Read from a protected file.
Expand Down Expand Up @@ -288,13 +291,14 @@ pf_status_t pf_set_size(pf_context_t* pf, uint64_t size);
/*!
* \brief Rename a PF.
*
* \param pf PF context.
* \param new_path New file path.
* \param pf PF context.
* \param new_path New file path.
* \param new_root_gmac if non-NULL, returns new root-hash of file
*
* Updates the path inside protected file header, and flushes all changes. The caller is responsible
* for renaming the underlying file.
*/
pf_status_t pf_rename(pf_context_t* pf, const char* new_path);
pf_status_t pf_rename(pf_context_t* pf, const char* new_path, pf_mac_t* new_root_gmac);

/*!
* \brief Get underlying handle of a PF.
Expand Down
3 changes: 3 additions & 0 deletions libos/include/libos_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ struct libos_mount_params {

/* Key name (used by `chroot_encrypted` filesystem), or NULL if not applicable */
const char* key_name;

/* Enforcement type (used by `chroot_encrypted` filesystem), or NULL if not applicable */
const char* protection_mode;
};

struct libos_fs_ops {
Expand Down
45 changes: 40 additions & 5 deletions libos/include/libos_fs_encrypted.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include <stddef.h>

#include "libos_checkpoint.h" // ofr include of uthash.h _and_ consistent uthash_fatal macros
#include "libos_types.h"
#include "list.h"
#include "pal.h"
Expand All @@ -34,6 +35,40 @@ struct libos_encrypted_files_key {
LIST_TYPE(libos_encrypted_files_key) list;
};

typedef enum {
PF_FILE_IN_USE_NEW = 0, // file is currently in-use but did not exist at open time
PF_FILE_IN_USE_EXISTING = 1, // file is currently in-use and existed at open time
PF_FILE_CLOSED = 2, // file was provisously seend with known (good committed) state
PF_FILE_DELETED = 3, // the old path of renames is also considered deleted
PF_FILE_ERROR = 4, // file is in non-determined state due to some errors
} libos_encrypted_file_state_t;

/*
* Map mapping file URIs to state providing information on files, in particular whether we have seen
* them before and what the last seen root-hash is. This is necessary to provide rollback
*/
struct libos_encrypted_volume_state_map {
char* uri; // assumptions: all paths canonicalized, symlinks are resolved & no hard links
libos_encrypted_file_state_t state;
pf_mac_t last_seen_root_gmac;
UT_hash_handle hh;
};

typedef enum {
PF_ENCLAVE_LIFE_RB_PROTECTION_NONE = 0,
PF_ENCLAVE_LIFE_RB_PROTECTION_NON_STRICT = 1,
PF_ENCLAVE_LIFE_RB_PROTECTION_STRICT = 2,
} libos_encrypted_files_mode_t;

struct libos_encrypted_volume {
libos_encrypted_files_mode_t protection_mode;

struct libos_encrypted_volume_state_map* files_state_map;
struct libos_lock files_state_map_lock;

struct libos_encrypted_files_key* key;
};

/*
* Represents a specific encrypted file. The file is open as long as `use_count` is greater than 0.
* Note that the file can be open and closed multiple times before it's destroyed.
Expand All @@ -44,7 +79,7 @@ struct libos_encrypted_files_key {
struct libos_encrypted_file {
size_t use_count;
char* uri;
struct libos_encrypted_files_key* key;
struct libos_encrypted_volume* volume;

/* `pf` and `pal_handle` are non-null as long as `use_count` is greater than 0 */
pf_context_t* pf;
Expand Down Expand Up @@ -110,29 +145,29 @@ void update_encrypted_files_key(struct libos_encrypted_files_key* key, const pf_
* \brief Open an existing encrypted file.
*
* \param uri PAL URI to open, has to begin with "file:".
* \param key Key, has to be already set.
* \param volume Volume assocated with file, has to be already set.
* \param[out] out_enc On success, set to a newly created `libos_encrypted_file` object.
*
* `uri` has to correspond to an existing file that can be decrypted with `key`.
*
* The newly created `libos_encrypted_file` object will have `use_count` set to 1.
*/
int encrypted_file_open(const char* uri, struct libos_encrypted_files_key* key,
int encrypted_file_open(const char* uri, struct libos_encrypted_volume* volume,
struct libos_encrypted_file** out_enc);

/*
* \brief Create a new encrypted file.
*
* \param uri PAL URI to open, has to begin with "file:".
* \param perm Permissions for the new file.
* \param key Key, has to be already set.
* \param volume Volume assocated with file, has to be already set.
* \param[out] out_enc On success, set to a newly created `libos_encrypted_file` object.
*
* `uri` must not correspond to an existing file.
*
* The newly created `libos_encrypted_file` object will have `use_count` set to 1.
*/
int encrypted_file_create(const char* uri, mode_t perm, struct libos_encrypted_files_key* key,
int encrypted_file_create(const char* uri, mode_t perm, struct libos_encrypted_volume* volume,
struct libos_encrypted_file** out_enc);

/*
Expand Down
61 changes: 49 additions & 12 deletions libos/src/fs/chroot/encrypted.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,64 @@ static int chroot_encrypted_mount(struct libos_mount_params* params, void** moun
if (ret < 0)
return ret;

*mount_data = key;
libos_encrypted_files_mode_t protection_mode =
PF_ENCLAVE_LIFE_RB_PROTECTION_STRICT; // default mode
if (params->protection_mode) {
if (strncmp(params->protection_mode, "strict", strlen("strict")) == 0)
protection_mode = PF_ENCLAVE_LIFE_RB_PROTECTION_STRICT;
else if (strncmp(params->protection_mode, "non-strict", strlen("non-strict")) == 0)
protection_mode = PF_ENCLAVE_LIFE_RB_PROTECTION_NON_STRICT;
else if (strncmp(params->protection_mode, "none", strlen("none")) == 0)
protection_mode = PF_ENCLAVE_LIFE_RB_PROTECTION_NONE;
else {
log_error("Invalid enforcement type: %s", params->protection_mode);
return -EINVAL;
}
protection_mode = PF_ENCLAVE_LIFE_RB_PROTECTION_STRICT;
protection_mode = PF_ENCLAVE_LIFE_RB_PROTECTION_NON_STRICT;
protection_mode = PF_ENCLAVE_LIFE_RB_PROTECTION_NONE;
}

struct libos_encrypted_volume* volume = malloc(sizeof(struct libos_encrypted_volume));
if (!volume)
return -ENOMEM;
volume->protection_mode = protection_mode;
volume->key = key;
if (!create_lock(&volume->files_state_map_lock)) {
free(volume);
return -ENOMEM;
}
volume->files_state_map = NULL;

*mount_data = volume;
return 0;
}

static ssize_t chroot_encrypted_checkpoint(void** checkpoint, void* mount_data) {
struct libos_encrypted_files_key* key = mount_data;
struct libos_encrypted_volume* volume = mount_data;

*checkpoint = strdup(key->name);
// TODO (MST): fix below, doesn't really makes sense: i guess i have to duplicate something
// about volume?
*checkpoint = strdup(volume->key->name);
if (!*checkpoint)
return -ENOMEM;
return strlen(key->name) + 1;
return strlen(volume->key->name) + 1;
}

static int chroot_encrypted_migrate(void* checkpoint, void** mount_data) {
const char* name = checkpoint;

struct libos_encrypted_files_key* key;
int ret = get_or_create_encrypted_files_key(name, &key);
struct libos_encrypted_volume* volume = malloc(sizeof(struct libos_encrypted_volume));
if (!volume)
return -ENOMEM;

if (!create_lock(&volume->files_state_map_lock))
return -ENOMEM;
// TODO (MST): initialize map
int ret = get_or_create_encrypted_files_key(name, &(volume->key));
if (ret < 0)
return ret;
*mount_data = key;
*mount_data = volume;
return 0;
}

Expand Down Expand Up @@ -153,8 +190,8 @@ static int chroot_encrypted_lookup(struct libos_dentry* dent) {
struct libos_encrypted_file* enc;
file_off_t size;

struct libos_encrypted_files_key* key = dent->mount->data;
ret = encrypted_file_open(uri, key, &enc);
struct libos_encrypted_volume* volume = dent->mount->data;
ret = encrypted_file_open(uri, volume, &enc);
if (ret < 0) {
if (ret == -EACCES) {
/* allow the inode to be created even if the underlying encrypted file is corrupted;
Expand Down Expand Up @@ -210,7 +247,7 @@ static int chroot_encrypted_open(struct libos_handle* hdl, struct libos_dentry*
get_inode(dent->inode);
hdl->type = TYPE_CHROOT_ENCRYPTED;
hdl->seekable = true;
hdl->pos = 0;
hdl->pos = 0;
return 0;
}

Expand All @@ -231,9 +268,9 @@ static int chroot_encrypted_creat(struct libos_handle* hdl, struct libos_dentry*
goto out;
}

struct libos_encrypted_files_key* key = dent->mount->data;
struct libos_encrypted_volume* volume = dent->mount->data;
struct libos_encrypted_file* enc;
ret = encrypted_file_create(uri, HOST_PERM(perm), key, &enc);
ret = encrypted_file_create(uri, HOST_PERM(perm), volume, &enc);
if (ret < 0)
goto out;

Expand Down
25 changes: 17 additions & 8 deletions libos/src/fs/libos_fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,11 @@ static int mount_one_nonroot(toml_table_t* mount, const char* prefix) {

int ret;

char* mount_type = NULL;
char* mount_path = NULL;
char* mount_uri = NULL;
char* mount_key_name = NULL;
char* mount_type = NULL;
char* mount_path = NULL;
char* mount_uri = NULL;
char* mount_key_name = NULL;
char* mount_protection_mode = NULL;

ret = toml_string_in(mount, "type", &mount_type);
if (ret < 0) {
Expand Down Expand Up @@ -228,6 +229,13 @@ static int mount_one_nonroot(toml_table_t* mount, const char* prefix) {
goto out;
}

ret = toml_string_in(mount, "protection_mode", &mount_protection_mode);
if (ret < 0) {
log_error("Cannot parse '%s.key_name'", prefix);
ret = -EINVAL;
goto out;
}

if (!mount_path) {
log_error("No value provided for '%s.path'", prefix);
ret = -EINVAL;
Expand Down Expand Up @@ -269,10 +277,11 @@ static int mount_one_nonroot(toml_table_t* mount, const char* prefix) {
}

struct libos_mount_params params = {
.type = mount_type ?: "chroot",
.path = mount_path,
.uri = mount_uri,
.key_name = mount_key_name,
.type = mount_type ?: "chroot",
.path = mount_path,
.uri = mount_uri,
.key_name = mount_key_name,
.protection_mode = mount_protection_mode,
};
ret = mount_fs(&params);

Expand Down

0 comments on commit 2233cf6

Please sign in to comment.