main.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. /* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
  2. /* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
  3. #define _GNU_SOURCE 1
  4. #ifndef __GNUC__
  5. #define __GNUC__ 1
  6. #endif
  7. #include <linux/unistd.h>
  8. #include <asm/mman.h>
  9. #include <stdint.h>
  10. #include <stddef.h>
  11. #include <fcntl.h>
  12. #include <elf/elf.h>
  13. #include <sysdeps/generic/ldsodefs.h>
  14. #include <asm-errno.h>
  15. #include <sys/socket.h>
  16. #include <netinet/in.h>
  17. #include "pal_security.h"
  18. #include "utils.h"
  19. struct pal_sec_info * pal_sec_info_addr = NULL;
  20. unsigned long pagesize = 4096;
  21. unsigned long pageshift = 4095;
  22. unsigned long pagemask = ~4095;
  23. #if __WORDSIZE == 2
  24. # define FILEBUF_SIZE 512
  25. #else
  26. # define FILEBUF_SIZE 832
  27. #endif
  28. char libname[PATH_MAX];
  29. const char * execname;
  30. int find_manifest (int * pargc, const char *** pargv)
  31. {
  32. int argc = *pargc;
  33. const char ** argv = *pargv, * name = *argv;
  34. if (!argc)
  35. return -EINVAL;
  36. int fd = INLINE_SYSCALL(open, 2, name, O_RDONLY|O_CLOEXEC);
  37. if (IS_ERR(fd))
  38. return -ERRNO(fd);
  39. char filebuf[4];
  40. INLINE_SYSCALL(read, 3, fd, filebuf, 2);
  41. /* check if the first argument is a manifest, in case it is
  42. a runnable script. */
  43. if (!memcmp(filebuf, "#!", 2)) {
  44. char * path = __alloca(80);
  45. if (!path)
  46. return -ENOMEM;
  47. int bytes = INLINE_SYSCALL(read, 3, fd, path, 80);
  48. for (int i = 0 ; i < bytes ; i++)
  49. if (path[i] == ' ' || path[i] == '\n') {
  50. path[i] = 0;
  51. bytes = i;
  52. break;
  53. }
  54. memcpy(libname, path, bytes + 1);
  55. goto opened;
  56. }
  57. INLINE_SYSCALL(close, 1, fd);
  58. memcpy(libname, name, strlen(name) + 1);
  59. argc--;
  60. argv++;
  61. name = *argv;
  62. if (!argc)
  63. return -EINVAL;
  64. fd = INLINE_SYSCALL(open, 2, name, O_RDONLY|O_CLOEXEC);
  65. if (IS_ERR(fd))
  66. return -ERRNO(fd);
  67. /* check if the first argument is a executable. If it is, try finding
  68. all the possible manifest files */
  69. INLINE_SYSCALL(read, 3, fd, filebuf, 4);
  70. if (!memcmp(filebuf, "\177ELF", 4)) {
  71. int len = strlen(name);
  72. char * execpath = malloc(len + 1);
  73. fast_strcpy(execpath, name, len);
  74. execname = execpath;
  75. char * filename = __alloca(len + 10);
  76. fast_strcpy(filename, name, len);
  77. fast_strcpy(filename + len, ".manifest", 9);
  78. fd = INLINE_SYSCALL(open, 2, filename, O_RDONLY|O_CLOEXEC);
  79. if (!IS_ERR(fd))
  80. goto opened;
  81. fd = INLINE_SYSCALL(open, 2, "manifest", O_RDONLY|O_CLOEXEC);
  82. if (!IS_ERR(fd))
  83. goto opened;
  84. return -ENOENT;
  85. }
  86. opened:
  87. *pargc = argc;
  88. *pargv = argv;
  89. return fd;
  90. }
  91. int load_manifest (int fd, struct config_store * config)
  92. {
  93. int nbytes = INLINE_SYSCALL(lseek, 3, fd, 0, SEEK_END);
  94. if (IS_ERR(nbytes))
  95. return -ERRNO(nbytes);
  96. void * config_raw = (void *)
  97. INLINE_SYSCALL(mmap, 6, NULL, nbytes,
  98. PROT_READ|PROT_WRITE, MAP_PRIVATE,
  99. fd, 0);
  100. if (IS_ERR_P(config_raw))
  101. return -ERRNO_P(config_raw);
  102. config->raw_data = config_raw;
  103. config->raw_size = nbytes;
  104. config->malloc = malloc;
  105. config->free = NULL;
  106. const char * errstring = NULL;
  107. int ret = read_config(config, NULL, &errstring);
  108. if (ret < 0) {
  109. printf("can't read manifest: %s\n", errstring);
  110. return ret;
  111. }
  112. return 0;
  113. }
  114. static int do_relocate (ElfW(Dyn) * dyn, ElfW(Addr) addr)
  115. {
  116. ElfW(Dyn) * dt_rela = NULL;
  117. ElfW(Dyn) * dt_relacount = NULL;
  118. for ( ; dyn->d_tag != DT_NULL ; dyn++)
  119. switch (dyn->d_tag) {
  120. case DT_RELA: dt_rela = dyn; break;
  121. case DT_RELACOUNT: dt_relacount = dyn; break;
  122. }
  123. if (!dt_rela || !dt_relacount)
  124. return -EINVAL;
  125. ElfW(Rela) * r = (void *) (addr + dt_rela->d_un.d_ptr);
  126. ElfW(Rela) * end = r + dt_relacount->d_un.d_val;
  127. for ( ; r < end ; r++)
  128. *(ElfW(Addr) *) (addr + r->r_offset) = addr + r->r_addend;
  129. return 0;
  130. }
  131. static void get_pal_sec_info (const ElfW(Dyn) * dyn, ElfW(Addr) addr)
  132. {
  133. const ElfW(Dyn) * dt_symtab = NULL;
  134. const ElfW(Dyn) * dt_strtab = NULL;
  135. const ElfW(Dyn) * dt_rela = NULL;
  136. const ElfW(Dyn) * dt_relasz = NULL;
  137. const ElfW(Dyn) * dt_relacount = NULL;
  138. for ( ; dyn->d_tag != DT_NULL ; dyn++)
  139. switch (dyn->d_tag) {
  140. case DT_SYMTAB: dt_symtab = dyn; break;
  141. case DT_STRTAB: dt_strtab = dyn; break;
  142. case DT_RELA: dt_rela = dyn; break;
  143. case DT_RELASZ: dt_relasz = dyn; break;
  144. case DT_RELACOUNT: dt_relacount = dyn; break;
  145. }
  146. if (!dt_symtab || !dt_strtab || !dt_rela || !dt_relasz || !dt_relacount)
  147. return;
  148. ElfW(Sym) * symtab = (void *) (addr + dt_symtab->d_un.d_ptr);
  149. const char * strtab = (void *) (addr + dt_strtab->d_un.d_ptr);
  150. ElfW(Rela) * r = (void *) (addr + dt_rela->d_un.d_ptr);
  151. ElfW(Rela) * rel = r + dt_relacount->d_un.d_val;
  152. ElfW(Rela) * end = r + dt_relasz->d_un.d_val / sizeof(ElfW(Rela));
  153. for (r = rel ; r < end ; r++) {
  154. ElfW(Sym) * sym = &symtab[ELFW(R_SYM) (r->r_info)];
  155. if (!sym->st_name)
  156. continue;
  157. const char * name = strtab + sym->st_name;
  158. if (!memcmp(name, "pal_sec_info", 13))
  159. pal_sec_info_addr = (void *) addr + sym->st_value;
  160. }
  161. }
  162. static int load_static (const char * filename,
  163. unsigned long * entry, unsigned long * load_addr,
  164. unsigned long * text_start, unsigned long * text_end,
  165. unsigned long * phoff, int * phnum)
  166. {
  167. int ret = 0;
  168. int fd = INLINE_SYSCALL(open, 2, filename, O_RDONLY|O_CLOEXEC);
  169. if (IS_ERR(fd))
  170. return -ERRNO(fd);
  171. char filebuf[FILEBUF_SIZE];
  172. ret = INLINE_SYSCALL(read, 3, fd, filebuf, FILEBUF_SIZE);
  173. if (INTERNAL_SYSCALL_ERROR(ret))
  174. goto out;
  175. const ElfW(Ehdr) * header = (void *) filebuf;
  176. const ElfW(Phdr) * phdr = (void *) filebuf + header->e_phoff;
  177. const ElfW(Phdr) * ph;
  178. const ElfW(Dyn) * dyn = NULL;
  179. ElfW(Addr) base = 0;
  180. *text_start = (unsigned long) -1;
  181. *text_end = 0;
  182. *phoff = header->e_phoff;
  183. *phnum = header->e_phnum;
  184. struct loadcmd {
  185. ElfW(Addr) mapstart, mapend, dataend, allocend;
  186. off_t mapoff;
  187. int prot;
  188. } loadcmds[16], *c;
  189. int nloadcmds = 0;
  190. for (ph = phdr ; ph < &phdr[header->e_phnum] ; ph++)
  191. switch (ph->p_type) {
  192. case PT_DYNAMIC:
  193. dyn = (void *) ph->p_vaddr;
  194. break;
  195. case PT_LOAD:
  196. if (nloadcmds == 16) {
  197. ret = -EINVAL;
  198. goto out;
  199. }
  200. c = &loadcmds[nloadcmds++];
  201. c->mapstart = ph->p_vaddr & pagemask;
  202. c->mapend = (ph->p_vaddr + ph->p_filesz + pageshift) & pagemask;
  203. c->dataend = ph->p_vaddr + ph->p_filesz;
  204. c->allocend = ph->p_vaddr + ph->p_memsz;
  205. c->mapoff = ph->p_offset & pagemask;
  206. c->prot = (ph->p_flags & PF_R ? PROT_READ : 0) |
  207. (ph->p_flags & PF_W ? PROT_WRITE : 0) |
  208. (ph->p_flags & PF_X ? PROT_EXEC : 0);
  209. break;
  210. }
  211. c = loadcmds;
  212. int maplength = loadcmds[nloadcmds - 1].allocend - c->mapstart;
  213. ElfW(Addr) addr = INLINE_SYSCALL(mmap, 6, NULL, maplength, c->prot,
  214. MAP_PRIVATE | MAP_FILE, fd, c->mapoff);
  215. *load_addr = base = addr;
  216. dyn = (void *) (base + (ElfW(Addr)) dyn);
  217. goto postmap;
  218. for ( ; c < &loadcmds[nloadcmds] ; c++) {
  219. addr = INLINE_SYSCALL(mmap, 6, base + c->mapstart,
  220. c->mapend - c->mapstart, c->prot,
  221. MAP_PRIVATE | MAP_FILE | MAP_FIXED,
  222. fd, c->mapoff);
  223. postmap:
  224. if (IS_ERR_P(addr)) {
  225. ret = -ERRNO_P(addr);
  226. goto out;
  227. }
  228. if (c == loadcmds)
  229. INLINE_SYSCALL(munmap, 2, base + c->mapend,
  230. maplength - c->mapend);
  231. if (c->prot & PROT_EXEC) {
  232. if (base + c->mapstart < *text_start)
  233. *text_start = base + c->mapstart;
  234. if (base + c->mapend > *text_end)
  235. *text_end = base + c->mapend;
  236. }
  237. if (c->allocend > c->dataend) {
  238. ElfW(Addr) zero, zeroend, zeropage;
  239. zero = base + c->dataend;
  240. zeroend = (base + c->allocend + pageshift) & pagemask;
  241. zeropage = (zero + pageshift) & pagemask;
  242. if (zeroend < zeropage)
  243. zeropage = zeroend;
  244. if (zeropage > zero)
  245. memset((void *) zero, 0, zeropage - zero);
  246. if (zeroend > zeropage) {
  247. addr = INLINE_SYSCALL(mmap, 6,
  248. zeropage, zeroend - zeropage, c->prot,
  249. MAP_PRIVATE | MAP_ANON | MAP_FIXED,
  250. -1, 0);
  251. if (IS_ERR_P(addr)) {
  252. ret = -ERRNO_P(addr);
  253. goto out;
  254. }
  255. }
  256. }
  257. }
  258. get_pal_sec_info(dyn, base);
  259. *entry = base + header->e_entry;
  260. out:
  261. INLINE_SYSCALL(close, 1, fd);
  262. return ret;
  263. }
  264. void __attribute__((noinline)) ___dl_debug_state (void) {}
  265. extern __typeof(___dl_debug_state) _dl_debug_state
  266. __attribute ((alias ("___dl_debug_state")));
  267. struct link_map {
  268. ElfW(Addr) l_addr;
  269. const char * l_name;
  270. const ElfW(Dyn) * l_ld;
  271. struct link_map * l_next, * l_prev;
  272. };
  273. static struct link_map init_link_map;
  274. struct r_debug {
  275. int r_version;
  276. struct link_map * r_map;
  277. ElfW(Addr) r_brk;
  278. enum {
  279. RT_CONSISTENT,
  280. RT_ADD,
  281. RT_DELETE
  282. } r_state;
  283. ElfW(Addr) r_ldbase;
  284. };
  285. struct r_debug ___r_debug =
  286. { 1, NULL, (ElfW(Addr)) &___dl_debug_state, RT_CONSISTENT, 0 };
  287. extern __typeof(___r_debug) _r_debug
  288. __attribute ((alias ("___r_debug")));
  289. static void run_library (unsigned long entry, void * stack,
  290. int argc, const char ** argv)
  291. {
  292. *((void **) (stack -= sizeof(void *))) = NULL;
  293. for (int i = argc - 1 ; i >= 0 ; i--)
  294. *((const void **) (stack -= sizeof(void *))) = argv[i];
  295. *((const void **) (stack -= sizeof(void *))) = libname;
  296. *((unsigned long *) (stack -= sizeof(unsigned long))) = argc + 1;
  297. asm volatile ("movq %0, %%rsp\r\n"
  298. "pushq %1\r\n"
  299. "retq\r\n"
  300. :: "r"(stack), "r"(entry) : "memory");
  301. }
  302. int install_syscall_filter (const char * lib_name, unsigned long lib_start,
  303. unsigned long lib_end, int trace);
  304. int install_initial_syscall_filter ();
  305. extern bool do_fork;
  306. extern bool do_trace;
  307. int init_child (int argc, const char ** argv, const char ** envp);
  308. int init_parent (pid_t child, int argc, const char ** argv, const char ** envp);
  309. int run_parent (pid_t child, int argc, const char ** argv, const char ** envp);
  310. void start(void);
  311. unsigned long pal_addr = 0;
  312. asm (".global start\r\n"
  313. " .type start,@function\r\n"
  314. ".global main\r\n"
  315. " .type do_main,@function\r\n");
  316. /* At the begining of entry point, rsp starts at argc, then argvs,
  317. envps and auxvs. Here we store rsp to rdi, so it will not be
  318. messed up by function calls */
  319. asm ("start:\r\n"
  320. " movq %rsp, %rdi\r\n"
  321. " call do_main\r\n");
  322. struct config_store root_config;
  323. int free_heaps (void);
  324. void do_main (void * args)
  325. {
  326. void **all_args = (void **) args;
  327. int argc = (uintptr_t) all_args[0];
  328. const char **argv = (const char **) &all_args[1];
  329. const char **envp = argv + argc + 1;
  330. ElfW(Addr) addr = 0;
  331. void ** auxv = (void **) envp;
  332. ElfW(auxv_t) * av;
  333. char cfgbuf[CONFIG_MAX];
  334. int ret = 0;
  335. while (*(auxv++));
  336. /* VERY IMPORTANT: This is the filter that gets applied to the startup code
  337. * before applying the real filter in the function install_syscall_filter. If
  338. * you face any issues, you may have to enable certain syscalls here to
  339. * successfully make changes to startup code.
  340. */
  341. ret = install_initial_syscall_filter();
  342. if (ret < 0) {
  343. printf("Unable to install initial system call filter\n");
  344. goto exit;
  345. }
  346. /* occupy PAL_INIT_FD */
  347. INLINE_SYSCALL(dup2, 2, 0, PROC_INIT_FD);
  348. for (av = (void *) auxv ; av->a_type != AT_NULL ; av++)
  349. switch (av->a_type) {
  350. case AT_BASE:
  351. addr = (ElfW(Addr)) av->a_un.a_val;
  352. break;
  353. }
  354. if (!addr) {
  355. asm ("leaq start(%%rip), %0\r\n"
  356. "subq 1f(%%rip), %0\r\n"
  357. ".section\t.data.rel.ro\r\n"
  358. "1:\t.quad start\r\n"
  359. ".previous\r\n"
  360. : "=r" (addr) : : "cc");
  361. }
  362. ElfW(Dyn) * dyn = (ElfW(Dyn) *) (addr + (ElfW(Addr)) &_DYNAMIC);
  363. do_relocate(dyn, addr);
  364. init_link_map.l_addr = addr;
  365. init_link_map.l_ld = dyn;
  366. init_link_map.l_name = libname;
  367. ___r_debug.r_map = &init_link_map;
  368. ___r_debug.r_ldbase = addr;
  369. int manifest;
  370. if (!argc || (manifest = find_manifest(&argc, &argv)) < 0) {
  371. printf("USAGE: %s [executable|manifest] args ...\n", libname);
  372. goto exit;
  373. }
  374. ret = load_manifest(manifest, &root_config);
  375. if (ret < 0)
  376. goto exit;
  377. if (!execname) {
  378. if (get_config(&root_config, "loader.exec", cfgbuf, CONFIG_MAX) > 0
  379. && is_file_uri(cfgbuf))
  380. execname = file_uri_to_path(cfgbuf, strlen(cfgbuf));
  381. }
  382. pid_t pid = 0;
  383. if (do_fork && (pid = INLINE_SYSCALL(fork, 0)) > 0) {
  384. ret = run_parent(pid, argc, argv, envp);
  385. goto exit;
  386. }
  387. if (IS_ERR(pid)) {
  388. ret = -ERRNO(pid);
  389. goto exit;
  390. }
  391. unsigned long pal_entry = 0;
  392. unsigned long pal_start = 0;
  393. unsigned long pal_end = 0;
  394. unsigned long pal_phoff = 0;
  395. int pal_phnum = 0;
  396. ret = load_static(PAL_LOADER, &pal_entry, &pal_addr, &pal_start, &pal_end,
  397. &pal_phoff, &pal_phnum);
  398. if (ret < 0) {
  399. printf("Unable to load PAL loader\n");
  400. goto exit;
  401. }
  402. if (!pal_sec_info_addr)
  403. goto exit;
  404. int rand = INLINE_SYSCALL(open, 2, "/dev/urandom", O_RDONLY);
  405. if (IS_ERR(rand)) {
  406. ret = -ERRNO(rand);
  407. goto exit;
  408. }
  409. ret = INLINE_SYSCALL(mkdir, 2, GRAPHENE_PIPEDIR, 0777);
  410. if (IS_ERR(ret) && ERRNO(ret) != EEXIST) {
  411. if (ERRNO(ret) == ENOENT) {
  412. ret = INLINE_SYSCALL(mkdir, 2, GRAPHENE_TEMPDIR, 0777);
  413. if (!IS_ERR(ret)) {
  414. INLINE_SYSCALL(chmod, 2, GRAPHENE_TEMPDIR, 0777);
  415. ret = INLINE_SYSCALL(mkdir, 2, GRAPHENE_PIPEDIR, 0777);
  416. }
  417. }
  418. if (IS_ERR(ret))
  419. goto exit;
  420. }
  421. if (!IS_ERR(ret))
  422. INLINE_SYSCALL(chmod, 2, GRAPHENE_PIPEDIR, 0777);
  423. unsigned int domainid = 0;
  424. char * tmpdir = __alloca(sizeof(GRAPHENE_PIPEDIR) + 12);
  425. memcpy(tmpdir, GRAPHENE_PIPEDIR, sizeof(GRAPHENE_PIPEDIR));
  426. tmpdir[sizeof(GRAPHENE_PIPEDIR) - 1] = '/';
  427. while (!domainid) {
  428. ret = INLINE_SYSCALL(read, 3, rand, &domainid,
  429. sizeof(unsigned int));
  430. if (IS_ERR(ret)) {
  431. ret = -ERRNO(ret);
  432. goto exit;
  433. }
  434. if (domainid) {
  435. snprintf(tmpdir + sizeof(GRAPHENE_PIPEDIR), 12, "%08x", domainid);
  436. ret = INLINE_SYSCALL(mkdir, 2, tmpdir, 0700);
  437. if (IS_ERR(ret)) {
  438. if ((ret = -ERRNO(ret)) != -EEXIST)
  439. goto exit;
  440. domainid = 0;
  441. }
  442. }
  443. }
  444. snprintf(pal_sec_info_addr->pipe_prefix, PIPE_MAX, "%08x", domainid);
  445. unsigned short mcast_port = 0;
  446. ret = INLINE_SYSCALL(read, 3, rand, &mcast_port, sizeof(mcast_port));
  447. if (IS_ERR(ret)) {
  448. ret = -ERRNO(ret);
  449. goto exit;
  450. }
  451. if (mcast_port < 1024) mcast_port += 1024;
  452. pal_sec_info_addr->domain_id = domainid;
  453. pal_sec_info_addr->rand_gen = rand;
  454. pal_sec_info_addr->mcast_port = mcast_port;
  455. pal_sec_info_addr->_dl_debug_state = &___dl_debug_state;
  456. pal_sec_info_addr->_r_debug = &___r_debug;
  457. ret = init_child(argc, argv, envp);
  458. if (ret < 0)
  459. goto exit;
  460. free_heaps();
  461. /* free PAL_INIT_FD */
  462. INLINE_SYSCALL(close, 1, PROC_INIT_FD);
  463. ret = install_syscall_filter(libname, pal_start, pal_end, do_trace);
  464. if (ret < 0) {
  465. printf("Unable to install system call filter\n");
  466. goto exit;
  467. }
  468. /* after installing syscall, you can't execute any system call */
  469. for (av = (void *) auxv ; av->a_type != AT_NULL ; av++)
  470. switch (av->a_type) {
  471. case AT_ENTRY:
  472. av->a_un.a_val = pal_entry;
  473. break;
  474. case AT_BASE:
  475. av->a_un.a_val = pal_start;
  476. break;
  477. case AT_PHDR:
  478. av->a_un.a_val = pal_start + pal_phoff;
  479. break;
  480. case AT_PHNUM:
  481. av->a_un.a_val = pal_phnum;
  482. break;
  483. }
  484. run_library(pal_entry, envp, argc, argv);
  485. exit:
  486. INLINE_SYSCALL(exit_group, 1, ret);
  487. }