]>
git.ipfire.org Git - people/stevee/pakfire.git/blob - src/libpakfire/fhs.c
1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2021 Pakfire development team #
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. #
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. #
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/>. #
19 #############################################################################*/
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>
30 This struct defines any FHS checks.
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.
35 static const struct pakfire_fhs_check
{
41 enum pakfire_fhs_check_flags
{
42 PAKFIRE_FHS_MUSTNOTEXIST
= (1 << 0),
43 PAKFIRE_FHS_NOEXEC
= (1 << 1),
45 } pakfire_fhs_check
[] = {
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 },
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
},
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
},
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 },
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 },
72 // Shared Libraries must not exist in /usr/lib
73 { "/usr/lib/*.so*", S_IFREG
, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
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 },
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 },
85 // /usr/share cannot have any exectuable files
86 { "/usr/share/**", S_IFREG
, 0, NULL
, NULL
, PAKFIRE_FHS_NOEXEC
},
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 },
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
},
106 // No files in /var may be executable
107 { "/var/**", S_IFREG
, 0, NULL
, NULL
, PAKFIRE_FHS_NOEXEC
},
110 { "/boot", S_IFDIR
, 0755, "root", "root", 0 },
111 { "/boot/efi", S_IFDIR
, 0755, "root", "root", 0 },
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, },
117 // /dev (nothing may exist in it)
118 { "/dev", S_IFDIR
, 0755, "root", "root", 0 },
119 { "/dev/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
122 { "/etc", S_IFDIR
, 0755, "root", "root", 0 },
125 { "/home", S_IFDIR
, 0755, "root", "root", 0 },
126 { "/home/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
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
},
140 { "/proc", S_IFDIR
, 0755, "root", "root", 0 },
141 { "/proc/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
144 { "/root", S_IFDIR
, 0700, "root", "root", 0 },
145 { "/root/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
148 { "/run", S_IFDIR
, 0755, "root", "root", 0 },
149 { "/run/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
152 { "/sys", S_IFDIR
, 0755, "root", "root", 0 },
153 { "/sys/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
156 { "/tmp", S_IFDIR
, 1755, "root", "root", 0 },
157 { "/tmp/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
160 { "/media", S_IFDIR
, 0755, "root", "root", 0 },
161 { "/mnt", S_IFDIR
, 0755, "root", "root", 0 },
162 { "/srv", S_IFDIR
, 0755, "root", "root", 0 },
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 },
170 // There cannot be anything else in /
171 { "/*", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
173 // Catch all so that we won't throw an error
174 { "/**", 0, 0, NULL
, NULL
, 0 },
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
;
185 // Fetch the file type
186 const mode_t type
= pakfire_file_get_type(file
);
189 const char* path
= pakfire_file_get_path(file
);
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
)
198 r
= pakfire_path_match(check
->path
, path
);
206 DEBUG(pakfire
, "%s matches check '%s'\n", path
, check
->path
);
217 ERROR(pakfire
, "Could not find FHS entry for %s: %m\n", path
);
222 static int pakfire_fhs_check_world_writable(
223 struct pakfire
* pakfire
, struct pakfire_file
* file
) {
225 const char* path
= pakfire_file_get_path(file
);
228 const mode_t perms
= pakfire_file_get_perms(file
);
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
);
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...
245 const char* path
= pakfire_file_get_path(file
);
248 const mode_t perms
= pakfire_file_get_perms(file
);
250 // Check if they match
251 if (check
->perms
!= perms
) {
252 DEBUG(pakfire
, "%s: Permissions do not match\n", path
);
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
);
266 const char* uname
= pakfire_file_get_uname(file
);
270 if (strcmp(check
->uname
, uname
) != 0) {
271 DEBUG(pakfire
, "%s: uname does not match\n", path
);
278 const char* gname
= pakfire_file_get_gname(file
);
282 if (strcmp(check
->gname
, gname
) != 0) {
283 DEBUG(pakfire
, "%s: gname does not match\n", path
);
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
))
299 const char* path
= pakfire_file_get_path(file
);
302 const mode_t perms
= pakfire_file_get_perms(file
);
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
);
313 int pakfire_fhs_check_file(struct pakfire
* pakfire
, struct pakfire_file
* file
) {
314 const struct pakfire_fhs_check
* check
= NULL
;
318 const char* path
= pakfire_file_get_path(file
);
322 // Check for world-writable permissions
323 r
= pakfire_fhs_check_world_writable(pakfire
, file
);
328 check
= pakfire_fhs_find_check(pakfire
, file
);
330 ERROR(pakfire
, "Could not match file %s: %m\n", path
);
334 // Should this file exist at all?
335 if (check
->flags
& PAKFIRE_FHS_MUSTNOTEXIST
) {
336 ERROR(pakfire
, "%s must not exist here\n", path
);
341 r
= pakfire_fhs_check_perms(pakfire
, check
, file
);
346 r
= pakfire_fhs_check_ownership(pakfire
, check
, file
);
350 // Check for PAKFIRE_FHS_NOEXEC
351 r
= pakfire_fhs_check_noexec(pakfire
, check
, file
);