]> git.ipfire.org Git - people/stevee/pakfire.git/blob - src/libpakfire/file.c
build: Move FHS check into hardening checks
[people/stevee/pakfire.git] / src / libpakfire / file.c
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
21 #include <errno.h>
22 #include <fnmatch.h>
23 #include <libgen.h>
24 #include <limits.h>
25 #include <linux/limits.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <time.h>
31
32 #include <archive_entry.h>
33
34 #include <gelf.h>
35
36 #include <pakfire/constants.h>
37 #include <pakfire/digest.h>
38 #include <pakfire/fhs.h>
39 #include <pakfire/file.h>
40 #include <pakfire/logging.h>
41 #include <pakfire/pakfire.h>
42 #include <pakfire/private.h>
43 #include <pakfire/string.h>
44 #include <pakfire/util.h>
45
46 enum pakfire_file_verification_status {
47 PAKFIRE_FILE_NOENT = (1 << 0),
48 PAKFIRE_FILE_TYPE_CHANGED = (1 << 1),
49 PAKFIRE_FILE_PERMISSIONS_CHANGED = (1 << 2),
50 PAKFIRE_FILE_DEV_CHANGED = (1 << 3),
51 PAKFIRE_FILE_SIZE_CHANGED = (1 << 4),
52 PAKFIRE_FILE_OWNER_CHANGED = (1 << 5),
53 PAKFIRE_FILE_GROUP_CHANGED = (1 << 6),
54 PAKFIRE_FILE_CTIME_CHANGED = (1 << 7),
55 PAKFIRE_FILE_MTIME_CHANGED = (1 << 8),
56 PAKFIRE_FILE_PAYLOAD_CHANGED = (1 << 9),
57 };
58
59 struct pakfire_file {
60 struct pakfire* pakfire;
61 int nrefs;
62
63 // The relative path
64 char path[PATH_MAX];
65
66 // The absolute path in the file system
67 char abspath[PATH_MAX];
68
69 // File Ownership
70 char uname[LOGIN_NAME_MAX];
71 char gname[LOGIN_NAME_MAX];
72
73 // Stat
74 struct stat st;
75
76 // Flags
77 int flags;
78
79 // Link destinations
80 char hardlink[PATH_MAX];
81 char symlink[PATH_MAX];
82
83 // Digests
84 struct pakfire_digests digests;
85
86 // MIME Type
87 char mimetype[NAME_MAX];
88
89 // Class
90 int class;
91
92 // Verification Status
93 int verify_status;
94
95 // Hardening Issues
96 int hardening_issues;
97 int hardening_check_done:1;
98
99 #warning TODO capabilities, data
100 // capabilities
101 //int is_datafile;
102 };
103
104 static int pakfire_file_from_archive_entry(struct pakfire_file* file, struct archive_entry* entry) {
105 char* buffer = NULL;
106 const char* path = NULL;
107 const char* attr = NULL;
108 const void* value = NULL;
109 size_t size = 0;
110 int r = 0;
111
112 // Set abspath
113 path = archive_entry_sourcepath(entry);
114 if (path) {
115 // Make path absolute
116 path = pakfire_path_abspath(path);
117 if (!path) {
118 r = 1;
119 goto ERROR;
120 }
121
122 // Set
123 r = pakfire_file_set_abspath(file, path);
124 if (r) {
125 ERROR(file->pakfire, "Could not set abspath: %m\n");
126 goto ERROR;
127 }
128 }
129
130 // Set path
131 path = archive_entry_pathname(entry);
132 if (path) {
133 r = pakfire_file_set_path(file, path);
134 if (r) {
135 ERROR(file->pakfire, "Could not set path: %m\n");
136 goto ERROR;
137 }
138 }
139
140 // Set links
141 pakfire_file_set_hardlink(file, archive_entry_hardlink(entry));
142 pakfire_file_set_symlink(file, archive_entry_symlink(entry));
143
144 pakfire_file_set_nlink(file, archive_entry_nlink(entry));
145 pakfire_file_set_inode(file, archive_entry_ino64(entry));
146 pakfire_file_set_dev(file, archive_entry_dev(entry));
147
148 // Set size
149 pakfire_file_set_size(file, archive_entry_size(entry));
150
151 // Set mode
152 pakfire_file_set_mode(file, archive_entry_mode(entry));
153
154 // Set dev type
155 if (archive_entry_dev_is_set(entry))
156 pakfire_file_set_dev(file, archive_entry_dev(entry));
157
158 // Set uname
159 pakfire_file_set_uname(file, archive_entry_uname(entry));
160
161 // Set gname
162 pakfire_file_set_gname(file, archive_entry_gname(entry));
163
164 // Set times
165 pakfire_file_set_ctime(file, archive_entry_ctime(entry));
166 pakfire_file_set_mtime(file, archive_entry_mtime(entry));
167
168 // Reset iterating over extended attributes
169 archive_entry_xattr_reset(entry);
170
171 // Read any extended attributes
172 while (archive_entry_xattr_next(entry, &attr, &value, &size) == ARCHIVE_OK) {
173 // Config Files
174 if (strcmp(attr, "PAKFIRE.config") == 0) {
175 r = pakfire_file_set_flags(file, PAKFIRE_FILE_CONFIG);
176 if (r)
177 goto ERROR;
178
179 // MIME type
180 } else if (strcmp(attr, "PAKFIRE.mimetype") == 0) {
181 // Copy the value into a NULL-terminated buffer
182 r = asprintf(&buffer, "%.*s", (int)size, (const char*)value);
183 if (r < 0)
184 goto ERROR;
185
186 // Assign the value
187 r = pakfire_file_set_mimetype(file, buffer);
188 if (r)
189 goto ERROR;
190
191 // Digest: SHA-3-512
192 } else if (strcmp(attr, "PAKFIRE.digests.sha3_512") == 0) {
193 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA3_512, value, size);
194 if (r)
195 goto ERROR;
196
197 // Digest: SHA-3-256
198 } else if (strcmp(attr, "PAKFIRE.digests.sha3_256") == 0) {
199 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA3_256, value, size);
200 if (r)
201 goto ERROR;
202
203 // Digest: BLAKE2b512
204 } else if (strcmp(attr, "PAKFIRE.digests.blake2b512") == 0) {
205 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_BLAKE2B512, value, size);
206 if (r)
207 goto ERROR;
208
209 // Digest: BLAKE2s256
210 } else if (strcmp(attr, "PAKFIRE.digests.blake2s256") == 0) {
211 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_BLAKE2S256, value, size);
212 if (r)
213 goto ERROR;
214
215 // Digest: SHA-2-512
216 } else if (strcmp(attr, "PAKFIRE.digests.sha2_512") == 0) {
217 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA2_512, value, size);
218 if (r)
219 goto ERROR;
220
221 // Digest: SHA-2-256
222 } else if (strcmp(attr, "PAKFIRE.digests.sha2_256") == 0) {
223 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA2_256, value, size);
224 if (r)
225 goto ERROR;
226
227 } else {
228 DEBUG(file->pakfire, "Received an unknown extended attribute: %s\n", attr);
229 }
230 }
231
232 ERROR:
233 if (buffer)
234 free(buffer);
235
236 return r;
237 }
238
239 PAKFIRE_EXPORT int pakfire_file_create(struct pakfire_file** file, struct pakfire* pakfire) {
240 struct pakfire_file* f = calloc(1, sizeof(*f));
241 if (!f)
242 return 1;
243
244 // Store reference to Pakfire
245 f->pakfire = pakfire_ref(pakfire);
246
247 // Initialize reference counter
248 f->nrefs = 1;
249
250 *file = f;
251 return 0;
252 }
253
254 int pakfire_file_create_from_path(struct pakfire_file** file,
255 struct pakfire* pakfire, const char* path) {
256 struct archive* reader = NULL;
257 struct archive_entry* entry = NULL;
258 int r = 1;
259
260 // Allocate a reader
261 reader = pakfire_make_archive_disk_reader(pakfire, 0);
262 if (!reader)
263 goto ERROR;
264
265 // Allocate a new archive entry
266 entry = archive_entry_new();
267 if (!entry)
268 goto ERROR;
269
270 // Set source path
271 archive_entry_copy_sourcepath(entry, path);
272
273 // Read all file attributes from disk
274 r = archive_read_disk_entry_from_file(reader, entry, -1, NULL);
275 if (r) {
276 ERROR(pakfire, "Could not read from %s: %m\n", path);
277 goto ERROR;
278 }
279
280 // Create file
281 r = pakfire_file_create_from_archive_entry(file, pakfire, entry);
282 if (r)
283 goto ERROR;
284
285 ERROR:
286 if (r)
287 ERROR(pakfire, "Could not create file from path %s: %m\n", path);
288 if (entry)
289 archive_entry_free(entry);
290 if (reader)
291 archive_read_free(reader);
292
293 return r;
294 }
295
296 int pakfire_file_create_from_archive_entry(struct pakfire_file** file, struct pakfire* pakfire,
297 struct archive_entry* entry) {
298 int r = pakfire_file_create(file, pakfire);
299 if (r)
300 return r;
301
302 // Copy archive entry
303 r = pakfire_file_from_archive_entry(*file, entry);
304 if (r)
305 goto ERROR;
306
307 return 0;
308
309 ERROR:
310 pakfire_file_unref(*file);
311 *file = NULL;
312
313 return r;
314 }
315
316 struct archive_entry* pakfire_file_archive_entry(struct pakfire_file* file, int digest_types) {
317 const char* path = NULL;
318 int r;
319
320 struct archive_entry* entry = archive_entry_new();
321 if (!entry) {
322 ERROR(file->pakfire, "Could not allocate archive entry: %m\n");
323 return NULL;
324 }
325
326 // Set source path
327 archive_entry_copy_sourcepath(entry, file->abspath);
328
329 // Set path
330 path = pakfire_file_get_path(file);
331 if (path && *path == '/') {
332 archive_entry_copy_pathname(entry, path + 1);
333 }
334
335 // Set links
336 if (*file->hardlink)
337 archive_entry_set_hardlink(entry, file->hardlink);
338 if (*file->symlink)
339 archive_entry_set_symlink(entry, file->symlink);
340
341 archive_entry_set_nlink(entry, pakfire_file_get_nlink(file));
342 archive_entry_set_ino64(entry, pakfire_file_get_inode(file));
343 archive_entry_set_dev(entry, pakfire_file_get_dev(file));
344
345 // Set size
346 archive_entry_set_size(entry, pakfire_file_get_size(file));
347
348 // Set mode
349 archive_entry_set_mode(entry, pakfire_file_get_mode(file));
350
351 // Set uname
352 archive_entry_set_uname(entry, pakfire_file_get_uname(file));
353
354 // Set gname
355 archive_entry_set_gname(entry, pakfire_file_get_gname(file));
356
357 // Set times
358 archive_entry_set_ctime(entry, pakfire_file_get_ctime(file), 0);
359 archive_entry_set_mtime(entry, pakfire_file_get_mtime(file), 0);
360
361 // Flags
362 if (pakfire_file_has_flag(file, PAKFIRE_FILE_CONFIG)) {
363 archive_entry_xattr_add_entry(entry,
364 "PAKFIRE.config", "1", strlen("1"));
365 }
366
367 // Set MIME type
368 const char* mimetype = pakfire_file_get_mimetype(file);
369 if (mimetype) {
370 archive_entry_xattr_add_entry(entry,
371 "PAKFIRE.mimetype", mimetype, strlen(mimetype));
372 }
373
374 // Compute any required file digests
375 r = pakfire_file_compute_digests(file, digest_types);
376 if (r)
377 goto ERROR;
378
379 // Copy digests
380
381 // SHA-3-512
382 if ((digest_types && PAKFIRE_DIGEST_SHA3_512)
383 && pakfire_digest_set(file->digests.sha3_512))
384 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha3_512",
385 file->digests.sha3_512, sizeof(file->digests.sha3_512));
386
387 // SHA-3-256
388 if ((digest_types && PAKFIRE_DIGEST_SHA3_256) &&
389 pakfire_digest_set(file->digests.sha3_256))
390 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha3_256",
391 file->digests.sha3_256, sizeof(file->digests.sha3_256));
392
393 // BLAKE2b512
394 if ((digest_types && PAKFIRE_DIGEST_BLAKE2B512) &&
395 pakfire_digest_set(file->digests.blake2b512))
396 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.blake2b512",
397 file->digests.blake2b512, sizeof(file->digests.blake2b512));
398
399 // BLAKE2s256
400 if ((digest_types && PAKFIRE_DIGEST_BLAKE2S256) &&
401 pakfire_digest_set(file->digests.blake2s256))
402 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.blake2s256",
403 file->digests.blake2s256, sizeof(file->digests.blake2s256));
404
405 // SHA-2-512
406 if ((digest_types && PAKFIRE_DIGEST_SHA2_512) &&
407 pakfire_digest_set(file->digests.sha2_512))
408 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha2_512",
409 file->digests.sha2_512, sizeof(file->digests.sha2_512));
410
411 // SHA-2-256
412 if ((digest_types && PAKFIRE_DIGEST_SHA2_512) &&
413 pakfire_digest_set(file->digests.sha2_256))
414 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha2_256",
415 file->digests.sha2_256, sizeof(file->digests.sha2_256));
416
417 return entry;
418
419 ERROR:
420 if (entry)
421 archive_entry_free(entry);
422
423 return NULL;
424 }
425
426 static void pakfire_file_free(struct pakfire_file* file) {
427 pakfire_unref(file->pakfire);
428 free(file);
429 }
430
431 PAKFIRE_EXPORT struct pakfire_file* pakfire_file_ref(struct pakfire_file* file) {
432 file->nrefs++;
433
434 return file;
435 }
436
437 PAKFIRE_EXPORT struct pakfire_file* pakfire_file_unref(struct pakfire_file* file) {
438 if (--file->nrefs > 0)
439 return file;
440
441 pakfire_file_free(file);
442 return NULL;
443 }
444
445 /*
446 Flags
447 */
448
449 int pakfire_file_has_flag(struct pakfire_file* file, int flag) {
450 return file->flags & flag;
451 }
452
453 int pakfire_file_set_flags(struct pakfire_file* file, int flag) {
454 file->flags |= flag;
455
456 return 0;
457 }
458
459 #define pakfire_file_strmode(file, buffer) \
460 __pakfire_file_strmode(file, buffer, sizeof(buffer))
461
462 static int __pakfire_file_strmode(struct pakfire_file* file, char* s, const size_t length) {
463 int r;
464
465 static const mode_t permbits[] = {
466 0400,
467 0200,
468 0100,
469 0040,
470 0020,
471 0010,
472 0004,
473 0002,
474 0001
475 };
476
477 const mode_t mode = pakfire_file_get_mode(file);
478 const mode_t type = pakfire_file_get_type(file);
479
480 // Set some default string
481 r = __pakfire_string_set(s, length, "?rwxrwxrwx ");
482 if (r)
483 return r;
484
485 switch (type) {
486 case S_IFREG:
487 s[0] = '-';
488 break;
489
490 case S_IFBLK:
491 s[0] = 'b';
492 break;
493
494 case S_IFCHR:
495 s[0] = 'c';
496 break;
497
498 case S_IFDIR:
499 s[0] = 'd';
500 break;
501
502 case S_IFLNK:
503 s[0] = 'l';
504 break;
505
506 case S_IFSOCK:
507 s[0] = 's';
508 break;
509
510 case S_IFIFO:
511 s[0] = 'p';
512 break;
513
514 default:
515 if (*file->hardlink) {
516 s[0] = 'h';
517 break;
518 }
519 }
520
521 for (unsigned int i = 0; i < 9; i++) {
522 if (mode & permbits[i])
523 continue;
524
525 s[i+1] = '-';
526 }
527
528 if (mode & S_ISUID) {
529 if (mode & 0100)
530 s[3] = 's';
531 else
532 s[3] = 'S';
533 }
534
535 if (mode & S_ISGID) {
536 if (mode & 0010)
537 s[6] = 's';
538 else
539 s[6] = 'S';
540 }
541
542 if (mode & S_ISVTX) {
543 if (mode & 0001)
544 s[9] = 't';
545 else
546 s[9] = 'T';
547 }
548
549 #if 0
550 if (file->caps)
551 s[10] = '+';
552 #endif
553
554 return 0;
555 }
556
557 char* pakfire_file_dump(struct pakfire_file* file, int flags) {
558 char* buffer = "";
559 int r;
560
561 char mode[12];
562 char time[32];
563
564 // Format mode
565 if (flags & PAKFIRE_FILE_DUMP_MODE) {
566 r = pakfire_file_strmode(file, mode);
567 if (r)
568 goto ERROR;
569
570 r = asprintf(&buffer, "%s %s", buffer, mode);
571 if (r < 0)
572 goto ERROR;
573 }
574
575 // Format ownership
576 if (flags & PAKFIRE_FILE_DUMP_OWNERSHIP) {
577 r = asprintf(&buffer, "%s %s/%s", buffer, file->uname, file->gname);
578 if (r < 0)
579 goto ERROR;
580 }
581
582 // Format size
583 if (flags & PAKFIRE_FILE_DUMP_SIZE) {
584 r = asprintf(&buffer, "%s %8zu", buffer, file->st.st_size);
585 if (r < 0)
586 goto ERROR;
587 }
588
589 // Format time
590 if (flags & PAKFIRE_FILE_DUMP_TIME) {
591 r = pakfire_strftime(time, "%Y-%m-%d %H:%M", file->st.st_ctime);
592 if (r)
593 goto ERROR;
594
595 r = asprintf(&buffer, "%s %s", buffer, time);
596 if (r < 0)
597 goto ERROR;
598 }
599
600 // Format path
601 r = asprintf(&buffer, "%s %s", buffer, file->path);
602 if (r < 0)
603 goto ERROR;
604
605 // Append symlink target
606 if (flags & PAKFIRE_FILE_DUMP_LINK_TARGETS) {
607 switch (pakfire_file_get_type(file)) {
608 case S_IFLNK:
609 r = asprintf(&buffer, "%s -> %s", buffer, file->symlink);
610 if (r < 0)
611 return NULL;
612
613 default:
614 break;
615 }
616 }
617
618 // Hardning Status
619 if (flags & PAKFIRE_FILE_DUMP_HARDENING) {
620 if (file->hardening_issues & PAKFIRE_FILE_FHS_ERROR) {
621 r = asprintf(&buffer, "%s [FHS-ERROR]", buffer);
622 if (r < 0)
623 goto ERROR;
624 }
625
626 if (pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
627 // Stack-smashing Protection
628 if (file->hardening_issues & PAKFIRE_FILE_NO_SSP) {
629 r = asprintf(&buffer, "%s [NO-SSP]", buffer);
630 if (r < 0)
631 goto ERROR;
632 }
633
634 // Position-independent Executable
635 if (file->hardening_issues & PAKFIRE_FILE_NO_PIE) {
636 r = asprintf(&buffer, "%s [NO-PIE]", buffer);
637 if (r < 0)
638 goto ERROR;
639 }
640
641 // Executable Stack
642 if (file->hardening_issues & PAKFIRE_FILE_EXECSTACK) {
643 r = asprintf(&buffer, "%s [EXECSTACK]", buffer);
644 if (r < 0)
645 goto ERROR;
646 }
647
648 // Not Partially RELRO
649 if (file->hardening_issues & PAKFIRE_FILE_NO_PARTIALLY_RELRO) {
650 r = asprintf(&buffer, "%s [NO-PART-RELRO]", buffer);
651 if (r < 0)
652 goto ERROR;
653 }
654 }
655 }
656
657 return buffer;
658
659 ERROR:
660 if (buffer)
661 free(buffer);
662
663 return NULL;
664 }
665
666 PAKFIRE_EXPORT int pakfire_file_cmp(struct pakfire_file* file1, struct pakfire_file* file2) {
667 const char* path1 = pakfire_file_get_path(file1);
668 const char* path2 = pakfire_file_get_path(file2);
669
670 return strcmp(path1, path2);
671 }
672
673 const char* pakfire_file_get_abspath(struct pakfire_file* file) {
674 return file->abspath;
675 }
676
677 int pakfire_file_set_abspath(struct pakfire_file* file, const char* path) {
678 int r;
679
680 // Check if path is set and absolute
681 if (!path || *path != '/') {
682 errno = EINVAL;
683 return 1;
684 }
685
686 // Store the abspath
687 r = pakfire_string_set(file->abspath, path);
688 if (r)
689 goto ERROR;
690
691 // Store path if it isn't set, yet
692 if (!*file->path) {
693 r = pakfire_file_set_path(file, path);
694 if (r)
695 goto ERROR;
696 }
697
698 return r;
699
700 ERROR:
701 ERROR(file->pakfire, "Could not set abspath '%s': %m\n", path);
702 return r;
703 }
704
705 PAKFIRE_EXPORT const char* pakfire_file_get_path(struct pakfire_file* file) {
706 return file->path;
707 }
708
709 PAKFIRE_EXPORT int pakfire_file_set_path(struct pakfire_file* file, const char* path) {
710 int r = 1;
711
712 // Check if path is set
713 if (!path) {
714 errno = EINVAL;
715 goto ERROR;
716 }
717
718 // Strip any leading dots from paths
719 if (pakfire_string_startswith(path, "./"))
720 path++;
721
722 switch (*path) {
723 // Just store the path if it is absolute
724 case '/':
725 r = pakfire_string_set(file->path, path);
726 if (r)
727 goto ERROR;
728 break;
729
730 // Handle relative paths
731 default:
732 r = pakfire_string_format(file->path, "/%s", path);
733 if (r)
734 goto ERROR;
735 break;
736 }
737
738 // Set abspath if it isn't set, yet
739 if (!*file->abspath) {
740 r = pakfire_file_set_abspath(file, file->path);
741 if (r)
742 goto ERROR;
743 }
744
745 return r;
746
747 ERROR:
748 ERROR(file->pakfire, "Could not set path '%s': %m\n", path);
749 return r;
750 }
751
752 PAKFIRE_EXPORT const char* pakfire_file_get_hardlink(struct pakfire_file* file) {
753 if (!*file->hardlink)
754 return NULL;
755
756 return file->hardlink;
757 }
758
759 PAKFIRE_EXPORT void pakfire_file_set_hardlink(struct pakfire_file* file, const char* link) {
760 pakfire_string_set(file->hardlink, link);
761 }
762
763 PAKFIRE_EXPORT const char* pakfire_file_get_symlink(struct pakfire_file* file) {
764 if (!*file->symlink)
765 return NULL;
766
767 return file->symlink;
768 }
769
770 PAKFIRE_EXPORT void pakfire_file_set_symlink(struct pakfire_file* file, const char* link) {
771 pakfire_string_set(file->symlink, link);
772 }
773
774 PAKFIRE_EXPORT nlink_t pakfire_file_get_nlink(struct pakfire_file* file) {
775 return file->st.st_nlink;
776 }
777
778 PAKFIRE_EXPORT void pakfire_file_set_nlink(struct pakfire_file* file, const nlink_t nlink) {
779 file->st.st_nlink = nlink;
780 }
781
782 PAKFIRE_EXPORT ino_t pakfire_file_get_inode(struct pakfire_file* file) {
783 return file->st.st_ino;
784 }
785
786 PAKFIRE_EXPORT void pakfire_file_set_inode(struct pakfire_file* file, const ino_t ino) {
787 file->st.st_ino = ino;
788 }
789
790 PAKFIRE_EXPORT dev_t pakfire_file_get_dev(struct pakfire_file* file) {
791 return file->st.st_dev;
792 }
793
794 PAKFIRE_EXPORT void pakfire_file_set_dev(struct pakfire_file* file, const dev_t dev) {
795 file->st.st_dev = dev;
796 }
797
798 PAKFIRE_EXPORT int pakfire_file_get_type(struct pakfire_file* file) {
799 return file->st.st_mode & S_IFMT;
800 }
801
802 PAKFIRE_EXPORT off_t pakfire_file_get_size(struct pakfire_file* file) {
803 return file->st.st_size;
804 }
805
806 PAKFIRE_EXPORT void pakfire_file_set_size(struct pakfire_file* file, off_t size) {
807 file->st.st_size = size;
808 }
809
810 PAKFIRE_EXPORT const char* pakfire_file_get_uname(struct pakfire_file* file) {
811 return file->uname;
812 }
813
814 PAKFIRE_EXPORT int pakfire_file_set_uname(struct pakfire_file* file, const char* uname) {
815 return pakfire_string_set(file->uname, uname);
816 }
817
818 PAKFIRE_EXPORT const char* pakfire_file_get_gname(struct pakfire_file* file) {
819 return file->gname;
820 }
821
822 PAKFIRE_EXPORT int pakfire_file_set_gname(struct pakfire_file* file, const char* gname) {
823 return pakfire_string_set(file->gname, gname);
824 }
825
826 PAKFIRE_EXPORT mode_t pakfire_file_get_mode(struct pakfire_file* file) {
827 return file->st.st_mode;
828 }
829
830 PAKFIRE_EXPORT void pakfire_file_set_mode(struct pakfire_file* file, mode_t mode) {
831 file->st.st_mode = mode;
832 }
833
834 PAKFIRE_EXPORT mode_t pakfire_file_get_perms(struct pakfire_file* file) {
835 return file->st.st_mode & ~S_IFMT;
836 }
837
838 PAKFIRE_EXPORT void pakfire_file_set_perms(struct pakfire_file* file, const mode_t perms) {
839 // Clear any previous permissions
840 file->st.st_mode &= S_IFMT;
841
842 // Set new bits (with format cleared)
843 file->st.st_mode |= ~S_IFMT & perms;
844 }
845
846 PAKFIRE_EXPORT time_t pakfire_file_get_ctime(struct pakfire_file* file) {
847 return file->st.st_ctime;
848 }
849
850 PAKFIRE_EXPORT void pakfire_file_set_ctime(struct pakfire_file* file, time_t time) {
851 file->st.st_ctime = time;
852 }
853
854 PAKFIRE_EXPORT time_t pakfire_file_get_mtime(struct pakfire_file* file) {
855 return file->st.st_mtime;
856 }
857
858 PAKFIRE_EXPORT void pakfire_file_set_mtime(struct pakfire_file* file, time_t time) {
859 file->st.st_mtime = time;
860 }
861
862 PAKFIRE_EXPORT const unsigned char* pakfire_file_get_digest(
863 struct pakfire_file* file, const enum pakfire_digest_types type, size_t* length) {
864
865 switch (type) {
866 case PAKFIRE_DIGEST_SHA3_512:
867 if (!pakfire_digest_set(file->digests.sha3_512))
868 return NULL;
869
870 if (length)
871 *length = sizeof(file->digests.sha3_512);
872
873 return file->digests.sha3_512;
874
875 case PAKFIRE_DIGEST_SHA3_256:
876 if (!pakfire_digest_set(file->digests.sha3_256))
877 return NULL;
878
879 if (length)
880 *length = sizeof(file->digests.sha3_256);
881
882 return file->digests.sha3_256;
883
884 case PAKFIRE_DIGEST_BLAKE2B512:
885 if (!pakfire_digest_set(file->digests.blake2b512))
886 return NULL;
887
888 if (length)
889 *length = sizeof(file->digests.blake2b512);
890
891 return file->digests.blake2b512;
892
893 case PAKFIRE_DIGEST_BLAKE2S256:
894 if (!pakfire_digest_set(file->digests.blake2s256))
895 return NULL;
896
897 if (length)
898 *length = sizeof(file->digests.blake2s256);
899
900 return file->digests.blake2s256;
901
902 case PAKFIRE_DIGEST_SHA2_512:
903 if (!pakfire_digest_set(file->digests.sha2_512))
904 return NULL;
905
906 if (length)
907 *length = sizeof(file->digests.sha2_512);
908
909 return file->digests.sha2_512;
910
911 case PAKFIRE_DIGEST_SHA2_256:
912 if (!pakfire_digest_set(file->digests.sha2_256))
913 return NULL;
914
915 if (length)
916 *length = sizeof(file->digests.sha2_256);
917
918 return file->digests.sha2_256;
919
920 case PAKFIRE_DIGEST_UNDEFINED:
921 break;
922 }
923
924 return NULL;
925 }
926
927 PAKFIRE_EXPORT int pakfire_file_set_digest(struct pakfire_file* file,
928 const enum pakfire_digest_types type, const unsigned char* digest, const size_t length) {
929 if (!digest) {
930 errno = EINVAL;
931 return 1;
932 }
933
934 // Check buffer length
935 if (pakfire_digest_length(type) != length) {
936 ERROR(file->pakfire, "Digest has an incorrect length of %zu byte(s)\n", length);
937 errno = ENOMSG;
938 return 1;
939 }
940
941 // Store the digest
942 switch (type) {
943 case PAKFIRE_DIGEST_SHA3_512:
944 memcpy(file->digests.sha3_512, digest, sizeof(file->digests.sha3_512));
945 break;
946
947 case PAKFIRE_DIGEST_SHA3_256:
948 memcpy(file->digests.sha3_256, digest, sizeof(file->digests.sha3_256));
949 break;
950
951 case PAKFIRE_DIGEST_BLAKE2B512:
952 memcpy(file->digests.blake2b512, digest, sizeof(file->digests.blake2b512));
953 break;
954
955 case PAKFIRE_DIGEST_BLAKE2S256:
956 memcpy(file->digests.blake2s256, digest, sizeof(file->digests.blake2s256));
957 break;
958
959 case PAKFIRE_DIGEST_SHA2_512:
960 memcpy(file->digests.sha2_512, digest, sizeof(file->digests.sha2_512));
961 break;
962
963 case PAKFIRE_DIGEST_SHA2_256:
964 memcpy(file->digests.sha2_256, digest, sizeof(file->digests.sha2_256));
965 break;
966
967 case PAKFIRE_DIGEST_UNDEFINED:
968 errno = ENOTSUP;
969 return 1;
970 }
971
972 return 0;
973 }
974
975 static int pakfire_file_levels(struct pakfire_file* file) {
976 if (!*file->path)
977 return 0;
978
979 int levels = 0;
980
981 for (char* p = file->path; *p; p++) {
982 if (*p == '/')
983 levels++;
984 }
985
986 return levels;
987 }
988
989 FILE* pakfire_file_open(struct pakfire_file* file) {
990 FILE* f = fopen(file->abspath, "r");
991 if (!f)
992 ERROR(file->pakfire, "Could not open %s: %m\n", file->abspath);
993
994 return f;
995 }
996
997 int pakfire_file_payload_matches(struct pakfire_file* file,
998 const void* needle, const size_t length) {
999 char buffer[1024 * 1024];
1000 FILE* f = NULL;
1001 void* p = NULL;
1002 int r;
1003
1004 // Only run for regular files
1005 if (!S_ISREG(file->st.st_mode))
1006 return 0;
1007
1008 // Open the file
1009 f = pakfire_file_open(file);
1010 if (!f)
1011 goto ERROR;
1012
1013 while (!feof(f)) {
1014 size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
1015
1016 // Raise any reading errors
1017 if (ferror(f)) {
1018 r = 1;
1019 goto ERROR;
1020 }
1021
1022 // Search for the needle
1023 p = memmem(buffer, bytes_read, needle, length);
1024 if (p) {
1025 r = 1;
1026 goto ERROR;
1027 }
1028 }
1029
1030 // No match
1031 r = 0;
1032
1033 ERROR:
1034 if (f)
1035 fclose(f);
1036
1037 return r;
1038 }
1039
1040 static int __pakfire_file_compute_digests(struct pakfire_file* file,
1041 struct pakfire_digests* digests, const int types) {
1042 FILE* f = NULL;
1043 int r = 1;
1044
1045 // Skip this for anything that isn't a regular file
1046 if (!S_ISREG(file->st.st_mode))
1047 return 0;
1048
1049 // Reset digests
1050 pakfire_digests_reset(digests, types);
1051
1052 // Open the file
1053 f = pakfire_file_open(file);
1054 if (!f)
1055 goto ERROR;
1056
1057 // Compute digests
1058 r = pakfire_digests_compute_from_file(file->pakfire, digests, types, f);
1059 if (r)
1060 goto ERROR;
1061
1062 ERROR:
1063 if (f)
1064 fclose(f);
1065
1066 return r;
1067 }
1068
1069 int pakfire_file_compute_digests(struct pakfire_file* file, const int types) {
1070 return __pakfire_file_compute_digests(file, &file->digests, types);
1071 }
1072
1073 int pakfire_file_remove(struct pakfire_file* file) {
1074 if (!*file->abspath) {
1075 errno = EINVAL;
1076 return 1;
1077 }
1078
1079 DEBUG(file->pakfire, "Removing %s...\n", file->path);
1080
1081 int r = remove(file->abspath);
1082 if (r) {
1083 switch (errno) {
1084 // Ignore when we could not remove directories
1085 case ENOTEMPTY:
1086 return 0;
1087
1088 // Ignore if the file didn't exist
1089 case ENOENT:
1090 return 0;
1091
1092 default:
1093 break;
1094 }
1095
1096 ERROR(file->pakfire, "Could not remove %s (%s): %m\n", file->path, file->abspath);
1097 }
1098
1099 return r;
1100 }
1101
1102 int pakfire_file_symlink_target_exists(struct pakfire_file* file) {
1103 // Fail if this got called for anything that isn't a symlink
1104 if (!S_ISLNK(file->st.st_mode)) {
1105 errno = EINVAL;
1106 return -1;
1107 }
1108
1109 return pakfire_path_exists(file->abspath);
1110 }
1111
1112 /*
1113 MIME Type
1114 */
1115
1116 int pakfire_file_detect_mimetype(struct pakfire_file* file) {
1117 // Only process regular files
1118 if (!S_ISREG(file->st.st_mode))
1119 return 0;
1120
1121 // Skip if MIME type is already set
1122 if (*file->mimetype)
1123 return 0;
1124
1125 // Fetch the magic cookie
1126 magic_t magic = pakfire_get_magic(file->pakfire);
1127 if (!magic)
1128 return 1;
1129
1130 // Check the file
1131 const char* mimetype = magic_file(magic, file->abspath);
1132 if (!mimetype) {
1133 ERROR(file->pakfire, "Could not classify %s: %s\n", file->path, magic_error(magic));
1134 return 1;
1135 }
1136
1137 DEBUG(file->pakfire, "Classified %s as %s\n", file->path, mimetype);
1138
1139 // Store the value
1140 return pakfire_file_set_mimetype(file, mimetype);
1141 }
1142
1143 PAKFIRE_EXPORT const char* pakfire_file_get_mimetype(struct pakfire_file* file) {
1144 // Return nothing on an empty mimetype
1145 if (!*file->mimetype)
1146 return NULL;
1147
1148 return file->mimetype;
1149 }
1150
1151 PAKFIRE_EXPORT int pakfire_file_set_mimetype(
1152 struct pakfire_file* file, const char* mimetype) {
1153 // Store the value
1154 return pakfire_string_set(file->mimetype, mimetype);
1155 }
1156
1157 /*
1158 Classification
1159 */
1160
1161 static int setup_libelf(struct pakfire* pakfire) {
1162 // Initialize libelf
1163 if (elf_version(EV_CURRENT) == EV_NONE) {
1164 ERROR(pakfire, "Could not initialize libelf: %s\n", elf_errmsg(-1));
1165
1166 return 1;
1167 }
1168
1169 return 0;
1170 }
1171
1172 static int pakfire_file_classify_mode(struct pakfire_file* file) {
1173 // Check for regular files
1174 if (S_ISREG(file->st.st_mode)) {
1175 file->class |= PAKFIRE_FILE_REGULAR;
1176
1177 // Does the file have executable permissions?
1178 if (file->st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
1179 file->class |= PAKFIRE_FILE_EXECUTABLE;
1180
1181 // Check for directories
1182 } else if (S_ISDIR(file->st.st_mode))
1183 file->class |= PAKFIRE_FILE_DIRECTORY;
1184
1185 // Check for symlinks
1186 else if (S_ISLNK(file->st.st_mode))
1187 file->class |= PAKFIRE_FILE_SYMLINK;
1188
1189 // Check for character devices
1190 else if (S_ISCHR(file->st.st_mode))
1191 file->class |= PAKFIRE_FILE_CHARACTER;
1192
1193 // Check for block devices
1194 else if (S_ISBLK(file->st.st_mode))
1195 file->class |= PAKFIRE_FILE_BLOCK;
1196
1197 // Check for FIFO pipes
1198 else if (S_ISFIFO(file->st.st_mode))
1199 file->class |= PAKFIRE_FILE_FIFO;
1200
1201 // Check for sockets
1202 else if (S_ISSOCK(file->st.st_mode))
1203 file->class |= PAKFIRE_FILE_SOCKET;
1204
1205 return 0;
1206 }
1207
1208 static const struct pattern {
1209 const char* pattern;
1210 int class;
1211 } patterns[] = {
1212 { "*.a", PAKFIRE_FILE_STATIC_LIBRARY },
1213 { "*.la", PAKFIRE_FILE_LIBTOOL_ARCHIVE },
1214 { "*.pm", PAKFIRE_FILE_PERL },
1215 { "*.pc", PAKFIRE_FILE_PKGCONFIG },
1216 { "/usr/lib/firmware/*", PAKFIRE_FILE_FIRMWARE },
1217 { "/usr/lib*/ld-*.so*", PAKFIRE_FILE_RUNTIME_LINKER },
1218 { NULL },
1219 };
1220
1221 static int pakfire_file_classify_pattern(struct pakfire_file* file) {
1222 for (const struct pattern* p = patterns; p->pattern; p++) {
1223 if (pakfire_file_matches(file, p->pattern)) {
1224 file->class |= p->class;
1225 break;
1226 }
1227 }
1228
1229 return 0;
1230 }
1231
1232 static const struct mimetype {
1233 const char* mimetype;
1234 int class;
1235 } mimetypes[] = {
1236 { "text/x-perl", PAKFIRE_FILE_PERL },
1237 { NULL, 0 },
1238 };
1239
1240 static int pakfire_file_classify_magic(struct pakfire_file* file) {
1241 int r;
1242
1243 // Detect the MIME type
1244 r = pakfire_file_detect_mimetype(file);
1245 if (r)
1246 return r;
1247
1248 // Fetch the MIME type
1249 const char* mimetype = pakfire_file_get_mimetype(file);
1250 if (!mimetype)
1251 return 1;
1252
1253 // Match the MIME type with a flag
1254 for (const struct mimetype* m = mimetypes; m->mimetype; m++) {
1255 if (strcmp(m->mimetype, mimetype) == 0) {
1256 file->class |= m->class;
1257 break;
1258 }
1259 }
1260
1261 return 0;
1262 }
1263
1264 static int pakfire_file_classify_elf(struct pakfire_file* file) {
1265 FILE* f = NULL;
1266 Elf* elf = NULL;
1267 int r;
1268
1269 // Don't run this if we already know that file is an ELF file
1270 if (file->class & PAKFIRE_FILE_ELF)
1271 return 0;
1272
1273 // Setup libelf
1274 r = setup_libelf(file->pakfire);
1275 if (r)
1276 return r;
1277
1278 // Open the file
1279 f = fopen(file->abspath, "r");
1280 if (!f) {
1281 ERROR(file->pakfire, "Could not open %s: %m\n", file->path);
1282 return 1;
1283 }
1284
1285 // Try to open the ELF file
1286 elf = elf_begin(fileno(f), ELF_C_READ, NULL);
1287 if (!elf) {
1288 // We fail silently here, because this file might be in a different format
1289 goto ERROR;
1290 }
1291
1292 switch (elf_kind(elf)) {
1293 // Mark this file as an ELF file
1294 case ELF_K_ELF:
1295 file->class |= PAKFIRE_FILE_ELF;
1296 break;
1297
1298 // Ignore everything else
1299 default:
1300 break;
1301 }
1302
1303 ERROR:
1304 if (elf)
1305 elf_end(elf);
1306 if (f)
1307 fclose(f);
1308
1309 return 0;
1310 }
1311
1312 int pakfire_file_classify(struct pakfire_file* file) {
1313 int r;
1314
1315 if (!file->class) {
1316 // First, check the mode so that we won't run magic on directories, symlinks, ...
1317 r = pakfire_file_classify_mode(file);
1318 if (r)
1319 goto ERROR;
1320
1321 // Only run this for regular files
1322 if (file->class & PAKFIRE_FILE_REGULAR) {
1323 // Then check for patterns
1324 r = pakfire_file_classify_pattern(file);
1325 if (r)
1326 goto ERROR;
1327
1328 // After that, we will use libmagic...
1329 r = pakfire_file_classify_magic(file);
1330 if (r)
1331 goto ERROR;
1332
1333 // Check if the file is an ELF file
1334 r = pakfire_file_classify_elf(file);
1335 if (r)
1336 goto ERROR;
1337 }
1338 }
1339
1340 return file->class;
1341
1342 ERROR:
1343 // Reset the class
1344 file->class = PAKFIRE_FILE_UNKNOWN;
1345
1346 return r;
1347 }
1348
1349 int pakfire_file_matches_class(struct pakfire_file* file, const int class) {
1350 return pakfire_file_classify(file) & class;
1351 }
1352
1353 /*
1354 This function tries to remove the file after it has been packaged.
1355
1356 It will try to delete any parent directories as well and ignore if directories
1357 cannot be deleted because they might contain other files
1358 */
1359 int pakfire_file_cleanup(struct pakfire_file* file) {
1360 char path[PATH_MAX];
1361
1362 // Try removing the file
1363 int r = pakfire_file_remove(file);
1364 if (r)
1365 return r;
1366
1367 // Create a working copy of abspath
1368 r = pakfire_string_set(path, file->abspath);
1369 if (r)
1370 return r;
1371
1372 // See how many levels this file has
1373 int levels = pakfire_file_levels(file);
1374
1375 // Walk all the way up and remove all parent directories if possible
1376 while (--levels) {
1377 dirname(path);
1378
1379 // Break if path is suddenly empty
1380 if (!*path)
1381 break;
1382
1383 DEBUG(file->pakfire, "Trying to remove parent directory %s\n", path);
1384
1385 r = rmdir(path);
1386
1387 // Break on any error
1388 if (r)
1389 break;
1390 }
1391
1392 return 0;
1393 }
1394
1395 static int pakfire_file_verify_mode(struct pakfire_file* file, const struct stat* st) {
1396 const mode_t type = pakfire_file_get_type(file);
1397
1398 // Did the type change?
1399 if (type != (st->st_mode & S_IFMT)) {
1400 file->verify_status |= PAKFIRE_FILE_TYPE_CHANGED;
1401
1402 DEBUG(file->pakfire, "%s: File Type changed\n", file->path);
1403 }
1404
1405 const mode_t perms = pakfire_file_get_perms(file);
1406
1407 // Check permissions
1408 if (perms != (st->st_mode & 0777)) {
1409 file->verify_status |= PAKFIRE_FILE_PERMISSIONS_CHANGED;
1410
1411 DEBUG(file->pakfire, "%s: Permissions changed\n", file->path);
1412 }
1413
1414 #if 0
1415 // XXX This does not check what it is supposed to check
1416
1417 // Check if device node changed
1418 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
1419 const dev_t dev = pakfire_file_get_dev(file);
1420
1421 if (dev != st->st_dev) {
1422 file->verify_status |= PAKFIRE_FILE_DEV_CHANGED;
1423
1424 DEBUG(file->pakfire, "%s: Device Node changed\n", file->path);
1425 }
1426 }
1427 #endif
1428
1429 return 0;
1430 }
1431
1432 static int pakfire_file_verify_size(struct pakfire_file* file, const struct stat* st) {
1433 // Nothing to do if size matches
1434 if (file->st.st_size == st->st_size)
1435 return 0;
1436
1437 // Size differs
1438 file->verify_status |= PAKFIRE_FILE_SIZE_CHANGED;
1439
1440 DEBUG(file->pakfire, "%s: Filesize differs (expected %zu, got %zu byte(s))\n",
1441 file->path, file->st.st_size, st->st_size);
1442
1443 return 0;
1444 }
1445
1446 static int pakfire_file_verify_ownership(struct pakfire_file* file, const struct stat* st) {
1447 // Fetch UID/GID
1448 #if 0
1449 const uid_t uid = pakfire_unmap_id(file->pakfire, st->st_uid);
1450 const gid_t gid = pakfire_unmap_id(file->pakfire, st->st_gid);
1451 #else
1452 const uid_t uid = st->st_uid;
1453 const gid_t gid = st->st_gid;
1454 #endif
1455
1456 // Fetch owner & group
1457 struct passwd* owner = pakfire_getpwnam(file->pakfire, file->uname);
1458 struct group* group = pakfire_getgrnam(file->pakfire, file->gname);
1459
1460 // Check if owner matches
1461 if (!owner || owner->pw_uid != uid) {
1462 file->verify_status |= PAKFIRE_FILE_OWNER_CHANGED;
1463
1464 DEBUG(file->pakfire, "%s: Owner differs\n", file->path);
1465 }
1466
1467 // Check if group matches
1468 if (!group || group->gr_gid != gid) {
1469 file->verify_status |= PAKFIRE_FILE_GROUP_CHANGED;
1470
1471 DEBUG(file->pakfire, "%s: Group differs\n", file->path);
1472 }
1473
1474 return 0;
1475 }
1476
1477 static int pakfire_file_verify_timestamps(struct pakfire_file* file, const struct stat* st) {
1478 // Check creation time
1479 if (file->st.st_ctime != st->st_ctime) {
1480 file->verify_status |= PAKFIRE_FILE_CTIME_CHANGED;
1481
1482 DEBUG(file->pakfire, "%s: Creation time changed\n", file->path);
1483 }
1484
1485 // Check modification time
1486 if (file->st.st_mtime != st->st_mtime) {
1487 file->verify_status |= PAKFIRE_FILE_MTIME_CHANGED;
1488
1489 DEBUG(file->pakfire, "%s: Modification time changed\n", file->path);
1490 }
1491
1492 return 0;
1493 }
1494
1495 static int pakfire_file_verify_payload(struct pakfire_file* file, const struct stat* st) {
1496 int r;
1497
1498 struct pakfire_digests computed_digests;
1499 int digest_types = PAKFIRE_DIGEST_UNDEFINED;
1500
1501 // Nothing to do for anything that isn't a regular file
1502 if (!S_ISREG(st->st_mode))
1503 return 0;
1504
1505 // Fast-path if size changed. The payload will have changed, too
1506 if (file->verify_status & PAKFIRE_FILE_SIZE_CHANGED) {
1507 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1508 return 0;
1509 }
1510
1511 // Check if this file has any digests at all
1512 digest_types = pakfire_digest_has_any(&file->digests);
1513
1514 if (!digest_types) {
1515 ERROR(file->pakfire, "%s: No digests available\n", file->path);
1516 return 0;
1517 }
1518
1519 // Compute digests
1520 r = __pakfire_file_compute_digests(file, &computed_digests, digest_types);
1521 if (r)
1522 goto ERROR;
1523
1524 // Compare digests
1525 r = pakfire_digests_compare(file->pakfire, &file->digests, &computed_digests, digest_types);
1526 if (r) {
1527 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1528
1529 DEBUG(file->pakfire, "%s: Digest(s) do not match\n", file->path);
1530 }
1531
1532 ERROR:
1533 return r;
1534 }
1535
1536 /*
1537 Verify the file - i.e. does the metadata match what is on disk?
1538 */
1539 int pakfire_file_verify(struct pakfire_file* file, int* status) {
1540 struct stat st;
1541 int r;
1542
1543 DEBUG(file->pakfire, "Verifying %s...\n", file->path);
1544
1545 // stat() the file
1546 r = lstat(file->abspath, &st);
1547 if (r) {
1548 // File does not exist
1549 if (errno == ENOENT) {
1550 file->verify_status |= PAKFIRE_FILE_NOENT;
1551 return 1;
1552 }
1553
1554 // Raise any other errors from stat()
1555 return r;
1556 }
1557
1558 // Verify mode
1559 r = pakfire_file_verify_mode(file, &st);
1560 if (r)
1561 return r;
1562
1563 // Verify size
1564 r = pakfire_file_verify_size(file, &st);
1565 if (r)
1566 return r;
1567
1568 // Verify ownership
1569 r = pakfire_file_verify_ownership(file, &st);
1570 if (r)
1571 return r;
1572
1573 // Verify timestamps
1574 r = pakfire_file_verify_timestamps(file, &st);
1575 if (r)
1576 return r;
1577
1578 // Verify payload
1579 r = pakfire_file_verify_payload(file, &st);
1580 if (r)
1581 return r;
1582
1583 return 0;
1584 }
1585
1586 PAKFIRE_EXPORT int pakfire_file_matches(struct pakfire_file* file, const char* pattern) {
1587 int r;
1588
1589 // Don't match on no pattern
1590 if (!pattern)
1591 return 0;
1592
1593 // Check if the pattern matches
1594 r = fnmatch(pattern, file->path, 0);
1595 switch (r) {
1596 // Match
1597 case 0:
1598 return 1;
1599
1600 // No Match
1601 case FNM_NOMATCH:
1602 return 0;
1603
1604 default:
1605 return -1;
1606 }
1607 }
1608
1609 /*
1610 ELF Stuff
1611 */
1612
1613 static int pakfire_file_open_elf(struct pakfire_file* file,
1614 int (*callback)(struct pakfire_file* file, Elf* elf, void* data), void* data) {
1615 FILE* f = NULL;
1616 Elf* elf = NULL;
1617 int r;
1618
1619 // Don't run this for non-ELF files
1620 if (!pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
1621 errno = EINVAL;
1622 return 1;
1623 }
1624
1625 // Setup libelf
1626 r = setup_libelf(file->pakfire);
1627 if (r)
1628 return r;
1629
1630 // Open the file
1631 f = fopen(file->abspath, "r");
1632 if (!f) {
1633 ERROR(file->pakfire, "Could not open %s: %m\n", file->abspath);
1634 return 1;
1635 }
1636
1637 // Parse the ELF header
1638 elf = elf_begin(fileno(f), ELF_C_READ, NULL);
1639 if (!elf) {
1640 ERROR(file->pakfire, "Could not open ELF file: %s\n", elf_errmsg(-1));
1641 r = 1;
1642 goto ERROR;
1643 }
1644
1645 // Check if this is an ELF file
1646 switch (elf_kind(elf)) {
1647 case ELF_K_ELF:
1648 break;
1649
1650 default:
1651 ERROR(file->pakfire, "%s is not an ELF object\n", file->path);
1652 r = 1;
1653 goto ERROR;
1654 }
1655
1656 // Call the callback
1657 r = callback(file, elf, data);
1658
1659 ERROR:
1660 if (elf)
1661 elf_end(elf);
1662 if (f)
1663 fclose(f);
1664
1665 return r;
1666 }
1667
1668 static int __pakfire_file_get_elf_type(struct pakfire_file* file, Elf* elf, void* data) {
1669 int* type = (int*)data;
1670 GElf_Ehdr ehdr;
1671
1672 // Fetch the ELF header
1673 if (!gelf_getehdr(elf, &ehdr)) {
1674 ERROR(file->pakfire, "Could not parse ELF header: %s\n", elf_errmsg(-1));
1675 return 1;
1676 }
1677
1678 // Store the type
1679 *type = ehdr.e_type;
1680
1681 return 0;
1682 }
1683
1684 static int pakfire_file_get_elf_type(struct pakfire_file* file) {
1685 int type = ET_NONE;
1686 int r;
1687
1688 r = pakfire_file_open_elf(file, __pakfire_file_get_elf_type, &type);
1689 if (r)
1690 return -1;
1691
1692 return type;
1693 }
1694
1695 static int __pakfire_file_is_stripped(struct pakfire_file* file, Elf* elf, void* data) {
1696 Elf_Scn* section = NULL;
1697 GElf_Shdr shdr;
1698
1699 // Walk through all sections
1700 for (;;) {
1701 section = elf_nextscn(elf, section);
1702 if (!section)
1703 break;
1704
1705 // Fetch the section header
1706 gelf_getshdr(section, &shdr);
1707
1708 switch (shdr.sh_type) {
1709 // Break if we found the symbol table
1710 case SHT_SYMTAB:
1711 return 0;
1712 }
1713 }
1714
1715 // Not found
1716 DEBUG(file->pakfire, "%s has no debug sections\n", file->path);
1717
1718 return 1;
1719 }
1720
1721 int pakfire_file_is_stripped(struct pakfire_file* file) {
1722 // Don't run this for non-ELF files
1723 if (!pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
1724 errno = EINVAL;
1725 return -1;
1726 }
1727
1728 // Do not perform this check on firmware
1729 if (pakfire_file_matches_class(file, PAKFIRE_FILE_FIRMWARE))
1730 return 0;
1731
1732 switch (pakfire_file_get_elf_type(file)) {
1733 // Do not check Relocatable Objects
1734 case ET_REL:
1735 return 0;
1736
1737 // Check everything else
1738 default:
1739 break;
1740 }
1741
1742 return pakfire_file_open_elf(file, __pakfire_file_is_stripped, NULL);
1743 }
1744
1745 static int __pakfire_file_hardening_check_ssp(
1746 struct pakfire_file* file, Elf* elf, void* data) {
1747 Elf_Scn* section = NULL;
1748 GElf_Shdr section_header;
1749 Elf_Data* elf_data = NULL;
1750 GElf_Sym symbol;
1751 const char* name = NULL;
1752
1753 // Count any global functions
1754 size_t counter = 0;
1755
1756 // Walk through all sections
1757 for (;;) {
1758 section = elf_nextscn(elf, section);
1759 if (!section) {
1760 ERROR(file->pakfire, "%s has no symbol table\n", file->path);
1761 return 1;
1762 }
1763
1764 // Fetch the section header
1765 gelf_getshdr(section, &section_header);
1766
1767 // Break if we found the symbol table
1768 if (section_header.sh_type == SHT_SYMTAB)
1769 break;
1770 }
1771
1772 // Fetch a pointer to the section data
1773 elf_data = elf_getdata(section, NULL);
1774
1775 // Walk through all symbols
1776 for (unsigned int i = 0; i < section_header.sh_size / section_header.sh_entsize; i++) {
1777 gelf_getsym(elf_data, i, &symbol);
1778
1779 // Fetch the symbol name
1780 name = elf_strptr(elf, section_header.sh_link, symbol.st_name);
1781
1782 // Skip empty section names
1783 if (!name || !*name)
1784 continue;
1785
1786 // Exit if there is a symbol called "__stack_chk_fail"
1787 if (pakfire_string_startswith(name, "__stack_chk_fail"))
1788 return 0;
1789
1790 // Count any global functions
1791 if ((ELF64_ST_BIND(symbol.st_info) == STB_GLOBAL) &&
1792 (ELF64_ST_TYPE(symbol.st_info) == STT_FUNC))
1793 counter++;
1794 }
1795
1796 // We do not perform the check for libraries that do not contain any functions.
1797 // Some packages use shared libraries to provide data.
1798 if (!counter) {
1799 DEBUG(file->pakfire, "%s: File has no functions. Skipping SSP check.\n", file->path);
1800 return 0;
1801 }
1802
1803 // The file does not seem to have SSP enabled
1804 file->hardening_issues |= PAKFIRE_FILE_NO_SSP;
1805
1806 return 0;
1807 }
1808
1809 static int pakfire_file_hardening_check_ssp(struct pakfire_file* file) {
1810 // Do not perform this check for runtime linkers
1811 if (pakfire_file_matches_class(file, PAKFIRE_FILE_RUNTIME_LINKER))
1812 return 0;
1813
1814 return pakfire_file_open_elf(file, __pakfire_file_hardening_check_ssp, NULL);
1815 }
1816
1817 static int pakfire_file_hardening_check_pie(struct pakfire_file* file) {
1818 switch (pakfire_file_get_elf_type(file)) {
1819 // Shared Object files are good
1820 case ET_DYN:
1821 break;
1822
1823 // Everything else is bad
1824 default:
1825 file->hardening_issues |= PAKFIRE_FILE_NO_PIE;
1826 break;
1827 }
1828
1829 return 0;
1830 }
1831
1832 static int __pakfire_file_hardening_check_execstack(
1833 struct pakfire_file* file, Elf* elf, void* data) {
1834 GElf_Phdr phdr;
1835 int r;
1836
1837 size_t phnum = 0;
1838
1839 // Fetch the total numbers of program headers
1840 r = elf_getphdrnum(elf, &phnum);
1841 if (r) {
1842 ERROR(file->pakfire, "Could not fetch number of program headers: %s\n",
1843 elf_errmsg(-1));
1844 return 1;
1845 }
1846
1847 // Walk through all program headers
1848 for (unsigned int i = 0; i < phnum; i++) {
1849 if (!gelf_getphdr(elf, i, &phdr)) {
1850 ERROR(file->pakfire, "Could not parse program header: %s\n", elf_errmsg(-1));
1851 return 1;
1852 }
1853
1854 switch (phdr.p_type) {
1855 case PT_GNU_STACK:
1856 DEBUG(file->pakfire,
1857 "%s: GNU_STACK flags: %c%c%c\n",
1858 file->path,
1859 (phdr.p_flags & PF_R) ? 'R' : '-',
1860 (phdr.p_flags & PF_W) ? 'W' : '-',
1861 (phdr.p_flags & PF_X) ? 'X' : '-'
1862 );
1863
1864 // The stack cannot be writable and executable
1865 if ((phdr.p_flags & PF_W) && (phdr.p_flags & PF_X))
1866 file->hardening_issues |= PAKFIRE_FILE_EXECSTACK;
1867
1868 // Done
1869 return 0;
1870
1871 default:
1872 break;
1873 }
1874 }
1875
1876 return 0;
1877 }
1878
1879 static int pakfire_file_hardening_check_execstack(struct pakfire_file* file) {
1880 return pakfire_file_open_elf(file, __pakfire_file_hardening_check_execstack, NULL);
1881 }
1882
1883 static int __pakfire_file_hardening_check_partially_relro(
1884 struct pakfire_file* file, Elf* elf, void* data) {
1885 GElf_Phdr phdr;
1886 int r;
1887
1888 size_t phnum = 0;
1889
1890 // Fetch the total numbers of program headers
1891 r = elf_getphdrnum(elf, &phnum);
1892 if (r) {
1893 ERROR(file->pakfire, "Could not fetch number of program headers: %s\n",
1894 elf_errmsg(-1));
1895 return 1;
1896 }
1897
1898 // Walk through all program headers
1899 for (unsigned int i = 0; i < phnum; i++) {
1900 if (!gelf_getphdr(elf, i, &phdr)) {
1901 ERROR(file->pakfire, "Could not parse program header: %s\n", elf_errmsg(-1));
1902 return 1;
1903 }
1904
1905 switch (phdr.p_type) {
1906 case PT_GNU_RELRO:
1907 return 0;
1908
1909 default:
1910 break;
1911 }
1912 }
1913
1914 // This file does not seem to have PT_GNU_RELRO set
1915 file->hardening_issues |= PAKFIRE_FILE_NO_PARTIALLY_RELRO;
1916
1917 return 0;
1918 }
1919
1920 static int pakfire_file_hardening_check_relro(struct pakfire_file* file) {
1921 return pakfire_file_open_elf(file, __pakfire_file_hardening_check_partially_relro, NULL);
1922 }
1923
1924
1925 int pakfire_file_check_hardening(struct pakfire_file* file, int* issues) {
1926 int r;
1927
1928 // Return previous result if this has been run before
1929 if (!file->hardening_check_done) {
1930 // Perform FHS check
1931 r = pakfire_fhs_check_file(file->pakfire, file);
1932 if (r)
1933 file->hardening_issues |= PAKFIRE_FILE_FHS_ERROR;
1934
1935 // Do not perform the following checks on firmware
1936 if (pakfire_file_matches_class(file, PAKFIRE_FILE_FIRMWARE))
1937 goto DONE;
1938
1939 // Run these checks only for ELF files
1940 if (pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
1941 switch (pakfire_file_get_elf_type(file)) {
1942 // Do not check Relocatable Objects
1943 case ET_REL:
1944 goto DONE;
1945
1946 // Check everything else
1947 default:
1948 break;
1949 }
1950
1951 // Check for SSP
1952 r = pakfire_file_hardening_check_ssp(file);
1953 if (r)
1954 return r;
1955
1956 // Check for PIE
1957 r = pakfire_file_hardening_check_pie(file);
1958 if (r)
1959 return r;
1960
1961 // Check for executable stacks
1962 r = pakfire_file_hardening_check_execstack(file);
1963 if (r)
1964 return r;
1965
1966 // Check for RELRO
1967 r = pakfire_file_hardening_check_relro(file);
1968 if (r)
1969 return r;
1970 }
1971
1972 DONE:
1973 // All checks done
1974 file->hardening_check_done = 1;
1975 }
1976
1977 // Return any issues
1978 if (issues)
1979 *issues = file->hardening_issues;
1980
1981 return 0;
1982 }