From a5ecaa493f12e9c05837a94cddc3688fab1e83b7 Mon Sep 17 00:00:00 2001 From: Juan RP Date: Wed, 9 Oct 2013 10:13:07 +0200 Subject: [PATCH] New utility: xbps-rkeys(8) to manage RSA public keys. --- .gitignore | 1 + NEWS | 9 +- bin/Makefile | 1 + bin/xbps-install/state_cb.c | 7 -- bin/xbps-rkeys/Makefile | 7 ++ bin/xbps-rkeys/defs.h | 44 +++++++ bin/xbps-rkeys/main.c | 239 ++++++++++++++++++++++++++++++++++++ bin/xbps-rkeys/xbps-rkeys.8 | 61 +++++++++ include/xbps.h.in | 13 +- include/xbps_api_impl.h | 7 +- lib/initend.c | 9 +- lib/repo.c | 18 +++ lib/repo_keys.c | 4 +- lib/rpool.c | 23 +--- 14 files changed, 406 insertions(+), 37 deletions(-) create mode 100644 bin/xbps-rkeys/Makefile create mode 100644 bin/xbps-rkeys/defs.h create mode 100644 bin/xbps-rkeys/main.c create mode 100644 bin/xbps-rkeys/xbps-rkeys.8 diff --git a/.gitignore b/.gitignore index 4251008f..f11f0109 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ bin/xbps-reconfigure/xbps-reconfigure bin/xbps-remove/xbps-remove bin/xbps-rindex/xbps-rindex bin/xbps-uhelper/xbps-uhelper +bin/xbps-rkeys/xbps-rkeys *.static *.so* *.o diff --git a/NEWS b/NEWS index c616d580..63214d55 100644 --- a/NEWS +++ b/NEWS @@ -3,14 +3,17 @@ xbps-0.27 (???): * xbps-rindex(8): -c --clean mode has been removed. Generating a local repository is almost as fast as cleaning up the repository data. + * xbps-rkeys(8): new utility to manage RSA public keys from remote signed repositories. + * Support for RSA signed repositories. A repository can be signed with your preferred RSA key (any ssh key works) as follows: $ xbps-rindex -s --signedby "foobar " --privkey /priv/key /path/to/repo - The first time xbps-install(8) access to a signed repository it will ask you - to import its public key to verify the signature. Please double-check the - hex fingerprint of the public key is the real one! + Public keys must be imported before using a remote signed repository thru + the xbps-rkeys(8) utility: + + $ xbps-rkeys -i Once the public key has been imported it's not expected to change, hence if the repository index has been modified or signed with another key, it will be ignored. diff --git a/bin/Makefile b/bin/Makefile index e3a1ea42..4e65cb1b 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -9,5 +9,6 @@ SUBDIRS += xbps-reconfigure SUBDIRS += xbps-remove SUBDIRS += xbps-rindex SUBDIRS += xbps-uhelper +SUBDIRS += xbps-rkeys include ../mk/subdir.mk diff --git a/bin/xbps-install/state_cb.c b/bin/xbps-install/state_cb.c index f7bcfdfd..58d8d8ff 100644 --- a/bin/xbps-install/state_cb.c +++ b/bin/xbps-install/state_cb.c @@ -47,13 +47,6 @@ state_cb(struct xbps_state_cb_data *xscd, void *cbdata _unused) switch (xscd->state) { /* notifications */ - case XBPS_STATE_REPO_KEY_IMPORT: - printf("%s\n", xscd->desc); - printf("Fingerprint: "); - xbps_print_hexfp(xscd->arg); - printf("\n"); - rv = noyes("Do you want to import this public key?"); - break; case XBPS_STATE_REPO_SIGVERIFIED: printf("[*] RSA signature verified correctly\n"); break; diff --git a/bin/xbps-rkeys/Makefile b/bin/xbps-rkeys/Makefile new file mode 100644 index 00000000..967549b2 --- /dev/null +++ b/bin/xbps-rkeys/Makefile @@ -0,0 +1,7 @@ +TOPDIR = ../.. +-include $(TOPDIR)/config.mk + +BIN = xbps-rkeys +OBJS = main.o ../xbps-install/question.o ../xbps-install/fetch_cb.o + +include $(TOPDIR)/mk/prog.mk diff --git a/bin/xbps-rkeys/defs.h b/bin/xbps-rkeys/defs.h new file mode 100644 index 00000000..95bcf8bd --- /dev/null +++ b/bin/xbps-rkeys/defs.h @@ -0,0 +1,44 @@ +/*- + * 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. + */ + +#ifndef _XBPS_RKEYS_DEFS_H_ +#define _XBPS_RKEYS_DEFS_H_ + +#include +#include + +struct xferstat { + struct timeval start; + struct timeval last; +}; + +/* from xbps-install/fetch_cb.c */ +void fetch_file_progress_cb(struct xbps_fetch_cb_data *, void *); + +/* from xbps-install/question.c */ +bool yesno(const char *, ...); +bool noyes(const char *, ...); + +#endif /* !_XBPS_RKEYS_DEFS_H_ */ diff --git a/bin/xbps-rkeys/main.c b/bin/xbps-rkeys/main.c new file mode 100644 index 00000000..ad006d91 --- /dev/null +++ b/bin/xbps-rkeys/main.c @@ -0,0 +1,239 @@ +/*- + * 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 "defs.h" + +static void __attribute__((noreturn)) +usage(bool fail) +{ + fprintf(stdout, + "Usage: xbps-rkeys [OPTIONS] [REPOURL...]\n\n" + "OPTIONS\n" + " -a --all Process all repositories in configuration file\n" + " -C --config Full path to configuration file\n" + " -d --debug Debug mode shown to stderr\n" + " -h --help Print usage help\n" + " -r --rootdir Full path to rootdir\n" + " -V --version Show XBPS version\n\n" + "MODE\n" + " -i --import Import public RSA key(s)\n" + " -R --remove Remove public RSA key(s)\n" + " -s --show Show repository info\n"); + exit(fail ? EXIT_FAILURE : EXIT_SUCCESS); +} + +static int +state_cb(struct xbps_state_cb_data *xscd, void *cbd _unused) +{ + int rv = 0; + + switch (xscd->state) { + /* notifications */ + case XBPS_STATE_REPO_KEY_IMPORT: + printf("%s\n", xscd->desc); + printf("Fingerprint: "); + xbps_print_hexfp(xscd->arg); + printf("\n"); + rv = noyes("Do you want to import this public key?"); + break; + case XBPS_STATE_REPOSYNC: + printf("[*] Downloading repository index `%s'...\n", xscd->arg); + break; + default: + xbps_dbg_printf(xscd->xhp, + "%s: unknown state %d\n", xscd->arg, xscd->state); + break; + } + + return rv; +} + +static int +repo_import_key_cb(struct xbps_repo *repo, void *arg _unused, bool *done _unused) +{ + return xbps_repo_key_import(repo); +} + +static int +repo_info_cb(struct xbps_repo *repo, void *arg _unused, bool *done _unused) +{ + xbps_dictionary_t rkeyd = NULL; + xbps_data_t rpubkey; + unsigned char *fp; + const char *signee; + uint16_t rpubkeysiz; + + if (!repo->is_remote) + return 0; + + printf("%s (%s, %s)\n", repo->uri, + repo->is_signed ? "RSA signed" : "unsigned", + repo->is_verified ? "verified" : "unverified"); + + rkeyd = xbps_dictionary_get(repo->xhp->repokeys, repo->uri); + if (xbps_object_type(rkeyd) == XBPS_TYPE_DICTIONARY) { + rpubkey = xbps_dictionary_get(rkeyd, "public-key"); + assert(rpubkey); + xbps_dictionary_get_uint16(rkeyd, "public-key-size", &rpubkeysiz); + xbps_dictionary_get_cstring_nocopy(rkeyd, "signature-by", &signee); + printf(" Signed by: %s\n", signee); + printf(" %u ", rpubkeysiz); + fp = xbps_pubkey2fp(repo->xhp, rpubkey); + assert(fp); + xbps_print_hexfp((const char *)fp); + free(fp); + printf("\n"); + } + return 0; +} + +static int +repo_remove_key_cb(struct xbps_repo *repo, void *arg, bool *done _unused) +{ + bool *flush = arg; + + if (xbps_object_type(repo->xhp->repokeys) != XBPS_TYPE_DICTIONARY) + return 0; + + xbps_dictionary_remove(repo->xhp->repokeys, repo->uri); + printf("Removed `%s' from storage.\n", repo->uri); + *flush = true; + return 0; +} + +int +main(int argc, char **argv) +{ + const char *shortopts = "aC:dhir:Rsv"; + const struct option longopts[] = { + { "all", no_argument, NULL, 'a' }, + { "config", required_argument, NULL, 'C' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "import", no_argument, NULL, 'i' }, + { "remove", no_argument, NULL, 'R' }, + { "show", no_argument, NULL, 's' }, + { "rootdir", required_argument, NULL, 'r' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + struct xbps_handle xh; + struct xferstat xfer; + char *rkeys; + const char *conffile = NULL, *rootdir = NULL; + int c, rv, flags = 0; + bool all, flush, import, remove, show; + + all = import = remove = show = flush = false; + + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { + switch (c) { + case 'a': + all = true; + break; + case 'C': + conffile = optarg; + break; + case 'd': + flags |= XBPS_FLAG_DEBUG; + break; + case 'h': + usage(false); + /* NOTREACHED */ + case 'i': + import = true; + break; + case 'R': + remove = true; + break; + case 'r': + rootdir = optarg; + break; + case 's': + show = true; + break; + case 'V': + printf("%s\n", XBPS_RELVER); + exit(EXIT_SUCCESS); + case '?': + default: + usage(true); + /* NOTREACHED */ + } + } + if (!all && (argc == optind)) + usage(true); + + memset(&xh, 0, sizeof(xh)); + xh.fetch_cb = fetch_file_progress_cb; + xh.fetch_cb_data = &xfer; + xh.state_cb = state_cb; + xh.rootdir = rootdir; + xh.conffile = conffile; + xh.flags = flags; + + /* register specified repos */ + if (!all) { + for (int i = optind; i < argc; i++) { + if (xh.repositories == NULL) + xh.repositories = xbps_array_create(); + + xbps_array_add_cstring_nocopy(xh.repositories, argv[i]); + } + } + /* initialize libxbps */ + if ((rv = xbps_init(&xh)) != 0) { + xbps_error_printf("Failed to initialize libxbps: %s\n", + strerror(rv)); + exit(EXIT_FAILURE); + } + + if (import) { + /* Sync remote repodata first */ + xbps_rpool_sync(&xh, NULL); + rv = xbps_rpool_foreach(&xh, repo_import_key_cb, NULL); + } else if (remove) { + rv = xbps_rpool_foreach(&xh, repo_remove_key_cb, &flush); + if (flush) { + rkeys = xbps_xasprintf("%s/%s", xh.metadir, XBPS_REPOKEYS); + xbps_dictionary_externalize_to_file(xh.repokeys, rkeys); + free(rkeys); + } + } else if (show) { + rv = xbps_rpool_foreach(&xh, repo_info_cb, NULL); + } + xbps_end(&xh); + + exit(rv ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/bin/xbps-rkeys/xbps-rkeys.8 b/bin/xbps-rkeys/xbps-rkeys.8 new file mode 100644 index 00000000..8d68fd83 --- /dev/null +++ b/bin/xbps-rkeys/xbps-rkeys.8 @@ -0,0 +1,61 @@ +.Dd October 9, 2013 +.Os Void Linux +.Dt xbps-rkeys 8 +.Sh NAME +.Nm xbps-rkeys +.Nd XBPS utility to manage RSA public keys in remote repositories +.Sh SYNOPSYS +.Nm xbps-rkeys +.Op OPTIONS +.Op MODE +.Op REPOURL... +.Sh DESCRIPTION +The +.Nm +utility manages RSA public keys from remote repositories. A public key from a remote +repository can be imported and removed, last but not least there is an option to +show the repository information with details. +.Sh OPTIONS +.Bl -tag -width -x +.It Fl a, Fl -all +Processes all repositories specified in a configuration file. +.It Fl C, Fl -config Ar file +Specifies a full path to the XBPS configuration file. +.It Fl d, Fl -debug +Enables extra debugging shown to stderr. +.It Fl h, Fl -help +Show the help usage. +.It Fl r, Fl -rootdir Ar dir +Specifies a full path for the target root directory. +.It Fl V, Fl -version +Shows the XBPS version. +.Sh MODE +.Bl -tag -width -x +.It Fl i, Fl -import +Imports the RSA public key of target repository into the database. +Please double-check the fingerprint is the real one. +.It Fl R, Fl -remove +Removes the RSA public key (and its properties) of target repository from +the database. +.It Fl s, Fl -show +Shows information of the target signed repositories. +.Sh FILES +.Bl -tag -width /var/db/xbps/repokeys.plist +.It Ar /var/db/xbps/repokeys.plist +Default plist file to store repository public keys and its properties. +.Sh SEE ALSO +.Xr xbps-create 8 , +.Xr xbps-dgraph 8 , +.Xr xbps-install 8 , +.Xr xbps-pkgdb 8 , +.Xr xbps-query 8 , +.Xr xbps-reconfigure 8 , +.Xr xbps-remove 8 , +.Xr xbps-rindex 8 +.Sh AUTHORS +.An Juan Romero Pardines +.Sh BUGS +Probably, but I try to make this not happen. Use it under your own +responsability and enjoy your life. +.Pp +Report bugs in https://github.com/xtraeme/xbps/issues diff --git a/include/xbps.h.in b/include/xbps.h.in index eaa303c7..e313c533 100644 --- a/include/xbps.h.in +++ b/include/xbps.h.in @@ -46,7 +46,7 @@ * * This header documents the full API for the XBPS Library. */ -#define XBPS_API_VERSION "20131007" +#define XBPS_API_VERSION "20131009" #ifndef XBPS_VERSION #define XBPS_VERSION "UNSET" @@ -1281,6 +1281,7 @@ struct xbps_repo *xbps_repo_open(struct xbps_handle *xhp, const char *url); */ void xbps_repo_close(struct xbps_repo *repo); + /** * Prepares the repository index-files.plist to have access to it. * The repository must be opened previously with \a xbps_repo_open(). @@ -1350,6 +1351,16 @@ xbps_dictionary_t xbps_repo_get_pkg_plist(struct xbps_handle *xhp, */ xbps_array_t xbps_repo_get_pkg_revdeps(struct xbps_repo *repo, const char *pkg); +/** + * Imports the RSA public key of target repository. The repository must be + * signed properly for this to work. + * + * @param[in] repo Pointer to the target xbps_repo structure. + * + * @return 0 on success, an errno value otherwise. + */ +int xbps_repo_key_import(struct xbps_repo *repo); + /*@}*/ /** @addtogroup archive_util */ diff --git a/include/xbps_api_impl.h b/include/xbps_api_impl.h index 5ce99665..fcb2dd4a 100644 --- a/include/xbps_api_impl.h +++ b/include/xbps_api_impl.h @@ -156,11 +156,16 @@ int HIDDEN xbps_entry_install_conf_file(struct xbps_handle *, const char *, const char *); +/** + * @private + * From lib/repo.c + */ +void HIDDEN xbps_repo_invalidate(struct xbps_repo *); + /** * @private * From lib/repo_keys.c */ -int HIDDEN xbps_repo_key_import(struct xbps_repo *); int HIDDEN xbps_repo_key_verify(struct xbps_repo *); /** diff --git a/lib/initend.c b/lib/initend.c index a5c29638..f291bd13 100644 --- a/lib/initend.c +++ b/lib/initend.c @@ -253,10 +253,13 @@ xbps_end(struct xbps_handle *xhp) xbps_pkgdb_release(xhp); xbps_rpool_release(xhp); - xbps_fetch_unset_cache_connection(); - if (xhp->pkgdb_revdeps != NULL) - xbps_object_release(xhp->pkgdb_revdeps); + if (xbps_object_type(xhp->pkgdb_revdeps) != XBPS_TYPE_UNKNOWN) + xbps_object_release(xhp->pkgdb_revdeps); + if (xbps_object_type(xhp->repokeys) != XBPS_TYPE_UNKNOWN) + xbps_object_release(xhp->repokeys); + + xbps_fetch_unset_cache_connection(); cfg_free(xhp->cfg); free(xhp->cachedir_priv); free(xhp->metadir_priv); diff --git a/lib/repo.c b/lib/repo.c index 9abbc9af..e3c29792 100644 --- a/lib/repo.c +++ b/lib/repo.c @@ -172,6 +172,24 @@ xbps_repo_open_idxfiles(struct xbps_repo *repo) repo->idxfiles = repo_get_dict(repo, XBPS_REPOIDX_FILES); } +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) { diff --git a/lib/repo_keys.c b/lib/repo_keys.c index 71b2b7fa..85a5336c 100644 --- a/lib/repo_keys.c +++ b/lib/repo_keys.c @@ -36,7 +36,7 @@ #include "xbps_api_impl.h" -int HIDDEN +int xbps_repo_key_import(struct xbps_repo *repo) { xbps_dictionary_t repokeyd, newmetad = NULL; @@ -58,8 +58,8 @@ xbps_repo_key_import(struct xbps_repo *repo) /* * Check if the public key has been stored for this repository. */ + rkeypath = xbps_xasprintf("%s/%s", repo->xhp->metadir, XBPS_REPOKEYS); if (repo->xhp->repokeys == NULL) { - rkeypath = xbps_xasprintf("%s/%s", repo->xhp->metadir, XBPS_REPOKEYS); repo->xhp->repokeys = xbps_dictionary_internalize_from_file(rkeypath); if (xbps_object_type(repo->xhp->repokeys) != XBPS_TYPE_DICTIONARY) repo->xhp->repokeys = xbps_dictionary_create(); diff --git a/lib/rpool.c b/lib/rpool.c index 86355855..85fa5ec3 100644 --- a/lib/rpool.c +++ b/lib/rpool.c @@ -78,23 +78,9 @@ xbps_rpool_init(struct xbps_handle *xhp) rp->repo->is_remote = true; } if (rp->repo->is_remote) { - /* - * Import the RSA public key (if it's signed). - */ - retval = xbps_repo_key_import(rp->repo); - if (retval == EAGAIN) { - /* signed but public key was not imported */ - xbps_dbg_printf(xhp, "[rpool] `%s': public-key not yet imported.\n", repouri); - rp->repo->is_signed = true; - rp->repo->is_verified = false; - } else if (retval != 0 && retval != EAGAIN) { - /* any error */ - xbps_dbg_printf(xhp, "[rpool] %s: key_import %s\n", - repouri, strerror(retval)); - } if (!rp->repo->is_signed) { /* ignore unsigned repositories */ - xbps_repo_close(rp->repo); + xbps_repo_invalidate(rp->repo); } else { /* * Check the repository index signature against @@ -107,13 +93,12 @@ xbps_rpool_init(struct xbps_handle *xhp) } else if (retval == EPERM) { /* signed, unverified */ xbps_set_cb_state(xhp, XBPS_STATE_REPO_SIGUNVERIFIED, 0, NULL, NULL); - xbps_repo_close(rp->repo); - rp->repo->is_verified = false; + xbps_repo_invalidate(rp->repo); } else { /* any error */ xbps_dbg_printf(xhp, "[rpool] %s: key_verify %s\n", repouri, strerror(retval)); - xbps_repo_close(rp->repo); + xbps_repo_invalidate(rp->repo); } } } @@ -155,8 +140,6 @@ xbps_rpool_release(struct xbps_handle *xhp) free(rp->repo); free(rp); } - xbps_object_release(xhp->repokeys); - xhp->repokeys = NULL; xhp->rpool_initialized = false; xbps_dbg_printf(xhp, "[rpool] released ok.\n"); }