| // Copyright (c) 2012-2017, Matt Godbolt |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // * 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. |
| // |
| // 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 HOLDER 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 _GNU_SOURCE |
| #include <dlfcn.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <libgen.h> |
| |
| #ifndef O_CREAT |
| #define O_CREAT 0100 |
| #endif |
| |
| // OS X et al doesn't have this. Rather than optionally replace it, I'd rather |
| // run using this substitute function all the time. |
| static const char *my_strchrnul(const char *s, int c) { |
| const char *ptr = strchr(s, c); |
| if (!ptr) |
| ptr = s + strlen(s); |
| return ptr; |
| } |
| |
| static int allowed_match(const char* path, const char* okpath, const char *denypath, |
| int debug) { |
| char resolvedBuf[PATH_MAX]; |
| const char* resolved = path; |
| if (!strncmp(resolved, "/proc/self", 10)) { |
| // Leave references to /proc/self.* alone as its real path is different |
| // each time. |
| } else { |
| resolved = realpath(path, resolvedBuf); |
| if (resolved == NULL) { |
| return 0; |
| } |
| } |
| |
| while (*denypath) { |
| const char* end = my_strchrnul(denypath, ':'); |
| if (strncmp(denypath, resolved, end - denypath) == 0) goto deny; |
| denypath = end; |
| while (*denypath == ':') ++denypath; |
| } |
| |
| while (*okpath) { |
| const char* end = my_strchrnul(okpath, ':'); |
| if (strncmp(okpath, resolved, end - okpath) == 0) return 1; |
| okpath = end; |
| while (*okpath == ':') ++okpath; |
| } |
| |
| deny: |
| if (debug) { |
| fprintf(stderr, "Access to \"%s\" denied by compiler-explorer policy\n", path); |
| } |
| errno = EACCES; |
| return 0; |
| } |
| |
| static int allowed_env(const char* pathname, const char* envvar) { |
| const char* okpath = getenv(envvar); |
| if (okpath == NULL) { |
| errno = EINVAL; |
| return 0; |
| } |
| const char* denypath = getenv("DENIED"); |
| if (denypath == NULL) denypath = ""; |
| |
| int debug = getenv("PRELOAD_DEBUG") ? 1 : 0; |
| |
| // Check file name first |
| if (allowed_match(pathname, okpath, denypath, debug)) return 1; |
| |
| // Check directory name |
| char* dirpathbuf = strdup(pathname); |
| char* dirpath = dirname(dirpathbuf); |
| int dir_ok = allowed_match(dirpath, okpath, denypath, debug); |
| free(dirpathbuf); |
| |
| return dir_ok; |
| } |
| |
| static int allowed(const char* pathname, int flags) { |
| if (flags & O_CREAT) |
| return allowed_env(pathname, "ALLOWED_FOR_CREATE"); |
| else |
| return allowed_env(pathname, "ALLOWED_FOR_READ"); |
| } |
| |
| int open(const char *pathname, int flags, mode_t mode) { |
| static int (*real_open)(const char*, int, mode_t) = NULL; |
| if (!real_open) real_open = dlsym(RTLD_NEXT, "open"); |
| |
| if (!allowed(pathname, flags)) { |
| return -1; |
| } |
| |
| return real_open(pathname, flags, mode); |
| } |
| |
| int creat(const char *pathname, mode_t mode) { |
| static int (*real_creat)(const char*, mode_t) = NULL; |
| if (!real_creat) real_creat = dlsym(RTLD_NEXT, "creat"); |
| |
| if (!allowed(pathname, O_CREAT)) { |
| return -1; |
| } |
| |
| return real_creat(pathname, mode); |
| } |
| |
| FILE* fopen(const char* name, const char* mode) { |
| static FILE* (*real_fopen)(const char*, const char*) = NULL; |
| if (!real_fopen) real_fopen = dlsym(RTLD_NEXT, "fopen"); |
| |
| if (!allowed(name, (mode[0] == 'r') ? 0 : O_CREAT)) { |
| return NULL; |
| } |
| |
| return real_fopen(name, mode); |
| } |