source: trunk/base/src/darwintracelib1.0/darwintrace.c @ 106639

Last change on this file since 106639 was 106639, checked in by jmr@…, 7 years ago

update copyright notices

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 31.1 KB
Line 
1/*
2 * Copyright (c) 2005 Apple Inc. All rights reserved.
3 * Copyright (c) 2005-2006 Paul Guyot <pguyot@kallisys.net>,
4 * All rights reserved.
5 * Copyright (c) 2006-2013 The MacPorts Project
6 *
7 * $Id: darwintrace.c 106639 2013-06-03 05:36:00Z jmr@macports.org $
8 *
9 * @APPLE_BSD_LICENSE_HEADER_START@
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * 1.  Redistributions of source code must retain the above copyright
16 *     notice, this list of conditions and the following disclaimer.
17 * 2.  Redistributions in binary form must reproduce the above copyright
18 *     notice, this list of conditions and the following disclaimer in the
19 *     documentation and/or other materials provided with the distribution.
20 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
21 *     its contributors may be used to endorse or promote products derived
22 *     from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
25 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
28 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 * @APPLE_BSD_LICENSE_HEADER_END@
36 */
37
38#ifdef HAVE_CONFIG_H
39#include <config.h>
40#endif
41
42#if HAVE_SYS_CDEFS_H
43#include <sys/cdefs.h>
44#endif
45#if defined(_DARWIN_FEATURE_64_BIT_INODE) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE)
46/* The architecture we're building for has multiple versions of stat.
47   We need to undo sys/cdefs.h changes for _DARWIN_FEATURE_64_BIT_INODE */
48#undef  __DARWIN_64_BIT_INO_T
49#define __DARWIN_64_BIT_INO_T 0
50#undef  __DARWIN_SUF_64_BIT_INO_T
51#define __DARWIN_SUF_64_BIT_INO_T ""
52#undef _DARWIN_FEATURE_64_BIT_INODE
53#endif
54
55#ifdef HAVE_CRT_EXTERNS_H
56#include <crt_externs.h>
57#endif
58
59#ifdef HAVE_SYS_PATHS_H
60#include <sys/paths.h>
61#endif
62
63#include <dirent.h>
64#include <dlfcn.h>
65#include <errno.h>
66#include <fcntl.h>
67#include <stdarg.h>
68#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <sys/param.h>
72#include <sys/socket.h>
73#include <sys/stat.h>
74#include <sys/syscall.h>
75#include <sys/types.h>
76#include <sys/un.h>
77#include <unistd.h>
78
79#ifndef HAVE_STRLCPY
80/* Define strlcpy if it's not available. */
81size_t strlcpy(char* dst, const char* src, size_t size);
82size_t strlcpy(char* dst, const char* src, size_t size)
83{
84        size_t result = strlen(src);
85        if (size > 0)
86        {
87                size_t copylen = size - 1;
88                if (copylen > result)
89                {
90                        copylen = result;
91                }
92                memcpy(dst, src, copylen);
93                dst[copylen] = 0;
94        }
95        return result;
96}
97#endif
98
99/*
100 * Compile time options:
101 * DARWINTRACE_SHOW_PROCESS: show the process id of every access
102 * DARWINTRACE_LOG_CREATE: log creation of files as well.
103 * DARWINTRACE_SANDBOX: control creation, deletion and writing to files and dirs.
104 * DARWINTRACE_LOG_FULL_PATH: use F_GETPATH to log the full path.
105 * DARWINTRACE_DEBUG_OUTPUT: verbose output of stuff to debug darwintrace.
106 *
107 * global variables (only checked when setup is first called)
108 * DARWINTRACE_LOG
109 *    path to the log file (no logging happens if it's unset).
110 * DARWINTRACE_SANDBOX_BOUNDS
111 *    : separated allowed paths for the creation of files.
112 *    \: -> :
113 *    \\ -> \
114 */
115
116#ifndef DARWINTRACE_SHOW_PROCESS
117#define DARWINTRACE_SHOW_PROCESS 0
118#endif
119#ifndef DARWINTRACE_LOG_CREATE
120#define DARWINTRACE_LOG_CREATE 0
121#endif
122#ifndef DARWINTRACE_SANDBOX
123#define DARWINTRACE_SANDBOX 1
124#endif
125#ifndef DARWINTRACE_DEBUG_OUTPUT
126#define DARWINTRACE_DEBUG_OUTPUT 0
127#endif
128#ifndef DARWINTRACE_LOG_FULL_PATH
129#define DARWINTRACE_LOG_FULL_PATH 1
130#endif
131
132#ifndef DEFFILEMODE
133#define DEFFILEMODE 0666
134#endif
135
136/*
137 * Prototypes.
138 */
139inline int __darwintrace_strbeginswith(const char* str, const char* prefix);
140inline void __darwintrace_log_op(const char* op, const char* path, int fd);
141void __darwintrace_copy_env() __attribute__((constructor));
142inline char* __darwintrace_alloc_env(const char* varName, const char* varValue);
143inline char* const* __darwintrace_restore_env(char* const envp[]);
144static inline void __darwintrace_setup();
145inline void __darwintrace_cleanup_path(char *path);
146static char * exchange_with_port(const char * buf, size_t len, int answer);
147
148static int __darwintrace_fd = -2;
149static FILE *__darwintrace_debug = NULL;
150static pid_t __darwintrace_pid = (pid_t) -1;
151#define BUFFER_SIZE     1024
152
153/**
154 * filemap: path\0whattodo\0path\0whattodo\0\0
155 * path: begin of path (for example /opt)
156 * whattodo:
157 *   0     -- allow
158 *   1PATH -- map
159 *   2     -- ask for allow
160**/
161static char * filemap=0;
162
163/* copy of the global variables */
164static char* __env_dyld_insert_libraries;
165static char* __env_dyld_force_flat_namespace;
166static char* __env_darwintrace_log;
167static char* __env_darwintrace_debug_log;
168
169#if DARWINTRACE_DEBUG_OUTPUT
170#if __STDC_VERSION__>=199901L
171#define debug_printf(format, ...) fprintf(stderr, "darwintrace[%d]: " format, getpid(), __VA_ARGS__); \
172        if (__darwintrace_debug) { \
173                fprintf(__darwintrace_debug, "darwintrace: " format, __VA_ARGS__); \
174        }
175#else
176__attribute__ ((format (printf, 1, 2)))
177static inline
178int debug_printf(const char *format, ...) {
179    int ret;
180    va_list args;
181    va_start(args, format);
182    ret = vfprintf(stderr, format, args);
183    va_end(args);
184    return ret;
185}
186#endif
187#else
188#define debug_printf(...)
189#endif
190
191/*
192 * return 0 if str doesn't begin with prefix, 1 otherwise.
193 */
194inline int __darwintrace_strbeginswith(const char* str, const char* prefix) {
195        char theCharS;
196        char theCharP;
197        do {
198                theCharS = *str++;
199                theCharP = *prefix++;
200        } while(theCharP && (theCharP == theCharS));
201        return (theCharP == 0);
202}
203
204/*
205 * Copy the environment variables, if they're defined.
206 */
207void __darwintrace_copy_env() {
208        char* theValue;
209        theValue = getenv("DYLD_INSERT_LIBRARIES");
210        if (theValue != NULL) {
211                __env_dyld_insert_libraries = strdup(theValue);
212        } else {
213                __env_dyld_insert_libraries = NULL;
214        }
215        theValue = getenv("DYLD_FORCE_FLAT_NAMESPACE");
216        if (theValue != NULL) {
217                __env_dyld_force_flat_namespace = strdup(theValue);
218        } else {
219                __env_dyld_force_flat_namespace = NULL;
220        }
221        theValue = getenv("DARWINTRACE_LOG");
222        if (theValue != NULL) {
223                __env_darwintrace_log = strdup(theValue);
224        } else {
225                __env_darwintrace_log = NULL;
226        }
227        theValue = getenv("DARWINTRACE_DEBUG_LOG");
228        if (theValue != NULL) {
229                __env_darwintrace_debug_log = strdup(theValue);
230        } else {
231                __env_darwintrace_debug_log = NULL;
232        }
233}
234
235/*
236 * Allocate a X=Y string where X is the variable name and Y its value.
237 * Return the new string.
238 *
239 * If the value is NULL, return NULL.
240 */
241inline char* __darwintrace_alloc_env(const char* varName, const char* varValue) {
242        char* theResult = NULL;
243        if (varValue) {
244                int theSize = strlen(varName) + strlen(varValue) + 2;
245                theResult = (char*) malloc(theSize);
246                if (theResult) {
247                    snprintf(theResult, theSize, "%s=%s", varName, varValue);
248                    theResult[theSize - 1] = 0;
249                }
250        }
251       
252        return theResult;
253}
254
255/*
256 * This function checks that envp contains the global variables we had when the
257 * library was loaded and modifies it if it doesn't.
258 */
259__attribute__((always_inline))
260inline char* const* __darwintrace_restore_env(char* const envp[]) {
261        /* allocate the strings. */
262        /* we don't care about the leak here because we're going to call execve,
263     * which, if it succeeds, will get rid of our heap */
264        char* dyld_insert_libraries_ptr =       
265                __darwintrace_alloc_env(
266                        "DYLD_INSERT_LIBRARIES",
267                        __env_dyld_insert_libraries);
268        char* dyld_force_flat_namespace_ptr =   
269                __darwintrace_alloc_env(
270                        "DYLD_FORCE_FLAT_NAMESPACE",
271                        __env_dyld_force_flat_namespace);
272        char* darwintrace_log_ptr =     
273                __darwintrace_alloc_env(
274                        "DARWINTRACE_LOG",
275                        __env_darwintrace_log);
276        char* darwintrace_debug_log_ptr =       
277                __darwintrace_alloc_env(
278                        "DARWINTRACE_DEBUG_LOG",
279                        __env_darwintrace_debug_log);
280
281        char* const * theEnvIter = envp;
282        int theEnvLength = 0;
283        char** theCopy;
284        char** theCopyIter;
285
286        while (*theEnvIter != NULL) {
287                theEnvLength++;
288                theEnvIter++;
289        }
290
291        /* 5 is sufficient for the four variables we copy and the terminator */
292        theCopy = (char**) malloc(sizeof(char*) * (theEnvLength + 5));
293        theEnvIter = envp;
294        theCopyIter = theCopy;
295
296        while (*theEnvIter != NULL) {
297                char* theValue = *theEnvIter;
298                if (__darwintrace_strbeginswith(theValue, "DYLD_INSERT_LIBRARIES=")) {
299                        theValue = dyld_insert_libraries_ptr;
300                        dyld_insert_libraries_ptr = NULL;
301                } else if (__darwintrace_strbeginswith(theValue, "DYLD_FORCE_FLAT_NAMESPACE=")) {
302                        theValue = dyld_force_flat_namespace_ptr;
303                        dyld_force_flat_namespace_ptr = NULL;
304                } else if (__darwintrace_strbeginswith(theValue, "DARWINTRACE_LOG=")) {
305                        theValue = darwintrace_log_ptr;
306                        darwintrace_log_ptr = NULL;
307                } else if (__darwintrace_strbeginswith(theValue, "DARWINTRACE_DEBUG_LOG=")) {
308                        theValue = darwintrace_debug_log_ptr;
309                        darwintrace_debug_log_ptr = NULL;
310                }
311               
312                if (theValue) {
313                        *theCopyIter++ = theValue;
314                }
315
316                theEnvIter++;
317        }
318       
319        if (dyld_insert_libraries_ptr) {
320                *theCopyIter++ = dyld_insert_libraries_ptr;
321        }
322        if (dyld_force_flat_namespace_ptr) {
323                *theCopyIter++ = dyld_force_flat_namespace_ptr;
324        }
325        if (darwintrace_log_ptr) {
326                *theCopyIter++ = darwintrace_log_ptr;
327        }
328        if (darwintrace_debug_log_ptr) {
329                *theCopyIter++ = darwintrace_debug_log_ptr;
330        }
331
332        *theCopyIter = 0;
333       
334        return theCopy;
335}
336
337static void ask_for_filemap()
338{
339        filemap=exchange_with_port("filemap\t", sizeof("filemap\t"), 1);
340        if(filemap==(char*)-1)
341                filemap=0;
342}
343
344__attribute__((always_inline))
345static inline void __darwintrace_setup() {
346#define open(x,y,z) syscall(SYS_open, (x), (y), (z))
347#define close(x) syscall(SYS_close, (x))
348        pid_t oldpid = __darwintrace_pid;
349        if (__darwintrace_pid != (pid_t) -1 && __darwintrace_pid != getpid()) {
350                if (__darwintrace_fd != -2) {
351                        close(__darwintrace_fd);
352                        __darwintrace_fd = -2;
353                }
354                if (__darwintrace_debug) {
355                        fclose(__darwintrace_debug);
356                        __darwintrace_debug = NULL;
357                }
358                __darwintrace_pid = (pid_t) -1;
359        }
360        if (__darwintrace_pid == (pid_t) -1) {
361                __darwintrace_pid = getpid();
362                if (__env_darwintrace_log != NULL) {
363                        int olderrno = errno;
364                        int sock = socket(AF_UNIX, SOCK_STREAM, 0);
365                        struct sockaddr_un sun;
366                        sun.sun_family = AF_UNIX;
367                        strncpy(sun.sun_path, __env_darwintrace_log, sizeof(sun.sun_path));
368                        if (connect(sock, (struct sockaddr*)&sun, strlen(__env_darwintrace_log) + 1 + sizeof(sun.sun_family)) != -1) {
369                                debug_printf("connect successful, socket %d in pid %d\n", sock, __darwintrace_pid);
370                                __darwintrace_fd = sock;
371                                ask_for_filemap();
372                        } else {
373                                debug_printf("connect failed: %s\n", strerror(errno));
374                                abort();
375                        }
376                        errno = olderrno;
377                }
378                if (__darwintrace_debug == NULL) {
379                        if (__env_darwintrace_debug_log != NULL) {
380                                char logpath[MAXPATHLEN];
381                                snprintf(logpath, MAXPATHLEN, __env_darwintrace_debug_log, getpid());
382                                if (NULL == (__darwintrace_debug = fopen(logpath, "w"))) {
383                                        fprintf(stderr, "failed to open logfile: %s\n", strerror(errno));
384                                        abort();
385                                }
386                                fprintf(__darwintrace_debug, "pid %d is process %s\n", getpid(), getenv("_"));
387                                debug_printf("logging socket communication to: %s\n", logpath);
388                        }
389                }
390                if (oldpid != (pid_t) -1) {
391                        debug_printf("seems to have forked from %d, re-opened files\n", oldpid);
392                }
393        }
394#undef close
395#undef open
396}
397
398/* log a call and optionally get the real path from the fd if it's not 0.
399 * op:                  the operation (open, readlink, execve)
400 * path:                the path of the file
401 * fd:                  a fd to the file, or 0 if we don't have any.
402 */
403__attribute__((always_inline))
404inline void __darwintrace_log_op(const char* op, const char* path, int fd) {
405        int size;
406        char somepath[MAXPATHLEN];
407        char logbuffer[BUFFER_SIZE];
408
409        do {
410#ifdef __APPLE__ /* Only Darwin has volfs and F_GETPATH */
411                if ((fd > 0) && (DARWINTRACE_LOG_FULL_PATH
412                        || (strncmp(path, "/.vol/", 6) == 0))) {
413                        if(fcntl(fd, F_GETPATH, somepath) == -1) {
414                                /* getpath failed. use somepath instead */
415                                strlcpy(somepath, path, sizeof(somepath));
416                                break;
417                        }
418                }
419#endif
420                if (path[0] != '/') {
421                        int len;
422                        (void) getcwd(somepath, sizeof(somepath));
423                        len = strlen(somepath);
424                        somepath[len++] = '/';
425                        strlcpy(&somepath[len], path, sizeof(somepath) - len);
426                        break;
427                }
428
429                /* otherwise, just copy the original path. */
430                strlcpy(somepath, path, sizeof(somepath));
431        } while (0);
432
433        /* clean the path. */
434        __darwintrace_cleanup_path(somepath);
435
436        size = snprintf(logbuffer, sizeof(logbuffer),
437                "%s\t%s",
438                op, somepath);
439
440        exchange_with_port(logbuffer, size+1, 0);
441       
442        return;
443}
444
445/* remap resource fork access to the data fork.
446 * do a partial realpath(3) to fix "foo//bar" to "foo/bar"
447 */
448inline void __darwintrace_cleanup_path(char *path) {
449        size_t pathlen;
450#ifdef __APPLE__
451        size_t rsrclen;
452#endif
453        size_t i, shiftamount;
454        enum { SAWSLASH, NOTHING } state = NOTHING;
455
456        /* if this is a foo/..namedfork/rsrc, strip it off */
457        pathlen = strlen(path);
458        /* ..namedfork/rsrc is only on OS X */
459#ifdef __APPLE__
460        rsrclen = strlen(_PATH_RSRCFORKSPEC);
461        if (pathlen > rsrclen && 0 == strcmp(path + pathlen - rsrclen, _PATH_RSRCFORKSPEC)) {
462                path[pathlen - rsrclen] = '\0';
463                pathlen -= rsrclen;
464        }
465#endif
466
467        /* for each position in string (including terminal \0), check if we're in
468         * a run of multiple slashes, and only emit the first one */
469        for(i = 0, shiftamount = 0; i <= pathlen; i++) {
470                if (state == SAWSLASH) {
471                        if (path[i] == '/') {
472                                /* consume it */
473                                shiftamount++;
474                                continue;
475                        } else {
476                                state = NOTHING;
477                        }
478                } else {
479                        if (path[i] == '/') {
480                                state = SAWSLASH;
481                        }
482                }
483                path[i - shiftamount] = path[i];
484        }
485}
486
487/*
488 * return 1 if path allowed, 0 otherwise
489 */
490static int ask_for_dependency(char * path) {
491#define stat(y, z) syscall(SYS_stat, (y), (z))
492        char buffer[BUFFER_SIZE], *p;
493        int result = 0;
494        struct stat st;
495
496        debug_printf("ask_for_dependency: %s\n", path);
497
498        if (-1 == stat(path, &st)) {
499                return 1;
500        }
501        if (S_ISDIR(st.st_mode)) {
502                debug_printf("%s is directory\n", path);
503                return 1;
504        }
505       
506        strncpy(buffer, "dep_check\t", sizeof(buffer));
507        strncpy(buffer+10, path, sizeof(buffer)-10);
508        p=exchange_with_port(buffer, strlen(buffer)+1, 1);
509        if(p==(char*)-1||!p)
510                return 0;
511       
512        if(*p=='+')
513                result=1;
514       
515        free(p);
516        return result;
517#undef stat
518}
519
520/*
521 * exchange_with_port - routine to send/recv from/to socket
522 * Parameters:
523 *   buf      -- buffer with data to send
524 *   len      -- length of data
525 *   answer   -- 1 (yes, I want to receive answer) and 0 (no, thanks, just send)
526 *   failures -- should be setted 0 on external calls (avoid infinite recursion)
527 * Return value:
528 *    -1     -- something went wrong
529 *    0      -- data successfully sent
530 *    string -- answer (caller shoud free it)
531 */
532static char * exchange_with_port(const char * buf, size_t len, int answer) {
533        size_t sent = 0;
534
535        if (__darwintrace_debug) {
536                fprintf(__darwintrace_debug, "> %s\n", buf);
537        }
538        while (sent < len) {
539                ssize_t local_sent = send(__darwintrace_fd, buf + sent, len - sent, 0);
540                if (local_sent == -1) {
541                        debug_printf("error communicating with socket %d: %s\n", __darwintrace_fd, strerror(errno));
542                        if (__darwintrace_debug)
543                                fprintf(__darwintrace_debug, "darwintrace: error communicating with socket %d: %s\n", __darwintrace_fd, strerror(errno));
544                        abort();
545                }
546                sent += local_sent;
547        }
548        if (!answer) {
549                return 0;
550        } else {
551                size_t recv_len = 0, received;
552                char *recv_buf;
553               
554                received = 0;
555                while (received < sizeof(recv_len)) {
556                        ssize_t local_received = recv(__darwintrace_fd, ((char *) &recv_len) + received, sizeof(recv_len) - received, 0);
557                        if (local_received == -1) {
558                                debug_printf("error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
559                                if (__darwintrace_debug)
560                                        fprintf(__darwintrace_debug, "darwintrace: error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
561                                abort();
562                        }
563                        received += local_received;
564                }
565                if (recv_len == 0) {
566                        return 0;
567                }
568
569                recv_buf = malloc(recv_len + 1);
570                recv_buf[recv_len] = '\0';
571
572                received = 0;
573                while (received < recv_len) {
574                        ssize_t local_received = recv(__darwintrace_fd, recv_buf + received, recv_len - received, 0);
575                        if (local_received == -1) {
576                                debug_printf("error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
577                                if (__darwintrace_debug)
578                                        fprintf(__darwintrace_debug, "darwintrace: error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
579                                abort();
580                        }
581                        received += local_received;
582                }
583                if (__darwintrace_debug) {
584                        fprintf(__darwintrace_debug, "< %s\n", recv_buf);
585                }
586                return recv_buf;
587        }
588}
589
590#define DARWINTRACE_STATUS_PATH    ((char) 0)
591#define DARWINTRACE_STATUS_COMMAND ((char) 1)
592#define DARWINTRACE_STATUS_DONE    ((char) 2)
593
594/*
595 * return 1 if path (once normalized) is in sandbox or redirected, 0 otherwise.
596 */
597__attribute__((always_inline))
598inline int __darwintrace_is_in_sandbox(const char* path, char * newpath) {
599        char *t, *p, *_;
600        char lpath[MAXPATHLEN];
601       
602        __darwintrace_setup();
603       
604        if (!filemap)
605                return 1;
606       
607        if (*path=='/') {
608                p = strcpy(lpath, path);
609        } else {
610                if (getcwd(lpath, MAXPATHLEN - 1) == NULL) {
611                        fprintf(stderr, "darwintrace: getcwd: %s, path was: %s\n", strerror(errno), path);
612                        abort();
613                }
614                strcat(lpath, "/");
615                strcat(lpath, path);
616        }
617
618        p = lpath;
619
620        for (t = filemap; *t;) {
621                char state;
622               
623                if (__darwintrace_strbeginswith(p, t)) {
624                        /* move t to the integer describing how to handle this match */
625                        t += strlen(t) + 1;
626                        switch (*t) {
627                                case 0:
628                                        return 1;
629                                case 1:
630                                        if (!newpath) {
631                                                return 0;
632                                        }
633                                        /* the redirected path starts right after the byte telling
634                                         * us we should redirect */
635                                        strcpy(newpath, t + 1);
636                                        _ = newpath + strlen(newpath);
637                                        /* append '/' if it's missing */
638                                        if (_[-1] != '/') {
639                                                *_ = '/';
640                                        }
641                                        strcpy(_, p);
642                                        return 1;
643                                case 2:
644                                        /* ask the socket whether this file is OK */
645                                        return ask_for_dependency(p);
646                                default:
647                                        fprintf(stderr, "darwintrace: error: unexpected byte in file map: `%x'\n", *t);
648                                        abort();
649                        }
650                }
651
652                /* advance the cursor: if the number after the string is not 1, there's
653                 * no path behind it and we can advance by strlen(t) + 3. If it is 1,
654                 * make sure to skip the path, too.
655                 */
656                state = DARWINTRACE_STATUS_PATH;
657                while (state != DARWINTRACE_STATUS_DONE) {
658                        switch (state) {
659                                case DARWINTRACE_STATUS_PATH:
660                                        if (!*t) {
661                                                state = DARWINTRACE_STATUS_COMMAND;
662                                        }
663                                        break;
664                                case DARWINTRACE_STATUS_COMMAND:
665                                        if (*t == 1) {
666                                                state = DARWINTRACE_STATUS_PATH;
667                                                t++;
668                                        } else {
669                                                state = DARWINTRACE_STATUS_DONE;
670                                        }
671                                        break;
672                        }
673                        t++;
674                }
675                t++;
676        }
677
678        __darwintrace_log_op("sandbox_violation", path, 0);
679        return 0;
680}
681
682/* wrapper for open(2) preventing opening files outside the sandbox */
683int open(const char* path, int flags, ...) {
684#define open(x,y,z) syscall(SYS_open, (x), (y), (z))
685        mode_t mode;
686        va_list args;
687        char newpath[MAXPATHLEN];
688
689        debug_printf("open(%s)\n", path);
690
691        *newpath = '\0';
692        if (!__darwintrace_is_in_sandbox(path, newpath)) {
693                debug_printf("open %s was forbidden\n", path);
694                errno = ((flags & O_CREAT) > 0) ? EACCES : ENOENT;
695                return -1;
696        }
697
698        if (*newpath) {
699                path = newpath;
700        }
701
702        /* Why mode here ? */
703        va_start(args, flags);
704        mode = va_arg(args, int);
705        va_end(args);
706
707        return open(path, flags, mode);
708#undef open
709}
710
711/* Log calls to readlink(2) into the file specified by DARWINTRACE_LOG.
712   Only logs if the DARWINTRACE_LOG environment variable is set.
713   Only logs files where the readlink succeeds.
714*/
715#ifdef READLINK_IS_NOT_P1003_1A
716int readlink(const char * path, char * buf, int bufsiz) {
717#else
718ssize_t readlink(const char * path, char * buf, size_t bufsiz) {
719#endif
720#define readlink(x,y,z) syscall(SYS_readlink, (x), (y), (z))
721        char newpath[MAXPATHLEN];
722
723        debug_printf("readlink(%s)\n", path);
724
725        *newpath = '\0';
726        if (!__darwintrace_is_in_sandbox(path, newpath)) {
727                errno = ENOENT;
728                return -1;
729        }
730
731        if (*newpath) {
732                path = newpath;
733        }
734
735        return readlink(path, buf, bufsiz);
736#undef readlink
737}
738
739int execve(const char* path, char* const argv[], char* const envp[]) {
740#define __execve(x,y,z) syscall(SYS_execve, (x), (y), (z))
741#define open(x,y,z) syscall(SYS_open, (x), (y), (z))
742#define close(x) syscall(SYS_close, (x))
743#define lstat(x, y) syscall(SYS_lstat, (x), (y))
744        debug_printf("execve(%s)\n", path);
745        __darwintrace_setup();
746        if (__darwintrace_fd >= 0) {
747                struct stat sb;
748                /* for symlinks, we want to capture both the original path and the
749                 * modified one, since for /usr/bin/gcc -> gcc-4.0, both "gcc_select"
750                 * and "gcc" are contributors
751                 */
752                if (lstat(path, &sb) == 0) {
753                        int fd;
754                        if (S_ISLNK(sb.st_mode)) {
755                                /* for symlinks, print both */
756                                __darwintrace_log_op("execve", path, 0);
757                        }
758
759                        fd = open(path, O_RDONLY, 0);
760                        if (fd > 0) {
761                                char buffer[MAXPATHLEN+1];
762                                ssize_t bytes_read;
763
764                                if(!__darwintrace_is_in_sandbox(path, NULL)) {
765                                        close(fd);
766                                        errno = ENOENT;
767                                        return -1;
768                                }
769       
770                                /* once we have an open fd, if a full path was requested, do it */
771                                __darwintrace_log_op("execve", path, fd);
772       
773                                /* read the file for the interpreter */
774                                bytes_read = read(fd, buffer, MAXPATHLEN);
775                                buffer[bytes_read] = 0;
776                                if (bytes_read > 2 && buffer[0] == '#' && buffer[1] == '!') {
777                                        const char* interp = &buffer[2];
778                                        int i;
779                                        /* skip past leading whitespace */
780                                        for (i = 2; i < bytes_read; ++i) {
781                                                if (buffer[i] != ' ' && buffer[i] != '\t') {
782                                                        interp = &buffer[i];
783                                                        break;
784                                                }
785                                        }
786                                        /* found interpreter (or ran out of data); skip until next
787                                         * whitespace, then terminate the string */
788                                        for (; i < bytes_read; ++i) {
789                                                if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n') {
790                                                        buffer[i] = 0;
791                                                        break;
792                                                }
793                                        }
794                                        /* we have liftoff */
795                                        if (interp && interp[0] != '\0') {
796                                                __darwintrace_log_op("execve", interp, 0);
797                                        }
798                                }
799                                close(fd);
800                        }
801                }
802        }
803        /* our variables won't survive exec, clean up */
804        if (__darwintrace_fd != -2) {
805                close(__darwintrace_fd);
806                __darwintrace_fd = -2;
807        }
808        if (__darwintrace_debug) {
809                fclose(__darwintrace_debug);
810                __darwintrace_debug = NULL;
811        }
812        __darwintrace_pid = (pid_t) -1;
813
814        /* call the original execve function, but fix the environment if required. */
815        return __execve(path, argv, __darwintrace_restore_env(envp));
816#undef lstat
817#undef close
818#undef open
819#undef execve
820}
821
822/* if darwintrace has been initialized, trap attempts to close our file
823 * descriptor */
824int close(int fd) {
825#define close(x) syscall(SYS_close, (x))
826        if (__darwintrace_fd != -2 && fd == __darwintrace_fd) {
827                errno = EBADF;
828                return -1;
829        }
830
831        return close(fd);
832#undef close
833}
834
835/* if darwintrace has been initialized, trap attempts to dup2 over our file descriptor */
836int dup2(int filedes, int filedes2) {
837#define dup2(x, y) syscall(SYS_dup2, (x), (y))
838
839        debug_printf("dup2(%d, %d)\n", filedes, filedes2);
840        if (__darwintrace_fd != -2 && filedes2 == __darwintrace_fd) {
841                /* if somebody tries to close our file descriptor, just move it out of
842                 * the way. Make sure it doesn't end up as stdin/stdout/stderr, though!
843                 * */
844                int new_darwintrace_fd;
845
846                if (-1 == (new_darwintrace_fd = fcntl(__darwintrace_fd, F_DUPFD, STDOUT_FILENO + 1))) {
847                        /* if duplicating fails, do not allow overwriting either! */
848                        return -1;
849                }
850
851                debug_printf("moving __darwintrace_fd from %d to %d\n", __darwintrace_fd, new_darwintrace_fd);
852                __darwintrace_fd = new_darwintrace_fd;
853        }
854
855        return dup2(filedes, filedes2);
856#undef dup2
857}
858
859
860/* Trap attempts to unlink a file outside the sandbox. */
861int unlink(const char* path) {
862#define __unlink(x) syscall(SYS_unlink, (x))
863        char newpath[MAXPATHLEN];
864
865        *newpath = '\0';
866        if (!__darwintrace_is_in_sandbox(path, newpath)) {
867                debug_printf("unlink %s was forbidden\n", path);
868                errno = ENOENT;
869                return -1;
870        }
871
872        if (*newpath) {
873                path = newpath;
874        }
875
876        debug_printf("unlink %s was allowed\n", path);
877
878        return __unlink(path);
879}
880
881/* Trap attempts to create directories outside the sandbox.
882 */
883int mkdir(const char* path, mode_t mode) {
884#define __mkdir(x,y) syscall(SYS_mkdir, (x), (y))
885        char newpath[MAXPATHLEN];
886
887        *newpath = '\0';
888        if (!__darwintrace_is_in_sandbox(path, newpath)) {
889                struct stat st;
890                if (-1 == lstat(path, &st)) {
891                        if (errno == ENOENT) {
892                                /* directory doesn't exist yet */
893                                debug_printf("mkdir was forbidden at %s\n", path);
894                                errno = EACCES;
895                                return -1;
896                        }
897                }
898                /* otherwise, mkdir will do nothing or fail with a hopefully meaningful
899                 * error */
900        } else {
901                if (*newpath) {
902                        path = newpath;
903                }
904
905                debug_printf("mkdir was allowed at %s\n", path);
906        }
907
908        return __mkdir(path, mode);
909}
910
911/* Trap attempts to remove directories outside the sandbox.
912 */
913int rmdir(const char* path) {
914#define __rmdir(x) syscall(SYS_rmdir, (x))
915        if (!__darwintrace_is_in_sandbox(path, NULL)) {
916                debug_printf("removing directory %s was forbidden\n", path);
917                errno = ENOENT;
918                return -1;
919        }
920
921        debug_printf("rmdir %s was allowed\n", path);
922
923        return __rmdir(path);
924}
925
926/* Trap attempts to rename files/directories outside the sandbox.
927 */
928int rename(const char* from, const char* to) {
929#define __rename(x,y) syscall(SYS_rename, (x), (y))
930        if (!__darwintrace_is_in_sandbox(from, NULL)) {
931                /* outside sandbox, forbid */
932                debug_printf("renaming from %s was forbidden\n", from);
933                errno = ENOENT;
934                return -1;
935        }
936        if (!__darwintrace_is_in_sandbox(to, NULL)) {
937                debug_printf("renaming to %s was forbidden\n", to);
938                errno = EACCES;
939                return -1;
940        }
941
942        debug_printf("renaming from %s to %s was allowed\n", from, to);
943
944        return __rename(from, to);
945}
946
947int stat(const char * path, struct stat * sb) {
948#define stat(path, sb) syscall(SYS_stat, path, sb)
949        int result = 0;
950        char newpath[MAXPATHLEN];
951
952        debug_printf("stat(%s)\n", path);
953        if (-1 == (result = stat(path, sb))) {
954                return -1;
955        }
956
957        if (S_ISDIR(sb->st_mode)) {
958                return result;
959        }
960
961        *newpath = '\0';
962        if (!__darwintrace_is_in_sandbox(path, newpath)) {
963                errno = ENOENT;
964                return -1;
965        }
966
967        if (*newpath) {
968                result = stat(newpath, sb);
969        }
970
971        return result;
972#undef stat
973}
974
975#if defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE)
976
977int stat64(const char * path, struct stat64 * sb) {
978#define stat64(path, sb) syscall(SYS_stat64, path, sb)
979        int result = 0;
980        char newpath[MAXPATHLEN];
981
982        debug_printf("stat64(%s)\n", path);
983        if (-1 == (result = stat64(path, sb))) {
984                return -1;
985        }
986
987        if (S_ISDIR(sb->st_mode)) {
988                return result;
989        }
990
991        *newpath = '\0';
992        if (!__darwintrace_is_in_sandbox(path, newpath)) {
993                errno = ENOENT;
994                return -1;
995        }
996
997        if (*newpath) {
998                result = stat64(newpath, sb);
999        }
1000
1001        return result;
1002#undef stat64
1003}
1004
1005int stat$INODE64(const char * path, struct stat64 * sb) {
1006    return stat64(path, sb);
1007}
1008
1009#endif /* defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE) */
1010
1011
1012int lstat(const char * path, struct stat * sb) {
1013#define lstat(path, sb) syscall(SYS_lstat, path, sb)
1014        int result = 0;
1015        char newpath[MAXPATHLEN];
1016
1017        debug_printf("lstat(%s)\n", path);
1018        if (-1 == (result = lstat(path, sb))) {
1019                return -1;
1020        }
1021
1022        if (S_ISDIR(sb->st_mode)) {
1023                return result;
1024        }
1025
1026        *newpath = '\0';
1027        if (!__darwintrace_is_in_sandbox(path, newpath)) {
1028                errno = ENOENT;
1029                return -1;
1030        }
1031
1032        if (*newpath) {
1033                result = lstat(newpath, sb);
1034        }
1035
1036        return result;
1037#undef lstat
1038}
1039
1040#if defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE)
1041
1042int lstat64(const char * path, struct stat64 * sb) {
1043#define lstat64(path, sb) syscall(SYS_lstat64, path, sb)
1044        int result = 0;
1045        char newpath[MAXPATHLEN];
1046
1047        debug_printf("lstat64(%s)\n", path);
1048        if (-1 == (result = lstat64(path, sb))) {
1049                return -1;
1050        }
1051
1052        if (S_ISDIR(sb->st_mode)) {
1053                return result;
1054        }
1055
1056        *newpath = '\0';
1057        if (!__darwintrace_is_in_sandbox(path, newpath)) {
1058                errno = ENOENT;
1059                return -1;
1060        }
1061
1062        if (*newpath) {
1063                result = lstat64(newpath, sb);
1064        }
1065
1066        return result;
1067#undef lstat64
1068}
1069
1070int lstat$INODE64(const char * path, struct stat64 * sb) {
1071    return lstat64(path, sb);
1072}
1073
1074#endif /* defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE) */
1075
1076/**
1077 * re-implementation of getdirent(2) and __getdirent64(2) preventing paths
1078 * outside the sandbox to show up when reading the contents of a directory.
1079 * Unfortunately, since we need to access the contents of the buffer, but the
1080 * contents differ by architecture, we can not rely on the dirent structure
1081 * defined by the header included by this program, because we don't know
1082 * whether darwintrace.dylib has been compiled for 64bit or 32bit inodes. We
1083 * thus copy both structs and decide at runtime.
1084 */
1085
1086#ifdef __APPLE__
1087/* only do this on mac, because fcntl(fd, F_GETPATH) might not be available on
1088 * other systems, and because other system's syscall names are probably
1089 * different anyway */
1090
1091#if defined(__DARWIN_64_BIT_INO_T)
1092
1093struct dirent64  {
1094        __uint64_t  d_ino;      /* file number of entry */
1095        __uint64_t  d_seekoff;  /* seek offset */
1096        __uint16_t  d_reclen;   /* length of this record */
1097        __uint16_t  d_namlen;   /* length of string in d_name */
1098        __uint8_t   d_type;     /* file type */
1099        char      d_name[__DARWIN_MAXPATHLEN]; /* entry name (up to MAXPATHLEN bytes) */
1100};
1101
1102size_t __getdirentries64(int fd, void *buf, size_t bufsize, __darwin_off_t *basep) {
1103#define __getdirentries64(w,x,y,z) syscall(SYS_getdirentries64, (w), (x), (y), (z))
1104        size_t sz = __getdirentries64(fd, buf, bufsize, basep);
1105        char dirname[MAXPATHLEN];
1106        size_t dnamelen;
1107
1108        if (-1 == fcntl(fd, F_GETPATH, dirname)) {
1109                errno = EBADF;
1110                return -1;
1111        }
1112
1113        dnamelen = strlen(dirname);
1114        if (dirname[dnamelen - 1] != '/') {
1115                dirname[dnamelen] = '/';
1116                dirname[dnamelen + 1] = '\0';
1117                dnamelen++;
1118        }
1119
1120        dnamelen = strlen(dirname);
1121        size_t offset;
1122        for (offset = 0; offset < sz;) {
1123                struct dirent64 *dent = (struct dirent64 *)(((char *) buf) + offset);
1124                dirname[dnamelen] = '\0';
1125                strcat(dirname, dent->d_name);
1126                if (!__darwintrace_is_in_sandbox(dirname, NULL)) {
1127                        debug_printf("__getdirentries64: filtered %s\n", dirname);
1128                        dent->d_ino = 0;
1129                } else {
1130                        debug_printf("__getdirentries64:  allowed %s\n", dirname);
1131                }
1132                offset += dent->d_reclen;
1133        }
1134
1135        return sz;
1136#undef __getdirentries64
1137}
1138
1139#endif /* defined(__DARWIN_64_BIT_INO_T) */
1140
1141#pragma pack(4)
1142struct dirent32 {
1143        ino_t d_ino;            /* file number of entry */
1144        __uint16_t d_reclen;    /* length of this record */
1145        __uint8_t  d_type;      /* file type */
1146        __uint8_t  d_namlen;    /* length of string in d_name */
1147        char d_name[__DARWIN_MAXNAMLEN + 1]; /* name must be no longer than this */
1148};
1149#pragma pack()
1150
1151int getdirentries(int fd, char *buf, int nbytes, long *basep) {
1152#define getdirentries(w,x,y,z) syscall(SYS_getdirentries, (w), (x), (y), (z))
1153        size_t sz = getdirentries(fd, buf, nbytes, basep);
1154        char dirname[MAXPATHLEN];
1155        size_t dnamelen;
1156
1157        if (-1 == fcntl(fd, F_GETPATH, dirname)) {
1158                errno = EBADF;
1159                return 0;
1160        }
1161
1162        dnamelen = strlen(dirname);
1163        if (dirname[dnamelen - 1] != '/') {
1164                dirname[dnamelen] = '/';
1165                dirname[dnamelen + 1] = '\0';
1166                dnamelen++;
1167        }
1168
1169        size_t offset;
1170        for (offset = 0; offset < sz;) {
1171                struct dirent32 *dent = (struct dirent32 *)(buf + offset);
1172                dirname[dnamelen] = '\0';
1173                strcat(dirname, dent->d_name);
1174                if (!__darwintrace_is_in_sandbox(dirname, NULL)) {
1175                        debug_printf("getdirentries: filtered %s\n", dirname);
1176                        dent->d_ino = 0;
1177                } else {
1178                        debug_printf("getdirentries:  allowed %s\n", dirname);
1179                }
1180                offset += dent->d_reclen;
1181        }
1182
1183        return sz;
1184#undef getdirentries
1185}
1186#endif /* __APPLE__ */
Note: See TracBrowser for help on using the repository browser.