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