diff --git a/include/xbps.h.in b/include/xbps.h.in index 46a5592f..5df41b57 100644 --- a/include/xbps.h.in +++ b/include/xbps.h.in @@ -110,7 +110,7 @@ */ #define XBPS_PKGFILES "files.plist" -/** +/** * @def XBPS_REPOIDX * Filename for the repository index property list. */ @@ -1897,6 +1897,22 @@ int xbps_file_hash_check(const char *file, const char *sha256); bool xbps_verify_signature(struct xbps_repo *repo, const char *sigfile, unsigned char *digest); +/** + * Verifies the RSA signature \sig_buf of bytes with hash \a digest + * with the RSA public-key associated in \a repo. + * + * @param[in] repo Repository to use with the RSA public key associated. + * @param[in] idxmeta Meta dictionary related to \a repo, containing + * public-key field. + * @param[in] sig_buf The signature of file content that has hash \digest. + * @param[in] sigfilelen The length of signature. + * @param[in] digest The digest of file content to verify. + * + * @return True if the signature is valid, false otherwise. + */ +bool xbps_verify_digest_signature(struct xbps_repo *repo, xbps_dictionary_t idxmeta, + unsigned char *sig_buf, size_t sigfilelen, unsigned char *digest); + /** * Verifies the RSA signature of \a fname with the RSA public-key associated * in \a repo. diff --git a/include/xbps_api_impl.h b/include/xbps_api_impl.h index d0a701b5..90f16a85 100644 --- a/include/xbps_api_impl.h +++ b/include/xbps_api_impl.h @@ -149,7 +149,7 @@ int HIDDEN xbps_register_pkg(struct xbps_handle *, xbps_dictionary_t); void HIDDEN xbps_transaction_conflicts(struct xbps_handle *, xbps_array_t); char HIDDEN *xbps_archive_get_file(struct archive *, struct archive_entry *); xbps_dictionary_t HIDDEN xbps_archive_get_dictionary(struct archive *, - struct archive_entry *); + struct archive_entry *, char **bytes); const char HIDDEN *vpkg_user_conf(struct xbps_handle *, const char *, bool); xbps_array_t HIDDEN xbps_get_pkg_fulldeptree(struct xbps_handle *, const char *, bool); diff --git a/lib/archive.c b/lib/archive.c index 94cba292..7a4c019d 100644 --- a/lib/archive.c +++ b/lib/archive.c @@ -56,7 +56,7 @@ xbps_archive_get_file(struct archive *ar, struct archive_entry *entry) } xbps_dictionary_t HIDDEN -xbps_archive_get_dictionary(struct archive *ar, struct archive_entry *entry) +xbps_archive_get_dictionary(struct archive *ar, struct archive_entry *entry, char **bytes) { xbps_dictionary_t d = NULL; char *buf; @@ -66,7 +66,11 @@ xbps_archive_get_dictionary(struct archive *ar, struct archive_entry *entry) /* If blob is already a dictionary we are done */ d = xbps_dictionary_internalize(buf); - free(buf); + if (bytes == NULL) { + free(buf); + } else { + *bytes = buf; + } return d; } diff --git a/lib/package_unpack.c b/lib/package_unpack.c index e995d40c..c761264a 100644 --- a/lib/package_unpack.c +++ b/lib/package_unpack.c @@ -180,13 +180,13 @@ unpack_archive(struct xbps_handle *xhp, goto out; } } else if (strcmp("./props.plist", entry_pname) == 0) { - binpkg_propsd = xbps_archive_get_dictionary(ar, entry); + binpkg_propsd = xbps_archive_get_dictionary(ar, entry, NULL); if (binpkg_propsd == NULL) { rv = EINVAL; goto out; } } else if (strcmp("./files.plist", entry_pname) == 0) { - binpkg_filesd = xbps_archive_get_dictionary(ar, entry); + binpkg_filesd = xbps_archive_get_dictionary(ar, entry, NULL); if (binpkg_filesd == NULL) { rv = EINVAL; goto out; diff --git a/lib/repo.c b/lib/repo.c index 1b0755d2..cd5796ea 100644 --- a/lib/repo.c +++ b/lib/repo.c @@ -61,11 +61,48 @@ xbps_repo_path_with_name(struct xbps_handle *xhp, const char *url, const char *n url, xhp->target_arch ? xhp->target_arch : xhp->native_arch, name); } +static bool +repo_verify_index(struct xbps_repo *repo, unsigned char *digest) { + bool verified = false; + unsigned char *sig_buf = NULL; + size_t sigfilelen = 0; + struct archive_entry *entry; + + if (archive_read_next_header(repo->ar, &entry) != ARCHIVE_OK) { + xbps_dbg_printf(repo->xhp, + "%s: read_next_header %s\n", repo->uri, + archive_error_string(repo->ar)); + return false; + } + + if (strcmp(archive_entry_pathname(entry), XBPS_REPOIDX_SIG) != 0) { + xbps_dbg_printf(repo->xhp, + "%s: no signature of %s\n", repo->uri, XBPS_REPOIDX); + return false; + } + + sigfilelen = (size_t)archive_entry_size(entry); + sig_buf = (unsigned char *) xbps_archive_get_file(repo->ar, entry); + if (sig_buf == NULL) { + return false; + } + verified = xbps_verify_digest_signature(repo, sig_buf, sigfilelen, digest); + + free(sig_buf); + return verified; +} + static xbps_dictionary_t -repo_get_dict(struct xbps_repo *repo) +repo_get_dict(struct xbps_repo *repo, bool *verified) { struct archive_entry *entry; int rv; + xbps_dictionary_t dict; + char *bytes = NULL; + unsigned char *digest = NULL; + + if (verified != NULL) + *verified = false; if (repo->ar == NULL) return NULL; @@ -77,7 +114,15 @@ repo_get_dict(struct xbps_repo *repo) archive_error_string(repo->ar)); return NULL; } - return xbps_archive_get_dictionary(repo->ar, entry); + dict = xbps_archive_get_dictionary(repo->ar, entry, &bytes); + if (verified != NULL && + bytes != NULL && + (digest = xbps_buffer_hash_raw(bytes, strlen(bytes))) != NULL && + repo_verify_index(repo, digest)) + *verified = true; + free(digest); + free(bytes); + return dict; } bool @@ -135,6 +180,7 @@ repo_open_local(struct xbps_repo *repo, const char *repofile) { struct stat st; int rv = 0; + bool verified = false; if (fstat(repo->fd, &st) == -1) { rv = errno; @@ -158,7 +204,7 @@ repo_open_local(struct xbps_repo *repo, const char *repofile) repofile, strerror(rv)); return false; } - if ((repo->idx = repo_get_dict(repo)) == NULL) { + if ((repo->idx = repo_get_dict(repo, &verified)) == NULL) { xbps_dbg_printf(repo->xhp, "[repo] `%s' failed to internalize " " index on archive, removing file.\n", repofile); /* broken archive, remove it */ @@ -166,13 +212,13 @@ repo_open_local(struct xbps_repo *repo, const char *repofile) return false; } xbps_dictionary_make_immutable(repo->idx); - repo->idxmeta = repo_get_dict(repo); + repo->idxmeta = repo_get_dict(repo, NULL); if (repo->idxmeta != NULL) { repo->is_signed = true; xbps_dictionary_make_immutable(repo->idxmeta); } - return true; + return verified || (!repo->is_remote && !repo->is_signed); } static bool diff --git a/lib/transaction_files.c b/lib/transaction_files.c index 41b35ca3..df1209e9 100644 --- a/lib/transaction_files.c +++ b/lib/transaction_files.c @@ -732,7 +732,7 @@ collect_binpkg_files(struct xbps_handle *xhp, xbps_dictionary_t pkg_repod, entry_pname = archive_entry_pathname(entry); if ((strcmp("./files.plist", entry_pname)) == 0) { - filesd = xbps_archive_get_dictionary(ar, entry); + filesd = xbps_archive_get_dictionary(ar, entry, NULL); if (filesd == NULL) { rv = EINVAL; goto out; diff --git a/lib/verifysig.c b/lib/verifysig.c index 58729a37..e0ccbfab 100644 --- a/lib/verifysig.c +++ b/lib/verifysig.c @@ -72,28 +72,25 @@ rsa_verify_hash(struct xbps_repo *repo, xbps_data_t pubkey, } bool -xbps_verify_signature(struct xbps_repo *repo, const char *sigfile, - unsigned char *digest) +xbps_verify_digest_signature(struct xbps_repo *repo, xbps_dictionary_t idxmeta, + unsigned char *sig_buf, size_t sigfilelen, unsigned char *digest) { xbps_dictionary_t repokeyd = NULL; xbps_data_t pubkey; char *hexfp = NULL; - unsigned char *sig_buf = NULL; - size_t sigbuflen, sigfilelen; char *rkeyfile = NULL; bool val = false; - if (!xbps_dictionary_count(repo->idxmeta)) { + if (!xbps_dictionary_count(idxmeta)) { xbps_dbg_printf(repo->xhp, "%s: unsigned repository\n", repo->uri); return false; } hexfp = xbps_pubkey2fp(repo->xhp, - xbps_dictionary_get(repo->idxmeta, "public-key")); + xbps_dictionary_get(idxmeta, "public-key")); if (hexfp == NULL) { xbps_dbg_printf(repo->xhp, "%s: incomplete signed repo, missing hexfp obj\n", repo->uri); return false; } - /* * Prepare repository RSA public key to verify fname signature. */ @@ -108,12 +105,6 @@ xbps_verify_signature(struct xbps_repo *repo, const char *sigfile, pubkey = xbps_dictionary_get(repokeyd, "public-key"); if (xbps_object_type(pubkey) != XBPS_TYPE_DATA) goto out; - - if (!xbps_mmap_file(sigfile, (void *)&sig_buf, &sigbuflen, &sigfilelen)) { - xbps_dbg_printf(repo->xhp, "can't open signature file %s: %s\n", - sigfile, strerror(errno)); - goto out; - } /* * Verify fname RSA signature. */ @@ -125,14 +116,32 @@ out: free(hexfp); if (rkeyfile) free(rkeyfile); - if (sig_buf) - (void)munmap(sig_buf, sigbuflen); if (repokeyd) xbps_object_release(repokeyd); return val; } +bool +xbps_verify_signature(struct xbps_repo *repo, const char *sigfile, + unsigned char *digest) +{ + unsigned char *sig_buf = NULL; + size_t sigbuflen, sigfilelen; + bool result = false; + + if (xbps_mmap_file(sigfile, (void *)&sig_buf, &sigbuflen, &sigfilelen)) { + result = xbps_verify_digest_signature(repo, repo->idxmeta, sig_buf, sigfilelen, digest); + } else { + xbps_dbg_printf(repo->xhp, "can't open signature file %s: %s\n", + sigfile, strerror(errno)); + } + + if (sig_buf) + (void)munmap(sig_buf, sigbuflen); + return result; +} + bool xbps_verify_file_signature(struct xbps_repo *repo, const char *fname) {