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