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