diff options
Diffstat (limited to 'src/firejail/protocol.c')
-rw-r--r-- | src/firejail/protocol.c | 247 |
1 files changed, 25 insertions, 222 deletions
diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c index 6321c703a..43f30e30a 100644 --- a/src/firejail/protocol.c +++ b/src/firejail/protocol.c | |||
@@ -18,241 +18,44 @@ | |||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | /* | ||
22 | struct sock_filter filter[] = { | ||
23 | VALIDATE_ARCHITECTURE, | ||
24 | EXAMINE_SYSCALL, | ||
25 | ONLY(SYS_socket), | ||
26 | EXAMINE_ARGUMENT(0), // allow only AF_INET and AF_INET6, drop everything else | ||
27 | WHITELIST(AF_INET), | ||
28 | WHITELIST(AF_INET6), | ||
29 | WHITELIST(AF_PACKET), | ||
30 | RETURN_ERRNO(ENOTSUP) | ||
31 | }; | ||
32 | struct sock_fprog prog = { | ||
33 | .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), | ||
34 | .filter = filter, | ||
35 | }; | ||
36 | |||
37 | |||
38 | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||
39 | perror("prctl(NO_NEW_PRIVS)"); | ||
40 | return 1; | ||
41 | } | ||
42 | if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { | ||
43 | perror("prctl"); | ||
44 | return 1; | ||
45 | } | ||
46 | */ | ||
47 | |||
48 | #ifdef HAVE_SECCOMP | 21 | #ifdef HAVE_SECCOMP |
49 | #include "firejail.h" | 22 | #include "firejail.h" |
50 | #include "../include/seccomp.h" | 23 | #include "../include/seccomp.h" |
51 | #include <sys/types.h> | ||
52 | #include <sys/socket.h> | ||
53 | |||
54 | static char *protocol[] = { | ||
55 | "unix", | ||
56 | "inet", | ||
57 | "inet6", | ||
58 | "netlink", | ||
59 | "packet", | ||
60 | NULL | ||
61 | }; | ||
62 | |||
63 | static struct sock_filter protocol_filter_command[] = { | ||
64 | WHITELIST(AF_UNIX), | ||
65 | WHITELIST(AF_INET), | ||
66 | WHITELIST(AF_INET6), | ||
67 | WHITELIST(AF_NETLINK), | ||
68 | WHITELIST(AF_PACKET) | ||
69 | }; | ||
70 | // Note: protocol[] and protocol_filter_command are synchronized | ||
71 | |||
72 | // command length | ||
73 | struct sock_filter whitelist[] = { | ||
74 | WHITELIST(AF_UNIX) | ||
75 | }; | ||
76 | unsigned whitelist_len = sizeof(whitelist) / sizeof(struct sock_filter); | ||
77 | |||
78 | 24 | ||
79 | 25 | // install protocol filter | |
80 | static int is_protocol(const char *p) { | 26 | void protocol_filter(const char *fname) { |
81 | int i = 0; | ||
82 | while (protocol[i] != NULL) { | ||
83 | if (strcmp(protocol[i], p) == 0) | ||
84 | return 1; | ||
85 | i++; | ||
86 | } | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static struct sock_filter *find_protocol_domain(const char *p) { | ||
92 | int i = 0; | ||
93 | while (protocol[i] != NULL) { | ||
94 | if (strcmp(protocol[i], p) == 0) | ||
95 | return &protocol_filter_command[i * whitelist_len]; | ||
96 | i++; | ||
97 | } | ||
98 | |||
99 | return NULL; | ||
100 | } | ||
101 | |||
102 | // --debug-protocols | ||
103 | void protocol_list(void) { | ||
104 | EUID_ASSERT(); | ||
105 | |||
106 | #ifndef SYS_socket | 27 | #ifndef SYS_socket |
107 | fprintf(stderr, "Warning: --protocol not supported on this platform\n"); | 28 | if (arg_debug) |
29 | printf("No support for --protocol on this platform\n"); | ||
108 | return; | 30 | return; |
109 | #endif | 31 | #else |
110 | 32 | assert(fname); | |
111 | int i = 0; | ||
112 | while (protocol[i] != NULL) { | ||
113 | printf("%s, ", protocol[i]); | ||
114 | i++; | ||
115 | } | ||
116 | printf("\n"); | ||
117 | } | ||
118 | |||
119 | 33 | ||
120 | // check protocol list and store it in cfg structure | 34 | // check file |
121 | void protocol_store(const char *prlist) { | 35 | struct stat s; |
122 | EUID_ASSERT(); | 36 | if (stat(fname, &s) == -1) { |
123 | assert(prlist); | 37 | fprintf(stderr, "Error: cannot read protocol filter file\n"); |
124 | 38 | exit(1); | |
125 | if (cfg.protocol && !arg_quiet) { | ||
126 | fprintf(stderr, "Warning: a protocol list is present, the new list \"%s\" will not be installed\n", prlist); | ||
127 | return; | ||
128 | } | 39 | } |
129 | 40 | int size = s.st_size; | |
130 | // temporary list | ||
131 | char *tmplist = strdup(prlist); | ||
132 | if (!tmplist) | ||
133 | errExit("strdup"); | ||
134 | |||
135 | // check list | ||
136 | char *token = strtok(tmplist, ","); | ||
137 | if (!token) | ||
138 | goto errout; | ||
139 | |||
140 | while (token) { | ||
141 | if (!is_protocol(token)) | ||
142 | goto errout; | ||
143 | token = strtok(NULL, ","); | ||
144 | } | ||
145 | free(tmplist); | ||
146 | |||
147 | // store list | ||
148 | cfg.protocol = strdup(prlist); | ||
149 | if (!cfg.protocol) | ||
150 | errExit("strdup"); | ||
151 | return; | ||
152 | |||
153 | errout: | ||
154 | fprintf(stderr, "Error: invalid protocol list\n"); | ||
155 | exit(1); | ||
156 | } | ||
157 | 41 | ||
158 | // install protocol filter | 42 | // read filter |
159 | void protocol_filter(void) { | ||
160 | assert(cfg.protocol); | ||
161 | if (arg_debug) | ||
162 | printf("Set protocol filter: %s\n", cfg.protocol); | ||
163 | |||
164 | #ifndef SYS_socket | ||
165 | (void) find_protocol_domain; | ||
166 | fprintf(stderr, "Warning: --protocol not supported on this platform\n"); | ||
167 | return; | ||
168 | #else | ||
169 | // build the filter | ||
170 | struct sock_filter filter[32]; // big enough | 43 | struct sock_filter filter[32]; // big enough |
171 | memset(&filter[0], 0, sizeof(filter)); | 44 | memset(&filter[0], 0, sizeof(filter)); |
172 | uint8_t *ptr = (uint8_t *) &filter[0]; | 45 | int src = open(fname, O_RDONLY); |
173 | 46 | int rd = 0; | |
174 | // header | 47 | while (rd < size) { |
175 | struct sock_filter filter_start[] = { | 48 | int rv = read(src, (unsigned char *) filter + rd, size - rd); |
176 | VALIDATE_ARCHITECTURE, | 49 | if (rv == -1) { |
177 | EXAMINE_SYSCALL, | 50 | fprintf(stderr, "Error: cannot read %s file\n", fname); |
178 | ONLY(SYS_socket), | 51 | exit(1); |
179 | EXAMINE_ARGUMENT(0) | 52 | } |
180 | }; | 53 | rd += rv; |
181 | memcpy(ptr, &filter_start[0], sizeof(filter_start)); | ||
182 | ptr += sizeof(filter_start); | ||
183 | |||
184 | #if 0 | ||
185 | printf("entries %u\n", (unsigned) (sizeof(filter_start) / sizeof(struct sock_filter))); | ||
186 | { | ||
187 | unsigned j; | ||
188 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
189 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
190 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
191 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
192 | printf("%02x, ", (*ptr2) & 0xff); | ||
193 | } | ||
194 | printf("\n"); | ||
195 | } | ||
196 | printf("whitelist_len %u, struct sock_filter len %u\n", whitelist_len, (unsigned) sizeof(struct sock_filter)); | ||
197 | #endif | ||
198 | |||
199 | |||
200 | // parse list and add commands | ||
201 | char *tmplist = strdup(cfg.protocol); | ||
202 | if (!tmplist) | ||
203 | errExit("strdup"); | ||
204 | char *token = strtok(tmplist, ","); | ||
205 | if (!token) | ||
206 | errExit("strtok"); | ||
207 | |||
208 | while (token) { | ||
209 | struct sock_filter *domain = find_protocol_domain(token); | ||
210 | assert(domain); | ||
211 | memcpy(ptr, domain, whitelist_len * sizeof(struct sock_filter)); | ||
212 | ptr += whitelist_len * sizeof(struct sock_filter); | ||
213 | token = strtok(NULL, ","); | ||
214 | |||
215 | #if 0 | ||
216 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
217 | { | ||
218 | unsigned j; | ||
219 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
220 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
221 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
222 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
223 | printf("%02x, ", (*ptr2) & 0xff); | ||
224 | } | ||
225 | printf("\n"); | ||
226 | } | ||
227 | #endif | ||
228 | |||
229 | |||
230 | } | ||
231 | free(tmplist); | ||
232 | |||
233 | // add end of filter | ||
234 | struct sock_filter filter_end[] = { | ||
235 | RETURN_ERRNO(ENOTSUP) | ||
236 | }; | ||
237 | memcpy(ptr, &filter_end[0], sizeof(filter_end)); | ||
238 | ptr += sizeof(filter_end); | ||
239 | |||
240 | #if 0 | ||
241 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
242 | { | ||
243 | unsigned j; | ||
244 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
245 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
246 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
247 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
248 | printf("%02x, ", (*ptr2) & 0xff); | ||
249 | } | 54 | } |
250 | printf("\n"); | 55 | close(src); |
251 | } | ||
252 | #endif | ||
253 | 56 | ||
254 | // install filter | 57 | // install filter |
255 | unsigned short entries = (unsigned short) ((uintptr_t) ptr - (uintptr_t) (filter)) / (unsigned) sizeof(struct sock_filter); | 58 | unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); |
256 | struct sock_fprog prog = { | 59 | struct sock_fprog prog = { |
257 | .len = entries, | 60 | .len = entries, |
258 | .filter = filter, | 61 | .filter = filter, |
@@ -262,7 +65,7 @@ printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (uns | |||
262 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); | 65 | fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n"); |
263 | return; | 66 | return; |
264 | } | 67 | } |
265 | #endif // SYS_socket | 68 | #endif |
266 | } | 69 | } |
267 | 70 | ||
268 | void protocol_filter_save(void) { | 71 | void protocol_filter_save(void) { |