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