]> git.ipfire.org Git - people/stevee/pakfire.git/blob - src/libpakfire/fhs.c
FHS: Add /root
[people/stevee/pakfire.git] / src / libpakfire / fhs.c
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 */
35 static const struct pakfire_fhs_check {
36 const char* path;
37 const mode_t type;
38 const mode_t perms;
39 const char* uname;
40 const char* gname;
41 enum pakfire_fhs_check_flags {
42 PAKFIRE_FHS_MUSTNOTEXIST = (1 << 0),
43 PAKFIRE_FHS_NOEXEC = (1 << 1),
44 } flags;
45 } pakfire_fhs_check[] = {
46 // /usr
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 },
55
56 // Allow no further files in /usr & /usr/src
57 { "/usr/*", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
58 { "/usr/src/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
59
60 // There cannot be any subdirectories in /usr/bin & /usr/sbin
61 { "/usr/bin/*", S_IFDIR, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
62 { "/usr/sbin/*", S_IFDIR, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
63
64 // Any files in /usr/{,s}bin must be owned by root and have 0755
65 { "/usr/bin/*", S_IFREG, 0755, "root", "root", 0 },
66 { "/usr/sbin/*", S_IFREG, 0755, "root", "root", 0 },
67
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
75 // /usr/include: Ensure that:
76 // * All files are non-executable and belong to root
77 // * All directories have 0755 and belong to root
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 },
84
85 // /usr/share cannot have any exectuable files
86 { "/usr/share/**", S_IFREG, 0, NULL, NULL, PAKFIRE_FHS_NOEXEC },
87
88 // /var
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 },
100
101 // Do not allow any subdirectories in /var
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 },
105
106 // No files in /var may be executable
107 { "/var/**", S_IFREG, 0, NULL, NULL, PAKFIRE_FHS_NOEXEC },
108
109 // /boot
110 { "/boot", S_IFDIR, 0755, "root", "root", 0 },
111 { "/boot/efi", S_IFDIR, 0755, "root", "root", 0 },
112
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
117 // /dev (nothing may exist in it)
118 { "/dev", S_IFDIR, 0755, "root", "root", 0 },
119 { "/dev/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
120
121 // /etc
122 { "/etc", S_IFDIR, 0755, "root", "root", 0 },
123
124 // /home
125 { "/home", S_IFDIR, 0755, "root", "root", 0 },
126 { "/home/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
127
128 // /opt
129 { "/opt", S_IFDIR, 0755, "root", "root", 0 },
130 // These directories belong to the "local administrator"
131 // https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s13.html
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 },
138
139 // /proc
140 { "/proc", S_IFDIR, 0755, "root", "root", 0 },
141 { "/proc/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
142
143 // root
144 { "/root", S_IFDIR, 0700, "root", "root", 0 },
145 { "/root/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
146
147 // /run
148 { "/run", S_IFDIR, 0755, "root", "root", 0 },
149 { "/run/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
150
151 // /sys
152 { "/sys", S_IFDIR, 0755, "root", "root", 0 },
153 { "/sys/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
154
155 // /tmp
156 { "/tmp", S_IFDIR, 1755, "root", "root", 0 },
157 { "/tmp/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
158
159 // FHS Directories
160 { "/media", S_IFDIR, 0755, "root", "root", 0 },
161 { "/mnt", S_IFDIR, 0755, "root", "root", 0 },
162 { "/srv", S_IFDIR, 0755, "root", "root", 0 },
163
164 // /bin, /sbin, /lib, and /lib64 have to be symlinks
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 },
169
170 // There cannot be anything else in /
171 { "/*", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST },
172
173 // Catch all so that we won't throw an error
174 { "/**", 0, 0, NULL, NULL, 0 },
175
176 // Sentinel
177 { NULL },
178 };
179
180 static const struct pakfire_fhs_check* pakfire_fhs_find_check(
181 struct pakfire* pakfire, struct pakfire_file* file) {
182 const struct pakfire_fhs_check* check = NULL;
183 int r;
184
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
191 // Walk through all possible checks
192 for (check = pakfire_fhs_check; check->path; check++) {
193 // Skip this check if the filetype doesn't match
194 if (check->type && check->type != type)
195 continue;
196
197 // Check path
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
216 ERROR:
217 ERROR(pakfire, "Could not find FHS entry for %s: %m\n", path);
218
219 return NULL;
220 }
221
222 static 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
239 static int pakfire_fhs_check_perms(struct pakfire* pakfire,
240 const struct pakfire_fhs_check* check, struct pakfire_file* file) {
241 // No permissions defined. Skipping check...
242 if (!check->perms)
243 return 0;
244
245 const char* path = pakfire_file_get_path(file);
246
247 // Fetch perms
248 const mode_t perms = pakfire_file_get_perms(file);
249
250 // Check if they match
251 if (check->perms != perms) {
252 DEBUG(pakfire, "%s: Permissions do not match\n", path);
253 return 1;
254 }
255
256 // Check passed
257 return 0;
258 }
259
260 static 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
292 static 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
313 int 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
322 // Check for world-writable permissions
323 r = pakfire_fhs_check_world_writable(pakfire, file);
324 if (r)
325 return r;
326
327 // Find a check
328 check = pakfire_fhs_find_check(pakfire, file);
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
340 // Check permissions
341 r = pakfire_fhs_check_perms(pakfire, check, file);
342 if (r)
343 return r;
344
345 // Check ownership
346 r = pakfire_fhs_check_ownership(pakfire, check, file);
347 if (r)
348 return r;
349
350 // Check for PAKFIRE_FHS_NOEXEC
351 r = pakfire_fhs_check_noexec(pakfire, check, file);
352 if (r)
353 return r;
354
355 // Check passed!
356 return 0;
357 }