/* * Copyright (C) 2011-2018 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "pve_logic.h" #include "aesm_logic.h" #include "PVEClass.h" #include "QEClass.h" #include "PCEClass.h" #include "oal/oal.h" #include "aesm_epid_blob.h" #include "se_wrapper.h" #include "prov_msg_size.h" #include "network_encoding_wrapper.h" #include "prof_fun.h" #include "aesm_long_lived_thread.h" #include "endpoint_select_info.h" #include #define SAFE_FREE(ptr) {if (NULL != (ptr)) {free(ptr); (ptr)=NULL;}} //Function to continue process Provisioning logic when the response of ProvMsg1 is ProvMsg2 ae_error_t PvEAESMLogic::process_pve_msg2(pve_data_t& data, const uint8_t* msg2, uint32_t msg2_size, const endpoint_selection_infos_t& es_info) { uint32_t msg_size = 0; uint8_t *msg = NULL; uint8_t *resp_msg = NULL; uint32_t resp_size = 0; epid_blob_with_cur_psvn_t epid_data; ae_error_t ret = AE_SUCCESS; AESM_PROFILE_FUN; AESM_DBG_DEBUG("enter fun"); AESM_DBG_TRACE("processing msg2 whose length is %d",msg2_size); memset(&epid_data, 0, sizeof(epid_data)); if(EPIDBlob::instance().read(epid_data)!=AE_SUCCESS){ //First try to read existing EPID Blob to get old epid blob //error code of reading epid blob will be ignored since old epid blob is optional AESM_DBG_TRACE("read old epid blob fail"); }else{ AESM_DBG_TRACE("succ read old epid blob"); } msg_size = estimate_msg3_size_by_msg2_size(msg2_size); //estimate an upbound for msg3 size AESM_DBG_TRACE("estimate msg3 size: %d",msg_size); assert(msg_size > 0); msg = static_cast(malloc(msg_size)); if(msg == NULL){ AESM_DBG_ERROR("malloc failed"); ret = AE_OUT_OF_MEMORY_ERROR; goto CLEANUP; } memset(msg, 0, msg_size); AESM_DBG_TRACE("start processing msg2 and gen msg3"); ret = static_cast(CPVEClass::instance().proc_prov_msg2(data, msg2, msg2_size, epid_data.trusted_epid_blob, SGX_TRUSTED_EPID_BLOB_SIZE_SDK,//discard curpsvn in epid blob msg, msg_size));//with help of PvE, process ProvMsg2 and generate ProvMsg3 if(ret == AE_SUCCESS){ if(GET_SIZE_FROM_PROVISION_REQUEST(msg)>msg_size){ AESM_DBG_ERROR("prov msg2 size %d is larger than buffer size %d", GET_SIZE_FROM_PROVISION_REQUEST(msg), msg_size); ret = PVE_UNEXPECTED_ERROR; goto CLEANUP; } AESM_DBG_TRACE("Start send msg3 and recv msg4"); msg_size = static_cast(GET_SIZE_FROM_PROVISION_REQUEST(msg));//get the real size of ProvMsg3 ret = AESMNetworkEncoding::aesm_send_recv_msg_encoding(es_info.provision_url, msg, msg_size, resp_msg, resp_size); //Encoding ProvMsg3, send to server, receive ProvMsg4 and decode if(ret != AE_SUCCESS){ AESM_LOG_ERROR("%s",g_event_string_table[SGX_EVENT_EPID_PROV_FAILURE]); AESM_DBG_WARN("send prov msg3 via network failed:(ae%d)",ret); goto CLEANUP; } assert(resp_msg!=NULL); AESM_DBG_TRACE("Start to proc msg4"); ret = process_pve_msg4(data, resp_msg, resp_size);//The response msg must be ProvMsg4, process it to generate EPIDBlob if(ret != AE_SUCCESS){ AESM_DBG_TRACE("processing msg4 failed:(ae%d)",ret); goto CLEANUP; } ret = AE_SUCCESS; AESM_DBG_TRACE("processing msg4 succ"); }else{ AESM_DBG_WARN("fail to process prov msg2:(ae%d)",ret); } CLEANUP: SAFE_FREE(msg); if(resp_msg!=NULL) AESMNetworkEncoding::aesm_free_response_msg(resp_msg); return ret; } //Function to finish the Provisioning Logic when a ProvMsg4 is expected or encountered ae_error_t PvEAESMLogic::process_pve_msg4(const pve_data_t& data, const uint8_t* msg4, uint32_t msg4_size) { AESM_PROFILE_FUN; epid_blob_with_cur_psvn_t epid_data; ae_error_t ret = AE_SUCCESS; AESM_DBG_DEBUG("enter fun"); AESM_DBG_TRACE("processing msg4 with size %d",msg4_size); memset(&epid_data, 0, sizeof(epid_data)); //with the help of PvE to process ProvMsg4 and generate EPIDDataBlob if((ret = static_cast(CPVEClass::instance().proc_prov_msg4(data, msg4, msg4_size, epid_data.trusted_epid_blob, SGX_TRUSTED_EPID_BLOB_SIZE_SDK)))!=AE_SUCCESS){ AESM_DBG_WARN("proc prov msg4 fail:(ae%d)",ret); goto fini; } if(0!=memcpy_s(&epid_data.cur_pi, sizeof(epid_data.cur_pi), &data.bpi, sizeof(data.bpi))){ AESM_DBG_ERROR("memcpy failed"); ret = PVE_UNEXPECTED_ERROR; goto fini; } #ifdef DBG_LOG char dbg_str[256]; aesm_dbg_format_hex(reinterpret_cast(&epid_data), sizeof(epid_data), dbg_str, 256); AESM_DBG_TRACE("write epid_data=%s",dbg_str); #endif ret=EPIDBlob::instance().write(epid_data);//save the data into persistent data storage if(AE_SUCCESS!=ret){ AESM_DBG_WARN("fail to write epid_data:(ae%d)",ret); } fini: return (ae_error_t)ret; } //Function to process the Provisioning Logic for backup retrieval of old epid data blob //The function assumes that the PvE state has been IDLE ae_error_t PvEAESMLogic::update_old_blob(pve_data_t& data, const endpoint_selection_infos_t& es_info) { uint32_t msg_size = 0; uint8_t *msg = NULL; uint32_t ae_ret = AE_SUCCESS; uint8_t *resp_msg = NULL; uint32_t resp_size = 0; AESM_PROFILE_FUN; AESM_DBG_DEBUG("enter fun"); msg_size = estimate_msg1_size(false); assert(msg_size > 0); msg = static_cast(malloc(msg_size)); if(msg == NULL){ AESM_DBG_ERROR("malloc fail"); ae_ret = AE_OUT_OF_MEMORY_ERROR; goto ret_point; } memset(msg, 0, msg_size); AESM_DBG_TRACE("start to gen prov msg1, estimate size %d", msg_size); data.is_backup_retrieval = true; data.is_performance_rekey = false; ae_ret = CPVEClass::instance().gen_prov_msg1(data, msg, msg_size);//generate ProvMsg1 if (ae_ret != AE_SUCCESS) { AESM_DBG_WARN("gen prov msg1 failed:(ae%d)",ae_ret); goto ret_point; } msg_size = static_cast(GET_SIZE_FROM_PROVISION_REQUEST(msg)); AESM_DBG_TRACE("start to send msg1 to server and recv msg4"); ae_ret = AESMNetworkEncoding::aesm_send_recv_msg_encoding(es_info.provision_url, msg, msg_size, resp_msg,resp_size);//encoding/send/receive/decoding if(ae_ret != AE_SUCCESS){ AESM_LOG_ERROR("%s",g_event_string_table[SGX_EVENT_EPID_PROV_FAILURE]); AESM_DBG_WARN("send prov msg1 via network failed:%d",ae_ret); goto ret_point; } assert(resp_msg != NULL); if (resp_size < PROVISION_RESPONSE_HEADER_SIZE) { AESM_DBG_WARN("response message %d too small",resp_size); ae_ret = PVE_UNEXPECTED_ERROR; goto ret_point; } AESM_DBG_TRACE("start to send msg4 to server"); if(GET_TYPE_FROM_PROVISION_RESPONSE(resp_msg) == TYPE_PROV_MSG4){ ae_ret = process_pve_msg4(data, resp_msg, resp_size);//process ProvMsg4 and generated/save EPID Data Blob AESM_DBG_TRACE("msg4 processing finished, status (ae%d)",ae_ret); }else{ AESM_DBG_WARN("response message is not prov msg4"); ae_ret = PVE_UNEXPECTED_ERROR; } ret_point: if(msg)free(msg); if(resp_msg!=NULL){ AESMNetworkEncoding::aesm_free_response_msg(resp_msg); } return (ae_error_t)ae_ret; } aesm_error_t PvEAESMLogic::pve_error_postprocess(ae_error_t ae_error) { switch(ae_error){ case AE_SUCCESS: return AESM_SUCCESS; case OAL_NETWORK_UNAVAILABLE_ERROR: { AESM_LOG_FATAL("%s", g_event_string_table[SGX_EVENT_EPID_PROV_FAILURE]); return AESM_NETWORK_ERROR; } case OAL_THREAD_TIMEOUT_ERROR: return AESM_BUSY; case OAL_NETWORK_BUSY: return AESM_NETWORK_BUSY_ERROR; case OAL_PROXY_SETTING_ASSIST: return AESM_PROXY_SETTING_ASSIST; case OAL_FILE_ACCESS_ERROR: case OAL_CONFIG_FILE_ERROR: return AESM_FILE_ACCESS_ERROR; case PVE_PARAMETER_ERROR: case AE_INVALID_PARAMETER: case OAL_PARAMETER_ERROR: return AESM_PARAMETER_ERROR; case PVE_EPIDBLOB_ERROR: return AESM_EPIDBLOB_ERROR; case AE_ENCLAVE_LOST: return AESM_NO_DEVICE_ERROR; case AE_SERVER_NOT_AVAILABLE: return AESM_SERVICE_UNAVAILABLE; case PVE_INTEGRITY_CHECK_ERROR: { AESM_LOG_FATAL("%s", g_event_string_table[SGX_EVENT_EPID_PROV_INTEGRITY_ERROR]); return AESM_SGX_PROVISION_FAILED; } case PVE_SIGRL_INTEGRITY_CHECK_ERROR: { AESM_LOG_FATAL("%s", g_event_string_table[SGX_EVENT_EPID20_SIGRL_INTEGRITY_ERROR]); return AESM_SGX_PROVISION_FAILED; } case PVE_SERVER_REPORTED_ERROR: case PVE_MSG_ERROR: return AESM_SGX_PROVISION_FAILED; case PVE_REVOKED_ERROR: return AESM_EPID_REVOKED_ERROR; case PVE_SERVER_BUSY_ERROR: return AESM_BACKEND_SERVER_BUSY; case PVE_PROV_ATTEST_KEY_NOT_FOUND: return AESM_UNRECOGNIZED_PLATFORM; case AE_OUT_OF_MEMORY_ERROR: return AESM_OUT_OF_MEMORY_ERROR; case PSW_UPDATE_REQUIRED: return AESM_UPDATE_AVAILABLE; case AESM_AE_OUT_OF_EPC: return AESM_OUT_OF_EPC; default: return AESM_UNEXPECTED_ERROR; } } aesm_error_t PvEAESMLogic::provision(bool performance_rekey_used, uint32_t timeout_usec) { ae_error_t ae_ret = AE_SUCCESS; AESM_PROFILE_FUN; AESM_DBG_DEBUG("enter fun"); AESM_DBG_TRACE("start end point selection"); ae_ret = start_epid_provision_thread(performance_rekey_used, timeout_usec); return pve_error_postprocess(ae_ret); } static void log_provision_result(ae_error_t ae_ret) { // Log provisioning results to the Admin Log switch (ae_ret) { case AE_SUCCESS: AESM_LOG_INFO_ADMIN("%s", g_admin_event_string_table[SGX_ADMIN_EVENT_EPID_PROV_SUCCESS]); break; case OAL_NETWORK_UNAVAILABLE_ERROR: AESM_LOG_ERROR_ADMIN("%s", g_admin_event_string_table[SGX_ADMIN_EVENT_EPID_PROV_FAIL_NW]); break; case PSW_UPDATE_REQUIRED: AESM_LOG_ERROR_ADMIN("%s", g_admin_event_string_table[SGX_ADMIN_EVENT_EPID_PROV_FAIL_PSWVER]); break; case PVE_REVOKED_ERROR: AESM_LOG_ERROR_ADMIN("%s", g_admin_event_string_table[SGX_ADMIN_EVENT_EPID_PROV_FAIL_REVOKED]); break; case OAL_PROXY_SETTING_ASSIST://do not log for proxy assist and thread time out error case OAL_THREAD_TIMEOUT_ERROR: break; default: AESM_LOG_ERROR_ADMIN("%s", g_admin_event_string_table[SGX_ADMIN_EVENT_EPID_PROV_FAIL]); break; } } ae_error_t PvEAESMLogic::epid_provision_thread_func(bool performance_rekey_used) { uint32_t msg_size = 0; uint8_t *msg = NULL; uint8_t *resp_msg = NULL; uint32_t resp_size = 0; ae_error_t ae_ret = AE_SUCCESS; uint32_t repeat = 0; endpoint_selection_infos_t es_info; pve_data_t pve_data; AESM_LOG_INFO_ADMIN("%s", g_admin_event_string_table[SGX_ADMIN_EVENT_EPID_PROV_START]); memset(&pve_data, 0, sizeof(pve_data)); if(AE_SUCCESS!=(ae_ret=aesm_start_request_wake_execution())){ AESM_DBG_ERROR("fail to request wake execution:(ae%d)", ae_ret); log_provision_result(ae_ret); return ae_ret; } AESM_DBG_TRACE("start end point selection"); if((ae_ret = EndpointSelectionInfo::instance().start_protocol(es_info))!=AE_SUCCESS){//EndPoint Selection Protocol to setup Provisioning URL (void)aesm_stop_request_wake_execution(); AESM_DBG_WARN("end point selection failed:(ae%d)",ae_ret); log_provision_result(ae_ret); return ae_ret; } //If enclave_lost encountered(such as S3/S4 reached, the retry will be increased by 1, for other kinds of exception like network error, repeat is increased by 1) while(repeat < AESM_RETRY_COUNT){ //estimate upbound of ProvMsg1 and alloc memory for it msg_size = estimate_msg1_size(performance_rekey_used); AESM_DBG_TRACE("estimate msg1 size :%d",msg_size); assert(msg_size > 0); if(msg!=NULL)free(msg); msg = (uint8_t *)malloc(msg_size); if(msg == NULL){ AESM_DBG_TRACE("malloc failed"); ae_ret = AE_OUT_OF_MEMORY_ERROR; break; } memset(msg, 0, msg_size); //Generate ProvMsg1 pve_data.is_backup_retrieval = false; pve_data.is_performance_rekey = performance_rekey_used; if(0!=memcpy_s(&pve_data.pek, sizeof(pve_data.pek), &es_info.pek, sizeof(es_info.pek))){ AESM_DBG_ERROR("memcpy error"); ae_ret = AE_FAILURE; break; } ae_ret = static_cast(CPVEClass::instance().gen_prov_msg1(pve_data, msg, msg_size));//Generate ProvMsg1 if (ae_ret != AE_SUCCESS) { AESM_DBG_WARN("fail to generate prov msg1:(ae%d)",ae_ret); break; } assert( msg != NULL && GET_SIZE_FROM_PROVISION_REQUEST(msg) >= PROVISION_REQUEST_HEADER_SIZE); msg_size = static_cast(GET_SIZE_FROM_PROVISION_REQUEST(msg)); AESM_DBG_TRACE("msg1 generated with size %d",msg_size); if(resp_msg!=NULL){ AESMNetworkEncoding::aesm_free_response_msg(resp_msg); resp_msg=NULL; } AESM_DBG_TRACE("start to send prov msg1 and recv response"); ae_ret = AESMNetworkEncoding::aesm_send_recv_msg_encoding(es_info.provision_url, msg,msg_size, resp_msg, resp_size);//encoding/send ProvMsg1, receiving and decoding resp message if(ae_ret != AE_SUCCESS){ AESM_DBG_WARN("send msg1 via network fail:(ae%d)",ae_ret); break;//aesm_send_recv_se_msg will not return AE_ENCLAVE_LOST } assert (resp_msg != NULL && resp_size >= PROVISION_RESPONSE_HEADER_SIZE); if(GET_TYPE_FROM_PROVISION_RESPONSE(resp_msg) == TYPE_PROV_MSG2){//If responsed msg is ProvMsg2 AESM_DBG_TRACE("start to process prov msg2, size %d", resp_size); ae_ret = process_pve_msg2(pve_data, resp_msg, resp_size, es_info);//processing following flow if response message is ProvMsg2 if(ae_ret != AE_SUCCESS){ if(ae_ret == PVE_EPIDBLOB_ERROR){//If it reports old EPID Blob Error AESM_DBG_TRACE("retrieve old epid blob"); if((ae_ret = update_old_blob(pve_data, es_info))!=AE_SUCCESS){//try to retrieve old EPID blob from backend server AESM_DBG_WARN("fail to retrieve old epid blob:(ae%d)",ae_ret); break; }else{ AESM_DBG_TRACE("retrieve old epid blob successfully"); ae_ret = AE_FAILURE;//set to failure repeat++;//only retry after update old epid blob continue; } }else{ AESM_DBG_WARN("processing prov msg2 failed:(ae%d)",ae_ret); break; } } }else if(GET_TYPE_FROM_PROVISION_RESPONSE(resp_msg) == TYPE_PROV_MSG4){ AESM_DBG_TRACE("start to process prov msg4 for current psvn"); if((ae_ret = process_pve_msg4(pve_data, resp_msg,resp_size))!=AE_SUCCESS){//process ProvMsg4 to generate EPID blob if resp is Msg4 AESM_DBG_WARN("fail to process prov msg4:(ae%d)",ae_ret); break; } }else{ AESM_DBG_ERROR("Invalid resp msg type from backend server:%d",(int)GET_TYPE_FROM_PROVISION_RESPONSE(resp_msg)); ae_ret = AE_FAILURE; break; } AESM_DBG_TRACE("provisioning succ"); ae_ret = AE_SUCCESS; break; } SAFE_FREE(msg); if(resp_msg!=NULL){ AESMNetworkEncoding::aesm_free_response_msg(resp_msg); } (void)aesm_stop_request_wake_execution(); log_provision_result(ae_ret); return ae_ret; }