]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/fhs.c
Hardening: Declare content of /usr/lib/grub as firmware files
[people/stevee/pakfire.git] / src / libpakfire / fhs.c
CommitLineData
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*/
35static 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 },
9cf6f5e5 55 { "/usr/src/kernels", S_IFDIR, 0755, "root", "root", 0 },
0c931ee9 56
fed053b7 57 // Allow no further files in /usr
21bd82b0 58 { "/usr/*", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
fed053b7
MT
59
60 // Allow no files in /usr/src except some kernel source
61 { "/usr/src/kernels/**", 0, 0, "root", "root", 0 },
21bd82b0 62 { "/usr/src/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
b3c8a073 63
f6dbfb8f 64 // There cannot be any subdirectories in /usr/bin & /usr/sbin
513dc5a9
MT
65 { "/usr/bin/*", S_IFDIR, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
66 { "/usr/sbin/*", S_IFDIR, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
f6dbfb8f 67
033373b3 68 // Permitted setuid binaries
35c9d82a
MT
69 { "/usr/bin/gpasswd", S_IFREG, S_ISUID|0755, "root", "root", 0 },
70 { "/usr/bin/ksu", S_IFREG, S_ISUID|0755, "root", "root", 0 },
844d9546 71 { "/usr/bin/passwd", S_IFREG, S_ISUID|0755, "root", "root", 0 },
35c9d82a 72 { "/usr/bin/pkexec", S_IFREG, S_ISUID|0755, "root", "root", 0 },
844d9546 73 { "/usr/bin/sudo", S_IFREG, S_ISUID|0755, "root", "root", 0 },
033373b3 74
2250db76 75 // Any files in /usr/{,s}bin must be owned by root and have 0755
21bd82b0
MT
76 { "/usr/bin/*", S_IFREG, 0755, "root", "root", 0 },
77 { "/usr/sbin/*", S_IFREG, 0755, "root", "root", 0 },
2250db76 78
ad6be9f7
MT
79 // Shared Libraries must be executable
80 { "/usr/lib64/*.so.*", S_IFREG, 0755, "root", "root", 0 },
81 { "/usr/lib64/**/*.so", S_IFREG, 0755, "root", "root", 0 },
82
68501d96
MT
83 // Tolerate runtime linkers in /usr/lib
84 { "/usr/lib/ld-*.so.*", S_IFREG, 0755, "root", "root", 0 },
85
ad6be9f7
MT
86 // Shared Libraries must not exist in /usr/lib
87 { "/usr/lib/*.so*", S_IFREG, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
88
b5416a4f
MT
89 // /usr/include: Ensure that:
90 // * All files are non-executable and belong to root
91 // * All directories have 0755 and belong to root
21bd82b0
MT
92 { "/usr/include/**", S_IFREG, 0644, "root", "root", 0 },
93 { "/usr/include/**", S_IFDIR, 0755, "root", "root", 0 },
94
95 // Firmware must not be executable
96 { "/usr/lib/firmware/**", S_IFREG, 0644, "root", "root", 0 },
97 { "/usr/lib/firmware/**", S_IFDIR, 0755, "root", "root", 0 },
b5416a4f 98
0c931ee9 99 // /var
21bd82b0
MT
100 { "/var", S_IFDIR, 0755, "root", "root", 0 },
101 { "/var/cache", S_IFDIR, 0755, "root", "root", 0 },
102 { "/var/db", S_IFDIR, 0755, "root", "root", 0 },
103 { "/var/empty", S_IFDIR, 0755, "root", "root", 0 },
104 { "/var/lib", S_IFDIR, 0755, "root", "root", 0 },
105 { "/var/log", S_IFDIR, 0755, "root", "root", 0 },
d352b91e 106 { "/var/mail", S_IFDIR, 0755, "root", "mail", 0 },
21bd82b0
MT
107 { "/var/opt", S_IFDIR, 0755, "root", "root", 0 },
108 { "/var/run", S_IFLNK, 0755, "root", "root", 0 },
109 { "/var/spool", S_IFDIR, 0755, "root", "root", 0 },
110 { "/var/tmp", S_IFDIR, 0755, "root", "root", 0 },
386b30c2
MT
111
112 // Do not allow any subdirectories in /var
21bd82b0
MT
113 { "/var/*", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
114 { "/var/empty/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
115 { "/var/tmp/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
0c931ee9 116
edd297db
MT
117 // No files in /var may be executable
118 { "/var/**", S_IFREG, 0, NULL, NULL, PAKFIRE_FHS_NOEXEC },
119
0c931ee9 120 // /boot
21bd82b0
MT
121 { "/boot", S_IFDIR, 0755, "root", "root", 0 },
122 { "/boot/efi", S_IFDIR, 0755, "root", "root", 0 },
0c931ee9 123
4116016e
MT
124 // All files in /boot must be owned by root
125 { "/boot/**", S_IFREG, 0, "root", "root", 0, },
126 { "/boot/**", S_IFDIR, 0, "root", "root", 0, },
127
0c931ee9 128 // /dev (nothing may exist in it)
21bd82b0
MT
129 { "/dev", S_IFDIR, 0755, "root", "root", 0 },
130 { "/dev/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
0c931ee9
MT
131
132 // /etc
21bd82b0 133 { "/etc", S_IFDIR, 0755, "root", "root", 0 },
0c931ee9
MT
134
135 // /home
21bd82b0
MT
136 { "/home", S_IFDIR, 0755, "root", "root", 0 },
137 { "/home/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
0c931ee9
MT
138
139 // /opt
21bd82b0 140 { "/opt", S_IFDIR, 0755, "root", "root", 0 },
0c931ee9
MT
141 // These directories belong to the "local administrator"
142 // https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s13.html
21bd82b0
MT
143 { "/opt/bin", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
144 { "/opt/doc", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
145 { "/opt/include", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
146 { "/opt/info", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
147 { "/opt/lib", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
148 { "/opt/man", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
0c931ee9
MT
149
150 // /proc
21bd82b0
MT
151 { "/proc", S_IFDIR, 0755, "root", "root", 0 },
152 { "/proc/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
0c931ee9 153
727f3b22
MT
154 // root
155 { "/root", S_IFDIR, 0700, "root", "root", 0 },
c905305d 156 { "/root/.*", S_IFREG, 0644, "root", "root", 0 },
727f3b22
MT
157 { "/root/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
158
0c931ee9 159 // /run
21bd82b0
MT
160 { "/run", S_IFDIR, 0755, "root", "root", 0 },
161 { "/run/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
0c931ee9
MT
162
163 // /sys
21bd82b0
MT
164 { "/sys", S_IFDIR, 0755, "root", "root", 0 },
165 { "/sys/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
0c931ee9
MT
166
167 // /tmp
21bd82b0
MT
168 { "/tmp", S_IFDIR, 1755, "root", "root", 0 },
169 { "/tmp/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
0c931ee9
MT
170
171 // FHS Directories
21bd82b0
MT
172 { "/media", S_IFDIR, 0755, "root", "root", 0 },
173 { "/mnt", S_IFDIR, 0755, "root", "root", 0 },
174 { "/srv", S_IFDIR, 0755, "root", "root", 0 },
0c931ee9
MT
175
176 // /bin, /sbin, /lib, and /lib64 have to be symlinks
21bd82b0
MT
177 { "/bin", S_IFLNK, 0777, NULL, NULL, 0 },
178 { "/lib", S_IFLNK, 0777, NULL, NULL, 0 },
179 { "/lib64", S_IFLNK, 0777, NULL, NULL, 0 },
180 { "/sbin", S_IFLNK, 0777, NULL, NULL, 0 },
0c931ee9
MT
181
182 // There cannot be anything else in /
21bd82b0 183 { "/*", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
0c931ee9
MT
184
185 // Catch all so that we won't throw an error
21bd82b0 186 { "/**", 0, 0, NULL, NULL, 0 },
0c931ee9
MT
187
188 // Sentinel
189 { NULL },
190};
191
192static const struct pakfire_fhs_check* pakfire_fhs_find_check(
84a9a7b7 193 struct pakfire* pakfire, struct pakfire_file* file) {
0c931ee9
MT
194 const struct pakfire_fhs_check* check = NULL;
195 int r;
196
84a9a7b7
MT
197 // Fetch the file type
198 const mode_t type = pakfire_file_get_type(file);
199
200 // Fetch the path
201 const char* path = pakfire_file_get_path(file);
202
0c931ee9
MT
203 // Walk through all possible checks
204 for (check = pakfire_fhs_check; check->path; check++) {
84a9a7b7
MT
205 // Skip this check if the filetype doesn't match
206 if (check->type && check->type != type)
207 continue;
208
209 // Check path
0c931ee9
MT
210 r = pakfire_path_match(check->path, path);
211 switch (r) {
212 // No match
213 case 0:
214 continue;
215
216 // Match!
217 case 1:
218 DEBUG(pakfire, "%s matches check '%s'\n", path, check->path);
219
220 return check;
221
222 // Error :(
223 default:
224 goto ERROR;
225 }
226 }
227
228ERROR:
229 ERROR(pakfire, "Could not find FHS entry for %s: %m\n", path);
230
231 return NULL;
232}
233
a2f877ba
MT
234static int pakfire_fhs_check_world_writable(
235 struct pakfire* pakfire, struct pakfire_file* file) {
15c1f85e
MT
236 // Run this check only for regular files
237 switch (pakfire_file_get_type(file)) {
238 case S_IFREG:
239 break;
240
241 default:
242 return 0;
243 }
244
a2f877ba
MT
245 // Fetch path
246 const char* path = pakfire_file_get_path(file);
247
248 // Fetch permissions
249 const mode_t perms = pakfire_file_get_perms(file);
250
251 // Check that none of the executable bits are set
252 if ((perms & (S_IWUSR|S_IWGRP|S_IWOTH)) == (S_IWUSR|S_IWGRP|S_IWOTH)) {
253 DEBUG(pakfire, "%s is world-writable\n", path);
254 return 1;
255 }
256
257 return 0;
258}
259
84a9a7b7 260static int pakfire_fhs_check_perms(struct pakfire* pakfire,
0c931ee9 261 const struct pakfire_fhs_check* check, struct pakfire_file* file) {
84a9a7b7
MT
262 // No permissions defined. Skipping check...
263 if (!check->perms)
0c931ee9
MT
264 return 0;
265
266 const char* path = pakfire_file_get_path(file);
267
84a9a7b7
MT
268 // Fetch perms
269 const mode_t perms = pakfire_file_get_perms(file);
0c931ee9 270
84a9a7b7
MT
271 // Check if they match
272 if (check->perms != perms) {
273 DEBUG(pakfire, "%s: Permissions do not match\n", path);
274 return 1;
0c931ee9
MT
275 }
276
277 // Check passed
278 return 0;
279}
280
26f2d651
MT
281static int pakfire_fhs_check_ownership(struct pakfire* pakfire,
282 const struct pakfire_fhs_check* check, struct pakfire_file* file) {
283 const char* path = pakfire_file_get_path(file);
284
285 // Check uname
286 if (check->uname) {
287 const char* uname = pakfire_file_get_uname(file);
288 if (!uname)
289 return 1;
290
291 if (strcmp(check->uname, uname) != 0) {
292 DEBUG(pakfire, "%s: uname does not match\n", path);
293 return 1;
294 }
295 }
296
297 // Check gname
298 if (check->gname) {
299 const char* gname = pakfire_file_get_gname(file);
300 if (!gname)
301 return 1;
302
303 if (strcmp(check->gname, gname) != 0) {
304 DEBUG(pakfire, "%s: gname does not match\n", path);
305 return 1;
306 }
307 }
308
309 // Pass
310 return 0;
311}
312
760e1615
MT
313static int pakfire_fhs_check_noexec(struct pakfire* pakfire,
314 const struct pakfire_fhs_check* check, struct pakfire_file* file) {
315 // Skip this check if PAKFIRE_FHS_NOEXEC is not set
316 if (!(check->flags & PAKFIRE_FHS_NOEXEC))
317 return 0;
318
319 // Fetch path
320 const char* path = pakfire_file_get_path(file);
321
322 // Fetch permissions
323 const mode_t perms = pakfire_file_get_perms(file);
324
325 // Check that none of the executable bits are set
326 if (perms & (S_IXUSR|S_IXGRP|S_IXOTH)) {
327 DEBUG(pakfire, "%s must not be executable\n", path);
328 return 1;
329 }
330
331 return 0;
332}
333
0c931ee9
MT
334int pakfire_fhs_check_file(struct pakfire* pakfire, struct pakfire_file* file) {
335 const struct pakfire_fhs_check* check = NULL;
336 int r;
337
338 // Get the file path
339 const char* path = pakfire_file_get_path(file);
340 if (!path)
341 return 1;
342
a2f877ba
MT
343 // Check for world-writable permissions
344 r = pakfire_fhs_check_world_writable(pakfire, file);
345 if (r)
346 return r;
347
0c931ee9 348 // Find a check
84a9a7b7 349 check = pakfire_fhs_find_check(pakfire, file);
0c931ee9
MT
350 if (!check) {
351 ERROR(pakfire, "Could not match file %s: %m\n", path);
352 return 1;
353 }
354
355 // Should this file exist at all?
356 if (check->flags & PAKFIRE_FHS_MUSTNOTEXIST) {
225accad 357 DEBUG(pakfire, "%s must not exist here\n", path);
0c931ee9
MT
358 return 1;
359 }
360
84a9a7b7
MT
361 // Check permissions
362 r = pakfire_fhs_check_perms(pakfire, check, file);
0c931ee9
MT
363 if (r)
364 return r;
365
26f2d651
MT
366 // Check ownership
367 r = pakfire_fhs_check_ownership(pakfire, check, file);
368 if (r)
369 return r;
370
760e1615
MT
371 // Check for PAKFIRE_FHS_NOEXEC
372 r = pakfire_fhs_check_noexec(pakfire, check, file);
373 if (r)
374 return r;
375
0c931ee9
MT
376 // Check passed!
377 return 0;
378}