From: Jeremy Huntwork <jeremy@merelinux.org> This is a proof of concept that shows how asignify can be used instead of gpgme to validate packages signed with the asignify tool. Signed-off-by: Jeremy Huntwork <jeremy@merelinux.org> --- lib/libalpm/alpm.c | 2 +- lib/libalpm/be_package.c | 10 ++++++ lib/libalpm/be_sync.c | 2 +- lib/libalpm/handle.c | 6 ++-- lib/libalpm/signing.c | 67 ++++++++++++++++++++++++++++++++++++++++ lib/libalpm/signing.h | 11 ++++--- meson.build | 11 ++++++- meson_options.txt | 3 ++ 8 files changed, 101 insertions(+), 11 deletions(-) diff --git a/lib/libalpm/alpm.c b/lib/libalpm/alpm.c index 5b326ab0..d1261660 100644 --- a/lib/libalpm/alpm.c +++ b/lib/libalpm/alpm.c @@ -137,7 +137,7 @@ int SYMEXPORT alpm_capabilities(void) #ifdef HAVE_LIBCURL | ALPM_CAPABILITY_DOWNLOADER #endif -#ifdef HAVE_LIBGPGME +#if defined(HAVE_LIBGPGME) || defined(HAVE_LIBASIGNIFY) | ALPM_CAPABILITY_SIGNATURES #endif | 0; diff --git a/lib/libalpm/be_package.c b/lib/libalpm/be_package.c index 5ca2865c..1b6887ce 100644 --- a/lib/libalpm/be_package.c +++ b/lib/libalpm/be_package.c @@ -342,12 +342,20 @@ int _alpm_pkg_validate_internal(alpm_handle_t *handle, handle->pm_errno = ALPM_ERR_PKG_MISSING_SIG; return -1; } +#ifdef HAVE_LIBGPGME if(_alpm_check_pgp_helper(handle, pkgfile, sig, level & ALPM_SIG_PACKAGE_OPTIONAL, level & ALPM_SIG_PACKAGE_MARGINAL_OK, level & ALPM_SIG_PACKAGE_UNKNOWN_OK, sigdata)) { handle->pm_errno = ALPM_ERR_PKG_INVALID_SIG; return -1; } +#endif +#ifdef HAVE_LIBASIGNIFY + if(_alpm_check_asignify_helper(handle, pkgfile, sigdata)) { + handle->pm_errno = ALPM_ERR_PKG_INVALID_SIG; + return -1; + } +#endif if(validation && has_sig) { *validation |= ALPM_PKG_VALIDATION_SIGNATURE; } @@ -743,6 +751,7 @@ int SYMEXPORT alpm_pkg_load(alpm_handle_t *handle, const char *filename, int ful return -1; } +#ifndef HAVE_LIBASIGNIFY if(alpm_extract_keyid(handle, filename, sig, len, &keys) == 0) { alpm_list_t *k; for(k = keys; k; k = k->next) { @@ -771,6 +780,7 @@ int SYMEXPORT alpm_pkg_load(alpm_handle_t *handle, const char *filename, int ful free(sigpath); return -1; } +#endif } } free(sigpath); diff --git a/lib/libalpm/be_sync.c b/lib/libalpm/be_sync.c index ede25985..81654902 100644 --- a/lib/libalpm/be_sync.c +++ b/lib/libalpm/be_sync.c @@ -697,7 +697,7 @@ alpm_db_t *_alpm_db_register_sync(alpm_handle_t *handle, const char *treename, _alpm_log(handle, ALPM_LOG_DEBUG, "registering sync database '%s'\n", treename); -#ifndef HAVE_LIBGPGME +#if !defined(HAVE_LIBGPGME) || !defined(HAVE_LIBASIGNIFY) if(level != 0 && level != ALPM_SIG_USE_DEFAULT) { RET_ERR(handle, ALPM_ERR_MISSING_CAPABILITY_SIGNATURES, NULL); } diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c index 101d4a78..78c7d7bd 100644 --- a/lib/libalpm/handle.c +++ b/lib/libalpm/handle.c @@ -829,7 +829,7 @@ int SYMEXPORT alpm_option_set_default_siglevel(alpm_handle_t *handle, if(level == ALPM_SIG_USE_DEFAULT) { RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1); } -#ifdef HAVE_LIBGPGME +#if defined(HAVE_LIBGPGME) || defined(HAVE_LIBASIGNIFY) handle->siglevel = level; #else if(level != 0) { @@ -849,7 +849,7 @@ int SYMEXPORT alpm_option_set_local_file_siglevel(alpm_handle_t *handle, int level) { CHECK_HANDLE(handle, return -1); -#ifdef HAVE_LIBGPGME +#if defined(HAVE_LIBGPGME) || defined(HAVE_LIBASIGNIFY) handle->localfilesiglevel = level; #else if(level != 0 && level != ALPM_SIG_USE_DEFAULT) { @@ -873,7 +873,7 @@ int SYMEXPORT alpm_option_set_remote_file_siglevel(alpm_handle_t *handle, int level) { CHECK_HANDLE(handle, return -1); -#ifdef HAVE_LIBGPGME +#if defined(HAVE_LIBGPGME) || defined(HAVE_LIBASIGNIFY) handle->remotefilesiglevel = level; #else if(level != 0 && level != ALPM_SIG_USE_DEFAULT) { diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c index 66cc3923..cf778f9d 100644 --- a/lib/libalpm/signing.c +++ b/lib/libalpm/signing.c @@ -26,6 +26,12 @@ #include <gpgme.h> #endif +#ifdef HAVE_LIBASIGNIFY +/* libasignify */ +#include <asignify.h> +#include <dirent.h> +#endif + /* libalpm */ #include "signing.h" #include "package.h" @@ -810,6 +816,67 @@ char *_alpm_sigpath(alpm_handle_t *handle, const char *path) return sigpath; } +/** + * Helper for checking the asignify signature for the given file path. + * @param handle the context handle + * @param path the full path to a file + * @param sigdata a pointer to storage for signature results + * @return 0 on success, -1 on error (consult pm_errno or sigdata) + */ +int _alpm_check_asignify_helper(alpm_handle_t *handle, const char *path, + alpm_siglist_t **sigdata) +{ + int ret = 0; + struct dirent *entry; + struct stat statbuf; + const char *pubkeydir = "/etc/pacman.d/trustedkeys"; /* Add config for this */ + + char *sigpath = _alpm_sigpath(handle, path); + asignify_verify_t *vrf = asignify_verify_init(); + + DIR *pkd = opendir(pubkeydir); + if(pkd == NULL) { + _alpm_log(handle, ALPM_LOG_DEBUG, "cannot open directory: %s\n", pubkeydir); + return -1; + } + + while((entry = readdir(pkd)) != NULL) { + char *fullpath = malloc(strlen(pubkeydir) + strlen(entry->d_name) + 2); + if (fullpath == NULL) { + _alpm_log(handle, ALPM_LOG_DEBUG, "malloc error\n"); + return -1; + } + sprintf(fullpath, "%s/%s", pubkeydir, entry->d_name); + stat(fullpath, &statbuf); + if (S_ISREG(statbuf.st_mode)) { + if (!asignify_verify_load_pubkey(vrf, fullpath)) { + /* Don't return here because there may be multiple public keys to load. */ + _alpm_log(handle, ALPM_LOG_DEBUG, "cannot load public key file: %s\n", fullpath); + } + } + free(fullpath); + } + + if (!asignify_verify_load_signature(vrf, sigpath)) { + _alpm_log(handle, ALPM_LOG_DEBUG, "cannot load signature\n"); + ret = -1; + goto asignify_cleanup; + } + + if (!asignify_verify_file(vrf, path)) { + _alpm_log(handle, ALPM_LOG_DEBUG, "signature is not valid\n"); + ret = -1; + goto asignify_cleanup; + } + + _alpm_log(handle, ALPM_LOG_DEBUG, "signature is valid\n"); + +asignify_cleanup: + free(sigpath); + asignify_verify_free(vrf); + return ret; +} + /** * Helper for checking the PGP signature for the given file path. * This wraps #_alpm_gpgme_checksig in a slightly friendlier manner to simplify diff --git a/lib/libalpm/signing.h b/lib/libalpm/signing.h index 112b2a1f..ce859373 100644 --- a/lib/libalpm/signing.h +++ b/lib/libalpm/signing.h @@ -23,13 +23,14 @@ char *_alpm_sigpath(alpm_handle_t *handle, const char *path); int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, - const char *base64_sig, alpm_siglist_t *result); - + const char *base64_sig, alpm_siglist_t *result); +int _alpm_check_asignify_helper(alpm_handle_t *handle, const char *path, + alpm_siglist_t **sigdata); int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, - const char *base64_sig, int optional, int marginal, int unknown, - alpm_siglist_t **sigdata); + const char *base64_sig, int optional, int marginal, int unknown, + alpm_siglist_t **sigdata); int _alpm_process_siglist(alpm_handle_t *handle, const char *identifier, - alpm_siglist_t *siglist, int optional, int marginal, int unknown); + alpm_siglist_t *siglist, int optional, int marginal, int unknown); int _alpm_key_in_keychain(alpm_handle_t *handle, const char *fpr); int _alpm_key_import(alpm_handle_t *handle, const char *uid, const char *fpr); diff --git a/meson.build b/meson.build index 76b9d2aa..fc9a6f1c 100644 --- a/meson.build +++ b/meson.build @@ -105,6 +105,14 @@ gpgme = dependency('gpgme', not_found_message : 'gpgme @0@ is needed for GPG signature support'.format(needed_gpgme_version)) conf.set('HAVE_LIBGPGME', gpgme.found()) +needed_libasignify_version = '>=1.0' +libasignify = dependency('libasignify', + version : needed_libasignify_version, + required : get_option('asignify'), + static : get_option('buildstatic'), + not_found_message : 'libasignify @0@ is needed for asignify signature support'.format(needed_libasignify_version)) +conf.set('HAVE_LIBASIGNIFY', libasignify.found()) + want_crypto = get_option('crypto') if want_crypto == 'openssl' libcrypto = dependency('libcrypto', static : get_option('buildstatic'), @@ -305,7 +313,7 @@ libcommon = static_library( gnu_symbol_visibility : 'hidden', install : false) -alpm_deps = [crypto_provider, libarchive, libcurl, libintl, gpgme] +alpm_deps = [crypto_provider, libarchive, libasignify, libcurl, libintl, gpgme] libalpm_a = static_library( 'alpm_objlib', @@ -453,6 +461,7 @@ message('\n '.join([ ' i18n support : @0@'.format(get_option('i18n')), ' Build docs : @0@'.format(build_doc), ' debug build : @0@'.format(get_option('buildtype') == 'debug'), + ' Use libasignify : @0@'.format(conf.get('HAVE_LIBASIGNIFY')), ' Use libcurl : @0@'.format(conf.get('HAVE_LIBCURL')), ' Use GPGME : @0@'.format(conf.get('HAVE_LIBGPGME')), ' Use OpenSSL : @0@'.format(conf.has('HAVE_LIBSSL') and diff --git a/meson_options.txt b/meson_options.txt index 4d8cc300..a254a5d4 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -48,6 +48,9 @@ option('crypto', type : 'combo', choices : ['openssl', 'nettle'], option('gpgme', type : 'feature', value : 'auto', description : 'use GPGME for PGP signature verification') +option('asignify', type : 'feature', value : 'auto', + description : 'use asignify for signature verification') + option('i18n', type : 'boolean', value : true, description : 'enable localization of pacman, libalpm and scripts') -- 2.34.1