blob: 62b240b9c9e06c0b88e3d1b03ed3f0701771bcba [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
#include "protected_fs_config.h"
#include "sgx_tprotected_fs.h"
#include "sgx_tprotected_fs_t.h"
#include "protected_fs_file.h"
#ifdef NON_SGX_PROTECTED_FS
#include "non_sgx_protected_fs.h"
#else
#include <sgx_utils.h>
//#include <sgx_trts.h>
#endif
#include <errno.h>
// this function returns 0 only if the specified file existed and it was actually deleted
// before we do that, we try to see if the file contained a monotonic counter, and if it did, we delete it from the system
int32_t protected_fs_file::remove(const char* filename)
{
sgx_status_t status = SGX_SUCCESS;
int32_t result32 = 0;
/*
void* file = NULL;
int64_t real_file_size = 0;
if (filename == NULL)
return 1;
meta_data_node_t* file_meta_data = NULL;
meta_data_encrypted_t* encrypted_part_plain = NULL;
// if we have a problem in any of the stages, we simply jump to the end and try to remove the file...
do {
status = u_sgxprotectedfs_check_if_file_exists(&result, filename);
if (status != SGX_SUCCESS)
break;
if (result == 0)
{
errno = EINVAL;
return 1; // no such file, or file locked so we can't delete it anyways
}
try {
file_meta_data = new meta_data_node_t;
encrypted_part_plain = new meta_data_encrypted_t;
}
catch (std::bad_alloc e) {
break;
}
status = u_sgxprotectedfs_exclusive_file_open(&file, filename, 1, &real_file_size, &result32);
if (status != SGX_SUCCESS || file == NULL)
break;
if (real_file_size == 0 || real_file_size % NODE_SIZE != 0)
break; // empty file or not an SGX protected FS file
// might be an SGX protected FS file
status = u_sgxprotectedfs_fread_node(&result32, file, 0, (uint8_t*)file_meta_data, NODE_SIZE);
if (status != SGX_SUCCESS || result32 != 0)
break;
if (file_meta_data->plain_part.major_version != SGX_FILE_MAJOR_VERSION)
break;
sgx_aes_gcm_128bit_key_t zero_key_id = {0};
sgx_aes_gcm_128bit_key_t key = {0};
if (consttime_memequal(&file_meta_data->plain_part.key_id, &zero_key_id, sizeof(sgx_aes_gcm_128bit_key_t)) == 1)
break; // shared file - no monotonic counter
sgx_key_request_t key_request = {0};
key_request.key_name = SGX_KEYSELECT_SEAL;
key_request.key_policy = SGX_KEYPOLICY_MRENCLAVE;
memcpy(&key_request.key_id, &file_meta_data->plain_part.key_id, sizeof(sgx_key_id_t));
status = sgx_get_key(&key_request, &key);
if (status != SGX_SUCCESS)
break;
status = sgx_rijndael128GCM_decrypt(&key,
file_meta_data->encrypted_part, sizeof(meta_data_encrypted_blob_t),
(uint8_t*)encrypted_part_plain,
file_meta_data->plain_part.meta_data_iv, SGX_AESGCM_IV_SIZE,
NULL, 0,
&file_meta_data->plain_part.meta_data_gmac);
if (status != SGX_SUCCESS)
break;
sgx_mc_uuid_t empty_mc_uuid = {0};
if (consttime_memequal(&empty_mc_uuid, &encrypted_part_plain->mc_uuid, sizeof(sgx_mc_uuid_t)) == 0)
{
status = sgx_destroy_monotonic_counter(&encrypted_part_plain->mc_uuid);
if (status != SGX_SUCCESS)
break;
// monotonic counter was deleted, mission accomplished!!
}
}
while (0);
// cleanup
if (file_meta_data != NULL)
delete file_meta_data;
if (encrypted_part_plain != NULL)
{
// scrub the encrypted part
memset_s(encrypted_part_plain, sizeof(meta_data_encrypted_t), 0, sizeof(meta_data_encrypted_t));
delete encrypted_part_plain;
}
if (file != NULL)
u_sgxprotectedfs_fclose(&result32, file);
*/
// do the actual file removal
#ifdef NON_SGX_PROTECTED_FS
result32 = u_sgxprotectedfs_remove(filename);
#else
status = u_sgxprotectedfs_remove(&result32, filename);
#endif
if (status != SGX_SUCCESS)
{
errno = status;
return 1;
}
if (result32 != 0)
{
if (result32 == -1) // no external errno value
errno = EPERM;
else
errno = result32;
return 1;
}
return 0;
}
int64_t protected_fs_file::tell()
{
int64_t result;
sgx_thread_mutex_lock(&mutex);
if (file_status != SGX_FILE_STATUS_OK)
{
errno = EPERM;
last_error = SGX_ERROR_FILE_BAD_STATUS;
sgx_thread_mutex_unlock(&mutex);
return -1;
}
result = offset;
sgx_thread_mutex_unlock(&mutex);
return result;
}
// we don't support sparse files, fseek beyond the current file size will fail
int protected_fs_file::seek(int64_t new_offset, int origin)
{
sgx_thread_mutex_lock(&mutex);
if (file_status != SGX_FILE_STATUS_OK)
{
last_error = SGX_ERROR_FILE_BAD_STATUS;
sgx_thread_mutex_unlock(&mutex);
return -1;
}
//if (open_mode.binary == 0 && origin != SEEK_SET && new_offset != 0)
//{
// last_error = EINVAL;
// sgx_thread_mutex_unlock(&mutex);
// return -1;
//}
int result = -1;
switch (origin)
{
case SEEK_SET:
if (new_offset >= 0 && new_offset <= encrypted_part_plain.size)
{
offset = new_offset;
result = 0;
}
break;
case SEEK_CUR:
if ((offset + new_offset) >= 0 && (offset + new_offset) <= encrypted_part_plain.size)
{
offset += new_offset;
result = 0;
}
break;
case SEEK_END:
if (new_offset <= 0 && new_offset >= (0 - encrypted_part_plain.size))
{
offset = encrypted_part_plain.size + new_offset;
result = 0;
}
break;
default:
break;
}
if (result == 0)
end_of_file = false;
else
last_error = EINVAL;
sgx_thread_mutex_unlock(&mutex);
return result;
}
uint32_t protected_fs_file::get_error()
{
uint32_t result = SGX_SUCCESS;
sgx_thread_mutex_lock(&mutex);
if (last_error != SGX_SUCCESS)
result = last_error;
else if (file_status != SGX_FILE_STATUS_OK)
result = SGX_ERROR_FILE_BAD_STATUS;
sgx_thread_mutex_unlock(&mutex);
return result;
}
bool protected_fs_file::get_eof()
{
return end_of_file;
}
void protected_fs_file::clear_error()
{
sgx_thread_mutex_lock(&mutex);
if (file_status == SGX_FILE_STATUS_NOT_INITIALIZED ||
file_status == SGX_FILE_STATUS_CLOSED ||
file_status == SGX_FILE_STATUS_CRYPTO_ERROR ||
file_status == SGX_FILE_STATUS_CORRUPTED ||
file_status == SGX_FILE_STATUS_MEMORY_CORRUPTED) // can't fix these...
{
sgx_thread_mutex_unlock(&mutex);
return;
}
if (file_status == SGX_FILE_STATUS_FLUSH_ERROR)
{
if (internal_flush(/*false,*/ true) == true)
file_status = SGX_FILE_STATUS_OK;
}
if (file_status == SGX_FILE_STATUS_WRITE_TO_DISK_FAILED)
{
if (write_all_changes_to_disk(true) == true)
{
need_writing = false;
file_status = SGX_FILE_STATUS_OK;
}
}
/*
if (file_status == SGX_FILE_STATUS_WRITE_TO_DISK_FAILED_NEED_MC)
{
if (write_all_changes_to_disk(true) == true)
{
need_writing = false;
file_status = SGX_FILE_STATUS_MC_NOT_INCREMENTED; // fall through...next 'if' should take care of this one
}
}
if ((file_status == SGX_FILE_STATUS_MC_NOT_INCREMENTED) &&
(encrypted_part_plain.mc_value <= (UINT_MAX-2)))
{
uint32_t mc_value;
sgx_status_t status = sgx_increment_monotonic_counter(&encrypted_part_plain.mc_uuid, &mc_value);
if (status == SGX_SUCCESS)
{
assert(mc_value == encrypted_part_plain.mc_value);
file_status = SGX_FILE_STATUS_OK;
}
else
{
last_error = status;
}
}
*/
if (file_status == SGX_FILE_STATUS_OK)
{
last_error = SGX_SUCCESS;
end_of_file = false;
}
sgx_thread_mutex_unlock(&mutex);
}
// clears the cache with all the plain data that was in it
// doesn't clear the meta-data and first node, which are part of the 'main' structure
int32_t protected_fs_file::clear_cache()
{
sgx_thread_mutex_lock(&mutex);
if (file_status != SGX_FILE_STATUS_OK)
{
sgx_thread_mutex_unlock(&mutex);
clear_error(); // attempt to fix the file, will also flush it
sgx_thread_mutex_lock(&mutex);
}
else // file_status == SGX_FILE_STATUS_OK
{
internal_flush(/*false,*/ true);
}
if (file_status != SGX_FILE_STATUS_OK) // clearing the cache might lead to losing un-saved data
{
sgx_thread_mutex_unlock(&mutex);
return 1;
}
while (cache.size() > 0)
{
void* data = cache.get_last();
assert(data != NULL);
assert(((file_data_node_t*)data)->need_writing == false); // need_writing is in the same offset in both node types
// for production -
if (data == NULL || ((file_data_node_t*)data)->need_writing == true)
{
sgx_thread_mutex_unlock(&mutex);
return 1;
}
cache.remove_last();
// before deleting the memory, need to scrub the plain secrets
if (((file_data_node_t*)data)->type == FILE_DATA_NODE_TYPE) // type is in the same offset in both node types
{
file_data_node_t* file_data_node = (file_data_node_t*)data;
memset_s(&file_data_node->plain, sizeof(data_node_t), 0, sizeof(data_node_t));
delete file_data_node;
}
else
{
file_mht_node_t* file_mht_node = (file_mht_node_t*)data;
memset_s(&file_mht_node->plain, sizeof(mht_node_t), 0, sizeof(mht_node_t));
delete file_mht_node;
}
}
sgx_thread_mutex_unlock(&mutex);
return 0;
}
int32_t protected_fs_file::get_current_meta_gmac(sgx_aes_gcm_128bit_tag_t out_gmac)
{
sgx_thread_mutex_lock(&mutex);
if (out_gmac == NULL) {
last_error = EINVAL;
sgx_thread_mutex_unlock(&mutex);
return -1;
}
memcpy(out_gmac, file_meta_data.plain_part.meta_data_gmac, sizeof(sgx_aes_gcm_128bit_tag_t));
sgx_thread_mutex_unlock(&mutex);
return 0;
}
int32_t protected_fs_file::rename_meta(const char* old_name, const char* new_name)
{
sgx_thread_mutex_lock(&mutex);
if ((old_name == NULL) || (new_name == NULL)) {
last_error = EINVAL;
sgx_thread_mutex_unlock(&mutex);
return -1;
}
if (strnlen(old_name, FILENAME_MAX_LEN) >= FILENAME_MAX_LEN-1)
{
last_error = ENAMETOOLONG;
sgx_thread_mutex_unlock(&mutex);
return -1;
}
if (strnlen(new_name, FILENAME_MAX_LEN) >= FILENAME_MAX_LEN-1)
{
last_error = ENAMETOOLONG;
sgx_thread_mutex_unlock(&mutex);
return -1;
}
if (strncmp(old_name, encrypted_part_plain.clean_filename, FILENAME_MAX_LEN) != 0)
{
last_error = SGX_ERROR_FILE_NAME_MISMATCH;
sgx_thread_mutex_unlock(&mutex);
return -1;
}
strncpy(encrypted_part_plain.clean_filename, new_name, FILENAME_MAX_LEN);
need_writing = true;
bool success = internal_flush(true);
if (success == false) {
last_error = SGX_ERROR_FILE_FLUSH_FAILED;
sgx_thread_mutex_unlock(&mutex);
return -1;
}
sgx_thread_mutex_unlock(&mutex);
return 0;
}