/* * exchange_with_port - routine to send/recv from/to socket * Parameters: * buf -- buffer with data to send * len -- length of data * answer -- 1 (yes, I want to receive answer) and 0 (no, thanks, just send) * failures -- should be setted 0 on external calls (avoid infinite recursion) * Return value: * -1 -- something went wrong * 0 -- data successfully sent * string -- answer (caller shoud free it) */ static char * exchange_with_port(const char * buf, size_t len, int answer, char failures) { wait_for_socket(__darwintrace_fd, 1); if(send(__darwintrace_fd, buf, len, 0)==-1) { if(errno==ENOTSOCK && failures<3) { __darwintrace_fd=-1; __darwintrace_setup(); return exchange_with_port(buf, len, answer, failures+1); } return (char*)-1; } if(!answer) return 0; { size_t l=0; char * b; wait_for_socket(__darwintrace_fd, 0); recv(__darwintrace_fd, &l, sizeof(l),0); if(!l) return 0; b=(char*)malloc(l+1); b[l]=0; recv(__darwintrace_fd, b, l, 0); return b; } }
int open(const char* path, int flags, ...) { #define open(x,y,z) syscall(SYS_open, (x), (y), (z)) mode_t mode; int result; va_list args; struct stat sb; char newpath[MAXPATHLEN]; int isInSandbox; /* Why mode here ? */ va_start(args, flags); mode = va_arg(args, int); va_end(args); result = 0; if((stat(path, &sb)!=-1 && !(sb.st_mode&S_IFDIR)) || flags & O_CREAT ) { *newpath=0; __darwintrace_setup(); isInSandbox = __darwintrace_is_in_sandbox(path, newpath); if (isInSandbox == 0) { debug_printf("darwintrace: creation/writing was forbidden at %s\n", path); errno = EACCES; result = -1; } if(*newpath) path=newpath; } if (result == 0) { result = open(path, flags, mode); } return result; #undef open }
/** * Wrapper around \c close(2) to deny closing the file descriptor used by * darwintrace to communicate with the control socket. Since we sometimes want * to close our socket using \c fclose(3) and that internally calls \c * close(2), we need a way to specifically allow closing the socket when we * need to. This possibility is the \c __darwintrace_close_sock variable, which * will be set to the FD to be closed when closing should be allowed. */ static int _dt_close(int fd) { #define close(x) syscall(SYS_close, (x)) __darwintrace_setup(); FILE *stream = __darwintrace_sock(); if (stream) { int dtsock = fileno(stream); if (fd == dtsock && dtsock != __darwintrace_close_sock) { errno = EBADF; return -1; } } return close(fd); #undef close }
/** * Wrapper around \c rmdir(2) to deny deleting directories outside of the * sandbox. */ static int _dt_rmdir(const char *path) { #define rmdir(x) syscall(SYS_rmdir, (x)) __darwintrace_setup(); int result = 0; if (!__darwintrace_is_in_sandbox(path, DT_REPORT | DT_FOLLOWSYMS)) { errno = ENOENT; result = -1; } else { result = rmdir(path); } debug_printf("rmdir(%s) = %d\n", path, result); return result; #undef rmdir }
static int _dt_access(const char *path, int amode) { #define access(x, y) syscall(SYS_access, (x), (y)) __darwintrace_setup(); int result = 0; if (!__darwintrace_is_in_sandbox(path, DT_REPORT | DT_ALLOWDIR | DT_FOLLOWSYMS)) { errno = ENOENT; result = -1; } else { result = access(path, amode); } debug_printf("access(%s) = %d\n", path, result); return result; #undef access }
int readlink(const char * path, char * buf, int bufsiz) { #else ssize_t readlink(const char * path, char * buf, size_t bufsiz) { #endif #define readlink(x,y,z) syscall(SYS_readlink, (x), (y), (z)) ssize_t result; int isInSandbox; result = readlink(path, buf, bufsiz); if (result >= 0) { __darwintrace_setup(); isInSandbox = __darwintrace_is_in_sandbox(path, 0); if (!isInSandbox) { errno=EACCES; result=-1; } } return result; #undef readlink }
static int _dt_readlink(const char *path, char *buf, int bufsiz) { #else static ssize_t _dt_readlink(const char *path, char *buf, size_t bufsiz) { #endif #define readlink(x,y,z) syscall(SYS_readlink, (x), (y), (z)) __darwintrace_setup(); int result = 0; // don't follow symlinks here; whether access to the link target is allowed // or not does not matter for reading the symlink if (!__darwintrace_is_in_sandbox(path, DT_REPORT | DT_ALLOWDIR)) { errno = ENOENT; result = -1; } else { result = readlink(path, buf, bufsiz); } debug_printf("readlink(%s) = %d\n", path, result); return result; #undef readlink }
int execve(const char* path, char* const argv[], char* const envp[]) { #define __execve(x,y,z) syscall(SYS_execve, (x), (y), (z)) #define open(x,y,z) syscall(SYS_open, (x), (y), (z)) #define close(x) syscall(SYS_close, (x)) #define lstat(x, y) syscall(SYS_lstat, (x), (y)) int result; __darwintrace_setup(); if (__darwintrace_fd >= 0) { struct stat sb; /* for symlinks, we want to capture * both the original path and the modified one, * since for /usr/bin/gcc -> gcc-4.0, * both "gcc_select" and "gcc" are contributors */ if (lstat(path, &sb) == 0) { int fd; if(S_ISLNK(sb.st_mode)) { /* for symlinks, print both */ __darwintrace_log_op("execve", path, 0); } fd = open(path, O_RDONLY, 0); if (fd > 0) { char buffer[MAXPATHLEN+1], newpath[MAXPATHLEN+1]; ssize_t bytes_read; *newpath=0; if(__darwintrace_is_in_sandbox(path, newpath)==0) { close(fd); errno=ENOENT; return -1; } if(*newpath) path=newpath; /* once we have an open fd, if a full path was requested, do it */ __darwintrace_log_op("execve", path, fd); /* read the file for the interpreter */ bytes_read = read(fd, buffer, MAXPATHLEN); buffer[bytes_read] = 0; if (bytes_read > 2 && buffer[0] == '#' && buffer[1] == '!') { const char* interp = &buffer[2]; int i; /* skip past leading whitespace */ for (i = 2; i < bytes_read; ++i) { if (buffer[i] != ' ' && buffer[i] != '\t') { interp = &buffer[i]; break; } } /* found interpreter (or ran out of data) skip until next whitespace, then terminate the string */ for (; i < bytes_read; ++i) { if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n') { buffer[i] = 0; break; } } /* we have liftoff */ if (interp && interp[0] != '\0') { __darwintrace_log_op("execve", interp, 0); } } close(fd); } } close(__darwintrace_fd); __darwintrace_fd=-1; } /* call the original execve function, but fix the environment if required. */ result = __execve(path, argv, __darwintrace_restore_env(envp)); return result; #undef lstat #undef close #undef open #undef execve }
inline int __darwintrace_is_in_sandbox(const char* path, char * newpath) { char * t, * p, * _; int result=-1; __darwintrace_setup(); if(!filemap) return 1; if(*path=='/') p=strdup(path); else { p=(char*)malloc(MAXPATHLEN); if (getcwd(p, MAXPATHLEN-1) == NULL) { fprintf(stderr, "darwintrace: getcwd: %s, path was: %s\n", strerror(errno), path); abort(); } if (p[strlen(p)-1] != '/') strcat(p, "/"); strcat(p, path); } __darwintrace_cleanup_path(p); do { for(t=filemap; *t;) { if(__darwintrace_strbeginswith(p, t)) { t+=strlen(t)+1; switch(*t) { case 0: result=1; break; case 1: if(!newpath) { result=0; break; } strcpy(newpath, t+1); _=newpath+strlen(newpath); if(_[-1]!='/') *_++='/'; strcpy(_, p); result=1; break; case 2: result=ask_for_dependency(p); break; } } if(result!=-1) break; t+=strlen(t)+1; if(*t==1) t+=strlen(t)+1; else t+=2; } if(result!=-1) break; __darwintrace_log_op("sandbox_violation", path, 0); result=0; } while(0); free(p); return result; }