Projects
New Ticket     Wiki     Browse Source     Timeline     Roadmap     Bug Reports     Search

root/trunk/base/src/darwintracelib1.0/darwintrace.c

Revision 40051, 22.6 KB (checked in by toby@…, 4 months ago)

fix warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 * Copyright (c) 2005-2006 Paul Guyot <pguyot@kallisys.net>,
4 * All rights reserved.
5 *
6 * $Id$
7 *
8 * @APPLE_BSD_LICENSE_HEADER_START@
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1.  Redistributions of source code must retain the above copyright
15 *     notice, this list of conditions and the following disclaimer.
16 * 2.  Redistributions in binary form must reproduce the above copyright
17 *     notice, this list of conditions and the following disclaimer in the
18 *     documentation and/or other materials provided with the distribution.
19 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
20 *     its contributors may be used to endorse or promote products derived
21 *     from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
27 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 *
34 * @APPLE_BSD_LICENSE_HEADER_END@
35 */
36
37#ifdef HAVE_CONFIG_H
38#include <config.h>
39#endif
40
41#ifdef HAVE_CRT_EXTERNS_H
42#include <crt_externs.h>
43#endif
44
45#ifdef HAVE_SYS_PATHS_H
46#include <sys/paths.h>
47#endif
48
49#include <fcntl.h>
50#include <stdarg.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <unistd.h>
55#include <sys/types.h>
56#include <sys/stat.h>
57#include <sys/param.h>
58#include <sys/syscall.h>
59#include <errno.h>
60#include <sys/socket.h>
61#include <sys/un.h>
62
63#ifndef HAVE_STRLCPY
64/* Define strlcpy if it's not available. */
65size_t strlcpy(char* dst, const char* src, size_t size);
66size_t strlcpy(char* dst, const char* src, size_t size)
67{
68        size_t result = strlen(src);
69        if (size > 0)
70        {
71                size_t copylen = size - 1;
72                if (copylen > result)
73                {
74                        copylen = result;
75                }
76                memcpy(dst, src, copylen);
77                dst[copylen] = 0;
78        }
79        return result;
80}
81#endif
82
83/*
84 * Compile time options:
85 * DARWINTRACE_SHOW_PROCESS: show the process id of every access
86 * DARWINTRACE_LOG_CREATE: log creation of files as well.
87 * DARWINTRACE_SANDBOX: control creation, deletion and writing to files and dirs.
88 * DARWINTRACE_LOG_FULL_PATH: use F_GETPATH to log the full path.
89 * DARWINTRACE_DEBUG_OUTPUT: verbose output of stuff to debug darwintrace.
90 *
91 * global variables (only checked when setup is first called)
92 * DARWINTRACE_LOG
93 *    path to the log file (no logging happens if it's unset).
94 * DARWINTRACE_SANDBOX_BOUNDS
95 *    : separated allowed paths for the creation of files.
96 *    \: -> :
97 *    \\ -> \
98 */
99
100#ifndef DARWINTRACE_SHOW_PROCESS
101#define DARWINTRACE_SHOW_PROCESS 0
102#endif
103#ifndef DARWINTRACE_LOG_CREATE
104#define DARWINTRACE_LOG_CREATE 0
105#endif
106#ifndef DARWINTRACE_SANDBOX
107#define DARWINTRACE_SANDBOX 1
108#endif
109#ifndef DARWINTRACE_DEBUG_OUTPUT
110#define DARWINTRACE_DEBUG_OUTPUT 0
111#endif
112#ifndef DARWINTRACE_LOG_FULL_PATH
113#define DARWINTRACE_LOG_FULL_PATH 1
114#endif
115
116#ifndef DEFFILEMODE
117#define DEFFILEMODE 0666
118#endif
119
120/*
121 * Prototypes.
122 */
123inline int __darwintrace_strbeginswith(const char* str, const char* prefix);
124inline void __darwintrace_log_op(const char* op, const char* path, int fd);
125void __darwintrace_copy_env() __attribute__((constructor));
126inline char* __darwintrace_alloc_env(const char* varName, const char* varValue);
127inline char* const* __darwintrace_restore_env(char* const envp[]);
128inline void __darwintrace_setup();
129inline void __darwintrace_cleanup_path(char *path);
130static char * exchange_with_port(const char * buf, size_t len, int answer, char failures);
131
132#define START_FD 81
133static int __darwintrace_fd = -1;
134#define BUFFER_SIZE     1024
135
136/**
137 * filemap: path\0whattodo\0path\0whattodo\0\0
138 * path: begin of path (for example /opt)
139 * whattodo:
140 *   0     -- allow
141 *   1PATH -- map
142 *   2     -- ask for allow
143**/
144static char * filemap=0;
145
146/* copy of the global variables */
147static char* __env_dyld_insert_libraries;
148static char* __env_dyld_force_flat_namespace;
149static char* __env_darwintrace_log;
150
151#if __STDC_VERSION__>=199901L
152#if DARWINTRACE_DEBUG_OUTPUT
153#define dprintf(...) fprintf(stderr, __VA_ARGS__)
154#else
155#define dprintf(...)
156#endif
157#else
158#if DARWINTRACE_DEBUG_OUTPUT
159#define dprintf(format, param) fprintf(stderr, format, param)
160#else
161#define dprintf(format, param)
162#endif
163#endif
164
165/*
166 * char wait_for_socket(int sock, char w)
167 * Function used for read/write operation to socket...
168 * Args:
169 *  sock - socket
170 *  w - what should socket do in next operation. 1 for write, 0 for read
171 * Return value:
172 *  1 - everything is ok, we can read/write to/from it
173 *  0 - something's went wrong
174 */
175static int wait_for_socket(int sock, char w)
176{
177        struct timeval tv;
178        fd_set fds;
179       
180        if(sock==-1)
181                return 0;
182       
183        tv.tv_sec=10;
184        tv.tv_usec=0;
185        FD_ZERO(&fds);
186        FD_SET(sock, &fds);
187        if(select(sock+1, (w==0 ? &fds : 0), (w==1 ? &fds : 0), 0, &tv)<1)
188                return 0;
189        return FD_ISSET(sock, &fds)!=0;
190}
191
192
193/*
194 * return 0 if str doesn't begin with prefix, 1 otherwise.
195 */
196inline int __darwintrace_strbeginswith(const char* str, const char* prefix) {
197        char theCharS;
198        char theCharP;
199        do {
200                theCharS = *str++;
201                theCharP = *prefix++;
202        } while(theCharP && (theCharP == theCharS));
203        return (theCharP == 0);
204}
205
206/*
207 * Copy the environment variables, if they're defined.
208 */
209void __darwintrace_copy_env() {
210        char* theValue;
211        theValue = getenv("DYLD_INSERT_LIBRARIES");
212        if (theValue != NULL) {
213                __env_dyld_insert_libraries = strdup(theValue);
214        } else {
215                __env_dyld_insert_libraries = NULL;
216        }
217        theValue = getenv("DYLD_FORCE_FLAT_NAMESPACE");
218        if (theValue != NULL) {
219                __env_dyld_force_flat_namespace = strdup(theValue);
220        } else {
221                __env_dyld_force_flat_namespace = NULL;
222        }
223        theValue = getenv("DARWINTRACE_LOG");
224        if (theValue != NULL) {
225                __env_darwintrace_log = strdup(theValue);
226        } else {
227                __env_darwintrace_log = NULL;
228        }
229}
230
231/*
232 * Allocate a X=Y string where X is the variable name and Y its value.
233 * Return the new string.
234 *
235 * If the value is NULL, return NULL.
236 */
237inline char* __darwintrace_alloc_env(const char* varName, const char* varValue) {
238        char* theResult = NULL;
239        if (varValue) {
240                int theSize = strlen(varName) + strlen(varValue) + 2;
241                theResult = (char*) malloc(theSize);
242                sprintf(theResult, "%s=%s", varName, varValue);
243                theResult[theSize - 1] = 0;
244        }
245       
246        return theResult;
247}
248
249/*
250 * This function checks that envp contains the global variables we had when the
251 * library was loaded and modifies it if it doesn't.
252 */
253inline char* const* __darwintrace_restore_env(char* const envp[]) {
254        /* allocate the strings. */
255        /* we don't care about the leak here because we're going to call execve,
256     * which, if it succeeds, will get rid of our heap */
257        char* dyld_insert_libraries_ptr =       
258                __darwintrace_alloc_env(
259                        "DYLD_INSERT_LIBRARIES",
260                        __env_dyld_insert_libraries);
261        char* dyld_force_flat_namespace_ptr =   
262                __darwintrace_alloc_env(
263                        "DYLD_FORCE_FLAT_NAMESPACE",
264                        __env_dyld_force_flat_namespace);
265        char* darwintrace_log_ptr =     
266                __darwintrace_alloc_env(
267                        "DARWINTRACE_LOG",
268                        __env_darwintrace_log);
269
270        char* const * theEnvIter = envp;
271        int theEnvLength = 0;
272        char** theCopy;
273        char** theCopyIter;
274
275        while (*theEnvIter != NULL) {
276                theEnvLength++;
277                theEnvIter++;
278        }
279
280        /* 5 is sufficient for the four variables we copy and the terminator */
281        theCopy = (char**) malloc(sizeof(char*) * (theEnvLength + 5));
282        theEnvIter = envp;
283        theCopyIter = theCopy;
284
285        while (*theEnvIter != NULL) {
286                char* theValue = *theEnvIter;
287                if (__darwintrace_strbeginswith(theValue, "DYLD_INSERT_LIBRARIES=")) {
288                        theValue = dyld_insert_libraries_ptr;
289                        dyld_insert_libraries_ptr = NULL;
290                } else if (__darwintrace_strbeginswith(theValue, "DYLD_FORCE_FLAT_NAMESPACE=")) {
291                        theValue = dyld_force_flat_namespace_ptr;
292                        dyld_force_flat_namespace_ptr = NULL;
293                } else if (__darwintrace_strbeginswith(theValue, "DARWINTRACE_LOG=")) {
294                        theValue = darwintrace_log_ptr;
295                        darwintrace_log_ptr = NULL;
296                }
297               
298                if (theValue) {
299                        *theCopyIter++ = theValue;
300                }
301
302                theEnvIter++;
303        }
304       
305        if (dyld_insert_libraries_ptr) {
306                *theCopyIter++ = dyld_insert_libraries_ptr;
307        }
308        if (dyld_force_flat_namespace_ptr) {
309                *theCopyIter++ = dyld_force_flat_namespace_ptr;
310        }
311        if (darwintrace_log_ptr) {
312                *theCopyIter++ = darwintrace_log_ptr;
313        }
314
315        *theCopyIter = 0;
316       
317        return theCopy;
318}
319
320static void ask_for_filemap()
321{
322        filemap=exchange_with_port("filemap\t", sizeof("filemap\t"), 1, 0);
323        if(filemap==(char*)-1)
324                filemap=0;
325}
326
327inline void __darwintrace_setup() {
328#define open(x,y,z) syscall(SYS_open, (x), (y), (z))
329#define close(x) syscall(SYS_close, (x))
330        if (__darwintrace_fd == -1) {
331                if (__env_darwintrace_log != NULL) {
332                        int olderrno = errno;
333                        int sock=socket(AF_UNIX, SOCK_STREAM, 0);
334                        struct sockaddr_un sun;
335                        sun.sun_family=AF_UNIX;
336                        strcpy(sun.sun_path, __env_darwintrace_log);
337                        if(connect(sock, (struct sockaddr*)&sun, strlen(__env_darwintrace_log)+1+sizeof(sun.sun_family))!=-1)
338                        {
339                                dprintf("darwintrace: connect successful. socket %d\n", sock);
340                                __darwintrace_fd=sock;
341                                ask_for_filemap();
342                        } else {
343                                dprintf("connect failed %d :-(\n", errno);
344                        }
345                        errno = olderrno;
346                }
347        }
348#undef close
349#undef open
350}
351
352/* log a call and optionally get the real path from the fd if it's not 0.
353 * op:                  the operation (open, readlink, execve)
354 * path:                the path of the file
355 * fd:                  a fd to the file, or 0 if we don't have any.
356 */
357inline void __darwintrace_log_op(const char* op, const char* path, int fd) {
358        int size;
359        char somepath[MAXPATHLEN];
360        char logbuffer[BUFFER_SIZE];
361       
362        do {
363#ifdef __APPLE__ /* Only Darwin has volfs and F_GETPATH */
364                if ((fd > 0) && (DARWINTRACE_LOG_FULL_PATH
365                        || (strncmp(path, "/.vol/", 6) == 0))) {
366                        if(fcntl(fd, F_GETPATH, somepath) == -1) {
367                                /* getpath failed. use somepath instead */
368                                strlcpy(somepath, path, sizeof(somepath));
369                                break;
370                        }
371                }
372#endif
373                if (path[0] != '/') {
374                        int len;
375                        (void) getcwd(somepath, sizeof(somepath));
376                        len = strlen(somepath);
377                        somepath[len++] = '/';
378                        strlcpy(&somepath[len], path, sizeof(somepath) - len);
379                        break;
380                }
381
382                /* otherwise, just copy the original path. */
383                strlcpy(somepath, path, sizeof(somepath));
384        } while (0);
385
386        /* clean the path. */
387        __darwintrace_cleanup_path(somepath);
388
389        size = snprintf(logbuffer, sizeof(logbuffer),
390                "%s\t%s",
391                op, somepath );
392
393        exchange_with_port(logbuffer, size+1, 0, 0);
394       
395        return;
396}
397
398/* remap resource fork access to the data fork.
399 * do a partial realpath(3) to fix "foo//bar" to "foo/bar"
400 */
401inline void __darwintrace_cleanup_path(char *path) {
402  size_t pathlen;
403#ifdef __APPLE__
404  size_t rsrclen;
405#endif
406  size_t i, shiftamount;
407  enum { SAWSLASH, NOTHING } state = NOTHING;
408
409  /* if this is a foo/..namedfork/rsrc, strip it off */
410  pathlen = strlen(path);
411  /* ..namedfork/rsrc is only on OS X */
412#ifdef __APPLE__
413  rsrclen = strlen(_PATH_RSRCFORKSPEC);
414  if(pathlen > rsrclen
415     && 0 == strcmp(path + pathlen - rsrclen,
416                    _PATH_RSRCFORKSPEC)) {
417    path[pathlen - rsrclen] = '\0';
418    pathlen -= rsrclen;
419  }
420#endif
421
422  /* for each position in string (including
423     terminal \0), check if we're in a run of
424     multiple slashes, and only emit the
425     first one
426  */
427  for(i=0, shiftamount=0; i <= pathlen; i++) {
428    if(state == SAWSLASH) {
429      if(path[i] == '/') {
430        /* consume it */
431        shiftamount++;
432      } else {
433        state = NOTHING;
434        path[i - shiftamount] = path[i];
435      }
436    } else {
437      if(path[i] == '/') {
438        state = SAWSLASH;
439      }
440      path[i - shiftamount] = path[i];
441    }
442  }
443
444  dprintf("darwintrace: cleanup resulted in %s\n", path);
445}
446
447/*
448 * return 1 if path is directory or not exists
449 * return 0 otherwise
450 */
451static int is_directory(const char * path)
452{
453#define stat(path, sb) syscall(SYS_stat, path, sb)
454        struct stat s;
455        if(stat(path, &s)==-1)
456                /* Actually is not directory, but anyway, we shouldn't test a dependency unless file exists */
457                return 1;
458       
459        return S_ISDIR(s.st_mode);
460#undef stat
461}
462
463
464/*
465 * return 1 if path allowed, 0 otherwise
466 */
467static int ask_for_dependency(char * path)
468{
469        char buffer[BUFFER_SIZE], *p;
470        int result=0;
471       
472        if(is_directory(path))
473                return 1;
474       
475        strcpy(buffer, "dep_check\t");
476        strcpy(buffer+10, path);
477        p=exchange_with_port(buffer, strlen(buffer)+1, 1, 0);
478        if(p==(char*)-1||!p)
479                return 0;
480       
481        if(*p=='+')
482                result=1;
483       
484        free(p);
485        return result;
486}
487
488/*
489 * exchange_with_port - routine to send/recv from/to socket
490 * Parameters:
491 *   buf      -- buffer with data to send
492 *   len      -- length of data
493 *   answer   -- 1 (yes, I want to receive answer) and 0 (no, thanks, just send)
494 *   failures -- should be setted 0 on external calls (avoid infinite recursion)
495 * Return value:
496 *    -1     -- something went wrong
497 *    0      -- data successfully sent
498 *    string -- answer (caller shoud free it)
499 */
500static char * exchange_with_port(const char * buf, size_t len, int answer, char failures)
501{
502        wait_for_socket(__darwintrace_fd, 1);
503        if(send(__darwintrace_fd, buf, len, 0)==-1)
504        {
505                if(errno==ENOTSOCK && failures<3)
506                {
507                        __darwintrace_fd=-1;
508                        __darwintrace_setup();
509                        return exchange_with_port(buf, len, answer, failures+1);
510                }
511                return (char*)-1;
512        }
513        if(!answer)
514                return 0;
515        {
516                size_t l=0;
517                char * b;
518               
519                wait_for_socket(__darwintrace_fd, 0);
520                recv(__darwintrace_fd, &l, sizeof(l),0);
521                if(!l)
522                        return 0;
523                b=(char*)malloc(l+1);
524                b[l]=0;
525                recv(__darwintrace_fd, b, l, 0);
526                return b;
527        }
528}
529
530/*
531 * return 1 if path (once normalized) is in sandbox or redirected, 0 otherwise.
532 */
533inline int __darwintrace_is_in_sandbox(const char* path, char * newpath) {
534        char * t, * p, * _;
535        int result=-1;
536       
537        __darwintrace_setup();
538       
539        if(!filemap)
540                return 1;
541       
542        if(*path=='/')
543                p=strdup(path);
544        else
545        {
546                p=(char*)malloc(BUFFER_SIZE);
547                (void) getcwd(p, BUFFER_SIZE-1);
548                _=p+strlen(p)+1;
549                if(_[-1]!='/')
550                        *_++='/';
551                strncpy(_, path, BUFFER_SIZE-(_-p));
552        }
553        __darwintrace_cleanup_path(p);
554                       
555        do
556        {
557                for(t=filemap; *t;)
558                {
559                        if(__darwintrace_strbeginswith(p, t))
560                        {
561                                t+=strlen(t)+1;
562                                switch(*t)
563                                {
564                                case 0:
565                                        result=1;
566                                        break;
567                                case 1:
568                                        if(!newpath)
569                                        {
570                                                result=0;
571                                                break;
572                                        }
573                                        strcpy(newpath, t+1);
574                                        _=newpath+strlen(newpath);
575                                        if(_[-1]!='/')
576                                                *_++='/';
577                                        strcpy(_, p);
578                                        result=1;
579                                        break;
580                                case 2:
581                                        result=ask_for_dependency(p);
582                                        break;
583                                }
584                        }
585                        if(result!=-1)
586                                break;
587                        t+=strlen(t)+1;
588                        if(*t==1)
589                                t+=strlen(t)+1;
590                        else
591                                t+=2;
592                }
593                if(result!=-1)
594                        break;
595                __darwintrace_log_op("sandbox_violation", path, 0);
596                result=0;
597        }
598        while(0);
599        free(p);
600        return result;
601}
602
603/* Log calls to open(2) into the file specified by DARWINTRACE_LOG.
604   Only logs if the DARWINTRACE_LOG environment variable is set.
605   Only logs files (or rather, do not logs directories)
606   Only logs files where the open succeeds.
607   Only logs files opened for read access, without the O_CREAT flag set
608        (unless DARWINTRACE_LOG_CREATE is set).
609   The assumption is that any file that can be created isn't necessary
610   to build the project.
611*/
612
613int open(const char* path, int flags, ...) {
614#define open(x,y,z) syscall(SYS_open, (x), (y), (z))
615        mode_t mode;
616        int result;
617        va_list args;
618        struct stat sb;
619        char newpath[MAXPATHLEN];
620        int isInSandbox;       
621
622        /* Why mode here ? */
623        va_start(args, flags);
624        mode = va_arg(args, int);
625        va_end(args);
626       
627        result = 0;
628       
629        if((stat(path, &sb)!=-1 && !(sb.st_mode&S_IFDIR)) || flags & O_CREAT )
630        {
631                *newpath=0;
632                __darwintrace_setup();
633                isInSandbox = __darwintrace_is_in_sandbox(path, newpath);
634                if (isInSandbox == 0) {
635                        dprintf("darwintrace: creation/writing was forbidden at %s\n", path);
636                        errno = EACCES;
637                        result = -1;
638                }
639                if(*newpath)
640                        path=newpath;
641        }
642        if (result == 0) {
643                result = open(path, flags, mode);
644        }
645        return result;
646#undef open
647}
648
649/* Log calls to readlink(2) into the file specified by DARWINTRACE_LOG.
650   Only logs if the DARWINTRACE_LOG environment variable is set.
651   Only logs files where the readlink succeeds.
652*/
653#ifdef READLINK_IS_NOT_P1003_1A
654int  readlink(const char * path, char * buf, int bufsiz) {
655#else
656ssize_t  readlink(const char * path, char * buf, size_t bufsiz) {
657#endif
658#define readlink(x,y,z) syscall(SYS_readlink, (x), (y), (z))
659        ssize_t result;
660        int isInSandbox;
661
662        result = readlink(path, buf, bufsiz);
663        if (result >= 0) {
664                __darwintrace_setup();
665                isInSandbox = __darwintrace_is_in_sandbox(path, 0);
666                if (!isInSandbox)
667                {
668                        errno=EACCES;
669                        result=-1;
670                }
671        }
672        return result;
673#undef readlink
674}
675
676int execve(const char* path, char* const argv[], char* const envp[]) {
677#define __execve(x,y,z) syscall(SYS_execve, (x), (y), (z))
678#define open(x,y,z) syscall(SYS_open, (x), (y), (z))
679#define close(x) syscall(SYS_close, (x))
680#define lstat(x, y) syscall(SYS_lstat, (x), (y))
681        int result;
682        __darwintrace_setup();
683        if (__darwintrace_fd >= 0) {
684          struct stat sb;
685          /* for symlinks, we want to capture
686           * both the original path and the modified one,
687           * since for /usr/bin/gcc -> gcc-4.0,
688           * both "gcc_select" and "gcc" are contributors
689           */
690          if (lstat(path, &sb) == 0) {
691                int fd;
692
693            if(S_ISLNK(sb.st_mode)) {
694              /* for symlinks, print both */
695                  __darwintrace_log_op("execve", path, 0);
696            }
697               
698                fd = open(path, O_RDONLY, 0);
699                if (fd > 0) {
700                  char buffer[MAXPATHLEN+1], newpath[MAXPATHLEN+1];
701                  ssize_t bytes_read;
702               
703                  *newpath=0;
704                  if(__darwintrace_is_in_sandbox(path, newpath)==0)
705                  {
706                        close(fd);
707                        errno=ENOENT;
708                    return -1;
709                  }