]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/fhs.c
FHS: Perform world writable check only for regular 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 },
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) {
15c1f85e
MT
224 // Run this check only for regular files
225 switch (pakfire_file_get_type(file)) {
226 case S_IFREG:
227 break;
228
229 default:
230 return 0;
231 }
232
a2f877ba
MT
233 // Fetch path
234 const char* path = pakfire_file_get_path(file);
235
236 // Fetch permissions
237 const mode_t perms = pakfire_file_get_perms(file);
238
239 // Check that none of the executable bits are set
240 if ((perms & (S_IWUSR|S_IWGRP|S_IWOTH)) == (S_IWUSR|S_IWGRP|S_IWOTH)) {
241 DEBUG(pakfire, "%s is world-writable\n", path);
242 return 1;
243 }
244
245 return 0;
246}
247
84a9a7b7 248static int pakfire_fhs_check_perms(struct pakfire* pakfire,
0c931ee9 249 const struct pakfire_fhs_check* check, struct pakfire_file* file) {
84a9a7b7
MT
250 // No permissions defined. Skipping check...
251 if (!check->perms)
0c931ee9
MT
252 return 0;
253
254 const char* path = pakfire_file_get_path(file);
255
84a9a7b7
MT
256 // Fetch perms
257 const mode_t perms = pakfire_file_get_perms(file);
0c931ee9 258
84a9a7b7
MT
259 // Check if they match
260 if (check->perms != perms) {
261 DEBUG(pakfire, "%s: Permissions do not match\n", path);
262 return 1;
0c931ee9
MT
263 }
264
265 // Check passed
266 return 0;
267}
268
26f2d651
MT
269static int pakfire_fhs_check_ownership(struct pakfire* pakfire,
270 const struct pakfire_fhs_check* check, struct pakfire_file* file) {
271 const char* path = pakfire_file_get_path(file);
272
273 // Check uname
274 if (check->uname) {
275 const char* uname = pakfire_file_get_uname(file);
276 if (!uname)
277 return 1;
278
279 if (strcmp(check->uname, uname) != 0) {
280 DEBUG(pakfire, "%s: uname does not match\n", path);
281 return 1;
282 }
283 }
284
285 // Check gname
286 if (check->gname) {
287 const char* gname = pakfire_file_get_gname(file);
288 if (!gname)
289 return 1;
290
291 if (strcmp(check->gname, gname) != 0) {
292 DEBUG(pakfire, "%s: gname does not match\n", path);
293 return 1;
294 }
295 }
296
297 // Pass
298 return 0;
299}
300
760e1615
MT
301static int pakfire_fhs_check_noexec(struct pakfire* pakfire,
302 const struct pakfire_fhs_check* check, struct pakfire_file* file) {
303 // Skip this check if PAKFIRE_FHS_NOEXEC is not set
304 if (!(check->flags & PAKFIRE_FHS_NOEXEC))
305 return 0;
306
307 // Fetch path
308 const char* path = pakfire_file_get_path(file);
309
310 // Fetch permissions
311 const mode_t perms = pakfire_file_get_perms(file);
312
313 // Check that none of the executable bits are set
314 if (perms & (S_IXUSR|S_IXGRP|S_IXOTH)) {
315 DEBUG(pakfire, "%s must not be executable\n", path);
316 return 1;
317 }
318
319 return 0;
320}
321
0c931ee9
MT
322int pakfire_fhs_check_file(struct pakfire* pakfire, struct pakfire_file* file) {
323 const struct pakfire_fhs_check* check = NULL;
324 int r;
325
326 // Get the file path
327 const char* path = pakfire_file_get_path(file);
328 if (!path)
329 return 1;
330
a2f877ba
MT
331 // Check for world-writable permissions
332 r = pakfire_fhs_check_world_writable(pakfire, file);
333 if (r)
334 return r;
335
0c931ee9 336 // Find a check
84a9a7b7 337 check = pakfire_fhs_find_check(pakfire, file);
0c931ee9
MT
338 if (!check) {
339 ERROR(pakfire, "Could not match file %s: %m\n", path);
340 return 1;
341 }
342
343 // Should this file exist at all?
344 if (check->flags & PAKFIRE_FHS_MUSTNOTEXIST) {
345 ERROR(pakfire, "%s must not exist here\n", path);
346 return 1;
347 }
348
84a9a7b7
MT
349 // Check permissions
350 r = pakfire_fhs_check_perms(pakfire, check, file);
0c931ee9
MT
351 if (r)
352 return r;
353
26f2d651
MT
354 // Check ownership
355 r = pakfire_fhs_check_ownership(pakfire, check, file);
356 if (r)
357 return r;
358
760e1615
MT
359 // Check for PAKFIRE_FHS_NOEXEC
360 r = pakfire_fhs_check_noexec(pakfire, check, file);
361 if (r)
362 return r;
363
0c931ee9
MT
364 // Check passed!
365 return 0;
366}