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