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