]> git.ipfire.org Git - people/stevee/pakfire.git/blob - src/libpakfire/file.c
10cd8439a808d24f66602f3165ead10de2cd78d8
[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 // File Issues
96 int issues;
97 int 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 // Dump Issues
619 if (flags & PAKFIRE_FILE_DUMP_ISSUES) {
620 if (file->issues & PAKFIRE_FILE_FHS_ERROR) {
621 r = asprintf(&buffer, "%s [FHS-ERROR]", buffer);
622 if (r < 0)
623 goto ERROR;
624 }
625
626 if (file->issues & PAKFIRE_FILE_MISSING_DEBUGINFO) {
627 r = asprintf(&buffer, "%s [MISSING-DEBUGINFO]", buffer);
628 if (r < 0)
629 goto ERROR;
630 }
631
632 // Stack-smashing Protection
633 if (file->issues & PAKFIRE_FILE_MISSING_SSP) {
634 r = asprintf(&buffer, "%s [MISSING-SSP]", buffer);
635 if (r < 0)
636 goto ERROR;
637 }
638
639 // Position-independent Executable
640 if (file->issues & PAKFIRE_FILE_MISSING_PIE) {
641 r = asprintf(&buffer, "%s [MISSING-PIE]", buffer);
642 if (r < 0)
643 goto ERROR;
644 }
645
646 // Executable Stack
647 if (file->issues & PAKFIRE_FILE_EXECSTACK) {
648 r = asprintf(&buffer, "%s [EXECSTACK]", buffer);
649 if (r < 0)
650 goto ERROR;
651 }
652
653 // Not Partially RELRO
654 if (file->issues & PAKFIRE_FILE_NO_PARTIALLY_RELRO) {
655 r = asprintf(&buffer, "%s [NO-PART-RELRO]", buffer);
656 if (r < 0)
657 goto ERROR;
658 }
659
660 // Has RUNPATH?
661 if (file->issues & PAKFIRE_FILE_HAS_RUNPATH) {
662 r = asprintf(&buffer, "%s [HAS-RUNPATH]", buffer);
663 if (r < 0)
664 goto ERROR;
665 }
666 }
667
668 return buffer;
669
670 ERROR:
671 if (buffer)
672 free(buffer);
673
674 return NULL;
675 }
676
677 PAKFIRE_EXPORT int pakfire_file_cmp(struct pakfire_file* file1, struct pakfire_file* file2) {
678 const char* path1 = pakfire_file_get_path(file1);
679 const char* path2 = pakfire_file_get_path(file2);
680
681 return strcmp(path1, path2);
682 }
683
684 const char* pakfire_file_get_abspath(struct pakfire_file* file) {
685 return file->abspath;
686 }
687
688 int pakfire_file_set_abspath(struct pakfire_file* file, const char* path) {
689 int r;
690
691 // Check if path is set and absolute
692 if (!path || *path != '/') {
693 errno = EINVAL;
694 return 1;
695 }
696
697 // Store the abspath
698 r = pakfire_string_set(file->abspath, path);
699 if (r)
700 goto ERROR;
701
702 // Store path if it isn't set, yet
703 if (!*file->path) {
704 r = pakfire_file_set_path(file, path);
705 if (r)
706 goto ERROR;
707 }
708
709 return r;
710
711 ERROR:
712 ERROR(file->pakfire, "Could not set abspath '%s': %m\n", path);
713 return r;
714 }
715
716 PAKFIRE_EXPORT const char* pakfire_file_get_path(struct pakfire_file* file) {
717 return file->path;
718 }
719
720 PAKFIRE_EXPORT int pakfire_file_set_path(struct pakfire_file* file, const char* path) {
721 int r = 1;
722
723 // Check if path is set
724 if (!path) {
725 errno = EINVAL;
726 goto ERROR;
727 }
728
729 // Strip any leading dots from paths
730 if (pakfire_string_startswith(path, "./"))
731 path++;
732
733 switch (*path) {
734 // Just store the path if it is absolute
735 case '/':
736 r = pakfire_string_set(file->path, path);
737 if (r)
738 goto ERROR;
739 break;
740
741 // Handle relative paths
742 default:
743 r = pakfire_string_format(file->path, "/%s", path);
744 if (r)
745 goto ERROR;
746 break;
747 }
748
749 // Set abspath if it isn't set, yet
750 if (!*file->abspath) {
751 r = pakfire_file_set_abspath(file, file->path);
752 if (r)
753 goto ERROR;
754 }
755
756 return r;
757
758 ERROR:
759 ERROR(file->pakfire, "Could not set path '%s': %m\n", path);
760 return r;
761 }
762
763 PAKFIRE_EXPORT const char* pakfire_file_get_hardlink(struct pakfire_file* file) {
764 if (!*file->hardlink)
765 return NULL;
766
767 return file->hardlink;
768 }
769
770 PAKFIRE_EXPORT void pakfire_file_set_hardlink(struct pakfire_file* file, const char* link) {
771 pakfire_string_set(file->hardlink, link);
772 }
773
774 PAKFIRE_EXPORT const char* pakfire_file_get_symlink(struct pakfire_file* file) {
775 if (!*file->symlink)
776 return NULL;
777
778 return file->symlink;
779 }
780
781 PAKFIRE_EXPORT void pakfire_file_set_symlink(struct pakfire_file* file, const char* link) {
782 pakfire_string_set(file->symlink, link);
783 }
784
785 PAKFIRE_EXPORT nlink_t pakfire_file_get_nlink(struct pakfire_file* file) {
786 return file->st.st_nlink;
787 }
788
789 PAKFIRE_EXPORT void pakfire_file_set_nlink(struct pakfire_file* file, const nlink_t nlink) {
790 file->st.st_nlink = nlink;
791 }
792
793 PAKFIRE_EXPORT ino_t pakfire_file_get_inode(struct pakfire_file* file) {
794 return file->st.st_ino;
795 }
796
797 PAKFIRE_EXPORT void pakfire_file_set_inode(struct pakfire_file* file, const ino_t ino) {
798 file->st.st_ino = ino;
799 }
800
801 PAKFIRE_EXPORT dev_t pakfire_file_get_dev(struct pakfire_file* file) {
802 return file->st.st_dev;
803 }
804
805 PAKFIRE_EXPORT void pakfire_file_set_dev(struct pakfire_file* file, const dev_t dev) {
806 file->st.st_dev = dev;
807 }
808
809 PAKFIRE_EXPORT int pakfire_file_get_type(struct pakfire_file* file) {
810 return file->st.st_mode & S_IFMT;
811 }
812
813 PAKFIRE_EXPORT off_t pakfire_file_get_size(struct pakfire_file* file) {
814 return file->st.st_size;
815 }
816
817 PAKFIRE_EXPORT void pakfire_file_set_size(struct pakfire_file* file, off_t size) {
818 file->st.st_size = size;
819 }
820
821 PAKFIRE_EXPORT const char* pakfire_file_get_uname(struct pakfire_file* file) {
822 return file->uname;
823 }
824
825 PAKFIRE_EXPORT int pakfire_file_set_uname(struct pakfire_file* file, const char* uname) {
826 return pakfire_string_set(file->uname, uname);
827 }
828
829 PAKFIRE_EXPORT const char* pakfire_file_get_gname(struct pakfire_file* file) {
830 return file->gname;
831 }
832
833 PAKFIRE_EXPORT int pakfire_file_set_gname(struct pakfire_file* file, const char* gname) {
834 return pakfire_string_set(file->gname, gname);
835 }
836
837 PAKFIRE_EXPORT mode_t pakfire_file_get_mode(struct pakfire_file* file) {
838 return file->st.st_mode;
839 }
840
841 PAKFIRE_EXPORT void pakfire_file_set_mode(struct pakfire_file* file, mode_t mode) {
842 file->st.st_mode = mode;
843 }
844
845 PAKFIRE_EXPORT mode_t pakfire_file_get_perms(struct pakfire_file* file) {
846 return file->st.st_mode & ~S_IFMT;
847 }
848
849 PAKFIRE_EXPORT void pakfire_file_set_perms(struct pakfire_file* file, const mode_t perms) {
850 // Clear any previous permissions
851 file->st.st_mode &= S_IFMT;
852
853 // Set new bits (with format cleared)
854 file->st.st_mode |= ~S_IFMT & perms;
855 }
856
857 PAKFIRE_EXPORT time_t pakfire_file_get_ctime(struct pakfire_file* file) {
858 return file->st.st_ctime;
859 }
860
861 PAKFIRE_EXPORT void pakfire_file_set_ctime(struct pakfire_file* file, time_t time) {
862 file->st.st_ctime = time;
863 }
864
865 PAKFIRE_EXPORT time_t pakfire_file_get_mtime(struct pakfire_file* file) {
866 return file->st.st_mtime;
867 }
868
869 PAKFIRE_EXPORT void pakfire_file_set_mtime(struct pakfire_file* file, time_t time) {
870 file->st.st_mtime = time;
871 }
872
873 PAKFIRE_EXPORT const unsigned char* pakfire_file_get_digest(
874 struct pakfire_file* file, const enum pakfire_digest_types type, size_t* length) {
875
876 switch (type) {
877 case PAKFIRE_DIGEST_SHA3_512:
878 if (!pakfire_digest_set(file->digests.sha3_512))
879 return NULL;
880
881 if (length)
882 *length = sizeof(file->digests.sha3_512);
883
884 return file->digests.sha3_512;
885
886 case PAKFIRE_DIGEST_SHA3_256:
887 if (!pakfire_digest_set(file->digests.sha3_256))
888 return NULL;
889
890 if (length)
891 *length = sizeof(file->digests.sha3_256);
892
893 return file->digests.sha3_256;
894
895 case PAKFIRE_DIGEST_BLAKE2B512:
896 if (!pakfire_digest_set(file->digests.blake2b512))
897 return NULL;
898
899 if (length)
900 *length = sizeof(file->digests.blake2b512);
901
902 return file->digests.blake2b512;
903
904 case PAKFIRE_DIGEST_BLAKE2S256:
905 if (!pakfire_digest_set(file->digests.blake2s256))
906 return NULL;
907
908 if (length)
909 *length = sizeof(file->digests.blake2s256);
910
911 return file->digests.blake2s256;
912
913 case PAKFIRE_DIGEST_SHA2_512:
914 if (!pakfire_digest_set(file->digests.sha2_512))
915 return NULL;
916
917 if (length)
918 *length = sizeof(file->digests.sha2_512);
919
920 return file->digests.sha2_512;
921
922 case PAKFIRE_DIGEST_SHA2_256:
923 if (!pakfire_digest_set(file->digests.sha2_256))
924 return NULL;
925
926 if (length)
927 *length = sizeof(file->digests.sha2_256);
928
929 return file->digests.sha2_256;
930
931 case PAKFIRE_DIGEST_UNDEFINED:
932 break;
933 }
934
935 return NULL;
936 }
937
938 PAKFIRE_EXPORT int pakfire_file_set_digest(struct pakfire_file* file,
939 const enum pakfire_digest_types type, const unsigned char* digest, const size_t length) {
940 if (!digest) {
941 errno = EINVAL;
942 return 1;
943 }
944
945 // Check buffer length
946 if (pakfire_digest_length(type) != length) {
947 ERROR(file->pakfire, "Digest has an incorrect length of %zu byte(s)\n", length);
948 errno = ENOMSG;
949 return 1;
950 }
951
952 // Store the digest
953 switch (type) {
954 case PAKFIRE_DIGEST_SHA3_512:
955 memcpy(file->digests.sha3_512, digest, sizeof(file->digests.sha3_512));
956 break;
957
958 case PAKFIRE_DIGEST_SHA3_256:
959 memcpy(file->digests.sha3_256, digest, sizeof(file->digests.sha3_256));
960 break;
961
962 case PAKFIRE_DIGEST_BLAKE2B512:
963 memcpy(file->digests.blake2b512, digest, sizeof(file->digests.blake2b512));
964 break;
965
966 case PAKFIRE_DIGEST_BLAKE2S256:
967 memcpy(file->digests.blake2s256, digest, sizeof(file->digests.blake2s256));
968 break;
969
970 case PAKFIRE_DIGEST_SHA2_512:
971 memcpy(file->digests.sha2_512, digest, sizeof(file->digests.sha2_512));
972 break;
973
974 case PAKFIRE_DIGEST_SHA2_256:
975 memcpy(file->digests.sha2_256, digest, sizeof(file->digests.sha2_256));
976 break;
977
978 case PAKFIRE_DIGEST_UNDEFINED:
979 errno = ENOTSUP;
980 return 1;
981 }
982
983 return 0;
984 }
985
986 static int pakfire_file_levels(struct pakfire_file* file) {
987 if (!*file->path)
988 return 0;
989
990 int levels = 0;
991
992 for (char* p = file->path; *p; p++) {
993 if (*p == '/')
994 levels++;
995 }
996
997 return levels;
998 }
999
1000 FILE* pakfire_file_open(struct pakfire_file* file) {
1001 FILE* f = fopen(file->abspath, "r");
1002 if (!f)
1003 ERROR(file->pakfire, "Could not open %s: %m\n", file->abspath);
1004
1005 return f;
1006 }
1007
1008 int pakfire_file_payload_matches(struct pakfire_file* file,
1009 const void* needle, const size_t length) {
1010 char buffer[1024 * 1024];
1011 FILE* f = NULL;
1012 void* p = NULL;
1013 int r;
1014
1015 // Only run for regular files
1016 if (!S_ISREG(file->st.st_mode))
1017 return 0;
1018
1019 // Skip empty files
1020 if (!file->st.st_size)
1021 return 0;
1022
1023 // Open the file
1024 f = pakfire_file_open(file);
1025 if (!f) {
1026 r = 1;
1027 goto ERROR;
1028 }
1029
1030 while (!feof(f)) {
1031 size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
1032
1033 // Raise any reading errors
1034 if (ferror(f)) {
1035 r = 1;
1036 goto ERROR;
1037 }
1038
1039 // Search for the needle
1040 p = memmem(buffer, bytes_read, needle, length);
1041 if (p) {
1042 r = 1;
1043 goto ERROR;
1044 }
1045 }
1046
1047 // No match
1048 r = 0;
1049
1050 ERROR:
1051 if (f)
1052 fclose(f);
1053
1054 return r;
1055 }
1056
1057 static int __pakfire_file_compute_digests(struct pakfire_file* file,
1058 struct pakfire_digests* digests, const int types) {
1059 FILE* f = NULL;
1060 int r = 1;
1061
1062 // Skip this for anything that isn't a regular file
1063 if (!S_ISREG(file->st.st_mode))
1064 return 0;
1065
1066 // Reset digests
1067 pakfire_digests_reset(digests, types);
1068
1069 // Open the file
1070 f = pakfire_file_open(file);
1071 if (!f)
1072 goto ERROR;
1073
1074 // Compute digests
1075 r = pakfire_digests_compute_from_file(file->pakfire, digests, types, f);
1076 if (r)
1077 goto ERROR;
1078
1079 ERROR:
1080 if (f)
1081 fclose(f);
1082
1083 return r;
1084 }
1085
1086 int pakfire_file_compute_digests(struct pakfire_file* file, const int types) {
1087 return __pakfire_file_compute_digests(file, &file->digests, types);
1088 }
1089
1090 int pakfire_file_remove(struct pakfire_file* file) {
1091 if (!*file->abspath) {
1092 errno = EINVAL;
1093 return 1;
1094 }
1095
1096 DEBUG(file->pakfire, "Removing %s...\n", file->path);
1097
1098 int r = remove(file->abspath);
1099 if (r) {
1100 switch (errno) {
1101 // Ignore when we could not remove directories
1102 case ENOTEMPTY:
1103 return 0;
1104
1105 // Ignore if the file didn't exist
1106 case ENOENT:
1107 return 0;
1108
1109 default:
1110 break;
1111 }
1112
1113 ERROR(file->pakfire, "Could not remove %s (%s): %m\n", file->path, file->abspath);
1114 }
1115
1116 return r;
1117 }
1118
1119 int pakfire_file_symlink_target_exists(struct pakfire_file* file) {
1120 // Fail if this got called for anything that isn't a symlink
1121 if (!S_ISLNK(file->st.st_mode)) {
1122 errno = EINVAL;
1123 return -1;
1124 }
1125
1126 return pakfire_path_exists(file->abspath);
1127 }
1128
1129 /*
1130 MIME Type
1131 */
1132
1133 int pakfire_file_detect_mimetype(struct pakfire_file* file) {
1134 // Only process regular files
1135 if (!S_ISREG(file->st.st_mode))
1136 return 0;
1137
1138 // Skip if MIME type is already set
1139 if (*file->mimetype)
1140 return 0;
1141
1142 // Fetch the magic cookie
1143 magic_t magic = pakfire_get_magic(file->pakfire);
1144 if (!magic)
1145 return 1;
1146
1147 // Check the file
1148 const char* mimetype = magic_file(magic, file->abspath);
1149 if (!mimetype) {
1150 ERROR(file->pakfire, "Could not classify %s: %s\n", file->path, magic_error(magic));
1151 return 1;
1152 }
1153
1154 DEBUG(file->pakfire, "Classified %s as %s\n", file->path, mimetype);
1155
1156 // Store the value
1157 return pakfire_file_set_mimetype(file, mimetype);
1158 }
1159
1160 PAKFIRE_EXPORT const char* pakfire_file_get_mimetype(struct pakfire_file* file) {
1161 // Return nothing on an empty mimetype
1162 if (!*file->mimetype)
1163 return NULL;
1164
1165 return file->mimetype;
1166 }
1167
1168 PAKFIRE_EXPORT int pakfire_file_set_mimetype(
1169 struct pakfire_file* file, const char* mimetype) {
1170 // Store the value
1171 return pakfire_string_set(file->mimetype, mimetype);
1172 }
1173
1174 /*
1175 Classification
1176 */
1177
1178 static int setup_libelf(struct pakfire* pakfire) {
1179 // Initialize libelf
1180 if (elf_version(EV_CURRENT) == EV_NONE) {
1181 ERROR(pakfire, "Could not initialize libelf: %s\n", elf_errmsg(-1));
1182
1183 return 1;
1184 }
1185
1186 return 0;
1187 }
1188
1189 static int pakfire_file_classify_mode(struct pakfire_file* file) {
1190 // Check for regular files
1191 if (S_ISREG(file->st.st_mode)) {
1192 file->class |= PAKFIRE_FILE_REGULAR;
1193
1194 // Does the file have executable permissions?
1195 if (file->st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
1196 file->class |= PAKFIRE_FILE_EXECUTABLE;
1197
1198 // Check for directories
1199 } else if (S_ISDIR(file->st.st_mode))
1200 file->class |= PAKFIRE_FILE_DIRECTORY;
1201
1202 // Check for symlinks
1203 else if (S_ISLNK(file->st.st_mode))
1204 file->class |= PAKFIRE_FILE_SYMLINK;
1205
1206 // Check for character devices
1207 else if (S_ISCHR(file->st.st_mode))
1208 file->class |= PAKFIRE_FILE_CHARACTER;
1209
1210 // Check for block devices
1211 else if (S_ISBLK(file->st.st_mode))
1212 file->class |= PAKFIRE_FILE_BLOCK;
1213
1214 // Check for FIFO pipes
1215 else if (S_ISFIFO(file->st.st_mode))
1216 file->class |= PAKFIRE_FILE_FIFO;
1217
1218 // Check for sockets
1219 else if (S_ISSOCK(file->st.st_mode))
1220 file->class |= PAKFIRE_FILE_SOCKET;
1221
1222 return 0;
1223 }
1224
1225 static const struct pattern {
1226 const char* pattern;
1227 int class;
1228 } patterns[] = {
1229 { "*.a", PAKFIRE_FILE_STATIC_LIBRARY },
1230 { "*.la", PAKFIRE_FILE_LIBTOOL_ARCHIVE },
1231 { "*.pm", PAKFIRE_FILE_PERL },
1232 { "*.pc", PAKFIRE_FILE_PKGCONFIG },
1233 { "/usr/lib/firmware/*", PAKFIRE_FILE_FIRMWARE },
1234 { "/usr/lib*/ld-*.so*", PAKFIRE_FILE_RUNTIME_LINKER },
1235 { NULL },
1236 };
1237
1238 static int pakfire_file_classify_pattern(struct pakfire_file* file) {
1239 for (const struct pattern* p = patterns; p->pattern; p++) {
1240 if (pakfire_file_matches(file, p->pattern)) {
1241 file->class |= p->class;
1242 break;
1243 }
1244 }
1245
1246 return 0;
1247 }
1248
1249 static const struct mimetype {
1250 const char* mimetype;
1251 int class;
1252 } mimetypes[] = {
1253 { "text/x-perl", PAKFIRE_FILE_PERL },
1254 { NULL, 0 },
1255 };
1256
1257 static int pakfire_file_classify_magic(struct pakfire_file* file) {
1258 int r;
1259
1260 // Detect the MIME type
1261 r = pakfire_file_detect_mimetype(file);
1262 if (r)
1263 return r;
1264
1265 // Fetch the MIME type
1266 const char* mimetype = pakfire_file_get_mimetype(file);
1267 if (!mimetype)
1268 return 1;
1269
1270 // Match the MIME type with a flag
1271 for (const struct mimetype* m = mimetypes; m->mimetype; m++) {
1272 if (strcmp(m->mimetype, mimetype) == 0) {
1273 file->class |= m->class;
1274 break;
1275 }
1276 }
1277
1278 return 0;
1279 }
1280
1281 static int pakfire_file_classify_elf(struct pakfire_file* file) {
1282 FILE* f = NULL;
1283 Elf* elf = NULL;
1284 int r;
1285
1286 // Don't run this if we already know that file is an ELF file
1287 if (file->class & PAKFIRE_FILE_ELF)
1288 return 0;
1289
1290 // Setup libelf
1291 r = setup_libelf(file->pakfire);
1292 if (r)
1293 return r;
1294
1295 // Open the file
1296 f = fopen(file->abspath, "r");
1297 if (!f) {
1298 ERROR(file->pakfire, "Could not open %s: %m\n", file->path);
1299 return 1;
1300 }
1301
1302 // Try to open the ELF file
1303 elf = elf_begin(fileno(f), ELF_C_READ, NULL);
1304 if (!elf) {
1305 // We fail silently here, because this file might be in a different format
1306 goto ERROR;
1307 }
1308
1309 switch (elf_kind(elf)) {
1310 // Mark this file as an ELF file
1311 case ELF_K_ELF:
1312 file->class |= PAKFIRE_FILE_ELF;
1313 break;
1314
1315 // Ignore everything else
1316 default:
1317 break;
1318 }
1319
1320 ERROR:
1321 if (elf)
1322 elf_end(elf);
1323 if (f)
1324 fclose(f);
1325
1326 return 0;
1327 }
1328
1329 int pakfire_file_classify(struct pakfire_file* file) {
1330 int r;
1331
1332 if (!file->class) {
1333 // First, check the mode so that we won't run magic on directories, symlinks, ...
1334 r = pakfire_file_classify_mode(file);
1335 if (r)
1336 goto ERROR;
1337
1338 // Only run this for regular files
1339 if (file->class & PAKFIRE_FILE_REGULAR) {
1340 // Then check for patterns
1341 r = pakfire_file_classify_pattern(file);
1342 if (r)
1343 goto ERROR;
1344
1345 // After that, we will use libmagic...
1346 r = pakfire_file_classify_magic(file);
1347 if (r)
1348 goto ERROR;
1349
1350 // Check if the file is an ELF file
1351 r = pakfire_file_classify_elf(file);
1352 if (r)
1353 goto ERROR;
1354 }
1355 }
1356
1357 return file->class;
1358
1359 ERROR:
1360 // Reset the class
1361 file->class = PAKFIRE_FILE_UNKNOWN;
1362
1363 return r;
1364 }
1365
1366 int pakfire_file_matches_class(struct pakfire_file* file, const int class) {
1367 return pakfire_file_classify(file) & class;
1368 }
1369
1370 /*
1371 This function tries to remove the file after it has been packaged.
1372
1373 It will try to delete any parent directories as well and ignore if directories
1374 cannot be deleted because they might contain other files
1375 */
1376 int pakfire_file_cleanup(struct pakfire_file* file) {
1377 char path[PATH_MAX];
1378
1379 // Try removing the file
1380 int r = pakfire_file_remove(file);
1381 if (r)
1382 return r;
1383
1384 // Create a working copy of abspath
1385 r = pakfire_string_set(path, file->abspath);
1386 if (r)
1387 return r;
1388
1389 // See how many levels this file has
1390 int levels = pakfire_file_levels(file);
1391
1392 // Walk all the way up and remove all parent directories if possible
1393 while (--levels) {
1394 dirname(path);
1395
1396 // Break if path is suddenly empty
1397 if (!*path)
1398 break;
1399
1400 DEBUG(file->pakfire, "Trying to remove parent directory %s\n", path);
1401
1402 r = rmdir(path);
1403
1404 // Break on any error
1405 if (r)
1406 break;
1407 }
1408
1409 return 0;
1410 }
1411
1412 static int pakfire_file_verify_mode(struct pakfire_file* file, const struct stat* st) {
1413 const mode_t type = pakfire_file_get_type(file);
1414
1415 // Did the type change?
1416 if (type != (st->st_mode & S_IFMT)) {
1417 file->verify_status |= PAKFIRE_FILE_TYPE_CHANGED;
1418
1419 DEBUG(file->pakfire, "%s: File Type changed\n", file->path);
1420 }
1421
1422 const mode_t perms = pakfire_file_get_perms(file);
1423
1424 // Check permissions
1425 if (perms != (st->st_mode & 0777)) {
1426 file->verify_status |= PAKFIRE_FILE_PERMISSIONS_CHANGED;
1427
1428 DEBUG(file->pakfire, "%s: Permissions changed\n", file->path);
1429 }
1430
1431 #if 0
1432 // XXX This does not check what it is supposed to check
1433
1434 // Check if device node changed
1435 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
1436 const dev_t dev = pakfire_file_get_dev(file);
1437
1438 if (dev != st->st_dev) {
1439 file->verify_status |= PAKFIRE_FILE_DEV_CHANGED;
1440
1441 DEBUG(file->pakfire, "%s: Device Node changed\n", file->path);
1442 }
1443 }
1444 #endif
1445
1446 return 0;
1447 }
1448
1449 static int pakfire_file_verify_size(struct pakfire_file* file, const struct stat* st) {
1450 // Nothing to do if size matches
1451 if (file->st.st_size == st->st_size)
1452 return 0;
1453
1454 // Size differs
1455 file->verify_status |= PAKFIRE_FILE_SIZE_CHANGED;
1456
1457 DEBUG(file->pakfire, "%s: Filesize differs (expected %zu, got %zu byte(s))\n",
1458 file->path, file->st.st_size, st->st_size);
1459
1460 return 0;
1461 }
1462
1463 static int pakfire_file_verify_ownership(struct pakfire_file* file, const struct stat* st) {
1464 // Fetch UID/GID
1465 #if 0
1466 const uid_t uid = pakfire_unmap_id(file->pakfire, st->st_uid);
1467 const gid_t gid = pakfire_unmap_id(file->pakfire, st->st_gid);
1468 #else
1469 const uid_t uid = st->st_uid;
1470 const gid_t gid = st->st_gid;
1471 #endif
1472
1473 // Fetch owner & group
1474 struct passwd* owner = pakfire_getpwnam(file->pakfire, file->uname);
1475 struct group* group = pakfire_getgrnam(file->pakfire, file->gname);
1476
1477 // Check if owner matches
1478 if (!owner || owner->pw_uid != uid) {
1479 file->verify_status |= PAKFIRE_FILE_OWNER_CHANGED;
1480
1481 DEBUG(file->pakfire, "%s: Owner differs\n", file->path);
1482 }
1483
1484 // Check if group matches
1485 if (!group || group->gr_gid != gid) {
1486 file->verify_status |= PAKFIRE_FILE_GROUP_CHANGED;
1487
1488 DEBUG(file->pakfire, "%s: Group differs\n", file->path);
1489 }
1490
1491 return 0;
1492 }
1493
1494 static int pakfire_file_verify_timestamps(struct pakfire_file* file, const struct stat* st) {
1495 // Check creation time
1496 if (file->st.st_ctime != st->st_ctime) {
1497 file->verify_status |= PAKFIRE_FILE_CTIME_CHANGED;
1498
1499 DEBUG(file->pakfire, "%s: Creation time changed\n", file->path);
1500 }
1501
1502 // Check modification time
1503 if (file->st.st_mtime != st->st_mtime) {
1504 file->verify_status |= PAKFIRE_FILE_MTIME_CHANGED;
1505
1506 DEBUG(file->pakfire, "%s: Modification time changed\n", file->path);
1507 }
1508
1509 return 0;
1510 }
1511
1512 static int pakfire_file_verify_payload(struct pakfire_file* file, const struct stat* st) {
1513 int r;
1514
1515 struct pakfire_digests computed_digests;
1516 int digest_types = PAKFIRE_DIGEST_UNDEFINED;
1517
1518 // Nothing to do for anything that isn't a regular file
1519 if (!S_ISREG(st->st_mode))
1520 return 0;
1521
1522 // Fast-path if size changed. The payload will have changed, too
1523 if (file->verify_status & PAKFIRE_FILE_SIZE_CHANGED) {
1524 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1525 return 0;
1526 }
1527
1528 // Check if this file has any digests at all
1529 digest_types = pakfire_digest_has_any(&file->digests);
1530
1531 if (!digest_types) {
1532 ERROR(file->pakfire, "%s: No digests available\n", file->path);
1533 return 0;
1534 }
1535
1536 // Compute digests
1537 r = __pakfire_file_compute_digests(file, &computed_digests, digest_types);
1538 if (r)
1539 goto ERROR;
1540
1541 // Compare digests
1542 r = pakfire_digests_compare(file->pakfire, &file->digests, &computed_digests, digest_types);
1543 if (r) {
1544 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1545
1546 DEBUG(file->pakfire, "%s: Digest(s) do not match\n", file->path);
1547 }
1548
1549 ERROR:
1550 return r;
1551 }
1552
1553 /*
1554 Verify the file - i.e. does the metadata match what is on disk?
1555 */
1556 int pakfire_file_verify(struct pakfire_file* file, int* status) {
1557 struct stat st;
1558 int r;
1559
1560 DEBUG(file->pakfire, "Verifying %s...\n", file->path);
1561
1562 // stat() the file
1563 r = lstat(file->abspath, &st);
1564 if (r) {
1565 // File does not exist
1566 if (errno == ENOENT) {
1567 file->verify_status |= PAKFIRE_FILE_NOENT;
1568 return 1;
1569 }
1570
1571 // Raise any other errors from stat()
1572 return r;
1573 }
1574
1575 // Verify mode
1576 r = pakfire_file_verify_mode(file, &st);
1577 if (r)
1578 return r;
1579
1580 // Verify size
1581 r = pakfire_file_verify_size(file, &st);
1582 if (r)
1583 return r;
1584
1585 // Verify ownership
1586 r = pakfire_file_verify_ownership(file, &st);
1587 if (r)
1588 return r;
1589
1590 // Verify timestamps
1591 r = pakfire_file_verify_timestamps(file, &st);
1592 if (r)
1593 return r;
1594
1595 // Verify payload
1596 r = pakfire_file_verify_payload(file, &st);
1597 if (r)
1598 return r;
1599
1600 return 0;
1601 }
1602
1603 PAKFIRE_EXPORT int pakfire_file_matches(struct pakfire_file* file, const char* pattern) {
1604 int r;
1605
1606 // Don't match on no pattern
1607 if (!pattern)
1608 return 0;
1609
1610 // Check if the pattern matches
1611 r = fnmatch(pattern, file->path, 0);
1612 switch (r) {
1613 // Match
1614 case 0:
1615 return 1;
1616
1617 // No Match
1618 case FNM_NOMATCH:
1619 return 0;
1620
1621 default:
1622 return -1;
1623 }
1624 }
1625
1626 /*
1627 ELF Stuff
1628 */
1629
1630 static int pakfire_file_open_elf(struct pakfire_file* file,
1631 int (*callback)(struct pakfire_file* file, Elf* elf, void* data), void* data) {
1632 FILE* f = NULL;
1633 Elf* elf = NULL;
1634 int r;
1635
1636 // Don't run this for non-ELF files
1637 if (!pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
1638 errno = EINVAL;
1639 return 1;
1640 }
1641
1642 // Setup libelf
1643 r = setup_libelf(file->pakfire);
1644 if (r)
1645 return r;
1646
1647 // Open the file
1648 f = fopen(file->abspath, "r");
1649 if (!f) {
1650 ERROR(file->pakfire, "Could not open %s: %m\n", file->abspath);
1651 return 1;
1652 }
1653
1654 // Parse the ELF header
1655 elf = elf_begin(fileno(f), ELF_C_READ, NULL);
1656 if (!elf) {
1657 ERROR(file->pakfire, "Could not open ELF file: %s\n", elf_errmsg(-1));
1658 r = 1;
1659 goto ERROR;
1660 }
1661
1662 // Check if this is an ELF file
1663 switch (elf_kind(elf)) {
1664 case ELF_K_ELF:
1665 break;
1666
1667 default:
1668 ERROR(file->pakfire, "%s is not an ELF object\n", file->path);
1669 r = 1;
1670 goto ERROR;
1671 }
1672
1673 // Call the callback
1674 r = callback(file, elf, data);
1675
1676 ERROR:
1677 if (elf)
1678 elf_end(elf);
1679 if (f)
1680 fclose(f);
1681
1682 return r;
1683 }
1684
1685 static int pakfire_file_get_elf_section(struct pakfire_file* file,
1686 Elf* elf, const Elf64_Word type, Elf_Scn** section, GElf_Shdr* header, Elf_Data** data) {
1687 Elf_Scn* s = NULL;
1688
1689 GElf_Shdr shdr;
1690
1691 // Walk through all sections
1692 for (;;) {
1693 s = elf_nextscn(elf, s);
1694 if (!s)
1695 break;
1696
1697 // Fetch the section header
1698 gelf_getshdr(s, &shdr);
1699
1700 // Return any matching sections
1701 if (shdr.sh_type == type) {
1702 *section = s;
1703
1704 // Send header if requested
1705 if (header)
1706 gelf_getshdr(s, header);
1707
1708 // Send data if requested
1709 if (data)
1710 *data = elf_getdata(s, NULL);
1711
1712 return 0;
1713 }
1714 }
1715
1716 // No section found
1717 return 1;
1718 }
1719
1720 static int __pakfire_file_get_elf_type(struct pakfire_file* file, Elf* elf, void* data) {
1721 int* type = (int*)data;
1722 GElf_Ehdr ehdr;
1723
1724 // Fetch the ELF header
1725 if (!gelf_getehdr(elf, &ehdr)) {
1726 ERROR(file->pakfire, "Could not parse ELF header: %s\n", elf_errmsg(-1));
1727 return 1;
1728 }
1729
1730 // Store the type
1731 *type = ehdr.e_type;
1732
1733 return 0;
1734 }
1735
1736 static int pakfire_file_get_elf_type(struct pakfire_file* file) {
1737 int type = ET_NONE;
1738 int r;
1739
1740 r = pakfire_file_open_elf(file, __pakfire_file_get_elf_type, &type);
1741 if (r)
1742 return -1;
1743
1744 return type;
1745 }
1746
1747 static int pakfire_file_elf_dyn_walk(struct pakfire_file* file, Elf* elf,
1748 int (*callback)(struct pakfire_file* file,
1749 Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data),
1750 void* data) {
1751 Elf_Scn* dynamic = NULL;
1752 GElf_Shdr shdr;
1753 Elf_Data* elf_data = NULL;
1754 GElf_Dyn dyn;
1755 int r;
1756
1757 // Find the dynamic linking information
1758 r = pakfire_file_get_elf_section(file, elf, SHT_DYNAMIC, &dynamic, &shdr, &elf_data);
1759 if (r) {
1760 ERROR(file->pakfire, "%s does not have a dynamic section\n", file->path);
1761 return 1;
1762 }
1763
1764 // Walk through all entries...
1765 for (unsigned int i = 0; ; i++) {
1766 // Fetch the next entry
1767 if (!gelf_getdyn(elf_data, i, &dyn))
1768 break;
1769
1770 // Call the callback
1771 r = callback(file, elf, &shdr, &dyn, data);
1772 if (r)
1773 return r;
1774 }
1775
1776 return 0;
1777 }
1778
1779 static int __pakfire_file_check_debuginfo(struct pakfire_file* file, Elf* elf, void* data) {
1780 Elf_Scn* symtab = NULL;
1781 int r;
1782
1783 // Fetch the symbol table
1784 r = pakfire_file_get_elf_section(file, elf, SHT_SYMTAB, &symtab, NULL, NULL);
1785
1786 // Not found
1787 if (r) {
1788 DEBUG(file->pakfire, "%s has no debug sections\n", file->path);
1789
1790 // Store the result
1791 file->issues |= PAKFIRE_FILE_MISSING_DEBUGINFO;
1792 }
1793
1794 return 0;
1795 }
1796
1797 static int pakfire_file_check_debuginfo(struct pakfire_file* file) {
1798 switch (pakfire_file_get_elf_type(file)) {
1799 // Do not check Relocatable Objects
1800 case ET_REL:
1801 return 0;
1802
1803 // Check everything else
1804 default:
1805 break;
1806 }
1807
1808 return pakfire_file_open_elf(file, __pakfire_file_check_debuginfo, NULL);
1809 }
1810
1811 static int __pakfire_file_check_ssp(
1812 struct pakfire_file* file, Elf* elf, void* data) {
1813 Elf_Scn* symtab = NULL;
1814 GElf_Shdr shdr;
1815 Elf_Data* elf_data = NULL;
1816 GElf_Sym symbol;
1817 const char* name = NULL;
1818 int r;
1819
1820 // Fetch the symbol table
1821 r = pakfire_file_get_elf_section(file, elf, SHT_SYMTAB, &symtab, &shdr, &elf_data);
1822 if (r) {
1823 ERROR(file->pakfire, "%s has no symbol table\n", file->path);
1824 return 1;
1825 }
1826
1827 // Count any global functions
1828 size_t counter = 0;
1829
1830 // Walk through all symbols
1831 for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
1832 gelf_getsym(elf_data, i, &symbol);
1833
1834 // Fetch the symbol name
1835 name = elf_strptr(elf, shdr.sh_link, symbol.st_name);
1836
1837 // Skip empty section names
1838 if (!name || !*name)
1839 continue;
1840
1841 // Exit if there is a symbol called "__stack_chk_fail"
1842 if (pakfire_string_startswith(name, "__stack_chk_fail"))
1843 return 0;
1844
1845 // Count any global functions
1846 if ((ELF64_ST_BIND(symbol.st_info) == STB_GLOBAL) &&
1847 (ELF64_ST_TYPE(symbol.st_info) == STT_FUNC))
1848 counter++;
1849 }
1850
1851 // We do not perform the check for libraries that do not contain any functions.
1852 // Some packages use shared libraries to provide data.
1853 if (!counter) {
1854 DEBUG(file->pakfire, "%s: File has no functions. Skipping SSP check.\n", file->path);
1855 return 0;
1856 }
1857
1858 // The file does not seem to have SSP enabled
1859 file->issues |= PAKFIRE_FILE_MISSING_SSP;
1860
1861 return 0;
1862 }
1863
1864 static int pakfire_file_check_ssp(struct pakfire_file* file) {
1865 // Do not perform this check for runtime linkers
1866 if (pakfire_file_matches_class(file, PAKFIRE_FILE_RUNTIME_LINKER))
1867 return 0;
1868
1869 // We cannot perform this check if we don't have debuginfo
1870 if (file->issues & PAKFIRE_FILE_MISSING_DEBUGINFO)
1871 return 0;
1872
1873 return pakfire_file_open_elf(file, __pakfire_file_check_ssp, NULL);
1874 }
1875
1876 static int pakfire_file_check_pie(struct pakfire_file* file) {
1877 switch (pakfire_file_get_elf_type(file)) {
1878 // Shared Object files are good
1879 case ET_DYN:
1880 break;
1881
1882 // Everything else is bad
1883 default:
1884 file->issues |= PAKFIRE_FILE_MISSING_PIE;
1885 break;
1886 }
1887
1888 return 0;
1889 }
1890
1891 static int __pakfire_file_check_execstack(
1892 struct pakfire_file* file, Elf* elf, void* data) {
1893 GElf_Phdr phdr;
1894 int r;
1895
1896 size_t phnum = 0;
1897
1898 // Fetch the total numbers of program headers
1899 r = elf_getphdrnum(elf, &phnum);
1900 if (r) {
1901 ERROR(file->pakfire, "Could not fetch number of program headers: %s\n",
1902 elf_errmsg(-1));
1903 return 1;
1904 }
1905
1906 // Walk through all program headers
1907 for (unsigned int i = 0; i < phnum; i++) {
1908 if (!gelf_getphdr(elf, i, &phdr)) {
1909 ERROR(file->pakfire, "Could not parse program header: %s\n", elf_errmsg(-1));
1910 return 1;
1911 }
1912
1913 switch (phdr.p_type) {
1914 case PT_GNU_STACK:
1915 DEBUG(file->pakfire,
1916 "%s: GNU_STACK flags: %c%c%c\n",
1917 file->path,
1918 (phdr.p_flags & PF_R) ? 'R' : '-',
1919 (phdr.p_flags & PF_W) ? 'W' : '-',
1920 (phdr.p_flags & PF_X) ? 'X' : '-'
1921 );
1922
1923 // The stack cannot be writable and executable
1924 if ((phdr.p_flags & PF_W) && (phdr.p_flags & PF_X))
1925 file->issues |= PAKFIRE_FILE_EXECSTACK;
1926
1927 // Done
1928 return 0;
1929
1930 default:
1931 break;
1932 }
1933 }
1934
1935 return 0;
1936 }
1937
1938 static int pakfire_file_check_execstack(struct pakfire_file* file) {
1939 return pakfire_file_open_elf(file, __pakfire_file_check_execstack, NULL);
1940 }
1941
1942 static int __pakfire_file_check_partially_relro(
1943 struct pakfire_file* file, Elf* elf, void* data) {
1944 GElf_Phdr phdr;
1945 int r;
1946
1947 size_t phnum = 0;
1948
1949 // Fetch the total numbers of program headers
1950 r = elf_getphdrnum(elf, &phnum);
1951 if (r) {
1952 ERROR(file->pakfire, "Could not fetch number of program headers: %s\n",
1953 elf_errmsg(-1));
1954 return 1;
1955 }
1956
1957 // Walk through all program headers
1958 for (unsigned int i = 0; i < phnum; i++) {
1959 if (!gelf_getphdr(elf, i, &phdr)) {
1960 ERROR(file->pakfire, "Could not parse program header: %s\n", elf_errmsg(-1));
1961 return 1;
1962 }
1963
1964 switch (phdr.p_type) {
1965 case PT_GNU_RELRO:
1966 return 0;
1967
1968 default:
1969 break;
1970 }
1971 }
1972
1973 // This file does not seem to have PT_GNU_RELRO set
1974 file->issues |= PAKFIRE_FILE_NO_RELRO;
1975
1976 return 0;
1977 }
1978
1979 static int pakfire_file_check_relro(struct pakfire_file* file) {
1980 return pakfire_file_open_elf(file, __pakfire_file_check_relro, NULL);
1981 }
1982
1983 /*
1984 RPATH/RUNPATH
1985 */
1986 static int __pakfire_file_process_runpath(struct pakfire_file* file,
1987 Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) {
1988 const char* value = NULL;
1989 const char* runpath = NULL;
1990 char buffer[PATH_MAX];
1991 char* p = NULL;
1992 int r;
1993
1994 switch (dyn->d_tag) {
1995 case DT_RUNPATH:
1996 case DT_RPATH:
1997 // Fetch the value
1998 value = elf_strptr(elf, shdr->sh_link, dyn->d_un.d_val);
1999 if (!value)
2000 return 1;
2001
2002 DEBUG(file->pakfire, "%s has a RUNPATH: %s\n", file->path, value);
2003
2004 // Copy the value into a buffer we can modify
2005 r = pakfire_string_set(buffer, value);
2006 if (r)
2007 return r;
2008
2009 // Split the value by :
2010 runpath = strtok_r(buffer, ":", &p);
2011
2012 // Iterate over all elements
2013 while (runpath) {
2014 DEBUG(file->pakfire, "Checking RUNPATH %s\n", runpath);
2015
2016 // We do not allow any relative RUNPATHs
2017 if (pakfire_path_match(runpath, "**/../**")) {
2018 file->issues |= PAKFIRE_FILE_HAS_RUNPATH;
2019 break;
2020 }
2021
2022 /*
2023 We allow some RUNPATHs where some software is loading some
2024 modules as shared objects from a private directory in /usr/lib64.
2025 */
2026 if (!pakfire_path_match(runpath, "/usr/lib64")
2027 && !pakfire_path_match(runpath, "/usr/lib64/**")) {
2028 file->issues |= PAKFIRE_FILE_HAS_RUNPATH;
2029 break;
2030 }
2031
2032 // Move on to the next RUNPATH
2033 runpath = strtok_r(NULL, ":", &p);
2034 }
2035
2036 default:
2037 break;
2038 }
2039
2040 return 0;
2041 }
2042
2043 static int __pakfire_file_check_runpath(struct pakfire_file* file, Elf* elf, void* data) {
2044 return pakfire_file_elf_dyn_walk(file, elf, __pakfire_file_process_runpath, data);
2045 }
2046
2047 static int pakfire_file_check_runpath(struct pakfire_file* file) {
2048 return pakfire_file_open_elf(file, __pakfire_file_check_runpath, NULL);
2049 }
2050
2051 int pakfire_file_check(struct pakfire_file* file, int* issues) {
2052 int r;
2053
2054 // Return previous result if this has been run before
2055 if (!file->check_done) {
2056 // Perform FHS check
2057 r = pakfire_fhs_check_file(file->pakfire, file);
2058 if (r)
2059 file->issues |= PAKFIRE_FILE_FHS_ERROR;
2060
2061 // Do not perform the following checks on firmware
2062 if (pakfire_file_matches_class(file, PAKFIRE_FILE_FIRMWARE))
2063 goto DONE;
2064
2065 // Run these checks only for ELF files
2066 if (pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
2067 switch (pakfire_file_get_elf_type(file)) {
2068 // Do not check Relocatable Objects
2069 case ET_REL:
2070 goto DONE;
2071
2072 // Check everything else
2073 default:
2074 break;
2075 }
2076
2077 // Check if the file has debug info
2078 r = pakfire_file_check_debuginfo(file);
2079 if (r)
2080 return r;
2081
2082 // Check for SSP
2083 r = pakfire_file_check_ssp(file);
2084 if (r)
2085 return r;
2086
2087 // Check for PIE
2088 r = pakfire_file_check_pie(file);
2089 if (r)
2090 return r;
2091
2092 // Check for executable stacks
2093 r = pakfire_file_check_execstack(file);
2094 if (r)
2095 return r;
2096
2097 // Check for RELRO
2098 r = pakfire_file_check_relro(file);
2099 if (r)
2100 return r;
2101
2102 // Check for RUNPATH
2103 r = pakfire_file_check_runpath(file);
2104 if (r)
2105 return r;
2106 }
2107
2108 DONE:
2109 // All checks done
2110 file->check_done = 1;
2111 }
2112
2113 // Return any issues
2114 if (issues)
2115 *issues = file->issues;
2116
2117 return 0;
2118 }