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/file.h>
39 #include <pakfire/logging.h>
40 #include <pakfire/pakfire.h>
41 #include <pakfire/private.h>
42 #include <pakfire/string.h>
43 #include <pakfire/util.h>
45 enum pakfire_file_verification_status
{
46 PAKFIRE_FILE_NOENT
= (1 << 0),
47 PAKFIRE_FILE_TYPE_CHANGED
= (1 << 1),
48 PAKFIRE_FILE_PERMISSIONS_CHANGED
= (1 << 2),
49 PAKFIRE_FILE_DEV_CHANGED
= (1 << 3),
50 PAKFIRE_FILE_SIZE_CHANGED
= (1 << 4),
51 PAKFIRE_FILE_OWNER_CHANGED
= (1 << 5),
52 PAKFIRE_FILE_GROUP_CHANGED
= (1 << 6),
53 PAKFIRE_FILE_CTIME_CHANGED
= (1 << 7),
54 PAKFIRE_FILE_MTIME_CHANGED
= (1 << 8),
55 PAKFIRE_FILE_PAYLOAD_CHANGED
= (1 << 9),
59 struct pakfire
* pakfire
;
65 // The absolute path in the file system
66 char abspath
[PATH_MAX
];
69 char uname
[LOGIN_NAME_MAX
];
70 char gname
[LOGIN_NAME_MAX
];
76 char hardlink
[PATH_MAX
];
77 char symlink
[PATH_MAX
];
80 struct pakfire_digests digests
;
83 char mimetype
[NAME_MAX
];
88 // Verification Status
93 int hardening_check_done
:1;
95 #warning TODO capabilities, config, data
101 static int pakfire_file_from_archive_entry(struct pakfire_file
* file
, struct archive_entry
* entry
) {
103 const char* path
= NULL
;
104 const char* attr
= NULL
;
105 const void* value
= NULL
;
110 path
= archive_entry_sourcepath(entry
);
112 // Make path absolute
113 path
= pakfire_path_abspath(path
);
120 r
= pakfire_file_set_abspath(file
, path
);
122 ERROR(file
->pakfire
, "Could not set abspath: %m\n");
128 path
= archive_entry_pathname(entry
);
130 r
= pakfire_file_set_path(file
, path
);
132 ERROR(file
->pakfire
, "Could not set path: %m\n");
138 pakfire_file_set_hardlink(file
, archive_entry_hardlink(entry
));
139 pakfire_file_set_symlink(file
, archive_entry_symlink(entry
));
141 pakfire_file_set_nlink(file
, archive_entry_nlink(entry
));
142 pakfire_file_set_inode(file
, archive_entry_ino64(entry
));
143 pakfire_file_set_dev(file
, archive_entry_dev(entry
));
146 pakfire_file_set_size(file
, archive_entry_size(entry
));
149 pakfire_file_set_mode(file
, archive_entry_mode(entry
));
152 if (archive_entry_dev_is_set(entry
))
153 pakfire_file_set_dev(file
, archive_entry_dev(entry
));
156 pakfire_file_set_uname(file
, archive_entry_uname(entry
));
159 pakfire_file_set_gname(file
, archive_entry_gname(entry
));
162 pakfire_file_set_ctime(file
, archive_entry_ctime(entry
));
163 pakfire_file_set_mtime(file
, archive_entry_mtime(entry
));
165 // Reset iterating over extended attributes
166 archive_entry_xattr_reset(entry
);
168 // Read any extended attributes
169 while (archive_entry_xattr_next(entry
, &attr
, &value
, &size
) == ARCHIVE_OK
) {
171 if (strcmp(attr
, "PAKFIRE.mimetype") == 0) {
172 // Copy the value into a NULL-terminated buffer
173 r
= asprintf(&buffer
, "%.*s", (int)size
, (const char*)value
);
178 r
= pakfire_file_set_mimetype(file
, buffer
);
183 } else if (strcmp(attr
, "PAKFIRE.digests.sha3_512") == 0) {
184 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA3_512
, value
, size
);
189 } else if (strcmp(attr
, "PAKFIRE.digests.sha3_256") == 0) {
190 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA3_256
, value
, size
);
194 // Digest: BLAKE2b512
195 } else if (strcmp(attr
, "PAKFIRE.digests.blake2b512") == 0) {
196 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_BLAKE2B512
, value
, size
);
200 // Digest: BLAKE2b512
201 } else if (strcmp(attr
, "PAKFIRE.digests.blake2s256") == 0) {
202 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_BLAKE2S256
, value
, size
);
207 } else if (strcmp(attr
, "PAKFIRE.digests.sha2_512") == 0) {
208 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA2_512
, value
, size
);
213 } else if (strcmp(attr
, "PAKFIRE.digests.sha2_256") == 0) {
214 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA2_256
, value
, size
);
219 DEBUG(file
->pakfire
, "Received an unknown extended attribute: %s\n", attr
);
230 PAKFIRE_EXPORT
int pakfire_file_create(struct pakfire_file
** file
, struct pakfire
* pakfire
) {
231 struct pakfire_file
* f
= calloc(1, sizeof(*f
));
235 // Store reference to Pakfire
236 f
->pakfire
= pakfire_ref(pakfire
);
238 // Initialize reference counter
245 int pakfire_file_create_from_path(struct pakfire_file
** file
,
246 struct pakfire
* pakfire
, const char* path
) {
247 struct archive
* reader
= NULL
;
248 struct archive_entry
* entry
= NULL
;
252 reader
= pakfire_make_archive_disk_reader(pakfire
, 0);
256 // Allocate a new archive entry
257 entry
= archive_entry_new();
262 archive_entry_copy_sourcepath(entry
, path
);
264 // Read all file attributes from disk
265 r
= archive_read_disk_entry_from_file(reader
, entry
, -1, NULL
);
267 ERROR(pakfire
, "Could not read from %s: %m\n", path
);
272 r
= pakfire_file_create_from_archive_entry(file
, pakfire
, entry
);
278 ERROR(pakfire
, "Could not create file from path %s: %m\n", path
);
280 archive_entry_free(entry
);
282 archive_read_free(reader
);
287 int pakfire_file_create_from_archive_entry(struct pakfire_file
** file
, struct pakfire
* pakfire
,
288 struct archive_entry
* entry
) {
289 int r
= pakfire_file_create(file
, pakfire
);
293 // Copy archive entry
294 r
= pakfire_file_from_archive_entry(*file
, entry
);
301 pakfire_file_unref(*file
);
307 struct archive_entry
* pakfire_file_archive_entry(struct pakfire_file
* file
, int digest_types
) {
308 const char* path
= NULL
;
311 struct archive_entry
* entry
= archive_entry_new();
313 ERROR(file
->pakfire
, "Could not allocate archive entry: %m\n");
318 archive_entry_copy_sourcepath(entry
, file
->abspath
);
321 path
= pakfire_file_get_path(file
);
322 if (path
&& *path
== '/') {
323 archive_entry_copy_pathname(entry
, path
+ 1);
328 archive_entry_set_hardlink(entry
, file
->hardlink
);
330 archive_entry_set_symlink(entry
, file
->symlink
);
332 archive_entry_set_nlink(entry
, pakfire_file_get_nlink(file
));
333 archive_entry_set_ino64(entry
, pakfire_file_get_inode(file
));
334 archive_entry_set_dev(entry
, pakfire_file_get_dev(file
));
337 archive_entry_set_size(entry
, pakfire_file_get_size(file
));
340 archive_entry_set_mode(entry
, pakfire_file_get_mode(file
));
343 archive_entry_set_uname(entry
, pakfire_file_get_uname(file
));
346 archive_entry_set_gname(entry
, pakfire_file_get_gname(file
));
349 archive_entry_set_ctime(entry
, pakfire_file_get_ctime(file
), 0);
350 archive_entry_set_mtime(entry
, pakfire_file_get_mtime(file
), 0);
353 const char* mimetype
= pakfire_file_get_mimetype(file
);
355 archive_entry_xattr_add_entry(entry
,
356 "PAKFIRE.mimetype", mimetype
, strlen(mimetype
));
359 // Compute any required file digests
360 r
= pakfire_file_compute_digests(file
, digest_types
);
367 if ((digest_types
&& PAKFIRE_DIGEST_SHA3_512
)
368 && pakfire_digest_set(file
->digests
.sha3_512
))
369 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha3_512",
370 file
->digests
.sha3_512
, sizeof(file
->digests
.sha3_512
));
373 if ((digest_types
&& PAKFIRE_DIGEST_SHA3_256
) &&
374 pakfire_digest_set(file
->digests
.sha3_256
))
375 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha3_256",
376 file
->digests
.sha3_256
, sizeof(file
->digests
.sha3_256
));
379 if ((digest_types
&& PAKFIRE_DIGEST_BLAKE2B512
) &&
380 pakfire_digest_set(file
->digests
.blake2b512
))
381 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.blake2b512",
382 file
->digests
.blake2b512
, sizeof(file
->digests
.blake2b512
));
385 if ((digest_types
&& PAKFIRE_DIGEST_BLAKE2S256
) &&
386 pakfire_digest_set(file
->digests
.blake2s256
))
387 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.blake2s256",
388 file
->digests
.blake2s256
, sizeof(file
->digests
.blake2s256
));
391 if ((digest_types
&& PAKFIRE_DIGEST_SHA2_512
) &&
392 pakfire_digest_set(file
->digests
.sha2_512
))
393 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha2_512",
394 file
->digests
.sha2_512
, sizeof(file
->digests
.sha2_512
));
397 if ((digest_types
&& PAKFIRE_DIGEST_SHA2_512
) &&
398 pakfire_digest_set(file
->digests
.sha2_256
))
399 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha2_256",
400 file
->digests
.sha2_256
, sizeof(file
->digests
.sha2_256
));
406 archive_entry_free(entry
);
411 static void pakfire_file_free(struct pakfire_file
* file
) {
412 pakfire_unref(file
->pakfire
);
416 PAKFIRE_EXPORT
struct pakfire_file
* pakfire_file_ref(struct pakfire_file
* file
) {
422 PAKFIRE_EXPORT
struct pakfire_file
* pakfire_file_unref(struct pakfire_file
* file
) {
423 if (--file
->nrefs
> 0)
426 pakfire_file_free(file
);
430 #define pakfire_file_strmode(file, buffer) \
431 __pakfire_file_strmode(file, buffer, sizeof(buffer))
433 static int __pakfire_file_strmode(struct pakfire_file
* file
, char* s
, const size_t length
) {
436 static const mode_t permbits
[] = {
448 const mode_t mode
= pakfire_file_get_mode(file
);
449 const mode_t type
= pakfire_file_get_type(file
);
451 // Set some default string
452 r
= __pakfire_string_set(s
, length
, "?rwxrwxrwx ");
486 if (*file
->hardlink
) {
492 for (unsigned int i
= 0; i
< 9; i
++) {
493 if (mode
& permbits
[i
])
499 if (mode
& S_ISUID
) {
506 if (mode
& S_ISGID
) {
513 if (mode
& S_ISVTX
) {
528 char* pakfire_file_dump(struct pakfire_file
* file
, int flags
) {
536 if (flags
& PAKFIRE_FILE_DUMP_MODE
) {
537 r
= pakfire_file_strmode(file
, mode
);
541 r
= asprintf(&buffer
, "%s %s", buffer
, mode
);
547 if (flags
& PAKFIRE_FILE_DUMP_OWNERSHIP
) {
548 r
= asprintf(&buffer
, "%s %s/%s", buffer
, file
->uname
, file
->gname
);
554 if (flags
& PAKFIRE_FILE_DUMP_SIZE
) {
555 r
= asprintf(&buffer
, "%s %8zu", buffer
, file
->st
.st_size
);
561 if (flags
& PAKFIRE_FILE_DUMP_TIME
) {
562 r
= pakfire_strftime(time
, "%Y-%m-%d %H:%M", file
->st
.st_ctime
);
566 r
= asprintf(&buffer
, "%s %s", buffer
, time
);
572 r
= asprintf(&buffer
, "%s %s", buffer
, file
->path
);
576 // Append symlink target
577 if (flags
& PAKFIRE_FILE_DUMP_LINK_TARGETS
) {
578 switch (pakfire_file_get_type(file
)) {
580 r
= asprintf(&buffer
, "%s -> %s", buffer
, file
->symlink
);
590 if (flags
& PAKFIRE_FILE_DUMP_HARDENING
) {
591 if (pakfire_file_matches_class(file
, PAKFIRE_FILE_ELF
)) {
592 // Stack-smashing Protection
593 if (file
->hardening_issues
& PAKFIRE_FILE_NO_SSP
) {
594 r
= asprintf(&buffer
, "%s [NO-SSP]", buffer
);
599 // Position-independent Executable
600 if (file
->hardening_issues
& PAKFIRE_FILE_NO_PIE
) {
601 r
= asprintf(&buffer
, "%s [NO-PIE]", buffer
);
607 if (file
->hardening_issues
& PAKFIRE_FILE_EXECSTACK
) {
608 r
= asprintf(&buffer
, "%s [EXECSTACK]", buffer
);
613 // Not Partially RELRO
614 if (file
->hardening_issues
& PAKFIRE_FILE_NO_PARTIALLY_RELRO
) {
615 r
= asprintf(&buffer
, "%s [NO-PART-RELRO]", buffer
);
631 PAKFIRE_EXPORT
int pakfire_file_cmp(struct pakfire_file
* file1
, struct pakfire_file
* file2
) {
632 const char* path1
= pakfire_file_get_path(file1
);
633 const char* path2
= pakfire_file_get_path(file2
);
635 return strcmp(path1
, path2
);
638 const char* pakfire_file_get_abspath(struct pakfire_file
* file
) {
639 return file
->abspath
;
642 int pakfire_file_set_abspath(struct pakfire_file
* file
, const char* path
) {
645 // Check if path is set and absolute
646 if (!path
|| *path
!= '/') {
652 r
= pakfire_string_set(file
->abspath
, path
);
656 // Store path if it isn't set, yet
658 r
= pakfire_file_set_path(file
, path
);
666 ERROR(file
->pakfire
, "Could not set abspath '%s': %m\n", path
);
670 PAKFIRE_EXPORT
const char* pakfire_file_get_path(struct pakfire_file
* file
) {
674 PAKFIRE_EXPORT
int pakfire_file_set_path(struct pakfire_file
* file
, const char* path
) {
677 // Check if path is set
683 // Strip any leading dots from paths
684 if (pakfire_string_startswith(path
, "./"))
688 // Just store the path if it is absolute
690 r
= pakfire_string_set(file
->path
, path
);
695 // Handle relative paths
697 r
= pakfire_string_format(file
->path
, "/%s", path
);
703 // Set abspath if it isn't set, yet
704 if (!*file
->abspath
) {
705 r
= pakfire_file_set_abspath(file
, file
->path
);
713 ERROR(file
->pakfire
, "Could not set path '%s': %m\n", path
);
717 PAKFIRE_EXPORT
const char* pakfire_file_get_hardlink(struct pakfire_file
* file
) {
718 if (!*file
->hardlink
)
721 return file
->hardlink
;
724 PAKFIRE_EXPORT
void pakfire_file_set_hardlink(struct pakfire_file
* file
, const char* link
) {
725 pakfire_string_set(file
->hardlink
, link
);
728 PAKFIRE_EXPORT
const char* pakfire_file_get_symlink(struct pakfire_file
* file
) {
732 return file
->symlink
;
735 PAKFIRE_EXPORT
void pakfire_file_set_symlink(struct pakfire_file
* file
, const char* link
) {
736 pakfire_string_set(file
->symlink
, link
);
739 PAKFIRE_EXPORT nlink_t
pakfire_file_get_nlink(struct pakfire_file
* file
) {
740 return file
->st
.st_nlink
;
743 PAKFIRE_EXPORT
void pakfire_file_set_nlink(struct pakfire_file
* file
, const nlink_t nlink
) {
744 file
->st
.st_nlink
= nlink
;
747 PAKFIRE_EXPORT ino_t
pakfire_file_get_inode(struct pakfire_file
* file
) {
748 return file
->st
.st_ino
;
751 PAKFIRE_EXPORT
void pakfire_file_set_inode(struct pakfire_file
* file
, const ino_t ino
) {
752 file
->st
.st_ino
= ino
;
755 PAKFIRE_EXPORT dev_t
pakfire_file_get_dev(struct pakfire_file
* file
) {
756 return file
->st
.st_dev
;
759 PAKFIRE_EXPORT
void pakfire_file_set_dev(struct pakfire_file
* file
, const dev_t dev
) {
760 file
->st
.st_dev
= dev
;
763 PAKFIRE_EXPORT
int pakfire_file_get_type(struct pakfire_file
* file
) {
764 return file
->st
.st_mode
& S_IFMT
;
767 PAKFIRE_EXPORT off_t
pakfire_file_get_size(struct pakfire_file
* file
) {
768 return file
->st
.st_size
;
771 PAKFIRE_EXPORT
void pakfire_file_set_size(struct pakfire_file
* file
, off_t size
) {
772 file
->st
.st_size
= size
;
775 PAKFIRE_EXPORT
const char* pakfire_file_get_uname(struct pakfire_file
* file
) {
779 PAKFIRE_EXPORT
int pakfire_file_set_uname(struct pakfire_file
* file
, const char* uname
) {
780 return pakfire_string_set(file
->uname
, uname
);
783 PAKFIRE_EXPORT
const char* pakfire_file_get_gname(struct pakfire_file
* file
) {
787 PAKFIRE_EXPORT
int pakfire_file_set_gname(struct pakfire_file
* file
, const char* gname
) {
788 return pakfire_string_set(file
->gname
, gname
);
791 PAKFIRE_EXPORT mode_t
pakfire_file_get_mode(struct pakfire_file
* file
) {
792 return file
->st
.st_mode
;
795 PAKFIRE_EXPORT
void pakfire_file_set_mode(struct pakfire_file
* file
, mode_t mode
) {
796 file
->st
.st_mode
= mode
;
799 PAKFIRE_EXPORT mode_t
pakfire_file_get_perms(struct pakfire_file
* file
) {
800 return file
->st
.st_mode
& ~S_IFMT
;
803 PAKFIRE_EXPORT
void pakfire_file_set_perms(struct pakfire_file
* file
, const mode_t perms
) {
804 // Clear any previous permissions
805 file
->st
.st_mode
&= S_IFMT
;
807 // Set new bits (with format cleared)
808 file
->st
.st_mode
|= ~S_IFMT
& perms
;
811 PAKFIRE_EXPORT
time_t pakfire_file_get_ctime(struct pakfire_file
* file
) {
812 return file
->st
.st_ctime
;
815 PAKFIRE_EXPORT
void pakfire_file_set_ctime(struct pakfire_file
* file
, time_t time
) {
816 file
->st
.st_ctime
= time
;
819 PAKFIRE_EXPORT
time_t pakfire_file_get_mtime(struct pakfire_file
* file
) {
820 return file
->st
.st_mtime
;
823 PAKFIRE_EXPORT
void pakfire_file_set_mtime(struct pakfire_file
* file
, time_t time
) {
824 file
->st
.st_mtime
= time
;
827 PAKFIRE_EXPORT
const unsigned char* pakfire_file_get_digest(
828 struct pakfire_file
* file
, const enum pakfire_digest_types type
, size_t* length
) {
831 case PAKFIRE_DIGEST_SHA3_512
:
832 if (!pakfire_digest_set(file
->digests
.sha3_512
))
836 *length
= sizeof(file
->digests
.sha3_512
);
838 return file
->digests
.sha3_512
;
840 case PAKFIRE_DIGEST_SHA3_256
:
841 if (!pakfire_digest_set(file
->digests
.sha3_256
))
845 *length
= sizeof(file
->digests
.sha3_256
);
847 return file
->digests
.sha3_256
;
849 case PAKFIRE_DIGEST_BLAKE2B512
:
850 if (!pakfire_digest_set(file
->digests
.blake2b512
))
854 *length
= sizeof(file
->digests
.blake2b512
);
856 return file
->digests
.blake2b512
;
858 case PAKFIRE_DIGEST_BLAKE2S256
:
859 if (!pakfire_digest_set(file
->digests
.blake2s256
))
863 *length
= sizeof(file
->digests
.blake2s256
);
865 return file
->digests
.blake2s256
;
867 case PAKFIRE_DIGEST_SHA2_512
:
868 if (!pakfire_digest_set(file
->digests
.sha2_512
))
872 *length
= sizeof(file
->digests
.sha2_512
);
874 return file
->digests
.sha2_512
;
876 case PAKFIRE_DIGEST_SHA2_256
:
877 if (!pakfire_digest_set(file
->digests
.sha2_256
))
881 *length
= sizeof(file
->digests
.sha2_256
);
883 return file
->digests
.sha2_256
;
885 case PAKFIRE_DIGEST_UNDEFINED
:
892 PAKFIRE_EXPORT
int pakfire_file_set_digest(struct pakfire_file
* file
,
893 const enum pakfire_digest_types type
, const unsigned char* digest
, const size_t length
) {
899 // Check buffer length
900 if (pakfire_digest_length(type
) != length
) {
901 ERROR(file
->pakfire
, "Digest has an incorrect length of %zu byte(s)\n", length
);
908 case PAKFIRE_DIGEST_SHA3_512
:
909 memcpy(file
->digests
.sha3_512
, digest
, sizeof(file
->digests
.sha3_512
));
912 case PAKFIRE_DIGEST_SHA3_256
:
913 memcpy(file
->digests
.sha3_256
, digest
, sizeof(file
->digests
.sha3_256
));
916 case PAKFIRE_DIGEST_BLAKE2B512
:
917 memcpy(file
->digests
.blake2b512
, digest
, sizeof(file
->digests
.blake2b512
));
920 case PAKFIRE_DIGEST_BLAKE2S256
:
921 memcpy(file
->digests
.blake2s256
, digest
, sizeof(file
->digests
.blake2s256
));
924 case PAKFIRE_DIGEST_SHA2_512
:
925 memcpy(file
->digests
.sha2_512
, digest
, sizeof(file
->digests
.sha2_512
));
928 case PAKFIRE_DIGEST_SHA2_256
:
929 memcpy(file
->digests
.sha2_256
, digest
, sizeof(file
->digests
.sha2_256
));
932 case PAKFIRE_DIGEST_UNDEFINED
:
940 static int pakfire_file_levels(struct pakfire_file
* file
) {
946 for (char* p
= file
->path
; *p
; p
++) {
954 FILE* pakfire_file_open(struct pakfire_file
* file
) {
955 FILE* f
= fopen(file
->abspath
, "r");
957 ERROR(file
->pakfire
, "Could not open %s: %m\n", file
->abspath
);
962 static int __pakfire_file_compute_digests(struct pakfire_file
* file
,
963 struct pakfire_digests
* digests
, const int types
) {
967 // Skip this for anything that isn't a regular file
968 if (!S_ISREG(file
->st
.st_mode
))
972 pakfire_digests_reset(digests
, types
);
975 f
= pakfire_file_open(file
);
980 r
= pakfire_digests_compute_from_file(file
->pakfire
, digests
, types
, f
);
991 int pakfire_file_compute_digests(struct pakfire_file
* file
, const int types
) {
992 return __pakfire_file_compute_digests(file
, &file
->digests
, types
);
995 int pakfire_file_remove(struct pakfire_file
* file
) {
996 if (!*file
->abspath
) {
1001 DEBUG(file
->pakfire
, "Removing %s...\n", file
->path
);
1003 int r
= remove(file
->abspath
);
1006 // Ignore when we could not remove directories
1010 // Ignore if the file didn't exist
1018 ERROR(file
->pakfire
, "Could not remove %s (%s): %m\n", file
->path
, file
->abspath
);
1024 int pakfire_file_symlink_target_exists(struct pakfire_file
* file
) {
1025 // Fail if this got called for anything that isn't a symlink
1026 if (!S_ISLNK(file
->st
.st_mode
)) {
1031 return pakfire_path_exists(file
->abspath
);
1038 int pakfire_file_detect_mimetype(struct pakfire_file
* file
) {
1039 // Only process regular files
1040 if (!S_ISREG(file
->st
.st_mode
))
1043 // Skip if MIME type is already set
1044 if (*file
->mimetype
)
1047 // Fetch the magic cookie
1048 magic_t magic
= pakfire_get_magic(file
->pakfire
);
1053 const char* mimetype
= magic_file(magic
, file
->abspath
);
1055 ERROR(file
->pakfire
, "Could not classify %s: %s\n", file
->path
, magic_error(magic
));
1059 DEBUG(file
->pakfire
, "Classified %s as %s\n", file
->path
, mimetype
);
1062 return pakfire_file_set_mimetype(file
, mimetype
);
1065 PAKFIRE_EXPORT
const char* pakfire_file_get_mimetype(struct pakfire_file
* file
) {
1066 // Return nothing on an empty mimetype
1067 if (!*file
->mimetype
)
1070 return file
->mimetype
;
1073 PAKFIRE_EXPORT
int pakfire_file_set_mimetype(
1074 struct pakfire_file
* file
, const char* mimetype
) {
1076 return pakfire_string_set(file
->mimetype
, mimetype
);
1083 static int setup_libelf(struct pakfire
* pakfire
) {
1084 // Initialize libelf
1085 if (elf_version(EV_CURRENT
) == EV_NONE
) {
1086 ERROR(pakfire
, "Could not initialize libelf: %s\n", elf_errmsg(-1));
1094 static int pakfire_file_classify_mode(struct pakfire_file
* file
) {
1095 // Check for regular files
1096 if (S_ISREG(file
->st
.st_mode
))
1097 file
->class |= PAKFIRE_FILE_REGULAR
;
1099 // Check for directories
1100 else if (S_ISDIR(file
->st
.st_mode
))
1101 file
->class |= PAKFIRE_FILE_DIRECTORY
;
1103 // Check for symlinks
1104 else if (S_ISLNK(file
->st
.st_mode
))
1105 file
->class |= PAKFIRE_FILE_SYMLINK
;
1107 // Check for character devices
1108 else if (S_ISCHR(file
->st
.st_mode
))
1109 file
->class |= PAKFIRE_FILE_CHARACTER
;
1111 // Check for block devices
1112 else if (S_ISBLK(file
->st
.st_mode
))
1113 file
->class |= PAKFIRE_FILE_BLOCK
;
1115 // Check for FIFO pipes
1116 else if (S_ISFIFO(file
->st
.st_mode
))
1117 file
->class |= PAKFIRE_FILE_FIFO
;
1119 // Check for sockets
1120 else if (S_ISSOCK(file
->st
.st_mode
))
1121 file
->class |= PAKFIRE_FILE_SOCKET
;
1126 static const struct extension
{
1127 const char* extension
;
1130 { "*.a", PAKFIRE_FILE_STATIC_LIBRARY
},
1131 { "*.la", PAKFIRE_FILE_LIBTOOL_ARCHIVE
},
1132 { "*.pm", PAKFIRE_FILE_PERL
},
1133 { "*.pc", PAKFIRE_FILE_PKGCONFIG
},
1137 static int pakfire_file_classify_extension(struct pakfire_file
* file
) {
1138 for (const struct extension
* e
= extensions
; e
->extension
; e
++) {
1139 if (pakfire_file_matches(file
, e
->extension
)) {
1140 file
->class |= e
->class;
1148 static const struct mimetype
{
1149 const char* mimetype
;
1152 { "application/x-pie-executable", PAKFIRE_FILE_EXECUTABLE
},
1153 { "application/x-sharedlibary", PAKFIRE_FILE_EXECUTABLE
},
1154 { "text/x-perl", PAKFIRE_FILE_PERL
},
1158 static int pakfire_file_classify_magic(struct pakfire_file
* file
) {
1161 // Don't run this if the file has already been classified
1162 if (file
->class & ~PAKFIRE_FILE_REGULAR
)
1165 // Detect the MIME type
1166 r
= pakfire_file_detect_mimetype(file
);
1170 // Fetch the MIME type
1171 const char* mimetype
= pakfire_file_get_mimetype(file
);
1175 // Match the MIME type with a flag
1176 for (const struct mimetype
* m
= mimetypes
; m
->mimetype
; m
++) {
1177 if (strcmp(m
->mimetype
, mimetype
) == 0) {
1178 file
->class |= m
->class;
1186 static int pakfire_file_classify_elf(struct pakfire_file
* file
) {
1191 // Don't run this if we already know that file is an ELF file
1192 if (file
->class & PAKFIRE_FILE_ELF
)
1196 r
= setup_libelf(file
->pakfire
);
1201 f
= fopen(file
->abspath
, "r");
1203 ERROR(file
->pakfire
, "Could not open %s: %m\n", file
->path
);
1207 // Try to open the ELF file
1208 elf
= elf_begin(fileno(f
), ELF_C_READ
, NULL
);
1210 // We fail silently here, because this file might be in a different format
1214 switch (elf_kind(elf
)) {
1215 // Mark this file as an ELF file
1217 file
->class |= PAKFIRE_FILE_ELF
;
1220 // Ignore everything else
1234 int pakfire_file_classify(struct pakfire_file
* file
) {
1238 // First, check the mode so that we won't run magic on directories, symlinks, ...
1239 r
= pakfire_file_classify_mode(file
);
1243 // Only run this for regular files
1244 if (file
->class & PAKFIRE_FILE_REGULAR
) {
1245 // Then check for the extension
1246 r
= pakfire_file_classify_extension(file
);
1250 // After that, we will use libmagic...
1251 r
= pakfire_file_classify_magic(file
);
1255 // Check if the file is an ELF file
1256 r
= pakfire_file_classify_elf(file
);
1266 file
->class = PAKFIRE_FILE_UNKNOWN
;
1271 int pakfire_file_matches_class(struct pakfire_file
* file
, const int class) {
1272 return pakfire_file_classify(file
) & class;
1276 This function tries to remove the file after it has been packaged.
1278 It will try to delete any parent directories as well and ignore if directories
1279 cannot be deleted because they might contain other files
1281 int pakfire_file_cleanup(struct pakfire_file
* file
) {
1282 char path
[PATH_MAX
];
1284 // Try removing the file
1285 int r
= pakfire_file_remove(file
);
1289 // Create a working copy of abspath
1290 r
= pakfire_string_set(path
, file
->abspath
);
1294 // See how many levels this file has
1295 int levels
= pakfire_file_levels(file
);
1297 // Walk all the way up and remove all parent directories if possible
1301 // Break if path is suddenly empty
1305 DEBUG(file
->pakfire
, "Trying to remove parent directory %s\n", path
);
1309 // Break on any error
1317 static int pakfire_file_verify_mode(struct pakfire_file
* file
, const struct stat
* st
) {
1318 const mode_t type
= pakfire_file_get_type(file
);
1320 // Did the type change?
1321 if (type
!= (st
->st_mode
& S_IFMT
)) {
1322 file
->verify_status
|= PAKFIRE_FILE_TYPE_CHANGED
;
1324 DEBUG(file
->pakfire
, "%s: File Type changed\n", file
->path
);
1327 const mode_t perms
= pakfire_file_get_perms(file
);
1329 // Check permissions
1330 if (perms
!= (st
->st_mode
& 0777)) {
1331 file
->verify_status
|= PAKFIRE_FILE_PERMISSIONS_CHANGED
;
1333 DEBUG(file
->pakfire
, "%s: Permissions changed\n", file
->path
);
1337 // XXX This does not check what it is supposed to check
1339 // Check if device node changed
1340 if (S_ISCHR(st
->st_mode
) || S_ISBLK(st
->st_mode
)) {
1341 const dev_t dev
= pakfire_file_get_dev(file
);
1343 if (dev
!= st
->st_dev
) {
1344 file
->verify_status
|= PAKFIRE_FILE_DEV_CHANGED
;
1346 DEBUG(file
->pakfire
, "%s: Device Node changed\n", file
->path
);
1354 static int pakfire_file_verify_size(struct pakfire_file
* file
, const struct stat
* st
) {
1355 // Nothing to do if size matches
1356 if (file
->st
.st_size
== st
->st_size
)
1360 file
->verify_status
|= PAKFIRE_FILE_SIZE_CHANGED
;
1362 DEBUG(file
->pakfire
, "%s: Filesize differs (expected %zu, got %zu byte(s))\n",
1363 file
->path
, file
->st
.st_size
, st
->st_size
);
1368 static int pakfire_file_verify_ownership(struct pakfire_file
* file
, const struct stat
* st
) {
1371 const uid_t uid
= pakfire_unmap_id(file
->pakfire
, st
->st_uid
);
1372 const gid_t gid
= pakfire_unmap_id(file
->pakfire
, st
->st_gid
);
1374 const uid_t uid
= st
->st_uid
;
1375 const gid_t gid
= st
->st_gid
;
1378 // Fetch owner & group
1379 struct passwd
* owner
= pakfire_getpwnam(file
->pakfire
, file
->uname
);
1380 struct group
* group
= pakfire_getgrnam(file
->pakfire
, file
->gname
);
1382 // Check if owner matches
1383 if (!owner
|| owner
->pw_uid
!= uid
) {
1384 file
->verify_status
|= PAKFIRE_FILE_OWNER_CHANGED
;
1386 DEBUG(file
->pakfire
, "%s: Owner differs\n", file
->path
);
1389 // Check if group matches
1390 if (!group
|| group
->gr_gid
!= gid
) {
1391 file
->verify_status
|= PAKFIRE_FILE_GROUP_CHANGED
;
1393 DEBUG(file
->pakfire
, "%s: Group differs\n", file
->path
);
1399 static int pakfire_file_verify_timestamps(struct pakfire_file
* file
, const struct stat
* st
) {
1400 // Check creation time
1401 if (file
->st
.st_ctime
!= st
->st_ctime
) {
1402 file
->verify_status
|= PAKFIRE_FILE_CTIME_CHANGED
;
1404 DEBUG(file
->pakfire
, "%s: Creation time changed\n", file
->path
);
1407 // Check modification time
1408 if (file
->st
.st_mtime
!= st
->st_mtime
) {
1409 file
->verify_status
|= PAKFIRE_FILE_MTIME_CHANGED
;
1411 DEBUG(file
->pakfire
, "%s: Modification time changed\n", file
->path
);
1417 static int pakfire_file_verify_payload(struct pakfire_file
* file
, const struct stat
* st
) {
1420 struct pakfire_digests computed_digests
;
1421 int digest_types
= PAKFIRE_DIGEST_UNDEFINED
;
1423 // Nothing to do for anything that isn't a regular file
1424 if (!S_ISREG(st
->st_mode
))
1427 // Fast-path if size changed. The payload will have changed, too
1428 if (file
->verify_status
& PAKFIRE_FILE_SIZE_CHANGED
) {
1429 file
->verify_status
|= PAKFIRE_FILE_PAYLOAD_CHANGED
;
1433 // Check if this file has any digests at all
1434 digest_types
= pakfire_digest_has_any(&file
->digests
);
1436 if (!digest_types
) {
1437 ERROR(file
->pakfire
, "%s: No digests available\n", file
->path
);
1442 r
= __pakfire_file_compute_digests(file
, &computed_digests
, digest_types
);
1447 r
= pakfire_digests_compare(file
->pakfire
, &file
->digests
, &computed_digests
, digest_types
);
1449 file
->verify_status
|= PAKFIRE_FILE_PAYLOAD_CHANGED
;
1451 DEBUG(file
->pakfire
, "%s: Digest(s) do not match\n", file
->path
);
1459 Verify the file - i.e. does the metadata match what is on disk?
1461 int pakfire_file_verify(struct pakfire_file
* file
, int* status
) {
1465 DEBUG(file
->pakfire
, "Verifying %s...\n", file
->path
);
1468 r
= lstat(file
->abspath
, &st
);
1470 // File does not exist
1471 if (errno
== ENOENT
) {
1472 file
->verify_status
|= PAKFIRE_FILE_NOENT
;
1476 // Raise any other errors from stat()
1481 r
= pakfire_file_verify_mode(file
, &st
);
1486 r
= pakfire_file_verify_size(file
, &st
);
1491 r
= pakfire_file_verify_ownership(file
, &st
);
1495 // Verify timestamps
1496 r
= pakfire_file_verify_timestamps(file
, &st
);
1501 r
= pakfire_file_verify_payload(file
, &st
);
1508 PAKFIRE_EXPORT
int pakfire_file_matches(struct pakfire_file
* file
, const char* pattern
) {
1511 // Don't match on no pattern
1515 // Check if the pattern matches
1516 r
= fnmatch(pattern
, file
->path
, 0);
1535 static int pakfire_file_open_elf(struct pakfire_file
* file
,
1536 int (*callback
)(struct pakfire_file
* file
, Elf
* elf
, void* data
), void* data
) {
1541 // Don't run this for non-ELF files
1542 if (!pakfire_file_matches_class(file
, PAKFIRE_FILE_ELF
)) {
1548 r
= setup_libelf(file
->pakfire
);
1553 f
= fopen(file
->abspath
, "r");
1555 ERROR(file
->pakfire
, "Could not open %s: %m\n", file
->abspath
);
1559 // Parse the ELF header
1560 elf
= elf_begin(fileno(f
), ELF_C_READ
, NULL
);
1562 ERROR(file
->pakfire
, "Could not open ELF file: %s\n", elf_errmsg(-1));
1567 // Check if this is an ELF file
1568 switch (elf_kind(elf
)) {
1573 ERROR(file
->pakfire
, "%s is not an ELF object\n", file
->path
);
1578 // Call the callback
1579 r
= callback(file
, elf
, data
);
1590 static int __pakfire_file_is_stripped(struct pakfire_file
* file
, Elf
* elf
, void* data
) {
1591 Elf_Scn
* section
= NULL
;
1594 // Walk through all sections
1596 section
= elf_nextscn(elf
, section
);
1600 // Fetch the section header
1601 gelf_getshdr(section
, &shdr
);
1603 switch (shdr
.sh_type
) {
1604 // Break if we found the symbol table
1611 DEBUG(file
->pakfire
, "%s has no debug sections\n", file
->path
);
1616 int pakfire_file_is_stripped(struct pakfire_file
* file
) {
1617 // Don't run this for non-ELF files
1618 if (!pakfire_file_matches_class(file
, PAKFIRE_FILE_ELF
)) {
1623 return pakfire_file_open_elf(file
, __pakfire_file_is_stripped
, NULL
);
1626 static int __pakfire_file_hardening_check_ssp(
1627 struct pakfire_file
* file
, Elf
* elf
, void* data
) {
1628 Elf_Scn
* section
= NULL
;
1629 GElf_Shdr section_header
;
1630 Elf_Data
* elf_data
= NULL
;
1632 const char* name
= NULL
;
1634 // Walk through all sections
1636 section
= elf_nextscn(elf
, section
);
1638 ERROR(file
->pakfire
, "%s has no symbol table\n", file
->path
);
1642 // Fetch the section header
1643 gelf_getshdr(section
, §ion_header
);
1645 // Break if we found the symbol table
1646 if (section_header
.sh_type
== SHT_SYMTAB
)
1650 // Fetch a pointer to the section data
1651 elf_data
= elf_getdata(section
, NULL
);
1653 // Walk through all symbols
1654 for (unsigned int i
= 0; i
< section_header
.sh_size
/ section_header
.sh_entsize
; i
++) {
1655 gelf_getsym(elf_data
, i
, &symbol
);
1657 // Fetch the symbol name
1658 name
= elf_strptr(elf
, section_header
.sh_link
, symbol
.st_name
);
1660 // Skip empty section names
1661 if (!name
|| !*name
)
1664 // Exit if there is a symbol called "__stack_chk_fail"
1665 if (pakfire_string_startswith(name
, "__stack_chk_fail"))
1669 // The file does not seem to have SSP enabled
1670 file
->hardening_issues
|= PAKFIRE_FILE_NO_SSP
;
1675 static int pakfire_file_hardening_check_ssp(struct pakfire_file
* file
) {
1676 return pakfire_file_open_elf(file
, __pakfire_file_hardening_check_ssp
, NULL
);
1679 static int __pakfire_file_hardening_check_pie(
1680 struct pakfire_file
* file
, Elf
* elf
, void* data
) {
1683 // Fetch the ELF header
1684 if (!gelf_getehdr(elf
, &eheader
)) {
1685 ERROR(file
->pakfire
, "Could not parse ELF header: %s\n", elf_errmsg(-1));
1689 // Check for the correct header type
1690 switch (eheader
.e_type
) {
1691 // Dynamic Objects are good
1695 // Anything else is bad
1697 file
->hardening_issues
|= PAKFIRE_FILE_NO_PIE
;
1704 static int pakfire_file_hardening_check_pie(struct pakfire_file
* file
) {
1705 return pakfire_file_open_elf(file
, __pakfire_file_hardening_check_pie
, NULL
);
1708 static int __pakfire_file_hardening_check_execstack(
1709 struct pakfire_file
* file
, Elf
* elf
, void* data
) {
1715 // Fetch the total numbers of program headers
1716 r
= elf_getphdrnum(elf
, &phnum
);
1718 ERROR(file
->pakfire
, "Could not fetch number of program headers: %s\n",
1723 // Walk through all program headers
1724 for (unsigned int i
= 0; i
< phnum
; i
++) {
1725 if (!gelf_getphdr(elf
, i
, &phdr
)) {
1726 ERROR(file
->pakfire
, "Could not parse program header: %s\n", elf_errmsg(-1));
1730 switch (phdr
.p_type
) {
1732 DEBUG(file
->pakfire
,
1733 "%s: GNU_STACK flags: %c%c%c\n",
1735 (phdr
.p_flags
& PF_R
) ? 'R' : '-',
1736 (phdr
.p_flags
& PF_W
) ? 'W' : '-',
1737 (phdr
.p_flags
& PF_X
) ? 'X' : '-'
1740 // The stack cannot be writable and executable
1741 if ((phdr
.p_flags
& PF_W
) && (phdr
.p_flags
& PF_X
))
1742 file
->hardening_issues
|= PAKFIRE_FILE_EXECSTACK
;
1755 static int pakfire_file_hardening_check_execstack(struct pakfire_file
* file
) {
1756 return pakfire_file_open_elf(file
, __pakfire_file_hardening_check_execstack
, NULL
);
1759 static __pakfire_file_hardening_check_partially_relro(
1760 struct pakfire_file
* file
, Elf
* elf
, void* data
) {
1766 // Fetch the total numbers of program headers
1767 r
= elf_getphdrnum(elf
, &phnum
);
1769 ERROR(file
->pakfire
, "Could not fetch number of program headers: %s\n",
1774 // Walk through all program headers
1775 for (unsigned int i
= 0; i
< phnum
; i
++) {
1776 if (!gelf_getphdr(elf
, i
, &phdr
)) {
1777 ERROR(file
->pakfire
, "Could not parse program header: %s\n", elf_errmsg(-1));
1781 switch (phdr
.p_type
) {
1790 // This file does not seem to have PT_GNU_RELRO set
1791 file
->hardening_issues
|= PAKFIRE_FILE_NO_PARTIALLY_RELRO
;
1796 static int pakfire_file_hardening_check_relro(struct pakfire_file
* file
) {
1797 return pakfire_file_open_elf(file
, __pakfire_file_hardening_check_partially_relro
, NULL
);
1801 int pakfire_file_check_hardening(struct pakfire_file
* file
, int* issues
) {
1804 // Return previous result if this has been run before
1805 if (!file
->hardening_check_done
) {
1807 r
= pakfire_file_hardening_check_ssp(file
);
1812 r
= pakfire_file_hardening_check_pie(file
);
1816 // Check for executable stacks
1817 r
= pakfire_file_hardening_check_execstack(file
);
1822 r
= pakfire_file_hardening_check_relro(file
);
1827 file
->hardening_check_done
= 1;
1830 // Return any issues
1832 *issues
= file
->hardening_issues
;