]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/fhs.c
macros: Define tmpfilesdir
[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
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
180static 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
216ERROR:
217 ERROR(pakfire, "Could not find FHS entry for %s: %m\n", path);
218
219 return NULL;
220}
221
a2f877ba
MT
222static 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 239static 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
260static 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
292static 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
313int 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}