[141420] trunk/base

cal at macports.org cal at macports.org
Sat Dec 12 01:27:24 PST 2015


Revision: 141420
          https://trac.macports.org/changeset/141420
Author:   cal at macports.org
Date:     2015-10-17 17:49:32 -0700 (Sat, 17 Oct 2015)
Log Message:
-----------
base: Fix trace mode on El Capitan

OS X El Capitan introduces System Integrity Protection for files. Executables
with this flag set will be started in a sanitized environment by the kernel,
stripping all DYLD_* variables. This breaks trace mode, because tracing relies
on preloading to wrap file related system calls using DYLD_INSERT_LIBRARIES.

A trivial workaround for the problem is to make a copy of the affected
binaries, which will strip the flag, and then adjust the invocation of the
binary to execute the copy instead (but leaving argv[0] as-is to avoid giving
the program an indication of being run from a non-standard location).

This change implements this approach by copying the SIP-flagged binaries to
$prefix/var/macports/sip-workaround on demand iff
 - the system has the SF_RESTRICTED flag defined
 - a binary is started with DYLD_INSERT_LIBRARIES set
 - the file exists and has SF_RESTRICTED set
 - the file isn't SUID or SGID (which we could not reliably copy, and which
   have never preserved DYLD_* variables)
If the file to be executed is a script and has a shebang line, the checks are
run on the interpreter instead, and if necessary, the interpreter is copied.
This requires interpreting the shebang line in user space.

Copies are created on-demand and are lazy: The file modification times are
checked before overwriting an existing copy. Copies are created in a per-user
folder, which will be created on-demand in a 1777 directory (like /tmp).

Changes are also needed way before darwintrace.dylib first runs: The DYLD_*
variables are already stripped in src/pextlib1.0/system.c, where
/usr/bin/sandbox-exec and /bin/sh are run, which both have the SF_RESTRICTED
flag on 10.11 now. Consequently, the same copying approach is applied there.

Because macports build run in a sandbox, the sandbox boundaries are extended to
allow access to $prefix/var/macports/sip-workaround.

Modified Paths:
--------------
    trunk/base/configure.ac
    trunk/base/doc/base.mtree.in
    trunk/base/src/darwintracelib1.0/Makefile.in
    trunk/base/src/darwintracelib1.0/proc.c
    trunk/base/src/pextlib1.0/Makefile.in
    trunk/base/src/pextlib1.0/system.c
    trunk/base/src/port1.0/portsandbox.tcl

Added Paths:
-----------
    trunk/base/src/pextlib1.0/sip_copy_proc.c
    trunk/base/src/pextlib1.0/sip_copy_proc.h

Property Changed:
----------------
    trunk/base/src/darwintracelib1.0/

Modified: trunk/base/configure.ac
===================================================================
--- trunk/base/configure.ac	2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/configure.ac	2015-10-18 00:49:32 UTC (rev 141420)
@@ -312,6 +312,14 @@
 eval "prefix_expanded=$prefix"
 eval "exec_prefix_expanded=$exec_prefix"
 AC_SUBST(prefix_expanded)
+
+# Define a path where our trace mode workaround for SIP-protected binaries on
+# >= 10.11 puts copies
+AC_DEFINE_UNQUOTED(
+	[DARWINTRACE_SIP_WORKAROUND_PATH],
+	["${prefix}/var/macports/sip-workaround"],
+	[Absolute path to a directory used by darwintrace to copy SIP-protected files before executing])
+
 # do this twice, since there is a nested variable of
 # ${prefix} inside of ${sysconfdir}
 eval "MPCONFIGDIR_EXPANDED=$MPCONFIGDIR"

Modified: trunk/base/doc/base.mtree.in
===================================================================
--- trunk/base/doc/base.mtree.in	2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/doc/base.mtree.in	2015-10-18 00:49:32 UTC (rev 141420)
@@ -23,6 +23,8 @@
             ..
             registry
             ..
+            sip-workaround mode=01777
+            ..
             software
             ..
         ..


Property changes on: trunk/base/src/darwintracelib1.0
___________________________________________________________________
Modified: svn:ignore
   - Makefile
