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>
34 #include <pakfire/constants.h>
35 #include <pakfire/digest.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_digests digests
;
80 // Verification Status
83 #warning TODO capabilities, config, data
90 Returns one if the digest is not all zeros.
92 #define pakfire_file_has_digest(digest) __pakfire_file_has_digest(digest, sizeof(digest))
94 static int __pakfire_file_has_digest(const unsigned char* digest
, const size_t length
) {
95 for (unsigned int i
= 0; i
< length
; i
++) {
103 static int pakfire_file_from_archive_entry(struct pakfire_file
* file
, struct archive_entry
* entry
) {
104 const char* attr
= NULL
;
105 const void* value
= NULL
;
110 r
= pakfire_file_set_abspath(file
, archive_entry_sourcepath(entry
));
112 ERROR(file
->pakfire
, "Could not set abspath: %m\n");
117 const char* path
= archive_entry_pathname(entry
);
119 // Strip any leading dots from paths
120 if (pakfire_string_startswith(path
, "./"))
123 r
= pakfire_file_set_path(file
, path
);
125 ERROR(file
->pakfire
, "Could not set path: %m\n");
131 pakfire_file_set_hardlink(file
, archive_entry_hardlink(entry
));
132 pakfire_file_set_symlink(file
, archive_entry_symlink(entry
));
135 pakfire_file_set_size(file
, archive_entry_size(entry
));
138 pakfire_file_set_mode(file
, archive_entry_mode(entry
));
141 if (archive_entry_dev_is_set(entry
))
142 pakfire_file_set_dev(file
, archive_entry_dev(entry
));
145 pakfire_file_set_user(file
, archive_entry_uname(entry
));
148 pakfire_file_set_group(file
, archive_entry_gname(entry
));
151 pakfire_file_set_ctime(file
, archive_entry_ctime(entry
));
152 pakfire_file_set_mtime(file
, archive_entry_mtime(entry
));
154 // Read any extended attributes
155 while (archive_entry_xattr_next(entry
, &attr
, &value
, &size
) == ARCHIVE_OK
) {
157 if (strcmp(attr
, "PAKFIRE.digests.sha512") == 0) {
158 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA512
, value
, size
);
163 } else if (strcmp(attr
, "PAKFIRE.digests.sha256") == 0) {
164 r
= pakfire_file_set_digest(file
, PAKFIRE_DIGEST_SHA256
, value
, size
);
169 DEBUG(file
->pakfire
, "Received an unknown extended attribute: %s\n", attr
);
177 PAKFIRE_EXPORT
int pakfire_file_create(struct pakfire_file
** file
, struct pakfire
* pakfire
) {
178 struct pakfire_file
* f
= calloc(1, sizeof(*f
));
182 // Store reference to Pakfire
183 f
->pakfire
= pakfire_ref(pakfire
);
185 // Initialize reference counter
192 int pakfire_file_create_from_path(struct pakfire_file
** file
,
193 struct pakfire
* pakfire
, const char* path
) {
194 struct archive
* reader
= NULL
;
195 struct archive_entry
* entry
= NULL
;
199 reader
= pakfire_make_archive_disk_reader(pakfire
, 0);
203 // Allocate a new archive entry
204 entry
= archive_entry_new();
209 archive_entry_copy_sourcepath(entry
, path
);
211 // Read all file attributes from disk
212 r
= archive_read_disk_entry_from_file(reader
, entry
, -1, NULL
);
214 ERROR(pakfire
, "Could not read from %s: %m\n", path
);
219 r
= pakfire_file_create_from_archive_entry(file
, pakfire
, entry
);
225 ERROR(pakfire
, "Could not create file from path %s: %m\n", path
);
227 archive_entry_free(entry
);
229 archive_read_free(reader
);
234 int pakfire_file_create_from_archive_entry(struct pakfire_file
** file
, struct pakfire
* pakfire
,
235 struct archive_entry
* entry
) {
236 int r
= pakfire_file_create(file
, pakfire
);
240 // Copy archive entry
241 r
= pakfire_file_from_archive_entry(*file
, entry
);
248 pakfire_file_unref(*file
);
254 struct archive_entry
* pakfire_file_archive_entry(struct pakfire_file
* file
) {
255 struct archive_entry
* entry
= archive_entry_new();
257 ERROR(file
->pakfire
, "Could not allocate archive entry: %m\n");
262 archive_entry_copy_pathname(entry
, pakfire_file_get_path(file
));
265 archive_entry_copy_sourcepath(entry
, file
->abspath
);
269 archive_entry_set_hardlink(entry
, file
->hardlink
);
271 archive_entry_set_symlink(entry
, file
->symlink
);
274 archive_entry_set_size(entry
, pakfire_file_get_size(file
));
277 archive_entry_set_mode(entry
, pakfire_file_get_mode(file
));
280 archive_entry_set_uname(entry
, pakfire_file_get_user(file
));
283 archive_entry_set_gname(entry
, pakfire_file_get_group(file
));
286 archive_entry_set_ctime(entry
, pakfire_file_get_ctime(file
), 0);
287 archive_entry_set_mtime(entry
, pakfire_file_get_mtime(file
), 0);
292 if (pakfire_file_has_digest(file
->digests
.sha512
))
293 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha512",
294 file
->digests
.sha512
, sizeof(file
->digests
.sha512
));
297 if (pakfire_file_has_digest(file
->digests
.sha256
))
298 archive_entry_xattr_add_entry(entry
, "PAKFIRE.digests.sha256",
299 file
->digests
.sha256
, sizeof(file
->digests
.sha256
));
304 static void pakfire_file_free(struct pakfire_file
* file
) {
305 pakfire_unref(file
->pakfire
);
309 PAKFIRE_EXPORT
struct pakfire_file
* pakfire_file_ref(struct pakfire_file
* file
) {
315 PAKFIRE_EXPORT
struct pakfire_file
* pakfire_file_unref(struct pakfire_file
* file
) {
316 if (--file
->nrefs
> 0)
319 pakfire_file_free(file
);
323 PAKFIRE_EXPORT
int pakfire_file_cmp(struct pakfire_file
* file1
, struct pakfire_file
* file2
) {
324 const char* path1
= pakfire_file_get_path(file1
);
325 const char* path2
= pakfire_file_get_path(file2
);
327 return strcmp(path1
, path2
);
330 const char* pakfire_file_get_abspath(struct pakfire_file
* file
) {
331 return file
->abspath
;
334 int pakfire_file_set_abspath(struct pakfire_file
* file
, const char* path
) {
335 // Check if path is set and absolute
336 if (!path
|| *path
!= '/') {
341 return pakfire_string_set(file
->abspath
, path
);
344 PAKFIRE_EXPORT
const char* pakfire_file_get_path(struct pakfire_file
* file
) {
348 PAKFIRE_EXPORT
int pakfire_file_set_path(struct pakfire_file
* file
, const char* path
) {
351 // Check if path is set and absolute
352 if (!path
|| *path
!= '/') {
358 r
= pakfire_string_set(file
->path
, path
);
362 // Set abspath if it isn't set, yet
363 if (!*file
->abspath
) {
364 r
= pakfire_path(file
->pakfire
, file
->abspath
, "%s", path
);
372 PAKFIRE_EXPORT
const char* pakfire_file_get_hardlink(struct pakfire_file
* file
) {
373 if (!*file
->hardlink
)
376 return file
->hardlink
;
379 PAKFIRE_EXPORT
void pakfire_file_set_hardlink(struct pakfire_file
* file
, const char* link
) {
381 *file
->hardlink
= '\0';
383 pakfire_string_set(file
->hardlink
, link
);
386 PAKFIRE_EXPORT
const char* pakfire_file_get_symlink(struct pakfire_file
* file
) {
390 return file
->symlink
;
393 PAKFIRE_EXPORT
void pakfire_file_set_symlink(struct pakfire_file
* file
, const char* link
) {
395 *file
->hardlink
= '\0';
397 pakfire_string_set(file
->symlink
, link
);
400 PAKFIRE_EXPORT
int pakfire_file_get_type(struct pakfire_file
* file
) {
401 return file
->st
.st_mode
& S_IFMT
;
404 PAKFIRE_EXPORT off_t
pakfire_file_get_size(struct pakfire_file
* file
) {
405 return file
->st
.st_size
;
408 PAKFIRE_EXPORT
void pakfire_file_set_size(struct pakfire_file
* file
, off_t size
) {
409 file
->st
.st_size
= size
;
412 PAKFIRE_EXPORT
const char* pakfire_file_get_user(struct pakfire_file
* file
) {
416 PAKFIRE_EXPORT
int pakfire_file_set_user(struct pakfire_file
* file
, const char* user
) {
417 return pakfire_string_set(file
->user
, user
);
420 PAKFIRE_EXPORT
const char* pakfire_file_get_group(struct pakfire_file
* file
) {
424 PAKFIRE_EXPORT
int pakfire_file_set_group(struct pakfire_file
* file
, const char* group
) {
425 return pakfire_string_set(file
->group
, group
);
428 PAKFIRE_EXPORT mode_t
pakfire_file_get_mode(struct pakfire_file
* file
) {
429 return file
->st
.st_mode
;
432 PAKFIRE_EXPORT
void pakfire_file_set_mode(struct pakfire_file
* file
, mode_t mode
) {
433 file
->st
.st_mode
= mode
;
436 PAKFIRE_EXPORT mode_t
pakfire_file_get_perms(struct pakfire_file
* file
) {
437 return file
->st
.st_mode
& ~AE_IFMT
;
440 PAKFIRE_EXPORT
void pakfire_file_set_perms(struct pakfire_file
* file
, const mode_t perms
) {
441 // Clear any previous permissions
442 file
->st
.st_mode
&= S_IFMT
;
444 // Set new bits (with format cleared)
445 file
->st
.st_mode
|= ~S_IFMT
& perms
;
448 PAKFIRE_EXPORT dev_t
pakfire_file_get_dev(struct pakfire_file
* file
) {
449 return file
->st
.st_dev
;
452 PAKFIRE_EXPORT
void pakfire_file_set_dev(struct pakfire_file
* file
, dev_t dev
) {
453 file
->st
.st_dev
= dev
;
456 PAKFIRE_EXPORT
time_t pakfire_file_get_ctime(struct pakfire_file
* file
) {
457 return file
->st
.st_ctime
;
460 PAKFIRE_EXPORT
void pakfire_file_set_ctime(struct pakfire_file
* file
, time_t time
) {
461 file
->st
.st_ctime
= time
;
464 PAKFIRE_EXPORT
time_t pakfire_file_get_mtime(struct pakfire_file
* file
) {
465 return file
->st
.st_mtime
;
468 PAKFIRE_EXPORT
void pakfire_file_set_mtime(struct pakfire_file
* file
, time_t time
) {
469 file
->st
.st_mtime
= time
;
472 PAKFIRE_EXPORT
const unsigned char* pakfire_file_get_digest(
473 struct pakfire_file
* file
, const enum pakfire_digest_types type
, size_t* length
) {
476 case PAKFIRE_DIGEST_SHA512
:
477 if (!pakfire_file_has_digest(file
->digests
.sha512
))
481 *length
= sizeof(file
->digests
.sha512
);
483 return file
->digests
.sha512
;
485 case PAKFIRE_DIGEST_SHA256
:
486 if (!pakfire_file_has_digest(file
->digests
.sha256
))
490 *length
= sizeof(file
->digests
.sha256
);
492 return file
->digests
.sha256
;
498 PAKFIRE_EXPORT
int pakfire_file_set_digest(struct pakfire_file
* file
,
499 const enum pakfire_digest_types type
, const unsigned char* digest
, const size_t length
) {
505 // Check buffer length
506 if (pakfire_digest_length(type
) != length
) {
507 ERROR(file
->pakfire
, "Digest has an incorrect length of %zu byte(s)\n", length
);
514 case PAKFIRE_DIGEST_SHA512
:
515 memcpy(file
->digests
.sha512
, digest
, sizeof(file
->digests
.sha512
));
518 case PAKFIRE_DIGEST_SHA256
:
519 memcpy(file
->digests
.sha256
, digest
, sizeof(file
->digests
.sha256
));
526 PAKFIRE_EXPORT
char* pakfire_file_get_hexdigest(
527 struct pakfire_file
* file
, const enum pakfire_digest_types type
) {
528 const unsigned char* digest
= NULL
;
532 digest
= pakfire_file_get_digest(file
, type
, &length
);
536 return __pakfire_hexlify(digest
, length
);
539 static int pakfire_file_levels(struct pakfire_file
* file
) {
545 for (char* p
= file
->path
; *p
; p
++) {
553 FILE* pakfire_file_open(struct pakfire_file
* file
) {
554 FILE* f
= fopen(file
->abspath
, "r");
556 ERROR(file
->pakfire
, "Could not open %s: %m\n", file
->abspath
);
561 int pakfire_file_remove(struct pakfire_file
* file
) {
562 if (!*file
->abspath
) {
567 DEBUG(file
->pakfire
, "Removing %s...\n", file
->path
);
569 int r
= remove(file
->abspath
);
572 // Ignore when we could not remove directories
576 // Ignore if the file didn't exist
584 ERROR(file
->pakfire
, "Could not remove %s (%s): %m\n", file
->path
, file
->abspath
);
591 This function tries to remove the file after it has been packaged.
593 It will try to delete any parent directories as well and ignore if directories
594 cannot be deleted because they might contain other files
596 int pakfire_file_cleanup(struct pakfire_file
* file
) {
599 // Try removing the file
600 int r
= pakfire_file_remove(file
);
604 // Create a working copy of abspath
605 r
= pakfire_string_set(path
, file
->abspath
);
609 // See how many levels this file has
610 int levels
= pakfire_file_levels(file
);
612 // Walk all the way up and remove all parent directories if possible
616 // Break if path is suddenly empty
622 if (errno
== ENOTEMPTY
)
632 static int pakfire_file_verify_mode(struct pakfire_file
* file
, const struct stat
* st
) {
633 const mode_t type
= pakfire_file_get_type(file
);
635 // Did the type change?
636 if (type
!= (st
->st_mode
& S_IFMT
)) {
637 file
->verify_status
|= PAKFIRE_FILE_TYPE_CHANGED
;
639 DEBUG(file
->pakfire
, "%s: File Type changed\n", file
->path
);
642 const mode_t perms
= pakfire_file_get_perms(file
);
645 if (perms
!= (st
->st_mode
& 0777)) {
646 file
->verify_status
|= PAKFIRE_FILE_PERMISSIONS_CHANGED
;
648 DEBUG(file
->pakfire
, "%s: Permissions changed\n", file
->path
);
651 // Check if device node changed
652 if (S_ISCHR(st
->st_mode
) || S_ISBLK(st
->st_mode
)) {
653 const dev_t dev
= pakfire_file_get_dev(file
);
655 if (dev
!= st
->st_dev
) {
656 file
->verify_status
|= PAKFIRE_FILE_DEV_CHANGED
;
658 DEBUG(file
->pakfire
, "%s: Device Node changed\n", file
->path
);
665 static int pakfire_file_verify_size(struct pakfire_file
* file
, const struct stat
* st
) {
666 // Nothing to do if size matches
667 if (file
->st
.st_size
== st
->st_size
)
671 file
->verify_status
|= PAKFIRE_FILE_SIZE_CHANGED
;
673 DEBUG(file
->pakfire
, "%s: Filesize differs (expected %zu, got %zu byte(s))\n",
674 file
->path
, file
->st
.st_size
, st
->st_size
);
679 static int pakfire_file_verify_ownership(struct pakfire_file
* file
, const struct stat
* st
) {
682 const uid_t uid
= pakfire_unmap_id(file
->pakfire
, st
->st_uid
);
683 const gid_t gid
= pakfire_unmap_id(file
->pakfire
, st
->st_gid
);
685 const uid_t uid
= st
->st_uid
;
686 const gid_t gid
= st
->st_gid
;
689 // Fetch owner & group
690 struct passwd
* owner
= pakfire_getpwnam(file
->pakfire
, file
->user
);
691 struct group
* group
= pakfire_getgrnam(file
->pakfire
, file
->group
);
693 // Check if owner matches
694 if (!owner
|| owner
->pw_uid
!= uid
) {
695 file
->verify_status
|= PAKFIRE_FILE_OWNER_CHANGED
;
697 DEBUG(file
->pakfire
, "%s: Owner differs\n", file
->path
);
700 // Check if group matches
701 if (!group
|| group
->gr_gid
!= gid
) {
702 file
->verify_status
|= PAKFIRE_FILE_GROUP_CHANGED
;
704 DEBUG(file
->pakfire
, "%s: Group differs\n", file
->path
);
710 static int pakfire_file_verify_timestamps(struct pakfire_file
* file
, const struct stat
* st
) {
711 // Check creation time
712 if (file
->st
.st_ctime
!= st
->st_ctime
) {
713 file
->verify_status
|= PAKFIRE_FILE_CTIME_CHANGED
;
715 DEBUG(file
->pakfire
, "%s: Creation time changed\n", file
->path
);
718 // Check modification time
719 if (file
->st
.st_mtime
!= st
->st_mtime
) {
720 file
->verify_status
|= PAKFIRE_FILE_MTIME_CHANGED
;
722 DEBUG(file
->pakfire
, "%s: Modification time changed\n", file
->path
);
728 static int pakfire_file_verify_payload(struct pakfire_file
* file
, const struct stat
* st
) {
729 char buffer
[PAKFIRE_BUFFER_SIZE
];
733 EVP_MD_CTX
* sha512_ctx
= NULL
;
734 EVP_MD_CTX
* sha256_ctx
= NULL
;
736 struct pakfire_digests computed_digests
;
738 // Nothing to do for anything that isn't a regular file
739 if (!S_ISREG(st
->st_mode
))
742 // Fast-path if size changed. The payload will have changed, too
743 if (file
->verify_status
& PAKFIRE_FILE_SIZE_CHANGED
) {
744 file
->verify_status
|= PAKFIRE_FILE_PAYLOAD_CHANGED
;
748 // Check if this file has any digests at all
749 if (!pakfire_file_has_digest(file
->digests
.sha512
) &&
750 !pakfire_file_has_digest(file
->digests
.sha256
)) {
751 ERROR(file
->pakfire
, "%s: No digests available\n", file
->path
);
755 // Initialize context for SHA-512
756 sha512_ctx
= EVP_MD_CTX_new();
758 ERROR(file
->pakfire
, "Could not initialize OpenSSL context: %s\n",
759 ERR_error_string(ERR_get_error(), NULL
));
764 // Setup SHA-512 digest
765 r
= EVP_DigestInit_ex(sha512_ctx
, EVP_sha512(), NULL
);
767 ERROR(file
->pakfire
, "Could not setup SHA-512 digest: %s\n",
768 ERR_error_string(ERR_get_error(), NULL
));
773 // Initialize context for SHA-256
774 sha256_ctx
= EVP_MD_CTX_new();
776 ERROR(file
->pakfire
, "Could not initialize OpenSSL context: %s\n",
777 ERR_error_string(ERR_get_error(), NULL
));
782 // Setup SHA-256 digest
783 r
= EVP_DigestInit_ex(sha256_ctx
, EVP_sha256(), NULL
);
785 ERROR(file
->pakfire
, "Could not setup SHA-256 digest: %s\n",
786 ERR_error_string(ERR_get_error(), NULL
));
792 f
= pakfire_file_open(file
);
794 ERROR(file
->pakfire
, "Could not open %s: %m\n", file
->path
);
798 // Read the file into the hash functions
800 size_t bytes_read
= fread(buffer
, 1, sizeof(buffer
), f
);
802 // Raise any reading errors
809 r
= EVP_DigestUpdate(sha512_ctx
, buffer
, bytes_read
);
811 ERROR(file
->pakfire
, "EVP_Digest_Update() failed: %s\n",
812 ERR_error_string(ERR_get_error(), NULL
));
818 r
= EVP_DigestUpdate(sha256_ctx
, buffer
, bytes_read
);
820 ERROR(file
->pakfire
, "EVP_Digest_Update() failed: %s\n",
821 ERR_error_string(ERR_get_error(), NULL
));
828 r
= EVP_DigestFinal_ex(sha512_ctx
, computed_digests
.sha512
, NULL
);
830 ERROR(file
->pakfire
, "EVP_DigestFinal_ex() failed: %s\n",
831 ERR_error_string(ERR_get_error(), NULL
));
837 r
= EVP_DigestFinal_ex(sha256_ctx
, computed_digests
.sha256
, NULL
);
839 ERROR(file
->pakfire
, "EVP_DigestFinal_ex() failed: %s\n",
840 ERR_error_string(ERR_get_error(), NULL
));
846 r
= CRYPTO_memcmp(computed_digests
.sha512
,
847 file
->digests
.sha512
, sizeof(file
->digests
.sha512
));
849 file
->verify_status
|= PAKFIRE_FILE_PAYLOAD_CHANGED
;
851 DEBUG(file
->pakfire
, "%s: SHA-512 digest does not match\n", file
->path
);
855 r
= CRYPTO_memcmp(computed_digests
.sha256
,
856 file
->digests
.sha256
, sizeof(file
->digests
.sha256
));
858 file
->verify_status
|= PAKFIRE_FILE_PAYLOAD_CHANGED
;
860 DEBUG(file
->pakfire
, "%s: SHA-256 digest does not match\n", file
->path
);
868 EVP_MD_CTX_free(sha512_ctx
);
870 EVP_MD_CTX_free(sha256_ctx
);
878 Verify the file - i.e. does the metadata match what is on disk?
880 int pakfire_file_verify(struct pakfire_file
* file
, int* status
) {
884 DEBUG(file
->pakfire
, "Verifying %s...\n", file
->path
);
887 r
= lstat(file
->abspath
, &st
);
889 // File does not exist
890 if (errno
== ENOENT
) {
891 file
->verify_status
|= PAKFIRE_FILE_NOENT
;
895 // Raise any other errors from stat()
900 r
= pakfire_file_verify_mode(file
, &st
);
905 r
= pakfire_file_verify_size(file
, &st
);
910 r
= pakfire_file_verify_ownership(file
, &st
);
915 r
= pakfire_file_verify_timestamps(file
, &st
);
920 r
= pakfire_file_verify_payload(file
, &st
);