Add two new static methods, key_search() and key_import(), to our
growing list of signing code.

If we come across a key we do not have, attempt to look it up remotely
and ask the user if they wish to import said key. If they do, flag the
validation process as a potential 'retry', meaning it might succeed the
next time it is ran.

These depend on you having a 'keyserver hkp://foo.example.com' line in
your gpg.conf file in your gnupg home directory to function.

Signed-off-by: Dan McGee <[email protected]>
---
 lib/libalpm/signing.c |   97 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 92 insertions(+), 5 deletions(-)

diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c
index e1b6452..4554d13 100644
--- a/lib/libalpm/signing.c
+++ b/lib/libalpm/signing.c
@@ -160,6 +160,74 @@ error:
        RET_ERR(handle, ALPM_ERR_GPGME, 1);
 }
 
+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);
+       if(gpg_err_code(err) == GPG_ERR_EOF) {
+               _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown 
key\n");
+       } else if(gpg_err_code(err) != GPG_ERR_NO_ERROR) {
+               _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;
+}
+
 /**
  * Decode a loaded signature in base64 form.
  * @param base64_data the signature to attempt to decode
@@ -521,6 +589,7 @@ int _alpm_process_siglist(alpm_handle_t *handle, const char 
*identifier,
        for(i = 0; i < siglist->count; i++) {
                alpm_sigresult_t *result = siglist->results + i;
                const char *name = result->key.uid ? result->key.uid : 
result->key.fingerprint;
+               int answer;
                switch(result->status) {
                        case ALPM_SIGSTATUS_VALID:
                        case ALPM_SIGSTATUS_KEY_EXPIRED:
@@ -532,6 +601,7 @@ int _alpm_process_siglist(alpm_handle_t *handle, const char 
*identifier,
                                                        _alpm_log(handle, 
ALPM_LOG_ERROR,
                                                                        _("%s: 
signature from \"%s\" is marginal trust\n"),
                                                                        
identifier, name);
+                                                       /* QUESTION(handle, 
ALPM_QUESTION_EDIT_KEY_TRUST, &result->key, NULL, NULL, &answer); */
                                                }
                                                break;
                                        case ALPM_SIGVALIDITY_UNKNOWN:
@@ -539,6 +609,7 @@ int _alpm_process_siglist(alpm_handle_t *handle, const char 
*identifier,
                                                        _alpm_log(handle, 
ALPM_LOG_ERROR,
                                                                        _("%s: 
signature from \"%s\" is unknown trust\n"),
                                                                        
identifier, name);
+                                                       /* QUESTION(handle, 
ALPM_QUESTION_EDIT_KEY_TRUST, &result->key, NULL, NULL, &answer); */
                                                }
                                                break;
                                        case ALPM_SIGVALIDITY_NEVER:
@@ -549,15 +620,31 @@ int _alpm_process_siglist(alpm_handle_t *handle, const 
char *identifier,
                                }
                                break;
                        case ALPM_SIGSTATUS_KEY_UNKNOWN:
-                               /* TODO import key here */
                                _alpm_log(handle, ALPM_LOG_ERROR,
-                                               _("%s: key \"%s\" is 
unknown\n"),
-                                               identifier, name);
+                                               _("%s: key \"%s\" is 
unknown\n"), identifier, name);
+                               {
+                                       alpm_pgpkey_t fetch_key;
+                                       memset(&fetch_key, 0, 
sizeof(fetch_key));
+
+                                       if(key_search(handle, 
result->key.fingerprint, &fetch_key)) {
+                                               _alpm_log(handle, 
ALPM_LOG_DEBUG,
+                                                               "unknown key, 
found %s on keyserver\n", fetch_key.uid);
+                                               QUESTION(handle, 
ALPM_QUESTION_IMPORT_KEY,
+                                                               &fetch_key, 
NULL, NULL, &answer);
+                                               if(answer && 
!key_import(handle, &fetch_key)) {
+                                                       retry = 1;
+                                               }
+                                       } else {
+                                               _alpm_log(handle, 
ALPM_LOG_DEBUG,
+                                                               "key could not 
be looked up remotely\n");
+                                       }
+                                       gpgme_key_unref(fetch_key.data);
+                                       break;
+                               }
                                break;
                        case ALPM_SIGSTATUS_SIG_EXPIRED:
                                _alpm_log(handle, ALPM_LOG_ERROR,
-                                               _("%s: signature from \"%s\" is 
expired\n"),
-                                               identifier, name);
+                                               _("%s: signature from \"%s\" is 
expired\n"), identifier, name);
                                break;
                        case ALPM_SIGSTATUS_INVALID:
                                _alpm_log(handle, ALPM_LOG_ERROR,
-- 
1.7.6.3


Reply via email to