diff options
Diffstat (limited to 'src/fseccomp')
-rw-r--r-- | src/fseccomp/Makefile.in | 43 | ||||
-rw-r--r-- | src/fseccomp/errno.c | 204 | ||||
-rw-r--r-- | src/fseccomp/fseccomp.h | 68 | ||||
-rw-r--r-- | src/fseccomp/main.c | 91 | ||||
-rw-r--r-- | src/fseccomp/protocol.c | 219 | ||||
-rw-r--r-- | src/fseccomp/seccomp.c | 292 | ||||
-rw-r--r-- | src/fseccomp/seccomp_file.c | 108 | ||||
-rw-r--r-- | src/fseccomp/seccomp_print.c | 116 | ||||
-rw-r--r-- | src/fseccomp/seccomp_secondary.c | 183 | ||||
-rw-r--r-- | src/fseccomp/syscall.c | 110 |
10 files changed, 1434 insertions, 0 deletions
diff --git a/src/fseccomp/Makefile.in b/src/fseccomp/Makefile.in new file mode 100644 index 000000000..110d2c95f --- /dev/null +++ b/src/fseccomp/Makefile.in | |||
@@ -0,0 +1,43 @@ | |||
1 | all: fseccomp | ||
2 | |||
3 | prefix=@prefix@ | ||
4 | exec_prefix=@exec_prefix@ | ||
5 | libdir=@libdir@ | ||
6 | sysconfdir=@sysconfdir@ | ||
7 | |||
8 | VERSION=@PACKAGE_VERSION@ | ||
9 | NAME=@PACKAGE_NAME@ | ||
10 | HAVE_SECCOMP_H=@HAVE_SECCOMP_H@ | ||
11 | HAVE_SECCOMP=@HAVE_SECCOMP@ | ||
12 | HAVE_CHROOT=@HAVE_CHROOT@ | ||
13 | HAVE_BIND=@HAVE_BIND@ | ||
14 | HAVE_FATAL_WARNINGS=@HAVE_FATAL_WARNINGS@ | ||
15 | HAVE_NETWORK=@HAVE_NETWORK@ | ||
16 | HAVE_USERNS=@HAVE_USERNS@ | ||
17 | HAVE_X11=@HAVE_X11@ | ||
18 | HAVE_FILE_TRANSFER=@HAVE_FILE_TRANSFER@ | ||
19 | HAVE_WHITELIST=@HAVE_WHITELIST@ | ||
20 | HAVE_GLOBALCFG=@HAVE_GLOBALCFG@ | ||
21 | HAVE_APPARMOR=@HAVE_APPARMOR@ | ||
22 | HAVE_OVERLAYFS=@HAVE_OVERLAYFS@ | ||
23 | HAVE_PRIVATE_HOME=@HAVE_PRIVATE_HOME@ | ||
24 | EXTRA_LDFLAGS +=@EXTRA_LDFLAGS@ | ||
25 | |||
26 | H_FILE_LIST = $(sort $(wildcard *.[h])) | ||
27 | C_FILE_LIST = $(sort $(wildcard *.c)) | ||
28 | OBJS = $(C_FILE_LIST:.c=.o) | ||
29 | BINOBJS = $(foreach file, $(OBJS), $file) | ||
30 | CFLAGS += -ggdb $(HAVE_FATAL_WARNINGS) -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(prefix)"' -DSYSCONFDIR='"$(sysconfdir)/firejail"' -DLIBDIR='"$(libdir)"' $(HAVE_X11) $(HAVE_PRIVATE_HOME) $(HAVE_APPARMOR) $(HAVE_OVERLAYFS) $(HAVE_SECCOMP) $(HAVE_GLOBALCFG) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_NETWORK) $(HAVE_USERNS) $(HAVE_BIND) $(HAVE_FILE_TRANSFER) $(HAVE_WHITELIST) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security | ||
31 | LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread | ||
32 | |||
33 | %.o : %.c $(H_FILE_LIST) ../include/common.h ../include/syscall.h | ||
34 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ | ||
35 | |||
36 | fseccomp: $(OBJS) ../lib/libnetlink.o ../lib/common.o | ||
37 | $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) $(EXTRA_LDFLAGS) | ||
38 | |||
39 | clean:; rm -f *.o fseccomp | ||
40 | |||
41 | distclean: clean | ||
42 | rm -fr Makefile | ||
43 | |||
diff --git a/src/fseccomp/errno.c b/src/fseccomp/errno.c new file mode 100644 index 000000000..dbee916d4 --- /dev/null +++ b/src/fseccomp/errno.c | |||
@@ -0,0 +1,204 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | |||
22 | #include <errno.h> | ||
23 | //#include <attr/xattr.h> | ||
24 | |||
25 | typedef struct { | ||
26 | char *name; | ||
27 | int nr; | ||
28 | } ErrnoEntry; | ||
29 | |||
30 | static ErrnoEntry errnolist[] = { | ||
31 | // | ||
32 | // code generated using tools/extract-errnos | ||
33 | // | ||
34 | {"EPERM", EPERM}, | ||
35 | {"ENOENT", ENOENT}, | ||
36 | {"ESRCH", ESRCH}, | ||
37 | {"EINTR", EINTR}, | ||
38 | {"EIO", EIO}, | ||
39 | {"ENXIO", ENXIO}, | ||
40 | {"E2BIG", E2BIG}, | ||
41 | {"ENOEXEC", ENOEXEC}, | ||
42 | {"EBADF", EBADF}, | ||
43 | {"ECHILD", ECHILD}, | ||
44 | {"EAGAIN", EAGAIN}, | ||
45 | {"ENOMEM", ENOMEM}, | ||
46 | {"EACCES", EACCES}, | ||
47 | {"EFAULT", EFAULT}, | ||
48 | {"ENOTBLK", ENOTBLK}, | ||
49 | {"EBUSY", EBUSY}, | ||
50 | {"EEXIST", EEXIST}, | ||
51 | {"EXDEV", EXDEV}, | ||
52 | {"ENODEV", ENODEV}, | ||
53 | {"ENOTDIR", ENOTDIR}, | ||
54 | {"EISDIR", EISDIR}, | ||
55 | {"EINVAL", EINVAL}, | ||
56 | {"ENFILE", ENFILE}, | ||
57 | {"EMFILE", EMFILE}, | ||
58 | {"ENOTTY", ENOTTY}, | ||
59 | {"ETXTBSY", ETXTBSY}, | ||
60 | {"EFBIG", EFBIG}, | ||
61 | {"ENOSPC", ENOSPC}, | ||
62 | {"ESPIPE", ESPIPE}, | ||
63 | {"EROFS", EROFS}, | ||
64 | {"EMLINK", EMLINK}, | ||
65 | {"EPIPE", EPIPE}, | ||
66 | {"EDOM", EDOM}, | ||
67 | {"ERANGE", ERANGE}, | ||
68 | {"EDEADLK", EDEADLK}, | ||
69 | {"ENAMETOOLONG", ENAMETOOLONG}, | ||
70 | {"ENOLCK", ENOLCK}, | ||
71 | {"ENOSYS", ENOSYS}, | ||
72 | {"ENOTEMPTY", ENOTEMPTY}, | ||
73 | {"ELOOP", ELOOP}, | ||
74 | {"EWOULDBLOCK", EWOULDBLOCK}, | ||
75 | {"ENOMSG", ENOMSG}, | ||
76 | {"EIDRM", EIDRM}, | ||
77 | {"ECHRNG", ECHRNG}, | ||
78 | {"EL2NSYNC", EL2NSYNC}, | ||
79 | {"EL3HLT", EL3HLT}, | ||
80 | {"EL3RST", EL3RST}, | ||
81 | {"ELNRNG", ELNRNG}, | ||
82 | {"EUNATCH", EUNATCH}, | ||
83 | {"ENOCSI", ENOCSI}, | ||
84 | {"EL2HLT", EL2HLT}, | ||
85 | {"EBADE", EBADE}, | ||
86 | {"EBADR", EBADR}, | ||
87 | {"EXFULL", EXFULL}, | ||
88 | {"ENOANO", ENOANO}, | ||
89 | {"EBADRQC", EBADRQC}, | ||
90 | {"EBADSLT", EBADSLT}, | ||
91 | {"EDEADLOCK", EDEADLOCK}, | ||
92 | {"EBFONT", EBFONT}, | ||
93 | {"ENOSTR", ENOSTR}, | ||
94 | {"ENODATA", ENODATA}, | ||
95 | {"ETIME", ETIME}, | ||
96 | {"ENOSR", ENOSR}, | ||
97 | {"ENONET", ENONET}, | ||
98 | {"ENOPKG", ENOPKG}, | ||
99 | {"EREMOTE", EREMOTE}, | ||
100 | {"ENOLINK", ENOLINK}, | ||
101 | {"EADV", EADV}, | ||
102 | {"ESRMNT", ESRMNT}, | ||
103 | {"ECOMM", ECOMM}, | ||
104 | {"EPROTO", EPROTO}, | ||
105 | {"EMULTIHOP", EMULTIHOP}, | ||
106 | {"EDOTDOT", EDOTDOT}, | ||
107 | {"EBADMSG", EBADMSG}, | ||
108 | {"EOVERFLOW", EOVERFLOW}, | ||
109 | {"ENOTUNIQ", ENOTUNIQ}, | ||
110 | {"EBADFD", EBADFD}, | ||
111 | {"EREMCHG", EREMCHG}, | ||
112 | {"ELIBACC", ELIBACC}, | ||
113 | {"ELIBBAD", ELIBBAD}, | ||
114 | {"ELIBSCN", ELIBSCN}, | ||
115 | {"ELIBMAX", ELIBMAX}, | ||
116 | {"ELIBEXEC", ELIBEXEC}, | ||
117 | {"EILSEQ", EILSEQ}, | ||
118 | {"ERESTART", ERESTART}, | ||
119 | {"ESTRPIPE", ESTRPIPE}, | ||
120 | {"EUSERS", EUSERS}, | ||
121 | {"ENOTSOCK", ENOTSOCK}, | ||
122 | {"EDESTADDRREQ", EDESTADDRREQ}, | ||
123 | {"EMSGSIZE", EMSGSIZE}, | ||
124 | {"EPROTOTYPE", EPROTOTYPE}, | ||
125 | {"ENOPROTOOPT", ENOPROTOOPT}, | ||
126 | {"EPROTONOSUPPORT", EPROTONOSUPPORT}, | ||
127 | {"ESOCKTNOSUPPORT", ESOCKTNOSUPPORT}, | ||
128 | {"EOPNOTSUPP", EOPNOTSUPP}, | ||
129 | {"EPFNOSUPPORT", EPFNOSUPPORT}, | ||
130 | {"EAFNOSUPPORT", EAFNOSUPPORT}, | ||
131 | {"EADDRINUSE", EADDRINUSE}, | ||
132 | {"EADDRNOTAVAIL", EADDRNOTAVAIL}, | ||
133 | {"ENETDOWN", ENETDOWN}, | ||
134 | {"ENETUNREACH", ENETUNREACH}, | ||
135 | {"ENETRESET", ENETRESET}, | ||
136 | {"ECONNABORTED", ECONNABORTED}, | ||
137 | {"ECONNRESET", ECONNRESET}, | ||
138 | {"ENOBUFS", ENOBUFS}, | ||
139 | {"EISCONN", EISCONN}, | ||
140 | {"ENOTCONN", ENOTCONN}, | ||
141 | {"ESHUTDOWN", ESHUTDOWN}, | ||
142 | {"ETOOMANYREFS", ETOOMANYREFS}, | ||
143 | {"ETIMEDOUT", ETIMEDOUT}, | ||
144 | {"ECONNREFUSED", ECONNREFUSED}, | ||
145 | {"EHOSTDOWN", EHOSTDOWN}, | ||
146 | {"EHOSTUNREACH", EHOSTUNREACH}, | ||
147 | {"EALREADY", EALREADY}, | ||
148 | {"EINPROGRESS", EINPROGRESS}, | ||
149 | {"ESTALE", ESTALE}, | ||
150 | {"EUCLEAN", EUCLEAN}, | ||
151 | {"ENOTNAM", ENOTNAM}, | ||
152 | {"ENAVAIL", ENAVAIL}, | ||
153 | {"EISNAM", EISNAM}, | ||
154 | {"EREMOTEIO", EREMOTEIO}, | ||
155 | {"EDQUOT", EDQUOT}, | ||
156 | {"ENOMEDIUM", ENOMEDIUM}, | ||
157 | {"EMEDIUMTYPE", EMEDIUMTYPE}, | ||
158 | {"ECANCELED", ECANCELED}, | ||
159 | {"ENOKEY", ENOKEY}, | ||
160 | {"EKEYEXPIRED", EKEYEXPIRED}, | ||
161 | {"EKEYREVOKED", EKEYREVOKED}, | ||
162 | {"EKEYREJECTED", EKEYREJECTED}, | ||
163 | {"EOWNERDEAD", EOWNERDEAD}, | ||
164 | {"ENOTRECOVERABLE", ENOTRECOVERABLE}, | ||
165 | {"ERFKILL", ERFKILL}, | ||
166 | {"EHWPOISON", EHWPOISON}, | ||
167 | {"ENOTSUP", ENOTSUP}, | ||
168 | #ifdef ENOATTR | ||
169 | {"ENOATTR", ENOATTR}, | ||
170 | #endif | ||
171 | }; | ||
172 | |||
173 | int errno_find_name(const char *name) { | ||
174 | int i; | ||
175 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | ||
176 | for (i = 0; i < elems; i++) { | ||
177 | if (strcasecmp(name, errnolist[i].name) == 0) | ||
178 | return errnolist[i].nr; | ||
179 | } | ||
180 | |||
181 | return -1; | ||
182 | } | ||
183 | |||
184 | char *errno_find_nr(int nr) { | ||
185 | int i; | ||
186 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | ||
187 | for (i = 0; i < elems; i++) { | ||
188 | if (nr == errnolist[i].nr) | ||
189 | return errnolist[i].name; | ||
190 | } | ||
191 | |||
192 | return "unknown"; | ||
193 | } | ||
194 | |||
195 | |||
196 | |||
197 | void errno_print(void) { | ||
198 | int i; | ||
199 | int elems = sizeof(errnolist) / sizeof(errnolist[0]); | ||
200 | for (i = 0; i < elems; i++) { | ||
201 | printf("%d\t- %s\n", errnolist[i].nr, errnolist[i].name); | ||
202 | } | ||
203 | printf("\n"); | ||
204 | } | ||
diff --git a/src/fseccomp/fseccomp.h b/src/fseccomp/fseccomp.h new file mode 100644 index 000000000..504f1c23f --- /dev/null +++ b/src/fseccomp/fseccomp.h | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | #ifndef FSECCOMP_H | ||
21 | #define FSECCOMP_H | ||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include <assert.h> | ||
26 | #include "../include/common.h" | ||
27 | |||
28 | // syscall.c | ||
29 | void syscall_print(void); | ||
30 | int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall, int arg), int fd, int arg); | ||
31 | int syscall_find_name(const char *name); | ||
32 | char *syscall_find_nr(int nr); | ||
33 | |||
34 | // errno.c | ||
35 | void errno_print(void); | ||
36 | int errno_find_name(const char *name); | ||
37 | char *errno_find_nr(int nr); | ||
38 | |||
39 | // protocol.c | ||
40 | void protocol_print(void); | ||
41 | void protocol_build_filter(const char *prlist, const char *fname); | ||
42 | |||
43 | // seccomp_secondary.c | ||
44 | void seccomp_secondary_64(const char *fname); | ||
45 | void seccomp_secondary_32(const char *fname); | ||
46 | |||
47 | // seccomp_file.c | ||
48 | void filter_init(int fd); | ||
49 | void filter_add_whitelist(int fd, int syscall, int arg); | ||
50 | void filter_add_blacklist(int fd, int syscall, int arg); | ||
51 | void filter_add_errno(int fd, int syscall, int arg); | ||
52 | void filter_end_blacklist(int fd); | ||
53 | void filter_end_whitelist(int fd); | ||
54 | |||
55 | // seccomp.c | ||
56 | // default list | ||
57 | void seccomp_default(const char *fname, int allow_debuggers); | ||
58 | // drop list | ||
59 | void seccomp_drop(const char *fname, char *list, int allow_debuggers); | ||
60 | // default+drop list | ||
61 | void seccomp_default_drop(const char *fname, char *list, int allow_debuggers); | ||
62 | // whitelisted filter | ||
63 | void seccomp_keep(const char *fname, char *list); | ||
64 | |||
65 | // seccomp_print | ||
66 | void filter_print(const char *fname); | ||
67 | |||
68 | #endif | ||
diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c new file mode 100644 index 000000000..22b13bcd9 --- /dev/null +++ b/src/fseccomp/main.c | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | |||
22 | static void usage(void) { | ||
23 | printf("Usage:\n"); | ||
24 | printf("\tfseccomp debug-syscalls\n"); | ||
25 | printf("\tfseccomp debug-errnos\n"); | ||
26 | printf("\tfseccomp debug-protocols\n"); | ||
27 | printf("\tfseccomp protocol build list file\n"); | ||
28 | printf("\tfseccomp secondary 64 file\n"); | ||
29 | printf("\tfseccomp secondary 32 file\n"); | ||
30 | printf("\tfseccomp default file\n"); | ||
31 | printf("\tfseccomp default file allow-debuggers\n"); | ||
32 | printf("\tfseccomp drop file list\n"); | ||
33 | printf("\tfseccomp drop file list allow-debuggers\n"); | ||
34 | printf("\tfseccomp default drop file list\n"); | ||
35 | printf("\tfseccomp default drop file list allow-debuggers\n"); | ||
36 | printf("\tfseccomp keep file list\n"); | ||
37 | printf("\tfseccomp print file\n"); | ||
38 | } | ||
39 | |||
40 | int main(int argc, char **argv) { | ||
41 | #if 0 | ||
42 | { | ||
43 | system("cat /proc/self/status"); | ||
44 | int i; | ||
45 | for (i = 0; i < argc; i++) | ||
46 | printf("*%s* ", argv[i]); | ||
47 | printf("\n"); | ||
48 | } | ||
49 | #endif | ||
50 | if (argc < 2) | ||
51 | return 1; | ||
52 | |||
53 | if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) { | ||
54 | usage(); | ||
55 | return 0; | ||
56 | } | ||
57 | else if (argc == 2 && strcmp(argv[1], "debug-syscalls") == 0) | ||
58 | syscall_print(); | ||
59 | else if (argc == 2 && strcmp(argv[1], "debug-errnos") == 0) | ||
60 | errno_print(); | ||
61 | else if (argc == 2 && strcmp(argv[1], "debug-protocols") == 0) | ||
62 | protocol_print(); | ||
63 | else if (argc == 5 && strcmp(argv[1], "protocol") == 0 && strcmp(argv[2], "build") == 0) | ||
64 | protocol_build_filter(argv[3], argv[4]); | ||
65 | else if (argc == 4 && strcmp(argv[1], "secondary") == 0 && strcmp(argv[2], "64") == 0) | ||
66 | seccomp_secondary_64(argv[3]); | ||
67 | else if (argc == 4 && strcmp(argv[1], "secondary") == 0 && strcmp(argv[2], "32") == 0) | ||
68 | seccomp_secondary_32(argv[3]); | ||
69 | else if (argc == 3 && strcmp(argv[1], "default") == 0) | ||
70 | seccomp_default(argv[2], 0); | ||
71 | else if (argc == 4 && strcmp(argv[1], "default") == 0 && strcmp(argv[3], "allow-debuggers") == 0) | ||
72 | seccomp_default(argv[2], 1); | ||
73 | else if (argc == 4 && strcmp(argv[1], "drop") == 0) | ||
74 | seccomp_drop(argv[2], argv[3], 0); | ||
75 | else if (argc == 5 && strcmp(argv[1], "drop") == 0 && strcmp(argv[4], "allow-debuggers") == 0) | ||
76 | seccomp_drop(argv[2], argv[3], 1); | ||
77 | else if (argc == 5 && strcmp(argv[1], "default") == 0 && strcmp(argv[2], "drop") == 0) | ||
78 | seccomp_default_drop(argv[3], argv[4], 0); | ||
79 | else if (argc == 6 && strcmp(argv[1], "default") == 0 && strcmp(argv[2], "drop") == 0 && strcmp(argv[5], "allow-debuggers") == 0) | ||
80 | seccomp_default_drop(argv[3], argv[4], 1); | ||
81 | else if (argc == 4 && strcmp(argv[1], "keep") == 0) | ||
82 | seccomp_keep(argv[2], argv[3]); | ||
83 | else if (argc == 3 && strcmp(argv[1], "print") == 0) | ||
84 | filter_print(argv[2]); | ||
85 | else { | ||
86 | fprintf(stderr, "Error fseccomp: invalid arguments\n"); | ||
87 | return 1; | ||
88 | } | ||
89 | |||
90 | return 0; | ||
91 | } \ No newline at end of file | ||
diff --git a/src/fseccomp/protocol.c b/src/fseccomp/protocol.c new file mode 100644 index 000000000..38c5f9d88 --- /dev/null +++ b/src/fseccomp/protocol.c | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 | |||
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 | #include "fseccomp.h" | ||
49 | #include "../include/seccomp.h" | ||
50 | #include <sys/syscall.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 | static struct sock_filter *find_protocol_domain(const char *p) { | ||
79 | int i = 0; | ||
80 | while (protocol[i] != NULL) { | ||
81 | if (strcmp(protocol[i], p) == 0) | ||
82 | return &protocol_filter_command[i * whitelist_len]; | ||
83 | i++; | ||
84 | } | ||
85 | |||
86 | return NULL; | ||
87 | } | ||
88 | |||
89 | |||
90 | void protocol_print(void) { | ||
91 | #ifndef SYS_socket | ||
92 | fprintf(stderr, "Warning fseccomp: firejail --protocol not supported on this platform\n"); | ||
93 | return; | ||
94 | #endif | ||
95 | |||
96 | int i = 0; | ||
97 | while (protocol[i] != NULL) { | ||
98 | printf("%s, ", protocol[i]); | ||
99 | i++; | ||
100 | } | ||
101 | printf("\n"); | ||
102 | } | ||
103 | |||
104 | // install protocol filter | ||
105 | void protocol_build_filter(const char *prlist, const char *fname) { | ||
106 | assert(prlist); | ||
107 | assert(fname); | ||
108 | |||
109 | #ifndef SYS_socket | ||
110 | fprintf(stderr, "Warning: --protocol not supported on this platform\n"); | ||
111 | return; | ||
112 | #else | ||
113 | // build the filter | ||
114 | struct sock_filter filter[32]; // big enough | ||
115 | memset(&filter[0], 0, sizeof(filter)); | ||
116 | uint8_t *ptr = (uint8_t *) &filter[0]; | ||
117 | |||
118 | // header | ||
119 | struct sock_filter filter_start[] = { | ||
120 | VALIDATE_ARCHITECTURE, | ||
121 | EXAMINE_SYSCALL, | ||
122 | ONLY(SYS_socket), | ||
123 | EXAMINE_ARGUMENT(0) | ||
124 | }; | ||
125 | memcpy(ptr, &filter_start[0], sizeof(filter_start)); | ||
126 | ptr += sizeof(filter_start); | ||
127 | |||
128 | #if 0 | ||
129 | printf("entries %u\n", (unsigned) (sizeof(filter_start) / sizeof(struct sock_filter))); | ||
130 | { | ||
131 | unsigned j; | ||
132 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
133 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
134 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
135 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
136 | printf("%02x, ", (*ptr2) & 0xff); | ||
137 | } | ||
138 | printf("\n"); | ||
139 | } | ||
140 | printf("whitelist_len %u, struct sock_filter len %u\n", whitelist_len, (unsigned) sizeof(struct sock_filter)); | ||
141 | #endif | ||
142 | |||
143 | |||
144 | // parse list and add commands | ||
145 | char *tmplist = strdup(prlist); | ||
146 | if (!tmplist) | ||
147 | errExit("strdup"); | ||
148 | char *token = strtok(tmplist, ","); | ||
149 | if (!token) | ||
150 | errExit("strtok"); | ||
151 | |||
152 | while (token) { | ||
153 | struct sock_filter *domain = find_protocol_domain(token); | ||
154 | if (domain == NULL) { | ||
155 | fprintf(stderr, "Error fseccomp: %s is not a valid protocol\n", token); | ||
156 | exit(1); | ||
157 | } | ||
158 | memcpy(ptr, domain, whitelist_len * sizeof(struct sock_filter)); | ||
159 | ptr += whitelist_len * sizeof(struct sock_filter); | ||
160 | token = strtok(NULL, ","); | ||
161 | |||
162 | #if 0 | ||
163 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
164 | { | ||
165 | unsigned j; | ||
166 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
167 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
168 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
169 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
170 | printf("%02x, ", (*ptr2) & 0xff); | ||
171 | } | ||
172 | printf("\n"); | ||
173 | } | ||
174 | #endif | ||
175 | |||
176 | |||
177 | } | ||
178 | free(tmplist); | ||
179 | |||
180 | // add end of filter | ||
181 | struct sock_filter filter_end[] = { | ||
182 | RETURN_ERRNO(ENOTSUP) | ||
183 | }; | ||
184 | memcpy(ptr, &filter_end[0], sizeof(filter_end)); | ||
185 | ptr += sizeof(filter_end); | ||
186 | |||
187 | #if 0 | ||
188 | printf("entries %u\n", (unsigned) ((uint64_t) ptr - (uint64_t) (filter)) / (unsigned) sizeof(struct sock_filter)); | ||
189 | { | ||
190 | unsigned j; | ||
191 | unsigned char *ptr2 = (unsigned char *) &filter[0]; | ||
192 | for (j = 0; j < sizeof(filter); j++, ptr2++) { | ||
193 | if ((j % (sizeof(struct sock_filter))) == 0) | ||
194 | printf("\n%u: ", 1 + (unsigned) (j / (sizeof(struct sock_filter)))); | ||
195 | printf("%02x, ", (*ptr2) & 0xff); | ||
196 | } | ||
197 | printf("\n"); | ||
198 | } | ||
199 | #endif | ||
200 | // save filter to file | ||
201 | int dst = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
202 | if (dst < 0) { | ||
203 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
204 | exit(1); | ||
205 | } | ||
206 | |||
207 | int size = (int) ((uintptr_t) ptr - (uintptr_t) (filter)); | ||
208 | int written = 0; | ||
209 | while (written < size) { | ||
210 | int rv = write(dst, (unsigned char *) filter + written, size - written); | ||
211 | if (rv == -1) { | ||
212 | fprintf(stderr, "Error fseccomp: cannot write %s file\n", fname); | ||
213 | exit(1); | ||
214 | } | ||
215 | written += rv; | ||
216 | } | ||
217 | close(dst); | ||
218 | #endif // SYS_socket | ||
219 | } | ||
diff --git a/src/fseccomp/seccomp.c b/src/fseccomp/seccomp.c new file mode 100644 index 000000000..cc6edc8ca --- /dev/null +++ b/src/fseccomp/seccomp.c | |||
@@ -0,0 +1,292 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | #include "../include/seccomp.h" | ||
22 | #include <sys/syscall.h> | ||
23 | |||
24 | static void add_default_list(int fd, int allow_debuggers) { | ||
25 | #ifdef SYS_mount | ||
26 | filter_add_blacklist(fd, SYS_mount, 0); | ||
27 | #endif | ||
28 | #ifdef SYS_umount2 | ||
29 | filter_add_blacklist(fd, SYS_umount2, 0); | ||
30 | #endif | ||
31 | |||
32 | if (!allow_debuggers) { | ||
33 | #ifdef SYS_ptrace | ||
34 | filter_add_blacklist(fd, SYS_ptrace, 0); | ||
35 | #endif | ||
36 | } | ||
37 | |||
38 | #ifdef SYS_kexec_load | ||
39 | filter_add_blacklist(fd, SYS_kexec_load, 0); | ||
40 | #endif | ||
41 | #ifdef SYS_kexec_file_load | ||
42 | filter_add_blacklist(fd, SYS_kexec_file_load, 0); | ||
43 | #endif | ||
44 | #ifdef SYS_open_by_handle_at | ||
45 | filter_add_blacklist(fd, SYS_open_by_handle_at, 0); | ||
46 | #endif | ||
47 | #ifdef SYS_name_to_handle_at | ||
48 | filter_add_blacklist(fd, SYS_name_to_handle_at, 0); | ||
49 | #endif | ||
50 | #ifdef SYS_init_module | ||
51 | filter_add_blacklist(fd, SYS_init_module, 0); | ||
52 | #endif | ||
53 | #ifdef SYS_finit_module | ||
54 | filter_add_blacklist(fd, SYS_finit_module, 0); | ||
55 | #endif | ||
56 | #ifdef SYS_create_module | ||
57 | filter_add_blacklist(fd, SYS_create_module, 0); | ||
58 | #endif | ||
59 | #ifdef SYS_delete_module | ||
60 | filter_add_blacklist(fd, SYS_delete_module, 0); | ||
61 | #endif | ||
62 | #ifdef SYS_iopl | ||
63 | filter_add_blacklist(fd, SYS_iopl, 0); | ||
64 | #endif | ||
65 | #ifdef SYS_ioperm | ||
66 | filter_add_blacklist(fd, SYS_ioperm, 0); | ||
67 | #endif | ||
68 | #ifdef SYS_ioprio_set | ||
69 | filter_add_blacklist(fd, SYS_ioprio_set, 0); | ||
70 | #endif | ||
71 | #ifdef SYS_ni_syscall | ||
72 | filter_add_blacklist(fd, SYS_ni_syscall, 0); | ||
73 | #endif | ||
74 | #ifdef SYS_swapon | ||
75 | filter_add_blacklist(fd, SYS_swapon, 0); | ||
76 | #endif | ||
77 | #ifdef SYS_swapoff | ||
78 | filter_add_blacklist(fd, SYS_swapoff, 0); | ||
79 | #endif | ||
80 | #ifdef SYS_syslog | ||
81 | filter_add_blacklist(fd, SYS_syslog, 0); | ||
82 | #endif | ||
83 | |||
84 | if (!allow_debuggers) { | ||
85 | #ifdef SYS_process_vm_readv | ||
86 | filter_add_blacklist(fd, SYS_process_vm_readv, 0); | ||
87 | #endif | ||
88 | } | ||
89 | |||
90 | #ifdef SYS_process_vm_writev | ||
91 | filter_add_blacklist(fd, SYS_process_vm_writev, 0); | ||
92 | #endif | ||
93 | |||
94 | // mknod removed in 0.9.29 - it brakes Zotero extension | ||
95 | //#ifdef SYS_mknod | ||
96 | // filter_add_blacklist(SYS_mknod, 0); | ||
97 | //#endif | ||
98 | |||
99 | #ifdef SYS_sysfs | ||
100 | filter_add_blacklist(fd, SYS_sysfs, 0); | ||
101 | #endif | ||
102 | #ifdef SYS__sysctl | ||
103 | filter_add_blacklist(fd, SYS__sysctl, 0); | ||
104 | #endif | ||
105 | #ifdef SYS_adjtimex | ||
106 | filter_add_blacklist(fd, SYS_adjtimex, 0); | ||
107 | #endif | ||
108 | #ifdef SYS_clock_adjtime | ||
109 | filter_add_blacklist(fd, SYS_clock_adjtime, 0); | ||
110 | #endif | ||
111 | #ifdef SYS_lookup_dcookie | ||
112 | filter_add_blacklist(fd, SYS_lookup_dcookie, 0); | ||
113 | #endif | ||
114 | #ifdef SYS_perf_event_open | ||
115 | filter_add_blacklist(fd, SYS_perf_event_open, 0); | ||
116 | #endif | ||
117 | #ifdef SYS_fanotify_init | ||
118 | filter_add_blacklist(fd, SYS_fanotify_init, 0); | ||
119 | #endif | ||
120 | #ifdef SYS_kcmp | ||
121 | filter_add_blacklist(fd, SYS_kcmp, 0); | ||
122 | #endif | ||
123 | #ifdef SYS_add_key | ||
124 | filter_add_blacklist(fd, SYS_add_key, 0); | ||
125 | #endif | ||
126 | #ifdef SYS_request_key | ||
127 | filter_add_blacklist(fd, SYS_request_key, 0); | ||
128 | #endif | ||
129 | #ifdef SYS_keyctl | ||
130 | filter_add_blacklist(fd, SYS_keyctl, 0); | ||
131 | #endif | ||
132 | #ifdef SYS_uselib | ||
133 | filter_add_blacklist(fd, SYS_uselib, 0); | ||
134 | #endif | ||
135 | #ifdef SYS_acct | ||
136 | filter_add_blacklist(fd, SYS_acct, 0); | ||
137 | #endif | ||
138 | #ifdef SYS_modify_ldt | ||
139 | filter_add_blacklist(fd, SYS_modify_ldt, 0); | ||
140 | #endif | ||
141 | #ifdef SYS_pivot_root | ||
142 | filter_add_blacklist(fd, SYS_pivot_root, 0); | ||
143 | #endif | ||
144 | #ifdef SYS_io_setup | ||
145 | filter_add_blacklist(fd, SYS_io_setup, 0); | ||
146 | #endif | ||
147 | #ifdef SYS_io_destroy | ||
148 | filter_add_blacklist(fd, SYS_io_destroy, 0); | ||
149 | #endif | ||
150 | #ifdef SYS_io_getevents | ||
151 | filter_add_blacklist(fd, SYS_io_getevents, 0); | ||
152 | #endif | ||
153 | #ifdef SYS_io_submit | ||
154 | filter_add_blacklist(fd, SYS_io_submit, 0); | ||
155 | #endif | ||
156 | #ifdef SYS_io_cancel | ||
157 | filter_add_blacklist(fd, SYS_io_cancel, 0); | ||
158 | #endif | ||
159 | #ifdef SYS_remap_file_pages | ||
160 | filter_add_blacklist(fd, SYS_remap_file_pages, 0); | ||
161 | #endif | ||
162 | #ifdef SYS_mbind | ||
163 | filter_add_blacklist(fd, SYS_mbind, 0); | ||
164 | #endif | ||
165 | #ifdef SYS_get_mempolicy | ||
166 | filter_add_blacklist(fd, SYS_get_mempolicy, 0); | ||
167 | #endif | ||
168 | #ifdef SYS_set_mempolicy | ||
169 | filter_add_blacklist(fd, SYS_set_mempolicy, 0); | ||
170 | #endif | ||
171 | #ifdef SYS_migrate_pages | ||
172 | filter_add_blacklist(fd, SYS_migrate_pages, 0); | ||
173 | #endif | ||
174 | #ifdef SYS_move_pages | ||
175 | filter_add_blacklist(fd, SYS_move_pages, 0); | ||
176 | #endif | ||
177 | #ifdef SYS_vmsplice | ||
178 | filter_add_blacklist(fd, SYS_vmsplice, 0); | ||
179 | #endif | ||
180 | #ifdef SYS_chroot | ||
181 | filter_add_blacklist(fd, SYS_chroot, 0); | ||
182 | #endif | ||
183 | #ifdef SYS_tuxcall | ||
184 | filter_add_blacklist(fd, SYS_tuxcall, 0); | ||
185 | #endif | ||
186 | #ifdef SYS_reboot | ||
187 | filter_add_blacklist(fd, SYS_reboot, 0); | ||
188 | #endif | ||
189 | #ifdef SYS_nfsservctl | ||
190 | filter_add_blacklist(fd, SYS_nfsservctl, 0); | ||
191 | #endif | ||
192 | #ifdef SYS_get_kernel_syms | ||
193 | filter_add_blacklist(fd, SYS_get_kernel_syms, 0); | ||
194 | #endif | ||
195 | } | ||
196 | |||
197 | // default list | ||
198 | void seccomp_default(const char *fname, int allow_debuggers) { | ||
199 | assert(fname); | ||
200 | |||
201 | // open file | ||
202 | int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
203 | if (fd < 0) { | ||
204 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
205 | exit(1); | ||
206 | } | ||
207 | |||
208 | // build filter | ||
209 | filter_init(fd); | ||
210 | add_default_list(fd, allow_debuggers); | ||
211 | filter_end_blacklist(fd); | ||
212 | |||
213 | // close file | ||
214 | close(fd); | ||
215 | } | ||
216 | |||
217 | // drop list | ||
218 | void seccomp_drop(const char *fname, char *list, int allow_debuggers) { | ||
219 | assert(fname); | ||
220 | (void) allow_debuggers; // todo: to implemnet it | ||
221 | |||
222 | // open file | ||
223 | int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
224 | if (fd < 0) { | ||
225 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
226 | exit(1); | ||
227 | } | ||
228 | |||
229 | // build filter | ||
230 | filter_init(fd); | ||
231 | if (syscall_check_list(list, filter_add_blacklist, fd, 0)) { | ||
232 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); | ||
233 | exit(1); | ||
234 | } | ||
235 | filter_end_blacklist(fd); | ||
236 | |||
237 | // close file | ||
238 | close(fd); | ||
239 | } | ||
240 | |||
241 | // default+drop | ||
242 | void seccomp_default_drop(const char *fname, char *list, int allow_debuggers) { | ||
243 | assert(fname); | ||
244 | |||
245 | // open file | ||
246 | int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
247 | if (fd < 0) { | ||
248 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
249 | exit(1); | ||
250 | } | ||
251 | |||
252 | // build filter | ||
253 | filter_init(fd); | ||
254 | add_default_list(fd, allow_debuggers); | ||
255 | if (syscall_check_list(list, filter_add_blacklist, fd, 0)) { | ||
256 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); | ||
257 | exit(1); | ||
258 | } | ||
259 | filter_end_blacklist(fd); | ||
260 | |||
261 | // close file | ||
262 | close(fd); | ||
263 | } | ||
264 | |||
265 | void seccomp_keep(const char *fname, char *list) { | ||
266 | // open file | ||
267 | int fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
268 | if (fd < 0) { | ||
269 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
270 | exit(1); | ||
271 | } | ||
272 | |||
273 | // build filter | ||
274 | filter_init(fd); | ||
275 | // these 4 syscalls are used by firejail after the seccomp filter is initialized | ||
276 | filter_add_whitelist(fd, SYS_setuid, 0); | ||
277 | filter_add_whitelist(fd, SYS_setgid, 0); | ||
278 | filter_add_whitelist(fd, SYS_setgroups, 0); | ||
279 | filter_add_whitelist(fd, SYS_dup, 0); | ||
280 | filter_add_whitelist(fd, SYS_prctl, 0); | ||
281 | |||
282 | if (syscall_check_list(list, filter_add_whitelist, fd, 0)) { | ||
283 | fprintf(stderr, "Error fseccomp: cannot build seccomp filter\n"); | ||
284 | exit(1); | ||
285 | } | ||
286 | |||
287 | filter_end_whitelist(fd); | ||
288 | |||
289 | // close file | ||
290 | close(fd); | ||
291 | } | ||
292 | |||
diff --git a/src/fseccomp/seccomp_file.c b/src/fseccomp/seccomp_file.c new file mode 100644 index 000000000..10ef9dd31 --- /dev/null +++ b/src/fseccomp/seccomp_file.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | #include "../include/seccomp.h" | ||
22 | #include <sys/syscall.h> | ||
23 | |||
24 | static void write_to_file(int fd, void *data, int size) { | ||
25 | assert(data); | ||
26 | assert(size); | ||
27 | |||
28 | int written = 0; | ||
29 | while (written < size) { | ||
30 | int rv = write(fd, (unsigned char *) data + written, size - written); | ||
31 | if (rv == -1) { | ||
32 | fprintf(stderr, "Error fseccomp: cannot write seccomp file\n"); | ||
33 | exit(1); | ||
34 | } | ||
35 | written += rv; | ||
36 | } | ||
37 | } | ||
38 | |||
39 | void filter_init(int fd) { | ||
40 | #if defined(__x86_64__) | ||
41 | #define X32_SYSCALL_BIT 0x40000000 | ||
42 | struct sock_filter filter[] = { | ||
43 | VALIDATE_ARCHITECTURE, | ||
44 | EXAMINE_SYSCALL, | ||
45 | // handle X32 ABI | ||
46 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, X32_SYSCALL_BIT, 1, 0), | ||
47 | BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, 0, 1, 0), | ||
48 | RETURN_ERRNO(EPERM) | ||
49 | }; | ||
50 | #else | ||
51 | struct sock_filter filter[] = { | ||
52 | VALIDATE_ARCHITECTURE, | ||
53 | EXAMINE_SYSCALL | ||
54 | }; | ||
55 | #endif | ||
56 | |||
57 | #if 0 | ||
58 | { | ||
59 | int i; | ||
60 | unsigned char *ptr = (unsigned char *) &filter[0]; | ||
61 | for (i = 0; i < sizeof(filter); i++, ptr++) | ||
62 | printf("%x, ", (*ptr) & 0xff); | ||
63 | printf("\n"); | ||
64 | } | ||
65 | #endif | ||
66 | |||
67 | write_to_file(fd, filter, sizeof(filter)); | ||
68 | } | ||
69 | |||
70 | void filter_add_whitelist(int fd, int syscall, int arg) { | ||
71 | (void) arg; | ||
72 | |||
73 | struct sock_filter filter[] = { | ||
74 | WHITELIST(syscall) | ||
75 | }; | ||
76 | write_to_file(fd, filter, sizeof(filter)); | ||
77 | } | ||
78 | |||
79 | void filter_add_blacklist(int fd, int syscall, int arg) { | ||
80 | (void) arg; | ||
81 | |||
82 | struct sock_filter filter[] = { | ||
83 | BLACKLIST(syscall) | ||
84 | }; | ||
85 | write_to_file(fd, filter, sizeof(filter)); | ||
86 | } | ||
87 | |||
88 | void filter_add_errno(int fd, int syscall, int arg) { | ||
89 | struct sock_filter filter[] = { | ||
90 | BLACKLIST_ERRNO(syscall, arg) | ||
91 | }; | ||
92 | write_to_file(fd, filter, sizeof(filter)); | ||
93 | } | ||
94 | |||
95 | void filter_end_blacklist(int fd) { | ||
96 | struct sock_filter filter[] = { | ||
97 | RETURN_ALLOW | ||
98 | }; | ||
99 | write_to_file(fd, filter, sizeof(filter)); | ||
100 | } | ||
101 | |||
102 | void filter_end_whitelist(int fd) { | ||
103 | struct sock_filter filter[] = { | ||
104 | KILL_PROCESS | ||
105 | }; | ||
106 | write_to_file(fd, filter, sizeof(filter)); | ||
107 | } | ||
108 | |||
diff --git a/src/fseccomp/seccomp_print.c b/src/fseccomp/seccomp_print.c new file mode 100644 index 000000000..7dc983b12 --- /dev/null +++ b/src/fseccomp/seccomp_print.c | |||
@@ -0,0 +1,116 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | #include "../include/seccomp.h" | ||
22 | #include <sys/syscall.h> | ||
23 | |||
24 | static struct sock_filter *filter = NULL; | ||
25 | static int filter_cnt = 0; | ||
26 | |||
27 | static void load_seccomp(const char *fname) { | ||
28 | assert(fname); | ||
29 | |||
30 | // check file | ||
31 | struct stat s; | ||
32 | if (stat(fname, &s) == -1) { | ||
33 | fprintf(stderr, "Error fseccomp: cannot read protocol filter file\n"); | ||
34 | exit(1); | ||
35 | } | ||
36 | int size = s.st_size; | ||
37 | unsigned short entries = (unsigned short) size / (unsigned short) sizeof(struct sock_filter); | ||
38 | filter_cnt = entries; | ||
39 | //printf("size %d, entries %d\n", s.st_size, entries); | ||
40 | |||
41 | filter = malloc(sizeof(struct sock_filter) * entries); | ||
42 | if (!filter) | ||
43 | errExit("malloc"); | ||
44 | |||
45 | // read filter | ||
46 | memset(filter, 0, sizeof(struct sock_filter) * entries); | ||
47 | int src = open(fname, O_RDONLY); | ||
48 | int rd = 0; | ||
49 | while (rd < size) { | ||
50 | int rv = read(src, (unsigned char *) filter + rd, size - rd); | ||
51 | if (rv == -1) { | ||
52 | fprintf(stderr, "Error fseccomp: cannot read %s file\n", fname); | ||
53 | exit(1); | ||
54 | } | ||
55 | rd += rv; | ||
56 | } | ||
57 | close(src); | ||
58 | } | ||
59 | |||
60 | // debug filter | ||
61 | void filter_print(const char *fname) { | ||
62 | assert(fname); | ||
63 | load_seccomp(fname); | ||
64 | |||
65 | // start filter | ||
66 | struct sock_filter start[] = { | ||
67 | VALIDATE_ARCHITECTURE, | ||
68 | EXAMINE_SYSCALL | ||
69 | }; | ||
70 | |||
71 | // print sizes | ||
72 | printf("SECCOMP Filter:\n"); | ||
73 | |||
74 | // test the start of the filter | ||
75 | if (memcmp(&start[0], filter, sizeof(start)) == 0) { | ||
76 | printf(" VALIDATE_ARCHITECTURE\n"); | ||
77 | printf(" EXAMINE_SYSCAL\n"); | ||
78 | } | ||
79 | else { | ||
80 | printf("Invalid seccomp filter %s\n", fname); | ||
81 | return; | ||
82 | } | ||
83 | |||
84 | // loop trough blacklists | ||
85 | int i = 4; | ||
86 | while (i < filter_cnt) { | ||
87 | // minimal parsing! | ||
88 | unsigned char *ptr = (unsigned char *) &filter[i]; | ||
89 | int *nr = (int *) (ptr + 4); | ||
90 | if (*ptr == 0x15 && *(ptr +14) == 0xff && *(ptr + 15) == 0x7f ) { | ||
91 | printf(" WHITELIST %d %s\n", *nr, syscall_find_nr(*nr)); | ||
92 | i += 2; | ||
93 | } | ||
94 | else if (*ptr == 0x15 && *(ptr +14) == 0 && *(ptr + 15) == 0) { | ||
95 | printf(" BLACKLIST %d %s\n", *nr, syscall_find_nr(*nr)); | ||
96 | i += 2; | ||
97 | } | ||
98 | else if (*ptr == 0x15 && *(ptr +14) == 0x5 && *(ptr + 15) == 0) { | ||
99 | int err = *(ptr + 13) << 8 | *(ptr + 12); | ||
100 | printf(" ERRNO %d %s %d %s\n", *nr, syscall_find_nr(*nr), err, errno_find_nr(err)); | ||
101 | i += 2; | ||
102 | } | ||
103 | else if (*ptr == 0x06 && *(ptr +6) == 0 && *(ptr + 7) == 0 ) { | ||
104 | printf(" KILL_PROCESS\n"); | ||
105 | i++; | ||
106 | } | ||
107 | else if (*ptr == 0x06 && *(ptr +6) == 0xff && *(ptr + 7) == 0x7f ) { | ||
108 | printf(" RETURN_ALLOW\n"); | ||
109 | i++; | ||
110 | } | ||
111 | else { | ||
112 | printf(" UNKNOWN ENTRY!!!\n"); | ||
113 | i++; | ||
114 | } | ||
115 | } | ||
116 | } | ||
diff --git a/src/fseccomp/seccomp_secondary.c b/src/fseccomp/seccomp_secondary.c new file mode 100644 index 000000000..a856e5aef --- /dev/null +++ b/src/fseccomp/seccomp_secondary.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | #include "../include/seccomp.h" | ||
22 | #include <sys/syscall.h> | ||
23 | |||
24 | void seccomp_secondary_64(const char *fname) { | ||
25 | // hardcoded syscall values | ||
26 | struct sock_filter filter[] = { | ||
27 | VALIDATE_ARCHITECTURE_64, | ||
28 | EXAMINE_SYSCALL, | ||
29 | BLACKLIST(165), // mount | ||
30 | BLACKLIST(166), // umount2 | ||
31 | // todo: implement --allow-debuggers | ||
32 | BLACKLIST(101), // ptrace | ||
33 | BLACKLIST(246), // kexec_load | ||
34 | BLACKLIST(304), // open_by_handle_at | ||
35 | BLACKLIST(303), // name_to_handle_at | ||
36 | BLACKLIST(174), // create_module | ||
37 | BLACKLIST(175), // init_module | ||
38 | BLACKLIST(313), // finit_module | ||
39 | BLACKLIST(176), // delete_module | ||
40 | BLACKLIST(172), // iopl | ||
41 | BLACKLIST(173), // ioperm | ||
42 | BLACKLIST(251), // ioprio_set | ||
43 | BLACKLIST(167), // swapon | ||
44 | BLACKLIST(168), // swapoff | ||
45 | BLACKLIST(103), // syslog | ||
46 | BLACKLIST(310), // process_vm_readv | ||
47 | BLACKLIST(311), // process_vm_writev | ||
48 | BLACKLIST(139), // sysfs | ||
49 | BLACKLIST(156), // _sysctl | ||
50 | BLACKLIST(159), // adjtimex | ||
51 | BLACKLIST(305), // clock_adjtime | ||
52 | BLACKLIST(212), // lookup_dcookie | ||
53 | BLACKLIST(298), // perf_event_open | ||
54 | BLACKLIST(300), // fanotify_init | ||
55 | BLACKLIST(312), // kcmp | ||
56 | BLACKLIST(248), // add_key | ||
57 | BLACKLIST(249), // request_key | ||
58 | BLACKLIST(250), // keyctl | ||
59 | BLACKLIST(134), // uselib | ||
60 | BLACKLIST(163), // acct | ||
61 | BLACKLIST(154), // modify_ldt | ||
62 | BLACKLIST(155), // pivot_root | ||
63 | BLACKLIST(206), // io_setup | ||
64 | BLACKLIST(207), // io_destroy | ||
65 | BLACKLIST(208), // io_getevents | ||
66 | BLACKLIST(209), // io_submit | ||
67 | BLACKLIST(210), // io_cancel | ||
68 | BLACKLIST(216), // remap_file_pages | ||
69 | BLACKLIST(237), // mbind | ||
70 | BLACKLIST(239), // get_mempolicy | ||
71 | BLACKLIST(238), // set_mempolicy | ||
72 | BLACKLIST(256), // migrate_pages | ||
73 | BLACKLIST(279), // move_pages | ||
74 | BLACKLIST(278), // vmsplice | ||
75 | BLACKLIST(161), // chroot | ||
76 | BLACKLIST(184), // tuxcall | ||
77 | BLACKLIST(169), // reboot | ||
78 | BLACKLIST(180), // nfsservctl | ||
79 | BLACKLIST(177), // get_kernel_syms | ||
80 | |||
81 | RETURN_ALLOW | ||
82 | }; | ||
83 | |||
84 | // save filter to file | ||
85 | int dst = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
86 | if (dst < 0) { | ||
87 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
88 | exit(1); | ||
89 | } | ||
90 | |||
91 | int size = (int) sizeof(filter); | ||
92 | int written = 0; | ||
93 | while (written < size) { | ||
94 | int rv = write(dst, (unsigned char *) filter + written, size - written); | ||
95 | if (rv == -1) { | ||
96 | fprintf(stderr, "Error fseccomp: cannot write %s file\n", fname); | ||
97 | exit(1); | ||
98 | } | ||
99 | written += rv; | ||
100 | } | ||
101 | close(dst); | ||
102 | } | ||
103 | |||
104 | // i386 filter installed on amd64 architectures | ||
105 | void seccomp_secondary_32(const char *fname) { | ||
106 | // hardcoded syscall values | ||
107 | struct sock_filter filter[] = { | ||
108 | VALIDATE_ARCHITECTURE_32, | ||
109 | EXAMINE_SYSCALL, | ||
110 | BLACKLIST(21), // mount | ||
111 | BLACKLIST(52), // umount2 | ||
112 | // todo: implement --allow-debuggers | ||
113 | BLACKLIST(26), // ptrace | ||
114 | BLACKLIST(283), // kexec_load | ||
115 | BLACKLIST(341), // name_to_handle_at | ||
116 | BLACKLIST(342), // open_by_handle_at | ||
117 | BLACKLIST(127), // create_module | ||
118 | BLACKLIST(128), // init_module | ||
119 | BLACKLIST(350), // finit_module | ||
120 | BLACKLIST(129), // delete_module | ||
121 | BLACKLIST(110), // iopl | ||
122 | BLACKLIST(101), // ioperm | ||
123 | BLACKLIST(289), // ioprio_set | ||
124 | BLACKLIST(87), // swapon | ||
125 | BLACKLIST(115), // swapoff | ||
126 | BLACKLIST(103), // syslog | ||
127 | BLACKLIST(347), // process_vm_readv | ||
128 | BLACKLIST(348), // process_vm_writev | ||
129 | BLACKLIST(135), // sysfs | ||
130 | BLACKLIST(149), // _sysctl | ||
131 | BLACKLIST(124), // adjtimex | ||
132 | BLACKLIST(343), // clock_adjtime | ||
133 | BLACKLIST(253), // lookup_dcookie | ||
134 | BLACKLIST(336), // perf_event_open | ||
135 | BLACKLIST(338), // fanotify_init | ||
136 | BLACKLIST(349), // kcmp | ||
137 | BLACKLIST(286), // add_key | ||
138 | BLACKLIST(287), // request_key | ||
139 | BLACKLIST(288), // keyctl | ||
140 | BLACKLIST(86), // uselib | ||
141 | BLACKLIST(51), // acct | ||
142 | BLACKLIST(123), // modify_ldt | ||
143 | BLACKLIST(217), // pivot_root | ||
144 | BLACKLIST(245), // io_setup | ||
145 | BLACKLIST(246), // io_destroy | ||
146 | BLACKLIST(247), // io_getevents | ||
147 | BLACKLIST(248), // io_submit | ||
148 | BLACKLIST(249), // io_cancel | ||
149 | BLACKLIST(257), // remap_file_pages | ||
150 | BLACKLIST(274), // mbind | ||
151 | BLACKLIST(275), // get_mempolicy | ||
152 | BLACKLIST(276), // set_mempolicy | ||
153 | BLACKLIST(294), // migrate_pages | ||
154 | BLACKLIST(317), // move_pages | ||
155 | BLACKLIST(316), // vmsplice | ||
156 | BLACKLIST(61), // chroot | ||
157 | BLACKLIST(88), // reboot | ||
158 | BLACKLIST(169), // nfsservctl | ||
159 | BLACKLIST(130), // get_kernel_syms | ||
160 | |||
161 | RETURN_ALLOW | ||
162 | }; | ||
163 | |||
164 | // save filter to file | ||
165 | int dst = open(fname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
166 | if (dst < 0) { | ||
167 | fprintf(stderr, "Error fseccomp: cannot open %s file\n", fname); | ||
168 | exit(1); | ||
169 | } | ||
170 | |||
171 | int size = (int) sizeof(filter); | ||
172 | int written = 0; | ||
173 | while (written < size) { | ||
174 | int rv = write(dst, (unsigned char *) filter + written, size - written); | ||
175 | if (rv == -1) { | ||
176 | fprintf(stderr, "Error fseccomp: cannot write %s file\n", fname); | ||
177 | exit(1); | ||
178 | } | ||
179 | written += rv; | ||
180 | } | ||
181 | close(dst); | ||
182 | } | ||
183 | |||
diff --git a/src/fseccomp/syscall.c b/src/fseccomp/syscall.c new file mode 100644 index 000000000..e2052efde --- /dev/null +++ b/src/fseccomp/syscall.c | |||
@@ -0,0 +1,110 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014-2016 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 "fseccomp.h" | ||
21 | #include <sys/syscall.h> | ||
22 | |||
23 | typedef struct { | ||
24 | char *name; | ||
25 | int nr; | ||
26 | } SyscallEntry; | ||
27 | |||
28 | static SyscallEntry syslist[] = { | ||
29 | // | ||
30 | // code generated using tools/extract-syscall | ||
31 | // | ||
32 | #include "../include/syscall.h" | ||
33 | // | ||
34 | // end of generated code | ||
35 | // | ||
36 | }; // end of syslist | ||
37 | |||
38 | // return -1 if error, or syscall number | ||
39 | int syscall_find_name(const char *name) { | ||
40 | int i; | ||
41 | int elems = sizeof(syslist) / sizeof(syslist[0]); | ||
42 | for (i = 0; i < elems; i++) { | ||
43 | if (strcmp(name, syslist[i].name) == 0) | ||
44 | return syslist[i].nr; | ||
45 | } | ||
46 | |||
47 | return -1; | ||
48 | } | ||
49 | |||
50 | char *syscall_find_nr(int nr) { | ||
51 | int i; | ||
52 | int elems = sizeof(syslist) / sizeof(syslist[0]); | ||
53 | for (i = 0; i < elems; i++) { | ||
54 | if (nr == syslist[i].nr) | ||
55 | return syslist[i].name; | ||
56 | } | ||
57 | |||
58 | return "unknown"; | ||
59 | } | ||
60 | |||
61 | void syscall_print(void) { | ||
62 | int i; | ||
63 | int elems = sizeof(syslist) / sizeof(syslist[0]); | ||
64 | for (i = 0; i < elems; i++) { | ||
65 | printf("%d\t- %s\n", syslist[i].nr, syslist[i].name); | ||
66 | } | ||
67 | printf("\n"); | ||
68 | } | ||
69 | |||
70 | // return 1 if error, 0 if OK | ||
71 | int syscall_check_list(const char *slist, void (*callback)(int fd, int syscall, int arg), int fd, int arg) { | ||
72 | // don't allow empty lists | ||
73 | if (slist == NULL || *slist == '\0') { | ||
74 | fprintf(stderr, "Error: empty syscall lists are not allowed\n"); | ||
75 | return -1; | ||
76 | } | ||
77 | |||
78 | // work on a copy of the string | ||
79 | char *str = strdup(slist); | ||
80 | if (!str) | ||
81 | errExit("strdup"); | ||
82 | |||
83 | char *ptr = str; | ||
84 | char *start = str; | ||
85 | while (*ptr != '\0') { | ||
86 | if (islower(*ptr) || isdigit(*ptr) || *ptr == '_') | ||
87 | ; | ||
88 | else if (*ptr == ',') { | ||
89 | *ptr = '\0'; | ||
90 | int nr = syscall_find_name(start); | ||
91 | if (nr == -1) | ||
92 | fprintf(stderr, "Warning: syscall %s not found\n", start); | ||
93 | else if (callback != NULL) | ||
94 | callback(fd, nr, arg); | ||
95 | |||
96 | start = ptr + 1; | ||
97 | } | ||
98 | ptr++; | ||
99 | } | ||
100 | if (*start != '\0') { | ||
101 | int nr = syscall_find_name(start); | ||
102 | if (nr == -1) | ||
103 | fprintf(stderr, "Warning: syscall %s not found\n", start); | ||
104 | else if (callback != NULL) | ||
105 | callback(fd, nr, arg); | ||
106 | } | ||
107 | |||
108 | free(str); | ||
109 | return 0; | ||
110 | } | ||