From 143b13f9e6dfe7a926efa5a82ada6ea34d7276e8 Mon Sep 17 00:00:00 2001 From: Duncaen Date: Sun, 7 Jul 2019 14:11:29 +0200 Subject: [PATCH] lib/transaction_fetch.c: move fetch and verify binpkgs and make use of xbps_fetch_file_digest --- include/xbps_api_impl.h | 2 + lib/Makefile | 2 +- lib/transaction_commit.c | 164 +---------------------- lib/transaction_fetch.c | 273 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 280 insertions(+), 161 deletions(-) create mode 100644 lib/transaction_fetch.c diff --git a/include/xbps_api_impl.h b/include/xbps_api_impl.h index 7dab942c..d0a701b5 100644 --- a/include/xbps_api_impl.h +++ b/include/xbps_api_impl.h @@ -158,5 +158,7 @@ struct xbps_repo HIDDEN *xbps_regget_repo(struct xbps_handle *, int HIDDEN xbps_conf_init(struct xbps_handle *); int HIDDEN xbps_transaction_files(struct xbps_handle *, xbps_object_iterator_t); +int HIDDEN xbps_transaction_fetch(struct xbps_handle *, + xbps_object_iterator_t); #endif /* !_XBPS_API_IMPL_H_ */ diff --git a/lib/Makefile b/lib/Makefile index 3a10da46..e29ce01e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -39,7 +39,7 @@ OBJS += package_msg.o transaction_shlibs.o OBJS += transaction_commit.o transaction_package_replace.o OBJS += transaction_prepare.o transaction_ops.o transaction_store.o OBJS += transaction_revdeps.o transaction_conflicts.o -OBJS += transaction_files.o +OBJS += transaction_files.o transaction_fetch.o OBJS += pubkey2fp.o package_fulldeptree.o OBJS += download.o initend.o pkgdb.o OBJS += plist.o plist_find.o plist_match.o archive.o diff --git a/lib/transaction_commit.c b/lib/transaction_commit.c index 08da1194..9f16f7bc 100644 --- a/lib/transaction_commit.c +++ b/lib/transaction_commit.c @@ -56,153 +56,6 @@ * data type is specified on its edge, i.e string, array, integer, dictionary. */ -static int -check_binpkgs(struct xbps_handle *xhp, xbps_object_iterator_t iter) -{ - xbps_object_t obj; - struct xbps_repo *repo; - const char *pkgver, *repoloc, *trans, *sha256; - char *binfile; - int rv = 0; - - while ((obj = xbps_object_iterator_next(iter)) != NULL) { - xbps_dictionary_get_cstring_nocopy(obj, "transaction", &trans); - if ((strcmp(trans, "remove") == 0) || - (strcmp(trans, "hold") == 0) || - (strcmp(trans, "configure") == 0)) - continue; - - xbps_dictionary_get_cstring_nocopy(obj, "repository", &repoloc); - xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); - - binfile = xbps_repository_pkg_path(xhp, obj); - if (binfile == NULL) { - rv = ENOMEM; - break; - } - /* - * For pkgs in local repos check the sha256 hash. - * For pkgs in remote repos check the RSA signature. - */ - if ((repo = xbps_rpool_get_repo(repoloc)) == NULL) { - rv = errno; - xbps_dbg_printf(xhp, "%s: failed to get repository " - "%s: %s\n", pkgver, repoloc, strerror(errno)); - break; - } - if (repo->is_remote) { - /* remote repo */ - xbps_set_cb_state(xhp, XBPS_STATE_VERIFY, 0, pkgver, - "%s: verifying RSA signature...", pkgver); - - if (!xbps_verify_file_signature(repo, binfile)) { - char *sigfile; - rv = EPERM; - xbps_set_cb_state(xhp, XBPS_STATE_VERIFY_FAIL, rv, pkgver, - "%s: the RSA signature is not valid!", pkgver); - xbps_set_cb_state(xhp, XBPS_STATE_VERIFY_FAIL, rv, pkgver, - "%s: removed pkg archive and its signature.", pkgver); - (void)remove(binfile); - sigfile = xbps_xasprintf("%s.sig", binfile); - (void)remove(sigfile); - free(sigfile); - free(binfile); - break; - } - } else { - /* local repo */ - xbps_set_cb_state(xhp, XBPS_STATE_VERIFY, 0, pkgver, - "%s: verifying SHA256 hash...", pkgver); - xbps_dictionary_get_cstring_nocopy(obj, "filename-sha256", &sha256); - if ((rv = xbps_file_hash_check(binfile, sha256)) != 0) { - xbps_set_cb_state(xhp, XBPS_STATE_VERIFY_FAIL, rv, pkgver, - "%s: SHA256 hash is not valid: %s", pkgver, strerror(rv)); - free(binfile); - break; - } - - } - free(binfile); - } - xbps_object_iterator_reset(iter); - - return rv; -} - -static int -download_binpkgs(struct xbps_handle *xhp, xbps_object_iterator_t iter) -{ - xbps_object_t obj; - const char *pkgver, *arch, *fetchstr, *repoloc, *trans; - char *file, *sigfile; - int rv = 0; - - while ((obj = xbps_object_iterator_next(iter)) != NULL) { - xbps_dictionary_get_cstring_nocopy(obj, "transaction", &trans); - if ((strcmp(trans, "remove") == 0) || - (strcmp(trans, "hold") == 0) || - (strcmp(trans, "configure") == 0)) - continue; - - xbps_dictionary_get_cstring_nocopy(obj, "repository", &repoloc); - if (!xbps_repository_is_remote(repoloc)) - continue; - - xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); - xbps_dictionary_get_cstring_nocopy(obj, "architecture", &arch); - - /* - * Download binary package. - */ - if ((file = xbps_repository_pkg_path(xhp, obj)) == NULL) { - rv = EINVAL; - break; - } - if (access(file, R_OK) == -1) { - xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD, 0, pkgver, - "Downloading `%s' package (from `%s')...", pkgver, repoloc); - if ((rv = xbps_fetch_file(xhp, file, NULL)) == -1) { - rv = fetchLastErrCode ? fetchLastErrCode : errno; - fetchstr = xbps_fetch_error_string(); - xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD_FAIL, rv, - pkgver, "[trans] failed to download `%s' package from `%s': %s", - pkgver, repoloc, fetchstr ? fetchstr : strerror(rv)); - free(file); - break; - } - rv = 0; - } - /* - * Download binary package signature. - */ - sigfile = xbps_xasprintf("%s.sig", file); - free(file); - file = NULL; - if (access(sigfile, R_OK) == -1) { - xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD, 0, pkgver, - "Downloading `%s' signature (from `%s')...", pkgver, repoloc); - file = xbps_xasprintf("%s/%s.%s.xbps.sig", repoloc, pkgver, arch); - if ((rv = xbps_fetch_file(xhp, file, NULL)) == -1) { - rv = fetchLastErrCode ? fetchLastErrCode : errno; - fetchstr = xbps_fetch_error_string(); - xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD_FAIL, rv, - pkgver, "[trans] failed to download `%s' signature from `%s': %s", - pkgver, repoloc, fetchstr ? fetchstr : strerror(rv)); - free(sigfile); - free(file); - break; - } - rv = 0; - } - free(sigfile); - if (file != NULL) - free(file); - } - xbps_object_iterator_reset(iter); - - return rv; -} - int xbps_transaction_commit(struct xbps_handle *xhp) { @@ -237,21 +90,12 @@ xbps_transaction_commit(struct xbps_handle *xhp) iter = xbps_array_iter_from_dict(xhp->transd, "packages"); if (iter == NULL) return EINVAL; + /* - * Download binary packages (if they come from a remote repository). + * Download and verify binary packages. */ - xbps_set_cb_state(xhp, XBPS_STATE_TRANS_DOWNLOAD, 0, NULL, NULL); - if ((rv = download_binpkgs(xhp, iter)) != 0) { - xbps_dbg_printf(xhp, "[trans] failed to download binpkgs: " - "%s\n", strerror(rv)); - goto out; - } - /* - * Check binary package integrity. - */ - xbps_set_cb_state(xhp, XBPS_STATE_TRANS_VERIFY, 0, NULL, NULL); - if ((rv = check_binpkgs(xhp, iter)) != 0) { - xbps_dbg_printf(xhp, "[trans] failed to check binpkgs: " + if ((rv = xbps_transaction_fetch(xhp, iter)) != 0) { + xbps_dbg_printf(xhp, "[trans] failed to fetch and verify binpkgs: " "%s\n", strerror(rv)); goto out; } diff --git a/lib/transaction_fetch.c b/lib/transaction_fetch.c new file mode 100644 index 00000000..658988cd --- /dev/null +++ b/lib/transaction_fetch.c @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 2009-2015 Juan Romero Pardines. + * Copyright (c) 2019 Duncan Overbruck . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include "xbps_api_impl.h" + +static int +verify_binpkg(struct xbps_handle *xhp, xbps_dictionary_t pkgd) +{ + struct xbps_repo *repo; + const char *pkgver, *repoloc, *sha256; + char *binfile; + int rv = 0; + + xbps_dictionary_get_cstring_nocopy(pkgd, "repository", &repoloc); + xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); + + binfile = xbps_repository_pkg_path(xhp, pkgd); + if (binfile == NULL) { + return ENOMEM; + } + /* + * For pkgs in local repos check the sha256 hash. + * For pkgs in remote repos check the RSA signature. + */ + if ((repo = xbps_rpool_get_repo(repoloc)) == NULL) { + rv = errno; + xbps_dbg_printf(xhp, "%s: failed to get repository " + "%s: %s\n", pkgver, repoloc, strerror(errno)); + goto out; + } + if (repo->is_remote) { + /* remote repo */ + xbps_set_cb_state(xhp, XBPS_STATE_VERIFY, 0, pkgver, + "%s: verifying RSA signature...", pkgver); + + if (!xbps_verify_file_signature(repo, binfile)) { + char *sigfile; + rv = EPERM; + xbps_set_cb_state(xhp, XBPS_STATE_VERIFY_FAIL, rv, pkgver, + "%s: the RSA signature is not valid!", pkgver); + xbps_set_cb_state(xhp, XBPS_STATE_VERIFY_FAIL, rv, pkgver, + "%s: removed pkg archive and its signature.", pkgver); + (void)remove(binfile); + sigfile = xbps_xasprintf("%s.sig", binfile); + (void)remove(sigfile); + free(sigfile); + goto out; + } + } else { + /* local repo */ + xbps_set_cb_state(xhp, XBPS_STATE_VERIFY, 0, pkgver, + "%s: verifying SHA256 hash...", pkgver); + xbps_dictionary_get_cstring_nocopy(pkgd, "filename-sha256", &sha256); + if ((rv = xbps_file_hash_check(binfile, sha256)) != 0) { + xbps_set_cb_state(xhp, XBPS_STATE_VERIFY_FAIL, rv, pkgver, + "%s: SHA256 hash is not valid: %s", pkgver, strerror(rv)); + goto out; + } + + } +out: + free(binfile); + return rv; +} + +static int +download_binpkg(struct xbps_handle *xhp, xbps_dictionary_t repo_pkgd) +{ + struct xbps_repo *repo; + char buf[PATH_MAX]; + char *sigsuffix; + const char *pkgver, *arch, *fetchstr, *repoloc; + unsigned char *digest = NULL; + int rv = 0; + + xbps_dictionary_get_cstring_nocopy(repo_pkgd, "repository", &repoloc); + if (!xbps_repository_is_remote(repoloc)) + return ENOTSUP; + + xbps_dictionary_get_cstring_nocopy(repo_pkgd, "pkgver", &pkgver); + xbps_dictionary_get_cstring_nocopy(repo_pkgd, "architecture", &arch); + + snprintf(buf, sizeof buf, "%s/%s.%s.xbps.sig", repoloc, pkgver, arch); + sigsuffix = buf+(strlen(buf)-sizeof (".sig")+1); + + xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD, 0, pkgver, + "Downloading `%s' signature (from `%s')...", pkgver, repoloc); + + if ((rv = xbps_fetch_file(xhp, buf, NULL)) == -1) { + rv = fetchLastErrCode ? fetchLastErrCode : errno; + fetchstr = xbps_fetch_error_string(); + xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD_FAIL, rv, + pkgver, "[trans] failed to download `%s' signature from `%s': %s", + pkgver, repoloc, fetchstr ? fetchstr : strerror(rv)); + return rv; + } + rv = 0; + + *sigsuffix = '\0'; + + xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD, 0, pkgver, + "Downloading `%s' package (from `%s')...", pkgver, repoloc); + + if ((rv = xbps_fetch_file_digest(xhp, buf, NULL, &digest)) == -1) { + rv = fetchLastErrCode ? fetchLastErrCode : errno; + fetchstr = xbps_fetch_error_string(); + xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD_FAIL, rv, + pkgver, "[trans] failed to download `%s' package from `%s': %s", + pkgver, repoloc, fetchstr ? fetchstr : strerror(rv)); + return rv; + } + rv = 0; + + xbps_set_cb_state(xhp, XBPS_STATE_VERIFY, 0, pkgver, + "%s: verifying RSA signature...", pkgver); + + snprintf(buf, sizeof buf, "%s/%s.%s.xbps.sig", xhp->cachedir, pkgver, arch); + sigsuffix = buf+(strlen(buf)-sizeof (".sig")+1); + + if ((repo = xbps_rpool_get_repo(repoloc)) == NULL) { + rv = errno; + xbps_dbg_printf(xhp, "%s: failed to get repository " + "%s: %s\n", pkgver, repoloc, strerror(errno)); + return rv; + } + + /* + * If digest is not set, binary package was not downloaded, + * i.e. 304 not modified, verify by file instead. + */ + if (!digest) { + *sigsuffix = '\0'; + if (!xbps_verify_file_signature(repo, buf)) { + rv = EPERM; + /* remove binpkg */ + (void)remove(buf); + /* remove signature */ + *sigsuffix = '.'; + (void)remove(buf); + } + } else { + if (!xbps_verify_signature(repo, buf, digest)) { + rv = EPERM; + /* remove signature */ + (void)remove(buf); + /* remove binpkg */ + *sigsuffix = '\0'; + (void)remove(buf); + } + } + + if (rv == EPERM) { + xbps_set_cb_state(xhp, XBPS_STATE_VERIFY_FAIL, rv, pkgver, + "%s: the RSA signature is not valid!", pkgver); + xbps_set_cb_state(xhp, XBPS_STATE_VERIFY_FAIL, rv, pkgver, + "%s: removed pkg archive and its signature.", pkgver); + } + + return rv; +} + +int +xbps_transaction_fetch(struct xbps_handle *xhp, xbps_object_iterator_t iter) +{ + xbps_object_t obj; + const char *trans, *repoloc; + int rv = 0; + xbps_array_t fetch = NULL, verify = NULL; + unsigned int i, n; + + while ((obj = xbps_object_iterator_next(iter)) != NULL) { + xbps_dictionary_get_cstring_nocopy(obj, "transaction", &trans); + if ((strcmp(trans, "remove") == 0) || + (strcmp(trans, "hold") == 0) || + (strcmp(trans, "configure") == 0)) + continue; + + xbps_dictionary_get_cstring_nocopy(obj, "repository", &repoloc); + + /* + * Download binary package and signature if either one + * of them don't exist. + */ + if (xbps_repository_is_remote(repoloc) && + !xbps_remote_binpkg_exists(xhp, obj)) { + if (!fetch && !(fetch = xbps_array_create())) { + rv = errno; + goto out; + } + xbps_array_add(fetch, obj); + continue; + } + + /* + * Verify binary package from local repository or cache. + */ + if (!verify && !(verify = xbps_array_create())) { + rv = errno; + goto out; + } + xbps_array_add(verify, obj); + } + xbps_object_iterator_reset(iter); + + /* + * Download binary packages (if they come from a remote repository) + * and don't exist already. + */ + n = xbps_array_count(fetch); + if (n) { + xbps_set_cb_state(xhp, XBPS_STATE_TRANS_DOWNLOAD, 0, NULL, NULL); + xbps_dbg_printf(xhp, "[trans] downloading %d packages.\n", n); + } + for (i = 0; i < n; i++) { + if ((rv = download_binpkg(xhp, xbps_array_get(fetch, i))) != 0) { + xbps_dbg_printf(xhp, "[trans] failed to download binpkgs: " + "%s\n", strerror(rv)); + goto out; + } + } + + /* + * Check binary package integrity. + */ + n = xbps_array_count(verify); + if (n) { + xbps_set_cb_state(xhp, XBPS_STATE_TRANS_VERIFY, 0, NULL, NULL); + xbps_dbg_printf(xhp, "[trans] verifying %d packages.\n", n); + } + for (i = 0; i < n; i++) { + if ((rv = verify_binpkg(xhp, xbps_array_get(verify, i))) != 0) { + xbps_dbg_printf(xhp, "[trans] failed to check binpkgs: " + "%s\n", strerror(rv)); + goto out; + } + } + +out: + if (fetch) + xbps_object_release(fetch); + if (verify) + xbps_object_release(verify); + return rv; +}