This allows the backend to prompt, as we do elsewhere, if we encounter an unknown key among those signatures we are attempting to verify. Add some search and import gpgme helper methods that do the heavy lifting, then add case code for the KEY_UNKNOWN case. Signed-off-by: Dan McGee <dan@archlinux.org> --- Note that this will prompt and import if Y is selected, but trust levels aren't being touched yet, so it still may show you the misleading invalid PGP signature error message and abort the transaction. However, if you do a before/after using pacman-key, you will see the new key is present. lib/libalpm/alpm.h | 1 + lib/libalpm/signing.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++-- src/pacman/callback.c | 9 ++++ 3 files changed, 117 insertions(+), 4 deletions(-) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index c94cdf7..57f95d9 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -940,6 +940,7 @@ typedef enum _alpm_transconv_t { ALPM_TRANS_CONV_LOCAL_NEWER = (1 << 4), ALPM_TRANS_CONV_REMOVE_PKGS = (1 << 5), ALPM_TRANS_CONV_SELECT_PROVIDER = (1 << 6), + ALPM_TRANS_CONV_IMPORT_KEY = (1 << 7) } alpm_transconv_t; /** Transaction Progress */ diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c index 2ca7cce..67a2280 100644 --- a/lib/libalpm/signing.c +++ b/lib/libalpm/signing.c @@ -36,6 +36,7 @@ #include "log.h" #include "alpm.h" #include "handle.h" +#include "trans.h" #if HAVE_LIBGPGME #define CHECK_ERR(void) do { \ @@ -408,13 +409,83 @@ error: } return ret; } -#else + +static int key_search(alpm_handle_t *handle, const char *fpr, + alpm_pgpkey_t *pgpkey) +{ + gpgme_error_t err; + gpgme_ctx_t ctx; + gpgme_keylist_mode_t mode; + gpgme_key_t key; + + memset(&ctx, 0, sizeof(ctx)); + err = gpgme_new(&ctx); + CHECK_ERR(); + + mode = gpgme_get_keylist_mode(ctx); + /* using LOCAL and EXTERN together doesn't work for GPG 1.X. Ugh. */ + mode &= ~GPGME_KEYLIST_MODE_LOCAL; + mode |= GPGME_KEYLIST_MODE_EXTERN; + err = gpgme_set_keylist_mode(ctx, mode); + CHECK_ERR(); + + _alpm_log(handle, ALPM_LOG_DEBUG, "looking up key %s\n", fpr); + + err = gpgme_get_key(ctx, fpr, &key, 0); + _alpm_log(handle, ALPM_LOG_DEBUG, "bogus logger\n"); + if(gpg_err_code(err) == GPG_ERR_EOF) { + _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n"); + } + _alpm_log(handle, ALPM_LOG_DEBUG, + "gpg error: %s\n", gpgme_strerror(err)); + CHECK_ERR(); + + /* should only get here if key actually exists */ + pgpkey->data = key; + if(key->subkeys->fpr) { + pgpkey->fingerprint = key->subkeys->fpr; + } else if(key->subkeys->keyid) { + pgpkey->fingerprint = key->subkeys->keyid; + } + pgpkey->uid = key->uids->uid; + pgpkey->name = key->uids->name; + pgpkey->email = key->uids->email; + pgpkey->created = key->subkeys->timestamp; + pgpkey->expires = key->subkeys->expires; + +error: + gpgme_release(ctx); + return gpg_err_code(err) == GPG_ERR_NO_ERROR; +} + +static int key_import(alpm_handle_t *handle, alpm_pgpkey_t *key) +{ + gpgme_error_t err; + gpgme_ctx_t ctx; + gpgme_key_t keys[2]; + + memset(&ctx, 0, sizeof(ctx)); + err = gpgme_new(&ctx); + CHECK_ERR(); + + + keys[0] = key->data; + keys[1] = NULL; + err = gpgme_op_import_keys(ctx, keys); + CHECK_ERR(); + +error: + gpgme_release(ctx); + return gpg_err_code(err) != GPG_ERR_NO_ERROR; +} + +#else /* HAVE_LIBGPGME */ int _alpm_gpgme_checksig(alpm_handle_t UNUSED *handle, const char UNUSED *path, const char UNUSED *base64_sig, alpm_sigresult_t UNUSED *result) { return -1; } -#endif +#endif /* HAVE_LIBGPGME */ /** * Form a signature path given a file path. @@ -441,7 +512,7 @@ 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 siglist; - int ret; + int ret, rerun = 0; memset(&siglist, 0, sizeof(alpm_siglist_t)); @@ -488,8 +559,34 @@ int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, break; } break; - case ALPM_SIGSTATUS_SIG_EXPIRED: case ALPM_SIGSTATUS_KEY_UNKNOWN: + { + alpm_pgpkey_t fetch_key; + const char *fpr = siglist.results[num].key.fingerprint; + + memset(&fetch_key, 0, sizeof(fetch_key)); + + if(key_search(handle, fpr, &fetch_key)) { + int doimport = 0; + _alpm_log(handle, ALPM_LOG_DEBUG, + "unknown key, found %s on keyserver\n", fetch_key.uid); + QUESTION(handle->trans, ALPM_TRANS_CONV_IMPORT_KEY, + &fetch_key, NULL, NULL, &doimport); + if(doimport && !key_import(handle, &fetch_key)) { + rerun = 1; + } else { + _alpm_log(handle, ALPM_LOG_DEBUG, + "signature is not valid, key not imported\n"); + ret = -1; + } + } else { + _alpm_log(handle, ALPM_LOG_DEBUG, + "signature is not valid, key unknown\n"); + ret = -1; + } + break; + } + case ALPM_SIGSTATUS_SIG_EXPIRED: case ALPM_SIGSTATUS_INVALID: _alpm_log(handle, ALPM_LOG_DEBUG, "signature is not valid\n"); ret = -1; @@ -499,6 +596,12 @@ int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, } alpm_siglist_cleanup(&siglist); + + if(rerun) { + return _alpm_check_pgp_helper(handle, path, base64_sig, + optional, marginal, unknown); + } + return ret; } diff --git a/src/pacman/callback.c b/src/pacman/callback.c index 5ee4e5a..5859788 100644 --- a/src/pacman/callback.c +++ b/src/pacman/callback.c @@ -336,6 +336,15 @@ void cb_trans_conv(alpm_transconv_t event, void *data1, void *data2, (char *)data1, alpm_strerror(*(enum _alpm_errno_t *)data2)); break; + case ALPM_TRANS_CONV_IMPORT_KEY: + { + alpm_pgpkey_t *key = data1; + char created[12]; + strftime(created, 12, "%Y-%m-%d", localtime(&(key->created))); + *response = yesno(_(":: Import PGP key %s, \"%s\", created %s?"), + key->fingerprint, key->uid, created); + } + break; } if(config->noask) { if(config->ask & event) { -- 1.7.6