diff --git a/Documentation/manifest-syntax.rst b/Documentation/manifest-syntax.rst index cb2086f73d..30a3e2f23f 100644 --- a/Documentation/manifest-syntax.rst +++ b/Documentation/manifest-syntax.rst @@ -136,6 +136,20 @@ arguments to be provided at runtime from an external (trusted) source. If none of the above arguments-handling manifest options is specified in the manifest, the application will get ``argv = [ ]``. +etc passthrough +^^^^^^^^^^^^^^^ + +:: + + libos.passthrough_etc_files = [true|false] + (Default: false) + +This specifies whether to passthrough extra runtime files from ``/etc``. +Before the files are available to the application, they will be sanitized. +The set of extra runtime files is limited to: + +- ``/etc/hostname`` + Environment variables ^^^^^^^^^^^^^^^^^^^^^ diff --git a/libos/include/libos_fs.h b/libos/include/libos_fs.h index eb1a75e12f..d1ced65630 100644 --- a/libos/include/libos_fs.h +++ b/libos/include/libos_fs.h @@ -518,6 +518,7 @@ extern struct libos_dentry* g_dentry_root; int init_fs(void); int init_mount_root(void); int init_mount(void); +int init_mount_etc(void); /* file system operations */ diff --git a/libos/include/libos_fs_pseudo.h b/libos/include/libos_fs_pseudo.h index 57a389b6ca..ebd2a751da 100644 --- a/libos/include/libos_fs_pseudo.h +++ b/libos/include/libos_fs_pseudo.h @@ -256,3 +256,6 @@ int sys_print_as_ranges(char* buf, size_t buf_size, size_t count, int sys_print_as_bitmask(char* buf, size_t buf_size, size_t count, bool (*is_present)(size_t ind, const void* arg), const void* callback_arg); + +/* etcfs */ +int init_etc(void); diff --git a/libos/include/libos_internal.h b/libos/include/libos_internal.h index 1e202eb83b..14567a661c 100644 --- a/libos/include/libos_internal.h +++ b/libos/include/libos_internal.h @@ -153,6 +153,8 @@ bool handle_signal(PAL_CONTEXT* context); */ long pal_to_unix_errno(long err); +long libos_set_hostname(char* name, size_t len); + void warn_unsupported_syscall(unsigned long sysno); void debug_print_syscall_before(unsigned long sysno, ...); void debug_print_syscall_after(unsigned long sysno, ...); diff --git a/libos/src/fs/etc/fs.c b/libos/src/fs/etc/fs.c new file mode 100644 index 0000000000..23db73d10d --- /dev/null +++ b/libos/src/fs/etc/fs.c @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* Copyright (C) 2022 Intel Corporation + * Mariusz Zaborski + */ + +/* + * This file contains the implementation of `etc` passthrough. + */ + +#include "libos_checkpoint.h" +#include "libos_fs.h" +#include "libos_fs_pseudo.h" + +static int provide_etc_hostname(struct libos_dentry* dent, char** out_data, size_t* out_size) { + __UNUSED(dent); + /* Use the string (without null terminator) as file data */ + size_t size = strlen(g_pal_public_state->hostname); + char* data = malloc(size); + if (!data) + return -ENOMEM; + memcpy(data, g_pal_public_state->hostname, size); + *out_data = data; + *out_size = size; + return 0; +} + +int init_etc(void) { + pseudo_add_str(NULL, "hostname", &provide_etc_hostname); + return 0; +} + +int init_mount_etc(void) { + int ret; + + if (!g_pal_public_state->passthrough_etc_files) + return 0; + + ret = mount_fs(&(struct libos_mount_params){ + .type = "pseudo", + .path = "/etc/hostname", + .uri = "hostname", + }); + if (ret < 0) + return ret; + + return 0; +} + +BEGIN_CP_FUNC(etc_info) { + __UNUSED(size); + __UNUSED(obj); + __UNUSED(objp); + + /* Propagate hostname */ + size_t off = ADD_CP_OFFSET(sizeof(g_pal_public_state->hostname) + + sizeof(g_pal_public_state->passthrough_etc_files)); + char* new_hostname = (char*)(base + off); + memcpy(new_hostname, g_pal_public_state->hostname, sizeof(g_pal_public_state->hostname)); + + /* Propagate passthrough_etc_files */ + bool* new_passthrough_etc_files = (bool*)(new_hostname + + sizeof(g_pal_public_state->hostname)); + *new_passthrough_etc_files = g_pal_public_state->passthrough_etc_files; + ADD_CP_FUNC_ENTRY(off); +} +END_CP_FUNC(etc_info) + +BEGIN_RS_FUNC(etc_info) { + __UNUSED(offset); + __UNUSED(rebase); + + const char* hostname = (const char*)(base + GET_CP_FUNC_ENTRY()); + memcpy(&g_pal_public_state->hostname, hostname, sizeof(g_pal_public_state->hostname)); + + g_pal_public_state->passthrough_etc_files = *(bool*)(hostname + + sizeof(g_pal_public_state->hostname)); +} +END_RS_FUNC(etc_info) diff --git a/libos/src/fs/libos_fs.c b/libos/src/fs/libos_fs.c index 7e850dad92..82573748a8 100644 --- a/libos/src/fs/libos_fs.c +++ b/libos/src/fs/libos_fs.c @@ -75,6 +75,9 @@ int init_fs(void) { if ((ret = init_sysfs()) < 0) goto err; + if ((ret = init_etc()) < 0) + goto err; + return 0; err: diff --git a/libos/src/libos_init.c b/libos/src/libos_init.c index a312f2a933..34776cb8d5 100644 --- a/libos/src/libos_init.c +++ b/libos/src/libos_init.c @@ -424,6 +424,7 @@ noreturn void libos_init(const char* const* argv, const char* const* envp) { RUN_INIT(init_threading); RUN_INIT(init_mount); RUN_INIT(init_std_handles); + RUN_INIT(init_mount_etc); char** expanded_argv = NULL; RUN_INIT(init_exec_handle, argv, &expanded_argv); @@ -487,6 +488,9 @@ noreturn void libos_init(const char* const* argv, const char* const* envp) { * communicates with server over a "loopback" IPC connection. */ RUN_INIT(init_sync_client); + RUN_INIT(libos_set_hostname, g_pal_public_state->hostname, + strlen(g_pal_public_state->hostname) + 1); + log_debug("LibOS initialized"); libos_tcb_t* cur_tcb = libos_get_tcb(); diff --git a/libos/src/meson.build b/libos/src/meson.build index 3f2ca34187..f7dd005ca9 100644 --- a/libos/src/meson.build +++ b/libos/src/meson.build @@ -19,6 +19,7 @@ libos_sources = files( 'fs/chroot/fs.c', 'fs/dev/attestation.c', 'fs/dev/fs.c', + 'fs/etc/fs.c', 'fs/eventfd/fs.c', 'fs/libos_dcache.c', 'fs/libos_fs.c', diff --git a/libos/src/sys/libos_clone.c b/libos/src/sys/libos_clone.c index 705cd3edb3..0b9124378a 100644 --- a/libos/src/sys/libos_clone.c +++ b/libos/src/sys/libos_clone.c @@ -115,6 +115,7 @@ static BEGIN_MIGRATION_DEF(fork, struct libos_process* process_description, DEFINE_MIGRATE(brk, NULL, 0); DEFINE_MIGRATE(loaded_elf_objects, NULL, 0); DEFINE_MIGRATE(topo_info, NULL, 0); + DEFINE_MIGRATE(etc_info, NULL, 0); #ifdef DEBUG DEFINE_MIGRATE(gdb_map, NULL, 0); #endif diff --git a/libos/src/sys/libos_uname.c b/libos/src/sys/libos_uname.c index 32b9edeab4..f601a2244b 100644 --- a/libos/src/sys/libos_uname.c +++ b/libos/src/sys/libos_uname.c @@ -40,18 +40,26 @@ long libos_syscall_uname(struct new_utsname* buf) { return 0; } -long libos_syscall_sethostname(char* name, int len) { - if (len < 0 || (size_t)len >= sizeof(g_current_uname.nodename)) +long libos_set_hostname(char* name, size_t len) { + if (len >= sizeof(g_current_uname.nodename)) return -EINVAL; - if (!is_user_memory_readable(name, len)) - return -EFAULT; - memcpy(&g_current_uname.nodename, name, len); memset(&g_current_uname.nodename[len], 0, sizeof(g_current_uname.nodename) - len); + return 0; } +long libos_syscall_sethostname(char* name, int len) { + if (!is_user_memory_readable(name, len)) + return -EFAULT; + + if (len < 0) + return -EINVAL; + + return libos_set_hostname(name, len); +} + long libos_syscall_setdomainname(char* name, int len) { if (len < 0 || (size_t)len >= sizeof(g_current_uname.domainname)) return -EINVAL; diff --git a/libos/test/regression/hostname.c b/libos/test/regression/hostname.c new file mode 100644 index 0000000000..564e35d6a8 --- /dev/null +++ b/libos/test/regression/hostname.c @@ -0,0 +1,108 @@ +#define _DEFAULT_SOURCE BSD /* This is required for gethostname */ + +#include +#include +#include +#include +#include +#include +#include + +static void test_fork(const char* tag, const char* name, void (*f)(const char*, const char*)) { + int status; + + pid_t pid = fork(); + if (pid == -1) { + printf("Unable to fork %s\n", tag); + exit(1); + } + + if (pid == 0) { + f(tag, name); + exit(0); + } + + if (wait(&status) == -1) { + printf("Wait failed %s\n", tag); + exit(1); + } + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + printf("Test failed %s\n", tag); + exit(1); + } +} + +static void test_gethostname(const char* tag, const char* name) { + char buf[512] = {0}; + + if (gethostname(buf, sizeof(buf)) != 0) { + printf("%sgethostname: failed %d\n", tag, errno); + exit(1); + } + + if (strcmp(buf, name) != 0) { + printf("%sgethostname dosen't match hostname (expected: %s, got: %s)\n", + tag, name, buf); + exit(1); + } +} + +static void test_etc_hostname(const char* tag, const char* name) { + char buf[512] = {0}; + int fd; + + fd = open("/etc/hostname", O_RDONLY); + + /* + * If the etc hostname was not provided, assume that etc shouldn't exists. + */ + if (strcmp(name, "") == 0) { + if (fd != -1 || errno != ENOENT) { + printf("The etc file shouldn't exists, but exists\n"); + exit(1); + } + return; + } + + if (fd == -1) { + printf("Unable to open /etc/hostname in %s\n", tag); + exit(1); + } + + int ret = read(fd, buf, sizeof(buf)); + if (ret <= 0) { + printf("Unable to read /etc/hostname in %s\n", tag); + exit(1); + } + + /* + * Sometimes etc hostname might have a trailing '\n', gramine is romving it, + * do the same in the test. + */ + size_t len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') { + buf[len - 1] = '\0'; + } + + if (strcmp(buf, name) != 0) { + printf("%s etc don't have a expected value (expected: %s, got: %s)\n", + tag, name, buf); + exit(1); + } +} + +int main(int argc, char** argv) { + if (argc != 3) { + printf("Usage: %s [hostname] [etc_hostname]\n", argv[0]); + return 1; + } + + test_gethostname("", argv[1]); + test_etc_hostname("", argv[2]); + test_fork("fork gethostname", argv[1], test_gethostname); + test_fork("fork etc gethostname", argv[2], test_etc_hostname); + + printf("hostname test passed\n"); + return 0; +} diff --git a/libos/test/regression/hostname_pass_etc.manifest.template b/libos/test/regression/hostname_pass_etc.manifest.template new file mode 100644 index 0000000000..15212bcb0d --- /dev/null +++ b/libos/test/regression/hostname_pass_etc.manifest.template @@ -0,0 +1,22 @@ +loader.entrypoint = "file:{{ gramine.libos }}" +libos.entrypoint = "hostname" + +loader.env.LD_LIBRARY_PATH = "/lib" +loader.insecure__use_cmdline_argv = true + +fs.mounts = [ + { path = "/lib", uri = "file:{{ gramine.runtimedir(libc) }}" }, + { path = "/hostname", uri = "file:{{ binary_dir }}/hostname" }, +] + +sgx.debug = true +sgx.nonpie_binary = true +sgx.thread_num = 16 + +sgx.trusted_files = [ + "file:{{ gramine.libos }}", + "file:{{ gramine.runtimedir(libc) }}/", + "file:{{ binary_dir }}/hostname", +] + +libos.passthrough_etc_files = true diff --git a/libos/test/regression/meson.build b/libos/test/regression/meson.build index aefba09839..6d94923655 100644 --- a/libos/test/regression/meson.build +++ b/libos/test/regression/meson.build @@ -49,6 +49,7 @@ tests = { 'groups': {}, 'helloworld': {}, 'host_root_fs': {}, + 'hostname': {}, 'init_fail': {}, 'keys': {}, 'kill_all': {}, diff --git a/libos/test/regression/test_libos.py b/libos/test/regression/test_libos.py index c66340690d..a5784be0b4 100644 --- a/libos/test/regression/test_libos.py +++ b/libos/test/regression/test_libos.py @@ -5,6 +5,8 @@ import subprocess import unittest +from socket import gethostname + from graminelibos.regression import ( HAS_SGX, ON_X86, @@ -1119,6 +1121,14 @@ def test_060_synthetic(self): stdout, _ = self.run_binary(['synthetic']) self.assertIn("TEST OK", stdout) + def test_070_hostname_localhost(self): + stdout, _ = self.run_binary(['hostname', 'localhost', '']) + self.assertIn("hostname test passed", stdout) + + def test_071_hostname_pass_etc(self): + stdout, _ = self.run_binary(['hostname_pass_etc', gethostname(), gethostname()]) + self.assertIn("hostname test passed", stdout) + class TC_50_GDB(RegressionTestCase): def setUp(self): diff --git a/libos/test/regression/tests.toml b/libos/test/regression/tests.toml index 680b32db2f..c861e9ae2b 100644 --- a/libos/test/regression/tests.toml +++ b/libos/test/regression/tests.toml @@ -49,6 +49,8 @@ manifests = [ "gettimeofday", "groups", "helloworld", + "hostname", + "hostname_pass_etc", "host_root_fs", "init_fail", "keys", diff --git a/libos/test/regression/tests_musl.toml b/libos/test/regression/tests_musl.toml index ddd6389731..e5d12c9ac9 100644 --- a/libos/test/regression/tests_musl.toml +++ b/libos/test/regression/tests_musl.toml @@ -51,6 +51,8 @@ manifests = [ "gettimeofday", "groups", "helloworld", + "hostname", + "hostname_pass_etc", "host_root_fs", "init_fail", "keys", diff --git a/pal/include/host/linux-common/host_info.h b/pal/include/host/linux-common/host_info.h new file mode 100644 index 0000000000..6074061166 --- /dev/null +++ b/pal/include/host/linux-common/host_info.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* Copyright (C) 2022 Intel Corporation + * Mariusz Zaborski + */ + +#pragma once + +#include "pal.h" + +/* Function to fetch the hostname */ +int get_hostname(char* hostname, size_t size); diff --git a/pal/include/pal/pal.h b/pal/include/pal/pal.h index 824e50c754..5eb17a0742 100644 --- a/pal/include/pal/pal.h +++ b/pal/include/pal/pal.h @@ -34,6 +34,9 @@ typedef uint32_t PAL_IDX; /*!< an index */ /* maximum length of URIs */ #define URI_MAX 4096 +/* maximum length of hostname */ +#define PAL_HOSTNAME_MAX 255 + /* Common types used by host specific header. */ enum pal_socket_domain { PAL_DISCONNECT, @@ -134,6 +137,9 @@ struct pal_public_state { struct pal_cpu_info cpu_info; struct pal_topo_info topo_info; /* received from untrusted host, but sanitized */ + + bool passthrough_etc_files; + char hostname[PAL_HOSTNAME_MAX]; }; /* We cannot mark this as returning a pointer to `const` object, because LibOS can diff --git a/pal/src/host/linux-common/host_info.c b/pal/src/host/linux-common/host_info.c new file mode 100644 index 0000000000..7280fceec3 --- /dev/null +++ b/pal/src/host/linux-common/host_info.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* Copyright (C) 2022 Intel Corporation + * Mariusz Zaborski + */ + +/* + * This file contains the APIs to expose host information. + */ + +#include +#include + +#include "linux_utils.h" +#include "host_info.h" + +int get_hostname(char* hostname, size_t size) { + struct new_utsname c_uname; + + assert(hostname != NULL); + assert(size > 0); + + if (DO_SYSCALL(uname, &c_uname) != 0) + return -1; + + size_t len = strlen(c_uname.nodename) + 1; + memcpy(hostname, &c_uname.nodename, + len > size ? size : len); + hostname[size - 1] = '\0'; + + return 0; +} diff --git a/pal/src/host/linux-common/meson.build b/pal/src/host/linux-common/meson.build index 182b72af06..d4a11f4475 100644 --- a/pal/src/host/linux-common/meson.build +++ b/pal/src/host/linux-common/meson.build @@ -6,6 +6,7 @@ pal_linux_common_sources_enclave = files( pal_linux_common_sources_host = files( 'debug_map.c', 'file_utils.c', + 'host_info.c', 'main_exec_path.c', 'proc_maps.c', 'timespec_utils.c', diff --git a/pal/src/host/linux-sgx/enclave_ecalls.c b/pal/src/host/linux-sgx/enclave_ecalls.c index 736af64090..5be72c9e94 100644 --- a/pal/src/host/linux-sgx/enclave_ecalls.c +++ b/pal/src/host/linux-sgx/enclave_ecalls.c @@ -95,8 +95,8 @@ void handle_ecall(long ecall_index, void* ecall_args, void* exit_target, void* e if (verify_and_init_rpc_queue(READ_ONCE(ms->rpc_queue))) return; - struct pal_topo_info* topo_info = READ_ONCE(ms->ms_topo_info); - if (!topo_info || !sgx_is_completely_outside_enclave(topo_info, sizeof(*topo_info))) + pal_host_info_t* host_info = READ_ONCE(ms->ms_host_info); + if (!host_info || !sgx_is_completely_outside_enclave(host_info, sizeof(*host_info))) return; /* xsave size must be initialized early, from a trusted source (EREPORT result) */ @@ -114,7 +114,7 @@ void handle_ecall(long ecall_index, void* ecall_args, void* exit_target, void* e pal_linux_main(READ_ONCE(ms->ms_libpal_uri), READ_ONCE(ms->ms_libpal_uri_len), READ_ONCE(ms->ms_args), READ_ONCE(ms->ms_args_size), READ_ONCE(ms->ms_env), READ_ONCE(ms->ms_env_size), READ_ONCE(ms->ms_parent_stream_fd), - READ_ONCE(ms->ms_qe_targetinfo), topo_info); + READ_ONCE(ms->ms_qe_targetinfo), host_info); } else { // ENCLAVE_START already called (maybe successfully, maybe not), so // only valid ecall is THREAD_START. diff --git a/pal/src/host/linux-sgx/host_ecalls.c b/pal/src/host/linux-sgx/host_ecalls.c index 631881cc41..a5d4db0afd 100644 --- a/pal/src/host/linux-sgx/host_ecalls.c +++ b/pal/src/host/linux-sgx/host_ecalls.c @@ -9,7 +9,7 @@ int ecall_enclave_start(char* libpal_uri, char* args, size_t args_size, char* env, size_t env_size, int parent_stream_fd, sgx_target_info_t* qe_targetinfo, - struct pal_topo_info* topo_info) { + pal_host_info_t* host_info) { g_rpc_queue = NULL; if (g_pal_enclave.rpc_thread_num > 0) { @@ -30,7 +30,7 @@ int ecall_enclave_start(char* libpal_uri, char* args, size_t args_size, char* en ms.ms_env_size = env_size; ms.ms_parent_stream_fd = parent_stream_fd; ms.ms_qe_targetinfo = qe_targetinfo; - ms.ms_topo_info = topo_info; + ms.ms_host_info = host_info; ms.rpc_queue = g_rpc_queue; return sgx_ecall(ECALL_ENCLAVE_START, &ms); } diff --git a/pal/src/host/linux-sgx/host_ecalls.h b/pal/src/host/linux-sgx/host_ecalls.h index 149daace0c..b30b8050de 100644 --- a/pal/src/host/linux-sgx/host_ecalls.h +++ b/pal/src/host/linux-sgx/host_ecalls.h @@ -3,11 +3,11 @@ #include #include "sgx_arch.h" -#include "pal_topology.h" +#include "pal_ecall_types.h" int ecall_enclave_start(char* libpal_uri, char* args, size_t args_size, char* env, size_t env_size, int parent_stream_fd, sgx_target_info_t* qe_targetinfo, - struct pal_topo_info* topo_info); + pal_host_info_t* host_info); int ecall_thread_start(void); diff --git a/pal/src/host/linux-sgx/host_main.c b/pal/src/host/linux-sgx/host_main.c index 462a193780..0983abb230 100644 --- a/pal/src/host/linux-sgx/host_main.c +++ b/pal/src/host/linux-sgx/host_main.c @@ -15,6 +15,7 @@ #include "gdb_integration/sgx_gdb.h" #include "host_ecalls.h" #include "host_internal.h" +#include "host_info.h" #include "host_log.h" #include "linux_utils.h" #include "pal_internal_arch.h" @@ -614,7 +615,8 @@ static int initialize_enclave(struct pal_enclave* enclave, const char* manifest_ } /* Parses only the information needed by the untrusted PAL to correctly initialize the enclave. */ -static int parse_loader_config(char* manifest, struct pal_enclave* enclave_info) { +static int parse_loader_config(char* manifest, struct pal_enclave* enclave_info, + pal_host_info_t* host_info) { int ret = 0; toml_table_t* manifest_root = NULL; char* dummy_sigfile_str = NULL; @@ -881,6 +883,14 @@ static int parse_loader_config(char* manifest, struct pal_enclave* enclave_info) } g_host_log_level = log_level; + ret = toml_bool_in(manifest_root, "libos.passthrough_etc_files", false, + &host_info->passthrough_etc); + if (ret < 0) { + log_error("Cannot parse 'libos.passthrough_etc_files'"); + ret = -EINVAL; + goto out; + } + ret = 0; out: @@ -895,13 +905,36 @@ static int parse_loader_config(char* manifest, struct pal_enclave* enclave_info) return ret; } +static int get_host_info(pal_host_info_t* host_info, int parent_stream_fd) { + int ret; + + /* Get host information only for the first process. This information will be + * checkpointed and restored during forking of the child process(es). */ + if (parent_stream_fd >= 0) + return 0; + + ret = get_topology_info(&host_info->topo_info); + if (ret < 0) + return ret; + + /* If we do not expose etc, we don't need any addiotianl information about host. */ + if (!host_info->passthrough_etc) + return 0; + + ret = get_hostname(host_info->hostname, sizeof(host_info->hostname)); + if (ret < 0) + return ret; + + return 0; +} + /* Warning: This function does not free up resources on failure - it assumes that the whole process * exits after this function's failure. */ static int load_enclave(struct pal_enclave* enclave, char* args, size_t args_size, char* env, size_t env_size, int parent_stream_fd, bool need_gsgx) { int ret; struct timeval tv; - struct pal_topo_info topo_info = {0}; + pal_host_info_t host_info = {0}; uint64_t start_time; DO_SYSCALL(gettimeofday, &tv, NULL); @@ -911,7 +944,7 @@ static int load_enclave(struct pal_enclave* enclave, char* args, size_t args_siz /* only print during main process's startup (note that this message is always printed) */ log_always("Gramine is starting. Parsing TOML manifest file, this may take some time..."); } - ret = parse_loader_config(enclave->raw_manifest_data, enclave); + ret = parse_loader_config(enclave->raw_manifest_data, enclave, &host_info); if (ret < 0) { log_error("Parsing manifest failed"); return -EINVAL; @@ -925,12 +958,10 @@ static int load_enclave(struct pal_enclave* enclave, char* args, size_t args_siz if (!is_wrfsbase_supported()) return -EPERM; - /* Get host topology information only for the first process. This information will be - * checkpointed and restored during forking of the child process(es). */ - if (parent_stream_fd < 0) { - ret = get_topology_info(&topo_info); - if (ret < 0) - return ret; + ret = get_host_info(&host_info, parent_stream_fd); + if (ret < 0) { + log_error("Unable to get host info"); + return ret; } enclave->libpal_uri = alloc_concat(URI_PREFIX_FILE, URI_PREFIX_FILE_LEN, g_libpal_path, -1); @@ -990,7 +1021,7 @@ static int load_enclave(struct pal_enclave* enclave, char* args, size_t args_siz /* start running trusted PAL */ ecall_enclave_start(enclave->libpal_uri, args, args_size, env, env_size, parent_stream_fd, - &qe_targetinfo, &topo_info); + &qe_targetinfo, &host_info); unmap_tcs(); DO_SYSCALL(munmap, alt_stack, ALT_STACK_SIZE); diff --git a/pal/src/host/linux-sgx/pal_ecall_types.h b/pal/src/host/linux-sgx/pal_ecall_types.h index b215f8a8c1..72337ca576 100644 --- a/pal/src/host/linux-sgx/pal_ecall_types.h +++ b/pal/src/host/linux-sgx/pal_ecall_types.h @@ -17,6 +17,12 @@ enum { struct rpc_queue; +typedef struct { + struct pal_topo_info topo_info; + bool passthrough_etc; + char hostname[PAL_HOSTNAME_MAX]; +} pal_host_info_t; + typedef struct { char* ms_libpal_uri; size_t ms_libpal_uri_len; @@ -26,7 +32,7 @@ typedef struct { size_t ms_env_size; int ms_parent_stream_fd; sgx_target_info_t* ms_qe_targetinfo; - struct pal_topo_info* ms_topo_info; + pal_host_info_t* ms_host_info; struct rpc_queue* rpc_queue; /* pointer to RPC queue in untrusted mem */ } ms_ecall_enclave_start_t; diff --git a/pal/src/host/linux-sgx/pal_linux.h b/pal/src/host/linux-sgx/pal_linux.h index 92eaf7d06c..04daa3e17f 100644 --- a/pal/src/host/linux-sgx/pal_linux.h +++ b/pal/src/host/linux-sgx/pal_linux.h @@ -22,6 +22,7 @@ #include "enclave_ocalls.h" #include "log.h" #include "pal.h" +#include "pal_ecall_types.h" #include "pal_internal.h" #include "pal_linux_defs.h" #include "pal_linux_types.h" @@ -74,7 +75,7 @@ extern size_t g_pal_internal_mem_size; noreturn void pal_linux_main(char* uptr_libpal_uri, size_t libpal_uri_len, char* uptr_args, size_t args_size, char* uptr_env, size_t env_size, int parent_stream_fd, sgx_target_info_t* uptr_qe_targetinfo, - struct pal_topo_info* uptr_topo_info); + pal_host_info_t* uptr_host_info); void pal_start_thread(void); extern char __text_start, __text_end, __data_start, __data_end; diff --git a/pal/src/host/linux-sgx/pal_main.c b/pal/src/host/linux-sgx/pal_main.c index 54458673cb..acf0041bfb 100644 --- a/pal/src/host/linux-sgx/pal_main.c +++ b/pal/src/host/linux-sgx/pal_main.c @@ -208,30 +208,22 @@ static int sanitize_topo_info(struct pal_topo_info* topo_info) { return 0; } -static int import_and_sanitize_topo_info(struct pal_topo_info* uptr_topo_info) { - /* Import topology information via an untrusted pointer. This is only a shallow copy and we use - * this temp variable to do deep copy into `g_pal_public_state.topo_info` */ - struct pal_topo_info shallow_topo_info; - if (!sgx_copy_to_enclave(&shallow_topo_info, sizeof(shallow_topo_info), - uptr_topo_info, sizeof(*uptr_topo_info))) { - return -PAL_ERROR_DENIED; - } - +static int import_and_sanitize_topo_info(struct pal_topo_info* shallow_topo_info) { struct pal_topo_info* topo_info = &g_pal_public_state.topo_info; - size_t caches_cnt = shallow_topo_info.caches_cnt; - size_t threads_cnt = shallow_topo_info.threads_cnt; - size_t cores_cnt = shallow_topo_info.cores_cnt; - size_t sockets_cnt = shallow_topo_info.sockets_cnt; - size_t numa_nodes_cnt = shallow_topo_info.numa_nodes_cnt; + size_t caches_cnt = shallow_topo_info->caches_cnt; + size_t threads_cnt = shallow_topo_info->threads_cnt; + size_t cores_cnt = shallow_topo_info->cores_cnt; + size_t sockets_cnt = shallow_topo_info->sockets_cnt; + size_t numa_nodes_cnt = shallow_topo_info->numa_nodes_cnt; - struct pal_cache_info* caches = sgx_import_array_to_enclave(shallow_topo_info.caches, sizeof(*caches), caches_cnt); - struct pal_cpu_thread_info* threads = sgx_import_array_to_enclave(shallow_topo_info.threads, sizeof(*threads), threads_cnt); - struct pal_cpu_core_info* cores = sgx_import_array_to_enclave(shallow_topo_info.cores, sizeof(*cores), cores_cnt); - struct pal_socket_info* sockets = sgx_import_array_to_enclave(shallow_topo_info.sockets, sizeof(*sockets), sockets_cnt); - struct pal_numa_node_info* numa_nodes = sgx_import_array_to_enclave(shallow_topo_info.numa_nodes, sizeof(*numa_nodes), numa_nodes_cnt); + struct pal_cache_info* caches = sgx_import_array_to_enclave(shallow_topo_info->caches, sizeof(*caches), caches_cnt); + struct pal_cpu_thread_info* threads = sgx_import_array_to_enclave(shallow_topo_info->threads, sizeof(*threads), threads_cnt); + struct pal_cpu_core_info* cores = sgx_import_array_to_enclave(shallow_topo_info->cores, sizeof(*cores), cores_cnt); + struct pal_socket_info* sockets = sgx_import_array_to_enclave(shallow_topo_info->sockets, sizeof(*sockets), sockets_cnt); + struct pal_numa_node_info* numa_nodes = sgx_import_array_to_enclave(shallow_topo_info->numa_nodes, sizeof(*numa_nodes), numa_nodes_cnt); - size_t* distances = sgx_import_array2d_to_enclave(shallow_topo_info.numa_distance_matrix, + size_t* distances = sgx_import_array2d_to_enclave(shallow_topo_info->numa_distance_matrix, sizeof(*distances), numa_nodes_cnt, numa_nodes_cnt); @@ -255,6 +247,64 @@ static int import_and_sanitize_topo_info(struct pal_topo_info* uptr_topo_info) { return sanitize_topo_info(topo_info); } +static bool is_hostname_valid(const char* hostname) { + const char* ptr = hostname; + size_t chrcount = 0; + + if (*ptr == '-') + return false; + + while (*ptr != '\0') { + if ((*ptr >= 'a' && *ptr <= 'z') || + (*ptr >= 'A' && *ptr <= 'Z') || + (*ptr >= '0' && *ptr <= '9') || + *ptr == '-') { + chrcount++; + ptr++; + continue; + } else if (*ptr == '.') { + if (chrcount == 0 || chrcount > 63) { + return false; + } + chrcount = 0; + ptr++; + continue; + } + + return false; + } + + if (chrcount == 0 || chrcount > 63) + return false; + /* rewind to last character */ + ptr--; + if (ptr - hostname > PAL_HOSTNAME_MAX) + return false; + if (*ptr == '-') + return false; + + return true; +} + +static int init_passthrough_etc_files(pal_host_info_t* shallow_host_info) { + coerce_untrusted_bool(&shallow_host_info->passthrough_etc); + + if (!shallow_host_info->passthrough_etc) + return 0; + + g_pal_public_state.passthrough_etc_files = true; + + if (!is_hostname_valid(shallow_host_info->hostname)) { + log_warning("The hostname on the host seems to be invalid." + "The Gramine hostname will be set to \"localhost\"."); + } else { + memcpy(g_pal_public_state.hostname, shallow_host_info->hostname, + sizeof(g_pal_public_state.hostname) - 1); + } + + return 0; +} + extern void* g_enclave_base; extern void* g_enclave_top; extern bool g_allowed_files_warn; @@ -416,7 +466,7 @@ __attribute_no_stack_protector noreturn void pal_linux_main(char* uptr_libpal_uri, size_t libpal_uri_len, char* uptr_args, size_t args_size, char* uptr_env, size_t env_size, int parent_stream_fd, sgx_target_info_t* uptr_qe_targetinfo, - struct pal_topo_info* uptr_topo_info) { + pal_host_info_t* uptr_host_info) { /* All our arguments are coming directly from the host. We are responsible to check them. */ int ret; @@ -559,6 +609,15 @@ noreturn void pal_linux_main(char* uptr_libpal_uri, size_t libpal_uri_len, char* g_pal_internal_mem_size += 64 * 1024 * 1024; /* 5MB manifest -> 64 + 64 MB PAL mem */ } + /* get host information */ + pal_host_info_t host_info = {0}; + assert(uptr_host_info != NULL); + if (!sgx_copy_to_enclave(&host_info, sizeof(host_info), uptr_host_info, + sizeof(host_info))) { + log_error("Unable to read host info"); + ocall_exit(1, /*is_exitgroup=*/true); + } + /* parse manifest */ char errbuf[256]; toml_table_t* manifest_root = toml_parse(manifest_addr, errbuf, sizeof(errbuf)); @@ -573,7 +632,7 @@ noreturn void pal_linux_main(char* uptr_libpal_uri, size_t libpal_uri_len, char* * checkpointed and restored during forking of the child process(es). */ if (parent_stream_fd < 0) { /* parse and store host topology info into g_pal_public_state struct */ - ret = import_and_sanitize_topo_info(uptr_topo_info); + ret = import_and_sanitize_topo_info(&host_info.topo_info); if (ret < 0) { log_error("Failed to copy and sanitize topology information"); ocall_exit(1, /*is_exitgroup=*/true); @@ -628,6 +687,15 @@ noreturn void pal_linux_main(char* uptr_libpal_uri, size_t libpal_uri_len, char* ocall_exit(1, /*is_exitgroup=*/true); } + /* Get host information only for the first process. This information will be + * checkpointed and restored during forking of the child process(es). */ + if (parent_stream_fd < 0) { + if ((ret = init_passthrough_etc_files(&host_info)) < 0) { + log_error("Failed to initialize etc files: %d", ret); + ocall_exit(1, /*is_exitgroup=*/true); + } + } + enum sgx_attestation_type attestation_type; ret = parse_attestation_type(g_pal_public_state.manifest_root, &attestation_type); if (ret < 0) { diff --git a/pal/src/host/linux-sgx/pal_misc.c b/pal/src/host/linux-sgx/pal_misc.c index f9f77b0c23..74d579d1f1 100644 --- a/pal/src/host/linux-sgx/pal_misc.c +++ b/pal/src/host/linux-sgx/pal_misc.c @@ -22,6 +22,7 @@ #include "spinlock.h" #include "toml_utils.h" #include "topo_info.h" +#include "linux_utils.h" /* The timeout of 50ms was found to be a safe TSC drift correction periodicity based on results * from multiple systems. Any higher or lower could pose risks of negative time drift or diff --git a/pal/src/host/linux/pal_main.c b/pal/src/host/linux/pal_main.c index bd4b4f9083..cf30b8e54b 100644 --- a/pal/src/host/linux/pal_main.c +++ b/pal/src/host/linux/pal_main.c @@ -14,6 +14,7 @@ #include "cpu.h" #include "debug_map.h" #include "elf/elf.h" +#include "host_info.h" #include "init.h" #include "linux_utils.h" #include "pal.h" @@ -120,6 +121,27 @@ noreturn static void print_usage_and_exit(const char* argv_0) { _PalProcessExit(1); } +static void get_host_info(void) { + int ret; + + ret = get_topology_info(&g_pal_public_state.topo_info); + if (ret < 0) + INIT_FAIL("get_topology_info() failed: %d", ret); + + ret = toml_bool_in(g_pal_public_state.manifest_root, "libos.passthrough_etc_files", + false, &g_pal_public_state.passthrough_etc_files); + if (ret < 0) + INIT_FAIL("Unable to parse manifest"); + + if (!g_pal_public_state.passthrough_etc_files) + return; + + if (get_hostname(g_pal_public_state.hostname, + sizeof(g_pal_public_state.hostname)) < 0) { + INIT_FAIL("Unable to get hostname"); + } +} + #ifdef ASAN __attribute_no_stack_protector __attribute_no_sanitize_address @@ -264,14 +286,6 @@ noreturn void pal_linux_main(void* initial_rsp, void* fini_callback) { INIT_FAIL("failed to init debug maps: %d", unix_to_pal_error(ret)); #endif - /* Get host topology information only for the first process. This information will be - * checkpointed and restored during forking of the child process(es). */ - if (first_process) { - ret = get_topology_info(&g_pal_public_state.topo_info); - if (ret < 0) - INIT_FAIL("get_topology_info() failed: %d", ret); - } - g_pal_loader_path = get_main_exec_path(); g_libpal_path = strdup(argv[1]); if (!g_pal_loader_path || !g_libpal_path) { @@ -403,6 +417,12 @@ noreturn void pal_linux_main(void* initial_rsp, void* fini_callback) { INIT_FAIL("Cannot parse 'loader.pal_internal_mem_size'"); } + /* Host information only for the first process. This information will be + * checkpointed and restored during forking of the child process(es). */ + if (first_process) { + get_host_info(); + } + void* internal_mem_addr = (void*)DO_SYSCALL(mmap, NULL, g_pal_internal_mem_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); diff --git a/pal/src/pal_main.c b/pal/src/pal_main.c index c21f0d9c13..f4583fd160 100644 --- a/pal/src/pal_main.c +++ b/pal/src/pal_main.c @@ -21,6 +21,7 @@ struct pal_common_state g_pal_common_state; struct pal_public_state g_pal_public_state = { /* Enable log to catch early initialization errors; it will be overwritten in pal_main(). */ .log_level = PAL_LOG_DEFAULT_LEVEL, + .hostname = "localhost" }; struct pal_public_state* PalGetPalPublicState(void) {