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