]>
Commit | Line | Data |
---|---|---|
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 | */ | |
35 | static 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), | |
43 | } flags; | |
0c931ee9 MT |
44 | } pakfire_fhs_check[] = { |
45 | // /usr | |
21bd82b0 MT |
46 | { "/usr", S_IFDIR, 0755, "root", "root", 0 }, |
47 | { "/usr/bin", S_IFDIR, 0755, "root", "root", 0 }, | |
48 | { "/usr/include", S_IFDIR, 0755, "root", "root", 0 }, | |
49 | { "/usr/lib", S_IFDIR, 0755, "root", "root", 0 }, | |
50 | { "/usr/lib64", S_IFDIR, 0755, "root", "root", 0 }, | |
51 | { "/usr/sbin", S_IFDIR, 0755, "root", "root", 0 }, | |
52 | { "/usr/share", S_IFDIR, 0755, "root", "root", 0 }, | |
53 | { "/usr/src", S_IFDIR, 0755, "root", "root", 0 }, | |
0c931ee9 | 54 | |
b3c8a073 | 55 | // Allow no further files in /usr & /usr/src |
21bd82b0 MT |
56 | { "/usr/*", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, |
57 | { "/usr/src/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
b3c8a073 | 58 | |
f6dbfb8f | 59 | // There cannot be any subdirectories in /usr/bin & /usr/sbin |
21bd82b0 MT |
60 | { "/usr/bin/*", S_IFDIR, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, |
61 | { "/usr/sbin/*", S_IFDIR, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
f6dbfb8f | 62 | |
2250db76 | 63 | // Any files in /usr/{,s}bin must be owned by root and have 0755 |
21bd82b0 MT |
64 | { "/usr/bin/*", S_IFREG, 0755, "root", "root", 0 }, |
65 | { "/usr/sbin/*", S_IFREG, 0755, "root", "root", 0 }, | |
2250db76 | 66 | |
b5416a4f MT |
67 | // /usr/include: Ensure that: |
68 | // * All files are non-executable and belong to root | |
69 | // * All directories have 0755 and belong to root | |
21bd82b0 MT |
70 | { "/usr/include/**", S_IFREG, 0644, "root", "root", 0 }, |
71 | { "/usr/include/**", S_IFDIR, 0755, "root", "root", 0 }, | |
72 | ||
73 | // Firmware must not be executable | |
74 | { "/usr/lib/firmware/**", S_IFREG, 0644, "root", "root", 0 }, | |
75 | { "/usr/lib/firmware/**", S_IFDIR, 0755, "root", "root", 0 }, | |
b5416a4f | 76 | |
0c931ee9 | 77 | // /var |
21bd82b0 MT |
78 | { "/var", S_IFDIR, 0755, "root", "root", 0 }, |
79 | { "/var/cache", S_IFDIR, 0755, "root", "root", 0 }, | |
80 | { "/var/db", S_IFDIR, 0755, "root", "root", 0 }, | |
81 | { "/var/empty", S_IFDIR, 0755, "root", "root", 0 }, | |
82 | { "/var/lib", S_IFDIR, 0755, "root", "root", 0 }, | |
83 | { "/var/log", S_IFDIR, 0755, "root", "root", 0 }, | |
84 | { "/var/mail", S_IFDIR, 0755, "root", "root", 0 }, | |
85 | { "/var/opt", S_IFDIR, 0755, "root", "root", 0 }, | |
86 | { "/var/run", S_IFLNK, 0755, "root", "root", 0 }, | |
87 | { "/var/spool", S_IFDIR, 0755, "root", "root", 0 }, | |
88 | { "/var/tmp", S_IFDIR, 0755, "root", "root", 0 }, | |
386b30c2 MT |
89 | |
90 | // Do not allow any subdirectories in /var | |
21bd82b0 MT |
91 | { "/var/*", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, |
92 | { "/var/empty/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
93 | { "/var/tmp/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
94 | |
95 | // /boot | |
21bd82b0 MT |
96 | { "/boot", S_IFDIR, 0755, "root", "root", 0 }, |
97 | { "/boot/efi", S_IFDIR, 0755, "root", "root", 0 }, | |
0c931ee9 MT |
98 | |
99 | // /dev (nothing may exist in it) | |
21bd82b0 MT |
100 | { "/dev", S_IFDIR, 0755, "root", "root", 0 }, |
101 | { "/dev/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
102 | |
103 | // /etc | |
21bd82b0 | 104 | { "/etc", S_IFDIR, 0755, "root", "root", 0 }, |
0c931ee9 MT |
105 | |
106 | // /home | |
21bd82b0 MT |
107 | { "/home", S_IFDIR, 0755, "root", "root", 0 }, |
108 | { "/home/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
109 | |
110 | // /opt | |
21bd82b0 | 111 | { "/opt", S_IFDIR, 0755, "root", "root", 0 }, |
0c931ee9 MT |
112 | // These directories belong to the "local administrator" |
113 | // https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s13.html | |
21bd82b0 MT |
114 | { "/opt/bin", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, |
115 | { "/opt/doc", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
116 | { "/opt/include", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
117 | { "/opt/info", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
118 | { "/opt/lib", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
119 | { "/opt/man", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
120 | |
121 | // /proc | |
21bd82b0 MT |
122 | { "/proc", S_IFDIR, 0755, "root", "root", 0 }, |
123 | { "/proc/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
124 | |
125 | // /run | |
21bd82b0 MT |
126 | { "/run", S_IFDIR, 0755, "root", "root", 0 }, |
127 | { "/run/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
128 | |
129 | // /sys | |
21bd82b0 MT |
130 | { "/sys", S_IFDIR, 0755, "root", "root", 0 }, |
131 | { "/sys/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
132 | |
133 | // /tmp | |
21bd82b0 MT |
134 | { "/tmp", S_IFDIR, 1755, "root", "root", 0 }, |
135 | { "/tmp/**", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, | |
0c931ee9 MT |
136 | |
137 | // FHS Directories | |
21bd82b0 MT |
138 | { "/media", S_IFDIR, 0755, "root", "root", 0 }, |
139 | { "/mnt", S_IFDIR, 0755, "root", "root", 0 }, | |
140 | { "/srv", S_IFDIR, 0755, "root", "root", 0 }, | |
0c931ee9 MT |
141 | |
142 | // /bin, /sbin, /lib, and /lib64 have to be symlinks | |
21bd82b0 MT |
143 | { "/bin", S_IFLNK, 0777, NULL, NULL, 0 }, |
144 | { "/lib", S_IFLNK, 0777, NULL, NULL, 0 }, | |
145 | { "/lib64", S_IFLNK, 0777, NULL, NULL, 0 }, | |
146 | { "/sbin", S_IFLNK, 0777, NULL, NULL, 0 }, | |
0c931ee9 MT |
147 | |
148 | // There cannot be anything else in / | |
21bd82b0 | 149 | { "/*", 0, 0, NULL, NULL, PAKFIRE_FHS_MUSTNOTEXIST }, |
0c931ee9 MT |
150 | |
151 | // Catch all so that we won't throw an error | |
21bd82b0 | 152 | { "/**", 0, 0, NULL, NULL, 0 }, |
0c931ee9 MT |
153 | |
154 | // Sentinel | |
155 | { NULL }, | |
156 | }; | |
157 | ||
158 | static const struct pakfire_fhs_check* pakfire_fhs_find_check( | |
84a9a7b7 | 159 | struct pakfire* pakfire, struct pakfire_file* file) { |
0c931ee9 MT |
160 | const struct pakfire_fhs_check* check = NULL; |
161 | int r; | |
162 | ||
84a9a7b7 MT |
163 | // Fetch the file type |
164 | const mode_t type = pakfire_file_get_type(file); | |
165 | ||
166 | // Fetch the path | |
167 | const char* path = pakfire_file_get_path(file); | |
168 | ||
0c931ee9 MT |
169 | // Walk through all possible checks |
170 | for (check = pakfire_fhs_check; check->path; check++) { | |
84a9a7b7 MT |
171 | // Skip this check if the filetype doesn't match |
172 | if (check->type && check->type != type) | |
173 | continue; | |
174 | ||
175 | // Check path | |
0c931ee9 MT |
176 | r = pakfire_path_match(check->path, path); |
177 | switch (r) { | |
178 | // No match | |
179 | case 0: | |
180 | continue; | |
181 | ||
182 | // Match! | |
183 | case 1: | |
184 | DEBUG(pakfire, "%s matches check '%s'\n", path, check->path); | |
185 | ||
186 | return check; | |
187 | ||
188 | // Error :( | |
189 | default: | |
190 | goto ERROR; | |
191 | } | |
192 | } | |
193 | ||
194 | ERROR: | |
195 | ERROR(pakfire, "Could not find FHS entry for %s: %m\n", path); | |
196 | ||
197 | return NULL; | |
198 | } | |
199 | ||
84a9a7b7 | 200 | static int pakfire_fhs_check_perms(struct pakfire* pakfire, |
0c931ee9 | 201 | const struct pakfire_fhs_check* check, struct pakfire_file* file) { |
84a9a7b7 MT |
202 | // No permissions defined. Skipping check... |
203 | if (!check->perms) | |
0c931ee9 MT |
204 | return 0; |
205 | ||
206 | const char* path = pakfire_file_get_path(file); | |
207 | ||
84a9a7b7 MT |
208 | // Fetch perms |
209 | const mode_t perms = pakfire_file_get_perms(file); | |
0c931ee9 | 210 | |
84a9a7b7 MT |
211 | // Check if they match |
212 | if (check->perms != perms) { | |
213 | DEBUG(pakfire, "%s: Permissions do not match\n", path); | |
214 | return 1; | |
0c931ee9 MT |
215 | } |
216 | ||
217 | // Check passed | |
218 | return 0; | |
219 | } | |
220 | ||
26f2d651 MT |
221 | static int pakfire_fhs_check_ownership(struct pakfire* pakfire, |
222 | const struct pakfire_fhs_check* check, struct pakfire_file* file) { | |
223 | const char* path = pakfire_file_get_path(file); | |
224 | ||
225 | // Check uname | |
226 | if (check->uname) { | |
227 | const char* uname = pakfire_file_get_uname(file); | |
228 | if (!uname) | |
229 | return 1; | |
230 | ||
231 | if (strcmp(check->uname, uname) != 0) { | |
232 | DEBUG(pakfire, "%s: uname does not match\n", path); | |
233 | return 1; | |
234 | } | |
235 | } | |
236 | ||
237 | // Check gname | |
238 | if (check->gname) { | |
239 | const char* gname = pakfire_file_get_gname(file); | |
240 | if (!gname) | |
241 | return 1; | |
242 | ||
243 | if (strcmp(check->gname, gname) != 0) { | |
244 | DEBUG(pakfire, "%s: gname does not match\n", path); | |
245 | return 1; | |
246 | } | |
247 | } | |
248 | ||
249 | // Pass | |
250 | return 0; | |
251 | } | |
252 | ||
0c931ee9 MT |
253 | int pakfire_fhs_check_file(struct pakfire* pakfire, struct pakfire_file* file) { |
254 | const struct pakfire_fhs_check* check = NULL; | |
255 | int r; | |
256 | ||
257 | // Get the file path | |
258 | const char* path = pakfire_file_get_path(file); | |
259 | if (!path) | |
260 | return 1; | |
261 | ||
262 | // Find a check | |
84a9a7b7 | 263 | check = pakfire_fhs_find_check(pakfire, file); |
0c931ee9 MT |
264 | if (!check) { |
265 | ERROR(pakfire, "Could not match file %s: %m\n", path); | |
266 | return 1; | |
267 | } | |
268 | ||
269 | // Should this file exist at all? | |
270 | if (check->flags & PAKFIRE_FHS_MUSTNOTEXIST) { | |
271 | ERROR(pakfire, "%s must not exist here\n", path); | |
272 | return 1; | |
273 | } | |
274 | ||
84a9a7b7 MT |
275 | // Check permissions |
276 | r = pakfire_fhs_check_perms(pakfire, check, file); | |
0c931ee9 MT |
277 | if (r) |
278 | return r; | |
279 | ||
26f2d651 MT |
280 | // Check ownership |
281 | r = pakfire_fhs_check_ownership(pakfire, check, file); | |
282 | if (r) | |
283 | return r; | |
284 | ||
0c931ee9 MT |
285 | // Check passed! |
286 | return 0; | |
287 | } |