sgx_platform.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /* Copyright (C) 2019, Texas A&M University.
  2. This file is part of Graphene Library OS.
  3. Graphene Library OS is free software: you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public License
  5. as published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. Graphene Library OS is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. #include <pal_linux.h>
  14. #include <pal_rtld.h>
  15. #include <pal_crypto.h>
  16. #include <hex.h>
  17. #include "sgx_internal.h"
  18. #include "sgx_arch.h"
  19. #include "sgx_enclave.h"
  20. #include "sgx_attest.h"
  21. #include "quote/aesm.pb-c.h"
  22. #include <asm/errno.h>
  23. #include <linux/fs.h>
  24. #include <linux/un.h>
  25. #define __USE_XOPEN2K8
  26. #include <stdlib.h>
  27. /*
  28. * Connect to the AESM service to interact with the architectural enclave. Must reconnect
  29. * for each request to the AESM service.
  30. *
  31. * Some older versions of AESM service use a UNIX socket at "\0sgx_aesm_socket_base".
  32. * The latest AESM service binds the socket at "/var/run/aesmd/aesm.socket". This function
  33. * tries to connect to either of the paths to ensure connectivity.
  34. */
  35. static int connect_aesm_service(void) {
  36. int sock = INLINE_SYSCALL(socket, 3, AF_UNIX, SOCK_STREAM, 0);
  37. if (IS_ERR(sock))
  38. return -ERRNO(sock);
  39. struct sockaddr_un addr;
  40. memset(&addr, 0, sizeof(addr));
  41. addr.sun_family = AF_UNIX;
  42. (void)strcpy_static(addr.sun_path, "\0sgx_aesm_socket_base", sizeof(addr.sun_path));
  43. int ret = INLINE_SYSCALL(connect, 3, sock, &addr, sizeof(addr));
  44. if (!IS_ERR(ret))
  45. return sock;
  46. if (ERRNO(ret) != ECONNREFUSED)
  47. goto err;
  48. memset(&addr, 0, sizeof(addr));
  49. addr.sun_family = AF_UNIX;
  50. (void)strcpy_static(addr.sun_path, "/var/run/aesmd/aesm.socket", sizeof(addr.sun_path));
  51. ret = INLINE_SYSCALL(connect, 3, sock, &addr, sizeof(addr));
  52. if (!IS_ERR(ret))
  53. return sock;
  54. err:
  55. INLINE_SYSCALL(close, 1, sock);
  56. return -ERRNO(ret);
  57. }
  58. /*
  59. * A wrapper for both creating a connection to the AESM service and submitting a request
  60. * to the service. Upon success, the function returns a response from the AESM service
  61. * back to the caller.
  62. */
  63. static int request_aesm_service(Request* req, Response** res) {
  64. int aesm_socket = connect_aesm_service();
  65. if (aesm_socket < 0)
  66. return aesm_socket;
  67. uint32_t req_len = (uint32_t) request__get_packed_size(req);
  68. uint8_t* req_buf = __alloca(req_len);
  69. request__pack(req, req_buf);
  70. int ret = INLINE_SYSCALL(write, 3, aesm_socket, &req_len, sizeof(req_len));
  71. if (IS_ERR(ret))
  72. goto err;
  73. ret = INLINE_SYSCALL(write, 3, aesm_socket, req_buf, req_len);
  74. if (IS_ERR(ret))
  75. goto err;
  76. uint32_t res_len;
  77. ret = INLINE_SYSCALL(read, 3, aesm_socket, &res_len, sizeof(res_len));
  78. if (IS_ERR(ret))
  79. goto err;
  80. uint8_t* res_buf = __alloca(res_len);
  81. ret = INLINE_SYSCALL(read, 3, aesm_socket, res_buf, res_len);
  82. if (IS_ERR(ret))
  83. goto err;
  84. *res = response__unpack(NULL, res_len, res_buf);
  85. ret = *res == NULL ? -EINVAL : 0;
  86. err:
  87. INLINE_SYSCALL(close, 1, aesm_socket);
  88. return -ERRNO(ret);
  89. }
  90. // Retrieve the targetinfo for the AESM enclave for generating the local attestation report.
  91. int init_aesm_targetinfo(sgx_target_info_t* aesm_targetinfo) {
  92. Request req = REQUEST__INIT;
  93. Request__InitQuoteRequest initreq = REQUEST__INIT_QUOTE_REQUEST__INIT;
  94. req.initquotereq = &initreq;
  95. Response* res = NULL;
  96. int ret = request_aesm_service(&req, &res);
  97. if (ret < 0)
  98. return ret;
  99. ret = -EPERM;
  100. if (!res->initquoteres) {
  101. SGX_DBG(DBG_E, "aesm_service returned wrong message\n");
  102. goto failed;
  103. }
  104. Response__InitQuoteResponse* r = res->initquoteres;
  105. if (r->errorcode != 0) {
  106. SGX_DBG(DBG_E, "aesm_service returned error: %d\n", r->errorcode);
  107. goto failed;
  108. }
  109. if (r->targetinfo.len != sizeof(*aesm_targetinfo)) {
  110. SGX_DBG(DBG_E, "aesm_service returned invalid target info\n");
  111. goto failed;
  112. }
  113. memcpy(aesm_targetinfo, r->targetinfo.data, sizeof(*aesm_targetinfo));
  114. ret = 0;
  115. failed:
  116. response__free_unpacked(res, NULL);
  117. return ret;
  118. }
  119. /*
  120. * Contact to Intel Attestation Service and retrieve the signed attestation report
  121. *
  122. * @subkey: SPID subscription key.
  123. * @nonce: Random nonce generated in the enclave.
  124. * @quote: Platform quote retrieved from AESMD.
  125. * @attestation: Attestation data to be returned to the enclave.
  126. */
  127. int contact_intel_attest_service(const char* subkey, const sgx_quote_nonce_t* nonce,
  128. const sgx_quote_t* quote, sgx_attestation_t* attestation) {
  129. size_t quote_len = sizeof(sgx_quote_t) + quote->sig_len;
  130. size_t quote_str_len;
  131. lib_Base64Encode((uint8_t*)quote, quote_len, NULL, &quote_str_len);
  132. char* quote_str = __alloca(quote_str_len);
  133. int ret = lib_Base64Encode((uint8_t*)quote, quote_len, quote_str, &quote_str_len);
  134. if (ret < 0)
  135. return ret;
  136. size_t nonce_str_len = sizeof(sgx_quote_nonce_t) * 2 + 1;
  137. char* nonce_str = __alloca(nonce_str_len);
  138. __bytes2hexstr((void *)nonce, sizeof(sgx_quote_nonce_t), nonce_str, nonce_str_len);
  139. // Create two temporary files for dumping the header and output of HTTPS request to IAS
  140. char https_header_path[] = "/tmp/gsgx-ias-header-XXXXXX";
  141. char https_output_path[] = "/tmp/gsgx-ias-output-XXXXXX";
  142. char* https_header = NULL;
  143. char* https_output = NULL;
  144. ssize_t https_header_len = 0;
  145. ssize_t https_output_len = 0;
  146. int header_fd = -1;
  147. int output_fd = -1;
  148. int fds[2] = {-1, -1};
  149. header_fd = mkstemp(https_header_path);
  150. if (header_fd < 0)
  151. goto failed;
  152. output_fd = mkstemp(https_output_path);
  153. if (output_fd < 0)
  154. goto failed;
  155. ret = INLINE_SYSCALL(socketpair, 4, AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &fds[0]);
  156. if (IS_ERR(ret))
  157. goto failed;
  158. // Write HTTPS request in XML format
  159. size_t https_request_len = quote_str_len + nonce_str_len + HTTPS_REQUEST_MAX_LENGTH;
  160. char* https_request = __alloca(https_request_len);
  161. https_request_len = snprintf(https_request, https_request_len,
  162. "{\"isvEnclaveQuote\":\"%s\",\"nonce\":\"%s\"}",
  163. quote_str, nonce_str);
  164. ret = INLINE_SYSCALL(write, 3, fds[1], https_request, https_request_len);
  165. if (IS_ERR(ret))
  166. goto failed;
  167. INLINE_SYSCALL(close, 1, fds[1]);
  168. fds[1] = -1;
  169. char subscription_header[64];
  170. snprintf(subscription_header, 64, "Ocp-Apim-Subscription-Key: %s", subkey);
  171. // Start a HTTPS client (using CURL)
  172. const char* https_client_args[] = {
  173. "/usr/bin/curl", "-s", "--tlsv1.2", "-X", "POST",
  174. "-H", "Content-Type: application/json",
  175. "-H", subscription_header,
  176. "--data", "@-", "-o", https_output_path, "-D", https_header_path,
  177. IAS_REPORT_URL, NULL,
  178. };
  179. int pid = ARCH_VFORK();
  180. if (IS_ERR(pid))
  181. goto failed;
  182. if (!pid) {
  183. INLINE_SYSCALL(dup2, 2, fds[0], 0);
  184. extern char** environ;
  185. INLINE_SYSCALL(execve, 3, https_client_args[0], https_client_args, environ);
  186. /* shouldn't get to here */
  187. SGX_DBG(DBG_E, "unexpected failure of new process\n");
  188. __asm__ volatile ("hlt");
  189. return 0;
  190. }
  191. // Make sure the HTTPS client has exited properly
  192. int status;
  193. ret = INLINE_SYSCALL(wait4, 4, pid, &status, 0, NULL);
  194. if (IS_ERR(ret) || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
  195. goto failed;
  196. // Read the HTTPS output
  197. ret = INLINE_SYSCALL(open, 2, https_output_path, O_RDONLY);
  198. if (IS_ERR(ret))
  199. goto failed;
  200. INLINE_SYSCALL(close, 1, output_fd);
  201. output_fd = ret;
  202. https_output_len = INLINE_SYSCALL(lseek, 3, output_fd, 0, SEEK_END);
  203. if (IS_ERR(https_output_len) || !https_output_len)
  204. goto failed;
  205. https_output = (char*)INLINE_SYSCALL(mmap, 6, NULL, ALLOC_ALIGN_UP(https_output_len),
  206. PROT_READ, MAP_PRIVATE|MAP_FILE, output_fd, 0);
  207. if (IS_ERR_P(https_output))
  208. goto failed;
  209. // Read the HTTPS headers
  210. ret = INLINE_SYSCALL(open, 2, https_header_path, O_RDONLY);
  211. if (IS_ERR(ret))
  212. goto failed;
  213. INLINE_SYSCALL(close, 1, header_fd);
  214. header_fd = ret;
  215. https_header_len = INLINE_SYSCALL(lseek, 3, header_fd, 0, SEEK_END);
  216. if (IS_ERR(https_header_len) || !https_header_len)
  217. goto failed;
  218. https_header = (char*)INLINE_SYSCALL(mmap, 6, NULL, ALLOC_ALIGN_UP(https_header_len),
  219. PROT_READ, MAP_PRIVATE|MAP_FILE, header_fd, 0);
  220. if (IS_ERR_P(https_header))
  221. goto failed;
  222. // Parse the HTTPS headers
  223. size_t ias_sig_len = 0;
  224. uint8_t* ias_sig = NULL;
  225. size_t ias_certs_len = 0;
  226. char* ias_certs = NULL;
  227. char* start = https_header;
  228. char* end = strchr(https_header, '\n');
  229. while (end) {
  230. char* next_start = end + 1;
  231. // If the eol (\n) is preceded by a return (\r), move the end pointer.
  232. if (end > start + 1 && *(end - 1) == '\r')
  233. end--;
  234. if (strstartswith_static(start, "X-IASReport-Signature: ")) {
  235. start += static_strlen("X-IASReport-Signature: ");
  236. // Decode IAS report signature
  237. ret = lib_Base64Decode(start, end - start, NULL, &ias_sig_len);
  238. if (ret < 0) {
  239. SGX_DBG(DBG_E, "Malformed IAS signature\n");
  240. goto failed;
  241. }
  242. ias_sig = (uint8_t*)INLINE_SYSCALL(mmap, 6, NULL, ALLOC_ALIGN_UP(ias_sig_len),
  243. PROT_READ|PROT_WRITE,
  244. MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  245. if (IS_ERR_P(ias_sig)) {
  246. SGX_DBG(DBG_E, "Cannot allocate memory for IAS report signature\n");
  247. goto failed;
  248. }
  249. ret = lib_Base64Decode(start, end - start, ias_sig, &ias_sig_len);
  250. if (ret < 0) {
  251. SGX_DBG(DBG_E, "Malformed IAS report signature\n");
  252. goto failed;
  253. }
  254. } else if (strstartswith_static(start, "X-IASReport-Signing-Certificate: ")) {
  255. start += static_strlen("X-IASReport-Signing-Certificate: ");
  256. // Decode IAS signature chain
  257. ias_certs_len = end - start;
  258. ias_certs = (char*)INLINE_SYSCALL(mmap, 6, NULL, ALLOC_ALIGN_UP(ias_certs_len),
  259. PROT_READ|PROT_WRITE,
  260. MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  261. if (IS_ERR_P(ias_certs)) {
  262. SGX_DBG(DBG_E, "Cannot allocate memory for IAS certificate chain\n");
  263. goto failed;
  264. }
  265. /*
  266. * The value of x-iasreport-signing-certificate is a certificate chain which
  267. * consists of multiple certificates represented in the PEM format. The value
  268. * is escaped using the % character. For example, a %20 in the certificate
  269. * needs to be replaced as the newline ("\n"). The following logic iteratively
  270. * reads the character and converts the escaped characters into the buffer.
  271. */
  272. size_t total_bytes = 0;
  273. // Convert escaped characters
  274. for (size_t i = 0; i < ias_certs_len; i++) {
  275. if (start[i] == '%') {
  276. int8_t hex1 = hex2dec(start[i + 1]), hex2 = hex2dec(start[i + 2]);
  277. if (hex1 < 0 || hex2 < 0)
  278. goto failed;
  279. char c = hex1 * 16 + hex2;
  280. if (c != '\n') ias_certs[total_bytes++] = c;
  281. i += 2;
  282. } else {
  283. ias_certs[total_bytes++] = start[i];
  284. }
  285. }
  286. // Adjust certificate chain length
  287. ias_certs[total_bytes++] = '\0';
  288. if (ALLOC_ALIGN_UP(total_bytes) < ALLOC_ALIGN_UP(ias_certs_len))
  289. INLINE_SYSCALL(munmap, 2, ALLOC_ALIGN_UP(total_bytes),
  290. ALLOC_ALIGN_UP(ias_certs_len) - ALLOC_ALIGN_UP(total_bytes));
  291. ias_certs_len = total_bytes;
  292. }
  293. start = next_start;
  294. end = strchr(start, '\n');
  295. }
  296. if (!ias_sig) {
  297. SGX_DBG(DBG_E, "IAS returned invalid headers: no report signature\n");
  298. goto failed;
  299. }
  300. if (!ias_certs) {
  301. SGX_DBG(DBG_E, "IAS returned invalid headers: no certificate chain\n");
  302. goto failed;
  303. }
  304. // Now return the attestation data, including the IAS response, signature, and the
  305. // certificate chain back to the caller.
  306. attestation->ias_report = https_output;
  307. attestation->ias_report_len = https_output_len;
  308. attestation->ias_sig = ias_sig;
  309. attestation->ias_sig_len = ias_sig_len;
  310. attestation->ias_certs = ias_certs;
  311. attestation->ias_certs_len = ias_certs_len;
  312. https_output = NULL; // Don't free the HTTPS output
  313. ret = 0;
  314. done:
  315. if (https_header)
  316. INLINE_SYSCALL(munmap, 2, https_header, ALLOC_ALIGN_UP(https_header_len));
  317. if (https_output)
  318. INLINE_SYSCALL(munmap, 2, https_output, ALLOC_ALIGN_UP(https_output_len));
  319. if (fds[0] != -1)
  320. INLINE_SYSCALL(close, 1, fds[0]);
  321. if (fds[1] != -1)
  322. INLINE_SYSCALL(close, 1, fds[1]);
  323. if (header_fd != -1) {
  324. INLINE_SYSCALL(close, 1, header_fd);
  325. INLINE_SYSCALL(unlink, 1, https_header_path);
  326. }
  327. if (output_fd != -1) {
  328. INLINE_SYSCALL(close, 1, output_fd);
  329. INLINE_SYSCALL(unlink, 1, https_output_path);
  330. }
  331. return ret;
  332. failed:
  333. ret = -PAL_ERROR_DENIED;
  334. goto done;
  335. }
  336. /*
  337. * This wrapper function performs the whole attestation procedure outside the enclave (except
  338. * retrieving the local remote attestation and verification). The function first contacts
  339. * the AESM service to retrieve a quote of the platform and the report of the quoting enclave.
  340. * Then, the function submits the quote to the IAS through a HTTPS client (CURL) to exchange
  341. * for a remote attestation report signed by a Intel-approved certificate chain. Finally, the
  342. * function returns the QE report, the quote, and the response from the IAS back to the enclave
  343. * for verification.
  344. *
  345. * @spid: The client SPID registered with IAS.
  346. * @subkey: SPID subscription key.
  347. * @linkable: A boolean that represents whether the SPID is linkable.
  348. * @report: The local report of the target enclave.
  349. * @nonce: A 16-byte nonce randomly generated inside the enclave.
  350. * @attestation: A structure for storing the response from the AESM service and the IAS.
  351. */
  352. int retrieve_verified_quote(const sgx_spid_t* spid, const char* subkey, bool linkable,
  353. const sgx_report_t* report, const sgx_quote_nonce_t* nonce,
  354. sgx_attestation_t* attestation) {
  355. int ret = connect_aesm_service();
  356. if (ret < 0)
  357. return ret;
  358. Request req = REQUEST__INIT;
  359. Request__GetQuoteRequest getreq = REQUEST__GET_QUOTE_REQUEST__INIT;
  360. getreq.report.data = (uint8_t*) report;
  361. getreq.report.len = SGX_REPORT_ACTUAL_SIZE;
  362. getreq.quote_type = linkable ? SGX_LINKABLE_SIGNATURE : SGX_UNLINKABLE_SIGNATURE;
  363. getreq.spid.data = (uint8_t*) spid;
  364. getreq.spid.len = sizeof(*spid);
  365. getreq.has_nonce = true;
  366. getreq.nonce.data = (uint8_t*) nonce;
  367. getreq.nonce.len = sizeof(*nonce);
  368. getreq.buf_size = SGX_QUOTE_MAX_SIZE;
  369. getreq.has_qe_report = true;
  370. getreq.qe_report = true;
  371. req.getquotereq = &getreq;
  372. Response* res = NULL;
  373. ret = request_aesm_service(&req, &res);
  374. if (ret < 0)
  375. return ret;
  376. if (!res->getquoteres) {
  377. SGX_DBG(DBG_E, "aesm_service returned wrong message\n");
  378. goto failed;
  379. }
  380. Response__GetQuoteResponse* r = res->getquoteres;
  381. if (r->errorcode != 0) {
  382. SGX_DBG(DBG_E, "aesm_service returned error: %d\n", r->errorcode);
  383. goto failed;
  384. }
  385. if (!r->has_quote || r->quote.len < sizeof(sgx_quote_t) ||
  386. !r->has_qe_report || r->qe_report.len != SGX_REPORT_ACTUAL_SIZE) {
  387. SGX_DBG(DBG_E, "aesm_service returned invalid quote or report\n");
  388. goto failed;
  389. }
  390. sgx_quote_t* quote = (sgx_quote_t*) INLINE_SYSCALL(mmap, 6, NULL, ALLOC_ALIGN_UP(r->quote.len),
  391. PROT_READ|PROT_WRITE,
  392. MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
  393. if (IS_ERR_P(quote)) {
  394. SGX_DBG(DBG_E, "Failed to allocate memory for the quote\n");
  395. goto failed;
  396. }
  397. memcpy(quote, r->quote.data, r->quote.len);
  398. attestation->quote = quote;
  399. attestation->quote_len = r->quote.len;
  400. ret = contact_intel_attest_service(subkey, nonce, (sgx_quote_t *) quote, attestation);
  401. if (ret < 0) {
  402. INLINE_SYSCALL(munmap, 2, quote, ALLOC_ALIGN_UP(r->quote.len));
  403. goto failed;
  404. }
  405. memcpy(&attestation->qe_report, r->qe_report.data, sizeof(sgx_report_t));
  406. response__free_unpacked(res, NULL);
  407. return 0;
  408. failed:
  409. response__free_unpacked(res, NULL);
  410. return -PAL_ERROR_DENIED;
  411. }