file_flush.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. /*
  2. * Copyright (C) 2011-2017 Intel Corporation. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * * Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * * Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in
  12. * the documentation and/or other materials provided with the
  13. * distribution.
  14. * * Neither the name of Intel Corporation nor the names of its
  15. * contributors may be used to endorse or promote products derived
  16. * from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. *
  30. */
  31. #include "sgx_tprotected_fs.h"
  32. #include "sgx_tprotected_fs_t.h"
  33. #include "protected_fs_file.h"
  34. #include "validation_hook_recovery.h"
  35. #include <sgx_trts.h>
  36. bool protected_fs_file::flush(/*bool mc*/)
  37. {
  38. bool result = false;
  39. int32_t result32 = sgx_thread_mutex_lock(&mutex);
  40. if (result32 != 0)
  41. {
  42. last_error = result32;
  43. file_status = SGX_FILE_STATUS_MEMORY_CORRUPTED;
  44. return false;
  45. }
  46. if (file_status != SGX_FILE_STATUS_OK)
  47. {
  48. last_error = SGX_ERROR_FILE_BAD_STATUS;
  49. sgx_thread_mutex_unlock(&mutex);
  50. return false;
  51. }
  52. result = internal_flush(/*mc,*/ true);
  53. if (result == false)
  54. {
  55. assert(file_status != SGX_FILE_STATUS_OK);
  56. if (file_status == SGX_FILE_STATUS_OK)
  57. file_status = SGX_FILE_STATUS_FLUSH_ERROR; // for release set this anyway
  58. }
  59. sgx_thread_mutex_unlock(&mutex);
  60. return result;
  61. }
  62. bool protected_fs_file::internal_flush(/*bool mc,*/ bool flush_to_disk)
  63. {
  64. if (need_writing == false) // no changes at all
  65. return true;
  66. /*
  67. if (mc == true && encrypted_part_plain.mc_value > (UINT_MAX-2))
  68. {
  69. last_error = SGX_ERROR_FILE_MONOTONIC_COUNTER_AT_MAX;
  70. return false;
  71. }
  72. */
  73. if (encrypted_part_plain.size > MD_USER_DATA_SIZE && root_mht.need_writing == true) // otherwise it's just one write - the meta-data node
  74. {
  75. if (_RECOVERY_HOOK_(0) || write_recovery_file() != true)
  76. {
  77. file_status = SGX_FILE_STATUS_FLUSH_ERROR;
  78. return false;
  79. }
  80. if (_RECOVERY_HOOK_(1) || set_update_flag(flush_to_disk) != true)
  81. {
  82. file_status = SGX_FILE_STATUS_FLUSH_ERROR;
  83. return false;
  84. }
  85. if (_RECOVERY_HOOK_(2) || update_all_data_and_mht_nodes() != true)
  86. {
  87. clear_update_flag();
  88. file_status = SGX_FILE_STATUS_CRYPTO_ERROR; // this is something that shouldn't happen, can't fix this...
  89. return false;
  90. }
  91. }
  92. /*
  93. sgx_status_t status;
  94. if (mc == true)
  95. {
  96. // increase monotonic counter local value - only if everything is ok, we will increase the real counter
  97. if (encrypted_part_plain.mc_value == 0)
  98. {
  99. // no monotonic counter so far, need to create a new one
  100. status = sgx_create_monotonic_counter(&encrypted_part_plain.mc_uuid, &encrypted_part_plain.mc_value);
  101. if (status != SGX_SUCCESS)
  102. {
  103. clear_update_flag();
  104. file_status = SGX_FILE_STATUS_FLUSH_ERROR;
  105. last_error = status;
  106. return false;
  107. }
  108. }
  109. encrypted_part_plain.mc_value++;
  110. }
  111. */
  112. if (_RECOVERY_HOOK_(3) || update_meta_data_node() != true)
  113. {
  114. clear_update_flag();
  115. /*
  116. if (mc == true)
  117. encrypted_part_plain.mc_value--; // don't have to do this as the file cannot be fixed, but doing it anyway to prevent future errors
  118. */
  119. file_status = SGX_FILE_STATUS_CRYPTO_ERROR; // this is something that shouldn't happen, can't fix this...
  120. return false;
  121. }
  122. if (_RECOVERY_HOOK_(4) || write_all_changes_to_disk(flush_to_disk) != true)
  123. {
  124. //if (mc == false)
  125. file_status = SGX_FILE_STATUS_WRITE_TO_DISK_FAILED; // special case, need only to repeat write_all_changes_to_disk in order to repair it
  126. //else
  127. //file_status = SGX_FILE_STATUS_WRITE_TO_DISK_FAILED_NEED_MC; // special case, need to repeat write_all_changes_to_disk AND increase the monotonic counter in order to repair it
  128. return false;
  129. }
  130. need_writing = false;
  131. /* this is causing problems when we delete and create the file rapidly
  132. we will just leave the file, and re-write it every time
  133. u_sgxprotectedfs_recovery_file_open opens it with 'w' so it is truncated
  134. if (encrypted_part_plain.size > MD_USER_DATA_SIZE)
  135. {
  136. erase_recovery_file();
  137. }
  138. */
  139. /*
  140. if (mc == true)
  141. {
  142. uint32_t mc_value;
  143. status = sgx_increment_monotonic_counter(&encrypted_part_plain.mc_uuid, &mc_value);
  144. if (status != SGX_SUCCESS)
  145. {
  146. file_status = SGX_FILE_STATUS_MC_NOT_INCREMENTED; // special case - need only to increase the MC in order to repair it
  147. last_error = status;
  148. return false;
  149. }
  150. assert(mc_value == encrypted_part_plain.mc_value);
  151. }
  152. */
  153. return true;
  154. }
  155. bool protected_fs_file::write_recovery_file()
  156. {
  157. void* recovery_file = NULL;
  158. sgx_status_t status;
  159. uint8_t result = 0;
  160. int32_t result32 = 0;
  161. status = u_sgxprotectedfs_recovery_file_open(&recovery_file, recovery_filename);
  162. if (status != SGX_SUCCESS || recovery_file == NULL)
  163. {
  164. last_error = status != SGX_SUCCESS ? status : SGX_ERROR_FILE_CANT_OPEN_RECOVERY_FILE;
  165. return false;
  166. }
  167. void* data = NULL;
  168. recovery_node_t* recovery_node = NULL;
  169. for (data = cache.get_first() ; data != NULL ; data = cache.get_next())
  170. {
  171. if (((file_data_node_t*)data)->type == FILE_DATA_NODE_TYPE) // type is in the same offset in both node types
  172. {
  173. file_data_node_t* file_data_node = (file_data_node_t*)data;
  174. if (file_data_node->need_writing == false || file_data_node->new_node == true)
  175. continue;
  176. recovery_node = &file_data_node->recovery_node;
  177. }
  178. else
  179. {
  180. file_mht_node_t* file_mht_node = (file_mht_node_t*)data;
  181. assert(file_mht_node->type == FILE_MHT_NODE_TYPE);
  182. if (file_mht_node->need_writing == false || file_mht_node->new_node == true)
  183. continue;
  184. recovery_node = &file_mht_node->recovery_node;
  185. }
  186. status = u_sgxprotectedfs_fwrite_recovery_node(&result, recovery_file, (uint8_t*)recovery_node, sizeof(recovery_node_t));
  187. if (status != SGX_SUCCESS || result != 0)
  188. {
  189. u_sgxprotectedfs_fclose(&result32, recovery_file);
  190. u_sgxprotectedfs_remove(&result32, recovery_filename);
  191. last_error = status != SGX_SUCCESS ? status : SGX_ERROR_FILE_CANT_WRITE_RECOVERY_FILE;
  192. return false;
  193. }
  194. }
  195. if (root_mht.need_writing == true && root_mht.new_node == false)
  196. {
  197. status = u_sgxprotectedfs_fwrite_recovery_node(&result, recovery_file, (uint8_t*)&root_mht.recovery_node, sizeof(recovery_node_t));
  198. if (status != SGX_SUCCESS || result != 0)
  199. {
  200. u_sgxprotectedfs_fclose(&result32, recovery_file);
  201. u_sgxprotectedfs_remove(&result32, recovery_filename);
  202. last_error = status != SGX_SUCCESS ? status : SGX_ERROR_FILE_CANT_WRITE_RECOVERY_FILE;
  203. return false;
  204. }
  205. }
  206. status = u_sgxprotectedfs_fwrite_recovery_node(&result, recovery_file, (uint8_t*)&meta_data_recovery_node, sizeof(recovery_node_t));
  207. if (status != SGX_SUCCESS || result != 0)
  208. {
  209. u_sgxprotectedfs_fclose(&result32, recovery_file);
  210. u_sgxprotectedfs_remove(&result32, recovery_filename);
  211. last_error = status != SGX_SUCCESS ? status : SGX_ERROR_FILE_CANT_WRITE_RECOVERY_FILE;
  212. return false;
  213. }
  214. u_sgxprotectedfs_fclose(&result32, recovery_file); // TODO - check result
  215. return true;
  216. }
  217. bool protected_fs_file::set_update_flag(bool flush_to_disk)
  218. {
  219. sgx_status_t status;
  220. uint8_t result;
  221. int32_t result32;
  222. file_meta_data.plain_part.update_flag = 1;
  223. status = u_sgxprotectedfs_fwrite_node(&result32, file, 0, (uint8_t*)&file_meta_data, NODE_SIZE);
  224. file_meta_data.plain_part.update_flag = 0; // turn it off in memory. at the end of the flush, when we'll write the meta-data to disk, this flag will also be cleared there.
  225. if (status != SGX_SUCCESS || result32 != 0)
  226. {
  227. last_error = (status != SGX_SUCCESS) ? status :
  228. (result32 != -1) ? result32 : EIO;
  229. return false;
  230. }
  231. if (flush_to_disk == true)
  232. {
  233. status = u_sgxprotectedfs_fflush(&result, file);
  234. if (status != SGX_SUCCESS || result != 0)
  235. {
  236. last_error = status != SGX_SUCCESS ? status : SGX_ERROR_FILE_FLUSH_FAILED;
  237. u_sgxprotectedfs_fwrite_node(&result32, file, 0, (uint8_t*)&file_meta_data, NODE_SIZE); // try to clear the update flag, in the OS cache at least...
  238. return false;
  239. }
  240. }
  241. return true;
  242. }
  243. // this function is called if we had an error after we updated the update flag
  244. // in normal flow, the flag is cleared when the meta-data is written to disk
  245. void protected_fs_file::clear_update_flag()
  246. {
  247. uint8_t result;
  248. int32_t result32;
  249. if (_RECOVERY_HOOK_(3))
  250. return;
  251. assert(file_meta_data.plain_part.update_flag == 0);
  252. u_sgxprotectedfs_fwrite_node(&result32, file, 0, (uint8_t*)&file_meta_data, NODE_SIZE);
  253. u_sgxprotectedfs_fflush(&result, file);
  254. }
  255. // sort function, we need the mht nodes sorted before we start to update their gmac's
  256. bool mht_order(const file_mht_node_t* first, const file_mht_node_t* second)
  257. {// higher (lower tree level) node number first
  258. return first->mht_node_number > second->mht_node_number;
  259. }
  260. bool protected_fs_file::update_all_data_and_mht_nodes()
  261. {
  262. std::list<file_mht_node_t*> mht_list;
  263. std::list<file_mht_node_t*>::iterator mht_list_it;
  264. file_mht_node_t* file_mht_node;
  265. sgx_status_t status;
  266. void* data = cache.get_first();
  267. // 1. encrypt the changed data
  268. // 2. set the IV+GMAC in the parent MHT
  269. // [3. set the need_writing flag for all the parents]
  270. while (data != NULL)
  271. {
  272. if (((file_data_node_t*)data)->type == FILE_DATA_NODE_TYPE) // type is in the same offset in both node types
  273. {
  274. file_data_node_t* data_node = (file_data_node_t*)data;
  275. if (data_node->need_writing == true)
  276. {
  277. if (derive_random_node_key(data_node->physical_node_number) == false)
  278. return false;
  279. gcm_crypto_data_t* gcm_crypto_data = &data_node->parent->plain.data_nodes_crypto[data_node->data_node_number % ATTACHED_DATA_NODES_COUNT];
  280. // encrypt the data, this also saves the gmac of the operation in the mht crypto node
  281. status = sgx_rijndael128GCM_encrypt(&cur_key, data_node->plain.data, NODE_SIZE, data_node->encrypted.cipher,
  282. empty_iv, SGX_AESGCM_IV_SIZE, NULL, 0, &gcm_crypto_data->gmac);
  283. if (status != SGX_SUCCESS)
  284. {
  285. last_error = status;
  286. return false;
  287. }
  288. memcpy(gcm_crypto_data->key, cur_key, sizeof(sgx_aes_gcm_128bit_key_t)); // save the key used for this encryption
  289. file_mht_node = data_node->parent;
  290. // this loop should do nothing, add it here just to be safe
  291. while (file_mht_node->mht_node_number != 0)
  292. {
  293. assert(file_mht_node->need_writing == true);
  294. file_mht_node->need_writing = true; // just in case, for release
  295. file_mht_node = file_mht_node->parent;
  296. }
  297. }
  298. }
  299. data = cache.get_next();
  300. }
  301. // add all the mht nodes that needs writing to a list
  302. data = cache.get_first();
  303. while (data != NULL)
  304. {
  305. if (((file_mht_node_t*)data)->type == FILE_MHT_NODE_TYPE) // type is in the same offset in both node types
  306. {
  307. file_mht_node = (file_mht_node_t*)data;
  308. if (file_mht_node->need_writing == true)
  309. mht_list.push_front(file_mht_node);
  310. }
  311. data = cache.get_next();
  312. }
  313. // sort the list from the last node to the first (bottom layers first)
  314. mht_list.sort(mht_order);
  315. // update the gmacs in the parents
  316. while ((mht_list_it = mht_list.begin()) != mht_list.end())
  317. {
  318. file_mht_node = *mht_list_it;
  319. gcm_crypto_data_t* gcm_crypto_data = &file_mht_node->parent->plain.mht_nodes_crypto[(file_mht_node->mht_node_number - 1) % CHILD_MHT_NODES_COUNT];
  320. if (derive_random_node_key(file_mht_node->physical_node_number) == false)
  321. {
  322. mht_list.clear();
  323. return false;
  324. }
  325. status = sgx_rijndael128GCM_encrypt(&cur_key, (const uint8_t*)&file_mht_node->plain, NODE_SIZE, file_mht_node->encrypted.cipher,
  326. empty_iv, SGX_AESGCM_IV_SIZE, NULL, 0, &gcm_crypto_data->gmac);
  327. if (status != SGX_SUCCESS)
  328. {
  329. mht_list.clear();
  330. last_error = status;
  331. return false;
  332. }
  333. memcpy(gcm_crypto_data->key, cur_key, sizeof(sgx_aes_gcm_128bit_key_t)); // save the key used for this gmac
  334. mht_list.pop_front();
  335. }
  336. // update mht root gmac in the meta data node
  337. if (derive_random_node_key(root_mht.physical_node_number) == false)
  338. return false;
  339. status = sgx_rijndael128GCM_encrypt(&cur_key, (const uint8_t*)&root_mht.plain, NODE_SIZE, root_mht.encrypted.cipher,
  340. empty_iv, SGX_AESGCM_IV_SIZE, NULL, 0, &encrypted_part_plain.mht_gmac);
  341. if (status != SGX_SUCCESS)
  342. {
  343. last_error = status;
  344. return false;
  345. }
  346. memcpy(&encrypted_part_plain.mht_key, cur_key, sizeof(sgx_aes_gcm_128bit_key_t)); // save the key used for this gmac
  347. return true;
  348. }
  349. bool protected_fs_file::update_meta_data_node()
  350. {
  351. sgx_status_t status;
  352. // randomize a new key, saves the key _id_ in the meta data plain part
  353. if (generate_random_meta_data_key() != true)
  354. {
  355. // last error already set
  356. return false;
  357. }
  358. // encrypt meta data encrypted part, also updates the gmac in the meta data plain part
  359. status = sgx_rijndael128GCM_encrypt(&cur_key,
  360. (const uint8_t*)&encrypted_part_plain, sizeof(meta_data_encrypted_t), (uint8_t*)&file_meta_data.encrypted_part,
  361. empty_iv, SGX_AESGCM_IV_SIZE,
  362. NULL, 0,
  363. &file_meta_data.plain_part.meta_data_gmac);
  364. if (status != SGX_SUCCESS)
  365. {
  366. last_error = status;
  367. return false;
  368. }
  369. return true;
  370. }
  371. bool protected_fs_file::write_all_changes_to_disk(bool flush_to_disk)
  372. {
  373. uint8_t result;
  374. int32_t result32;
  375. sgx_status_t status;
  376. if (encrypted_part_plain.size > MD_USER_DATA_SIZE && root_mht.need_writing == true)
  377. {
  378. void* data = NULL;
  379. uint8_t* data_to_write;
  380. uint64_t node_number;
  381. file_data_node_t* file_data_node;
  382. file_mht_node_t* file_mht_node;
  383. for (data = cache.get_first() ; data != NULL ; data = cache.get_next())
  384. {
  385. file_data_node = NULL;
  386. file_mht_node = NULL;
  387. if (((file_data_node_t*)data)->type == FILE_DATA_NODE_TYPE) // type is in the same offset in both node types
  388. {
  389. file_data_node = (file_data_node_t*)data;
  390. if (file_data_node->need_writing == false)
  391. continue;
  392. data_to_write = (uint8_t*)&file_data_node->encrypted;
  393. node_number = file_data_node->physical_node_number;
  394. }
  395. else
  396. {
  397. file_mht_node = (file_mht_node_t*)data;
  398. assert(file_mht_node->type == FILE_MHT_NODE_TYPE);
  399. if (file_mht_node->need_writing == false)
  400. continue;
  401. data_to_write = (uint8_t*)&file_mht_node->encrypted;
  402. node_number = file_mht_node->physical_node_number;
  403. }
  404. status = u_sgxprotectedfs_fwrite_node(&result32, file, node_number, data_to_write, NODE_SIZE);
  405. if (status != SGX_SUCCESS || result32 != 0)
  406. {
  407. last_error = (status != SGX_SUCCESS) ? status :
  408. (result32 != -1) ? result32 : EIO;
  409. return false;
  410. }
  411. // data written - clear the need_writing and the new_node flags (for future transactions, this node it no longer 'new' and should be written to recovery file)
  412. if (file_data_node != NULL)
  413. {
  414. file_data_node->need_writing = false;
  415. file_data_node->new_node = false;
  416. }
  417. else
  418. {
  419. file_mht_node->need_writing = false;
  420. file_mht_node->new_node = false;
  421. }
  422. }
  423. status = u_sgxprotectedfs_fwrite_node(&result32, file, 1, (uint8_t*)&root_mht.encrypted, NODE_SIZE);
  424. if (status != SGX_SUCCESS || result32 != 0)
  425. {
  426. last_error = (status != SGX_SUCCESS) ? status :
  427. (result32 != -1) ? result32 : EIO;
  428. return false;
  429. }
  430. root_mht.need_writing = false;
  431. root_mht.new_node = false;
  432. }
  433. status = u_sgxprotectedfs_fwrite_node(&result32, file, 0, (uint8_t*)&file_meta_data, NODE_SIZE);
  434. if (status != SGX_SUCCESS || result32 != 0)
  435. {
  436. last_error = (status != SGX_SUCCESS) ? status :
  437. (result32 != -1) ? result32 : EIO;
  438. return false;
  439. }
  440. if (flush_to_disk == true)
  441. {
  442. status = u_sgxprotectedfs_fflush(&result, file);
  443. if (status != SGX_SUCCESS || result != 0)
  444. {
  445. last_error = status != SGX_SUCCESS ? status : SGX_ERROR_FILE_FLUSH_FAILED;
  446. return false;
  447. }
  448. }
  449. return true;
  450. }
  451. void protected_fs_file::erase_recovery_file()
  452. {
  453. sgx_status_t status;
  454. int32_t result32;
  455. if (recovery_filename[0] == '\0') // not initialized yet
  456. return;
  457. status = u_sgxprotectedfs_remove(&result32, recovery_filename);
  458. (void)status; // don't care if it succeeded or failed...just remove the warning
  459. }