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