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

Basic implementation of Copy-On-Write #96

Open
wants to merge 2 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
1 change: 1 addition & 0 deletions src/GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ OBJECTS += \
extension/extension.o \
extension/kompat/kompat.o \
extension/fake_id0/fake_id0.o \
extension/cow/cow.o \
loader/loader-wrapped.o

define define_from_arch.h
Expand Down
6 changes: 6 additions & 0 deletions src/cli/proot.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ static int handle_option_k(Tracee *tracee, const Cli *cli UNUSED, const char *va
return 0;
}

static int handle_option_c(Tracee *tracee, const Cli *cli UNUSED, const char *value)
{
(void) initialize_extension(tracee, cow_callback, value);
return 0;
}

static int handle_option_i(Tracee *tracee, const Cli *cli UNUSED, const char *value)
{
(void) initialize_extension(tracee, fake_id0_callback, value);
Expand Down
30 changes: 22 additions & 8 deletions src/cli/proot.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ static int handle_option_V(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_h(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_k(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_0(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_c(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_i(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_R(Tracee *tracee, const Cli *cli, const char *value);
static int handle_option_S(Tracee *tracee, const Cli *cli, const char *value);
Expand Down Expand Up @@ -205,15 +206,28 @@ Copyright (C) 2014 STMicroelectronics, licensed under GPL v2 or later.",
},
{ .class = "Extension options",
.arguments = {
{ .name = "-i", .separator = ' ', .value = "string" },
{ .name = "--change-id", .separator = '=', .value = "string" },
{ .name = "-0", .separator = '\0', .value = NULL },
{ .name = "--root-id", .separator = '\0', .value = NULL },
{ .name = NULL, .separator = '\0', .value = NULL } },
.handler = handle_option_0,
.description = "Make current user appear as \"root\" and fake its privileges.",
.detail = "\tSome programs will refuse to work if they are not run with \"root\"\n\
\tprivileges, even if there is no technical reason for that. This\n\
\tis typically the case with package managers. This option allows\n\
\tusers to bypass this kind of limitation by faking the user/group\n\
\tidentity, and by faking the success of some operations like\n\
\tchanging the ownership of files, changing the root directory to\n\
\t/, ... Note that this option is quite limited compared to\n\
\tfakeroot.",
},
{ .class = "Extension options",
.arguments = {
{ .name = "-c", .separator = '\0', .value = NULL },
{ .name = "--cow", .separator = '\0', .value = NULL },
{ .name = NULL, .separator = '\0', .value = NULL } },
.handler = handle_option_i,
.description = "Make current user and group appear as *string* \"uid:gid\".",
.detail = "\tThis option makes the current user and group appear as uid and\n\
\tgid. Likewise, files actually owned by the current user and\n\
\tgroup appear as if they were owned by uid and gid instead.\n\
\tNote that the -0 option is the same as -i 0:0.",
.handler = handle_option_c,
.description = "Implement Copy-on-Write semantics",
.detail = "\tThis option implments Copy-on-Write (COW) semantics",
},
{ .class = "Alias options",
.arguments = {
Expand Down
196 changes: 196 additions & 0 deletions src/extension/cow/cow.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
* This file is part of PRoot.
*
* Copyright (C) 2016 Alejandro Liu
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/

#include <stdint.h> /* intptr_t, */
#include <stdlib.h> /* strtoul(3), */
#include <linux/version.h> /* KERNEL_VERSION, */
#include <assert.h> /* assert(3), */
#include <sys/utsname.h> /* uname(2), utsname, */
#include <string.h> /* str*(3), memcpy(3), */
#include <talloc.h> /* talloc_*, */
#include <fcntl.h> /* AT_*, */
#include <sys/ptrace.h> /* linux.git:c0a3a20b */
#include <errno.h> /* errno, */
#include <linux/auxvec.h> /* AT_, */
#include <linux/futex.h> /* FUTEX_PRIVATE_FLAG */
#include <sys/param.h> /* MIN, */
#include <sys/mman.h> /* mmap, munmap */
#include <unistd.h> /* unlink, close, write, fchown */

#include "extension/extension.h"
#include "syscall/seccomp.h"
#include "syscall/sysnum.h"
#include "syscall/syscall.h"
#include "syscall/chain.h"
#include "tracee/tracee.h"
#include "tracee/reg.h"
#include "tracee/abi.h"
#include "tracee/mem.h"
#include "execve/auxv.h"
#include "cli/note.h"
#include "arch.h"

typedef int Config;

/* List of syscalls handled by this extensions. */
static FilteredSysnum filtered_sysnums[] = {
{ PR_open, FILTER_SYSEXIT },
{ PR_openat, FILTER_SYSEXIT },
FILTERED_SYSNUM_END,
};


static int check_flags(Tracee *tracee, Reg reg) {
int flags;
flags = peek_reg(tracee,CURRENT, reg);
switch (flags & O_ACCMODE) {
case O_WRONLY:
case O_RDWR:
return 1;
}
return 0;
}

static void check_inode_and_copy(Tracee *tracee,char const *name) {
struct stat stb;
int sfd, nfd;
if (stat(name,&stb) == -1) return;
if (stb.st_nlink < 2) return;
if (-1 == (sfd = open(name,O_RDONLY))) return;
if (unlink(name) == -1) {
close(sfd);
return;
}
if (-1 == (nfd = open(name,O_CREAT|O_EXCL|O_WRONLY,stb.st_mode))) {
note(tracee, ERROR, USER, "Error opening %s",name);
close(sfd);
return;
}
void *addr;
if ((addr=mmap(NULL,stb.st_size,PROT_READ,MAP_PRIVATE, sfd,0))==MAP_FAILED) {
note(tracee, ERROR, USER, "Error mmaping %s",name);
close(nfd);
close(sfd);
return;
}
if (write(nfd,addr,stb.st_size) != stb.st_size) {
note(tracee, ERROR, USER, "Error writing %s",name);
}
munmap(addr,stb.st_size);
close(sfd);
fchown(nfd,stb.st_uid,stb.st_gid);
close(nfd);
}

/**
* Adjust current @tracee's syscall parameters according to @config.
* This function always returns 0.
*/
static int handle_sysenter_end(Tracee *tracee, const Config *config UNUSED)
{
word_t sysnum;
char old_path[PATH_MAX];
int status;

sysnum = get_sysnum(tracee, ORIGINAL);
switch (sysnum) {
case PR_open:
if (!check_flags(tracee, SYSARG_2)) return 0;
/* Extract the currrent path. */
status = get_sysarg_path(tracee, old_path, SYSARG_1);
if (status < 0) return status;
//note(tracee, INFO, USER, "PR_open(%s)",old_path);
check_inode_and_copy(tracee,old_path);
return 0;
case PR_openat:
if (!check_flags(tracee, SYSARG_3)) return 0;
/* Extract the currrent path. */
status = get_sysarg_path(tracee, old_path, SYSARG_2);
if (status < 0) return status;
//note(tracee, INFO, USER, "PR_openat(%s)",old_path);
check_inode_and_copy(tracee,old_path);
return 0;
default:
return 0;
}

/* Never reached */
assert(0);
return 0;

}

/**
* Handler for this @extension. It is triggered each time an @event
* occurred. See ExtensionEvent for the meaning of @data1 and @data2.
*/
int cow_callback(Extension *extension, ExtensionEvent event, intptr_t data1, intptr_t data2 UNUSED)
{
switch (event) {
case INITIALIZATION: {
Config *config;
extension->config = talloc(extension, Config);
if (extension->config == NULL)
return -1;

config = talloc_get_type_abort(extension->config, Config);
*config = 0;
extension->filtered_sysnums = filtered_sysnums;
return 0;
}

case INHERIT_PARENT: /* Inheritable for sub reconfiguration ... */
return 1;

case INHERIT_CHILD: {
/* Copy the parent configuration to the child. The
* structure should not be shared as uid/gid changes
* in one process should not affect other processes.
* This assertion is not true for POSIX threads
* sharing the same group, however Linux threads never
* share uid/gid information. As a consequence, the
* GlibC emulates the POSIX behavior on Linux by
* sending a signal to all group threads to cause them
* to invoke the system call too. Finally, PRoot
* doesn't have to worry about clone flags.
*/

Extension *parent = (Extension *) data1;
extension->config = talloc_zero(extension, Config);
if (extension->config == NULL)
return -1;

memcpy(extension->config, parent->config, sizeof(Config));
return 0;
}

case SYSCALL_ENTER_END: {
Tracee *tracee = TRACEE(extension);
Config *config = talloc_get_type_abort(extension->config, Config);

return handle_sysenter_end(tracee, config);
}


default:
return 0;
}
}
2 changes: 1 addition & 1 deletion src/extension/extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,5 +179,5 @@ static inline int notify_extensions(Tracee *tracee, ExtensionEvent event,
extern int kompat_callback(Extension *extension, ExtensionEvent event, intptr_t d1, intptr_t d2);
extern int fake_id0_callback(Extension *extension, ExtensionEvent event, intptr_t d1, intptr_t d2);
extern int care_callback(Extension *extension, ExtensionEvent event, intptr_t d1, intptr_t d2);

extern int cow_callback(Extension *extension, ExtensionEvent event, intptr_t d1, intptr_t d2);
#endif /* EXTENSION_H */