]> git.ipfire.org Git - people/stevee/pakfire.git/blame - src/libpakfire/fhs.c
FHS: Ensure that firmware files are not executable
[people/stevee/pakfire.git] / src / libpakfire / fhs.c
CommitLineData
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*/
35static 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
158static 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
194ERROR:
195 ERROR(pakfire, "Could not find FHS entry for %s: %m\n", path);
196
197 return NULL;
198}
199
84a9a7b7 200static 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
221static 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
253int 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}