darwintrace.dylib

   + *.d
Makefile
darwintrace.dylib
sip_copy_proc.c
sip_copy_proc.h


Modified: trunk/base/src/darwintracelib1.0/Makefile.in
===================================================================
--- trunk/base/src/darwintracelib1.0/Makefile.in	2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/src/darwintracelib1.0/Makefile.in	2015-10-18 00:49:32 UTC (rev 141420)
@@ -5,30 +5,62 @@
 
 # This Makefile will only be run on Darwin systems; we can safely use
 # Apple-specifics here
-SRCS=		$(wildcard *.c)
-OBJS=		$(SRCS:%.c=%.o)
-SHLIB_NAME=	darwintrace${SHLIB_SUFFIX}
-INSTALLDIR=	${DESTDIR}${TCL_PACKAGE_PATH}/darwintrace1.0
+SRCS = \
+	access.c \
+	close.c \
+	darwintrace.c \
+	dup2.c \
+	mkdir.c \
+	open.c \
+	proc.c \
+	readdir.c \
+	readlink.c \
+	rename.c \
+	rmdir.c \
+	sip_copy_proc.c \
+	stat.c \
+	unlink.c
 
+OBJS = $(SRCS:%.c=%.o)
+SHLIB_NAME = darwintrace$(SHLIB_SUFFIX)
+INSTALLDIR = $(DESTDIR)$(TCL_PACKAGE_PATH)/darwintrace1.0
+
 # Yes, we know having $ signs in identifiers is not a very good idea; in the
 # case of darwintrace we still need them, though.
-CFLAGS_PEDANTIC=
-CFLAGS+= -fPIC ${UNIVERSAL_ARCHFLAGS}
-SHLIB_LDFLAGS+=${UNIVERSAL_ARCHFLAGS}
+CFLAGS_PEDANTIC =
+CFLAGS += -fPIC $(UNIVERSAL_ARCHFLAGS)
+SHLIB_LDFLAGS += $(UNIVERSAL_ARCHFLAGS)
 
-all:: ${SHLIB_NAME}
+# Generate dependency information
+CPPFLAGS += -MMD -MP
 
-$(SHLIB_NAME):: ${OBJS}
-	${SHLIB_LD} ${OBJS} -o ${SHLIB_NAME} ${SHLIB_LDFLAGS} ${LIBS}
+all:: $(SHLIB_NAME)
 
+# Copy sip_copy_proc.{c,h} from pextlib1.0 where they are also needed
+sip_copy_proc.c: ../pextlib1.0/sip_copy_proc.c sip_copy_proc.h 
+	cp $< $@
+
+sip_copy_proc.h: ../pextlib1.0/sip_copy_proc.h
+	cp $< $@
+
+# This won't be automatically detected during the first run of make, where the
+# .d files do not exist yet
+proc.c: sip_copy_proc.h
+
+$(SHLIB_NAME):: $(OBJS)
+	$(SHLIB_LD) $(OBJS) -o $(SHLIB_NAME) $(SHLIB_LDFLAGS) $(LIBS)
+
 clean::
-	rm -f ${OBJS} ${SHLIB_NAME} so_locations
+	rm -f $(OBJS) $(SHLIB_NAME) so_locations sip_copy_proc.c sip_copy_proc.h $(SRCS:%.c=%.d)
 
 distclean:: clean
 	rm -f Makefile
 
 install:: all
-	$(INSTALL) -d -o "${DSTUSR}" -g "${DSTGRP}" -m "${DSTMODE}" "${INSTALLDIR}"
-	$(INSTALL)    -o "${DSTUSR}" -g "${DSTGRP}" -m 444 "${SHLIB_NAME}" "${INSTALLDIR}"
+	$(INSTALL) -d -o "$(DSTUSR)" -g "$(DSTGRP)" -m "$(DSTMODE)" "$(INSTALLDIR)"
+	$(INSTALL)    -o "$(DSTUSR)" -g "$(DSTGRP)" -m 444 "$(SHLIB_NAME)" "$(INSTALLDIR)"
 
 test::
