SDK_PF_New

April 8, 2021 at 11:45 am
Analysis Artifact SGX

Construction function

  1. check key validity
  2. create report
  3. check if file exist
  4. open file
  5. check if it's a valid SGX SDK PF (by size)

https://github.com/intel/linux-sgx/blob/master/sdk/protected_fs/sgx_tprotected_fs/file_init.cpp

protected_fs_file::protected_fs_file(const char* filename, const char* mode, const sgx_aes_gcm_128bit_key_t* import_key, const sgx_aes_gcm_128bit_key_t* kdk_key)
{
    sgx_status_t status = SGX_SUCCESS;
    uint8_t result = 0;
    int32_t result32 = 0;
    
    init_fields();

    if (filename == NULL || mode == NULL || 
        strnlen(filename, 1) == 0 || strnlen(mode, 1) == 0)
    {
        last_error = EINVAL;
        return;
    }

    if (strnlen(filename, FULLNAME_MAX_LEN) >= FULLNAME_MAX_LEN - 1)
    {
        last_error = ENAMETOOLONG;
        return;
    }

    if (import_key != NULL && kdk_key != NULL)
    {// import key is used only with auto generated keys
        last_error = EINVAL;
        return;
    }

    status = sgx_create_report(NULL, NULL, &report);
    if (status != SGX_SUCCESS)
    {
        last_error = status;
        return;
    }

    result32 = sgx_thread_mutex_init(&mutex, NULL);
    if (result32 != 0)
    {
        last_error = result32;
        return;
    }

    if (init_session_master_key() == false) 
        // last_error already set
        return;

    if (kdk_key != NULL)
    {
        // for new file, this value will later be saved in the meta data plain part (init_new_file)
        // for existing file, we will later compare this value with the value from the file (init_existing_file)
        use_user_kdk_key = 1; 
        memcpy(user_kdk_key, kdk_key, sizeof(sgx_aes_gcm_128bit_key_t));
    }
    
    // get the clean file name (original name might be clean or with relative path or with absolute path...)
    char clean_filename[FILENAME_MAX_LEN];
    if (cleanup_filename(filename, clean_filename) == false)
        // last_error already set
        return;
    
    if (import_key != NULL)
    {// verify the key is not empty - note from SAFE review
        sgx_aes_gcm_128bit_key_t empty_aes_key = {0};
        if (consttime_memequal(import_key, &empty_aes_key, sizeof(sgx_aes_gcm_128bit_key_t)) == 1)
        {
            last_error = EINVAL;
            return;
        }
    }

    if (parse_mode(mode) == false)
    {
        last_error = EINVAL;
        return;
    }

    status = u_sgxprotectedfs_check_if_file_exists(&result, filename); // if result == 1 --> file exists
    if (status != SGX_SUCCESS)
    {
        last_error = status;
        return;
    }

    if (open_mode.write == 1 && result == 1)
    {// try to delete existing file
        int32_t saved_errno = 0;

        result32 = remove(filename);
        if (result32 != 0)
        {
            // either can't delete or the file was already deleted by someone else
            saved_errno = errno;
            errno = 0;
        }

        // re-check
        status = u_sgxprotectedfs_check_if_file_exists(&result, filename);
        if (status != SGX_SUCCESS || result == 1)
        {
            last_error = (status != SGX_SUCCESS) ? status :
                         (saved_errno != 0) ? saved_errno : EACCES;
            return;
        }
    }

    if (open_mode.read == 1 && result == 0)
    {// file must exists
        last_error = ENOENT;
        return;
    }

    if (import_key != NULL && result == 0)
    {// file must exists - otherwise the user key is not used
        last_error = ENOENT;
        return;
    }

    // now open the file
    read_only = (open_mode.read == 1 && open_mode.update == 0); // read only files can be opened simultaneously by many enclaves

    do {
        status = u_sgxprotectedfs_exclusive_file_open(&file, filename, read_only, &real_file_size, &result32);
        if (status != SGX_SUCCESS || file == NULL)
        {
            last_error = (status != SGX_SUCCESS) ? status :
                         (result32 != 0) ? result32 : EACCES;
            break;
        }

        if (real_file_size < 0)
        {
            last_error = EINVAL;
            break;
        }

        if (real_file_size % NODE_SIZE != 0)
        {
            last_error = SGX_ERROR_FILE_NOT_SGX_FILE;
            break;
        }
        
        strncpy(recovery_filename, filename, FULLNAME_MAX_LEN - 1); // copy full file name
        recovery_filename[FULLNAME_MAX_LEN - 1] = '\0'; // just to be safe
        size_t full_name_len = strnlen(recovery_filename, RECOVERY_FILE_MAX_LEN);
        strncpy(&recovery_filename[full_name_len], "_recovery", 10);

        if (real_file_size > 0)
        {// existing file
            if (open_mode.write == 1) // redundant check, just in case
            {
                last_error = EACCES;
                break;
            }

            if (init_existing_file(filename, clean_filename, import_key) == false)
                break;
                
            if (open_mode.append == 1 && open_mode.update == 0)
                offset = encrypted_part_plain.size;
        }
        else
        {// new file
            if (init_new_file(clean_filename) == false)
                break;
        }

        file_status = SGX_FILE_STATUS_OK;

    } while(0);

    if (file_status != SGX_FILE_STATUS_OK)
    {
        if (file != NULL)
        {
            u_sgxprotectedfs_fclose(&result32, file); // we don't care about the result
            file = NULL;
        }
    }
}