1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2014 Pakfire development team #
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. #
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. #
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/>. #
19 #############################################################################*/
25 #include <linux/limits.h>
29 #include <sys/types.h>
32 #include <archive_entry.h>
36 #include <pakfire/constants.h>
37 #include <pakfire/digest.h>
38 #include <pakfire/fhs.h>
39 #include <pakfire/file.h>
40 #include <pakfire/logging.h>
41 #include <pakfire/pakfire.h>
42 #include <pakfire/private.h>
43 #include <pakfire/string.h>
44 #include <pakfire/util.h>
46 enum pakfire_file_verification_status
{
47 PAKFIRE_FILE_NOENT
= (1 << 0),
48 PAKFIRE_FILE_TYPE_CHANGED
= (1 << 1),
49 PAKFIRE_FILE_PERMISSIONS_CHANGED
= (1 << 2),
50 PAKFIRE_FILE_DEV_CHANGED
= (1 << 3),
51 PAKFIRE_FILE_SIZE_CHANGED
= (1 << 4),
52 PAKFIRE_FILE_OWNER_CHANGED
= (1 << 5),
53 PAKFIRE_FILE_GROUP_CHANGED
= (1 << 6),
54 PAKFIRE_FILE_CTIME_CHANGED
= (1 << 7),
55 PAKFIRE_FILE_MTIME_CHANGED
= (1 << 8),
56 PAKFIRE_FILE_PAYLOAD_CHANGED
= (1 << 9),
60 struct pakfire
* pakfire
;
66 // The absolute path in the file system
67 char abspath
[PATH_MAX
];
70 char uname
[LOGIN_NAME_MAX
];
71 char gname
[LOGIN_NAME_MAX
];
80 char hardlink
[PATH_MAX
];
81 char symlink
[PATH_MAX
];
84 struct pakfire_digests digests
;
87 char mimetype
[NAME_MAX
];
92 // Verification Status
97 int hardening_check_done
:1;
99 #warning TODO capabilities, data
104 static int pakfire_file_from_archive_entry(struct pakfire_file
* file
, struct archive_entry
* entry
) {
106 const char* path
= NULL
;
107 const char* attr
= NULL
;
108 const void* value
= NULL
;
113 path
= archive_entry_sourcepath(entry
);
115 // Make path absolute
116 path
= pakfire_path_abspath(path
);
123 r
= pakfire_file_set_abspath(file
, path
);
125 ERROR(file
->pakfire
, "Could not set abspath: %m\n");
131 path
= archive_entry_pathname(entry
);
133 r
= pakfire_file_set_path(file
, path
);
135 ERROR(file
->pakfire
, "Could not set path: %m\n");
141 pakfire_file_set_hardlink(file
, archive_entry_hardlink(entry
));
142 pakfire_file_set_symlink(file
, archive_entry_symlink(entry
));
144 pakfire_file_set_nlink(file
, archive_entry_nlink(entry
));
145 pakfire_file_set_inode(file
, archive_entry_ino64(entry
));
146 pakfire_file_set_dev(file
, archive_entry_dev(entry
));
149 pakfire_file_set_size(file
, archive_entry_size(entry
));
152 pakfire_file_set_mode(file
, archive_entry_mode(entry
));
155 if (archive_entry_dev_is_set(entry
))
156 pakfire_file_set_dev(file
, archive_entry_dev(entry
));
159 pakfire_file_set_uname(file
, archive_entry_uname(entry
));
162 pakfire_file_set_gname(file
, archive_entry_gname(entry
));
165 pakfire_file_set_ctime(file
, archive_entry_ctime(entry
));
166 pakfire_file_set_mtime(file
, archive_entry_mtime(entry
));
168 // Reset iterating over extended attributes
169 archive_entry_xattr_reset(entry
);
171 // Read any extended attributes
172 while (archive_entry_xattr_next(entry
, &attr
, &value
, &size
) == ARCHIVE_OK
) {
174 if (strcmp(attr
, "PAKFIRE.config") == 0) {
175 r
= pakfire_file_set_flags(file
, PAKFIRE_FILE_CONFIG
);
180 } else if (strcmp(attr
, "PAKFIRE.mimetype") == 0) {
181 // Copy the value into a NULL-terminated buffer
182 r
= asprintf(&buffer
, "%.*s", (int)size
, (const char*)value
);
187 r
= pakfire_file_set_mimetype(file
, buffer
);
192 } else if (strcmp(attr
, "PAKFIRE.digests.sha3_512") == 0) {
193 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA3_512
, value
, size
);
198 } else if (strcmp(attr
, "PAKFIRE.digests.sha3_256") == 0) {
199 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA3_256
, value
, size
);
203 // Digest: BLAKE2b512
204 } else if (strcmp(attr
, "PAKFIRE.digests.blake2b512") == 0) {
205 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_BLAKE2B512
, value
, size
);
209 // Digest: BLAKE2s256
210 } else if (strcmp(attr
, "PAKFIRE.digests.blake2s256") == 0) {
211 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_BLAKE2S256
, value
, size
);
216 } else if (strcmp(attr
, "PAKFIRE.digests.sha2_512") == 0) {
217 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA2_512
, value
, size
);
222 } else if (strcmp(attr
, "PAKFIRE.digests.sha2_256") == 0) {
223 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA2_256
, value
, size
);
228 DEBUG(file
->pakfire
, "Received an unknown extended attribute: %s\n", attr
);
239 PAKFIRE_EXPORT
int pakfire_file_create(struct pakfire_file
** file
, struct pakfire
* pakfire
) {
240 struct pakfire_file
* f
= calloc(1, sizeof(*f
));
244 // Store reference to Pakfire
245 f
->pakfire
= pakfire_ref(pakfire
);
247 // Initialize reference counter
254 int pakfire_file_create_from_path(struct pakfire_file
** file
,
255 struct pakfire
* pakfire
, const char* path
) {
256 struct archive
* reader
= NULL
;
257 struct archive_entry
* entry
= NULL
;
261 reader
= pakfire_make_archive_disk_reader(pakfire
, 0);
265 // Allocate a new archive entry
266 entry
= archive_entry_new();
271 archive_entry_copy_sourcepath(entry
, path
);
273 // Read all file attributes from disk
274 r
= archive_read_disk_entry_from_file(reader
, entry
, -1, NULL
);
276 ERROR(pakfire
, "Could not read from %s: %m\n", path
);
281 r
= pakfire_file_create_from_archive_entry(file
, pakfire
, entry
);
287 ERROR(pakfire
, "Could not create file from path %s: %m\n", path
);
289 archive_entry_free(entry
);
291 archive_read_free(reader
);
296 int pakfire_file_create_from_archive_entry(struct pakfire_file
** file
, struct pakfire
* pakfire
,
297 struct archive_entry
* entry
) {
298 int r
= pakfire_file_create(file
, pakfire
);
302 // Copy archive entry
303 r
= pakfire_file_from_archive_entry(*file
, entry
);
310 pakfire_file_unref(*file
);
316 struct archive_entry
* pakfire_file_archive_entry(struct pakfire_file
* file
, int digest_types
) {
317 const char* path
= NULL
;
320 struct archive_entry
* entry
= archive_entry_new();
322 ERROR(file
->pakfire
, "Could not allocate archive entry: %m\n");
327 archive_entry_copy_sourcepath(entry
, file
->abspath
);
330 path
= pakfire_file_get_path(file
);
331 if (path
&& *path
== '/') {
332 archive_entry_copy_pathname(entry
, path
+ 1);
337 archive_entry_set_hardlink(entry
, file
->hardlink
);
339 archive_entry_set_symlink(entry
, file
->symlink
);
341 archive_entry_set_nlink(entry
, pakfire_file_get_nlink(file
));
342 archive_entry_set_ino64(entry
, pakfire_file_get_inode(file
));
343 archive_entry_set_dev(entry
, pakfire_file_get_dev(file
));
346 archive_entry_set_size(entry
, pakfire_file_get_size(file
));
349 archive_entry_set_mode(entry
, pakfire_file_get_mode(file
));
352 archive_entry_set_uname(entry
, pakfire_file_get_uname(file
));
355 archive_entry_set_gname(entry
, pakfire_file_get_gname(file
));
358 archive_entry_set_ctime(entry
, pakfire_file_get_ctime(file
), 0);
359 archive_entry_set_mtime(entry
, pakfire_file_get_mtime(file
), 0);
362 if (pakfire_file_has_flag(file
, PAKFIRE_FILE_CONFIG
)) {
363 archive_entry_xattr_add_entry(entry
,
364 "PAKFIRE.config", "1", strlen("1"));
368 const char* mimetype
= pakfire_file_get_mimetype(file
);
370 archive_entry_xattr_add_entry(entry
,
371 "PAKFIRE.mimetype", mimetype
, strlen(mimetype
));
374 // Compute any required file digests
375 r
= pakfire_file_compute_digests(file
, digest_types
);
382 if ((digest_types
&& PAKFIRE_DIGEST_SHA3_512
)
383 && pakfire_digest_set(file
->digests
.sha3_512
))
384 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha3_512",
385 file
->digests
.sha3_512
, sizeof(file
->digests
.sha3_512
));
388 if ((digest_types
&& PAKFIRE_DIGEST_SHA3_256
) &&
389 pakfire_digest_set(file
->digests
.sha3_256
))
390 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha3_256",
391 file
->digests
.sha3_256
, sizeof(file
->digests
.sha3_256
));
394 if ((digest_types
&& PAKFIRE_DIGEST_BLAKE2B512
) &&
395 pakfire_digest_set(file
->digests
.blake2b512
))
396 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.blake2b512",
397 file
->digests
.blake2b512
, sizeof(file
->digests
.blake2b512
));
400 if ((digest_types
&& PAKFIRE_DIGEST_BLAKE2S256
) &&
401 pakfire_digest_set(file
->digests
.blake2s256
))
402 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.blake2s256",
403 file
->digests
.blake2s256
, sizeof(file
->digests
.blake2s256
));
406 if ((digest_types
&& PAKFIRE_DIGEST_SHA2_512
) &&
407 pakfire_digest_set(file
->digests
.sha2_512
))
408 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha2_512",
409 file
->digests
.sha2_512
, sizeof(file
->digests
.sha2_512
));
412 if ((digest_types
&& PAKFIRE_DIGEST_SHA2_512
) &&
413 pakfire_digest_set(file
->digests
.sha2_256
))
414 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha2_256",
415 file
->digests
.sha2_256
, sizeof(file
->digests
.sha2_256
));
421 archive_entry_free(entry
);
426 static void pakfire_file_free(struct pakfire_file
* file
) {
427 pakfire_unref(file
->pakfire
);
431 PAKFIRE_EXPORT
struct pakfire_file
* pakfire_file_ref(struct pakfire_file
* file
) {
437 PAKFIRE_EXPORT
struct pakfire_file
* pakfire_file_unref(struct pakfire_file
* file
) {
438 if (--file
->nrefs
> 0)
441 pakfire_file_free(file
);
449 int pakfire_file_has_flag(struct pakfire_file
* file
, int flag
) {
450 return file
->flags
& flag
;
453 int pakfire_file_set_flags(struct pakfire_file
* file
, int flag
) {
459 #define pakfire_file_strmode(file, buffer) \
460 __pakfire_file_strmode(file, buffer, sizeof(buffer))
462 static int __pakfire_file_strmode(struct pakfire_file
* file
, char* s
, const size_t length
) {
465 static const mode_t permbits
[] = {
477 const mode_t mode
= pakfire_file_get_mode(file
);
478 const mode_t type
= pakfire_file_get_type(file
);
480 // Set some default string
481 r
= __pakfire_string_set(s
, length
, "?rwxrwxrwx ");
515 if (*file
->hardlink
) {
521 for (unsigned int i
= 0; i
< 9; i
++) {
522 if (mode
& permbits
[i
])
528 if (mode
& S_ISUID
) {
535 if (mode
& S_ISGID
) {
542 if (mode
& S_ISVTX
) {
557 char* pakfire_file_dump(struct pakfire_file
* file
, int flags
) {
565 if (flags
& PAKFIRE_FILE_DUMP_MODE
) {
566 r
= pakfire_file_strmode(file
, mode
);
570 r
= asprintf(&buffer
, "%s %s", buffer
, mode
);
576 if (flags
& PAKFIRE_FILE_DUMP_OWNERSHIP
) {
577 r
= asprintf(&buffer
, "%s %s/%s", buffer
, file
->uname
, file
->gname
);
583 if (flags
& PAKFIRE_FILE_DUMP_SIZE
) {
584 r
= asprintf(&buffer
, "%s %8zu", buffer
, file
->st
.st_size
);
590 if (flags
& PAKFIRE_FILE_DUMP_TIME
) {
591 r
= pakfire_strftime(time
, "%Y-%m-%d %H:%M", file
->st
.st_ctime
);
595 r
= asprintf(&buffer
, "%s %s", buffer
, time
);
601 r
= asprintf(&buffer
, "%s %s", buffer
, file
->path
);
605 // Append symlink target
606 if (flags
& PAKFIRE_FILE_DUMP_LINK_TARGETS
) {
607 switch (pakfire_file_get_type(file
)) {
609 r
= asprintf(&buffer
, "%s -> %s", buffer
, file
->symlink
);
619 if (flags
& PAKFIRE_FILE_DUMP_HARDENING
) {
620 if (file
->hardening_issues
& PAKFIRE_FILE_FHS_ERROR
) {
621 r
= asprintf(&buffer
, "%s [FHS-ERROR]", buffer
);
626 if (pakfire_file_matches_class(file
, PAKFIRE_FILE_ELF
)) {
627 // Stack-smashing Protection
628 if (file
->hardening_issues
& PAKFIRE_FILE_NO_SSP
) {
629 r
= asprintf(&buffer
, "%s [NO-SSP]", buffer
);
634 // Position-independent Executable
635 if (file
->hardening_issues
& PAKFIRE_FILE_NO_PIE
) {
636 r
= asprintf(&buffer
, "%s [NO-PIE]", buffer
);
642 if (file
->hardening_issues
& PAKFIRE_FILE_EXECSTACK
) {
643 r
= asprintf(&buffer
, "%s [EXECSTACK]", buffer
);
648 // Not Partially RELRO
649 if (file
->hardening_issues
& PAKFIRE_FILE_NO_PARTIALLY_RELRO
) {
650 r
= asprintf(&buffer
, "%s [NO-PART-RELRO]", buffer
);
666 PAKFIRE_EXPORT
int pakfire_file_cmp(struct pakfire_file
* file1
, struct pakfire_file
* file2
) {
667 const char* path1
= pakfire_file_get_path(file1
);
668 const char* path2
= pakfire_file_get_path(file2
);
670 return strcmp(path1
, path2
);
673 const char* pakfire_file_get_abspath(struct pakfire_file
* file
) {
674 return file
->abspath
;
677 int pakfire_file_set_abspath(struct pakfire_file
* file
, const char* path
) {
680 // Check if path is set and absolute
681 if (!path
|| *path
!= '/') {
687 r
= pakfire_string_set(file
->abspath
, path
);
691 // Store path if it isn't set, yet
693 r
= pakfire_file_set_path(file
, path
);
701 ERROR(file
->pakfire
, "Could not set abspath '%s': %m\n", path
);
705 PAKFIRE_EXPORT
const char* pakfire_file_get_path(struct pakfire_file
* file
) {
709 PAKFIRE_EXPORT
int pakfire_file_set_path(struct pakfire_file
* file
, const char* path
) {
712 // Check if path is set
718 // Strip any leading dots from paths
719 if (pakfire_string_startswith(path
, "./"))
723 // Just store the path if it is absolute
725 r
= pakfire_string_set(file
->path
, path
);
730 // Handle relative paths
732 r
= pakfire_string_format(file
->path
, "/%s", path
);
738 // Set abspath if it isn't set, yet
739 if (!*file
->abspath
) {
740 r
= pakfire_file_set_abspath(file
, file
->path
);
748 ERROR(file
->pakfire
, "Could not set path '%s': %m\n", path
);
752 PAKFIRE_EXPORT
const char* pakfire_file_get_hardlink(struct pakfire_file
* file
) {
753 if (!*file
->hardlink
)
756 return file
->hardlink
;
759 PAKFIRE_EXPORT
void pakfire_file_set_hardlink(struct pakfire_file
* file
, const char* link
) {
760 pakfire_string_set(file
->hardlink
, link
);
763 PAKFIRE_EXPORT
const char* pakfire_file_get_symlink(struct pakfire_file
* file
) {
767 return file
->symlink
;
770 PAKFIRE_EXPORT
void pakfire_file_set_symlink(struct pakfire_file
* file
, const char* link
) {
771 pakfire_string_set(file
->symlink
, link
);
774 PAKFIRE_EXPORT nlink_t
pakfire_file_get_nlink(struct pakfire_file
* file
) {
775 return file
->st
.st_nlink
;
778 PAKFIRE_EXPORT
void pakfire_file_set_nlink(struct pakfire_file
* file
, const nlink_t nlink
) {
779 file
->st
.st_nlink
= nlink
;
782 PAKFIRE_EXPORT ino_t
pakfire_file_get_inode(struct pakfire_file
* file
) {
783 return file
->st
.st_ino
;
786 PAKFIRE_EXPORT
void pakfire_file_set_inode(struct pakfire_file
* file
, const ino_t ino
) {
787 file
->st
.st_ino
= ino
;
790 PAKFIRE_EXPORT dev_t
pakfire_file_get_dev(struct pakfire_file
* file
) {
791 return file
->st
.st_dev
;
794 PAKFIRE_EXPORT
void pakfire_file_set_dev(struct pakfire_file
* file
, const dev_t dev
) {
795 file
->st
.st_dev
= dev
;
798 PAKFIRE_EXPORT
int pakfire_file_get_type(struct pakfire_file
* file
) {
799 return file
->st
.st_mode
& S_IFMT
;
802 PAKFIRE_EXPORT off_t
pakfire_file_get_size(struct pakfire_file
* file
) {
803 return file
->st
.st_size
;
806 PAKFIRE_EXPORT
void pakfire_file_set_size(struct pakfire_file
* file
, off_t size
) {
807 file
->st
.st_size
= size
;
810 PAKFIRE_EXPORT
const char* pakfire_file_get_uname(struct pakfire_file
* file
) {
814 PAKFIRE_EXPORT
int pakfire_file_set_uname(struct pakfire_file
* file
, const char* uname
) {
815 return pakfire_string_set(file
->uname
, uname
);
818 PAKFIRE_EXPORT
const char* pakfire_file_get_gname(struct pakfire_file
* file
) {
822 PAKFIRE_EXPORT
int pakfire_file_set_gname(struct pakfire_file
* file
, const char* gname
) {
823 return pakfire_string_set(file
->gname
, gname
);
826 PAKFIRE_EXPORT mode_t
pakfire_file_get_mode(struct pakfire_file
* file
) {
827 return file
->st
.st_mode
;
830 PAKFIRE_EXPORT
void pakfire_file_set_mode(struct pakfire_file
* file
, mode_t mode
) {
831 file
->st
.st_mode
= mode
;
834 PAKFIRE_EXPORT mode_t
pakfire_file_get_perms(struct pakfire_file
* file
) {
835 return file
->st
.st_mode
& ~S_IFMT
;
838 PAKFIRE_EXPORT
void pakfire_file_set_perms(struct pakfire_file
* file
, const mode_t perms
) {
839 // Clear any previous permissions
840 file
->st
.st_mode
&= S_IFMT
;
842 // Set new bits (with format cleared)
843 file
->st
.st_mode
|= ~S_IFMT
& perms
;
846 PAKFIRE_EXPORT
time_t pakfire_file_get_ctime(struct pakfire_file
* file
) {
847 return file
->st
.st_ctime
;
850 PAKFIRE_EXPORT
void pakfire_file_set_ctime(struct pakfire_file
* file
, time_t time
) {
851 file
->st
.st_ctime
= time
;
854 PAKFIRE_EXPORT
time_t pakfire_file_get_mtime(struct pakfire_file
* file
) {
855 return file
->st
.st_mtime
;
858 PAKFIRE_EXPORT
void pakfire_file_set_mtime(struct pakfire_file
* file
, time_t time
) {
859 file
->st
.st_mtime
= time
;
862 PAKFIRE_EXPORT
const unsigned char* pakfire_file_get_digest(
863 struct pakfire_file
* file
, const enum pakfire_digest_types type
, size_t* length
) {
866 case PAKFIRE_DIGEST_SHA3_512
:
867 if (!pakfire_digest_set(file
->digests
.sha3_512
))
871 *length
= sizeof(file
->digests
.sha3_512
);
873 return file
->digests
.sha3_512
;
875 case PAKFIRE_DIGEST_SHA3_256
:
876 if (!pakfire_digest_set(file
->digests
.sha3_256
))
880 *length
= sizeof(file
->digests
.sha3_256
);
882 return file
->digests
.sha3_256
;
884 case PAKFIRE_DIGEST_BLAKE2B512
:
885 if (!pakfire_digest_set(file
->digests
.blake2b512
))
889 *length
= sizeof(file
->digests
.blake2b512
);
891 return file
->digests
.blake2b512
;
893 case PAKFIRE_DIGEST_BLAKE2S256
:
894 if (!pakfire_digest_set(file
->digests
.blake2s256
))
898 *length
= sizeof(file
->digests
.blake2s256
);
900 return file
->digests
.blake2s256
;
902 case PAKFIRE_DIGEST_SHA2_512
:
903 if (!pakfire_digest_set(file
->digests
.sha2_512
))
907 *length
= sizeof(file
->digests
.sha2_512
);
909 return file
->digests
.sha2_512
;
911 case PAKFIRE_DIGEST_SHA2_256
:
912 if (!pakfire_digest_set(file
->digests
.sha2_256
))
916 *length
= sizeof(file
->digests
.sha2_256
);
918 return file
->digests
.sha2_256
;
920 case PAKFIRE_DIGEST_UNDEFINED
:
927 PAKFIRE_EXPORT
int pakfire_file_set_digest(struct pakfire_file
* file
,
928 const enum pakfire_digest_types type
, const unsigned char* digest
, const size_t length
) {
934 // Check buffer length
935 if (pakfire_digest_length(type
) != length
) {
936 ERROR(file
->pakfire
, "Digest has an incorrect length of %zu byte(s)\n", length
);
943 case PAKFIRE_DIGEST_SHA3_512
:
944 memcpy(file
->digests
.sha3_512
, digest
, sizeof(file
->digests
.sha3_512
));
947 case PAKFIRE_DIGEST_SHA3_256
:
948 memcpy(file
->digests
.sha3_256
, digest
, sizeof(file
->digests
.sha3_256
));
951 case PAKFIRE_DIGEST_BLAKE2B512
:
952 memcpy(file
->digests
.blake2b512
, digest
, sizeof(file
->digests
.blake2b512
));
955 case PAKFIRE_DIGEST_BLAKE2S256
:
956 memcpy(file
->digests
.blake2s256
, digest
, sizeof(file
->digests
.blake2s256
));
959 case PAKFIRE_DIGEST_SHA2_512
:
960 memcpy(file
->digests
.sha2_512
, digest
, sizeof(file
->digests
.sha2_512
));
963 case PAKFIRE_DIGEST_SHA2_256
:
964 memcpy(file
->digests
.sha2_256
, digest
, sizeof(file
->digests
.sha2_256
));
967 case PAKFIRE_DIGEST_UNDEFINED
:
975 static int pakfire_file_levels(struct pakfire_file
* file
) {
981 for (char* p
= file
->path
; *p
; p
++) {
989 FILE* pakfire_file_open(struct pakfire_file
* file
) {
990 FILE* f
= fopen(file
->abspath
, "r");
992 ERROR(file
->pakfire
, "Could not open %s: %m\n", file
->abspath
);
997 int pakfire_file_payload_matches(struct pakfire_file
* file
,
998 const void* needle
, const size_t length
) {
999 char buffer
[1024 * 1024];
1004 // Only run for regular files
1005 if (!S_ISREG(file
->st
.st_mode
))
1009 f
= pakfire_file_open(file
);
1014 size_t bytes_read
= fread(buffer
, 1, sizeof(buffer
), f
);
1016 // Raise any reading errors
1022 // Search for the needle
1023 p
= memmem(buffer
, bytes_read
, needle
, length
);
1040 static int __pakfire_file_compute_digests(struct pakfire_file
* file
,
1041 struct pakfire_digests
* digests
, const int types
) {
1045 // Skip this for anything that isn't a regular file
1046 if (!S_ISREG(file
->st
.st_mode
))
1050 pakfire_digests_reset(digests
, types
);
1053 f
= pakfire_file_open(file
);
1058 r
= pakfire_digests_compute_from_file(file
->pakfire
, digests
, types
, f
);
1069 int pakfire_file_compute_digests(struct pakfire_file
* file
, const int types
) {
1070 return __pakfire_file_compute_digests(file
, &file
->digests
, types
);
1073 int pakfire_file_remove(struct pakfire_file
* file
) {
1074 if (!*file
->abspath
) {
1079 DEBUG(file
->pakfire
, "Removing %s...\n", file
->path
);
1081 int r
= remove(file
->abspath
);
1084 // Ignore when we could not remove directories
1088 // Ignore if the file didn't exist
1096 ERROR(file
->pakfire
, "Could not remove %s (%s): %m\n", file
->path
, file
->abspath
);
1102 int pakfire_file_symlink_target_exists(struct pakfire_file
* file
) {
1103 // Fail if this got called for anything that isn't a symlink
1104 if (!S_ISLNK(file
->st
.st_mode
)) {
1109 return pakfire_path_exists(file
->abspath
);
1116 int pakfire_file_detect_mimetype(struct pakfire_file
* file
) {
1117 // Only process regular files
1118 if (!S_ISREG(file
->st
.st_mode
))
1121 // Skip if MIME type is already set
1122 if (*file
->mimetype
)
1125 // Fetch the magic cookie
1126 magic_t magic
= pakfire_get_magic(file
->pakfire
);
1131 const char* mimetype
= magic_file(magic
, file
->abspath
);
1133 ERROR(file
->pakfire
, "Could not classify %s: %s\n", file
->path
, magic_error(magic
));
1137 DEBUG(file
->pakfire
, "Classified %s as %s\n", file
->path
, mimetype
);
1140 return pakfire_file_set_mimetype(file
, mimetype
);
1143 PAKFIRE_EXPORT
const char* pakfire_file_get_mimetype(struct pakfire_file
* file
) {
1144 // Return nothing on an empty mimetype
1145 if (!*file
->mimetype
)
1148 return file
->mimetype
;
1151 PAKFIRE_EXPORT
int pakfire_file_set_mimetype(
1152 struct pakfire_file
* file
, const char* mimetype
) {
1154 return pakfire_string_set(file
->mimetype
, mimetype
);
1161 static int setup_libelf(struct pakfire
* pakfire
) {
1162 // Initialize libelf
1163 if (elf_version(EV_CURRENT
) == EV_NONE
) {
1164 ERROR(pakfire
, "Could not initialize libelf: %s\n", elf_errmsg(-1));
1172 static int pakfire_file_classify_mode(struct pakfire_file
* file
) {
1173 // Check for regular files
1174 if (S_ISREG(file
->st
.st_mode
)) {
1175 file
->class |= PAKFIRE_FILE_REGULAR
;
1177 // Does the file have executable permissions?
1178 if (file
->st
.st_mode
& (S_IXUSR
|S_IXGRP
|S_IXOTH
))
1179 file
->class |= PAKFIRE_FILE_EXECUTABLE
;
1181 // Check for directories
1182 } else if (S_ISDIR(file
->st
.st_mode
))
1183 file
->class |= PAKFIRE_FILE_DIRECTORY
;
1185 // Check for symlinks
1186 else if (S_ISLNK(file
->st
.st_mode
))
1187 file
->class |= PAKFIRE_FILE_SYMLINK
;
1189 // Check for character devices
1190 else if (S_ISCHR(file
->st
.st_mode
))
1191 file
->class |= PAKFIRE_FILE_CHARACTER
;
1193 // Check for block devices
1194 else if (S_ISBLK(file
->st
.st_mode
))
1195 file
->class |= PAKFIRE_FILE_BLOCK
;
1197 // Check for FIFO pipes
1198 else if (S_ISFIFO(file
->st
.st_mode
))
1199 file
->class |= PAKFIRE_FILE_FIFO
;
1201 // Check for sockets
1202 else if (S_ISSOCK(file
->st
.st_mode
))
1203 file
->class |= PAKFIRE_FILE_SOCKET
;
1208 static const struct pattern
{
1209 const char* pattern
;
1212 { "*.a", PAKFIRE_FILE_STATIC_LIBRARY
},
1213 { "*.la", PAKFIRE_FILE_LIBTOOL_ARCHIVE
},
1214 { "*.pm", PAKFIRE_FILE_PERL
},
1215 { "*.pc", PAKFIRE_FILE_PKGCONFIG
},
1216 { "/usr/lib/firmware/*", PAKFIRE_FILE_FIRMWARE
},
1217 { "/usr/lib*/ld-*.so*", PAKFIRE_FILE_RUNTIME_LINKER
},
1221 static int pakfire_file_classify_pattern(struct pakfire_file
* file
) {
1222 for (const struct pattern
* p
= patterns
; p
->pattern
; p
++) {
1223 if (pakfire_file_matches(file
, p
->pattern
)) {
1224 file
->class |= p
->class;
1232 static const struct mimetype
{
1233 const char* mimetype
;
1236 { "text/x-perl", PAKFIRE_FILE_PERL
},
1240 static int pakfire_file_classify_magic(struct pakfire_file
* file
) {
1243 // Detect the MIME type
1244 r
= pakfire_file_detect_mimetype(file
);
1248 // Fetch the MIME type
1249 const char* mimetype
= pakfire_file_get_mimetype(file
);
1253 // Match the MIME type with a flag
1254 for (const struct mimetype
* m
= mimetypes
; m
->mimetype
; m
++) {
1255 if (strcmp(m
->mimetype
, mimetype
) == 0) {
1256 file
->class |= m
->class;
1264 static int pakfire_file_classify_elf(struct pakfire_file
* file
) {
1269 // Don't run this if we already know that file is an ELF file
1270 if (file
->class & PAKFIRE_FILE_ELF
)
1274 r
= setup_libelf(file
->pakfire
);
1279 f
= fopen(file
->abspath
, "r");
1281 ERROR(file
->pakfire
, "Could not open %s: %m\n", file
->path
);
1285 // Try to open the ELF file
1286 elf
= elf_begin(fileno(f
), ELF_C_READ
, NULL
);
1288 // We fail silently here, because this file might be in a different format
1292 switch (elf_kind(elf
)) {
1293 // Mark this file as an ELF file
1295 file
->class |= PAKFIRE_FILE_ELF
;
1298 // Ignore everything else
1312 int pakfire_file_classify(struct pakfire_file
* file
) {
1316 // First, check the mode so that we won't run magic on directories, symlinks, ...
1317 r
= pakfire_file_classify_mode(file
);
1321 // Only run this for regular files
1322 if (file
->class & PAKFIRE_FILE_REGULAR
) {
1323 // Then check for patterns
1324 r
= pakfire_file_classify_pattern(file
);
1328 // After that, we will use libmagic...
1329 r
= pakfire_file_classify_magic(file
);
1333 // Check if the file is an ELF file
1334 r
= pakfire_file_classify_elf(file
);
1344 file
->class = PAKFIRE_FILE_UNKNOWN
;
1349 int pakfire_file_matches_class(struct pakfire_file
* file
, const int class) {
1350 return pakfire_file_classify(file
) & class;
1354 This function tries to remove the file after it has been packaged.
1356 It will try to delete any parent directories as well and ignore if directories
1357 cannot be deleted because they might contain other files
1359 int pakfire_file_cleanup(struct pakfire_file
* file
) {
1360 char path
[PATH_MAX
];
1362 // Try removing the file
1363 int r
= pakfire_file_remove(file
);
1367 // Create a working copy of abspath
1368 r
= pakfire_string_set(path
, file
->abspath
);
1372 // See how many levels this file has
1373 int levels
= pakfire_file_levels(file
);
1375 // Walk all the way up and remove all parent directories if possible
1379 // Break if path is suddenly empty
1383 DEBUG(file
->pakfire
, "Trying to remove parent directory %s\n", path
);
1387 // Break on any error
1395 static int pakfire_file_verify_mode(struct pakfire_file
* file
, const struct stat
* st
) {
1396 const mode_t type
= pakfire_file_get_type(file
);
1398 // Did the type change?
1399 if (type
!= (st
->st_mode
& S_IFMT
)) {
1400 file
->verify_status
|= PAKFIRE_FILE_TYPE_CHANGED
;
1402 DEBUG(file
->pakfire
, "%s: File Type changed\n", file
->path
);
1405 const mode_t perms
= pakfire_file_get_perms(file
);
1407 // Check permissions
1408 if (perms
!= (st
->st_mode
& 0777)) {
1409 file
->verify_status
|= PAKFIRE_FILE_PERMISSIONS_CHANGED
;
1411 DEBUG(file
->pakfire
, "%s: Permissions changed\n", file
->path
);
1415 // XXX This does not check what it is supposed to check
1417 // Check if device node changed
1418 if (S_ISCHR(st
->st_mode
) || S_ISBLK(st
->st_mode
)) {
1419 const dev_t dev
= pakfire_file_get_dev(file
);
1421 if (dev
!= st
->st_dev
) {
1422 file
->verify_status
|= PAKFIRE_FILE_DEV_CHANGED
;
1424 DEBUG(file
->pakfire
, "%s: Device Node changed\n", file
->path
);
1432 static int pakfire_file_verify_size(struct pakfire_file
* file
, const struct stat
* st
) {
1433 // Nothing to do if size matches
1434 if (file
->st
.st_size
== st
->st_size
)
1438 file
->verify_status
|= PAKFIRE_FILE_SIZE_CHANGED
;
1440 DEBUG(file
->pakfire
, "%s: Filesize differs (expected %zu, got %zu byte(s))\n",
1441 file
->path
, file
->st
.st_size
, st
->st_size
);
1446 static int pakfire_file_verify_ownership(struct pakfire_file
* file
, const struct stat
* st
) {
1449 const uid_t uid
= pakfire_unmap_id(file
->pakfire
, st
->st_uid
);
1450 const gid_t gid
= pakfire_unmap_id(file
->pakfire
, st
->st_gid
);
1452 const uid_t uid
= st
->st_uid
;
1453 const gid_t gid
= st
->st_gid
;
1456 // Fetch owner & group
1457 struct passwd
* owner
= pakfire_getpwnam(file
->pakfire
, file
->uname
);
1458 struct group
* group
= pakfire_getgrnam(file
->pakfire
, file
->gname
);
1460 // Check if owner matches
1461 if (!owner
|| owner
->pw_uid
!= uid
) {
1462 file
->verify_status
|= PAKFIRE_FILE_OWNER_CHANGED
;
1464 DEBUG(file
->pakfire
, "%s: Owner differs\n", file
->path
);
1467 // Check if group matches
1468 if (!group
|| group
->gr_gid
!= gid
) {
1469 file
->verify_status
|= PAKFIRE_FILE_GROUP_CHANGED
;
1471 DEBUG(file
->pakfire
, "%s: Group differs\n", file
->path
);
1477 static int pakfire_file_verify_timestamps(struct pakfire_file
* file
, const struct stat
* st
) {
1478 // Check creation time
1479 if (file
->st
.st_ctime
!= st
->st_ctime
) {
1480 file
->verify_status
|= PAKFIRE_FILE_CTIME_CHANGED
;
1482 DEBUG(file
->pakfire
, "%s: Creation time changed\n", file
->path
);
1485 // Check modification time
1486 if (file
->st
.st_mtime
!= st
->st_mtime
) {
1487 file
->verify_status
|= PAKFIRE_FILE_MTIME_CHANGED
;
1489 DEBUG(file
->pakfire
, "%s: Modification time changed\n", file
->path
);
1495 static int pakfire_file_verify_payload(struct pakfire_file
* file
, const struct stat
* st
) {
1498 struct pakfire_digests computed_digests
;
1499 int digest_types
= PAKFIRE_DIGEST_UNDEFINED
;
1501 // Nothing to do for anything that isn't a regular file
1502 if (!S_ISREG(st
->st_mode
))
1505 // Fast-path if size changed. The payload will have changed, too
1506 if (file
->verify_status
& PAKFIRE_FILE_SIZE_CHANGED
) {
1507 file
->verify_status
|= PAKFIRE_FILE_PAYLOAD_CHANGED
;
1511 // Check if this file has any digests at all
1512 digest_types
= pakfire_digest_has_any(&file
->digests
);
1514 if (!digest_types
) {
1515 ERROR(file
->pakfire
, "%s: No digests available\n", file
->path
);
1520 r
= __pakfire_file_compute_digests(file
, &computed_digests
, digest_types
);
1525 r
= pakfire_digests_compare(file
->pakfire
, &file
->digests
, &computed_digests
, digest_types
);
1527 file
->verify_status
|= PAKFIRE_FILE_PAYLOAD_CHANGED
;
1529 DEBUG(file
->pakfire
, "%s: Digest(s) do not match\n", file
->path
);
1537 Verify the file - i.e. does the metadata match what is on disk?
1539 int pakfire_file_verify(struct pakfire_file
* file
, int* status
) {
1543 DEBUG(file
->pakfire
, "Verifying %s...\n", file
->path
);
1546 r
= lstat(file
->abspath
, &st
);
1548 // File does not exist
1549 if (errno
== ENOENT
) {
1550 file
->verify_status
|= PAKFIRE_FILE_NOENT
;
1554 // Raise any other errors from stat()
1559 r
= pakfire_file_verify_mode(file
, &st
);
1564 r
= pakfire_file_verify_size(file
, &st
);
1569 r
= pakfire_file_verify_ownership(file
, &st
);
1573 // Verify timestamps
1574 r
= pakfire_file_verify_timestamps(file
, &st
);
1579 r
= pakfire_file_verify_payload(file
, &st
);
1586 PAKFIRE_EXPORT
int pakfire_file_matches(struct pakfire_file
* file
, const char* pattern
) {
1589 // Don't match on no pattern
1593 // Check if the pattern matches
1594 r
= fnmatch(pattern
, file
->path
, 0);
1613 static int pakfire_file_open_elf(struct pakfire_file
* file
,
1614 int (*callback
)(struct pakfire_file
* file
, Elf
* elf
, void* data
), void* data
) {
1619 // Don't run this for non-ELF files
1620 if (!pakfire_file_matches_class(file
, PAKFIRE_FILE_ELF
)) {
1626 r
= setup_libelf(file
->pakfire
);
1631 f
= fopen(file
->abspath
, "r");
1633 ERROR(file
->pakfire
, "Could not open %s: %m\n", file
->abspath
);
1637 // Parse the ELF header
1638 elf
= elf_begin(fileno(f
), ELF_C_READ
, NULL
);
1640 ERROR(file
->pakfire
, "Could not open ELF file: %s\n", elf_errmsg(-1));
1645 // Check if this is an ELF file
1646 switch (elf_kind(elf
)) {
1651 ERROR(file
->pakfire
, "%s is not an ELF object\n", file
->path
);
1656 // Call the callback
1657 r
= callback(file
, elf
, data
);
1668 static int __pakfire_file_get_elf_type(struct pakfire_file
* file
, Elf
* elf
, void* data
) {
1669 int* type
= (int*)data
;
1672 // Fetch the ELF header
1673 if (!gelf_getehdr(elf
, &ehdr
)) {
1674 ERROR(file
->pakfire
, "Could not parse ELF header: %s\n", elf_errmsg(-1));
1679 *type
= ehdr
.e_type
;
1684 static int pakfire_file_get_elf_type(struct pakfire_file
* file
) {
1688 r
= pakfire_file_open_elf(file
, __pakfire_file_get_elf_type
, &type
);
1695 static int __pakfire_file_is_stripped(struct pakfire_file
* file
, Elf
* elf
, void* data
) {
1696 Elf_Scn
* section
= NULL
;
1699 // Walk through all sections
1701 section
= elf_nextscn(elf
, section
);
1705 // Fetch the section header
1706 gelf_getshdr(section
, &shdr
);
1708 switch (shdr
.sh_type
) {
1709 // Break if we found the symbol table
1716 DEBUG(file
->pakfire
, "%s has no debug sections\n", file
->path
);
1721 int pakfire_file_is_stripped(struct pakfire_file
* file
) {
1722 // Don't run this for non-ELF files
1723 if (!pakfire_file_matches_class(file
, PAKFIRE_FILE_ELF
)) {
1728 // Do not perform this check on firmware
1729 if (pakfire_file_matches_class(file
, PAKFIRE_FILE_FIRMWARE
))
1732 switch (pakfire_file_get_elf_type(file
)) {
1733 // Do not check Relocatable Objects
1737 // Check everything else
1742 return pakfire_file_open_elf(file
, __pakfire_file_is_stripped
, NULL
);
1745 static int __pakfire_file_hardening_check_ssp(
1746 struct pakfire_file
* file
, Elf
* elf
, void* data
) {
1747 Elf_Scn
* section
= NULL
;
1748 GElf_Shdr section_header
;
1749 Elf_Data
* elf_data
= NULL
;
1751 const char* name
= NULL
;
1753 // Count any global functions
1756 // Walk through all sections
1758 section
= elf_nextscn(elf
, section
);
1760 ERROR(file
->pakfire
, "%s has no symbol table\n", file
->path
);
1764 // Fetch the section header
1765 gelf_getshdr(section
, §ion_header
);
1767 // Break if we found the symbol table
1768 if (section_header
.sh_type
== SHT_SYMTAB
)
1772 // Fetch a pointer to the section data
1773 elf_data
= elf_getdata(section
, NULL
);
1775 // Walk through all symbols
1776 for (unsigned int i
= 0; i
< section_header
.sh_size
/ section_header
.sh_entsize
; i
++) {
1777 gelf_getsym(elf_data
, i
, &symbol
);
1779 // Fetch the symbol name
1780 name
= elf_strptr(elf
, section_header
.sh_link
, symbol
.st_name
);
1782 // Skip empty section names
1783 if (!name
|| !*name
)
1786 // Exit if there is a symbol called "__stack_chk_fail"
1787 if (pakfire_string_startswith(name
, "__stack_chk_fail"))
1790 // Count any global functions
1791 if ((ELF64_ST_BIND(symbol
.st_info
) == STB_GLOBAL
) &&
1792 (ELF64_ST_TYPE(symbol
.st_info
) == STT_FUNC
))
1796 // We do not perform the check for libraries that do not contain any functions.
1797 // Some packages use shared libraries to provide data.
1799 DEBUG(file
->pakfire
, "%s: File has no functions. Skipping SSP check.\n", file
->path
);
1803 // The file does not seem to have SSP enabled
1804 file
->hardening_issues
|= PAKFIRE_FILE_NO_SSP
;
1809 static int pakfire_file_hardening_check_ssp(struct pakfire_file
* file
) {
1810 // Do not perform this check for runtime linkers
1811 if (pakfire_file_matches_class(file
, PAKFIRE_FILE_RUNTIME_LINKER
))
1814 return pakfire_file_open_elf(file
, __pakfire_file_hardening_check_ssp
, NULL
);
1817 static int pakfire_file_hardening_check_pie(struct pakfire_file
* file
) {
1818 switch (pakfire_file_get_elf_type(file
)) {
1819 // Shared Object files are good
1823 // Everything else is bad
1825 file
->hardening_issues
|= PAKFIRE_FILE_NO_PIE
;
1832 static int __pakfire_file_hardening_check_execstack(
1833 struct pakfire_file
* file
, Elf
* elf
, void* data
) {
1839 // Fetch the total numbers of program headers
1840 r
= elf_getphdrnum(elf
, &phnum
);
1842 ERROR(file
->pakfire
, "Could not fetch number of program headers: %s\n",
1847 // Walk through all program headers
1848 for (unsigned int i
= 0; i
< phnum
; i
++) {
1849 if (!gelf_getphdr(elf
, i
, &phdr
)) {
1850 ERROR(file
->pakfire
, "Could not parse program header: %s\n", elf_errmsg(-1));
1854 switch (phdr
.p_type
) {
1856 DEBUG(file
->pakfire
,
1857 "%s: GNU_STACK flags: %c%c%c\n",
1859 (phdr
.p_flags
& PF_R
) ? 'R' : '-',
1860 (phdr
.p_flags
& PF_W
) ? 'W' : '-',
1861 (phdr
.p_flags
& PF_X
) ? 'X' : '-'
1864 // The stack cannot be writable and executable
1865 if ((phdr
.p_flags
& PF_W
) && (phdr
.p_flags
& PF_X
))
1866 file
->hardening_issues
|= PAKFIRE_FILE_EXECSTACK
;
1879 static int pakfire_file_hardening_check_execstack(struct pakfire_file
* file
) {
1880 return pakfire_file_open_elf(file
, __pakfire_file_hardening_check_execstack
, NULL
);
1883 static int __pakfire_file_hardening_check_partially_relro(
1884 struct pakfire_file
* file
, Elf
* elf
, void* data
) {
1890 // Fetch the total numbers of program headers
1891 r
= elf_getphdrnum(elf
, &phnum
);
1893 ERROR(file
->pakfire
, "Could not fetch number of program headers: %s\n",
1898 // Walk through all program headers
1899 for (unsigned int i
= 0; i
< phnum
; i
++) {
1900 if (!gelf_getphdr(elf
, i
, &phdr
)) {
1901 ERROR(file
->pakfire
, "Could not parse program header: %s\n", elf_errmsg(-1));
1905 switch (phdr
.p_type
) {
1914 // This file does not seem to have PT_GNU_RELRO set
1915 file
->hardening_issues
|= PAKFIRE_FILE_NO_PARTIALLY_RELRO
;
1920 static int pakfire_file_hardening_check_relro(struct pakfire_file
* file
) {
1921 return pakfire_file_open_elf(file
, __pakfire_file_hardening_check_partially_relro
, NULL
);
1925 int pakfire_file_check_hardening(struct pakfire_file
* file
, int* issues
) {
1928 // Return previous result if this has been run before
1929 if (!file
->hardening_check_done
) {
1930 // Perform FHS check
1931 r
= pakfire_fhs_check_file(file
->pakfire
, file
);
1933 file
->hardening_issues
|= PAKFIRE_FILE_FHS_ERROR
;
1935 // Do not perform the following checks on firmware
1936 if (pakfire_file_matches_class(file
, PAKFIRE_FILE_FIRMWARE
))
1939 // Run these checks only for ELF files
1940 if (pakfire_file_matches_class(file
, PAKFIRE_FILE_ELF
)) {
1941 switch (pakfire_file_get_elf_type(file
)) {
1942 // Do not check Relocatable Objects
1946 // Check everything else
1952 r
= pakfire_file_hardening_check_ssp(file
);
1957 r
= pakfire_file_hardening_check_pie(file
);
1961 // Check for executable stacks
1962 r
= pakfire_file_hardening_check_execstack(file
);
1967 r
= pakfire_file_hardening_check_relro(file
);
1974 file
->hardening_check_done
= 1;
1977 // Return any issues
1979 *issues
= file
->hardening_issues
;