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