Skip to content
Permalink
Browse files

option -t update : now creations (and file write) outside workpath, and

temporary directories are forbidden (instead of just being reported).

Notes:
	file deletion aren't forbidden.
	there are other ways to alter the filesystem that aren't trapped and watched.

This works with the following changes:
- darwintracelib1.0 now can forbid creations/writing outside the sandbox. This
  is controlled at compile time with a global variable to define the sandbox
  bounds.
- option -t of port(1) now uses this feature and reports the violations
- trace test was updated to work with this new feature (actually, I realized the
  test only passed on my box because the $pwd was hard coded).

git-svn-id: https://svn.macports.org/repository/macports/trunk/base@18709 d073be05-634f-4543-b044-5fe20cf6d1d6
  • Loading branch information
pguyot committed Jul 24, 2006
1 parent a179f0a commit 18a451aacc64f12fb90eac629d7186890acc33d8
Showing with 199 additions and 48 deletions.
  1. +131 −2 src/darwintracelib1.0/darwintrace.c
  2. +49 −30 src/port1.0/porttrace.tcl
  3. +11 −9 src/port1.0/portutil.tcl
  4. +2 −1 tests/Makefile
  5. +2 −1 tests/trace/Makefile
  6. +3 −4 tests/trace/Portfile
  7. +1 −1 tests/trace/master
@@ -1,6 +1,9 @@
/*
* Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
* $Id: darwintrace.c,v 1.14 2006/07/23 00:36:42 pguyot Exp $
* Copyright (c) 2005-2006 Paul Guyot <pguyot@kallisys.net>,
* All rights reserved.
*
* $Id: darwintrace.c,v 1.15 2006/07/24 05:55:43 pguyot Exp $
*
* @APPLE_BSD_LICENSE_HEADER_START@
*
@@ -55,15 +58,27 @@
* Compile time options:
* DARWINTRACE_SHOW_PROCESS: show the process id of every access
* DARWINTRACE_LOG_CREATE: log creation of files as well.
* DARWINTRACE_SANDBOX: control creation and writing to files.
* DARWINTRACE_LOG_FULL_PATH: use F_GETPATH to log the full path.
* DARWINTRACE_DEBUG_OUTPUT: verbose output of stuff to debug darwintrace.
*
* global variables (only checked when setup is first called)
* DARWINTRACE_LOG
* path to the log file (no logging happens if it's unset).
* DARWINTRACE_SANDBOX_BOUNDS
* : separated allowed paths for the creation of files.
* \: -> :
* \\ -> \
*/

#ifndef DARWINTRACE_SHOW_PROCESS
#define DARWINTRACE_SHOW_PROCESS 0
#endif
#ifndef DARWINTRACE_LOG_CREATE
#define DARWINTRACE_LOG_CREATE 1
#define DARWINTRACE_LOG_CREATE 0
#endif
#ifndef DARWINTRACE_SANDBOX
#define DARWINTRACE_SANDBOX 1
#endif
#ifndef DARWINTRACE_DEBUG_OUTPUT
#define DARWINTRACE_DEBUG_OUTPUT 0
@@ -79,6 +94,7 @@
/*
* Prototypes.
*/
inline int __darwintrace_strbeginswith(const char* str, const char* prefix);
inline void __darwintrace_log_op(const char* op, const char* procname, const char* path, int fd);
inline void __darwintrace_setup();
inline void __darwintrace_cleanup_path(char *path);
@@ -90,6 +106,9 @@ static int __darwintrace_fd = -2;
static char __darwintrace_progname[BUFFER_SIZE];
static pid_t __darwintrace_pid = -1;
#endif
#if DARWINTRACE_SANDBOX
static char** __darwintrace_sandbox_bounds = NULL;
#endif

#if __STDC_VERSION__==199901L
#if DARWINTRACE_DEBUG_OUTPUT
@@ -105,6 +124,19 @@ static pid_t __darwintrace_pid = -1;
#endif
#endif

/*
* return 0 if str doesn't begin with prefix, 1 otherwise.
*/
inline int __darwintrace_strbeginswith(const char* str, const char* prefix) {
char theCharS;
char theCharP;
do {
theCharS = *str++;
theCharP = *prefix++;
} while(theCharP && (theCharP == theCharS));
return (theCharP == 0);
}

