diff options
Diffstat (limited to '')
-rw-r--r-- | g10/getkey.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/g10/getkey.c b/g10/getkey.c index ad0c207bf..45c3f512b 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -366,6 +366,282 @@ getkey_disable_caches () } +void +pubkey_free (struct pubkey *key) +{ + xfree (key->pk); + release_kbnode (key->keyblock); + xfree (key); +} + +void +pubkeys_free (struct pubkey *keys) +{ + while (keys) + { + struct pubkey *next = keys->next; + pubkey_free (keys); + keys = next; + } +} + +/* Returns all keys that match the search specfication SEARCH_TERMS. + + This function also checks for and warns about duplicate entries in + the keydb, which can occur if the user has configured multiple + keyrings or keyboxes or if a keyring or keybox was corrupted. + + Note: SEARCH_TERMS will not be expanded (i.e., it may not be a + group). + + USE is the operation for which the key is required. It must be + either PUBKEY_USAGE_ENC, PUBKEY_USAGE_SIG, PUBKEY_USAGE_CERT or + PUBKEY_USAGE_AUTH. + + XXX: Currently, only PUBKEY_USAGE_ENC and PUBKEY_USAGE_SIG are + implemented. + + INCLUDE_UNUSABLE indicates whether disabled keys are allowed. + (Recipients specified with --encrypt-to and --hidden-encrypt-to may + be disabled. It is possible to edit disabled keys.) + + SOURCE is the context in which SEARCH_TERMS was specified, e.g., + "--encrypt-to", etc. If this function is called interactively, + then this should be NULL. + + If WARN_POSSIBLY_AMBIGUOUS is set, then emits a warning if the user + does not specify a long key id or a fingerprint. + + The results are placed in *KEYS. *KEYS must be NULL! */ +gpg_error_t +get_pubkeys (ctrl_t ctrl, + char *search_terms, int use, int include_unusable, char *source, + int warn_possibly_ambiguous, + struct pubkey **keys) +{ + /* We show a warning when a key appears multiple times in the DB. + This can happen for two reasons: + + - The user has configured multiple keyrings or keyboxes. + + - The keyring or keybox has been corrupted in some way, e.g., a + bug or a random process changing them. + + For each duplicate, we only want to show the key once. Hence, + this list. */ + static strlist_t key_dups; + + /* USE transformed to a string. */ + char *use_str; + + gpg_error_t err; + + KEYDB_SEARCH_DESC desc; + + GETKEY_CTX ctx; + struct pubkey *results = NULL; + struct pubkey *r; + + int count; + + char fingerprint[2 * MAX_FINGERPRINT_LEN + 1]; + + if (DBG_LOOKUP) + { + log_debug ("\n"); + log_debug ("%s: Checking %s=%s\n", + __func__, source ? source : "user input", search_terms); + } + + if (*keys) + log_bug ("%s: KEYS should be NULL!\n", __func__); + + switch (use) + { + case PUBKEY_USAGE_ENC: use_str = "encrypt"; break; + case PUBKEY_USAGE_SIG: use_str = "sign"; break; + case PUBKEY_USAGE_CERT: use_str = "cetify"; break; + case PUBKEY_USAGE_AUTH: use_str = "authentication"; break; + default: log_bug ("%s: Bad value for USE (%d)\n", __func__, use); + } + + if (use == PUBKEY_USAGE_CERT || use == PUBKEY_USAGE_AUTH) + log_bug ("%s: use=%s is unimplemented.\n", __func__, use_str); + + err = classify_user_id (search_terms, &desc, 1); + if (err) + { + log_info (_("key \"%s\" not found: %s\n"), + search_terms, gpg_strerror (err)); + if (!opt.quiet && source) + log_info (_("(check argument of option '%s')\n"), source); + goto out; + } + + if (warn_possibly_ambiguous + && ! (desc.mode == KEYDB_SEARCH_MODE_LONG_KID + || desc.mode == KEYDB_SEARCH_MODE_FPR16 + || desc.mode == KEYDB_SEARCH_MODE_FPR20 + || desc.mode == KEYDB_SEARCH_MODE_FPR)) + { + log_info (_("Warning: '%s' should be a long key ID or a fingerprint\n"), + search_terms); + if (!opt.quiet && source) + log_info (_("(check argument of option '%s')\n"), source); + } + + /* Gather all of the results. */ + ctx = NULL; + count = 0; + do + { + PKT_public_key *pk = xmalloc_clear (sizeof *pk); + KBNODE kb; + pk->req_usage = use; + + if (! ctx) + err = get_pubkey_byname (ctrl, &ctx, pk, search_terms, &kb, NULL, + include_unusable, 1); + else + err = getkey_next (ctx, pk, &kb); + + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) + /* No more results. */ + { + xfree (pk); + break; + } + else if (err) + /* An error (other than "not found"). */ + { + log_error (_("error looking up: %s\n"), + gpg_strerror (err)); + xfree (pk); + break; + } + + /* Another result! */ + count ++; + + r = xmalloc_clear (sizeof (*r)); + r->pk = pk; + r->keyblock = kb; + r->next = results; + results = r; + } + while (ctx); + getkey_end (ctx); + + if (DBG_LOOKUP) + { + log_debug ("%s resulted in %d matches.\n", search_terms, count); + for (r = results; r; r = r->next) + log_debug (" %s\n", + hexfingerprint (r->keyblock->pkt->pkt.public_key, + fingerprint, sizeof (fingerprint))); + } + + if (! results && gpg_err_code (err) == GPG_ERR_NOT_FOUND) + /* No match. */ + { + if (DBG_LOOKUP) + log_debug ("%s: '%s' not found.\n", __func__, search_terms); + + log_info (_("key \"%s\" not found\n"), search_terms); + if (!opt.quiet && source) + log_info (_("(check argument of option '%s')\n"), source); + + goto out; + } + else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) + /* No more matches. */ + ; + else if (err) + /* Some other error. An error message was already printed + out. Free RESULTS and continue. */ + goto out; + + /* Check for duplicates. */ + if (DBG_LOOKUP) + log_debug ("%s: Checking results of %s='%s' for dups\n", + __func__, source ? source : "user input", search_terms); + count = 0; + for (r = results; r; r = r->next) + { + struct pubkey **prevp; + struct pubkey *next; + struct pubkey *r2; + int dups = 0; + + prevp = &r->next; + next = r->next; + while ((r2 = next)) + { + if (cmp_public_keys (r->keyblock->pkt->pkt.public_key, + r2->keyblock->pkt->pkt.public_key) != 0) + /* Not a dup. */ + { + prevp = &r2->next; + next = r2->next; + continue; + } + + dups ++; + count ++; + + /* Remove R2 from the list. */ + *prevp = r2->next; + release_kbnode (r2->keyblock); + next = r2->next; + xfree (r2); + } + + if (dups) + { + hexfingerprint (r->keyblock->pkt->pkt.public_key, + fingerprint, sizeof fingerprint); + if (! strlist_find (key_dups, fingerprint)) + { + char fingerprint_formatted[MAX_FORMATTED_FINGERPRINT_LEN + 1]; + + log_info (_("Warning: %s appears in the keyring %d times.\n"), + format_hexfingerprint (fingerprint, + fingerprint_formatted, + sizeof fingerprint_formatted), + 1 + dups); + add_to_strlist (&key_dups, fingerprint); + } + } + } + + if (DBG_LOOKUP && count) + { + log_debug ("After removing %d dups:\n", count); + for (r = results, count = 0; r; r = r->next) + log_debug (" %d: %s\n", + count, + hexfingerprint (r->keyblock->pkt->pkt.public_key, + fingerprint, sizeof fingerprint)); + } + + out: + if (err) + { + while ((r = results)) + { + results = results->next; + pubkey_free (r); + release_kbnode (r->keyblock); + xfree (r); + } + } + else + *keys = results; + + return err; +} + + static void pk_from_block (GETKEY_CTX ctx, PKT_public_key * pk, KBNODE keyblock, KBNODE found_key) |