123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614 |
- /* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */
- /* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */
- #define _GNU_SOURCE 1
- #ifndef __GNUC__
- #define __GNUC__ 1
- #endif
- #include <linux/unistd.h>
- #include <asm/mman.h>
- #include <stdint.h>
- #include <stddef.h>
- #include <fcntl.h>
- #include <elf/elf.h>
- #include <sysdeps/generic/ldsodefs.h>
- #include <asm-errno.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include "pal_security.h"
- #include "utils.h"
- struct pal_sec_info * pal_sec_info_addr = NULL;
- unsigned long pagesize = 4096;
- unsigned long pageshift = 4095;
- unsigned long pagemask = ~4095;
- #if __WORDSIZE == 2
- # define FILEBUF_SIZE 512
- #else
- # define FILEBUF_SIZE 832
- #endif
- char libname[PATH_MAX];
- const char * execname;
- int find_manifest (int * pargc, const char *** pargv)
- {
- int argc = *pargc;
- const char ** argv = *pargv, * name = *argv;
- if (!argc)
- return -EINVAL;
- int fd = INLINE_SYSCALL(open, 2, name, O_RDONLY|O_CLOEXEC);
- if (IS_ERR(fd))
- return -ERRNO(fd);
- char filebuf[4];
- INLINE_SYSCALL(read, 3, fd, filebuf, 2);
- /* check if the first argument is a manifest, in case it is
- a runnable script. */
- if (!memcmp(filebuf, "#!", 2)) {
- char * path = __alloca(80);
- if (!path)
- return -ENOMEM;
- int bytes = INLINE_SYSCALL(read, 3, fd, path, 80);
- for (int i = 0 ; i < bytes ; i++)
- if (path[i] == ' ' || path[i] == '\n') {
- path[i] = 0;
- bytes = i;
- break;
- }
- memcpy(libname, path, bytes + 1);
- goto opened;
- }
- INLINE_SYSCALL(close, 1, fd);
- memcpy(libname, name, strlen(name) + 1);
- argc--;
- argv++;
- name = *argv;
- if (!argc)
- return -EINVAL;
- fd = INLINE_SYSCALL(open, 2, name, O_RDONLY|O_CLOEXEC);
- if (IS_ERR(fd))
- return -ERRNO(fd);
- /* check if the first argument is a executable. If it is, try finding
- all the possible manifest files */
- INLINE_SYSCALL(read, 3, fd, filebuf, 4);
- if (!memcmp(filebuf, "\177ELF", 4)) {
- int len = strlen(name);
- char * execpath = malloc(len + 1);
- fast_strcpy(execpath, name, len);
- execname = execpath;
- char * filename = __alloca(len + 10);
- fast_strcpy(filename, name, len);
- fast_strcpy(filename + len, ".manifest", 9);
- fd = INLINE_SYSCALL(open, 2, filename, O_RDONLY|O_CLOEXEC);
- if (!IS_ERR(fd))
- goto opened;
- fd = INLINE_SYSCALL(open, 2, "manifest", O_RDONLY|O_CLOEXEC);
- if (!IS_ERR(fd))
- goto opened;
- return -ENOENT;
- }
- opened:
- *pargc = argc;
- *pargv = argv;
- return fd;
- }
- int load_manifest (int fd, struct config_store * config)
- {
- int nbytes = INLINE_SYSCALL(lseek, 3, fd, 0, SEEK_END);
- if (IS_ERR(nbytes))
- return -ERRNO(nbytes);
- void * config_raw = (void *)
- INLINE_SYSCALL(mmap, 6, NULL, nbytes,
- PROT_READ|PROT_WRITE, MAP_PRIVATE,
- fd, 0);
- if (IS_ERR_P(config_raw))
- return -ERRNO_P(config_raw);
- config->raw_data = config_raw;
- config->raw_size = nbytes;
- config->malloc = malloc;
- config->free = NULL;
- const char * errstring = NULL;
- int ret = read_config(config, NULL, &errstring);
- if (ret < 0) {
- printf("can't read manifest: %s\n", errstring);
- return ret;
- }
- return 0;
- }
- static int do_relocate (ElfW(Dyn) * dyn, ElfW(Addr) addr)
- {
- ElfW(Dyn) * dt_rela = NULL;
- ElfW(Dyn) * dt_relacount = NULL;
- for ( ; dyn->d_tag != DT_NULL ; dyn++)
- switch (dyn->d_tag) {
- case DT_RELA: dt_rela = dyn; break;
- case DT_RELACOUNT: dt_relacount = dyn; break;
- }
- if (!dt_rela || !dt_relacount)
- return -EINVAL;
- ElfW(Rela) * r = (void *) (addr + dt_rela->d_un.d_ptr);
- ElfW(Rela) * end = r + dt_relacount->d_un.d_val;
- for ( ; r < end ; r++)
- *(ElfW(Addr) *) (addr + r->r_offset) = addr + r->r_addend;
- return 0;
- }
- static void get_pal_sec_info (const ElfW(Dyn) * dyn, ElfW(Addr) addr)
- {
- const ElfW(Dyn) * dt_symtab = NULL;
- const ElfW(Dyn) * dt_strtab = NULL;
- const ElfW(Dyn) * dt_rela = NULL;
- const ElfW(Dyn) * dt_relasz = NULL;
- const ElfW(Dyn) * dt_relacount = NULL;
- for ( ; dyn->d_tag != DT_NULL ; dyn++)
- switch (dyn->d_tag) {
- case DT_SYMTAB: dt_symtab = dyn; break;
- case DT_STRTAB: dt_strtab = dyn; break;
- case DT_RELA: dt_rela = dyn; break;
- case DT_RELASZ: dt_relasz = dyn; break;
- case DT_RELACOUNT: dt_relacount = dyn; break;
- }
- if (!dt_symtab || !dt_strtab || !dt_rela || !dt_relasz || !dt_relacount)
- return;
- ElfW(Sym) * symtab = (void *) (addr + dt_symtab->d_un.d_ptr);
- const char * strtab = (void *) (addr + dt_strtab->d_un.d_ptr);
- ElfW(Rela) * r = (void *) (addr + dt_rela->d_un.d_ptr);
- ElfW(Rela) * rel = r + dt_relacount->d_un.d_val;
- ElfW(Rela) * end = r + dt_relasz->d_un.d_val / sizeof(ElfW(Rela));
- for (r = rel ; r < end ; r++) {
- ElfW(Sym) * sym = &symtab[ELFW(R_SYM) (r->r_info)];
- if (!sym->st_name)
- continue;
- const char * name = strtab + sym->st_name;
- if (!memcmp(name, "pal_sec_info", 13))
- pal_sec_info_addr = (void *) addr + sym->st_value;
- }
- }
- static int load_static (const char * filename,
- unsigned long * entry, unsigned long * load_addr,
- unsigned long * text_start, unsigned long * text_end,
- unsigned long * phoff, int * phnum)
- {
- int ret = 0;
- int fd = INLINE_SYSCALL(open, 2, filename, O_RDONLY|O_CLOEXEC);
- if (IS_ERR(fd))
- return -ERRNO(fd);
- char filebuf[FILEBUF_SIZE];
- ret = INLINE_SYSCALL(read, 3, fd, filebuf, FILEBUF_SIZE);
- if (INTERNAL_SYSCALL_ERROR(ret))
- goto out;
- const ElfW(Ehdr) * header = (void *) filebuf;
- const ElfW(Phdr) * phdr = (void *) filebuf + header->e_phoff;
- const ElfW(Phdr) * ph;
- const ElfW(Dyn) * dyn = NULL;
- ElfW(Addr) base = 0;
- *text_start = (unsigned long) -1;
- *text_end = 0;
- *phoff = header->e_phoff;
- *phnum = header->e_phnum;
- struct loadcmd {
- ElfW(Addr) mapstart, mapend, dataend, allocend;
- off_t mapoff;
- int prot;
- } loadcmds[16], *c;
- int nloadcmds = 0;
- for (ph = phdr ; ph < &phdr[header->e_phnum] ; ph++)
- switch (ph->p_type) {
- case PT_DYNAMIC:
- dyn = (void *) ph->p_vaddr;
- break;
- case PT_LOAD:
- if (nloadcmds == 16) {
- ret = -EINVAL;
- goto out;
- }
- c = &loadcmds[nloadcmds++];
- c->mapstart = ph->p_vaddr & pagemask;
- c->mapend = (ph->p_vaddr + ph->p_filesz + pageshift) & pagemask;
- c->dataend = ph->p_vaddr + ph->p_filesz;
- c->allocend = ph->p_vaddr + ph->p_memsz;
- c->mapoff = ph->p_offset & pagemask;
- c->prot = (ph->p_flags & PF_R ? PROT_READ : 0) |
- (ph->p_flags & PF_W ? PROT_WRITE : 0) |
- (ph->p_flags & PF_X ? PROT_EXEC : 0);
- break;
- }
- c = loadcmds;
- int maplength = loadcmds[nloadcmds - 1].allocend - c->mapstart;
- ElfW(Addr) addr = INLINE_SYSCALL(mmap, 6, NULL, maplength, c->prot,
- MAP_PRIVATE | MAP_FILE, fd, c->mapoff);
- *load_addr = base = addr;
- dyn = (void *) (base + (ElfW(Addr)) dyn);
- goto postmap;
- for ( ; c < &loadcmds[nloadcmds] ; c++) {
- addr = INLINE_SYSCALL(mmap, 6, base + c->mapstart,
- c->mapend - c->mapstart, c->prot,
- MAP_PRIVATE | MAP_FILE | MAP_FIXED,
- fd, c->mapoff);
- postmap:
- if (IS_ERR_P(addr)) {
- ret = -ERRNO_P(addr);
- goto out;
- }
- if (c == loadcmds)
- INLINE_SYSCALL(munmap, 2, base + c->mapend,
- maplength - c->mapend);
- if (c->prot & PROT_EXEC) {
- if (base + c->mapstart < *text_start)
- *text_start = base + c->mapstart;
- if (base + c->mapend > *text_end)
- *text_end = base + c->mapend;
- }
- if (c->allocend > c->dataend) {
- ElfW(Addr) zero, zeroend, zeropage;
- zero = base + c->dataend;
- zeroend = (base + c->allocend + pageshift) & pagemask;
- zeropage = (zero + pageshift) & pagemask;
- if (zeroend < zeropage)
- zeropage = zeroend;
- if (zeropage > zero)
- memset((void *) zero, 0, zeropage - zero);
- if (zeroend > zeropage) {
- addr = INLINE_SYSCALL(mmap, 6,
- zeropage, zeroend - zeropage, c->prot,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED,
- -1, 0);
- if (IS_ERR_P(addr)) {
- ret = -ERRNO_P(addr);
- goto out;
- }
- }
- }
- }
- get_pal_sec_info(dyn, base);
- *entry = base + header->e_entry;
- out:
- INLINE_SYSCALL(close, 1, fd);
- return ret;
- }
- void __attribute__((noinline)) ___dl_debug_state (void) {}
- extern __typeof(___dl_debug_state) _dl_debug_state
- __attribute ((alias ("___dl_debug_state")));
- struct link_map {
- ElfW(Addr) l_addr;
- const char * l_name;
- const ElfW(Dyn) * l_ld;
- struct link_map * l_next, * l_prev;
- };
- static struct link_map init_link_map;
- struct r_debug {
- int r_version;
- struct link_map * r_map;
- ElfW(Addr) r_brk;
- enum {
- RT_CONSISTENT,
- RT_ADD,
- RT_DELETE
- } r_state;
- ElfW(Addr) r_ldbase;
- };
- struct r_debug ___r_debug =
- { 1, NULL, (ElfW(Addr)) &___dl_debug_state, RT_CONSISTENT, 0 };
- extern __typeof(___r_debug) _r_debug
- __attribute ((alias ("___r_debug")));
- static void run_library (unsigned long entry, void * stack,
- int argc, const char ** argv)
- {
- *((void **) (stack -= sizeof(void *))) = NULL;
- for (int i = argc - 1 ; i >= 0 ; i--)
- *((const void **) (stack -= sizeof(void *))) = argv[i];
- *((const void **) (stack -= sizeof(void *))) = libname;
- *((unsigned long *) (stack -= sizeof(unsigned long))) = argc + 1;
- asm volatile ("movq %0, %%rsp\r\n"
- "pushq %1\r\n"
- "retq\r\n"
- :: "r"(stack), "r"(entry) : "memory");
- }
- int install_syscall_filter (const char * lib_name, unsigned long lib_start,
- unsigned long lib_end, int trace);
- int install_initial_syscall_filter ();
- extern bool do_fork;
- extern bool do_trace;
- int init_child (int argc, const char ** argv, const char ** envp);
- int init_parent (pid_t child, int argc, const char ** argv, const char ** envp);
- int run_parent (pid_t child, int argc, const char ** argv, const char ** envp);
- void start(void);
- unsigned long pal_addr = 0;
- asm (".global start\r\n"
- " .type start,@function\r\n"
- ".global main\r\n"
- " .type do_main,@function\r\n");
- /* At the begining of entry point, rsp starts at argc, then argvs,
- envps and auxvs. Here we store rsp to rdi, so it will not be
- messed up by function calls */
- asm ("start:\r\n"
- " movq %rsp, %rdi\r\n"
- " call do_main\r\n");
- struct config_store root_config;
- int free_heaps (void);
- void do_main (void * args)
- {
- void **all_args = (void **) args;
- int argc = (uintptr_t) all_args[0];
- const char **argv = (const char **) &all_args[1];
- const char **envp = argv + argc + 1;
- ElfW(Addr) addr = 0;
- void ** auxv = (void **) envp;
- ElfW(auxv_t) * av;
- char cfgbuf[CONFIG_MAX];
- int ret = 0;
- while (*(auxv++));
- /* VERY IMPORTANT: This is the filter that gets applied to the startup code
- * before applying the real filter in the function install_syscall_filter. If
- * you face any issues, you may have to enable certain syscalls here to
- * successfully make changes to startup code.
- */
- ret = install_initial_syscall_filter();
- if (ret < 0) {
- printf("Unable to install initial system call filter\n");
- goto exit;
- }
- /* occupy PAL_INIT_FD */
- INLINE_SYSCALL(dup2, 2, 0, PROC_INIT_FD);
- for (av = (void *) auxv ; av->a_type != AT_NULL ; av++)
- switch (av->a_type) {
- case AT_BASE:
- addr = (ElfW(Addr)) av->a_un.a_val;
- break;
- }
- if (!addr) {
- asm ("leaq start(%%rip), %0\r\n"
- "subq 1f(%%rip), %0\r\n"
- ".section\t.data.rel.ro\r\n"
- "1:\t.quad start\r\n"
- ".previous\r\n"
- : "=r" (addr) : : "cc");
- }
- ElfW(Dyn) * dyn = (ElfW(Dyn) *) (addr + (ElfW(Addr)) &_DYNAMIC);
- do_relocate(dyn, addr);
- init_link_map.l_addr = addr;
- init_link_map.l_ld = dyn;
- init_link_map.l_name = libname;
- ___r_debug.r_map = &init_link_map;
- ___r_debug.r_ldbase = addr;
- int manifest;
- if (!argc || (manifest = find_manifest(&argc, &argv)) < 0) {
- printf("USAGE: %s [executable|manifest] args ...\n", libname);
- goto exit;
- }
- ret = load_manifest(manifest, &root_config);
- if (ret < 0)
- goto exit;
- if (!execname) {
- if (get_config(&root_config, "loader.exec", cfgbuf, CONFIG_MAX) > 0
- && is_file_uri(cfgbuf))
- execname = file_uri_to_path(cfgbuf, strlen(cfgbuf));
- }
- pid_t pid = 0;
- if (do_fork && (pid = INLINE_SYSCALL(fork, 0)) > 0) {
- ret = run_parent(pid, argc, argv, envp);
- goto exit;
- }
- if (IS_ERR(pid)) {
- ret = -ERRNO(pid);
- goto exit;
- }
- unsigned long pal_entry = 0;
- unsigned long pal_start = 0;
- unsigned long pal_end = 0;
- unsigned long pal_phoff = 0;
- int pal_phnum = 0;
- ret = load_static(PAL_LOADER, &pal_entry, &pal_addr, &pal_start, &pal_end,
- &pal_phoff, &pal_phnum);
- if (ret < 0) {
- printf("Unable to load PAL loader\n");
- goto exit;
- }
- if (!pal_sec_info_addr)
- goto exit;
- int rand = INLINE_SYSCALL(open, 2, "/dev/urandom", O_RDONLY);
- if (IS_ERR(rand)) {
- ret = -ERRNO(rand);
- goto exit;
- }
- ret = INLINE_SYSCALL(mkdir, 2, GRAPHENE_PIPEDIR, 0777);
- if (IS_ERR(ret) && ERRNO(ret) != EEXIST) {
- if (ERRNO(ret) == ENOENT) {
- ret = INLINE_SYSCALL(mkdir, 2, GRAPHENE_TEMPDIR, 0777);
- if (!IS_ERR(ret)) {
- INLINE_SYSCALL(chmod, 2, GRAPHENE_TEMPDIR, 0777);
- ret = INLINE_SYSCALL(mkdir, 2, GRAPHENE_PIPEDIR, 0777);
- }
- }
- if (IS_ERR(ret))
- goto exit;
- }
- if (!IS_ERR(ret))
- INLINE_SYSCALL(chmod, 2, GRAPHENE_PIPEDIR, 0777);
- unsigned int domainid = 0;
- char * tmpdir = __alloca(sizeof(GRAPHENE_PIPEDIR) + 12);
- memcpy(tmpdir, GRAPHENE_PIPEDIR, sizeof(GRAPHENE_PIPEDIR));
- tmpdir[sizeof(GRAPHENE_PIPEDIR) - 1] = '/';
- while (!domainid) {
- ret = INLINE_SYSCALL(read, 3, rand, &domainid,
- sizeof(unsigned int));
- if (IS_ERR(ret)) {
- ret = -ERRNO(ret);
- goto exit;
- }
- if (domainid) {
- snprintf(tmpdir + sizeof(GRAPHENE_PIPEDIR), 12, "%08x", domainid);
- ret = INLINE_SYSCALL(mkdir, 2, tmpdir, 0700);
- if (IS_ERR(ret)) {
- if ((ret = -ERRNO(ret)) != -EEXIST)
- goto exit;
- domainid = 0;
- }
- }
- }
- snprintf(pal_sec_info_addr->pipe_prefix, PIPE_MAX, "%08x", domainid);
- unsigned short mcast_port = 0;
- ret = INLINE_SYSCALL(read, 3, rand, &mcast_port, sizeof(mcast_port));
- if (IS_ERR(ret)) {
- ret = -ERRNO(ret);
- goto exit;
- }
- if (mcast_port < 1024) mcast_port += 1024;
- pal_sec_info_addr->domain_id = domainid;
- pal_sec_info_addr->rand_gen = rand;
- pal_sec_info_addr->mcast_port = mcast_port;
- pal_sec_info_addr->_dl_debug_state = &___dl_debug_state;
- pal_sec_info_addr->_r_debug = &___r_debug;
- ret = init_child(argc, argv, envp);
- if (ret < 0)
- goto exit;
- free_heaps();
- /* free PAL_INIT_FD */
- INLINE_SYSCALL(close, 1, PROC_INIT_FD);
- ret = install_syscall_filter(libname, pal_start, pal_end, do_trace);
- if (ret < 0) {
- printf("Unable to install system call filter\n");
- goto exit;
- }
- /* after installing syscall, you can't execute any system call */
- for (av = (void *) auxv ; av->a_type != AT_NULL ; av++)
- switch (av->a_type) {
- case AT_ENTRY:
- av->a_un.a_val = pal_entry;
- break;
- case AT_BASE:
- av->a_un.a_val = pal_start;
- break;
- case AT_PHDR:
- av->a_un.a_val = pal_start + pal_phoff;
- break;
- case AT_PHNUM:
- av->a_un.a_val = pal_phnum;
- break;
- }
- run_library(pal_entry, envp, argc, argv);
- exit:
- INLINE_SYSCALL(exit_group, 1, ret);
- }
|