inline void __darwintrace_setup() {
#define open(x,y,z) syscall(SYS_open, (x), (y), (z))
#define close(x) syscall(SYS_close, (x))
@@ -136,6 +168,67 @@ inline void __darwintrace_setup() {
}
}
#endif
#if DARWINTRACE_SANDBOX
if (__darwintrace_sandbox_bounds == NULL) {
char* paths = getenv("DARWINTRACE_SANDBOX_BOUNDS");
if (paths != NULL) {
/* copy the string */
char* copy = strdup(paths);
if (copy != NULL) {
int nbPaths = 1;
int nbAllocatedPaths = 5;
char** paths = (char**) malloc(sizeof(char*) * nbAllocatedPaths);
char* crsr = copy;
char** pathsCrsr = paths;
/* first path */
*pathsCrsr++ = crsr;
/* parse the paths (modify the copy) */
do {
char theChar = *crsr;
if (theChar == '\0') {
/* the end of the paths */
break;
}
if (theChar == ':') {
/* the end of this path */
*crsr = 0;
nbPaths++;
if (nbPaths == nbAllocatedPaths) {
nbAllocatedPaths += 5;
paths = (char**) realloc(paths, sizeof(char*) * nbAllocatedPaths);
/* reset the cursor in case paths pointer was moved */
pathsCrsr = paths + (nbPaths - 1);
}
*pathsCrsr++ = crsr + 1;
}
if (theChar == '\\') {
/* escape character. test next char */
char nextChar = crsr[1];
if (nextChar == '\\') {
/* rewrite the string */
char* rewriteCrsr = crsr + 1;
do {
char theChar = *rewriteCrsr;
rewriteCrsr[-1] = theChar;
rewriteCrsr++;
} while (theChar != 0);
} else if (nextChar == ':') {
crsr++;
}
/* otherwise, ignore (keep the backslash) */
}

/* next char */
crsr++;
} while (1);
/* null terminate the array */
*pathsCrsr = 0;
/* resize and save it */
__darwintrace_sandbox_bounds = (char**) realloc(paths, sizeof(char*) * (nbPaths + 1));
}
}
}
#endif
#undef close
#undef open
}
@@ -257,7 +350,43 @@ int open(const char* path, int flags, ...) {
va_start(args, flags);
mode = va_arg(args, int);
va_end(args);
#if DARWINTRACE_SANDBOX
result = 0;
if (flags & (O_CREAT | O_APPEND | O_RDWR | O_WRONLY | O_TRUNC)) {
__darwintrace_setup();
if (__darwintrace_sandbox_bounds != NULL) {
/* check the path */
char** basePathsCrsr = __darwintrace_sandbox_bounds;
char* basepath = *basePathsCrsr++;
/* normalize the path */
char createpath[MAXPATHLEN];
if (realpath(path, createpath) != NULL) {
__darwintrace_cleanup_path(createpath);
/* forbid unless allowed */
result = -1;
while (basepath != NULL) {
if (__darwintrace_strbeginswith(createpath, basepath)) {
result = 0;
break;
}
basepath = *basePathsCrsr++;;
}
} /* otherwise, open will fail anyway */
}
if (result == 0) {
dprintf("darwintrace: creation/writing was allowed at %s\n", path);
}
}
if (result == 0) {
result = open(path, flags, mode);
} else {
dprintf("darwintrace: creation/writing was forbidden at %s\n", path);
__darwintrace_log_op("sandbox_violation", NULL, path, result);
errno = EACCES;
}
#else
result = open(path, flags, mode);
#endif
if (result >= 0) {
/* check that it's a file */
struct stat sb;
@@ -1,9 +1,9 @@
# et:ts=4
# porttrace.tcl
#
# $Id: porttrace.tcl,v 1.16 2006/07/22 09:16:10 pguyot Exp $
# $Id: porttrace.tcl,v 1.17 2006/07/24 05:55:44 pguyot Exp $
#
# Copyright (c) 2005 Paul Guyot <pguyot@kallisys.net>,
# Copyright (c) 2005-2006 Paul Guyot <pguyot@kallisys.net>,
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -41,7 +41,7 @@ proc trace_start {workpath} {
if {[catch {package require Thread} error]} {
ui_warn "trace requires Tcl Thread package ($error)"
} else {
global env trace_fifo
global env trace_fifo trace_sandboxbounds
# Create a fifo.
set trace_fifo "$workpath/trace_fifo"
file delete -force $trace_fifo
@@ -56,10 +56,37 @@ proc trace_start {workpath} {
[file join ${portutil::autoconf::prefix} share darwinports Tcl darwintrace1.0 darwintrace.dylib]
set env(DYLD_FORCE_FLAT_NAMESPACE) 1
set env(DARWINTRACE_LOG) "$trace_fifo"
# The sandbox is limited to:
# workpath
# /tmp
# /var/tmp
# $TMPDIR
# /dev/null
# /dev/tty
set trace_sandboxbounds "/tmp:/var/tmp:/dev/null:/dev/tty:${workpath}"
if {[info exists env(TMPDIR)]} {
set trace_sandboxbounds "${trace_sandboxbounds}:$env(TMPDIR)"
}
}
}
}

# Enable the fence.
# Only done for targets that should only happen in the sandbox.
proc trace_enable_fence {} {
global env trace_sandboxbounds
set env(DARWINTRACE_SANDBOX_BOUNDS) $trace_sandboxbounds
}

# Disable the fence.
# Unused yet.
proc trace_disable_fence {} {
global env
if [info exists env(DARWINTRACE_SANDBOX_BOUNDS)] {
unset env(DARWINTRACE_SANDBOX_BOUNDS)
}
}

# Check the list of ports.
# Output a warning for every port the trace revealed a dependency on
# that isn't included in portslist
@@ -82,17 +109,15 @@ proc trace_check_deps {target portslist} {
}
}

# Check that no file were created outside workpath.
# Output a warning for every created file the trace revealed.
# Check that no violation happened.
# Output a warning for every sandbox violation the trace revealed.
# This method must be called after trace_start
proc trace_check_create {} {
# Get the list of created files.
set created [slave_send slave_get_created]
proc trace_check_violations {} {
# Get the list of violations.
set violations [slave_send slave_get_sandbox_violations]

# Compare with portslist
set created [lsort $created]
foreach created_file $created {
ui_warn "A file was created outside \${workpath}: $created_file"
foreach violation [lsort $violations] {
ui_warn "A file creation/writing was attempted outside sandbox: $violation"
}
}

@@ -105,6 +130,9 @@ proc trace_stop {} {
unset env(DYLD_INSERT_LIBRARIES)
unset env(DYLD_FORCE_FLAT_NAMESPACE)
unset env(DARWINTRACE_LOG)
if [info exists env(DARWINTRACE_SANDBOX_BOUNDS)] {
unset env(DARWINTRACE_SANDBOX_BOUNDS)
}

# Clean up.
slave_send slave_stop
@@ -161,7 +189,7 @@ proc delete_slave {} {
# Private.
# Slave method to read a line from the trace.
proc slave_read_line {chan} {
global ports_list trace_filemap created_list workpath
global ports_list trace_filemap sandbox_violation_list workpath
global env

while 1 {
@@ -212,17 +240,8 @@ proc slave_read_line {chan} {
catch {filemap set trace_filemap $path $port}
}
}
} elseif {$op == "create"} {
# Only keep entries not under workpath, under /tmp/, under
# /var/tmp/, $TMPDIR and /dev/null
if {![string equal -length [string length "/tmp/"] "/tmp/" $path]
&& ![string equal -length [string length "/var/tmp/"] "/var/tmp/" $path]
&& (![info exists env(TMPDIR)]
|| ![string equal -length [string length $env(TMPDIR)] $env(TMPDIR) $path])
&& ![string equal "/dev/null" $path]
&& ![string equal -length [string length $workpath] $workpath $path]} {
lappend created_list $path
}
} elseif {$op == "sandbox_violation"} {
lappend sandbox_violation_list $path
}
}
}
@@ -231,14 +250,14 @@ proc slave_read_line {chan} {
# Private.
# Slave init method.
proc slave_start {fifo p_workpath} {
global ports_list trace_filemap created_list trace_fifo_r_chan \
global ports_list trace_filemap sandbox_violation_list trace_fifo_r_chan \
trace_fifo_w_chan workpath
# Save the workpath.
set workpath $p_workpath
# Create a virtual filemap.
filemap create trace_filemap
set ports_list {}
set created_list {}
set sandbox_violation_list {}
set trace_fifo_r_chan [open $fifo {RDONLY NONBLOCK}]
# To prevent EOF when darwintrace closes the file, I also open the pipe
# myself as write only.
@@ -270,8 +289,8 @@ proc slave_get_ports {} {
}

# Private.
# Slave created export method.
proc slave_get_created {} {
global created_list
return $created_list
# Slave sandbox violations export method.
proc slave_get_sandbox_violations {} {
global sandbox_violation_list
return $sandbox_violation_list
}
@@ -1,6 +1,6 @@
# et:ts=4
# portutil.tcl
# $Id: portutil.tcl,v 1.192 2006/05/29 16:57:03 mww Exp $
# $Id: portutil.tcl,v 1.193 2006/07/24 05:55:44 pguyot Exp $
#
# Copyright (c) 2004 Robert Shaw <rshaw@opendarwin.org>
# Copyright (c) 2002 Apple Computer, Inc.
@@ -635,6 +635,15 @@ proc target_run {ditem} {
&& $ports_trace == "yes"
&& $target != "clean")} {
trace_start $workpath

# Enable the fence to prevent any creation/modification
# outside the sandbox.
if {$target != "activate"
&& $target != "archive"
&& $target != "fetch"
&& $target != "install"} {
trace_enable_fence
}
}

# Execute pre-run procedure
@@ -708,15 +717,8 @@ proc target_run {ditem} {
lappend depsPorts $dep_portname
}
trace_check_deps $target $depsPorts
trace_check_violations

# Check files that were created.
if {$target != "activate"
&& $target != "archive"
&& $target != "fetch"
&& $target != "install"} {
trace_check_create
}

# End of trace.
trace_stop
}
@@ -33,7 +33,8 @@ test: /tmp/darwinports-tests/opt/local/etc/ports/sources.conf
PORTSRC=$(PWD)/test-ports.conf $(bindir)/port clean > /dev/null && \
PORTSRC=$(PWD)/test-ports.conf $(bindir)/port test > output 2>&1 \
|| (cat output; exit 1) && \
diff output master 2>&1 | tee difference && \
sed -e "s|${PWD}|PWD|g" < output > output.sed && \
diff output.sed master 2>&1 | tee difference && \
if [ -s difference ]; then \
exit 1; \
else \

0 comments on commit 18a451a

Please sign in to comment.
You can’t perform that action at this time.