+
+# Include dependency information
+-include $(SRCS:%.c=%.d)

Modified: trunk/base/src/darwintracelib1.0/proc.c
===================================================================
--- trunk/base/src/darwintracelib1.0/proc.c	2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/src/darwintracelib1.0/proc.c	2015-10-18 00:49:32 UTC (rev 141420)
@@ -37,6 +37,7 @@
 
 #define DARWINTRACE_USE_PRIVATE_API 1
 #include "darwintrace.h"
+#include "sip_copy_proc.h"
 
 #include <ctype.h>
 #include <dlfcn.h>
@@ -278,7 +279,7 @@
 
 			// Call the original execve function, but restore environment
 			char **newenv = restore_env(envp);
-			result = execve(path, argv, newenv);
+			result = sip_copy_execve(path, argv, newenv);
 			free(newenv);
 		}
 	}
@@ -334,7 +335,7 @@
 			 * we need to call the original posix_spawn from here. */
 			// call the original posix_spawn function, but restore environment
 			char **newenv = restore_env(envp);
-			result = posix_spawn(pid, path, file_actions, attrp, argv, newenv);
+			result = sip_copy_posix_spawn(pid, path, file_actions, attrp, argv, newenv);
 			free(newenv);
 		}
 	}

Modified: trunk/base/src/pextlib1.0/Makefile.in
===================================================================
--- trunk/base/src/pextlib1.0/Makefile.in	2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/src/pextlib1.0/Makefile.in	2015-10-18 00:49:32 UTC (rev 141420)
@@ -4,11 +4,32 @@
 include ../../Mk/macports.autoconf.mk
 
 OBJS= \
-	Pextlib.o strsed.o fgetln.o md5cmd.o setmode.o xinstall.o \
-	fs-traverse.o strcasecmp.o vercomp.o filemap.o base32cmd.o \
-	sha1cmd.o curl.o rmd160cmd.o sha256cmd.o readline.o uid.o \
-	tracelib.o tty.o readdir.o pipe.o adv-flock.o system.o \
-	mktemp.o realpath.o
+	Pextlib.o \
+	adv-flock.o \
+	base32cmd.o \
+	curl.o \
+	fgetln.o \
+	filemap.o \
+	fs-traverse.o \
+	md5cmd.o \
+	mktemp.o \
+	pipe.o \
+	readdir.o \
+	readline.o \
+	realpath.o \
+	rmd160cmd.o \
+	setmode.o \
+	sha1cmd.o \
+	sha256cmd.o \
+	sip_copy_proc.o \
+	strcasecmp.o \
+	strsed.o \
+	system.o \
+	tracelib.o \
+	tty.o \
+	uid.o \
+	vercomp.o \
+	xinstall.o
 ifneq (@ac_cv_func_strlcat@,yes)
 OBJS+=strlcat.o
 endif

