db_process.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. /* Copyright (C) 2014 Stony Brook 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. /*
  14. * db_process.c
  15. *
  16. * This source file contains functions to create a child process and terminate
  17. * the running process. Child does not inherit any objects or memory from its
  18. * parent pricess. A Parent process may not modify the execution of its
  19. * children. It can wait for a child to exit using its handle. Also, parent and
  20. * child may communicate through I/O streams provided by the parent to the child
  21. * at creation.
  22. */
  23. #include "pal_defs.h"
  24. #include "pal_linux_defs.h"
  25. #include "pal.h"
  26. #include "pal_internal.h"
  27. #include "pal_linux.h"
  28. #include "pal_linux_error.h"
  29. #include "pal_debug.h"
  30. #include "pal_error.h"
  31. #include "pal_security.h"
  32. #include "pal_crypto.h"
  33. #include "api.h"
  34. #include <linux/sched.h>
  35. #include <linux/types.h>
  36. #include <linux/fs.h>
  37. typedef __kernel_pid_t pid_t;
  38. #include <asm/fcntl.h>
  39. DEFINE_LIST(trusted_child);
  40. struct trusted_child {
  41. LIST_TYPE(trusted_child) list;
  42. sgx_arch_hash_t mrenclave;
  43. char uri[];
  44. };
  45. DEFINE_LISTP(trusted_child);
  46. static LISTP_TYPE(trusted_child) trusted_children = LISTP_INIT;
  47. static struct spinlock trusted_children_lock = LOCK_INIT;
  48. int register_trusted_child(const char * uri, const char * mrenclave_str)
  49. {
  50. struct trusted_child * tc = NULL, * new;
  51. int uri_len = strlen(uri);
  52. _DkSpinLock(&trusted_children_lock);
  53. LISTP_FOR_EACH_ENTRY(tc, &trusted_children, list) {
  54. if (!memcmp(tc->uri, uri, uri_len + 1)) {
  55. _DkSpinUnlock(&trusted_children_lock);
  56. return 0;
  57. }
  58. }
  59. _DkSpinUnlock(&trusted_children_lock);
  60. new = malloc(sizeof(struct trusted_child) + uri_len);
  61. if (!new)
  62. return -PAL_ERROR_NOMEM;
  63. INIT_LIST_HEAD(new, list);
  64. memcpy(new->uri, uri, uri_len + 1);
  65. char mrenclave_text[sizeof(sgx_arch_hash_t) * 2 + 1] = "\0";
  66. size_t nbytes = 0;
  67. for (; nbytes < sizeof(sgx_arch_hash_t) ; nbytes++) {
  68. char byte1 = mrenclave_str[nbytes * 2];
  69. char byte2 = mrenclave_str[nbytes * 2 + 1];
  70. unsigned char val = 0;
  71. if (byte1 == 0 || byte2 == 0) {
  72. break;
  73. }
  74. if (!(byte1 >= '0' && byte1 <= '9') &&
  75. !(byte1 >= 'a' && byte1 <= 'f')) {
  76. break;
  77. }
  78. if (!(byte2 >= '0' && byte2 <= '9') &&
  79. !(byte2 >= 'a' && byte2 <= 'f')) {
  80. break;
  81. }
  82. if (byte1 >= '0' && byte1 <= '9')
  83. val = byte1 - '0';
  84. if (byte1 >= 'a' && byte1 <= 'f')
  85. val = byte1 - 'a' + 10;
  86. val *= 16;
  87. if (byte2 >= '0' && byte2 <= '9')
  88. val += byte2 - '0';
  89. if (byte2 >= 'a' && byte2 <= 'f')
  90. val += byte2 - 'a' + 10;
  91. new->mrenclave[nbytes] = val;
  92. snprintf(mrenclave_text + nbytes * 2, 3, "%02x", val);
  93. }
  94. if (nbytes < sizeof(sgx_arch_hash_t)) {
  95. free(new);
  96. return -PAL_ERROR_INVAL;
  97. }
  98. SGX_DBG(DBG_S, "trusted: %s %s\n", mrenclave_text, new->uri);
  99. _DkSpinLock(&trusted_children_lock);
  100. LISTP_FOR_EACH_ENTRY(tc, &trusted_children, list) {
  101. if (!memcmp(tc->uri, uri, uri_len + 1)) {
  102. _DkSpinUnlock(&trusted_children_lock);
  103. free(new);
  104. return 0;
  105. }
  106. }
  107. LISTP_ADD_TAIL(new, &trusted_children, list);
  108. _DkSpinUnlock(&trusted_children_lock);
  109. return 0;
  110. }
  111. /*
  112. * For SGX, the creation of a child process requires a clean enclave and a secure channel
  113. * between the parent and child processes (enclaves). The establishment of the secure
  114. * channel must be resilient to a host-level, root-privilege adversary. Such an adversary
  115. * can either create arbitrary enclaves, or intercept the handshake protocol between the
  116. * parent and child enclaves to launch a man-in-the-middle attack.
  117. *
  118. * Prerequisites of a secure channel:
  119. * (1) A session key needs to be shared only between the parent and child enclaves.
  120. *
  121. * See the implementation in _DkStreamKeyExchange().
  122. * When initializing an RPC stream, both ends of the stream needs to use
  123. * Diffie-Hellman to exchange a session key. The key will be used to both identify
  124. * the connection (to prevent man-in-the-middle attack) and for future encryption.
  125. *
  126. * (2) Both the parent and child enclaves need to be proven by the Intel CPU.
  127. *
  128. * See the implementation in _DkStreamReportRequest() and _DkStreamReportRespond().
  129. * The two ends of the RPC stream need to exchange local attestation reports
  130. * signed by the Intel CPUs to prove themselves to be running inside enclaves
  131. * on the same platform. The local attestation reports contain no secret information
  132. * and can be verified cryptographically, and can be sent on an unencrypted channel.
  133. *
  134. * The flow of local attestation is as follows:
  135. * - Parent: Send targetinfo(Parent) to Child
  136. * - Child: Generate report(Child -> Parent) and send to Parent
  137. * - Parent: Verify report(Child -> Parent)
  138. * - Parent: Extract targetinfo(Child) from report(Child -> Parent)
  139. * and then generate report(Parent -> Child)
  140. * - Child: Verify report(Parent -> Child)
  141. *
  142. * (3) Both the parent and child enclaves need to have a white-listed measurement.
  143. *
  144. * See the implementation in check_child_mrenclave() and check_parent_mrenclave().
  145. * For a child process, we check if the child's mrenclave is listed as
  146. * "sgx.trusted_children.xxx = ..." in the manifest.
  147. * For a parent process, we currently don't check its mrenclave in the child.
  148. * This is a limitation because listing the parent's mrenclave in the child's
  149. * manifest will change the child's mrenclave, which then needs to be updated
  150. * in the parent's manifest, and eventually falls into a loop of updating both
  151. * manifest files.
  152. *
  153. * (4) The two parties who create the session key need to be the ones proven by the CPU
  154. * (for preventing man-in-the-middle attacks).
  155. *
  156. * See the implementation in check_child_mrenclave() and check_parent_mrenclave().
  157. * The local reports from both sides will contain a MAC, generated by hashing
  158. * the unique enclave ID (a 64-bit integer) using AES-CMAC with the session key.
  159. * Because both the enclave ID and the session key are randomly created for each
  160. * enclave, no report can be reused even from an enclave with the same mrenclave.
  161. */
  162. struct proc_data {
  163. sgx_arch_mac_t eid_mac;
  164. };
  165. static int generate_sign_data(const PAL_SESSION_KEY* session_key, uint64_t enclave_id,
  166. sgx_sign_data_t* sign_data) {
  167. struct proc_data data;
  168. int ret = lib_AESCMAC((uint8_t*)session_key, sizeof(*session_key),
  169. (uint8_t*)&enclave_id, sizeof(enclave_id),
  170. (uint8_t*)&data.eid_mac, sizeof(data.eid_mac));
  171. if (ret < 0)
  172. return ret;
  173. SGX_DBG(DBG_P|DBG_S, "Enclave identifier: %016lx -> %s\n", enclave_id,
  174. ALLOCA_BYTES2HEXSTR(data.eid_mac));
  175. /* Copy proc_data into sgx_sign_data_t */
  176. assert(sizeof(data) <= sizeof(*sign_data));
  177. memset(sign_data, 0, sizeof(*sign_data));
  178. memcpy(sign_data, &data, sizeof(data));
  179. return 0;
  180. }
  181. static int check_child_mrenclave(PAL_HANDLE child, sgx_arch_hash_t* mrenclave,
  182. struct pal_enclave_state* remote_state) {
  183. /* the process must be a clean process */
  184. if (remote_state->enclave_flags & PAL_ENCLAVE_INITIALIZED)
  185. return 1;
  186. sgx_sign_data_t sign_data;
  187. int ret = generate_sign_data(&child->process.session_key, remote_state->enclave_id,
  188. &sign_data);
  189. if (ret < 0)
  190. return ret;
  191. /* must make sure the signer of the report is also the owner of the key,
  192. in order to prevent man-in-the-middle attack */
  193. if (memcmp(&remote_state->enclave_data, &sign_data, sizeof(sign_data)))
  194. return 1;
  195. /* Always accept the same mrenclave as child process */
  196. if (!memcmp(mrenclave, pal_sec.mrenclave, sizeof(sgx_arch_hash_t))) {
  197. SGX_DBG(DBG_S, "trusted child: <forked>\n");
  198. return 0;
  199. }
  200. struct trusted_child * tc;
  201. _DkSpinLock(&trusted_children_lock);
  202. /* Try to find a matching mrenclave from the manifest */
  203. LISTP_FOR_EACH_ENTRY(tc, &trusted_children, list) {
  204. if (!memcmp(mrenclave, tc->mrenclave, sizeof(sgx_arch_hash_t))) {
  205. _DkSpinUnlock(&trusted_children_lock);
  206. SGX_DBG(DBG_S, "trusted child: %s\n", tc->uri);
  207. return 0;
  208. }
  209. }
  210. _DkSpinUnlock(&trusted_children_lock);
  211. return 1;
  212. }
  213. int _DkProcessCreate (PAL_HANDLE * handle, const char * uri, const char ** args)
  214. {
  215. /* only access creating process with regular file */
  216. if (!strstartswith_static(uri, "file:"))
  217. return -PAL_ERROR_INVAL;
  218. unsigned int child_pid;
  219. int proc_fds[3];
  220. int nargs = 0, ret;
  221. if (args)
  222. for (const char ** a = args ; *a ; a++)
  223. nargs++;
  224. ret = ocall_create_process(uri, nargs, args, proc_fds, &child_pid);
  225. if (ret < 0)
  226. return ret;
  227. PAL_HANDLE child = malloc(HANDLE_SIZE(process));
  228. SET_HANDLE_TYPE(child, process);
  229. HANDLE_HDR(child)->flags |= RFD(0)|WFD(1)|RFD(2)|WFD(2)|WRITABLE(1)|WRITABLE(2);
  230. child->process.stream_in = proc_fds[0];
  231. child->process.stream_out = proc_fds[1];
  232. child->process.cargo = proc_fds[2];
  233. child->process.pid = child_pid;
  234. child->process.nonblocking = PAL_FALSE;
  235. ret = _DkStreamKeyExchange(child, &child->process.session_key);
  236. if (ret < 0)
  237. goto failed;
  238. sgx_sign_data_t sign_data;
  239. ret = generate_sign_data(&child->process.session_key, pal_enclave_state.enclave_id,
  240. &sign_data);
  241. if (ret < 0)
  242. goto failed;
  243. ret = _DkStreamReportRequest(child, &sign_data, &check_child_mrenclave);
  244. if (ret < 0)
  245. goto failed;
  246. *handle = child;
  247. return 0;
  248. failed:
  249. free(child);
  250. return ret;
  251. }
  252. static int check_parent_mrenclave(PAL_HANDLE parent, sgx_arch_hash_t* mrenclave,
  253. struct pal_enclave_state* remote_state) {
  254. __UNUSED(mrenclave);
  255. sgx_sign_data_t sign_data;
  256. int ret = generate_sign_data(&parent->process.session_key, remote_state->enclave_id,
  257. &sign_data);
  258. if (ret < 0)
  259. return ret;
  260. if (memcmp(&remote_state->enclave_data, &sign_data, sizeof(sign_data)))
  261. return 1;
  262. /* XXX: For now, accept any enclave, but eventually should challenge the parent process */
  263. return 0;
  264. }
  265. int init_child_process (PAL_HANDLE * parent_handle)
  266. {
  267. PAL_HANDLE parent = malloc(HANDLE_SIZE(process));
  268. SET_HANDLE_TYPE(parent, process);
  269. HANDLE_HDR(parent)->flags |= RFD(0)|WFD(1)|RFD(2)|WFD(2)|WRITABLE(1)|WRITABLE(2);
  270. parent->process.stream_in = pal_sec.proc_fds[0];
  271. parent->process.stream_out = pal_sec.proc_fds[1];
  272. parent->process.cargo = pal_sec.proc_fds[2];
  273. parent->process.pid = pal_sec.ppid;
  274. parent->process.nonblocking = PAL_FALSE;
  275. int ret = _DkStreamKeyExchange(parent, &parent->process.session_key);
  276. if (ret < 0)
  277. return ret;
  278. sgx_sign_data_t sign_data;
  279. ret = generate_sign_data(&parent->process.session_key, pal_enclave_state.enclave_id,
  280. &sign_data);
  281. if (ret < 0)
  282. return ret;
  283. ret = _DkStreamReportRespond(parent, &sign_data, &check_parent_mrenclave);
  284. if (ret < 0)
  285. return ret;
  286. *parent_handle = parent;
  287. return 0;
  288. }
  289. void print_alloced_pages (void);
  290. noreturn void _DkProcessExit (int exitcode)
  291. {
  292. #if PRINT_ENCLAVE_STAT
  293. print_alloced_pages();
  294. #endif
  295. if (exitcode)
  296. SGX_DBG(DBG_I, "DkProcessExit: Returning exit code %d\n", exitcode);
  297. ocall_exit(exitcode, /*is_exitgroup=*/true);
  298. while (true) {
  299. /* nothing */;
  300. }
  301. }
  302. static int64_t proc_read (PAL_HANDLE handle, uint64_t offset, uint64_t count,
  303. void * buffer)
  304. {
  305. if (offset)
  306. return -PAL_ERROR_INVAL;
  307. if (count >= (1ULL << (sizeof(unsigned int) * 8)))
  308. return -PAL_ERROR_INVAL;
  309. int bytes = ocall_read(handle->process.stream_in, buffer, count);
  310. return IS_ERR(bytes) ? unix_to_pal_error(ERRNO(bytes)) : bytes;
  311. }
  312. static int64_t proc_write (PAL_HANDLE handle, uint64_t offset, uint64_t count,
  313. const void * buffer)
  314. {
  315. if (offset)
  316. return -PAL_ERROR_INVAL;
  317. if (count >= (1ULL << (sizeof(unsigned int) * 8)))
  318. return -PAL_ERROR_INVAL;
  319. int bytes = ocall_write(handle->process.stream_out, buffer, count);
  320. if (IS_ERR(bytes)) {
  321. bytes = unix_to_pal_error(ERRNO(bytes));
  322. if (bytes == -PAL_ERROR_TRYAGAIN)
  323. HANDLE_HDR(handle)->flags &= ~WRITABLE(1);
  324. return bytes;
  325. }
  326. if ((uint64_t)bytes == count)
  327. HANDLE_HDR(handle)->flags |= WRITABLE(1);
  328. else
  329. HANDLE_HDR(handle)->flags &= ~WRITABLE(1);
  330. return bytes;
  331. }
  332. static int proc_close (PAL_HANDLE handle)
  333. {
  334. if (handle->process.stream_in != PAL_IDX_POISON) {
  335. ocall_close(handle->process.stream_in);
  336. handle->process.stream_in = PAL_IDX_POISON;
  337. }
  338. if (handle->process.stream_out != PAL_IDX_POISON) {
  339. ocall_close(handle->process.stream_out);
  340. handle->process.stream_out = PAL_IDX_POISON;
  341. }
  342. if (handle->process.cargo != PAL_IDX_POISON) {
  343. ocall_close(handle->process.cargo);
  344. handle->process.cargo = PAL_IDX_POISON;
  345. }
  346. return 0;
  347. }
  348. static int proc_delete (PAL_HANDLE handle, int access)
  349. {
  350. int shutdown;
  351. switch (access) {
  352. case 0:
  353. shutdown = SHUT_RDWR;
  354. break;
  355. case PAL_DELETE_RD:
  356. shutdown = SHUT_RD;
  357. break;
  358. case PAL_DELETE_WR:
  359. shutdown = SHUT_WR;
  360. break;
  361. default:
  362. return -PAL_ERROR_INVAL;
  363. }
  364. if (access != PAL_DELETE_WR &&
  365. handle->process.stream_in != PAL_IDX_POISON) {
  366. ocall_close(handle->process.stream_in);
  367. handle->process.stream_in = PAL_IDX_POISON;
  368. }
  369. if (access != PAL_DELETE_RD &&
  370. handle->process.stream_out != PAL_IDX_POISON) {
  371. ocall_close(handle->process.stream_out);
  372. handle->process.stream_out = PAL_IDX_POISON;
  373. }
  374. if (handle->process.cargo != PAL_IDX_POISON)
  375. ocall_sock_shutdown(handle->process.cargo, shutdown);
  376. return 0;
  377. }
  378. static int proc_attrquerybyhdl (PAL_HANDLE handle, PAL_STREAM_ATTR * attr)
  379. {
  380. if (handle->process.stream_in == PAL_IDX_POISON)
  381. return -PAL_ERROR_BADHANDLE;
  382. int ret = ocall_fionread(handle->process.stream_in);
  383. if (IS_ERR(ret))
  384. return unix_to_pal_error(ERRNO(ret));
  385. memset(attr, 0, sizeof(PAL_STREAM_ATTR));
  386. attr->pending_size = ret;
  387. attr->disconnected = HANDLE_HDR(handle)->flags & (ERROR(0)|ERROR(1));
  388. attr->readable = (attr->pending_size > 0);
  389. attr->writable = HANDLE_HDR(handle)->flags & WRITABLE(1);
  390. attr->nonblocking = handle->process.nonblocking;
  391. return 0;
  392. }
  393. static int proc_attrsetbyhdl (PAL_HANDLE handle, PAL_STREAM_ATTR * attr)
  394. {
  395. if (handle->process.stream_in == PAL_IDX_POISON)
  396. return -PAL_ERROR_BADHANDLE;
  397. if (attr->nonblocking != handle->process.nonblocking) {
  398. int ret = ocall_fsetnonblock(handle->process.stream_in,
  399. handle->process.nonblocking);
  400. if (IS_ERR(ret))
  401. return unix_to_pal_error(ERRNO(ret));
  402. handle->process.nonblocking = attr->nonblocking;
  403. }
  404. return 0;
  405. }
  406. struct handle_ops proc_ops = {
  407. .read = &proc_read,
  408. .write = &proc_write,
  409. .close = &proc_close,
  410. .delete = &proc_delete,
  411. .attrquerybyhdl = &proc_attrquerybyhdl,
  412. .attrsetbyhdl = &proc_attrsetbyhdl,
  413. };