commit d9cdf19
aabacchus
·
2022-06-20 02:58:29 +0000 UTC
parent fdb52a7
add simple file sandboxing below cwd Uses realpath(3) libc function to find the canonical and longest part of the pathname which exists, then checking if it starts with cwd. Prints a warning to stderr if an attempt is made to access a file outside the sandbox.
1 files changed,
+60,
-2
+60,
-2
1@@ -1,9 +1,17 @@
2+#define _XOPEN_SOURCE 500
3 #include <stdio.h>
4 #include <dirent.h>
5+#include <errno.h>
6+#include <limits.h>
7 #include <string.h>
8+#include <stdlib.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11
12+#ifndef PATH_MAX
13+#define PATH_MAX 4096
14+#endif
15+
16 #include "../uxn.h"
17 #include "file.h"
18
19@@ -27,6 +35,7 @@ typedef struct {
20 FILE_READ,
21 FILE_WRITE,
22 DIR_READ } state;
23+ int outside_sandbox;
24 } UxnFile;
25
26 static UxnFile uxn_file[POLYFILEY];
27@@ -44,6 +53,7 @@ reset(UxnFile *c)
28 }
29 c->de = NULL;
30 c->state = IDLE;
31+ c->outside_sandbox = 0;
32 }
33
34 static Uint16
35@@ -84,6 +94,49 @@ file_read_dir(UxnFile *c, char *dest, Uint16 len)
36 return p - dest;
37 }
38
39+static char *
40+retry_realpath(const char *file_name)
41+{
42+ char r[PATH_MAX] = {'\0'}, p[PATH_MAX] = {'\0'}, *x;
43+ if(file_name == NULL) {
44+ errno = EINVAL;
45+ return NULL;
46+ } else if(strlen(file_name) >= PATH_MAX) {
47+ errno = ENAMETOOLONG;
48+ return NULL;
49+ }
50+ if(file_name[0] != '/') {
51+ /* TODO: use a macro instead of '/' for absolute path first character so that other systems can work */
52+ /* if a relative path, prepend cwd */
53+ getcwd(p, sizeof(p));
54+ strcat(p, "/"); /* TODO: use a macro instead of '/' for the path delimiter */
55+ }
56+ strcat(p, file_name);
57+ while(realpath(p, r) == NULL) {
58+ if(errno != ENOENT)
59+ return NULL;
60+ x = strrchr(p, '/'); /* TODO: path delimiter macro */
61+ if(x)
62+ *x = '\0';
63+ else
64+ return NULL;
65+ }
66+ return strdup(r);
67+}
68+
69+static void
70+file_check_sandbox(UxnFile *c)
71+{
72+ char *x, *rp, cwd[PATH_MAX] = {'\0'};
73+ x = getcwd(cwd, sizeof(cwd));
74+ rp = retry_realpath(c->current_filename);
75+ if(rp == NULL || (x && strncmp(cwd, rp, strlen(cwd)) != 0)) {
76+ c->outside_sandbox = 1;
77+ fprintf(stderr, "file warning: blocked attempt to access %s outside of sandbox\n", c->current_filename);
78+ }
79+ free(rp);
80+}
81+
82 static Uint16
83 file_init(UxnFile *c, char *filename, size_t max_len)
84 {
85@@ -92,8 +145,10 @@ file_init(UxnFile *c, char *filename, size_t max_len)
86 reset(c);
87 if(len > max_len) len = max_len;
88 while(len) {
89- if((*p++ = *filename++) == '\0')
90+ if((*p++ = *filename++) == '\0') {
91+ file_check_sandbox(c);
92 return 0;
93+ }
94 len--;
95 }
96 c->current_filename[0] = '\0';
97@@ -103,6 +158,7 @@ file_init(UxnFile *c, char *filename, size_t max_len)
98 static Uint16
99 file_read(UxnFile *c, void *dest, Uint16 len)
100 {
101+ if(c->outside_sandbox) return 0;
102 if(c->state != FILE_READ && c->state != DIR_READ) {
103 reset(c);
104 if((c->dir = opendir(c->current_filename)) != NULL)
105@@ -121,6 +177,7 @@ static Uint16
106 file_write(UxnFile *c, void *src, Uint16 len, Uint8 flags)
107 {
108 Uint16 ret = 0;
109+ if(c->outside_sandbox) return 0;
110 if(c->state != FILE_WRITE) {
111 reset(c);
112 if((c->f = fopen(c->current_filename, (flags & 0x01) ? "ab" : "wb")) != NULL)
113@@ -137,6 +194,7 @@ static Uint16
114 file_stat(UxnFile *c, void *dest, Uint16 len)
115 {
116 char *basename = strrchr(c->current_filename, '/');
117+ if(c->outside_sandbox) return 0;
118 if(basename != NULL)
119 basename++;
120 else
121@@ -147,7 +205,7 @@ file_stat(UxnFile *c, void *dest, Uint16 len)
122 static Uint16
123 file_delete(UxnFile *c)
124 {
125- return unlink(c->current_filename);
126+ return c->outside_sandbox ? 0 : unlink(c->current_filename);
127 }
128
129 /* IO */