Added: trunk/base/src/pextlib1.0/sip_copy_proc.c
===================================================================
--- trunk/base/src/pextlib1.0/sip_copy_proc.c	                        (rev 0)
+++ trunk/base/src/pextlib1.0/sip_copy_proc.c	2015-10-18 00:49:32 UTC (rev 141420)
@@ -0,0 +1,503 @@
+/* vim: set et sw=4 ts=4 sts=4: */
+/*
+ * sip_copy_proc.c
+ * $Id$
+ *
+ * Copyright (c) 2015 Clemens Lang <cal at macports.org>
+ * Copyright (c) 2015 The MacPorts Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The MacPorts Project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _DARWIN_FEATURE_64_BIT_INODE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "sip_copy_proc.h"
+
+#include <config.h>
+#ifndef DARWINTRACE_SIP_WORKAROUND_PATH
+#warning No value for DARWINTRACE_SIP_WORKAROUND_PATH found in config.h, using default of /tmp/macports-sip, which will fail unless you create it with mode 01777
+#define DARWINTRACE_SIP_WORKAROUND_PATH "/tmp/macports-sip"
+#endif
+
+/**
+ * Frees an array of strings and the array itself.
+ */
+static void free_argv(char *argv[]) {
+	char **arg = argv;
+	while (arg && *arg) {
+		free(*arg);
+		*arg = NULL;
+		arg++;
+	}
+
+	free(argv);
+}
+
+typedef enum _copy_needed_return_t {
+    copy_needed_error,
+    copy_not_needed,
+    copy_is_needed
+} copy_needed_return_t;
+
+/**
+ * Helper function to determine whether the binary indicated by \a path
+ * supports library injection using DYLD_INSERT_LIBRARIES directly or needs to
+ * be copied to a temporary path to support it.
+ *
+ * The following conditions must be fulfilled for the copy to be necessary:
+ *  - \a environ needs to contain a variable that starts with
+ *    DYLD_INSERT_LIBRARIES
+ *  - If the file at \a path has a shebang, its shebang line will be read and
+ *    the following checks will be done against the interpreter binary.
+ *    Additionally, if the copy is necessary, the arguments given in \a argc
+ *    (the number of arguments) and \a argv (the arguments itself) will be
+ *    prefixed with the command and arguments from the shebang line. The
+ *    original first argument will be replaced with \a path to make sure it is
+ *    absolute.
+ *  - \a path (or the interpreter given in the shebang of path) must have the
+ *    \c SF_RESTRICTED flag set.
+ *  - \a path (or the interpreter given in the shebang of path) must not be
+ *    SUID or SGID.
+ *
+ * @param path The absolute path of the binary to be executed
+ * @param argc The number of arguments passed in \a argv
+ * @param argv The arguments to be passed to the file to be executed
+ * @param outargv Pointer to a modified array of arguments. Only valid if \c
+ *                copy_is_needed is returned. May be \c NULL, in which case no
+ *                modifications to the original \c argv were necessary. If
+ *                non-null, a dynamically allocated array of dynamically
+ *                allocated elements. The last element of the array is \c NULL,
+ *                which makes \c *outargv suitable for passing to execve(2).
+ *                Note that instead of the given \c path, you should pass \c
+ *                (*outargv)[0] to execve(2) as first argument.
+ * @param environ The environment for the program to be started. Will be
+ *                checked for the presence of a DYLD_INSERT_LIBRARIES variable.
+ * @param st Pointer to struct stat that will contain information about \c
+ *           path, or of \c outargv isn't \c NULL, about \c (*outargv)[0]. This
+ *           can be used to determine metadata of the file such as modification
+ *           time and size to avoid unnecessary copies.
+ * @return \c copy_isneeded iff a copy is required. \c copy_not_needed if a copy
+ *         is not needed. \c copy_needed_error on error, where errno will be
+ *         set.
+ */
+static copy_needed_return_t copy_needed(const char *path, char *const argv[],
+        char **outargv[], char *const environ[], struct stat *st) {
+#ifndef SF_RESTRICTED /* no system integrity protection */
+	return copy_not_needed;
+#else /* defined(SF_RESTRICTED) */
+	// check whether DYLD_INSERT_LIBRARIES is set
+	bool dyld_insert_libraries_present = false;
+	char *const *env = environ;
+	while (env && *env) {
+		if (strncmp("DYLD_INSERT_LIBRARIES=", *env, strlen("DYLD_INSERT_LIBRARIES=")) == 0) {
+			dyld_insert_libraries_present = true;
+			break;
+		}
+		env++;
+	}
+	// if we didn't find DYLD_INSERT_LIBRARIES, a copy isn't needed
+	if (!dyld_insert_libraries_present) {
+		return copy_not_needed;
+	}
+
+	// open file to check for shebangs
+	const char *realpath = path;
+	size_t new_argc = 0;
+	char **new_argv = NULL;
+	FILE *f = fopen(path, "r");
+	if (!f) {
+		// if opening fails we won't be able to copy anyway
+		return copy_not_needed;
+	}
+
+	/* no error checking for fgetc(3) here, because this isn't a shebang if an
+	 * error occurs */
+	if (fgetc(f) == '#' && fgetc(f) == '!') {
+		/* This is an interpreted script. The interpreter's flags are what
+		 * affects whether DYLD_* is stripped, so read the interpreter's path
+		 * from the file to check that instead. Additionally, read any flags
+		 * that may be passed to the interpreter, since we'll have to do the
+		 * shebang expansion in user space if we move the interpreter. */
+		char *linep = NULL;
+		size_t linecapp = 0;
+		// read first line to get the interpreter and its arguments
+		if (getline(&linep, &linecapp, f) > 0) {
+			char *ctxt;
+            char *word;
+            size_t idx;
+			// do word splitting on the interpreter line and store it in new_argv
+			for (idx = 0, word = strtok_r(linep, " \t\n", &ctxt);
+					word != NULL;
+					idx++, word = strtok_r(NULL, " \t\n", &ctxt)) {
+				// make sure we have enough space allocated
+				if (new_argv == NULL) {
+					if ((new_argv = malloc(2 * sizeof(*new_argv))) == NULL) {
+                        free(linep);
+                        return copy_needed_error;
+					}
+					new_argc = 1;
+
+					// new_argv[0] will be overwritten in a second
+					// new_argv[1] is the terminating NULL
+					new_argv[0] = NULL;
+					new_argv[1] = NULL;
+				} else if (idx >= new_argc) {
+					// realloc to increase the size
+					char **oldargv = new_argv;
+					if ((new_argv = realloc(oldargv, (idx + 2) * sizeof(*new_argv))) == NULL) {
+						free_argv(oldargv);
+                        free(linep);
+                        return copy_needed_error;
+					}
+					new_argc = idx + 1;
+				}
+
+				// store a copy of the word in new_argv
+				new_argv[idx] = strdup(word);
+				if (!new_argv[idx]) {
+					free_argv(new_argv);
+                    free(linep);
+                    return copy_needed_error;
+				}
+                new_argv[idx + 1] = NULL;
+			}
+
+			free(linep);
+
+			if (new_argv && *new_argv) {
+				// interpreter found, check that instead of given path
+				realpath = *new_argv;
+			}
+		}
+	}
+
+	// check whether the binary has SF_RESTRICTED and isn't SUID/SGID
+	if (-1 == stat(realpath, st)) {
+		// on error, return and let execve(2) deal with it
+		free_argv(new_argv);
+		return copy_not_needed;
+	} else {
+		if (!(st->st_flags & SF_RESTRICTED)) {
+			// no SIP on this binary
+			free_argv(new_argv);
+			return copy_not_needed;
+		}
+		if ((st->st_flags & (S_ISUID | S_ISGID)) > 0) {
+			// the binary is SUID/SGID, which would get lost when copying;
+			// DYLD_ variables are stripped for SUID/SGID binaries anyway
+			free_argv(new_argv);
+			return copy_not_needed;
+		}
+	}
+
+	// prefix the shebang line to the original argv
+	if (new_argv != NULL) {
+        size_t argc = 0;
+        for (char *const *argvwalk = argv; argvwalk && *argvwalk; ++argvwalk) {
+            argc++;
+        }
+
+		// realloc to increase the size
+		char **oldargv = new_argv;
+		if ((new_argv = realloc(oldargv, (new_argc + argc + 1) * sizeof(*new_argv))) == NULL) {
+			free_argv(oldargv);
+            return copy_needed_error;
+		}
+
+		new_argv[new_argc] = strdup(path);
+		if (!new_argv[new_argc]) {
+            free_argv(new_argv);
+            return copy_needed_error;
+		}
+        new_argv[new_argc + 1] = NULL;
+
+		for (size_t idx = 1; idx < argc; ++idx) {
+			new_argv[new_argc + idx] = strdup(argv[idx]);
+			if (!new_argv[new_argc + idx]) {
+                free_argv(new_argv);
+                return copy_needed_error;
+			}
+            new_argv[new_argc + idx + 1] = NULL;
+		}
+
+		new_argc = new_argc + argc;
+
+		*outargv = new_argv;
+	}
+
+	return copy_is_needed;
+#endif /* defined(SF_RESTRICTED) */
+}
+
+static char *lazy_copy(const char *path, struct stat *in_st) {
+    char *retval = NULL;
+    uid_t euid = geteuid();
+    int outfd = -1;
+    int infd = -1;
+
+    char *target_folder = NULL;
+    char *target_path = NULL;
+    char *target_path_temp = NULL;
+    char *dir = strdup(path);
+    if (!dir) {
+        goto lazy_copy_out;
+    }
+    char *endslash = strrchr(dir, '/');
+    if (endslash) {
+        *endslash = '\0';
+    }
+
+    if (-1 == asprintf(&target_folder, "%s/%lu%s", DARWINTRACE_SIP_WORKAROUND_PATH, (unsigned long) euid, dir)) {
+        goto lazy_copy_out;
+    }
+
+    if (-1 == asprintf(&target_path, "%s/%lu%s", DARWINTRACE_SIP_WORKAROUND_PATH, (unsigned long) euid, path)) {
+        goto lazy_copy_out;
+    }
+
+    if (-1 == asprintf(&target_path_temp, "%s/%lu/.XXXXXXXXXXXXXX", DARWINTRACE_SIP_WORKAROUND_PATH, (unsigned long) euid)) {
+        goto lazy_copy_out;
+    }
+
+    // ensure directory exists
+    char *pos = target_folder + strlen(DARWINTRACE_SIP_WORKAROUND_PATH);
+    while (pos && *pos) {
+        *pos = '\0';
+        if (-1 == mkdir(target_folder, 0755) && errno != EEXIST) {
+            fprintf(stderr, "sip_copy_proc: mkdir(%s): %s\n", target_folder, strerror(errno));
+            goto lazy_copy_out;
+        }
+        *pos = '/';
+        pos++;
+        pos = strchr(pos, '/');
+    }
+    if (-1 == mkdir(target_folder, 0755) && errno != EEXIST) {
+        fprintf(stderr, "sip_copy_proc: mkdir(%s): %s\n", target_folder, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    // check whether copying is needed; it isn't if the file exists and the
+    // modification times match
+    struct stat out_st;
+    if (   -1 != stat(target_path, &out_st)
+        && in_st->st_mtimespec.tv_sec == out_st.st_mtimespec.tv_sec
+        && in_st->st_mtimespec.tv_nsec == out_st.st_mtimespec.tv_nsec) {
+        // copying not needed
+        retval = target_path;
+        goto lazy_copy_out;
+    }
+
+    // create temporary file to copy into and then later atomically replace
+    // target file
+    if (-1 == (outfd = mkstemp(target_path_temp))) {
+        fprintf(stderr, "sip_copy_proc: mkstemp(%s): %s\n", target_path_temp, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    if (-1 == (infd = open(path, O_RDONLY | O_CLOEXEC))) {
+        fprintf(stderr, "sip_copy_proc: open(%s, O_RDONLY | O_CLOEXEC): %s\n", path, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    // ensure mode is copied
+    if (-1 == fchmod(outfd, in_st->st_mode)) {
+        fprintf(stderr, "sip_copy_proc: fchmod(%s, %o): %s\n", target_path_temp, in_st->st_mode, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    char *buf = malloc(in_st->st_blksize);
+    ssize_t bytes_read = 0;
+    ssize_t bytes_written = 0;
+    bool error = false;
+    do {
+        bytes_read = read(infd, buf, in_st->st_blksize);
+        if (bytes_read < 0) {
+            if (errno == EINTR || errno == EAGAIN) {
+                continue;
+            } else {
+                error = true;
+                break;
+            }
+        }
+        if (bytes_read == 0) {
+            // EOF
+            break;
+        }
+
+        bytes_written = 0;
+        while (bytes_written < bytes_read) {
+            ssize_t written = write(outfd, buf + bytes_written, bytes_read - bytes_written);
+            if (written < 0) {
+                if (errno == EINTR || errno == EAGAIN) {
+                    continue;
+                }
+                error = true;
+                break;
+            }
+
+            bytes_written += written;
+        }
+    } while (!error);
+    if (bytes_read < 0 || bytes_written < 0) {
+        goto lazy_copy_out;
+    }
+
+    struct timeval times[2];
+    TIMESPEC_TO_TIMEVAL(&times[0], &in_st->st_mtimespec);
+    TIMESPEC_TO_TIMEVAL(&times[1], &in_st->st_mtimespec);
+    if (-1 == futimes(outfd, times)) {
+        fprintf(stderr, "sip_copy_proc: futimes(%s): %s\n", target_path_temp, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    if (-1 == rename(target_path_temp, target_path)) {
+        fprintf(stderr, "sip_copy_proc: rename(%s, %s): %s\n", target_path_temp, target_path, strerror(errno));
+        goto lazy_copy_out;
+    }
+
+    retval = target_path;
+
+lazy_copy_out:
+    {
+        int errno_save = errno;
+        close(outfd);
+        close(infd);
+        if (target_path_temp != NULL && -1 == unlink(target_path_temp) && errno != ENOENT) {
+            fprintf(stderr, "sip_copy_proc: unlink(%s): %s\n", target_path_temp, strerror(errno));
+            retval = NULL;
+        } else {
+            errno = errno_save;
+        }
+    }
+    free(dir);
+    free(target_path_temp);
+    free(target_folder);
+    if (retval != target_path) {
+        free(target_path);
+    }
+    return retval;
+}
+
+/**
+ * Behaves like execve(2), but checks whether trace mode is enabled (by
+ * checking for DYLD_INSERT_LIBRARIES in the environment) and the binary is
+ * covered by 10.11's new system integrity protection. If it is, the binary
+ * will be copied to a separate folder (or updated if already there and
+ * modification time differs) and executed from there.
+ */
+int sip_copy_execve(const char *path, char *const argv[], char *const envp[]) {
+    char **outargv = NULL;
+    struct stat st;
+
+    copy_needed_return_t need_copy = copy_needed(path, argv, &outargv, envp, &st);
+    switch (need_copy) {
+        case copy_needed_error:
+            return -1;
+            break;
+        case copy_not_needed:
+            return execve(path, argv, envp);
+            break;
+        case copy_is_needed: {
+                const char *to_be_copied = path;
+                char *const *to_be_argv = argv;
+                if (outargv) {
+                    to_be_copied = outargv[0];
+                    to_be_argv = outargv;
+                }
+
+                char *new_path = lazy_copy(to_be_copied, &st);
+                if (!new_path) {
+                    return -1;
+                }
+
+                int ret = execve(new_path, to_be_argv, envp);
+                free_argv(outargv);
+                free(new_path);
+                return ret;
+            }
+            break;
+    }
+}
+
+/**
+ * Behaves like posix_spawn(2), but checks whether trace mode is enabled (by
+ * checking for DYLD_INSERT_LIBRARIES in the environment) and the binary is
+ * covered by 10.11's new system integrity protection. If it is, the binary
+ * will be copied to a separate folder (or updated if already there and
+ * modification time differs) and executed from there.
+ */
+int sip_copy_posix_spawn(
+        pid_t *restrict pid,
+        const char *restrict path,
+        const posix_spawn_file_actions_t *file_actions,
+        const posix_spawnattr_t *restrict attrp,
+        char *const argv[restrict],
+        char *const envp[restrict]) {
+    char **outargv = NULL;
+    struct stat st;
+
+    copy_needed_return_t need_copy = copy_needed(path, argv, &outargv, envp, &st);
+    switch (need_copy) {
+        case copy_needed_error:
+            return -1;
+            break;
+        case copy_not_needed:
+            return posix_spawn(pid, path, file_actions, attrp, argv, envp);
+            break;
+        case copy_is_needed: {
+                const char *to_be_copied = path;
+                char *const *to_be_argv = argv;
+                if (outargv) {
+                    to_be_copied = outargv[0];
+                    to_be_argv = outargv;
+                }
+
+                char *new_path = lazy_copy(to_be_copied, &st);
+                if (!new_path) {
+                    return -1;
+                }
+
+                int ret = posix_spawn(pid, new_path, file_actions, attrp, to_be_argv, envp);
+                free_argv(outargv);
+                free(new_path);
+                return ret;
+            }
+            break;
+    }
+}

Added: trunk/base/src/pextlib1.0/sip_copy_proc.h
===================================================================
--- trunk/base/src/pextlib1.0/sip_copy_proc.h	                        (rev 0)
+++ trunk/base/src/pextlib1.0/sip_copy_proc.h	2015-10-18 00:49:32 UTC (rev 141420)
@@ -0,0 +1,60 @@
+/* vim: set et sw=4 ts=4 sts=4: */
+/*
+ * system.c
+ * $Id: system.c 138943 2015-07-24 20:35:45Z raimue at macports.org $
+ *
+ * Copyright (c) 2015 Clemens Lang <cal at macports.org>
+ * Copyright (c) 2015 The MacPorts Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The MacPorts Project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <spawn.h>
+
+/**
+ * Behaves like execve(2), but checks whether trace mode is enabled (by
+ * checking for DYLD_INSERT_LIBRARIES in the environment) and the binary is
+ * covered by 10.11's new system integrity protection. If it is, the binary
+ * will be copied to a separate folder (or updated if already there and
+ * modification time differs) and executed from there.
+ */
+int sip_copy_execve(const char *path, char *const argv[], char *const envp[]);
+
+
+/**
+ * Behaves like posix_spawn(2), but checks whether trace mode is enabled (by
+ * checking for DYLD_INSERT_LIBRARIES in the environment) and the binary is
+ * covered by 10.11's new system integrity protection. If it is, the binary
+ * will be copied to a separate folder (or updated if already there and
+ * modification time differs) and executed from there.
+ */
+int sip_copy_posix_spawn(
+        pid_t *restrict pid,
+        const char *restrict path,
+        const posix_spawn_file_actions_t *file_actions,
+        const posix_spawnattr_t *restrict attrp,
+        char *const argv[restrict],
+        char *const envp[restrict]);

Modified: trunk/base/src/pextlib1.0/system.c
===================================================================
--- trunk/base/src/pextlib1.0/system.c	2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/src/pextlib1.0/system.c	2015-10-18 00:49:32 UTC (rev 141420)
@@ -58,6 +58,7 @@
 #include <errno.h>
 
 #include "system.h"
+#include "sip_copy_proc.h"
 #include "Pextlib.h"
 
 #if HAVE_CRT_EXTERNS_H
@@ -237,13 +238,13 @@
             args[4] = "-c";
             args[5] = cmdstring;
             args[6] = NULL;
-            execve(sandbox_exec_path, args, environ);
+            sip_copy_execve(sandbox_exec_path, args, environ);
         } else {
             args[0] = "sh";
             args[1] = "-c";
             args[2] = cmdstring;
             args[3] = NULL;
-            execve("/bin/sh", args, environ);
+            sip_copy_execve("/bin/sh", args, environ);
         }
         exit(128);
         /*NOTREACHED*/

Modified: trunk/base/src/port1.0/portsandbox.tcl
===================================================================
--- trunk/base/src/port1.0/portsandbox.tcl	2015-10-18 00:49:18 UTC (rev 141419)
+++ trunk/base/src/port1.0/portsandbox.tcl	2015-10-18 00:49:32 UTC (rev 141420)
@@ -42,7 +42,7 @@
 # command line usage would be:
 # sandbox-exec -p '(version 1) (allow default) (deny file-write*) (allow file-write* <filter>)' some-command
 proc portsandbox::set_profile {target} {
-    global os.major portsandbox_profile workpath distpath altprefix \
+    global os.major portsandbox_profile workpath distpath prefix altprefix \
         package.destpath configure.ccache ccache_dir
 
     switch $target {
@@ -78,6 +78,7 @@
 
     # TODO: remove altprefix support
     lappend allow_dirs $workpath $altprefix
+    lappend allow_dirs $prefix/var/macports/sip-workaround
     if {${configure.ccache}} {
         lappend allow_dirs $ccache_dir
     }
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/macports-changes/attachments/20151212/687affee/attachment-0001.html>


More information about the macports-changes mailing list