]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/file.c
file: Remove the legacy logger
[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
375a06e3 21#include <ctype.h>
5e9463ec 22#include <errno.h>
c064d9ec 23#include <fnmatch.h>
3fca5032 24#include <libgen.h>
293881bc 25#include <limits.h>
c98132d1 26#include <linux/limits.h>
221cc3ce
MT
27#include <stdlib.h>
28#include <string.h>
c2fe443d 29#include <sys/capability.h>
a09b95e0 30#include <sys/stat.h>
221cc3ce 31#include <sys/types.h>
221cc3ce
MT
32#include <time.h>
33
9a6e3e2d
MT
34#include <archive_entry.h>
35
df71dc60
MT
36#include <gelf.h>
37
8780f2ec 38#include <pakfire/ctx.h>
221cc3ce 39#include <pakfire/constants.h>
c52e0148 40#include <pakfire/digest.h>
6d9dd7bc 41#include <pakfire/fhs.h>
221cc3ce 42#include <pakfire/file.h>
3fca5032 43#include <pakfire/logging.h>
883b3be9 44#include <pakfire/pakfire.h>
9f953e68 45#include <pakfire/private.h>
d973a13d 46#include <pakfire/string.h>
221cc3ce
MT
47#include <pakfire/util.h>
48
c0b051bb
MT
49enum pakfire_file_verification_status {
50 PAKFIRE_FILE_NOENT = (1 << 0),
9e09e361
MT
51 PAKFIRE_FILE_TYPE_CHANGED = (1 << 1),
52 PAKFIRE_FILE_PERMISSIONS_CHANGED = (1 << 2),
53 PAKFIRE_FILE_DEV_CHANGED = (1 << 3),
54 PAKFIRE_FILE_SIZE_CHANGED = (1 << 4),
55 PAKFIRE_FILE_OWNER_CHANGED = (1 << 5),
56 PAKFIRE_FILE_GROUP_CHANGED = (1 << 6),
0eeac4a5
MT
57 PAKFIRE_FILE_CTIME_CHANGED = (1 << 7),
58 PAKFIRE_FILE_MTIME_CHANGED = (1 << 8),
76011205 59 PAKFIRE_FILE_PAYLOAD_CHANGED = (1 << 9),
c0b051bb
MT
60};
61
5803b5f6 62struct pakfire_file {
8780f2ec 63 struct pakfire_ctx* ctx;
ac4c607b 64 struct pakfire* pakfire;
5e9463ec 65 int nrefs;
221cc3ce 66
37c5d873
MT
67 // Use the libarchive entry to store common attributes
68 struct archive_entry* entry;
d03fa9a3 69
c2fe443d
MT
70 // Capabilities
71 cap_t caps;
72
2f3563b9
MT
73 // Flags
74 int flags;
75
65131b30 76 // Digests
c52e0148 77 struct pakfire_digests digests;
5e9463ec 78
210aabe9
MT
79 // MIME Type
80 char mimetype[NAME_MAX];
81
71f6f465
MT
82 // Class
83 int class;
84
c0b051bb
MT
85 // Verification Status
86 int verify_status;
87
28886e21
MT
88 // File Issues
89 int issues;
90 int check_done:1;
f7f44921 91
c2fe443d 92 #warning TODO data
5e9463ec
MT
93 //int is_datafile;
94};
95
c2fe443d
MT
96/*
97 Capabilities
98*/
99static int pakfire_file_read_fcaps(struct pakfire_file* file,
100 const struct vfs_cap_data* cap_data, size_t length) {
101 int max = 0;
102 int r;
103
104 uint32_t magic_etc = le32toh(cap_data->magic_etc);
105
106 // Which version are we dealing with?
107 switch (magic_etc & VFS_CAP_REVISION_MASK) {
108 case VFS_CAP_REVISION_1:
109 length -= XATTR_CAPS_SZ_1;
110 max = VFS_CAP_U32_1;
111 break;
112
113 case VFS_CAP_REVISION_2:
114 length -= XATTR_CAPS_SZ_2;
115 max = VFS_CAP_U32_2;
116 break;
117
118 case VFS_CAP_REVISION_3:
119 length -= XATTR_CAPS_SZ_3;
120 max = VFS_CAP_U32_3;
121 break;
122
123 // Unknown version
124 default:
125 errno = EINVAL;
126 return 1;
127 }
128
129 // Check if we have received the correct data
130 if (length) {
131 errno = EINVAL;
132 return 1;
133 }
134
135 // Allocate capabilities
136 file->caps = cap_init();
137 if (!file->caps) {
c3e5b4e6 138 CTX_ERROR(file->ctx, "Could not allocate capabilities: %m\n");
c2fe443d
MT
139 r = 1;
140 goto ERROR;
141 }
142
143 int mask;
144 int index;
145
146 int cap_permitted;
147 int cap_inheritable;
148 int cap_effective = 0;
149
150 for (unsigned int cap = 0; cap_valid(cap); cap++) {
151 // Find the index where to find this cap
152 index = CAP_TO_INDEX(cap);
153 mask = CAP_TO_MASK(cap);
154
155 // End if we have reached the end of the data
156 if (index > max)
157 break;
158
159 // Check for permitted/inheritable flag set
160 cap_permitted = le32toh(cap_data->data[index].permitted) & mask;
161 cap_inheritable = le32toh(cap_data->data[index].inheritable) & mask;
162
163 // Check for effective
164 if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
165 cap_effective = cap_permitted | cap_inheritable;
166
167 cap_value_t caps[] = { cap };
168
169 if (cap_permitted) {
170 r = cap_set_flag(file->caps, CAP_PERMITTED, 1, caps, CAP_SET);
171 if (r) {
c3e5b4e6 172 CTX_ERROR(file->ctx, "Could not set capability %u: %m\n", cap);
c2fe443d
MT
173 goto ERROR;
174 }
175 }
176
177 if (cap_inheritable) {
178 r = cap_set_flag(file->caps, CAP_INHERITABLE, 1, caps, CAP_SET);
179 if (r) {
c3e5b4e6 180 CTX_ERROR(file->ctx, "Could not set capability %u: %m\n", cap);
c2fe443d
MT
181 goto ERROR;
182 }
183 }
184
185 if (cap_effective) {
186 r = cap_set_flag(file->caps, CAP_EFFECTIVE, 1, caps, CAP_SET);
187 if (r) {
c3e5b4e6 188 CTX_ERROR(file->ctx, "Could not set capability %u: %m\n", cap);
c2fe443d
MT
189 goto ERROR;
190 }
191 }
192 }
193
194#ifdef ENABLE_DEBUG
195 char* text = cap_to_text(file->caps, NULL);
196 if (text) {
c3e5b4e6 197 CTX_DEBUG(file->ctx, "%s: Capabilities %s\n", pakfire_file_get_path(file), text);
c2fe443d
MT
198 cap_free(text);
199 }
200#endif
201
202 return 0;
203
204ERROR:
205 if (file->caps) {
206 cap_free(file->caps);
207 file->caps = NULL;
208 }
209
210 return r;
211}
212
729568d0
MT
213int pakfire_file_write_fcaps(struct pakfire_file* file, struct vfs_cap_data* cap_data) {
214 cap_flag_value_t cap_permitted;
215 cap_flag_value_t cap_inheritable;
216 cap_flag_value_t cap_effective;
217 int r;
218
219 // This should not be called when we have no caps
220 if (!file->caps) {
221 errno = EINVAL;
222 return 1;
223 }
224
225 uint32_t magic = VFS_CAP_REVISION_2;
226
227 for (unsigned int cap = 0; cap_valid(cap); cap++) {
228 // Find the index where to find this cap
229 int index = CAP_TO_INDEX(cap);
230 int mask = CAP_TO_MASK(cap);
231
232 // Fetch CAP_PERMITTED
233 r = cap_get_flag(file->caps, cap, CAP_PERMITTED, &cap_permitted);
234 if (r) {
c3e5b4e6 235 CTX_ERROR(file->ctx, "Could not fetch capability %u: %m\n", cap);
729568d0
MT
236 goto ERROR;
237 }
238
239 // Fetch CAP_INHERITABLE
240 r = cap_get_flag(file->caps, cap, CAP_INHERITABLE, &cap_inheritable);
241 if (r) {
c3e5b4e6 242 CTX_ERROR(file->ctx, "Could not fetch capability %u: %m\n", cap);
729568d0
MT
243 goto ERROR;
244 }
245
246 // Fetch CAP_EFFECTIVE
247 r = cap_get_flag(file->caps, cap, CAP_EFFECTIVE, &cap_effective);
248 if (r) {
c3e5b4e6 249 CTX_ERROR(file->ctx, "Could not fetch capability %u: %m\n", cap);
729568d0
MT
250 goto ERROR;
251 }
252
253 // Store CAP_PERMITTED
254 if (cap_permitted)
255 cap_data->data[index].permitted |= htole32(mask);
256
257 // Store CAP_INHERITED
258 if (cap_inheritable)
259 cap_data->data[index].inheritable |= htole32(mask);
260
261 // Set EFFECTIVE flag if CAP_EFFECTIVE is set
262 if (cap_effective)
263 magic |= VFS_CAP_FLAGS_EFFECTIVE;
264 }
265
266 // Store the magic value
267 cap_data->magic_etc = htole32(magic);
268
269ERROR:
270 return r;
271}
272
eb5daf3f 273static int pakfire_file_from_archive_entry(struct pakfire_file* file, struct archive_entry* entry) {
210aabe9 274 char* buffer = NULL;
eb5daf3f
MT
275 const char* attr = NULL;
276 const void* value = NULL;
277 size_t size = 0;
278 int r = 0;
279
37c5d873
MT
280 // Remove the existing entry
281 if (file->entry)
282 archive_entry_free(file->entry);
eb5daf3f 283
37c5d873
MT
284 // Clone the given entry
285 file->entry = archive_entry_clone(entry);
286 if (!file->entry)
287 return -ENOMEM;
eb5daf3f 288
210aabe9 289 // Reset iterating over extended attributes
37c5d873 290 archive_entry_xattr_reset(file->entry);
471ccfc2 291
eb5daf3f
MT
292 // Read any extended attributes
293 while (archive_entry_xattr_next(entry, &attr, &value, &size) == ARCHIVE_OK) {
2f3563b9
MT
294 // Config Files
295 if (strcmp(attr, "PAKFIRE.config") == 0) {
296 r = pakfire_file_set_flags(file, PAKFIRE_FILE_CONFIG);
297 if (r)
298 goto ERROR;
299
210aabe9 300 // MIME type
2f3563b9 301 } else if (strcmp(attr, "PAKFIRE.mimetype") == 0) {
210aabe9
MT
302 // Copy the value into a NULL-terminated buffer
303 r = asprintf(&buffer, "%.*s", (int)size, (const char*)value);
304 if (r < 0)
305 goto ERROR;
306
307 // Assign the value
308 r = pakfire_file_set_mimetype(file, buffer);
309 if (r)
310 goto ERROR;
311
d98740de 312 // Digest: SHA-3-512
210aabe9 313 } else if (strcmp(attr, "PAKFIRE.digests.sha3_512") == 0) {
d98740de
MT
314 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA3_512, value, size);
315 if (r)
316 goto ERROR;
317
318 // Digest: SHA-3-256
319 } else if (strcmp(attr, "PAKFIRE.digests.sha3_256") == 0) {
320 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA3_256, value, size);
321 if (r)
322 goto ERROR;
323
f1e6c5df 324 // Digest: BLAKE2b512
d98740de 325 } else if (strcmp(attr, "PAKFIRE.digests.blake2b512") == 0) {
f1e6c5df
MT
326 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_BLAKE2B512, value, size);
327 if (r)
328 goto ERROR;
329
2822561a 330 // Digest: BLAKE2s256
f1e6c5df
MT
331 } else if (strcmp(attr, "PAKFIRE.digests.blake2s256") == 0) {
332 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_BLAKE2S256, value, size);
333 if (r)
334 goto ERROR;
335
4500ed0a
MT
336 // Digest: SHA-2-512
337 } else if (strcmp(attr, "PAKFIRE.digests.sha2_512") == 0) {
338 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA2_512, value, size);
eb5daf3f
MT
339 if (r)
340 goto ERROR;
341
4500ed0a
MT
342 // Digest: SHA-2-256
343 } else if (strcmp(attr, "PAKFIRE.digests.sha2_256") == 0) {
344 r = pakfire_file_set_digest(file, PAKFIRE_DIGEST_SHA2_256, value, size);
eb5daf3f
MT
345 if (r)
346 goto ERROR;
347
c2fe443d
MT
348 // Capabilities
349 } else if (strcmp(attr, "security.capability") == 0) {
350 r = pakfire_file_read_fcaps(file, value, size);
351 if (r)
352 goto ERROR;
353
eb5daf3f 354 } else {
c3e5b4e6 355 CTX_DEBUG(file->ctx, "Received an unknown extended attribute: %s\n", attr);
eb5daf3f
MT
356 }
357 }
358
359ERROR:
210aabe9
MT
360 if (buffer)
361 free(buffer);
362
eb5daf3f
MT
363 return r;
364}
365
ac4c607b 366PAKFIRE_EXPORT int pakfire_file_create(struct pakfire_file** file, struct pakfire* pakfire) {
5803b5f6 367 struct pakfire_file* f = calloc(1, sizeof(*f));
5e9463ec 368 if (!f)
7403779a 369 return 1;
5e9463ec 370
8780f2ec
MT
371 // Store a reference to the context
372 f->ctx = pakfire_ctx(pakfire);
373
e8d3b985 374 // Store reference to Pakfire
883b3be9 375 f->pakfire = pakfire_ref(pakfire);
e8d3b985
MT
376
377 // Initialize reference counter
5e9463ec
MT
378 f->nrefs = 1;
379
37c5d873
MT
380 // Create a new archive entry
381 f->entry = archive_entry_new();
382 if (!f->entry)
383 return -ENOMEM;
384
5e9463ec
MT
385 *file = f;
386 return 0;
d03fa9a3
MT
387}
388
37c5d873
MT
389int pakfire_file_read(struct pakfire_file* file, struct archive* reader, const char* path) {
390 int r;
61b0856b 391
37c5d873
MT
392 // Check inputs
393 if (!reader || !path)
394 return -EINVAL;
61b0856b 395
37c5d873
MT
396 // Set abspath
397 r = pakfire_file_set_abspath(file, path);
398 if (r)
399 return r;
61b0856b 400
37c5d873
MT
401 // Read everything
402 r = archive_read_disk_entry_from_file(reader, file->entry, -1, NULL);
61b0856b 403 if (r) {
c3e5b4e6 404 CTX_ERROR(file->ctx, "Could not read %s: %s\n", path, archive_error_string(reader));
37c5d873 405 return 1;
61b0856b
MT
406 }
407
37c5d873 408 return 0;
61b0856b
MT
409}
410
ac4c607b 411int pakfire_file_create_from_archive_entry(struct pakfire_file** file, struct pakfire* pakfire,
d0985e31
MT
412 struct archive_entry* entry) {
413 int r = pakfire_file_create(file, pakfire);
414 if (r)
415 return r;
416
417 // Copy archive entry
eb5daf3f 418 r = pakfire_file_from_archive_entry(*file, entry);
d0985e31
MT
419 if (r)
420 goto ERROR;
421
422 return 0;
423
424ERROR:
425 pakfire_file_unref(*file);
426 *file = NULL;
427
428 return r;
429}
430
49a39ad9 431struct archive_entry* pakfire_file_archive_entry(struct pakfire_file* file, int digest_types) {
37c5d873 432 struct archive_entry* entry = NULL;
729568d0 433 struct vfs_cap_data cap_data = {};
d4b2ef2d 434 int r;
d7b476ef 435
37c5d873
MT
436 // Clone the entry
437 entry = archive_entry_clone(file->entry);
438 if (!entry)
a03246e0 439 goto ERROR;
5acd852e 440
2f3563b9
MT
441 // Flags
442 if (pakfire_file_has_flag(file, PAKFIRE_FILE_CONFIG)) {
443 archive_entry_xattr_add_entry(entry,
444 "PAKFIRE.config", "1", strlen("1"));
445 }
446
210aabe9
MT
447 // Set MIME type
448 const char* mimetype = pakfire_file_get_mimetype(file);
449 if (mimetype) {
450 archive_entry_xattr_add_entry(entry,
451 "PAKFIRE.mimetype", mimetype, strlen(mimetype));
452 }
453
d4b2ef2d
MT
454 // Compute any required file digests
455 r = pakfire_file_compute_digests(file, digest_types);
456 if (r)
a03246e0 457 goto ERROR;
d4b2ef2d 458
399d14c2
MT
459 // Copy digests
460
d98740de 461 // SHA-3-512
da3b7f52 462 if ((digest_types & PAKFIRE_DIGEST_SHA3_512)
045fa504 463 && pakfire_digest_set(file->digests.sha3_512))
d98740de
MT
464 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha3_512",
465 file->digests.sha3_512, sizeof(file->digests.sha3_512));
466
467 // SHA-3-256
da3b7f52 468 if ((digest_types & PAKFIRE_DIGEST_SHA3_256) &&
045fa504 469 pakfire_digest_set(file->digests.sha3_256))
d98740de
MT
470 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha3_256",
471 file->digests.sha3_256, sizeof(file->digests.sha3_256));
472
f1e6c5df 473 // BLAKE2b512
da3b7f52 474 if ((digest_types & PAKFIRE_DIGEST_BLAKE2B512) &&
045fa504 475 pakfire_digest_set(file->digests.blake2b512))
f1e6c5df
MT
476 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.blake2b512",
477 file->digests.blake2b512, sizeof(file->digests.blake2b512));
478
479 // BLAKE2s256
da3b7f52 480 if ((digest_types & PAKFIRE_DIGEST_BLAKE2S256) &&
045fa504 481 pakfire_digest_set(file->digests.blake2s256))
f1e6c5df
MT
482 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.blake2s256",
483 file->digests.blake2s256, sizeof(file->digests.blake2s256));
484
4500ed0a 485 // SHA-2-512
da3b7f52 486 if ((digest_types & PAKFIRE_DIGEST_SHA2_512) &&
045fa504 487 pakfire_digest_set(file->digests.sha2_512))
4500ed0a
MT
488 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha2_512",
489 file->digests.sha2_512, sizeof(file->digests.sha2_512));
399d14c2 490
4500ed0a 491 // SHA-2-256
da3b7f52 492 if ((digest_types & PAKFIRE_DIGEST_SHA2_512) &&
045fa504 493 pakfire_digest_set(file->digests.sha2_256))
4500ed0a
MT
494 archive_entry_xattr_add_entry(entry, "PAKFIRE.digests.sha2_256",
495 file->digests.sha2_256, sizeof(file->digests.sha2_256));
5acd852e 496
729568d0
MT
497 // Capabilities
498 if (file->caps) {
499 r = pakfire_file_write_fcaps(file, &cap_data);
500 if (r) {
c3e5b4e6 501 CTX_ERROR(file->ctx, "Could not export capabilities: %m\n");
a03246e0 502 goto ERROR;
729568d0
MT
503 }
504
505 // Store capabilities in archive entry
506 archive_entry_xattr_add_entry(entry,
507 "security.capability", &cap_data, sizeof(cap_data));
508 }
509
5acd852e 510 return entry;
a03246e0
MT
511
512ERROR:
513 if (entry)
514 archive_entry_free(entry);
515
516 return NULL;
5acd852e
MT
517}
518
5803b5f6 519static void pakfire_file_free(struct pakfire_file* file) {
c2fe443d
MT
520 // Free capabilities
521 if (file->caps)
522 cap_free(file->caps);
37c5d873
MT
523 if (file->entry)
524 archive_entry_free(file->entry);
8780f2ec
MT
525 if (file->pakfire)
526 pakfire_unref(file->pakfire);
527 if (file->ctx)
528 pakfire_ctx_unref(file->ctx);
f0d6233d 529 free(file);
221cc3ce
MT
530}
531
5803b5f6 532PAKFIRE_EXPORT struct pakfire_file* pakfire_file_ref(struct pakfire_file* file) {
5e9463ec 533 file->nrefs++;
221cc3ce 534
5e9463ec
MT
535 return file;
536}
221cc3ce 537
5803b5f6 538PAKFIRE_EXPORT struct pakfire_file* pakfire_file_unref(struct pakfire_file* file) {
5e9463ec
MT
539 if (--file->nrefs > 0)
540 return file;
541
542 pakfire_file_free(file);
543 return NULL;
221cc3ce
MT
544}
545
2f3563b9
MT
546/*
547 Flags
548*/
549
550int pakfire_file_has_flag(struct pakfire_file* file, int flag) {
551 return file->flags & flag;
552}
553
554int pakfire_file_set_flags(struct pakfire_file* file, int flag) {
555 file->flags |= flag;
556
557 return 0;
558}
559
5852b822
MT
560#define pakfire_file_strmode(file, buffer) \
561 __pakfire_file_strmode(file, buffer, sizeof(buffer))
562
563static int __pakfire_file_strmode(struct pakfire_file* file, char* s, const size_t length) {
564 int r;
565
566 static const mode_t permbits[] = {
567 0400,
568 0200,
569 0100,
570 0040,
571 0020,
572 0010,
573 0004,
574 0002,
575 0001
576 };
577
578 const mode_t mode = pakfire_file_get_mode(file);
579 const mode_t type = pakfire_file_get_type(file);
580
581 // Set some default string
582 r = __pakfire_string_set(s, length, "?rwxrwxrwx ");
583 if (r)
584 return r;
585
586 switch (type) {
587 case S_IFREG:
588 s[0] = '-';
589 break;
590
591 case S_IFBLK:
592 s[0] = 'b';
593 break;
594
595 case S_IFCHR:
596 s[0] = 'c';
597 break;
598
599 case S_IFDIR:
600 s[0] = 'd';
601 break;
602
603 case S_IFLNK:
604 s[0] = 'l';
605 break;
606
607 case S_IFSOCK:
608 s[0] = 's';
609 break;
610
611 case S_IFIFO:
612 s[0] = 'p';
613 break;
614
615 default:
37c5d873
MT
616 const char* hardlink = pakfire_file_get_hardlink(file);
617
618 if (hardlink) {
5852b822
MT
619 s[0] = 'h';
620 break;
621 }
622 }
623
624 for (unsigned int i = 0; i < 9; i++) {
625 if (mode & permbits[i])
626 continue;
627
628 s[i+1] = '-';
629 }
630
631 if (mode & S_ISUID) {
632 if (mode & 0100)
633 s[3] = 's';
634 else
635 s[3] = 'S';
636 }
637
638 if (mode & S_ISGID) {
639 if (mode & 0010)
640 s[6] = 's';
641 else
642 s[6] = 'S';
643 }
644
645 if (mode & S_ISVTX) {
646 if (mode & 0001)
647 s[9] = 't';
648 else
649 s[9] = 'T';
650 }
651
652#if 0
653 if (file->caps)
654 s[10] = '+';
655#endif
656
657 return 0;
658}
659
f7f44921 660char* pakfire_file_dump(struct pakfire_file* file, int flags) {
acfb811a 661 char* buffer = NULL;
5852b822
MT
662 int r;
663
664 char mode[12];
665 char time[32];
666
7fe6e18f
MT
667 // Initialize the buffer
668 r = asprintf(&buffer, "%s", "");
669 if (r < 0)
670 goto ERROR;
671
5852b822 672 // Format mode
f7f44921
MT
673 if (flags & PAKFIRE_FILE_DUMP_MODE) {
674 r = pakfire_file_strmode(file, mode);
675 if (r)
676 goto ERROR;
677
678 r = asprintf(&buffer, "%s %s", buffer, mode);
679 if (r < 0)
680 goto ERROR;
681 }
682
683 // Format ownership
684 if (flags & PAKFIRE_FILE_DUMP_OWNERSHIP) {
37c5d873
MT
685 const char* uname = pakfire_file_get_uname(file);
686 const char* gname = pakfire_file_get_gname(file);
687
688 r = asprintf(&buffer, "%s %s/%s", buffer, uname, gname);
f7f44921
MT
689 if (r < 0)
690 goto ERROR;
691 }
692
693 // Format size
694 if (flags & PAKFIRE_FILE_DUMP_SIZE) {
37c5d873
MT
695 const size_t size = pakfire_file_get_size(file);
696
697 r = asprintf(&buffer, "%s %8zu", buffer, size);
f7f44921
MT
698 if (r < 0)
699 goto ERROR;
700 }
5852b822
MT
701
702 // Format time
f7f44921 703 if (flags & PAKFIRE_FILE_DUMP_TIME) {
37c5d873
MT
704 const time_t ctime = pakfire_file_get_ctime(file);
705
706 r = pakfire_strftime(time, "%Y-%m-%d %H:%M", ctime);
f7f44921
MT
707 if (r)
708 goto ERROR;
709
710 r = asprintf(&buffer, "%s %s", buffer, time);
711 if (r < 0)
712 goto ERROR;
713 }
5852b822 714
f7f44921 715 // Format path
37c5d873 716 r = asprintf(&buffer, "%s %s", buffer, pakfire_file_get_path(file));
5852b822 717 if (r < 0)
f7f44921 718 goto ERROR;
5852b822 719
a3bf05ad 720 // Append symlink target
f7f44921
MT
721 if (flags & PAKFIRE_FILE_DUMP_LINK_TARGETS) {
722 switch (pakfire_file_get_type(file)) {
723 case S_IFLNK:
37c5d873 724 r = asprintf(&buffer, "%s -> %s", buffer, pakfire_file_get_symlink(file));
f7f44921
MT
725 if (r < 0)
726 return NULL;
a3bf05ad 727
f7f44921
MT
728 default:
729 break;
730 }
731 }
732
28886e21
MT
733 // Dump Issues
734 if (flags & PAKFIRE_FILE_DUMP_ISSUES) {
735 if (file->issues & PAKFIRE_FILE_FHS_ERROR) {
6d9dd7bc
MT
736 r = asprintf(&buffer, "%s [FHS-ERROR]", buffer);
737 if (r < 0)
738 goto ERROR;
739 }
740
03a5ac95
MT
741 if (file->issues & PAKFIRE_FILE_MISSING_DEBUGINFO) {
742 r = asprintf(&buffer, "%s [MISSING-DEBUGINFO]", buffer);
743 if (r < 0)
744 goto ERROR;
745 }
746
62bf2777 747 // Stack-smashing Protection
d8acc837
MT
748 if (file->issues & PAKFIRE_FILE_MISSING_SSP) {
749 r = asprintf(&buffer, "%s [MISSING-SSP]", buffer);
62bf2777
MT
750 if (r < 0)
751 goto ERROR;
752 }
f7f44921 753
62bf2777 754 // Position-independent Executable
d8acc837
MT
755 if (file->issues & PAKFIRE_FILE_MISSING_PIE) {
756 r = asprintf(&buffer, "%s [MISSING-PIE]", buffer);
62bf2777
MT
757 if (r < 0)
758 goto ERROR;
759 }
03d5abd3 760
62bf2777
MT
761 // Executable Stack
762 if (file->issues & PAKFIRE_FILE_EXECSTACK) {
763 r = asprintf(&buffer, "%s [EXECSTACK]", buffer);
764 if (r < 0)
765 goto ERROR;
766 }
03d5abd3 767
9a789d72
MT
768 // Not RELRO
769 if (file->issues & PAKFIRE_FILE_NO_RELRO) {
770 r = asprintf(&buffer, "%s [NO-RELRO]", buffer);
62bf2777
MT
771 if (r < 0)
772 goto ERROR;
f7f44921 773 }
7b461c8b
MT
774
775 // Has RUNPATH?
776 if (file->issues & PAKFIRE_FILE_HAS_RUNPATH) {
777 r = asprintf(&buffer, "%s [HAS-RUNPATH]", buffer);
778 if (r < 0)
779 goto ERROR;
780 }
691091ad
MT
781
782 // Invalid capabilities
783 if (file->issues & PAKFIRE_FILE_INVALID_CAPS) {
784 r = asprintf(&buffer, "%s [INVALID-CAPS]", buffer);
785 if (r < 0)
786 goto ERROR;
787 }
375a06e3
MT
788
789 // Invalid interpreters
790 if (file->issues & PAKFIRE_FILE_INVALID_INTERPRETER) {
791 r = asprintf(&buffer, "%s [INVALID-INTERPRETER]", buffer);
792 if (r < 0)
793 goto ERROR;
794 }
a3bf05ad
MT
795 }
796
5852b822 797 return buffer;
f7f44921
MT
798
799ERROR:
800 if (buffer)
801 free(buffer);
802
803 return NULL;
5852b822
MT
804}
805
5803b5f6 806PAKFIRE_EXPORT int pakfire_file_cmp(struct pakfire_file* file1, struct pakfire_file* file2) {
32485f6c
MT
807 const char* path1 = pakfire_file_get_path(file1);
808 const char* path2 = pakfire_file_get_path(file2);
221cc3ce 809
32485f6c 810 return strcmp(path1, path2);
221cc3ce
MT
811}
812
5803b5f6 813const char* pakfire_file_get_abspath(struct pakfire_file* file) {
37c5d873 814 return archive_entry_sourcepath(file->entry);
e4c2f7a9
MT
815}
816
5803b5f6 817int pakfire_file_set_abspath(struct pakfire_file* file, const char* path) {
d13bd5b7 818 // Check if path is set and absolute
ac1765b0
MT
819 if (!path || *path != '/')
820 return -EINVAL;
d13bd5b7 821
49a39ad9 822 // Store the abspath
37c5d873 823 archive_entry_copy_sourcepath(file->entry, path);
49a39ad9 824
37c5d873 825 return 0;
3b9e3970
MT
826}
827
5803b5f6 828PAKFIRE_EXPORT const char* pakfire_file_get_path(struct pakfire_file* file) {
37c5d873 829 return archive_entry_pathname(file->entry);
221cc3ce
MT
830}
831
5803b5f6 832PAKFIRE_EXPORT int pakfire_file_set_path(struct pakfire_file* file, const char* path) {
37c5d873
MT
833 char buffer[PATH_MAX];
834 int r = 0;
d13bd5b7 835
ac1765b0
MT
836 // Check input
837 if (!path)
838 return -EINVAL;
839
2cc8b5f4
MT
840 // Strip any leading dots from paths
841 if (pakfire_string_startswith(path, "./"))
842 path++;
843
844 switch (*path) {
845 // Just store the path if it is absolute
846 case '/':
37c5d873 847 archive_entry_set_pathname(file->entry, path);
2cc8b5f4
MT
848 break;
849
850 // Handle relative paths
851 default:
37c5d873 852 r = pakfire_string_format(buffer, "/%s", path);
2cc8b5f4
MT
853 if (r)
854 goto ERROR;
520213e9 855
37c5d873
MT
856 archive_entry_set_pathname(file->entry, buffer);
857 break;
520213e9
MT
858 }
859
860 return r;
12a327de
MT
861
862ERROR:
c3e5b4e6 863 CTX_ERROR(file->ctx, "Could not set path '%s': %m\n", path);
12a327de 864 return r;
221cc3ce
MT
865}
866
59d8c727 867PAKFIRE_EXPORT const char* pakfire_file_get_hardlink(struct pakfire_file* file) {
37c5d873 868 return archive_entry_hardlink(file->entry);
59d8c727
MT
869}
870
871PAKFIRE_EXPORT void pakfire_file_set_hardlink(struct pakfire_file* file, const char* link) {
37c5d873 872 archive_entry_set_hardlink(file->entry, link);
59d8c727
MT
873}
874
875PAKFIRE_EXPORT const char* pakfire_file_get_symlink(struct pakfire_file* file) {
37c5d873 876 return archive_entry_symlink(file->entry);
59d8c727
MT
877}
878
879PAKFIRE_EXPORT void pakfire_file_set_symlink(struct pakfire_file* file, const char* link) {
37c5d873 880 archive_entry_set_symlink(file->entry, link);
59d8c727
MT
881}
882
f8733b31 883PAKFIRE_EXPORT nlink_t pakfire_file_get_nlink(struct pakfire_file* file) {
37c5d873 884 return archive_entry_nlink(file->entry);
f8733b31
MT
885}
886
887PAKFIRE_EXPORT void pakfire_file_set_nlink(struct pakfire_file* file, const nlink_t nlink) {
37c5d873 888 archive_entry_set_nlink(file->entry, nlink);
f8733b31
MT
889}
890
891PAKFIRE_EXPORT ino_t pakfire_file_get_inode(struct pakfire_file* file) {
37c5d873 892 return archive_entry_ino64(file->entry);
f8733b31
MT
893}
894
895PAKFIRE_EXPORT void pakfire_file_set_inode(struct pakfire_file* file, const ino_t ino) {
37c5d873 896 archive_entry_set_ino64(file->entry, ino);
f8733b31
MT
897}
898
899PAKFIRE_EXPORT dev_t pakfire_file_get_dev(struct pakfire_file* file) {
37c5d873 900 return archive_entry_dev(file->entry);
f8733b31
MT
901}
902
903PAKFIRE_EXPORT void pakfire_file_set_dev(struct pakfire_file* file, const dev_t dev) {
37c5d873 904 archive_entry_set_dev(file->entry, dev);
f8733b31
MT
905}
906
5803b5f6 907PAKFIRE_EXPORT int pakfire_file_get_type(struct pakfire_file* file) {
37c5d873 908 return archive_entry_filetype(file->entry);
221cc3ce
MT
909}
910
a09b95e0 911PAKFIRE_EXPORT off_t pakfire_file_get_size(struct pakfire_file* file) {
37c5d873 912 return archive_entry_size(file->entry);
221cc3ce
MT
913}
914
a09b95e0 915PAKFIRE_EXPORT void pakfire_file_set_size(struct pakfire_file* file, off_t size) {
37c5d873 916 archive_entry_set_size(file->entry, size);
221cc3ce
MT
917}
918
302e3253 919PAKFIRE_EXPORT const char* pakfire_file_get_uname(struct pakfire_file* file) {
37c5d873 920 return archive_entry_uname(file->entry);
221cc3ce
MT
921}
922
302e3253 923PAKFIRE_EXPORT int pakfire_file_set_uname(struct pakfire_file* file, const char* uname) {
37c5d873
MT
924 archive_entry_set_uname(file->entry, uname);
925
926 return 0;
221cc3ce
MT
927}
928
302e3253 929PAKFIRE_EXPORT const char* pakfire_file_get_gname(struct pakfire_file* file) {
37c5d873 930 return archive_entry_gname(file->entry);
221cc3ce
MT
931}
932
302e3253 933PAKFIRE_EXPORT int pakfire_file_set_gname(struct pakfire_file* file, const char* gname) {
37c5d873
MT
934 archive_entry_set_gname(file->entry, gname);
935
936 return 0;
221cc3ce
MT
937}
938
5803b5f6 939PAKFIRE_EXPORT mode_t pakfire_file_get_mode(struct pakfire_file* file) {
37c5d873 940 return archive_entry_mode(file->entry);
221cc3ce
MT
941}
942
5803b5f6 943PAKFIRE_EXPORT void pakfire_file_set_mode(struct pakfire_file* file, mode_t mode) {
37c5d873 944 archive_entry_set_mode(file->entry, mode);
221cc3ce
MT
945}
946
134545d5 947PAKFIRE_EXPORT mode_t pakfire_file_get_perms(struct pakfire_file* file) {
37c5d873 948 return archive_entry_perm(file->entry);
134545d5
MT
949}
950
951PAKFIRE_EXPORT void pakfire_file_set_perms(struct pakfire_file* file, const mode_t perms) {
37c5d873 952 archive_entry_set_mode(file->entry, pakfire_file_get_type(file) | perms);
134545d5
MT
953}
954
691091ad 955static int pakfire_file_is_executable(struct pakfire_file* file) {
37c5d873 956 return pakfire_file_get_mode(file) & (S_IXUSR|S_IXGRP|S_IXOTH);
691091ad
MT
957}
958
5803b5f6 959PAKFIRE_EXPORT time_t pakfire_file_get_ctime(struct pakfire_file* file) {
37c5d873 960 return archive_entry_ctime(file->entry);
ef4e8460
MT
961}
962
5803b5f6 963PAKFIRE_EXPORT void pakfire_file_set_ctime(struct pakfire_file* file, time_t time) {
37c5d873 964 archive_entry_set_ctime(file->entry, time, 0);
ef4e8460
MT
965}
966
5803b5f6 967PAKFIRE_EXPORT time_t pakfire_file_get_mtime(struct pakfire_file* file) {
37c5d873 968 return archive_entry_mtime(file->entry);
221cc3ce
MT
969}
970
5803b5f6 971PAKFIRE_EXPORT void pakfire_file_set_mtime(struct pakfire_file* file, time_t time) {
37c5d873 972 archive_entry_set_mtime(file->entry, time, 0);
221cc3ce
MT
973}
974
65131b30 975PAKFIRE_EXPORT const unsigned char* pakfire_file_get_digest(
c52e0148 976 struct pakfire_file* file, const enum pakfire_digest_types type, size_t* length) {
65131b30 977
9802aaf6 978 switch (type) {
d98740de
MT
979 case PAKFIRE_DIGEST_SHA3_512:
980 if (!pakfire_digest_set(file->digests.sha3_512))
981 return NULL;
982
983 if (length)
984 *length = sizeof(file->digests.sha3_512);
985
986 return file->digests.sha3_512;
987
988 case PAKFIRE_DIGEST_SHA3_256:
989 if (!pakfire_digest_set(file->digests.sha3_256))
990 return NULL;
991
992 if (length)
993 *length = sizeof(file->digests.sha3_256);
994
995 return file->digests.sha3_256;
996
f1e6c5df
MT
997 case PAKFIRE_DIGEST_BLAKE2B512:
998 if (!pakfire_digest_set(file->digests.blake2b512))
999 return NULL;
1000
1001 if (length)
1002 *length = sizeof(file->digests.blake2b512);
1003
1004 return file->digests.blake2b512;
1005
1006 case PAKFIRE_DIGEST_BLAKE2S256:
1007 if (!pakfire_digest_set(file->digests.blake2s256))
1008 return NULL;
1009
1010 if (length)
1011 *length = sizeof(file->digests.blake2s256);
1012
1013 return file->digests.blake2s256;
1014
4500ed0a
MT
1015 case PAKFIRE_DIGEST_SHA2_512:
1016 if (!pakfire_digest_set(file->digests.sha2_512))
9802aaf6 1017 return NULL;
65131b30 1018
9802aaf6 1019 if (length)
4500ed0a 1020 *length = sizeof(file->digests.sha2_512);
65131b30 1021
4500ed0a 1022 return file->digests.sha2_512;
9802aaf6 1023
4500ed0a
MT
1024 case PAKFIRE_DIGEST_SHA2_256:
1025 if (!pakfire_digest_set(file->digests.sha2_256))
9802aaf6 1026 return NULL;
5e8dfbeb 1027
9802aaf6 1028 if (length)
4500ed0a 1029 *length = sizeof(file->digests.sha2_256);
5e8dfbeb 1030
4500ed0a 1031 return file->digests.sha2_256;
2c4b4a02
MT
1032
1033 case PAKFIRE_DIGEST_UNDEFINED:
1034 break;
5e8dfbeb
MT
1035 }
1036
9802aaf6 1037 return NULL;
5e8dfbeb
MT
1038}
1039
1040PAKFIRE_EXPORT int pakfire_file_set_digest(struct pakfire_file* file,
c52e0148 1041 const enum pakfire_digest_types type, const unsigned char* digest, const size_t length) {
37c5d873
MT
1042 if (!digest)
1043 return -EINVAL;
5e8dfbeb 1044
399d14c2
MT
1045 // Check buffer length
1046 if (pakfire_digest_length(type) != length) {
c3e5b4e6 1047 CTX_ERROR(file->ctx, "Digest has an incorrect length of %zu byte(s)\n", length);
37c5d873 1048 return -ENOMSG;
399d14c2
MT
1049 }
1050
1051 // Store the digest
9802aaf6 1052 switch (type) {
d98740de
MT
1053 case PAKFIRE_DIGEST_SHA3_512:
1054 memcpy(file->digests.sha3_512, digest, sizeof(file->digests.sha3_512));
1055 break;
1056
1057 case PAKFIRE_DIGEST_SHA3_256:
1058 memcpy(file->digests.sha3_256, digest, sizeof(file->digests.sha3_256));
1059 break;
1060
f1e6c5df
MT
1061 case PAKFIRE_DIGEST_BLAKE2B512:
1062 memcpy(file->digests.blake2b512, digest, sizeof(file->digests.blake2b512));
1063 break;
1064
1065 case PAKFIRE_DIGEST_BLAKE2S256:
1066 memcpy(file->digests.blake2s256, digest, sizeof(file->digests.blake2s256));
1067 break;
1068
4500ed0a
MT
1069 case PAKFIRE_DIGEST_SHA2_512:
1070 memcpy(file->digests.sha2_512, digest, sizeof(file->digests.sha2_512));
9802aaf6 1071 break;
65131b30 1072
4500ed0a
MT
1073 case PAKFIRE_DIGEST_SHA2_256:
1074 memcpy(file->digests.sha2_256, digest, sizeof(file->digests.sha2_256));
9802aaf6 1075 break;
2c4b4a02
MT
1076
1077 case PAKFIRE_DIGEST_UNDEFINED:
1078 errno = ENOTSUP;
1079 return 1;
5e8dfbeb
MT
1080 }
1081
5e8dfbeb 1082 return 0;
221cc3ce
MT
1083}
1084
729568d0
MT
1085PAKFIRE_EXPORT int pakfire_file_has_caps(struct pakfire_file* file) {
1086 if (file->caps)
1087 return 1;
1088
1089 return 0;
1090}
1091
1092PAKFIRE_EXPORT char* pakfire_file_get_caps(struct pakfire_file* file) {
1093 char* copy = NULL;
1094 char* text = NULL;
1095 ssize_t length = 0;
1096
1097 if (file->caps) {
1098 text = cap_to_text(file->caps, &length);
1099 if (!text) {
c3e5b4e6 1100 CTX_ERROR(file->ctx, "Could not export capabilities: %m\n");
729568d0
MT
1101 goto ERROR;
1102 }
1103
1104 // libcap is being weird and uses its own allocator so we have to copy the string
1105 copy = strndup(text, length);
1106 if (!copy)
1107 goto ERROR;
1108 }
1109
1110ERROR:
1111 if (text)
1112 cap_free(text);
1113
1114 return copy;
1115}
1116
5803b5f6 1117static int pakfire_file_levels(struct pakfire_file* file) {
37c5d873
MT
1118 const char* path = pakfire_file_get_path(file);
1119 if (!path)
3fca5032
MT
1120 return 0;
1121
1122 int levels = 0;
1123
37c5d873 1124 for (const char* p = path; *p; p++) {
3fca5032
MT
1125 if (*p == '/')
1126 levels++;
1127 }
1128
1129 return levels;
1130}
1131
d5a13ade 1132FILE* pakfire_file_open(struct pakfire_file* file) {
37c5d873
MT
1133 const char* path = pakfire_file_get_abspath(file);
1134 if (!path)
1135 return NULL;
1136
1137 FILE* f = fopen(path, "r+");
d5a13ade 1138 if (!f)
c3e5b4e6 1139 CTX_ERROR(file->ctx, "Could not open %s: %m\n", path);
d5a13ade
MT
1140
1141 return f;
1142}
1143
fabc09a9
MT
1144int pakfire_file_payload_matches(struct pakfire_file* file,
1145 const void* needle, const size_t length) {
1146 char buffer[1024 * 1024];
1147 FILE* f = NULL;
1148 void* p = NULL;
1149 int r;
1150
37c5d873
MT
1151 const mode_t mode = pakfire_file_get_mode(file);
1152
fabc09a9 1153 // Only run for regular files
37c5d873 1154 if (!S_ISREG(mode))
fabc09a9
MT
1155 return 0;
1156
37c5d873
MT
1157 const size_t size = pakfire_file_get_size(file);
1158
e4d7e9b4 1159 // Skip empty files
37c5d873 1160 if (!size)
e4d7e9b4
MT
1161 return 0;
1162
fabc09a9
MT
1163 // Open the file
1164 f = pakfire_file_open(file);
ede8bda8
MT
1165 if (!f) {
1166 r = 1;
fabc09a9 1167 goto ERROR;
ede8bda8 1168 }
fabc09a9 1169
fabc09a9
MT
1170 while (!feof(f)) {
1171 size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
1172
1173 // Raise any reading errors
1174 if (ferror(f)) {
1175 r = 1;
1176 goto ERROR;
1177 }
1178
1179 // Search for the needle
1180 p = memmem(buffer, bytes_read, needle, length);
fabc09a9
MT
1181 if (p) {
1182 r = 1;
1183 goto ERROR;
1184 }
1185 }
1186
1187 // No match
1188 r = 0;
1189
1190ERROR:
1191 if (f)
1192 fclose(f);
1193
1194 return r;
1195}
1196
1e76689a
MT
1197static int __pakfire_file_compute_digests(struct pakfire_file* file,
1198 struct pakfire_digests* digests, const int types) {
1199 FILE* f = NULL;
1200 int r = 1;
1201
37c5d873
MT
1202 const mode_t mode = pakfire_file_get_mode(file);
1203
1e76689a 1204 // Skip this for anything that isn't a regular file
37c5d873 1205 if (!S_ISREG(mode))
1e76689a
MT
1206 return 0;
1207
1208 // Reset digests
1209 pakfire_digests_reset(digests, types);
1210
1211 // Open the file
1212 f = pakfire_file_open(file);
1213 if (!f)
1214 goto ERROR;
1215
1216 // Compute digests
a6d84de0 1217 r = pakfire_digests_compute_from_file(file->ctx, digests, types, f);
1e76689a
MT
1218 if (r)
1219 goto ERROR;
1220
1221ERROR:
1222 if (f)
1223 fclose(f);
1224
1225 return r;
1226}
1227
1228int pakfire_file_compute_digests(struct pakfire_file* file, const int types) {
6b32db11 1229 return __pakfire_file_compute_digests(file, &file->digests, types);
1e76689a
MT
1230}
1231
9274236e 1232static int pakfire_file_remove(struct pakfire_file* file) {
84eee57a
MT
1233 int r;
1234
37c5d873
MT
1235 const char* path = pakfire_file_get_path(file);
1236 const char* abspath = pakfire_file_get_abspath(file);
1237
c3e5b4e6 1238 CTX_DEBUG(file->ctx, "Removing %s...\n", path);
3fca5032 1239
37c5d873
MT
1240 // We cannot delete if we don't have the absolute path
1241 if (!abspath)
1242 return -ENOTSUP;
3fca5032 1243
37c5d873 1244 switch (pakfire_file_get_type(file)) {
84eee57a
MT
1245 // Call rmdir() for directories
1246 case S_IFDIR:
37c5d873 1247 r = rmdir(abspath);
84eee57a
MT
1248 if (r) {
1249 switch (errno) {
1250 // Ignore when the directory is not empty
1251 case ENOTEMPTY:
1252 r = 0;
1253 break;
1254
1255 // Ignore if the directory didn't exist
1256 case ENOENT:
1257 r = 0;
1258 break;
1259
1260 // Ignore if the directory changed type
1261 case ENOTDIR:
1262 r = 0;
1263 break;
1264
1265 // Log anything else
1266 default:
c3e5b4e6 1267 CTX_ERROR(file->ctx, "Could not remove directory %s: %m\n", path);
84eee57a
MT
1268 break;
1269 }
1270 }
1271 break;
3fca5032 1272
84eee57a
MT
1273 // Call unlink() for everything else
1274 default:
37c5d873 1275 r = unlink(abspath);
84eee57a
MT
1276 if (r) {
1277 switch (errno) {
1278 // Ignore if the file didn't exist
1279 case ENOENT:
1280 r = 0;
1281 break;
1282
1283 default:
c3e5b4e6 1284 CTX_ERROR(file->ctx, "Could not remove %s: %m\n", path);
84eee57a
MT
1285 break;
1286 }
1287 }
1288 break;
3fca5032
MT
1289 }
1290
ac71886a
MT
1291 return r;
1292}
1293
7434fa90 1294int pakfire_file_symlink_target_exists(struct pakfire_file* file) {
37c5d873
MT
1295 const mode_t mode = pakfire_file_get_mode(file);
1296
7434fa90 1297 // Fail if this got called for anything that isn't a symlink
37c5d873
MT
1298 if (!S_ISLNK(mode))
1299 return -EINVAL;
7434fa90 1300
37c5d873
MT
1301 const char* path = pakfire_file_get_abspath(file);
1302
1303 return pakfire_path_exists(path);
7434fa90
MT
1304}
1305
210aabe9
MT
1306/*
1307 MIME Type
1308*/
1309
1310int pakfire_file_detect_mimetype(struct pakfire_file* file) {
37c5d873
MT
1311 const mode_t mode = pakfire_file_get_mode(file);
1312
210aabe9 1313 // Only process regular files
37c5d873 1314 if (!S_ISREG(mode))
210aabe9
MT
1315 return 0;
1316
1317 // Skip if MIME type is already set
1318 if (*file->mimetype)
1319 return 0;
1320
1321 // Fetch the magic cookie
1322 magic_t magic = pakfire_get_magic(file->pakfire);
1323 if (!magic)
1324 return 1;
1325
37c5d873
MT
1326 const char* path = pakfire_file_get_abspath(file);
1327
210aabe9 1328 // Check the file
37c5d873 1329 const char* mimetype = magic_file(magic, path);
210aabe9 1330 if (!mimetype) {
c3e5b4e6 1331 CTX_ERROR(file->ctx, "Could not classify %s: %s\n",
37c5d873 1332 pakfire_file_get_path(file), magic_error(magic));
210aabe9
MT
1333 return 1;
1334 }
1335
c3e5b4e6 1336 CTX_DEBUG(file->ctx, "Classified %s as %s\n", pakfire_file_get_path(file), mimetype);
210aabe9
MT
1337
1338 // Store the value
1339 return pakfire_file_set_mimetype(file, mimetype);
1340}
1341
1342PAKFIRE_EXPORT const char* pakfire_file_get_mimetype(struct pakfire_file* file) {
1343 // Return nothing on an empty mimetype
1344 if (!*file->mimetype)
1345 return NULL;
1346
1347 return file->mimetype;
1348}
1349
1350PAKFIRE_EXPORT int pakfire_file_set_mimetype(
1351 struct pakfire_file* file, const char* mimetype) {
1352 // Store the value
1353 return pakfire_string_set(file->mimetype, mimetype);
1354}
1355
71f6f465
MT
1356/*
1357 Classification
1358*/
1359
c3e5b4e6 1360static int setup_libelf(struct pakfire_ctx* ctx) {
df71dc60
MT
1361 // Initialize libelf
1362 if (elf_version(EV_CURRENT) == EV_NONE) {
c3e5b4e6 1363 CTX_ERROR(ctx, "Could not initialize libelf: %s\n", elf_errmsg(-1));
df71dc60
MT
1364
1365 return 1;
1366 }
1367
1368 return 0;
1369}
1370
71f6f465 1371static int pakfire_file_classify_mode(struct pakfire_file* file) {
37c5d873
MT
1372 const mode_t mode = pakfire_file_get_mode(file);
1373
71f6f465 1374 // Check for regular files
37c5d873 1375 if (S_ISREG(mode)) {
71f6f465
MT
1376 file->class |= PAKFIRE_FILE_REGULAR;
1377
72a36c9d 1378 // Does the file have executable permissions?
37c5d873 1379 if (mode & (S_IXUSR|S_IXGRP|S_IXOTH))
72a36c9d
MT
1380 file->class |= PAKFIRE_FILE_EXECUTABLE;
1381
71f6f465 1382 // Check for directories
37c5d873 1383 } else if (S_ISDIR(mode))
71f6f465
MT
1384 file->class |= PAKFIRE_FILE_DIRECTORY;
1385
1386 // Check for symlinks
37c5d873 1387 else if (S_ISLNK(mode))
71f6f465
MT
1388 file->class |= PAKFIRE_FILE_SYMLINK;
1389
1390 // Check for character devices
37c5d873 1391 else if (S_ISCHR(mode))
71f6f465
MT
1392 file->class |= PAKFIRE_FILE_CHARACTER;
1393
1394 // Check for block devices
37c5d873 1395 else if (S_ISBLK(mode))
71f6f465
MT
1396 file->class |= PAKFIRE_FILE_BLOCK;
1397
1398 // Check for FIFO pipes
37c5d873 1399 else if (S_ISFIFO(mode))
71f6f465
MT
1400 file->class |= PAKFIRE_FILE_FIFO;
1401
1402 // Check for sockets
37c5d873 1403 else if (S_ISSOCK(mode))
71f6f465
MT
1404 file->class |= PAKFIRE_FILE_SOCKET;
1405
1406 return 0;
1407}
1408
239230c2
MT
1409static const struct pattern {
1410 const char* pattern;
71f6f465 1411 int class;
239230c2 1412} patterns[] = {
c77b52ac
MT
1413 { "**.a", PAKFIRE_FILE_STATIC_LIBRARY },
1414 { "**.la", PAKFIRE_FILE_LIBTOOL_ARCHIVE },
1415 { "**.pm", PAKFIRE_FILE_PERL },
1416 { "**.pc", PAKFIRE_FILE_PKGCONFIG },
1417 { "/usr/lib/firmware/**", PAKFIRE_FILE_FIRMWARE },
1418 { "/usr/lib/grub/**", PAKFIRE_FILE_FIRMWARE },
65eb1fbf 1419 { "/usr/lib/valgrind/*", PAKFIRE_FILE_FIRMWARE },
fc35e5cc 1420 { "/usr/lib*/ld-*.so*", PAKFIRE_FILE_RUNTIME_LINKER },
239230c2 1421 { NULL },
71f6f465
MT
1422};
1423
239230c2
MT
1424static int pakfire_file_classify_pattern(struct pakfire_file* file) {
1425 for (const struct pattern* p = patterns; p->pattern; p++) {
1426 if (pakfire_file_matches(file, p->pattern)) {
1427 file->class |= p->class;
71f6f465
MT
1428 break;
1429 }
1430 }
1431
1432 return 0;
1433}
1434
1435static const struct mimetype {
1436 const char* mimetype;
1437 int class;
1438} mimetypes[] = {
71f6f465
MT
1439 { "text/x-perl", PAKFIRE_FILE_PERL },
1440 { NULL, 0 },
1441};
1442
1443static int pakfire_file_classify_magic(struct pakfire_file* file) {
210aabe9
MT
1444 int r;
1445
210aabe9
MT
1446 // Detect the MIME type
1447 r = pakfire_file_detect_mimetype(file);
1448 if (r)
1449 return r;
71f6f465 1450
210aabe9
MT
1451 // Fetch the MIME type
1452 const char* mimetype = pakfire_file_get_mimetype(file);
1453 if (!mimetype)
71f6f465 1454 return 1;
71f6f465 1455
210aabe9 1456 // Match the MIME type with a flag
71f6f465
MT
1457 for (const struct mimetype* m = mimetypes; m->mimetype; m++) {
1458 if (strcmp(m->mimetype, mimetype) == 0) {
1459 file->class |= m->class;
1460 break;
1461 }
1462 }
1463
1464 return 0;
1465}
1466
df71dc60
MT
1467static int pakfire_file_classify_elf(struct pakfire_file* file) {
1468 FILE* f = NULL;
1469 Elf* elf = NULL;
1470 int r;
1471
1472 // Don't run this if we already know that file is an ELF file
1473 if (file->class & PAKFIRE_FILE_ELF)
1474 return 0;
1475
1476 // Setup libelf
c3e5b4e6 1477 r = setup_libelf(file->ctx);
df71dc60
MT
1478 if (r)
1479 return r;
1480
1481 // Open the file
37c5d873 1482 f = pakfire_file_open(file);
df71dc60 1483 if (!f) {
c3e5b4e6 1484 CTX_ERROR(file->ctx, "Could not open %s: %m\n", pakfire_file_get_path(file));
df71dc60
MT
1485 return 1;
1486 }
1487
1488 // Try to open the ELF file
1489 elf = elf_begin(fileno(f), ELF_C_READ, NULL);
1490 if (!elf) {
1491 // We fail silently here, because this file might be in a different format
1492 goto ERROR;
1493 }
1494
1495 switch (elf_kind(elf)) {
1496 // Mark this file as an ELF file
1497 case ELF_K_ELF:
1498 file->class |= PAKFIRE_FILE_ELF;
1499 break;
1500
1501 // Ignore everything else
1502 default:
1503 break;
1504 }
1505
1506ERROR:
1507 if (elf)
1508 elf_end(elf);
1509 if (f)
1510 fclose(f);
1511
1512 return 0;
1513}
1514
71f6f465
MT
1515int pakfire_file_classify(struct pakfire_file* file) {
1516 int r;
1517
1518 if (!file->class) {
1519 // First, check the mode so that we won't run magic on directories, symlinks, ...
1520 r = pakfire_file_classify_mode(file);
1521 if (r)
1522 goto ERROR;
1523
1524 // Only run this for regular files
1525 if (file->class & PAKFIRE_FILE_REGULAR) {
239230c2
MT
1526 // Then check for patterns
1527 r = pakfire_file_classify_pattern(file);
71f6f465
MT
1528 if (r)
1529 goto ERROR;
1530
1531 // After that, we will use libmagic...
1532 r = pakfire_file_classify_magic(file);
1533 if (r)
df71dc60
MT
1534 goto ERROR;
1535
1536 // Check if the file is an ELF file
1537 r = pakfire_file_classify_elf(file);
1538 if (r)
71f6f465
MT
1539 goto ERROR;
1540 }
1541 }
1542
1543 return file->class;
1544
1545ERROR:
1546 // Reset the class
1547 file->class = PAKFIRE_FILE_UNKNOWN;
1548
1549 return r;
1550}
1551
1552int pakfire_file_matches_class(struct pakfire_file* file, const int class) {
1553 return pakfire_file_classify(file) & class;
1554}
1555
ac71886a
MT
1556/*
1557 This function tries to remove the file after it has been packaged.
1558
1559 It will try to delete any parent directories as well and ignore if directories
1560 cannot be deleted because they might contain other files
1561*/
9274236e 1562int pakfire_file_cleanup(struct pakfire_file* file, int flags) {
ac71886a
MT
1563 char path[PATH_MAX];
1564
1565 // Try removing the file
1566 int r = pakfire_file_remove(file);
1567 if (r)
1568 return r;
1569
9274236e
MT
1570 // Try to tidy up afterwards
1571 if (flags & PAKFIRE_FILE_CLEANUP_TIDY) {
37c5d873
MT
1572 const char* abspath = pakfire_file_get_abspath(file);
1573
9274236e 1574 // Create a working copy of abspath
37c5d873 1575 r = pakfire_string_set(path, abspath);
9274236e
MT
1576 if (r)
1577 return r;
3fca5032 1578
9274236e
MT
1579 // See how many levels this file has
1580 int levels = pakfire_file_levels(file);
3fca5032 1581
9274236e
MT
1582 // Walk all the way up and remove all parent directories if possible
1583 while (--levels) {
1584 dirname(path);
3fca5032 1585
9274236e
MT
1586 // Break if path is suddenly empty
1587 if (!*path)
1588 break;
3fca5032 1589
c3e5b4e6 1590 CTX_DEBUG(file->ctx, "Trying to remove parent directory %s\n", path);
a8783709 1591
9274236e 1592 r = rmdir(path);
3fca5032 1593
9274236e
MT
1594 // Break on any error
1595 if (r)
1596 break;
1597 }
3fca5032
MT
1598 }
1599
a942166c 1600 return 0;
3fca5032 1601}
cf9bd6f4 1602
9e09e361 1603static int pakfire_file_verify_mode(struct pakfire_file* file, const struct stat* st) {
a09b95e0
MT
1604 const mode_t type = pakfire_file_get_type(file);
1605
9e09e361 1606 // Did the type change?
a09b95e0 1607 if (type != (st->st_mode & S_IFMT)) {
9e09e361
MT
1608 file->verify_status |= PAKFIRE_FILE_TYPE_CHANGED;
1609
c3e5b4e6 1610 CTX_DEBUG(file->ctx, "%s: File Type changed\n", pakfire_file_get_path(file));
9e09e361
MT
1611 }
1612
a09b95e0
MT
1613 const mode_t perms = pakfire_file_get_perms(file);
1614
9e09e361 1615 // Check permissions
a09b95e0 1616 if (perms != (st->st_mode & 0777)) {
9e09e361
MT
1617 file->verify_status |= PAKFIRE_FILE_PERMISSIONS_CHANGED;
1618
c3e5b4e6 1619 CTX_DEBUG(file->ctx, "%s: Permissions changed\n", pakfire_file_get_path(file));
9e09e361
MT
1620 }
1621
f8733b31
MT
1622#if 0
1623 // XXX This does not check what it is supposed to check
1624
9e09e361
MT
1625 // Check if device node changed
1626 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
a09b95e0
MT
1627 const dev_t dev = pakfire_file_get_dev(file);
1628
1629 if (dev != st->st_dev) {
9e09e361
MT
1630 file->verify_status |= PAKFIRE_FILE_DEV_CHANGED;
1631
c3e5b4e6 1632 CTX_DEBUG(file->ctx, "%s: Device Node changed\n", pakfire_file_get_path(file));
9e09e361
MT
1633 }
1634 }
f8733b31 1635#endif
9e09e361
MT
1636
1637 return 0;
1638}
1639
c0b051bb 1640static int pakfire_file_verify_size(struct pakfire_file* file, const struct stat* st) {
37c5d873
MT
1641 const ssize_t size = pakfire_file_get_size(file);
1642
c0b051bb 1643 // Nothing to do if size matches
37c5d873 1644 if (size == st->st_size)
c0b051bb
MT
1645 return 0;
1646
1647 // Size differs
d2b0e219 1648 file->verify_status |= PAKFIRE_FILE_SIZE_CHANGED;
c0b051bb 1649
c3e5b4e6 1650 CTX_DEBUG(file->ctx, "%s: Filesize differs (expected %zd, got %zd byte(s))\n",
37c5d873 1651 pakfire_file_get_path(file), size, st->st_size);
c0b051bb
MT
1652
1653 return 0;
1654}
1655
1656static int pakfire_file_verify_ownership(struct pakfire_file* file, const struct stat* st) {
1657 // Fetch UID/GID
1658#if 0
1659 const uid_t uid = pakfire_unmap_id(file->pakfire, st->st_uid);
1660 const gid_t gid = pakfire_unmap_id(file->pakfire, st->st_gid);
1661#else
1662 const uid_t uid = st->st_uid;
1663 const gid_t gid = st->st_gid;
1664#endif
1665
37c5d873
MT
1666 const char* uname = pakfire_file_get_uname(file);
1667 const char* gname = pakfire_file_get_gname(file);
1668
c0b051bb 1669 // Fetch owner & group
37c5d873
MT
1670 struct passwd* owner = pakfire_getpwnam(file->pakfire, uname);
1671 struct group* group = pakfire_getgrnam(file->pakfire, gname);
c0b051bb
MT
1672
1673 // Check if owner matches
1674 if (!owner || owner->pw_uid != uid) {
d2b0e219 1675 file->verify_status |= PAKFIRE_FILE_OWNER_CHANGED;
c0b051bb 1676
c3e5b4e6 1677 CTX_DEBUG(file->ctx, "%s: Owner differs\n", pakfire_file_get_path(file));
c0b051bb
MT
1678 }
1679
1680 // Check if group matches
1681 if (!group || group->gr_gid != gid) {
d2b0e219 1682 file->verify_status |= PAKFIRE_FILE_GROUP_CHANGED;
c0b051bb 1683
c3e5b4e6 1684 CTX_DEBUG(file->ctx, "%s: Group differs\n", pakfire_file_get_path(file));
c0b051bb
MT
1685 }
1686
1687 return 0;
1688}
1689
0eeac4a5 1690static int pakfire_file_verify_timestamps(struct pakfire_file* file, const struct stat* st) {
37c5d873
MT
1691 const time_t ctime = pakfire_file_get_ctime(file);
1692 const time_t mtime = pakfire_file_get_mtime(file);
1693
0eeac4a5 1694 // Check creation time
37c5d873 1695 if (ctime != st->st_ctime) {
0eeac4a5
MT
1696 file->verify_status |= PAKFIRE_FILE_CTIME_CHANGED;
1697
c3e5b4e6 1698 CTX_DEBUG(file->ctx, "%s: Creation time changed\n", pakfire_file_get_path(file));
0eeac4a5
MT
1699 }
1700
1701 // Check modification time
37c5d873 1702 if (mtime != st->st_mtime) {
0eeac4a5
MT
1703 file->verify_status |= PAKFIRE_FILE_MTIME_CHANGED;
1704
c3e5b4e6 1705 CTX_DEBUG(file->ctx, "%s: Modification time changed\n", pakfire_file_get_path(file));
0eeac4a5
MT
1706 }
1707
1708 return 0;
1709}
1710
76011205 1711static int pakfire_file_verify_payload(struct pakfire_file* file, const struct stat* st) {
76011205
MT
1712 int r;
1713
c52e0148 1714 struct pakfire_digests computed_digests;
293881bc 1715 int digest_types = PAKFIRE_DIGEST_UNDEFINED;
76011205
MT
1716
1717 // Nothing to do for anything that isn't a regular file
1718 if (!S_ISREG(st->st_mode))
1719 return 0;
1720
1721 // Fast-path if size changed. The payload will have changed, too
1722 if (file->verify_status & PAKFIRE_FILE_SIZE_CHANGED) {
1723 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1724 return 0;
1725 }
1726
1727 // Check if this file has any digests at all
6b32db11 1728 digest_types = pakfire_digest_has_any(&file->digests);
76011205 1729
293881bc 1730 if (!digest_types) {
c3e5b4e6 1731 CTX_ERROR(file->ctx, "%s: No digests available\n", pakfire_file_get_path(file));
293881bc 1732 return 0;
76011205
MT
1733 }
1734
293881bc 1735 // Compute digests
1e76689a 1736 r = __pakfire_file_compute_digests(file, &computed_digests, digest_types);
293881bc 1737 if (r)
76011205 1738 goto ERROR;
76011205 1739
293881bc 1740 // Compare digests
a6d84de0 1741 r = pakfire_digests_compare(file->ctx, &file->digests, &computed_digests, digest_types);
76011205
MT
1742 if (r) {
1743 file->verify_status |= PAKFIRE_FILE_PAYLOAD_CHANGED;
1744
c3e5b4e6 1745 CTX_DEBUG(file->ctx, "%s: Digest(s) do not match\n", pakfire_file_get_path(file));
76011205
MT
1746 }
1747
76011205 1748ERROR:
76011205
MT
1749 return r;
1750}
1751
cf9bd6f4
MT
1752/*
1753 Verify the file - i.e. does the metadata match what is on disk?
1754*/
c0b051bb
MT
1755int pakfire_file_verify(struct pakfire_file* file, int* status) {
1756 struct stat st;
1757 int r;
1758
c3e5b4e6 1759 CTX_DEBUG(file->ctx, "Verifying %s...\n", pakfire_file_get_path(file));
37c5d873
MT
1760
1761 const char* abspath = pakfire_file_get_abspath(file);
cf9bd6f4 1762
c0b051bb 1763 // stat() the file
37c5d873 1764 r = lstat(abspath, &st);
c0b051bb
MT
1765 if (r) {
1766 // File does not exist
1767 if (errno == ENOENT) {
1768 file->verify_status |= PAKFIRE_FILE_NOENT;
1769 return 1;
1770 }
1771
1772 // Raise any other errors from stat()
1773 return r;
1774 }
1775
9e09e361
MT
1776 // Verify mode
1777 r = pakfire_file_verify_mode(file, &st);
1778 if (r)
1779 return r;
1780
c0b051bb
MT
1781 // Verify size
1782 r = pakfire_file_verify_size(file, &st);
1783 if (r)
1784 return r;
1785
1786 // Verify ownership
1787 r = pakfire_file_verify_ownership(file, &st);
1788 if (r)
1789 return r;
1790
0eeac4a5
MT
1791 // Verify timestamps
1792 r = pakfire_file_verify_timestamps(file, &st);
1793 if (r)
1794 return r;
1795
76011205
MT
1796 // Verify payload
1797 r = pakfire_file_verify_payload(file, &st);
1798 if (r)
1799 return r;
1800
cf9bd6f4
MT
1801 return 0;
1802}
c064d9ec
MT
1803
1804PAKFIRE_EXPORT int pakfire_file_matches(struct pakfire_file* file, const char* pattern) {
c064d9ec
MT
1805 // Don't match on no pattern
1806 if (!pattern)
1807 return 0;
1808
37c5d873 1809 const char* path = pakfire_file_get_path(file);
c064d9ec 1810
37c5d873 1811 return pakfire_path_match(pattern, path);
c064d9ec 1812}
2dcb43e7
MT
1813
1814/*
1815 ELF Stuff
1816*/
1817
1818static int pakfire_file_open_elf(struct pakfire_file* file,
1819 int (*callback)(struct pakfire_file* file, Elf* elf, void* data), void* data) {
1820 FILE* f = NULL;
1821 Elf* elf = NULL;
1822 int r;
1823
1824 // Don't run this for non-ELF files
1825 if (!pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
1826 errno = EINVAL;
1827 return 1;
1828 }
1829
1830 // Setup libelf
c3e5b4e6 1831 r = setup_libelf(file->ctx);
2dcb43e7
MT
1832 if (r)
1833 return r;
1834
1835 // Open the file
37c5d873 1836 f = pakfire_file_open(file);
2dcb43e7 1837 if (!f) {
c3e5b4e6 1838 CTX_ERROR(file->ctx, "Could not open %s: %m\n", pakfire_file_get_abspath(file));
2dcb43e7
MT
1839 return 1;
1840 }
1841
1842 // Parse the ELF header
1843 elf = elf_begin(fileno(f), ELF_C_READ, NULL);
1844 if (!elf) {
c3e5b4e6 1845 CTX_ERROR(file->ctx, "Could not open ELF file: %s\n", elf_errmsg(-1));
2dcb43e7
MT
1846 r = 1;
1847 goto ERROR;
1848 }
1849
1850 // Check if this is an ELF file
1851 switch (elf_kind(elf)) {
1852 case ELF_K_ELF:
1853 break;
1854
1855 default:
c3e5b4e6 1856 CTX_ERROR(file->ctx, "%s is not an ELF object\n", pakfire_file_get_path(file));
2dcb43e7
MT
1857 r = 1;
1858 goto ERROR;
1859 }
1860
1861 // Call the callback
1862 r = callback(file, elf, data);
1863
1864ERROR:
1865 if (elf)
1866 elf_end(elf);
1867 if (f)
1868 fclose(f);
1869
1870 return r;
1871}
1872
dbae97eb
MT
1873typedef int (*__pakfire_file_for_elf_section_callback)(
1874 struct pakfire_file* file,
1875 Elf* elf,
1876 const Elf_Scn* section,
1877 const GElf_Shdr* shdr,
1878 Elf_Data* data);
1879
1880static int pakfire_file_for_elf_section(struct pakfire_file* file,
1881 Elf* elf, const Elf64_Word type, __pakfire_file_for_elf_section_callback callback) {
1882 Elf_Scn* section = NULL;
1883 GElf_Shdr shdr;
1884 Elf_Data* data;
1885 int r;
1886
1887 // Walk through all sections
1888 for (;;) {
1889 section = elf_nextscn(elf, section);
1890 if (!section)
1891 break;
1892
1893 // Fetch the section header
1894 gelf_getshdr(section, &shdr);
1895
1896 // Skip sections that don't match
1897 if (type && shdr.sh_type != type)
1898 continue;
1899
1900 // Fetch the data
1901 data = elf_getdata(section, NULL);
1902
1903 // Call the callback
1904 r = callback(file, elf, section, &shdr, data);
1905 if (r)
1906 break;
1907 }
1908
1909 return r;
1910}
1911
e9d5d508
MT
1912static int pakfire_file_get_elf_section(struct pakfire_file* file,
1913 Elf* elf, const Elf64_Word type, Elf_Scn** section, GElf_Shdr* header, Elf_Data** data) {
1914 Elf_Scn* s = NULL;
1915
0ef20579
MT
1916 GElf_Shdr shdr;
1917
1918 // Walk through all sections
1919 for (;;) {
e9d5d508
MT
1920 s = elf_nextscn(elf, s);
1921 if (!s)
0ef20579
MT
1922 break;
1923
1924 // Fetch the section header
e9d5d508 1925 gelf_getshdr(s, &shdr);
0ef20579
MT
1926
1927 // Return any matching sections
e9d5d508
MT
1928 if (shdr.sh_type == type) {
1929 *section = s;
1930
1931 // Send header if requested
1932 if (header)
1933 gelf_getshdr(s, header);
1934
1935 // Send data if requested
1936 if (data)
1937 *data = elf_getdata(s, NULL);
1938
1939 return 0;
1940 }
0ef20579
MT
1941 }
1942
1943 // No section found
e9d5d508 1944 return 1;
0ef20579
MT
1945}
1946
8a385eff
MT
1947static int __pakfire_file_get_elf_type(struct pakfire_file* file, Elf* elf, void* data) {
1948 int* type = (int*)data;
1949 GElf_Ehdr ehdr;
1950
1951 // Fetch the ELF header
1952 if (!gelf_getehdr(elf, &ehdr)) {
c3e5b4e6 1953 CTX_ERROR(file->ctx, "Could not parse ELF header: %s\n", elf_errmsg(-1));
8a385eff
MT
1954 return 1;
1955 }
1956
1957 // Store the type
1958 *type = ehdr.e_type;
1959
1960 return 0;
1961}
1962
1963static int pakfire_file_get_elf_type(struct pakfire_file* file) {
1964 int type = ET_NONE;
1965 int r;
1966
1967 r = pakfire_file_open_elf(file, __pakfire_file_get_elf_type, &type);
1968 if (r)
1969 return -1;
1970
1971 return type;
1972}
1973
7b461c8b 1974static int pakfire_file_elf_dyn_walk(struct pakfire_file* file, Elf* elf,
4bea58f5
MT
1975 int (*callback)(struct pakfire_file* file,
1976 Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data),
7b461c8b
MT
1977 void* data) {
1978 Elf_Scn* dynamic = NULL;
1979 GElf_Shdr shdr;
1980 Elf_Data* elf_data = NULL;
4bea58f5 1981 GElf_Dyn dyn;
7b461c8b
MT
1982 int r;
1983
1984 // Find the dynamic linking information
1985 r = pakfire_file_get_elf_section(file, elf, SHT_DYNAMIC, &dynamic, &shdr, &elf_data);
1986 if (r) {
c3e5b4e6 1987 CTX_DEBUG(file->ctx, "%s does not have a dynamic section\n", pakfire_file_get_path(file));
a17d0a4b 1988 return 0;
7b461c8b
MT
1989 }
1990
7b461c8b
MT
1991 // Walk through all entries...
1992 for (unsigned int i = 0; ; i++) {
1993 // Fetch the next entry
1994 if (!gelf_getdyn(elf_data, i, &dyn))
1995 break;
1996
7b461c8b 1997 // Call the callback
4bea58f5 1998 r = callback(file, elf, &shdr, &dyn, data);
7b461c8b
MT
1999 if (r)
2000 return r;
2001 }
2002
2003 return 0;
2004}
2005
03a5ac95 2006static int __pakfire_file_check_debuginfo(struct pakfire_file* file, Elf* elf, void* data) {
e9d5d508
MT
2007 Elf_Scn* symtab = NULL;
2008 int r;
2009
0ef20579 2010 // Fetch the symbol table
e9d5d508 2011 r = pakfire_file_get_elf_section(file, elf, SHT_SYMTAB, &symtab, NULL, NULL);
8e8de3c1
MT
2012
2013 // Not found
e9d5d508 2014 if (r) {
c3e5b4e6 2015 CTX_DEBUG(file->ctx, "%s has no debug sections\n", pakfire_file_get_path(file));
8e8de3c1 2016
0ef20579
MT
2017 // Store the result
2018 file->issues |= PAKFIRE_FILE_MISSING_DEBUGINFO;
2019 }
8e8de3c1 2020
03a5ac95
MT
2021 return 0;
2022}
ec95c0c4 2023
03a5ac95 2024static int pakfire_file_check_debuginfo(struct pakfire_file* file) {
8a385eff
MT
2025 switch (pakfire_file_get_elf_type(file)) {
2026 // Do not check Relocatable Objects
2027 case ET_REL:
2028 return 0;
2029
2030 // Check everything else
2031 default:
2032 break;
2033 }
2034
03a5ac95 2035 return pakfire_file_open_elf(file, __pakfire_file_check_debuginfo, NULL);
8e8de3c1
MT
2036}
2037
28886e21 2038static int __pakfire_file_check_ssp(
2dcb43e7 2039 struct pakfire_file* file, Elf* elf, void* data) {
0ef20579
MT
2040 Elf_Scn* symtab = NULL;
2041 GElf_Shdr shdr;
2dcb43e7
MT
2042 Elf_Data* elf_data = NULL;
2043 GElf_Sym symbol;
2044 const char* name = NULL;
e9d5d508 2045 int r;
2dcb43e7 2046
0ef20579 2047 // Fetch the symbol table
e9d5d508
MT
2048 r = pakfire_file_get_elf_section(file, elf, SHT_SYMTAB, &symtab, &shdr, &elf_data);
2049 if (r) {
c3e5b4e6 2050 CTX_ERROR(file->ctx, "%s has no symbol table\n", pakfire_file_get_path(file));
0ef20579
MT
2051 return 1;
2052 }
2dcb43e7 2053
0ef20579
MT
2054 // Count any global functions
2055 size_t counter = 0;
2dcb43e7 2056
2dcb43e7 2057 // Walk through all symbols
0ef20579 2058 for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
2dcb43e7
MT
2059 gelf_getsym(elf_data, i, &symbol);
2060
2061 // Fetch the symbol name
0ef20579 2062 name = elf_strptr(elf, shdr.sh_link, symbol.st_name);
2dcb43e7
MT
2063
2064 // Skip empty section names
2065 if (!name || !*name)
2066 continue;
2067
f7f44921 2068 // Exit if there is a symbol called "__stack_chk_fail"
2dcb43e7 2069 if (pakfire_string_startswith(name, "__stack_chk_fail"))
f7f44921 2070 return 0;
0f7c90ef
MT
2071
2072 // Count any global functions
2073 if ((ELF64_ST_BIND(symbol.st_info) == STB_GLOBAL) &&
2074 (ELF64_ST_TYPE(symbol.st_info) == STT_FUNC))
2075 counter++;
2076 }
2077
2078 // We do not perform the check for libraries that do not contain any functions.
2079 // Some packages use shared libraries to provide data.
2080 if (!counter) {
c3e5b4e6 2081 CTX_DEBUG(file->ctx, "%s: File has no functions. Skipping SSP check.\n",
37c5d873 2082 pakfire_file_get_path(file));
0f7c90ef 2083 return 0;
2dcb43e7
MT
2084 }
2085
f7f44921 2086 // The file does not seem to have SSP enabled
d8acc837 2087 file->issues |= PAKFIRE_FILE_MISSING_SSP;
f7f44921 2088
2dcb43e7
MT
2089 return 0;
2090}
2091
28886e21 2092static int pakfire_file_check_ssp(struct pakfire_file* file) {
d7761941
MT
2093 // This check will be skipped for these files
2094 static const char* whitelist[] = {
2095 "/usr/lib64/libgcc_s.so.*",
2096 "/usr/lib64/libmvec.so.*",
2097 NULL,
2098 };
2099
fc35e5cc
MT
2100 // Do not perform this check for runtime linkers
2101 if (pakfire_file_matches_class(file, PAKFIRE_FILE_RUNTIME_LINKER))
2102 return 0;
2103
03a5ac95
MT
2104 // We cannot perform this check if we don't have debuginfo
2105 if (file->issues & PAKFIRE_FILE_MISSING_DEBUGINFO)
2106 return 0;
2107
d7761941
MT
2108 // Check if this file is whitelisted
2109 for (const char** path = whitelist; *path; path++) {
2110 if (pakfire_file_matches(file, *path)) {
c3e5b4e6 2111 CTX_DEBUG(file->ctx, "Skipping SSP check for whitelisted file %s\n",
d7761941
MT
2112 pakfire_file_get_path(file));
2113 return 0;
2114 }
2115 }
2116
28886e21 2117 return pakfire_file_open_elf(file, __pakfire_file_check_ssp, NULL);
2dcb43e7 2118}
2a67d72c 2119
28886e21 2120static int pakfire_file_check_pie(struct pakfire_file* file) {
8a385eff
MT
2121 switch (pakfire_file_get_elf_type(file)) {
2122 // Shared Object files are good
2a67d72c 2123 case ET_DYN:
22e696bd 2124 break;
2a67d72c 2125
8a385eff 2126 // Everything else is bad
2a67d72c 2127 default:
d8acc837 2128 file->issues |= PAKFIRE_FILE_MISSING_PIE;
22e696bd 2129 break;
2a67d72c 2130 }
22e696bd
MT
2131
2132 return 0;
f7f44921
MT
2133}
2134
28886e21 2135static int __pakfire_file_check_execstack(
dae43e1f
MT
2136 struct pakfire_file* file, Elf* elf, void* data) {
2137 GElf_Phdr phdr;
2138 int r;
2139
2140 size_t phnum = 0;
2141
2142 // Fetch the total numbers of program headers
2143 r = elf_getphdrnum(elf, &phnum);
2144 if (r) {
c3e5b4e6 2145 CTX_ERROR(file->ctx, "Could not fetch number of program headers: %s\n",
dae43e1f
MT
2146 elf_errmsg(-1));
2147 return 1;
2148 }
2149
2150 // Walk through all program headers
2151 for (unsigned int i = 0; i < phnum; i++) {
2152 if (!gelf_getphdr(elf, i, &phdr)) {
c3e5b4e6 2153 CTX_ERROR(file->ctx, "Could not parse program header: %s\n", elf_errmsg(-1));
dae43e1f
MT
2154 return 1;
2155 }
2156
2157 switch (phdr.p_type) {
2158 case PT_GNU_STACK:
c3e5b4e6 2159 CTX_DEBUG(file->ctx,
dae43e1f 2160 "%s: GNU_STACK flags: %c%c%c\n",
37c5d873 2161 pakfire_file_get_path(file),
dae43e1f
MT
2162 (phdr.p_flags & PF_R) ? 'R' : '-',
2163 (phdr.p_flags & PF_W) ? 'W' : '-',
2164 (phdr.p_flags & PF_X) ? 'X' : '-'
2165 );
2166
2167 // The stack cannot be writable and executable
2168 if ((phdr.p_flags & PF_W) && (phdr.p_flags & PF_X))
28886e21 2169 file->issues |= PAKFIRE_FILE_EXECSTACK;
dae43e1f
MT
2170
2171 // Done
2172 return 0;
2173
2174 default:
2175 break;
2176 }
2177 }
2178
2179 return 0;
2180}
2181
28886e21
MT
2182static int pakfire_file_check_execstack(struct pakfire_file* file) {
2183 return pakfire_file_open_elf(file, __pakfire_file_check_execstack, NULL);
dae43e1f
MT
2184}
2185
9a789d72
MT
2186static int __pakfire_file_process_bind_now(struct pakfire_file* file,
2187 Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) {
2188 int* has_bind_now = (int*)data;
2189
2190 switch (dyn->d_tag) {
2191 case DT_BIND_NOW:
2192 *has_bind_now = 1;
2193 break;
2194
2195 case DT_FLAGS:
2196 if (dyn->d_un.d_val & DF_BIND_NOW)
2197 *has_bind_now = 1;
2198 break;
2199
2200 case DT_FLAGS_1:
2201 if (dyn->d_un.d_val & DF_1_NOW)
2202 *has_bind_now = 1;
2203 break;
2204
2205 default:
2206 break;
2207 }
2208
2209 return 0;
2210}
2211
2212static int __pakfire_file_check_relro(
d265bed6 2213 struct pakfire_file* file, Elf* elf, void* data) {
9a789d72 2214 int has_bind_now = 0;
d265bed6
MT
2215 GElf_Phdr phdr;
2216 int r;
2217
9a789d72
MT
2218 // Check if we have BIND_NOW
2219 r = pakfire_file_elf_dyn_walk(file, elf,
2220 __pakfire_file_process_bind_now, &has_bind_now);
2221 if (r)
2222 return r;
d265bed6 2223
9a789d72
MT
2224 // We are not fully RELRO
2225 if (!has_bind_now) {
2226 file->issues |= PAKFIRE_FILE_NO_RELRO;
2227
2228 return 0;
d265bed6
MT
2229 }
2230
2231 // Walk through all program headers
9a789d72
MT
2232 for (unsigned int i = 0;; i++) {
2233 if (!gelf_getphdr(elf, i, &phdr))
2234 break;
d265bed6
MT
2235
2236 switch (phdr.p_type) {
2237 case PT_GNU_RELRO:
2238 return 0;
2239
2240 default:
2241 break;
2242 }
2243 }
2244
2245 // This file does not seem to have PT_GNU_RELRO set
4bea58f5 2246 file->issues |= PAKFIRE_FILE_NO_RELRO;
d265bed6
MT
2247
2248 return 0;
2249}
2250
28886e21 2251static int pakfire_file_check_relro(struct pakfire_file* file) {
4bea58f5 2252 return pakfire_file_open_elf(file, __pakfire_file_check_relro, NULL);
d265bed6
MT
2253}
2254
7b461c8b
MT
2255/*
2256 RPATH/RUNPATH
2257*/
2258static int __pakfire_file_process_runpath(struct pakfire_file* file,
4bea58f5
MT
2259 Elf* elf, const GElf_Shdr* shdr, const GElf_Dyn* dyn, void* data) {
2260 const char* value = NULL;
7b461c8b
MT
2261 const char* runpath = NULL;
2262 char buffer[PATH_MAX];
2263 char* p = NULL;
2264 int r;
2265
4bea58f5 2266 switch (dyn->d_tag) {
7b461c8b
MT
2267 case DT_RUNPATH:
2268 case DT_RPATH:
4bea58f5
MT
2269 // Fetch the value
2270 value = elf_strptr(elf, shdr->sh_link, dyn->d_un.d_val);
2271 if (!value)
2272 return 1;
2273
c3e5b4e6 2274 CTX_DEBUG(file->ctx, "%s has a RUNPATH: %s\n",
37c5d873 2275 pakfire_file_get_path(file), value);
7b461c8b
MT
2276
2277 // Copy the value into a buffer we can modify
2278 r = pakfire_string_set(buffer, value);
2279 if (r)
2280 return r;
2281
2282 // Split the value by :
2283 runpath = strtok_r(buffer, ":", &p);
2284
2285 // Iterate over all elements
2286 while (runpath) {
c3e5b4e6 2287 CTX_ERROR(file->ctx, "Checking RUNPATH %s\n", runpath);
7b461c8b
MT
2288
2289 // We do not allow any relative RUNPATHs
35ead2de
MT
2290 if (pakfire_path_match(runpath, "**/../**"))
2291 goto RUNPATH_DENIED;
7b461c8b 2292
35ead2de
MT
2293 // We allow /usr/lib64 as libtool seems to link it in quite a lot
2294 if (pakfire_path_match("/usr/lib64", runpath))
2295 goto RUNPATH_PERMITTED;
2296
2297 // We allow any subdirectories of /usr/lib64
2298 if (pakfire_path_match( "/usr/lib64/**", runpath))
2299 goto RUNPATH_PERMITTED;
2300
2301RUNPATH_DENIED:
2302 // If we make it here, this check has failed
2303 file->issues |= PAKFIRE_FILE_HAS_RUNPATH;
2304 break;
2305
2306RUNPATH_PERMITTED:
7b461c8b
MT
2307 // Move on to the next RUNPATH
2308 runpath = strtok_r(NULL, ":", &p);
2309 }
2310
2311 default:
2312 break;
2313 }
2314
2315 return 0;
2316}
2317
2318static int __pakfire_file_check_runpath(struct pakfire_file* file, Elf* elf, void* data) {
2319 return pakfire_file_elf_dyn_walk(file, elf, __pakfire_file_process_runpath, data);
2320}
2321
2322static int pakfire_file_check_runpath(struct pakfire_file* file) {
2323 return pakfire_file_open_elf(file, __pakfire_file_check_runpath, NULL);
2324}
d265bed6 2325
375a06e3
MT
2326static int pakfire_file_get_script_interpreter(struct pakfire_file* file, char** interpreter) {
2327 FILE* f = NULL;
2328 char shebang[1024];
2329 char* interp = NULL;
2330 char* p = NULL;
2331 int r;
2332
2333 // Check inputs
2334 if (!interpreter) {
2335 errno = EINVAL;
2336 return 1;
2337 }
2338
37c5d873
MT
2339 const mode_t mode = pakfire_file_get_mode(file);
2340
375a06e3 2341 // Only run for regular files
37c5d873 2342 if (!S_ISREG(mode))
375a06e3
MT
2343 return 0;
2344
2345 // Only run for executable files
2346 if (!pakfire_file_is_executable(file))
2347 return 0;
2348
37c5d873
MT
2349 const size_t size = pakfire_file_get_size(file);
2350
375a06e3 2351 // Nothing to do if the file is empty
37c5d873 2352 if (!size)
375a06e3
MT
2353 return 0;
2354
2355 // Open the file
2356 f = pakfire_file_open(file);
2357 if (!f) {
2358 r = 1;
2359 goto ERROR;
2360 }
2361
2362 // Fill the buffer with the first couple of bytes of the file
2363 ssize_t bytes_read = fread(shebang, 1, sizeof(shebang), f);
2364
2365 // Handle any reading errors
2366 if (bytes_read < 0) {
c3e5b4e6 2367 CTX_ERROR(file->ctx, "Could not read from file %s: %m\n",
37c5d873 2368 pakfire_file_get_path(file));
375a06e3
MT
2369 r = 1;
2370 goto ERROR;
2371 }
2372
2373 // We did not read enough data
2374 if (bytes_read < 2) {
2375 r = 1;
2376 goto ERROR;
2377 }
2378
2379 if (strncmp("#!", shebang, 2) == 0) {
c3e5b4e6 2380 CTX_DEBUG(file->ctx, "%s is a script\n", pakfire_file_get_path(file));
375a06e3
MT
2381
2382 // Find the end of the first line (to be able to perform string operations)
2383 p = memchr(shebang, '\n', sizeof(shebang));
2384 if (!p) {
c3e5b4e6 2385 CTX_ERROR(file->ctx, "%s: First line seems to be too long\n",
37c5d873 2386 pakfire_file_get_path(file));
375a06e3
MT
2387 errno = ENOBUFS;
2388 r = 1;
2389 goto ERROR;
2390 }
2391
2392 // Terminate the string
2393 *p = '\0';
2394
2395 // Find the beginning of the interpreter
2396 interp = shebang + 2;
2397
2398 // Consume any space between #! and the path
2399 while (*interp && isspace(*interp))
2400 interp++;
2401
2402 p = interp;
2403
2404 // Find the end of the command (cuts off any options)
2405 while (*p) {
2406 if (isspace(*p)) {
2407 *p = '\0';
2408 break;
2409 }
2410
2411 p++;
2412 }
2413
2414 // Copy the interpreter to the heap
2415 *interpreter = strdup(interp);
2416 }
2417
2418 // Success
2419 r = 0;
2420
2421ERROR:
2422 if (f)
2423 fclose(f);
2424
2425 return r;
2426}
2427
33d2d4df
MT
2428static int pakfire_file_fix_interpreter(struct pakfire_file* file, const char* interpreter) {
2429 FILE* f = NULL;
33d2d4df
MT
2430 int r;
2431
94a53bda 2432 char* buffer = NULL;
740a3668 2433 size_t l = 0;
94a53bda 2434 char* line = NULL;
33d2d4df 2435 size_t length = 0;
94a53bda 2436 char* p = NULL;
33d2d4df 2437
94a53bda
MT
2438 char command[PATH_MAX];
2439 const char* args = NULL;
2440
2441 const char* path = pakfire_file_get_path(file);
2442
c3e5b4e6 2443 CTX_DEBUG(file->ctx, "%s: Fixing interpreter %s\n", path, interpreter);
33d2d4df
MT
2444
2445 // Open the file
2446 f = pakfire_file_open(file);
2447 if (!f) {
c3e5b4e6 2448 CTX_ERROR(file->ctx, "Could not open %s: %m\n", path);
94a53bda 2449 r = -errno;
33d2d4df
MT
2450 goto ERROR;
2451 }
2452
94a53bda
MT
2453 for (unsigned int lineno = 0;; lineno++) {
2454 r = getline(&line, &length, f);
2455 if (r < 0) {
2456 // We finished reading when we reach the end
2457 if (feof(f))
2458 break;
33d2d4df 2459
c3e5b4e6 2460 CTX_ERROR(file->ctx, "Could not read line from %s: %m\n", path);
94a53bda
MT
2461 goto ERROR;
2462 }
33d2d4df 2463
94a53bda
MT
2464 switch (lineno) {
2465 case 0:
2466 // Check if the first line starts with #!
2467 if (!pakfire_string_startswith(line, "#!")) {
2468 r = -EINVAL;
2469 goto ERROR;
2470 }
33d2d4df 2471
94a53bda
MT
2472 // Remove the trailing newline
2473 pakfire_remove_trailing_newline(line);
33d2d4df 2474
94a53bda
MT
2475 // Start at the beginning of the line (after shebang)
2476 p = line + 2;
33d2d4df 2477
94a53bda
MT
2478 // Consume any whitespace
2479 while (*p && isspace(*p))
2480 p++;
33d2d4df 2481
94a53bda
MT
2482 // Consume the old interpreter
2483 while (*p && !isspace(*p))
2484 p++;
33d2d4df 2485
94a53bda
MT
2486 // Consume any whitespace
2487 while (*p && isspace(*p))
2488 p++;
33d2d4df 2489
94a53bda
MT
2490 // The actual interpreter begins here
2491 interpreter = p;
33d2d4df 2492
94a53bda
MT
2493 // Find the end of the interpreter
2494 while (*p && !isspace(*p))
2495 p++;
33d2d4df 2496
94a53bda
MT
2497 // Terminate the interpreter
2498 *p++ = '\0';
33d2d4df 2499
94a53bda
MT
2500 // Any arguments begin here (if we didn't consume the entire string, yet)
2501 if (*p)
2502 args = p;
33d2d4df 2503
c3e5b4e6 2504 CTX_DEBUG(file->ctx, "%s: Found command %s (%s)\n", path, interpreter, args);
33d2d4df 2505
94a53bda
MT
2506 // Find the real path
2507 r = pakfire_which(file->pakfire, command, interpreter);
2508 if (r) {
c3e5b4e6 2509 CTX_ERROR(file->ctx, "%s: Could not resolve %s: %m\n", path, interpreter);
94a53bda
MT
2510 goto ERROR;
2511 }
33d2d4df 2512
94a53bda
MT
2513 // If we could not resolve the command, this file has an invalid interpreter
2514 if (!*command) {
c3e5b4e6 2515 CTX_ERROR(file->ctx, "%s: Could not find path for command %s\n", path, interpreter);
33d2d4df 2516
94a53bda
MT
2517 file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER;
2518 r = 0;
2519 goto ERROR;
2520 }
33d2d4df 2521
94a53bda
MT
2522 // Write the new first line to the buffer
2523 r = asprintf(&buffer, "#!%s", command);
2524 if (r < 0)
2525 goto ERROR;
33d2d4df 2526
94a53bda
MT
2527 // Append arguments if any
2528 if (args) {
2529 r = asprintf(&buffer, "%s %s", buffer, args);
2530 if (r < 0)
2531 goto ERROR;
2532 }
2533
2534 // Terminate the line
2535 r = asprintf(&buffer, "%s\n", buffer);
2536 if (r < 0)
2537 goto ERROR;
2538
740a3668
MT
2539 // Store length of the buffer
2540 l = r;
2541
94a53bda
MT
2542 // Process the next line
2543 break;
2544
2545 default:
2546 // Append the line to the buffer
2547 r = asprintf(&buffer, "%s%s", buffer, line);
2548 if (r < 0)
2549 goto ERROR;
740a3668
MT
2550
2551 // Store length of the buffer
2552 l = r;
2553
94a53bda
MT
2554 break;
2555 }
33d2d4df
MT
2556 }
2557
94a53bda
MT
2558 // Go back to the beginning of the file
2559 rewind(f);
33d2d4df 2560
740a3668
MT
2561 // Truncate the existing content
2562 r = ftruncate(fileno(f), 0);
2563 if (r) {
c3e5b4e6 2564 CTX_ERROR(file->ctx, "Could not truncate %s: %m\n", path);
740a3668
MT
2565 r = -errno;
2566 goto ERROR;
2567 }
2568
94a53bda
MT
2569 // Write back the buffer
2570 if (buffer) {
740a3668
MT
2571 size_t bytes_written = fwrite(buffer, 1, l, f);
2572 if (bytes_written < l) {
c3e5b4e6 2573 CTX_ERROR(file->ctx, "%s: Could not write the payload: %m\n", path);
94a53bda
MT
2574 r = -errno;
2575 goto ERROR;
2576 }
33d2d4df
MT
2577 }
2578
2579 // Success!
2580 r = 0;
2581
2582ERROR:
94a53bda
MT
2583 if (buffer)
2584 free(buffer);
2585 if (line)
2586 free(line);
33d2d4df
MT
2587 if (f)
2588 fclose(f);
2589
2590 return r;
2591}
2592
375a06e3
MT
2593static int pakfire_file_check_interpreter(struct pakfire_file* file) {
2594 char* interpreter = NULL;
2595 int r;
2596
2597 // Fetch the script interpreter
2598 r = pakfire_file_get_script_interpreter(file, &interpreter);
2599 if (r)
2600 return r;
2601
2602 // If there is no result, the file is not a script
2603 if (!interpreter)
2604 return 0;
2605
c3e5b4e6 2606 CTX_DEBUG(file->ctx, "%s: Interpreter: %s\n",
37c5d873 2607 pakfire_file_get_path(file), interpreter);
375a06e3
MT
2608
2609 // Paths must be absolute
2610 if (*interpreter != '/')
2611 file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER;
2612
2613 // Check if the interpreter is in /usr/local
2614 else if (pakfire_string_startswith(interpreter, "/usr/local/"))
2615 file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER;
2616
33d2d4df
MT
2617 // We don't support "env", but will automatically fix it
2618 else if (strcmp(interpreter, "/usr/bin/env") == 0) {
2619 r = pakfire_file_fix_interpreter(file, interpreter);
2620 if (r) {
c3e5b4e6 2621 CTX_ERROR(file->ctx, "%s: Could not fix interpreter: %m\n",
37c5d873 2622 pakfire_file_get_path(file));
33d2d4df
MT
2623 goto ERROR;
2624 }
2625 }
375a06e3 2626
33d2d4df 2627ERROR:
375a06e3
MT
2628 if (interpreter)
2629 free(interpreter);
2630
33d2d4df 2631 return r;
375a06e3
MT
2632}
2633
691091ad
MT
2634static int pakfire_file_check_capabilities(struct pakfire_file* file) {
2635 // Files cannot have capabilities but not be executable
2636 if (!pakfire_file_is_executable(file) && pakfire_file_has_caps(file))
2637 file->issues |= PAKFIRE_FILE_INVALID_CAPS;
2638
2639 return 0;
2640}
2641
dbae97eb
MT
2642static uint32_t read_4_bytes(const int endianess, const char* data) {
2643 uint32_t bytes = 0;
2644
2645 switch (endianess) {
2646 // Litte Endian
2647 case ELFDATA2LSB:
2648 bytes = data[0] |
2649 ((uint32_t)data[1] << 8) |
2650 ((uint32_t)data[2] << 16) |
2651 ((uint32_t)data[3] << 24);
2652 break;
2653
2654 // Big endian
2655 case ELFDATA2MSB:
2656 bytes = data[3] |
2657 ((uint32_t)data[2] << 8) |
2658 ((uint32_t)data[1] << 16) |
2659 ((uint32_t)data[0] << 24);
2660 break;
2661 }
2662
2663 return bytes;
2664}
2665
2666static int __pakfire_file_check_cf_protection_x86(struct pakfire_file* file,
2667 const int endianess, const uint32_t type, const char* payload) {
2668 switch (type) {
2669 case GNU_PROPERTY_X86_FEATURE_1_AND:
2670 break;
2671
2672 // XXX what should we do in this case? Just ignore?
2673 default:
2674 return 0;
2675 }
2676
2677 uint32_t property = read_4_bytes(endianess, payload);
2678
2679 // Check for IBT
2680 if (!(property & GNU_PROPERTY_X86_FEATURE_1_IBT)) {
c3e5b4e6 2681 CTX_DEBUG(file->ctx, "%s: IBT property is not enabled\n",
37c5d873 2682 pakfire_file_get_path(file));
dbae97eb
MT
2683
2684 // XXX TODO Actually modify any flags
2685
2686 return 0;
2687 }
2688
2689 // Check for Shadow Stack
2690 if (!(property & GNU_PROPERTY_X86_FEATURE_1_SHSTK)) {
c3e5b4e6 2691 CTX_DEBUG(file->ctx, "%s: Shadow Stack is not enabled\n",
37c5d873 2692 pakfire_file_get_path(file));
dbae97eb
MT
2693
2694 // XXX TODO Actually modify any flags
2695
2696 return 0;
2697 }
2698
2699 return 0;
2700}
2701
32f68461
MT
2702static int __pakfire_file_check_cf_protection_aarch64(struct pakfire_file* file,
2703 const int endianess, const uint32_t type, const char* payload) {
2704 # warning TODO
2705 return 0;
2706}
2707
dbae97eb
MT
2708static int pakfire_file_check_cf_protection_callback(struct pakfire_file* file,
2709 Elf* elf, const Elf_Scn* section, const GElf_Shdr* shdr, Elf_Data* data) {
2710 GElf_Ehdr ehdr = {};
2711 GElf_Nhdr nhdr = {};
2712 size_t offset = 0;
2713 size_t offset_name = 0;
2714 size_t offset_data = 0;
2715 int r;
2716
2717 // Fetch the ELF header
2718 if (!gelf_getehdr(elf, &ehdr)) {
c3e5b4e6 2719 CTX_ERROR(file->ctx, "Could not fetch the ELF header for %s: %m\n",
37c5d873 2720 pakfire_file_get_path(file));
dbae97eb
MT
2721 return 1;
2722 }
2723
2724 // Fetch the endianess
2725 const int endianess = ehdr.e_ident[EI_DATA];
2726
2727 // Fetch the .note header
2728 offset = gelf_getnote(data, offset, &nhdr, &offset_name, &offset_data);
2729 if (!offset) {
c3e5b4e6 2730 CTX_ERROR(file->ctx, "Could not read note section: %m\n");
dbae97eb
MT
2731 return 1;
2732 }
2733
2734 switch (nhdr.n_type) {
2735 case NT_GNU_PROPERTY_TYPE_0:
2736 break;
2737
2738 // We are not interested in this here
2739 default:
2740 return 0;
2741 }
2742
2743#if 0
2744 // Check if the name starts with "GNU"
2745 if (nhdr.n_namesz == sizeof(ELF_NOTE_GNU) {
2746 if (strcmp(data->d_buf + offset_name, ELF_NOTE_GNU) == 0)
2747 break;
2748 }
2749#endif
2750
2751 size_t length = nhdr.n_descsz;
2752
2753 // XXX Check if this is aligned to 8 bytes or 4 bytes on 32 bit
2754
2755 const char* payload = (const char*)data->d_buf + offset_data;
2756
2757 while (length) {
2758 // Read the type and size of the .note section
2759 const uint32_t type = read_4_bytes(endianess, payload);
2760 const uint32_t size = read_4_bytes(endianess, payload + 4);
2761
2762 // Skip header
2763 length -= sizeof(type) + sizeof(size);
2764 payload += sizeof(type) + sizeof(size);
2765
2766 if (length < size) {
c3e5b4e6 2767 CTX_ERROR(file->ctx, "GNU Property note has an incorrect format\n");
dbae97eb
MT
2768 return 1;
2769 }
2770
2771 switch (ehdr.e_machine) {
dbae97eb
MT
2772 case EM_AARCH64:
2773 r = __pakfire_file_check_cf_protection_aarch64(file, endianess, type, payload);
2774 if (r)
2775 return r;
2776 break;
dbae97eb
MT
2777
2778 case EM_X86_64:
2779 r = __pakfire_file_check_cf_protection_x86(file, endianess, type, payload);
2780 if (r)
2781 return r;
2782 break;
2783
2784 default:
c3e5b4e6 2785 CTX_ERROR(file->ctx, "Unsupported ELF type (%d)\n", ehdr.e_machine);
dbae97eb
MT
2786 return 1;
2787 }
2788
2789 // Skip data part
2790 length -= (size + 7) & ~7;
2791 payload += (size + 7) & ~7;
2792 }
2793
2794 return 0;
2795}
2796
2797static int __pakfire_file_check_cf_protection(struct pakfire_file* file, Elf* elf, void* data) {
2798 return pakfire_file_for_elf_section(file, elf, SHT_NOTE, pakfire_file_check_cf_protection_callback);
2799}
2800
2801static int pakfire_file_check_cf_protection(struct pakfire_file* file) {
2802 return pakfire_file_open_elf(file, __pakfire_file_check_cf_protection, NULL);
2803}
691091ad 2804
28886e21 2805int pakfire_file_check(struct pakfire_file* file, int* issues) {
f7f44921
MT
2806 int r;
2807
2808 // Return previous result if this has been run before
28886e21 2809 if (!file->check_done) {
6d9dd7bc
MT
2810 // Perform FHS check
2811 r = pakfire_fhs_check_file(file->pakfire, file);
2812 if (r)
28886e21 2813 file->issues |= PAKFIRE_FILE_FHS_ERROR;
8a385eff 2814
375a06e3
MT
2815 // Perform interpreter check
2816 r = pakfire_file_check_interpreter(file);
2817 if (r)
2818 return r;
2819
691091ad
MT
2820 // Perform capability check
2821 r = pakfire_file_check_capabilities(file);
2822 if (r)
2823 return r;
2824
6d9dd7bc
MT
2825 // Do not perform the following checks on firmware
2826 if (pakfire_file_matches_class(file, PAKFIRE_FILE_FIRMWARE))
2827 goto DONE;
8a385eff 2828
6d9dd7bc
MT
2829 // Run these checks only for ELF files
2830 if (pakfire_file_matches_class(file, PAKFIRE_FILE_ELF)) {
2831 switch (pakfire_file_get_elf_type(file)) {
2832 // Do not check Relocatable Objects
2833 case ET_REL:
2834 goto DONE;
2835
2836 // Check everything else
2837 default:
2838 break;
2839 }
f7f44921 2840
03a5ac95
MT
2841 // Check if the file has debug info
2842 r = pakfire_file_check_debuginfo(file);
2843 if (r)
2844 return r;
2845
6d9dd7bc 2846 // Check for SSP
28886e21 2847 r = pakfire_file_check_ssp(file);
6d9dd7bc
MT
2848 if (r)
2849 return r;
f7f44921 2850
6d9dd7bc 2851 // Check for PIE
28886e21 2852 r = pakfire_file_check_pie(file);
6d9dd7bc
MT
2853 if (r)
2854 return r;
dae43e1f 2855
6d9dd7bc 2856 // Check for executable stacks
28886e21 2857 r = pakfire_file_check_execstack(file);
6d9dd7bc
MT
2858 if (r)
2859 return r;
2860
2861 // Check for RELRO
28886e21 2862 r = pakfire_file_check_relro(file);
6d9dd7bc
MT
2863 if (r)
2864 return r;
7b461c8b
MT
2865
2866 // Check for RUNPATH
2867 r = pakfire_file_check_runpath(file);
2868 if (r)
2869 return r;
dbae97eb
MT
2870
2871 // Check CF protection
2872 r = pakfire_file_check_cf_protection(file);
2873 if (r)
2874 return r;
6d9dd7bc 2875 }
d265bed6 2876
8a385eff 2877DONE:
f7f44921 2878 // All checks done
28886e21 2879 file->check_done = 1;
f7f44921
MT
2880 }
2881
2882 // Return any issues
2883 if (issues)
28886e21 2884 *issues = file->issues;
f7f44921
MT
2885
2886 return 0;
2a67d72c 2887}