]>
git.ipfire.org Git - 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 },
55 { "/usr/src/kernels", S_IFDIR
, 0755, "root", "root", 0 },
57 // Allow no further files in /usr
58 { "/usr/*", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
60 // Allow no files in /usr/src except some kernel source
61 { "/usr/src/kernels/**", 0, 0, "root", "root", 0 },
62 { "/usr/src/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
64 // There cannot be any subdirectories in /usr/bin & /usr/sbin
65 { "/usr/bin/*", S_IFDIR
, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
66 { "/usr/sbin/*", S_IFDIR
, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
68 // Permitted setuid binaries
69 { "/usr/bin/gpasswd", S_IFREG
, S_ISUID
|0755, "root", "root", 0 },
70 { "/usr/bin/ksu", S_IFREG
, S_ISUID
|0755, "root", "root", 0 },
71 { "/usr/bin/passwd", S_IFREG
, S_ISUID
|0755, "root", "root", 0 },
72 { "/usr/bin/pkexec", S_IFREG
, S_ISUID
|0755, "root", "root", 0 },
73 { "/usr/bin/sudo", S_IFREG
, S_ISUID
|0755, "root", "root", 0 },
75 // Any files in /usr/{,s}bin must be owned by root and have 0755
76 { "/usr/bin/*", S_IFREG
, 0755, "root", "root", 0 },
77 { "/usr/sbin/*", S_IFREG
, 0755, "root", "root", 0 },
79 // Shared Libraries must be executable
80 { "/usr/lib64/*.so.*", S_IFREG
, 0755, "root", "root", 0 },
81 { "/usr/lib64/**/*.so", S_IFREG
, 0755, "root", "root", 0 },
83 // Shared Libraries must not exist in /usr/lib
84 { "/usr/lib/*.so*", S_IFREG
, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
86 // /usr/include: Ensure that:
87 // * All files are non-executable and belong to root
88 // * All directories have 0755 and belong to root
89 { "/usr/include/**", S_IFREG
, 0644, "root", "root", 0 },
90 { "/usr/include/**", S_IFDIR
, 0755, "root", "root", 0 },
92 // Firmware must not be executable
93 { "/usr/lib/firmware/**", S_IFREG
, 0644, "root", "root", 0 },
94 { "/usr/lib/firmware/**", S_IFDIR
, 0755, "root", "root", 0 },
97 { "/var", S_IFDIR
, 0755, "root", "root", 0 },
98 { "/var/cache", S_IFDIR
, 0755, "root", "root", 0 },
99 { "/var/db", S_IFDIR
, 0755, "root", "root", 0 },
100 { "/var/empty", S_IFDIR
, 0755, "root", "root", 0 },
101 { "/var/lib", S_IFDIR
, 0755, "root", "root", 0 },
102 { "/var/log", S_IFDIR
, 0755, "root", "root", 0 },
103 { "/var/mail", S_IFDIR
, 0755, "root", "mail", 0 },
104 { "/var/opt", S_IFDIR
, 0755, "root", "root", 0 },
105 { "/var/run", S_IFLNK
, 0755, "root", "root", 0 },
106 { "/var/spool", S_IFDIR
, 0755, "root", "root", 0 },
107 { "/var/tmp", S_IFDIR
, 0755, "root", "root", 0 },
109 // Do not allow any subdirectories in /var
110 { "/var/*", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
111 { "/var/empty/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
112 { "/var/tmp/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
114 // No files in /var may be executable
115 { "/var/**", S_IFREG
, 0, NULL
, NULL
, PAKFIRE_FHS_NOEXEC
},
118 { "/boot", S_IFDIR
, 0755, "root", "root", 0 },
119 { "/boot/efi", S_IFDIR
, 0755, "root", "root", 0 },
121 // All files in /boot must be owned by root
122 { "/boot/**", S_IFREG
, 0, "root", "root", 0, },
123 { "/boot/**", S_IFDIR
, 0, "root", "root", 0, },
125 // /dev (nothing may exist in it)
126 { "/dev", S_IFDIR
, 0755, "root", "root", 0 },
127 { "/dev/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
130 { "/etc", S_IFDIR
, 0755, "root", "root", 0 },
133 { "/home", S_IFDIR
, 0755, "root", "root", 0 },
134 { "/home/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
137 { "/opt", S_IFDIR
, 0755, "root", "root", 0 },
138 // These directories belong to the "local administrator"
139 // https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s13.html
140 { "/opt/bin", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
141 { "/opt/doc", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
142 { "/opt/include", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
143 { "/opt/info", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
144 { "/opt/lib", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
145 { "/opt/man", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
148 { "/proc", S_IFDIR
, 0755, "root", "root", 0 },
149 { "/proc/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
152 { "/root", S_IFDIR
, 0700, "root", "root", 0 },
153 { "/root/.*", S_IFREG
, 0644, "root", "root", 0 },
154 { "/root/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
157 { "/run", S_IFDIR
, 0755, "root", "root", 0 },
158 { "/run/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
161 { "/sys", S_IFDIR
, 0755, "root", "root", 0 },
162 { "/sys/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
165 { "/tmp", S_IFDIR
, 1755, "root", "root", 0 },
166 { "/tmp/**", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
169 { "/media", S_IFDIR
, 0755, "root", "root", 0 },
170 { "/mnt", S_IFDIR
, 0755, "root", "root", 0 },
171 { "/srv", S_IFDIR
, 0755, "root", "root", 0 },
173 // /bin, /sbin, /lib, and /lib64 have to be symlinks
174 { "/bin", S_IFLNK
, 0777, NULL
, NULL
, 0 },
175 { "/lib", S_IFLNK
, 0777, NULL
, NULL
, 0 },
176 { "/lib64", S_IFLNK
, 0777, NULL
, NULL
, 0 },
177 { "/sbin", S_IFLNK
, 0777, NULL
, NULL
, 0 },
179 // There cannot be anything else in /
180 { "/*", 0, 0, NULL
, NULL
, PAKFIRE_FHS_MUSTNOTEXIST
},
182 // Catch all so that we won't throw an error
183 { "/**", 0, 0, NULL
, NULL
, 0 },
189 static const struct pakfire_fhs_check
* pakfire_fhs_find_check(
190 struct pakfire
* pakfire
, struct pakfire_file
* file
) {
191 const struct pakfire_fhs_check
* check
= NULL
;
194 // Fetch the file type
195 const mode_t type
= pakfire_file_get_type(file
);
198 const char* path
= pakfire_file_get_path(file
);
200 // Walk through all possible checks
201 for (check
= pakfire_fhs_check
; check
->path
; check
++) {
202 // Skip this check if the filetype doesn't match
203 if (check
->type
&& check
->type
!= type
)
207 r
= pakfire_path_match(check
->path
, path
);
215 DEBUG(pakfire
, "%s matches check '%s'\n", path
, check
->path
);
226 ERROR(pakfire
, "Could not find FHS entry for %s: %m\n", path
);
231 static int pakfire_fhs_check_world_writable(
232 struct pakfire
* pakfire
, struct pakfire_file
* file
) {
233 // Run this check only for regular files
234 switch (pakfire_file_get_type(file
)) {
243 const char* path
= pakfire_file_get_path(file
);
246 const mode_t perms
= pakfire_file_get_perms(file
);
248 // Check that none of the executable bits are set
249 if ((perms
& (S_IWUSR
|S_IWGRP
|S_IWOTH
)) == (S_IWUSR
|S_IWGRP
|S_IWOTH
)) {
250 DEBUG(pakfire
, "%s is world-writable\n", path
);
257 static int pakfire_fhs_check_perms(struct pakfire
* pakfire
,
258 const struct pakfire_fhs_check
* check
, struct pakfire_file
* file
) {
259 // No permissions defined. Skipping check...
263 const char* path
= pakfire_file_get_path(file
);
266 const mode_t perms
= pakfire_file_get_perms(file
);
268 // Check if they match
269 if (check
->perms
!= perms
) {
270 DEBUG(pakfire
, "%s: Permissions do not match\n", path
);
278 static int pakfire_fhs_check_ownership(struct pakfire
* pakfire
,
279 const struct pakfire_fhs_check
* check
, struct pakfire_file
* file
) {
280 const char* path
= pakfire_file_get_path(file
);
284 const char* uname
= pakfire_file_get_uname(file
);
288 if (strcmp(check
->uname
, uname
) != 0) {
289 DEBUG(pakfire
, "%s: uname does not match\n", path
);
296 const char* gname
= pakfire_file_get_gname(file
);
300 if (strcmp(check
->gname
, gname
) != 0) {
301 DEBUG(pakfire
, "%s: gname does not match\n", path
);
310 static int pakfire_fhs_check_noexec(struct pakfire
* pakfire
,
311 const struct pakfire_fhs_check
* check
, struct pakfire_file
* file
) {
312 // Skip this check if PAKFIRE_FHS_NOEXEC is not set
313 if (!(check
->flags
& PAKFIRE_FHS_NOEXEC
))
317 const char* path
= pakfire_file_get_path(file
);
320 const mode_t perms
= pakfire_file_get_perms(file
);
322 // Check that none of the executable bits are set
323 if (perms
& (S_IXUSR
|S_IXGRP
|S_IXOTH
)) {
324 DEBUG(pakfire
, "%s must not be executable\n", path
);
331 int pakfire_fhs_check_file(struct pakfire
* pakfire
, struct pakfire_file
* file
) {
332 const struct pakfire_fhs_check
* check
= NULL
;
336 const char* path
= pakfire_file_get_path(file
);
340 // Check for world-writable permissions
341 r
= pakfire_fhs_check_world_writable(pakfire
, file
);
346 check
= pakfire_fhs_find_check(pakfire
, file
);
348 ERROR(pakfire
, "Could not match file %s: %m\n", path
);
352 // Should this file exist at all?
353 if (check
->flags
& PAKFIRE_FHS_MUSTNOTEXIST
) {
354 DEBUG(pakfire
, "%s must not exist here\n", path
);
359 r
= pakfire_fhs_check_perms(pakfire
, check
, file
);
364 r
= pakfire_fhs_check_ownership(pakfire
, check
, file
);
368 // Check for PAKFIRE_FHS_NOEXEC
369 r
= pakfire_fhs_check_noexec(pakfire
, check
, file
);