]>
Commit | Line | Data |
---|---|---|
0c931ee9 MT |
1 | /*############################################################################# |
2 | # # | |
3 | # Pakfire - The IPFire package management system # | |
4 | # Copyright (C) 2021 Pakfire development team # | |
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 3 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 # | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
18 | # # | |
19 | #############################################################################*/ | |
20 | ||
21 | #include <sys/stat.h> | |
22 | ||
23 | #include <pakfire/fhs.h> | |
24 | #include <pakfire/file.h> | |
25 | #include <pakfire/logging.h> | |
26 | #include <pakfire/pakfire.h> | |
27 | #include <pakfire/util.h> | |
28 | ||
29 | /* | |
30 | This struct defines any FHS checks. | |
31 | ||
32 | They are being processed in order from top to bottom which is why we are starting | |
33 | with some more prominent matches and have the less important stuff at the bottom. | |
34 | */ | |
35 | static const struct pakfire_fhs_check { | |
36 | const char* path; | |
84a9a7b7 | 37 | const mode_t type; |
84a9a7b7 | 38 | const mode_t perms; |
26f2d651 MT |
39 | const char* uname; |
40 | const char* gname; | |
08ec3fc4 MT |
41 | enum pakfire_fhs_check_flags { |
42 | PAKFIRE_FHS_MUSTNOTEXIST = (1 << 0), | |
760e1615 | 43 | PAKFIRE_FHS_NOEXEC = (1 << 1), |
08ec3fc4 | 44 | } flags; |
0c931ee9 MT |
45 | } pakfire_fhs_check[] = { |
46 | // /usr | |
21bd82b0 MT |
47 | { "/usr", S_IFDIR, 0755, "root", "root", 0 }, |
48 | { "/usr/bin", S_IFDIR, 0755, "root", "root", 0 }, | |
49 | { "/usr/include", S_IFDIR, 0755, "root", "root", 0 }, | |
50 | { "/usr/lib", S_IFDIR, 0755, "root", "root", 0 }, | |
51 | { "/usr/lib64", S_IFDIR, 0755, "root", "root", 0 }, | |
52 | { "/usr/sbin", S_IFDIR, 0755, "root", "root", 0 }, | |
53 | { "/usr/share", S_IFDIR, 0755, "root", "root", 0 }, | |
54 | { "/usr/src", S_IFDIR, 0755, "root", "root", 0 }, | |
0c931ee9 | 55 | |
b3c8a073 | 56 | // Allow no further files in /usr & /usr/src |
21bd82b0 MT |
57 | { "/usr/*", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, |
58 | { "/usr/src/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
b3c8a073 | 59 | |
f6dbfb8f | 60 | // There cannot be any subdirectories in /usr/bin & /usr/sbin |
513dc5a9 MT |
61 | { "/usr/bin/*", S_IFDIR, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, |
62 | { "/usr/sbin/*", S_IFDIR, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
f6dbfb8f | 63 | |
2250db76 | 64 | // Any files in /usr/{,s}bin must be owned by root and have 0755 |
21bd82b0 MT |
65 | { "/usr/bin/*", S_IFREG, 0755, "root", "root", 0 }, |
66 | { "/usr/sbin/*", S_IFREG, 0755, "root", "root", 0 }, | |
2250db76 | 67 | |
ad6be9f7 MT |
68 | // Shared Libraries must be executable |
69 | { "/usr/lib64/*.so.*", S_IFREG, 0755, "root", "root", 0 }, | |
70 | { "/usr/lib64/**/*.so", S_IFREG, 0755, "root", "root", 0 }, | |
71 | ||
72 | // Shared Libraries must not exist in /usr/lib | |
73 | { "/usr/lib/*.so*", S_IFREG, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
74 | ||
b5416a4f MT |
75 | // /usr/include: Ensure that: |
76 | // * All files are non-executable and belong to root | |
77 | // * All directories have 0755 and belong to root | |
21bd82b0 MT |
78 | { "/usr/include/**", S_IFREG, 0644, "root", "root", 0 }, |
79 | { "/usr/include/**", S_IFDIR, 0755, "root", "root", 0 }, | |
80 | ||
81 | // Firmware must not be executable | |
82 | { "/usr/lib/firmware/**", S_IFREG, 0644, "root", "root", 0 }, | |
83 | { "/usr/lib/firmware/**", S_IFDIR, 0755, "root", "root", 0 }, | |
b5416a4f | 84 | |
760e1615 MT |
85 | // /usr/share cannot have any exectuable files |
86 | { "/usr/share/**", S_IFREG, 0, NULL, NULL, PAKFIRE_FHS_NOEXEC }, | |
87 | ||
0c931ee9 | 88 | // /var |
21bd82b0 MT |
89 | { "/var", S_IFDIR, 0755, "root", "root", 0 }, |
90 | { "/var/cache", S_IFDIR, 0755, "root", "root", 0 }, | |
91 | { "/var/db", S_IFDIR, 0755, "root", "root", 0 }, | |
92 | { "/var/empty", S_IFDIR, 0755, "root", "root", 0 }, | |
93 | { "/var/lib", S_IFDIR, 0755, "root", "root", 0 }, | |
94 | { "/var/log", S_IFDIR, 0755, "root", "root", 0 }, | |
95 | { "/var/mail", S_IFDIR, 0755, "root", "root", 0 }, | |
96 | { "/var/opt", S_IFDIR, 0755, "root", "root", 0 }, | |
97 | { "/var/run", S_IFLNK, 0755, "root", "root", 0 }, | |
98 | { "/var/spool", S_IFDIR, 0755, "root", "root", 0 }, | |
99 | { "/var/tmp", S_IFDIR, 0755, "root", "root", 0 }, | |
386b30c2 MT |
100 | |
101 | // Do not allow any subdirectories in /var | |
21bd82b0 MT |
102 | { "/var/*", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, |
103 | { "/var/empty/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
104 | { "/var/tmp/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 | 105 | |
edd297db MT |
106 | // No files in /var may be executable |
107 | { "/var/**", S_IFREG, 0, NULL, NULL, PAKFIRE_FHS_NOEXEC }, | |
108 | ||
0c931ee9 | 109 | // /boot |
21bd82b0 MT |
110 | { "/boot", S_IFDIR, 0755, "root", "root", 0 }, |
111 | { "/boot/efi", S_IFDIR, 0755, "root", "root", 0 }, | |
0c931ee9 | 112 | |
4116016e MT |
113 | // All files in /boot must be owned by root |
114 | { "/boot/**", S_IFREG, 0, "root", "root", 0, }, | |
115 | { "/boot/**", S_IFDIR, 0, "root", "root", 0, }, | |
116 | ||
0c931ee9 | 117 | // /dev (nothing may exist in it) |
21bd82b0 MT |
118 | { "/dev", S_IFDIR, 0755, "root", "root", 0 }, |
119 | { "/dev/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
120 | |
121 | // /etc | |
21bd82b0 | 122 | { "/etc", S_IFDIR, 0755, "root", "root", 0 }, |
0c931ee9 MT |
123 | |
124 | // /home | |
21bd82b0 MT |
125 | { "/home", S_IFDIR, 0755, "root", "root", 0 }, |
126 | { "/home/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
127 | |
128 | // /opt | |
21bd82b0 | 129 | { "/opt", S_IFDIR, 0755, "root", "root", 0 }, |
0c931ee9 MT |
130 | // These directories belong to the "local administrator" |
131 | // https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s13.html | |
21bd82b0 MT |
132 | { "/opt/bin", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, |
133 | { "/opt/doc", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
134 | { "/opt/include", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
135 | { "/opt/info", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
136 | { "/opt/lib", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
137 | { "/opt/man", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
138 | |
139 | // /proc | |
21bd82b0 MT |
140 | { "/proc", S_IFDIR, 0755, "root", "root", 0 }, |
141 | { "/proc/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 | 142 | |
727f3b22 MT |
143 | // root |
144 | { "/root", S_IFDIR, 0700, "root", "root", 0 }, | |
145 | { "/root/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
146 | ||
0c931ee9 | 147 | // /run |
21bd82b0 MT |
148 | { "/run", S_IFDIR, 0755, "root", "root", 0 }, |
149 | { "/run/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
150 | |
151 | // /sys | |
21bd82b0 MT |
152 | { "/sys", S_IFDIR, 0755, "root", "root", 0 }, |
153 | { "/sys/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
154 | |
155 | // /tmp | |
21bd82b0 MT |
156 | { "/tmp", S_IFDIR, 1755, "root", "root", 0 }, |
157 | { "/tmp/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
158 | |
159 | // FHS Directories | |
21bd82b0 MT |
160 | { "/media", S_IFDIR, 0755, "root", "root", 0 }, |
161 | { "/mnt", S_IFDIR, 0755, "root", "root", 0 }, | |
162 | { "/srv", S_IFDIR, 0755, "root", "root", 0 }, | |
0c931ee9 MT |
163 | |
164 | // /bin, /sbin, /lib, and /lib64 have to be symlinks | |
21bd82b0 MT |
165 | { "/bin", S_IFLNK, 0777, NULL, NULL, 0 }, |
166 | { "/lib", S_IFLNK, 0777, NULL, NULL, 0 }, | |
167 | { "/lib64", S_IFLNK, 0777, NULL, NULL, 0 }, | |
168 | { "/sbin", S_IFLNK, 0777, NULL, NULL, 0 }, | |
0c931ee9 MT |
169 | |
170 | // There cannot be anything else in / | |
21bd82b0 | 171 | { "/*", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, |
0c931ee9 MT |
172 | |
173 | // Catch all so that we won't throw an error | |
21bd82b0 | 174 | { "/**", 0, 0, NULL, NULL, 0 }, |
0c931ee9 MT |
175 | |
176 | // Sentinel | |
177 | { NULL }, | |
178 | }; | |
179 | ||
180 | static const struct pakfire_fhs_check* pakfire_fhs_find_check( | |
84a9a7b7 | 181 | struct pakfire* pakfire, struct pakfire_file* file) { |
0c931ee9 MT |
182 | const struct pakfire_fhs_check* check = NULL; |
183 | int r; | |
184 | ||
84a9a7b7 MT |
185 | // Fetch the file type |
186 | const mode_t type = pakfire_file_get_type(file); | |
187 | ||
188 | // Fetch the path | |
189 | const char* path = pakfire_file_get_path(file); | |
190 | ||
0c931ee9 MT |
191 | // Walk through all possible checks |
192 | for (check = pakfire_fhs_check; check->path; check++) { | |
84a9a7b7 MT |
193 | // Skip this check if the filetype doesn't match |
194 | if (check->type && check->type != type) | |
195 | continue; | |
196 | ||
197 | // Check path | |
0c931ee9 MT |
198 | r = pakfire_path_match(check->path, path); |
199 | switch (r) { | |
200 | // No match | |
201 | case 0: | |
202 | continue; | |
203 | ||
204 | // Match! | |
205 | case 1: | |
206 | DEBUG(pakfire, "%s matches check '%s'\n", path, check->path); | |
207 | ||
208 | return check; | |
209 | ||
210 | // Error :( | |
211 | default: | |
212 | goto ERROR; | |
213 | } | |
214 | } | |
215 | ||
216 | ERROR: | |
217 | ERROR(pakfire, "Could not find FHS entry for %s: %m\n", path); | |
218 | ||
219 | return NULL; | |
220 | } | |
221 | ||
a2f877ba MT |
222 | static int pakfire_fhs_check_world_writable( |
223 | struct pakfire* pakfire, struct pakfire_file* file) { | |
224 | // Fetch path | |
225 | const char* path = pakfire_file_get_path(file); | |
226 | ||
227 | // Fetch permissions | |
228 | const mode_t perms = pakfire_file_get_perms(file); | |
229 | ||
230 | // Check that none of the executable bits are set | |
231 | if ((perms & (S_IWUSR|S_IWGRP|S_IWOTH)) == (S_IWUSR|S_IWGRP|S_IWOTH)) { | |
232 | DEBUG(pakfire, "%s is world-writable\n", path); | |
233 | return 1; | |
234 | } | |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
84a9a7b7 | 239 | static int pakfire_fhs_check_perms(struct pakfire* pakfire, |
0c931ee9 | 240 | const struct pakfire_fhs_check* check, struct pakfire_file* file) { |
84a9a7b7 MT |
241 | // No permissions defined. Skipping check... |
242 | if (!check->perms) | |
0c931ee9 MT |
243 | return 0; |
244 | ||
245 | const char* path = pakfire_file_get_path(file); | |
246 | ||
84a9a7b7 MT |
247 | // Fetch perms |
248 | const mode_t perms = pakfire_file_get_perms(file); | |
0c931ee9 | 249 | |
84a9a7b7 MT |
250 | // Check if they match |
251 | if (check->perms != perms) { | |
252 | DEBUG(pakfire, "%s: Permissions do not match\n", path); | |
253 | return 1; | |
0c931ee9 MT |
254 | } |
255 | ||
256 | // Check passed | |
257 | return 0; | |
258 | } | |
259 | ||
26f2d651 MT |
260 | static int pakfire_fhs_check_ownership(struct pakfire* pakfire, |
261 | const struct pakfire_fhs_check* check, struct pakfire_file* file) { | |
262 | const char* path = pakfire_file_get_path(file); | |
263 | ||
264 | // Check uname | |
265 | if (check->uname) { | |
266 | const char* uname = pakfire_file_get_uname(file); | |
267 | if (!uname) | |
268 | return 1; | |
269 | ||
270 | if (strcmp(check->uname, uname) != 0) { | |
271 | DEBUG(pakfire, "%s: uname does not match\n", path); | |
272 | return 1; | |
273 | } | |
274 | } | |
275 | ||
276 | // Check gname | |
277 | if (check->gname) { | |
278 | const char* gname = pakfire_file_get_gname(file); | |
279 | if (!gname) | |
280 | return 1; | |
281 | ||
282 | if (strcmp(check->gname, gname) != 0) { | |
283 | DEBUG(pakfire, "%s: gname does not match\n", path); | |
284 | return 1; | |
285 | } | |
286 | } | |
287 | ||
288 | // Pass | |
289 | return 0; | |
290 | } | |
291 | ||
760e1615 MT |
292 | static int pakfire_fhs_check_noexec(struct pakfire* pakfire, |
293 | const struct pakfire_fhs_check* check, struct pakfire_file* file) { | |
294 | // Skip this check if PAKFIRE_FHS_NOEXEC is not set | |
295 | if (!(check->flags & PAKFIRE_FHS_NOEXEC)) | |
296 | return 0; | |
297 | ||
298 | // Fetch path | |
299 | const char* path = pakfire_file_get_path(file); | |
300 | ||
301 | // Fetch permissions | |
302 | const mode_t perms = pakfire_file_get_perms(file); | |
303 | ||
304 | // Check that none of the executable bits are set | |
305 | if (perms & (S_IXUSR|S_IXGRP|S_IXOTH)) { | |
306 | DEBUG(pakfire, "%s must not be executable\n", path); | |
307 | return 1; | |
308 | } | |
309 | ||
310 | return 0; | |
311 | } | |
312 | ||
0c931ee9 MT |
313 | int pakfire_fhs_check_file(struct pakfire* pakfire, struct pakfire_file* file) { |
314 | const struct pakfire_fhs_check* check = NULL; | |
315 | int r; | |
316 | ||
317 | // Get the file path | |
318 | const char* path = pakfire_file_get_path(file); | |
319 | if (!path) | |
320 | return 1; | |
321 | ||
a2f877ba MT |
322 | // Check for world-writable permissions |
323 | r = pakfire_fhs_check_world_writable(pakfire, file); | |
324 | if (r) | |
325 | return r; | |
326 | ||
0c931ee9 | 327 | // Find a check |
84a9a7b7 | 328 | check = pakfire_fhs_find_check(pakfire, file); |
0c931ee9 MT |
329 | if (!check) { |
330 | ERROR(pakfire, "Could not match file %s: %m\n", path); | |
331 | return 1; | |
332 | } | |
333 | ||
334 | // Should this file exist at all? | |
335 | if (check->flags & PAKFIRE_FHS_MUSTNOTEXIST) { | |
336 | ERROR(pakfire, "%s must not exist here\n", path); | |
337 | return 1; | |
338 | } | |
339 | ||
84a9a7b7 MT |
340 | // Check permissions |
341 | r = pakfire_fhs_check_perms(pakfire, check, file); | |
0c931ee9 MT |
342 | if (r) |
343 | return r; | |
344 | ||
26f2d651 MT |
345 | // Check ownership |
346 | r = pakfire_fhs_check_ownership(pakfire, check, file); | |
347 | if (r) | |
348 | return r; | |
349 | ||
760e1615 MT |
350 | // Check for PAKFIRE_FHS_NOEXEC |
351 | r = pakfire_fhs_check_noexec(pakfire, check, file); | |
352 | if (r) | |
353 | return r; | |
354 | ||
0c931ee9 MT |
355 | // Check passed! |
356 | return 0; | |
357 | } |