summaryrefslogtreecommitdiffstats
path: root/src/firejail/macros.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/firejail/macros.c')
-rw-r--r--src/firejail/macros.c381
1 files changed, 381 insertions, 0 deletions
diff --git a/src/firejail/macros.c b/src/firejail/macros.c
new file mode 100644
index 000000000..f111802d7
--- /dev/null
+++ b/src/firejail/macros.c
@@ -0,0 +1,381 @@
1/*
2 * Copyright (C) 2014-2018 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20#include "firejail.h"
21#include <sys/stat.h>
22#define MAXBUF 4098
23
24typedef struct macro_t {
25 char *name; // macro name
26 char *xdg; // xdg line in ~/.config/user-dirs.dirs
27#define MAX_TRANSLATIONS 3 // several translations in case ~/.config/user-dirs.dirs not found
28 char *translation[MAX_TRANSLATIONS];
29} Macro;
30
31Macro macro[] = {
32 {
33 "${DOWNLOADS}",
34 "XDG_DOWNLOAD_DIR=\"$HOME/",
35 { "Downloads", "Загрузки", "Téléchargement" }
36 },
37
38 {
39 "${MUSIC}",
40 "XDG_MUSIC_DIR=\"$HOME/",
41 {"Music", "Музыка", "Musique"}
42 },
43
44 {
45 "${VIDEOS}",
46 "XDG_VIDEOS_DIR=\"$HOME/",
47 {"Videos", "Видео", "Vidéos"}
48 },
49
50 {
51 "${PICTURES}",
52 "XDG_PICTURES_DIR=\"$HOME/",
53 {"Pictures", "Изображения", "Photos"}
54 },
55
56 {
57 "${DESKTOP}",
58 "XDG_DESKTOP_DIR=\"$HOME/",
59 {"Desktop", "Рабочий стол", "Bureau"}
60 },
61
62 {
63 "${DOCUMENTS}",
64 "XDG_DOCUMENTS_DIR=\"$HOME/",
65 {"Documents", "Документы", "Documents"}
66 },
67
68 { 0 }
69};
70
71// return -1 if not found
72int macro_id(const char *name) {
73 int i = 0;
74 while (macro[i].name != NULL) {
75 if (strcmp(name, macro[i].name) == 0)
76 return i;
77 i++;
78 }
79
80 return -1;
81}
82
83int is_macro(const char *name) {
84 assert(name);
85 int len = strlen(name);
86 if (len <= 4)
87 return 0;
88 if (*name == '$' && name[1] == '{' && name[len - 1] == '}')
89 return 1;
90 return 0;
91}
92
93static char *resolve_xdg(const char *var) {
94 char *fname;
95 struct stat s;
96 size_t length = strlen(var);
97
98 if (asprintf(&fname, "%s/.config/user-dirs.dirs", cfg.homedir) == -1)
99 errExit("asprintf");
100 FILE *fp = fopen(fname, "r");
101 if (!fp) {
102 free(fname);
103 return NULL;
104 }
105 free(fname);
106
107 char buf[MAXBUF];
108 while (fgets(buf, MAXBUF, fp)) {
109 char *ptr = buf;
110
111 // skip blanks
112 while (*ptr == ' ' || *ptr == '\t')
113 ptr++;
114 if (*ptr == '\0' || *ptr == '\n' || *ptr == '#')
115 continue;
116
117 if (strncmp(ptr, var, length) == 0) {
118 char *ptr1 = ptr + length;
119 char *ptr2 = strchr(ptr1, '"');
120 if (ptr2) {
121 fclose(fp);
122 *ptr2 = '\0';
123 if (strlen(ptr1) != 0) {
124 if (asprintf(&fname, "%s/%s", cfg.homedir, ptr1) == -1)
125 errExit("asprintf");
126
127 if (stat(fname, &s) == -1) {
128 free(fname);
129 return NULL;
130 }
131 free(fname);
132
133 char *rv = strdup(ptr1);
134 if (!rv)
135 errExit(ptr1);
136 return rv;
137 }
138 else
139 return NULL;
140 }
141 }
142 }
143
144 fclose(fp);
145 return NULL;
146}
147
148static char *resolve_hardcoded(char *entries[]) {
149 char *fname;
150 struct stat s;
151
152 int i = 0;
153 while (entries[i] != NULL) {
154 if (asprintf(&fname, "%s/%s", cfg.homedir, entries[i]) == -1)
155 errExit("asprintf");
156
157 if (stat(fname, &s) == 0) {
158 free(fname);
159 return entries[i];
160 }
161 free(fname);
162 i++;
163 }
164
165 return NULL;
166}
167
168char *resolve_macro(const char *name) {
169 char *rv = NULL;
170 int id = macro_id(name);
171 if (id == -1)
172 return NULL;
173
174 rv = resolve_xdg(macro[id].xdg);
175 if (rv == NULL)
176 rv = resolve_hardcoded(macro[id].translation);
177 if (rv)
178 printf("Directory %s resolved as %s\n", name, rv);
179
180 return rv;
181}
182
183// This function takes a pathname supplied by the user and expands '~' and
184// '${HOME}' at the start, to refer to a path relative to the user's home
185// directory (supplied).
186// The return value is allocated using malloc and must be freed by the caller.
187// The function returns NULL if there are any errors.
188char *expand_home(const char *path, const char *homedir) {
189 assert(path);
190 assert(homedir);
191
192 int called_as_root = 0;
193
194 if(geteuid() == 0)
195 called_as_root = 1;
196
197 if(called_as_root) {
198 EUID_USER();
199 }
200
201 EUID_ASSERT();
202
203 // Replace home macro
204 char *new_name = NULL;
205 if (strncmp(path, "${HOME}", 7) == 0) {
206 if (asprintf(&new_name, "%s%s", homedir, path + 7) == -1)
207 errExit("asprintf");
208 if(called_as_root)
209 EUID_ROOT();
210 return new_name;
211 }
212 else if (*path == '~') {
213 if (asprintf(&new_name, "%s%s", homedir, path + 1) == -1)
214 errExit("asprintf");
215 if(called_as_root)
216 EUID_ROOT();
217 return new_name;
218 }
219 else if (strncmp(path, "${CFG}", 6) == 0) {
220 if (asprintf(&new_name, "%s%s", SYSCONFDIR, path + 6) == -1)
221 errExit("asprintf");
222 if(called_as_root)
223 EUID_ROOT();
224 return new_name;
225 }
226#if 0
227 else if (strncmp(path, "${DOWNLOADS}", 12) == 0) {
228 char *tmp = resolve_xdg("XDG_DOWNLOAD_DIR=\"$HOME/", 24, "Downloads");
229 char *tmp2 = resolve_hardcoded(dentry, "Downloads");
230 if(tmp) {
231 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 12) == -1)
232 errExit("asprintf");
233 if(called_as_root)
234 EUID_ROOT();
235 return new_name;
236 }
237 else if(tmp2) {
238 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 12) == -1)
239 errExit("asprintf");
240 if(called_as_root)
241 EUID_ROOT();
242 return new_name;
243 }
244 }
245
246 else if (strncmp(path, "${MUSIC}", 8) == 0) {
247 char *tmp = resolve_xdg("XDG_MUSIC_DIR=\"$HOME/", 21, "Music");
248 char *tmp2 = resolve_hardcoded(mentry, "Music");
249 if(tmp) {
250 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 8) == -1)
251 errExit("asprintf");
252 if(called_as_root)
253 EUID_ROOT();
254 return new_name;
255 }
256 else if(tmp2) {
257 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 8) == -1)
258 errExit("asprintf");
259 if(called_as_root)
260 EUID_ROOT();
261 return new_name;
262 }
263 }
264
265 else if (strncmp(path, "${VIDEOS}", 9) == 0) {
266 char *tmp = resolve_xdg("XDG_VIDEOS_DIR=\"$HOME/", 22, "Videos");
267 char *tmp2 = resolve_hardcoded(ventry, "Videos");
268 if(tmp) {
269 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 9) == -1)
270 errExit("asprintf");
271 if(called_as_root)
272 EUID_ROOT();
273 return new_name;
274 }
275 else if(tmp2) {
276 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 9) == -1)
277 errExit("asprintf");
278 if(called_as_root)
279 EUID_ROOT();
280 return new_name;
281 }
282 }
283
284 else if (strncmp(path, "${PICTURES}", 11) == 0) {
285 char *tmp = resolve_xdg("XDG_PICTURES_DIR=\"$HOME/", 24, "Pictures");
286 char *tmp2 = resolve_hardcoded(pentry, "Pictures");
287 if(tmp) {
288 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 11) == -1)
289 errExit("asprintf");
290 if(called_as_root)
291 EUID_ROOT();
292 return new_name;
293 }
294 else if(tmp2) {
295 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 11) == -1)
296 errExit("asprintf");
297 if(called_as_root)
298 EUID_ROOT();
299 return new_name;
300 }
301 }
302
303 else if (strncmp(path, "${DESKTOP}", 10) == 0) {
304 char *tmp = resolve_xdg("XDG_DESKTOP_DIR=\"$HOME/", 24, "Desktop");
305 char *tmp2 = resolve_hardcoded(deentry, "Desktop");
306 if(tmp) {
307 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 10) == -1)
308 errExit("asprintf");
309 if(called_as_root)
310 EUID_ROOT();
311 return new_name;
312 }
313 else if(tmp2) {
314 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 10) == -1)
315 errExit("asprintf");
316 if(called_as_root)
317 EUID_ROOT();
318 return new_name;
319 }
320 }
321
322 else if (strncmp(path, "${DOCUMENTS}", 12) == 0) {
323 char *tmp = resolve_xdg("XDG_DOCUMENTS_DIR=\"$HOME/", 25, "Documents");
324 char *tmp2 = resolve_hardcoded(doentry, "Documents");
325 if(tmp) {
326 if (asprintf(&new_name, "%s/%s%s", homedir, tmp, path + 12) == -1)
327 errExit("asprintf");
328 if(called_as_root)
329 EUID_ROOT();
330 return new_name;
331 }
332 else if(tmp2) {
333 if (asprintf(&new_name, "%s/%s%s", homedir, tmp2, path + 12) == -1)
334 errExit("asprintf");
335 if(called_as_root)
336 EUID_ROOT();
337 return new_name;
338 }
339 }
340#endif
341 char *rv = strdup(path);
342 if (!rv)
343 errExit("strdup");
344
345 if(called_as_root)
346 EUID_ROOT();
347
348 return rv;
349}
350
351void invalid_filename(const char *fname, int globbing) {
352// EUID_ASSERT();
353 assert(fname);
354 const char *ptr = fname;
355
356 if (strncmp(ptr, "${HOME}", 7) == 0)
357 ptr = fname + 7;
358 else if (strncmp(ptr, "${PATH}", 7) == 0)
359 ptr = fname + 7;
360 else {
361 int id = macro_id(fname);
362 if (id != -1)
363 return;
364 }
365
366 int len = strlen(ptr);
367
368 if (globbing) {
369 // file globbing ('*?[]') is allowed
370 if (strcspn(ptr, "\\&!\"'<>%^(){};,") != (size_t)len) {
371 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
372 exit(1);
373 }
374 }
375 else {
376 if (strcspn(ptr, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) {
377 fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
378 exit(1);
379 }
380 }
381}