From eca7704f328250f3f44ecff69f645789ba0448b9 Mon Sep 17 00:00:00 2001 From: jorge Date: Wed, 8 Nov 2017 23:23:52 +0000 Subject: [PATCH 1/6] patches for udocker-1.1.1 --- src/GNUmakefile | 4 +- src/tracee/event.c | 266 ++++++++++++++++++++++++++++++++++++++++++++- src/tracee/event.h | 1 + 3 files changed, 268 insertions(+), 3 deletions(-) diff --git a/src/GNUmakefile b/src/GNUmakefile index f584631a..d84db403 100644 --- a/src/GNUmakefile +++ b/src/GNUmakefile @@ -19,9 +19,9 @@ HAS_PYTHON_CONFIG := $(shell python2.7-config --ldflags 2>/dev/null) CPPFLAGS += -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -I. -I$(VPATH) CFLAGS += -Wall -Wextra -O2 -LDFLAGS += -ltalloc -Wl,-z,noexecstack +LDFLAGS += -L. -ltalloc -Wl,-z,noexecstack -static -CARE_LDFLAGS = -larchive +CARE_LDFLAGS = -larchive -static OBJECTS += \ cli/cli.o \ diff --git a/src/tracee/event.c b/src/tracee/event.c index db8cb21c..e87afe58 100644 --- a/src/tracee/event.c +++ b/src/tracee/event.c @@ -20,6 +20,7 @@ * 02110-1301 USA. */ +#include #include /* CLONE_*, */ #include /* pid_t, */ #include /* ptrace(1), PTRACE_*, */ @@ -49,6 +50,7 @@ #include "attribute.h" #include "compat.h" + /** * Start @tracee->exe with the given @argv[]. This function * returns -errno if an error occurred, otherwise 0. @@ -205,6 +207,29 @@ static void print_talloc_hierarchy(int signum, siginfo_t *siginfo UNUSED, void * static int last_exit_status = -1; +/** + * Check if kernel >= 4.8 + */ +bool is_kernel_4_8(void) { + static int version_48 = -1; + static int major = 0; + static int minor = 0; + + if (version_48 != -1) + return version_48; + + version_48 = false; + struct utsname utsname; + if (uname(&utsname) < 0) + return false; + + sscanf(utsname.release, "%d.%d", &major, &minor); + if (major >= 4) + if (minor >= 8) + version_48 = true; + return version_48; +} + /** * Check if this instance of PRoot can *technically* handle @tracee. */ @@ -254,6 +279,9 @@ int event_loop() struct sigaction signal_action; long status; int signum; + int kernel_4_8; + + kernel_4_8 = is_kernel_4_8(); /* Kill all tracees when exiting. */ status = atexit(kill_all_tracees); @@ -347,7 +375,12 @@ int event_loop() continue; } - signal = handle_tracee_event(tracee, tracee_status); + if (kernel_4_8) { + signal = handle_tracee_event_kernel_4_8(tracee, tracee_status); + } + else { + signal = handle_tracee_event(tracee, tracee_status); + } (void) restart_tracee(tracee, signal); } @@ -355,6 +388,236 @@ int event_loop() } /** + * For kernels >= 4.8.0 + * Handle the current event (@tracee_status) of the given @tracee. + * This function returns the "computed" signal that should be used to + * restart the given @tracee. + */ +int handle_tracee_event_kernel_4_8(Tracee *tracee, int tracee_status) +{ + static bool seccomp_detected = false; + static bool seccomp_enabled = false; /* added for 4.8.0 */ + long status; + int signal; + + /* Don't overwrite restart_how if it is explicitly set + * elsewhere, i.e in the ptrace emulation when single + * stepping. */ + if (tracee->restart_how == 0) { + /* When seccomp is enabled, all events are restarted in + * non-stop mode, but this default choice could be overwritten + * later if necessary. The check against "sysexit_pending" + * ensures PTRACE_SYSCALL (used to hit the exit stage under + * seccomp) is not cleared due to an event that would happen + * before the exit stage, eg. PTRACE_EVENT_EXEC for the exit + * stage of execve(2). */ + if (tracee->seccomp == ENABLED && !tracee->sysexit_pending) + tracee->restart_how = PTRACE_CONT; + else + tracee->restart_how = PTRACE_SYSCALL; + } + + /* Not a signal-stop by default. */ + signal = 0; + + if (WIFEXITED(tracee_status)) { + last_exit_status = WEXITSTATUS(tracee_status); + VERBOSE(tracee, 1, + "vpid %" PRIu64 ": exited with status %d", + tracee->vpid, last_exit_status); + terminate_tracee(tracee); + } + else if (WIFSIGNALED(tracee_status)) { + check_architecture(tracee); + VERBOSE(tracee, (int) (last_exit_status != -1), + "vpid %" PRIu64 ": terminated with signal %d", + tracee->vpid, WTERMSIG(tracee_status)); + terminate_tracee(tracee); + } + else if (WIFSTOPPED(tracee_status)) { + /* Don't use WSTOPSIG() to extract the signal + * since it clears the PTRACE_EVENT_* bits. */ + signal = (tracee_status & 0xfff00) >> 8; + + switch (signal) { + static bool deliver_sigtrap = false; + + case SIGTRAP: { + const unsigned long default_ptrace_options = ( + PTRACE_O_TRACESYSGOOD | + PTRACE_O_TRACEFORK | + PTRACE_O_TRACEVFORK | + PTRACE_O_TRACEVFORKDONE | + PTRACE_O_TRACEEXEC | + PTRACE_O_TRACECLONE | + PTRACE_O_TRACEEXIT); + + /* Distinguish some events from others and + * automatically trace each new process with + * the same options. + * + * Note that only the first bare SIGTRAP is + * related to the tracing loop, others SIGTRAP + * carry tracing information because of + * TRACE*FORK/CLONE/EXEC. */ + if (deliver_sigtrap) + break; /* Deliver this signal as-is. */ + + deliver_sigtrap = true; + + /* Try to enable seccomp mode 2... */ + status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL, + default_ptrace_options | PTRACE_O_TRACESECCOMP); + if (status < 0) { + seccomp_enabled = false; + /* ... otherwise use default options only. */ + status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL, + default_ptrace_options); + if (status < 0) { + note(tracee, ERROR, SYSTEM, "ptrace(PTRACE_SETOPTIONS)"); + exit(EXIT_FAILURE); + } + } + else { + if (getenv("PROOT_NO_SECCOMP") == NULL) + seccomp_enabled = true; + } + } + /* Fall through. */ + case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8: + case SIGTRAP | PTRACE_EVENT_SECCOMP << 8: + + if (!seccomp_detected && seccomp_enabled) { + VERBOSE(tracee, 1, "ptrace acceleration (seccomp mode 2) enabled"); + tracee->seccomp = ENABLED; + seccomp_detected = true; + } + + if (signal == (SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8) || + signal == (SIGTRAP | PTRACE_EVENT_SECCOMP << 8)) { + + unsigned long flags = 0; + signal = 0; + + /* SECCOMP TRAP can only be received for + * sysenter events, ignore otherwise */ + if (!IS_IN_SYSENTER(tracee)) { + tracee->restart_how = PTRACE_CONT; + return 0; + } + status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags); + if (status < 0) + break; + + if (tracee->seccomp == ENABLED && (flags & FILTER_SYSEXIT) == 0) { + tracee->restart_how = PTRACE_CONT; + translate_syscall(tracee); + + if (tracee->seccomp == DISABLING) + tracee->restart_how = PTRACE_SYSCALL; + break; + } + } + + /* Fall through. */ + case SIGTRAP | 0x80: + + signal = 0; + + /* This tracee got signaled then freed during the + sysenter stage but the kernel reports the sysexit + stage; just discard this spurious tracee/event. */ + + if (tracee->exe == NULL) { + tracee->restart_how = PTRACE_CONT; /* SYSCALL OR CONT */ + return 0; + } + + switch (tracee->seccomp) { + case ENABLED: + if (IS_IN_SYSENTER(tracee)) { + /* sysenter: ensure the sysexit + * stage will be hit under seccomp. */ + tracee->restart_how = PTRACE_SYSCALL; + tracee->sysexit_pending = true; + } + else { + /* sysexit: the next sysenter + * will be notified by seccomp. */ + tracee->restart_how = PTRACE_CONT; + tracee->sysexit_pending = false; + } + /* Fall through. */ + case DISABLED: + translate_syscall(tracee); + + /* This syscall has disabled seccomp. */ + if (tracee->seccomp == DISABLING) { + tracee->restart_how = PTRACE_SYSCALL; + tracee->seccomp = DISABLED; + } + + break; + + case DISABLING: + /* Seccomp was disabled by the + * previous syscall, but its sysenter + * stage was already handled. */ + tracee->seccomp = DISABLED; + if (IS_IN_SYSENTER(tracee)) + tracee->status = 1; + break; + } + break; + + case SIGTRAP | PTRACE_EVENT_VFORK << 8: + signal = 0; + (void) new_child(tracee, CLONE_VFORK); + break; + + case SIGTRAP | PTRACE_EVENT_FORK << 8: + case SIGTRAP | PTRACE_EVENT_CLONE << 8: + signal = 0; + (void) new_child(tracee, 0); + break; + + case SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8: + case SIGTRAP | PTRACE_EVENT_EXEC << 8: + case SIGTRAP | PTRACE_EVENT_EXIT << 8: + signal = 0; + break; + + case SIGSTOP: + /* Stop this tracee until PRoot has received + * the EVENT_*FORK|CLONE notification. */ + if (tracee->exe == NULL) { + tracee->sigstop = SIGSTOP_PENDING; + signal = -1; + } + + /* For each tracee, the first SIGSTOP + * is only used to notify the tracer. */ + if (tracee->sigstop == SIGSTOP_IGNORED) { + tracee->sigstop = SIGSTOP_ALLOWED; + signal = 0; + } + break; + + default: + /* Deliver this signal as-is. */ + break; + } + } + + /* Clear the pending event, if any. */ + tracee->as_ptracee.event4.proot.pending = false; + + return signal; +} + + +/** + * For kernels < 4.8.0 * Handle the current event (@tracee_status) of the given @tracee. * This function returns the "computed" signal that should be used to * restart the given @tracee. @@ -627,6 +890,7 @@ int handle_tracee_event(Tracee *tracee, int tracee_status) return signal; } + /** * Restart the given @tracee with the specified @signal. This * function returns false if the tracee was not restarted (error or diff --git a/src/tracee/event.h b/src/tracee/event.h index ff00cfbc..7899e730 100644 --- a/src/tracee/event.h +++ b/src/tracee/event.h @@ -30,6 +30,7 @@ extern int launch_process(Tracee *tracee, char *const argv[]); extern int event_loop(); extern int handle_tracee_event(Tracee *tracee, int tracee_status); +extern int handle_tracee_event_kernel_4_8(Tracee *tracee, int tracee_status); extern bool restart_tracee(Tracee *tracee, int signal); #endif /* TRACEE_EVENT_H */ From 6797b9e2fe1152b9fee8eff9ced1318ef18d4642 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Fri, 13 Apr 2018 14:35:49 +0900 Subject: [PATCH 2/6] add rootlesscontainers support This commit allows preserving file owner information as `user.rootlesscontainers` xattr values. Please refer to https://rootlesscontaine.rs/ for further information. Signed-off-by: Akihiro Suda --- doc/proot/rpm-spec | 2 +- doc/proot/stylesheets/rpm-spec.xsl | 2 +- src/GNUmakefile | 13 ++ src/extension/fake_id0/fake_id0.c | 158 +++++++++++++- src/rootlesscontainers/COPYING.APACHE2 | 202 ++++++++++++++++++ src/rootlesscontainers/README.md | 13 ++ .../rootlesscontainers.pb-c.c | 103 +++++++++ .../rootlesscontainers.pb-c.h | 86 ++++++++ .../rootlesscontainers.proto | 33 +++ 9 files changed, 609 insertions(+), 3 deletions(-) create mode 100644 src/rootlesscontainers/COPYING.APACHE2 create mode 100644 src/rootlesscontainers/README.md create mode 100644 src/rootlesscontainers/rootlesscontainers.pb-c.c create mode 100644 src/rootlesscontainers/rootlesscontainers.pb-c.h create mode 100644 src/rootlesscontainers/rootlesscontainers.proto diff --git a/doc/proot/rpm-spec b/doc/proot/rpm-spec index 686f96f6..229ab11b 100644 --- a/doc/proot/rpm-spec +++ b/doc/proot/rpm-spec @@ -10,7 +10,7 @@ Buildroot : %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) Prefix : /usr Name : proot -BuildRequires: libtalloc-devel +BuildRequires: libtalloc-devel, protobuf-c-devel, libattr-devel %if 0%{?suse_version} >= 1210 || 0%{?fedora_version} >= 15 BuildRequires: glibc-static diff --git a/doc/proot/stylesheets/rpm-spec.xsl b/doc/proot/stylesheets/rpm-spec.xsl index 497de088..df58ce63 100644 --- a/doc/proot/stylesheets/rpm-spec.xsl +++ b/doc/proot/stylesheets/rpm-spec.xsl @@ -15,7 +15,7 @@ Buildroot : %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) Prefix : /usr Name : proot -BuildRequires: libtalloc-devel +BuildRequires: libtalloc-devel, protobuf-c-devel, libattr-devel %if 0%{?suse_version} >= 1210 || 0%{?fedora_version} >= 15 BuildRequires: glibc-static diff --git a/src/GNUmakefile b/src/GNUmakefile index d84db403..84902bdb 100644 --- a/src/GNUmakefile +++ b/src/GNUmakefile @@ -1,6 +1,8 @@ # If you want to build outside of the source tree, use the -f option: # make -f ${SOMEWHERE}/proot/src/GNUmakefile +PERSISTENT_CHOWN := 1 + # the VPATH variable must point to the actual makefile directory VPATH := $(dir $(lastword $(MAKEFILE_LIST))) SRC = $(dir $(firstword $(MAKEFILE_LIST))) @@ -18,8 +20,15 @@ HAS_SWIG := $(shell swig -version 2>/dev/null) HAS_PYTHON_CONFIG := $(shell python2.7-config --ldflags 2>/dev/null) CPPFLAGS += -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -I. -I$(VPATH) +ifdef PERSISTENT_CHOWN + CPPFLAGS += -DPERSISTENT_CHOWN + CPPFLAGS += $(shell pkg-config --cflags libprotobuf-c) +endif CFLAGS += -Wall -Wextra -O2 LDFLAGS += -L. -ltalloc -Wl,-z,noexecstack -static +ifdef PERSISTENT_CHOWN + LDFLAGS += $(shell pkg-config --libs libprotobuf-c) +endif CARE_LDFLAGS = -larchive -static @@ -64,6 +73,10 @@ OBJECTS += \ extension/portmap/map.o \ loader/loader-wrapped.o +ifdef PERSISTENT_CHOWN + OBJECTS += rootlesscontainers/rootlesscontainers.pb-c.o +endif + define define_from_arch.h $2$1 := $(shell $(CC) $1 -E -dM -DNO_LIBC_HEADER $(SRC)/arch.h | grep -w $2 | cut -f 3 -d ' ') endef diff --git a/src/extension/fake_id0/fake_id0.c b/src/extension/fake_id0/fake_id0.c index 9e1d0712..674e12be 100644 --- a/src/extension/fake_id0/fake_id0.c +++ b/src/extension/fake_id0/fake_id0.c @@ -44,6 +44,81 @@ #include "path/binding.h" #include "arch.h" +#if defined(PERSISTENT_CHOWN) +#include +#include +#include "path/path.h" +#include "rootlesscontainers/rootlesscontainers.pb-c.h" +#define XATTR_USER_ROOTLESSCONTAINERS "user.rootlesscontainers" + +// return 0 on success. +int rootlesscontainers_get_xattr (int fd, const char *path, uid_t *uid, gid_t *gid) { + Rootlesscontainers__Resource *msg; + void *buf; + assert(fd >= 0 || path != NULL); + ssize_t bufsz = (path != NULL) ? + lgetxattr(path, XATTR_USER_ROOTLESSCONTAINERS, NULL, 0) : + fgetxattr(fd, XATTR_USER_ROOTLESSCONTAINERS, NULL, 0); + // errno can be ENOATTR, EBADF... Typically not critical. + if (bufsz <= 0) + return bufsz; + buf = malloc(bufsz); + if (buf == NULL) + return -1; + bufsz = (path != NULL) ? + lgetxattr(path, XATTR_USER_ROOTLESSCONTAINERS, buf, bufsz) : + fgetxattr(fd, XATTR_USER_ROOTLESSCONTAINERS, buf, bufsz); + if (bufsz <= 0) + return bufsz; + msg = rootlesscontainers__resource__unpack(NULL, bufsz, buf); + if (msg == NULL) { + free(buf); + return -1; + } + if (!msg->has_uid) + msg->uid = (uid_t)-1; + if (!msg->has_gid) + msg->gid = (gid_t)-1; + *uid = (uid_t)msg->uid; + *gid = (gid_t)msg->gid; + free(buf); + rootlesscontainers__resource__free_unpacked(msg, NULL); + return 0; +} + +int rootlesscontainers_set_xattr (int fd, const char *path, uid_t uid, gid_t gid) { + int rc; + Rootlesscontainers__Resource msg = ROOTLESSCONTAINERS__RESOURCE__INIT; + void *buf; + size_t bufsz; + assert(fd >= 0 || path != NULL); + msg.has_uid = true; + msg.uid = (uint32_t)uid; + msg.has_gid = true; + msg.gid = (uint32_t)gid; + bufsz = rootlesscontainers__resource__get_packed_size(&msg); + buf = malloc(bufsz); + if (buf == NULL) + return -1; + rootlesscontainers__resource__pack(&msg, buf); + rc = (path != NULL) ? + lsetxattr(path, XATTR_USER_ROOTLESSCONTAINERS, buf, bufsz, 0): + fsetxattr(fd, XATTR_USER_ROOTLESSCONTAINERS, buf, bufsz, 0); + free(buf); + return rc; +} + +// ENOATTR is ignored +int rootlesscontainers_remove_xattr (int fd, const char *path) { + assert(fd >= 0 || path != NULL); + int rc = (path != NULL) ? + lremovexattr(path, XATTR_USER_ROOTLESSCONTAINERS): + fremovexattr(fd, XATTR_USER_ROOTLESSCONTAINERS); + return ( rc < 0 && errno == ENOATTR) ? 0 : rc; +} + +#endif /* if defined(PERSISTENT_CHOWN) */ + typedef struct { uid_t ruid; uid_t euid; @@ -231,7 +306,7 @@ static void override_permissions(const Tracee *tracee, const char *path, bool is /** * Adjust current @tracee's syscall parameters according to @config. - * This function always returns 0. + * This function returns 0 unless there is an error. */ static int handle_sysenter_end(Tracee *tracee, const Config *config) { @@ -270,6 +345,12 @@ static int handle_sysenter_end(Tracee *tracee, const Config *config) Reg gid_sysarg; uid_t uid; gid_t gid; +#if defined(PERSISTENT_CHOWN) + int status; + int fd = -1; // shut up gcc uninitialization warning + char path[PATH_MAX]; + bool with_path = (sysnum == PR_chown || sysnum == PR_chown32 || sysnum == PR_lchown || sysnum == PR_lchown32 || sysnum == PR_fchownat); +#endif if (sysnum == PR_fchownat) { uid_sysarg = SYSARG_3; @@ -283,6 +364,41 @@ static int handle_sysenter_end(Tracee *tracee, const Config *config) uid = peek_reg(tracee, ORIGINAL, uid_sysarg); gid = peek_reg(tracee, ORIGINAL, gid_sysarg); +#if defined(PERSISTENT_CHOWN) + if (with_path) { + word_t input; + char guestpath[PATH_MAX]; + int dirfd = AT_FDCWD; + if (sysnum == PR_fchownat) { + dirfd = peek_reg(tracee, ORIGINAL, SYSARG_1); + input = peek_reg(tracee, ORIGINAL, SYSARG_2); + status = read_path(tracee, guestpath, input); + } else { + input = peek_reg(tracee, ORIGINAL, SYSARG_1); + status = read_path(tracee, guestpath, input); + } + if (status < 0) + return status; + status = translate_path(tracee, path, dirfd, guestpath, false); + if (status < 0) + return status; + } else { + fd = peek_reg(tracee, ORIGINAL, SYSARG_1); + } + + // TODO: we can also remove xattr when [ug]id == -1 && the recoded one == config->r[ug]id + if (uid != config->ruid || gid != config->rgid) + status = rootlesscontainers_set_xattr(fd, with_path ? path : NULL, uid, gid); + else + status = rootlesscontainers_remove_xattr(fd, with_path ? path : NULL); + + if (status < 0) + return status; + + /* this should always succeed */ + poke_reg(tracee, uid_sysarg, getuid()); + poke_reg(tracee, gid_sysarg, getgid()); +#else /* Swap actual and emulated ids to get a chance of * success. */ if (uid == config->ruid) @@ -290,6 +406,7 @@ static int handle_sysenter_end(Tracee *tracee, const Config *config) if (gid == config->rgid) poke_reg(tracee, gid_sysarg, getgid()); +#endif return 0; } @@ -624,6 +741,14 @@ static int handle_sysexit_end(Tracee *tracee, Config *config) Reg sysarg; uid_t uid; gid_t gid; +#if defined(PERSISTENT_CHOWN) + int status; + char path[PATH_MAX]; + bool with_path = !(sysnum == PR_fstat64 || sysnum == PR_fstat); + int fd = -1; + uid_t uid_xattr; + gid_t gid_xattr; +#endif /* Override only if it succeed. */ result = peek_reg(tracee, CURRENT, SYSARG_RESULT); @@ -659,6 +784,37 @@ static int handle_sysexit_end(Tracee *tracee, Config *config) if (gid == getgid()) poke_uint32(tracee, address + offsetof_stat_gid(tracee), config->sgid); +#if defined(PERSISTENT_CHOWN) + if (with_path) { + word_t input; + char guestpath[PATH_MAX]; + int dirfd = AT_FDCWD; + if (sysnum == PR_fstatat64 || sysnum == PR_newfstatat) { + dirfd = peek_reg(tracee, ORIGINAL, SYSARG_1); + input = peek_reg(tracee, ORIGINAL, SYSARG_2); + status = read_path(tracee, guestpath, input); + } else { + input = peek_reg(tracee, ORIGINAL, SYSARG_1); + status = read_path(tracee, guestpath, input); + } + if (status < 0) + return status; + status = translate_path(tracee, path, dirfd, guestpath, false); + if (status < 0) + return status; + } else { + fd = peek_reg(tracee, ORIGINAL, SYSARG_1); + } + status = rootlesscontainers_get_xattr(fd, with_path ? path: NULL, &uid_xattr, &gid_xattr); + // error is not critical, typically. e.g. ENOATTR + if (status >= 0) { + if (uid_xattr != (uid_t)-1) + poke_uint32(tracee, address + offsetof_stat_uid(tracee), uid_xattr); + if (gid_xattr != (gid_t)-1) + poke_uint32(tracee, address + offsetof_stat_gid(tracee), gid_xattr); + } +#endif + return 0; } diff --git a/src/rootlesscontainers/COPYING.APACHE2 b/src/rootlesscontainers/COPYING.APACHE2 new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/src/rootlesscontainers/COPYING.APACHE2 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/rootlesscontainers/README.md b/src/rootlesscontainers/README.md new file mode 100644 index 00000000..acc6fb85 --- /dev/null +++ b/src/rootlesscontainers/README.md @@ -0,0 +1,13 @@ +# rootlesscontainers.proto + +This directory contains `rootlesscontainers.proto`, which is used for preserving emulated file owner information as `user.rootlesscontainers` xattr values. + +## Source + +https://raw.githubusercontent.com/rootless-containers/rootlesscontaine.rs/f7b0f5486bb0e0be75771ce50c54471296f040eb/docs/proto/rootlesscontainers.proto + +## Compile + +```console +$ protoc-c --c_out=. rootlesscontainers.proto +``` diff --git a/src/rootlesscontainers/rootlesscontainers.pb-c.c b/src/rootlesscontainers/rootlesscontainers.pb-c.c new file mode 100644 index 00000000..96753b0e --- /dev/null +++ b/src/rootlesscontainers/rootlesscontainers.pb-c.c @@ -0,0 +1,103 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: rootlesscontainers.proto */ + +/* Do not generate deprecated warnings for self */ +#ifndef PROTOBUF_C__NO_DEPRECATED +#define PROTOBUF_C__NO_DEPRECATED +#endif + +#include "rootlesscontainers.pb-c.h" +void rootlesscontainers__resource__init + (Rootlesscontainers__Resource *message) +{ + static Rootlesscontainers__Resource init_value = ROOTLESSCONTAINERS__RESOURCE__INIT; + *message = init_value; +} +size_t rootlesscontainers__resource__get_packed_size + (const Rootlesscontainers__Resource *message) +{ + assert(message->base.descriptor == &rootlesscontainers__resource__descriptor); + return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message)); +} +size_t rootlesscontainers__resource__pack + (const Rootlesscontainers__Resource *message, + uint8_t *out) +{ + assert(message->base.descriptor == &rootlesscontainers__resource__descriptor); + return protobuf_c_message_pack ((const ProtobufCMessage*)message, out); +} +size_t rootlesscontainers__resource__pack_to_buffer + (const Rootlesscontainers__Resource *message, + ProtobufCBuffer *buffer) +{ + assert(message->base.descriptor == &rootlesscontainers__resource__descriptor); + return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer); +} +Rootlesscontainers__Resource * + rootlesscontainers__resource__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data) +{ + return (Rootlesscontainers__Resource *) + protobuf_c_message_unpack (&rootlesscontainers__resource__descriptor, + allocator, len, data); +} +void rootlesscontainers__resource__free_unpacked + (Rootlesscontainers__Resource *message, + ProtobufCAllocator *allocator) +{ + assert(message->base.descriptor == &rootlesscontainers__resource__descriptor); + protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator); +} +static const ProtobufCFieldDescriptor rootlesscontainers__resource__field_descriptors[2] = +{ + { + "uid", + 1, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Rootlesscontainers__Resource, has_uid), + offsetof(Rootlesscontainers__Resource, uid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, + { + "gid", + 2, + PROTOBUF_C_LABEL_OPTIONAL, + PROTOBUF_C_TYPE_UINT32, + offsetof(Rootlesscontainers__Resource, has_gid), + offsetof(Rootlesscontainers__Resource, gid), + NULL, + NULL, + 0, /* flags */ + 0,NULL,NULL /* reserved1,reserved2, etc */ + }, +}; +static const unsigned rootlesscontainers__resource__field_indices_by_name[] = { + 1, /* field[1] = gid */ + 0, /* field[0] = uid */ +}; +static const ProtobufCIntRange rootlesscontainers__resource__number_ranges[1 + 1] = +{ + { 1, 0 }, + { 0, 2 } +}; +const ProtobufCMessageDescriptor rootlesscontainers__resource__descriptor = +{ + PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC, + "rootlesscontainers.Resource", + "Resource", + "Rootlesscontainers__Resource", + "rootlesscontainers", + sizeof(Rootlesscontainers__Resource), + 2, + rootlesscontainers__resource__field_descriptors, + rootlesscontainers__resource__field_indices_by_name, + 1, rootlesscontainers__resource__number_ranges, + (ProtobufCMessageInit) rootlesscontainers__resource__init, + NULL,NULL,NULL /* reserved[123] */ +}; diff --git a/src/rootlesscontainers/rootlesscontainers.pb-c.h b/src/rootlesscontainers/rootlesscontainers.pb-c.h new file mode 100644 index 00000000..a999faf3 --- /dev/null +++ b/src/rootlesscontainers/rootlesscontainers.pb-c.h @@ -0,0 +1,86 @@ +/* Generated by the protocol buffer compiler. DO NOT EDIT! */ +/* Generated from: rootlesscontainers.proto */ + +#ifndef PROTOBUF_C_rootlesscontainers_2eproto__INCLUDED +#define PROTOBUF_C_rootlesscontainers_2eproto__INCLUDED + +#include + +PROTOBUF_C__BEGIN_DECLS + +#if PROTOBUF_C_VERSION_NUMBER < 1000000 +# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers. +#elif 1002001 < PROTOBUF_C_MIN_COMPILER_VERSION +# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c. +#endif + + +typedef struct _Rootlesscontainers__Resource Rootlesscontainers__Resource; + + +/* --- enums --- */ + + +/* --- messages --- */ + +/* + * Resource defines the schema for "user.rootlesscontainers" xattr values. + * The resource can be used as a persistent storage for emulated `chown(2)` syscall. + * Syscall emulators SHOULD try to hide this xattr from the emulated environment. + */ +struct _Rootlesscontainers__Resource +{ + ProtobufCMessage base; + /* + * Zero-value MUST be parsed as a literally zero-value, not "unset". + * To keep both uid and gid unchaged, the entire xattr value SHOULD be removed. + * To keep either one of uid or gid unchaged, 0xFFFFFFFF (in other words, + * `(uint32_t) -1`, see also chown(2)) value SHOULD be set. + * (Because some protobuf bindings cannot distinguish "unset" from zero-value.) + */ + protobuf_c_boolean has_uid; + uint32_t uid; + protobuf_c_boolean has_gid; + uint32_t gid; +}; +#define ROOTLESSCONTAINERS__RESOURCE__INIT \ + { PROTOBUF_C_MESSAGE_INIT (&rootlesscontainers__resource__descriptor) \ + , 0,0, 0,0 } + + +/* Rootlesscontainers__Resource methods */ +void rootlesscontainers__resource__init + (Rootlesscontainers__Resource *message); +size_t rootlesscontainers__resource__get_packed_size + (const Rootlesscontainers__Resource *message); +size_t rootlesscontainers__resource__pack + (const Rootlesscontainers__Resource *message, + uint8_t *out); +size_t rootlesscontainers__resource__pack_to_buffer + (const Rootlesscontainers__Resource *message, + ProtobufCBuffer *buffer); +Rootlesscontainers__Resource * + rootlesscontainers__resource__unpack + (ProtobufCAllocator *allocator, + size_t len, + const uint8_t *data); +void rootlesscontainers__resource__free_unpacked + (Rootlesscontainers__Resource *message, + ProtobufCAllocator *allocator); +/* --- per-message closures --- */ + +typedef void (*Rootlesscontainers__Resource_Closure) + (const Rootlesscontainers__Resource *message, + void *closure_data); + +/* --- services --- */ + + +/* --- descriptors --- */ + +extern const ProtobufCMessageDescriptor rootlesscontainers__resource__descriptor; + +PROTOBUF_C__END_DECLS + + +#endif /* PROTOBUF_C_rootlesscontainers_2eproto__INCLUDED */ diff --git a/src/rootlesscontainers/rootlesscontainers.proto b/src/rootlesscontainers/rootlesscontainers.proto new file mode 100644 index 00000000..5bcfdfd9 --- /dev/null +++ b/src/rootlesscontainers/rootlesscontainers.proto @@ -0,0 +1,33 @@ +// rootlesscontainers.proto +// Copyright (C) 2018 Rootless Containers authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +// The rootlesscontainers package is maintained at https://rootlesscontaine.rs/ . +// If you want to extend the resource definition, please open a PR. +package rootlesscontainers; + +// Resource defines the schema for "user.rootlesscontainers" xattr values. +// The resource can be used as a persistent storage for emulated `chown(2)` syscall. +// Syscall emulators SHOULD try to hide this xattr from the emulated environment. +message Resource { + // Zero-value MUST be parsed as a literally zero-value, not "unset". + // To keep both uid and gid unchaged, the entire xattr value SHOULD be removed. + // To keep either one of uid or gid unchaged, 0xFFFFFFFF (in other words, + // `(uint32_t) -1`, see also chown(2)) value SHOULD be set. + // (Because some protobuf bindings cannot distinguish "unset" from zero-value.) + uint32 uid = 1; + uint32 gid = 2; +} From 537ce95ff0dfa5ddc8d8ee2270823511d5011453 Mon Sep 17 00:00:00 2001 From: Max Goltzsche Date: Sun, 23 Sep 2018 19:33:01 +0200 Subject: [PATCH 3/6] Added libattr fallback from ENOATTR to ENODATA in fake_id0 to support build on fedora. Signed-off-by: Max Goltzsche --- src/extension/fake_id0/fake_id0.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/extension/fake_id0/fake_id0.c b/src/extension/fake_id0/fake_id0.c index 674e12be..ac256ed4 100644 --- a/src/extension/fake_id0/fake_id0.c +++ b/src/extension/fake_id0/fake_id0.c @@ -51,6 +51,10 @@ #include "rootlesscontainers/rootlesscontainers.pb-c.h" #define XATTR_USER_ROOTLESSCONTAINERS "user.rootlesscontainers" +#ifndef ENOATTR +# define ENOATTR ENODATA +#endif + // return 0 on success. int rootlesscontainers_get_xattr (int fd, const char *path, uid_t *uid, gid_t *gid) { Rootlesscontainers__Resource *msg; From a2e37c3b1fec16eddf69f463eb65bc6498ae7ed7 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Thu, 29 Nov 2018 14:17:32 +0900 Subject: [PATCH 4/6] update travis Signed-off-by: Akihiro Suda --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 232d3832..70cc83ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ sudo: required language: c -dist: bionic +dist: xenial services: - docker From 4ec728fce29454a7132a990e7e0540d98b4942f8 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Thu, 29 Nov 2018 14:31:55 +0900 Subject: [PATCH 5/6] disable coverity Signed-off-by: Akihiro Suda --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 70cc83ab..6f787981 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,4 +18,3 @@ script: - make -C src loader.elf loader-m32.elf build.h - env CFLAGS=--coverage LDFLAGS='--coverage' make -C src proot care - env PATH=/bin:/usr/bin:/sbin:/usr/sbin:$PWD/src PROOT_NO_SECCOMP=1 make -C test - From a539f0f5fe42faff2dc67d09483d92c299ae85f4 Mon Sep 17 00:00:00 2001 From: Sven Dowideit Date: Tue, 7 Jan 2020 18:09:07 +1000 Subject: [PATCH 6/6] example Dockerfile build as used by https://github.com/rootless-containers/runrootless Signed-off-by: Sven Dowideit --- .dockerignore | 8 ++++++++ Dockerfile | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..185cb21d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +AUTHORS +contrib +COPYING +doc +Dockerfile +install-proot.sh +ISSUE_TEMPLATE +*.rst \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..6db1bd3a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM debian:9 AS build +RUN apt-get update && apt-get install -q -y build-essential git libseccomp-dev libtalloc-dev \ + # deps for PERSISTENT_CHOWN extension + libprotobuf-c-dev libattr1-dev +ADD . PRoot/ +RUN cd PRoot \ + && cd src \ + && make && mv proot / && make clean + +FROM scratch as proot +COPY --from=build /proot /runrootless-proot + +# +# can install the binary using: +# +# docker build --target proot --output ~/.runrootless . +# +# \ No newline at end of file