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