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 #############################################################################*/
23 #include <linux/limits.h>
27 #include <sys/types.h>
30 #include <archive_entry.h>
31 #include <openssl/err.h>
32 #include <openssl/evp.h>
33 #include <openssl/sha.h>
35 #include <pakfire/constants.h>
36 #include <pakfire/file.h>
37 #include <pakfire/logging.h>
38 #include <pakfire/pakfire.h>
39 #include <pakfire/private.h>
40 #include <pakfire/string.h>
41 #include <pakfire/util.h>
43 enum pakfire_file_verification_status
{
44 PAKFIRE_FILE_NOENT
= (1 << 0),
45 PAKFIRE_FILE_TYPE_CHANGED
= (1 << 1),
46 PAKFIRE_FILE_PERMISSIONS_CHANGED
= (1 << 2),
47 PAKFIRE_FILE_DEV_CHANGED
= (1 << 3),
48 PAKFIRE_FILE_SIZE_CHANGED
= (1 << 4),
49 PAKFIRE_FILE_OWNER_CHANGED
= (1 << 5),
50 PAKFIRE_FILE_GROUP_CHANGED
= (1 << 6),
51 PAKFIRE_FILE_CTIME_CHANGED
= (1 << 7),
52 PAKFIRE_FILE_MTIME_CHANGED
= (1 << 8),
53 PAKFIRE_FILE_PAYLOAD_CHANGED
= (1 << 9),
57 struct pakfire
* pakfire
;
63 // The absolute path in the file system
64 char abspath
[PATH_MAX
];
67 char user
[LOGIN_NAME_MAX
];
68 char group
[LOGIN_NAME_MAX
];
74 char hardlink
[PATH_MAX
];
75 char symlink
[PATH_MAX
];
78 struct pakfire_file_digests
{
80 unsigned char sha512
[SHA512_DIGEST_LENGTH
];
83 unsigned char sha256
[SHA256_DIGEST_LENGTH
];
86 // Verification Status
89 #warning TODO capabilities, config, data
96 Returns one if the digest is not all zeros.
98 #define pakfire_file_has_digest(digest) __pakfire_file_has_digest(digest, sizeof(digest))
100 static int __pakfire_file_has_digest(const unsigned char* digest
, const size_t length
) {
101 for (unsigned int i
= 0; i
< length
; i
++) {
109 static int pakfire_file_from_archive_entry(struct pakfire_file
* file
, struct archive_entry
* entry
) {
110 const char* attr
= NULL
;
111 const void* value
= NULL
;
116 r
= pakfire_file_set_abspath(file
, archive_entry_sourcepath(entry
));
118 ERROR(file
->pakfire
, "Could not set abspath: %m\n");
123 const char* path
= archive_entry_pathname(entry
);
125 // Strip any leading dots from paths
126 if (pakfire_string_startswith(path
, "./"))
129 r
= pakfire_file_set_path(file
, path
);
131 ERROR(file
->pakfire
, "Could not set path: %m\n");
137 pakfire_file_set_hardlink(file
, archive_entry_hardlink(entry
));
138 pakfire_file_set_symlink(file
, archive_entry_symlink(entry
));
141 pakfire_file_set_size(file
, archive_entry_size(entry
));
144 pakfire_file_set_mode(file
, archive_entry_mode(entry
));
147 if (archive_entry_dev_is_set(entry
))
148 pakfire_file_set_dev(file
, archive_entry_dev(entry
));
151 pakfire_file_set_user(file
, archive_entry_uname(entry
));
154 pakfire_file_set_group(file
, archive_entry_gname(entry
));
157 pakfire_file_set_ctime(file
, archive_entry_ctime(entry
));
158 pakfire_file_set_mtime(file
, archive_entry_mtime(entry
));
160 // Read any extended attributes
161 while (archive_entry_xattr_next(entry
, &attr
, &value
, &size
) == ARCHIVE_OK
) {
163 if (strcmp(attr
, "PAKFIRE.digests.sha512") == 0) {
164 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA512
, value
, size
);
169 } else if (strcmp(attr
, "PAKFIRE.digests.sha256") == 0) {
170 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA256
, value
, size
);
175 DEBUG(file
->pakfire
, "Received an unknown extended attribute: %s\n", attr
);
183 PAKFIRE_EXPORT
int pakfire_file_create(struct pakfire_file
** file
, struct pakfire
* pakfire
) {
184 struct pakfire_file
* f
= calloc(1, sizeof(*f
));
188 // Store reference to Pakfire
189 f
->pakfire
= pakfire_ref(pakfire
);
191 // Initialize reference counter
198 int pakfire_file_create_from_path(struct pakfire_file
** file
,
199 struct pakfire
* pakfire
, const char* path
) {
200 struct archive
* reader
= NULL
;
201 struct archive_entry
* entry
= NULL
;
205 reader
= pakfire_make_archive_disk_reader(pakfire
, 0);
209 // Allocate a new archive entry
210 entry
= archive_entry_new();
215 archive_entry_copy_sourcepath(entry
, path
);
217 // Read all file attributes from disk
218 r
= archive_read_disk_entry_from_file(reader
, entry
, -1, NULL
);
220 ERROR(pakfire
, "Could not read from %s: %m\n", path
);
225 r
= pakfire_file_create_from_archive_entry(file
, pakfire
, entry
);
231 ERROR(pakfire
, "Could not create file from path %s: %m\n", path
);
233 archive_entry_free(entry
);
235 archive_read_free(reader
);
240 int pakfire_file_create_from_archive_entry(struct pakfire_file
** file
, struct pakfire
* pakfire
,
241 struct archive_entry
* entry
) {
242 int r
= pakfire_file_create(file
, pakfire
);
246 // Copy archive entry
247 r
= pakfire_file_from_archive_entry(*file
, entry
);
254 pakfire_file_unref(*file
);
260 struct archive_entry
* pakfire_file_archive_entry(struct pakfire_file
* file
) {
261 struct archive_entry
* entry
= archive_entry_new();
263 ERROR(file
->pakfire
, "Could not allocate archive entry: %m\n");
268 archive_entry_copy_pathname(entry
, pakfire_file_get_path(file
));
271 archive_entry_copy_sourcepath(entry
, file
->abspath
);
275 archive_entry_set_hardlink(entry
, file
->hardlink
);
277 archive_entry_set_symlink(entry
, file
->symlink
);
280 archive_entry_set_size(entry
, pakfire_file_get_size(file
));
283 archive_entry_set_mode(entry
, pakfire_file_get_mode(file
));
286 archive_entry_set_uname(entry
, pakfire_file_get_user(file
));
289 archive_entry_set_gname(entry
, pakfire_file_get_group(file
));
292 archive_entry_set_ctime(entry
, pakfire_file_get_ctime(file
), 0);
293 archive_entry_set_mtime(entry
, pakfire_file_get_mtime(file
), 0);
298 if (pakfire_file_has_digest(file
->digests
.sha512
))
299 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha512",
300 file
->digests
.sha512
, sizeof(file
->digests
.sha512
));
303 if (pakfire_file_has_digest(file
->digests
.sha256
))
304 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha256",
305 file
->digests
.sha256
, sizeof(file
->digests
.sha256
));
310 static void pakfire_file_free(struct pakfire_file
* file
) {
311 pakfire_unref(file
->pakfire
);
315 PAKFIRE_EXPORT
struct pakfire_file
* pakfire_file_ref(struct pakfire_file
* file
) {
321 PAKFIRE_EXPORT
struct pakfire_file
* pakfire_file_unref(struct pakfire_file
* file
) {
322 if (--file
->nrefs
> 0)
325 pakfire_file_free(file
);
329 PAKFIRE_EXPORT
int pakfire_file_cmp(struct pakfire_file
* file1
, struct pakfire_file
* file2
) {
330 const char* path1
= pakfire_file_get_path(file1
);
331 const char* path2
= pakfire_file_get_path(file2
);
333 return strcmp(path1
, path2
);
336 const char* pakfire_file_get_abspath(struct pakfire_file
* file
) {
337 return file
->abspath
;
340 int pakfire_file_set_abspath(struct pakfire_file
* file
, const char* path
) {
341 // Check if path is set and absolute
342 if (!path
|| *path
!= '/') {
347 return pakfire_string_set(file
->abspath
, path
);
350 PAKFIRE_EXPORT
const char* pakfire_file_get_path(struct pakfire_file
* file
) {
354 PAKFIRE_EXPORT
int pakfire_file_set_path(struct pakfire_file
* file
, const char* path
) {
357 // Check if path is set and absolute
358 if (!path
|| *path
!= '/') {
364 r
= pakfire_string_set(file
->path
, path
);
368 // Set abspath if it isn't set, yet
369 if (!*file
->abspath
) {
370 r
= pakfire_path(file
->pakfire
, file
->abspath
, "%s", path
);
378 PAKFIRE_EXPORT
const char* pakfire_file_get_hardlink(struct pakfire_file
* file
) {
379 if (!*file
->hardlink
)
382 return file
->hardlink
;
385 PAKFIRE_EXPORT
void pakfire_file_set_hardlink(struct pakfire_file
* file
, const char* link
) {
387 *file
->hardlink
= '\0';
389 pakfire_string_set(file
->hardlink
, link
);
392 PAKFIRE_EXPORT
const char* pakfire_file_get_symlink(struct pakfire_file
* file
) {
396 return file
->symlink
;
399 PAKFIRE_EXPORT
void pakfire_file_set_symlink(struct pakfire_file
* file
, const char* link
) {
401 *file
->hardlink
= '\0';
403 pakfire_string_set(file
->symlink
, link
);
406 PAKFIRE_EXPORT
int pakfire_file_get_type(struct pakfire_file
* file
) {
407 return file
->st
.st_mode
& S_IFMT
;
410 PAKFIRE_EXPORT off_t
pakfire_file_get_size(struct pakfire_file
* file
) {
411 return file
->st
.st_size
;
414 PAKFIRE_EXPORT
void pakfire_file_set_size(struct pakfire_file
* file
, off_t size
) {
415 file
->st
.st_size
= size
;
418 PAKFIRE_EXPORT
const char* pakfire_file_get_user(struct pakfire_file
* file
) {
422 PAKFIRE_EXPORT
int pakfire_file_set_user(struct pakfire_file
* file
, const char* user
) {
423 return pakfire_string_set(file
->user
, user
);
426 PAKFIRE_EXPORT
const char* pakfire_file_get_group(struct pakfire_file
* file
) {
430 PAKFIRE_EXPORT
int pakfire_file_set_group(struct pakfire_file
* file
, const char* group
) {
431 return pakfire_string_set(file
->group
, group
);
434 PAKFIRE_EXPORT mode_t
pakfire_file_get_mode(struct pakfire_file
* file
) {
435 return file
->st
.st_mode
;
438 PAKFIRE_EXPORT
void pakfire_file_set_mode(struct pakfire_file
* file
, mode_t mode
) {
439 file
->st
.st_mode
= mode
;
442 PAKFIRE_EXPORT mode_t
pakfire_file_get_perms(struct pakfire_file
* file
) {
443 return file
->st
.st_mode
& ~AE_IFMT
;
446 PAKFIRE_EXPORT
void pakfire_file_set_perms(struct pakfire_file
* file
, const mode_t perms
) {
447 // Clear any previous permissions
448 file
->st
.st_mode
&= S_IFMT
;
450 // Set new bits (with format cleared)
451 file
->st
.st_mode
|= ~S_IFMT
& perms
;
454 PAKFIRE_EXPORT dev_t
pakfire_file_get_dev(struct pakfire_file
* file
) {
455 return file
->st
.st_dev
;
458 PAKFIRE_EXPORT
void pakfire_file_set_dev(struct pakfire_file
* file
, dev_t dev
) {
459 file
->st
.st_dev
= dev
;
462 PAKFIRE_EXPORT
time_t pakfire_file_get_ctime(struct pakfire_file
* file
) {
463 return file
->st
.st_ctime
;
466 PAKFIRE_EXPORT
void pakfire_file_set_ctime(struct pakfire_file
* file
, time_t time
) {
467 file
->st
.st_ctime
= time
;
470 PAKFIRE_EXPORT
time_t pakfire_file_get_mtime(struct pakfire_file
* file
) {
471 return file
->st
.st_mtime
;
474 PAKFIRE_EXPORT
void pakfire_file_set_mtime(struct pakfire_file
* file
, time_t time
) {
475 file
->st
.st_mtime
= time
;
478 PAKFIRE_EXPORT
const unsigned char* pakfire_file_get_digest(
479 struct pakfire_file
* file
, const enum pakfire_digests type
, size_t* length
) {
482 case PAKFIRE_DIGEST_SHA512
:
483 if (!pakfire_file_has_digest(file
->digests
.sha512
))
487 *length
= sizeof(file
->digests
.sha512
);
489 return file
->digests
.sha512
;
491 case PAKFIRE_DIGEST_SHA256
:
492 if (!pakfire_file_has_digest(file
->digests
.sha256
))
496 *length
= sizeof(file
->digests
.sha256
);
498 return file
->digests
.sha256
;
504 PAKFIRE_EXPORT
int pakfire_file_set_digest(struct pakfire_file
* file
,
505 const enum pakfire_digests type
, const unsigned char* digest
, const size_t length
) {
511 // Check buffer length
512 if (pakfire_digest_length(type
) != length
) {
513 ERROR(file
->pakfire
, "Digest has an incorrect length of %zu byte(s)\n", length
);
520 case PAKFIRE_DIGEST_SHA512
:
521 memcpy(file
->digests
.sha512
, digest
, sizeof(file
->digests
.sha512
));
524 case PAKFIRE_DIGEST_SHA256
:
525 memcpy(file
->digests
.sha256
, digest
, sizeof(file
->digests
.sha256
));
532 PAKFIRE_EXPORT
char* pakfire_file_get_hexdigest(
533 struct pakfire_file
* file
, const enum pakfire_digests type
) {
534 const unsigned char* digest
= NULL
;
538 digest
= pakfire_file_get_digest(file
, type
, &length
);
542 return __pakfire_hexlify(digest
, length
);
545 static int pakfire_file_levels(struct pakfire_file
* file
) {
551 for (char* p
= file
->path
; *p
; p
++) {
559 FILE* pakfire_file_open(struct pakfire_file
* file
) {
560 FILE* f
= fopen(file
->abspath
, "r");
562 ERROR(file
->pakfire
, "Could not open %s: %m\n", file
->abspath
);
567 int pakfire_file_remove(struct pakfire_file
* file
) {
568 if (!*file
->abspath
) {
573 DEBUG(file
->pakfire
, "Removing %s...\n", file
->path
);
575 int r
= remove(file
->abspath
);
578 // Ignore when we could not remove directories
582 // Ignore if the file didn't exist
590 ERROR(file
->pakfire
, "Could not remove %s (%s): %m\n", file
->path
, file
->abspath
);
597 This function tries to remove the file after it has been packaged.
599 It will try to delete any parent directories as well and ignore if directories
600 cannot be deleted because they might contain other files
602 int pakfire_file_cleanup(struct pakfire_file
* file
) {
605 // Try removing the file
606 int r
= pakfire_file_remove(file
);
610 // Create a working copy of abspath
611 r
= pakfire_string_set(path
, file
->abspath
);
615 // See how many levels this file has
616 int levels
= pakfire_file_levels(file
);
618 // Walk all the way up and remove all parent directories if possible
622 // Break if path is suddenly empty
628 if (errno
== ENOTEMPTY
)
638 static int pakfire_file_verify_mode(struct pakfire_file
* file
, const struct stat
* st
) {
639 const mode_t type
= pakfire_file_get_type(file
);
641 // Did the type change?
642 if (type
!= (st
->st_mode
& S_IFMT
)) {
643 file
->verify_status
|= PAKFIRE_FILE_TYPE_CHANGED
;
645 DEBUG(file
->pakfire
, "%s: File Type changed\n", file
->path
);
648 const mode_t perms
= pakfire_file_get_perms(file
);
651 if (perms
!= (st
->st_mode
& 0777)) {
652 file
->verify_status
|= PAKFIRE_FILE_PERMISSIONS_CHANGED
;
654 DEBUG(file
->pakfire
, "%s: Permissions changed\n", file
->path
);
657 // Check if device node changed
658 if (S_ISCHR(st
->st_mode
) || S_ISBLK(st
->st_mode
)) {
659 const dev_t dev
= pakfire_file_get_dev(file
);
661 if (dev
!= st
->st_dev
) {
662 file
->verify_status
|= PAKFIRE_FILE_DEV_CHANGED
;
664 DEBUG(file
->pakfire
, "%s: Device Node changed\n", file
->path
);
671 static int pakfire_file_verify_size(struct pakfire_file
* file
, const struct stat
* st
) {
672 // Nothing to do if size matches
673 if (file
->st
.st_size
== st
->st_size
)
677 file
->verify_status
|= PAKFIRE_FILE_SIZE_CHANGED
;
679 DEBUG(file
->pakfire
, "%s: Filesize differs (expected %zu, got %zu byte(s))\n",
680 file
->path
, file
->st
.st_size
, st
->st_size
);
685 static int pakfire_file_verify_ownership(struct pakfire_file
* file
, const struct stat
* st
) {
688 const uid_t uid
= pakfire_unmap_id(file
->pakfire
, st
->st_uid
);
689 const gid_t gid
= pakfire_unmap_id(file
->pakfire
, st
->st_gid
);
691 const uid_t uid
= st
->st_uid
;
692 const gid_t gid
= st
->st_gid
;
695 // Fetch owner & group
696 struct passwd
* owner
= pakfire_getpwnam(file
->pakfire
, file
->user
);
697 struct group
* group
= pakfire_getgrnam(file
->pakfire
, file
->group
);
699 // Check if owner matches
700 if (!owner
|| owner
->pw_uid
!= uid
) {
701 file
->verify_status
|= PAKFIRE_FILE_OWNER_CHANGED
;
703 DEBUG(file
->pakfire
, "%s: Owner differs\n", file
->path
);
706 // Check if group matches
707 if (!group
|| group
->gr_gid
!= gid
) {
708 file
->verify_status
|= PAKFIRE_FILE_GROUP_CHANGED
;
710 DEBUG(file
->pakfire
, "%s: Group differs\n", file
->path
);
716 static int pakfire_file_verify_timestamps(struct pakfire_file
* file
, const struct stat
* st
) {
717 // Check creation time
718 if (file
->st
.st_ctime
!= st
->st_ctime
) {
719 file
->verify_status
|= PAKFIRE_FILE_CTIME_CHANGED
;
721 DEBUG(file
->pakfire
, "%s: Creation time changed\n", file
->path
);
724 // Check modification time
725 if (file
->st
.st_mtime
!= st
->st_mtime
) {
726 file
->verify_status
|= PAKFIRE_FILE_MTIME_CHANGED
;
728 DEBUG(file
->pakfire
, "%s: Modification time changed\n", file
->path
);
734 static int pakfire_file_verify_payload(struct pakfire_file
* file
, const struct stat
* st
) {
735 char buffer
[PAKFIRE_BUFFER_SIZE
];
739 EVP_MD_CTX
* sha512_ctx
= NULL
;
740 EVP_MD_CTX
* sha256_ctx
= NULL
;
742 struct pakfire_file_digests computed_digests
;
744 // Nothing to do for anything that isn't a regular file
745 if (!S_ISREG(st
->st_mode
))
748 // Fast-path if size changed. The payload will have changed, too
749 if (file
->verify_status
& PAKFIRE_FILE_SIZE_CHANGED
) {
750 file
->verify_status
|= PAKFIRE_FILE_PAYLOAD_CHANGED
;
754 // Check if this file has any digests at all
755 if (!pakfire_file_has_digest(file
->digests
.sha512
) &&
756 !pakfire_file_has_digest(file
->digests
.sha256
)) {
757 ERROR(file
->pakfire
, "%s: No digests available\n", file
->path
);
761 // Initialize context for SHA-512
762 sha512_ctx
= EVP_MD_CTX_new();
764 ERROR(file
->pakfire
, "Could not initialize OpenSSL context: %s\n",
765 ERR_error_string(ERR_get_error(), NULL
));
770 // Setup SHA-512 digest
771 r
= EVP_DigestInit_ex(sha512_ctx
, EVP_sha512(), NULL
);
773 ERROR(file
->pakfire
, "Could not setup SHA-512 digest: %s\n",
774 ERR_error_string(ERR_get_error(), NULL
));
779 // Initialize context for SHA-256
780 sha256_ctx
= EVP_MD_CTX_new();
782 ERROR(file
->pakfire
, "Could not initialize OpenSSL context: %s\n",
783 ERR_error_string(ERR_get_error(), NULL
));
788 // Setup SHA-256 digest
789 r
= EVP_DigestInit_ex(sha256_ctx
, EVP_sha256(), NULL
);
791 ERROR(file
->pakfire
, "Could not setup SHA-256 digest: %s\n",
792 ERR_error_string(ERR_get_error(), NULL
));
798 f
= pakfire_file_open(file
);
800 ERROR(file
->pakfire
, "Could not open %s: %m\n", file
->path
);
804 // Read the file into the hash functions
806 size_t bytes_read
= fread(buffer
, 1, sizeof(buffer
), f
);
808 // Raise any reading errors
815 r
= EVP_DigestUpdate(sha512_ctx
, buffer
, bytes_read
);
817 ERROR(file
->pakfire
, "EVP_Digest_Update() failed: %s\n",
818 ERR_error_string(ERR_get_error(), NULL
));
824 r
= EVP_DigestUpdate(sha256_ctx
, buffer
, bytes_read
);
826 ERROR(file
->pakfire
, "EVP_Digest_Update() failed: %s\n",
827 ERR_error_string(ERR_get_error(), NULL
));
834 r
= EVP_DigestFinal_ex(sha512_ctx
, computed_digests
.sha512
, NULL
);
836 ERROR(file
->pakfire
, "EVP_DigestFinal_ex() failed: %s\n",
837 ERR_error_string(ERR_get_error(), NULL
));
843 r
= EVP_DigestFinal_ex(sha256_ctx
, computed_digests
.sha256
, NULL
);
845 ERROR(file
->pakfire
, "EVP_DigestFinal_ex() failed: %s\n",
846 ERR_error_string(ERR_get_error(), NULL
));
852 r
= CRYPTO_memcmp(computed_digests
.sha512
,
853 file
->digests
.sha512
, sizeof(file
->digests
.sha512
));
855 file
->verify_status
|= PAKFIRE_FILE_PAYLOAD_CHANGED
;
857 DEBUG(file
->pakfire
, "%s: SHA-512 digest does not match\n", file
->path
);
861 r
= CRYPTO_memcmp(computed_digests
.sha256
,
862 file
->digests
.sha256
, sizeof(file
->digests
.sha256
));
864 file
->verify_status
|= PAKFIRE_FILE_PAYLOAD_CHANGED
;
866 DEBUG(file
->pakfire
, "%s: SHA-256 digest does not match\n", file
->path
);
874 EVP_MD_CTX_free(sha512_ctx
);
876 EVP_MD_CTX_free(sha256_ctx
);
884 Verify the file - i.e. does the metadata match what is on disk?
886 int pakfire_file_verify(struct pakfire_file
* file
, int* status
) {
890 DEBUG(file
->pakfire
, "Verifying %s...\n", file
->path
);
893 r
= lstat(file
->abspath
, &st
);
895 // File does not exist
896 if (errno
== ENOENT
) {
897 file
->verify_status
|= PAKFIRE_FILE_NOENT
;
901 // Raise any other errors from stat()
906 r
= pakfire_file_verify_mode(file
, &st
);
911 r
= pakfire_file_verify_size(file
, &st
);
916 r
= pakfire_file_verify_ownership(file
, &st
);
921 r
= pakfire_file_verify_timestamps(file
, &st
);
926 r
= pakfire_file_verify_payload(file
, &st
);