]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/file.c
logging: Make the legacy logger configurable
[pakfire.git] / src / libpakfire / file.c
CommitLineData
221cc3ce
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2014 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
375a06e3 21#include <ctype.h>
5e9463ec 22#include <errno.h>
c064d9ec 23#include <fnmatch.h>
3fca5032 24#include <libgen.h>
293881bc 25#include <limits.h>
c98132d1 26#include <linux/limits.h>
221cc3ce
MT
27#include <stdlib.h>
28#include <string.h>
c2fe443d 29#include <sys/capability.h>
a09b95e0 30#include <sys/stat.h>
221cc3ce 31#include <sys/types.h>
221cc3ce
MT
32#include <time.h>
33
9a6e3e2d
MT
34#include <archive_entry.h>
35
df71dc60
MT
36#include <gelf.h>
37
517708c8
MT
38// Enable legacy logging
39#define PAKFIRE_LEGACY_LOGGING
40
8780f2ec 41#include <pakfire/ctx.h>
221cc3ce 42#include <pakfire/constants.h>
c52e0148 43#include <pakfire/digest.h>
6d9dd7bc 44#include <pakfire/fhs.h>
221cc3ce 45#include <pakfire/file.h>
3fca5032 46#include <pakfire/logging.h>
883b3be9 47#include <pakfire/pakfire.h>
9f953e68 48#include <pakfire/private.h>
d973a13d 49#include <pakfire/string.h>
221cc3ce
MT
50#include <pakfire/util.h>
51
c0b051bb
MT
52enum pakfire_file_verification_status {
53 PAKFIRE_FILE_NOENT = (1 << 0),
9e09e361
MT
54 PAKFIRE_FILE_TYPE_CHANGED = (1 << 1),
55 PAKFIRE_FILE_PERMISSIONS_CHANGED = (1 << 2),
56 PAKFIRE_FILE_DEV_CHANGED = (1 << 3),
57 PAKFIRE_FILE_SIZE_CHANGED = (1 << 4),
58 PAKFIRE_FILE_OWNER_CHANGED = (1 << 5),
59 PAKFIRE_FILE_GROUP_CHANGED = (1 << 6),
0eeac4a5
MT
60 PAKFIRE_FILE_CTIME_CHANGED = (1 << 7),
61 PAKFIRE_FILE_MTIME_CHANGED = (1 << 8),
76011205 62 PAKFIRE_FILE_PAYLOAD_CHANGED = (1 << 9),
c0b051bb
MT
63};
64
5803b5f6 65struct pakfire_file {
8780f2ec 66 struct pakfire_ctx* ctx;
ac4c607b 67 struct pakfire* pakfire;
5e9463ec 68 int nrefs;
221cc3ce 69
37c5d873
MT
70 // Use the libarchive entry to store common attributes
71 struct archive_entry* entry;
d03fa9a3 72
c2fe443d
MT
73 // Capabilities
74 cap_t caps;
75
2f3563b9
MT
76 // Flags
77 int flags;
78
65131b30 79 // Digests
c52e0148 80 struct pakfire_digests digests;
5e9463ec 81
210aabe9
MT
82 // MIME Type
83 char mimetype[NAME_MAX];
84
71f6f465
MT
85 // Class
86 int class;
87
c0b051bb
MT
88 // Verification Status
89 int verify_status;
90
28886e21
MT
91 // File Issues
92 int issues;
93 int check_done:1;
f7f44921 94
c2fe443d 95 #warning TODO data
5e9463ec
MT
96 //int is_datafile;
97};
98
c2fe443d
MT
99/*
100 Capabilities
101*/
102static int pakfire_file_read_fcaps(struct pakfire_file* file,
103 const struct vfs_cap_data* cap_data, size_t length) {
104 int max = 0;
105 int r;
106
107 uint32_t magic_etc = le32toh(cap_data->magic_etc);
108
109 // Which version are we dealing with?
110 switch (magic_etc & VFS_CAP_REVISION_MASK) {
111 case VFS_CAP_REVISION_1:
112 length -= XATTR_CAPS_SZ_1;
113 max = VFS_CAP_U32_1;
114 break;
115
116 case VFS_CAP_REVISION_2:
117 length -= XATTR_CAPS_SZ_2;
118 max = VFS_CAP_U32_2;
119 break;
120
121 case VFS_CAP_REVISION_3:
122 length -= XATTR_CAPS_SZ_3;
123 max = VFS_CAP_U32_3;
124 break;
125
126 // Unknown version
127 default:
128 errno = EINVAL;
129 return 1;
130 }
131
132 // Check if we have received the correct data
133 if (length) {
134 errno = EINVAL;
135 return 1;
136 }
137
138 // Allocate capabilities
139 file->caps = cap_init();
140 if (!file->caps) {
141 ERROR(file->pakfire, "Could not allocate capabilities: %m\n");
142 r = 1;
143 goto ERROR;
144 }
145
146 int mask;
147 int index;
148
149 int cap_permitted;
150 int cap_inheritable;
151 int cap_effective = 0;
152
153 for (unsigned int cap = 0; cap_valid(cap); cap++) {
154 // Find the index where to find this cap
155 index = CAP_TO_INDEX(cap);
156 mask = CAP_TO_MASK(cap);
157
158 // End if we have reached the end of the data
159 if (index > max)
160 break;
161
162 // Check for permitted/inheritable flag set
163 cap_permitted = le32toh(cap_data->data[index].permitted) & mask;
164 cap_inheritable = le32toh(cap_data->data[index].inheritable) & mask;
165
166 // Check for effective
167 if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
168 cap_effective = cap_permitted | cap_inheritable;
169
170 cap_value_t caps[] = { cap };
171
172 if (cap_permitted) {
173 r = cap_set_flag(file->caps, CAP_PERMITTED, 1, caps, CAP_SET);
174 if (r) {
a8a41064 175 ERROR(file->pakfire, "Could not set capability %u: %m\n", cap);
c2fe443d
MT
176 goto ERROR;
177 }
178 }
179
180 if (cap_inheritable) {
181 r = cap_set_flag(file->caps, CAP_INHERITABLE, 1, caps, CAP_SET);
182 if (r) {
a8a41064 183 ERROR(file->pakfire, "Could not set capability %u: %m\n", cap);
c2fe443d
MT
184 goto ERROR;
185 }
186 }
187
188 if (cap_effective) {
189 r = cap_set_flag(file->caps, CAP_EFFECTIVE, 1, caps, CAP_SET);
190 if (r) {
a8a41064 191 ERROR(file->pakfire, "Could not set capability %u: %m\n", cap);
c2fe443d
MT
192 goto ERROR;
193 }
194 }
195 }
196
197#ifdef ENABLE_DEBUG
198 char* text = cap_to_text(file->caps, NULL);
199 if (text) {
37c5d873 200 DEBUG(file->pakfire, "%s: Capabilities %s\n", pakfire_file_get_path(file), text);
c2fe443d
MT
201 cap_free(text);
202 }
203#endif
204
205 return 0;
206
207ERROR:
208 if (file->caps) {
209 cap_free(file->caps);
210 file->caps = NULL;
211 }
212
213 return r;
214}
215
729568d0
MT
216int pakfire_file_write_fcaps(struct pakfire_file* file, struct vfs_cap_data* cap_data) {
217 cap_flag_value_t cap_permitted;
218 cap_flag_value_t cap_inheritable;
219 cap_flag_value_t cap_effective;
220 int r;
221
222 // This should not be called when we have no caps
223 if (!file->caps) {
224 errno = EINVAL;
225 return 1;
226 }
227
228 uint32_t magic = VFS_CAP_REVISION_2;
229
230 for (unsigned int cap = 0; cap_valid(cap); cap++) {
231 // Find the index where to find this cap
232 int index = CAP_TO_INDEX(cap);
233 int mask = CAP_TO_MASK(cap);
234
235 // Fetch CAP_PERMITTED
236 r = cap_get_flag(file->caps, cap, CAP_PERMITTED, &cap_permitted);
237 if (r) {
a8a41064 238 ERROR(file->pakfire, "Could not fetch capability %u: %m\n", cap);
729568d0
MT
239 goto ERROR;
240 }
241
242 // Fetch CAP_INHERITABLE
243 r = cap_get_flag(file->caps, cap, CAP_INHERITABLE, &cap_inheritable);
244 if (r) {
a8a41064 245 ERROR(file->pakfire, "Could not fetch capability %u: %m\n", cap);
729568d0
MT
246 goto ERROR;
247 }
248
249 // Fetch CAP_EFFECTIVE
250 r = cap_get_flag(file->caps, cap, CAP_EFFECTIVE, &cap_effective);
251 if (r) {
a8a41064 252 ERROR(file->pakfire, "Could not fetch capability %u: %m\n", cap);
729568d0
MT
253 goto ERROR;
254 }
255
256 // Store CAP_PERMITTED
257 if (cap_permitted)
258 cap_data->data[index].permitted |= htole32(mask);
259
260 // Store CAP_INHERITED
261 if (cap_inheritable)
262 cap_data->data[index].inheritable |= htole32(mask);
263
264 // Set EFFECTIVE flag if CAP_EFFECTIVE is set
265 if (cap_effective)
266 magic |= VFS_CAP_FLAGS_EFFECTIVE;
267 }
268
269 // Store the magic value
270 cap_data->magic_etc = htole32(magic);
271
272ERROR:
273 return r;
274}
275
eb5daf3f 276static int pakfire_file_from_archive_entry(struct pakfire_file* file, struct archive_entry* entry) {
210aabe9 277 char* buffer = NULL;
eb5daf3f
MT
278 const char* attr = NULL;
279 const void* value = NULL;
280 size_t size = 0;
281 int r = 0;
282
37c5d873
MT
283 // Remove the existing entry
284 if (file->entry)
285 archive_entry_free(file->entry);
eb5daf3f 286
37c5d873
MT
287 // Clone the given entry
288 file->entry = archive_entry_clone(entry);
289 if (!file->entry)
290 return -ENOMEM;
eb5daf3f 291
210aabe9 292 // Reset iterating over extended attributes
37c5d873 293 archive_entry_xattr_reset(file->entry);
471ccfc2 294
eb5daf3f
MT
295 // Read any extended attributes
296 while (archive_entry_xattr_next(entry, &attr, &value, &size) == ARCHIVE_OK) {
2f3563b9
MT
297 // Config Files
298 if (strcmp(attr, "PAKFIRE.config") == 0) {
299 r = pakfire_file_set_flags(file, PAKFIRE_FILE_CONFIG);
300 if (r)
301 goto ERROR;
302
210aabe9 303 // MIME type
2f3563b9 304 } else if (strcmp(attr, "PAKFIRE.mimetype") == 0) {
210aabe9
MT
305 // Copy the value into a NULL-terminated buffer
306 r = asprintf(&buffer, "%.*s", (int)size, (const char*)value);
307 if (r < 0)
308 goto ERROR;
309
310 // Assign the value
311 r = pakfire_file_set_mimetype(file, buffer);
312 if (r)
313 goto ERROR;
314
d98740de 315 // Digest: SHA-3-512
210aabe9 316 } else if (strcmp(attr, "PAKFIRE.digests.sha3_512") == 0) {
d98740de
MT
317 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA3_512, value, size);
318 if (r)
319 goto ERROR;
320
321 // Digest: SHA-3-256
322 } else if (strcmp(attr, "PAKFIRE.digests.sha3_256") == 0) {
323 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA3_256, value, size);
324 if (r)
325 goto ERROR;
326
f1e6c5df 327 // Digest: BLAKE2b512
d98740de 328 } else if (strcmp(attr, "PAKFIRE.digests.blake2b512") == 0) {
f1e6c5df
MT
329 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_BLAKE2B512, value, size);
330 if (r)
331 goto ERROR;
332
2822561a 333 // Digest: BLAKE2s256
f1e6c5df
MT
334 } else if (strcmp(attr, "PAKFIRE.digests.blake2s256") == 0) {
335 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_BLAKE2S256, value, size);
336 if (r)
337 goto ERROR;
338
4500ed0a
MT
339 // Digest: SHA-2-512
340 } else if (strcmp(attr, "PAKFIRE.digests.sha2_512") == 0) {
341 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA2_512, value, size);
eb5daf3f
MT
342 if (r)
343 goto ERROR;
344
4500ed0a
MT
345 // Digest: SHA-2-256
346 } else if (strcmp(attr, "PAKFIRE.digests.sha2_256") == 0) {
347 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA2_256, value, size);
eb5daf3f
MT
348 if (r)
349 goto ERROR;
350
c2fe443d
MT
351 // Capabilities
352 } else if (strcmp(attr, "security.capability") == 0) {
353 r = pakfire_file_read_fcaps(file, value, size);
354 if (r)
355 goto ERROR;
356
eb5daf3f
MT
357 } else {
358 DEBUG(file->pakfire, "Received an unknown extended attribute: %s\n", attr);
359 }
360 }
361
362ERROR:
210aabe9
MT
363 if (buffer)
364 free(buffer);
365
eb5daf3f
MT
366 return r;
367}
368
ac4c607b 369PAKFIRE_EXPORT int pakfire_file_create(struct pakfire_file** file, struct pakfire* pakfire) {
5803b5f6 370 struct pakfire_file* f = calloc(1, sizeof(*f));
5e9463ec 371 if (!f)
7403779a 372 return 1;
5e9463ec 373
8780f2ec
MT
374 // Store a reference to the context
375 f->ctx = pakfire_ctx(pakfire);
376
e8d3b985 377 // Store reference to Pakfire
883b3be9 378 f->pakfire = pakfire_ref(pakfire);
e8d3b985
MT
379
380 // Initialize reference counter
5e9463ec
MT
381 f->nrefs = 1;
382
37c5d873
MT
383 // Create a new archive entry
384 f->entry = archive_entry_new();
385 if (!f->entry)
386 return -ENOMEM;
387
5e9463ec
MT
388 *file = f;
389 return 0;
d03fa9a3
MT
390}
391
37c5d873
MT
392int pakfire_file_read(struct pakfire_file* file, struct archive* reader, const char* path) {
393 int r;
61b0856b 394
37c5d873
MT
395 // Check inputs
396 if (!reader || !path)
397 return -EINVAL;
61b0856b 398
37c5d873
MT
399 // Set abspath
400 r = pakfire_file_set_abspath(file, path);
401 if (r)
402 return r;
61b0856b 403
37c5d873
MT
404 // Read everything
405 r = archive_read_disk_entry_from_file(reader, file->entry, -1, NULL);
61b0856b 406 if (r) {
37c5d873
MT
407 ERROR(file->pakfire, "Could not read %s: %s\n", path, archive_error_string(reader));
408 return 1;
61b0856b
MT
409 }
410
37c5d873 411 return 0;
61b0856b
MT
412}
413
ac4c607b 414int pakfire_file_create_from_archive_entry(struct pakfire_file** file, struct pakfire* pakfire,
d0985e31
MT
415 struct archive_entry* entry) {
416 int r = pakfire_file_create(file, pakfire);
417 if (r)
418 return r;
419
420 // Copy archive entry
eb5daf3f 421 r = pakfire_file_from_archive_entry(*file, entry);
d0985e31
MT
422 if (r)
423 goto ERROR;
424
425 return 0;
426
427ERROR:
428 pakfire_file_unref(*file);
429 *file = NULL;
430
431 return r;
432}
433
49a39ad9 434struct archive_entry* pakfire_file_archive_entry(struct pakfire_file* file, int digest_types) {
37c5d873 435 struct archive_entry* entry = NULL;
729568d0 436 struct vfs_cap_data cap_data = {};
d4b2ef2d 437 int r;
d7b476ef 438
37c5d873
MT
439 // Clone the entry
440 entry = archive_entry_clone(file->entry);
441 if (!entry)
a03246e0 442 goto ERROR;
5acd852e 443
2f3563b9
MT
444 // Flags
445 if (pakfire_file_has_flag(file, PAKFIRE_FILE_CONFIG)) {
446 archive_entry_xattr_add_entry(entry,
447 "PAKFIRE.config", "1", strlen("1"));
448 }
449
210aabe9
MT
450 // Set MIME type
451 const char* mimetype = pakfire_file_get_mimetype(file);
452 if (mimetype) {
453 archive_entry_xattr_add_entry(entry,
454 "PAKFIRE.mimetype", mimetype, strlen(mimetype));
455 }
456
d4b2ef2d
MT
457 // Compute any required file digests
458 r = pakfire_file_compute_digests(file, digest_types);
459 if (r)
a03246e0 460 goto ERROR;
d4b2ef2d 461
399d14c2
MT
462 // Copy digests
463
d98740de 464 // SHA-3-512
da3b7f52 465 if ((digest_types & PAKFIRE_DIGEST_SHA3_512)
045fa504 466 && pakfire_digest_set(file->digests.sha3_512))
d98740de
MT
467 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha3_512",
468 file->digests.sha3_512, sizeof(file->digests.sha3_512));
469
470 // SHA-3-256
da3b7f52 471 if ((digest_types & PAKFIRE_DIGEST_SHA3_256) &&
045fa504 472 pakfire_digest_set(file->digests.sha3_256))
d98740de
MT
473 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha3_256",
474 file->digests.sha3_256, sizeof(file->digests.sha3_256));
475
f1e6c5df 476 // BLAKE2b512
da3b7f52 477 if ((digest_types & PAKFIRE_DIGEST_BLAKE2B512) &&
045fa504 478 pakfire_digest_set(file->digests.blake2b512))
f1e6c5df
MT
479 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.blake2b512",
480 file->digests.blake2b512, sizeof(file->digests.blake2b512));
481
482 // BLAKE2s256
da3b7f52 483 if ((digest_types & PAKFIRE_DIGEST_BLAKE2S256) &&
045fa504 484 pakfire_digest_set(file->digests.blake2s256))
f1e6c5df
MT
485 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.blake2s256",
486 file->digests.blake2s256, sizeof(file->digests.blake2s256));
487
4500ed0a 488 // SHA-2-512
da3b7f52 489 if ((digest_types & PAKFIRE_DIGEST_SHA2_512) &&
045fa504 490 pakfire_digest_set(file->digests.sha2_512))
4500ed0a
MT
491 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha2_512",
492 file->digests.sha2_512, sizeof(file->digests.sha2_512));
399d14c2 493
4500ed0a 494 // SHA-2-256
da3b7f52 495 if ((digest_types & PAKFIRE_DIGEST_SHA2_512) &&
045fa504 496 pakfire_digest_set(file->digests.sha2_256))
4500ed0a
MT
497 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha2_256",
498 file->digests.sha2_256, sizeof(file->digests.sha2_256));
5acd852e 499
729568d0
MT
500 // Capabilities
501 if (file->caps) {
502 r = pakfire_file_write_fcaps(file, &cap_data);
503 if (r) {
504 ERROR(file->pakfire, "Could not export capabilities: %m\n");
a03246e0 505 goto ERROR;
729568d0
MT
506 }
507
508 // Store capabilities in archive entry
509 archive_entry_xattr_add_entry(entry,
510 "security.capability", &cap_data, sizeof(cap_data));
511 }
512
5acd852e 513 return entry;
a03246e0
MT
514
515ERROR:
516 if (entry)
517 archive_entry_free(entry);
518
519 return NULL;
5acd852e
MT
520}
521
5803b5f6 522static void pakfire_file_free(struct pakfire_file* file) {
c2fe443d
MT
523 // Free capabilities
524 if (file->caps)
525 cap_free(file->caps);
37c5d873
MT
526 if (file->entry)
527 archive_entry_free(file->entry);
8780f2ec
MT
528 if (file->pakfire)
529 pakfire_unref(file->pakfire);
530 if (file->ctx)
531 pakfire_ctx_unref(file->ctx);
f0d6233d 532 free(file);
221cc3ce
MT
533}
534
5803b5f6 535PAKFIRE_EXPORT struct pakfire_file* pakfire_file_ref(struct pakfire_file* file) {
5e9463ec 536 file->nrefs++;
221cc3ce 537
5e9463ec
MT
538 return file;
539}
221cc3ce 540
5803b5f6 541PAKFIRE_EXPORT struct pakfire_file* pakfire_file_unref(struct pakfire_file* file) {
5e9463ec
MT
542 if (--file->nrefs > 0)
543 return file;
544
545 pakfire_file_free(file);
546 return NULL;
221cc3ce
MT
547}
548
2f3563b9
MT
549/*
550 Flags
551*/
552
553int pakfire_file_has_flag(struct pakfire_file* file, int flag) {
554 return file->flags & flag;
555}
556
557int pakfire_file_set_flags(struct pakfire_file* file, int flag) {
558 file->flags |= flag;
559
560 return 0;
561}
562
5852b822
MT
563#define pakfire_file_strmode(file, buffer) \
564 __pakfire_file_strmode(file, buffer, sizeof(buffer))
565
566static int __pakfire_file_strmode(struct pakfire_file* file, char* s, const size_t length) {
567 int r;
568
569 static const mode_t permbits[] = {
570 0400,
571 0200,
572 0100,
573 0040,
574 0020,
575 0010,
576 0004,
577 0002,
578 0001
579 };
580
581 const mode_t mode = pakfire_file_get_mode(file);
582 const mode_t type = pakfire_file_get_type(file);
583
584 // Set some default string
585 r = __pakfire_string_set(s, length, "?rwxrwxrwx ");
586 if (r)
587 return r;
588
589 switch (type) {
590 case S_IFREG:
591 s[0] = '-';
592 break;
593
594 case S_IFBLK:
595 s[0] = 'b';
596 break;
597
598 case S_IFCHR:
599 s[0] = 'c';
600 break;
601
602 case S_IFDIR:
603 s[0] = 'd';
604 break;
605
606 case S_IFLNK:
607 s[0] = 'l';
608 break;
609
610 case S_IFSOCK:
611 s[0] = 's';
612 break;
613
614 case S_IFIFO:
615 s[0] = 'p';
616 break;
617
618 default:
37c5d873
MT
619 const char* hardlink = pakfire_file_get_hardlink(file);
620
621 if (hardlink) {
5852b822
MT
622 s[0] = 'h';
623 break;
624 }
625 }
626
627 for (unsigned int i = 0; i < 9; i++) {
628 if (mode & permbits[i])
629 continue;
630
631 s[i+1] = '-';
632 }
633
634 if (mode & S_ISUID) {
635 if (mode & 0100)
636 s[3] = 's';
637 else
638 s[3] = 'S';
639 }
640
641 if (mode & S_ISGID) {
642 if (mode & 0010)
643 s[6] = 's';
644 else
645 s[6] = 'S';
646 }
647
648 if (mode & S_ISVTX) {
649 if (mode & 0001)
650 s[9] = 't';
651 else
652 s[9] = 'T';
653 }
654
655#if 0
656 if (file->caps)
657 s[10] = '+';
658#endif
659
660 return 0;
661}
662
f7f44921 663char* pakfire_file_dump(struct pakfire_file* file, int flags) {
acfb811a 664 char* buffer = NULL;
5852b822
MT
665 int r;
666
667 char mode[12];
668 char time[32];
669
7fe6e18f
MT
670 // Initialize the buffer
671 r = asprintf(&buffer, "%s", "");
672 if (r < 0)
673 goto ERROR;
674
5852b822 675 // Format mode
f7f44921
MT
676 if (flags & PAKFIRE_FILE_DUMP_MODE) {
677 r = pakfire_file_strmode(file, mode);
678 if (r)
679 goto ERROR;
680
681 r = asprintf(&buffer, "%s %s", buffer, mode);
682 if (r < 0)
683 goto ERROR;
684 }
685
686 // Format ownership
687 if (flags & PAKFIRE_FILE_DUMP_OWNERSHIP) {
37c5d873
MT
688 const char* uname = pakfire_file_get_uname(file);
689 const char* gname = pakfire_file_get_gname(file);
690
691 r = asprintf(&buffer, "%s %s/%s", buffer, uname, gname);
f7f44921
MT
692 if (r < 0)
693 goto ERROR;
694 }
695
696 // Format size
697 if (flags & PAKFIRE_FILE_DUMP_SIZE) {
37c5d873
MT
698 const size_t size = pakfire_file_get_size(file);
699
700 r = asprintf(&buffer, "%s %8zu", buffer, size);
f7f44921
MT
701 if (r < 0)
702 goto ERROR;
703 }
5852b822
MT
704
705 // Format time
f7f44921 706 if (flags & PAKFIRE_FILE_DUMP_TIME) {
37c5d873
MT
707 const time_t ctime = pakfire_file_get_ctime(file);
708
709 r = pakfire_strftime(time, "%Y-%m-%d %H:%M", ctime);
f7f44921
MT
710 if (r)
711 goto ERROR;
712
713 r = asprintf(&buffer, "%s %s", buffer, time);
714 if (r < 0)
715 goto ERROR;
716 }
5852b822 717
f7f44921 718 // Format path
37c5d873 719 r = asprintf(&buffer, "%s %s", buffer, pakfire_file_get_path(file));
5852b822 720 if (r < 0)
f7f44921 721 goto ERROR;
5852b822 722
a3bf05ad 723 // Append symlink target
f7f44921
MT
724 if (flags & PAKFIRE_FILE_DUMP_LINK_TARGETS) {
725 switch (pakfire_file_get_type(file)) {
726 case S_IFLNK:
37c5d873 727 r = asprintf(&buffer, "%s -> %s", buffer, pakfire_file_get_symlink(file));
f7f44921
MT
728 if (r < 0)
729 return NULL;
a3bf05ad 730
f7f44921
MT
731 default:
732 break;
733 }
734 }
735
28886e21
MT
736 // Dump Issues
737 if (flags & PAKFIRE_FILE_DUMP_ISSUES) {
738 if (file->issues & PAKFIRE_FILE_FHS_ERROR) {
6d9dd7bc
MT
739 r = asprintf(&buffer, "%s [FHS-ERROR]", buffer);
740 if (r < 0)
741 goto ERROR;
742 }
743
03a5ac95
MT
744 if (file->issues & PAKFIRE_FILE_MISSING_DEBUGINFO) {
745 r = asprintf(&buffer, "%s [MISSING-DEBUGINFO]", buffer);
746 if (r < 0)
747 goto ERROR;
748 }
749
62bf2777 750 // Stack-smashing Protection
d8acc837
MT
751 if (file->issues & PAKFIRE_FILE_MISSING_SSP) {
752 r = asprintf(&buffer, "%s [MISSING-SSP]", buffer);
62bf2777
MT
753 if (r < 0)
754 goto ERROR;
755 }
f7f44921 756
62bf2777 757 // Position-independent Executable
d8acc837
MT
758 if (file->issues & PAKFIRE_FILE_MISSING_PIE) {
759 r = asprintf(&buffer, "%s [MISSING-PIE]", buffer);
62bf2777
MT
760 if (r < 0)
761 goto ERROR;
762 }
03d5abd3 763
62bf2777
MT
764 // Executable Stack
765 if (file->issues & PAKFIRE_FILE_EXECSTACK) {
766 r = asprintf(&buffer, "%s [EXECSTACK]", buffer);
767 if (r < 0)
768 goto ERROR;
769 }
03d5abd3 770
9a789d72
MT
771 // Not RELRO
772 if (file->issues & PAKFIRE_FILE_NO_RELRO) {
773 r = asprintf(&buffer, "%s [NO-RELRO]", buffer);
62bf2777
MT
774 if (r < 0)
775 goto ERROR;
f7f44921 776 }
7b461c8b
MT
777
778 // Has RUNPATH?
779 if (file->issues & PAKFIRE_FILE_HAS_RUNPATH) {
780 r = asprintf(&buffer, "%s [HAS-RUNPATH]", buffer);
781 if (r < 0)
782 goto ERROR;
783 }
691091ad
MT
784
785 // Invalid capabilities
786 if (file->issues & PAKFIRE_FILE_INVALID_CAPS) {
787 r = asprintf(&buffer, "%s [INVALID-CAPS]", buffer);
788 if (r < 0)
789 goto ERROR;
790 }
375a06e3
MT
791
792 // Invalid interpreters
793 if (file->issues & PAKFIRE_FILE_INVALID_INTERPRETER) {
794 r = asprintf(&buffer, "%s [INVALID-INTERPRETER]", buffer);
795 if (r < 0)
796 goto ERROR;
797 }
a3bf05ad
MT
798 }
799
5852b822 800 return buffer;
f7f44921
MT
801
802ERROR:
803 if (buffer)
804 free(buffer);
805
806 return NULL;
5852b822
MT
807}
808
5803b5f6 809PAKFIRE_EXPORT int pakfire_file_cmp(struct pakfire_file* file1, struct pakfire_file* file2) {
32485f6c
MT
810 const char* path1 = pakfire_file_get_path(file1);
811 const char* path2 = pakfire_file_get_path(file2);
221cc3ce 812
32485f6c 813 return strcmp(path1, path2);
221cc3ce
MT
814}
815
5803b5f6 816const char* pakfire_file_get_abspath(struct pakfire_file* file) {
37c5d873 817 return archive_entry_sourcepath(file->entry);
e4c2f7a9
MT
818}
819
5803b5f6 820int pakfire_file_set_abspath(struct pakfire_file* file, const char* path) {
d13bd5b7 821 // Check if path is set and absolute
ac1765b0
MT
822 if (!path || *path != '/')
823 return -EINVAL;
d13bd5b7 824
49a39ad9 825 // Store the abspath
37c5d873 826 archive_entry_copy_sourcepath(file->entry, path);
49a39ad9 827
37c5d873 828 return 0;
3b9e3970
MT
829}
830
5803b5f6 831PAKFIRE_EXPORT const char* pakfire_file_get_path(struct pakfire_file* file) {
37c5d873 832 return archive_entry_pathname(file->entry);
221cc3ce
MT
833}
834
5803b5f6 835PAKFIRE_EXPORT int pakfire_file_set_path(struct pakfire_file* file, const char* path) {
37c5d873
MT
836 char buffer[PATH_MAX];
837 int r = 0;
d13bd5b7 838
ac1765b0
MT
839 // Check input
840 if (!path)
841 return -EINVAL;
842
2cc8b5f4
MT
843 // Strip any leading dots from paths
844 if (pakfire_string_startswith(path, "./"))
845 path++;
846
847 switch (*path) {
848 // Just store the path if it is absolute
849 case '/':
37c5d873 850 archive_entry_set_pathname(file->entry, path);
2cc8b5f4
MT
851 break;
852
853 // Handle relative paths
854 default:
37c5d873 855 r = pakfire_string_format(buffer, "/%s", path);
2cc8b5f4
MT
856 if (r)
857 goto ERROR;
520213e9 858
37c5d873
MT
859 archive_entry_set_pathname(file->entry, buffer);
860 break;
520213e9
MT
861 }
862
863 return r;
12a327de
MT
864
865ERROR:
866 ERROR(file->pakfire, "Could not set path '%s': %m\n", path);
867 return r;
221cc3ce
MT
868}
869
59d8c727 870PAKFIRE_EXPORT const char* pakfire_file_get_hardlink(struct pakfire_file* file) {
37c5d873 871 return archive_entry_hardlink(file->entry);
59d8c727
MT
872}
873
874PAKFIRE_EXPORT void pakfire_file_set_hardlink(struct pakfire_file* file, const char* link) {
37c5d873 875 archive_entry_set_hardlink(file->entry, link);
59d8c727
MT
876}
877
878PAKFIRE_EXPORT const char* pakfire_file_get_symlink(struct pakfire_file* file) {
37c5d873 879 return archive_entry_symlink(file->entry);
59d8c727
MT
880}
881
882PAKFIRE_EXPORT void pakfire_file_set_symlink(struct pakfire_file* file, const char* link) {
37c5d873 883 archive_entry_set_symlink(file->entry, link);
59d8c727
MT
884}
885
f8733b31 886PAKFIRE_EXPORT nlink_t pakfire_file_get_nlink(struct pakfire_file* file) {
37c5d873 887 return archive_entry_nlink(file->entry);
f8733b31
MT
888}
889
890PAKFIRE_EXPORT void pakfire_file_set_nlink(struct pakfire_file* file, const nlink_t nlink) {
37c5d873 891 archive_entry_set_nlink(file->entry, nlink);
f8733b31
MT
892}
893
894PAKFIRE_EXPORT ino_t pakfire_file_get_inode(struct pakfire_file* file) {
37c5d873 895 return archive_entry_ino64(file->entry);
f8733b31
MT
896}
897
898PAKFIRE_EXPORT void pakfire_file_set_inode(struct pakfire_file* file, const ino_t ino) {
37c5d873 899 archive_entry_set_ino64(file->entry, ino);
f8733b31
MT
900}
901
902PAKFIRE_EXPORT dev_t pakfire_file_get_dev(struct pakfire_file* file) {
37c5d873 903 return archive_entry_dev(file->entry);
f8733b31
MT
904}
905
906PAKFIRE_EXPORT void pakfire_file_set_dev(struct pakfire_file* file, const dev_t dev) {
37c5d873 907 archive_entry_set_dev(file->entry, dev);
f8733b31
MT
908}
909
5803b5f6 910PAKFIRE_EXPORT int pakfire_file_get_type(struct pakfire_file* file) {
37c5d873 911 return archive_entry_filetype(file->entry);
221cc3ce
MT
912}
913
a09b95e0 914PAKFIRE_EXPORT off_t pakfire_file_get_size(struct pakfire_file* file) {
37c5d873 915 return archive_entry_size(file->entry);
221cc3ce
MT
916}
917
a09b95e0 918PAKFIRE_EXPORT void pakfire_file_set_size(struct pakfire_file* file, off_t size) {
37c5d873 919 archive_entry_set_size(file->entry, size);
221cc3ce
MT
920}
921
302e3253 922PAKFIRE_EXPORT const char* pakfire_file_get_uname(struct pakfire_file* file) {
37c5d873 923 return archive_entry_uname(file->entry);
221cc3ce
MT
924}
925
302e3253 926PAKFIRE_EXPORT int pakfire_file_set_uname(struct pakfire_file* file, const char* uname) {
37c5d873
MT
927 archive_entry_set_uname(file->entry, uname);
928
929 return 0;
221cc3ce
MT
930}
931
302e3253 932PAKFIRE_EXPORT const char* pakfire_file_get_gname(struct pakfire_file* file) {
37c5d873 933 return archive_entry_gname(file->entry);
221cc3ce
MT
934}
935
302e3253 936PAKFIRE_EXPORT int pakfire_file_set_gname(struct pakfire_file* file, const char* gname) {
37c5d873
MT
937 archive_entry_set_gname(file->entry, gname);
938
939 return 0;
221cc3ce
MT
940}
941
5803b5f6 942PAKFIRE_EXPORT mode_t pakfire_file_get_mode(struct pakfire_file* file) {
37c5d873 943 return archive_entry_mode(file->entry);
221cc3ce
MT
944}
945
5803b5f6 946PAKFIRE_EXPORT void pakfire_file_set_mode(struct pakfire_file* file, mode_t mode) {
37c5d873 947 archive_entry_set_mode(file->entry, mode);
221cc3ce
MT
948}
949
134545d5 950PAKFIRE_EXPORT mode_t pakfire_file_get_perms(struct pakfire_file* file) {
37c5d873 951 return archive_entry_perm(file->entry);
134545d5
MT
952}
953
954PAKFIRE_EXPORT void pakfire_file_set_perms(struct pakfire_file* file, const mode_t perms) {
37c5d873 955 archive_entry_set_mode(file->entry, pakfire_file_get_type(file) | perms);
134545d5
MT
956}
957
691091ad 958static int pakfire_file_is_executable(struct pakfire_file* file) {
37c5d873 959 return pakfire_file_get_mode(file) & (S_IXUSR|S_IXGRP|S_IXOTH);
691091ad
MT
960}
961
5803b5f6 962PAKFIRE_EXPORT time_t pakfire_file_get_ctime(struct pakfire_file* file) {
37c5d873 963 return archive_entry_ctime(file->entry);
ef4e8460
MT
964}
965
5803b5f6 966PAKFIRE_EXPORT void pakfire_file_set_ctime(struct pakfire_file* file, time_t time) {
37c5d873 967 archive_entry_set_ctime(file->entry, time, 0);
ef4e8460
MT
968}
969
5803b5f6 970PAKFIRE_EXPORT time_t pakfire_file_get_mtime(struct pakfire_file* file) {
37c5d873 971 return archive_entry_mtime(file->entry);
221cc3ce
MT
972}
973
5803b5f6 974PAKFIRE_EXPORT void pakfire_file_set_mtime(struct pakfire_file* file, time_t time) {
37c5d873 975 archive_entry_set_mtime(file->entry, time, 0);
221cc3ce
MT
976}
977
65131b30 978PAKFIRE_EXPORT const unsigned char* pakfire_file_get_digest(
c52e0148 979 struct pakfire_file* file, const enum pakfire_digest_types type, size_t* length) {
65131b30 980
9802aaf6 981 switch (type) {
d98740de
MT
982 case PAKFIRE_DIGEST_SHA3_512:
983 if (!pakfire_digest_set(file->digests.sha3_512))
984 return NULL;
985
986 if (length)
987 *length = sizeof(file->digests.sha3_512);
988
989 return file->digests.sha3_512;
990
991 case PAKFIRE_DIGEST_SHA3_256:
992 if (!pakfire_digest_set(file->digests.sha3_256))
993 return NULL;
994
995 if (length)
996 *length = sizeof(file->digests.sha3_256);
997
998 return file->digests.sha3_256;
999
f1e6c5df
MT
1000 case PAKFIRE_DIGEST_BLAKE2B512:
1001 if (!pakfire_digest_set(file->digests.blake2b512))
1002 return NULL;
1003
1004 if (length)
1005 *length = sizeof(file->digests.blake2b512);
1006
1007 return file->digests.blake2b512;
1008
1009 case PAKFIRE_DIGEST_BLAKE2S256:
1010 if (!pakfire_digest_set(file->digests.blake2s256))
1011 return NULL;
1012
1013 if (length)
1014 *length = sizeof(file->digests.blake2s256);
1015
1016 return file->digests.blake2s256;
1017
4500ed0a
MT
1018 case PAKFIRE_DIGEST_SHA2_512:
1019 if (!pakfire_digest_set(file->digests.sha2_512))
9802aaf6 1020 return NULL;
65131b30 1021
9802aaf6 1022 if (length)
4500ed0a 1023 *length = sizeof(file->digests.sha2_512);
65131b30 1024
4500ed0a 1025 return file->digests.sha2_512;
9802aaf6 1026
4500ed0a
MT
1027 case PAKFIRE_DIGEST_SHA2_256:
1028 if (!pakfire_digest_set(file->digests.sha2_256))
9802aaf6 1029 return NULL;
5e8dfbeb 1030
9802aaf6 1031 if (length)
4500ed0a 1032 *length = sizeof(file->digests.sha2_256);
5e8dfbeb 1033
4500ed0a 1034 return file->digests.sha2_256;
2c4b4a02
MT
1035
1036 case PAKFIRE_DIGEST_UNDEFINED:
1037 break;
5e8dfbeb
MT
1038 }
1039
9802aaf6 1040 return NULL;
5e8dfbeb
MT
1041}
1042
1043PAKFIRE_EXPORT int pakfire_file_set_digest(struct pakfire_file* file,
c52e0148 1044 const enum pakfire_digest_types type, const unsigned char* digest, const size_t length) {
37c5d873
MT
1045 if (!digest)
1046 return -EINVAL;
5e8dfbeb 1047
399d14c2
MT
1048 // Check buffer length
1049 if (pakfire_digest_length(type) != length) {
1050 ERROR(file->pakfire, "Digest has an incorrect length of %zu byte(s)\n", length);
37c5d873 1051 return -ENOMSG;
399d14c2
MT
1052 }
1053
1054 // Store the digest
9802aaf6 1055 switch (type) {
d98740de
MT
1056 case PAKFIRE_DIGEST_SHA3_512:
1057 memcpy(file->digests.sha3_512, digest, sizeof(file->digests.sha3_512));
1058 break;
1059
1060 case PAKFIRE_DIGEST_SHA3_256:
1061 memcpy(file->digests.sha3_256, digest, sizeof(file->digests.sha3_256));
1062 break;
1063
f1e6c5df
MT
1064 case PAKFIRE_DIGEST_BLAKE2B512:
1065 memcpy(file->digests.blake2b512, digest, sizeof(file->digests.blake2b512));
1066 break;
1067
1068 case PAKFIRE_DIGEST_BLAKE2S256:
1069 memcpy(file->digests.blake2s256, digest, sizeof(file->digests.blake2s256));
1070 break;
1071
4500ed0a
MT
1072 case PAKFIRE_DIGEST_SHA2_512:
1073 memcpy(file->digests.sha2_512, digest, sizeof(file->digests.sha2_512));
9802aaf6 1074 break;
65131b30 1075
4500ed0a
MT
1076 case PAKFIRE_DIGEST_SHA2_256:
1077 memcpy(file->digests.sha2_256, digest, sizeof(file->digests.sha2_256));
9802aaf6 1078 break;
2c4b4a02
MT
1079
1080 case PAKFIRE_DIGEST_UNDEFINED:
1081 errno = ENOTSUP;
1082 return 1;
5e8dfbeb
MT
1083 }
1084
5e8dfbeb 1085 return 0;
221cc3ce
MT
1086}
1087
729568d0
MT
1088PAKFIRE_EXPORT int pakfire_file_has_caps(struct pakfire_file* file) {
1089 if (file->caps)
1090 return 1;
1091
1092 return 0;
1093}
1094
1095PAKFIRE_EXPORT char* pakfire_file_get_caps(struct pakfire_file* file) {
1096 char* copy = NULL;
1097 char* text = NULL;
1098 ssize_t length = 0;
1099
1100 if (file->caps) {
1101 text = cap_to_text(file->caps, &length);
1102 if (!text) {
1103 ERROR(file->pakfire, "Could not export capabilities: %m\n");
1104 goto ERROR;
1105 }
1106
1107 // libcap is being weird and uses its own allocator so we have to copy the string
1108 copy = strndup(text, length);
1109 if (!copy)
1110 goto ERROR;
1111 }
1112
1113ERROR:
1114 if (text)
1115 cap_free(text);
1116
1117 return copy;
1118}
1119
5803b5f6 1120static int pakfire_file_levels(struct pakfire_file* file) {
37c5d873
MT
1121 const char* path = pakfire_file_get_path(file);
1122 if (!path)
3fca5032
MT
1123 return 0;
1124
1125 int levels = 0;
1126
37c5d873 1127 for (const char* p = path; *p; p++) {
3fca5032
MT
1128 if (*p == '/')
1129 levels++;
1130 }
1131
1132 return levels;
1133}
1134
d5a13ade 1135FILE* pakfire_file_open(struct pakfire_file* file) {
37c5d873
MT
1136 const char* path = pakfire_file_get_abspath(file);
1137 if (!path)
1138 return NULL;
1139
1140 FILE* f = fopen(path, "r+");
d5a13ade 1141 if (!f)
37c5d873 1142 ERROR(file->pakfire, "Could not open %s: %m\n", path);
d5a13ade
MT
1143
1144 return f;
1145}
1146
fabc09a9
MT
1147int pakfire_file_payload_matches(struct pakfire_file* file,
1148 const void* needle, const size_t length) {
1149 char buffer[1024 * 1024];
1150 FILE* f = NULL;
1151 void* p = NULL;
1152 int r;
1153
37c5d873
MT
1154 const mode_t mode = pakfire_file_get_mode(file);
1155
fabc09a9 1156 // Only run for regular files
37c5d873 1157 if (!S_ISREG(mode))
fabc09a9
MT
1158 return 0;
1159
37c5d873
MT
1160 const size_t size = pakfire_file_get_size(file);
1161
e4d7e9b4 1162 // Skip empty files
37c5d873 1163 if (!size)
e4d7e9b4
MT
1164 return 0;
1165
fabc09a9
MT
1166 // Open the file
1167 f = pakfire_file_open(file);
ede8bda8
MT
1168 if (!f) {
1169 r = 1;
fabc09a9 1170 goto ERROR;
ede8bda8 1171 }
fabc09a9 1172
fabc09a9
MT
1173 while (!feof(f)) {
1174 size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
1175
1176 // Raise any reading errors
1177 if (ferror(f)) {
1178 r = 1;
1179 goto ERROR;
1180 }
1181
1182 // Search for the needle
1183 p = memmem(buffer, bytes_read, needle, length);
fabc09a9
MT
1184 if (p) {
1185 r = 1;
1186 goto ERROR;
1187 }
1188 }
1189
1190 // No match
1191 r = 0;
1192
1193ERROR:
1194 if (f)
1195 fclose(f);
1196
1197 return r;
1198}
1199
1e76689a
MT
1200static int __pakfire_file_compute_digests(struct pakfire_file* file,
1201 struct pakfire_digests* digests, const int types) {
1202 FILE* f = NULL;
1203 int r = 1;
1204
37c5d873
MT
1205 const mode_t mode = pakfire_file_get_mode(file);
1206
1e76689a 1207 // Skip this for anything that isn't a regular file
37c5d873 1208 if (!S_ISREG(mode))
1e76689a
MT
1209 return 0;
1210
1211 // Reset digests
1212 pakfire_digests_reset(digests, types);
1213
1214 // Open the file
1215 f = pakfire_file_open(file);
1216 if (!f)
1217 goto ERROR;
1218
1219 // Compute digests
a6d84de0 1220 r = pakfire_digests_compute_from_file(file->ctx, digests, types, f);
1e76689a
MT
1221 if (r)
1222 goto ERROR;
1223
1224ERROR:
1225 if (f)
1226 fclose(f);
1227
1228 return r;
1229}
1230
1231int pakfire_file_compute_digests(struct pakfire_file* file, const int types) {
6b32db11 1232 return __pakfire_file_compute_digests(file, &file->digests, types);
1e76689a
MT
1233}
1234
9274236e 1235static int pakfire_file_remove(struct pakfire_file* file) {
84eee57a
MT
1236 int r;
1237
37c5d873
MT
1238 const char* path = pakfire_file_get_path(file);
1239 const char* abspath = pakfire_file_get_abspath(file);
1240
1241 DEBUG(file->pakfire, "Removing %s...\n", path);
3fca5032 1242
37c5d873
MT
1243 // We cannot delete if we don't have the absolute path
1244 if (!abspath)
1245 return -ENOTSUP;
3fca5032 1246
37c5d873 1247 switch (pakfire_file_get_type(file)) {
84eee57a
MT
1248 // Call rmdir() for directories
1249 case S_IFDIR:
37c5d873 1250 r = rmdir(abspath);
84eee57a
MT
1251 if (r) {
1252 switch (errno) {
1253 // Ignore when the directory is not empty
1254 case ENOTEMPTY:
1255 r = 0;
1256 break;
1257
1258 // Ignore if the directory didn't exist
1259 case ENOENT:
1260 r = 0;
1261 break;
1262
1263 // Ignore if the directory changed type
1264 case ENOTDIR:
1265 r = 0;
1266 break;
1267
1268 // Log anything else
1269 default:
37c5d873 1270 ERROR(file->pakfire, "Could not remove directory %s: %m\n", path);
84eee57a
MT
1271 break;
1272 }
1273 }
1274 break;
3fca5032 1275
84eee57a
MT
1276 // Call unlink() for everything else
1277 default:
37c5d873 1278 r = unlink(abspath);
84eee57a
MT
1279 if (r) {
1280 switch (errno) {
1281 // Ignore if the file didn't exist
1282 case ENOENT:
1283 r = 0;
1284 break;
1285
1286 default:
37c5d873 1287 ERROR(file->pakfire, "Could not remove %s: %m\n", path);
84eee57a
MT
1288 break;
1289 }
1290 }
1291 break;
3fca5032
MT
1292 }
1293
ac71886a
MT
1294 return r;
1295}
1296
7434fa90 1297int pakfire_file_symlink_target_exists(struct pakfire_file* file) {
37c5d873
MT
1298 const mode_t mode = pakfire_file_get_mode(file);
1299
7434fa90 1300 // Fail if this got called for anything that isn't a symlink
37c5d873
MT
1301 if (!S_ISLNK(mode))
1302 return -EINVAL;
7434fa90 1303
37c5d873
MT
1304 const char* path = pakfire_file_get_abspath(file);
1305
1306 return pakfire_path_exists(path);
7434fa90
MT
1307}
1308
210aabe9
MT
1309/*
1310 MIME Type
1311*/
1312
1313int pakfire_file_detect_mimetype(struct pakfire_file* file) {
37c5d873
MT
1314 const mode_t mode = pakfire_file_get_mode(file);
1315
210aabe9 1316 // Only process regular files
37c5d873 1317 if (!S_ISREG(mode))
210aabe9
MT
1318 return 0;
1319
1320 // Skip if MIME type is already set
1321 if (*file->mimetype)
1322 return 0;
1323
1324 // Fetch the magic cookie
1325 magic_t magic = pakfire_get_magic(file->pakfire);
1326 if (!magic)
1327 return 1;
1328
37c5d873
MT
1329 const char* path = pakfire_file_get_abspath(file);
1330
210aabe9 1331 // Check the file
37c5d873 1332 const char* mimetype = magic_file(magic, path);
210aabe9 1333 if (!mimetype) {
37c5d873
MT
1334 ERROR(file->pakfire, "Could not classify %s: %s\n",
1335 pakfire_file_get_path(file), magic_error(magic));
210aabe9
MT
1336 return 1;
1337 }
1338
37c5d873 1339 DEBUG(file->pakfire, "Classified %s as %s\n", pakfire_file_get_path(file), mimetype);
210aabe9
MT
1340
1341 // Store the value
1342 return pakfire_file_set_mimetype(file, mimetype);
1343}
1344
1345PAKFIRE_EXPORT const char* pakfire_file_get_mimetype(struct pakfire_file* file) {
1346 // Return nothing on an empty mimetype
1347 if (!*file->mimetype)
1348 return NULL;
1349
1350 return file->mimetype;
1351}
1352
1353PAKFIRE_EXPORT int pakfire_file_set_mimetype(
1354 struct pakfire_file* file, const char* mimetype) {
1355 // Store the value
1356 return pakfire_string_set(file->mimetype, mimetype);
1357}
1358
71f6f465
MT
1359/*
1360 Classification
1361*/
1362
df71dc60
MT
1363static int setup_libelf(struct pakfire* pakfire) {
1364 // Initialize libelf
1365 if (elf_version(EV_CURRENT) == EV_NONE) {
1366 ERROR(pakfire, "Could not initialize libelf: %s\n", elf_errmsg(-1));
1367
1368 return 1;
1369 }
1370
1371 return 0;
1372}
1373
71f6f465 1374static int pakfire_file_classify_mode(struct pakfire_file* file) {
37c5d873
MT
1375 const mode_t mode = pakfire_file_get_mode(file);
1376
71f6f465 1377 // Check for regular files
37c5d873 1378 if (S_ISREG(mode)) {
71f6f465
MT
1379 file->class |= PAKFIRE_FILE_REGULAR;
1380
72a36c9d 1381 // Does the file have executable permissions?
37c5d873 1382 if (mode & (S_IXUSR|S_IXGRP|S_IXOTH))
72a36c9d
MT
1383 file->class |= PAKFIRE_FILE_EXECUTABLE;
1384
71f6f465 1385 // Check for directories
37c5d873 1386 } else if (S_ISDIR(mode))
71f6f465
MT
1387 file->class |= PAKFIRE_FILE_DIRECTORY;
1388
1389 // Check for symlinks
37c5d873 1390 else if (S_ISLNK(mode))
71f6f465
MT
1391 file->class |= PAKFIRE_FILE_SYMLINK;
1392
1393 // Check for character devices
37c5d873 1394 else if (S_ISCHR(mode))
71f6f465
MT
1395 file->class |= PAKFIRE_FILE_CHARACTER;
1396
1397 // Check for block devices
37c5d873 1398 else if (S_ISBLK(mode))
71f6f465
MT
1399 file->class |= PAKFIRE_FILE_BLOCK;
1400
1401 // Check for FIFO pipes
37c5d873 1402 else if (S_ISFIFO(mode))
71f6f465
MT
1403 file->class |= PAKFIRE_FILE_FIFO;
1404
1405 // Check for sockets
37c5d873 1406 else if (S_ISSOCK(mode))
71f6f465
MT
1407 file->class |= PAKFIRE_FILE_SOCKET;
1408
1409 return 0;
1410}
1411
239230c2
MT
1412static const struct pattern {
1413 const char* pattern;
71f6f465 1414 int class;
239230c2 1415} patterns[] = {
c77b52ac
MT
1416 { "**.a", PAKFIRE_FILE_STATIC_LIBRARY },
1417 { "**.la", PAKFIRE_FILE_LIBTOOL_ARCHIVE },
1418 { "**.pm", PAKFIRE_FILE_PERL },
1419 { "**.pc", PAKFIRE_FILE_PKGCONFIG },
1420 { "/usr/lib/firmware/**", PAKFIRE_FILE_FIRMWARE },
1421 { "/usr/lib/grub/**", PAKFIRE_FILE_FIRMWARE },
65eb1fbf 1422 { "/usr/lib/valgrind/*", PAKFIRE_FILE_FIRMWARE },
fc35e5cc 1423 { "/usr/lib*/ld-*.so*", PAKFIRE_FILE_RUNTIME_LINKER },
239230c2 1424 { NULL },
71f6f465
MT
1425};
1426
239230c2
MT
1427static int pakfire_file_classify_pattern(struct pakfire_file* file) {
1428 for (const struct pattern* p = patterns; p->pattern; p++) {
1429 if (pakfire_file_matches(file, p->pattern)) {
1430 file->class |= p->class;
71f6f465
MT
1431 break;
1432 }
1433 }
1434
1435 return 0;
1436}
1437
1438static const struct mimetype {
1439 const char* mimetype;
1440 int class;
1441} mimetypes[] = {
71f6f465
MT
1442 { "text/x-perl", PAKFIRE_FILE_PERL },
1443 { NULL, 0 },
1444};
1445
1446static int pakfire_file_classify_magic(struct pakfire_file* file) {
210aabe9
MT
1447 int r;
1448
210aabe9
MT
1449 // Detect the MIME type
1450 r = pakfire_file_detect_mimetype(file);
1451 if (r)
1452 return r;
71f6f465 1453
210aabe9
MT
1454 // Fetch the MIME type
1455 const char* mimetype = pakfire_file_get_mimetype(file);
1456 if (!mimetype)
71f6f465 1457 return 1;
71f6f465 1458
210aabe9 1459 // Match the MIME type with a flag
71f6f465
MT
1460 for (const struct mimetype* m = mimetypes; m->mimetype; m++) {
1461 if (strcmp(m->mimetype, mimetype) == 0) {
1462 file->class |= m->class;
1463 break;
1464 }
1465 }
1466
1467 return 0;
1468}
1469
df71dc60
MT
1470static int pakfire_file_classify_elf(struct pakfire_file* file) {
1471 FILE* f = NULL;
1472 Elf* elf = NULL;
1473 int r;
1474
1475 // Don't run this if we already know that file is an ELF file
1476 if (file->class & PAKFIRE_FILE_ELF)
1477 return 0;
1478
1479 // Setup libelf
1480 r = setup_libelf(file->pakfire);
1481 if (r)
1482 return r;
1483
1484 // Open the file
37c5d873 1485 f = pakfire_file_open(file);
df71dc60 1486 if (!f) {
37c5d873 1487 ERROR(file->pakfire, "Could not open %s: %m\n", pakfire_file_get_path(file));
df71dc60
MT
1488 return 1;
1489 }
1490
1491 // Try to open the ELF file
1492 elf = elf_begin(fileno(f), ELF_C_READ, NULL);
1493 if (!elf) {
1494 // We fail silently here, because this file might be in a different format
1495 goto ERROR;
1496 }
1497
1498 switch (elf_kind(elf)) {
1499 // Mark this file as an ELF file
1500 case ELF_K_ELF:
1501 file->class |= PAKFIRE_FILE_ELF;
1502 break;
1503
1504 // Ignore everything else
1505 default:
1506 break;
1507 }
1508
1509ERROR:
1510 if (elf)
1511 elf_end(elf);
1512 if (f)
1513 fclose(f);
1514
1515 return 0;
1516}
1517
71f6f465
MT
1518int pakfire_file_classify(struct pakfire_file* file) {
1519 int r;
1520
1521 if (!file->class) {
1522 // First, check the mode so that we won't run magic on directories, symlinks, ...
1523 r = pakfire_file_classify_mode(file);
1524 if (r)
1525 goto ERROR;
1526
1527 // Only run this for regular files
1528 if (file->class & PAKFIRE_FILE_REGULAR) {
239230c2
MT
1529 // Then check for patterns
1530 r = pakfire_file_classify_pattern(file);
71f6f465
MT
1531 if (r)
1532 goto ERROR;
1533
1534 // After that, we will use libmagic...
1535 r = pakfire_file_classify_magic(file);
1536 if (r)
df71dc60
MT
1537 goto ERROR;
1538
1539 // Check if the file is an ELF file
1540 r = pakfire_file_classify_elf(file);
1541 if (r)
71f6f465
MT
1542 goto ERROR;
1543 }
1544 }
1545
1546 return file->class;
1547
1548ERROR:
1549 // Reset the class
1550 file->class = PAKFIRE_FILE_UNKNOWN;
1551
1552 return r;
1553}
1554
1555int pakfire_file_matches_class(struct pakfire_file* file, const int class) {
1556 return pakfire_file_classify(file) & class;
1557}
1558
ac71886a
MT
1559/*
1560 This function tries to remove the file after it has been packaged.
1561
1562 It will try to delete any parent directories as well and ignore if directories
1563 cannot be deleted because they might contain other files
1564*/
9274236e 1565int pakfire_file_cleanup(struct pakfire_file* file, int flags) {
ac71886a
MT
1566 char path[PATH_MAX];
1567
1568 // Try removing the file
1569 int r = pakfire_file_remove(file);
1570 if (r)
1571 return r;
1572
9274236e
MT
1573 // Try to tidy up afterwards
1574 if (flags & PAKFIRE_FILE_CLEANUP_TIDY) {
37c5d873
MT
1575 const char* abspath = pakfire_file_get_abspath(file);
1576
9274236e 1577 // Create a working copy of abspath
37c5d873 1578 r = pakfire_string_set(path, abspath);
9274236e
MT
1579 if (r)
1580 return r;
3fca5032 1581
9274236e
MT
1582 // See how many levels this file has
1583 int levels = pakfire_file_levels(file);
3fca5032 1584
9274236e
MT
1585 // Walk all the way up and remove all parent directories if possible
1586 while (--levels) {
1587 dirname(path);
3fca5032 1588
9274236e
MT
1589 // Break if path is suddenly empty
1590 if (!*path)
1591 break;
3fca5032 1592
9274236e 1593 DEBUG(file->pakfire, "Trying to remove parent directory %s\n", path);
a8783709 1594
9274236e 1595 r = rmdir(path);
3fca5032 1596
9274236e
MT
1597 // Break on any error
1598 if (r)
1599 break;
1600 }
3fca5032
MT
1601 }
1602
a942166c 1603 return 0;
3fca5032 1604}
cf9bd6f4 1605
9e09e361 1606static int pakfire_file_verify_mode(struct pakfire_file* file, const struct stat* st) {
a09b95e0
MT
1607 const mode_t type = pakfire_file_get_type(file);
1608
9e09e361 1609 // Did the type change?
a09b95e0 1610 if (type != (st->st_mode & S_IFMT)) {
9e09e361
MT
1611 file->verify_status |= PAKFIRE_FILE_TYPE_CHANGED;
1612
37c5d873 1613 DEBUG(file->pakfire, "%s: File Type changed\n", pakfire_file_get_path(file));
9e09e361
MT
1614 }
1615
a09b95e0
MT
1616 const mode_t perms = pakfire_file_get_perms(file);
1617
9e09e361 1618 // Check permissions
a09b95e0 1619 if (perms != (st->st_mode & 0777)) {
9e09e361
MT
1620 file->verify_status |= PAKFIRE_FILE_PERMISSIONS_CHANGED;
1621
37c5d873 1622 DEBUG(file->pakfire, "%s: Permissions changed\n", pakfire_file_get_path(file));
9e09e361
MT
1623 }
1624
f8733b31
MT
1625#if 0
1626 // XXX This does not check what it is supposed to check
1627
9e09e361
MT
1628 // Check if device node changed
1629 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
a09b95e0
MT
1630 const dev_t dev = pakfire_file_get_dev(file);
1631
1632 if (dev != st->st_dev) {
9e09e361
MT
1633 file->verify_status |= PAKFIRE_FILE_DEV_CHANGED;
1634
37c5d873 1635 DEBUG(file->pakfire, "%s: Device Node changed\n", pakfire_file_get_path(file));
9e09e361
MT
1636 }
1637 }
f8733b31 1638#endif
9e09e361
MT
1639
1640 return 0;
1641}
1642
c0b051bb 1643static int pakfire_file_verify_size(struct pakfire_file* file, const struct stat* st) {
37c5d873
MT
1644 const ssize_t size = pakfire_file_get_size(file);
1645
c0b051bb 1646 // Nothing to do if size matches
37c5d873 1647 if (size == st->st_size)
c0b051bb
MT
1648 return 0;
1649
1650 // Size differs
d2b0e219 1651 file->verify_status |= PAKFIRE_FILE_SIZE_CHANGED;
c0b051bb 1652
a8a41064 1653 DEBUG(file->pakfire, "%s: Filesize differs (expected %zd, got %zd byte(s))\n",
37c5d873 1654 pakfire_file_get_path(file), size, st->st_size);
c0b051bb
MT
1655
1656 return 0;
1657}
1658
1659static int pakfire_file_verify_ownership(struct pakfire_file* file, const struct stat* st) {
1660 // Fetch UID/GID
1661#if 0
1662 const uid_t uid = pakfire_unmap_id(file->pakfire, st->st_uid);
1663 const gid_t gid = pakfire_unmap_id(file->pakfire, st->st_gid);
1664#else
1665 const uid_t uid = st->st_uid;
1666 const gid_t gid = st->st_gid;
1667#endif
1668
37c5d873
MT
1669 const char* uname = pakfire_file_get_uname(file);
1670 const char* gname = pakfire_file_get_gname(file);
1671
c0b051bb 1672 // Fetch owner & group
37c5d873
MT
1673 struct passwd* owner = pakfire_getpwnam(file->pakfire, uname);
1674 struct group* group = pakfire_getgrnam(file->pakfire, gname);
c0b051bb
MT
1675
1676 // Check if owner matches
1677 if (!owner || owner->pw_uid != uid) {
d2b0e219 1678 file->verify_status |= PAKFIRE_FILE_OWNER_CHANGED;
c0b051bb 1679
37c5d873 1680 DEBUG(file->pakfire, "%s: Owner differs\n", pakfire_file_get_path(file));
c0b051bb
MT
1681 }
1682
1683 // Check if group matches
1684 if (!group || group->gr_gid != gid) {
d2b0e219 1685 file->verify_status |= PAKFIRE_FILE_GROUP_CHANGED;
c0b051bb 1686
37c5d873 1687 DEBUG(file->pakfire, "%s: Group differs\n", pakfire_file_get_path(file));
c0b051bb
MT
1688 }
1689
1690 return 0;
1691}
1692
0eeac4a5 1693static int pakfire_file_verify_timestamps(struct pakfire_file* file, const struct stat* st) {
37c5d873
MT
1694 const time_t ctime = pakfire_file_get_ctime(file);
1695 const time_t mtime = pakfire_file_get_mtime(file);
1696
0eeac4a5 1697 // Check creation time
37c5d873 1698 if (ctime != st->st_ctime) {
0eeac4a5
MT
1699 file->verify_status |= PAKFIRE_FILE_CTIME_CHANGED;
1700
37c5d873 1701 DEBUG(file->pakfire, "%s: Creation time changed\n", pakfire_file_get_path(file));
0eeac4a5
MT
1702 }
1703
1704 // Check modification time
37c5d873 1705 if (mtime != st->st_mtime) {
0eeac4a5
MT
1706 file->verify_status |= PAKFIRE_FILE_MTIME_CHANGED;
1707
37c5d873 1708 DEBUG(file->pakfire, "%s: Modification time changed\n", pakfire_file_get_path(file));
0eeac4a5
MT
1709 }
1710
1711 return 0;
1712}
1713
76011205 1714static int pakfire_file_verify_payload(struct pakfire_file* file, const struct stat* st) {
76011205
MT
1715 int r;
1716
c52e0148 1717 struct pakfire_digests computed_digests;
293881bc 1718 int digest_types = PAKFIRE_DIGEST_UNDEFINED;
76011205
MT
1719
1720 // Nothing to do for anything that isn't a regular file
1721 if (!S_ISREG(st->st_mode))
1722 return 0;
1723
1724 // Fast-path if size changed. The payload will have changed, too
1725 if (file->verify_status & PAKFIRE_FILE_SIZE_CHANGED) {
1726 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1727 return 0;
1728 }
1729
1730 // Check if this file has any digests at all
6b32db11 1731 digest_types = pakfire_digest_has_any(&file->digests);
76011205 1732
293881bc 1733 if (!digest_types) {
37c5d873 1734 ERROR(file->pakfire, "%s: No digests available\n", pakfire_file_get_path(file));
293881bc 1735 return 0;
76011205
MT
1736 }
1737
293881bc 1738 // Compute digests
1e76689a 1739 r = __pakfire_file_compute_digests(file, &computed_digests, digest_types);
293881bc 1740 if (r)
76011205 1741 goto ERROR;
76011205 1742
293881bc 1743 // Compare digests
a6d84de0 1744 r = pakfire_digests_compare(file->ctx, &file->digests, &computed_digests, digest_types);
76011205
MT
1745 if (r) {
1746 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1747
37c5d873 1748 DEBUG(file->pakfire, "%s: Digest(s) do not match\n", pakfire_file_get_path(file));
76011205
MT
1749 }
1750
76011205 1751ERROR:
76011205
MT
1752 return r;
1753}
1754
cf9bd6f4
MT
1755/*
1756 Verify the file - i.e. does the metadata match what is on disk?
1757*/
c0b051bb
MT
1758int pakfire_file_verify(struct pakfire_file* file, int* status) {
1759 struct stat st;
1760 int r;
1761
37c5d873
MT
1762 DEBUG(file->pakfire, "Verifying %s...\n", pakfire_file_get_path(file));
1763
1764 const char* abspath = pakfire_file_get_abspath(file);
cf9bd6f4 1765
c0b051bb 1766 // stat() the file
37c5d873 1767 r = lstat(abspath, &st);
c0b051bb
MT
1768 if (r) {
1769 // File does not exist
1770 if (errno == ENOENT) {
1771 file->verify_status |= PAKFIRE_FILE_NOENT;
1772 return 1;
1773 }
1774
1775 // Raise any other errors from stat()
1776 return r;
1777 }
1778
9e09e361
MT
1779 // Verify mode
1780 r = pakfire_file_verify_mode(file, &st);
1781 if (r)
1782 return r;
1783
c0b051bb
MT
1784 // Verify size
1785 r = pakfire_file_verify_size(file, &st);
1786 if (r)
1787 return r;
1788
1789 // Verify ownership
1790 r = pakfire_file_verify_ownership(file, &st);
1791 if (r)
1792 return r;
1793
0eeac4a5
MT
1794 // Verify timestamps
1795 r = pakfire_file_verify_timestamps(file, &st);
1796 if (r)
1797 return r;
1798
76011205
MT
1799 // Verify payload
1800 r = pakfire_file_verify_payload(file, &st);
1801 if (r)
1802 return r;
1803
cf9bd6f4
MT
1804 return 0;
1805}
c064d9ec
MT
1806
1807PAKFIRE_EXPORT int pakfire_file_matches(struct pakfire_file* file, const char* pattern) {
c064d9ec
MT
1808 // Don't match on no pattern
1809 if (!pattern)
1810 return 0;
1811
37c5d873 1812 const char* path = pakfire_file_get_path(file);
c064d9ec 1813
37c5d873 1814 return pakfire_path_match(pattern, path);
c064d9ec 1815}
2dcb43e7
MT
1816
1817/*
1818 ELF Stuff
1819*/
1820
1821static int pakfire_file_open_elf(struct pakfire_file* file,
1822 int (*callback)(struct pakfire_file* file, Elf* elf, void* data), void* data) {
1823 FILE* f = NULL;
1824 Elf* elf = NULL;
1825 int r;
1826
1827 // Don't run this for non-ELF files
1828 if (!pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
1829 errno = EINVAL;
1830 return 1;
1831 }
1832
1833 // Setup libelf
1834 r = setup_libelf(file->pakfire);
1835 if (r)
1836 return r;
1837
1838 // Open the file
37c5d873 1839 f = pakfire_file_open(file);
2dcb43e7 1840 if (!f) {
37c5d873 1841 ERROR(file->pakfire, "Could not open %s: %m\n", pakfire_file_get_abspath(file));
2dcb43e7
MT
1842 return 1;
1843 }
1844
1845 // Parse the ELF header
1846 elf = elf_begin(fileno(f), ELF_C_READ, NULL);
1847 if (!elf) {
1848 ERROR(file->pakfire, "Could not open ELF file: %s\n", elf_errmsg(-1));
1849 r = 1;
1850 goto ERROR;
1851 }
1852
1853 // Check if this is an ELF file
1854 switch (elf_kind(elf)) {
1855 case ELF_K_ELF:
1856 break;
1857
1858 default:
37c5d873 1859 ERROR(file->pakfire, "%s is not an ELF object\n", pakfire_file_get_path(file));
2dcb43e7
MT
1860 r = 1;
1861 goto ERROR;
1862 }
1863
1864 // Call the callback
1865 r = callback(file, elf, data);
1866
1867ERROR:
1868 if (elf)
1869 elf_end(elf);
1870 if (f)
1871 fclose(f);
1872
1873 return r;
1874}
1875
dbae97eb
MT
1876typedef int (*__pakfire_file_for_elf_section_callback)(
1877 struct pakfire_file* file,
1878 Elf* elf,
1879 const Elf_Scn* section,
1880 const GElf_Shdr* shdr,
1881 Elf_Data* data);
1882
1883static int pakfire_file_for_elf_section(struct pakfire_file* file,
1884 Elf* elf, const Elf64_Word type, __pakfire_file_for_elf_section_callback callback) {
1885 Elf_Scn* section = NULL;
1886 GElf_Shdr shdr;
1887 Elf_Data* data;
1888 int r;
1889
1890 // Walk through all sections
1891 for (;;) {
1892 section = elf_nextscn(elf, section);
1893 if (!section)
1894 break;
1895
1896 // Fetch the section header
1897 gelf_getshdr(section, &shdr);
1898
1899 // Skip sections that don't match
1900 if (type && shdr.sh_type != type)
1901 continue;
1902
1903 // Fetch the data
1904 data = elf_getdata(section, NULL);
1905
1906 // Call the callback
1907 r = callback(file, elf, section, &shdr, data);
1908 if (r)
1909 break;
1910 }
1911
1912 return r;
1913}
1914
e9d5d508
MT
1915static int pakfire_file_get_elf_section(struct pakfire_file* file,
1916 Elf* elf, const Elf64_Word type, Elf_Scn** section, GElf_Shdr* header, Elf_Data** data) {
1917 Elf_Scn* s = NULL;
1918
0ef20579
MT
1919 GElf_Shdr shdr;
1920
1921 // Walk through all sections
1922 for (;;) {
e9d5d508
MT
1923 s = elf_nextscn(elf, s);
1924 if (!s)
0ef20579
MT
1925 break;
1926
1927 // Fetch the section header
e9d5d508 1928 gelf_getshdr(s, &shdr);
0ef20579
MT
1929
1930 // Return any matching sections
e9d5d508
MT
1931 if (shdr.sh_type == type) {
1932 *section = s;
1933
1934 // Send header if requested
1935 if (header)
1936 gelf_getshdr(s, header);
1937
1938 // Send data if requested
1939 if (data)
1940 *data = elf_getdata(s, NULL);
1941
1942 return 0;
1943 }
0ef20579
MT
1944 }
1945
1946 // No section found
e9d5d508 1947 return 1;
0ef20579
MT
1948}
1949
8a385eff
MT
1950static int __pakfire_file_get_elf_type(struct pakfire_file* file, Elf* elf, void* data) {
1951 int* type = (int*)data;
1952 GElf_Ehdr ehdr;
1953
1954 // Fetch the ELF header
1955 if (!gelf_getehdr(elf, &ehdr)) {
1956 ERROR(file->pakfire, "Could not parse ELF header: %s\n", elf_errmsg(-1));
1957 return 1;
1958 }
1959
1960 // Store the type
1961 *type = ehdr.e_type;
1962
1963 return 0;
1964}
1965
1966static int pakfire_file_get_elf_type(struct pakfire_file* file) {
1967 int type = ET_NONE;
1968 int r;
1969
1970 r = pakfire_file_open_elf(file, __pakfire_file_get_elf_type, &type);
1971 if (r)
1972 return -1;
1973
1974 return type;
1975}
1976
7b461c8b 1977static int pakfire_file_elf_dyn_walk(struct pakfire_file* file, Elf* elf,
4bea58f5
MT
1978 int (*callback)(struct pakfire_file* file,
1979 Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data),
7b461c8b
MT
1980 void* data) {
1981 Elf_Scn* dynamic = NULL;
1982 GElf_Shdr shdr;
1983 Elf_Data* elf_data = NULL;
4bea58f5 1984 GElf_Dyn dyn;
7b461c8b
MT
1985 int r;
1986
1987 // Find the dynamic linking information
1988 r = pakfire_file_get_elf_section(file, elf, SHT_DYNAMIC, &dynamic, &shdr, &elf_data);
1989 if (r) {
a17d0a4b
MT
1990 DEBUG(file->pakfire, "%s does not have a dynamic section\n", pakfire_file_get_path(file));
1991 return 0;
7b461c8b
MT
1992 }
1993
7b461c8b
MT
1994 // Walk through all entries...
1995 for (unsigned int i = 0; ; i++) {
1996 // Fetch the next entry
1997 if (!gelf_getdyn(elf_data, i, &dyn))
1998 break;
1999
7b461c8b 2000 // Call the callback
4bea58f5 2001 r = callback(file, elf, &shdr, &dyn, data);
7b461c8b
MT
2002 if (r)
2003 return r;
2004 }
2005
2006 return 0;
2007}
2008
03a5ac95 2009static int __pakfire_file_check_debuginfo(struct pakfire_file* file, Elf* elf, void* data) {
e9d5d508
MT
2010 Elf_Scn* symtab = NULL;
2011 int r;
2012
0ef20579 2013 // Fetch the symbol table
e9d5d508 2014 r = pakfire_file_get_elf_section(file, elf, SHT_SYMTAB, &symtab, NULL, NULL);
8e8de3c1
MT
2015
2016 // Not found
e9d5d508 2017 if (r) {
37c5d873 2018 DEBUG(file->pakfire, "%s has no debug sections\n", pakfire_file_get_path(file));
8e8de3c1 2019
0ef20579
MT
2020 // Store the result
2021 file->issues |= PAKFIRE_FILE_MISSING_DEBUGINFO;
2022 }
8e8de3c1 2023
03a5ac95
MT
2024 return 0;
2025}
ec95c0c4 2026
03a5ac95 2027static int pakfire_file_check_debuginfo(struct pakfire_file* file) {
8a385eff
MT
2028 switch (pakfire_file_get_elf_type(file)) {
2029 // Do not check Relocatable Objects
2030 case ET_REL:
2031 return 0;
2032
2033 // Check everything else
2034 default:
2035 break;
2036 }
2037
03a5ac95 2038 return pakfire_file_open_elf(file, __pakfire_file_check_debuginfo, NULL);
8e8de3c1
MT
2039}
2040
28886e21 2041static int __pakfire_file_check_ssp(
2dcb43e7 2042 struct pakfire_file* file, Elf* elf, void* data) {
0ef20579
MT
2043 Elf_Scn* symtab = NULL;
2044 GElf_Shdr shdr;
2dcb43e7
MT
2045 Elf_Data* elf_data = NULL;
2046 GElf_Sym symbol;
2047 const char* name = NULL;
e9d5d508 2048 int r;
2dcb43e7 2049
0ef20579 2050 // Fetch the symbol table
e9d5d508
MT
2051 r = pakfire_file_get_elf_section(file, elf, SHT_SYMTAB, &symtab, &shdr, &elf_data);
2052 if (r) {
37c5d873 2053 ERROR(file->pakfire, "%s has no symbol table\n", pakfire_file_get_path(file));
0ef20579
MT
2054 return 1;
2055 }
2dcb43e7 2056
0ef20579
MT
2057 // Count any global functions
2058 size_t counter = 0;
2dcb43e7 2059
2dcb43e7 2060 // Walk through all symbols
0ef20579 2061 for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
2dcb43e7
MT
2062 gelf_getsym(elf_data, i, &symbol);
2063
2064 // Fetch the symbol name
0ef20579 2065 name = elf_strptr(elf, shdr.sh_link, symbol.st_name);
2dcb43e7
MT
2066
2067 // Skip empty section names
2068 if (!name || !*name)
2069 continue;
2070
f7f44921 2071 // Exit if there is a symbol called "__stack_chk_fail"
2dcb43e7 2072 if (pakfire_string_startswith(name, "__stack_chk_fail"))
f7f44921 2073 return 0;
0f7c90ef
MT
2074
2075 // Count any global functions
2076 if ((ELF64_ST_BIND(symbol.st_info) == STB_GLOBAL) &&
2077 (ELF64_ST_TYPE(symbol.st_info) == STT_FUNC))
2078 counter++;
2079 }
2080
2081 // We do not perform the check for libraries that do not contain any functions.
2082 // Some packages use shared libraries to provide data.
2083 if (!counter) {
37c5d873
MT
2084 DEBUG(file->pakfire, "%s: File has no functions. Skipping SSP check.\n",
2085 pakfire_file_get_path(file));
0f7c90ef 2086 return 0;
2dcb43e7
MT
2087 }
2088
f7f44921 2089 // The file does not seem to have SSP enabled
d8acc837 2090 file->issues |= PAKFIRE_FILE_MISSING_SSP;
f7f44921 2091
2dcb43e7
MT
2092 return 0;
2093}
2094
28886e21 2095static int pakfire_file_check_ssp(struct pakfire_file* file) {
d7761941
MT
2096 // This check will be skipped for these files
2097 static const char* whitelist[] = {
2098 "/usr/lib64/libgcc_s.so.*",
2099 "/usr/lib64/libmvec.so.*",
2100 NULL,
2101 };
2102
fc35e5cc
MT
2103 // Do not perform this check for runtime linkers
2104 if (pakfire_file_matches_class(file, PAKFIRE_FILE_RUNTIME_LINKER))
2105 return 0;
2106
03a5ac95
MT
2107 // We cannot perform this check if we don't have debuginfo
2108 if (file->issues & PAKFIRE_FILE_MISSING_DEBUGINFO)
2109 return 0;
2110
d7761941
MT
2111 // Check if this file is whitelisted
2112 for (const char** path = whitelist; *path; path++) {
2113 if (pakfire_file_matches(file, *path)) {
2114 DEBUG(file->pakfire, "Skipping SSP check for whitelisted file %s\n",
2115 pakfire_file_get_path(file));
2116 return 0;
2117 }
2118 }
2119
28886e21 2120 return pakfire_file_open_elf(file, __pakfire_file_check_ssp, NULL);
2dcb43e7 2121}
2a67d72c 2122
28886e21 2123static int pakfire_file_check_pie(struct pakfire_file* file) {
8a385eff
MT
2124 switch (pakfire_file_get_elf_type(file)) {
2125 // Shared Object files are good
2a67d72c 2126 case ET_DYN:
22e696bd 2127 break;
2a67d72c 2128
8a385eff 2129 // Everything else is bad
2a67d72c 2130 default:
d8acc837 2131 file->issues |= PAKFIRE_FILE_MISSING_PIE;
22e696bd 2132 break;
2a67d72c 2133 }
22e696bd
MT
2134
2135 return 0;
f7f44921
MT
2136}
2137
28886e21 2138static int __pakfire_file_check_execstack(
dae43e1f
MT
2139 struct pakfire_file* file, Elf* elf, void* data) {
2140 GElf_Phdr phdr;
2141 int r;
2142
2143 size_t phnum = 0;
2144
2145 // Fetch the total numbers of program headers
2146 r = elf_getphdrnum(elf, &phnum);
2147 if (r) {
2148 ERROR(file->pakfire, "Could not fetch number of program headers: %s\n",
2149 elf_errmsg(-1));
2150 return 1;
2151 }
2152
2153 // Walk through all program headers
2154 for (unsigned int i = 0; i < phnum; i++) {
2155 if (!gelf_getphdr(elf, i, &phdr)) {
2156 ERROR(file->pakfire, "Could not parse program header: %s\n", elf_errmsg(-1));
2157 return 1;
2158 }
2159
2160 switch (phdr.p_type) {
2161 case PT_GNU_STACK:
2162 DEBUG(file->pakfire,
2163 "%s: GNU_STACK flags: %c%c%c\n",
37c5d873 2164 pakfire_file_get_path(file),
dae43e1f
MT
2165 (phdr.p_flags & PF_R) ? 'R' : '-',
2166 (phdr.p_flags & PF_W) ? 'W' : '-',
2167 (phdr.p_flags & PF_X) ? 'X' : '-'
2168 );
2169
2170 // The stack cannot be writable and executable
2171 if ((phdr.p_flags & PF_W) && (phdr.p_flags & PF_X))
28886e21 2172 file->issues |= PAKFIRE_FILE_EXECSTACK;
dae43e1f
MT
2173
2174 // Done
2175 return 0;
2176
2177 default:
2178 break;
2179 }
2180 }
2181
2182 return 0;
2183}
2184
28886e21
MT
2185static int pakfire_file_check_execstack(struct pakfire_file* file) {
2186 return pakfire_file_open_elf(file, __pakfire_file_check_execstack, NULL);
dae43e1f
MT
2187}
2188
9a789d72
MT
2189static int __pakfire_file_process_bind_now(struct pakfire_file* file,
2190 Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) {
2191 int* has_bind_now = (int*)data;
2192
2193 switch (dyn->d_tag) {
2194 case DT_BIND_NOW:
2195 *has_bind_now = 1;
2196 break;
2197
2198 case DT_FLAGS:
2199 if (dyn->d_un.d_val & DF_BIND_NOW)
2200 *has_bind_now = 1;
2201 break;
2202
2203 case DT_FLAGS_1:
2204 if (dyn->d_un.d_val & DF_1_NOW)
2205 *has_bind_now = 1;
2206 break;
2207
2208 default:
2209 break;
2210 }
2211
2212 return 0;
2213}
2214
2215static int __pakfire_file_check_relro(
d265bed6 2216 struct pakfire_file* file, Elf* elf, void* data) {
9a789d72 2217 int has_bind_now = 0;
d265bed6
MT
2218 GElf_Phdr phdr;
2219 int r;
2220
9a789d72
MT
2221 // Check if we have BIND_NOW
2222 r = pakfire_file_elf_dyn_walk(file, elf,
2223 __pakfire_file_process_bind_now, &has_bind_now);
2224 if (r)
2225 return r;
d265bed6 2226
9a789d72
MT
2227 // We are not fully RELRO
2228 if (!has_bind_now) {
2229 file->issues |= PAKFIRE_FILE_NO_RELRO;
2230
2231 return 0;
d265bed6
MT
2232 }
2233
2234 // Walk through all program headers
9a789d72
MT
2235 for (unsigned int i = 0;; i++) {
2236 if (!gelf_getphdr(elf, i, &phdr))
2237 break;
d265bed6
MT
2238
2239 switch (phdr.p_type) {
2240 case PT_GNU_RELRO:
2241 return 0;
2242
2243 default:
2244 break;
2245 }
2246 }
2247
2248 // This file does not seem to have PT_GNU_RELRO set
4bea58f5 2249 file->issues |= PAKFIRE_FILE_NO_RELRO;
d265bed6
MT
2250
2251 return 0;
2252}
2253
28886e21 2254static int pakfire_file_check_relro(struct pakfire_file* file) {
4bea58f5 2255 return pakfire_file_open_elf(file, __pakfire_file_check_relro, NULL);
d265bed6
MT
2256}
2257
7b461c8b
MT
2258/*
2259 RPATH/RUNPATH
2260*/
2261static int __pakfire_file_process_runpath(struct pakfire_file* file,
4bea58f5
MT
2262 Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) {
2263 const char* value = NULL;
7b461c8b
MT
2264 const char* runpath = NULL;
2265 char buffer[PATH_MAX];
2266 char* p = NULL;
2267 int r;
2268
4bea58f5 2269 switch (dyn->d_tag) {
7b461c8b
MT
2270 case DT_RUNPATH:
2271 case DT_RPATH:
4bea58f5
MT
2272 // Fetch the value
2273 value = elf_strptr(elf, shdr->sh_link, dyn->d_un.d_val);
2274 if (!value)
2275 return 1;
2276
37c5d873
MT
2277 DEBUG(file->pakfire, "%s has a RUNPATH: %s\n",
2278 pakfire_file_get_path(file), value);
7b461c8b
MT
2279
2280 // Copy the value into a buffer we can modify
2281 r = pakfire_string_set(buffer, value);
2282 if (r)
2283 return r;
2284
2285 // Split the value by :
2286 runpath = strtok_r(buffer, ":", &p);
2287
2288 // Iterate over all elements
2289 while (runpath) {
35ead2de 2290 ERROR(file->pakfire, "Checking RUNPATH %s\n", runpath);
7b461c8b
MT
2291
2292 // We do not allow any relative RUNPATHs
35ead2de
MT
2293 if (pakfire_path_match(runpath, "**/../**"))
2294 goto RUNPATH_DENIED;
7b461c8b 2295
35ead2de
MT
2296 // We allow /usr/lib64 as libtool seems to link it in quite a lot
2297 if (pakfire_path_match("/usr/lib64", runpath))
2298 goto RUNPATH_PERMITTED;
2299
2300 // We allow any subdirectories of /usr/lib64
2301 if (pakfire_path_match( "/usr/lib64/**", runpath))
2302 goto RUNPATH_PERMITTED;
2303
2304RUNPATH_DENIED:
2305 // If we make it here, this check has failed
2306 file->issues |= PAKFIRE_FILE_HAS_RUNPATH;
2307 break;
2308
2309RUNPATH_PERMITTED:
7b461c8b
MT
2310 // Move on to the next RUNPATH
2311 runpath = strtok_r(NULL, ":", &p);
2312 }
2313
2314 default:
2315 break;
2316 }
2317
2318 return 0;
2319}
2320
2321static int __pakfire_file_check_runpath(struct pakfire_file* file, Elf* elf, void* data) {
2322 return pakfire_file_elf_dyn_walk(file, elf, __pakfire_file_process_runpath, data);
2323}
2324
2325static int pakfire_file_check_runpath(struct pakfire_file* file) {
2326 return pakfire_file_open_elf(file, __pakfire_file_check_runpath, NULL);
2327}
d265bed6 2328
375a06e3
MT
2329static int pakfire_file_get_script_interpreter(struct pakfire_file* file, char** interpreter) {
2330 FILE* f = NULL;
2331 char shebang[1024];
2332 char* interp = NULL;
2333 char* p = NULL;
2334 int r;
2335
2336 // Check inputs
2337 if (!interpreter) {
2338 errno = EINVAL;
2339 return 1;
2340 }
2341
37c5d873
MT
2342 const mode_t mode = pakfire_file_get_mode(file);
2343
375a06e3 2344 // Only run for regular files
37c5d873 2345 if (!S_ISREG(mode))
375a06e3
MT
2346 return 0;
2347
2348 // Only run for executable files
2349 if (!pakfire_file_is_executable(file))
2350 return 0;
2351
37c5d873
MT
2352 const size_t size = pakfire_file_get_size(file);
2353
375a06e3 2354 // Nothing to do if the file is empty
37c5d873 2355 if (!size)
375a06e3
MT
2356 return 0;
2357
2358 // Open the file
2359 f = pakfire_file_open(file);
2360 if (!f) {
2361 r = 1;
2362 goto ERROR;
2363 }
2364
2365 // Fill the buffer with the first couple of bytes of the file
2366 ssize_t bytes_read = fread(shebang, 1, sizeof(shebang), f);
2367
2368 // Handle any reading errors
2369 if (bytes_read < 0) {
37c5d873
MT
2370 ERROR(file->pakfire, "Could not read from file %s: %m\n",
2371 pakfire_file_get_path(file));
375a06e3
MT
2372 r = 1;
2373 goto ERROR;
2374 }
2375
2376 // We did not read enough data
2377 if (bytes_read < 2) {
2378 r = 1;
2379 goto ERROR;
2380 }
2381
2382 if (strncmp("#!", shebang, 2) == 0) {
37c5d873 2383 DEBUG(file->pakfire, "%s is a script\n", pakfire_file_get_path(file));
375a06e3
MT
2384
2385 // Find the end of the first line (to be able to perform string operations)
2386 p = memchr(shebang, '\n', sizeof(shebang));
2387 if (!p) {
37c5d873
MT
2388 ERROR(file->pakfire, "%s: First line seems to be too long\n",
2389 pakfire_file_get_path(file));
375a06e3
MT
2390 errno = ENOBUFS;
2391 r = 1;
2392 goto ERROR;
2393 }
2394
2395 // Terminate the string
2396 *p = '\0';
2397
2398 // Find the beginning of the interpreter
2399 interp = shebang + 2;
2400
2401 // Consume any space between #! and the path
2402 while (*interp && isspace(*interp))
2403 interp++;
2404
2405 p = interp;
2406
2407 // Find the end of the command (cuts off any options)
2408 while (*p) {
2409 if (isspace(*p)) {
2410 *p = '\0';
2411 break;
2412 }
2413
2414 p++;
2415 }
2416
2417 // Copy the interpreter to the heap
2418 *interpreter = strdup(interp);
2419 }
2420
2421 // Success
2422 r = 0;
2423
2424ERROR:
2425 if (f)
2426 fclose(f);
2427
2428 return r;
2429}
2430
33d2d4df
MT
2431static int pakfire_file_fix_interpreter(struct pakfire_file* file, const char* interpreter) {
2432 FILE* f = NULL;
33d2d4df
MT
2433 int r;
2434
94a53bda 2435 char* buffer = NULL;
740a3668 2436 size_t l = 0;
94a53bda 2437 char* line = NULL;
33d2d4df 2438 size_t length = 0;
94a53bda 2439 char* p = NULL;
33d2d4df 2440
94a53bda
MT
2441 char command[PATH_MAX];
2442 const char* args = NULL;
2443
2444 const char* path = pakfire_file_get_path(file);
2445
2446 DEBUG(file->pakfire, "%s: Fixing interpreter %s\n", path, interpreter);
33d2d4df
MT
2447
2448 // Open the file
2449 f = pakfire_file_open(file);
2450 if (!f) {
94a53bda
MT
2451 ERROR(file->pakfire, "Could not open %s: %m\n", path);
2452 r = -errno;
33d2d4df
MT
2453 goto ERROR;
2454 }
2455
94a53bda
MT
2456 for (unsigned int lineno = 0;; lineno++) {
2457 r = getline(&line, &length, f);
2458 if (r < 0) {
2459 // We finished reading when we reach the end
2460 if (feof(f))
2461 break;
33d2d4df 2462
94a53bda
MT
2463 ERROR(file->pakfire, "Could not read line from %s: %m\n", path);
2464 goto ERROR;
2465 }
33d2d4df 2466
94a53bda
MT
2467 switch (lineno) {
2468 case 0:
2469 // Check if the first line starts with #!
2470 if (!pakfire_string_startswith(line, "#!")) {
2471 r = -EINVAL;
2472 goto ERROR;
2473 }
33d2d4df 2474
94a53bda
MT
2475 // Remove the trailing newline
2476 pakfire_remove_trailing_newline(line);
33d2d4df 2477
94a53bda
MT
2478 // Start at the beginning of the line (after shebang)
2479 p = line + 2;
33d2d4df 2480
94a53bda
MT
2481 // Consume any whitespace
2482 while (*p && isspace(*p))
2483 p++;
33d2d4df 2484
94a53bda
MT
2485 // Consume the old interpreter
2486 while (*p && !isspace(*p))
2487 p++;
33d2d4df 2488
94a53bda
MT
2489 // Consume any whitespace
2490 while (*p && isspace(*p))
2491 p++;
33d2d4df 2492
94a53bda
MT
2493 // The actual interpreter begins here
2494 interpreter = p;
33d2d4df 2495
94a53bda
MT
2496 // Find the end of the interpreter
2497 while (*p && !isspace(*p))
2498 p++;
33d2d4df 2499
94a53bda
MT
2500 // Terminate the interpreter
2501 *p++ = '\0';
33d2d4df 2502
94a53bda
MT
2503 // Any arguments begin here (if we didn't consume the entire string, yet)
2504 if (*p)
2505 args = p;
33d2d4df 2506
6541e008 2507 DEBUG(file->pakfire, "%s: Found command %s (%s)\n", path, interpreter, args);
33d2d4df 2508
94a53bda
MT
2509 // Find the real path
2510 r = pakfire_which(file->pakfire, command, interpreter);
2511 if (r) {
2512 ERROR(file->pakfire, "%s: Could not resolve %s: %m\n", path, interpreter);
2513 goto ERROR;
2514 }
33d2d4df 2515
94a53bda
MT
2516 // If we could not resolve the command, this file has an invalid interpreter
2517 if (!*command) {
2518 ERROR(file->pakfire, "%s: Could not find path for command %s\n", path, interpreter);
33d2d4df 2519
94a53bda
MT
2520 file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER;
2521 r = 0;
2522 goto ERROR;
2523 }
33d2d4df 2524
94a53bda
MT
2525 // Write the new first line to the buffer
2526 r = asprintf(&buffer, "#!%s", command);
2527 if (r < 0)
2528 goto ERROR;
33d2d4df 2529
94a53bda
MT
2530 // Append arguments if any
2531 if (args) {
2532 r = asprintf(&buffer, "%s %s", buffer, args);
2533 if (r < 0)
2534 goto ERROR;
2535 }
2536
2537 // Terminate the line
2538 r = asprintf(&buffer, "%s\n", buffer);
2539 if (r < 0)
2540 goto ERROR;
2541
740a3668
MT
2542 // Store length of the buffer
2543 l = r;
2544
94a53bda
MT
2545 // Process the next line
2546 break;
2547
2548 default:
2549 // Append the line to the buffer
2550 r = asprintf(&buffer, "%s%s", buffer, line);
2551 if (r < 0)
2552 goto ERROR;
740a3668
MT
2553
2554 // Store length of the buffer
2555 l = r;
2556
94a53bda
MT
2557 break;
2558 }
33d2d4df
MT
2559 }
2560
94a53bda
MT
2561 // Go back to the beginning of the file
2562 rewind(f);
33d2d4df 2563
740a3668
MT
2564 // Truncate the existing content
2565 r = ftruncate(fileno(f), 0);
2566 if (r) {
2567 ERROR(file->pakfire, "Could not truncate %s: %m\n", path);
2568 r = -errno;
2569 goto ERROR;
2570 }
2571
94a53bda
MT
2572 // Write back the buffer
2573 if (buffer) {
740a3668
MT
2574 size_t bytes_written = fwrite(buffer, 1, l, f);
2575 if (bytes_written < l) {
94a53bda
MT
2576 ERROR(file->pakfire, "%s: Could not write the payload: %m\n", path);
2577 r = -errno;
2578 goto ERROR;
2579 }
33d2d4df
MT
2580 }
2581
2582 // Success!
2583 r = 0;
2584
2585ERROR:
94a53bda
MT
2586 if (buffer)
2587 free(buffer);
2588 if (line)
2589 free(line);
33d2d4df
MT
2590 if (f)
2591 fclose(f);
2592
2593 return r;
2594}
2595
375a06e3
MT
2596static int pakfire_file_check_interpreter(struct pakfire_file* file) {
2597 char* interpreter = NULL;
2598 int r;
2599
2600 // Fetch the script interpreter
2601 r = pakfire_file_get_script_interpreter(file, &interpreter);
2602 if (r)
2603 return r;
2604
2605 // If there is no result, the file is not a script
2606 if (!interpreter)
2607 return 0;
2608
37c5d873
MT
2609 DEBUG(file->pakfire, "%s: Interpreter: %s\n",
2610 pakfire_file_get_path(file), interpreter);
375a06e3
MT
2611
2612 // Paths must be absolute
2613 if (*interpreter != '/')
2614 file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER;
2615
2616 // Check if the interpreter is in /usr/local
2617 else if (pakfire_string_startswith(interpreter, "/usr/local/"))
2618 file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER;
2619
33d2d4df
MT
2620 // We don't support "env", but will automatically fix it
2621 else if (strcmp(interpreter, "/usr/bin/env") == 0) {
2622 r = pakfire_file_fix_interpreter(file, interpreter);
2623 if (r) {
37c5d873
MT
2624 ERROR(file->pakfire, "%s: Could not fix interpreter: %m\n",
2625 pakfire_file_get_path(file));
33d2d4df
MT
2626 goto ERROR;
2627 }
2628 }
375a06e3 2629
33d2d4df 2630ERROR:
375a06e3
MT
2631 if (interpreter)
2632 free(interpreter);
2633
33d2d4df 2634 return r;
375a06e3
MT
2635}
2636
691091ad
MT
2637static int pakfire_file_check_capabilities(struct pakfire_file* file) {
2638 // Files cannot have capabilities but not be executable
2639 if (!pakfire_file_is_executable(file) && pakfire_file_has_caps(file))
2640 file->issues |= PAKFIRE_FILE_INVALID_CAPS;
2641
2642 return 0;
2643}
2644
dbae97eb
MT
2645static uint32_t read_4_bytes(const int endianess, const char* data) {
2646 uint32_t bytes = 0;
2647
2648 switch (endianess) {
2649 // Litte Endian
2650 case ELFDATA2LSB:
2651 bytes = data[0] |
2652 ((uint32_t)data[1] << 8) |
2653 ((uint32_t)data[2] << 16) |
2654 ((uint32_t)data[3] << 24);
2655 break;
2656
2657 // Big endian
2658 case ELFDATA2MSB:
2659 bytes = data[3] |
2660 ((uint32_t)data[2] << 8) |
2661 ((uint32_t)data[1] << 16) |
2662 ((uint32_t)data[0] << 24);
2663 break;
2664 }
2665
2666 return bytes;
2667}
2668
2669static int __pakfire_file_check_cf_protection_x86(struct pakfire_file* file,
2670 const int endianess, const uint32_t type, const char* payload) {
2671 switch (type) {
2672 case GNU_PROPERTY_X86_FEATURE_1_AND:
2673 break;
2674
2675 // XXX what should we do in this case? Just ignore?
2676 default:
2677 return 0;
2678 }
2679
2680 uint32_t property = read_4_bytes(endianess, payload);
2681
2682 // Check for IBT
2683 if (!(property & GNU_PROPERTY_X86_FEATURE_1_IBT)) {
37c5d873
MT
2684 DEBUG(file->pakfire, "%s: IBT property is not enabled\n",
2685 pakfire_file_get_path(file));
dbae97eb
MT
2686
2687 // XXX TODO Actually modify any flags
2688
2689 return 0;
2690 }
2691
2692 // Check for Shadow Stack
2693 if (!(property & GNU_PROPERTY_X86_FEATURE_1_SHSTK)) {
37c5d873
MT
2694 DEBUG(file->pakfire, "%s: Shadow Stack is not enabled\n",
2695 pakfire_file_get_path(file));
dbae97eb
MT
2696
2697 // XXX TODO Actually modify any flags
2698
2699 return 0;
2700 }
2701
2702 return 0;
2703}
2704
32f68461
MT
2705static int __pakfire_file_check_cf_protection_aarch64(struct pakfire_file* file,
2706 const int endianess, const uint32_t type, const char* payload) {
2707 # warning TODO
2708 return 0;
2709}
2710
dbae97eb
MT
2711static int pakfire_file_check_cf_protection_callback(struct pakfire_file* file,
2712 Elf* elf, const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data) {
2713 GElf_Ehdr ehdr = {};
2714 GElf_Nhdr nhdr = {};
2715 size_t offset = 0;
2716 size_t offset_name = 0;
2717 size_t offset_data = 0;
2718 int r;
2719
2720 // Fetch the ELF header
2721 if (!gelf_getehdr(elf, &ehdr)) {
37c5d873
MT
2722 ERROR(file->pakfire, "Could not fetch the ELF header for %s: %m\n",
2723 pakfire_file_get_path(file));
dbae97eb
MT
2724 return 1;
2725 }
2726
2727 // Fetch the endianess
2728 const int endianess = ehdr.e_ident[EI_DATA];
2729
2730 // Fetch the .note header
2731 offset = gelf_getnote(data, offset, &nhdr, &offset_name, &offset_data);
2732 if (!offset) {
2733 ERROR(file->pakfire, "Could not read note section: %m\n");
2734 return 1;
2735 }
2736
2737 switch (nhdr.n_type) {
2738 case NT_GNU_PROPERTY_TYPE_0:
2739 break;
2740
2741 // We are not interested in this here
2742 default:
2743 return 0;
2744 }
2745
2746#if 0
2747 // Check if the name starts with "GNU"
2748 if (nhdr.n_namesz == sizeof(ELF_NOTE_GNU) {
2749 if (strcmp(data->d_buf + offset_name, ELF_NOTE_GNU) == 0)
2750 break;
2751 }
2752#endif
2753
2754 size_t length = nhdr.n_descsz;
2755
2756 // XXX Check if this is aligned to 8 bytes or 4 bytes on 32 bit
2757
2758 const char* payload = (const char*)data->d_buf + offset_data;
2759
2760 while (length) {
2761 // Read the type and size of the .note section
2762 const uint32_t type = read_4_bytes(endianess, payload);
2763 const uint32_t size = read_4_bytes(endianess, payload + 4);
2764
2765 // Skip header
2766 length -= sizeof(type) + sizeof(size);
2767 payload += sizeof(type) + sizeof(size);
2768
2769 if (length < size) {
2770 ERROR(file->pakfire, "GNU Property note has an incorrect format\n");
2771 return 1;
2772 }
2773
2774 switch (ehdr.e_machine) {
dbae97eb
MT
2775 case EM_AARCH64:
2776 r = __pakfire_file_check_cf_protection_aarch64(file, endianess, type, payload);
2777 if (r)
2778 return r;
2779 break;
dbae97eb
MT
2780
2781 case EM_X86_64:
2782 r = __pakfire_file_check_cf_protection_x86(file, endianess, type, payload);
2783 if (r)
2784 return r;
2785 break;
2786
2787 default:
32f68461 2788 ERROR(file->pakfire, "Unsupported ELF type (%d)\n", ehdr.e_machine);
dbae97eb
MT
2789 return 1;
2790 }
2791
2792 // Skip data part
2793 length -= (size + 7) & ~7;
2794 payload += (size + 7) & ~7;
2795 }
2796
2797 return 0;
2798}
2799
2800static int __pakfire_file_check_cf_protection(struct pakfire_file* file, Elf* elf, void* data) {
2801 return pakfire_file_for_elf_section(file, elf, SHT_NOTE, pakfire_file_check_cf_protection_callback);
2802}
2803
2804static int pakfire_file_check_cf_protection(struct pakfire_file* file) {
2805 return pakfire_file_open_elf(file, __pakfire_file_check_cf_protection, NULL);
2806}
691091ad 2807
28886e21 2808int pakfire_file_check(struct pakfire_file* file, int* issues) {
f7f44921
MT
2809 int r;
2810
2811 // Return previous result if this has been run before
28886e21 2812 if (!file->check_done) {
6d9dd7bc
MT
2813 // Perform FHS check
2814 r = pakfire_fhs_check_file(file->pakfire, file);
2815 if (r)
28886e21 2816 file->issues |= PAKFIRE_FILE_FHS_ERROR;
8a385eff 2817
375a06e3
MT
2818 // Perform interpreter check
2819 r = pakfire_file_check_interpreter(file);
2820 if (r)
2821 return r;
2822
691091ad
MT
2823 // Perform capability check
2824 r = pakfire_file_check_capabilities(file);
2825 if (r)
2826 return r;
2827
6d9dd7bc
MT
2828 // Do not perform the following checks on firmware
2829 if (pakfire_file_matches_class(file, PAKFIRE_FILE_FIRMWARE))
2830 goto DONE;
8a385eff 2831
6d9dd7bc
MT
2832 // Run these checks only for ELF files
2833 if (pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
2834 switch (pakfire_file_get_elf_type(file)) {
2835 // Do not check Relocatable Objects
2836 case ET_REL:
2837 goto DONE;
2838
2839 // Check everything else
2840 default:
2841 break;
2842 }
f7f44921 2843
03a5ac95
MT
2844 // Check if the file has debug info
2845 r = pakfire_file_check_debuginfo(file);
2846 if (r)
2847 return r;
2848
6d9dd7bc 2849 // Check for SSP
28886e21 2850 r = pakfire_file_check_ssp(file);
6d9dd7bc
MT
2851 if (r)
2852 return r;
f7f44921 2853
6d9dd7bc 2854 // Check for PIE
28886e21 2855 r = pakfire_file_check_pie(file);
6d9dd7bc
MT
2856 if (r)
2857 return r;
dae43e1f 2858
6d9dd7bc 2859 // Check for executable stacks
28886e21 2860 r = pakfire_file_check_execstack(file);
6d9dd7bc
MT
2861 if (r)
2862 return r;
2863
2864 // Check for RELRO
28886e21 2865 r = pakfire_file_check_relro(file);
6d9dd7bc
MT
2866 if (r)
2867 return r;
7b461c8b
MT
2868
2869 // Check for RUNPATH
2870 r = pakfire_file_check_runpath(file);
2871 if (r)
2872 return r;
dbae97eb
MT
2873
2874 // Check CF protection
2875 r = pakfire_file_check_cf_protection(file);
2876 if (r)
2877 return r;
6d9dd7bc 2878 }
d265bed6 2879
8a385eff 2880DONE:
f7f44921 2881 // All checks done
28886e21 2882 file->check_done = 1;
f7f44921
MT
2883 }
2884
2885 // Return any issues
2886 if (issues)
28886e21 2887 *issues = file->issues;
f7f44921
MT
2888
2889 return 0;
2a67d72c 2890}