Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for rootless containers #204

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
AUTHORS
contrib
COPYING
doc
Dockerfile
install-proot.sh
ISSUE_TEMPLATE
*.rst
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ sudo: required

language: c

dist: bionic
dist: xenial

services:
- docker
Expand All @@ -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

18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM debian:9 AS build
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 .
#
#
2 changes: 1 addition & 1 deletion doc/proot/rpm-spec
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be declared as optional dependencies. Not sure off the top of my head how to do that with RPM.


%if 0%{?suse_version} >= 1210 || 0%{?fedora_version} >= 15
BuildRequires: glibc-static
Expand Down
2 changes: 1 addition & 1 deletion doc/proot/stylesheets/rpm-spec.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 15 additions & 2 deletions src/GNUmakefile
Original file line number Diff line number Diff line change
@@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be disabled by default.


# the VPATH variable must point to the actual makefile directory
VPATH := $(dir $(lastword $(MAKEFILE_LIST)))
SRC = $(dir $(firstword $(MAKEFILE_LIST)))
Expand All @@ -18,10 +20,17 @@ 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 += -ltalloc -Wl,-z,noexecstack
LDFLAGS += -L. -ltalloc -Wl,-z,noexecstack -static
ifdef PERSISTENT_CHOWN
LDFLAGS += $(shell pkg-config --libs libprotobuf-c)
endif

CARE_LDFLAGS = -larchive
CARE_LDFLAGS = -larchive -static
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be static by default.


OBJECTS += \
cli/cli.o \
Expand Down Expand Up @@ -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
Expand Down
162 changes: 161 additions & 1 deletion src/extension/fake_id0/fake_id0.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,85 @@
#include "path/binding.h"
#include "arch.h"

#if defined(PERSISTENT_CHOWN)
#include <unistd.h>
#include <attr/xattr.h>
#include "path/path.h"
#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;
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;
Expand Down Expand Up @@ -231,7 +310,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)
{
Expand Down Expand Up @@ -270,6 +349,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;
Expand All @@ -283,13 +368,49 @@ 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)
poke_reg(tracee, uid_sysarg, getuid());
if (gid == config->rgid)
poke_reg(tracee, gid_sysarg, getgid());

#endif
return 0;
}

Expand Down Expand Up @@ -624,6 +745,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);
Expand Down Expand Up @@ -659,6 +788,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;
}

Expand Down
Loading