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