diff --git a/NEWS b/NEWS index 2963aefa..e0046e13 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,12 @@ xbps-0.28 (2014-??-??): + * Implemented Per package RSA signatures, replacing the repository index + signature from 0.27. There's now a `.sig` file with the RSA signature + for any pkg in a repository; the signature files are generated by `xbps-rindex(8)`. + + * Implemented on-demand repository access: repositories are registered into + a private queue only on first access and not unconditionally. + * Fixed issue #26: https://github.com/voidlinux/xbps/issues/26 * xbps-query(8): -o (local ownedby mode) does not print directories; diff --git a/bin/xbps-install/state_cb.c b/bin/xbps-install/state_cb.c index 34f6b9ad..8387bb1e 100644 --- a/bin/xbps-install/state_cb.c +++ b/bin/xbps-install/state_cb.c @@ -47,17 +47,11 @@ state_cb(struct xbps_state_cb_data *xscd, void *cbdata _unused) switch (xscd->state) { /* notifications */ - case XBPS_STATE_REPO_SIGVERIFIED: - printf("[*] %s: RSA signature verified\n", xscd->arg); - break; - case XBPS_STATE_REPO_SIGUNVERIFIED: - printf("[*] %s: RSA signature invalid! ignoring...\n", xscd->arg); - break; case XBPS_STATE_TRANS_DOWNLOAD: printf("\n[*] Downloading binary packages\n"); break; case XBPS_STATE_TRANS_VERIFY: - printf("\n[*] Verifying binary package integrity\n"); + printf("\n[*] Verifying package integrity\n"); break; case XBPS_STATE_TRANS_RUN: printf("\n[*] Running transaction tasks\n"); @@ -69,7 +63,7 @@ state_cb(struct xbps_state_cb_data *xscd, void *cbdata _unused) printf("[*] Updating `%s' ...\n", xscd->arg); break; case XBPS_STATE_VERIFY: - printf("%s: checking binary pkg integrity ...\n", xscd->arg); + printf("%s\n", xscd->desc); break; case XBPS_STATE_CONFIG_FILE: if (xscd->desc != NULL) diff --git a/bin/xbps-query/list.c b/bin/xbps-query/list.c index 23e29e01..bbf46a92 100644 --- a/bin/xbps-query/list.c +++ b/bin/xbps-query/list.c @@ -159,16 +159,12 @@ repo_list_uri_cb(struct xbps_repo *repo, void *arg _unused, bool *done _unused) printf("%5zd %s", repo->idx ? (ssize_t)xbps_dictionary_count(repo->idx) : -1, repo->uri); - if (repo->is_remote) { - printf(" (RSA %s, %s)\n", - repo->is_signed ? "signed" : "unsigned", - repo->is_verified ? "verified" : "unverified"); - if (repo->xhp->flags & XBPS_FLAG_VERBOSE) { - if (repo->signedby) - printf(" Signed-by: %s\n", repo->signedby); - if (repo->hexfp) - printf(" %u %s\n", repo->pubkey_size, repo->hexfp); - } + printf(" (RSA %s)\n", repo->is_signed ? "signed" : "unsigned"); + if (repo->xhp->flags & XBPS_FLAG_VERBOSE) { + if (repo->signedby) + printf(" Signed-by: %s\n", repo->signedby); + if (repo->hexfp) + printf(" %u %s\n", repo->pubkey_size, repo->hexfp); } else { printf("\n"); } diff --git a/bin/xbps-query/show-info-files.c b/bin/xbps-query/show-info-files.c index 0cc34347..3ead2443 100644 --- a/bin/xbps-query/show-info-files.c +++ b/bin/xbps-query/show-info-files.c @@ -291,10 +291,14 @@ repo_show_pkg_info(struct xbps_handle *xhp, { xbps_dictionary_t pkgd; - if (((pkgd = xbps_rpool_get_pkg(xhp, pattern)) == NULL) && - ((pkgd = xbps_rpool_get_virtualpkg(xhp, pattern)) == NULL)) - return errno; - + pkgd = xbps_rpool_get_pkg_plist(xhp, pattern, "./props.plist"); + if (pkgd == NULL) { + if (errno != ENOTSUP && errno != ENOENT) { + fprintf(stderr, "Unexpected error: %s\n", + strerror(errno)); + return errno; + } + } if (option) show_pkg_info_one(pkgd, option); else diff --git a/bin/xbps-rindex/index-add.c b/bin/xbps-rindex/index-add.c index 572e2a4b..566fdb67 100644 --- a/bin/xbps-rindex/index-add.c +++ b/bin/xbps-rindex/index-add.c @@ -37,26 +37,20 @@ #include #include "defs.h" -/* - * Adds a binary package into the index and removes old binary package - * and entry when it's necessary. - */ int index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) { - xbps_array_t filespkgar, pkg_files, pkg_links, pkg_cffiles; - xbps_dictionary_t idx, idxfiles, newpkgd, newpkgfilesd, curpkgd; + xbps_array_t array, pkg_files, pkg_links, pkg_cffiles; + xbps_dictionary_t idx, idxfiles, idxpkgd, binpkgd, pkg_filesd, curpkgd; xbps_object_t obj, fileobj; struct xbps_repo *repo; struct stat st; - const char *arch; - char *pkgver, *opkgver, *oarch, *pkgname, *sha256, *repodir; - char *tmprepodir; + uint64_t instsize; + const char *arch, *desc; + char *sha256, *pkgver, *opkgver, *oarch, *pkgname, *tmprepodir, *repodir; int rv = 0, ret = 0; bool flush = false, found = false; - idx = idxfiles = newpkgd = newpkgfilesd = curpkgd = NULL; - if ((tmprepodir = strdup(argv[0])) == NULL) return ENOMEM; @@ -66,14 +60,14 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) repodir = dirname(tmprepodir); repo = xbps_repo_open(xhp, repodir); - if (repo == NULL) { - idx = xbps_dictionary_create(); - idxfiles = xbps_dictionary_create(); - } else { + if (repo && repo->idx) { xbps_repo_open_idxfiles(repo); idx = xbps_dictionary_copy(repo->idx); idxfiles = xbps_dictionary_copy(repo->idxfiles); xbps_repo_close(repo); + } else { + idx = xbps_dictionary_create(); + idxfiles = xbps_dictionary_create(); } /* @@ -83,20 +77,17 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) /* * Read metadata props plist dictionary from binary package. */ - newpkgd = xbps_get_pkg_plist_from_binpkg(argv[i], + binpkgd = xbps_get_pkg_plist_from_binpkg(argv[i], "./props.plist"); - if (newpkgd == NULL) { - fprintf(stderr, "failed to read %s metadata for `%s'," - " skipping!\n", XBPS_PKGPROPS, argv[i]); + if (binpkgd == NULL) { + fprintf(stderr, "failed to read %s metadata for `%s', skipping!\n", XBPS_PKGPROPS, argv[i]); continue; } - xbps_dictionary_get_cstring_nocopy(newpkgd, "architecture", - &arch); - xbps_dictionary_get_cstring(newpkgd, "pkgver", &pkgver); + xbps_dictionary_get_cstring_nocopy(binpkgd, "architecture", &arch); + xbps_dictionary_get_cstring(binpkgd, "pkgver", &pkgver); if (!xbps_pkg_arch_match(xhp, arch, NULL)) { - fprintf(stderr, "index: ignoring %s, unmatched " - "arch (%s)\n", pkgver, arch); - xbps_object_release(newpkgd); + fprintf(stderr, "index: ignoring %s, unmatched arch (%s)\n", pkgver, arch); + xbps_object_release(binpkgd); continue; } pkgname = xbps_pkg_name(pkgver); @@ -121,10 +112,8 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) ret = xbps_cmpver(pkgver, opkgver); if (ret <= 0) { /* Same version or index version greater */ - fprintf(stderr, "index: skipping `%s' " - "(%s), already registered.\n", - pkgver, arch); - xbps_object_release(newpkgd); + fprintf(stderr, "index: skipping `%s' (%s), already registered.\n", pkgver, arch); + xbps_object_release(binpkgd); free(opkgver); free(oarch); free(pkgver); @@ -141,75 +130,125 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) free(opkgver); free(oarch); } + idxpkgd = xbps_dictionary_create(); + assert(idxpkgd); /* - * We have the dictionary now, add the required - * objects for the index. + * Only copy relevant objects from binpkg: + * - architecture + * - pkgver + * - short_desc + * - installed_size + * - run_depends + * - provides + * - replaces + * - shlib-requires + */ + if (!xbps_dictionary_set_cstring(idxpkgd, "architecture", arch)) { + free(pkgver); + free(pkgname); + return errno; + } + if (!xbps_dictionary_set_cstring(idxpkgd, "pkgver", pkgver)) { + free(pkgver); + free(pkgname); + return errno; + } + xbps_dictionary_get_cstring_nocopy(binpkgd, "short_desc", &desc); + if (!xbps_dictionary_set_cstring(idxpkgd, "short_desc", desc)) { + free(pkgver); + free(pkgname); + return errno; + } + xbps_dictionary_get_uint64(binpkgd, "installed_size", &instsize); + if (!xbps_dictionary_set_uint64(idxpkgd, "installed_size", instsize)) { + free(pkgver); + free(pkgname); + return errno; + } + array = xbps_dictionary_get(binpkgd, "run_depends"); + if (xbps_array_count(array) && !xbps_dictionary_set(idxpkgd, "run_depends", array)) { + free(pkgver); + free(pkgname); + return errno; + } + array = xbps_dictionary_get(binpkgd, "provides"); + if (xbps_array_count(array) && !xbps_dictionary_set(idxpkgd, "provides", array)) { + free(pkgver); + free(pkgname); + return errno; + } + array = xbps_dictionary_get(binpkgd, "replaces"); + if (xbps_array_count(array) && !xbps_dictionary_set(idxpkgd, "replaces", array)) { + free(pkgver); + free(pkgname); + return errno; + } + array = xbps_dictionary_get(binpkgd, "shlib-requires"); + if (xbps_array_count(array) && !xbps_dictionary_set(idxpkgd, "shlib-requires", array)) { + free(pkgver); + free(pkgname); + return errno; + } + /* + * Add additional objects for repository ops: + * - filename-size + * - filename-sha256 */ if ((sha256 = xbps_file_hash(argv[i])) == NULL) { free(pkgver); free(pkgname); return errno; } - if (!xbps_dictionary_set_cstring(newpkgd, "filename-sha256", - sha256)) { + if (!xbps_dictionary_set_cstring(idxpkgd, "filename-sha256", sha256)) { free(pkgver); free(pkgname); return errno; } - - free(sha256); if (stat(argv[i], &st) == -1) { free(pkgver); free(pkgname); return errno; } - - if (!xbps_dictionary_set_uint64(newpkgd, "filename-size", - (uint64_t)st.st_size)) { + if (!xbps_dictionary_set_uint64(idxpkgd, "filename-size", (uint64_t)st.st_size)) { free(pkgver); free(pkgname); return errno; } - /* - * Remove obsolete package objects. - */ - xbps_dictionary_remove(newpkgd, "pkgname"); - xbps_dictionary_remove(newpkgd, "version"); /* * Add new pkg dictionary into the index. */ - if (!xbps_dictionary_set(idx, pkgname, newpkgd)) { + if (!xbps_dictionary_set(idx, pkgname, idxpkgd)) { free(pkgname); return EINVAL; } - free(pkgname); flush = true; printf("index: added `%s' (%s).\n", pkgver, arch); + xbps_object_release(idxpkgd); + xbps_object_release(binpkgd); + free(pkgname); /* * Add new pkg dictionary into the index-files. */ found = false; - newpkgfilesd = xbps_get_pkg_plist_from_binpkg(argv[i], - "./files.plist"); - if (newpkgfilesd == NULL) { + pkg_filesd = xbps_get_pkg_plist_from_binpkg(argv[i], "./files.plist"); + if (pkg_filesd == NULL) { free(pkgver); return EINVAL; } - /* Find out if binary pkg stored in index contain any file */ - pkg_cffiles = xbps_dictionary_get(newpkgfilesd, "conf_files"); + pkg_cffiles = xbps_dictionary_get(pkg_filesd, "conf_files"); if (xbps_array_count(pkg_cffiles)) found = true; else pkg_cffiles = NULL; - pkg_files = xbps_dictionary_get(newpkgfilesd, "files"); + pkg_files = xbps_dictionary_get(pkg_filesd, "files"); if (xbps_array_count(pkg_files)) found = true; else pkg_files = NULL; - pkg_links = xbps_dictionary_get(newpkgfilesd, "links"); + pkg_links = xbps_dictionary_get(pkg_filesd, "links"); if (xbps_array_count(pkg_links)) found = true; else @@ -217,45 +256,42 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) /* If pkg does not contain any file, ignore it */ if (!found) { - xbps_object_release(newpkgfilesd); - xbps_object_release(newpkgd); + xbps_object_release(pkg_filesd); free(pkgver); continue; } /* create pkg files array */ - filespkgar = xbps_array_create(); - assert(filespkgar); + array = xbps_array_create(); + assert(array); /* add conf_files in pkg files array */ if (pkg_cffiles != NULL) { for (unsigned int x = 0; x < xbps_array_count(pkg_cffiles); x++) { obj = xbps_array_get(pkg_cffiles, x); fileobj = xbps_dictionary_get(obj, "file"); - xbps_array_add(filespkgar, fileobj); + xbps_array_add(array, fileobj); } } - /* add files array in pkg array */ + /* add files array in pkg files array */ if (pkg_files != NULL) { for (unsigned int x = 0; x < xbps_array_count(pkg_files); x++) { obj = xbps_array_get(pkg_files, x); fileobj = xbps_dictionary_get(obj, "file"); - xbps_array_add(filespkgar, fileobj); + xbps_array_add(array, fileobj); } } - /* add links array in pkgd */ + /* add links array in pkg files array */ if (pkg_links != NULL) { for (unsigned int x = 0; x < xbps_array_count(pkg_links); x++) { obj = xbps_array_get(pkg_links, x); fileobj = xbps_dictionary_get(obj, "file"); - xbps_array_add(filespkgar, fileobj); + xbps_array_add(array, fileobj); } } - xbps_object_release(newpkgfilesd); - /* add pkg files array into index-files */ - xbps_dictionary_set(idxfiles, pkgver, filespkgar); - xbps_object_release(filespkgar); - xbps_object_release(newpkgd); + xbps_dictionary_set(idxfiles, pkgver, array); + xbps_object_release(array); + xbps_object_release(pkg_filesd); free(pkgver); } /* @@ -263,15 +299,12 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) */ if (flush) { if (!repodata_flush(xhp, repodir, idx, idxfiles, NULL)) { - fprintf(stderr, "failed to write repodata: %s\n", - strerror(errno)); + fprintf(stderr, "failed to write repodata: %s\n", strerror(errno)); return -1; } } - printf("index: %u packages registered.\n", - xbps_dictionary_count(idx)); - printf("index-files: %u packages registered.\n", - xbps_dictionary_count(idxfiles)); + printf("index: %u packages registered.\n", xbps_dictionary_count(idx)); + printf("index-files: %u packages registered.\n", xbps_dictionary_count(idxfiles)); return rv; } diff --git a/bin/xbps-rindex/sign.c b/bin/xbps-rindex/sign.c index 288f1f91..d6e8e587 100644 --- a/bin/xbps-rindex/sign.c +++ b/bin/xbps-rindex/sign.c @@ -92,52 +92,43 @@ pubkey_from_privkey(RSA *rsa) return buf; } -static RSA * -rsa_sign_buf(const char *privkey, const char *buf, +static bool +rsa_sign_buf(RSA *rsa, const char *buf, unsigned int buflen, unsigned char **sigret, unsigned int *siglen) { SHA256_CTX context; - RSA *rsa; unsigned char sha256[SHA256_DIGEST_LENGTH]; - ERR_load_crypto_strings(); - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - OpenSSL_add_all_ciphers(); - OpenSSL_add_all_digests(); - - rsa = load_rsa_privkey(privkey); - if (rsa == NULL) { - fprintf(stderr, "can't load private key from %s\n", privkey); - return NULL; - } - SHA256_Init(&context); - SHA256_Update(&context, buf, strlen(buf)); + SHA256_Update(&context, buf, buflen); SHA256_Final(sha256, &context); *sigret = calloc(1, RSA_size(rsa) + 1); - if (RSA_sign(NID_sha1, sha256, sizeof(sha256), - *sigret, siglen, rsa) == 0) { - fprintf(stderr, "%s: %s\n", privkey, - ERR_error_string(ERR_get_error(), NULL)); - return NULL; + if (!RSA_sign(NID_sha1, sha256, sizeof(sha256), + *sigret, siglen, rsa)) { + free(*sigret); + return false; } - return rsa; + return true; } int sign_repo(struct xbps_handle *xhp, const char *repodir, const char *privkey, const char *signedby) { - RSA *rsa = NULL; + struct stat st; struct xbps_repo *repo; - xbps_dictionary_t idx, idxfiles, meta = NULL; + xbps_dictionary_t pkgd, idx, idxfiles, meta = NULL; xbps_data_t data; - unsigned int siglen; + xbps_object_iterator_t iter = NULL; + xbps_object_t obj; + RSA *rsa = NULL; unsigned char *sig; - char *buf = NULL, *xml = NULL, *defprivkey = NULL; - int rv = -1; + unsigned int siglen; + const char *arch, *pkgver; + char *binpkg, *binpkg_sig, *buf, *defprivkey; + int binpkg_fd, binpkg_sig_fd; + bool flush = false; if (signedby == NULL) { fprintf(stderr, "--signedby unset! cannot sign repository\n"); @@ -160,15 +151,6 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, idx = xbps_dictionary_copy(repo->idx); idxfiles = xbps_dictionary_copy(repo->idxfiles); xbps_repo_close(repo); - - /* - * Externalize the index and then sign it. - */ - xml = xbps_dictionary_externalize(idx); - if (xml == NULL) { - fprintf(stderr, "failed to externalize repository index: %s\n", strerror(errno)); - goto out; - } /* * If privkey not set, default to ~/.ssh/id_rsa. */ @@ -177,50 +159,116 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, else defprivkey = strdup(privkey); - rsa = rsa_sign_buf(defprivkey, xml, &sig, &siglen); - if (rsa == NULL) - goto out; - /* - * If the signature in repo has not changed do not generate the - * repodata file again. - */ - if (xbps_data_equals_data(repo->signature, sig, siglen)) { - fprintf(stderr, "Not signing again, matched signature found.\n"); - rv = 0; - goto out; + ERR_load_crypto_strings(); + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_digests(); + + if ((rsa = load_rsa_privkey(defprivkey)) == NULL) { + fprintf(stderr, "failed to read the RSA privkey\n"); + return -1; } /* - * Prepare the XBPS_REPOIDX_META for our repository data. + * Iterate over the idx dictionary and then sign all binary + * packages in this repository. */ - meta = xbps_dictionary_create(); - xbps_dictionary_set_cstring_nocopy(meta, "signature-by", signedby); - xbps_dictionary_set_cstring_nocopy(meta, "signature-type", "rsa"); - data = xbps_data_create_data_nocopy(sig, siglen); - xbps_dictionary_set(meta, "signature", data); + iter = xbps_dictionary_iterator(idx); + while ((obj = xbps_object_iterator_next(iter))) { + pkgd = xbps_dictionary_get_keysym(idx, obj); + xbps_dictionary_get_cstring_nocopy(pkgd, "architecture", &arch); + xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver); - buf = pubkey_from_privkey(rsa); - assert(buf); - data = xbps_data_create_data_nocopy(buf, strlen(buf)); - xbps_dictionary_set(meta, "public-key", data); - xbps_dictionary_set_uint16(meta, "public-key-size", RSA_size(rsa) * 8); - - /* - * and finally write our repodata file! - */ - if (!repodata_flush(xhp, repodir, idx, idxfiles, meta)) { - fprintf(stderr, "failed to write repodata: %s\n", strerror(errno)); - goto out; - } - - rv = 0; - -out: - if (xml != NULL) - free(xml); - if (buf != NULL) + binpkg = xbps_xasprintf("%s/%s.%s.xbps", repodir, pkgver, arch); + binpkg_sig = xbps_xasprintf("%s.sig", binpkg); + /* + * Skip pkg if file signature exists + */ + if ((binpkg_sig_fd = open(binpkg_sig, O_RDONLY)) == 0) { + fprintf(stdout, "skipping %s, file signature found.\n", pkgver); + free(binpkg); + free(binpkg_sig); + close(binpkg_sig_fd); + continue; + } + /* + * Generate pkg file signature. + */ + if ((binpkg_fd = open(binpkg, O_RDONLY)) == -1) { + fprintf(stderr, "cannot read %s: %s\n", binpkg, strerror(errno)); + free(binpkg); + free(binpkg_sig); + continue; + } + fstat(binpkg_fd, &st); + buf = malloc(st.st_size); + assert(buf); + if (read(binpkg_fd, buf, st.st_size) != st.st_size) { + fprintf(stderr, "failed to read %s: %s\n", binpkg, strerror(errno)); + close(binpkg_fd); + free(buf); + free(binpkg); + free(binpkg_sig); + continue; + } + close(binpkg_fd); + if (!rsa_sign_buf(rsa, buf, st.st_size, &sig, &siglen)) { + fprintf(stderr, "failed to sign %s: %s\n", binpkg, strerror(errno)); + free(buf); + free(binpkg); + free(binpkg_sig); + continue; + } free(buf); - if (rsa != NULL) - RSA_free(rsa); + free(binpkg); + /* + * Write pkg file signature. + */ + binpkg_sig_fd = creat(binpkg_sig, 0644); + if (binpkg_sig_fd == -1) { + fprintf(stderr, "failed to create %s: %s\n", binpkg_sig, strerror(errno)); + free(binpkg_sig); + continue; + } + if (write(binpkg_sig_fd, sig, siglen) != siglen) { + fprintf(stderr, "failed to write %s: %s\n", binpkg_sig, strerror(errno)); + free(sig); + free(binpkg_sig); + close(binpkg_sig_fd); + continue; + } + flush = true; + free(sig); + free(binpkg_sig); + close(binpkg_sig_fd); + binpkg_fd = binpkg_sig_fd = -1; + printf("Signed successfully %s\n", pkgver); + } + xbps_object_iterator_release(iter); - return rv; + if (flush) { + /* + * Prepare the XBPS_REPOIDX_META for our repository data. + */ + meta = xbps_dictionary_create(); + xbps_dictionary_set_cstring_nocopy(meta, "signature-by", signedby); + xbps_dictionary_set_cstring_nocopy(meta, "signature-type", "rsa"); + buf = pubkey_from_privkey(rsa); + assert(buf); + data = xbps_data_create_data(buf, strlen(buf)); + xbps_dictionary_set(meta, "public-key", data); + xbps_dictionary_set_uint16(meta, "public-key-size", RSA_size(rsa) * 8); + free(buf); + xbps_object_release(data); + /* + * and finally write our repodata file! + */ + if (!repodata_flush(xhp, repodir, idx, idxfiles, meta)) { + fprintf(stderr, "failed to write repodata: %s\n", strerror(errno)); + RSA_free(rsa); + return -1; + } + } + RSA_free(rsa); + + return 0; } diff --git a/include/xbps.h.in b/include/xbps.h.in index 10f0fc1e..956b0803 100644 --- a/include/xbps.h.in +++ b/include/xbps.h.in @@ -47,7 +47,7 @@ * * This header documents the full API for the XBPS Library. */ -#define XBPS_API_VERSION "20131216-2" +#define XBPS_API_VERSION "20131224" #ifndef XBPS_VERSION #define XBPS_VERSION "UNSET" @@ -81,12 +81,6 @@ */ #define XBPS_PKGDB "pkgdb-0.21.plist" -/** - * @def XBPS_REPOKEYS - * Filename for the repository keys. - */ -#define XBPS_REPOKEYS "repokeys.plist" - /** * @def XBPS_PKGPROPS * Filename for package metadata property list. @@ -220,6 +214,7 @@ extern "C" { * install, update, remove and replace. * - XBPS_STATE_TRANS_CONFIGURE: transaction is configuring all * unpacked packages. + * - XBPS_STATE_TRANS_FAIL: transaction has failed. * - XBPS_STATE_DOWNLOAD: a binary package is being downloaded. * - XBPS_STATE_VERIFY: a binary package is being verified. * - XBPS_STATE_REMOVE: a package is being removed. @@ -252,8 +247,6 @@ extern "C" { * - XBPS_STATE_UNPACK_FAIL: package unpack has failed. * - XBPS_STATE_REPOSYNC_FAIL: syncing remote repositories has failed. * - XBPS_STATE_REPO_KEY_IMPORT: repository is signed and needs to import pubkey. - * - XBPS_STATE_REPO_SIGVERIFIED: repository is signed and verified. - * - XBPS_STATE_REPO_SIGUNVERIFIED: repository is signed and UNVERIFIED. */ typedef enum xbps_state { XBPS_STATE_UNKNOWN = 0, @@ -261,6 +254,7 @@ typedef enum xbps_state { XBPS_STATE_TRANS_VERIFY, XBPS_STATE_TRANS_RUN, XBPS_STATE_TRANS_CONFIGURE, + XBPS_STATE_TRANS_FAIL, XBPS_STATE_DOWNLOAD, XBPS_STATE_VERIFY, XBPS_STATE_REMOVE, @@ -291,9 +285,7 @@ typedef enum xbps_state { XBPS_STATE_UNPACK_FAIL, XBPS_STATE_REPOSYNC_FAIL, XBPS_STATE_CONFIGURE_DONE, - XBPS_STATE_REPO_KEY_IMPORT, - XBPS_STATE_REPO_SIGVERIFIED, - XBPS_STATE_REPO_SIGUNVERIFIED + XBPS_STATE_REPO_KEY_IMPORT } xbps_state_t; /** @@ -589,11 +581,6 @@ struct xbps_handle { * - XBPS_FLAG_INSTALL_AUTO */ int flags; - /** - * @private - */ - bool initialized; - bool rpool_initialized; }; void xbps_dbg_printf(struct xbps_handle *, const char *, ...); @@ -1138,12 +1125,6 @@ struct xbps_repo { * Proplib dictionary associated with the repository index-files. */ xbps_dictionary_t idxfiles; - /** - * @var signature - * - * RSA signature associated with this repository in a prop_data object. - */ - xbps_data_t signature; /** * @var pubkey * @@ -1186,13 +1167,6 @@ struct xbps_repo { * True if this repository has been signed, false otherwise. */ bool is_signed; - /** - * var is_verified - * - * True if this repository has been signed and verified against its public key. - * False if the stored public key did not match its signature. - */ - bool is_verified; }; /** @@ -1227,6 +1201,14 @@ int xbps_rpool_foreach(struct xbps_handle *xhp, int (*fn)(struct xbps_repo *, void *, bool *), void *arg); +/** + * Returns a pointer to a struct xbps_repo matching \a url. + * + * @param[in] url Repository url to match. + * @return The matched xbps_repo pointer, NULL otherwise. + */ +struct xbps_repo *xbps_rpool_get_repo(const char *url); + /** * Finds a package dictionary in the repository pool by specifying a * package pattern or a package name. This function does not take into @@ -1392,6 +1374,7 @@ xbps_array_t xbps_repo_get_pkg_revdeps(struct xbps_repo *repo, const char *pkg); */ int xbps_repo_key_import(struct xbps_repo *repo); + /*@}*/ /** @addtogroup archive_util */ @@ -1552,6 +1535,18 @@ char *xbps_file_hash(const char *file); */ int xbps_file_hash_check(const char *file, const char *sha256); +/** + * Verifies the RSA signature of \a fname with the RSA public-key associated + * in \a repo. + * + * @param[in] repo Repository to use with the RSA public key associated. + * @param[in] fname The filename to verify, the signature file must have a .sig + * extension, i.e `.sig`. + * + * @return True if the signature is valid, false otherwise. + */ +bool xbps_verify_file_signature(struct xbps_repo *repo, const char *fname); + /** * Checks if a package is currently installed by matching \a pkg. * diff --git a/include/xbps_api_impl.h b/include/xbps_api_impl.h index 861906f0..ed48e595 100644 --- a/include/xbps_api_impl.h +++ b/include/xbps_api_impl.h @@ -60,6 +60,9 @@ /* libarchive compat */ #if ARCHIVE_VERSION_NUMBER >= 3000000 +#define archive_read_support_compression_all(x) \ + archive_read_support_filter_all(x) + #define archive_read_support_compression_gzip(x) \ archive_read_support_filter_gzip(x) @@ -131,13 +134,6 @@ bool HIDDEN xbps_remove_string_from_array(xbps_array_t, const char *); */ char HIDDEN *xbps_repository_pkg_path(struct xbps_handle *, xbps_dictionary_t); -/** - * @private - * From lib/rpool.c - */ -int HIDDEN xbps_rpool_init(struct xbps_handle *); -void HIDDEN xbps_rpool_release(struct xbps_handle *); - /** * @private * From lib/download.c @@ -163,12 +159,6 @@ int HIDDEN xbps_entry_install_conf_file(struct xbps_handle *, */ void HIDDEN xbps_repo_invalidate(struct xbps_repo *); -/** - * @private - * From lib/repo_keys.c - */ -int HIDDEN xbps_repo_key_verify(struct xbps_repo *); - /** * @private * From lib/repo_pkgdeps.c diff --git a/lib/Makefile b/lib/Makefile index 7012d185..234e843a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -35,14 +35,14 @@ EXTOBJS = external/dewey.o external/fexec.o external/mkpath.o # libxbps OBJS = package_configure.o package_config_files.o package_orphans.o OBJS += package_remove.o package_find_obsoletes.o package_state.o -OBJS += package_unpack.o package_register.o package_script.o +OBJS += package_unpack.o package_register.o package_script.o verifysig.o OBJS += transaction_commit.o transaction_package_replace.o OBJS += transaction_dictionary.o transaction_sortdeps.o transaction_ops.o OBJS += transaction_revdeps.o pubkey2fp.o OBJS += download.o initend.o pkgdb.o package_conflicts.o OBJS += plist.o plist_find.o plist_match.o archive.o OBJS += plist_remove.o plist_fetch.o util.o util_hash.o -OBJS += repo.o repo_pkgdeps.o repo_sync.o repo_keys.o +OBJS += repo.o repo_pkgdeps.o repo_sync.o OBJS += rpool.o rpool_get.o cb_util.o proplib_wrapper.o OBJS += $(EXTOBJS) $(COMPAT_SRCS) diff --git a/lib/initend.c b/lib/initend.c index d00fe566..21d3bc57 100644 --- a/lib/initend.c +++ b/lib/initend.c @@ -93,9 +93,6 @@ xbps_init(struct xbps_handle *xhp) assert(xhp != NULL); - if (xhp->initialized) - return 0; - if (xhp->conffile == NULL) xhp->conffile = XBPS_CONF_DEF; @@ -219,9 +216,6 @@ xbps_init(struct xbps_handle *xhp) xbps_dbg_printf(xhp, "Repository[%u]=%s\n", i, repodir); } } - - xhp->initialized = true; - return 0; } @@ -230,18 +224,13 @@ xbps_end(struct xbps_handle *xhp) { assert(xhp); - if (!xhp->initialized) - return; - xbps_pkgdb_release(xhp); - xbps_rpool_release(xhp); if (xbps_object_type(xhp->pkgdb_revdeps) != XBPS_TYPE_UNKNOWN) xbps_object_release(xhp->pkgdb_revdeps); xbps_fetch_unset_cache_connection(); cfg_free(xhp->cfg); - xhp->initialized = false; } static void diff --git a/lib/plist_fetch.c b/lib/plist_fetch.c index a3fe30bc..08d24e91 100644 --- a/lib/plist_fetch.c +++ b/lib/plist_fetch.c @@ -146,7 +146,7 @@ xbps_get_pkg_plist_from_binpkg(const char *fname, const char *plistf) xbps_dictionary_t plistd = NULL; struct archive *a; struct archive_entry *entry; - const char *curpath, *comptype; + const char *comptype; int i = 0; assert(fname != NULL); @@ -161,8 +161,7 @@ xbps_get_pkg_plist_from_binpkg(const char *fname, const char *plistf) comptype = archive_compression_name(a); while ((archive_read_next_header(a, &entry)) == ARCHIVE_OK) { - curpath = archive_entry_pathname(entry); - if (strcmp(curpath, plistf)) { + if (strcmp(archive_entry_pathname(entry), plistf)) { archive_read_data_skip(a); if (i >= 3) { /* diff --git a/lib/repo.c b/lib/repo.c index 25507956..5daeccbc 100644 --- a/lib/repo.c +++ b/lib/repo.c @@ -28,8 +28,15 @@ #include #include #include +#include #include +#include +#include +#include +#include +#include + #include "xbps_api_impl.h" /** @@ -97,12 +104,10 @@ struct xbps_repo * xbps_repo_open(struct xbps_handle *xhp, const char *url) { xbps_dictionary_t meta; - struct archive *ar = NULL; - struct xbps_repo *repo = NULL; + struct xbps_repo *repo; struct stat st; const char *arch; char *repofile; - bool is_remote = false; assert(xhp); assert(url); @@ -112,6 +117,11 @@ xbps_repo_open(struct xbps_handle *xhp, const char *url) else arch = xhp->native_arch; + repo = calloc(1, sizeof(struct xbps_repo)); + assert(repo); + repo->xhp = xhp; + repo->uri = url; + if (xbps_repository_is_remote(url)) { /* remote repository */ char *rpath; @@ -120,7 +130,7 @@ xbps_repo_open(struct xbps_handle *xhp, const char *url) return NULL; repofile = xbps_xasprintf("%s/%s/%s-repodata", xhp->metadir, rpath, arch); free(rpath); - is_remote = true; + repo->is_remote = true; } else { /* local repository */ repofile = xbps_repo_path(xhp, url); @@ -129,43 +139,31 @@ xbps_repo_open(struct xbps_handle *xhp, const char *url) if (stat(repofile, &st) == -1) { xbps_dbg_printf(xhp, "[repo] `%s' stat repodata %s\n", repofile, strerror(errno)); - free(repofile); - return NULL; - } - - ar = archive_read_new(); - archive_read_support_compression_gzip(ar); - archive_read_support_format_tar(ar); - - if (archive_read_open_filename(ar, repofile, st.st_blksize) == ARCHIVE_FATAL) { - xbps_dbg_printf(xhp, - "[repo] `%s' failed to open repodata archive %s\n", - repofile, strerror(archive_errno(repo->ar))); - archive_read_free(ar); - free(repo); - repo = NULL; goto out; } - repo = calloc(1, sizeof(struct xbps_repo)); - assert(repo); - repo->ar = ar; - repo->xhp = xhp; - repo->uri = url; - repo->is_remote = is_remote; + repo->ar = archive_read_new(); + archive_read_support_compression_gzip(repo->ar); + archive_read_support_format_tar(repo->ar); + if (archive_read_open_filename(repo->ar, repofile, st.st_blksize) == ARCHIVE_FATAL) { + xbps_dbg_printf(xhp, + "[repo] `%s' failed to open repodata archive %s\n", + repofile, strerror(archive_errno(repo->ar))); + archive_read_free(repo->ar); + repo->ar = NULL; + goto out; + } if ((repo->idx = repo_get_dict(repo)) == NULL) { xbps_dbg_printf(xhp, "[repo] `%s' failed to internalize index on archive %s: %s\n", url, repofile, strerror(archive_errno(repo->ar))); archive_read_finish(repo->ar); - free(repo); - repo = NULL; + repo->ar = NULL; goto out; } if ((meta = repo_get_dict(repo))) { repo->is_signed = true; - repo->signature = xbps_dictionary_get(meta, "signature"); xbps_dictionary_get_cstring_nocopy(meta, "signature-by", &repo->signedby); repo->pubkey = xbps_dictionary_get(meta, "public-key"); xbps_dictionary_get_uint16(meta, "public-key-size", &repo->pubkey_size); @@ -184,24 +182,6 @@ xbps_repo_open_idxfiles(struct xbps_repo *repo) repo->idxfiles = repo_get_dict(repo); } -void HIDDEN -xbps_repo_invalidate(struct xbps_repo *repo) -{ - if (repo->ar != NULL) { - archive_read_finish(repo->ar); - repo->ar = NULL; - } - if (repo->idx != NULL) { - xbps_object_release(repo->idx); - repo->idx = NULL; - } - if (repo->idxfiles != NULL) { - xbps_object_release(repo->idxfiles); - repo->idxfiles = NULL; - } - repo->is_verified = false; -} - void xbps_repo_close(struct xbps_repo *repo) { @@ -230,7 +210,7 @@ xbps_repo_get_virtualpkg(struct xbps_repo *repo, const char *pkg) assert(repo); assert(pkg); - if (repo->ar == NULL || repo->idx == NULL) + if (repo->idx == NULL) return NULL; pkgd = xbps_find_virtualpkg_in_dict(repo->xhp, repo->idx, pkg); @@ -250,7 +230,7 @@ xbps_repo_get_pkg(struct xbps_repo *repo, const char *pkg) assert(repo); assert(pkg); - if (repo->ar == NULL || repo->idx == NULL) + if (repo->idx == NULL) return NULL; pkgd = xbps_find_pkg_in_dict(repo->idx, pkg); @@ -427,3 +407,91 @@ xbps_repo_get_pkg_revdeps(struct xbps_repo *repo, const char *pkg) return revdeps; } + +int +xbps_repo_key_import(struct xbps_repo *repo) +{ + xbps_dictionary_t repokeyd = NULL; + char *p, *dbkeyd, *rkeyfile = NULL; + int import, rv = 0; + + assert(repo); + /* + * If repository does not have required metadata plist, ignore it. + */ + if (repo->pubkey == NULL) { + xbps_dbg_printf(repo->xhp, + "[repo] `%s' unsigned repository!\n", repo->uri); + return 0; + } + /* + * Check the repository provides a working public-key data object. + */ + repo->is_signed = true; + if (repo->hexfp == NULL) { + xbps_dbg_printf(repo->xhp, + "[repo] `%s': invalid hex fingerprint: %s\n", + repo->uri, strerror(errno)); + rv = EINVAL; + goto out; + } + /* + * Check if the public key is alredy stored. + */ + rkeyfile = xbps_xasprintf("%s/keys/%s.plist", + repo->xhp->metadir, repo->hexfp); + repokeyd = xbps_dictionary_internalize_from_zfile(rkeyfile); + if (xbps_object_type(repokeyd) == XBPS_TYPE_DICTIONARY) { + xbps_dbg_printf(repo->xhp, + "[repo] `%s' public key already stored.\n", repo->uri); + goto out; + } + /* + * Notify the client and take appropiate action to import + * the repository public key. Pass back the public key openssh fingerprint + * to the client. + */ + import = xbps_set_cb_state(repo->xhp, XBPS_STATE_REPO_KEY_IMPORT, 0, + repo->hexfp, "`%s' repository has been RSA signed by \"%s\"", + repo->uri, repo->signedby); + if (import <= 0) { + rv = EAGAIN; + goto out; + } + + p = strdup(rkeyfile); + dbkeyd = dirname(p); + assert(dbkeyd); + if (access(dbkeyd, R_OK|W_OK) == -1) { + if (errno == ENOENT) { + xbps_mkpath(dbkeyd, 0755); + } else { + rv = errno; + xbps_dbg_printf(repo->xhp, + "[repo] `%s' cannot create %s: %s\n", + repo->uri, dbkeyd, strerror(errno)); + free(p); + goto out; + } + } + free(p); + + repokeyd = xbps_dictionary_create(); + xbps_dictionary_set(repokeyd, "public-key", repo->pubkey); + xbps_dictionary_set_uint16(repokeyd, "public-key-size", repo->pubkey_size); + xbps_dictionary_set_cstring_nocopy(repokeyd, "signature-by", repo->signedby); + + if (!xbps_dictionary_externalize_to_zfile(repokeyd, rkeyfile)) { + rv = errno; + xbps_dbg_printf(repo->xhp, + "[repo] `%s' failed to externalize %s: %s\n", + repo->uri, rkeyfile, strerror(rv)); + } + +out: + if (repokeyd) + xbps_object_release(repokeyd); + if (rkeyfile) + free(rkeyfile); + return rv; +} diff --git a/lib/repo_keys.c b/lib/repo_keys.c deleted file mode 100644 index f70d32d7..00000000 --- a/lib/repo_keys.c +++ /dev/null @@ -1,213 +0,0 @@ -/*- - * Copyright (c) 2013 Juan Romero Pardines. - * 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 -#include -#include -#include -#include - -#include "xbps_api_impl.h" - -int -xbps_repo_key_import(struct xbps_repo *repo) -{ - xbps_dictionary_t repokeyd = NULL; - char *p, *dbkeyd, *rkeyfile = NULL; - int import, rv = 0; - - assert(repo); - /* - * Ignore local repositories. - */ - if (!xbps_repository_is_remote(repo->uri)) - return 0; - /* - * If repository does not have required metadata plist, ignore it. - */ - if (repo->signature == NULL && repo->pubkey == NULL) { - xbps_dbg_printf(repo->xhp, - "[repo] `%s' unsigned repository!\n", repo->uri); - return 0; - } - /* - * Check the repository provides a working public-key data object. - */ - repo->is_signed = true; - if (repo->hexfp == NULL) { - xbps_dbg_printf(repo->xhp, - "[repo] `%s': invalid hex fingerprint: %s\n", - repo->uri, strerror(errno)); - rv = EINVAL; - goto out; - } - /* - * Check if the public key is alredy stored. - */ - rkeyfile = xbps_xasprintf("%s/keys/%s.plist", - repo->xhp->metadir, repo->hexfp); - repokeyd = xbps_dictionary_internalize_from_zfile(rkeyfile); - if (xbps_object_type(repokeyd) == XBPS_TYPE_DICTIONARY) { - xbps_dbg_printf(repo->xhp, - "[repo] `%s' public key already stored.\n", repo->uri); - goto out; - } - /* - * Notify the client and take appropiate action to import - * the repository public key. Pass back the public key openssh fingerprint - * to the client. - */ - import = xbps_set_cb_state(repo->xhp, XBPS_STATE_REPO_KEY_IMPORT, 0, - repo->hexfp, "`%s' repository has been RSA signed by \"%s\"", - repo->uri, repo->signedby); - if (import <= 0) { - rv = EAGAIN; - goto out; - } - - p = strdup(rkeyfile); - dbkeyd = dirname(p); - assert(dbkeyd); - if (access(dbkeyd, R_OK|W_OK) == -1) { - if (errno == ENOENT) { - xbps_mkpath(dbkeyd, 0755); - } else { - rv = errno; - xbps_dbg_printf(repo->xhp, - "[repo] `%s' cannot create %s: %s\n", - repo->uri, dbkeyd, strerror(errno)); - free(p); - goto out; - } - } - free(p); - - repokeyd = xbps_dictionary_create(); - xbps_dictionary_set(repokeyd, "public-key", repo->pubkey); - xbps_dictionary_set_uint16(repokeyd, "public-key-size", repo->pubkey_size); - xbps_dictionary_set_cstring_nocopy(repokeyd, "signature-by", repo->signedby); - - if (!xbps_dictionary_externalize_to_zfile(repokeyd, rkeyfile)) { - rv = errno; - xbps_dbg_printf(repo->xhp, - "[repo] `%s' failed to externalize %s: %s\n", - repo->uri, rkeyfile, strerror(rv)); - } - -out: - if (repokeyd) - xbps_object_release(repokeyd); - if (rkeyfile) - free(rkeyfile); - return rv; -} - -static int -rsa_verify_buf(struct xbps_repo *repo, xbps_data_t sigdata, - xbps_data_t pubkey, const char *buf) -{ - SHA256_CTX context; - BIO *bio; - RSA *rsa; - unsigned char sha256[SHA256_DIGEST_LENGTH]; - int rv = 0; - - ERR_load_crypto_strings(); - SSL_load_error_strings(); - - bio = BIO_new_mem_buf(__UNCONST(xbps_data_data_nocopy(pubkey)), - xbps_data_size(pubkey)); - assert(bio); - - rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); - if (rsa == NULL) { - xbps_dbg_printf(repo->xhp, - "[repo] `%s' error reading public key: %s\n", - repo->uri, ERR_error_string(ERR_get_error(), NULL)); - return EINVAL; - } - - SHA256_Init(&context); - SHA256_Update(&context, buf, strlen(buf)); - SHA256_Final(sha256, &context); - - if (RSA_verify(NID_sha1, sha256, sizeof(sha256), - xbps_data_data_nocopy(sigdata), - xbps_data_size(sigdata), rsa) == 0) { - xbps_dbg_printf(repo->xhp, - "[repo] `%s' failed to verify signature: %s\n", - repo->uri, ERR_error_string(ERR_get_error(), NULL)); - rv = EPERM; - } - RSA_free(rsa); - BIO_free(bio); - ERR_free_strings(); - - return rv; -} - -int HIDDEN -xbps_repo_key_verify(struct xbps_repo *repo) -{ - xbps_dictionary_t repokeyd; - xbps_data_t xbps_pubkey; - char *idx_xml, *rkeyfile; - - if (!repo->signature || !repo->hexfp) - return EINVAL; - - rkeyfile = xbps_xasprintf("%s/keys/%s.plist", - repo->xhp->metadir, repo->hexfp); - repokeyd = xbps_dictionary_internalize_from_zfile(rkeyfile); - free(rkeyfile); - if (xbps_object_type(repokeyd) != XBPS_TYPE_DICTIONARY) - return EINVAL; - - xbps_pubkey = xbps_dictionary_get(repokeyd, "public-key"); - if (xbps_object_type(xbps_pubkey) != XBPS_TYPE_DATA) { - xbps_object_release(repokeyd); - return EINVAL; - } - - idx_xml = xbps_dictionary_externalize(repo->idx); - if (idx_xml == NULL) { - xbps_object_release(repokeyd); - return EINVAL; - } - - if (rsa_verify_buf(repo, repo->signature, xbps_pubkey, idx_xml) == 0) - repo->is_verified = true; - - free(idx_xml); - xbps_object_release(repokeyd); - - return repo->is_verified ? 0 : EPERM; -} diff --git a/lib/rpool.c b/lib/rpool.c index a3628238..face8029 100644 --- a/lib/rpool.c +++ b/lib/rpool.c @@ -42,97 +42,6 @@ static SIMPLEQ_HEAD(rpool_head, xbps_repo) rpool_queue = * @defgroup repopool Repository pool functions */ -int HIDDEN -xbps_rpool_init(struct xbps_handle *xhp) -{ - struct xbps_repo *repo; - const char *repouri; - bool foundrepo = false; - int retval, rv = 0; - - assert(xhp); - - if (xhp->rpool_initialized) - return 0; - - for (unsigned int i = 0; i < xbps_array_count(xhp->repositories); i++) { - xbps_array_get_cstring_nocopy(xhp->repositories, i, &repouri); - if ((repo = xbps_repo_open(xhp, repouri)) == NULL) { - repo = calloc(1, sizeof(struct xbps_repo)); - assert(repo); - repo->xhp = xhp; - repo->uri = repouri; - if (xbps_repository_is_remote(repouri)) - repo->is_remote = true; - } - if (repo->is_remote) { - if (!repo->is_signed) { - /* ignore unsigned repositories */ - xbps_repo_invalidate(repo); - } else { - /* - * Check the repository index signature against - * stored public key. - */ - retval = xbps_repo_key_verify(repo); - if (retval == 0) { - /* signed, verified */ - xbps_set_cb_state(xhp, XBPS_STATE_REPO_SIGVERIFIED, - 0, repouri, NULL); - } else if (retval == EPERM) { - /* signed, unverified */ - xbps_set_cb_state(xhp, XBPS_STATE_REPO_SIGUNVERIFIED, - 0, repouri, NULL); - xbps_repo_invalidate(repo); - } else { - /* any error */ - xbps_dbg_printf(xhp, "[rpool] %s: key_verify %s\n", - repouri, strerror(retval)); - xbps_repo_invalidate(repo); - } - } - } - /* - * If repository has passed signature checks, add it to the pool. - */ - SIMPLEQ_INSERT_TAIL(&rpool_queue, repo, entries); - foundrepo = true; - xbps_dbg_printf(xhp, "[rpool] `%s' registered (%s, %s).\n", - repouri, repo->is_signed ? "signed" : "unsigned", - repo->is_verified ? "verified" : "unverified"); - } - if (!foundrepo) { - /* no repositories available, error out */ - rv = ENOTSUP; - goto out; - } - xhp->rpool_initialized = true; - xbps_dbg_printf(xhp, "[rpool] initialized ok.\n"); -out: - if (rv != 0) - xbps_rpool_release(xhp); - - return rv; - -} - -void HIDDEN -xbps_rpool_release(struct xbps_handle *xhp) -{ - struct xbps_repo *repo; - - if (!xhp->rpool_initialized) - return; - - while ((repo = SIMPLEQ_FIRST(&rpool_queue))) { - SIMPLEQ_REMOVE(&rpool_queue, repo, xbps_repo, entries); - xbps_repo_close(repo); - free(repo); - } - xhp->rpool_initialized = false; - xbps_dbg_printf(xhp, "[rpool] released ok.\n"); -} - int xbps_rpool_sync(struct xbps_handle *xhp, const char *uri) { @@ -155,31 +64,45 @@ xbps_rpool_sync(struct xbps_handle *xhp, const char *uri) return 0; } +struct xbps_repo * +xbps_rpool_get_repo(const char *url) +{ + struct xbps_repo *repo; + + SIMPLEQ_FOREACH(repo, &rpool_queue, entries) + if (strcmp(url, repo->uri) == 0) + return repo; + + return NULL; +} + int xbps_rpool_foreach(struct xbps_handle *xhp, int (*fn)(struct xbps_repo *, void *, bool *), void *arg) { struct xbps_repo *repo; + const char *repouri; int rv = 0; - bool done = false; + bool foundrepo = false, done = false; assert(fn != NULL); - /* Initialize repository pool */ - if ((rv = xbps_rpool_init(xhp)) != 0) { - if (rv == ENOTSUP) { - xbps_dbg_printf(xhp, "[rpool] empty repository list.\n"); - } else if (rv != ENOENT && rv != ENOTSUP) { - xbps_dbg_printf(xhp, "[rpool] couldn't initialize: %s\n", strerror(rv)); + + for (unsigned int i = 0; i < xbps_array_count(xhp->repositories); i++) { + xbps_array_get_cstring_nocopy(xhp->repositories, i, &repouri); + repo = xbps_rpool_get_repo(repouri); + if (!repo) { + repo = xbps_repo_open(xhp, repouri); + SIMPLEQ_INSERT_TAIL(&rpool_queue, repo, entries); + xbps_dbg_printf(xhp, "[rpool] `%s' registered.\n", repouri); } - return rv; - } - /* Iterate over repository pool */ - SIMPLEQ_FOREACH(repo, &rpool_queue, entries) { + foundrepo = true; rv = (*fn)(repo, arg, &done); if (rv != 0 || done) break; } + if (!foundrepo) + rv = ENOTSUP; return rv; } diff --git a/lib/transaction_commit.c b/lib/transaction_commit.c index c9b2f385..9c023c08 100644 --- a/lib/transaction_commit.c +++ b/lib/transaction_commit.c @@ -56,11 +56,12 @@ */ static int -check_binpkgs_hash(struct xbps_handle *xhp, xbps_object_iterator_t iter) +check_binpkgs(struct xbps_handle *xhp, xbps_object_iterator_t iter) { xbps_object_t obj; - const char *pkgver, *arch, *repoloc, *sha256, *trans; - char *binfile, *filen; + struct xbps_repo *repo; + const char *pkgver, *repoloc, *trans, *sha256; + char *binfile; int rv = 0; while ((obj = xbps_object_iterator_next(iter)) != NULL) { @@ -69,33 +70,50 @@ check_binpkgs_hash(struct xbps_handle *xhp, xbps_object_iterator_t iter) (strcmp(trans, "configure") == 0)) continue; - xbps_dictionary_get_cstring_nocopy(obj, "architecture", &arch); xbps_dictionary_get_cstring_nocopy(obj, "repository", &repoloc); xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); - xbps_dictionary_get_cstring_nocopy(obj, - "filename-sha256", &sha256); binfile = xbps_repository_pkg_path(xhp, obj); if (binfile == NULL) { - rv = EINVAL; + rv = ENOMEM; break; } - filen = xbps_xasprintf("%s.%s.xbps", pkgver, arch); - - xbps_set_cb_state(xhp, XBPS_STATE_VERIFY, 0, pkgver, - "Verifying `%s' package integrity...", filen, repoloc); - rv = xbps_file_hash_check(binfile, sha256); - if (rv != 0) { - free(binfile); - xbps_set_cb_state(xhp, XBPS_STATE_VERIFY_FAIL, - rv, pkgver, - "Failed to verify `%s' package integrity: %s", - filen, strerror(rv)); - free(filen); + /* + * 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)) { + rv = EPERM; + xbps_set_cb_state(xhp, XBPS_STATE_VERIFY_FAIL, rv, pkgver, + "%s: the RSA signature is not valid!", pkgver); + 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!", pkgver, strerror(rv)); + free(binfile); + break; + } + + } free(binfile); - free(filen); } xbps_object_iterator_reset(iter); @@ -107,9 +125,8 @@ download_binpkgs(struct xbps_handle *xhp, xbps_object_iterator_t iter) { xbps_object_t obj; const char *pkgver, *arch, *fetchstr, *repoloc, *trans; - char *binfile, *filen; + char *binfile, *sigfile; int rv = 0; - bool state_dload = false; while ((obj = xbps_object_iterator_next(iter)) != NULL) { xbps_dictionary_get_cstring_nocopy(obj, "transaction", &trans); @@ -127,66 +144,47 @@ download_binpkgs(struct xbps_handle *xhp, xbps_object_iterator_t iter) break; } /* - * If downloaded package is in cachedir continue. + * If binary package is in cachedir or in a local repository, continue. */ if (access(binfile, R_OK) == 0) { free(binfile); continue; } - /* - * Create cachedir. - */ - if (access(xhp->cachedir, R_OK|X_OK|W_OK) == -1) { - if (xbps_mkpath(xhp->cachedir, 0755) == -1) { - xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD_FAIL, - errno, pkgver, - "%s: [trans] cannot create cachedir `%s':" - "%s", pkgver, xhp->cachedir, - strerror(errno)); - free(binfile); - rv = errno; - break; - } - } - if (state_dload == false) { - xbps_set_cb_state(xhp, XBPS_STATE_TRANS_DOWNLOAD, - 0, NULL, NULL); - state_dload = true; - } - filen = xbps_xasprintf("%s.%s.xbps", pkgver, arch); - xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD, - 0, pkgver, "Downloading binary package `%s' (from `%s')...", - filen, repoloc); + xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD, 0, pkgver, + "Downloading `%s' package (from `%s')...", pkgver, repoloc); /* * Fetch binary package. */ - if (chdir(xhp->cachedir) == -1) { - xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD_FAIL, - errno, pkgver, - "%s: [trans] failed to change dir to cachedir" - "`%s': %s", pkgver, xhp->cachedir, - strerror(errno)); - rv = errno; - free(binfile); - free(filen); - break; - } - rv = xbps_fetch_file(xhp, binfile, NULL); if (rv == -1) { fetchstr = xbps_fetch_error_string(); xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD_FAIL, fetchLastErrCode != 0 ? fetchLastErrCode : errno, - pkgver, "%s: [trans] failed to download binary package " - "`%s' from `%s': %s", pkgver, filen, repoloc, - fetchstr ? fetchstr : strerror(errno)); + pkgver, "[trans] failed to download `%s' package from `%s': %s", + pkgver, repoloc, fetchstr ? fetchstr : strerror(errno)); free(binfile); - free(filen); + break; + } + /* + * Fetch package signature. + */ + sigfile = xbps_xasprintf("%s.sig", binfile); + free(binfile); + + xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD, 0, pkgver, + "Downloading `%s' signature (from `%s')...", pkgver, repoloc); + rv = xbps_fetch_file(xhp, sigfile, NULL); + if (rv == -1) { + fetchstr = xbps_fetch_error_string(); + xbps_set_cb_state(xhp, XBPS_STATE_DOWNLOAD_FAIL, + fetchLastErrCode != 0 ? fetchLastErrCode : errno, + pkgver, "[trans] failed to download `%s' signature from `%s': %s", + pkgver, repoloc, fetchstr ? fetchstr : strerror(errno)); + free(sigfile); break; } rv = 0; - free(binfile); - free(filen); + free(sigfile); } xbps_object_iterator_reset(iter); @@ -199,25 +197,44 @@ xbps_transaction_commit(struct xbps_handle *xhp) xbps_object_t obj; xbps_object_iterator_t iter; const char *pkgver, *tract; + char *pkgname; int rv = 0; - bool update, install, sr; + bool update; assert(xbps_object_type(xhp->transd) == XBPS_TYPE_DICTIONARY); - - update = install = false; + /* + * Create cachedir if necessary. + */ + if (access(xhp->cachedir, R_OK|X_OK|W_OK) == -1) { + if (xbps_mkpath(xhp->cachedir, 0755) == -1) { + xbps_set_cb_state(xhp, XBPS_STATE_TRANS_FAIL, + errno, NULL, + "[trans] cannot create cachedir `%s': %s", + xhp->cachedir, strerror(errno)); + return errno; + } + } + if (chdir(xhp->cachedir) == -1) { + xbps_set_cb_state(xhp, XBPS_STATE_TRANS_FAIL, + errno, NULL, + "[trans] failed to change dir to cachedir `%s': %s", + xhp->cachedir, strerror(errno)); + return errno; + } iter = xbps_array_iter_from_dict(xhp->transd, "packages"); if (iter == NULL) return EINVAL; /* * Download binary packages (if they come from a remote repository). */ + xbps_set_cb_state(xhp, XBPS_STATE_TRANS_DOWNLOAD, 0, NULL, NULL); if ((rv = download_binpkgs(xhp, iter)) != 0) goto out; /* - * Check SHA256 hashes for binary packages in transaction. + * Check binary package integrity. */ xbps_set_cb_state(xhp, XBPS_STATE_TRANS_VERIFY, 0, NULL, NULL); - if ((rv = check_binpkgs_hash(xhp, iter)) != 0) + if ((rv = check_binpkgs(xhp, iter)) != 0) goto out; /* * Install, update, configure or remove packages as specified @@ -226,25 +243,22 @@ xbps_transaction_commit(struct xbps_handle *xhp) xbps_set_cb_state(xhp, XBPS_STATE_TRANS_RUN, 0, NULL, NULL); while ((obj = xbps_object_iterator_next(iter)) != NULL) { - update = false; xbps_dictionary_get_cstring_nocopy(obj, "transaction", &tract); xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); if (strcmp(tract, "remove") == 0) { - update = false; - sr = false; /* * Remove package. */ - xbps_dictionary_get_bool(obj, "remove-and-update", - &update); - xbps_dictionary_get_bool(obj, "softreplace", &sr); - rv = xbps_remove_pkg(xhp, pkgver, update, sr); + xbps_dictionary_get_bool(obj, "remove-and-update", &update); + rv = xbps_remove_pkg(xhp, pkgver, update, false); if (rv != 0) { xbps_dbg_printf(xhp, "[trans] failed to " "remove %s\n", pkgver); goto out; } + continue; + } else if (strcmp(tract, "configure") == 0) { /* * Reconfigure pending package. @@ -252,20 +266,17 @@ xbps_transaction_commit(struct xbps_handle *xhp) rv = xbps_configure_pkg(xhp, pkgver, false, false, false); if (rv != 0) goto out; - } else { - /* - * Install or update a package. - */ - if (strcmp(tract, "update") == 0) - update = true; - else - install = true; - if (update && xbps_pkgdb_get_pkg(xhp, pkgver)) { - /* - * Update a package: execute pre-remove - * action if found before unpacking. - */ + continue; + + } else if (strcmp(tract, "update") == 0) { + /* + * Update a package: execute pre-remove action of + * existing package before unpacking new version. + */ + pkgname = xbps_pkg_name(pkgver); + assert(pkgname); + if (xbps_pkgdb_get_pkg(xhp, pkgname)) { xbps_set_cb_state(xhp, XBPS_STATE_UPDATE, 0, pkgver, NULL); rv = xbps_remove_pkg(xhp, pkgver, true, false); @@ -276,27 +287,30 @@ xbps_transaction_commit(struct xbps_handle *xhp) "%s: [trans] failed to update " "package `%s'", pkgver, strerror(rv)); + free(pkgname); goto out; } - } else { - /* Install a package */ - xbps_set_cb_state(xhp, XBPS_STATE_INSTALL, - 0, pkgver, NULL); } - /* - * Unpack binary package. - */ - if ((rv = xbps_unpack_binary_pkg(xhp, obj)) != 0) - goto out; - /* - * Register package. - */ - if ((rv = xbps_register_pkg(xhp, obj)) != 0) - goto out; + free(pkgname); + } else { + /* Install a package */ + xbps_set_cb_state(xhp, XBPS_STATE_INSTALL, 0, + pkgver, NULL); } + /* + * Unpack binary package. + */ + if ((rv = xbps_unpack_binary_pkg(xhp, obj)) != 0) + goto out; + /* + * Register package. + */ + if ((rv = xbps_register_pkg(xhp, obj)) != 0) + goto out; } /* if there are no packages to install or update we are done */ - if (!update && !install) + if (!xbps_dictionary_get(xhp->transd, "total-update-pkgs") && + !xbps_dictionary_get(xhp->transd, "total-install-pkgs")) goto out; /* if installing packages for target_arch, don't configure anything */ diff --git a/lib/verifysig.c b/lib/verifysig.c new file mode 100644 index 00000000..2ac262e7 --- /dev/null +++ b/lib/verifysig.c @@ -0,0 +1,159 @@ +/*- + * Copyright (c) 2013 Juan Romero Pardines. + * 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 +#include + +#include +#include +#include +#include +#include + +#include "xbps_api_impl.h" + +static bool +rsa_verify_buf(struct xbps_repo *repo, xbps_data_t pubkey, + unsigned char *sig, unsigned int siglen, + unsigned char *buf, unsigned int buflen) +{ + SHA256_CTX context; + BIO *bio; + RSA *rsa; + unsigned char sha256[SHA256_DIGEST_LENGTH]; + int rv; + + ERR_load_crypto_strings(); + SSL_load_error_strings(); + + bio = BIO_new_mem_buf(__UNCONST(xbps_data_data_nocopy(pubkey)), + xbps_data_size(pubkey)); + assert(bio); + + rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); + if (rsa == NULL) { + xbps_dbg_printf(repo->xhp, "`%s' error reading public key: %s\n", + repo->uri, ERR_error_string(ERR_get_error(), NULL)); + return false; + } + + SHA256_Init(&context); + SHA256_Update(&context, buf, buflen); + SHA256_Final(sha256, &context); + + rv = RSA_verify(NID_sha1, sha256, sizeof(sha256), sig, siglen, rsa); + RSA_free(rsa); + BIO_free(bio); + ERR_free_strings(); + + return rv ? true : false; +} + +bool +xbps_verify_file_signature(struct xbps_repo *repo, const char *fname) +{ + xbps_dictionary_t repokeyd = NULL; + xbps_data_t pubkey; + struct stat st, sig_st; + unsigned char *buf = NULL, *sig_buf = NULL; + char *rkeyfile = NULL, *sig = NULL; + int fd = -1, sig_fd = -1; + bool val = false; + + if (!repo->hexfp) + return false; + /* + * Prepare repository RSA public key to verify fname signature. + */ + rkeyfile = xbps_xasprintf("%s/keys/%s.plist", + repo->xhp->metadir, repo->hexfp); + repokeyd = xbps_dictionary_internalize_from_zfile(rkeyfile); + if (xbps_object_type(repokeyd) != XBPS_TYPE_DICTIONARY) { + xbps_dbg_printf(repo->xhp, "cannot read rkey data at %s: %s\n", + rkeyfile, strerror(errno)); + goto out; + } + + pubkey = xbps_dictionary_get(repokeyd, "public-key"); + if (xbps_object_type(pubkey) != XBPS_TYPE_DATA) + goto out; + + /* + * Prepare fname and signature data buffers. + */ + if ((fd = open(fname, O_RDONLY)) == -1) { + xbps_dbg_printf(repo->xhp, "can't open file %s: %s\n", fname, strerror(errno)); + goto out; + } + sig = xbps_xasprintf("%s.sig", fname); + if ((sig_fd = open(sig, O_RDONLY)) == -1) { + xbps_dbg_printf(repo->xhp, "can't open signature file %s: %s\n", sig, strerror(errno)); + goto out; + } + fstat(fd, &st); + fstat(sig_fd, &sig_st); + + buf = malloc(st.st_size); + assert(buf); + sig_buf = malloc(sig_st.st_size); + assert(sig_buf); + + if (read(fd, buf, st.st_size) != st.st_size) { + xbps_dbg_printf(repo->xhp, "failed to read file %s: %s\n", fname, strerror(errno)); + goto out; + } + if (read(sig_fd, sig_buf, sig_st.st_size) != sig_st.st_size) { + xbps_dbg_printf(repo->xhp, "failed to read signature file %s: %s\n", sig, strerror(errno)); + goto out; + } + /* + * Verify fname RSA signature. + */ + if (rsa_verify_buf(repo, pubkey, sig_buf, sig_st.st_size, buf, st.st_size)) + val = true; + +out: + if (rkeyfile) + free(rkeyfile); + if (fd != -1) + close(fd); + if (sig_fd != -1) + close(sig_fd); + if (buf) + free(buf); + if (sig) + free(sig); + if (sig_buf) + free(sig_buf); + if (repokeyd) + xbps_object_release(repokeyd); + + return val; +}