1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
/* security.c -- manage access to files and audio */
/* Roger B. Dannenberg
* July 2014
*/
#include <stdlib.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <string.h>
#include <xlisp.h>
static void find_full_path(const char *filename, char *fullname);
static int in_tree(const char *fullname, char *secure_read_path);
static int full_name(const char *filename);
static int file_sep(char c);
/* run_time_limit is a feature to shut down infinite loops
* calls to oscheck are counted. These occur at around 66Hz
* on a 2010 vintage laptop, so a value of 66 is roughly 1 CPU
* second of computation. The rate fluctuates depending on
* the computation, so it is a very rough guide, but then if
* you are restricting CPU time, you'll probably want to pad
* the expected time by a factor of at least 2, so hopefully
* exact numbers are not important. The default is 0, meaning
* no limit. Set the limit from the command line with -L.
* Read the current run time by calling (GET-RUN-TIME)
*/
int run_time_limit = 0;
/* memory_limit is a feature to shut down rampant memory
* allocation which tends to lead to thrashing on a virtual
* (memory) machine. The number is roughly the number of
* megabytes of sample and cons cell memory that can be
* allocated. The default is 0 which means no limit.
*/
int memory_limit = 0;
/* run_time is the current run time -- incremented by oscheck() */
int run_time = 0;
/* secure_read_path is NULL if reading any file is permitted
* o.w. secure_read_path is a semicolon-separated list of
* paths to directory trees that are readable
*/
char *secure_read_path = NULL;
/* safe_write_path is NULL if writing any file is permitted
* o.w. safe_write_path is a semicolon-separated list of
* paths to directory tress that are writeable
*/
char *safe_write_path = NULL;
/* ok_to_open - true if it is OK to open the file under
* the security model implied by secure_read_path
* and safe_write_path
*/
int ok_to_open(const char *filename, const char *mode)
{
char fullname[STRMAX];
if (strchr(mode, 'r')) { /* asking for read permission */
if (secure_read_path) { /* filename must be in path */
find_full_path(filename, fullname);
if (!in_tree(fullname, secure_read_path)) return FALSE;
}
}
if (strchr(mode, 'w')) { /* asking for write permission */
if (safe_write_path) { /* filename must be in path */
find_full_path(filename, fullname);
if (!in_tree(fullname, safe_write_path)) return FALSE;
}
}
return TRUE;
}
/* find_full_path - find full path corresponding to filename */
/**/
void find_full_path(const char *filename, char *fullname)
{
if (full_name(filename)) {
strncpy(fullname, filename, STRMAX);
fullname[STRMAX - 1] = 0;
return;
}
if (!getcwd(fullname, STRMAX)) {
/* something is really wrong. Pretend we found a
cwd that will not match anything */
goto error;
}
/* see if we need a separator (probably) */
int len = strlen(fullname);
if (!file_sep(fullname[len - 1])) {
fullname[len++] = '/';
if (len >= STRMAX) goto error;
}
/* append filename to fullname */
strncpy(fullname + len, filename, STRMAX - len);
fullname[STRMAX - 1] = 0; /* just in case of overflow */
/* if windows, replace \ with / to simplify the rest */
char *loc = fullname;
if (os_pathchar != '/') {
while ((loc = strchr(loc, os_pathchar))) {
*loc = '/';
}
}
/* strip out .. and . */
while ((loc = strstr(fullname, "/.."))) {
/* back up to separator */
if (loc == fullname) goto error;
char *loc2 = loc - 1;
while (*loc2 != '/') {
loc2--;
if (loc2 <= fullname) goto error;
}
/* now loc2 points to /parent/.., and loc points to /.. */
/* copy from beyond /.. to loc2 */
memmove(loc2, loc, strlen(loc) + 1);
}
return;
error:
strcpy(fullname, "//////");
return;
}
static int file_sep(char c)
{
return (c == os_pathchar || c == '/');
}
/* full_name - test if filename is a full path */
/**/
static int full_name(const char *filename)
{
if (!filename) return FALSE;
if (filename[0] == os_pathchar) return TRUE;
/* windows allows '/' instead of '\' */
if (filename[0] == '/') return TRUE;
if (strlen(filename) > 2 &&
isalpha(filename[0]) &&
filename[1] == ':') return TRUE;
return FALSE;
}
static int in_tree(const char *fullname, char *secure_read_path)
{
/* fullname is in a tree if a path in secure_read_path
is a prefix of fullname
Algorithm: extract each path, check for prefix
*/
char path[STRMAX];
while (secure_read_path && *secure_read_path) {
/* skip over separator */
while (*secure_read_path == os_sepchar ||
*secure_read_path == ';') secure_read_path++;
/* find next directory and copy into path*/
path[0] = 0;
int i = 0;
while (*secure_read_path && (*secure_read_path != os_sepchar &&
*secure_read_path != ';')) {
path[i++] = *secure_read_path++;
}
path[i] = 0;
/* see if directory is a prefix of fullname */
if (strstr(fullname, path) == fullname) {
return TRUE;
}
}
return FALSE;
}
|